diff --git a/.gitignore b/.gitignore
index e400c01f5..758a324f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -109,7 +109,6 @@ target/
!*.gitkeep
!config_examples/config_binance.example.json
!config_examples/config_bittrex.example.json
-!config_examples/config_ftx.example.json
!config_examples/config_full.example.json
!config_examples/config_kraken.example.json
!config_examples/config_freqai.example.json
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ca42402dd..b4a8336b9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,9 +15,9 @@ repos:
additional_dependencies:
- types-cachetools==5.2.1
- types-filelock==3.2.7
- - types-requests==2.28.11.2
+ - types-requests==2.28.11.4
- types-tabulate==0.9.0.0
- - types-python-dateutil==2.8.19.2
+ - types-python-dateutil==2.8.19.3
# stages: [push]
- repo: https://github.com/pycqa/isort
diff --git a/README.md b/README.md
index c0452fa85..30ad1ad2a 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,6 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even
- [X] [Binance](https://www.binance.com/)
- [X] [Bittrex](https://bittrex.com/)
-- [X] [FTX](https://ftx.com/#a=2258149)
- [X] [Gate.io](https://www.gate.io/ref/6266643)
- [X] [Huobi](http://huobi.com/)
- [X] [Kraken](https://kraken.com/)
diff --git a/config_examples/config_ftx.example.json b/config_examples/config_ftx.example.json
deleted file mode 100644
index c49898277..000000000
--- a/config_examples/config_ftx.example.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
- "max_open_trades": 3,
- "stake_currency": "USD",
- "stake_amount": 50,
- "tradable_balance_ratio": 0.99,
- "fiat_display_currency": "USD",
- "timeframe": "5m",
- "dry_run": true,
- "cancel_open_orders_on_exit": false,
- "unfilledtimeout": {
- "entry": 10,
- "exit": 10,
- "exit_timeout_count": 0,
- "unit": "minutes"
- },
- "entry_pricing": {
- "price_side": "same",
- "use_order_book": true,
- "order_book_top": 1,
- "price_last_balance": 0.0,
- "check_depth_of_market": {
- "enabled": false,
- "bids_to_ask_delta": 1
- }
- },
- "exit_pricing": {
- "price_side": "same",
- "use_order_book": true,
- "order_book_top": 1
- },
- "exchange": {
- "name": "ftx",
- "key": "your_exchange_key",
- "secret": "your_exchange_secret",
- "ccxt_config": {},
- "ccxt_async_config": {},
- "pair_whitelist": [
- "BTC/USD",
- "ETH/USD",
- "BNB/USD",
- "USDT/USD",
- "LTC/USD",
- "SRM/USD",
- "SXP/USD",
- "XRP/USD",
- "DOGE/USD",
- "1INCH/USD",
- "CHZ/USD",
- "MATIC/USD",
- "LINK/USD",
- "OXY/USD",
- "SUSHI/USD"
- ],
- "pair_blacklist": [
- "FTT/USD"
- ]
- },
- "pairlists": [
- {"method": "StaticPairList"}
- ],
- "edge": {
- "enabled": false,
- "process_throttle_secs": 3600,
- "calculate_since_number_of_days": 7,
- "allowed_risk": 0.01,
- "stoploss_range_min": -0.01,
- "stoploss_range_max": -0.1,
- "stoploss_range_step": -0.01,
- "minimum_winrate": 0.60,
- "minimum_expectancy": 0.20,
- "min_trade_number": 10,
- "max_trade_duration_minute": 1440,
- "remove_pumps": false
- },
- "telegram": {
- "enabled": false,
- "token": "your_telegram_token",
- "chat_id": "your_telegram_chat_id"
- },
- "api_server": {
- "enabled": false,
- "listen_ip_address": "127.0.0.1",
- "listen_port": 8080,
- "verbosity": "error",
- "jwt_secret_key": "somethingrandom",
- "CORS_origins": [],
- "username": "freqtrader",
- "password": "SuperSecurePassword"
- },
- "bot_name": "freqtrade",
- "initial_state": "running",
- "force_entry_enable": false,
- "internals": {
- "process_throttle_secs": 5
- }
-}
diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json
index 5a5096f81..b60957b58 100644
--- a/config_examples/config_full.example.json
+++ b/config_examples/config_full.example.json
@@ -204,6 +204,7 @@
"strategy_path": "user_data/strategies/",
"recursive_strategy_search": false,
"add_config_files": [],
+ "reduce_df_footprint": false,
"dataformat_ohlcv": "json",
"dataformat_trades": "jsongz"
}
diff --git a/docs/configuration.md b/docs/configuration.md
index e773e1878..ce4453561 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -253,6 +253,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `add_config_files` | Additional config files. These files will be loaded and merged with the current config file. The files are resolved relative to the initial file.
*Defaults to `[]`*.
**Datatype:** List of strings
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data.
*Defaults to `json`*.
**Datatype:** String
| `dataformat_trades` | Data format to use to store historical trades data.
*Defaults to `jsongz`*.
**Datatype:** String
+| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage (and decreasing train/inference timing in FreqAI). (Currently only affects FreqAI use-cases)
**Datatype:** Boolean.
Default: `False`.
### Parameters in the strategy
@@ -552,7 +553,7 @@ The possible values are: `GTC` (default), `FOK` or `IOC`.
```
!!! Warning
- This is ongoing work. For now, it is supported only for binance, gate, ftx and kucoin.
+ This is ongoing work. For now, it is supported only for binance, gate and kucoin.
Please don't change the default value unless you know what you are doing and have researched the impact of using different values for your particular exchange.
### What values can be used for fiat_display_currency?
diff --git a/docs/data-download.md b/docs/data-download.md
index 700ca04f4..a7b1987aa 100644
--- a/docs/data-download.md
+++ b/docs/data-download.md
@@ -177,13 +177,13 @@ freqtrade download-data --exchange binance --pairs ETH/USDT XRP/USDT BTC/USDT --
### Data format
-Freqtrade currently supports 3 data-formats for both OHLCV and trades data:
+Freqtrade currently supports the following data-formats:
* `json` - plain "text" json files
* `jsongz` - a gzip-zipped version of json files
* `hdf5` - a high performance datastore
-* `feather` - a dataformat based on Apache Arrow
-* `parquet` - columnar datastore
+* `feather` - a dataformat based on Apache Arrow (OHLCV only)
+* `parquet` - columnar datastore (OHLCV only)
By default, OHLCV data is stored as `json` data, while trades data is stored as `jsongz` data.
diff --git a/docs/exchanges.md b/docs/exchanges.md
index bae7c929c..b4eb7e023 100644
--- a/docs/exchanges.md
+++ b/docs/exchanges.md
@@ -173,30 +173,6 @@ res = [p for p, x in lm.items() if 'US' in x['info']['prohibitedIn']]
print(res)
```
-## FTX
-
-!!! Warning
- Due to the current situation, we can no longer recommend FTX.
- Please make sure to investigate the current situation before depositing any funds to FTX.
-
-!!! Tip "Stoploss on Exchange"
- FTX supports `stoploss_on_exchange` and can use both stop-loss-market and stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it.
- You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type of stoploss shall be used.
-
-### Using subaccounts
-
-To use subaccounts with FTX, you need to edit the configuration and add the following:
-
-``` json
-"exchange": {
- "ccxt_config": {
- "headers": {
- "FTX-SUBACCOUNT": "name"
- }
- },
-}
-```
-
## Kucoin
Kucoin requires a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows:
diff --git a/docs/freqai-parameter-table.md b/docs/freqai-parameter-table.md
index 8a240c372..c027a12b1 100644
--- a/docs/freqai-parameter-table.md
+++ b/docs/freqai-parameter-table.md
@@ -50,3 +50,4 @@ Mandatory parameters are marked as **Required** and have to be set in one of the
| | **Extraneous parameters**
| `keras` | If the selected model makes use of Keras (typical for Tensorflow-based prediction models), this flag needs to be activated so that the model save/loading follows Keras standards.
**Datatype:** Boolean.
Default: `False`.
| `conv_width` | The width of a convolutional neural network input tensor. This replaces the need for shifting candles (`include_shifted_candles`) by feeding in historical data points as the second dimension of the tensor. Technically, this parameter can also be used for regressors, but it only adds computational overhead and does not change the model training/prediction.
**Datatype:** Integer.
Default: `2`.
+| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage and decreasing train/inference timing. This parameter is set in the main level of the Freqtrade configuration file (not inside FreqAI).
**Datatype:** Boolean.
Default: `False`.
diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md
index dd3149e98..d61718c7d 100644
--- a/docs/includes/pairlists.md
+++ b/docs/includes/pairlists.md
@@ -268,7 +268,7 @@ This option is disabled by default, and will only apply if set to > 0.
The `max_value` setting removes pairs where the minimum value change is above a specified value.
This is useful when an exchange has unbalanced limits. For example, if step-size = 1 (so you can only buy 1, or 2, or 3, but not 1.1 Coins) - and the price is pretty high (like 20\$) as the coin has risen sharply since the last limit adaption.
As a result of the above, you can only buy for 20\$, or 40\$ - but not for 25\$.
-On exchanges that deduct fees from the receiving currency (e.g. FTX) - this can result in high value coins / amounts that are unsellable as the amount is slightly below the limit.
+On exchanges that deduct fees from the receiving currency (e.g. binance) - this can result in high value coins / amounts that are unsellable as the amount is slightly below the limit.
The `low_price_ratio` setting removes pairs where a raise of 1 price unit (pip) is above the `low_price_ratio` ratio.
This option is disabled by default, and will only apply if set to > 0.
diff --git a/docs/index.md b/docs/index.md
index 5b4add52c..5459eac1b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -40,7 +40,6 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual,
- [X] [Binance](https://www.binance.com/)
- [X] [Bittrex](https://bittrex.com/)
-- [X] [FTX](https://ftx.com/#a=2258149)
- [X] [Gate.io](https://www.gate.io/ref/6266643)
- [X] [Huobi](http://huobi.com/)
- [X] [Kraken](https://kraken.com/)
diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt
index d7d2e27b7..224e9b548 100644
--- a/docs/requirements-docs.txt
+++ b/docs/requirements-docs.txt
@@ -1,6 +1,6 @@
markdown==3.3.7
mkdocs==1.4.2
-mkdocs-material==8.5.8
+mkdocs-material==8.5.10
mdx_truly_sane_lists==1.3
-pymdown-extensions==9.7
+pymdown-extensions==9.8
jinja2==3.1.2
diff --git a/docs/stoploss.md b/docs/stoploss.md
index a8285cf04..20e53d8f5 100644
--- a/docs/stoploss.md
+++ b/docs/stoploss.md
@@ -24,7 +24,7 @@ These modes can be configured with these values:
```
!!! Note
- Stoploss on exchange is only supported for Binance (stop-loss-limit), Huobi (stop-limit), Kraken (stop-loss-market, stop-loss-limit), FTX (stop limit and stop-market) Gateio (stop-limit), and Kucoin (stop-limit and stop-market) as of now.
+ Stoploss on exchange is only supported for Binance (stop-loss-limit), Huobi (stop-limit), Kraken (stop-loss-market, stop-loss-limit), Gateio (stop-limit), and Kucoin (stop-limit and stop-market) as of now.
Do not set too low/tight stoploss value if using stop loss on exchange!
If set to low/tight then you have greater risk of missing fill on the order and stoploss will not work.
diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md
index f036182e3..3e8bab8ee 100644
--- a/docs/strategy-customization.md
+++ b/docs/strategy-customization.md
@@ -723,7 +723,7 @@ if self.dp.runmode.value in ('live', 'dry_run'):
!!! Warning
Although the ticker data structure is a part of the ccxt Unified Interface, the values returned by this method can
- vary for different exchanges. For instance, many exchanges do not return `vwap` values, the FTX exchange
+ vary for different exchanges. For instance, many exchanges do not return `vwap` values, some exchanges
does not always fills in the `last` field (so it can be None), etc. So you need to carefully verify the ticker
data returned from the exchange and add appropriate error handling / defaults.
diff --git a/docs/utils.md b/docs/utils.md
index ee8793159..3d8a3bd03 100644
--- a/docs/utils.md
+++ b/docs/utils.md
@@ -263,7 +263,6 @@ equos True missing opt: fetchTicker, fetchTickers
eterbase True
fcoin True missing opt: fetchMyTrades, fetchTickers
fcoinjp True missing opt: fetchMyTrades, fetchTickers
-ftx True
gateio True
gemini True
gopax True
@@ -369,7 +368,6 @@ fcoin True missing opt: fetchMyTrades, fetchTickers
fcoinjp True missing opt: fetchMyTrades, fetchTickers
flowbtc False missing: fetchOrder, fetchOHLCV
foxbit False missing: fetchOrder, fetchOHLCV
-ftx True
gateio True
gemini True
gopax True
diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py
index 1abd26328..f95a08ba5 100644
--- a/freqtrade/commands/build_config_commands.py
+++ b/freqtrade/commands/build_config_commands.py
@@ -108,7 +108,6 @@ def ask_user_config() -> Dict[str, Any]:
"binance",
"binanceus",
"bittrex",
- "ftx",
"gateio",
"huobi",
"kraken",
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
index 022cbd400..534d06fd4 100644
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -159,6 +159,7 @@ CONF_SCHEMA = {
'ignore_buying_expired_candle_after': {'type': 'number'},
'trading_mode': {'type': 'string', 'enum': TRADING_MODES},
'margin_mode': {'type': 'string', 'enum': MARGIN_MODES},
+ 'reduce_df_footprint': {'type': 'boolean', 'default': False},
'liquidation_buffer': {'type': 'number', 'minimum': 0.0, 'maximum': 0.99},
'backtest_breakdown': {
'type': 'array',
diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py
index 98ed15489..6a49a4799 100644
--- a/freqtrade/data/converter.py
+++ b/freqtrade/data/converter.py
@@ -7,6 +7,7 @@ from datetime import datetime, timezone
from operator import itemgetter
from typing import Dict, List
+import numpy as np
import pandas as pd
from pandas import DataFrame, to_datetime
@@ -313,3 +314,29 @@ def convert_ohlcv_format(
if erase and convert_from != convert_to:
logger.info(f"Deleting source data for {pair} / {timeframe}")
src.ohlcv_purge(pair=pair, timeframe=timeframe, candle_type=candle_type)
+
+
+def reduce_dataframe_footprint(df: DataFrame) -> DataFrame:
+ """
+ Ensure all values are float32 in the incoming dataframe.
+ :param df: Dataframe to be converted to float/int 32s
+ :return: Dataframe converted to float/int 32s
+ """
+
+ logger.debug(f"Memory usage of dataframe is "
+ f"{df.memory_usage().sum() / 1024**2:.2f} MB")
+
+ df_dtypes = df.dtypes
+ for column, dtype in df_dtypes.items():
+ if column in ['open', 'high', 'low', 'close', 'volume']:
+ continue
+ if dtype == np.float64:
+ df_dtypes[column] = np.float32
+ elif dtype == np.int64:
+ df_dtypes[column] = np.int32
+ df = df.astype(df_dtypes)
+
+ logger.debug(f"Memory usage after optimization is: "
+ f"{df.memory_usage().sum() / 1024**2:.2f} MB")
+
+ return df
diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py
index 45b4cd8f1..4656b7c93 100644
--- a/freqtrade/edge/edge_positioning.py
+++ b/freqtrade/edge/edge_positioning.py
@@ -392,7 +392,7 @@ class Edge:
# Returning a list of pairs in order of "expectancy"
return final
- def _find_trades_for_stoploss_range(self, df, pair, stoploss_range):
+ def _find_trades_for_stoploss_range(self, df, pair: str, stoploss_range) -> list:
buy_column = df['enter_long'].values
sell_column = df['exit_long'].values
date_column = df['date'].values
@@ -407,7 +407,7 @@ class Edge:
return result
def _detect_next_stop_or_sell_point(self, buy_column, sell_column, date_column,
- ohlc_columns, stoploss, pair):
+ ohlc_columns, stoploss, pair: str):
"""
Iterate through ohlc_columns in order to find the next trade
Next trade opens from the first buy signal noticed to
diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py
index 8294838b1..9aed5c507 100644
--- a/freqtrade/exchange/__init__.py
+++ b/freqtrade/exchange/__init__.py
@@ -18,7 +18,6 @@ from freqtrade.exchange.exchange_utils import (amount_to_contract_precision, amo
timeframe_to_next_date, timeframe_to_prev_date,
timeframe_to_seconds, validate_exchange,
validate_exchanges)
-from freqtrade.exchange.ftx import Ftx
from freqtrade.exchange.gateio import Gateio
from freqtrade.exchange.hitbtc import Hitbtc
from freqtrade.exchange.huobi import Huobi
diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py
index 5765dc459..6d09c4f95 100644
--- a/freqtrade/exchange/common.py
+++ b/freqtrade/exchange/common.py
@@ -52,7 +52,6 @@ MAP_EXCHANGE_CHILDCLASS = {
SUPPORTED_EXCHANGES = [
'binance',
'bittrex',
- 'ftx',
'gateio',
'huobi',
'kraken',
diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py
deleted file mode 100644
index 6a43ab302..000000000
--- a/freqtrade/exchange/ftx.py
+++ /dev/null
@@ -1,178 +0,0 @@
-""" FTX exchange subclass """
-import logging
-from typing import Any, Dict, List, Optional, Tuple
-
-import ccxt
-
-from freqtrade.constants import BuySell
-from freqtrade.enums import MarginMode, TradingMode
-from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException,
- OperationalException, TemporaryError)
-from freqtrade.exchange import Exchange
-from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT, retrier
-from freqtrade.misc import safe_value_fallback2
-
-
-logger = logging.getLogger(__name__)
-
-
-class Ftx(Exchange):
-
- _ft_has: Dict = {
- "order_time_in_force": ['GTC', 'IOC', 'PO'],
- "stoploss_on_exchange": True,
- "ohlcv_candle_limit": 1500,
- "ohlcv_require_since": True,
- "ohlcv_volume_currency": "quote",
- "mark_ohlcv_price": "index",
- "mark_ohlcv_timeframe": "1h",
- }
-
- _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
- # TradingMode.SPOT always supported and not required in this list
- # (TradingMode.MARGIN, MarginMode.CROSS),
- # (TradingMode.FUTURES, MarginMode.CROSS)
- ]
-
- def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
- """
- Verify stop_loss against stoploss-order value (limit or price)
- Returns True if adjustment is necessary.
- """
- return order['type'] == 'stop' and (
- side == "sell" and stop_loss > float(order['price']) or
- side == "buy" and stop_loss < float(order['price'])
- )
-
- @retrier(retries=0)
- def stoploss(self, pair: str, amount: float, stop_price: float,
- order_types: Dict, side: BuySell, leverage: float) -> Dict:
- """
- Creates a stoploss order.
- depending on order_types.stoploss configuration, uses 'market' or limit order.
-
- Limit orders are defined by having orderPrice set, otherwise a market order is used.
- """
- limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99)
- if side == "sell":
- limit_rate = stop_price * limit_price_pct
- else:
- limit_rate = stop_price * (2 - limit_price_pct)
-
- ordertype = "stop"
-
- stop_price = self.price_to_precision(pair, stop_price)
-
- if self._config['dry_run']:
- dry_order = self.create_dry_run_order(
- pair, ordertype, side, amount, stop_price, leverage, stop_loss=True)
- return dry_order
-
- try:
- params = self._params.copy()
- if order_types.get('stoploss', 'market') == 'limit':
- # set orderPrice to place limit order, otherwise it's a market order
- params['orderPrice'] = limit_rate
- if self.trading_mode == TradingMode.FUTURES:
- params.update({'reduceOnly': True})
-
- params['stopPrice'] = stop_price
- amount = self.amount_to_precision(pair, amount)
-
- self._lev_prep(pair, leverage, side)
- order = self._api.create_order(symbol=pair, type=ordertype, side=side,
- amount=amount, params=params)
- self._log_exchange_response('create_stoploss_order', order)
- logger.info('stoploss order added for %s. '
- 'stop price: %s.', pair, stop_price)
- return order
- except ccxt.InsufficientFunds as e:
- raise InsufficientFundsError(
- f'Insufficient funds to create {ordertype} {side} order on market {pair}. '
- f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
- f'Message: {e}') from e
- except ccxt.InvalidOrder as e:
- raise InvalidOrderException(
- f'Could not create {ordertype} {side} order on market {pair}. '
- f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
- f'Message: {e}') from e
- except ccxt.DDoSProtection as e:
- raise DDosProtection(e) from e
- except (ccxt.NetworkError, ccxt.ExchangeError) as e:
- raise TemporaryError(
- f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}') from e
- except ccxt.BaseError as e:
- raise OperationalException(e) from e
-
- @retrier(retries=API_FETCH_ORDER_RETRY_COUNT)
- def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
- if self._config['dry_run']:
- return self.fetch_dry_run_order(order_id)
-
- try:
- orders = self._api.fetch_orders(pair, None, params={'type': 'stop'})
-
- order = [order for order in orders if order['id'] == order_id]
- self._log_exchange_response('fetch_stoploss_order', order)
- if len(order) == 1:
- if order[0].get('status') == 'closed':
- # Trigger order was triggered ...
- real_order_id: Optional[str] = order[0].get('info', {}).get('orderId')
- # OrderId may be None for stoploss-market orders
- # So we need to get it through the endpoint
- # /conditional_orders/{conditional_order_id}/triggers
- if not real_order_id:
- res = self._api.privateGetConditionalOrdersConditionalOrderIdTriggers(
- params={'conditional_order_id': order_id})
- self._log_exchange_response('fetch_stoploss_order2', res)
- real_order_id = res['result'][0]['orderId'] if res.get(
- 'result', []) else None
-
- if real_order_id:
- order1 = self._api.fetch_order(real_order_id, pair)
- self._log_exchange_response('fetch_stoploss_order1', order1)
- # Fake type to stop - as this was really a stop order.
- order1['id_stop'] = order1['id']
- order1['id'] = order_id
- order1['type'] = 'stop'
- order1['status_stop'] = 'triggered'
- return order1
-
- return order[0]
- else:
- raise InvalidOrderException(f"Could not get stoploss order for id {order_id}")
-
- except ccxt.InvalidOrder as e:
- raise InvalidOrderException(
- f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e
- except ccxt.DDoSProtection as e:
- raise DDosProtection(e) from e
- except (ccxt.NetworkError, ccxt.ExchangeError) as e:
- raise TemporaryError(
- f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e
- except ccxt.BaseError as e:
- raise OperationalException(e) from e
-
- @retrier
- def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
- if self._config['dry_run']:
- return {}
- try:
- order = self._api.cancel_order(order_id, pair, params={'type': 'stop'})
- self._log_exchange_response('cancel_stoploss_order', order)
- return order
- except ccxt.InvalidOrder as e:
- raise InvalidOrderException(
- f'Could not cancel order. Message: {e}') from e
- except ccxt.DDoSProtection as e:
- raise DDosProtection(e) from e
- except (ccxt.NetworkError, ccxt.ExchangeError) as e:
- raise TemporaryError(
- f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e
- except ccxt.BaseError as e:
- raise OperationalException(e) from e
-
- def get_order_id_conditional(self, order: Dict[str, Any]) -> str:
- if order['type'] == 'stop':
- return safe_value_fallback2(order, order, 'id_stop', 'id')
- return order['id']
diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py
index 12a3cd519..d717858d2 100644
--- a/freqtrade/freqai/data_kitchen.py
+++ b/freqtrade/freqai/data_kitchen.py
@@ -19,6 +19,7 @@ from sklearn.neighbors import NearestNeighbors
from freqtrade.configuration import TimeRange
from freqtrade.constants import Config
+from freqtrade.data.converter import reduce_dataframe_footprint
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_seconds
from freqtrade.strategy.interface import IStrategy
@@ -1275,6 +1276,9 @@ class FreqaiDataKitchen:
dataframe = self.remove_special_chars_from_feature_names(dataframe)
+ if self.config.get('reduce_df_footprint', False):
+ dataframe = reduce_dataframe_footprint(dataframe)
+
return dataframe
def fit_labels(self) -> None:
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index ea7c2f1f9..2e2638126 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -354,7 +354,7 @@ class FreqtradeBot(LoggingMixin):
if self.trading_mode == TradingMode.FUTURES:
self._schedule.run_pending()
- def update_closed_trades_without_assigned_fees(self):
+ def update_closed_trades_without_assigned_fees(self) -> None:
"""
Update closed trades without close fees assigned.
Only acts when Orders are in the database, otherwise the last order-id is unknown.
@@ -379,7 +379,7 @@ class FreqtradeBot(LoggingMixin):
stoploss_order=order.ft_order_side == 'stoploss',
send_msg=False)
- trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
+ trades = Trade.get_open_trades_without_assigned_fees()
for trade in trades:
if trade.is_open and not trade.fee_updated(trade.entry_side):
order = trade.select_order(trade.entry_side, False)
diff --git a/freqtrade/leverage/interest.py b/freqtrade/leverage/interest.py
index ddeea2b42..d18cc458f 100644
--- a/freqtrade/leverage/interest.py
+++ b/freqtrade/leverage/interest.py
@@ -35,9 +35,5 @@ def interest(
elif exchange_name == "kraken":
# Rounded based on https://kraken-fees-calculator.github.io/
return borrowed * rate * (one + FtPrecise(ceil(hours / four)))
- elif exchange_name == "ftx":
- # As Explained under #Interest rates section in
- # https://help.ftx.com/hc/en-us/articles/360053007671-Spot-Margin-Trading-Explainer
- return borrowed * rate * FtPrecise(ceil(hours)) / twenty_four
else:
raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade")
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 3436eac44..427ad121f 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -166,7 +166,7 @@ class Backtesting:
PairLocks.use_db = True
Trade.use_db = True
- def init_backtest_detail(self):
+ def init_backtest_detail(self) -> None:
# Load detail timeframe if specified
self.timeframe_detail = str(self.config.get('timeframe_detail', ''))
if self.timeframe_detail:
diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py
index a54c5570f..edbcd6be3 100644
--- a/freqtrade/persistence/migrations.py
+++ b/freqtrade/persistence/migrations.py
@@ -1,5 +1,5 @@
import logging
-from typing import List
+from typing import List, Optional
from sqlalchemy import inspect, select, text, tuple_, update
@@ -31,9 +31,9 @@ def get_backup_name(tabs: List[str], backup_prefix: str):
return table_back_name
-def get_last_sequence_ids(engine, trade_back_name, order_back_name):
- order_id: int = None
- trade_id: int = None
+def get_last_sequence_ids(engine, trade_back_name: str, order_back_name: str):
+ order_id: Optional[int] = None
+ trade_id: Optional[int] = None
if engine.name == 'postgresql':
with engine.begin() as connection:
diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py
index 743aa5eba..e032b6b96 100644
--- a/freqtrade/persistence/trade_model.py
+++ b/freqtrade/persistence/trade_model.py
@@ -1144,7 +1144,8 @@ class Trade(_DECL_BASE, LocalTrade):
id = Column(Integer, primary_key=True)
- orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan", lazy="joined")
+ orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan",
+ lazy="selectin", innerjoin=True)
exchange = Column(String(25), nullable=False)
pair = Column(String(25), nullable=False, index=True)
diff --git a/freqtrade/rpc/api_server/api_ws.py b/freqtrade/rpc/api_server/api_ws.py
index 118d70d78..785773b39 100644
--- a/freqtrade/rpc/api_server/api_ws.py
+++ b/freqtrade/rpc/api_server/api_ws.py
@@ -84,11 +84,8 @@ async def _process_consumer_request(
# Limit the amount of candles per dataframe to 'limit' or 1500
limit = max(data.get('limit', 1500), 1500)
- # They requested the full historical analyzed dataframes
- analyzed_df = rpc._ws_request_analyzed_df(limit)
-
- # For every dataframe, send as a separate message
- for _, message in analyzed_df.items():
+ # For every pair in the generator, send a separate message
+ for message in rpc._ws_request_analyzed_df(limit):
response = WSAnalyzedDFMessage(data=message)
await channel_manager.send_direct(channel, response.dict(exclude_none=True))
diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py
index e9a12e4df..6464ae44e 100644
--- a/freqtrade/rpc/api_server/webserver.py
+++ b/freqtrade/rpc/api_server/webserver.py
@@ -2,7 +2,7 @@ import asyncio
import logging
from ipaddress import IPv4Address
from threading import Thread
-from typing import Any, Dict
+from typing import Any, Dict, Optional
import orjson
import uvicorn
@@ -51,9 +51,9 @@ class ApiServer(RPCHandler):
# Exchange - only available in webserver mode.
_exchange = None
# websocket message queue stuff
- _ws_channel_manager = None
+ _ws_channel_manager: ChannelManager
_ws_thread = None
- _ws_loop = None
+ _ws_loop: Optional[asyncio.AbstractEventLoop] = None
def __new__(cls, *args, **kwargs):
"""
@@ -71,7 +71,7 @@ class ApiServer(RPCHandler):
return
self._standalone: bool = standalone
self._server = None
- self._ws_queue = None
+ self._ws_queue: Optional[ThreadedQueue] = None
self._ws_background_task = None
ApiServer.__initialized = True
@@ -186,7 +186,7 @@ class ApiServer(RPCHandler):
self._ws_background_task = asyncio.run_coroutine_threadsafe(
self._broadcast_queue_data(), loop=self._ws_loop)
- async def _broadcast_queue_data(self):
+ async def _broadcast_queue_data(self) -> None:
# Instantiate the queue in this coroutine so it's attached to our loop
self._ws_queue = ThreadedQueue()
async_queue = self._ws_queue.async_q
@@ -210,7 +210,8 @@ class ApiServer(RPCHandler):
finally:
# Disconnect channels and stop the loop on cancel
await self._ws_channel_manager.disconnect_all()
- self._ws_loop.stop()
+ if self._ws_loop:
+ self._ws_loop.stop()
# Avoid adding more items to the queue if they aren't
# going to get broadcasted.
self._ws_queue = None
diff --git a/freqtrade/rpc/api_server/ws/channel.py b/freqtrade/rpc/api_server/ws/channel.py
index 3c97d05b1..88b4db9ba 100644
--- a/freqtrade/rpc/api_server/ws/channel.py
+++ b/freqtrade/rpc/api_server/ws/channel.py
@@ -35,8 +35,6 @@ class WebSocketChannel:
# The WebSocket object
self._websocket = WebSocketProxy(websocket)
- # The Serializing class for the WebSocket object
- self._serializer_cls = serializer_cls
self.drain_timeout = drain_timeout
self.throttle = throttle
@@ -50,7 +48,7 @@ class WebSocketChannel:
self._closed = asyncio.Event()
# Wrap the WebSocket in the Serializing class
- self._wrapped_ws = self._serializer_cls(self._websocket)
+ self._wrapped_ws = serializer_cls(self._websocket)
def __repr__(self):
return f"WebSocketChannel({self.channel_id}, {self.remote_addr})"
diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py
index 143b11911..55bb5a34b 100644
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -5,7 +5,7 @@ import logging
from abc import abstractmethod
from datetime import date, datetime, timedelta, timezone
from math import isnan
-from typing import Any, Dict, List, Optional, Tuple, Union
+from typing import Any, Dict, Generator, List, Optional, Tuple, Union
import arrow
import psutil
@@ -1063,23 +1063,20 @@ class RPC:
self,
pairlist: List[str],
limit: Optional[int]
- ) -> Dict[str, Any]:
+ ) -> Generator[Dict[str, Any], None, None]:
""" Get the analysed dataframes of each pair in the pairlist """
timeframe = self._freqtrade.config['timeframe']
candle_type = self._freqtrade.config.get('candle_type_def', CandleType.SPOT)
- _data = {}
for pair in pairlist:
dataframe, last_analyzed = self.__rpc_analysed_dataframe_raw(pair, timeframe, limit)
- _data[pair] = {
+ yield {
"key": (pair, timeframe, candle_type),
"df": dataframe,
"la": last_analyzed
}
- return _data
-
def _ws_request_analyzed_df(self, limit: Optional[int]):
""" Historical Analyzed Dataframes for WebSocket """
whitelist = self._freqtrade.active_pair_whitelist
diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py
index 247373817..708a1ce53 100644
--- a/freqtrade/rpc/telegram.py
+++ b/freqtrade/rpc/telegram.py
@@ -1061,7 +1061,8 @@ class Telegram(RPCHandler):
try:
self._rpc._rpc_force_entry(pair, price, order_side=order_side)
except RPCException as e:
- self._send_msg(str(e))
+ logger.exception("Forcebuy error!")
+ self._send_msg(str(e), ParseMode.HTML)
def _force_enter_inline(self, update: Update, _: CallbackContext) -> None:
if update.callback_query:
diff --git a/pyproject.toml b/pyproject.toml
index 8020b0636..2de2c957b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,6 +30,8 @@ asyncio_mode = "auto"
[tool.mypy]
ignore_missing_imports = true
+namespace_packages = false
+implicit_optional = true
warn_unused_ignores = true
exclude = [
'^build_helpers\.py$'
diff --git a/requirements-dev.txt b/requirements-dev.txt
index a46ec7e48..6e4d42538 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -8,23 +8,25 @@
coveralls==3.3.1
flake8==5.0.4
flake8-tidy-imports==4.8.0
-mypy==0.982
+mypy==0.990
pre-commit==2.20.0
pytest==7.2.0
-pytest-asyncio==0.20.1
+pytest-asyncio==0.20.2
pytest-cov==4.0.0
pytest-mock==3.10.0
pytest-random-order==1.0.4
isort==5.10.1
# For datetime mocking
time-machine==2.8.2
+# fastapi testing
+httpx==0.23.0
# Convert jupyter notebooks to markdown documents
-nbconvert==7.2.3
+nbconvert==7.2.4
# mypy types
types-cachetools==5.2.1
types-filelock==3.2.7
-types-requests==2.28.11.2
+types-requests==2.28.11.4
types-tabulate==0.9.0.0
-types-python-dateutil==2.8.19.2
+types-python-dateutil==2.8.19.3
diff --git a/requirements-freqai.txt b/requirements-freqai.txt
index a90b9df69..66730e29f 100644
--- a/requirements-freqai.txt
+++ b/requirements-freqai.txt
@@ -8,4 +8,4 @@ joblib==1.2.0
catboost==1.1.1; platform_machine != 'aarch64'
lightgbm==3.3.3
xgboost==1.7.1
-tensorboard==2.10.1
+tensorboard==2.11.0
diff --git a/requirements.txt b/requirements.txt
index b98973cc7..ec8b5ce7c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,11 +2,12 @@ numpy==1.23.4
pandas==1.5.1
pandas-ta==0.3.14b
-ccxt==2.1.54
+ccxt==2.1.75
# Pin cryptography for now due to rust build errors with piwheels
-cryptography==38.0.1
+cryptography==38.0.1; platform_machine == 'armv7l'
+cryptography==38.0.3; platform_machine != 'armv7l'
aiohttp==3.8.3
-SQLAlchemy==1.4.43
+SQLAlchemy==1.4.44
python-telegram-bot==13.14
arrow==1.2.3
cachetools==4.2.2
@@ -35,12 +36,12 @@ orjson==3.8.1
sdnotify==0.3.2
# API Server
-fastapi==0.85.1
+fastapi==0.87.0
pydantic==1.10.2
uvicorn==0.19.0
pyjwt==2.6.0
aiofiles==22.1.0
-psutil==5.9.3
+psutil==5.9.4
# Support for colorized terminal output
colorama==0.4.6
diff --git a/tests/commands/test_build_config.py b/tests/commands/test_build_config.py
index e30d5bf94..7bf374ae0 100644
--- a/tests/commands/test_build_config.py
+++ b/tests/commands/test_build_config.py
@@ -30,7 +30,7 @@ def test_validate_is_int():
assert not validate_is_int('-ee')
-@pytest.mark.parametrize('exchange', ['bittrex', 'binance', 'kraken', 'ftx'])
+@pytest.mark.parametrize('exchange', ['bittrex', 'binance', 'kraken'])
def test_start_new_config(mocker, caplog, exchange):
wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
diff --git a/tests/conftest.py b/tests/conftest.py
index 9f71709f1..d228c64b7 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1748,28 +1748,7 @@ def limit_buy_order_canceled_empty(request):
# https://docs.pytest.org/en/latest/example/parametrize.html#apply-indirect-on-particular-arguments
exchange_name = request.param
- if exchange_name == 'ftx':
- return {
- 'info': {},
- 'id': '1234512345',
- 'clientOrderId': None,
- 'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
- 'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
- 'lastTradeTimestamp': None,
- 'symbol': 'LTC/USDT',
- 'type': 'limit',
- 'side': 'buy',
- 'price': 34.3225,
- 'amount': 0.55,
- 'cost': 0.0,
- 'average': None,
- 'filled': 0.0,
- 'remaining': 0.0,
- 'status': 'closed',
- 'fee': None,
- 'trades': None
- }
- elif exchange_name == 'kraken':
+ if exchange_name == 'kraken':
return {
'info': {},
'id': 'AZNPFF-4AC4N-7MKTAT',
diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py
index f74383d15..760ad8b76 100644
--- a/tests/data/test_converter.py
+++ b/tests/data/test_converter.py
@@ -3,18 +3,19 @@ import logging
from pathlib import Path
from shutil import copyfile
+import numpy as np
import pytest
from freqtrade.configuration.timerange import TimeRange
from freqtrade.data.converter import (convert_ohlcv_format, convert_trades_format,
ohlcv_fill_up_missing_data, ohlcv_to_dataframe,
- trades_dict_to_list, trades_remove_duplicates,
- trades_to_ohlcv, trim_dataframe)
+ reduce_dataframe_footprint, trades_dict_to_list,
+ trades_remove_duplicates, trades_to_ohlcv, trim_dataframe)
from freqtrade.data.history import (get_timerange, load_data, load_pair_history,
validate_backtest_data)
from freqtrade.data.history.idatahandler import IDataHandler
from freqtrade.enums import CandleType
-from tests.conftest import log_has, log_has_re
+from tests.conftest import generate_test_data, log_has, log_has_re
from tests.data.test_history import _clean_test_file
@@ -344,3 +345,33 @@ def test_convert_ohlcv_format(default_conf, testdatadir, tmpdir, file_base, cand
assert file.exists()
for file in (files_new):
assert not file.exists()
+
+
+def test_reduce_dataframe_footprint():
+ data = generate_test_data('15m', 40)
+
+ data['open_copy'] = data['open']
+ data['close_copy'] = data['close']
+ data['close_copy'] = data['close']
+
+ assert data['open'].dtype == np.float64
+ assert data['open_copy'].dtype == np.float64
+ assert data['close_copy'].dtype == np.float64
+
+ df2 = reduce_dataframe_footprint(data)
+
+ # Does not modify original dataframe
+ assert data['open'].dtype == np.float64
+ assert data['open_copy'].dtype == np.float64
+ assert data['close_copy'].dtype == np.float64
+
+ # skips ohlcv columns
+ assert df2['open'].dtype == np.float64
+ assert df2['high'].dtype == np.float64
+ assert df2['low'].dtype == np.float64
+ assert df2['close'].dtype == np.float64
+ assert df2['volume'].dtype == np.float64
+
+ # Changes dtype of returned dataframe
+ assert df2['open_copy'].dtype == np.float32
+ assert df2['close_copy'].dtype == np.float32
diff --git a/tests/data/test_datahandler.py b/tests/data/test_datahandler.py
index c067d0339..4d6489f11 100644
--- a/tests/data/test_datahandler.py
+++ b/tests/data/test_datahandler.py
@@ -70,7 +70,7 @@ def test_datahandler_ohlcv_regex(filename, pair, timeframe, candletype):
('BTC_USDT_USDT', 'BTC/USDT:USDT'), # Futures
('XRP_USDT_USDT', 'XRP/USDT:USDT'), # futures
('BTC-PERP', 'BTC-PERP'),
- ('BTC-PERP_USDT', 'BTC-PERP:USDT'), # potential FTX case
+ ('BTC-PERP_USDT', 'BTC-PERP:USDT'),
('UNITTEST_USDT', 'UNITTEST/USDT'),
])
def test_rebuild_pair_from_filename(input, expected):
diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py
index 9f9866aba..55d463c68 100644
--- a/tests/exchange/test_ccxt_compat.py
+++ b/tests/exchange/test_ccxt_compat.py
@@ -45,16 +45,6 @@ EXCHANGES = {
'leverage_tiers_public': False,
'leverage_in_spot_market': True,
},
- 'ftx': {
- 'pair': 'BTC/USD',
- 'stake_currency': 'USD',
- 'hasQuoteVolume': True,
- 'timeframe': '5m',
- 'futures_pair': 'BTC/USD:USD',
- 'futures': False,
- 'leverage_tiers_public': False, # TODO: Set to True once implemented on CCXT
- 'leverage_in_spot_market': True,
- },
'kucoin': {
'pair': 'XRP/USDT',
'stake_currency': 'USDT',
diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
index 25ba294a3..a719496e5 100644
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -27,7 +27,7 @@ from tests.conftest import (generate_test_data_raw, get_mock_coro, get_patched_e
# Make sure to always keep one exchange here which is NOT subclassed!!
-EXCHANGES = ['bittrex', 'binance', 'kraken', 'ftx', 'gateio']
+EXCHANGES = ['bittrex', 'binance', 'kraken', 'gateio']
get_entry_rate_data = [
('other', 20, 19, 10, 0.0, 20), # Full ask side
@@ -3162,19 +3162,16 @@ def test_cancel_stoploss_order(default_conf, mocker, exchange_name):
def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', return_value={'for': 123})
- mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', return_value={'for': 123})
mocker.patch('freqtrade.exchange.Gateio.fetch_stoploss_order', return_value={'for': 123})
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
res = {'fee': {}, 'status': 'canceled', 'amount': 1234}
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', return_value=res)
- mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', return_value=res)
mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', return_value=res)
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
assert co == res
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', return_value='canceled')
- mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', return_value='canceled')
mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', return_value='canceled')
# Fall back to fetch_stoploss_order
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
@@ -3182,7 +3179,6 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
exc = InvalidOrderException("")
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', side_effect=exc)
- mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', side_effect=exc)
mocker.patch('freqtrade.exchange.Gateio.fetch_stoploss_order', side_effect=exc)
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
assert co['amount'] == 555
@@ -3191,7 +3187,6 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
with pytest.raises(InvalidOrderException):
exc = InvalidOrderException("Did not find order")
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', side_effect=exc)
- mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', side_effect=exc)
mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', side_effect=exc)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123)
@@ -3253,9 +3248,6 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
@pytest.mark.usefixtures("init_persistence")
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
- # Don't test FTX here - that needs a separate test
- if exchange_name == 'ftx':
- return
default_conf['dry_run'] = True
order = MagicMock()
order.myid = 123
@@ -3699,16 +3691,6 @@ def test_date_minus_candles():
# no darkpools
("BTC/EUR.d", 'BTC', 'EUR', "kraken", True, False, False, 'spot',
{"darkpool": True}, False),
- ("BTC/USD", 'BTC', 'USD', "ftx", True, False, False, 'spot', {}, True),
- ("USD/BTC", 'USD', 'BTC', "ftx", True, False, False, 'spot', {}, True),
- # Can only trade spot markets
- ("BTC/USD", 'BTC', 'USD', "ftx", False, False, True, 'spot', {}, False),
- ("BTC/USD", 'BTC', 'USD', "ftx", False, False, True, 'futures', {}, True),
- # Can only trade spot markets
- ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'spot', {}, False),
- ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'margin', {}, False),
- ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'futures', {}, True),
-
("BTC/USDT:USDT", 'BTC', 'USD', "okx", False, False, True, 'spot', {}, False),
("BTC/USDT:USDT", 'BTC', 'USD', "okx", False, False, True, 'margin', {}, False),
("BTC/USDT:USDT", 'BTC', 'USD', "okx", False, False, True, 'futures', {}, True),
@@ -3841,7 +3823,7 @@ def test_calculate_backoff(retrycount, max_retries, expected):
assert calculate_backoff(retrycount, max_retries) == expected
-@pytest.mark.parametrize("exchange_name", ['binance', 'ftx'])
+@pytest.mark.parametrize("exchange_name", ['binance'])
def test__get_funding_fees_from_exchange(default_conf, mocker, exchange_name):
api_mock = MagicMock()
api_mock.fetch_funding_history = MagicMock(return_value=[
@@ -3909,7 +3891,7 @@ def test__get_funding_fees_from_exchange(default_conf, mocker, exchange_name):
)
-@pytest.mark.parametrize('exchange', ['binance', 'kraken', 'ftx'])
+@pytest.mark.parametrize('exchange', ['binance', 'kraken'])
@pytest.mark.parametrize('stake_amount,leverage,min_stake_with_lev', [
(9.0, 3.0, 3.0),
(20.0, 5.0, 4.0),
@@ -3930,8 +3912,6 @@ def test_get_stake_amount_considering_leverage(
@pytest.mark.parametrize("exchange_name,trading_mode", [
("binance", TradingMode.FUTURES),
- ("ftx", TradingMode.MARGIN),
- ("ftx", TradingMode.FUTURES)
])
def test__set_leverage(mocker, default_conf, exchange_name, trading_mode):
@@ -3982,9 +3962,6 @@ def test_set_margin_mode(mocker, default_conf, margin_mode):
("kraken", TradingMode.SPOT, None, False),
("kraken", TradingMode.MARGIN, MarginMode.ISOLATED, True),
("kraken", TradingMode.FUTURES, MarginMode.ISOLATED, True),
- ("ftx", TradingMode.SPOT, None, False),
- ("ftx", TradingMode.MARGIN, MarginMode.ISOLATED, True),
- ("ftx", TradingMode.FUTURES, MarginMode.ISOLATED, True),
("bittrex", TradingMode.SPOT, None, False),
("bittrex", TradingMode.MARGIN, MarginMode.CROSS, True),
("bittrex", TradingMode.MARGIN, MarginMode.ISOLATED, True),
@@ -4005,8 +3982,6 @@ def test_set_margin_mode(mocker, default_conf, margin_mode):
("binance", TradingMode.FUTURES, MarginMode.CROSS, True),
("kraken", TradingMode.MARGIN, MarginMode.CROSS, True),
("kraken", TradingMode.FUTURES, MarginMode.CROSS, True),
- ("ftx", TradingMode.MARGIN, MarginMode.CROSS, True),
- ("ftx", TradingMode.FUTURES, MarginMode.CROSS, True),
("gateio", TradingMode.MARGIN, MarginMode.CROSS, True),
("gateio", TradingMode.FUTURES, MarginMode.CROSS, True),
@@ -4015,8 +3990,6 @@ def test_set_margin_mode(mocker, default_conf, margin_mode):
# ("binance", TradingMode.FUTURES, MarginMode.CROSS, False),
# ("kraken", TradingMode.MARGIN, MarginMode.CROSS, False),
# ("kraken", TradingMode.FUTURES, MarginMode.CROSS, False),
- # ("ftx", TradingMode.MARGIN, MarginMode.CROSS, False),
- # ("ftx", TradingMode.FUTURES, MarginMode.CROSS, False),
# ("gateio", TradingMode.MARGIN, MarginMode.CROSS, False),
# ("gateio", TradingMode.FUTURES, MarginMode.CROSS, False),
])
@@ -4046,7 +4019,6 @@ def test_validate_trading_mode_and_margin_mode(
("bibox", "futures", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "swap"}}),
("bybit", "spot", {"options": {"defaultType": "spot"}}),
("bybit", "futures", {"options": {"defaultType": "linear"}}),
- ("ftx", "futures", {"options": {"defaultType": "swap"}}),
("gateio", "futures", {"options": {"defaultType": "swap"}}),
("hitbtc", "futures", {"options": {"defaultType": "swap"}}),
("kraken", "futures", {"options": {"defaultType": "swap"}}),
@@ -4223,11 +4195,6 @@ def test_combine_funding_and_mark(
# ('kraken', "2021-09-01 00:00:00", "2021-09-01 07:59:59", 30.0, -0.0012443999999999999),
# ('kraken', "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, 0.0045759),
# ('kraken', "2021-09-01 00:00:01", "2021-09-01 08:00:00", 30.0, -0.0008289),
- ('ftx', 0, 2, "2021-09-01 00:10:00", "2021-09-01 00:30:00", 30.0, 0.0),
- ('ftx', 0, 9, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, 0.0010008),
- ('ftx', 0, 13, "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, 0.0146691),
- ('ftx', 0, 9, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 50.0, 0.001668),
- ('ftx', 1, 9, "2021-09-01 00:00:01", "2021-09-01 08:00:00", 30.0, 0.0019932),
('gateio', 0, 2, "2021-09-01 00:10:00", "2021-09-01 04:00:00", 30.0, 0.0),
('gateio', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.0009140999),
('gateio', 0, 2, "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, -0.0009140999),
@@ -4289,7 +4256,6 @@ def test__fetch_and_calculate_funding_fees(
d2 = datetime.strptime(f"{d2} +0000", '%Y-%m-%d %H:%M:%S %z')
funding_rate_history = {
'binance': funding_rate_history_octohourly,
- 'ftx': funding_rate_history_hourly,
'gateio': funding_rate_history_octohourly,
}[exchange][rate_start:rate_end]
api_mock = MagicMock()
@@ -5056,7 +5022,7 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
exchange.get_max_leverage("BTC/USDT", 1000000000.01)
-@pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'ftx', 'gateio', 'okx'])
+@pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'gateio', 'okx'])
def test__get_params(mocker, default_conf, exchange_name):
api_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py
deleted file mode 100644
index 5213c1b36..000000000
--- a/tests/exchange/test_ftx.py
+++ /dev/null
@@ -1,272 +0,0 @@
-from random import randint
-from unittest.mock import MagicMock
-
-import ccxt
-import pytest
-
-from freqtrade.exceptions import DependencyException, InvalidOrderException
-from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT
-from tests.conftest import get_patched_exchange
-
-from .test_exchange import ccxt_exceptionhandlers
-
-
-STOPLOSS_ORDERTYPE = 'stop'
-
-
-@pytest.mark.parametrize('order_price,exchangelimitratio,side', [
- (217.8, 1.05, "sell"),
- (222.2, 0.95, "buy"),
-])
-def test_stoploss_order_ftx(default_conf, mocker, order_price, exchangelimitratio, side):
- api_mock = MagicMock()
- order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
-
- api_mock.create_order = MagicMock(return_value={
- 'id': order_id,
- 'info': {
- 'foo': 'bar'
- }
- })
-
- default_conf['dry_run'] = False
- mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
- mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
-
- exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
-
- # stoploss_on_exchange_limit_ratio is irrelevant for ftx market orders
- order = exchange.stoploss(
- pair='ETH/BTC',
- amount=1,
- stop_price=190,
- side=side,
- order_types={'stoploss_on_exchange_limit_ratio': exchangelimitratio},
- leverage=1.0
- )
-
- assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC'
- assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE
- assert api_mock.create_order.call_args_list[0][1]['side'] == side
- assert api_mock.create_order.call_args_list[0][1]['amount'] == 1
- assert 'orderPrice' not in api_mock.create_order.call_args_list[0][1]['params']
- assert 'stopPrice' in api_mock.create_order.call_args_list[0][1]['params']
- assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 190
-
- assert api_mock.create_order.call_count == 1
-
- api_mock.create_order.reset_mock()
-
- order = exchange.stoploss(
- pair='ETH/BTC',
- amount=1,
- stop_price=220,
- order_types={},
- side=side,
- leverage=1.0
- )
-
- assert 'id' in order
- assert 'info' in order
- assert order['id'] == order_id
- assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC'
- assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE
- assert api_mock.create_order.call_args_list[0][1]['side'] == side
- assert api_mock.create_order.call_args_list[0][1]['amount'] == 1
- assert 'orderPrice' not in api_mock.create_order.call_args_list[0][1]['params']
- assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 220
-
- api_mock.create_order.reset_mock()
- order = exchange.stoploss(
- pair='ETH/BTC',
- amount=1,
- stop_price=220,
- order_types={'stoploss': 'limit'}, side=side,
- leverage=1.0
- )
-
- assert 'id' in order
- assert 'info' in order
- assert order['id'] == order_id
- assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC'
- assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE
- assert api_mock.create_order.call_args_list[0][1]['side'] == side
- assert api_mock.create_order.call_args_list[0][1]['amount'] == 1
- assert 'orderPrice' in api_mock.create_order.call_args_list[0][1]['params']
- assert api_mock.create_order.call_args_list[0][1]['params']['orderPrice'] == order_price
- assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 220
-
- # test exception handling
- with pytest.raises(DependencyException):
- api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance"))
- exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
- exchange.stoploss(
- pair='ETH/BTC',
- amount=1,
- stop_price=220,
- order_types={},
- side=side,
- leverage=1.0
- )
-
- with pytest.raises(InvalidOrderException):
- api_mock.create_order = MagicMock(
- side_effect=ccxt.InvalidOrder("ftx Order would trigger immediately."))
- exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
- exchange.stoploss(
- pair='ETH/BTC',
- amount=1,
- stop_price=220,
- order_types={},
- side=side,
- leverage=1.0
- )
-
- ccxt_exceptionhandlers(mocker, default_conf, api_mock, "ftx",
- "stoploss", "create_order", retries=1,
- pair='ETH/BTC', amount=1, stop_price=220, order_types={},
- side=side, leverage=1.0)
-
-
-@pytest.mark.parametrize('side', [("sell"), ("buy")])
-def test_stoploss_order_dry_run_ftx(default_conf, mocker, side):
- api_mock = MagicMock()
- default_conf['dry_run'] = True
- mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
- mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
-
- exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
-
- api_mock.create_order.reset_mock()
-
- order = exchange.stoploss(
- pair='ETH/BTC',
- amount=1,
- stop_price=220,
- order_types={},
- side=side,
- leverage=1.0
- )
-
- assert 'id' in order
- assert 'info' in order
- assert 'type' in order
-
- assert order['type'] == STOPLOSS_ORDERTYPE
- assert order['price'] == 220
- assert order['amount'] == 1
-
-
-@pytest.mark.parametrize('sl1,sl2,sl3,side', [
- (1501, 1499, 1501, "sell"),
- (1499, 1501, 1499, "buy")
-])
-def test_stoploss_adjust_ftx(mocker, default_conf, sl1, sl2, sl3, side):
- exchange = get_patched_exchange(mocker, default_conf, id='ftx')
- order = {
- 'type': STOPLOSS_ORDERTYPE,
- 'price': 1500,
- }
- assert exchange.stoploss_adjust(sl1, order, side=side)
- assert not exchange.stoploss_adjust(sl2, order, side=side)
- # Test with invalid order case ...
- order['type'] = 'stop_loss_limit'
- assert not exchange.stoploss_adjust(sl3, order, side=side)
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_fetch_stoploss_order_ftx(default_conf, mocker, limit_sell_order, limit_buy_order):
- default_conf['dry_run'] = True
- order = MagicMock()
- order.myid = 123
- exchange = get_patched_exchange(mocker, default_conf, id='ftx')
- exchange._dry_run_open_orders['X'] = order
- assert exchange.fetch_stoploss_order('X', 'TKN/BTC').myid == 123
-
- with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'):
- exchange.fetch_stoploss_order('Y', 'TKN/BTC')
-
- default_conf['dry_run'] = False
- api_mock = MagicMock()
- api_mock.fetch_orders = MagicMock(return_value=[{'id': 'X', 'status': '456'}])
- exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')
- assert exchange.fetch_stoploss_order('X', 'TKN/BTC')['status'] == '456'
-
- api_mock.fetch_orders = MagicMock(return_value=[{'id': 'Y', 'status': '456'}])
- exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')
- with pytest.raises(InvalidOrderException, match=r"Could not get stoploss order for id X"):
- exchange.fetch_stoploss_order('X', 'TKN/BTC')['status']
-
- # stoploss Limit order
- api_mock.fetch_orders = MagicMock(return_value=[
- {'id': 'X', 'status': 'closed',
- 'info': {
- 'orderId': 'mocked_limit_sell',
- }}])
- api_mock.fetch_order = MagicMock(return_value=limit_sell_order.copy())
-
- # No orderId field - no call to fetch_order
- resp = exchange.fetch_stoploss_order('X', 'TKN/BTC')
- assert resp
- assert api_mock.fetch_order.call_count == 1
- assert resp['id_stop'] == 'mocked_limit_sell'
- assert resp['id'] == 'X'
- assert resp['type'] == 'stop'
- assert resp['status_stop'] == 'triggered'
-
- # Stoploss market order
- # Contains no new Order, but "average" instead
- order = {'id': 'X', 'status': 'closed', 'info': {'orderId': None}, 'average': 0.254}
- api_mock.fetch_orders = MagicMock(return_value=[order])
- api_mock.fetch_order.reset_mock()
- api_mock.privateGetConditionalOrdersConditionalOrderIdTriggers = MagicMock(
- return_value={'result': [
- {'orderId': 'mocked_market_sell', 'type': 'market', 'side': 'sell', 'price': 0.254}
- ]})
- resp = exchange.fetch_stoploss_order('X', 'TKN/BTC')
- assert resp
- # fetch_order not called (no regular order ID)
- assert api_mock.fetch_order.call_count == 1
- api_mock.privateGetConditionalOrdersConditionalOrderIdTriggers.call_count == 1
- expected_resp = limit_sell_order.copy()
- expected_resp.update({
- 'id_stop': 'X',
- 'id': 'X',
- 'type': 'stop',
- 'status_stop': 'triggered',
- })
- assert expected_resp == resp
-
- with pytest.raises(InvalidOrderException):
- api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
- exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')
- exchange.fetch_stoploss_order(order_id='_', pair='TKN/BTC')
- assert api_mock.fetch_orders.call_count == 1
-
- ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'ftx',
- 'fetch_stoploss_order', 'fetch_orders',
- retries=API_FETCH_ORDER_RETRY_COUNT + 1,
- order_id='_', pair='TKN/BTC')
-
-
-def test_get_order_id(mocker, default_conf):
- exchange = get_patched_exchange(mocker, default_conf, id='ftx')
- order = {
- 'type': STOPLOSS_ORDERTYPE,
- 'price': 1500,
- 'id': '1111',
- 'id_stop': '1234',
- 'info': {
- }
- }
- assert exchange.get_order_id_conditional(order) == '1234'
-
- order = {
- 'type': 'limit',
- 'price': 1500,
- 'id': '1111',
- 'id_stop': '1234',
- 'info': {
- }
- }
- assert exchange.get_order_id_conditional(order) == '1111'
diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py
index 10bb79971..25bc99580 100644
--- a/tests/freqai/test_freqai_interface.py
+++ b/tests/freqai/test_freqai_interface.py
@@ -27,13 +27,13 @@ def is_mac() -> bool:
return "Darwin" in machine
-@pytest.mark.parametrize('model, pca, dbscan', [
- ('LightGBMRegressor', True, False),
- ('XGBoostRegressor', False, True),
- ('XGBoostRFRegressor', False, False),
- ('CatboostRegressor', False, False),
+@pytest.mark.parametrize('model, pca, dbscan, float32', [
+ ('LightGBMRegressor', True, False, True),
+ ('XGBoostRegressor', False, True, False),
+ ('XGBoostRFRegressor', False, False, False),
+ ('CatboostRegressor', False, False, False),
])
-def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan):
+def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan, float32):
if is_arm() and model == 'CatboostRegressor':
pytest.skip("CatBoost is not supported on ARM")
@@ -43,6 +43,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
freqai_conf.update({"strategy": "freqai_test_strat"})
freqai_conf['freqai']['feature_parameters'].update({"principal_component_analysis": pca})
freqai_conf['freqai']['feature_parameters'].update({"use_DBSCAN_to_remove_outliers": dbscan})
+ freqai_conf.update({"reduce_df_footprint": float32})
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
exchange = get_patched_exchange(mocker, freqai_conf)
diff --git a/tests/leverage/test_interest.py b/tests/leverage/test_interest.py
index 64e99b6b4..6afa73e6a 100644
--- a/tests/leverage/test_interest.py
+++ b/tests/leverage/test_interest.py
@@ -19,11 +19,6 @@ twentyfive_hours = FtPrecise(25.0)
('kraken', 0.00025, ten_mins, 0.03),
('kraken', 0.00025, five_hours, 0.045),
('kraken', 0.00025, twentyfive_hours, 0.12),
- # FTX
- ('ftx', 0.0005, ten_mins, 0.00125),
- ('ftx', 0.00025, ten_mins, 0.000625),
- ('ftx', 0.00025, five_hours, 0.003125),
- ('ftx', 0.00025, twentyfive_hours, 0.015625),
])
def test_interest(exchange, interest_rate, hours, expected):
borrowed = FtPrecise(60.0)
diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py
index 3323dd7c6..fbb639d50 100644
--- a/tests/persistence/test_persistence.py
+++ b/tests/persistence/test_persistence.py
@@ -2197,15 +2197,16 @@ def test_get_trades__query(fee, is_short):
# without orders there should be no join issued.
query1 = Trade.get_trades([], include_orders=False)
- assert "JOIN orders" in str(query)
- assert "JOIN orders" not in str(query1)
+ # Empty "with-options -> default - selectin"
+ assert query._with_options == ()
+ assert query1._with_options != ()
create_mock_trades(fee, is_short)
query = Trade.get_trades([])
query1 = Trade.get_trades([], include_orders=False)
- assert "JOIN orders" in str(query)
- assert "JOIN orders" not in str(query1)
+ assert query._with_options == ()
+ assert query1._with_options != ()
def test_get_trades_backtest():
diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py
index 359291476..ecc1da3e3 100644
--- a/tests/plugins/test_pairlist.py
+++ b/tests/plugins/test_pairlist.py
@@ -612,9 +612,9 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
"lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}],
"BTC", "binance", ['ETH/BTC', 'LTC/BTC', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC']),
# ftx data is already in Quote currency, therefore won't require conversion
- ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
- "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
- "BTC", "ftx", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']),
+ # ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
+ # "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
+ # "BTC", "ftx", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']),
])
def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history,
pairlists, base_currency, exchange, volumefilter_result) -> None:
@@ -636,8 +636,6 @@ def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers,
ohlcv_history_high_volume['high'] = ohlcv_history_high_volume.loc[:, 'high'] * 0.01
ohlcv_history_high_volume['close'] = ohlcv_history_high_volume.loc[:, 'close'] * 0.01
- mocker.patch('freqtrade.exchange.ftx.Ftx.market_is_tradable', return_value=True)
-
ohlcv_data = {
('ETH/BTC', '1d', CandleType.SPOT): ohlcv_history,
('TKN/BTC', '1d', CandleType.SPOT): ohlcv_history,
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
index 54a4cbe9a..c3a0d539d 100644
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -33,6 +33,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_fee=fee,
+ _is_dry_limit_order_filled=MagicMock(side_effect=[False, True]),
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
@@ -44,6 +45,91 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
rpc._rpc_trade_status()
freqtradebot.enter_positions()
+
+ # Open order...
+ results = rpc._rpc_trade_status()
+ assert results[0] == {
+ 'trade_id': 1,
+ 'pair': 'ETH/BTC',
+ 'base_currency': 'ETH',
+ 'quote_currency': 'BTC',
+ 'open_date': ANY,
+ 'open_timestamp': ANY,
+ 'is_open': ANY,
+ 'fee_open': ANY,
+ 'fee_open_cost': ANY,
+ 'fee_open_currency': ANY,
+ 'fee_close': fee.return_value,
+ 'fee_close_cost': ANY,
+ 'fee_close_currency': ANY,
+ 'open_rate_requested': ANY,
+ 'open_trade_value': 0.0010025,
+ 'close_rate_requested': ANY,
+ 'sell_reason': ANY,
+ 'exit_reason': ANY,
+ 'exit_order_status': ANY,
+ 'min_rate': ANY,
+ 'max_rate': ANY,
+ 'strategy': ANY,
+ 'buy_tag': ANY,
+ 'enter_tag': ANY,
+ 'timeframe': 5,
+ 'open_order_id': ANY,
+ 'close_date': None,
+ 'close_timestamp': None,
+ 'open_rate': 1.098e-05,
+ 'close_rate': None,
+ 'current_rate': 1.099e-05,
+ 'amount': 91.07468124,
+ 'amount_requested': 91.07468124,
+ 'stake_amount': 0.001,
+ 'trade_duration': None,
+ 'trade_duration_s': None,
+ 'close_profit': None,
+ 'close_profit_pct': None,
+ 'close_profit_abs': None,
+ 'current_profit': 0.0,
+ 'current_profit_pct': 0.0,
+ 'current_profit_abs': 0.0,
+ 'profit_ratio': 0.0,
+ 'profit_pct': 0.0,
+ 'profit_abs': 0.0,
+ 'profit_fiat': ANY,
+ 'stop_loss_abs': 0.0,
+ 'stop_loss_pct': None,
+ 'stop_loss_ratio': None,
+ 'stoploss_order_id': None,
+ 'stoploss_last_update': ANY,
+ 'stoploss_last_update_timestamp': ANY,
+ 'initial_stop_loss_abs': 0.0,
+ 'initial_stop_loss_pct': None,
+ 'initial_stop_loss_ratio': None,
+ 'stoploss_current_dist': -1.099e-05,
+ 'stoploss_current_dist_ratio': -1.0,
+ 'stoploss_current_dist_pct': pytest.approx(-100.0),
+ 'stoploss_entry_dist': -0.0010025,
+ 'stoploss_entry_dist_ratio': -1.0,
+ 'open_order': '(limit buy rem=91.07468123)',
+ 'realized_profit': 0.0,
+ 'exchange': 'binance',
+ 'leverage': 1.0,
+ 'interest_rate': 0.0,
+ 'liquidation_price': None,
+ 'is_short': False,
+ 'funding_fees': 0.0,
+ 'trading_mode': TradingMode.SPOT,
+ 'orders': [{
+ 'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05,
+ 'cost': 0.0009999999999054, 'filled': 0.0, 'ft_order_side': 'buy',
+ 'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY,
+ 'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05,
+ 'is_open': True, 'pair': 'ETH/BTC', 'order_id': ANY,
+ 'remaining': 91.07468123, 'status': ANY, 'ft_is_entry': True,
+ }],
+ }
+
+ # Fill open order ...
+ freqtradebot.manage_open_orders()
trades = Trade.get_open_trades()
freqtradebot.exit_positions(trades)
diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py
index 019b8fc82..969728b6f 100644
--- a/tests/rpc/test_rpc_apiserver.py
+++ b/tests/rpc/test_rpc_apiserver.py
@@ -67,7 +67,7 @@ def botclient(default_conf, mocker):
def client_post(client, url, data={}):
return client.post(url,
- data=data,
+ content=data,
headers={'Authorization': _basic_auth_str(_TEST_USER, _TEST_PASS),
'Origin': 'http://example.com',
'content-type': 'application/json'
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
index 10e6228f8..6b47dc1d1 100644
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -3036,7 +3036,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_
@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
+@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'kraken', 'bittrex'],
indirect=['limit_buy_order_canceled_empty'])
def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_short, fee,
limit_buy_order_canceled_empty) -> None: