# pragma pylint: disable=missing-docstring import json import logging import re from copy import deepcopy from datetime import datetime, timedelta from pathlib import Path from typing import Optional, Tuple from unittest.mock import MagicMock, Mock, PropertyMock import arrow import numpy as np import pytest from telegram import Chat, Message, Update from freqtrade import constants from freqtrade.commands import Arguments from freqtrade.data.converter import ohlcv_to_dataframe from freqtrade.edge import PairInfo from freqtrade.enums import CandleType, Collateral, RunMode, SignalDirection, TradingMode from freqtrade.exchange import Exchange from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import LocalTrade, Trade, init_db from freqtrade.resolvers import ExchangeResolver from freqtrade.worker import Worker from tests.conftest_trades import (leverage_trade, mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4, mock_trade_5, mock_trade_6, short_trade) from tests.conftest_trades_usdt import (mock_trade_usdt_1, mock_trade_usdt_2, mock_trade_usdt_3, mock_trade_usdt_4, mock_trade_usdt_5, mock_trade_usdt_6) logging.getLogger('').setLevel(logging.INFO) # Do not mask numpy errors as warnings that no one read, raise the exсeption np.seterr(all='raise') CURRENT_TEST_STRATEGY = 'StrategyTestV3' TRADE_SIDES = ('long', 'short') def pytest_addoption(parser): parser.addoption('--longrun', action='store_true', dest="longrun", default=False, help="Enable long-run tests (ccxt compat)") def pytest_configure(config): config.addinivalue_line( "markers", "longrun: mark test that is running slowly and should not be run regularily" ) if not config.option.longrun: setattr(config.option, 'markexpr', 'not longrun') def log_has(line, logs): """Check if line is found on some caplog's message.""" return any(line == message for message in logs.messages) def log_has_re(line, logs): """Check if line matches some caplog's message.""" return any(re.match(line, message) for message in logs.messages) def num_log_has(line, logs): """Check how many times line is found in caplog's messages.""" return sum(line == message for message in logs.messages) def num_log_has_re(line, logs): """Check how many times line matches caplog's messages.""" return sum(bool(re.match(line, message)) for message in logs.messages) def get_args(args): return Arguments(args).get_parsed_arg() # Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines def get_mock_coro(return_value): async def mock_coro(*args, **kwargs): return return_value return Mock(wraps=mock_coro) def patched_configuration_load_config_file(mocker, config) -> None: mocker.patch( 'freqtrade.configuration.configuration.load_config_file', lambda *args, **kwargs: config ) def patch_exchange( mocker, api_mock=None, id='binance', mock_markets=True, mock_supported_modes=True ) -> None: mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id)) mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title())) mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2)) if mock_markets: if isinstance(mock_markets, bool): mock_markets = get_markets() mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=mock_markets)) if mock_supported_modes: mocker.patch( f'freqtrade.exchange.{id.capitalize()}._supported_trading_mode_collateral_pairs', PropertyMock(return_value=[ (TradingMode.MARGIN, Collateral.CROSS), (TradingMode.MARGIN, Collateral.ISOLATED), (TradingMode.FUTURES, Collateral.CROSS), (TradingMode.FUTURES, Collateral.ISOLATED) ]) ) if api_mock: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) else: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock()) 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 try: exchange = ExchangeResolver.load_exchange(id, config) except ImportError: exchange = Exchange(config) return exchange def patch_wallet(mocker, free=999.9) -> None: 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'])) def patch_edge(mocker) -> None: # "ETH/BTC", # "LTC/BTC", # "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)) # Functions for recurrent object patching def patch_freqtradebot(mocker, config) -> None: """ This function patch _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches :param config: Config to pass to the bot :return: None """ mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) init_db(config['db_url']) patch_exchange(mocker) mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock()) patch_whitelist(mocker, config) def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: """ This function patches _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches :param config: Config to pass to the bot :return: FreqtradeBot """ patch_freqtradebot(mocker, config) config['datadir'] = Path(config['datadir']) return FreqtradeBot(config) def get_patched_worker(mocker, config) -> Worker: """ This function patches _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches :param config: Config to pass to the bot :return: Worker """ patch_freqtradebot(mocker, config) return Worker(args=None, config=config) def patch_get_signal( freqtrade: FreqtradeBot, enter_long=True, exit_long=False, enter_short=False, exit_short=False, enter_tag: Optional[str] = None, exit_tag: Optional[str] = None, ) -> None: """ :param mocker: mocker to patch IStrategy class :return: None """ # returns (Signal-direction, signaname) def patched_get_entry_signal(*args, **kwargs): direction = None if enter_long and not any([exit_long, enter_short]): direction = SignalDirection.LONG if enter_short and not any([exit_short, enter_long]): direction = SignalDirection.SHORT return direction, enter_tag freqtrade.strategy.get_entry_signal = patched_get_entry_signal def patched_get_exit_signal(pair, timeframe, dataframe, is_short): if is_short: return enter_short, exit_short, exit_tag else: return enter_long, exit_long, exit_tag # returns (enter, exit) freqtrade.strategy.get_exit_signal = patched_get_exit_signal freqtrade.exchange.refresh_latest_ohlcv = lambda p: None def create_mock_trades(fee, is_short: Optional[bool] = False, use_db: bool = True): """ 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.query.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 trade = mock_trade_1(fee, is_short1) add_trade(trade) trade = mock_trade_2(fee, is_short1) add_trade(trade) trade = mock_trade_3(fee, is_short2) add_trade(trade) trade = mock_trade_4(fee, is_short2) add_trade(trade) trade = mock_trade_5(fee, is_short2) add_trade(trade) trade = mock_trade_6(fee, is_short1) add_trade(trade) if use_db: Trade.commit() def create_mock_trades_with_leverage(fee, use_db: bool = True): """ Create some fake trades ... """ def add_trade(trade): if use_db: Trade.query.session.add(trade) else: LocalTrade.add_bt_trade(trade) # Simulate dry_run entries trade = mock_trade_1(fee, False) add_trade(trade) trade = mock_trade_2(fee, False) add_trade(trade) trade = mock_trade_3(fee, False) add_trade(trade) trade = mock_trade_4(fee, False) add_trade(trade) trade = mock_trade_5(fee, False) add_trade(trade) trade = mock_trade_6(fee, False) add_trade(trade) trade = short_trade(fee) add_trade(trade) trade = leverage_trade(fee) add_trade(trade) if use_db: Trade.query.session.flush() def create_mock_trades_usdt(fee, use_db: bool = True): """ Create some fake trades ... """ def add_trade(trade): if use_db: Trade.query.session.add(trade) else: LocalTrade.add_bt_trade(trade) # Simulate dry_run entries trade = mock_trade_usdt_1(fee) add_trade(trade) trade = mock_trade_usdt_2(fee) add_trade(trade) trade = mock_trade_usdt_3(fee) add_trade(trade) trade = mock_trade_usdt_4(fee) add_trade(trade) trade = mock_trade_usdt_5(fee) add_trade(trade) trade = mock_trade_usdt_6(fee) add_trade(trade) if use_db: Trade.commit() def get_sides(is_short: bool) -> Tuple[str, str]: return ("sell", "buy") if is_short else ("buy", "sell") @pytest.fixture(autouse=True) def patch_coingekko(mocker) -> None: """ Mocker to coingekko to speed up tests :param mocker: mocker to patch coingekko class :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'} ]) mocker.patch.multiple( 'freqtrade.rpc.fiat_convert.CoinGeckoAPI', get_price=tickermock, get_coins_list=listmock, ) @pytest.fixture(scope='function') def init_persistence(default_conf): init_db(default_conf['db_url'], default_conf['dry_run']) @pytest.fixture(scope="function") def default_conf(testdatadir): return get_default_conf(testdatadir) @pytest.fixture(scope="function") def default_conf_usdt(testdatadir): return get_default_conf_usdt(testdatadir) def get_default_conf(testdatadir): """ 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', "dry_run": True, "cancel_open_orders_on_exit": False, "minimal_roi": { "40": 0.0, "30": 0.01, "20": 0.02, "0": 0.04 }, "dry_run_wallet": 1000, "stoploss": -0.10, "unfilledtimeout": { "buy": 10, "sell": 30 }, "bid_strategy": { "ask_last_balance": 0.0, "use_order_book": False, "order_book_top": 1, "check_depth_of_market": { "enabled": False, "bids_to_ask_delta": 1 } }, "ask_strategy": { "use_order_book": False, "order_book_top": 1, }, "exchange": { "name": "binance", "enabled": True, "key": "key", "secret": "secret", "pair_whitelist": [ "ETH/BTC", "LTC/BTC", "XRP/BTC", "NEO/BTC" ], "pair_blacklist": [ "DOGE/BTC", "HOT/BTC", ] }, "pairlists": [ {"method": "StaticPairList"} ], "telegram": { "enabled": True, "token": "token", "chat_id": "0", "notification_settings": {}, }, "datadir": str(testdatadir), "initial_state": "running", "db_url": "sqlite://", "user_data_dir": Path("user_data"), "verbosity": 3, "strategy_path": str(Path(__file__).parent / "strategy" / "strats"), "strategy": CURRENT_TEST_STRATEGY, "disableparamexport": True, "internals": {}, "export": "none", "candle_type_def": CandleType.SPOT, } return configuration 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", ] }, }) return configuration @pytest.fixture def update(): _update = Update(0) _update.message = Message(0, datetime.utcnow(), Chat(0, 0)) return _update @pytest.fixture def fee(): return MagicMock(return_value=0.0025) @pytest.fixture def ticker(): 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, }) @pytest.fixture def ticker_sell_down(): 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, }) @pytest.fixture def ticker_usdt_sell_up(): 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, }) @pytest.fixture def markets(): return get_markets() 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, 'type': 'spot', '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, }, 'leverage': { 'min': 1.0, 'max': 2.0 } }, }, '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, 'type': 'spot', '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, }, 'leverage': { 'min': 1.0, 'max': 5.0 } }, }, 'BLK/BTC': { 'id': 'blkbtc', 'symbol': 'BLK/BTC', 'base': 'BLK', 'quote': 'BTC', 'active': True, 'spot': True, 'type': 'spot', '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, }, 'leverage': { 'min': 1.0, 'max': 3.0 }, }, }, 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'active': True, 'spot': True, 'type': 'spot', '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, }, 'leverage': { 'min': None, 'max': None }, }, 'info': {}, }, 'XRP/BTC': { 'id': 'xrpbtc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'active': True, 'spot': True, 'type': 'spot', '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, }, 'leverage': { 'min': None, 'max': None, }, }, 'info': {}, }, 'NEO/BTC': { 'id': 'neobtc', 'symbol': 'NEO/BTC', 'base': 'NEO', 'quote': 'BTC', 'active': True, 'spot': True, 'type': 'spot', '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, }, 'leverage': { 'min': None, 'max': None, }, }, 'info': {}, }, 'BTT/BTC': { 'id': 'BTTBTC', 'symbol': 'BTT/BTC', 'base': 'BTT', 'quote': 'BTC', 'active': False, 'spot': True, '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', 'spot': True, 'future': True, 'swap': True, 'margin': True, 'type': 'spot', 'contractSize': None, 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02214286, 'max': None }, 'price': { 'min': 1e-08, 'max': None }, 'leverage': { 'min': None, 'max': None, }, 'cost': { 'min': None, 'max': None, }, }, 'active': True, 'info': {}, }, 'LTC/USDT': { 'id': 'USDT-LTC', 'symbol': 'LTC/USDT', 'base': 'LTC', 'quote': 'USDT', 'active': False, 'spot': True, 'future': True, 'swap': True, 'margin': True, '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': {}, }, 'XRP/USDT': { 'id': 'xrpusdt', 'symbol': 'XRP/USDT', 'base': 'XRP', 'quote': 'USDT', 'active': True, 'spot': True, 'type': 'spot', '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', 'active': True, 'spot': True, 'type': 'spot', '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': {}, }, 'TKN/USDT': { 'id': 'tknusdt', 'symbol': 'TKN/USDT', 'base': 'TKN', 'quote': 'USDT', 'active': True, 'spot': True, 'type': 'spot', 'contractSize': None, 'precision': { 'price': 8, 'amount': 8, 'cost': 8, }, 'lot': 0.00000001, 'limits': { 'amount': { 'min': 0.01, 'max': 1000, }, '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, '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, '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, '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, 'futures': False, 'option': False, 'derivative': True, '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': {} } } @pytest.fixture 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'] all_markets = get_markets() return {m: all_markets[m] for m in static_markets} @pytest.fixture 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 }, 'limits': { 'amount': { 'min': 1.0, 'max': 90000000.0 }, 'price': { 'min': None, 'max': None }, 'cost': { 'min': 0.001, 'max': None } }, '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 }, 'limits': { 'amount': { 'min': 1.0, 'max': 90000000.0 }, 'price': { 'min': 1e-08, 'max': 1000.0 }, 'cost': { 'min': 0.001, 'max': None } }, 'info': {}, }, '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 }, '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 @pytest.fixture def markets_empty(): return MagicMock(return_value=[]) @pytest.fixture(scope='function') def limit_buy_order_open(): return { 'id': 'mocked_limit_buy', 'type': 'limit', 'side': 'buy', 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'timestamp': arrow.utcnow().int_timestamp, 'price': 0.00001099, 'amount': 90.99181073, 'filled': 0.0, 'cost': 0.0009999, 'remaining': 90.99181073, 'status': 'open' } @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 return order @pytest.fixture(scope='function') def market_buy_order(): return { 'id': 'mocked_market_buy', 'type': 'market', 'side': 'buy', 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'price': 0.00004099, 'amount': 91.99181073, 'filled': 91.99181073, 'remaining': 0.0, 'status': 'closed' } @pytest.fixture def market_sell_order(): return { 'id': 'mocked_limit_sell', 'type': 'market', 'side': 'sell', 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'price': 0.00004173, 'amount': 91.99181073, 'filled': 91.99181073, 'remaining': 0.0, 'status': 'closed' } @pytest.fixture def limit_buy_order_old(): return { 'id': 'mocked_limit_buy_old', 'type': 'limit', 'side': 'buy', 'symbol': 'mocked', 'datetime': str(arrow.utcnow().shift(minutes=-601).datetime), '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', 'datetime': arrow.utcnow().shift(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', 'datetime': arrow.utcnow().shift(minutes=-601).isoformat(), 'price': 0.00001099, 'amount': 90.99181073, 'filled': 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'} return res @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 == 'ftx': return { 'info': {}, 'id': '1234512345', 'clientOrderId': None, 'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp, 'datetime': arrow.utcnow().shift(minutes=-601).isoformat(), 'lastTradeTimestamp': None, 'symbol': 'LTC/USDT', 'type': 'limit', 'side': 'buy', 'price': 34.3225, 'amount': 0.55, 'cost': 0.0, 'average': None, 'filled': 0.0, 'remaining': 0.0, 'status': 'closed', 'fee': None, 'trades': None } elif exchange_name == 'kraken': return { 'info': {}, 'id': 'AZNPFF-4AC4N-7MKTAT', 'clientOrderId': None, 'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp, 'datetime': arrow.utcnow().shift(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': return { 'info': {}, 'id': '1234512345', 'clientOrderId': 'alb1234123', 'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp, 'datetime': arrow.utcnow().shift(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': arrow.utcnow().shift(minutes=-601).int_timestamp, 'datetime': arrow.utcnow().shift(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', 'pair': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'timestamp': arrow.utcnow().int_timestamp, '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' 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 }) @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 }) @pytest.fixture 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) ], [ 1511686500000, 8.88e-05, 8.942e-05, 8.88e-05, 8.893e-05, 0.05874751, ], [ 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) @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.720Z', '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 result(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) @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' } }] @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], [1565798399872, '1261aa81333', None, 'sell', 0.019626, 0.011, 0.00021588599999999999]] @pytest.fixture(scope="function") def fetch_trades_result(): return [{'info': {'a': 126181329, 'p': '0.01962700', 'q': '0.04000000', 'f': 138604155, 'l': 138604155, 'T': 1565798399463, 'm': False, 'M': True}, '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': {'a': 126181330, 'p': '0.01962700', 'q': '0.24400000', 'f': 138604156, 'l': 138604156, 'T': 1565798399629, 'm': False, 'M': True}, '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': {'a': 126181331, 'p': '0.01962600', 'q': '0.01100000', 'f': 138604157, 'l': 138604157, 'T': 1565798399752, 'm': True, 'M': True}, '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': {'a': 126181332, 'p': '0.01962600', 'q': '0.01100000', 'f': 138604158, 'l': 138604158, 'T': 1565798399862, 'm': True, 'M': True}, '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': {'a': 126181333, 'p': '0.01952600', 'q': '0.01200000', 'f': 138604158, 'l': 138604158, 'T': 1565798399872, 'm': True, 'M': True}, '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'}}] @pytest.fixture def buy_order_fee(): return { 'id': 'mocked_limit_buy_old', 'type': 'limit', 'side': 'buy', 'symbol': 'mocked', 'datetime': str(arrow.utcnow().shift(minutes=-601).datetime), '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'] = { "enabled": True, "process_throttle_secs": 1800, "calculate_since_number_of_days": 14, "allowed_risk": 0.01, "stoploss_range_min": -0.01, "stoploss_range_max": -0.1, "stoploss_range_step": -0.01, "maximum_winrate": 0.80, "minimum_expectancy": 0.20, "min_trade_number": 15, "max_trade_duration_minute": 1440, "remove_pumps": False } return 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 }, } @pytest.fixture def testdatadir() -> Path: """Return the path where testdata files are stored""" return (Path(__file__).parent / "testdata").resolve() @pytest.fixture(scope="function") 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", 'systemd.journal', 'uvloop']: raise ImportError(f"No module named '{name}'") return realimport(name, *args, **kwargs) builtins.__import__ = mockedimport # Run test - then cleanup yield # restore previous importfunction builtins.__import__ = realimport @pytest.fixture(scope="function") def open_trade(): return Trade( pair='ETH/BTC', open_rate=0.00001099, exchange='binance', open_order_id='123456789', amount=90.99181073, fee_open=0.0, fee_close=0.0, stake_amount=1, open_date=arrow.utcnow().shift(minutes=-601).datetime, is_open=True ) @pytest.fixture(scope="function") def open_trade_usdt(): return Trade( pair='ADA/USDT', open_rate=2.0, exchange='binance', open_order_id='123456789', amount=30.0, fee_open=0.0, fee_close=0.0, stake_amount=60.0, open_date=arrow.utcnow().shift(minutes=-601).datetime, is_open=True ) @pytest.fixture 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, '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_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, '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_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, '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_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, '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_best': False }, { '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, '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_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, '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_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, '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_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, '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_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, '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_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, '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_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, '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_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, '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_best': False } ] for res in hyperopt_res: res['results_metrics']['holding_avg_s'] = res['results_metrics']['holding_avg' ].total_seconds() return hyperopt_res @pytest.fixture(scope='function') def limit_buy_order_usdt_open(): return { 'id': 'mocked_limit_buy_usdt', 'type': 'limit', 'side': 'buy', 'symbol': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'timestamp': arrow.utcnow().int_timestamp, 'price': 2.00, 'amount': 30.0, 'filled': 0.0, 'cost': 60.0, 'remaining': 30.0, 'status': 'open' } @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 return order @pytest.fixture def limit_sell_order_usdt_open(): return { 'id': 'mocked_limit_sell_usdt', 'type': 'limit', 'side': 'sell', 'pair': 'mocked', 'datetime': arrow.utcnow().isoformat(), 'timestamp': arrow.utcnow().int_timestamp, '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' return order @pytest.fixture(scope='function') def market_buy_order_usdt(): return { 'id': 'mocked_market_buy', 'type': 'market', 'side': 'buy', 'symbol': 'mocked', 'datetime': arrow.utcnow().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 # 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['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', 'datetime': arrow.utcnow().isoformat(), 'price': 2.20, 'amount': 30.0, 'filled': 30.0, 'remaining': 0.0, 'status': 'closed' } @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 } @pytest.fixture(scope='function') def market_order(market_buy_order_usdt, market_sell_order_usdt): return { 'buy': market_buy_order_usdt, 'sell': market_sell_order_usdt } @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 } @pytest.fixture(scope='function') def mark_ohlcv(): return [ [1630454400000, 2.77, 2.77, 2.73, 2.73, 0], [1630458000000, 2.73, 2.76, 2.72, 2.74, 0], [1630461600000, 2.74, 2.76, 2.74, 2.76, 0], [1630465200000, 2.76, 2.76, 2.74, 2.76, 0], [1630468800000, 2.76, 2.77, 2.75, 2.77, 0], [1630472400000, 2.77, 2.79, 2.75, 2.78, 0], [1630476000000, 2.78, 2.80, 2.77, 2.77, 0], [1630479600000, 2.78, 2.79, 2.77, 2.77, 0], [1630483200000, 2.77, 2.79, 2.77, 2.78, 0], [1630486800000, 2.77, 2.84, 2.77, 2.84, 0], [1630490400000, 2.84, 2.85, 2.81, 2.81, 0], [1630494000000, 2.81, 2.83, 2.81, 2.81, 0], [1630497600000, 2.81, 2.84, 2.81, 2.82, 0], [1630501200000, 2.82, 2.83, 2.81, 2.81, 0], ] @pytest.fixture(scope='function') def funding_rate_history_hourly(): return [ { "symbol": "ADA/USDT", "fundingRate": -0.000008, "timestamp": 1630454400000, "datetime": "2021-09-01T00:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": -0.000004, "timestamp": 1630458000000, "datetime": "2021-09-01T01:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000012, "timestamp": 1630461600000, "datetime": "2021-09-01T02:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": -0.000003, "timestamp": 1630465200000, "datetime": "2021-09-01T03:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": -0.000007, "timestamp": 1630468800000, "datetime": "2021-09-01T04:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000003, "timestamp": 1630472400000, "datetime": "2021-09-01T05:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000019, "timestamp": 1630476000000, "datetime": "2021-09-01T06:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000003, "timestamp": 1630479600000, "datetime": "2021-09-01T07:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": -0.000003, "timestamp": 1630483200000, "datetime": "2021-09-01T08:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0, "timestamp": 1630486800000, "datetime": "2021-09-01T09:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000013, "timestamp": 1630490400000, "datetime": "2021-09-01T10:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000077, "timestamp": 1630494000000, "datetime": "2021-09-01T11:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000072, "timestamp": 1630497600000, "datetime": "2021-09-01T12:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": 0.000097, "timestamp": 1630501200000, "datetime": "2021-09-01T13:00:00.000Z" }, ] @pytest.fixture(scope='function') def funding_rate_history_octohourly(): return [ { "symbol": "ADA/USDT", "fundingRate": -0.000008, "timestamp": 1630454400000, "datetime": "2021-09-01T00:00:00.000Z" }, { "symbol": "ADA/USDT", "fundingRate": -0.000003, "timestamp": 1630483200000, "datetime": "2021-09-01T08:00:00.000Z" } ]