From 23427bec085f817bcd03961e20139ca6ac71f42e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 12 May 2024 15:35:47 +0200 Subject: [PATCH] ruff format: Update tests/ base directory --- tests/conftest.py | 5410 ++++++++++++++++------------ tests/test_arguments.py | 275 +- tests/test_configuration.py | 1375 +++---- tests/test_directory_operations.py | 75 +- tests/test_log_setup.py | 83 +- tests/test_strategy_updater.py | 29 +- tests/test_timerange.py | 70 +- 7 files changed, 4084 insertions(+), 3233 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8bbb38af3..c2e4f6725 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,20 +45,25 @@ from tests.conftest_trades_usdt import ( ) -logging.getLogger('').setLevel(logging.INFO) +logging.getLogger("").setLevel(logging.INFO) # Do not mask numpy errors as warnings that no one read, raise the exсeption -np.seterr(all='raise') +np.seterr(all="raise") -CURRENT_TEST_STRATEGY = 'StrategyTestV3' -TRADE_SIDES = ('long', 'short') -EXMS = 'freqtrade.exchange.exchange.Exchange' +CURRENT_TEST_STRATEGY = "StrategyTestV3" +TRADE_SIDES = ("long", "short") +EXMS = "freqtrade.exchange.exchange.Exchange" def pytest_addoption(parser): - parser.addoption('--longrun', action='store_true', dest="longrun", - default=False, help="Enable long-run tests (ccxt compat)") + parser.addoption( + "--longrun", + action="store_true", + dest="longrun", + default=False, + help="Enable long-run tests (ccxt compat)", + ) def pytest_configure(config): @@ -66,7 +71,7 @@ def pytest_configure(config): "markers", "longrun: mark test that is running slowly and should not be run regularly" ) if not config.option.longrun: - config.option.markexpr = 'not longrun' + config.option.markexpr = "not longrun" class FixtureScheduler(LoadScopeScheduling): @@ -74,10 +79,10 @@ class FixtureScheduler(LoadScopeScheduling): # https://github.com/pytest-dev/pytest-xdist/issues/18 def _split_scope(self, nodeid): - if 'exchange_online' in nodeid: + if "exchange_online" in nodeid: try: # Extract exchange ID from nodeid - exchange_id = nodeid.split('[')[1].split('-')[0].rstrip(']') + exchange_id = nodeid.split("[")[1].split("-")[0].rstrip("]") return exchange_id except Exception as e: print(e) @@ -130,14 +135,13 @@ def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days= _end_timestamp = pd.to_datetime(end_date).timestamp() random_timestamps_in_seconds = np.random.uniform(_start_timestamp, _end_timestamp, n_rows) - timestamp = pd.to_datetime(random_timestamps_in_seconds, unit='s') + timestamp = pd.to_datetime(random_timestamps_in_seconds, unit="s") id = [ - f'a{np.random.randint(1e6, 1e7 - 1)}cd{np.random.randint(100, 999)}' - for _ in range(n_rows) + f"a{np.random.randint(1e6, 1e7 - 1)}cd{np.random.randint(100, 999)}" for _ in range(n_rows) ] - side = np.random.choice(['buy', 'sell'], n_rows) + side = np.random.choice(["buy", "sell"], n_rows) # Initial price and subsequent changes initial_price = 0.019626 @@ -148,50 +152,60 @@ def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days= cost = price * amount # Create DataFrame - df = pd.DataFrame({'timestamp': timestamp, 'id': id, 'type': None, 'side': side, - 'price': price, 'amount': amount, 'cost': cost}) - df['date'] = pd.to_datetime(df['timestamp'], unit='ms', utc=True) - df = df.sort_values('timestamp').reset_index(drop=True) - assert list(df.columns) == constants.DEFAULT_TRADES_COLUMNS + ['date'] + df = pd.DataFrame( + { + "timestamp": timestamp, + "id": id, + "type": None, + "side": side, + "price": price, + "amount": amount, + "cost": cost, + } + ) + df["date"] = pd.to_datetime(df["timestamp"], unit="ms", utc=True) + df = df.sort_values("timestamp").reset_index(drop=True) + assert list(df.columns) == constants.DEFAULT_TRADES_COLUMNS + ["date"] return df -def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05', random_seed=42): +def generate_test_data(timeframe: str, size: int, start: str = "2020-07-05", random_seed=42): np.random.seed(random_seed) base = np.random.normal(20, 2, size=size) - if timeframe == '1y': - date = pd.date_range(start, periods=size, freq='1YS', tz='UTC') - elif timeframe == '1M': - date = pd.date_range(start, periods=size, freq='1MS', tz='UTC') - elif timeframe == '3M': - date = pd.date_range(start, periods=size, freq='3MS', tz='UTC') - elif timeframe == '1w' or timeframe == '7d': - date = pd.date_range(start, periods=size, freq='1W-MON', tz='UTC') + if timeframe == "1y": + date = pd.date_range(start, periods=size, freq="1YS", tz="UTC") + elif timeframe == "1M": + date = pd.date_range(start, periods=size, freq="1MS", tz="UTC") + elif timeframe == "3M": + date = pd.date_range(start, periods=size, freq="3MS", tz="UTC") + elif timeframe == "1w" or timeframe == "7d": + date = pd.date_range(start, periods=size, freq="1W-MON", tz="UTC") else: tf_mins = timeframe_to_minutes(timeframe) if tf_mins >= 1: - date = pd.date_range(start, periods=size, freq=f'{tf_mins}min', tz='UTC') + date = pd.date_range(start, periods=size, freq=f"{tf_mins}min", tz="UTC") else: tf_secs = timeframe_to_seconds(timeframe) - date = pd.date_range(start, periods=size, freq=f'{tf_secs}s', tz='UTC') - df = pd.DataFrame({ - 'date': date, - 'open': base, - 'high': base + np.random.normal(2, 1, size=size), - 'low': base - np.random.normal(2, 1, size=size), - 'close': base + np.random.normal(0, 1, size=size), - 'volume': np.random.normal(200, size=size) - } + date = pd.date_range(start, periods=size, freq=f"{tf_secs}s", tz="UTC") + df = pd.DataFrame( + { + "date": date, + "open": base, + "high": base + np.random.normal(2, 1, size=size), + "low": base - np.random.normal(2, 1, size=size), + "close": base + np.random.normal(0, 1, size=size), + "volume": np.random.normal(200, size=size), + } ) df = df.dropna() return df -def generate_test_data_raw(timeframe: str, size: int, start: str = '2020-07-05', random_seed=42): - """ Generates data in the ohlcv format used by ccxt """ +def generate_test_data_raw(timeframe: str, size: int, start: str = "2020-07-05", random_seed=42): + """Generates data in the ohlcv format used by ccxt""" df = generate_test_data(timeframe, size, start, random_seed) - df['date'] = df.loc[:, 'date'].astype(np.int64) // 1000 // 1000 + df["date"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000 return list(list(x) for x in zip(*(df[x].values.tolist() for x in df.columns))) @@ -217,56 +231,53 @@ def get_mock_coro(return_value=None, side_effect=None): def patched_configuration_load_config_file(mocker, config) -> None: mocker.patch( - 'freqtrade.configuration.load_config.load_config_file', - lambda *args, **kwargs: config + "freqtrade.configuration.load_config.load_config_file", lambda *args, **kwargs: config ) def patch_exchange( - mocker, - api_mock=None, - id='binance', - mock_markets=True, - mock_supported_modes=True + mocker, api_mock=None, id="binance", mock_markets=True, mock_supported_modes=True ) -> None: - mocker.patch(f'{EXMS}._load_async_markets', return_value={}) - mocker.patch(f'{EXMS}.validate_config', MagicMock()) - mocker.patch(f'{EXMS}.validate_timeframes', MagicMock()) - mocker.patch(f'{EXMS}.id', PropertyMock(return_value=id)) - mocker.patch(f'{EXMS}.name', PropertyMock(return_value=id.title())) - mocker.patch(f'{EXMS}.precisionMode', PropertyMock(return_value=2)) + mocker.patch(f"{EXMS}._load_async_markets", return_value={}) + mocker.patch(f"{EXMS}.validate_config", MagicMock()) + mocker.patch(f"{EXMS}.validate_timeframes", MagicMock()) + mocker.patch(f"{EXMS}.id", PropertyMock(return_value=id)) + mocker.patch(f"{EXMS}.name", PropertyMock(return_value=id.title())) + mocker.patch(f"{EXMS}.precisionMode", PropertyMock(return_value=2)) # Temporary patch ... - mocker.patch('freqtrade.exchange.bybit.Bybit.cache_leverage_tiers') + mocker.patch("freqtrade.exchange.bybit.Bybit.cache_leverage_tiers") if mock_markets: if isinstance(mock_markets, bool): mock_markets = get_markets() - mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=mock_markets)) + mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=mock_markets)) if mock_supported_modes: mocker.patch( - f'freqtrade.exchange.{id}.{id.capitalize()}._supported_trading_mode_margin_pairs', - PropertyMock(return_value=[ - (TradingMode.MARGIN, MarginMode.CROSS), - (TradingMode.MARGIN, MarginMode.ISOLATED), - (TradingMode.FUTURES, MarginMode.CROSS), - (TradingMode.FUTURES, MarginMode.ISOLATED) - ]) + f"freqtrade.exchange.{id}.{id.capitalize()}._supported_trading_mode_margin_pairs", + PropertyMock( + return_value=[ + (TradingMode.MARGIN, MarginMode.CROSS), + (TradingMode.MARGIN, MarginMode.ISOLATED), + (TradingMode.FUTURES, MarginMode.CROSS), + (TradingMode.FUTURES, MarginMode.ISOLATED), + ] + ), ) if api_mock: - mocker.patch(f'{EXMS}._init_ccxt', return_value=api_mock) + mocker.patch(f"{EXMS}._init_ccxt", return_value=api_mock) else: - mocker.patch(f'{EXMS}.get_fee', return_value=0.0025) - mocker.patch(f'{EXMS}._init_ccxt', MagicMock()) - mocker.patch(f'{EXMS}.timeframes', PropertyMock( - return_value=['5m', '15m', '1h', '1d'])) + mocker.patch(f"{EXMS}.get_fee", return_value=0.0025) + mocker.patch(f"{EXMS}._init_ccxt", MagicMock()) + mocker.patch(f"{EXMS}.timeframes", PropertyMock(return_value=["5m", "15m", "1h", "1d"])) -def get_patched_exchange(mocker, config, api_mock=None, id='binance', - mock_markets=True, mock_supported_modes=True) -> Exchange: +def get_patched_exchange( + mocker, config, api_mock=None, id="binance", mock_markets=True, mock_supported_modes=True +) -> Exchange: patch_exchange(mocker, api_mock, id, mock_markets, mock_supported_modes) - config['exchange']['name'] = id + config["exchange"]["name"] = id try: exchange = ExchangeResolver.load_exchange(config, load_leverage_tiers=True) except ImportError: @@ -275,14 +286,14 @@ def get_patched_exchange(mocker, config, api_mock=None, id='binance', def patch_wallet(mocker, free=999.9) -> None: - mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock( - return_value=free - )) + mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=free)) def patch_whitelist(mocker, conf) -> None: - mocker.patch('freqtrade.freqtradebot.FreqtradeBot._refresh_active_whitelist', - MagicMock(return_value=conf['exchange']['pair_whitelist'])) + mocker.patch( + "freqtrade.freqtradebot.FreqtradeBot._refresh_active_whitelist", + MagicMock(return_value=conf["exchange"]["pair_whitelist"]), + ) def patch_edge(mocker) -> None: @@ -291,13 +302,16 @@ def patch_edge(mocker) -> None: # "XRP/BTC", # "NEO/BTC" - mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock( - return_value={ - 'NEO/BTC': PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25), - 'LTC/BTC': PairInfo(-0.21, 0.66, 3.71, 0.50, 1.71, 11, 20), - } - )) - mocker.patch('freqtrade.edge.Edge.calculate', MagicMock(return_value=True)) + mocker.patch( + "freqtrade.edge.Edge._cached_pairs", + mocker.PropertyMock( + return_value={ + "NEO/BTC": PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25), + "LTC/BTC": PairInfo(-0.21, 0.66, 3.71, 0.50, 1.71, 11, 20), + } + ), + ) + mocker.patch("freqtrade.edge.Edge.calculate", MagicMock(return_value=True)) # Functions for recurrent object patching @@ -310,13 +324,13 @@ def patch_freqtradebot(mocker, config) -> None: :param config: Config to pass to the bot :return: None """ - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock()) patch_exchange(mocker) - mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock()) - mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager._init", MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager.send_msg", MagicMock()) patch_whitelist(mocker, config) - mocker.patch('freqtrade.freqtradebot.ExternalMessageConsumer') - mocker.patch('freqtrade.configuration.config_validation._validate_consumers') + mocker.patch("freqtrade.freqtradebot.ExternalMessageConsumer") + mocker.patch("freqtrade.configuration.config_validation._validate_consumers") def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: @@ -354,6 +368,7 @@ def patch_get_signal( :param mocker: mocker to patch IStrategy class :return: None """ + # returns (Signal-direction, signaname) def patched_get_entry_signal(*args, **kwargs): direction = None @@ -383,11 +398,13 @@ def create_mock_trades(fee, is_short: Optional[bool] = False, use_db: bool = Tru Create some fake trades ... :param is_short: Optional bool, None creates a mix of long and short trades. """ + def add_trade(trade): if use_db: Trade.session.add(trade) else: LocalTrade.add_bt_trade(trade) + is_short1 = is_short if is_short is not None else True is_short2 = is_short if is_short is not None else False # Simulate dry_run entries @@ -459,6 +476,7 @@ def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool """ Create some fake trades ... """ + def add_trade(trade): if use_db: Trade.session.add(trade) @@ -501,8 +519,7 @@ def patch_gc(mocker) -> None: @pytest.fixture(autouse=True) def user_dir(mocker, tmp_path) -> Path: user_dir = tmp_path / "user_data" - mocker.patch('freqtrade.configuration.configuration.create_userdata_dir', - return_value=user_dir) + mocker.patch("freqtrade.configuration.configuration.create_userdata_dir", return_value=user_dir) return user_dir @@ -514,23 +531,23 @@ def patch_coingecko(mocker) -> None: :return: None """ - tickermock = MagicMock(return_value={'bitcoin': {'usd': 12345.0}, 'ethereum': {'usd': 12345.0}}) - listmock = MagicMock(return_value=[{'id': 'bitcoin', 'name': 'Bitcoin', 'symbol': 'btc', - 'website_slug': 'bitcoin'}, - {'id': 'ethereum', 'name': 'Ethereum', 'symbol': 'eth', - 'website_slug': 'ethereum'} - ]) + tickermock = MagicMock(return_value={"bitcoin": {"usd": 12345.0}, "ethereum": {"usd": 12345.0}}) + listmock = MagicMock( + return_value=[ + {"id": "bitcoin", "name": "Bitcoin", "symbol": "btc", "website_slug": "bitcoin"}, + {"id": "ethereum", "name": "Ethereum", "symbol": "eth", "website_slug": "ethereum"}, + ] + ) mocker.patch.multiple( - 'freqtrade.rpc.fiat_convert.CoinGeckoAPI', + "freqtrade.rpc.fiat_convert.CoinGeckoAPI", get_price=tickermock, get_coins_list=listmock, - ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def init_persistence(default_conf): - init_db(default_conf['db_url']) + init_db(default_conf["db_url"]) @pytest.fixture(scope="function") @@ -544,35 +561,24 @@ def default_conf_usdt(testdatadir): def get_default_conf(testdatadir): - """ Returns validated configuration suitable for most tests """ + """Returns validated configuration suitable for most tests""" configuration = { "max_open_trades": 1, "stake_currency": "BTC", "stake_amount": 0.001, "fiat_display_currency": "USD", - "timeframe": '5m', + "timeframe": "5m", "dry_run": True, "cancel_open_orders_on_exit": False, - "minimal_roi": { - "40": 0.0, - "30": 0.01, - "20": 0.02, - "0": 0.04 - }, + "minimal_roi": {"40": 0.0, "30": 0.01, "20": 0.02, "0": 0.04}, "dry_run_wallet": 1000, "stoploss": -0.10, - "unfilledtimeout": { - "entry": 10, - "exit": 30 - }, + "unfilledtimeout": {"entry": 10, "exit": 30}, "entry_pricing": { "price_last_balance": 0.0, "use_order_book": False, "order_book_top": 1, - "check_depth_of_market": { - "enabled": False, - "bids_to_ask_delta": 1 - } + "check_depth_of_market": {"enabled": False, "bids_to_ask_delta": 1}, }, "exit_pricing": { "use_order_book": False, @@ -582,20 +588,13 @@ def get_default_conf(testdatadir): "name": "binance", "key": "key", "secret": "secret", - "pair_whitelist": [ - "ETH/BTC", - "LTC/BTC", - "XRP/BTC", - "NEO/BTC" - ], + "pair_whitelist": ["ETH/BTC", "LTC/BTC", "XRP/BTC", "NEO/BTC"], "pair_blacklist": [ "DOGE/BTC", "HOT/BTC", - ] + ], }, - "pairlists": [ - {"method": "StaticPairList"} - ], + "pairlists": [{"method": "StaticPairList"}], "telegram": { "enabled": False, "token": "token", @@ -621,27 +620,29 @@ def get_default_conf(testdatadir): def get_default_conf_usdt(testdatadir): configuration = get_default_conf(testdatadir) - configuration.update({ - "stake_amount": 60.0, - "stake_currency": "USDT", - "exchange": { - "name": "binance", - "enabled": True, - "key": "key", - "secret": "secret", - "pair_whitelist": [ - "ETH/USDT", - "LTC/USDT", - "XRP/USDT", - "NEO/USDT", - "TKN/USDT", - ], - "pair_blacklist": [ - "DOGE/USDT", - "HOT/USDT", - ] - }, - }) + configuration.update( + { + "stake_amount": 60.0, + "stake_currency": "USDT", + "exchange": { + "name": "binance", + "enabled": True, + "key": "key", + "secret": "secret", + "pair_whitelist": [ + "ETH/USDT", + "LTC/USDT", + "XRP/USDT", + "NEO/USDT", + "TKN/USDT", + ], + "pair_blacklist": [ + "DOGE/USDT", + "HOT/USDT", + ], + }, + } + ) return configuration @@ -652,56 +653,68 @@ def fee(): @pytest.fixture def ticker(): - return MagicMock(return_value={ - 'bid': 0.00001098, - 'ask': 0.00001099, - 'last': 0.00001098, - }) + return MagicMock( + return_value={ + "bid": 0.00001098, + "ask": 0.00001099, + "last": 0.00001098, + } + ) @pytest.fixture def ticker_sell_up(): - return MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172, - }) + return MagicMock( + return_value={ + "bid": 0.00001172, + "ask": 0.00001173, + "last": 0.00001172, + } + ) @pytest.fixture def ticker_sell_down(): - return MagicMock(return_value={ - 'bid': 0.00001044, - 'ask': 0.00001043, - 'last': 0.00001044, - }) + return MagicMock( + return_value={ + "bid": 0.00001044, + "ask": 0.00001043, + "last": 0.00001044, + } + ) @pytest.fixture def ticker_usdt(): - return MagicMock(return_value={ - 'bid': 2.0, - 'ask': 2.02, - 'last': 2.0, - }) + return MagicMock( + return_value={ + "bid": 2.0, + "ask": 2.02, + "last": 2.0, + } + ) @pytest.fixture def ticker_usdt_sell_up(): - return MagicMock(return_value={ - 'bid': 2.2, - 'ask': 2.3, - 'last': 2.2, - }) + return MagicMock( + return_value={ + "bid": 2.2, + "ask": 2.3, + "last": 2.2, + } + ) @pytest.fixture def ticker_usdt_sell_down(): - return MagicMock(return_value={ - 'bid': 2.01, - 'ask': 2.0, - 'last': 2.01, - }) + return MagicMock( + return_value={ + "bid": 2.01, + "ask": 2.0, + "last": 2.01, + } + ) @pytest.fixture @@ -713,874 +726,799 @@ def get_markets(): # See get_markets_static() for immutable markets and do not modify them unless absolutely # necessary! return { - 'ETH/BTC': { - 'id': 'ethbtc', - 'symbol': 'ETH/BTC', - 'base': 'ETH', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, + "ETH/BTC": { + "id": "ethbtc", + "symbol": "ETH/BTC", + "base": "ETH", + "quote": "BTC", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 100000000, + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 100000000, }, - 'price': { - 'min': None, - 'max': 500000, + "price": { + "min": None, + "max": 500000, }, - 'cost': { - 'min': 0.0001, - 'max': 500000, + "cost": { + "min": 0.0001, + "max": 500000, }, - 'leverage': { - 'min': 1.0, - 'max': 2.0 - } + "leverage": {"min": 1.0, "max": 2.0}, }, }, - 'TKN/BTC': { - 'id': 'tknbtc', - 'symbol': 'TKN/BTC', - 'base': 'TKN', - 'quote': 'BTC', + "TKN/BTC": { + "id": "tknbtc", + "symbol": "TKN/BTC", + "base": "TKN", + "quote": "BTC", # According to ccxt, markets without active item set are also active # 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 100000000, + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 100000000, }, - 'price': { - 'min': None, - 'max': 500000, + "price": { + "min": None, + "max": 500000, }, - 'cost': { - 'min': 0.0001, - 'max': 500000, + "cost": { + "min": 0.0001, + "max": 500000, }, - 'leverage': { - 'min': 1.0, - 'max': 5.0 - } + "leverage": {"min": 1.0, "max": 5.0}, }, }, - 'BLK/BTC': { - 'id': 'blkbtc', - 'symbol': 'BLK/BTC', - 'base': 'BLK', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, + "BLK/BTC": { + "id": "blkbtc", + "symbol": "BLK/BTC", + "base": "BLK", + "quote": "BTC", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 1000, + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 1000, }, - 'price': { - 'min': None, - 'max': 500000, + "price": { + "min": None, + "max": 500000, }, - 'cost': { - 'min': 0.0001, - 'max': 500000, - }, - 'leverage': { - 'min': 1.0, - 'max': 3.0 + "cost": { + "min": 0.0001, + "max": 500000, }, + "leverage": {"min": 1.0, "max": 3.0}, }, }, - 'LTC/BTC': { - 'id': 'ltcbtc', - 'symbol': 'LTC/BTC', - 'base': 'LTC', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, + "LTC/BTC": { + "id": "ltcbtc", + "symbol": "LTC/BTC", + "base": "LTC", + "quote": "BTC", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 100000000, + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 100000000, }, - 'price': { - 'min': None, - 'max': 500000, + "price": { + "min": None, + "max": 500000, }, - 'cost': { - 'min': 0.0001, - 'max': 500000, - }, - 'leverage': { - 'min': None, - 'max': None + "cost": { + "min": 0.0001, + "max": 500000, }, + "leverage": {"min": None, "max": None}, }, - 'info': {}, + "info": {}, }, - 'XRP/BTC': { - 'id': 'xrpbtc', - 'symbol': 'XRP/BTC', - 'base': 'XRP', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, + "XRP/BTC": { + "id": "xrpbtc", + "symbol": "XRP/BTC", + "base": "XRP", + "quote": "BTC", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 100000000, + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 100000000, }, - 'price': { - 'min': None, - 'max': 500000, + "price": { + "min": None, + "max": 500000, }, - 'cost': { - 'min': 0.0001, - 'max': 500000, + "cost": { + "min": 0.0001, + "max": 500000, }, - 'leverage': { - 'min': None, - 'max': None, - }, - }, - 'info': {}, - }, - 'NEO/BTC': { - 'id': 'neobtc', - 'symbol': 'NEO/BTC', - 'base': 'NEO', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, - }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 100000000, - }, - 'price': { - 'min': None, - 'max': 500000, - }, - 'cost': { - 'min': 0.0001, - 'max': 500000, - }, - 'leverage': { - 'min': None, - 'max': None, - }, - }, - 'info': {}, - }, - 'BTT/BTC': { - 'id': 'BTTBTC', - 'symbol': 'BTT/BTC', - 'base': 'BTT', - 'quote': 'BTC', - 'active': False, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'contractSize': None, - 'precision': { - 'base': 8, - 'quote': 8, - 'amount': 0, - 'price': 8 - }, - 'limits': { - 'amount': { - 'min': 1.0, - 'max': 90000000.0 - }, - 'price': { - 'min': None, - 'max': None - }, - 'cost': { - 'min': 0.0001, - 'max': None - }, - 'leverage': { - 'min': None, - 'max': None, - }, - }, - 'info': {}, - }, - 'ETH/USDT': { - 'id': 'USDT-ETH', - 'symbol': 'ETH/USDT', - 'base': 'ETH', - 'quote': 'USDT', - 'settle': None, - 'baseId': 'ETH', - 'quoteId': 'USDT', - 'settleId': None, - 'type': 'spot', - 'spot': True, - 'margin': True, - 'swap': True, - 'future': True, - 'option': False, - 'active': True, - 'contract': None, - 'linear': None, - 'inverse': None, - 'taker': 0.0006, - 'maker': 0.0002, - 'contractSize': None, - 'expiry': None, - 'expiryDateTime': None, - 'strike': None, - 'optionType': None, - 'precision': { - 'amount': 8, - 'price': 8, - }, - 'limits': { - 'leverage': { - 'min': 1, - 'max': 100, - }, - 'amount': { - 'min': 0.02214286, - 'max': None, - }, - 'price': { - 'min': 1e-08, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - }, - }, - 'info': { - 'maintenance_rate': '0.005', - }, - }, - 'BTC/USDT': { - 'id': 'USDT-BTC', - 'symbol': 'BTC/USDT', - 'base': 'BTC', - 'quote': 'USDT', - 'settle': None, - 'baseId': 'BTC', - 'quoteId': 'USDT', - 'settleId': None, - 'type': 'spot', - 'spot': True, - 'margin': True, - 'swap': False, - 'future': False, - 'option': False, - 'active': True, - 'contract': None, - 'linear': None, - 'inverse': None, - 'taker': 0.0006, - 'maker': 0.0002, - 'contractSize': None, - 'expiry': None, - 'expiryDateTime': None, - 'strike': None, - 'optionType': None, - 'precision': { - 'amount': 4, - 'price': 4, - }, - 'limits': { - 'leverage': { - 'min': 1, - 'max': 100, - }, - 'amount': { - 'min': 0.000221, - 'max': None, - }, - 'price': { - 'min': 1e-02, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - }, - }, - 'info': { - 'maintenance_rate': '0.005', - }, - }, - 'LTC/USDT': { - 'id': 'USDT-LTC', - 'symbol': 'LTC/USDT', - 'base': 'LTC', - 'quote': 'USDT', - 'active': False, - 'spot': True, - 'future': True, - 'swap': True, - 'margin': True, - 'linear': None, - 'inverse': False, - 'type': 'spot', - 'contractSize': None, - 'taker': 0.0006, - 'maker': 0.0002, - 'precision': { - 'amount': 8, - 'price': 8 - }, - 'limits': { - 'amount': { - 'min': 0.06646786, - 'max': None - }, - 'price': { - 'min': 1e-08, - 'max': None - }, - 'leverage': { - 'min': None, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - }, - }, - 'info': {}, - }, - 'XRP/USDT': { - 'id': 'xrpusdt', - 'symbol': 'XRP/USDT', - 'base': 'XRP', - 'quote': 'USDT', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'taker': 0.0006, - 'maker': 0.0002, - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, - }, - 'lot': 0.00000001, - 'contractSize': None, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 1000, - }, - 'price': { - 'min': None, - 'max': 500000, - }, - 'cost': { - 'min': 0.0001, - 'max': 500000, - }, - }, - 'info': {}, - }, - 'NEO/USDT': { - 'id': 'neousdt', - 'symbol': 'NEO/USDT', - 'base': 'NEO', - 'quote': 'USDT', - 'settle': '', - 'baseId': 'NEO', - 'quoteId': 'USDT', - 'settleId': '', - 'type': 'spot', - 'spot': True, - 'margin': True, - 'swap': False, - 'futures': False, - 'option': False, - 'active': True, - 'contract': False, - 'linear': None, - 'inverse': None, - 'taker': 0.0006, - 'maker': 0.0002, - 'contractSize': None, - 'expiry': None, - 'expiryDatetime': None, - 'strike': None, - 'optionType': None, - 'tierBased': None, - 'percentage': None, - 'lot': 0.00000001, - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, - }, - 'limits': { "leverage": { - 'min': 1, - 'max': 10 - }, - 'amount': { - 'min': 0.01, - 'max': 1000, - }, - 'price': { - 'min': None, - 'max': 500000, - }, - 'cost': { - 'min': 0.0001, - 'max': 500000, + "min": None, + "max": None, }, }, - 'info': {}, + "info": {}, }, - 'TKN/USDT': { - 'id': 'tknusdt', - 'symbol': 'TKN/USDT', - 'base': 'TKN', - 'quote': 'USDT', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'contractSize': None, - 'taker': 0.0006, - 'maker': 0.0002, - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, + "NEO/BTC": { + "id": "neobtc", + "symbol": "NEO/BTC", + "base": "NEO", + "quote": "BTC", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'lot': 0.00000001, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 100000000000, + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 100000000, }, - 'price': { - 'min': None, - 'max': 500000 + "price": { + "min": None, + "max": 500000, }, - 'cost': { - 'min': 0.0001, - 'max': 500000, + "cost": { + "min": 0.0001, + "max": 500000, }, - 'leverage': { - 'min': None, - 'max': None, + "leverage": { + "min": None, + "max": None, }, }, - 'info': {}, + "info": {}, }, - 'LTC/USD': { - 'id': 'USD-LTC', - 'symbol': 'LTC/USD', - 'base': 'LTC', - 'quote': 'USD', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'contractSize': None, - 'precision': { - 'amount': 8, - 'price': 8 - }, - 'limits': { - 'amount': { - 'min': 0.06646786, - 'max': None - }, - 'price': { - 'min': 1e-08, - 'max': None - }, - 'leverage': { - 'min': None, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, + "BTT/BTC": { + "id": "BTTBTC", + "symbol": "BTT/BTC", + "base": "BTT", + "quote": "BTC", + "active": False, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "contractSize": None, + "precision": {"base": 8, "quote": 8, "amount": 0, "price": 8}, + "limits": { + "amount": {"min": 1.0, "max": 90000000.0}, + "price": {"min": None, "max": None}, + "cost": {"min": 0.0001, "max": None}, + "leverage": { + "min": None, + "max": None, }, }, - 'info': {}, + "info": {}, }, - 'XLTCUSDT': { - 'id': 'xLTCUSDT', - 'symbol': 'XLTCUSDT', - 'base': 'LTC', - 'quote': 'USDT', - 'active': True, - 'spot': False, - 'type': 'swap', - 'contractSize': 0.01, - 'swap': False, - 'linear': False, - 'taker': 0.0006, - 'maker': 0.0002, - 'precision': { - 'amount': 8, - 'price': 8 + "ETH/USDT": { + "id": "USDT-ETH", + "symbol": "ETH/USDT", + "base": "ETH", + "quote": "USDT", + "settle": None, + "baseId": "ETH", + "quoteId": "USDT", + "settleId": None, + "type": "spot", + "spot": True, + "margin": True, + "swap": True, + "future": True, + "option": False, + "active": True, + "contract": None, + "linear": None, + "inverse": None, + "taker": 0.0006, + "maker": 0.0002, + "contractSize": None, + "expiry": None, + "expiryDateTime": None, + "strike": None, + "optionType": None, + "precision": { + "amount": 8, + "price": 8, }, - 'limits': { - 'leverage': { - 'min': None, - 'max': None, + "limits": { + "leverage": { + "min": 1, + "max": 100, }, - 'amount': { - 'min': 0.06646786, - 'max': None + "amount": { + "min": 0.02214286, + "max": None, }, - 'price': { - 'min': 1e-08, - 'max': None + "price": { + "min": 1e-08, + "max": None, }, - 'cost': { - 'min': None, - 'max': None, + "cost": { + "min": None, + "max": None, }, }, - 'info': {}, + "info": { + "maintenance_rate": "0.005", + }, }, - 'LTC/ETH': { - 'id': 'LTCETH', - 'symbol': 'LTC/ETH', - 'base': 'LTC', - 'quote': 'ETH', - 'active': True, - 'spot': True, - 'swap': False, - 'linear': None, - 'type': 'spot', - 'contractSize': None, - 'precision': { - 'base': 8, - 'quote': 8, - 'amount': 3, - 'price': 5 + "BTC/USDT": { + "id": "USDT-BTC", + "symbol": "BTC/USDT", + "base": "BTC", + "quote": "USDT", + "settle": None, + "baseId": "BTC", + "quoteId": "USDT", + "settleId": None, + "type": "spot", + "spot": True, + "margin": True, + "swap": False, + "future": False, + "option": False, + "active": True, + "contract": None, + "linear": None, + "inverse": None, + "taker": 0.0006, + "maker": 0.0002, + "contractSize": None, + "expiry": None, + "expiryDateTime": None, + "strike": None, + "optionType": None, + "precision": { + "amount": 4, + "price": 4, }, - 'limits': { - 'leverage': { - 'min': None, - 'max': None, + "limits": { + "leverage": { + "min": 1, + "max": 100, }, - 'amount': { - 'min': 0.001, - 'max': 10000000.0 + "amount": { + "min": 0.000221, + "max": None, }, - 'price': { - 'min': 1e-05, - 'max': 1000.0 + "price": { + "min": 1e-02, + "max": None, + }, + "cost": { + "min": None, + "max": None, }, - 'cost': { - 'min': 0.01, - 'max': None - } }, - 'info': { - } + "info": { + "maintenance_rate": "0.005", + }, }, - 'ETH/USDT:USDT': { - 'id': 'ETH_USDT', - 'symbol': 'ETH/USDT:USDT', - 'base': 'ETH', - 'quote': 'USDT', - 'settle': 'USDT', - 'baseId': 'ETH', - 'quoteId': 'USDT', - 'settleId': 'USDT', - 'type': 'swap', - 'spot': False, - 'margin': False, - 'swap': True, - 'future': True, # Binance mode ... - 'option': False, - 'contract': True, - 'linear': True, - 'inverse': False, - 'tierBased': False, - 'percentage': True, - 'taker': 0.0006, - 'maker': 0.0002, - 'contractSize': 10, - 'active': True, - 'expiry': None, - 'expiryDatetime': None, - 'strike': None, - 'optionType': None, - 'limits': { - 'leverage': { - 'min': 1, - 'max': 100 + "LTC/USDT": { + "id": "USDT-LTC", + "symbol": "LTC/USDT", + "base": "LTC", + "quote": "USDT", + "active": False, + "spot": True, + "future": True, + "swap": True, + "margin": True, + "linear": None, + "inverse": False, + "type": "spot", + "contractSize": None, + "taker": 0.0006, + "maker": 0.0002, + "precision": {"amount": 8, "price": 8}, + "limits": { + "amount": {"min": 0.06646786, "max": None}, + "price": {"min": 1e-08, "max": None}, + "leverage": { + "min": None, + "max": None, }, - 'amount': { - 'min': 1, - 'max': 300000 + "cost": { + "min": None, + "max": None, }, - 'price': { - 'min': None, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - } }, - 'precision': { - 'price': 0.05, - 'amount': 1 - }, - 'info': {} + "info": {}, }, - 'ADA/USDT:USDT': { - 'limits': { - 'leverage': { - 'min': 1, - 'max': 20, - }, - 'amount': { - 'min': 1, - 'max': 1000000, - }, - 'price': { - 'min': 0.52981, - 'max': 1.58943, - }, - 'cost': { - 'min': None, - 'max': None, - } + "XRP/USDT": { + "id": "xrpusdt", + "symbol": "XRP/USDT", + "base": "XRP", + "quote": "USDT", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "taker": 0.0006, + "maker": 0.0002, + "precision": { + "price": 8, + "amount": 8, + "cost": 8, }, - 'precision': { - 'amount': 1, - 'price': 0.00001 + "lot": 0.00000001, + "contractSize": None, + "limits": { + "amount": { + "min": 0.01, + "max": 1000, + }, + "price": { + "min": None, + "max": 500000, + }, + "cost": { + "min": 0.0001, + "max": 500000, + }, }, - 'tierBased': True, - 'percentage': True, - 'taker': 0.0000075, - 'maker': -0.0000025, - 'feeSide': 'get', - 'tiers': { - 'maker': [ - [0, 0.002], [1.5, 0.00185], - [3, 0.00175], [6, 0.00165], - [12.5, 0.00155], [25, 0.00145], - [75, 0.00135], [200, 0.00125], - [500, 0.00115], [1250, 0.00105], - [2500, 0.00095], [3000, 0.00085], - [6000, 0.00075], [11000, 0.00065], - [20000, 0.00055], [40000, 0.00055], - [75000, 0.00055] + "info": {}, + }, + "NEO/USDT": { + "id": "neousdt", + "symbol": "NEO/USDT", + "base": "NEO", + "quote": "USDT", + "settle": "", + "baseId": "NEO", + "quoteId": "USDT", + "settleId": "", + "type": "spot", + "spot": True, + "margin": True, + "swap": False, + "futures": False, + "option": False, + "active": True, + "contract": False, + "linear": None, + "inverse": None, + "taker": 0.0006, + "maker": 0.0002, + "contractSize": None, + "expiry": None, + "expiryDatetime": None, + "strike": None, + "optionType": None, + "tierBased": None, + "percentage": None, + "lot": 0.00000001, + "precision": { + "price": 8, + "amount": 8, + "cost": 8, + }, + "limits": { + "leverage": {"min": 1, "max": 10}, + "amount": { + "min": 0.01, + "max": 1000, + }, + "price": { + "min": None, + "max": 500000, + }, + "cost": { + "min": 0.0001, + "max": 500000, + }, + }, + "info": {}, + }, + "TKN/USDT": { + "id": "tknusdt", + "symbol": "TKN/USDT", + "base": "TKN", + "quote": "USDT", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "contractSize": None, + "taker": 0.0006, + "maker": 0.0002, + "precision": { + "price": 8, + "amount": 8, + "cost": 8, + }, + "lot": 0.00000001, + "limits": { + "amount": { + "min": 0.01, + "max": 100000000000, + }, + "price": {"min": None, "max": 500000}, + "cost": { + "min": 0.0001, + "max": 500000, + }, + "leverage": { + "min": None, + "max": None, + }, + }, + "info": {}, + }, + "LTC/USD": { + "id": "USD-LTC", + "symbol": "LTC/USD", + "base": "LTC", + "quote": "USD", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "contractSize": None, + "precision": {"amount": 8, "price": 8}, + "limits": { + "amount": {"min": 0.06646786, "max": None}, + "price": {"min": 1e-08, "max": None}, + "leverage": { + "min": None, + "max": None, + }, + "cost": { + "min": None, + "max": None, + }, + }, + "info": {}, + }, + "XLTCUSDT": { + "id": "xLTCUSDT", + "symbol": "XLTCUSDT", + "base": "LTC", + "quote": "USDT", + "active": True, + "spot": False, + "type": "swap", + "contractSize": 0.01, + "swap": False, + "linear": False, + "taker": 0.0006, + "maker": 0.0002, + "precision": {"amount": 8, "price": 8}, + "limits": { + "leverage": { + "min": None, + "max": None, + }, + "amount": {"min": 0.06646786, "max": None}, + "price": {"min": 1e-08, "max": None}, + "cost": { + "min": None, + "max": None, + }, + }, + "info": {}, + }, + "LTC/ETH": { + "id": "LTCETH", + "symbol": "LTC/ETH", + "base": "LTC", + "quote": "ETH", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "contractSize": None, + "precision": {"base": 8, "quote": 8, "amount": 3, "price": 5}, + "limits": { + "leverage": { + "min": None, + "max": None, + }, + "amount": {"min": 0.001, "max": 10000000.0}, + "price": {"min": 1e-05, "max": 1000.0}, + "cost": {"min": 0.01, "max": None}, + }, + "info": {}, + }, + "ETH/USDT:USDT": { + "id": "ETH_USDT", + "symbol": "ETH/USDT:USDT", + "base": "ETH", + "quote": "USDT", + "settle": "USDT", + "baseId": "ETH", + "quoteId": "USDT", + "settleId": "USDT", + "type": "swap", + "spot": False, + "margin": False, + "swap": True, + "future": True, # Binance mode ... + "option": False, + "contract": True, + "linear": True, + "inverse": False, + "tierBased": False, + "percentage": True, + "taker": 0.0006, + "maker": 0.0002, + "contractSize": 10, + "active": True, + "expiry": None, + "expiryDatetime": None, + "strike": None, + "optionType": None, + "limits": { + "leverage": {"min": 1, "max": 100}, + "amount": {"min": 1, "max": 300000}, + "price": { + "min": None, + "max": None, + }, + "cost": { + "min": None, + "max": None, + }, + }, + "precision": {"price": 0.05, "amount": 1}, + "info": {}, + }, + "ADA/USDT:USDT": { + "limits": { + "leverage": { + "min": 1, + "max": 20, + }, + "amount": { + "min": 1, + "max": 1000000, + }, + "price": { + "min": 0.52981, + "max": 1.58943, + }, + "cost": { + "min": None, + "max": None, + }, + }, + "precision": {"amount": 1, "price": 0.00001}, + "tierBased": True, + "percentage": True, + "taker": 0.0000075, + "maker": -0.0000025, + "feeSide": "get", + "tiers": { + "maker": [ + [0, 0.002], + [1.5, 0.00185], + [3, 0.00175], + [6, 0.00165], + [12.5, 0.00155], + [25, 0.00145], + [75, 0.00135], + [200, 0.00125], + [500, 0.00115], + [1250, 0.00105], + [2500, 0.00095], + [3000, 0.00085], + [6000, 0.00075], + [11000, 0.00065], + [20000, 0.00055], + [40000, 0.00055], + [75000, 0.00055], + ], + "taker": [ + [0, 0.002], + [1.5, 0.00195], + [3, 0.00185], + [6, 0.00175], + [12.5, 0.00165], + [25, 0.00155], + [75, 0.00145], + [200, 0.00135], + [500, 0.00125], + [1250, 0.00115], + [2500, 0.00105], + [3000, 0.00095], + [6000, 0.00085], + [11000, 0.00075], + [20000, 0.00065], + [40000, 0.00065], + [75000, 0.00065], ], - 'taker': [ - [0, 0.002], [1.5, 0.00195], - [3, 0.00185], [6, 0.00175], - [12.5, 0.00165], [25, 0.00155], - [75, 0.00145], [200, 0.00135], - [500, 0.00125], [1250, 0.00115], - [2500, 0.00105], [3000, 0.00095], - [6000, 0.00085], [11000, 0.00075], - [20000, 0.00065], [40000, 0.00065], - [75000, 0.00065] - ] }, - 'id': 'ADA_USDT', - 'symbol': 'ADA/USDT:USDT', - 'base': 'ADA', - 'quote': 'USDT', - 'settle': 'USDT', - 'baseId': 'ADA', - 'quoteId': 'USDT', - 'settleId': 'usdt', - 'type': 'swap', - 'spot': False, - 'margin': False, - 'swap': True, - 'future': True, # Binance mode ... - 'option': False, - 'active': True, - 'contract': True, - 'linear': True, - 'inverse': False, - 'contractSize': 0.01, - 'expiry': None, - 'expiryDatetime': None, - 'strike': None, - 'optionType': None, - 'info': {} + "id": "ADA_USDT", + "symbol": "ADA/USDT:USDT", + "base": "ADA", + "quote": "USDT", + "settle": "USDT", + "baseId": "ADA", + "quoteId": "USDT", + "settleId": "usdt", + "type": "swap", + "spot": False, + "margin": False, + "swap": True, + "future": True, # Binance mode ... + "option": False, + "active": True, + "contract": True, + "linear": True, + "inverse": False, + "contractSize": 0.01, + "expiry": None, + "expiryDatetime": None, + "strike": None, + "optionType": None, + "info": {}, }, - 'SOL/BUSD:BUSD': { - 'limits': { - 'leverage': {'min': None, 'max': None}, - 'amount': {'min': 1, 'max': 1000000}, - 'price': {'min': 0.04, 'max': 100000}, - 'cost': {'min': 5, 'max': None}, - 'market': {'min': 1, 'max': 1500} + "SOL/BUSD:BUSD": { + "limits": { + "leverage": {"min": None, "max": None}, + "amount": {"min": 1, "max": 1000000}, + "price": {"min": 0.04, "max": 100000}, + "cost": {"min": 5, "max": None}, + "market": {"min": 1, "max": 1500}, }, - 'precision': {'amount': 0, 'price': 2, 'base': 8, 'quote': 8}, - 'tierBased': False, - 'percentage': True, - 'taker': 0.0004, - 'maker': 0.0002, - 'feeSide': 'get', - 'id': 'SOLBUSD', - 'lowercaseId': 'solbusd', - 'symbol': 'SOL/BUSD', - 'base': 'SOL', - 'quote': 'BUSD', - 'settle': 'BUSD', - 'baseId': 'SOL', - 'quoteId': 'BUSD', - 'settleId': 'BUSD', - 'type': 'future', - 'spot': False, - 'margin': False, - 'future': True, - 'delivery': False, - 'option': False, - 'active': True, - 'contract': True, - 'linear': True, - 'inverse': False, - 'contractSize': 1, - 'expiry': None, - 'expiryDatetime': None, - 'strike': None, - 'optionType': None, - 'info': { - 'symbol': 'SOLBUSD', - 'pair': 'SOLBUSD', - 'contractType': 'PERPETUAL', - 'deliveryDate': '4133404800000', - 'onboardDate': '1630566000000', - 'status': 'TRADING', - 'maintMarginPercent': '2.5000', - 'requiredMarginPercent': '5.0000', - 'baseAsset': 'SOL', - 'quoteAsset': 'BUSD', - 'marginAsset': 'BUSD', - 'pricePrecision': '4', - 'quantityPrecision': '0', - 'baseAssetPrecision': '8', - 'quotePrecision': '8', - 'underlyingType': 'COIN', - 'underlyingSubType': [], - 'settlePlan': '0', - 'triggerProtect': '0.0500', - 'liquidationFee': '0.005000', - 'marketTakeBound': '0.05', - 'filters': [ + "precision": {"amount": 0, "price": 2, "base": 8, "quote": 8}, + "tierBased": False, + "percentage": True, + "taker": 0.0004, + "maker": 0.0002, + "feeSide": "get", + "id": "SOLBUSD", + "lowercaseId": "solbusd", + "symbol": "SOL/BUSD", + "base": "SOL", + "quote": "BUSD", + "settle": "BUSD", + "baseId": "SOL", + "quoteId": "BUSD", + "settleId": "BUSD", + "type": "future", + "spot": False, + "margin": False, + "future": True, + "delivery": False, + "option": False, + "active": True, + "contract": True, + "linear": True, + "inverse": False, + "contractSize": 1, + "expiry": None, + "expiryDatetime": None, + "strike": None, + "optionType": None, + "info": { + "symbol": "SOLBUSD", + "pair": "SOLBUSD", + "contractType": "PERPETUAL", + "deliveryDate": "4133404800000", + "onboardDate": "1630566000000", + "status": "TRADING", + "maintMarginPercent": "2.5000", + "requiredMarginPercent": "5.0000", + "baseAsset": "SOL", + "quoteAsset": "BUSD", + "marginAsset": "BUSD", + "pricePrecision": "4", + "quantityPrecision": "0", + "baseAssetPrecision": "8", + "quotePrecision": "8", + "underlyingType": "COIN", + "underlyingSubType": [], + "settlePlan": "0", + "triggerProtect": "0.0500", + "liquidationFee": "0.005000", + "marketTakeBound": "0.05", + "filters": [ { - 'minPrice': '0.0400', - 'maxPrice': '100000', - 'filterType': 'PRICE_FILTER', - 'tickSize': '0.0100' + "minPrice": "0.0400", + "maxPrice": "100000", + "filterType": "PRICE_FILTER", + "tickSize": "0.0100", }, + {"stepSize": "1", "filterType": "LOT_SIZE", "maxQty": "1000000", "minQty": "1"}, { - 'stepSize': '1', - 'filterType': 'LOT_SIZE', - 'maxQty': '1000000', - 'minQty': '1' + "stepSize": "1", + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1500", + "minQty": "1", }, + {"limit": "200", "filterType": "MAX_NUM_ORDERS"}, + {"limit": "10", "filterType": "MAX_NUM_ALGO_ORDERS"}, + {"notional": "5", "filterType": "MIN_NOTIONAL"}, { - 'stepSize': '1', - 'filterType': 'MARKET_LOT_SIZE', - 'maxQty': '1500', - 'minQty': '1' + "multiplierDown": "0.9500", + "multiplierUp": "1.0500", + "multiplierDecimal": "4", + "filterType": "PERCENT_PRICE", }, - {'limit': '200', 'filterType': 'MAX_NUM_ORDERS'}, - {'limit': '10', 'filterType': 'MAX_NUM_ALGO_ORDERS'}, - {'notional': '5', 'filterType': 'MIN_NOTIONAL'}, - { - 'multiplierDown': '0.9500', - 'multiplierUp': '1.0500', - 'multiplierDecimal': '4', - 'filterType': 'PERCENT_PRICE' - } ], - 'orderTypes': [ - 'LIMIT', - 'MARKET', - 'STOP', - 'STOP_MARKET', - 'TAKE_PROFIT', - 'TAKE_PROFIT_MARKET', - 'TRAILING_STOP_MARKET' + "orderTypes": [ + "LIMIT", + "MARKET", + "STOP", + "STOP_MARKET", + "TAKE_PROFIT", + "TAKE_PROFIT_MARKET", + "TRAILING_STOP_MARKET", ], - 'timeInForce': ['GTC', 'IOC', 'FOK', 'GTX'] - } + "timeInForce": ["GTC", "IOC", "FOK", "GTX"], + }, }, } @@ -1590,10 +1528,22 @@ def markets_static(): # These markets are used in some tests that would need adaptation should anything change in # market list. Do not modify this list without a good reason! Do not modify market parameters # of listed pairs in get_markets() without a good reason either! - static_markets = ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', - 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC', - 'ADA/USDT:USDT', 'ETH/USDT:USDT', - ] + static_markets = [ + "BLK/BTC", + "BTT/BTC", + "ETH/BTC", + "ETH/USDT", + "LTC/BTC", + "LTC/ETH", + "LTC/USD", + "LTC/USDT", + "NEO/BTC", + "TKN/BTC", + "XLTCUSDT", + "XRP/BTC", + "ADA/USDT:USDT", + "ETH/USDT:USDT", + ] all_markets = get_markets() return {m: all_markets[m] for m in static_markets} @@ -1604,191 +1554,150 @@ def shitcoinmarkets(markets_static): Fixture with shitcoin markets - used to test filters in pairlists """ shitmarkets = deepcopy(markets_static) - shitmarkets.update({ - 'HOT/BTC': { - 'id': 'HOTBTC', - 'symbol': 'HOT/BTC', - 'base': 'HOT', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'type': 'spot', - 'precision': { - 'base': 8, - 'quote': 8, - 'amount': 0, - 'price': 8 + shitmarkets.update( + { + "HOT/BTC": { + "id": "HOTBTC", + "symbol": "HOT/BTC", + "base": "HOT", + "quote": "BTC", + "active": True, + "spot": True, + "type": "spot", + "precision": {"base": 8, "quote": 8, "amount": 0, "price": 8}, + "limits": { + "amount": {"min": 1.0, "max": 90000000.0}, + "price": {"min": None, "max": None}, + "cost": {"min": 0.001, "max": None}, + }, + "info": {}, }, - 'limits': { - 'amount': { - 'min': 1.0, - 'max': 90000000.0 + "FUEL/BTC": { + "id": "FUELBTC", + "symbol": "FUEL/BTC", + "base": "FUEL", + "quote": "BTC", + "active": True, + "spot": True, + "type": "spot", + "precision": {"base": 8, "quote": 8, "amount": 0, "price": 8}, + "limits": { + "amount": {"min": 1.0, "max": 90000000.0}, + "price": {"min": 1e-08, "max": 1000.0}, + "cost": {"min": 0.001, "max": None}, }, - 'price': { - 'min': None, - 'max': None - }, - 'cost': { - 'min': 0.001, - 'max': None - } + "info": {}, }, - 'info': {}, - }, - 'FUEL/BTC': { - 'id': 'FUELBTC', - 'symbol': 'FUEL/BTC', - 'base': 'FUEL', - 'quote': 'BTC', - 'active': True, - 'spot': True, - 'type': 'spot', - 'precision': { - 'base': 8, - 'quote': 8, - 'amount': 0, - 'price': 8 + "NANO/USDT": { + "percentage": True, + "tierBased": False, + "taker": 0.001, + "maker": 0.001, + "precision": {"base": 8, "quote": 8, "amount": 2, "price": 4}, + "limits": { + "leverage": { + "min": None, + "max": None, + }, + "amount": { + "min": None, + "max": None, + }, + "price": { + "min": None, + "max": None, + }, + "cost": { + "min": None, + "max": None, + }, + }, + "id": "NANOUSDT", + "symbol": "NANO/USDT", + "base": "NANO", + "quote": "USDT", + "baseId": "NANO", + "quoteId": "USDT", + "info": {}, + "type": "spot", + "spot": True, + "future": False, + "active": True, }, - 'limits': { - 'amount': { - 'min': 1.0, - 'max': 90000000.0 + "ADAHALF/USDT": { + "percentage": True, + "tierBased": False, + "taker": 0.001, + "maker": 0.001, + "precision": {"base": 8, "quote": 8, "amount": 2, "price": 4}, + "limits": { + "leverage": { + "min": None, + "max": None, + }, + "amount": { + "min": None, + "max": None, + }, + "price": { + "min": None, + "max": None, + }, + "cost": { + "min": None, + "max": None, + }, }, - 'price': { - 'min': 1e-08, - 'max': 1000.0 - }, - 'cost': { - 'min': 0.001, - 'max': None - } + "id": "ADAHALFUSDT", + "symbol": "ADAHALF/USDT", + "base": "ADAHALF", + "quote": "USDT", + "baseId": "ADAHALF", + "quoteId": "USDT", + "info": {}, + "type": "spot", + "spot": True, + "future": False, + "active": True, }, - 'info': {}, - }, - 'NANO/USDT': { - "percentage": True, - "tierBased": False, - "taker": 0.001, - "maker": 0.001, - "precision": { - "base": 8, - "quote": 8, - "amount": 2, - "price": 4 + "ADADOUBLE/USDT": { + "percentage": True, + "tierBased": False, + "taker": 0.001, + "maker": 0.001, + "precision": {"base": 8, "quote": 8, "amount": 2, "price": 4}, + "limits": { + "leverage": { + "min": None, + "max": None, + }, + "amount": { + "min": None, + "max": None, + }, + "price": { + "min": None, + "max": None, + }, + "cost": { + "min": None, + "max": None, + }, + }, + "id": "ADADOUBLEUSDT", + "symbol": "ADADOUBLE/USDT", + "base": "ADADOUBLE", + "quote": "USDT", + "baseId": "ADADOUBLE", + "quoteId": "USDT", + "info": {}, + "type": "spot", + "spot": True, + "future": False, + "active": True, }, - "limits": { - 'leverage': { - 'min': None, - 'max': None, - }, - 'amount': { - 'min': None, - 'max': None, - }, - 'price': { - 'min': None, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - }, - }, - "id": "NANOUSDT", - "symbol": "NANO/USDT", - "base": "NANO", - "quote": "USDT", - "baseId": "NANO", - "quoteId": "USDT", - "info": {}, - "type": "spot", - "spot": True, - "future": False, - "active": True - }, - 'ADAHALF/USDT': { - "percentage": True, - "tierBased": False, - "taker": 0.001, - "maker": 0.001, - "precision": { - "base": 8, - "quote": 8, - "amount": 2, - "price": 4 - }, - "limits": { - 'leverage': { - 'min': None, - 'max': None, - }, - 'amount': { - 'min': None, - 'max': None, - }, - 'price': { - 'min': None, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - }, - }, - "id": "ADAHALFUSDT", - "symbol": "ADAHALF/USDT", - "base": "ADAHALF", - "quote": "USDT", - "baseId": "ADAHALF", - "quoteId": "USDT", - "info": {}, - "type": "spot", - "spot": True, - "future": False, - "active": True - }, - 'ADADOUBLE/USDT': { - "percentage": True, - "tierBased": False, - "taker": 0.001, - "maker": 0.001, - "precision": { - "base": 8, - "quote": 8, - "amount": 2, - "price": 4 - }, - "limits": { - 'leverage': { - 'min': None, - 'max': None, - }, - 'amount': { - 'min': None, - 'max': None, - }, - 'price': { - 'min': None, - 'max': None, - }, - 'cost': { - 'min': None, - 'max': None, - }, - }, - "id": "ADADOUBLEUSDT", - "symbol": "ADADOUBLE/USDT", - "base": "ADADOUBLE", - "quote": "USDT", - "baseId": "ADADOUBLE", - "quoteId": "USDT", - "info": {}, - "type": "spot", - "spot": True, - "future": False, - "active": True - }, - }) + } + ) return shitmarkets @@ -1797,257 +1706,261 @@ def markets_empty(): return MagicMock(return_value=[]) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_buy_order_open(): return { - 'id': 'mocked_limit_buy', - 'type': 'limit', - 'side': 'buy', - 'symbol': 'mocked', - 'timestamp': dt_ts(), - 'datetime': dt_now().isoformat(), - 'price': 0.00001099, - 'average': 0.00001099, - 'amount': 90.99181073, - 'filled': 0.0, - 'cost': 0.0009999, - 'remaining': 90.99181073, - 'status': 'open' + "id": "mocked_limit_buy", + "type": "limit", + "side": "buy", + "symbol": "mocked", + "timestamp": dt_ts(), + "datetime": dt_now().isoformat(), + "price": 0.00001099, + "average": 0.00001099, + "amount": 90.99181073, + "filled": 0.0, + "cost": 0.0009999, + "remaining": 90.99181073, + "status": "open", } -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_buy_order(limit_buy_order_open): order = deepcopy(limit_buy_order_open) - order['status'] = 'closed' - order['filled'] = order['amount'] - order['remaining'] = 0.0 + order["status"] = "closed" + order["filled"] = order["amount"] + order["remaining"] = 0.0 return order @pytest.fixture def limit_buy_order_old(): return { - 'id': 'mocked_limit_buy_old', - 'type': 'limit', - 'side': 'buy', - 'symbol': 'mocked', - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'price': 0.00001099, - 'amount': 90.99181073, - 'filled': 0.0, - 'remaining': 90.99181073, - 'status': 'open' + "id": "mocked_limit_buy_old", + "type": "limit", + "side": "buy", + "symbol": "mocked", + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "price": 0.00001099, + "amount": 90.99181073, + "filled": 0.0, + "remaining": 90.99181073, + "status": "open", } @pytest.fixture def limit_sell_order_old(): return { - 'id': 'mocked_limit_sell_old', - 'type': 'limit', - 'side': 'sell', - 'symbol': 'ETH/BTC', - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'price': 0.00001099, - 'amount': 90.99181073, - 'filled': 0.0, - 'remaining': 90.99181073, - 'status': 'open' + "id": "mocked_limit_sell_old", + "type": "limit", + "side": "sell", + "symbol": "ETH/BTC", + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "price": 0.00001099, + "amount": 90.99181073, + "filled": 0.0, + "remaining": 90.99181073, + "status": "open", } @pytest.fixture def limit_buy_order_old_partial(): return { - 'id': 'mocked_limit_buy_old_partial', - 'type': 'limit', - 'side': 'buy', - 'symbol': 'ETH/BTC', - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'price': 0.00001099, - 'amount': 90.99181073, - 'filled': 23.0, - 'cost': 90.99181073 * 23.0, - 'remaining': 67.99181073, - 'status': 'open' + "id": "mocked_limit_buy_old_partial", + "type": "limit", + "side": "buy", + "symbol": "ETH/BTC", + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "price": 0.00001099, + "amount": 90.99181073, + "filled": 23.0, + "cost": 90.99181073 * 23.0, + "remaining": 67.99181073, + "status": "open", } @pytest.fixture def limit_buy_order_old_partial_canceled(limit_buy_order_old_partial): res = deepcopy(limit_buy_order_old_partial) - res['status'] = 'canceled' - res['fee'] = {'cost': 0.023, 'currency': 'ETH'} + res["status"] = "canceled" + res["fee"] = {"cost": 0.023, "currency": "ETH"} return res -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_buy_order_canceled_empty(request): # Indirect fixture # Documentation: # https://docs.pytest.org/en/latest/example/parametrize.html#apply-indirect-on-particular-arguments exchange_name = request.param - if exchange_name == 'kraken': + if exchange_name == "kraken": return { - 'info': {}, - 'id': 'AZNPFF-4AC4N-7MKTAT', - 'clientOrderId': None, - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'lastTradeTimestamp': None, - 'status': 'canceled', - 'symbol': 'LTC/USDT', - 'type': 'limit', - 'side': 'buy', - 'price': 34.3225, - 'cost': 0.0, - 'amount': 0.55, - 'filled': 0.0, - 'average': 0.0, - 'remaining': 0.55, - 'fee': {'cost': 0.0, 'rate': None, 'currency': 'USDT'}, - 'trades': [] + "info": {}, + "id": "AZNPFF-4AC4N-7MKTAT", + "clientOrderId": None, + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "lastTradeTimestamp": None, + "status": "canceled", + "symbol": "LTC/USDT", + "type": "limit", + "side": "buy", + "price": 34.3225, + "cost": 0.0, + "amount": 0.55, + "filled": 0.0, + "average": 0.0, + "remaining": 0.55, + "fee": {"cost": 0.0, "rate": None, "currency": "USDT"}, + "trades": [], } - elif exchange_name == 'binance': + elif exchange_name == "binance": return { - 'info': {}, - 'id': '1234512345', - 'clientOrderId': 'alb1234123', - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'lastTradeTimestamp': None, - 'symbol': 'LTC/USDT', - 'type': 'limit', - 'side': 'buy', - 'price': 0.016804, - 'amount': 0.55, - 'cost': 0.0, - 'average': None, - 'filled': 0.0, - 'remaining': 0.55, - 'status': 'canceled', - 'fee': None, - 'trades': None + "info": {}, + "id": "1234512345", + "clientOrderId": "alb1234123", + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "lastTradeTimestamp": None, + "symbol": "LTC/USDT", + "type": "limit", + "side": "buy", + "price": 0.016804, + "amount": 0.55, + "cost": 0.0, + "average": None, + "filled": 0.0, + "remaining": 0.55, + "status": "canceled", + "fee": None, + "trades": None, } else: return { - 'info': {}, - 'id': '1234512345', - 'clientOrderId': 'alb1234123', - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'lastTradeTimestamp': None, - 'symbol': 'LTC/USDT', - 'type': 'limit', - 'side': 'buy', - 'price': 0.016804, - 'amount': 0.55, - 'cost': 0.0, - 'average': None, - 'filled': 0.0, - 'remaining': 0.55, - 'status': 'canceled', - 'fee': None, - 'trades': None + "info": {}, + "id": "1234512345", + "clientOrderId": "alb1234123", + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "lastTradeTimestamp": None, + "symbol": "LTC/USDT", + "type": "limit", + "side": "buy", + "price": 0.016804, + "amount": 0.55, + "cost": 0.0, + "average": None, + "filled": 0.0, + "remaining": 0.55, + "status": "canceled", + "fee": None, + "trades": None, } @pytest.fixture def limit_sell_order_open(): return { - 'id': 'mocked_limit_sell', - 'type': 'limit', - 'side': 'sell', - 'symbol': 'mocked', - 'datetime': dt_now().isoformat(), - 'timestamp': dt_ts(), - 'price': 0.00001173, - 'amount': 90.99181073, - 'filled': 0.0, - 'remaining': 90.99181073, - 'status': 'open' + "id": "mocked_limit_sell", + "type": "limit", + "side": "sell", + "symbol": "mocked", + "datetime": dt_now().isoformat(), + "timestamp": dt_ts(), + "price": 0.00001173, + "amount": 90.99181073, + "filled": 0.0, + "remaining": 90.99181073, + "status": "open", } @pytest.fixture def limit_sell_order(limit_sell_order_open): order = deepcopy(limit_sell_order_open) - order['remaining'] = 0.0 - order['filled'] = order['amount'] - order['status'] = 'closed' + order["remaining"] = 0.0 + order["filled"] = order["amount"] + order["status"] = "closed" return order @pytest.fixture def order_book_l2(): - return MagicMock(return_value={ - 'bids': [ - [0.043936, 10.442], - [0.043935, 31.865], - [0.043933, 11.212], - [0.043928, 0.088], - [0.043925, 10.0], - [0.043921, 10.0], - [0.04392, 37.64], - [0.043899, 0.066], - [0.043885, 0.676], - [0.04387, 22.758] - ], - 'asks': [ - [0.043949, 0.346], - [0.04395, 0.608], - [0.043951, 3.948], - [0.043954, 0.288], - [0.043958, 9.277], - [0.043995, 1.566], - [0.044, 0.588], - [0.044002, 0.992], - [0.044003, 0.095], - [0.04402, 37.64] - ], - 'timestamp': None, - 'datetime': None, - 'nonce': 288004540 - }) + return MagicMock( + return_value={ + "bids": [ + [0.043936, 10.442], + [0.043935, 31.865], + [0.043933, 11.212], + [0.043928, 0.088], + [0.043925, 10.0], + [0.043921, 10.0], + [0.04392, 37.64], + [0.043899, 0.066], + [0.043885, 0.676], + [0.04387, 22.758], + ], + "asks": [ + [0.043949, 0.346], + [0.04395, 0.608], + [0.043951, 3.948], + [0.043954, 0.288], + [0.043958, 9.277], + [0.043995, 1.566], + [0.044, 0.588], + [0.044002, 0.992], + [0.044003, 0.095], + [0.04402, 37.64], + ], + "timestamp": None, + "datetime": None, + "nonce": 288004540, + } + ) @pytest.fixture def order_book_l2_usd(): - return MagicMock(return_value={ - 'symbol': 'LTC/USDT', - 'bids': [ - [25.563, 49.269], - [25.562, 83.0], - [25.56, 106.0], - [25.559, 15.381], - [25.558, 29.299], - [25.557, 34.624], - [25.556, 10.0], - [25.555, 14.684], - [25.554, 45.91], - [25.553, 50.0] - ], - 'asks': [ - [25.566, 14.27], - [25.567, 48.484], - [25.568, 92.349], - [25.572, 31.48], - [25.573, 23.0], - [25.574, 20.0], - [25.575, 89.606], - [25.576, 262.016], - [25.577, 178.557], - [25.578, 78.614] - ], - 'timestamp': None, - 'datetime': None, - 'nonce': 2372149736 - }) + return MagicMock( + return_value={ + "symbol": "LTC/USDT", + "bids": [ + [25.563, 49.269], + [25.562, 83.0], + [25.56, 106.0], + [25.559, 15.381], + [25.558, 29.299], + [25.557, 34.624], + [25.556, 10.0], + [25.555, 14.684], + [25.554, 45.91], + [25.553, 50.0], + ], + "asks": [ + [25.566, 14.27], + [25.567, 48.484], + [25.568, 92.349], + [25.572, 31.48], + [25.573, 23.0], + [25.574, 20.0], + [25.575, 89.606], + [25.576, 262.016], + [25.577, 178.557], + [25.578, 78.614], + ], + "timestamp": None, + "datetime": None, + "nonce": 2372149736, + } + ) @pytest.fixture @@ -2055,11 +1968,11 @@ def ohlcv_history_list(): return [ [ 1511686200000, # unix timestamp ms - 8.794e-05, # open - 8.948e-05, # high - 8.794e-05, # low - 8.88e-05, # close - 0.0877869, # volume (in quote currency) + 8.794e-05, # open + 8.948e-05, # high + 8.794e-05, # low + 8.88e-05, # close + 0.0877869, # volume (in quote currency) ], [ 1511686500000, @@ -2069,557 +1982,581 @@ def ohlcv_history_list(): 8.893e-05, 0.05874751, ], - [ - 1511686800000, - 8.891e-05, - 8.893e-05, - 8.875e-05, - 8.877e-05, - 0.7039405 - ] + [1511686800000, 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05, 0.7039405], ] @pytest.fixture def ohlcv_history(ohlcv_history_list): - return ohlcv_to_dataframe(ohlcv_history_list, "5m", pair="UNITTEST/BTC", - fill_missing=True, drop_incomplete=False) + return ohlcv_to_dataframe( + ohlcv_history_list, "5m", pair="UNITTEST/BTC", fill_missing=True, drop_incomplete=False + ) @pytest.fixture def tickers(): - return MagicMock(return_value={ - 'ETH/BTC': { - 'symbol': 'ETH/BTC', - 'timestamp': 1522014806207, - 'datetime': '2018-03-25T21:53:26.207Z', - 'high': 0.061697, - 'low': 0.060531, - 'bid': 0.061588, - 'bidVolume': 3.321, - 'ask': 0.061655, - 'askVolume': 0.212, - 'vwap': 0.06105296, - 'open': 0.060809, - 'close': 0.060761, - 'first': None, - 'last': 0.061588, - 'change': 1.281, - 'percentage': None, - 'average': None, - 'baseVolume': 111649.001, - 'quoteVolume': 6816.50176926, - 'info': {} - }, - 'TKN/BTC': { - 'symbol': 'TKN/BTC', - 'timestamp': 1522014806169, - 'datetime': '2018-03-25T21:53:26.169Z', - 'high': 0.01885, - 'low': 0.018497, - 'bid': 0.018799, - 'bidVolume': 8.38, - 'ask': 0.018802, - 'askVolume': 15.0, - 'vwap': 0.01869197, - 'open': 0.018585, - 'close': 0.018573, - 'last': 0.018799, - 'baseVolume': 81058.66, - 'quoteVolume': 2247.48374509, - }, - 'BLK/BTC': { - 'symbol': 'BLK/BTC', - 'timestamp': 1522014806072, - 'datetime': '2018-03-25T21:53:26.072Z', - 'high': 0.007745, - 'low': 0.007512, - 'bid': 0.007729, - 'bidVolume': 0.01, - 'ask': 0.007743, - 'askVolume': 21.37, - 'vwap': 0.00761466, - 'open': 0.007653, - 'close': 0.007652, - 'first': None, - 'last': 0.007743, - 'change': 1.176, - 'percentage': None, - 'average': None, - 'baseVolume': 295152.26, - 'quoteVolume': 1515.14631229, - 'info': {} - }, - 'LTC/BTC': { - 'symbol': 'LTC/BTC', - 'timestamp': 1523787258992, - 'datetime': '2018-04-15T10:14:19.992Z', - 'high': 0.015978, - 'low': 0.0157, - 'bid': 0.015954, - 'bidVolume': 12.83, - 'ask': 0.015957, - 'askVolume': 0.49, - 'vwap': 0.01581636, - 'open': 0.015823, - 'close': 0.01582, - 'first': None, - 'last': 0.015951, - 'change': 0.809, - 'percentage': None, - 'average': None, - 'baseVolume': 88620.68, - 'quoteVolume': 1401.65697943, - 'info': {} - }, - 'BTT/BTC': { - 'symbol': 'BTT/BTC', - 'timestamp': 1550936557206, - 'datetime': '2019-02-23T15:42:37.206Z', - 'high': 0.00000026, - 'low': 0.00000024, - 'bid': 0.00000024, - 'bidVolume': 2446894197.0, - 'ask': 0.00000025, - 'askVolume': 2447913837.0, - 'vwap': 0.00000025, - 'open': 0.00000026, - 'close': 0.00000024, - 'last': 0.00000024, - 'previousClose': 0.00000026, - 'change': -0.00000002, - 'percentage': -7.692, - 'average': None, - 'baseVolume': 4886464537.0, - 'quoteVolume': 1215.14489611, - 'info': {} - }, - 'HOT/BTC': { - 'symbol': 'HOT/BTC', - 'timestamp': 1572273518661, - 'datetime': '2019-10-28T14:38:38.661Z', - 'high': 0.00000011, - 'low': 0.00000009, - 'bid': 0.0000001, - 'bidVolume': 1476027288.0, - 'ask': 0.00000011, - 'askVolume': 820153831.0, - 'vwap': 0.0000001, - 'open': 0.00000009, - 'close': 0.00000011, - 'last': 0.00000011, - 'previousClose': 0.00000009, - 'change': 0.00000002, - 'percentage': 22.222, - 'average': None, - 'baseVolume': 1442290324.0, - 'quoteVolume': 143.78311994, - 'info': {} - }, - 'FUEL/BTC': { - 'symbol': 'FUEL/BTC', - 'timestamp': 1572340250771, - 'datetime': '2019-10-29T09:10:50.771Z', - 'high': 0.00000040, - 'low': 0.00000035, - 'bid': 0.00000036, - 'bidVolume': 8932318.0, - 'ask': 0.00000037, - 'askVolume': 10140774.0, - 'vwap': 0.00000037, - 'open': 0.00000039, - 'close': 0.00000037, - 'last': 0.00000037, - 'previousClose': 0.00000038, - 'change': -0.00000002, - 'percentage': -5.128, - 'average': None, - 'baseVolume': 168927742.0, - 'quoteVolume': 62.68220262, - 'info': {} - }, - 'BTC/USDT': { - 'symbol': 'BTC/USDT', - 'timestamp': 1573758371399, - 'datetime': '2019-11-14T19:06:11.399Z', - 'high': 8800.0, - 'low': 8582.6, - 'bid': 8648.16, - 'bidVolume': 0.238771, - 'ask': 8648.72, - 'askVolume': 0.016253, - 'vwap': 8683.13647806, - 'open': 8759.7, - 'close': 8648.72, - 'last': 8648.72, - 'previousClose': 8759.67, - 'change': -110.98, - 'percentage': -1.267, - 'average': None, - 'baseVolume': 35025.943355, - 'quoteVolume': 304135046.4242901, - 'info': {} - }, - 'ETH/USDT': { - 'symbol': 'ETH/USDT', - 'timestamp': 1522014804118, - 'datetime': '2018-03-25T21:53:24.118Z', - 'high': 530.88, - 'low': 512.0, - 'bid': 529.73, - 'bidVolume': 0.2, - 'ask': 530.21, - 'askVolume': 0.2464, - 'vwap': 521.02438405, - 'open': 527.27, - 'close': 528.42, - 'first': None, - 'last': 530.21, - 'change': 0.558, - 'percentage': None, - 'average': None, - 'baseVolume': 72300.0659, - 'quoteVolume': 37670097.3022171, - 'info': {} - }, - 'TKN/USDT': { - 'symbol': 'TKN/USDT', - 'timestamp': 1522014806198, - 'datetime': '2018-03-25T21:53:26.198Z', - 'high': 8718.0, - 'low': 8365.77, - 'bid': 8603.64, - 'bidVolume': 0.15846, - 'ask': 8603.67, - 'askVolume': 0.069147, - 'vwap': 8536.35621697, - 'open': 8680.0, - 'close': 8680.0, - 'first': None, - 'last': 8603.67, - 'change': -0.879, - 'percentage': None, - 'average': None, - 'baseVolume': 30414.604298, - 'quoteVolume': 259629896.48584127, - 'info': {} - }, - 'BLK/USDT': { - 'symbol': 'BLK/USDT', - 'timestamp': 1522014806145, - 'datetime': '2018-03-25T21:53:26.145Z', - 'high': 66.95, - 'low': 63.38, - 'bid': 66.473, - 'bidVolume': 4.968, - 'ask': 66.54, - 'askVolume': 2.704, - 'vwap': 65.0526901, - 'open': 66.43, - 'close': 66.383, - 'first': None, - 'last': 66.5, - 'change': 0.105, - 'percentage': None, - 'average': None, - 'baseVolume': 294106.204, - 'quoteVolume': 19132399.743954, - 'info': {} - }, - 'LTC/USDT': { - 'symbol': 'LTC/USDT', - 'timestamp': 1523787257812, - 'datetime': '2018-04-15T10:14:18.812Z', - 'high': 129.94, - 'low': 124.0, - 'bid': 129.28, - 'bidVolume': 0.03201, - 'ask': 129.52, - 'askVolume': 0.14529, - 'vwap': 126.92838682, - 'open': 127.0, - 'close': 127.1, - 'first': None, - 'last': 129.28, - 'change': 1.795, - 'percentage': None, - 'average': None, - 'baseVolume': 59698.79897, - 'quoteVolume': 29132399.743954, - 'info': {} - }, - 'XRP/BTC': { - 'symbol': 'XRP/BTC', - 'timestamp': 1573758257534, - 'datetime': '2019-11-14T19:04:17.534Z', - 'high': 3.126e-05, - 'low': 3.061e-05, - 'bid': 3.093e-05, - 'bidVolume': 27901.0, - 'ask': 3.095e-05, - 'askVolume': 10551.0, - 'vwap': 3.091e-05, - 'open': 3.119e-05, - 'close': 3.094e-05, - 'last': 3.094e-05, - 'previousClose': 3.117e-05, - 'change': -2.5e-07, - 'percentage': -0.802, - 'average': None, - 'baseVolume': 37334921.0, - 'quoteVolume': 1154.19266394, - 'info': {} - }, - "NANO/USDT": { - "symbol": "NANO/USDT", - "timestamp": 1580469388244, - "datetime": "2020-01-31T11:16:28.244Z", - "high": 0.7519, - "low": 0.7154, - "bid": 0.7305, - "bidVolume": 300.3, - "ask": 0.7342, - "askVolume": 15.14, - "vwap": 0.73645591, - "open": 0.7154, - "close": 0.7342, - "last": 0.7342, - "previousClose": 0.7189, - "change": 0.0188, - "percentage": 2.628, - "average": None, - "baseVolume": 439472.44, - "quoteVolume": 323652.075405, - "info": {} - }, - # Example of leveraged pair with incomplete info - "ADAHALF/USDT": { - "symbol": "ADAHALF/USDT", - "timestamp": 1580469388244, - "datetime": "2020-01-31T11:16:28.244Z", - "high": None, - "low": None, - "bid": 0.7305, - "bidVolume": None, - "ask": 0.7342, - "askVolume": None, - "vwap": None, - "open": None, - "close": None, - "last": None, - "previousClose": None, - "change": None, - "percentage": 2.628, - "average": None, - "baseVolume": 0.0, - "quoteVolume": 0.0, - "info": {} - }, - "ADADOUBLE/USDT": { - "symbol": "ADADOUBLE/USDT", - "timestamp": 1580469388244, - "datetime": "2020-01-31T11:16:28.244Z", - "high": None, - "low": None, - "bid": 0.7305, - "bidVolume": None, - "ask": 0.7342, - "askVolume": None, - "vwap": None, - "open": None, - "close": None, - "last": 0, - "previousClose": None, - "change": None, - "percentage": 2.628, - "average": None, - "baseVolume": 0.0, - "quoteVolume": 0.0, - "info": {} - }, - }) + return MagicMock( + return_value={ + "ETH/BTC": { + "symbol": "ETH/BTC", + "timestamp": 1522014806207, + "datetime": "2018-03-25T21:53:26.207Z", + "high": 0.061697, + "low": 0.060531, + "bid": 0.061588, + "bidVolume": 3.321, + "ask": 0.061655, + "askVolume": 0.212, + "vwap": 0.06105296, + "open": 0.060809, + "close": 0.060761, + "first": None, + "last": 0.061588, + "change": 1.281, + "percentage": None, + "average": None, + "baseVolume": 111649.001, + "quoteVolume": 6816.50176926, + "info": {}, + }, + "TKN/BTC": { + "symbol": "TKN/BTC", + "timestamp": 1522014806169, + "datetime": "2018-03-25T21:53:26.169Z", + "high": 0.01885, + "low": 0.018497, + "bid": 0.018799, + "bidVolume": 8.38, + "ask": 0.018802, + "askVolume": 15.0, + "vwap": 0.01869197, + "open": 0.018585, + "close": 0.018573, + "last": 0.018799, + "baseVolume": 81058.66, + "quoteVolume": 2247.48374509, + }, + "BLK/BTC": { + "symbol": "BLK/BTC", + "timestamp": 1522014806072, + "datetime": "2018-03-25T21:53:26.072Z", + "high": 0.007745, + "low": 0.007512, + "bid": 0.007729, + "bidVolume": 0.01, + "ask": 0.007743, + "askVolume": 21.37, + "vwap": 0.00761466, + "open": 0.007653, + "close": 0.007652, + "first": None, + "last": 0.007743, + "change": 1.176, + "percentage": None, + "average": None, + "baseVolume": 295152.26, + "quoteVolume": 1515.14631229, + "info": {}, + }, + "LTC/BTC": { + "symbol": "LTC/BTC", + "timestamp": 1523787258992, + "datetime": "2018-04-15T10:14:19.992Z", + "high": 0.015978, + "low": 0.0157, + "bid": 0.015954, + "bidVolume": 12.83, + "ask": 0.015957, + "askVolume": 0.49, + "vwap": 0.01581636, + "open": 0.015823, + "close": 0.01582, + "first": None, + "last": 0.015951, + "change": 0.809, + "percentage": None, + "average": None, + "baseVolume": 88620.68, + "quoteVolume": 1401.65697943, + "info": {}, + }, + "BTT/BTC": { + "symbol": "BTT/BTC", + "timestamp": 1550936557206, + "datetime": "2019-02-23T15:42:37.206Z", + "high": 0.00000026, + "low": 0.00000024, + "bid": 0.00000024, + "bidVolume": 2446894197.0, + "ask": 0.00000025, + "askVolume": 2447913837.0, + "vwap": 0.00000025, + "open": 0.00000026, + "close": 0.00000024, + "last": 0.00000024, + "previousClose": 0.00000026, + "change": -0.00000002, + "percentage": -7.692, + "average": None, + "baseVolume": 4886464537.0, + "quoteVolume": 1215.14489611, + "info": {}, + }, + "HOT/BTC": { + "symbol": "HOT/BTC", + "timestamp": 1572273518661, + "datetime": "2019-10-28T14:38:38.661Z", + "high": 0.00000011, + "low": 0.00000009, + "bid": 0.0000001, + "bidVolume": 1476027288.0, + "ask": 0.00000011, + "askVolume": 820153831.0, + "vwap": 0.0000001, + "open": 0.00000009, + "close": 0.00000011, + "last": 0.00000011, + "previousClose": 0.00000009, + "change": 0.00000002, + "percentage": 22.222, + "average": None, + "baseVolume": 1442290324.0, + "quoteVolume": 143.78311994, + "info": {}, + }, + "FUEL/BTC": { + "symbol": "FUEL/BTC", + "timestamp": 1572340250771, + "datetime": "2019-10-29T09:10:50.771Z", + "high": 0.00000040, + "low": 0.00000035, + "bid": 0.00000036, + "bidVolume": 8932318.0, + "ask": 0.00000037, + "askVolume": 10140774.0, + "vwap": 0.00000037, + "open": 0.00000039, + "close": 0.00000037, + "last": 0.00000037, + "previousClose": 0.00000038, + "change": -0.00000002, + "percentage": -5.128, + "average": None, + "baseVolume": 168927742.0, + "quoteVolume": 62.68220262, + "info": {}, + }, + "BTC/USDT": { + "symbol": "BTC/USDT", + "timestamp": 1573758371399, + "datetime": "2019-11-14T19:06:11.399Z", + "high": 8800.0, + "low": 8582.6, + "bid": 8648.16, + "bidVolume": 0.238771, + "ask": 8648.72, + "askVolume": 0.016253, + "vwap": 8683.13647806, + "open": 8759.7, + "close": 8648.72, + "last": 8648.72, + "previousClose": 8759.67, + "change": -110.98, + "percentage": -1.267, + "average": None, + "baseVolume": 35025.943355, + "quoteVolume": 304135046.4242901, + "info": {}, + }, + "ETH/USDT": { + "symbol": "ETH/USDT", + "timestamp": 1522014804118, + "datetime": "2018-03-25T21:53:24.118Z", + "high": 530.88, + "low": 512.0, + "bid": 529.73, + "bidVolume": 0.2, + "ask": 530.21, + "askVolume": 0.2464, + "vwap": 521.02438405, + "open": 527.27, + "close": 528.42, + "first": None, + "last": 530.21, + "change": 0.558, + "percentage": None, + "average": None, + "baseVolume": 72300.0659, + "quoteVolume": 37670097.3022171, + "info": {}, + }, + "TKN/USDT": { + "symbol": "TKN/USDT", + "timestamp": 1522014806198, + "datetime": "2018-03-25T21:53:26.198Z", + "high": 8718.0, + "low": 8365.77, + "bid": 8603.64, + "bidVolume": 0.15846, + "ask": 8603.67, + "askVolume": 0.069147, + "vwap": 8536.35621697, + "open": 8680.0, + "close": 8680.0, + "first": None, + "last": 8603.67, + "change": -0.879, + "percentage": None, + "average": None, + "baseVolume": 30414.604298, + "quoteVolume": 259629896.48584127, + "info": {}, + }, + "BLK/USDT": { + "symbol": "BLK/USDT", + "timestamp": 1522014806145, + "datetime": "2018-03-25T21:53:26.145Z", + "high": 66.95, + "low": 63.38, + "bid": 66.473, + "bidVolume": 4.968, + "ask": 66.54, + "askVolume": 2.704, + "vwap": 65.0526901, + "open": 66.43, + "close": 66.383, + "first": None, + "last": 66.5, + "change": 0.105, + "percentage": None, + "average": None, + "baseVolume": 294106.204, + "quoteVolume": 19132399.743954, + "info": {}, + }, + "LTC/USDT": { + "symbol": "LTC/USDT", + "timestamp": 1523787257812, + "datetime": "2018-04-15T10:14:18.812Z", + "high": 129.94, + "low": 124.0, + "bid": 129.28, + "bidVolume": 0.03201, + "ask": 129.52, + "askVolume": 0.14529, + "vwap": 126.92838682, + "open": 127.0, + "close": 127.1, + "first": None, + "last": 129.28, + "change": 1.795, + "percentage": None, + "average": None, + "baseVolume": 59698.79897, + "quoteVolume": 29132399.743954, + "info": {}, + }, + "XRP/BTC": { + "symbol": "XRP/BTC", + "timestamp": 1573758257534, + "datetime": "2019-11-14T19:04:17.534Z", + "high": 3.126e-05, + "low": 3.061e-05, + "bid": 3.093e-05, + "bidVolume": 27901.0, + "ask": 3.095e-05, + "askVolume": 10551.0, + "vwap": 3.091e-05, + "open": 3.119e-05, + "close": 3.094e-05, + "last": 3.094e-05, + "previousClose": 3.117e-05, + "change": -2.5e-07, + "percentage": -0.802, + "average": None, + "baseVolume": 37334921.0, + "quoteVolume": 1154.19266394, + "info": {}, + }, + "NANO/USDT": { + "symbol": "NANO/USDT", + "timestamp": 1580469388244, + "datetime": "2020-01-31T11:16:28.244Z", + "high": 0.7519, + "low": 0.7154, + "bid": 0.7305, + "bidVolume": 300.3, + "ask": 0.7342, + "askVolume": 15.14, + "vwap": 0.73645591, + "open": 0.7154, + "close": 0.7342, + "last": 0.7342, + "previousClose": 0.7189, + "change": 0.0188, + "percentage": 2.628, + "average": None, + "baseVolume": 439472.44, + "quoteVolume": 323652.075405, + "info": {}, + }, + # Example of leveraged pair with incomplete info + "ADAHALF/USDT": { + "symbol": "ADAHALF/USDT", + "timestamp": 1580469388244, + "datetime": "2020-01-31T11:16:28.244Z", + "high": None, + "low": None, + "bid": 0.7305, + "bidVolume": None, + "ask": 0.7342, + "askVolume": None, + "vwap": None, + "open": None, + "close": None, + "last": None, + "previousClose": None, + "change": None, + "percentage": 2.628, + "average": None, + "baseVolume": 0.0, + "quoteVolume": 0.0, + "info": {}, + }, + "ADADOUBLE/USDT": { + "symbol": "ADADOUBLE/USDT", + "timestamp": 1580469388244, + "datetime": "2020-01-31T11:16:28.244Z", + "high": None, + "low": None, + "bid": 0.7305, + "bidVolume": None, + "ask": 0.7342, + "askVolume": None, + "vwap": None, + "open": None, + "close": None, + "last": 0, + "previousClose": None, + "change": None, + "percentage": 2.628, + "average": None, + "baseVolume": 0.0, + "quoteVolume": 0.0, + "info": {}, + }, + } + ) @pytest.fixture def dataframe_1m(testdatadir): - with (testdatadir / 'UNITTEST_BTC-1m.json').open('r') as data_file: - return ohlcv_to_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC", - fill_missing=True) + with (testdatadir / "UNITTEST_BTC-1m.json").open("r") as data_file: + return ohlcv_to_dataframe( + json.load(data_file), "1m", pair="UNITTEST/BTC", fill_missing=True + ) @pytest.fixture(scope="function") def trades_for_order(): - return [{ - 'info': { - 'id': 34567, - 'orderId': 123456, - 'price': '2.0', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True - }, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/USDT', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 2.0, - 'cost': 16.0, - 'amount': 8.0, - 'fee': { - 'cost': 0.008, - 'currency': 'LTC' + return [ + { + "info": { + "id": 34567, + "orderId": 123456, + "price": "2.0", + "qty": "8.00000000", + "commission": "0.00800000", + "commissionAsset": "LTC", + "time": 1521663363189, + "isBuyer": True, + "isMaker": False, + "isBestMatch": True, + }, + "timestamp": 1521663363189, + "datetime": "2018-03-21T20:16:03.189Z", + "symbol": "LTC/USDT", + "id": "34567", + "order": "123456", + "type": None, + "side": "buy", + "price": 2.0, + "cost": 16.0, + "amount": 8.0, + "fee": {"cost": 0.008, "currency": "LTC"}, } - }] + ] @pytest.fixture(scope="function") def trades_history(): - return [[1565798389463, '12618132aa9', None, 'buy', 0.019627, 0.04, 0.00078508], - [1565798399629, '1261813bb30', None, 'buy', 0.019627, 0.244, 0.004788987999999999], - [1565798399752, '1261813cc31', None, 'sell', 0.019626, 0.011, 0.00021588599999999999], - [1565798399862, '126181cc332', None, 'sell', 0.019626, 0.011, 0.00021588599999999999], - [1565798399862, '126181cc333', None, 'sell', 0.019626, 0.012, 0.00021588599999999999], - [1565798399872, '1261aa81334', None, 'sell', 0.019626, 0.011, 0.00021588599999999999]] + return [ + [1565798389463, "12618132aa9", None, "buy", 0.019627, 0.04, 0.00078508], + [1565798399629, "1261813bb30", None, "buy", 0.019627, 0.244, 0.004788987999999999], + [1565798399752, "1261813cc31", None, "sell", 0.019626, 0.011, 0.00021588599999999999], + [1565798399862, "126181cc332", None, "sell", 0.019626, 0.011, 0.00021588599999999999], + [1565798399862, "126181cc333", None, "sell", 0.019626, 0.012, 0.00021588599999999999], + [1565798399872, "1261aa81334", None, "sell", 0.019626, 0.011, 0.00021588599999999999], + ] @pytest.fixture(scope="function") def trades_history_df(trades_history): trades = trades_list_to_df(trades_history) - trades['date'] = pd.to_datetime(trades['timestamp'], unit='ms', utc=True) + trades["date"] = pd.to_datetime(trades["timestamp"], unit="ms", utc=True) return trades @pytest.fixture(scope="function") def fetch_trades_result(): - return [{'info': ['0.01962700', '0.04000000', '1565798399.4631551', 'b', 'm', '', '126181329'], - 'timestamp': 1565798399463, - 'datetime': '2019-08-14T15:59:59.463Z', - 'symbol': 'ETH/BTC', - 'id': '126181329', - 'order': None, - 'type': None, - 'takerOrMaker': None, - 'side': 'buy', - 'price': 0.019627, - 'amount': 0.04, - 'cost': 0.00078508, - 'fee': None}, - {'info': ['0.01962700', '0.24400000', '1565798399.6291551', 'b', 'm', '', '126181330'], - 'timestamp': 1565798399629, - 'datetime': '2019-08-14T15:59:59.629Z', - 'symbol': 'ETH/BTC', - 'id': '126181330', - 'order': None, - 'type': None, - 'takerOrMaker': None, - 'side': 'buy', - 'price': 0.019627, - 'amount': 0.244, - 'cost': 0.004788987999999999, - 'fee': None}, - {'info': ['0.01962600', '0.01100000', '1565798399.7521551', 's', 'm', '', '126181331'], - 'timestamp': 1565798399752, - 'datetime': '2019-08-14T15:59:59.752Z', - 'symbol': 'ETH/BTC', - 'id': '126181331', - 'order': None, - 'type': None, - 'takerOrMaker': None, - 'side': 'sell', - 'price': 0.019626, - 'amount': 0.011, - 'cost': 0.00021588599999999999, - 'fee': None}, - {'info': ['0.01962600', '0.01100000', '1565798399.8621551', 's', 'm', '', '126181332'], - 'timestamp': 1565798399862, - 'datetime': '2019-08-14T15:59:59.862Z', - 'symbol': 'ETH/BTC', - 'id': '126181332', - 'order': None, - 'type': None, - 'takerOrMaker': None, - 'side': 'sell', - 'price': 0.019626, - 'amount': 0.011, - 'cost': 0.00021588599999999999, - 'fee': None}, - {'info': ['0.01952600', '0.01200000', '1565798399.8721551', 's', 'm', '', '126181333', - 1565798399872512133], - 'timestamp': 1565798399872, - 'datetime': '2019-08-14T15:59:59.872Z', - 'symbol': 'ETH/BTC', - 'id': '126181333', - 'order': None, - 'type': None, - 'takerOrMaker': None, - 'side': 'sell', - 'price': 0.019626, - 'amount': 0.011, - 'cost': 0.00021588599999999999, - 'fee': None}] + return [ + { + "info": ["0.01962700", "0.04000000", "1565798399.4631551", "b", "m", "", "126181329"], + "timestamp": 1565798399463, + "datetime": "2019-08-14T15:59:59.463Z", + "symbol": "ETH/BTC", + "id": "126181329", + "order": None, + "type": None, + "takerOrMaker": None, + "side": "buy", + "price": 0.019627, + "amount": 0.04, + "cost": 0.00078508, + "fee": None, + }, + { + "info": ["0.01962700", "0.24400000", "1565798399.6291551", "b", "m", "", "126181330"], + "timestamp": 1565798399629, + "datetime": "2019-08-14T15:59:59.629Z", + "symbol": "ETH/BTC", + "id": "126181330", + "order": None, + "type": None, + "takerOrMaker": None, + "side": "buy", + "price": 0.019627, + "amount": 0.244, + "cost": 0.004788987999999999, + "fee": None, + }, + { + "info": ["0.01962600", "0.01100000", "1565798399.7521551", "s", "m", "", "126181331"], + "timestamp": 1565798399752, + "datetime": "2019-08-14T15:59:59.752Z", + "symbol": "ETH/BTC", + "id": "126181331", + "order": None, + "type": None, + "takerOrMaker": None, + "side": "sell", + "price": 0.019626, + "amount": 0.011, + "cost": 0.00021588599999999999, + "fee": None, + }, + { + "info": ["0.01962600", "0.01100000", "1565798399.8621551", "s", "m", "", "126181332"], + "timestamp": 1565798399862, + "datetime": "2019-08-14T15:59:59.862Z", + "symbol": "ETH/BTC", + "id": "126181332", + "order": None, + "type": None, + "takerOrMaker": None, + "side": "sell", + "price": 0.019626, + "amount": 0.011, + "cost": 0.00021588599999999999, + "fee": None, + }, + { + "info": [ + "0.01952600", + "0.01200000", + "1565798399.8721551", + "s", + "m", + "", + "126181333", + 1565798399872512133, + ], + "timestamp": 1565798399872, + "datetime": "2019-08-14T15:59:59.872Z", + "symbol": "ETH/BTC", + "id": "126181333", + "order": None, + "type": None, + "takerOrMaker": None, + "side": "sell", + "price": 0.019626, + "amount": 0.011, + "cost": 0.00021588599999999999, + "fee": None, + }, + ] @pytest.fixture(scope="function") def trades_for_order2(): - return [{'info': {}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 4.0, - 'fee': {'cost': 0.004, 'currency': 'LTC'}}, - {'info': {}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 4.0, - 'fee': {'cost': 0.004, 'currency': 'LTC'}}] + return [ + { + "info": {}, + "timestamp": 1521663363189, + "datetime": "2018-03-21T20:16:03.189Z", + "symbol": "LTC/ETH", + "id": "34567", + "order": "123456", + "type": None, + "side": "buy", + "price": 0.245441, + "cost": 1.963528, + "amount": 4.0, + "fee": {"cost": 0.004, "currency": "LTC"}, + }, + { + "info": {}, + "timestamp": 1521663363189, + "datetime": "2018-03-21T20:16:03.189Z", + "symbol": "LTC/ETH", + "id": "34567", + "order": "123456", + "type": None, + "side": "buy", + "price": 0.245441, + "cost": 1.963528, + "amount": 4.0, + "fee": {"cost": 0.004, "currency": "LTC"}, + }, + ] @pytest.fixture def buy_order_fee(): return { - 'id': 'mocked_limit_buy_old', - 'type': 'limit', - 'side': 'buy', - 'symbol': 'mocked', - 'timestamp': dt_ts(dt_now() - timedelta(minutes=601)), - 'datetime': (dt_now() - timedelta(minutes=601)).isoformat(), - 'price': 0.245441, - 'amount': 8.0, - 'cost': 1.963528, - 'remaining': 90.99181073, - 'status': 'closed', - 'fee': None + "id": "mocked_limit_buy_old", + "type": "limit", + "side": "buy", + "symbol": "mocked", + "timestamp": dt_ts(dt_now() - timedelta(minutes=601)), + "datetime": (dt_now() - timedelta(minutes=601)).isoformat(), + "price": 0.245441, + "amount": 8.0, + "cost": 1.963528, + "remaining": 90.99181073, + "status": "closed", + "fee": None, } @pytest.fixture(scope="function") def edge_conf(default_conf): conf = deepcopy(default_conf) - conf['runmode'] = RunMode.DRY_RUN - conf['max_open_trades'] = -1 - conf['tradable_balance_ratio'] = 0.5 - conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT - conf['edge'] = { + conf["runmode"] = RunMode.DRY_RUN + conf["max_open_trades"] = -1 + conf["tradable_balance_ratio"] = 0.5 + conf["stake_amount"] = constants.UNLIMITED_STAKE_AMOUNT + conf["edge"] = { "enabled": True, "process_throttle_secs": 1800, "calculate_since_number_of_days": 14, @@ -2631,7 +2568,7 @@ def edge_conf(default_conf): "minimum_expectancy": 0.20, "min_trade_number": 15, "max_trade_duration_minute": 1440, - "remove_pumps": False + "remove_pumps": False, } return conf @@ -2640,36 +2577,12 @@ def edge_conf(default_conf): @pytest.fixture def rpc_balance(): return { - 'BTC': { - 'total': 12.0, - 'free': 12.0, - 'used': 0.0 - }, - 'ETH': { - 'total': 0.0, - 'free': 0.0, - 'used': 0.0 - }, - 'USDT': { - 'total': 10000.0, - 'free': 10000.0, - 'used': 0.0 - }, - 'LTC': { - 'total': 10.0, - 'free': 10.0, - 'used': 0.0 - }, - 'XRP': { - 'total': 0.1, - 'free': 0.01, - 'used': 0.0 - }, - 'EUR': { - 'total': 10.0, - 'free': 10.0, - 'used': 0.0 - }, + "BTC": {"total": 12.0, "free": 12.0, "used": 0.0}, + "ETH": {"total": 0.0, "free": 0.0, "used": 0.0}, + "USDT": {"total": 10000.0, "free": 10000.0, "used": 0.0}, + "LTC": {"total": 10.0, "free": 10.0, "used": 0.0}, + "XRP": {"total": 0.1, "free": 0.01, "used": 0.0}, + "EUR": {"total": 10.0, "free": 10.0, "used": 0.0}, } @@ -2684,10 +2597,11 @@ def import_fails() -> None: # Source of this test-method: # https://stackoverflow.com/questions/2481511/mocking-importerror-in-python import builtins + realimport = builtins.__import__ def mockedimport(name, *args, **kwargs): - if name in ["filelock", 'cysystemd.journal', 'uvloop']: + if name in ["filelock", "cysystemd.journal", "uvloop"]: raise ImportError(f"No module named '{name}'") return realimport(name, *args, **kwargs) @@ -2703,24 +2617,24 @@ def import_fails() -> None: @pytest.fixture(scope="function") def open_trade(): trade = Trade( - pair='ETH/BTC', + pair="ETH/BTC", open_rate=0.00001099, - exchange='binance', + exchange="binance", amount=90.99181073, fee_open=0.0, fee_close=0.0, stake_amount=1, open_date=dt_now() - timedelta(minutes=601), - is_open=True + is_open=True, ) trade.orders = [ Order( - ft_order_side='buy', + ft_order_side="buy", ft_pair=trade.pair, ft_is_open=True, ft_amount=trade.amount, ft_price=trade.open_rate, - order_id='123456789', + order_id="123456789", status="closed", symbol=trade.pair, order_type="market", @@ -2740,24 +2654,24 @@ def open_trade(): @pytest.fixture(scope="function") def open_trade_usdt(): trade = Trade( - pair='ADA/USDT', + pair="ADA/USDT", open_rate=2.0, - exchange='binance', + exchange="binance", amount=30.0, fee_open=0.0, fee_close=0.0, stake_amount=60.0, open_date=dt_now() - timedelta(minutes=601), - is_open=True + is_open=True, ) trade.orders = [ Order( - ft_order_side='buy', + ft_order_side="buy", ft_pair=trade.pair, ft_is_open=False, ft_amount=trade.amount, ft_price=trade.open_rate, - order_id='123456789', + order_id="123456789", status="closed", symbol=trade.pair, order_type="market", @@ -2771,12 +2685,12 @@ def open_trade_usdt(): order_filled_date=trade.open_date, ), Order( - ft_order_side='exit', + ft_order_side="exit", ft_pair=trade.pair, ft_is_open=True, ft_amount=trade.amount, ft_price=trade.open_rate, - order_id='123456789_exit', + order_id="123456789_exit", status="open", symbol=trade.pair, order_type="limit", @@ -2788,7 +2702,7 @@ def open_trade_usdt(): cost=trade.open_rate * trade.amount, order_date=trade.open_date, order_filled_date=trade.open_date, - ) + ), ] return trade @@ -2797,299 +2711,1143 @@ def open_trade_usdt(): def saved_hyperopt_results(): hyperopt_res = [ { - 'loss': 0.4366182531161519, - 'params_dict': { - 'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1190, 'roi_t2': 541, 'roi_t3': 408, 'roi_p1': 0.026035863879169705, 'roi_p2': 0.12508730043628782, 'roi_p3': 0.27766427921605896, 'stoploss': -0.2562930402099556}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4287874435315165, 408: 0.15112316431545753, 949: 0.026035863879169705, 2139: 0}, 'stoploss': {'stoploss': -0.2562930402099556}}, # noqa: E501 - 'results_metrics': {'total_trades': 2, 'trade_count_long': 2, 'trade_count_short': 0, 'wins': 0, 'draws': 0, 'losses': 2, 'profit_mean': -0.01254995, 'profit_median': -0.012222, 'profit_total': -0.00125625, 'profit_total_abs': -2.50999, 'max_drawdown': 0.23, 'max_drawdown_abs': -0.00125625, 'holding_avg': timedelta(minutes=3930.0), 'stake_currency': 'BTC', 'strategy_name': 'SampleStrategy'}, # noqa: E501 - 'results_explanation': ' 2 trades. Avg profit -1.25%. Total profit -0.00125625 BTC ( -2.51Σ%). Avg duration 3930.0 min.', # noqa: E501 - 'total_profit': -0.00125625, - 'current_epoch': 1, - 'is_initial_point': True, - 'is_random': False, - 'is_best': True, - - }, { - 'loss': 20.0, - 'params_dict': { - 'mfi-value': 17, 'fastd-value': 38, 'adx-value': 48, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 334, 'roi_t2': 683, 'roi_t3': 140, 'roi_p1': 0.06403981740598495, 'roi_p2': 0.055519840060645045, 'roi_p3': 0.3253712811342459, 'stoploss': -0.338070047333259}, # noqa: E501 - 'params_details': { - 'buy': {'mfi-value': 17, 'fastd-value': 38, 'adx-value': 48, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, # noqa: E501 - 'sell': {'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, # noqa: E501 - 'roi': {0: 0.4449309386008759, 140: 0.11955965746663, 823: 0.06403981740598495, 1157: 0}, # noqa: E501 - 'stoploss': {'stoploss': -0.338070047333259}}, - 'results_metrics': {'total_trades': 1, 'trade_count_long': 1, 'trade_count_short': 0, 'wins': 0, 'draws': 0, 'losses': 1, 'profit_mean': 0.012357, 'profit_median': -0.012222, 'profit_total': 6.185e-05, 'profit_total_abs': 0.12357, 'max_drawdown': 0.23, 'max_drawdown_abs': -0.00125625, 'holding_avg': timedelta(minutes=1200.0)}, # noqa: E501 - 'results_explanation': ' 1 trades. Avg profit 0.12%. Total profit 0.00006185 BTC ( 0.12Σ%). Avg duration 1200.0 min.', # noqa: E501 - 'total_profit': 6.185e-05, - 'current_epoch': 2, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': 14.241196856510731, - 'params_dict': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 889, 'roi_t2': 533, 'roi_t3': 263, 'roi_p1': 0.04759065393663096, 'roi_p2': 0.1488819964638463, 'roi_p3': 0.4102801822104605, 'stoploss': -0.05394588767607611}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.6067528326109377, 263: 0.19647265040047726, 796: 0.04759065393663096, 1685: 0}, 'stoploss': {'stoploss': -0.05394588767607611}}, # noqa: E501 - 'results_metrics': {'total_trades': 621, 'trade_count_long': 621, 'trade_count_short': 0, 'wins': 320, 'draws': 0, 'losses': 301, 'profit_mean': -0.043883302093397747, 'profit_median': -0.012222, 'profit_total': -0.13639474, 'profit_total_abs': -272.515306, 'max_drawdown': 0.25, 'max_drawdown_abs': -272.515306, 'holding_avg': timedelta(minutes=1691.207729468599)}, # noqa: E501 - 'results_explanation': ' 621 trades. Avg profit -0.44%. Total profit -0.13639474 BTC (-272.52Σ%). Avg duration 1691.2 min.', # noqa: E501 - 'total_profit': -0.13639474, - 'current_epoch': 3, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': 100000, - 'params_dict': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1402, 'roi_t2': 676, 'roi_t3': 215, 'roi_p1': 0.06264755784937427, 'roi_p2': 0.14258587851894644, 'roi_p3': 0.20671291201040828, 'stoploss': -0.11818343570194478}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.411946348378729, 215: 0.2052334363683207, 891: 0.06264755784937427, 2293: 0}, 'stoploss': {'stoploss': -0.11818343570194478}}, # noqa: E501 - 'results_metrics': {'total_trades': 0, 'trade_count_long': 0, 'trade_count_short': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit': 0.0, 'holding_avg': timedelta()}, # noqa: E501 - 'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501 - 'total_profit': 0, 'current_epoch': 4, 'is_initial_point': True, 'is_random': False, 'is_best': False # noqa: E501 - }, { - 'loss': 0.22195522184191518, - 'params_dict': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 1269, 'roi_t2': 601, 'roi_t3': 444, 'roi_p1': 0.07280999507931168, 'roi_p2': 0.08946698095898986, 'roi_p3': 0.1454876733325284, 'stoploss': -0.18181041180901014}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3077646493708299, 444: 0.16227697603830155, 1045: 0.07280999507931168, 2314: 0}, 'stoploss': {'stoploss': -0.18181041180901014}}, # noqa: E501 - 'results_metrics': {'total_trades': 14, 'trade_count_long': 14, 'trade_count_short': 0, 'wins': 6, 'draws': 0, 'losses': 8, 'profit_mean': -0.003539515, 'profit_median': -0.012222, 'profit_total': -0.002480140000000001, 'profit_total_abs': -4.955321, 'max_drawdown': 0.34, 'max_drawdown_abs': -4.955321, 'holding_avg': timedelta(minutes=3402.8571428571427)}, # noqa: E501 - 'results_explanation': ' 14 trades. Avg profit -0.35%. Total profit -0.00248014 BTC ( -4.96Σ%). Avg duration 3402.9 min.', # noqa: E501 - 'total_profit': -0.002480140000000001, - 'current_epoch': 5, - 'is_initial_point': True, - 'is_random': False, - 'is_best': True - }, { - 'loss': 0.545315889154162, - 'params_dict': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower', 'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 319, 'roi_t2': 556, 'roi_t3': 216, 'roi_p1': 0.06251955472249589, 'roi_p2': 0.11659519602202795, 'roi_p3': 0.0953744132197762, 'stoploss': -0.024551752215582423}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.2744891639643, 216: 0.17911475074452382, 772: 0.06251955472249589, 1091: 0}, 'stoploss': {'stoploss': -0.024551752215582423}}, # noqa: E501 - 'results_metrics': {'total_trades': 39, 'trade_count_long': 39, 'trade_count_short': 0, 'wins': 20, 'draws': 0, 'losses': 19, 'profit_mean': -0.0021400679487179478, 'profit_median': -0.012222, 'profit_total': -0.0041773, 'profit_total_abs': -8.346264999999997, 'max_drawdown': 0.45, 'max_drawdown_abs': -4.955321, 'holding_avg': timedelta(minutes=636.9230769230769)}, # noqa: E501 - 'results_explanation': ' 39 trades. Avg profit -0.21%. Total profit -0.00417730 BTC ( -8.35Σ%). Avg duration 636.9 min.', # noqa: E501 - 'total_profit': -0.0041773, - 'current_epoch': 6, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': 4.713497421432944, - 'params_dict': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 771, 'roi_t2': 620, 'roi_t3': 145, 'roi_p1': 0.0586919200378493, 'roi_p2': 0.04984118697312542, 'roi_p3': 0.37521058680247044, 'stoploss': -0.14613268022709905}, # noqa: E501 - 'params_details': { - 'buy': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.4837436938134452, 145: 0.10853310701097472, 765: 0.0586919200378493, 1536: 0}, # noqa: E501 - 'stoploss': {'stoploss': -0.14613268022709905}}, # noqa: E501 - 'results_metrics': {'total_trades': 318, 'trade_count_long': 318, 'trade_count_short': 0, 'wins': 100, 'draws': 0, 'losses': 218, 'profit_mean': -0.0039833954716981146, 'profit_median': -0.012222, 'profit_total': -0.06339929, 'profit_total_abs': -126.67197600000004, 'max_drawdown': 0.50, 'max_drawdown_abs': -200.955321, 'holding_avg': timedelta(minutes=3140.377358490566)}, # noqa: E501 - 'results_explanation': ' 318 trades. Avg profit -0.40%. Total profit -0.06339929 BTC (-126.67Σ%). Avg duration 3140.4 min.', # noqa: E501 - 'total_profit': -0.06339929, - 'current_epoch': 7, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': 20.0, # noqa: E501 - 'params_dict': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal', 'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 1149, 'roi_t2': 375, 'roi_t3': 289, 'roi_p1': 0.05571820757172588, 'roi_p2': 0.0606240398618907, 'roi_p3': 0.1729012220156157, 'stoploss': -0.1588514289110401}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.2892434694492323, 289: 0.11634224743361658, 664: 0.05571820757172588, 1813: 0}, 'stoploss': {'stoploss': -0.1588514289110401}}, # noqa: E501 - 'results_metrics': {'total_trades': 1, 'trade_count_long': 1, 'trade_count_short': 0, 'wins': 0, 'draws': 1, 'losses': 0, 'profit_mean': 0.0, 'profit_median': 0.0, 'profit_total': 0.0, 'profit_total_abs': 0.0, 'max_drawdown': 0.0, 'max_drawdown_abs': 0.52, 'holding_avg': timedelta(minutes=5340.0)}, # noqa: E501 - 'results_explanation': ' 1 trades. Avg profit 0.00%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration 5340.0 min.', # noqa: E501 - 'total_profit': 0.0, - 'current_epoch': 8, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': 2.4731817780991223, - 'params_dict': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1012, 'roi_t2': 584, 'roi_t3': 422, 'roi_p1': 0.036764323603472565, 'roi_p2': 0.10335480573205287, 'roi_p3': 0.10322347377503042, 'stoploss': -0.2780610808108503}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.2433426031105559, 422: 0.14011912933552545, 1006: 0.036764323603472565, 2018: 0}, 'stoploss': {'stoploss': -0.2780610808108503}}, # noqa: E501 - 'results_metrics': {'total_trades': 229, 'trade_count_long': 229, 'trade_count_short': 0, 'wins': 150, 'draws': 0, 'losses': 79, 'profit_mean': -0.0038433433624454144, 'profit_median': -0.012222, 'profit_total': -0.044050070000000004, 'profit_total_abs': -88.01256299999999, 'max_drawdown': 0.41, 'max_drawdown_abs': -150.955321, 'holding_avg': timedelta(minutes=6505.676855895196)}, # noqa: E501 - 'results_explanation': ' 229 trades. Avg profit -0.38%. Total profit -0.04405007 BTC ( -88.01Σ%). Avg duration 6505.7 min.', # noqa: E501 - 'total_profit': -0.044050070000000004, # noqa: E501 - 'current_epoch': 9, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': -0.2604606005845212, # noqa: E501 - 'params_dict': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 792, 'roi_t2': 464, 'roi_t3': 215, 'roi_p1': 0.04594053535385903, 'roi_p2': 0.09623192684243963, 'roi_p3': 0.04428219070850663, 'stoploss': -0.16992287161634415}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.18645465290480528, 215: 0.14217246219629864, 679: 0.04594053535385903, 1471: 0}, 'stoploss': {'stoploss': -0.16992287161634415}}, # noqa: E501 - 'results_metrics': {'total_trades': 4, 'trade_count_long': 4, 'trade_count_short': 0, 'wins': 0, 'draws': 0, 'losses': 4, 'profit_mean': 0.001080385, 'profit_median': -0.012222, 'profit_total': 0.00021629, 'profit_total_abs': 0.432154, 'max_drawdown': 0.13, 'max_drawdown_abs': -4.955321, 'holding_avg': timedelta(minutes=2850.0)}, # noqa: E501 - 'results_explanation': ' 4 trades. Avg profit 0.11%. Total profit 0.00021629 BTC ( 0.43Σ%). Avg duration 2850.0 min.', # noqa: E501 - 'total_profit': 0.00021629, - 'current_epoch': 10, - 'is_initial_point': True, - 'is_random': False, - 'is_best': True - }, { - 'loss': 4.876465945994304, # noqa: E501 - 'params_dict': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 579, 'roi_t2': 614, 'roi_t3': 273, 'roi_p1': 0.05307643172744114, 'roi_p2': 0.1352282078262871, 'roi_p3': 0.1913307406325751, 'stoploss': -0.25728526022513887}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3796353801863034, 273: 0.18830463955372825, 887: 0.05307643172744114, 1466: 0}, 'stoploss': {'stoploss': -0.25728526022513887}}, # noqa: E501 + "loss": 0.4366182531161519, + "params_dict": { + "mfi-value": 15, + "fastd-value": 20, + "adx-value": 25, + "rsi-value": 28, + "mfi-enabled": False, + "fastd-enabled": True, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + "sell-mfi-value": 88, + "sell-fastd-value": 97, + "sell-adx-value": 51, + "sell-rsi-value": 67, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + "roi_t1": 1190, + "roi_t2": 541, + "roi_t3": 408, + "roi_p1": 0.026035863879169705, + "roi_p2": 0.12508730043628782, + "roi_p3": 0.27766427921605896, + "stoploss": -0.2562930402099556, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 15, + "fastd-value": 20, + "adx-value": 25, + "rsi-value": 28, + "mfi-enabled": False, + "fastd-enabled": True, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + }, + "sell": { + "sell-mfi-value": 88, + "sell-fastd-value": 97, + "sell-adx-value": 51, + "sell-rsi-value": 67, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + }, + "roi": { + 0: 0.4287874435315165, + 408: 0.15112316431545753, + 949: 0.026035863879169705, + 2139: 0, + }, + "stoploss": {"stoploss": -0.2562930402099556}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 2, + "trade_count_long": 2, + "trade_count_short": 0, + "wins": 0, + "draws": 0, + "losses": 2, + "profit_mean": -0.01254995, + "profit_median": -0.012222, + "profit_total": -0.00125625, + "profit_total_abs": -2.50999, + "max_drawdown": 0.23, + "max_drawdown_abs": -0.00125625, + "holding_avg": timedelta(minutes=3930.0), + "stake_currency": "BTC", + "strategy_name": "SampleStrategy", + }, # noqa: E501 + "results_explanation": " 2 trades. Avg profit -1.25%. Total profit -0.00125625 BTC ( -2.51Σ%). Avg duration 3930.0 min.", # noqa: E501 + "total_profit": -0.00125625, + "current_epoch": 1, + "is_initial_point": True, + "is_random": False, + "is_best": True, + }, + { + "loss": 20.0, + "params_dict": { + "mfi-value": 17, + "fastd-value": 38, + "adx-value": 48, + "rsi-value": 22, + "mfi-enabled": True, + "fastd-enabled": False, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + "sell-mfi-value": 96, + "sell-fastd-value": 68, + "sell-adx-value": 63, + "sell-rsi-value": 81, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-sar_reversal", + "roi_t1": 334, + "roi_t2": 683, + "roi_t3": 140, + "roi_p1": 0.06403981740598495, + "roi_p2": 0.055519840060645045, + "roi_p3": 0.3253712811342459, + "stoploss": -0.338070047333259, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 17, + "fastd-value": 38, + "adx-value": 48, + "rsi-value": 22, + "mfi-enabled": True, + "fastd-enabled": False, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + }, # noqa: E501 + "sell": { + "sell-mfi-value": 96, + "sell-fastd-value": 68, + "sell-adx-value": 63, + "sell-rsi-value": 81, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-sar_reversal", + }, # noqa: E501 + "roi": { + 0: 0.4449309386008759, + 140: 0.11955965746663, + 823: 0.06403981740598495, + 1157: 0, + }, # noqa: E501 + "stoploss": {"stoploss": -0.338070047333259}, + }, + "results_metrics": { + "total_trades": 1, + "trade_count_long": 1, + "trade_count_short": 0, + "wins": 0, + "draws": 0, + "losses": 1, + "profit_mean": 0.012357, + "profit_median": -0.012222, + "profit_total": 6.185e-05, + "profit_total_abs": 0.12357, + "max_drawdown": 0.23, + "max_drawdown_abs": -0.00125625, + "holding_avg": timedelta(minutes=1200.0), + }, # noqa: E501 + "results_explanation": " 1 trades. Avg profit 0.12%. Total profit 0.00006185 BTC ( 0.12Σ%). Avg duration 1200.0 min.", # noqa: E501 + "total_profit": 6.185e-05, + "current_epoch": 2, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": 14.241196856510731, + "params_dict": { + "mfi-value": 25, + "fastd-value": 16, + "adx-value": 29, + "rsi-value": 20, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "macd_cross_signal", + "sell-mfi-value": 98, + "sell-fastd-value": 72, + "sell-adx-value": 51, + "sell-rsi-value": 82, + "sell-mfi-enabled": True, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-macd_cross_signal", + "roi_t1": 889, + "roi_t2": 533, + "roi_t3": 263, + "roi_p1": 0.04759065393663096, + "roi_p2": 0.1488819964638463, + "roi_p3": 0.4102801822104605, + "stoploss": -0.05394588767607611, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 25, + "fastd-value": 16, + "adx-value": 29, + "rsi-value": 20, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "macd_cross_signal", + }, + "sell": { + "sell-mfi-value": 98, + "sell-fastd-value": 72, + "sell-adx-value": 51, + "sell-rsi-value": 82, + "sell-mfi-enabled": True, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-macd_cross_signal", + }, + "roi": { + 0: 0.6067528326109377, + 263: 0.19647265040047726, + 796: 0.04759065393663096, + 1685: 0, + }, + "stoploss": {"stoploss": -0.05394588767607611}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 621, + "trade_count_long": 621, + "trade_count_short": 0, + "wins": 320, + "draws": 0, + "losses": 301, + "profit_mean": -0.043883302093397747, + "profit_median": -0.012222, + "profit_total": -0.13639474, + "profit_total_abs": -272.515306, + "max_drawdown": 0.25, + "max_drawdown_abs": -272.515306, + "holding_avg": timedelta(minutes=1691.207729468599), + }, # noqa: E501 + "results_explanation": " 621 trades. Avg profit -0.44%. Total profit -0.13639474 BTC (-272.52Σ%). Avg duration 1691.2 min.", # noqa: E501 + "total_profit": -0.13639474, + "current_epoch": 3, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": 100000, + "params_dict": { + "mfi-value": 13, + "fastd-value": 35, + "adx-value": 39, + "rsi-value": 29, + "mfi-enabled": True, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + "sell-mfi-value": 87, + "sell-fastd-value": 54, + "sell-adx-value": 63, + "sell-rsi-value": 93, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + "roi_t1": 1402, + "roi_t2": 676, + "roi_t3": 215, + "roi_p1": 0.06264755784937427, + "roi_p2": 0.14258587851894644, + "roi_p3": 0.20671291201040828, + "stoploss": -0.11818343570194478, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 13, + "fastd-value": 35, + "adx-value": 39, + "rsi-value": 29, + "mfi-enabled": True, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + }, + "sell": { + "sell-mfi-value": 87, + "sell-fastd-value": 54, + "sell-adx-value": 63, + "sell-rsi-value": 93, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + }, + "roi": { + 0: 0.411946348378729, + 215: 0.2052334363683207, + 891: 0.06264755784937427, + 2293: 0, + }, + "stoploss": {"stoploss": -0.11818343570194478}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 0, + "trade_count_long": 0, + "trade_count_short": 0, + "wins": 0, + "draws": 0, + "losses": 0, + "profit_mean": None, + "profit_median": None, + "profit_total": 0, + "profit": 0.0, + "holding_avg": timedelta(), + }, # noqa: E501 + "results_explanation": " 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.", # noqa: E501 + "total_profit": 0, + "current_epoch": 4, + "is_initial_point": True, + "is_random": False, + "is_best": False, # noqa: E501 + }, + { + "loss": 0.22195522184191518, + "params_dict": { + "mfi-value": 17, + "fastd-value": 21, + "adx-value": 38, + "rsi-value": 33, + "mfi-enabled": True, + "fastd-enabled": False, + "adx-enabled": True, + "rsi-enabled": False, + "trigger": "macd_cross_signal", + "sell-mfi-value": 87, + "sell-fastd-value": 82, + "sell-adx-value": 78, + "sell-rsi-value": 69, + "sell-mfi-enabled": True, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": False, + "sell-trigger": "sell-macd_cross_signal", + "roi_t1": 1269, + "roi_t2": 601, + "roi_t3": 444, + "roi_p1": 0.07280999507931168, + "roi_p2": 0.08946698095898986, + "roi_p3": 0.1454876733325284, + "stoploss": -0.18181041180901014, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 17, + "fastd-value": 21, + "adx-value": 38, + "rsi-value": 33, + "mfi-enabled": True, + "fastd-enabled": False, + "adx-enabled": True, + "rsi-enabled": False, + "trigger": "macd_cross_signal", + }, + "sell": { + "sell-mfi-value": 87, + "sell-fastd-value": 82, + "sell-adx-value": 78, + "sell-rsi-value": 69, + "sell-mfi-enabled": True, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": False, + "sell-trigger": "sell-macd_cross_signal", + }, + "roi": { + 0: 0.3077646493708299, + 444: 0.16227697603830155, + 1045: 0.07280999507931168, + 2314: 0, + }, + "stoploss": {"stoploss": -0.18181041180901014}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 14, + "trade_count_long": 14, + "trade_count_short": 0, + "wins": 6, + "draws": 0, + "losses": 8, + "profit_mean": -0.003539515, + "profit_median": -0.012222, + "profit_total": -0.002480140000000001, + "profit_total_abs": -4.955321, + "max_drawdown": 0.34, + "max_drawdown_abs": -4.955321, + "holding_avg": timedelta(minutes=3402.8571428571427), + }, # noqa: E501 + "results_explanation": " 14 trades. Avg profit -0.35%. Total profit -0.00248014 BTC ( -4.96Σ%). Avg duration 3402.9 min.", # noqa: E501 + "total_profit": -0.002480140000000001, + "current_epoch": 5, + "is_initial_point": True, + "is_random": False, + "is_best": True, + }, + { + "loss": 0.545315889154162, + "params_dict": { + "mfi-value": 22, + "fastd-value": 43, + "adx-value": 46, + "rsi-value": 20, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "bb_lower", + "sell-mfi-value": 87, + "sell-fastd-value": 65, + "sell-adx-value": 94, + "sell-rsi-value": 63, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-macd_cross_signal", + "roi_t1": 319, + "roi_t2": 556, + "roi_t3": 216, + "roi_p1": 0.06251955472249589, + "roi_p2": 0.11659519602202795, + "roi_p3": 0.0953744132197762, + "stoploss": -0.024551752215582423, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 22, + "fastd-value": 43, + "adx-value": 46, + "rsi-value": 20, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "bb_lower", + }, + "sell": { + "sell-mfi-value": 87, + "sell-fastd-value": 65, + "sell-adx-value": 94, + "sell-rsi-value": 63, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-macd_cross_signal", + }, + "roi": { + 0: 0.2744891639643, + 216: 0.17911475074452382, + 772: 0.06251955472249589, + 1091: 0, + }, + "stoploss": {"stoploss": -0.024551752215582423}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 39, + "trade_count_long": 39, + "trade_count_short": 0, + "wins": 20, + "draws": 0, + "losses": 19, + "profit_mean": -0.0021400679487179478, + "profit_median": -0.012222, + "profit_total": -0.0041773, + "profit_total_abs": -8.346264999999997, + "max_drawdown": 0.45, + "max_drawdown_abs": -4.955321, + "holding_avg": timedelta(minutes=636.9230769230769), + }, # noqa: E501 + "results_explanation": " 39 trades. Avg profit -0.21%. Total profit -0.00417730 BTC ( -8.35Σ%). Avg duration 636.9 min.", # noqa: E501 + "total_profit": -0.0041773, + "current_epoch": 6, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": 4.713497421432944, + "params_dict": { + "mfi-value": 13, + "fastd-value": 41, + "adx-value": 21, + "rsi-value": 29, + "mfi-enabled": False, + "fastd-enabled": True, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "bb_lower", + "sell-mfi-value": 99, + "sell-fastd-value": 60, + "sell-adx-value": 81, + "sell-rsi-value": 69, + "sell-mfi-enabled": True, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": False, + "sell-trigger": "sell-macd_cross_signal", + "roi_t1": 771, + "roi_t2": 620, + "roi_t3": 145, + "roi_p1": 0.0586919200378493, + "roi_p2": 0.04984118697312542, + "roi_p3": 0.37521058680247044, + "stoploss": -0.14613268022709905, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 13, + "fastd-value": 41, + "adx-value": 21, + "rsi-value": 29, + "mfi-enabled": False, + "fastd-enabled": True, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "bb_lower", + }, + "sell": { + "sell-mfi-value": 99, + "sell-fastd-value": 60, + "sell-adx-value": 81, + "sell-rsi-value": 69, + "sell-mfi-enabled": True, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": False, + "sell-trigger": "sell-macd_cross_signal", + }, + "roi": { + 0: 0.4837436938134452, + 145: 0.10853310701097472, + 765: 0.0586919200378493, + 1536: 0, + }, # noqa: E501 + "stoploss": {"stoploss": -0.14613268022709905}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 318, + "trade_count_long": 318, + "trade_count_short": 0, + "wins": 100, + "draws": 0, + "losses": 218, + "profit_mean": -0.0039833954716981146, + "profit_median": -0.012222, + "profit_total": -0.06339929, + "profit_total_abs": -126.67197600000004, + "max_drawdown": 0.50, + "max_drawdown_abs": -200.955321, + "holding_avg": timedelta(minutes=3140.377358490566), + }, # noqa: E501 + "results_explanation": " 318 trades. Avg profit -0.40%. Total profit -0.06339929 BTC (-126.67Σ%). Avg duration 3140.4 min.", # noqa: E501 + "total_profit": -0.06339929, + "current_epoch": 7, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": 20.0, # noqa: E501 + "params_dict": { + "mfi-value": 24, + "fastd-value": 43, + "adx-value": 33, + "rsi-value": 20, + "mfi-enabled": False, + "fastd-enabled": True, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "sar_reversal", + "sell-mfi-value": 89, + "sell-fastd-value": 74, + "sell-adx-value": 70, + "sell-rsi-value": 70, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": False, + "sell-rsi-enabled": True, + "sell-trigger": "sell-sar_reversal", + "roi_t1": 1149, + "roi_t2": 375, + "roi_t3": 289, + "roi_p1": 0.05571820757172588, + "roi_p2": 0.0606240398618907, + "roi_p3": 0.1729012220156157, + "stoploss": -0.1588514289110401, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 24, + "fastd-value": 43, + "adx-value": 33, + "rsi-value": 20, + "mfi-enabled": False, + "fastd-enabled": True, + "adx-enabled": True, + "rsi-enabled": True, + "trigger": "sar_reversal", + }, + "sell": { + "sell-mfi-value": 89, + "sell-fastd-value": 74, + "sell-adx-value": 70, + "sell-rsi-value": 70, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": False, + "sell-rsi-enabled": True, + "sell-trigger": "sell-sar_reversal", + }, + "roi": { + 0: 0.2892434694492323, + 289: 0.11634224743361658, + 664: 0.05571820757172588, + 1813: 0, + }, + "stoploss": {"stoploss": -0.1588514289110401}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 1, + "trade_count_long": 1, + "trade_count_short": 0, + "wins": 0, + "draws": 1, + "losses": 0, + "profit_mean": 0.0, + "profit_median": 0.0, + "profit_total": 0.0, + "profit_total_abs": 0.0, + "max_drawdown": 0.0, + "max_drawdown_abs": 0.52, + "holding_avg": timedelta(minutes=5340.0), + }, # noqa: E501 + "results_explanation": " 1 trades. Avg profit 0.00%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration 5340.0 min.", # noqa: E501 + "total_profit": 0.0, + "current_epoch": 8, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": 2.4731817780991223, + "params_dict": { + "mfi-value": 22, + "fastd-value": 20, + "adx-value": 29, + "rsi-value": 40, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "sar_reversal", + "sell-mfi-value": 97, + "sell-fastd-value": 65, + "sell-adx-value": 81, + "sell-rsi-value": 64, + "sell-mfi-enabled": True, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + "roi_t1": 1012, + "roi_t2": 584, + "roi_t3": 422, + "roi_p1": 0.036764323603472565, + "roi_p2": 0.10335480573205287, + "roi_p3": 0.10322347377503042, + "stoploss": -0.2780610808108503, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 22, + "fastd-value": 20, + "adx-value": 29, + "rsi-value": 40, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "sar_reversal", + }, + "sell": { + "sell-mfi-value": 97, + "sell-fastd-value": 65, + "sell-adx-value": 81, + "sell-rsi-value": 64, + "sell-mfi-enabled": True, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + }, + "roi": { + 0: 0.2433426031105559, + 422: 0.14011912933552545, + 1006: 0.036764323603472565, + 2018: 0, + }, + "stoploss": {"stoploss": -0.2780610808108503}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 229, + "trade_count_long": 229, + "trade_count_short": 0, + "wins": 150, + "draws": 0, + "losses": 79, + "profit_mean": -0.0038433433624454144, + "profit_median": -0.012222, + "profit_total": -0.044050070000000004, + "profit_total_abs": -88.01256299999999, + "max_drawdown": 0.41, + "max_drawdown_abs": -150.955321, + "holding_avg": timedelta(minutes=6505.676855895196), + }, # noqa: E501 + "results_explanation": " 229 trades. Avg profit -0.38%. Total profit -0.04405007 BTC ( -88.01Σ%). Avg duration 6505.7 min.", # noqa: E501 + "total_profit": -0.044050070000000004, # noqa: E501 + "current_epoch": 9, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": -0.2604606005845212, # noqa: E501 + "params_dict": { + "mfi-value": 23, + "fastd-value": 24, + "adx-value": 22, + "rsi-value": 24, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + "sell-mfi-value": 97, + "sell-fastd-value": 70, + "sell-adx-value": 64, + "sell-rsi-value": 80, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-sar_reversal", + "roi_t1": 792, + "roi_t2": 464, + "roi_t3": 215, + "roi_p1": 0.04594053535385903, + "roi_p2": 0.09623192684243963, + "roi_p3": 0.04428219070850663, + "stoploss": -0.16992287161634415, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 23, + "fastd-value": 24, + "adx-value": 22, + "rsi-value": 24, + "mfi-enabled": False, + "fastd-enabled": False, + "adx-enabled": False, + "rsi-enabled": True, + "trigger": "macd_cross_signal", + }, + "sell": { + "sell-mfi-value": 97, + "sell-fastd-value": 70, + "sell-adx-value": 64, + "sell-rsi-value": 80, + "sell-mfi-enabled": False, + "sell-fastd-enabled": True, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-sar_reversal", + }, + "roi": { + 0: 0.18645465290480528, + 215: 0.14217246219629864, + 679: 0.04594053535385903, + 1471: 0, + }, + "stoploss": {"stoploss": -0.16992287161634415}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 4, + "trade_count_long": 4, + "trade_count_short": 0, + "wins": 0, + "draws": 0, + "losses": 4, + "profit_mean": 0.001080385, + "profit_median": -0.012222, + "profit_total": 0.00021629, + "profit_total_abs": 0.432154, + "max_drawdown": 0.13, + "max_drawdown_abs": -4.955321, + "holding_avg": timedelta(minutes=2850.0), + }, # noqa: E501 + "results_explanation": " 4 trades. Avg profit 0.11%. Total profit 0.00021629 BTC ( 0.43Σ%). Avg duration 2850.0 min.", # noqa: E501 + "total_profit": 0.00021629, + "current_epoch": 10, + "is_initial_point": True, + "is_random": False, + "is_best": True, + }, + { + "loss": 4.876465945994304, # noqa: E501 + "params_dict": { + "mfi-value": 20, + "fastd-value": 32, + "adx-value": 49, + "rsi-value": 23, + "mfi-enabled": True, + "fastd-enabled": True, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "bb_lower", + "sell-mfi-value": 75, + "sell-fastd-value": 56, + "sell-adx-value": 61, + "sell-rsi-value": 62, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-macd_cross_signal", + "roi_t1": 579, + "roi_t2": 614, + "roi_t3": 273, + "roi_p1": 0.05307643172744114, + "roi_p2": 0.1352282078262871, + "roi_p3": 0.1913307406325751, + "stoploss": -0.25728526022513887, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 20, + "fastd-value": 32, + "adx-value": 49, + "rsi-value": 23, + "mfi-enabled": True, + "fastd-enabled": True, + "adx-enabled": False, + "rsi-enabled": False, + "trigger": "bb_lower", + }, + "sell": { + "sell-mfi-value": 75, + "sell-fastd-value": 56, + "sell-adx-value": 61, + "sell-rsi-value": 62, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-macd_cross_signal", + }, + "roi": { + 0: 0.3796353801863034, + 273: 0.18830463955372825, + 887: 0.05307643172744114, + 1466: 0, + }, + "stoploss": {"stoploss": -0.25728526022513887}, + }, # noqa: E501 # New Hyperopt mode! - 'results_metrics': {'total_trades': 117, 'trade_count_long': 117, 'trade_count_short': 0, 'wins': 67, 'draws': 0, 'losses': 50, 'profit_mean': -0.012698609145299145, 'profit_median': -0.012222, 'profit_total': -0.07436117, 'profit_total_abs': -148.573727, 'max_drawdown': 0.52, 'max_drawdown_abs': -224.955321, 'holding_avg': timedelta(minutes=4282.5641025641025)}, # noqa: E501 - 'results_explanation': ' 117 trades. Avg profit -1.27%. Total profit -0.07436117 BTC (-148.57Σ%). Avg duration 4282.6 min.', # noqa: E501 - 'total_profit': -0.07436117, - 'current_epoch': 11, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - }, { - 'loss': 100000, - 'params_dict': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1156, 'roi_t2': 581, 'roi_t3': 408, 'roi_p1': 0.06860454019988212, 'roi_p2': 0.12473718444931989, 'roi_p3': 0.2896360635226823, 'stoploss': -0.30889015124682806}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4829777881718843, 408: 0.19334172464920202, 989: 0.06860454019988212, 2145: 0}, 'stoploss': {'stoploss': -0.30889015124682806}}, # noqa: E501 - 'results_metrics': {'total_trades': 0, 'trade_count_long': 0, 'trade_count_short': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit_total_abs': 0.0, 'max_drawdown': 0.0, 'max_drawdown_abs': 0.0, 'holding_avg': timedelta()}, # noqa: E501 - 'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501 - 'total_profit': 0, - 'current_epoch': 12, - 'is_initial_point': True, - 'is_random': False, - 'is_best': False - } + "results_metrics": { + "total_trades": 117, + "trade_count_long": 117, + "trade_count_short": 0, + "wins": 67, + "draws": 0, + "losses": 50, + "profit_mean": -0.012698609145299145, + "profit_median": -0.012222, + "profit_total": -0.07436117, + "profit_total_abs": -148.573727, + "max_drawdown": 0.52, + "max_drawdown_abs": -224.955321, + "holding_avg": timedelta(minutes=4282.5641025641025), + }, # noqa: E501 + "results_explanation": " 117 trades. Avg profit -1.27%. Total profit -0.07436117 BTC (-148.57Σ%). Avg duration 4282.6 min.", # noqa: E501 + "total_profit": -0.07436117, + "current_epoch": 11, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, + { + "loss": 100000, + "params_dict": { + "mfi-value": 10, + "fastd-value": 36, + "adx-value": 31, + "rsi-value": 22, + "mfi-enabled": True, + "fastd-enabled": True, + "adx-enabled": True, + "rsi-enabled": False, + "trigger": "sar_reversal", + "sell-mfi-value": 80, + "sell-fastd-value": 71, + "sell-adx-value": 60, + "sell-rsi-value": 85, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + "roi_t1": 1156, + "roi_t2": 581, + "roi_t3": 408, + "roi_p1": 0.06860454019988212, + "roi_p2": 0.12473718444931989, + "roi_p3": 0.2896360635226823, + "stoploss": -0.30889015124682806, + }, # noqa: E501 + "params_details": { + "buy": { + "mfi-value": 10, + "fastd-value": 36, + "adx-value": 31, + "rsi-value": 22, + "mfi-enabled": True, + "fastd-enabled": True, + "adx-enabled": True, + "rsi-enabled": False, + "trigger": "sar_reversal", + }, + "sell": { + "sell-mfi-value": 80, + "sell-fastd-value": 71, + "sell-adx-value": 60, + "sell-rsi-value": 85, + "sell-mfi-enabled": False, + "sell-fastd-enabled": False, + "sell-adx-enabled": True, + "sell-rsi-enabled": True, + "sell-trigger": "sell-bb_upper", + }, + "roi": { + 0: 0.4829777881718843, + 408: 0.19334172464920202, + 989: 0.06860454019988212, + 2145: 0, + }, + "stoploss": {"stoploss": -0.30889015124682806}, + }, # noqa: E501 + "results_metrics": { + "total_trades": 0, + "trade_count_long": 0, + "trade_count_short": 0, + "wins": 0, + "draws": 0, + "losses": 0, + "profit_mean": None, + "profit_median": None, + "profit_total": 0, + "profit_total_abs": 0.0, + "max_drawdown": 0.0, + "max_drawdown_abs": 0.0, + "holding_avg": timedelta(), + }, # noqa: E501 + "results_explanation": " 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.", # noqa: E501 + "total_profit": 0, + "current_epoch": 12, + "is_initial_point": True, + "is_random": False, + "is_best": False, + }, ] for res in hyperopt_res: - res['results_metrics']['holding_avg_s'] = res['results_metrics']['holding_avg' - ].total_seconds() + res["results_metrics"]["holding_avg_s"] = res["results_metrics"][ + "holding_avg" + ].total_seconds() return hyperopt_res -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_buy_order_usdt_open(): return { - 'id': 'mocked_limit_buy_usdt', - 'type': 'limit', - 'side': 'buy', - 'symbol': 'mocked', - 'datetime': dt_now().isoformat(), - 'timestamp': dt_ts(), - 'price': 2.00, - 'average': 2.00, - 'amount': 30.0, - 'filled': 0.0, - 'cost': 60.0, - 'remaining': 30.0, - 'status': 'open' + "id": "mocked_limit_buy_usdt", + "type": "limit", + "side": "buy", + "symbol": "mocked", + "datetime": dt_now().isoformat(), + "timestamp": dt_ts(), + "price": 2.00, + "average": 2.00, + "amount": 30.0, + "filled": 0.0, + "cost": 60.0, + "remaining": 30.0, + "status": "open", } -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_buy_order_usdt(limit_buy_order_usdt_open): order = deepcopy(limit_buy_order_usdt_open) - order['status'] = 'closed' - order['filled'] = order['amount'] - order['remaining'] = 0.0 + order["status"] = "closed" + order["filled"] = order["amount"] + order["remaining"] = 0.0 return order @pytest.fixture def limit_sell_order_usdt_open(): return { - 'id': 'mocked_limit_sell_usdt', - 'type': 'limit', - 'side': 'sell', - 'symbol': 'mocked', - 'datetime': dt_now().isoformat(), - 'timestamp': dt_ts(), - 'price': 2.20, - 'amount': 30.0, - 'cost': 66.0, - 'filled': 0.0, - 'remaining': 30.0, - 'status': 'open' + "id": "mocked_limit_sell_usdt", + "type": "limit", + "side": "sell", + "symbol": "mocked", + "datetime": dt_now().isoformat(), + "timestamp": dt_ts(), + "price": 2.20, + "amount": 30.0, + "cost": 66.0, + "filled": 0.0, + "remaining": 30.0, + "status": "open", } @pytest.fixture def limit_sell_order_usdt(limit_sell_order_usdt_open): order = deepcopy(limit_sell_order_usdt_open) - order['remaining'] = 0.0 - order['filled'] = order['amount'] - order['status'] = 'closed' + order["remaining"] = 0.0 + order["filled"] = order["amount"] + order["status"] = "closed" return order -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def market_buy_order_usdt(): return { - 'id': 'mocked_market_buy', - 'type': 'market', - 'side': 'buy', - 'symbol': 'mocked', - 'timestamp': dt_ts(), - 'datetime': dt_now().isoformat(), - 'price': 2.00, - 'amount': 30.0, - 'filled': 30.0, - 'remaining': 0.0, - 'status': 'closed' + "id": "mocked_market_buy", + "type": "market", + "side": "buy", + "symbol": "mocked", + "timestamp": dt_ts(), + "datetime": dt_now().isoformat(), + "price": 2.00, + "amount": 30.0, + "filled": 30.0, + "remaining": 0.0, + "status": "closed", } @pytest.fixture def market_buy_order_usdt_doublefee(market_buy_order_usdt): order = deepcopy(market_buy_order_usdt) - order['fee'] = None + order["fee"] = None # Market orders filled with 2 trades can have fees in different currencies # assuming the account runs out of BNB. - order['fees'] = [ - {'cost': 0.00025125, 'currency': 'BNB'}, - {'cost': 0.05030681, 'currency': 'USDT'}, + order["fees"] = [ + {"cost": 0.00025125, "currency": "BNB"}, + {"cost": 0.05030681, "currency": "USDT"}, + ] + order["trades"] = [ + { + "timestamp": None, + "datetime": None, + "symbol": "ETH/USDT", + "id": None, + "order": "123", + "type": "market", + "side": "sell", + "takerOrMaker": None, + "price": 2.01, + "amount": 25.0, + "cost": 50.25, + "fee": {"cost": 0.00025125, "currency": "BNB"}, + }, + { + "timestamp": None, + "datetime": None, + "symbol": "ETH/USDT", + "id": None, + "order": "123", + "type": "market", + "side": "sell", + "takerOrMaker": None, + "price": 2.0, + "amount": 5, + "cost": 10, + "fee": {"cost": 0.0100306, "currency": "USDT"}, + }, ] - order['trades'] = [{ - 'timestamp': None, - 'datetime': None, - 'symbol': 'ETH/USDT', - 'id': None, - 'order': '123', - 'type': 'market', - 'side': 'sell', - 'takerOrMaker': None, - 'price': 2.01, - 'amount': 25.0, - 'cost': 50.25, - 'fee': {'cost': 0.00025125, 'currency': 'BNB'} - }, { - 'timestamp': None, - 'datetime': None, - 'symbol': 'ETH/USDT', - 'id': None, - 'order': '123', - 'type': 'market', - 'side': 'sell', - 'takerOrMaker': None, - 'price': 2.0, - 'amount': 5, - 'cost': 10, - 'fee': {'cost': 0.0100306, 'currency': 'USDT'} - }] return order @pytest.fixture def market_sell_order_usdt(): return { - 'id': 'mocked_limit_sell', - 'type': 'market', - 'side': 'sell', - 'symbol': 'mocked', - 'timestamp': dt_ts(), - 'datetime': dt_now().isoformat(), - 'price': 2.20, - 'amount': 30.0, - 'filled': 30.0, - 'remaining': 0.0, - 'status': 'closed' + "id": "mocked_limit_sell", + "type": "market", + "side": "sell", + "symbol": "mocked", + "timestamp": dt_ts(), + "datetime": dt_now().isoformat(), + "price": 2.20, + "amount": 30.0, + "filled": 30.0, + "remaining": 0.0, + "status": "closed", } -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_order(limit_buy_order_usdt, limit_sell_order_usdt): - return { - 'buy': limit_buy_order_usdt, - 'sell': limit_sell_order_usdt - } + return {"buy": limit_buy_order_usdt, "sell": limit_sell_order_usdt} -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def limit_order_open(limit_buy_order_usdt_open, limit_sell_order_usdt_open): - return { - 'buy': limit_buy_order_usdt_open, - 'sell': limit_sell_order_usdt_open - } + return {"buy": limit_buy_order_usdt_open, "sell": limit_sell_order_usdt_open} -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def mark_ohlcv(): return [ [1630454400000, 2.77, 2.77, 2.73, 2.73, 0], @@ -3109,254 +3867,254 @@ def mark_ohlcv(): ] -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def funding_rate_history_hourly(): return [ { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000008, "timestamp": 1630454400000, - "datetime": "2021-09-01T00:00:00.000Z" + "datetime": "2021-09-01T00:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000004, "timestamp": 1630458000000, - "datetime": "2021-09-01T01:00:00.000Z" + "datetime": "2021-09-01T01:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000012, "timestamp": 1630461600000, - "datetime": "2021-09-01T02:00:00.000Z" + "datetime": "2021-09-01T02:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000003, "timestamp": 1630465200000, - "datetime": "2021-09-01T03:00:00.000Z" + "datetime": "2021-09-01T03:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000007, "timestamp": 1630468800000, - "datetime": "2021-09-01T04:00:00.000Z" + "datetime": "2021-09-01T04:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000003, "timestamp": 1630472400000, - "datetime": "2021-09-01T05:00:00.000Z" + "datetime": "2021-09-01T05:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000019, "timestamp": 1630476000000, - "datetime": "2021-09-01T06:00:00.000Z" + "datetime": "2021-09-01T06:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000003, "timestamp": 1630479600000, - "datetime": "2021-09-01T07:00:00.000Z" + "datetime": "2021-09-01T07:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000003, "timestamp": 1630483200000, - "datetime": "2021-09-01T08:00:00.000Z" + "datetime": "2021-09-01T08:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0, "timestamp": 1630486800000, - "datetime": "2021-09-01T09:00:00.000Z" + "datetime": "2021-09-01T09:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000013, "timestamp": 1630490400000, - "datetime": "2021-09-01T10:00:00.000Z" + "datetime": "2021-09-01T10:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000077, "timestamp": 1630494000000, - "datetime": "2021-09-01T11:00:00.000Z" + "datetime": "2021-09-01T11:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000072, "timestamp": 1630497600000, - "datetime": "2021-09-01T12:00:00.000Z" + "datetime": "2021-09-01T12:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": 0.000097, "timestamp": 1630501200000, - "datetime": "2021-09-01T13:00:00.000Z" + "datetime": "2021-09-01T13:00:00.000Z", }, ] -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def funding_rate_history_octohourly(): return [ { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000008, "timestamp": 1630454400000, - "datetime": "2021-09-01T00:00:00.000Z" + "datetime": "2021-09-01T00:00:00.000Z", }, { "symbol": "ADA/USDT:USDT", "fundingRate": -0.000003, "timestamp": 1630483200000, - "datetime": "2021-09-01T08:00:00.000Z" - } + "datetime": "2021-09-01T08:00:00.000Z", + }, ] -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def leverage_tiers(): return { "1000SHIB/USDT:USDT": [ { - 'minNotional': 0, - 'maxNotional': 50000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 50, - 'maintAmt': 0.0 + "minNotional": 0, + "maxNotional": 50000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50, + "maintAmt": 0.0, }, { - 'minNotional': 50000, - 'maxNotional': 150000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 20, - 'maintAmt': 750.0 + "minNotional": 50000, + "maxNotional": 150000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20, + "maintAmt": 750.0, }, { - 'minNotional': 150000, - 'maxNotional': 250000, - 'maintenanceMarginRate': 0.05, - 'maxLeverage': 10, - 'maintAmt': 4500.0 + "minNotional": 150000, + "maxNotional": 250000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "maintAmt": 4500.0, }, { - 'minNotional': 250000, - 'maxNotional': 500000, - 'maintenanceMarginRate': 0.1, - 'maxLeverage': 5, - 'maintAmt': 17000.0 + "minNotional": 250000, + "maxNotional": 500000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "maintAmt": 17000.0, }, { - 'minNotional': 500000, - 'maxNotional': 1000000, - 'maintenanceMarginRate': 0.125, - 'maxLeverage': 4, - 'maintAmt': 29500.0 + "minNotional": 500000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4, + "maintAmt": 29500.0, }, { - 'minNotional': 1000000, - 'maxNotional': 2000000, - 'maintenanceMarginRate': 0.25, - 'maxLeverage': 2, - 'maintAmt': 154500.0 + "minNotional": 1000000, + "maxNotional": 2000000, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2, + "maintAmt": 154500.0, }, { - 'minNotional': 2000000, - 'maxNotional': 30000000, - 'maintenanceMarginRate': 0.5, - 'maxLeverage': 1, - 'maintAmt': 654500.0 + "minNotional": 2000000, + "maxNotional": 30000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "maintAmt": 654500.0, }, ], "1INCH/USDT:USDT": [ { - 'minNotional': 0, - 'maxNotional': 5000, - 'maintenanceMarginRate': 0.012, - 'maxLeverage': 50, - 'maintAmt': 0.0 + "minNotional": 0, + "maxNotional": 5000, + "maintenanceMarginRate": 0.012, + "maxLeverage": 50, + "maintAmt": 0.0, }, { - 'minNotional': 5000, - 'maxNotional': 25000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 20, - 'maintAmt': 65.0 + "minNotional": 5000, + "maxNotional": 25000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20, + "maintAmt": 65.0, }, { - 'minNotional': 25000, - 'maxNotional': 100000, - 'maintenanceMarginRate': 0.05, - 'maxLeverage': 10, - 'maintAmt': 690.0 + "minNotional": 25000, + "maxNotional": 100000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "maintAmt": 690.0, }, { - 'minNotional': 100000, - 'maxNotional': 250000, - 'maintenanceMarginRate': 0.1, - 'maxLeverage': 5, - 'maintAmt': 5690.0 + "minNotional": 100000, + "maxNotional": 250000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "maintAmt": 5690.0, }, { - 'minNotional': 250000, - 'maxNotional': 1000000, - 'maintenanceMarginRate': 0.125, - 'maxLeverage': 2, - 'maintAmt': 11940.0 + "minNotional": 250000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2, + "maintAmt": 11940.0, }, { - 'minNotional': 1000000, - 'maxNotional': 100000000, - 'maintenanceMarginRate': 0.5, - 'maxLeverage': 1, - 'maintAmt': 386940.0 + "minNotional": 1000000, + "maxNotional": 100000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "maintAmt": 386940.0, }, ], "AAVE/USDT:USDT": [ { - 'minNotional': 0, - 'maxNotional': 5000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 50, - 'maintAmt': 0.0 + "minNotional": 0, + "maxNotional": 5000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50, + "maintAmt": 0.0, }, { - 'minNotional': 5000, - 'maxNotional': 25000, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 25, - 'maintAmt': 75.0 + "minNotional": 5000, + "maxNotional": 25000, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25, + "maintAmt": 75.0, }, { - 'minNotional': 25000, - 'maxNotional': 100000, - 'maintenanceMarginRate': 0.05, - 'maxLeverage': 10, - 'maintAmt': 700.0 + "minNotional": 25000, + "maxNotional": 100000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "maintAmt": 700.0, }, { - 'minNotional': 100000, - 'maxNotional': 250000, - 'maintenanceMarginRate': 0.1, - 'maxLeverage': 5, - 'maintAmt': 5700.0 + "minNotional": 100000, + "maxNotional": 250000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "maintAmt": 5700.0, }, { - 'minNotional': 250000, - 'maxNotional': 1000000, - 'maintenanceMarginRate': 0.125, - 'maxLeverage': 2, - 'maintAmt': 11950.0 + "minNotional": 250000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2, + "maintAmt": 11950.0, }, { - 'minNotional': 10000000, - 'maxNotional': 50000000, - 'maintenanceMarginRate': 0.5, - 'maxLeverage': 1, - 'maintAmt': 386950.0 + "minNotional": 10000000, + "maxNotional": 50000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "maintAmt": 386950.0, }, ], "ADA/USDT:USDT": [ @@ -3365,274 +4123,274 @@ def leverage_tiers(): "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, - "maintAmt": 0.0 + "maintAmt": 0.0, }, { "minNotional": 100000, "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, - "maintAmt": 2500.0 + "maintAmt": 2500.0, }, { "minNotional": 500000, "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, - "maintAmt": 27500.0 + "maintAmt": 27500.0, }, { "minNotional": 1000000, "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, - "maintAmt": 77500.0 + "maintAmt": 77500.0, }, { "minNotional": 2000000, "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, - "maintAmt": 277500.0 + "maintAmt": 277500.0, }, { "minNotional": 5000000, "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, - "maintAmt": 1527500.0 + "maintAmt": 1527500.0, }, ], - 'XRP/USDT:USDT': [ + "XRP/USDT:USDT": [ { - "minNotional": 0, # stake(before leverage) = 0 + "minNotional": 0, # stake(before leverage) = 0 "maxNotional": 100000, # max stake(before leverage) = 5000 "maintenanceMarginRate": 0.025, "maxLeverage": 20, - "maintAmt": 0.0 + "maintAmt": 0.0, }, { "minNotional": 100000, # stake = 10000.0 "maxNotional": 500000, # max_stake = 50000.0 "maintenanceMarginRate": 0.05, "maxLeverage": 10, - "maintAmt": 2500.0 + "maintAmt": 2500.0, }, { - "minNotional": 500000, # stake = 100000.0 + "minNotional": 500000, # stake = 100000.0 "maxNotional": 1000000, # max_stake = 200000.0 "maintenanceMarginRate": 0.1, "maxLeverage": 5, - "maintAmt": 27500.0 + "maintAmt": 27500.0, }, { "minNotional": 1000000, # stake = 333333.3333333333 "maxNotional": 2000000, # max_stake = 666666.6666666666 "maintenanceMarginRate": 0.15, "maxLeverage": 3, - "maintAmt": 77500.0 + "maintAmt": 77500.0, }, { "minNotional": 2000000, # stake = 1000000.0 "maxNotional": 5000000, # max_stake = 2500000.0 "maintenanceMarginRate": 0.25, "maxLeverage": 2, - "maintAmt": 277500.0 + "maintAmt": 277500.0, }, { - "minNotional": 5000000, # stake = 5000000.0 + "minNotional": 5000000, # stake = 5000000.0 "maxNotional": 30000000, # max_stake = 30000000.0 "maintenanceMarginRate": 0.5, "maxLeverage": 1, - "maintAmt": 1527500.0 - } + "maintAmt": 1527500.0, + }, ], - 'BNB/USDT:USDT': [ + "BNB/USDT:USDT": [ { - "minNotional": 0, # stake = 0.0 + "minNotional": 0, # stake = 0.0 "maxNotional": 10000, # max_stake = 133.33333333333334 "maintenanceMarginRate": 0.0065, "maxLeverage": 75, - "maintAmt": 0.0 + "maintAmt": 0.0, }, { "minNotional": 10000, # stake = 200.0 "maxNotional": 50000, # max_stake = 1000.0 "maintenanceMarginRate": 0.01, "maxLeverage": 50, - "maintAmt": 35.0 + "maintAmt": 35.0, }, { - "minNotional": 50000, # stake = 2000.0 + "minNotional": 50000, # stake = 2000.0 "maxNotional": 250000, # max_stake = 10000.0 "maintenanceMarginRate": 0.02, "maxLeverage": 25, - "maintAmt": 535.0 + "maintAmt": 535.0, }, { - "minNotional": 250000, # stake = 25000.0 + "minNotional": 250000, # stake = 25000.0 "maxNotional": 1000000, # max_stake = 100000.0 "maintenanceMarginRate": 0.05, "maxLeverage": 10, - "maintAmt": 8035.0 + "maintAmt": 8035.0, }, { "minNotional": 1000000, # stake = 200000.0 "maxNotional": 2000000, # max_stake = 400000.0 "maintenanceMarginRate": 0.1, "maxLeverage": 5, - "maintAmt": 58035.0 + "maintAmt": 58035.0, }, { "minNotional": 2000000, # stake = 500000.0 "maxNotional": 5000000, # max_stake = 1250000.0 "maintenanceMarginRate": 0.125, "maxLeverage": 4, - "maintAmt": 108035.0 + "maintAmt": 108035.0, }, { - "minNotional": 5000000, # stake = 1666666.6666666667 + "minNotional": 5000000, # stake = 1666666.6666666667 "maxNotional": 10000000, # max_stake = 3333333.3333333335 "maintenanceMarginRate": 0.15, "maxLeverage": 3, - "maintAmt": 233035.0 + "maintAmt": 233035.0, }, { "minNotional": 10000000, # stake = 5000000.0 "maxNotional": 20000000, # max_stake = 10000000.0 "maintenanceMarginRate": 0.25, "maxLeverage": 2, - "maintAmt": 1233035.0 + "maintAmt": 1233035.0, }, { "minNotional": 20000000, # stake = 20000000.0 "maxNotional": 50000000, # max_stake = 50000000.0 "maintenanceMarginRate": 0.5, "maxLeverage": 1, - "maintAmt": 6233035.0 + "maintAmt": 6233035.0, }, ], - 'BTC/USDT:USDT': [ + "BTC/USDT:USDT": [ { - "minNotional": 0, # stake = 0.0 + "minNotional": 0, # stake = 0.0 "maxNotional": 50000, # max_stake = 400.0 "maintenanceMarginRate": 0.004, "maxLeverage": 125, - "maintAmt": 0.0 + "maintAmt": 0.0, }, { - "minNotional": 50000, # stake = 500.0 + "minNotional": 50000, # stake = 500.0 "maxNotional": 250000, # max_stake = 2500.0 "maintenanceMarginRate": 0.005, "maxLeverage": 100, - "maintAmt": 50.0 + "maintAmt": 50.0, }, { - "minNotional": 250000, # stake = 5000.0 + "minNotional": 250000, # stake = 5000.0 "maxNotional": 1000000, # max_stake = 20000.0 "maintenanceMarginRate": 0.01, "maxLeverage": 50, - "maintAmt": 1300.0 + "maintAmt": 1300.0, }, { "minNotional": 1000000, # stake = 50000.0 "maxNotional": 7500000, # max_stake = 375000.0 "maintenanceMarginRate": 0.025, "maxLeverage": 20, - "maintAmt": 16300.0 + "maintAmt": 16300.0, }, { - "minNotional": 7500000, # stake = 750000.0 + "minNotional": 7500000, # stake = 750000.0 "maxNotional": 40000000, # max_stake = 4000000.0 "maintenanceMarginRate": 0.05, "maxLeverage": 10, - "maintAmt": 203800.0 + "maintAmt": 203800.0, }, { - "minNotional": 40000000, # stake = 8000000.0 + "minNotional": 40000000, # stake = 8000000.0 "maxNotional": 100000000, # max_stake = 20000000.0 "maintenanceMarginRate": 0.1, "maxLeverage": 5, - "maintAmt": 2203800.0 + "maintAmt": 2203800.0, }, { "minNotional": 100000000, # stake = 25000000.0 "maxNotional": 200000000, # max_stake = 50000000.0 "maintenanceMarginRate": 0.125, "maxLeverage": 4, - "maintAmt": 4703800.0 + "maintAmt": 4703800.0, }, { "minNotional": 200000000, # stake = 66666666.666666664 "maxNotional": 400000000, # max_stake = 133333333.33333333 "maintenanceMarginRate": 0.15, "maxLeverage": 3, - "maintAmt": 9703800.0 + "maintAmt": 9703800.0, }, { "minNotional": 400000000, # stake = 200000000.0 "maxNotional": 600000000, # max_stake = 300000000.0 "maintenanceMarginRate": 0.25, "maxLeverage": 2, - "maintAmt": 4.97038E7 + "maintAmt": 4.97038e7, }, { - "minNotional": 600000000, # stake = 600000000.0 + "minNotional": 600000000, # stake = 600000000.0 "maxNotional": 1000000000, # max_stake = 1000000000.0 "maintenanceMarginRate": 0.5, "maxLeverage": 1, - "maintAmt": 1.997038E8 + "maintAmt": 1.997038e8, }, ], "ZEC/USDT:USDT": [ { - 'minNotional': 0, - 'maxNotional': 50000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 50, - 'maintAmt': 0.0 + "minNotional": 0, + "maxNotional": 50000, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50, + "maintAmt": 0.0, }, { - 'minNotional': 50000, - 'maxNotional': 150000, - 'maintenanceMarginRate': 0.025, - 'maxLeverage': 20, - 'maintAmt': 750.0 + "minNotional": 50000, + "maxNotional": 150000, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20, + "maintAmt": 750.0, }, { - 'minNotional': 150000, - 'maxNotional': 250000, - 'maintenanceMarginRate': 0.05, - 'maxLeverage': 10, - 'maintAmt': 4500.0 + "minNotional": 150000, + "maxNotional": 250000, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10, + "maintAmt": 4500.0, }, { - 'minNotional': 250000, - 'maxNotional': 500000, - 'maintenanceMarginRate': 0.1, - 'maxLeverage': 5, - 'maintAmt': 17000.0 + "minNotional": 250000, + "maxNotional": 500000, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5, + "maintAmt": 17000.0, }, { - 'minNotional': 500000, - 'maxNotional': 1000000, - 'maintenanceMarginRate': 0.125, - 'maxLeverage': 4, - 'maintAmt': 29500.0 + "minNotional": 500000, + "maxNotional": 1000000, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4, + "maintAmt": 29500.0, }, { - 'minNotional': 1000000, - 'maxNotional': 2000000, - 'maintenanceMarginRate': 0.25, - 'maxLeverage': 2, - 'maintAmt': 154500.0 + "minNotional": 1000000, + "maxNotional": 2000000, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2, + "maintAmt": 154500.0, }, { - 'minNotional': 2000000, - 'maxNotional': 30000000, - 'maintenanceMarginRate': 0.5, - 'maxLeverage': 1, - 'maintAmt': 654500.0 + "minNotional": 2000000, + "maxNotional": 30000000, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1, + "maintAmt": 654500.0, }, - ] + ], } diff --git a/tests/test_arguments.py b/tests/test_arguments.py index e14b09719..488363685 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -12,7 +12,7 @@ from tests.conftest import CURRENT_TEST_STRATEGY # Parse common command-line-arguments. Used for all tools def test_parse_args_none() -> None: - arguments = Arguments(['trade']) + arguments = Arguments(["trade"]) assert isinstance(arguments, Arguments) x = arguments.get_parsed_arg() assert isinstance(x, dict) @@ -20,265 +20,278 @@ def test_parse_args_none() -> None: def test_parse_args_defaults(mocker) -> None: - mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[False, True])) - args = Arguments(['trade']).get_parsed_arg() - assert args['config'] == ['config.json'] - assert args['strategy_path'] is None - assert args['datadir'] is None - assert args['verbosity'] == 0 + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) + args = Arguments(["trade"]).get_parsed_arg() + assert args["config"] == ["config.json"] + assert args["strategy_path"] is None + assert args["datadir"] is None + assert args["verbosity"] == 0 def test_parse_args_default_userdatadir(mocker) -> None: - mocker.patch.object(Path, 'is_file', MagicMock(return_value=True)) - args = Arguments(['trade']).get_parsed_arg() + mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) + args = Arguments(["trade"]).get_parsed_arg() # configuration defaults to user_data if that is available. - assert args['config'] == [str(Path('user_data/config.json'))] - assert args['strategy_path'] is None - assert args['datadir'] is None - assert args['verbosity'] == 0 + assert args["config"] == [str(Path("user_data/config.json"))] + assert args["strategy_path"] is None + assert args["datadir"] is None + assert args["verbosity"] == 0 def test_parse_args_userdatadir(mocker) -> None: - mocker.patch.object(Path, 'is_file', MagicMock(return_value=True)) - args = Arguments(['trade', '--user-data-dir', 'user_data']).get_parsed_arg() + mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) + args = Arguments(["trade", "--user-data-dir", "user_data"]).get_parsed_arg() # configuration defaults to user_data if that is available. - assert args['config'] == [str(Path('user_data/config.json'))] - assert args['strategy_path'] is None - assert args['datadir'] is None - assert args['verbosity'] == 0 + assert args["config"] == [str(Path("user_data/config.json"))] + assert args["strategy_path"] is None + assert args["datadir"] is None + assert args["verbosity"] == 0 def test_parse_args_config() -> None: - args = Arguments(['trade', '-c', '/dev/null']).get_parsed_arg() - assert args['config'] == ['/dev/null'] + args = Arguments(["trade", "-c", "/dev/null"]).get_parsed_arg() + assert args["config"] == ["/dev/null"] - args = Arguments(['trade', '--config', '/dev/null']).get_parsed_arg() - assert args['config'] == ['/dev/null'] + args = Arguments(["trade", "--config", "/dev/null"]).get_parsed_arg() + assert args["config"] == ["/dev/null"] - args = Arguments(['trade', '--config', '/dev/null', - '--config', '/dev/zero'],).get_parsed_arg() - assert args['config'] == ['/dev/null', '/dev/zero'] + args = Arguments( + ["trade", "--config", "/dev/null", "--config", "/dev/zero"], + ).get_parsed_arg() + assert args["config"] == ["/dev/null", "/dev/zero"] def test_parse_args_db_url() -> None: - args = Arguments(['trade', '--db-url', 'sqlite:///test.sqlite']).get_parsed_arg() - assert args['db_url'] == 'sqlite:///test.sqlite' + args = Arguments(["trade", "--db-url", "sqlite:///test.sqlite"]).get_parsed_arg() + assert args["db_url"] == "sqlite:///test.sqlite" def test_parse_args_verbose() -> None: - args = Arguments(['trade', '-v']).get_parsed_arg() - assert args['verbosity'] == 1 + args = Arguments(["trade", "-v"]).get_parsed_arg() + assert args["verbosity"] == 1 - args = Arguments(['trade', '--verbose']).get_parsed_arg() - assert args['verbosity'] == 1 + args = Arguments(["trade", "--verbose"]).get_parsed_arg() + assert args["verbosity"] == 1 def test_common_scripts_options() -> None: - args = Arguments(['download-data', '-p', 'ETH/BTC', 'XRP/BTC']).get_parsed_arg() + args = Arguments(["download-data", "-p", "ETH/BTC", "XRP/BTC"]).get_parsed_arg() - assert args['pairs'] == ['ETH/BTC', 'XRP/BTC'] - assert 'func' in args + assert args["pairs"] == ["ETH/BTC", "XRP/BTC"] + assert "func" in args def test_parse_args_version() -> None: - with pytest.raises(SystemExit, match=r'0'): - Arguments(['--version']).get_parsed_arg() + with pytest.raises(SystemExit, match=r"0"): + Arguments(["--version"]).get_parsed_arg() def test_parse_args_invalid() -> None: - with pytest.raises(SystemExit, match=r'2'): - Arguments(['-c']).get_parsed_arg() + with pytest.raises(SystemExit, match=r"2"): + Arguments(["-c"]).get_parsed_arg() def test_parse_args_strategy() -> None: - args = Arguments(['trade', '--strategy', 'SomeStrategy']).get_parsed_arg() - assert args['strategy'] == 'SomeStrategy' + args = Arguments(["trade", "--strategy", "SomeStrategy"]).get_parsed_arg() + assert args["strategy"] == "SomeStrategy" def test_parse_args_strategy_invalid() -> None: - with pytest.raises(SystemExit, match=r'2'): - Arguments(['--strategy']).get_parsed_arg() + with pytest.raises(SystemExit, match=r"2"): + Arguments(["--strategy"]).get_parsed_arg() def test_parse_args_strategy_path() -> None: - args = Arguments(['trade', '--strategy-path', '/some/path']).get_parsed_arg() - assert args['strategy_path'] == '/some/path' + args = Arguments(["trade", "--strategy-path", "/some/path"]).get_parsed_arg() + assert args["strategy_path"] == "/some/path" def test_parse_args_strategy_path_invalid() -> None: - with pytest.raises(SystemExit, match=r'2'): - Arguments(['--strategy-path']).get_parsed_arg() + with pytest.raises(SystemExit, match=r"2"): + Arguments(["--strategy-path"]).get_parsed_arg() def test_parse_args_backtesting_invalid() -> None: - with pytest.raises(SystemExit, match=r'2'): - Arguments(['backtesting --timeframe']).get_parsed_arg() + with pytest.raises(SystemExit, match=r"2"): + Arguments(["backtesting --timeframe"]).get_parsed_arg() - with pytest.raises(SystemExit, match=r'2'): - Arguments(['backtesting --timeframe', 'abc']).get_parsed_arg() + with pytest.raises(SystemExit, match=r"2"): + Arguments(["backtesting --timeframe", "abc"]).get_parsed_arg() def test_parse_args_backtesting_custom() -> None: args = [ - 'backtesting', - '-c', 'test_conf.json', - '--timeframe', '1m', - '--strategy-list', + "backtesting", + "-c", + "test_conf.json", + "--timeframe", + "1m", + "--strategy-list", CURRENT_TEST_STRATEGY, - 'SampleStrategy' + "SampleStrategy", ] call_args = Arguments(args).get_parsed_arg() - assert call_args['config'] == ['test_conf.json'] - assert call_args['verbosity'] == 0 - assert call_args['command'] == 'backtesting' - assert call_args['func'] is not None - assert call_args['timeframe'] == '1m' - assert isinstance(call_args['strategy_list'], list) - assert len(call_args['strategy_list']) == 2 + assert call_args["config"] == ["test_conf.json"] + assert call_args["verbosity"] == 0 + assert call_args["command"] == "backtesting" + assert call_args["func"] is not None + assert call_args["timeframe"] == "1m" + assert isinstance(call_args["strategy_list"], list) + assert len(call_args["strategy_list"]) == 2 def test_parse_args_hyperopt_custom() -> None: - args = [ - 'hyperopt', - '-c', 'test_conf.json', - '--epochs', '20', - '--spaces', 'buy' - ] + args = ["hyperopt", "-c", "test_conf.json", "--epochs", "20", "--spaces", "buy"] call_args = Arguments(args).get_parsed_arg() - assert call_args['config'] == ['test_conf.json'] - assert call_args['epochs'] == 20 - assert call_args['verbosity'] == 0 - assert call_args['command'] == 'hyperopt' - assert call_args['spaces'] == ['buy'] - assert call_args['func'] is not None - assert callable(call_args['func']) + assert call_args["config"] == ["test_conf.json"] + assert call_args["epochs"] == 20 + assert call_args["verbosity"] == 0 + assert call_args["command"] == "hyperopt" + assert call_args["spaces"] == ["buy"] + assert call_args["func"] is not None + assert callable(call_args["func"]) def test_download_data_options() -> None: args = [ - 'download-data', - '--datadir', 'datadir/directory', - '--pairs-file', 'file_with_pairs', - '--days', '30', - '--exchange', 'binance' + "download-data", + "--datadir", + "datadir/directory", + "--pairs-file", + "file_with_pairs", + "--days", + "30", + "--exchange", + "binance", ] pargs = Arguments(args).get_parsed_arg() - assert pargs['pairs_file'] == 'file_with_pairs' - assert pargs['datadir'] == 'datadir/directory' - assert pargs['days'] == 30 - assert pargs['exchange'] == 'binance' + assert pargs["pairs_file"] == "file_with_pairs" + assert pargs["datadir"] == "datadir/directory" + assert pargs["days"] == 30 + assert pargs["exchange"] == "binance" def test_plot_dataframe_options() -> None: args = [ - 'plot-dataframe', - '-c', 'tests/testdata/testconfigs/main_test_config.json', - '--indicators1', 'sma10', 'sma100', - '--indicators2', 'macd', 'fastd', 'fastk', - '--plot-limit', '30', - '-p', 'UNITTEST/BTC', + "plot-dataframe", + "-c", + "tests/testdata/testconfigs/main_test_config.json", + "--indicators1", + "sma10", + "sma100", + "--indicators2", + "macd", + "fastd", + "fastk", + "--plot-limit", + "30", + "-p", + "UNITTEST/BTC", ] pargs = Arguments(args).get_parsed_arg() - assert pargs['indicators1'] == ['sma10', 'sma100'] - assert pargs['indicators2'] == ['macd', 'fastd', 'fastk'] - assert pargs['plot_limit'] == 30 - assert pargs['pairs'] == ['UNITTEST/BTC'] + assert pargs["indicators1"] == ["sma10", "sma100"] + assert pargs["indicators2"] == ["macd", "fastd", "fastk"] + assert pargs["plot_limit"] == 30 + assert pargs["pairs"] == ["UNITTEST/BTC"] -@pytest.mark.parametrize('auto_open_arg', [True, False]) +@pytest.mark.parametrize("auto_open_arg", [True, False]) def test_plot_profit_options(auto_open_arg: bool) -> None: args = [ - 'plot-profit', - '-p', 'UNITTEST/BTC', - '--trade-source', 'DB', - '--db-url', 'sqlite:///whatever.sqlite', + "plot-profit", + "-p", + "UNITTEST/BTC", + "--trade-source", + "DB", + "--db-url", + "sqlite:///whatever.sqlite", ] if auto_open_arg: - args.append('--auto-open') + args.append("--auto-open") pargs = Arguments(args).get_parsed_arg() - assert pargs['trade_source'] == 'DB' - assert pargs['pairs'] == ['UNITTEST/BTC'] - assert pargs['db_url'] == 'sqlite:///whatever.sqlite' - assert pargs['plot_auto_open'] == auto_open_arg + assert pargs["trade_source"] == "DB" + assert pargs["pairs"] == ["UNITTEST/BTC"] + assert pargs["db_url"] == "sqlite:///whatever.sqlite" + assert pargs["plot_auto_open"] == auto_open_arg def test_config_notallowed(mocker) -> None: - mocker.patch.object(Path, 'is_file', MagicMock(return_value=False)) + mocker.patch.object(Path, "is_file", MagicMock(return_value=False)) args = [ - 'create-userdir', + "create-userdir", ] pargs = Arguments(args).get_parsed_arg() - assert 'config' not in pargs + assert "config" not in pargs # When file exists: - mocker.patch.object(Path, 'is_file', MagicMock(return_value=True)) + mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) args = [ - 'create-userdir', + "create-userdir", ] pargs = Arguments(args).get_parsed_arg() # config is not added even if it exists, since create-userdir is in the notallowed list - assert 'config' not in pargs + assert "config" not in pargs def test_config_notrequired(mocker) -> None: - mocker.patch.object(Path, 'is_file', MagicMock(return_value=False)) + mocker.patch.object(Path, "is_file", MagicMock(return_value=False)) args = [ - 'download-data', + "download-data", ] pargs = Arguments(args).get_parsed_arg() - assert pargs['config'] is None + assert pargs["config"] is None # When file exists: - mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[False, True])) + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) args = [ - 'download-data', + "download-data", ] pargs = Arguments(args).get_parsed_arg() # config is added if it exists - assert pargs['config'] == ['config.json'] + assert pargs["config"] == ["config.json"] def test_check_int_positive() -> None: - assert check_int_positive('3') == 3 - assert check_int_positive('1') == 1 - assert check_int_positive('100') == 100 + assert check_int_positive("3") == 3 + assert check_int_positive("1") == 1 + assert check_int_positive("100") == 100 with pytest.raises(argparse.ArgumentTypeError): - check_int_positive('-2') + check_int_positive("-2") with pytest.raises(argparse.ArgumentTypeError): - check_int_positive('0') + check_int_positive("0") with pytest.raises(argparse.ArgumentTypeError): check_int_positive(0) with pytest.raises(argparse.ArgumentTypeError): - check_int_positive('3.5') + check_int_positive("3.5") with pytest.raises(argparse.ArgumentTypeError): - check_int_positive('DeadBeef') + check_int_positive("DeadBeef") def test_check_int_nonzero() -> None: - assert check_int_nonzero('3') == 3 - assert check_int_nonzero('1') == 1 - assert check_int_nonzero('100') == 100 + assert check_int_nonzero("3") == 3 + assert check_int_nonzero("1") == 1 + assert check_int_nonzero("100") == 100 - assert check_int_nonzero('-2') == -2 + assert check_int_nonzero("-2") == -2 with pytest.raises(argparse.ArgumentTypeError): - check_int_nonzero('0') + check_int_nonzero("0") with pytest.raises(argparse.ArgumentTypeError): check_int_nonzero(0) with pytest.raises(argparse.ArgumentTypeError): - check_int_nonzero('3.5') + check_int_nonzero("3.5") with pytest.raises(argparse.ArgumentTypeError): - check_int_nonzero('DeadBeef') + check_int_nonzero("DeadBeef") diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 1a2a4a129..104987b99 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -45,85 +45,86 @@ def all_conf(): def test_load_config_missing_attributes(default_conf) -> None: conf = deepcopy(default_conf) - conf.pop('exchange') + conf.pop("exchange") with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"): validate_config_schema(conf) conf = deepcopy(default_conf) - conf.pop('stake_currency') - conf['runmode'] = RunMode.DRY_RUN + conf.pop("stake_currency") + conf["runmode"] = RunMode.DRY_RUN with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"): validate_config_schema(conf) def test_load_config_incorrect_stake_amount(default_conf) -> None: - default_conf['stake_amount'] = 'fake' + default_conf["stake_amount"] = "fake" with pytest.raises(ValidationError, match=r".*'fake' does not match 'unlimited'.*"): validate_config_schema(default_conf) def test_load_config_file(default_conf, mocker, caplog) -> None: - del default_conf['user_data_dir'] - default_conf['datadir'] = str(default_conf['datadir']) - file_mock = mocker.patch('freqtrade.configuration.load_config.Path.open', mocker.mock_open( - read_data=json.dumps(default_conf) - )) + del default_conf["user_data_dir"] + default_conf["datadir"] = str(default_conf["datadir"]) + file_mock = mocker.patch( + "freqtrade.configuration.load_config.Path.open", + mocker.mock_open(read_data=json.dumps(default_conf)), + ) - validated_conf = load_config_file('somefile') + validated_conf = load_config_file("somefile") assert file_mock.call_count == 1 assert validated_conf.items() >= default_conf.items() def test_load_config_file_error(default_conf, mocker, caplog) -> None: - del default_conf['user_data_dir'] - default_conf['datadir'] = str(default_conf['datadir']) - filedata = json.dumps(default_conf).replace( - '"stake_amount": 0.001,', '"stake_amount": .001,') - mocker.patch('freqtrade.configuration.load_config.Path.open', - mocker.mock_open(read_data=filedata)) + del default_conf["user_data_dir"] + default_conf["datadir"] = str(default_conf["datadir"]) + filedata = json.dumps(default_conf).replace('"stake_amount": 0.001,', '"stake_amount": .001,') + mocker.patch( + "freqtrade.configuration.load_config.Path.open", mocker.mock_open(read_data=filedata) + ) mocker.patch.object(Path, "read_text", MagicMock(return_value=filedata)) with pytest.raises(OperationalException, match=r".*Please verify the following segment.*"): - load_config_file('somefile') + load_config_file("somefile") def test_load_config_file_error_range(default_conf, mocker, caplog) -> None: - del default_conf['user_data_dir'] - default_conf['datadir'] = str(default_conf['datadir']) - filedata = json.dumps(default_conf).replace( - '"stake_amount": 0.001,', '"stake_amount": .001,') + del default_conf["user_data_dir"] + default_conf["datadir"] = str(default_conf["datadir"]) + filedata = json.dumps(default_conf).replace('"stake_amount": 0.001,', '"stake_amount": .001,') mocker.patch.object(Path, "read_text", MagicMock(return_value=filedata)) - x = log_config_error_range('somefile', 'Parse error at offset 64: Invalid value.') + x = log_config_error_range("somefile", "Parse error at offset 64: Invalid value.") assert isinstance(x, str) - assert (x == '{"max_open_trades": 1, "stake_currency": "BTC", ' - '"stake_amount": .001, "fiat_display_currency": "USD", ' - '"timeframe": "5m", "dry_run": true, "cance') + assert ( + x == '{"max_open_trades": 1, "stake_currency": "BTC", ' + '"stake_amount": .001, "fiat_display_currency": "USD", ' + '"timeframe": "5m", "dry_run": true, "cance' + ) filedata = json.dumps(default_conf, indent=2).replace( - '"stake_amount": 0.001,', '"stake_amount": .001,') + '"stake_amount": 0.001,', '"stake_amount": .001,' + ) mocker.patch.object(Path, "read_text", MagicMock(return_value=filedata)) - x = log_config_error_range('somefile', 'Parse error at offset 4: Invalid value.') + x = log_config_error_range("somefile", "Parse error at offset 4: Invalid value.") assert isinstance(x, str) - assert (x == ' "max_open_trades": 1,\n "stake_currency": "BTC",\n' - ' "stake_amount": .001,') + assert x == ' "max_open_trades": 1,\n "stake_currency": "BTC",\n' ' "stake_amount": .001,' - x = log_config_error_range('-', '') - assert x == '' + x = log_config_error_range("-", "") + assert x == "" def test_load_file_error(tmp_path): - testpath = tmp_path / 'config.json' + testpath = tmp_path / "config.json" with pytest.raises(OperationalException, match=r"File .* not found!"): load_file(testpath) def test__args_to_config(caplog): - - arg_list = ['trade', '--strategy-path', 'TestTest'] + arg_list = ["trade", "--strategy-path", "TestTest"] args = Arguments(arg_list).get_parsed_arg() configuration = Configuration(args) config = {} @@ -133,90 +134,94 @@ def test__args_to_config(caplog): configuration._args_to_config(config, argname="strategy_path", logstring="DeadBeef") assert len(w) == 0 assert log_has("DeadBeef", caplog) - assert config['strategy_path'] == "TestTest" + assert config["strategy_path"] == "TestTest" configuration = Configuration(args) config = {} with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") # Deprecation warnings! - configuration._args_to_config(config, argname="strategy_path", logstring="DeadBeef", - deprecated_msg="Going away soon!") + configuration._args_to_config( + config, argname="strategy_path", logstring="DeadBeef", deprecated_msg="Going away soon!" + ) assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) assert "DEPRECATED: Going away soon!" in str(w[-1].message) assert log_has("DeadBeef", caplog) - assert config['strategy_path'] == "TestTest" + assert config["strategy_path"] == "TestTest" def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None: - default_conf['max_open_trades'] = 0 + default_conf["max_open_trades"] = 0 patched_configuration_load_config_file(mocker, default_conf) - args = Arguments(['trade']).get_parsed_arg() + args = Arguments(["trade"]).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf['max_open_trades'] == 0 - assert 'internals' in validated_conf + assert validated_conf["max_open_trades"] == 0 + assert "internals" in validated_conf def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: conf1 = deepcopy(default_conf) conf2 = deepcopy(default_conf) - del conf1['exchange']['key'] - del conf1['exchange']['secret'] - del conf2['exchange']['name'] - conf2['exchange']['pair_whitelist'] += ['NANO/BTC'] + del conf1["exchange"]["key"] + del conf1["exchange"]["secret"] + del conf2["exchange"]["name"] + conf2["exchange"]["pair_whitelist"] += ["NANO/BTC"] config_files = [conf1, conf2] configsmock = MagicMock(side_effect=config_files) - mocker.patch( - 'freqtrade.configuration.load_config.load_config_file', - configsmock - ) + mocker.patch("freqtrade.configuration.load_config.load_config_file", configsmock) - arg_list = ['trade', '-c', 'test_conf.json', '--config', 'test2_conf.json', ] + arg_list = [ + "trade", + "-c", + "test_conf.json", + "--config", + "test2_conf.json", + ] args = Arguments(arg_list).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - exchange_conf = default_conf['exchange'] - assert validated_conf['exchange']['name'] == exchange_conf['name'] - assert validated_conf['exchange']['key'] == exchange_conf['key'] - assert validated_conf['exchange']['secret'] == exchange_conf['secret'] - assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist'] - assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] + exchange_conf = default_conf["exchange"] + assert validated_conf["exchange"]["name"] == exchange_conf["name"] + assert validated_conf["exchange"]["key"] == exchange_conf["key"] + assert validated_conf["exchange"]["secret"] == exchange_conf["secret"] + assert validated_conf["exchange"]["pair_whitelist"] != conf1["exchange"]["pair_whitelist"] + assert validated_conf["exchange"]["pair_whitelist"] == conf2["exchange"]["pair_whitelist"] - assert 'internals' in validated_conf + assert "internals" in validated_conf def test_from_config(default_conf, mocker, caplog) -> None: conf1 = deepcopy(default_conf) conf2 = deepcopy(default_conf) - del conf1['exchange']['key'] - del conf1['exchange']['secret'] - del conf2['exchange']['name'] - conf2['exchange']['pair_whitelist'] += ['NANO/BTC'] - conf2['fiat_display_currency'] = "EUR" + del conf1["exchange"]["key"] + del conf1["exchange"]["secret"] + del conf2["exchange"]["name"] + conf2["exchange"]["pair_whitelist"] += ["NANO/BTC"] + conf2["fiat_display_currency"] = "EUR" config_files = [conf1, conf2] - mocker.patch('freqtrade.configuration.configuration.create_datadir', lambda c, x: x) + mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) configsmock = MagicMock(side_effect=config_files) - mocker.patch('freqtrade.configuration.load_config.load_config_file', configsmock) + mocker.patch("freqtrade.configuration.load_config.load_config_file", configsmock) - validated_conf = Configuration.from_files(['test_conf.json', 'test2_conf.json']) + validated_conf = Configuration.from_files(["test_conf.json", "test2_conf.json"]) - exchange_conf = default_conf['exchange'] - assert validated_conf['exchange']['name'] == exchange_conf['name'] - assert validated_conf['exchange']['key'] == exchange_conf['key'] - assert validated_conf['exchange']['secret'] == exchange_conf['secret'] - assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist'] - assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] - assert validated_conf['fiat_display_currency'] == "EUR" - assert 'internals' in validated_conf - assert isinstance(validated_conf['user_data_dir'], Path) + exchange_conf = default_conf["exchange"] + assert validated_conf["exchange"]["name"] == exchange_conf["name"] + assert validated_conf["exchange"]["key"] == exchange_conf["key"] + assert validated_conf["exchange"]["secret"] == exchange_conf["secret"] + assert validated_conf["exchange"]["pair_whitelist"] != conf1["exchange"]["pair_whitelist"] + assert validated_conf["exchange"]["pair_whitelist"] == conf2["exchange"]["pair_whitelist"] + assert validated_conf["fiat_display_currency"] == "EUR" + assert "internals" in validated_conf + assert isinstance(validated_conf["user_data_dir"], Path) def test_from_recursive_files(testdatadir) -> None: @@ -226,20 +231,20 @@ def test_from_recursive_files(testdatadir) -> None: assert conf # Exchange comes from "the first config" - assert conf['exchange'] + assert conf["exchange"] # Pricing comes from the 2nd config - assert conf['entry_pricing'] - assert conf['entry_pricing']['price_side'] == "same" - assert conf['exit_pricing'] + assert conf["entry_pricing"] + assert conf["entry_pricing"]["price_side"] == "same" + assert conf["exit_pricing"] # The other key comes from pricing2, which is imported by pricing.json. # pricing.json is a level higher, therefore wins. - assert conf['exit_pricing']['price_side'] == "same" + assert conf["exit_pricing"]["price_side"] == "same" - assert len(conf['config_files']) == 4 - assert 'testconfig.json' in conf['config_files'][0] - assert 'test_pricing_conf.json' in conf['config_files'][1] - assert 'test_base_config.json' in conf['config_files'][2] - assert 'test_pricing2_conf.json' in conf['config_files'][3] + assert len(conf["config_files"]) == 4 + assert "testconfig.json" in conf["config_files"][0] + assert "test_pricing_conf.json" in conf["config_files"][1] + assert "test_base_config.json" in conf["config_files"][2] + assert "test_pricing2_conf.json" in conf["config_files"][3] files = testdatadir / "testconfigs/recursive.json" with pytest.raises(OperationalException, match="Config loop detected."): @@ -249,74 +254,77 @@ def test_from_recursive_files(testdatadir) -> None: def test_print_config(default_conf, mocker, caplog) -> None: conf1 = deepcopy(default_conf) # Delete non-json elements from default_conf - del conf1['user_data_dir'] - conf1['datadir'] = str(conf1['datadir']) + del conf1["user_data_dir"] + conf1["datadir"] = str(conf1["datadir"]) config_files = [conf1] configsmock = MagicMock(side_effect=config_files) - mocker.patch('freqtrade.configuration.configuration.create_datadir', lambda c, x: x) - mocker.patch('freqtrade.configuration.configuration.load_from_files', configsmock) + mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) + mocker.patch("freqtrade.configuration.configuration.load_from_files", configsmock) - validated_conf = Configuration.from_files(['test_conf.json']) + validated_conf = Configuration.from_files(["test_conf.json"]) - assert isinstance(validated_conf['user_data_dir'], Path) + assert isinstance(validated_conf["user_data_dir"], Path) assert "user_data_dir" in validated_conf assert "original_config" in validated_conf - assert isinstance(json.dumps(validated_conf['original_config']), str) + assert isinstance(json.dumps(validated_conf["original_config"]), str) def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None: - default_conf['max_open_trades'] = -1 + default_conf["max_open_trades"] = -1 patched_configuration_load_config_file(mocker, default_conf) - args = Arguments(['trade']).get_parsed_arg() + args = Arguments(["trade"]).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf['max_open_trades'] > 999999999 - assert validated_conf['max_open_trades'] == float('inf') + assert validated_conf["max_open_trades"] > 999999999 + assert validated_conf["max_open_trades"] == float("inf") assert "runmode" in validated_conf - assert validated_conf['runmode'] == RunMode.DRY_RUN + assert validated_conf["runmode"] == RunMode.DRY_RUN def test_load_config_file_exception(mocker) -> None: mocker.patch( - 'freqtrade.configuration.configuration.Path.open', - MagicMock(side_effect=FileNotFoundError('File not found')) + "freqtrade.configuration.configuration.Path.open", + MagicMock(side_effect=FileNotFoundError("File not found")), ) with pytest.raises(OperationalException, match=r'.*Config file "somefile" not found!*'): - load_config_file('somefile') + load_config_file("somefile") def test_load_config(default_conf, mocker) -> None: - del default_conf['strategy_path'] + del default_conf["strategy_path"] patched_configuration_load_config_file(mocker, default_conf) - args = Arguments(['trade']).get_parsed_arg() + args = Arguments(["trade"]).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('strategy_path') is None - assert 'edge' not in validated_conf + assert validated_conf.get("strategy_path") is None + assert "edge" not in validated_conf def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'trade', - '--strategy', 'TestStrategy', - '--strategy-path', '/some/path', - '--db-url', 'sqlite:///someurl', + "trade", + "--strategy", + "TestStrategy", + "--strategy-path", + "/some/path", + "--db-url", + "sqlite:///someurl", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('strategy') == 'TestStrategy' - assert validated_conf.get('strategy_path') == '/some/path' - assert validated_conf.get('db_url') == 'sqlite:///someurl' + assert validated_conf.get("strategy") == "TestStrategy" + assert validated_conf.get("strategy_path") == "/some/path" + assert validated_conf.get("db_url") == "sqlite:///someurl" # Test conf provided db_url prod conf = default_conf.copy() @@ -324,16 +332,12 @@ def test_load_config_with_params(default_conf, mocker) -> None: conf["db_url"] = "sqlite:///path/to/db.sqlite" patched_configuration_load_config_file(mocker, conf) - arglist = [ - 'trade', - '--strategy', 'TestStrategy', - '--strategy-path', '/some/path' - ] + arglist = ["trade", "--strategy", "TestStrategy", "--strategy-path", "/some/path"] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite" + assert validated_conf.get("db_url") == "sqlite:///path/to/db.sqlite" # Test conf provided db_url dry_run conf = default_conf.copy() @@ -341,16 +345,12 @@ def test_load_config_with_params(default_conf, mocker) -> None: conf["db_url"] = "sqlite:///path/to/db.sqlite" patched_configuration_load_config_file(mocker, conf) - arglist = [ - 'trade', - '--strategy', 'TestStrategy', - '--strategy-path', '/some/path' - ] + arglist = ["trade", "--strategy", "TestStrategy", "--strategy-path", "/some/path"] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite" + assert validated_conf.get("db_url") == "sqlite:///path/to/db.sqlite" # Test args provided db_url prod conf = default_conf.copy() @@ -358,18 +358,14 @@ def test_load_config_with_params(default_conf, mocker) -> None: del conf["db_url"] patched_configuration_load_config_file(mocker, conf) - arglist = [ - 'trade', - '--strategy', 'TestStrategy', - '--strategy-path', '/some/path' - ] + arglist = ["trade", "--strategy", "TestStrategy", "--strategy-path", "/some/path"] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL + assert validated_conf.get("db_url") == DEFAULT_DB_PROD_URL assert "runmode" in validated_conf - assert validated_conf['runmode'] == RunMode.LIVE + assert validated_conf["runmode"] == RunMode.LIVE # Test args provided db_url dry_run conf = default_conf.copy() @@ -377,58 +373,60 @@ def test_load_config_with_params(default_conf, mocker) -> None: conf["db_url"] = DEFAULT_DB_PROD_URL patched_configuration_load_config_file(mocker, conf) - arglist = [ - 'trade', - '--strategy', 'TestStrategy', - '--strategy-path', '/some/path' - ] + arglist = ["trade", "--strategy", "TestStrategy", "--strategy-path", "/some/path"] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('db_url') == DEFAULT_DB_DRYRUN_URL + assert validated_conf.get("db_url") == DEFAULT_DB_DRYRUN_URL -@pytest.mark.parametrize("config_value,expected,arglist", [ - (True, True, ['trade', '--dry-run']), # Leave config untouched - (False, True, ['trade', '--dry-run']), # Override config untouched - (False, False, ['trade']), # Leave config untouched - (True, True, ['trade']), # Leave config untouched -]) +@pytest.mark.parametrize( + "config_value,expected,arglist", + [ + (True, True, ["trade", "--dry-run"]), # Leave config untouched + (False, True, ["trade", "--dry-run"]), # Override config untouched + (False, False, ["trade"]), # Leave config untouched + (True, True, ["trade"]), # Leave config untouched + ], +) def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) -> None: - - default_conf['dry_run'] = config_value + default_conf["dry_run"] = config_value patched_configuration_load_config_file(mocker, default_conf) configuration = Configuration(Arguments(arglist).get_parsed_arg()) validated_conf = configuration.load_config() - assert validated_conf['dry_run'] is expected - assert validated_conf['runmode'] == (RunMode.DRY_RUN if expected else RunMode.LIVE) + assert validated_conf["dry_run"] is expected + assert validated_conf["runmode"] == (RunMode.DRY_RUN if expected else RunMode.LIVE) def test_load_custom_strategy(default_conf, mocker) -> None: - default_conf.update({ - 'strategy': 'CustomStrategy', - 'strategy_path': '/tmp/strategies', - }) + default_conf.update( + { + "strategy": "CustomStrategy", + "strategy_path": "/tmp/strategies", + } + ) patched_configuration_load_config_file(mocker, default_conf) - args = Arguments(['trade']).get_parsed_arg() + args = Arguments(["trade"]).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('strategy') == 'CustomStrategy' - assert validated_conf.get('strategy_path') == '/tmp/strategies' + assert validated_conf.get("strategy") == "CustomStrategy" + assert validated_conf.get("strategy_path") == "/tmp/strategies" def test_show_info(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'trade', - '--strategy', 'TestStrategy', - '--db-url', 'sqlite:///tmp/testdb', + "trade", + "--strategy", + "TestStrategy", + "--db-url", + "sqlite:///tmp/testdb", ] args = Arguments(arglist).get_parsed_arg() @@ -436,95 +434,101 @@ def test_show_info(default_conf, mocker, caplog) -> None: configuration.get_config() assert log_has('Using DB: "sqlite:///tmp/testdb"', caplog) - assert log_has('Dry run is enabled', caplog) + assert log_has("Dry run is enabled", caplog) def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, + "backtesting", + "--config", + "config.json", + "--strategy", + CURRENT_TEST_STRATEGY, ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) config = configuration.get_config() - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert 'user_data_dir' in config - assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'timeframe' in config - assert not log_has('Parameter -i/--timeframe detected ...', caplog) + assert "max_open_trades" in config + assert "stake_currency" in config + assert "stake_amount" in config + assert "exchange" in config + assert "pair_whitelist" in config["exchange"] + assert "datadir" in config + assert "user_data_dir" in config + assert log_has("Using data directory: {} ...".format(config["datadir"]), caplog) + assert "timeframe" in config + assert not log_has("Parameter -i/--timeframe detected ...", caplog) - assert 'position_stacking' not in config - assert not log_has('Parameter --enable-position-stacking detected ...', caplog) + assert "position_stacking" not in config + assert not log_has("Parameter --enable-position-stacking detected ...", caplog) - assert 'timerange' not in config + assert "timerange" not in config def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) + mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) mocker.patch( - 'freqtrade.configuration.configuration.create_datadir', - lambda c, x: x - ) - mocker.patch( - 'freqtrade.configuration.configuration.create_userdata_dir', - lambda x, *args, **kwargs: Path(x) + "freqtrade.configuration.configuration.create_userdata_dir", + lambda x, *args, **kwargs: Path(x), ) arglist = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--datadir', '/foo/bar', - '--userdir', "/tmp/freqtrade", - '--timeframe', '1m', - '--enable-position-stacking', - '--disable-max-market-positions', - '--timerange', ':100', - '--export', 'trades', - '--stake-amount', 'unlimited' + "backtesting", + "--config", + "config.json", + "--strategy", + CURRENT_TEST_STRATEGY, + "--datadir", + "/foo/bar", + "--userdir", + "/tmp/freqtrade", + "--timeframe", + "1m", + "--enable-position-stacking", + "--disable-max-market-positions", + "--timerange", + ":100", + "--export", + "trades", + "--stake-amount", + "unlimited", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) config = configuration.get_config() - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert log_has('Using data directory: {} ...'.format("/foo/bar"), caplog) - assert log_has('Using user-data directory: {} ...'.format(Path("/tmp/freqtrade")), caplog) - assert 'user_data_dir' in config + assert "max_open_trades" in config + assert "stake_currency" in config + assert "stake_amount" in config + assert "exchange" in config + assert "pair_whitelist" in config["exchange"] + assert "datadir" in config + assert log_has("Using data directory: {} ...".format("/foo/bar"), caplog) + assert log_has("Using user-data directory: {} ...".format(Path("/tmp/freqtrade")), caplog) + assert "user_data_dir" in config - assert 'timeframe' in config - assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - caplog) + assert "timeframe" in config + assert log_has("Parameter -i/--timeframe detected ... Using timeframe: 1m ...", caplog) - assert 'position_stacking' in config - assert log_has('Parameter --enable-position-stacking detected ...', caplog) + assert "position_stacking" in config + assert log_has("Parameter --enable-position-stacking detected ...", caplog) - assert 'use_max_market_positions' in config - assert log_has('Parameter --disable-max-market-positions detected ...', caplog) - assert log_has('max_open_trades set to unlimited ...', caplog) + assert "use_max_market_positions" in config + assert log_has("Parameter --disable-max-market-positions detected ...", caplog) + assert log_has("max_open_trades set to unlimited ...", caplog) - assert 'timerange' in config - assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) + assert "timerange" in config + assert log_has("Parameter --timerange detected: {} ...".format(config["timerange"]), caplog) - assert 'export' in config - assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog) - assert 'stake_amount' in config - assert config['stake_amount'] == 'unlimited' + assert "export" in config + assert log_has("Parameter --export detected: {} ...".format(config["export"]), caplog) + assert "stake_amount" in config + assert config["stake_amount"] == "unlimited" def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> None: @@ -534,82 +538,87 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'backtesting', - '--config', 'config.json', - '--timeframe', '1m', - '--export', 'trades', - '--strategy-list', + "backtesting", + "--config", + "config.json", + "--timeframe", + "1m", + "--export", + "trades", + "--strategy-list", CURRENT_TEST_STRATEGY, - 'TestStrategy' + "TestStrategy", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args, RunMode.BACKTEST) config = configuration.get_config() - assert config['runmode'] == RunMode.BACKTEST - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'timeframe' in config - assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - caplog) + assert config["runmode"] == RunMode.BACKTEST + assert "max_open_trades" in config + assert "stake_currency" in config + assert "stake_amount" in config + assert "exchange" in config + assert "pair_whitelist" in config["exchange"] + assert "datadir" in config + assert log_has("Using data directory: {} ...".format(config["datadir"]), caplog) + assert "timeframe" in config + assert log_has("Parameter -i/--timeframe detected ... Using timeframe: 1m ...", caplog) - assert 'strategy_list' in config - assert log_has('Using strategy list of 2 strategies', caplog) + assert "strategy_list" in config + assert log_has("Using strategy list of 2 strategies", caplog) - assert 'position_stacking' not in config + assert "position_stacking" not in config - assert 'use_max_market_positions' not in config + assert "use_max_market_positions" not in config - assert 'timerange' not in config + assert "timerange" not in config - assert 'export' in config - assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog) + assert "export" in config + assert log_has("Parameter --export detected: {} ...".format(config["export"]), caplog) def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'hyperopt', - '--epochs', '10', - '--spaces', 'all', + "hyperopt", + "--epochs", + "10", + "--spaces", + "all", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args, RunMode.HYPEROPT) config = configuration.get_config() - assert 'epochs' in config - assert int(config['epochs']) == 10 - assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...', - caplog) + assert "epochs" in config + assert int(config["epochs"]) == 10 + assert log_has( + "Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...", caplog + ) - assert 'spaces' in config - assert config['spaces'] == ['all'] + assert "spaces" in config + assert config["spaces"] == ["all"] assert log_has("Parameter -s/--spaces detected: ['all']", caplog) assert "runmode" in config - assert config['runmode'] == RunMode.HYPEROPT + assert config["runmode"] == RunMode.HYPEROPT def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) # Prevent setting loggers - mocker.patch('freqtrade.loggers.set_loggers', MagicMock) - arglist = ['trade', '-vvv'] + mocker.patch("freqtrade.loggers.set_loggers", MagicMock) + arglist = ["trade", "-vvv"] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('verbosity') == 3 - assert log_has('Verbosity set to 3', caplog) + assert validated_conf.get("verbosity") == 3 + assert log_has("Verbosity set to 3", caplog) def test_set_logfile(default_conf, mocker, tmp_path): @@ -617,13 +626,15 @@ def test_set_logfile(default_conf, mocker, tmp_path): f = tmp_path / "test_file.log" assert not f.is_file() arglist = [ - 'trade', '--logfile', str(f), + "trade", + "--logfile", + str(f), ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf['logfile'] == str(f) + assert validated_conf["logfile"] == str(f) assert f.is_file() try: f.unlink() @@ -632,15 +643,15 @@ def test_set_logfile(default_conf, mocker, tmp_path): def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None: - default_conf['force_entry_enable'] = True + default_conf["force_entry_enable"] = True patched_configuration_load_config_file(mocker, default_conf) - args = Arguments(['trade']).get_parsed_arg() + args = Arguments(["trade"]).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('force_entry_enable') - assert log_has('`force_entry_enable` RPC message enabled.', caplog) + assert validated_conf.get("force_entry_enable") + assert log_has("`force_entry_enable` RPC message enabled.", caplog) def test_validate_default_conf(default_conf) -> None: @@ -649,15 +660,17 @@ def test_validate_default_conf(default_conf) -> None: def test_validate_max_open_trades(default_conf): - default_conf['max_open_trades'] = float('inf') - default_conf['stake_amount'] = 'unlimited' - with pytest.raises(OperationalException, match='`max_open_trades` and `stake_amount` ' - 'cannot both be unlimited.'): + default_conf["max_open_trades"] = float("inf") + default_conf["stake_amount"] = "unlimited" + with pytest.raises( + OperationalException, + match="`max_open_trades` and `stake_amount` " "cannot both be unlimited.", + ): validate_config_consistency(default_conf) def test_validate_price_side(default_conf): - default_conf['order_types'] = { + default_conf["order_types"] = { "entry": "limit", "exit": "limit", "stoploss": "limit", @@ -667,116 +680,161 @@ def test_validate_price_side(default_conf): validate_config_consistency(default_conf) conf = deepcopy(default_conf) - conf['order_types']['entry'] = 'market' - with pytest.raises(OperationalException, - match='Market entry orders require entry_pricing.price_side = "other".'): + conf["order_types"]["entry"] = "market" + with pytest.raises( + OperationalException, + match='Market entry orders require entry_pricing.price_side = "other".', + ): validate_config_consistency(conf) conf = deepcopy(default_conf) - conf['order_types']['exit'] = 'market' - with pytest.raises(OperationalException, - match='Market exit orders require exit_pricing.price_side = "other".'): + conf["order_types"]["exit"] = "market" + with pytest.raises( + OperationalException, match='Market exit orders require exit_pricing.price_side = "other".' + ): validate_config_consistency(conf) # Validate inversed case conf = deepcopy(default_conf) - conf['order_types']['exit'] = 'market' - conf['order_types']['entry'] = 'market' - conf['exit_pricing']['price_side'] = 'bid' - conf['entry_pricing']['price_side'] = 'ask' + conf["order_types"]["exit"] = "market" + conf["order_types"]["entry"] = "market" + conf["exit_pricing"]["price_side"] = "bid" + conf["entry_pricing"]["price_side"] = "ask" validate_config_consistency(conf) def test_validate_tsl(default_conf): - default_conf['stoploss'] = 0.0 - with pytest.raises(OperationalException, match='The config stoploss needs to be different ' - 'from 0 to avoid problems with sell orders.'): + default_conf["stoploss"] = 0.0 + with pytest.raises( + OperationalException, + match="The config stoploss needs to be different " + "from 0 to avoid problems with sell orders.", + ): validate_config_consistency(default_conf) - default_conf['stoploss'] = -0.10 + default_conf["stoploss"] = -0.10 - default_conf['trailing_stop'] = True - default_conf['trailing_stop_positive'] = 0 - default_conf['trailing_stop_positive_offset'] = 0 + default_conf["trailing_stop"] = True + default_conf["trailing_stop_positive"] = 0 + default_conf["trailing_stop_positive_offset"] = 0 - default_conf['trailing_only_offset_is_reached'] = True - with pytest.raises(OperationalException, - match=r'The config trailing_only_offset_is_reached needs ' - 'trailing_stop_positive_offset to be more than 0 in your config.'): + default_conf["trailing_only_offset_is_reached"] = True + with pytest.raises( + OperationalException, + match=r"The config trailing_only_offset_is_reached needs " + "trailing_stop_positive_offset to be more than 0 in your config.", + ): validate_config_consistency(default_conf) - default_conf['trailing_stop_positive_offset'] = 0.01 - default_conf['trailing_stop_positive'] = 0.015 - with pytest.raises(OperationalException, - match=r'The config trailing_stop_positive_offset needs ' - 'to be greater than trailing_stop_positive in your config.'): + default_conf["trailing_stop_positive_offset"] = 0.01 + default_conf["trailing_stop_positive"] = 0.015 + with pytest.raises( + OperationalException, + match=r"The config trailing_stop_positive_offset needs " + "to be greater than trailing_stop_positive in your config.", + ): validate_config_consistency(default_conf) - default_conf['trailing_stop_positive'] = 0.01 - default_conf['trailing_stop_positive_offset'] = 0.015 + default_conf["trailing_stop_positive"] = 0.01 + default_conf["trailing_stop_positive_offset"] = 0.015 validate_config_consistency(default_conf) # 0 trailing stop positive - results in "Order would trigger immediately" - default_conf['trailing_stop_positive'] = 0 - default_conf['trailing_stop_positive_offset'] = 0.02 - default_conf['trailing_only_offset_is_reached'] = False - with pytest.raises(OperationalException, - match='The config trailing_stop_positive needs to be different from 0 ' - 'to avoid problems with sell orders'): + default_conf["trailing_stop_positive"] = 0 + default_conf["trailing_stop_positive_offset"] = 0.02 + default_conf["trailing_only_offset_is_reached"] = False + with pytest.raises( + OperationalException, + match="The config trailing_stop_positive needs to be different from 0 " + "to avoid problems with sell orders", + ): validate_config_consistency(default_conf) def test_validate_edge2(edge_conf): - edge_conf.update({ - "use_exit_signal": True, - }) + edge_conf.update( + { + "use_exit_signal": True, + } + ) # Passes test validate_config_consistency(edge_conf) - edge_conf.update({ - "use_exit_signal": False, - }) - with pytest.raises(OperationalException, match="Edge requires `use_exit_signal` to be True, " - "otherwise no sells will happen."): + edge_conf.update( + { + "use_exit_signal": False, + } + ) + with pytest.raises( + OperationalException, + match="Edge requires `use_exit_signal` to be True, " "otherwise no sells will happen.", + ): validate_config_consistency(edge_conf) def test_validate_whitelist(default_conf): - default_conf['runmode'] = RunMode.DRY_RUN + default_conf["runmode"] = RunMode.DRY_RUN # Test regular case - has whitelist and uses StaticPairlist validate_config_consistency(default_conf) conf = deepcopy(default_conf) - del conf['exchange']['pair_whitelist'] + del conf["exchange"]["pair_whitelist"] # Test error case - with pytest.raises(OperationalException, - match="StaticPairList requires pair_whitelist to be set."): - + with pytest.raises( + OperationalException, match="StaticPairList requires pair_whitelist to be set." + ): validate_config_consistency(conf) conf = deepcopy(default_conf) - conf.update({"pairlists": [{ - "method": "VolumePairList", - }]}) + conf.update( + { + "pairlists": [ + { + "method": "VolumePairList", + } + ] + } + ) # Dynamic whitelist should not care about pair_whitelist validate_config_consistency(conf) - del conf['exchange']['pair_whitelist'] + del conf["exchange"]["pair_whitelist"] validate_config_consistency(conf) -@pytest.mark.parametrize('protconf,expected', [ - ([], None), - ([{"method": "StoplossGuard", "lookback_period": 2000, "stop_duration_candles": 10}], None), - ([{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}], None), - ([{"method": "StoplossGuard", "lookback_period_candles": 20, "lookback_period": 2000, - "stop_duration": 10}], r'Protections must specify either `lookback_period`.*'), - ([{"method": "StoplossGuard", "lookback_period": 20, "stop_duration": 10, - "stop_duration_candles": 10}], r'Protections must specify either `stop_duration`.*'), -]) +@pytest.mark.parametrize( + "protconf,expected", + [ + ([], None), + ([{"method": "StoplossGuard", "lookback_period": 2000, "stop_duration_candles": 10}], None), + ([{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}], None), + ( + [ + { + "method": "StoplossGuard", + "lookback_period_candles": 20, + "lookback_period": 2000, + "stop_duration": 10, + } + ], + r"Protections must specify either `lookback_period`.*", + ), + ( + [ + { + "method": "StoplossGuard", + "lookback_period": 20, + "stop_duration": 10, + "stop_duration_candles": 10, + } + ], + r"Protections must specify either `stop_duration`.*", + ), + ], +) def test_validate_protections(default_conf, protconf, expected): conf = deepcopy(default_conf) - conf['protections'] = protconf + conf["protections"] = protconf if expected: with pytest.raises(OperationalException, match=expected): validate_config_consistency(conf) @@ -786,144 +844,150 @@ def test_validate_protections(default_conf, protconf, expected): def test_validate_ask_orderbook(default_conf, caplog) -> None: conf = deepcopy(default_conf) - conf['exit_pricing']['use_order_book'] = True - conf['exit_pricing']['order_book_min'] = 2 - conf['exit_pricing']['order_book_max'] = 2 + conf["exit_pricing"]["use_order_book"] = True + conf["exit_pricing"]["order_book_min"] = 2 + conf["exit_pricing"]["order_book_max"] = 2 validate_config_consistency(conf) assert log_has_re(r"DEPRECATED: Please use `order_book_top` instead of.*", caplog) - assert conf['exit_pricing']['order_book_top'] == 2 + assert conf["exit_pricing"]["order_book_top"] == 2 - conf['exit_pricing']['order_book_max'] = 5 + conf["exit_pricing"]["order_book_max"] = 5 - with pytest.raises(OperationalException, - match=r"Using order_book_max != order_book_min in exit_pricing.*"): + with pytest.raises( + OperationalException, match=r"Using order_book_max != order_book_min in exit_pricing.*" + ): validate_config_consistency(conf) def test_validate_time_in_force(default_conf, caplog) -> None: conf = deepcopy(default_conf) - conf['order_time_in_force'] = { - 'buy': 'gtc', - 'sell': 'GTC', + conf["order_time_in_force"] = { + "buy": "gtc", + "sell": "GTC", } validate_config_consistency(conf) assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for time_in_force is.*", caplog) - assert conf['order_time_in_force']['entry'] == 'gtc' - assert conf['order_time_in_force']['exit'] == 'GTC' + assert conf["order_time_in_force"]["entry"] == "gtc" + assert conf["order_time_in_force"]["exit"] == "GTC" conf = deepcopy(default_conf) - conf['order_time_in_force'] = { - 'buy': 'GTC', - 'sell': 'GTC', + conf["order_time_in_force"] = { + "buy": "GTC", + "sell": "GTC", } - conf['trading_mode'] = 'futures' - with pytest.raises(OperationalException, - match=r"Please migrate your time_in_force settings .* 'entry' and 'exit'\."): + conf["trading_mode"] = "futures" + with pytest.raises( + OperationalException, + match=r"Please migrate your time_in_force settings .* 'entry' and 'exit'\.", + ): validate_config_consistency(conf) def test__validate_order_types(default_conf, caplog) -> None: conf = deepcopy(default_conf) - conf['order_types'] = { - 'buy': 'limit', - 'sell': 'market', - 'forcesell': 'market', - 'forcebuy': 'limit', - 'stoploss': 'market', - 'stoploss_on_exchange': False, + conf["order_types"] = { + "buy": "limit", + "sell": "market", + "forcesell": "market", + "forcebuy": "limit", + "stoploss": "market", + "stoploss_on_exchange": False, } validate_config_consistency(conf) assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for order_types is.*", caplog) - assert conf['order_types']['entry'] == 'limit' - assert conf['order_types']['exit'] == 'market' - assert conf['order_types']['force_entry'] == 'limit' - assert 'buy' not in conf['order_types'] - assert 'sell' not in conf['order_types'] - assert 'forcebuy' not in conf['order_types'] - assert 'forcesell' not in conf['order_types'] + assert conf["order_types"]["entry"] == "limit" + assert conf["order_types"]["exit"] == "market" + assert conf["order_types"]["force_entry"] == "limit" + assert "buy" not in conf["order_types"] + assert "sell" not in conf["order_types"] + assert "forcebuy" not in conf["order_types"] + assert "forcesell" not in conf["order_types"] conf = deepcopy(default_conf) - conf['order_types'] = { - 'buy': 'limit', - 'sell': 'market', - 'forcesell': 'market', - 'forcebuy': 'limit', - 'stoploss': 'market', - 'stoploss_on_exchange': False, + conf["order_types"] = { + "buy": "limit", + "sell": "market", + "forcesell": "market", + "forcebuy": "limit", + "stoploss": "market", + "stoploss_on_exchange": False, } - conf['trading_mode'] = 'futures' - with pytest.raises(OperationalException, - match=r"Please migrate your order_types settings to use the new wording\."): + conf["trading_mode"] = "futures" + with pytest.raises( + OperationalException, + match=r"Please migrate your order_types settings to use the new wording\.", + ): validate_config_consistency(conf) def test__validate_unfilledtimeout(default_conf, caplog) -> None: conf = deepcopy(default_conf) - conf['unfilledtimeout'] = { - 'buy': 30, - 'sell': 35, + conf["unfilledtimeout"] = { + "buy": 30, + "sell": 35, } validate_config_consistency(conf) assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for unfilledtimeout is.*", caplog) - assert conf['unfilledtimeout']['entry'] == 30 - assert conf['unfilledtimeout']['exit'] == 35 - assert 'buy' not in conf['unfilledtimeout'] - assert 'sell' not in conf['unfilledtimeout'] + assert conf["unfilledtimeout"]["entry"] == 30 + assert conf["unfilledtimeout"]["exit"] == 35 + assert "buy" not in conf["unfilledtimeout"] + assert "sell" not in conf["unfilledtimeout"] conf = deepcopy(default_conf) - conf['unfilledtimeout'] = { - 'buy': 30, - 'sell': 35, + conf["unfilledtimeout"] = { + "buy": 30, + "sell": 35, } - conf['trading_mode'] = 'futures' + conf["trading_mode"] = "futures" with pytest.raises( - OperationalException, - match=r"Please migrate your unfilledtimeout settings to use the new wording\."): + OperationalException, + match=r"Please migrate your unfilledtimeout settings to use the new wording\.", + ): validate_config_consistency(conf) def test__validate_pricing_rules(default_conf, caplog) -> None: def_conf = deepcopy(default_conf) - del def_conf['entry_pricing'] - del def_conf['exit_pricing'] + del def_conf["entry_pricing"] + del def_conf["exit_pricing"] - def_conf['ask_strategy'] = { - 'price_side': 'ask', - 'use_order_book': True, - 'bid_last_balance': 0.5 + def_conf["ask_strategy"] = { + "price_side": "ask", + "use_order_book": True, + "bid_last_balance": 0.5, } - def_conf['bid_strategy'] = { - 'price_side': 'bid', - 'use_order_book': False, - 'ask_last_balance': 0.7 + def_conf["bid_strategy"] = { + "price_side": "bid", + "use_order_book": False, + "ask_last_balance": 0.7, } conf = deepcopy(def_conf) validate_config_consistency(conf) - assert log_has_re( - r"DEPRECATED: Using 'ask_strategy' and 'bid_strategy' is.*", caplog) - assert conf['exit_pricing']['price_side'] == 'ask' - assert conf['exit_pricing']['use_order_book'] is True - assert conf['exit_pricing']['price_last_balance'] == 0.5 - assert conf['entry_pricing']['price_side'] == 'bid' - assert conf['entry_pricing']['use_order_book'] is False - assert conf['entry_pricing']['price_last_balance'] == 0.7 - assert 'ask_strategy' not in conf - assert 'bid_strategy' not in conf + assert log_has_re(r"DEPRECATED: Using 'ask_strategy' and 'bid_strategy' is.*", caplog) + assert conf["exit_pricing"]["price_side"] == "ask" + assert conf["exit_pricing"]["use_order_book"] is True + assert conf["exit_pricing"]["price_last_balance"] == 0.5 + assert conf["entry_pricing"]["price_side"] == "bid" + assert conf["entry_pricing"]["use_order_book"] is False + assert conf["entry_pricing"]["price_last_balance"] == 0.7 + assert "ask_strategy" not in conf + assert "bid_strategy" not in conf conf = deepcopy(def_conf) - conf['trading_mode'] = 'futures' + conf["trading_mode"] = "futures" with pytest.raises( - OperationalException, - match=r"Please migrate your pricing settings to use the new wording\."): + OperationalException, match=r"Please migrate your pricing settings to use the new wording\." + ): validate_config_consistency(conf) def test__validate_freqai_include_timeframes(default_conf, caplog) -> None: conf = deepcopy(default_conf) - conf.update({ + conf.update( + { "freqai": { "enabled": True, "feature_parameters": { @@ -931,74 +995,81 @@ def test__validate_freqai_include_timeframes(default_conf, caplog) -> None: "include_corr_pairlist": [], }, "data_split_parameters": {}, - "model_training_parameters": {} + "model_training_parameters": {}, } - }) + } + ) with pytest.raises(OperationalException, match=r"Main timeframe of .*"): validate_config_consistency(conf) # Validation pass - conf.update({'timeframe': '1m'}) + conf.update({"timeframe": "1m"}) validate_config_consistency(conf) # Ensure base timeframe is in include_timeframes - conf['freqai']['feature_parameters']['include_timeframes'] = ["5m", "15m"] + conf["freqai"]["feature_parameters"]["include_timeframes"] = ["5m", "15m"] validate_config_consistency(conf) - assert conf['freqai']['feature_parameters']['include_timeframes'] == ["1m", "5m", "15m"] + assert conf["freqai"]["feature_parameters"]["include_timeframes"] == ["1m", "5m", "15m"] - conf.update({'analyze_per_epoch': True}) - with pytest.raises(OperationalException, - match=r"Using analyze-per-epoch .* not supported with a FreqAI strategy."): + conf.update({"analyze_per_epoch": True}) + with pytest.raises( + OperationalException, + match=r"Using analyze-per-epoch .* not supported with a FreqAI strategy.", + ): validate_config_consistency(conf) def test__validate_consumers(default_conf, caplog) -> None: conf = deepcopy(default_conf) - conf.update({ + conf.update({"external_message_consumer": {"enabled": True, "producers": []}}) + with pytest.raises( + OperationalException, match="You must specify at least 1 Producer to connect to." + ): + validate_config_consistency(conf) + + conf = deepcopy(default_conf) + conf.update( + { "external_message_consumer": { "enabled": True, - "producers": [] - } - }) - with pytest.raises(OperationalException, - match="You must specify at least 1 Producer to connect to."): + "producers": [ + { + "name": "default", + "host": "127.0.0.1", + "port": 8081, + "ws_token": "secret_ws_t0ken.", + }, + { + "name": "default", + "host": "127.0.0.1", + "port": 8080, + "ws_token": "secret_ws_t0ken.", + }, + ], + } + } + ) + with pytest.raises( + OperationalException, match="Producer names must be unique. Duplicate: default" + ): validate_config_consistency(conf) conf = deepcopy(default_conf) - conf.update({ - "external_message_consumer": { - "enabled": True, - "producers": [ - { - "name": "default", - "host": "127.0.0.1", - "port": 8081, - "ws_token": "secret_ws_t0ken." - }, { - "name": "default", - "host": "127.0.0.1", - "port": 8080, - "ws_token": "secret_ws_t0ken." - } - ]} - }) - with pytest.raises(OperationalException, - match="Producer names must be unique. Duplicate: default"): - validate_config_consistency(conf) - - conf = deepcopy(default_conf) - conf.update({ - "process_only_new_candles": True, - "external_message_consumer": { - "enabled": True, - "producers": [ - { - "name": "default", - "host": "127.0.0.1", - "port": 8081, - "ws_token": "secret_ws_t0ken." - } - ]} - }) + conf.update( + { + "process_only_new_candles": True, + "external_message_consumer": { + "enabled": True, + "producers": [ + { + "name": "default", + "host": "127.0.0.1", + "port": 8081, + "ws_token": "secret_ws_t0ken.", + } + ], + }, + } + ) validate_config_consistency(conf) assert log_has_re("To receive best performance with external data.*", caplog) @@ -1018,12 +1089,11 @@ def test_load_config_default_exchange(all_conf) -> None: config['exchange'] subtree has required options in it so it cannot be omitted in the config """ - del all_conf['exchange'] + del all_conf["exchange"] - assert 'exchange' not in all_conf + assert "exchange" not in all_conf - with pytest.raises(ValidationError, - match=r"'exchange' is a required property"): + with pytest.raises(ValidationError, match=r"'exchange' is a required property"): validate_config_schema(all_conf) @@ -1032,27 +1102,29 @@ def test_load_config_default_exchange_name(all_conf) -> None: config['exchange']['name'] option is required so it cannot be omitted in the config """ - del all_conf['exchange']['name'] + del all_conf["exchange"]["name"] - assert 'name' not in all_conf['exchange'] + assert "name" not in all_conf["exchange"] - with pytest.raises(ValidationError, - match=r"'name' is a required property"): + with pytest.raises(ValidationError, match=r"'name' is a required property"): validate_config_schema(all_conf) def test_load_config_stoploss_exchange_limit_ratio(all_conf) -> None: - all_conf['order_types']['stoploss_on_exchange_limit_ratio'] = 1.15 + all_conf["order_types"]["stoploss_on_exchange_limit_ratio"] = 1.15 - with pytest.raises(ValidationError, - match=r"1.15 is greater than the maximum"): + with pytest.raises(ValidationError, match=r"1.15 is greater than the maximum"): validate_config_schema(all_conf) -@pytest.mark.parametrize("keys", [("exchange", "key", ""), - ("exchange", "secret", ""), - ("exchange", "password", ""), - ]) +@pytest.mark.parametrize( + "keys", + [ + ("exchange", "key", ""), + ("exchange", "secret", ""), + ("exchange", "password", ""), + ], +) def test_load_config_default_subkeys(all_conf, keys) -> None: """ Test for parameters with default values in sub-paths @@ -1074,27 +1146,24 @@ def test_load_config_default_subkeys(all_conf, keys) -> None: def test_pairlist_resolving(): - arglist = [ - 'download-data', - '--pairs', 'ETH/BTC', 'XRP/BTC', - '--exchange', 'binance' - ] + arglist = ["download-data", "--pairs", "ETH/BTC", "XRP/BTC", "--exchange", "binance"] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args, RunMode.OTHER) config = configuration.get_config() - assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] - assert config['exchange']['pair_whitelist'] == ['ETH/BTC', 'XRP/BTC'] - assert config['exchange']['name'] == 'binance' + assert config["pairs"] == ["ETH/BTC", "XRP/BTC"] + assert config["exchange"]["pair_whitelist"] == ["ETH/BTC", "XRP/BTC"] + assert config["exchange"]["name"] == "binance" def test_pairlist_resolving_with_config(mocker, default_conf): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'download-data', - '--config', 'config.json', + "download-data", + "--config", + "config.json", ] args = Arguments(arglist).get_parsed_arg() @@ -1102,14 +1171,17 @@ def test_pairlist_resolving_with_config(mocker, default_conf): configuration = Configuration(args) config = configuration.get_config() - assert config['pairs'] == default_conf['exchange']['pair_whitelist'] - assert config['exchange']['name'] == default_conf['exchange']['name'] + assert config["pairs"] == default_conf["exchange"]["pair_whitelist"] + assert config["exchange"]["name"] == default_conf["exchange"]["name"] # Override pairs arglist = [ - 'download-data', - '--config', 'config.json', - '--pairs', 'ETH/BTC', 'XRP/BTC', + "download-data", + "--config", + "config.json", + "--pairs", + "ETH/BTC", + "XRP/BTC", ] args = Arguments(arglist).get_parsed_arg() @@ -1117,36 +1189,40 @@ def test_pairlist_resolving_with_config(mocker, default_conf): configuration = Configuration(args) config = configuration.get_config() - assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] - assert config['exchange']['name'] == default_conf['exchange']['name'] + assert config["pairs"] == ["ETH/BTC", "XRP/BTC"] + assert config["exchange"]["name"] == default_conf["exchange"]["name"] def test_pairlist_resolving_with_config_pl(mocker, default_conf): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'download-data', - '--config', 'config.json', - '--pairs-file', 'tests/testdata/pairs.json', + "download-data", + "--config", + "config.json", + "--pairs-file", + "tests/testdata/pairs.json", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) config = configuration.get_config() - assert len(config['pairs']) == 23 - assert 'ETH/BTC' in config['pairs'] - assert 'XRP/BTC' in config['pairs'] - assert config['exchange']['name'] == default_conf['exchange']['name'] + assert len(config["pairs"]) == 23 + assert "ETH/BTC" in config["pairs"] + assert "XRP/BTC" in config["pairs"] + assert config["exchange"]["name"] == default_conf["exchange"]["name"] def test_pairlist_resolving_with_config_pl_not_exists(mocker, default_conf): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - 'download-data', - '--config', 'config.json', - '--pairs-file', 'tests/testdata/pairs_doesnotexist.json', + "download-data", + "--config", + "config.json", + "--pairs-file", + "tests/testdata/pairs_doesnotexist.json", ] args = Arguments(arglist).get_parsed_arg() @@ -1159,31 +1235,38 @@ def test_pairlist_resolving_with_config_pl_not_exists(mocker, default_conf): def test_pairlist_resolving_fallback(mocker, tmp_path): mocker.patch.object(Path, "exists", MagicMock(return_value=True)) mocker.patch.object(Path, "open", MagicMock(return_value=MagicMock())) - mocker.patch("freqtrade.configuration.configuration.load_file", - MagicMock(return_value=['XRP/BTC', 'ETH/BTC'])) - arglist = [ - 'download-data', - '--exchange', 'binance' - ] + mocker.patch( + "freqtrade.configuration.configuration.load_file", + MagicMock(return_value=["XRP/BTC", "ETH/BTC"]), + ) + arglist = ["download-data", "--exchange", "binance"] args = Arguments(arglist).get_parsed_arg() # Fix flaky tests if config.json exists - args['config'] = None + args["config"] = None configuration = Configuration(args, RunMode.OTHER) config = configuration.get_config() - assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] - assert config['exchange']['name'] == 'binance' - assert config['datadir'] == tmp_path / "user_data/data/binance" + assert config["pairs"] == ["ETH/BTC", "XRP/BTC"] + assert config["exchange"]["name"] == "binance" + assert config["datadir"] == tmp_path / "user_data/data/binance" -@pytest.mark.parametrize("setting", [ - ("webhook", "webhookbuy", 'testWEbhook', - "webhook", "webhookentry", 'testWEbhook'), - ("ask_strategy", "ignore_buying_expired_candle_after", 5, - None, "ignore_buying_expired_candle_after", 6), -]) +@pytest.mark.parametrize( + "setting", + [ + ("webhook", "webhookbuy", "testWEbhook", "webhook", "webhookentry", "testWEbhook"), + ( + "ask_strategy", + "ignore_buying_expired_candle_after", + 5, + None, + "ignore_buying_expired_candle_after", + 6, + ), + ], +) def test_process_temporary_deprecated_settings(mocker, default_conf, setting, caplog): patched_configuration_load_config_file(mocker, default_conf) @@ -1201,7 +1284,7 @@ def test_process_temporary_deprecated_settings(mocker, default_conf, setting, ca default_conf[setting[4]] = setting[5] # New and deprecated settings are conflicting ones - with pytest.raises(OperationalException, match=r'DEPRECATED'): + with pytest.raises(OperationalException, match=r"DEPRECATED"): process_temporary_deprecated_settings(default_conf) caplog.clear() @@ -1213,7 +1296,7 @@ def test_process_temporary_deprecated_settings(mocker, default_conf, setting, ca del default_conf[setting[4]] process_temporary_deprecated_settings(default_conf) - assert log_has_re('DEPRECATED', caplog) + assert log_has_re("DEPRECATED", caplog) # The value of the new setting shall have been set to the # value of the deprecated one if setting[3]: @@ -1222,11 +1305,14 @@ def test_process_temporary_deprecated_settings(mocker, default_conf, setting, ca assert default_conf[setting[4]] == setting[2] -@pytest.mark.parametrize("setting", [ - ("experimental", "use_sell_signal", False), - ("experimental", "sell_profit_only", True), - ("experimental", "ignore_roi_if_buy_signal", True), -]) +@pytest.mark.parametrize( + "setting", + [ + ("experimental", "use_sell_signal", False), + ("experimental", "sell_profit_only", True), + ("experimental", "ignore_roi_if_buy_signal", True), + ], +) def test_process_removed_settings(mocker, default_conf, setting): patched_configuration_load_config_file(mocker, default_conf) @@ -1237,20 +1323,24 @@ def test_process_removed_settings(mocker, default_conf, setting): default_conf[setting[0]][setting[1]] = setting[2] # New and deprecated settings are conflicting ones - with pytest.raises(OperationalException, - match=r'Setting .* has been moved'): + with pytest.raises(OperationalException, match=r"Setting .* has been moved"): process_temporary_deprecated_settings(default_conf) def test_process_deprecated_setting_edge(mocker, edge_conf): patched_configuration_load_config_file(mocker, edge_conf) - edge_conf.update({'edge': { - 'enabled': True, - 'capital_available_percentage': 0.5, - }}) + edge_conf.update( + { + "edge": { + "enabled": True, + "capital_available_percentage": 0.5, + } + } + ) - with pytest.raises(OperationalException, - match=r"DEPRECATED.*Using 'edge.capital_available_percentage'*"): + with pytest.raises( + OperationalException, match=r"DEPRECATED.*Using 'edge.capital_available_percentage'*" + ): process_temporary_deprecated_settings(edge_conf) @@ -1259,40 +1349,40 @@ def test_check_conflicting_settings(mocker, default_conf, caplog): # Create sections for new and deprecated settings # (they may not exist in the config) - default_conf['sectionA'] = {} - default_conf['sectionB'] = {} + default_conf["sectionA"] = {} + default_conf["sectionB"] = {} # Assign new setting - default_conf['sectionA']['new_setting'] = 'valA' + default_conf["sectionA"]["new_setting"] = "valA" # Assign deprecated setting - default_conf['sectionB']['deprecated_setting'] = 'valB' + default_conf["sectionB"]["deprecated_setting"] = "valB" # New and deprecated settings are conflicting ones - with pytest.raises(OperationalException, match=r'DEPRECATED'): - check_conflicting_settings(default_conf, - 'sectionB', 'deprecated_setting', - 'sectionA', 'new_setting') + with pytest.raises(OperationalException, match=r"DEPRECATED"): + check_conflicting_settings( + default_conf, "sectionB", "deprecated_setting", "sectionA", "new_setting" + ) caplog.clear() # Delete new setting (deprecated exists) - del default_conf['sectionA']['new_setting'] - check_conflicting_settings(default_conf, - 'sectionB', 'deprecated_setting', - 'sectionA', 'new_setting') - assert not log_has_re('DEPRECATED', caplog) - assert 'new_setting' not in default_conf['sectionA'] + del default_conf["sectionA"]["new_setting"] + check_conflicting_settings( + default_conf, "sectionB", "deprecated_setting", "sectionA", "new_setting" + ) + assert not log_has_re("DEPRECATED", caplog) + assert "new_setting" not in default_conf["sectionA"] caplog.clear() # Assign new setting - default_conf['sectionA']['new_setting'] = 'valA' + default_conf["sectionA"]["new_setting"] = "valA" # Delete deprecated setting - del default_conf['sectionB']['deprecated_setting'] - check_conflicting_settings(default_conf, - 'sectionB', 'deprecated_setting', - 'sectionA', 'new_setting') - assert not log_has_re('DEPRECATED', caplog) - assert default_conf['sectionA']['new_setting'] == 'valA' + del default_conf["sectionB"]["deprecated_setting"] + check_conflicting_settings( + default_conf, "sectionB", "deprecated_setting", "sectionA", "new_setting" + ) + assert not log_has_re("DEPRECATED", caplog) + assert default_conf["sectionA"]["new_setting"] == "valA" def test_process_deprecated_setting(mocker, default_conf, caplog): @@ -1300,56 +1390,54 @@ def test_process_deprecated_setting(mocker, default_conf, caplog): # Create sections for new and deprecated settings # (they may not exist in the config) - default_conf['sectionA'] = {} - default_conf['sectionB'] = {} + default_conf["sectionA"] = {} + default_conf["sectionB"] = {} # Assign deprecated setting - default_conf['sectionB']['deprecated_setting'] = 'valB' + default_conf["sectionB"]["deprecated_setting"] = "valB" # Both new and deprecated settings exists - process_deprecated_setting(default_conf, - 'sectionB', 'deprecated_setting', - 'sectionA', 'new_setting') - assert log_has_re('DEPRECATED', caplog) + process_deprecated_setting( + default_conf, "sectionB", "deprecated_setting", "sectionA", "new_setting" + ) + assert log_has_re("DEPRECATED", caplog) # The value of the new setting shall have been set to the # value of the deprecated one - assert default_conf['sectionA']['new_setting'] == 'valB' + assert default_conf["sectionA"]["new_setting"] == "valB" # Old setting is removed - assert 'deprecated_setting' not in default_conf['sectionB'] + assert "deprecated_setting" not in default_conf["sectionB"] caplog.clear() # Delete new setting (deprecated exists) - del default_conf['sectionA']['new_setting'] - default_conf['sectionB']['deprecated_setting'] = 'valB' - process_deprecated_setting(default_conf, - 'sectionB', 'deprecated_setting', - 'sectionA', 'new_setting') - assert log_has_re('DEPRECATED', caplog) + del default_conf["sectionA"]["new_setting"] + default_conf["sectionB"]["deprecated_setting"] = "valB" + process_deprecated_setting( + default_conf, "sectionB", "deprecated_setting", "sectionA", "new_setting" + ) + assert log_has_re("DEPRECATED", caplog) # The value of the new setting shall have been set to the # value of the deprecated one - assert default_conf['sectionA']['new_setting'] == 'valB' + assert default_conf["sectionA"]["new_setting"] == "valB" caplog.clear() # Assign new setting - default_conf['sectionA']['new_setting'] = 'valA' + default_conf["sectionA"]["new_setting"] = "valA" # Delete deprecated setting - default_conf['sectionB'].pop('deprecated_setting', None) - process_deprecated_setting(default_conf, - 'sectionB', 'deprecated_setting', - 'sectionA', 'new_setting') - assert not log_has_re('DEPRECATED', caplog) - assert default_conf['sectionA']['new_setting'] == 'valA' + default_conf["sectionB"].pop("deprecated_setting", None) + process_deprecated_setting( + default_conf, "sectionB", "deprecated_setting", "sectionA", "new_setting" + ) + assert not log_has_re("DEPRECATED", caplog) + assert default_conf["sectionA"]["new_setting"] == "valA" caplog.clear() # Test moving to root - default_conf['sectionB']['deprecated_setting2'] = "DeadBeef" - process_deprecated_setting(default_conf, - 'sectionB', 'deprecated_setting2', - None, 'new_setting') + default_conf["sectionB"]["deprecated_setting2"] = "DeadBeef" + process_deprecated_setting(default_conf, "sectionB", "deprecated_setting2", None, "new_setting") - assert log_has_re('DEPRECATED', caplog) - assert default_conf['new_setting'] + assert log_has_re("DEPRECATED", caplog) + assert default_conf["new_setting"] def test_process_removed_setting(mocker, default_conf, caplog): @@ -1357,23 +1445,18 @@ def test_process_removed_setting(mocker, default_conf, caplog): # Create sections for new and deprecated settings # (they may not exist in the config) - default_conf['sectionA'] = {} - default_conf['sectionB'] = {} + default_conf["sectionA"] = {} + default_conf["sectionB"] = {} # Assign new setting - default_conf['sectionB']['somesetting'] = 'valA' + default_conf["sectionB"]["somesetting"] = "valA" # Only new setting exists (nothing should happen) - process_removed_setting(default_conf, - 'sectionA', 'somesetting', - 'sectionB', 'somesetting') + process_removed_setting(default_conf, "sectionA", "somesetting", "sectionB", "somesetting") # Assign removed setting - default_conf['sectionA']['somesetting'] = 'valB' + default_conf["sectionA"]["somesetting"] = "valB" - with pytest.raises(OperationalException, - match=r"Setting .* has been moved"): - process_removed_setting(default_conf, - 'sectionA', 'somesetting', - 'sectionB', 'somesetting') + with pytest.raises(OperationalException, match=r"Setting .* has been moved"): + process_removed_setting(default_conf, "sectionA", "somesetting", "sectionB", "somesetting") def test_process_deprecated_ticker_interval(default_conf, caplog): @@ -1383,10 +1466,11 @@ def test_process_deprecated_ticker_interval(default_conf, caplog): process_temporary_deprecated_settings(config) assert not log_has(message, caplog) - del config['timeframe'] - config['ticker_interval'] = '15m' - with pytest.raises(OperationalException, - match=r"DEPRECATED: 'ticker_interval' detected. Please use.*"): + del config["timeframe"] + config["ticker_interval"] = "15m" + with pytest.raises( + OperationalException, match=r"DEPRECATED: 'ticker_interval' detected. Please use.*" + ): process_temporary_deprecated_settings(config) @@ -1396,39 +1480,36 @@ def test_process_deprecated_protections(default_conf, caplog): process_temporary_deprecated_settings(config) assert not log_has(message, caplog) - config['protections'] = [] + config["protections"] = [] process_temporary_deprecated_settings(config) assert log_has(message, caplog) def test_flat_vars_to_nested_dict(caplog): - test_args = { - 'FREQTRADE__EXCHANGE__SOME_SETTING': 'true', - 'FREQTRADE__EXCHANGE__SOME_FALSE_SETTING': 'false', - 'FREQTRADE__EXCHANGE__CONFIG__whatever': 'sometime', - 'FREQTRADE__EXIT_PRICING__PRICE_SIDE': 'bid', - 'FREQTRADE__EXIT_PRICING__cccc': '500', - 'FREQTRADE__STAKE_AMOUNT': '200.05', - 'FREQTRADE__TELEGRAM__CHAT_ID': '2151', - 'NOT_RELEVANT': '200.0', # Will be ignored + "FREQTRADE__EXCHANGE__SOME_SETTING": "true", + "FREQTRADE__EXCHANGE__SOME_FALSE_SETTING": "false", + "FREQTRADE__EXCHANGE__CONFIG__whatever": "sometime", + "FREQTRADE__EXIT_PRICING__PRICE_SIDE": "bid", + "FREQTRADE__EXIT_PRICING__cccc": "500", + "FREQTRADE__STAKE_AMOUNT": "200.05", + "FREQTRADE__TELEGRAM__CHAT_ID": "2151", + "NOT_RELEVANT": "200.0", # Will be ignored } expected = { - 'stake_amount': 200.05, - 'exit_pricing': { - 'price_side': 'bid', - 'cccc': 500, + "stake_amount": 200.05, + "exit_pricing": { + "price_side": "bid", + "cccc": 500, }, - 'exchange': { - 'config': { - 'whatever': 'sometime', + "exchange": { + "config": { + "whatever": "sometime", }, - 'some_setting': True, - 'some_false_setting': False, + "some_setting": True, + "some_false_setting": False, }, - 'telegram': { - 'chat_id': '2151' - } + "telegram": {"chat_id": "2151"}, } res = _flat_vars_to_nested_dict(test_args, ENV_VAR_PREFIX) assert res == expected @@ -1439,31 +1520,29 @@ def test_flat_vars_to_nested_dict(caplog): def test_setup_hyperopt_freqai(mocker, default_conf) -> None: patched_configuration_load_config_file(mocker, default_conf) + mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) mocker.patch( - 'freqtrade.configuration.configuration.create_datadir', - lambda c, x: x - ) - mocker.patch( - 'freqtrade.configuration.configuration.create_userdata_dir', - lambda x, *args, **kwargs: Path(x) + "freqtrade.configuration.configuration.create_userdata_dir", + lambda x, *args, **kwargs: Path(x), ) arglist = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--timerange', '20220801-20220805', + "hyperopt", + "--config", + "config.json", + "--strategy", + CURRENT_TEST_STRATEGY, + "--timerange", + "20220801-20220805", "--freqaimodel", "LightGBMRegressorMultiTarget", - "--analyze-per-epoch" + "--analyze-per-epoch", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) config = configuration.get_config() - config['freqai'] = { - "enabled": True - } + config["freqai"] = {"enabled": True} with pytest.raises( OperationalException, match=r".*analyze-per-epoch parameter is not supported.*" ): @@ -1472,29 +1551,29 @@ def test_setup_hyperopt_freqai(mocker, default_conf) -> None: def test_setup_freqai_backtesting(mocker, default_conf) -> None: patched_configuration_load_config_file(mocker, default_conf) + mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) mocker.patch( - 'freqtrade.configuration.configuration.create_datadir', - lambda c, x: x - ) - mocker.patch( - 'freqtrade.configuration.configuration.create_userdata_dir', - lambda x, *args, **kwargs: Path(x) + "freqtrade.configuration.configuration.create_userdata_dir", + lambda x, *args, **kwargs: Path(x), ) arglist = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--timerange', '20220801-20220805', + "backtesting", + "--config", + "config.json", + "--strategy", + CURRENT_TEST_STRATEGY, + "--timerange", + "20220801-20220805", "--freqaimodel", "LightGBMRegressorMultiTarget", - "--freqai-backtest-live-models" + "--freqai-backtest-live-models", ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) config = configuration.get_config() - config['runmode'] = RunMode.BACKTEST + config["runmode"] = RunMode.BACKTEST with pytest.raises( OperationalException, match=r".*--freqai-backtest-live-models parameter is only.*" @@ -1502,16 +1581,14 @@ def test_setup_freqai_backtesting(mocker, default_conf) -> None: validate_config_consistency(config) conf = deepcopy(config) - conf['freqai'] = { - "enabled": True - } + conf["freqai"] = {"enabled": True} with pytest.raises( OperationalException, match=r".* timerange parameter is not supported with .*" ): validate_config_consistency(conf) - conf['timerange'] = None - conf['freqai_backtest_live_models'] = False + conf["timerange"] = None + conf["freqai_backtest_live_models"] = False with pytest.raises( OperationalException, match=r".* pass --timerange if you intend to use FreqAI .*" @@ -1520,14 +1597,14 @@ def test_setup_freqai_backtesting(mocker, default_conf) -> None: def test_sanitize_config(default_conf_usdt): - assert default_conf_usdt['exchange']['key'] != 'REDACTED' + assert default_conf_usdt["exchange"]["key"] != "REDACTED" res = sanitize_config(default_conf_usdt) # Didn't modify original dict - assert default_conf_usdt['exchange']['key'] != 'REDACTED' + assert default_conf_usdt["exchange"]["key"] != "REDACTED" - assert res['exchange']['key'] == 'REDACTED' - assert res['exchange']['secret'] == 'REDACTED' + assert res["exchange"]["key"] == "REDACTED" + assert res["exchange"]["secret"] == "REDACTED" res = sanitize_config(default_conf_usdt, show_sensitive=True) - assert res['exchange']['key'] == default_conf_usdt['exchange']['key'] - assert res['exchange']['secret'] == default_conf_usdt['exchange']['secret'] + assert res["exchange"]["key"] == default_conf_usdt["exchange"]["key"] + assert res["exchange"]["secret"] == default_conf_usdt["exchange"]["secret"] diff --git a/tests/test_directory_operations.py b/tests/test_directory_operations.py index dc256f931..45297fba8 100644 --- a/tests/test_directory_operations.py +++ b/tests/test_directory_operations.py @@ -17,93 +17,100 @@ from tests.conftest import log_has, log_has_re def test_create_datadir(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) - md = mocker.patch.object(Path, 'mkdir', MagicMock()) + md = mocker.patch.object(Path, "mkdir", MagicMock()) - create_datadir(default_conf, '/foo/bar') - assert md.call_args[1]['parents'] is True - assert log_has('Created data directory: /foo/bar', caplog) + create_datadir(default_conf, "/foo/bar") + assert md.call_args[1]["parents"] is True + assert log_has("Created data directory: /foo/bar", caplog) def test_create_userdata_dir(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) - md = mocker.patch.object(Path, 'mkdir', MagicMock()) + md = mocker.patch.object(Path, "mkdir", MagicMock()) - x = create_userdata_dir('/tmp/bar', create_dir=True) + x = create_userdata_dir("/tmp/bar", create_dir=True) assert md.call_count == 10 - assert md.call_args[1]['parents'] is False + assert md.call_args[1]["parents"] is False assert log_has(f'Created user-data directory: {Path("/tmp/bar")}', caplog) assert isinstance(x, Path) assert str(x) == str(Path("/tmp/bar")) def test_create_userdata_dir_and_chown(mocker, tmp_path, caplog) -> None: - sp_mock = mocker.patch('subprocess.check_output') - path = tmp_path / 'bar' + sp_mock = mocker.patch("subprocess.check_output") + path = tmp_path / "bar" assert not path.is_dir() x = create_userdata_dir(str(path), create_dir=True) assert sp_mock.call_count == 0 - assert log_has(f'Created user-data directory: {path}', caplog) + assert log_has(f"Created user-data directory: {path}", caplog) assert isinstance(x, Path) assert path.is_dir() - assert (path / 'data').is_dir() + assert (path / "data").is_dir() - os.environ['FT_APP_ENV'] = 'docker' - chown_user_directory(path / 'data') + os.environ["FT_APP_ENV"] = "docker" + chown_user_directory(path / "data") assert sp_mock.call_count == 1 - del os.environ['FT_APP_ENV'] + del os.environ["FT_APP_ENV"] def test_create_userdata_dir_exists(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=True)) - md = mocker.patch.object(Path, 'mkdir', MagicMock()) + md = mocker.patch.object(Path, "mkdir", MagicMock()) - create_userdata_dir('/tmp/bar') + create_userdata_dir("/tmp/bar") assert md.call_count == 0 def test_create_userdata_dir_exists_exception(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) - md = mocker.patch.object(Path, 'mkdir', MagicMock()) + md = mocker.patch.object(Path, "mkdir", MagicMock()) - with pytest.raises(OperationalException, - match=r'Directory `.{1,2}tmp.{1,2}bar` does not exist.*'): - create_userdata_dir('/tmp/bar', create_dir=False) + with pytest.raises( + OperationalException, match=r"Directory `.{1,2}tmp.{1,2}bar` does not exist.*" + ): + create_userdata_dir("/tmp/bar", create_dir=False) assert md.call_count == 0 def test_copy_sample_files(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=True)) mocker.patch.object(Path, "exists", MagicMock(return_value=False)) - copymock = mocker.patch('shutil.copy', MagicMock()) + copymock = mocker.patch("shutil.copy", MagicMock()) - copy_sample_files(Path('/tmp/bar')) + copy_sample_files(Path("/tmp/bar")) assert copymock.call_count == 3 assert copymock.call_args_list[0][0][1] == str( - Path('/tmp/bar') / 'strategies/sample_strategy.py') + Path("/tmp/bar") / "strategies/sample_strategy.py" + ) assert copymock.call_args_list[1][0][1] == str( - Path('/tmp/bar') / 'hyperopts/sample_hyperopt_loss.py') + Path("/tmp/bar") / "hyperopts/sample_hyperopt_loss.py" + ) assert copymock.call_args_list[2][0][1] == str( - Path('/tmp/bar') / 'notebooks/strategy_analysis_example.ipynb') + Path("/tmp/bar") / "notebooks/strategy_analysis_example.ipynb" + ) def test_copy_sample_files_errors(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) mocker.patch.object(Path, "exists", MagicMock(return_value=False)) - mocker.patch('shutil.copy', MagicMock()) - with pytest.raises(OperationalException, - match=r"Directory `.{1,2}tmp.{1,2}bar` does not exist\."): - copy_sample_files(Path('/tmp/bar')) + mocker.patch("shutil.copy", MagicMock()) + with pytest.raises( + OperationalException, match=r"Directory `.{1,2}tmp.{1,2}bar` does not exist\." + ): + copy_sample_files(Path("/tmp/bar")) mocker.patch.object(Path, "is_dir", MagicMock(side_effect=[True, False])) - with pytest.raises(OperationalException, - match=r"Directory `.{1,2}tmp.{1,2}bar.{1,2}strategies` does not exist\."): - copy_sample_files(Path('/tmp/bar')) + with pytest.raises( + OperationalException, + match=r"Directory `.{1,2}tmp.{1,2}bar.{1,2}strategies` does not exist\.", + ): + copy_sample_files(Path("/tmp/bar")) mocker.patch.object(Path, "is_dir", MagicMock(return_value=True)) mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - copy_sample_files(Path('/tmp/bar')) + copy_sample_files(Path("/tmp/bar")) assert log_has_re(r"File `.*` exists already, not deploying sample file\.", caplog) caplog.clear() - copy_sample_files(Path('/tmp/bar'), overwrite=True) + copy_sample_files(Path("/tmp/bar"), overwrite=True) assert log_has_re(r"File `.*` exists already, overwriting\.", caplog) diff --git a/tests/test_log_setup.py b/tests/test_log_setup.py index b2142943b..142134b34 100644 --- a/tests/test_log_setup.py +++ b/tests/test_log_setup.py @@ -19,42 +19,42 @@ from freqtrade.loggers.set_log_levels import ( def test_set_loggers() -> None: # Reset Logging to Debug, otherwise this fails randomly as it's set globally - logging.getLogger('requests').setLevel(logging.DEBUG) + logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("urllib3").setLevel(logging.DEBUG) - logging.getLogger('ccxt.base.exchange').setLevel(logging.DEBUG) - logging.getLogger('telegram').setLevel(logging.DEBUG) + logging.getLogger("ccxt.base.exchange").setLevel(logging.DEBUG) + logging.getLogger("telegram").setLevel(logging.DEBUG) - previous_value1 = logging.getLogger('requests').level - previous_value2 = logging.getLogger('ccxt.base.exchange').level - previous_value3 = logging.getLogger('telegram').level + previous_value1 = logging.getLogger("requests").level + previous_value2 = logging.getLogger("ccxt.base.exchange").level + previous_value3 = logging.getLogger("telegram").level set_loggers() - value1 = logging.getLogger('requests').level + value1 = logging.getLogger("requests").level assert previous_value1 is not value1 assert value1 is logging.INFO - value2 = logging.getLogger('ccxt.base.exchange').level + value2 = logging.getLogger("ccxt.base.exchange").level assert previous_value2 is not value2 assert value2 is logging.INFO - value3 = logging.getLogger('telegram').level + value3 = logging.getLogger("telegram").level assert previous_value3 is not value3 assert value3 is logging.INFO set_loggers(verbosity=2) - assert logging.getLogger('requests').level is logging.DEBUG - assert logging.getLogger('ccxt.base.exchange').level is logging.INFO - assert logging.getLogger('telegram').level is logging.INFO - assert logging.getLogger('werkzeug').level is logging.INFO + assert logging.getLogger("requests").level is logging.DEBUG + assert logging.getLogger("ccxt.base.exchange").level is logging.INFO + assert logging.getLogger("telegram").level is logging.INFO + assert logging.getLogger("werkzeug").level is logging.INFO - set_loggers(verbosity=3, api_verbosity='error') + set_loggers(verbosity=3, api_verbosity="error") - assert logging.getLogger('requests').level is logging.DEBUG - assert logging.getLogger('ccxt.base.exchange').level is logging.DEBUG - assert logging.getLogger('telegram').level is logging.INFO - assert logging.getLogger('werkzeug').level is logging.ERROR + assert logging.getLogger("requests").level is logging.DEBUG + assert logging.getLogger("ccxt.base.exchange").level is logging.DEBUG + assert logging.getLogger("telegram").level is logging.INFO + assert logging.getLogger("werkzeug").level is logging.ERROR @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") @@ -63,9 +63,10 @@ def test_set_loggers_syslog(): orig_handlers = logger.handlers logger.handlers = [] - config = {'verbosity': 2, - 'logfile': 'syslog:/dev/log', - } + config = { + "verbosity": 2, + "logfile": "syslog:/dev/log", + } setup_logging_pre() setup_logging(config) @@ -85,10 +86,11 @@ def test_set_loggers_Filehandler(tmp_path): logger = logging.getLogger() orig_handlers = logger.handlers logger.handlers = [] - logfile = tmp_path / 'ft_logfile.log' - config = {'verbosity': 2, - 'logfile': str(logfile), - } + logfile = tmp_path / "ft_logfile.log" + config = { + "verbosity": 2, + "logfile": str(logfile), + } setup_logging_pre() setup_logging(config) @@ -111,9 +113,10 @@ def test_set_loggers_journald(mocker): orig_handlers = logger.handlers logger.handlers = [] - config = {'verbosity': 2, - 'logfile': 'journald', - } + config = { + "verbosity": 2, + "logfile": "journald", + } setup_logging_pre() setup_logging(config) @@ -129,11 +132,11 @@ def test_set_loggers_journald_importerror(import_fails): orig_handlers = logger.handlers logger.handlers = [] - config = {'verbosity': 2, - 'logfile': 'journald', - } - with pytest.raises(OperationalException, - match=r'You need the cysystemd python package.*'): + config = { + "verbosity": 2, + "logfile": "journald", + } + with pytest.raises(OperationalException, match=r"You need the cysystemd python package.*"): setup_logging(config) logger.handlers = orig_handlers @@ -141,16 +144,16 @@ def test_set_loggers_journald_importerror(import_fails): def test_reduce_verbosity(): setup_logging_pre() reduce_verbosity_for_bias_tester() - prior_level = logging.getLogger('freqtrade').getEffectiveLevel() + prior_level = logging.getLogger("freqtrade").getEffectiveLevel() - assert logging.getLogger('freqtrade.resolvers').getEffectiveLevel() == logging.WARNING - assert logging.getLogger('freqtrade.strategy.hyper').getEffectiveLevel() == logging.WARNING + assert logging.getLogger("freqtrade.resolvers").getEffectiveLevel() == logging.WARNING + assert logging.getLogger("freqtrade.strategy.hyper").getEffectiveLevel() == logging.WARNING # base level wasn't changed - assert logging.getLogger('freqtrade').getEffectiveLevel() == prior_level + assert logging.getLogger("freqtrade").getEffectiveLevel() == prior_level restore_verbosity_for_bias_tester() - assert logging.getLogger('freqtrade.resolvers').getEffectiveLevel() == prior_level - assert logging.getLogger('freqtrade.strategy.hyper').getEffectiveLevel() == prior_level - assert logging.getLogger('freqtrade').getEffectiveLevel() == prior_level + assert logging.getLogger("freqtrade.resolvers").getEffectiveLevel() == prior_level + assert logging.getLogger("freqtrade.strategy.hyper").getEffectiveLevel() == prior_level + assert logging.getLogger("freqtrade").getEffectiveLevel() == prior_level # base level wasn't changed diff --git a/tests/test_strategy_updater.py b/tests/test_strategy_updater.py index 7f4ae4349..05cfffe18 100644 --- a/tests/test_strategy_updater.py +++ b/tests/test_strategy_updater.py @@ -18,41 +18,34 @@ if sys.version_info < (3, 9): def test_strategy_updater_start(user_dir, capsys) -> None: # Effective test without mocks. - teststrats = Path(__file__).parent / 'strategy/strats' + teststrats = Path(__file__).parent / "strategy/strats" tmpdirp = Path(user_dir) / "strategies" tmpdirp.mkdir(parents=True, exist_ok=True) - shutil.copy(teststrats / 'strategy_test_v2.py', tmpdirp) - old_code = (teststrats / 'strategy_test_v2.py').read_text() + shutil.copy(teststrats / "strategy_test_v2.py", tmpdirp) + old_code = (teststrats / "strategy_test_v2.py").read_text() - args = [ - "strategy-updater", - "--userdir", - str(user_dir), - "--strategy-list", - "StrategyTestV2" - ] + args = ["strategy-updater", "--userdir", str(user_dir), "--strategy-list", "StrategyTestV2"] pargs = get_args(args) - pargs['config'] = None + pargs["config"] = None start_strategy_update(pargs) assert Path(user_dir / "strategies_orig_updater").exists() # Backup file exists - assert Path(user_dir / "strategies_orig_updater" / 'strategy_test_v2.py').exists() + assert Path(user_dir / "strategies_orig_updater" / "strategy_test_v2.py").exists() # updated file exists - new_file = tmpdirp / 'strategy_test_v2.py' + new_file = tmpdirp / "strategy_test_v2.py" assert new_file.exists() new_code = new_file.read_text() - assert 'INTERFACE_VERSION = 3' in new_code - assert 'INTERFACE_VERSION = 2' in old_code + assert "INTERFACE_VERSION = 3" in new_code + assert "INTERFACE_VERSION = 2" in old_code captured = capsys.readouterr() - assert 'Conversion of strategy_test_v2.py started.' in captured.out - assert re.search(r'Conversion of strategy_test_v2\.py took .* seconds', captured.out) + assert "Conversion of strategy_test_v2.py started." in captured.out + assert re.search(r"Conversion of strategy_test_v2\.py took .* seconds", captured.out) def test_strategy_updater_methods(default_conf, caplog) -> None: - instance_strategy_updater = StrategyUpdater() modified_code1 = instance_strategy_updater.update_code(""" class testClass(IStrategy): diff --git a/tests/test_timerange.py b/tests/test_timerange.py index d1c61704f..c7471e263 100644 --- a/tests/test_timerange.py +++ b/tests/test_timerange.py @@ -8,61 +8,61 @@ from freqtrade.exceptions import OperationalException def test_parse_timerange_incorrect(): - - timerange = TimeRange.parse_timerange('') + timerange = TimeRange.parse_timerange("") assert timerange == TimeRange(None, None, 0, 0) - timerange = TimeRange.parse_timerange('20100522-') - assert TimeRange('date', None, 1274486400, 0) == timerange - assert timerange.timerange_str == '20100522-' - timerange = TimeRange.parse_timerange('-20100522') - assert TimeRange(None, 'date', 0, 1274486400) == timerange - assert timerange.timerange_str == '-20100522' - timerange = TimeRange.parse_timerange('20100522-20150730') - assert timerange == TimeRange('date', 'date', 1274486400, 1438214400) - assert timerange.timerange_str == '20100522-20150730' - assert timerange.start_fmt == '2010-05-22 00:00:00' - assert timerange.stop_fmt == '2015-07-30 00:00:00' + timerange = TimeRange.parse_timerange("20100522-") + assert TimeRange("date", None, 1274486400, 0) == timerange + assert timerange.timerange_str == "20100522-" + timerange = TimeRange.parse_timerange("-20100522") + assert TimeRange(None, "date", 0, 1274486400) == timerange + assert timerange.timerange_str == "-20100522" + timerange = TimeRange.parse_timerange("20100522-20150730") + assert timerange == TimeRange("date", "date", 1274486400, 1438214400) + assert timerange.timerange_str == "20100522-20150730" + assert timerange.start_fmt == "2010-05-22 00:00:00" + assert timerange.stop_fmt == "2015-07-30 00:00:00" # Added test for unix timestamp - BTC genesis date - assert TimeRange('date', None, 1231006505, 0) == TimeRange.parse_timerange('1231006505-') - assert TimeRange(None, 'date', 0, 1233360000) == TimeRange.parse_timerange('-1233360000') - timerange = TimeRange.parse_timerange('1231006505-1233360000') - assert TimeRange('date', 'date', 1231006505, 1233360000) == timerange + assert TimeRange("date", None, 1231006505, 0) == TimeRange.parse_timerange("1231006505-") + assert TimeRange(None, "date", 0, 1233360000) == TimeRange.parse_timerange("-1233360000") + timerange = TimeRange.parse_timerange("1231006505-1233360000") + assert TimeRange("date", "date", 1231006505, 1233360000) == timerange assert isinstance(timerange.startdt, datetime) assert isinstance(timerange.stopdt, datetime) assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=timezone.utc) assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=timezone.utc) - assert timerange.timerange_str == '20090103-20090131' + assert timerange.timerange_str == "20090103-20090131" - timerange = TimeRange.parse_timerange('1231006505000-1233360000000') - assert TimeRange('date', 'date', 1231006505, 1233360000) == timerange + timerange = TimeRange.parse_timerange("1231006505000-1233360000000") + assert TimeRange("date", "date", 1231006505, 1233360000) == timerange - timerange = TimeRange.parse_timerange('1231006505000-') - assert TimeRange('date', None, 1231006505, 0) == timerange + timerange = TimeRange.parse_timerange("1231006505000-") + assert TimeRange("date", None, 1231006505, 0) == timerange - timerange = TimeRange.parse_timerange('-1231006505000') - assert TimeRange(None, 'date', 0, 1231006505) == timerange + timerange = TimeRange.parse_timerange("-1231006505000") + assert TimeRange(None, "date", 0, 1231006505) == timerange - with pytest.raises(OperationalException, match=r'Incorrect syntax.*'): - TimeRange.parse_timerange('-') + with pytest.raises(OperationalException, match=r"Incorrect syntax.*"): + TimeRange.parse_timerange("-") - with pytest.raises(OperationalException, - match=r'Start date is after stop date for timerange.*'): - TimeRange.parse_timerange('20100523-20100522') + with pytest.raises( + OperationalException, match=r"Start date is after stop date for timerange.*" + ): + TimeRange.parse_timerange("20100523-20100522") def test_subtract_start(): - x = TimeRange('date', 'date', 1274486400, 1438214400) + x = TimeRange("date", "date", 1274486400, 1438214400) x.subtract_start(300) assert x.startts == 1274486400 - 300 # Do nothing if no startdate exists - x = TimeRange(None, 'date', 0, 1438214400) + x = TimeRange(None, "date", 0, 1438214400) x.subtract_start(300) assert not x.startts assert not x.startdt - x = TimeRange('date', None, 1274486400, 0) + x = TimeRange("date", None, 1274486400, 0) x.subtract_start(300) assert x.startts == 1274486400 - 300 @@ -70,17 +70,17 @@ def test_subtract_start(): def test_adjust_start_if_necessary(): min_date = datetime(2017, 11, 14, 21, 15, 00, tzinfo=timezone.utc) - x = TimeRange('date', 'date', 1510694100, 1510780500) + x = TimeRange("date", "date", 1510694100, 1510780500) # Adjust by 20 candles - min_date == startts x.adjust_start_if_necessary(300, 20, min_date) assert x.startts == 1510694100 + (20 * 300) - x = TimeRange('date', 'date', 1510700100, 1510780500) + x = TimeRange("date", "date", 1510700100, 1510780500) # Do nothing, startup is set and different min_date x.adjust_start_if_necessary(300, 20, min_date) assert x.startts == 1510694100 + (20 * 300) - x = TimeRange(None, 'date', 0, 1510780500) + x = TimeRange(None, "date", 0, 1510780500) # Adjust by 20 candles = 20 * 5m x.adjust_start_if_necessary(300, 20, min_date) assert x.startts == 1510694100 + (20 * 300)