Merge branch 'develop' into interface_ordertimeoutcallback

This commit is contained in:
Matthias
2020-03-15 14:56:14 +01:00
101 changed files with 2205 additions and 1228 deletions

View File

@@ -217,8 +217,9 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 9 active markets: "
"BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n"
assert ("Exchange Bittrex has 10 active markets: "
"BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, NEO/BTC, "
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
in captured.out)
patch_exchange(mocker, api_mock=api_mock, id="binance")
@@ -231,7 +232,7 @@ def test_list_markets(mocker, markets, capsys):
pargs['config'] = None
start_list_markets(pargs, False)
captured = capsys.readouterr()
assert re.match("\nExchange Binance has 9 active markets:\n",
assert re.match("\nExchange Binance has 10 active markets:\n",
captured.out)
patch_exchange(mocker, api_mock=api_mock, id="bittrex")
@@ -243,8 +244,8 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 11 markets: "
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, LTC/USDT, NEO/BTC, "
assert ("Exchange Bittrex has 12 markets: "
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, "
"TKN/BTC, XLTCUSDT, XRP/BTC.\n"
in captured.out)
@@ -256,8 +257,8 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), True)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 8 active pairs: "
"BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, NEO/BTC, TKN/BTC, XRP/BTC.\n"
assert ("Exchange Bittrex has 9 active pairs: "
"BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, NEO/BTC, TKN/BTC, XRP/BTC.\n"
in captured.out)
# Test list-pairs subcommand with --all: all pairs
@@ -268,8 +269,8 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), True)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 10 pairs: "
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, LTC/USDT, NEO/BTC, "
assert ("Exchange Bittrex has 11 pairs: "
"BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, LTC/USDT, NEO/BTC, "
"TKN/BTC, XRP/BTC.\n"
in captured.out)
@@ -282,8 +283,8 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 5 active markets with ETH, LTC as base currencies: "
"ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, XLTCUSDT.\n"
assert ("Exchange Bittrex has 6 active markets with ETH, LTC as base currencies: "
"ETH/BTC, ETH/USDT, LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
in captured.out)
# active markets, base=LTC
@@ -295,8 +296,8 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 3 active markets with LTC as base currency: "
"LTC/BTC, LTC/USD, XLTCUSDT.\n"
assert ("Exchange Bittrex has 4 active markets with LTC as base currency: "
"LTC/BTC, LTC/ETH, LTC/USD, XLTCUSDT.\n"
in captured.out)
# active markets, quote=USDT, USD
@@ -384,7 +385,7 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
assert ("Exchange Bittrex has 9 active markets:\n"
assert ("Exchange Bittrex has 10 active markets:\n"
in captured.out)
# Test tabular output, no markets found
@@ -407,7 +408,7 @@ def test_list_markets(mocker, markets, capsys):
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
assert ('["BLK/BTC","ETH/BTC","ETH/USDT","LTC/BTC","LTC/USD","NEO/BTC",'
assert ('["BLK/BTC","ETH/BTC","ETH/USDT","LTC/BTC","LTC/ETH","LTC/USD","NEO/BTC",'
'"TKN/BTC","XLTCUSDT","XRP/BTC"]'
in captured.out)
@@ -446,11 +447,6 @@ def test_create_datadir_failed(caplog):
def test_create_datadir(caplog, mocker):
# Ensure that caplog is empty before starting ...
# Should prevent random failures.
caplog.clear()
# Added assert here to analyze random test-failures ...
assert len(caplog.record_tuples) == 0
cud = mocker.patch("freqtrade.commands.deploy_commands.create_userdata_dir", MagicMock())
csf = mocker.patch("freqtrade.commands.deploy_commands.copy_sample_files", MagicMock())
@@ -463,7 +459,6 @@ def test_create_datadir(caplog, mocker):
assert cud.call_count == 1
assert csf.call_count == 1
assert len(caplog.record_tuples) == 0
def test_start_new_strategy(mocker, caplog):
@@ -778,6 +773,20 @@ def test_hyperopt_list(mocker, capsys, hyperopt_results):
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--profitable"
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in [" 2/12", " 10/12", "Best result:", "Buy hyperspace params",
"Sell hyperspace params", "ROI table", "Stoploss"])
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12",
" 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
@@ -893,6 +902,21 @@ def test_hyperopt_list(mocker, capsys, hyperopt_results):
assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12"
" 9/12", " 10/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--export-csv", "test_file.csv"
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in ["CSV-File created!"])
f = Path("test_file.csv")
assert 'Best,1,2,-1.25%,-0.00125625,,-2.51,"3,930.0 m",0.43662' in f.read_text()
assert f.is_file()
f.unlink()
def test_hyperopt_show(mocker, capsys, hyperopt_results):

View File

@@ -15,7 +15,7 @@ from telegram import Chat, Message, Update
from freqtrade import constants, persistence
from freqtrade.commands import Arguments
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.converter import ohlcv_to_dataframe
from freqtrade.edge import Edge, PairInfo
from freqtrade.exchange import Exchange
from freqtrade.freqtradebot import FreqtradeBot
@@ -167,23 +167,23 @@ def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None:
@pytest.fixture(autouse=True)
def patch_coinmarketcap(mocker) -> None:
def patch_coingekko(mocker) -> None:
"""
Mocker to coinmarketcap to speed up tests
:param mocker: mocker to patch coinmarketcap class
Mocker to coingekko to speed up tests
:param mocker: mocker to patch coingekko class
:return: None
"""
tickermock = MagicMock(return_value={'price_usd': 12345.0})
listmock = MagicMock(return_value={'data': [{'id': 1, 'name': 'Bitcoin', 'symbol': 'BTC',
'website_slug': 'bitcoin'},
{'id': 1027, '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.Market',
ticker=tickermock,
listings=listmock,
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=tickermock,
get_coins_list=listmock,
)
@@ -575,7 +575,34 @@ def get_markets():
}
},
'info': {},
}
},
'LTC/ETH': {
'id': 'LTCETH',
'symbol': 'LTC/ETH',
'base': 'LTC',
'quote': 'ETH',
'active': True,
'precision': {
'base': 8,
'quote': 8,
'amount': 3,
'price': 5
},
'limits': {
'amount': {
'min': 0.001,
'max': 10000000.0
},
'price': {
'min': 1e-05,
'max': 1000.0
},
'cost': {
'min': 0.01,
'max': None
}
},
},
}
@@ -822,15 +849,15 @@ def order_book_l2():
@pytest.fixture
def ticker_history_list():
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,
@@ -852,8 +879,9 @@ def ticker_history_list():
@pytest.fixture
def ticker_history(ticker_history_list):
return parse_ticker_dataframe(ticker_history_list, "5m", pair="UNITTEST/BTC", fill_missing=True)
def ohlcv_history(ohlcv_history_list):
return ohlcv_to_dataframe(ohlcv_history_list, "5m", pair="UNITTEST/BTC",
fill_missing=True)
@pytest.fixture
@@ -1168,8 +1196,8 @@ def tickers():
@pytest.fixture
def result(testdatadir):
with (testdatadir / 'UNITTEST_BTC-1m.json').open('r') as data_file:
return parse_ticker_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
fill_missing=True)
return ohlcv_to_dataframe(json.load(data_file), '1m', pair="UNITTEST/BTC",
fill_missing=True)
@pytest.fixture(scope="function")

View File

@@ -1,16 +1,19 @@
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from arrow import Arrow
from pandas import DataFrame, DateOffset, to_datetime
from pandas import DataFrame, DateOffset, Timestamp, to_datetime
from freqtrade.configuration import TimeRange
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
combine_tickers_with_mean,
analyze_trade_parallelism,
calculate_max_drawdown,
combine_dataframes_with_mean,
create_cum_profit,
extract_trades_of_period,
load_backtest_data, load_trades,
load_trades_from_db, analyze_trade_parallelism)
load_trades_from_db)
from freqtrade.data.history import load_data, load_pair_history
from tests.test_persistence import create_mock_trades
@@ -109,7 +112,7 @@ def test_load_trades(default_conf, mocker):
db_mock.reset_mock()
bt_mock.reset_mock()
default_conf['exportfilename'] = "testfile.json"
default_conf['exportfilename'] = Path("testfile.json")
load_trades("file",
db_url=default_conf.get('db_url'),
exportfilename=default_conf.get('exportfilename'),)
@@ -118,13 +121,10 @@ def test_load_trades(default_conf, mocker):
assert bt_mock.call_count == 1
def test_combine_tickers_with_mean(testdatadir):
def test_combine_dataframes_with_mean(testdatadir):
pairs = ["ETH/BTC", "ADA/BTC"]
tickers = load_data(datadir=testdatadir,
pairs=pairs,
timeframe='5m'
)
df = combine_tickers_with_mean(tickers)
data = load_data(datadir=testdatadir, pairs=pairs, timeframe='5m')
df = combine_dataframes_with_mean(data)
assert isinstance(df, DataFrame)
assert "ETH/BTC" in df.columns
assert "ADA/BTC" in df.columns
@@ -163,3 +163,17 @@ def test_create_cum_profit1(testdatadir):
assert "cum_profits" in cum_profits.columns
assert cum_profits.iloc[0]['cum_profits'] == 0
assert cum_profits.iloc[-1]['cum_profits'] == 0.0798005
def test_calculate_max_drawdown(testdatadir):
filename = testdatadir / "backtest-result_test.json"
bt_data = load_backtest_data(filename)
drawdown, h, low = calculate_max_drawdown(bt_data)
assert isinstance(drawdown, float)
assert pytest.approx(drawdown) == 0.21142322
assert isinstance(h, Timestamp)
assert isinstance(low, Timestamp)
assert h == Timestamp('2018-01-24 14:25:00', tz='UTC')
assert low == Timestamp('2018-01-30 04:45:00', tz='UTC')
with pytest.raises(ValueError, match='Trade dataframe empty.'):
drawdown, h, low = calculate_max_drawdown(DataFrame())

View File

@@ -5,9 +5,12 @@ from freqtrade.configuration.timerange import TimeRange
from freqtrade.data.converter import (convert_ohlcv_format,
convert_trades_format,
ohlcv_fill_up_missing_data,
parse_ticker_dataframe, trim_dataframe)
from freqtrade.data.history import (get_timerange, load_data,
load_pair_history, validate_backtest_data)
ohlcv_to_dataframe,
trim_dataframe)
from freqtrade.data.history import (get_timerange,
load_data,
load_pair_history,
validate_backtest_data)
from tests.conftest import log_has
from tests.data.test_history import _backup_file, _clean_test_file
@@ -16,15 +19,15 @@ def test_dataframe_correct_columns(result):
assert result.columns.tolist() == ['date', 'open', 'high', 'low', 'close', 'volume']
def test_parse_ticker_dataframe(ticker_history_list, caplog):
def test_ohlcv_to_dataframe(ohlcv_history_list, caplog):
columns = ['date', 'open', 'high', 'low', 'close', 'volume']
caplog.set_level(logging.DEBUG)
# Test file with BV data
dataframe = parse_ticker_dataframe(ticker_history_list, '5m',
pair="UNITTEST/BTC", fill_missing=True)
dataframe = ohlcv_to_dataframe(ohlcv_history_list, '5m', pair="UNITTEST/BTC",
fill_missing=True)
assert dataframe.columns.tolist() == columns
assert log_has('Parsing tickerlist to dataframe', caplog)
assert log_has('Converting candle (OHLCV) data to dataframe for pair UNITTEST/BTC.', caplog)
def test_ohlcv_fill_up_missing_data(testdatadir, caplog):
@@ -84,7 +87,8 @@ def test_ohlcv_fill_up_missing_data2(caplog):
]
# Generate test-data without filling missing
data = parse_ticker_dataframe(ticks, timeframe, pair="UNITTEST/BTC", fill_missing=False)
data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC",
fill_missing=False)
assert len(data) == 3
caplog.set_level(logging.DEBUG)
data2 = ohlcv_fill_up_missing_data(data, timeframe, "UNITTEST/BTC")
@@ -140,14 +144,14 @@ def test_ohlcv_drop_incomplete(caplog):
]
]
caplog.set_level(logging.DEBUG)
data = parse_ticker_dataframe(ticks, timeframe, pair="UNITTEST/BTC",
fill_missing=False, drop_incomplete=False)
data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC",
fill_missing=False, drop_incomplete=False)
assert len(data) == 4
assert not log_has("Dropping last candle", caplog)
# Drop last candle
data = parse_ticker_dataframe(ticks, timeframe, pair="UNITTEST/BTC",
fill_missing=False, drop_incomplete=True)
data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC",
fill_missing=False, drop_incomplete=True)
assert len(data) == 3
assert log_has("Dropping last candle", caplog)

View File

@@ -7,19 +7,19 @@ from freqtrade.state import RunMode
from tests.conftest import get_patched_exchange
def test_ohlcv(mocker, default_conf, ticker_history):
def test_ohlcv(mocker, default_conf, ohlcv_history):
default_conf["runmode"] = RunMode.DRY_RUN
timeframe = default_conf["ticker_interval"]
exchange = get_patched_exchange(mocker, default_conf)
exchange._klines[("XRP/BTC", timeframe)] = ticker_history
exchange._klines[("UNITTEST/BTC", timeframe)] = ticker_history
exchange._klines[("XRP/BTC", timeframe)] = ohlcv_history
exchange._klines[("UNITTEST/BTC", timeframe)] = ohlcv_history
dp = DataProvider(default_conf, exchange)
assert dp.runmode == RunMode.DRY_RUN
assert ticker_history.equals(dp.ohlcv("UNITTEST/BTC", timeframe))
assert ohlcv_history.equals(dp.ohlcv("UNITTEST/BTC", timeframe))
assert isinstance(dp.ohlcv("UNITTEST/BTC", timeframe), DataFrame)
assert dp.ohlcv("UNITTEST/BTC", timeframe) is not ticker_history
assert dp.ohlcv("UNITTEST/BTC", timeframe, copy=False) is ticker_history
assert dp.ohlcv("UNITTEST/BTC", timeframe) is not ohlcv_history
assert dp.ohlcv("UNITTEST/BTC", timeframe, copy=False) is ohlcv_history
assert not dp.ohlcv("UNITTEST/BTC", timeframe).empty
assert dp.ohlcv("NONESENSE/AAA", timeframe).empty
@@ -37,8 +37,8 @@ def test_ohlcv(mocker, default_conf, ticker_history):
assert dp.ohlcv("UNITTEST/BTC", timeframe).empty
def test_historic_ohlcv(mocker, default_conf, ticker_history):
historymock = MagicMock(return_value=ticker_history)
def test_historic_ohlcv(mocker, default_conf, ohlcv_history):
historymock = MagicMock(return_value=ohlcv_history)
mocker.patch("freqtrade.data.dataprovider.load_pair_history", historymock)
dp = DataProvider(default_conf, None)
@@ -48,18 +48,18 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history):
assert historymock.call_args_list[0][1]["timeframe"] == "5m"
def test_get_pair_dataframe(mocker, default_conf, ticker_history):
def test_get_pair_dataframe(mocker, default_conf, ohlcv_history):
default_conf["runmode"] = RunMode.DRY_RUN
ticker_interval = default_conf["ticker_interval"]
exchange = get_patched_exchange(mocker, default_conf)
exchange._klines[("XRP/BTC", ticker_interval)] = ticker_history
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ticker_history
exchange._klines[("XRP/BTC", ticker_interval)] = ohlcv_history
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ohlcv_history
dp = DataProvider(default_conf, exchange)
assert dp.runmode == RunMode.DRY_RUN
assert ticker_history.equals(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval))
assert ohlcv_history.equals(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval))
assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame)
assert dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval) is not ticker_history
assert dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval) is not ohlcv_history
assert not dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval).empty
assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty
@@ -73,7 +73,7 @@ def test_get_pair_dataframe(mocker, default_conf, ticker_history):
assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame)
assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty
historymock = MagicMock(return_value=ticker_history)
historymock = MagicMock(return_value=ohlcv_history)
mocker.patch("freqtrade.data.dataprovider.load_pair_history", historymock)
default_conf["runmode"] = RunMode.BACKTEST
dp = DataProvider(default_conf, exchange)
@@ -82,11 +82,11 @@ def test_get_pair_dataframe(mocker, default_conf, ticker_history):
# assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty
def test_available_pairs(mocker, default_conf, ticker_history):
def test_available_pairs(mocker, default_conf, ohlcv_history):
exchange = get_patched_exchange(mocker, default_conf)
ticker_interval = default_conf["ticker_interval"]
exchange._klines[("XRP/BTC", ticker_interval)] = ticker_history
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ticker_history
exchange._klines[("XRP/BTC", ticker_interval)] = ohlcv_history
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ohlcv_history
dp = DataProvider(default_conf, exchange)
assert len(dp.available_pairs) == 2
@@ -96,7 +96,7 @@ def test_available_pairs(mocker, default_conf, ticker_history):
]
def test_refresh(mocker, default_conf, ticker_history):
def test_refresh(mocker, default_conf, ohlcv_history):
refresh_mock = MagicMock()
mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock)

View File

@@ -12,7 +12,7 @@ from pandas import DataFrame
from pandas.testing import assert_frame_equal
from freqtrade.configuration import TimeRange
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.converter import ohlcv_to_dataframe
from freqtrade.data.history.history_utils import (
_download_pair_history, _download_trades_history,
_load_cached_data_for_updating, convert_trades_to_ohlcv, get_timerange,
@@ -63,7 +63,7 @@ def _clean_test_file(file: Path) -> None:
file_swp.rename(file)
def test_load_data_30min_ticker(mocker, caplog, default_conf, testdatadir) -> None:
def test_load_data_30min_timeframe(mocker, caplog, default_conf, testdatadir) -> None:
ld = load_pair_history(pair='UNITTEST/BTC', timeframe='30m', datadir=testdatadir)
assert isinstance(ld, DataFrame)
assert not log_has(
@@ -72,7 +72,7 @@ def test_load_data_30min_ticker(mocker, caplog, default_conf, testdatadir) -> No
)
def test_load_data_7min_ticker(mocker, caplog, default_conf, testdatadir) -> None:
def test_load_data_7min_timeframe(mocker, caplog, default_conf, testdatadir) -> None:
ld = load_pair_history(pair='UNITTEST/BTC', timeframe='7m', datadir=testdatadir)
assert isinstance(ld, DataFrame)
assert ld.empty
@@ -82,8 +82,8 @@ def test_load_data_7min_ticker(mocker, caplog, default_conf, testdatadir) -> Non
)
def test_load_data_1min_ticker(ticker_history, mocker, caplog, testdatadir) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ticker_history)
def test_load_data_1min_timeframe(ohlcv_history, mocker, caplog, testdatadir) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ohlcv_history)
file = testdatadir / 'UNITTEST_BTC-1m.json'
_backup_file(file, copy_file=True)
load_data(datadir=testdatadir, timeframe='1m', pairs=['UNITTEST/BTC'])
@@ -110,12 +110,12 @@ def test_load_data_startup_candles(mocker, caplog, default_conf, testdatadir) ->
assert ltfmock.call_args_list[0][1]['timerange'].startts == timerange.startts - 20 * 60
def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog,
def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog,
default_conf, testdatadir) -> None:
"""
Test load_pair_history() with 1 min ticker
Test load_pair_history() with 1 min timeframe
"""
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ticker_history_list)
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ohlcv_history_list)
exchange = get_patched_exchange(mocker, default_conf)
file = testdatadir / 'MEME_BTC-1m.json'
@@ -188,8 +188,8 @@ def test_load_cached_data_for_updating(mocker, testdatadir) -> None:
with open(test_filename, "rt") as file:
test_data = json.load(file)
test_data_df = parse_ticker_dataframe(test_data, '1m', 'UNITTEST/BTC',
fill_missing=False, drop_incomplete=False)
test_data_df = ohlcv_to_dataframe(test_data, '1m', 'UNITTEST/BTC',
fill_missing=False, drop_incomplete=False)
# now = last cached item + 1 hour
now_ts = test_data[-1][0] / 1000 + 60 * 60
mocker.patch('arrow.utcnow', return_value=arrow.get(now_ts))
@@ -230,8 +230,8 @@ def test_load_cached_data_for_updating(mocker, testdatadir) -> None:
assert start_ts is None
def test_download_pair_history(ticker_history_list, mocker, default_conf, testdatadir) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ticker_history_list)
def test_download_pair_history(ohlcv_history_list, mocker, default_conf, testdatadir) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=ohlcv_history_list)
exchange = get_patched_exchange(mocker, default_conf)
file1_1 = testdatadir / 'MEME_BTC-1m.json'
file1_5 = testdatadir / 'MEME_BTC-5m.json'
@@ -293,7 +293,7 @@ def test_download_pair_history2(mocker, default_conf, testdatadir) -> None:
assert json_dump_mock.call_count == 2
def test_download_backtesting_data_exception(ticker_history, mocker, caplog,
def test_download_backtesting_data_exception(ohlcv_history, mocker, caplog,
default_conf, testdatadir) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv',
side_effect=Exception('File Error'))
@@ -321,15 +321,15 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
# Make sure we start fresh - test missing data at start
start = arrow.get('2018-01-01T00:00:00')
end = arrow.get('2018-01-11T00:00:00')
tickerdata = load_data(testdatadir, '5m', ['UNITTEST/BTC'], startup_candles=20,
timerange=TimeRange('date', 'date', start.timestamp, end.timestamp))
data = load_data(testdatadir, '5m', ['UNITTEST/BTC'], startup_candles=20,
timerange=TimeRange('date', 'date', start.timestamp, end.timestamp))
assert log_has(
'Using indicator startup period: 20 ...', caplog
)
# timedifference in 5 minutes
td = ((end - start).total_seconds() // 60 // 5) + 1
assert td != len(tickerdata['UNITTEST/BTC'])
start_real = tickerdata['UNITTEST/BTC'].iloc[0, 0]
assert td != len(data['UNITTEST/BTC'])
start_real = data['UNITTEST/BTC'].iloc[0, 0]
assert log_has(f'Missing data at start for pair '
f'UNITTEST/BTC, data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}',
caplog)
@@ -337,14 +337,14 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
caplog.clear()
start = arrow.get('2018-01-10T00:00:00')
end = arrow.get('2018-02-20T00:00:00')
tickerdata = load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'],
timerange=TimeRange('date', 'date', start.timestamp, end.timestamp))
data = load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'],
timerange=TimeRange('date', 'date', start.timestamp, end.timestamp))
# timedifference in 5 minutes
td = ((end - start).total_seconds() // 60 // 5) + 1
assert td != len(tickerdata['UNITTEST/BTC'])
assert td != len(data['UNITTEST/BTC'])
# Shift endtime with +5 - as last candle is dropped (partial candle)
end_real = arrow.get(tickerdata['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5)
end_real = arrow.get(data['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5)
assert log_has(f'Missing data at end for pair '
f'UNITTEST/BTC, data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}',
caplog)
@@ -403,7 +403,7 @@ def test_get_timerange(default_conf, mocker, testdatadir) -> None:
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf)
data = strategy.tickerdata_to_dataframe(
data = strategy.ohlcvdata_to_dataframe(
load_data(
datadir=testdatadir,
timeframe='1m',
@@ -421,7 +421,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir)
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf)
data = strategy.tickerdata_to_dataframe(
data = strategy.ohlcvdata_to_dataframe(
load_data(
datadir=testdatadir,
timeframe='1m',
@@ -446,7 +446,7 @@ def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> No
strategy = StrategyResolver.load_strategy(default_conf)
timerange = TimeRange('index', 'index', 200, 250)
data = strategy.tickerdata_to_dataframe(
data = strategy.ohlcvdata_to_dataframe(
load_data(
datadir=testdatadir,
timeframe='5m',

View File

@@ -11,7 +11,7 @@ import pytest
from pandas import DataFrame, to_datetime
from freqtrade.exceptions import OperationalException
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.converter import ohlcv_to_dataframe
from freqtrade.edge import Edge, PairInfo
from freqtrade.strategy.interface import SellType
from tests.conftest import get_patched_freqtradebot, log_has
@@ -26,7 +26,7 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
# 5) Stoploss and sell are hit. should sell on stoploss
####################################################################
ticker_start_time = arrow.get(2018, 10, 3)
tests_start_time = arrow.get(2018, 10, 3)
ticker_interval_in_minute = 60
_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7}
@@ -43,10 +43,10 @@ def _validate_ohlc(buy_ohlc_sell_matrice):
def _build_dataframe(buy_ohlc_sell_matrice):
_validate_ohlc(buy_ohlc_sell_matrice)
tickers = []
data = []
for ohlc in buy_ohlc_sell_matrice:
ticker = {
'date': ticker_start_time.shift(
d = {
'date': tests_start_time.shift(
minutes=(
ohlc[0] *
ticker_interval_in_minute)).timestamp *
@@ -57,9 +57,9 @@ def _build_dataframe(buy_ohlc_sell_matrice):
'low': ohlc[4],
'close': ohlc[5],
'sell': ohlc[6]}
tickers.append(ticker)
data.append(d)
frame = DataFrame(tickers)
frame = DataFrame(data)
frame['date'] = to_datetime(frame['date'],
unit='ms',
utc=True,
@@ -69,7 +69,7 @@ def _build_dataframe(buy_ohlc_sell_matrice):
def _time_on_candle(number):
return np.datetime64(ticker_start_time.shift(
return np.datetime64(tests_start_time.shift(
minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms')
@@ -158,13 +158,13 @@ def test_edge_results(edge_conf, mocker, caplog, data) -> None:
assert len(trades) == len(data.trades)
if not results.empty:
assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3)
assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3)
for c, trade in enumerate(data.trades):
res = results.iloc[c]
assert res.exit_type == trade.sell_reason
assert res.open_time == np.datetime64(_get_frame_time_from_offset(trade.open_tick))
assert res.close_time == np.datetime64(_get_frame_time_from_offset(trade.close_tick))
assert res.open_time == _get_frame_time_from_offset(trade.open_tick).replace(tzinfo=None)
assert res.close_time == _get_frame_time_from_offset(trade.close_tick).replace(tzinfo=None)
def test_adjust(mocker, edge_conf):
@@ -262,7 +262,7 @@ def mocked_load_data(datadir, pairs=[], timeframe='0m',
NEOBTC = [
[
ticker_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
tests_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
math.sin(x * hz) / 1000 + base,
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
@@ -274,7 +274,7 @@ def mocked_load_data(datadir, pairs=[], timeframe='0m',
base = 0.002
LTCBTC = [
[
ticker_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
tests_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
math.sin(x * hz) / 1000 + base,
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
@@ -282,8 +282,10 @@ def mocked_load_data(datadir, pairs=[], timeframe='0m',
123.45
] for x in range(0, 500)]
pairdata = {'NEO/BTC': parse_ticker_dataframe(NEOBTC, '1h', pair="NEO/BTC", fill_missing=True),
'LTC/BTC': parse_ticker_dataframe(LTCBTC, '1h', pair="LTC/BTC", fill_missing=True)}
pairdata = {'NEO/BTC': ohlcv_to_dataframe(NEOBTC, '1h', pair="NEO/BTC",
fill_missing=True),
'LTC/BTC': ohlcv_to_dataframe(LTCBTC, '1h', pair="LTC/BTC",
fill_missing=True)}
return pairdata

View File

@@ -400,13 +400,40 @@ def test_validate_stake_currency_error(default_conf, mocker, caplog):
def test_get_quote_currencies(default_conf, mocker):
ex = get_patched_exchange(mocker, default_conf)
assert set(ex.get_quote_currencies()) == set(['USD', 'BTC', 'USDT'])
assert set(ex.get_quote_currencies()) == set(['USD', 'ETH', 'BTC', 'USDT'])
@pytest.mark.parametrize('pair,expected', [
('XRP/BTC', 'BTC'),
('LTC/USD', 'USD'),
('ETH/USDT', 'USDT'),
('XLTCUSDT', 'USDT'),
('XRP/NOCURRENCY', ''),
])
def test_get_pair_quote_currency(default_conf, mocker, pair, expected):
ex = get_patched_exchange(mocker, default_conf)
assert ex.get_pair_quote_currency(pair) == expected
@pytest.mark.parametrize('pair,expected', [
('XRP/BTC', 'XRP'),
('LTC/USD', 'LTC'),
('ETH/USDT', 'ETH'),
('XLTCUSDT', 'LTC'),
('XRP/NOCURRENCY', ''),
])
def test_get_pair_base_currency(default_conf, mocker, pair, expected):
ex = get_patched_exchange(mocker, default_conf)
assert ex.get_pair_base_currency(pair) == expected
def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly
api_mock = MagicMock()
type(api_mock).markets = PropertyMock(return_value={
'ETH/BTC': {}, 'LTC/BTC': {}, 'XRP/BTC': {}, 'NEO/BTC': {}
'ETH/BTC': {'quote': 'BTC'},
'LTC/BTC': {'quote': 'BTC'},
'XRP/BTC': {'quote': 'BTC'},
'NEO/BTC': {'quote': 'BTC'},
})
id_mock = PropertyMock(return_value='test_exchange')
type(api_mock).id = id_mock
@@ -454,9 +481,9 @@ def test_validate_pairs_exception(default_conf, mocker, caplog):
def test_validate_pairs_restricted(default_conf, mocker, caplog):
api_mock = MagicMock()
type(api_mock).markets = PropertyMock(return_value={
'ETH/BTC': {}, 'LTC/BTC': {},
'XRP/BTC': {'info': {'IsRestricted': True}},
'NEO/BTC': {'info': 'TestString'}, # info can also be a string ...
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
'XRP/BTC': {'quote': 'BTC', 'info': {'IsRestricted': True}},
'NEO/BTC': {'quote': 'BTC', 'info': 'TestString'}, # info can also be a string ...
})
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
@@ -469,6 +496,54 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
f"on the exchange and eventually remove XRP/BTC from your whitelist.", caplog)
def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog):
api_mock = MagicMock()
type(api_mock).markets = PropertyMock(return_value={
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'},
'HELLO-WORLD': {'quote': 'BTC'},
})
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
Exchange(default_conf)
def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, caplog):
api_mock = MagicMock()
default_conf['stake_currency'] = ''
type(api_mock).markets = PropertyMock(return_value={
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'},
'HELLO-WORLD': {'quote': 'BTC'},
})
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
Exchange(default_conf)
def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog):
default_conf['exchange']['pair_whitelist'].append('HELLO-WORLD')
api_mock = MagicMock()
type(api_mock).markets = PropertyMock(return_value={
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'},
'HELLO-WORLD': {'quote': 'USDT'},
})
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
with pytest.raises(OperationalException, match=r"Stake-currency 'BTC' not compatible with.*"):
Exchange(default_conf)
@pytest.mark.parametrize("timeframe", [
('5m'), ("1m"), ("15m"), ("1h")
])
@@ -506,7 +581,7 @@ def test_validate_timeframes_failed(default_conf, mocker):
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
with pytest.raises(OperationalException,
match=r"Invalid ticker interval '3m'. This exchange supports.*"):
match=r"Invalid timeframe '3m'. This exchange supports.*"):
Exchange(default_conf)
default_conf["ticker_interval"] = "15s"
@@ -1121,31 +1196,22 @@ def test_fetch_ticker(default_conf, mocker, exchange_name):
assert ticker['bid'] == 0.5
assert ticker['ask'] == 1
assert 'ETH/BTC' in exchange._cached_ticker
assert exchange._cached_ticker['ETH/BTC']['bid'] == 0.5
assert exchange._cached_ticker['ETH/BTC']['ask'] == 1
# Test caching
api_mock.fetch_ticker = MagicMock()
exchange.fetch_ticker(pair='ETH/BTC', refresh=False)
assert api_mock.fetch_ticker.call_count == 0
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
"fetch_ticker", "fetch_ticker",
pair='ETH/BTC', refresh=True)
pair='ETH/BTC')
api_mock.fetch_ticker = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange.fetch_ticker(pair='ETH/BTC', refresh=True)
exchange.fetch_ticker(pair='ETH/BTC')
with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'):
exchange.fetch_ticker(pair='XRP/ETH', refresh=True)
exchange.fetch_ticker(pair='XRP/ETH')
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
tick = [
ohlcv = [
[
arrow.utcnow().timestamp * 1000, # unix timestamp ms
1, # open
@@ -1158,7 +1224,7 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name):
pair = 'ETH/BTC'
async def mock_candle_hist(pair, timeframe, since_ms):
return pair, timeframe, tick
return pair, timeframe, ohlcv
exchange._async_get_candle_history = Mock(wraps=mock_candle_hist)
# one_call calculation * 1.8 should do 2 calls
@@ -1166,12 +1232,12 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name):
ret = exchange.get_historic_ohlcv(pair, "5m", int((arrow.utcnow().timestamp - since) * 1000))
assert exchange._async_get_candle_history.call_count == 2
# Returns twice the above tick
# Returns twice the above OHLCV data
assert len(ret) == 2
def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
tick = [
ohlcv = [
[
(arrow.utcnow().timestamp - 1) * 1000, # unix timestamp ms
1, # open
@@ -1192,14 +1258,14 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf)
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
pairs = [('IOTA/ETH', '5m'), ('XRP/ETH', '5m')]
# empty dicts
assert not exchange._klines
exchange.refresh_latest_ohlcv(pairs)
assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog)
assert log_has(f'Refreshing candle (OHLCV) data for {len(pairs)} pairs', caplog)
assert exchange._klines
assert exchange._api_async.fetch_ohlcv.call_count == 2
for pair in pairs:
@@ -1217,14 +1283,15 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')])
assert exchange._api_async.fetch_ohlcv.call_count == 2
assert log_has(f"Using cached ohlcv data for pair {pairs[0][0]}, timeframe {pairs[0][1]} ...",
assert log_has(f"Using cached candle (OHLCV) data for pair {pairs[0][0]}, "
f"timeframe {pairs[0][1]} ...",
caplog)
@pytest.mark.asyncio
@pytest.mark.parametrize("exchange_name", EXCHANGES)
async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_name):
tick = [
ohlcv = [
[
arrow.utcnow().timestamp * 1000, # unix timestamp ms
1, # open
@@ -1238,7 +1305,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
# Monkey-patch async function
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
pair = 'ETH/BTC'
res = await exchange._async_get_candle_history(pair, "5m")
@@ -1246,9 +1313,9 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
assert len(res) == 3
assert res[0] == pair
assert res[1] == "5m"
assert res[2] == tick
assert res[2] == ohlcv
assert exchange._api_async.fetch_ohlcv.call_count == 1
assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog)
assert not log_has(f"Using cached candle (OHLCV) data for {pair} ...", caplog)
# exchange = Exchange(default_conf)
await async_ccxt_exception(mocker, default_conf, MagicMock(),
@@ -1256,14 +1323,15 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
pair='ABCD/BTC', timeframe=default_conf['ticker_interval'])
api_mock = MagicMock()
with pytest.raises(OperationalException, match=r'Could not fetch ticker data*'):
with pytest.raises(OperationalException,
match=r'Could not fetch historical candle \(OHLCV\) data.*'):
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError("Unknown error"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
await exchange._async_get_candle_history(pair, "5m",
(arrow.utcnow().timestamp - 2000) * 1000)
with pytest.raises(OperationalException, match=r'Exchange.* does not support fetching '
r'historical candlestick data\..*'):
r'historical candle \(OHLCV\) data\..*'):
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NotSupported("Not supported"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
await exchange._async_get_candle_history(pair, "5m",
@@ -1273,7 +1341,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
@pytest.mark.asyncio
async def test__async_get_candle_history_empty(default_conf, mocker, caplog):
""" Test empty exchange result """
tick = []
ohlcv = []
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf)
@@ -1287,7 +1355,7 @@ async def test__async_get_candle_history_empty(default_conf, mocker, caplog):
assert len(res) == 3
assert res[0] == pair
assert res[1] == "5m"
assert res[2] == tick
assert res[2] == ohlcv
assert exchange._api_async.fetch_ohlcv.call_count == 1
@@ -1365,8 +1433,8 @@ async def test___async_get_candle_history_sort(default_conf, mocker, exchange_na
return sorted(data, key=key)
# GDAX use-case (real data from GDAX)
# This ticker history is ordered DESC (newest first, oldest last)
tick = [
# This OHLCV data is ordered DESC (newest first, oldest last)
ohlcv = [
[1527833100000, 0.07666, 0.07671, 0.07666, 0.07668, 16.65244264],
[1527832800000, 0.07662, 0.07666, 0.07662, 0.07666, 1.30051526],
[1527832500000, 0.07656, 0.07661, 0.07656, 0.07661, 12.034778840000001],
@@ -1379,31 +1447,31 @@ async def test___async_get_candle_history_sort(default_conf, mocker, exchange_na
[1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867]
]
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data))
# Test the ticker history sort
# Test the OHLCV data sort
res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval'])
assert res[0] == 'ETH/BTC'
ticks = res[2]
res_ohlcv = res[2]
assert sort_mock.call_count == 1
assert ticks[0][0] == 1527830400000
assert ticks[0][1] == 0.07649
assert ticks[0][2] == 0.07651
assert ticks[0][3] == 0.07649
assert ticks[0][4] == 0.07651
assert ticks[0][5] == 2.5734867
assert res_ohlcv[0][0] == 1527830400000
assert res_ohlcv[0][1] == 0.07649
assert res_ohlcv[0][2] == 0.07651
assert res_ohlcv[0][3] == 0.07649
assert res_ohlcv[0][4] == 0.07651
assert res_ohlcv[0][5] == 2.5734867
assert ticks[9][0] == 1527833100000
assert ticks[9][1] == 0.07666
assert ticks[9][2] == 0.07671
assert ticks[9][3] == 0.07666
assert ticks[9][4] == 0.07668
assert ticks[9][5] == 16.65244264
assert res_ohlcv[9][0] == 1527833100000
assert res_ohlcv[9][1] == 0.07666
assert res_ohlcv[9][2] == 0.07671
assert res_ohlcv[9][3] == 0.07666
assert res_ohlcv[9][4] == 0.07668
assert res_ohlcv[9][5] == 16.65244264
# Bittrex use-case (real data from Bittrex)
# This ticker history is ordered ASC (oldest first, newest last)
tick = [
# This OHLCV data is ordered ASC (oldest first, newest last)
ohlcv = [
[1527827700000, 0.07659999, 0.0766, 0.07627, 0.07657998, 1.85216924],
[1527828000000, 0.07657995, 0.07657995, 0.0763, 0.0763, 26.04051037],
[1527828300000, 0.0763, 0.07659998, 0.0763, 0.0764, 10.36434124],
@@ -1415,29 +1483,29 @@ async def test___async_get_candle_history_sort(default_conf, mocker, exchange_na
[1527830100000, 0.076695, 0.07671, 0.07624171, 0.07671, 1.80689244],
[1527830400000, 0.07671, 0.07674399, 0.07629216, 0.07655213, 2.31452783]
]
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv)
# Reset sort mock
sort_mock = mocker.patch('freqtrade.exchange.sorted', MagicMock(side_effect=sort_data))
# Test the ticker history sort
# Test the OHLCV data sort
res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval'])
assert res[0] == 'ETH/BTC'
assert res[1] == default_conf['ticker_interval']
ticks = res[2]
res_ohlcv = res[2]
# Sorted not called again - data is already in order
assert sort_mock.call_count == 0
assert ticks[0][0] == 1527827700000
assert ticks[0][1] == 0.07659999
assert ticks[0][2] == 0.0766
assert ticks[0][3] == 0.07627
assert ticks[0][4] == 0.07657998
assert ticks[0][5] == 1.85216924
assert res_ohlcv[0][0] == 1527827700000
assert res_ohlcv[0][1] == 0.07659999
assert res_ohlcv[0][2] == 0.0766
assert res_ohlcv[0][3] == 0.07627
assert res_ohlcv[0][4] == 0.07657998
assert res_ohlcv[0][5] == 1.85216924
assert ticks[9][0] == 1527830400000
assert ticks[9][1] == 0.07671
assert ticks[9][2] == 0.07674399
assert ticks[9][3] == 0.07629216
assert ticks[9][4] == 0.07655213
assert ticks[9][5] == 2.31452783
assert res_ohlcv[9][0] == 1527830400000
assert res_ohlcv[9][1] == 0.07671
assert res_ohlcv[9][2] == 0.07674399
assert res_ohlcv[9][3] == 0.07629216
assert res_ohlcv[9][4] == 0.07655213
assert res_ohlcv[9][5] == 2.31452783
@pytest.mark.asyncio
@@ -1828,6 +1896,7 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
# 'ETH/BTC': 'active': True
# 'ETH/USDT': 'active': True
# 'LTC/BTC': 'active': False
# 'LTC/ETH': 'active': True
# 'LTC/USD': 'active': True
# 'LTC/USDT': 'active': True
# 'NEO/BTC': 'active': False
@@ -1836,26 +1905,26 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
# 'XRP/BTC': 'active': False
# all markets
([], [], False, False,
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD',
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']),
# active markets
([], [], False, True,
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'NEO/BTC',
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC',
'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']),
# all pairs
([], [], True, False,
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD',
['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD',
'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC']),
# active pairs
([], [], True, True,
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'NEO/BTC',
['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC',
'TKN/BTC', 'XRP/BTC']),
# all markets, base=ETH, LTC
(['ETH', 'LTC'], [], False, False,
['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
# all markets, base=LTC
(['LTC'], [], False, False,
['LTC/BTC', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']),
# all markets, quote=USDT
([], ['USDT'], False, False,
['ETH/USDT', 'LTC/USDT', 'XLTCUSDT']),

View File

@@ -6,7 +6,7 @@ from pandas import DataFrame
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.strategy.interface import SellType
ticker_start_time = arrow.get(2018, 10, 3)
tests_start_time = arrow.get(2018, 10, 3)
tests_timeframe = '1h'
@@ -36,14 +36,14 @@ class BTContainer(NamedTuple):
def _get_frame_time_from_offset(offset):
return ticker_start_time.shift(minutes=(offset * timeframe_to_minutes(tests_timeframe))
).datetime
minutes = offset * timeframe_to_minutes(tests_timeframe)
return tests_start_time.shift(minutes=minutes).datetime
def _build_backtest_dataframe(ticker_with_signals):
def _build_backtest_dataframe(data):
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell']
frame = DataFrame.from_records(ticker_with_signals, columns=columns)
frame = DataFrame.from_records(data, columns=columns)
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
# Ensure floats are in place
for column in ['open', 'high', 'low', 'close', 'volume']:

View File

@@ -84,7 +84,7 @@ def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None:
backtesting = Backtesting(config)
data = load_data_test(contour, testdatadir)
processed = backtesting.strategy.tickerdata_to_dataframe(data)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
assert isinstance(processed, dict)
results = backtesting.backtest(
@@ -105,7 +105,7 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
data = trim_dictlist(data, -201)
patch_exchange(mocker)
backtesting = Backtesting(conf)
processed = backtesting.strategy.tickerdata_to_dataframe(data)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
return {
'processed': processed,
@@ -224,6 +224,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
assert 'export' in config
assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog)
assert 'exportfilename' in config
assert isinstance(config['exportfilename'], Path)
assert log_has('Storing backtest results to {} ...'.format(config['exportfilename']), caplog)
assert 'fee' in config
@@ -241,7 +242,7 @@ def test_setup_optimize_configuration_unlimited_stake_amount(mocker, default_con
'--strategy', 'DefaultStrategy',
]
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
with pytest.raises(DependencyException, match=r'.`stake_amount`.*'):
setup_optimize_configuration(get_args(args), RunMode.BACKTEST)
@@ -275,7 +276,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
backtesting = Backtesting(default_conf)
assert backtesting.config == default_conf
assert backtesting.timeframe == '5m'
assert callable(backtesting.strategy.tickerdata_to_dataframe)
assert callable(backtesting.strategy.ohlcvdata_to_dataframe)
assert callable(backtesting.strategy.advise_buy)
assert callable(backtesting.strategy.advise_sell)
assert isinstance(backtesting.strategy.dp, DataProvider)
@@ -297,7 +298,7 @@ def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> No
"or as cli argument `--ticker-interval 5m`", caplog)
def test_tickerdata_with_fee(default_conf, mocker, testdatadir) -> None:
def test_data_with_fee(default_conf, mocker, testdatadir) -> None:
patch_exchange(mocker)
default_conf['fee'] = 0.1234
@@ -307,21 +308,21 @@ def test_tickerdata_with_fee(default_conf, mocker, testdatadir) -> None:
assert fee_mock.call_count == 0
def test_tickerdata_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
patch_exchange(mocker)
timerange = TimeRange.parse_timerange('1510694220-1510700340')
tickerlist = history.load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange,
fill_up_missing=True)
data = history.load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange,
fill_up_missing=True)
backtesting = Backtesting(default_conf)
data = backtesting.strategy.tickerdata_to_dataframe(tickerlist)
assert len(data['UNITTEST/BTC']) == 102
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
assert len(processed['UNITTEST/BTC']) == 102
# Load strategy to compare the result between Backtesting function and strategy are the same
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf)
data2 = strategy.tickerdata_to_dataframe(tickerlist)
assert data['UNITTEST/BTC'].equals(data2['UNITTEST/BTC'])
processed2 = strategy.ohlcvdata_to_dataframe(data)
assert processed['UNITTEST/BTC'].equals(processed2['UNITTEST/BTC'])
def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
@@ -329,7 +330,6 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
mocker.patch('freqtrade.data.history.get_timerange', get_timerange)
mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock())
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.generate_text_table', MagicMock(return_value=1))
@@ -360,7 +360,6 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) ->
mocker.patch('freqtrade.data.history.history_utils.load_pair_history',
MagicMock(return_value=pd.DataFrame()))
mocker.patch('freqtrade.data.history.get_timerange', get_timerange)
mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock())
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.generate_text_table', MagicMock(return_value=1))
@@ -385,10 +384,10 @@ def test_backtest(default_conf, fee, mocker, testdatadir) -> None:
timerange = TimeRange('date', None, 1517227800, 0)
data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'],
timerange=timerange)
data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timerange(data_processed)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
results = backtesting.backtest(
processed=data_processed,
processed=processed,
stake_amount=default_conf['stake_amount'],
start_date=min_date,
end_date=max_date,
@@ -416,7 +415,7 @@ def test_backtest(default_conf, fee, mocker, testdatadir) -> None:
'sell_reason': [SellType.ROI, SellType.ROI]
})
pd.testing.assert_frame_equal(results, expected)
data_pair = data_processed[pair]
data_pair = processed[pair]
for _, t in results.iterrows():
ln = data_pair.loc[data_pair["date"] == t["open_time"]]
# Check open trade rate alignes to open rate
@@ -439,7 +438,7 @@ def test_backtest_1min_ticker_interval(default_conf, fee, mocker, testdatadir) -
timerange = TimeRange.parse_timerange('1510688220-1510700340')
data = history.load_data(datadir=testdatadir, timeframe='1m', pairs=['UNITTEST/BTC'],
timerange=timerange)
processed = backtesting.strategy.tickerdata_to_dataframe(data)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
results = backtesting.backtest(
processed=processed,
@@ -458,7 +457,7 @@ def test_processed(default_conf, mocker, testdatadir) -> None:
backtesting = Backtesting(default_conf)
dict_of_tickerrows = load_data_test('raise', testdatadir)
dataframes = backtesting.strategy.tickerdata_to_dataframe(dict_of_tickerrows)
dataframes = backtesting.strategy.ohlcvdata_to_dataframe(dict_of_tickerrows)
dataframe = dataframes['UNITTEST/BTC']
cols = dataframe.columns
# assert the dataframe got some of the indicator columns
@@ -557,10 +556,10 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
backtesting.strategy.advise_buy = _trend_alternate_hold # Override
backtesting.strategy.advise_sell = _trend_alternate_hold # Override
data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timerange(data_processed)
processed = backtesting.strategy.ohlcvdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
backtest_conf = {
'processed': data_processed,
'processed': processed,
'stake_amount': default_conf['stake_amount'],
'start_date': min_date,
'end_date': max_date,
@@ -576,7 +575,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
assert len(evaluate_result_multi(results, '5m', 3)) == 0
backtest_conf = {
'processed': data_processed,
'processed': processed,
'stake_amount': default_conf['stake_amount'],
'start_date': min_date,
'end_date': max_date,

View File

@@ -10,10 +10,11 @@ import pytest
from arrow import Arrow
from filelock import Timeout
from freqtrade import constants
from freqtrade.commands.optimize_commands import (setup_optimize_configuration,
start_hyperopt)
from freqtrade.data.history import load_data
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
from freqtrade.optimize.hyperopt import Hyperopt
@@ -158,6 +159,21 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
assert log_has('Parameter --print-all detected ...', caplog)
def test_setup_hyperopt_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
patched_configuration_load_config_file(mocker, default_conf)
args = [
'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
]
with pytest.raises(DependencyException, match=r'.`stake_amount`.*'):
setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf)
@@ -369,6 +385,42 @@ def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results
assert under > correct
def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
@@ -390,17 +442,27 @@ def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results)
def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
hyperopt.current_best_loss = 2
hyperopt.total_epochs = 2
hyperopt.print_results(
{
'is_best': True,
'loss': 1,
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
},
'total_profit': 0,
'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
'results_explanation': 'foo.',
'is_initial_point': False
'is_initial_point': False,
'is_best': True
}
)
out, err = capsys.readouterr()
assert ' 2/2: foo. Objective: 1.00000' in out
assert all(x in out
for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "20.0 m"])
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
@@ -422,13 +484,11 @@ def test_save_trials_saves_trials(mocker, hyperopt, testdatadir, caplog) -> None
hyperopt.trials = trials
hyperopt.save_trials(final=True)
assert log_has("Saving 1 epoch.", caplog)
assert log_has(f"1 epoch saved to '{trials_file}'.", caplog)
mock_dump.assert_called_once()
hyperopt.trials = trials + trials
hyperopt.save_trials(final=True)
assert log_has("Saving 2 epochs.", caplog)
assert log_has(f"2 epochs saved to '{trials_file}'.", caplog)
@@ -466,11 +526,21 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result',
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0}}])
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result',
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
},
}])
)
patch_exchange(mocker)
# Co-test loading ticker-interval from strategy
# Co-test loading timeframe from strategy
del default_conf['ticker_interval']
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
@@ -480,7 +550,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
@@ -490,7 +560,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
@@ -576,8 +646,8 @@ def test_has_space(hyperopt, spaces, expected_results):
def test_populate_indicators(hyperopt, testdatadir) -> None:
tickerlist = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.tickerdata_to_dataframe(tickerlist)
data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.ohlcvdata_to_dataframe(data)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
@@ -588,8 +658,8 @@ def test_populate_indicators(hyperopt, testdatadir) -> None:
def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
tickerlist = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.tickerdata_to_dataframe(tickerlist)
data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.ohlcvdata_to_dataframe(data)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
@@ -729,7 +799,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog):
h = Hyperopt(default_conf)
assert unlinkmock.call_count == 2
assert log_has(f"Removing `{h.tickerdata_pickle}`.", caplog)
assert log_has(f"Removing `{h.data_pickle_file}`.", caplog)
def test_continue_hyperopt(mocker, default_conf, caplog):
@@ -761,11 +831,23 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'buy': {'mfi-value': None},
'sell': {'sell-mfi-value': None},
'roi': {}, 'stoploss': {'stoploss': None},
'trailing': {'trailing_stop': None}}}])
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {
'buy': {'mfi-value': None},
'sell': {'sell-mfi-value': None},
'roi': {}, 'stoploss': {'stoploss': None},
'trailing': {'trailing_stop': None}
},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
}
}])
)
patch_exchange(mocker)
@@ -779,7 +861,7 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
})
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
@@ -787,9 +869,13 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null,"trailing_stop":null}' in out # noqa: E501
result_str = (
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi"'
':{},"stoploss":null,"trailing_stop":null}'
)
assert result_str in out # noqa: E501
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
@@ -804,10 +890,22 @@ def test_print_json_spaces_default(mocker, default_conf, caplog, capsys) -> None
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'buy': {'mfi-value': None},
'sell': {'sell-mfi-value': None},
'roi': {}, 'stoploss': {'stoploss': None}}}])
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {
'buy': {'mfi-value': None},
'sell': {'sell-mfi-value': None},
'roi': {}, 'stoploss': {'stoploss': None}
},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
}
}])
)
patch_exchange(mocker)
@@ -821,7 +919,7 @@ def test_print_json_spaces_default(mocker, default_conf, caplog, capsys) -> None
})
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
@@ -831,7 +929,7 @@ def test_print_json_spaces_default(mocker, default_conf, caplog, capsys) -> None
out, err = capsys.readouterr()
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' in out # noqa: E501
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
@@ -846,8 +944,18 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}}}])
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
}
}])
)
patch_exchange(mocker)
@@ -861,7 +969,7 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
})
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
@@ -871,7 +979,7 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
out, err = capsys.readouterr()
assert '{"minimal_roi":{},"stoploss":null}' in out
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
@@ -887,7 +995,16 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0}}])
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
}
}])
)
patch_exchange(mocker)
@@ -899,7 +1016,7 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
@@ -914,7 +1031,7 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
@@ -942,7 +1059,7 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) -
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
@@ -965,7 +1082,17 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result', 'params': {},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
}
}])
)
patch_exchange(mocker)
@@ -977,7 +1104,7 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
# TODO: sell_strategy_generator() is actually not called because
@@ -992,7 +1119,7 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
@@ -1012,7 +1139,17 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
MagicMock(return_value=[{
'loss': 1, 'results_explanation': 'foo result', 'params': {},
'results_metrics':
{
'trade_count': 1,
'avg_profit': 0.1,
'total_profit': 0.001,
'profit': 1.0,
'duration': 20.0
}
}])
)
patch_exchange(mocker)
@@ -1024,7 +1161,7 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
# TODO: buy_strategy_generator() is actually not called because
@@ -1039,7 +1176,7 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
out, err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
# Should be called twice, once for tickerdata, once to save evaluations
# Should be called twice, once for historical candle data, once to save evaluations
assert dumper.call_count == 2
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
@@ -1073,7 +1210,7 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
delattr(hyperopt.custom_hyperopt.__class__, method)

View File

@@ -22,14 +22,14 @@ def test_generate_text_table(default_conf, mocker):
)
result_str = (
'| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
' Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
'|:--------|-------:|---------------:|---------------:|-----------------:|'
'---------------:|:---------------|-------:|--------:|---------:|\n'
'| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
' Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
'|---------+--------+----------------+----------------+------------------+'
'----------------+----------------+--------+---------+----------|\n'
'| ETH/BTC | 2 | 15.00 | 30.00 | 0.60000000 |'
' 15.00 | 0:20:00 | 2 | 0 | 0 |\n'
'| TOTAL | 2 | 15.00 | 30.00 | 0.60000000 |'
' 15.00 | 0:20:00 | 2 | 0 | 0 |'
' 15.00 | 0:20:00 | 2 | 0 | 0 |\n'
'| TOTAL | 2 | 15.00 | 30.00 | 0.60000000 |'
' 15.00 | 0:20:00 | 2 | 0 | 0 |'
)
assert generate_text_table(data={'ETH/BTC': {}},
stake_currency='BTC', max_open_trades=2,
@@ -52,13 +52,13 @@ def test_generate_text_table_sell_reason(default_conf, mocker):
)
result_str = (
'| Sell Reason | Sells | Wins | Draws | Losses |'
'| Sell Reason | Sells | Wins | Draws | Losses |'
' Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % |\n'
'|:--------------|--------:|-------:|--------:|---------:|'
'---------------:|---------------:|-----------------:|---------------:|\n'
'| roi | 2 | 2 | 0 | 0 |'
'|---------------+---------+--------+---------+----------+'
'----------------+----------------+------------------+----------------|\n'
'| roi | 2 | 2 | 0 | 0 |'
' 15 | 30 | 0.6 | 15 |\n'
'| stop_loss | 1 | 0 | 0 | 1 |'
'| stop_loss | 1 | 0 | 0 | 1 |'
' -10 | -10 | -0.2 | -5 |'
)
assert generate_text_table_sell_reason(
@@ -95,14 +95,14 @@ def test_generate_text_table_strategy(default_conf, mocker):
)
result_str = (
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot'
' Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
'|:--------------|-------:|---------------:|---------------:|------'
'-----------:|---------------:|:---------------|-------:|--------:|---------:|\n'
'| TestStrategy1 | 3 | 20.00 | 60.00 | '
' 1.10000000 | 30.00 | 0:17:00 | 3 | 0 | 0 |\n'
'| TestStrategy2 | 3 | 30.00 | 90.00 | '
' 1.30000000 | 45.00 | 0:20:00 | 3 | 0 | 0 |'
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot'
' Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses |\n'
'|---------------+--------+----------------+----------------+------------------+'
'----------------+----------------+--------+---------+----------|\n'
'| TestStrategy1 | 3 | 20.00 | 60.00 | 1.10000000 |'
' 30.00 | 0:17:00 | 3 | 0 | 0 |\n'
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
' 45.00 | 0:20:00 | 3 | 0 | 0 |'
)
assert generate_text_table_strategy('BTC', 2, all_results=results) == result_str
@@ -111,8 +111,7 @@ def test_generate_edge_table(edge_conf, mocker):
results = {}
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
assert generate_edge_table(results).count(':|') == 7
assert generate_edge_table(results).count('+') == 7
assert generate_edge_table(results).count('| ETH/BTC |') == 1
assert generate_edge_table(results).count(
'| Risk Reward Ratio | Required Risk Reward | Expectancy |') == 1

View File

@@ -240,8 +240,6 @@ def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
(['ETH/BTC', 'TKN/BTC', 'ETH/USDT'], "is not compatible with your stake currency"),
# BCH/BTC not available
(['ETH/BTC', 'TKN/BTC', 'BCH/BTC'], "is not compatible with exchange"),
# BLK/BTC in blacklist
(['ETH/BTC', 'TKN/BTC', 'BLK/BTC'], "in your blacklist. Removing "),
# BTT/BTC is inactive
(['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active")
])

View File

@@ -8,7 +8,7 @@ import pytest
from requests.exceptions import RequestException
from freqtrade.rpc.fiat_convert import CryptoFiat, CryptoToFiatConverter
from tests.conftest import log_has
from tests.conftest import log_has, log_has_re
def test_pair_convertion_object():
@@ -22,8 +22,8 @@ def test_pair_convertion_object():
assert pair_convertion.CACHE_DURATION == 6 * 60 * 60
# Check a regular usage
assert pair_convertion.crypto_symbol == 'BTC'
assert pair_convertion.fiat_symbol == 'USD'
assert pair_convertion.crypto_symbol == 'btc'
assert pair_convertion.fiat_symbol == 'usd'
assert pair_convertion.price == 12345.0
assert pair_convertion.is_expired() is False
@@ -57,15 +57,15 @@ def test_fiat_convert_add_pair(mocker):
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='usd', price=12345.0)
pair_len = len(fiat_convert._pairs)
assert pair_len == 1
assert fiat_convert._pairs[0].crypto_symbol == 'BTC'
assert fiat_convert._pairs[0].fiat_symbol == 'USD'
assert fiat_convert._pairs[0].crypto_symbol == 'btc'
assert fiat_convert._pairs[0].fiat_symbol == 'usd'
assert fiat_convert._pairs[0].price == 12345.0
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='Eur', price=13000.2)
pair_len = len(fiat_convert._pairs)
assert pair_len == 2
assert fiat_convert._pairs[1].crypto_symbol == 'BTC'
assert fiat_convert._pairs[1].fiat_symbol == 'EUR'
assert fiat_convert._pairs[1].crypto_symbol == 'btc'
assert fiat_convert._pairs[1].fiat_symbol == 'eur'
assert fiat_convert._pairs[1].price == 13000.2
@@ -100,15 +100,15 @@ def test_fiat_convert_get_price(mocker):
fiat_convert = CryptoToFiatConverter()
with pytest.raises(ValueError, match=r'The fiat US DOLLAR is not supported.'):
fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='US Dollar')
with pytest.raises(ValueError, match=r'The fiat us dollar is not supported.'):
fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='US Dollar')
# Check the value return by the method
pair_len = len(fiat_convert._pairs)
assert pair_len == 0
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 28000.0
assert fiat_convert._pairs[0].crypto_symbol == 'BTC'
assert fiat_convert._pairs[0].fiat_symbol == 'USD'
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 28000.0
assert fiat_convert._pairs[0].crypto_symbol == 'btc'
assert fiat_convert._pairs[0].fiat_symbol == 'usd'
assert fiat_convert._pairs[0].price == 28000.0
assert fiat_convert._pairs[0]._expiration != 0
assert len(fiat_convert._pairs) == 1
@@ -116,13 +116,13 @@ def test_fiat_convert_get_price(mocker):
# Verify the cached is used
fiat_convert._pairs[0].price = 9867.543
expiration = fiat_convert._pairs[0]._expiration
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 9867.543
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 9867.543
assert fiat_convert._pairs[0]._expiration == expiration
# Verify the cache expiration
expiration = time.time() - 2 * 60 * 60
fiat_convert._pairs[0]._expiration = expiration
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 28000.0
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 28000.0
assert fiat_convert._pairs[0]._expiration is not expiration
@@ -143,15 +143,15 @@ def test_loadcryptomap(mocker):
fiat_convert = CryptoToFiatConverter()
assert len(fiat_convert._cryptomap) == 2
assert fiat_convert._cryptomap["BTC"] == "1"
assert fiat_convert._cryptomap["btc"] == "bitcoin"
def test_fiat_init_network_exception(mocker):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(side_effect=RequestException)
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
listings=listmock,
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_coins_list=listmock,
)
# with pytest.raises(RequestEsxception):
fiat_convert = CryptoToFiatConverter()
@@ -163,24 +163,24 @@ def test_fiat_init_network_exception(mocker):
def test_fiat_convert_without_network(mocker):
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap
# Because CryptoToFiatConverter is a Singleton we reset the value of _coingekko
fiat_convert = CryptoToFiatConverter()
cmc_temp = CryptoToFiatConverter._coinmarketcap
CryptoToFiatConverter._coinmarketcap = None
cmc_temp = CryptoToFiatConverter._coingekko
CryptoToFiatConverter._coingekko = None
assert fiat_convert._coinmarketcap is None
assert fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='USD') == 0.0
CryptoToFiatConverter._coinmarketcap = cmc_temp
assert fiat_convert._coingekko is None
assert fiat_convert._find_price(crypto_symbol='btc', fiat_symbol='usd') == 0.0
CryptoToFiatConverter._coingekko = cmc_temp
def test_fiat_invalid_response(mocker, caplog):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(return_value="{'novalidjson':DEADBEEFf}")
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
listings=listmock,
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_coins_list=listmock,
)
# with pytest.raises(RequestEsxception):
fiat_convert = CryptoToFiatConverter()
@@ -189,8 +189,8 @@ def test_fiat_invalid_response(mocker, caplog):
length_cryptomap = len(fiat_convert._cryptomap)
assert length_cryptomap == 0
assert log_has('Could not load FIAT Cryptocurrency map for the following problem: TypeError',
caplog)
assert log_has_re('Could not load FIAT Cryptocurrency map for the following problem: .*',
caplog)
def test_convert_amount(mocker):

View File

@@ -51,13 +51,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_date_hum': ANY,
'close_date': None,
'close_date_hum': None,
'open_rate': 1.099e-05,
'open_rate': 1.098e-05,
'close_rate': None,
'current_rate': 1.098e-05,
'amount': 90.99181074,
'current_rate': 1.099e-05,
'amount': 91.07468124,
'stake_amount': 0.001,
'close_profit': None,
'current_profit': -0.59,
'current_profit': -0.41,
'stop_loss': 0.0,
'initial_stop_loss': 0.0,
'initial_stop_loss_pct': None,
@@ -65,10 +65,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_order': '(limit buy rem=0.00000000)'
} == results[0]
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
# invalidate ticker cache
rpc._freqtrade.exchange._cached_ticker = {}
results = rpc._rpc_trade_status()
assert isnan(results[0]['current_profit'])
assert isnan(results[0]['current_rate'])
@@ -80,10 +78,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_date_hum': ANY,
'close_date': None,
'close_date_hum': None,
'open_rate': 1.099e-05,
'open_rate': 1.098e-05,
'close_rate': None,
'current_rate': ANY,
'amount': 90.99181074,
'amount': 91.07468124,
'stake_amount': 0.001,
'close_profit': None,
'current_profit': ANY,
@@ -97,8 +95,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
@@ -123,7 +121,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert "Pair" in headers
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
assert '-0.59%' == result[0][3]
assert '-0.41%' == result[0][3]
# Test with fiatconvert
rpc._fiat_converter = CryptoToFiatConverter()
@@ -132,12 +130,10 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert "Pair" in headers
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
assert '-0.59% (-0.09)' == result[0][3]
assert '-0.41% (-0.06)' == result[0][3]
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
# invalidate ticker cache
rpc._freqtrade.exchange._cached_ticker = {}
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
@@ -182,7 +178,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
day[1] == '0.00006217 BTC')
assert (day[2] == '0.000 USD' or
day[2] == '0.933 USD')
day[2] == '0.767 USD')
# ensure first day is current date
assert str(days[0][0]) == str(datetime.utcnow().date())
@@ -194,8 +190,8 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
@@ -249,9 +245,9 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
assert prec_satoshi(stats['profit_closed_percent'], 6.2)
assert prec_satoshi(stats['profit_closed_fiat'], 0.93255)
assert prec_satoshi(stats['profit_all_coin'], 5.632e-05)
assert prec_satoshi(stats['profit_all_percent'], 2.81)
assert prec_satoshi(stats['profit_all_fiat'], 0.8448)
assert prec_satoshi(stats['profit_all_coin'], 5.802e-05)
assert prec_satoshi(stats['profit_all_percent'], 2.89)
assert prec_satoshi(stats['profit_all_fiat'], 0.8703)
assert stats['trade_count'] == 2
assert stats['first_trade_date'] == 'just now'
assert stats['latest_trade_date'] == 'just now'
@@ -260,10 +256,8 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert prec_satoshi(stats['best_rate'], 6.2)
# Test non-available pair
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
# invalidate ticker cache
rpc._freqtrade.exchange._cached_ticker = {}
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert stats['trade_count'] == 2
assert stats['first_trade_date'] == 'just now'
@@ -279,8 +273,8 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
ticker_sell_up, limit_buy_order, limit_sell_order):
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
)
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
return_value=15000.0)
@@ -347,8 +341,8 @@ def test_rpc_balance_handle_error(default_conf, mocker):
# ETH will be skipped due to mocked Error below
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
@@ -386,8 +380,8 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
}
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
ticker=MagicMock(return_value={'price_usd': 15000.0}),
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
@@ -674,7 +668,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None
trade = rpc._rpc_forcebuy(pair, None)
assert isinstance(trade, Trade)
assert trade.pair == pair
assert trade.open_rate == ticker()['ask']
assert trade.open_rate == ticker()['bid']
# Test buy duplicate
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
@@ -687,7 +681,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None
# Test buy pair not with stakes
with pytest.raises(RPCException, match=r'Wrong pair selected. Please pairs with stake.*'):
rpc._rpc_forcebuy('XRP/ETH', 0.0001)
rpc._rpc_forcebuy('LTC/ETH', 0.0001)
pair = 'XRP/BTC'
# Test not buying

View File

@@ -426,20 +426,20 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
rc = client_get(client, f"{BASE_URI}/status")
assert_response(rc)
assert len(rc.json) == 1
assert rc.json == [{'amount': 90.99181074,
assert rc.json == [{'amount': 91.07468124,
'base_currency': 'BTC',
'close_date': None,
'close_date_hum': None,
'close_profit': None,
'close_rate': None,
'current_profit': -0.59,
'current_rate': 1.098e-05,
'current_profit': -0.41,
'current_rate': 1.099e-05,
'initial_stop_loss': 0.0,
'initial_stop_loss_pct': None,
'open_date': ANY,
'open_date_hum': 'just now',
'open_order': '(limit buy rem=0.00000000)',
'open_rate': 1.099e-05,
'open_rate': 1.098e-05,
'pair': 'ETH/BTC',
'stake_amount': 0.001,
'stop_loss': 0.0,

View File

@@ -720,13 +720,13 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
'exchange': 'Bittrex',
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.172e-05,
'amount': 90.99181073703367,
'limit': 1.173e-05,
'amount': 91.07468123861567,
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.172e-05,
'profit_amount': 6.126e-05,
'profit_percent': 0.0611052,
'open_rate': 1.098e-05,
'current_rate': 1.173e-05,
'profit_amount': 6.314e-05,
'profit_ratio': 0.0629778,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.FORCE_SELL.value,
@@ -779,13 +779,13 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
'exchange': 'Bittrex',
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.044e-05,
'amount': 90.99181073703367,
'limit': 1.043e-05,
'amount': 91.07468123861567,
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.044e-05,
'profit_amount': -5.492e-05,
'profit_percent': -0.05478342,
'open_rate': 1.098e-05,
'current_rate': 1.043e-05,
'profit_amount': -5.497e-05,
'profit_ratio': -0.05482878,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.FORCE_SELL.value,
@@ -827,13 +827,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
'exchange': 'Bittrex',
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.098e-05,
'amount': 90.99181073703367,
'limit': 1.099e-05,
'amount': 91.07468123861567,
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.098e-05,
'profit_amount': -5.91e-06,
'profit_percent': -0.00589291,
'open_rate': 1.098e-05,
'current_rate': 1.099e-05,
'profit_amount': -4.09e-06,
'profit_ratio': -0.00408133,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.FORCE_SELL.value,
@@ -1210,7 +1210,7 @@ def test_send_msg_buy_notification(default_conf, mocker) -> None:
'*Amount:* `1333.33333333`\n' \
'*Open Rate:* `0.00001099`\n' \
'*Current Rate:* `0.00001099`\n' \
'*Total:* `(0.001000 BTC, 0.000 USD)`'
'*Total:* `(0.001000 BTC, 12.345 USD)`'
def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None:
@@ -1253,7 +1253,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'open_rate': 7.5e-05,
'current_rate': 3.201e-05,
'profit_amount': -0.05746268,
'profit_percent': -0.57405275,
'profit_ratio': -0.57405275,
'stake_currency': 'ETH',
'fiat_currency': 'USD',
'sell_reason': SellType.STOP_LOSS.value,
@@ -1282,7 +1282,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'open_rate': 7.5e-05,
'current_rate': 3.201e-05,
'profit_amount': -0.05746268,
'profit_percent': -0.57405275,
'profit_ratio': -0.57405275,
'stake_currency': 'ETH',
'sell_reason': SellType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
@@ -1448,7 +1448,7 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
'open_rate': 7.5e-05,
'current_rate': 3.201e-05,
'profit_amount': -0.05746268,
'profit_percent': -0.57405275,
'profit_ratio': -0.57405275,
'stake_currency': 'ETH',
'fiat_currency': 'USD',
'sell_reason': SellType.STOP_LOSS.value,

View File

@@ -28,12 +28,12 @@ def get_webhook_dict() -> dict:
"webhooksell": {
"value1": "Selling {pair}",
"value2": "limit {limit:8f}",
"value3": "profit: {profit_amount:8f} {stake_currency}"
"value3": "profit: {profit_amount:8f} {stake_currency} ({profit_ratio})"
},
"webhooksellcancel": {
"value1": "Cancelling Open Sell Order for {pair}",
"value2": "limit {limit:8f}",
"value3": "profit: {profit_amount:8f} {stake_currency}"
"value3": "profit: {profit_amount:8f} {stake_currency} ({profit_ratio})"
},
"webhookstatus": {
"value1": "Status: {status}",
@@ -110,7 +110,7 @@ def test_send_msg(default_conf, mocker):
'open_rate': 0.004,
'current_rate': 0.005,
'profit_amount': 0.001,
'profit_percent': 0.20,
'profit_ratio': 0.20,
'stake_currency': 'BTC',
'sell_reason': SellType.STOP_LOSS.value
}
@@ -136,7 +136,7 @@ def test_send_msg(default_conf, mocker):
'open_rate': 0.004,
'current_rate': 0.005,
'profit_amount': 0.001,
'profit_percent': 0.20,
'profit_ratio': 0.20,
'stake_currency': 'BTC',
'sell_reason': SellType.STOP_LOSS.value
}

View File

@@ -68,7 +68,7 @@ class DefaultStrategy(IStrategy):
Performance Note: For the best performance be frugal on the number of indicators
you are using. Let uncomment only the indicator you are using in your strategies
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
:param dataframe: Dataframe with data from the exchange
:param metadata: Additional information, like the currently traded pair
:return: a Dataframe with all mandatory indicators for the strategies
"""

View File

@@ -21,69 +21,69 @@ from .strats.default_strategy import DefaultStrategy
_STRATEGY = DefaultStrategy(config={})
def test_returns_latest_buy_signal(mocker, default_conf, ticker_history):
def test_returns_latest_buy_signal(mocker, default_conf, ohlcv_history):
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ticker_history) == (True, False)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (True, False)
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ticker_history) == (False, True)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (False, True)
def test_returns_latest_sell_signal(mocker, default_conf, ticker_history):
def test_returns_latest_sell_signal(mocker, default_conf, ohlcv_history):
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
return_value=DataFrame([{'sell': 1, 'buy': 0, 'date': arrow.utcnow()}])
)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ticker_history) == (False, True)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (False, True)
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ticker_history) == (True, False)
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (True, False)
def test_get_signal_empty(default_conf, mocker, caplog):
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
DataFrame())
assert log_has('Empty ticker history for pair foo', caplog)
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
caplog.clear()
assert (False, False) == _STRATEGY.get_signal('bar', default_conf['ticker_interval'],
[])
assert log_has('Empty ticker history for pair bar', caplog)
assert log_has('Empty candle (OHLCV) data for pair bar', caplog)
def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_history):
def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ohlcv_history):
caplog.set_level(logging.INFO)
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
side_effect=ValueError('xyz')
)
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
ticker_history)
ohlcv_history)
assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog)
def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history):
def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ohlcv_history):
caplog.set_level(logging.INFO)
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
return_value=DataFrame([])
)
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
ticker_history)
ohlcv_history)
assert log_has('Empty dataframe for pair xyz', caplog)
def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history):
def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history):
caplog.set_level(logging.INFO)
# default_conf defines a 5m interval. we check interval * 2 + 5m
# this is necessary as the last candle is removed (partial candles) by default
@@ -94,7 +94,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history):
return_value=DataFrame(ticks)
)
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
ticker_history)
ohlcv_history)
assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog)
@@ -107,15 +107,15 @@ def test_get_signal_handles_exceptions(mocker, default_conf):
assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
def test_tickerdata_to_dataframe(default_conf, testdatadir) -> None:
def test_ohlcvdata_to_dataframe(default_conf, testdatadir) -> None:
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf)
timerange = TimeRange.parse_timerange('1510694220-1510700340')
tickerlist = load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange,
fill_up_missing=True)
data = strategy.tickerdata_to_dataframe(tickerlist)
assert len(data['UNITTEST/BTC']) == 102 # partial candle was removed
data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange,
fill_up_missing=True)
processed = strategy.ohlcvdata_to_dataframe(data)
assert len(processed['UNITTEST/BTC']) == 102 # partial candle was removed
def test_min_roi_reached(default_conf, fee) -> None:
@@ -226,7 +226,7 @@ def test_min_roi_reached3(default_conf, fee) -> None:
assert strategy.min_roi_reached(trade, 0.31, arrow.utcnow().shift(minutes=-2).datetime)
def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None:
def test_analyze_ticker_default(ohlcv_history, mocker, caplog) -> None:
caplog.set_level(logging.DEBUG)
ind_mock = MagicMock(side_effect=lambda x, meta: x)
buy_mock = MagicMock(side_effect=lambda x, meta: x)
@@ -239,7 +239,7 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None:
)
strategy = DefaultStrategy({})
strategy.analyze_ticker(ticker_history, {'pair': 'ETH/BTC'})
strategy.analyze_ticker(ohlcv_history, {'pair': 'ETH/BTC'})
assert ind_mock.call_count == 1
assert buy_mock.call_count == 1
assert buy_mock.call_count == 1
@@ -248,7 +248,7 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None:
assert not log_has('Skipping TA Analysis for already analyzed candle', caplog)
caplog.clear()
strategy.analyze_ticker(ticker_history, {'pair': 'ETH/BTC'})
strategy.analyze_ticker(ohlcv_history, {'pair': 'ETH/BTC'})
# No analysis happens as process_only_new_candles is true
assert ind_mock.call_count == 2
assert buy_mock.call_count == 2
@@ -257,7 +257,7 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None:
assert not log_has('Skipping TA Analysis for already analyzed candle', caplog)
def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -> None:
def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) -> None:
caplog.set_level(logging.DEBUG)
ind_mock = MagicMock(side_effect=lambda x, meta: x)
buy_mock = MagicMock(side_effect=lambda x, meta: x)
@@ -272,7 +272,7 @@ def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -
strategy = DefaultStrategy({})
strategy.process_only_new_candles = True
ret = strategy._analyze_ticker_internal(ticker_history, {'pair': 'ETH/BTC'})
ret = strategy._analyze_ticker_internal(ohlcv_history, {'pair': 'ETH/BTC'})
assert 'high' in ret.columns
assert 'low' in ret.columns
assert 'close' in ret.columns
@@ -284,7 +284,7 @@ def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -
assert not log_has('Skipping TA Analysis for already analyzed candle', caplog)
caplog.clear()
ret = strategy._analyze_ticker_internal(ticker_history, {'pair': 'ETH/BTC'})
ret = strategy._analyze_ticker_internal(ohlcv_history, {'pair': 'ETH/BTC'})
# No analysis happens as process_only_new_candles is true
assert ind_mock.call_count == 1
assert buy_mock.call_count == 1

View File

@@ -34,13 +34,6 @@ def all_conf():
return conf
def test_load_config_invalid_pair(default_conf) -> None:
default_conf['exchange']['pair_whitelist'].append('ETH-BTC')
with pytest.raises(ValidationError, match=r'.*does not match.*'):
validate_config_schema(default_conf)
def test_load_config_missing_attributes(default_conf) -> None:
conf = deepcopy(default_conf)
conf.pop('exchange')
@@ -326,6 +319,7 @@ def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) ->
validated_conf = configuration.load_config()
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:
@@ -810,12 +804,6 @@ def test_validate_whitelist(default_conf):
validate_config_consistency(conf)
conf = deepcopy(default_conf)
conf['stake_currency'] = 'USDT'
with pytest.raises(OperationalException,
match=r"Stake-currency 'USDT' not compatible with pair-whitelist.*"):
validate_config_consistency(conf)
def test_load_config_test_comments() -> None:
"""

View File

@@ -761,8 +761,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
assert trade.is_open
assert trade.open_date is not None
assert trade.exchange == 'bittrex'
assert trade.open_rate == 0.00001099
assert trade.amount == 90.99181073703367
assert trade.open_rate == 0.00001098
assert trade.amount == 91.07468123861567
assert log_has(
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
@@ -782,7 +782,7 @@ def test_process_exchange_failures(default_conf, ticker, mocker) -> None:
worker = Worker(args=None, config=default_conf)
patch_get_signal(worker.freqtrade)
worker._process()
worker._process_running()
assert sleep_mock.has_calls()
@@ -799,7 +799,7 @@ def test_process_operational_exception(default_conf, ticker, mocker) -> None:
assert worker.freqtrade.state == State.RUNNING
worker._process()
worker._process_running()
assert worker.freqtrade.state == State.STOPPED
assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status']
@@ -906,22 +906,47 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None:
assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
@pytest.mark.parametrize("ask,last,last_ab,expected", [
(20, 10, 0.0, 20), # Full ask side
(20, 10, 1.0, 10), # Full last side
(20, 10, 0.5, 15), # Between ask and last
(20, 10, 0.7, 13), # Between ask and last
(20, 10, 0.3, 17), # Between ask and last
(5, 10, 1.0, 5), # last bigger than ask
(5, 10, 0.5, 5), # last bigger than ask
@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [
('ask', 20, 19, 10, 0.0, 20), # Full ask side
('ask', 20, 19, 10, 1.0, 10), # Full last side
('ask', 20, 19, 10, 0.5, 15), # Between ask and last
('ask', 20, 19, 10, 0.7, 13), # Between ask and last
('ask', 20, 19, 10, 0.3, 17), # Between ask and last
('ask', 5, 6, 10, 1.0, 5), # last bigger than ask
('ask', 5, 6, 10, 0.5, 5), # last bigger than ask
('ask', 10, 20, None, 0.5, 10), # last not available - uses ask
('ask', 4, 5, None, 0.5, 4), # last not available - uses ask
('ask', 4, 5, None, 1, 4), # last not available - uses ask
('ask', 4, 5, None, 0, 4), # last not available - uses ask
('bid', 10, 20, 10, 0.0, 20), # Full bid side
('bid', 10, 20, 10, 1.0, 10), # Full last side
('bid', 10, 20, 10, 0.5, 15), # Between bid and last
('bid', 10, 20, 10, 0.7, 13), # Between bid and last
('bid', 10, 20, 10, 0.3, 17), # Between bid and last
('bid', 4, 5, 10, 1.0, 5), # last bigger than bid
('bid', 4, 5, 10, 0.5, 5), # last bigger than bid
('bid', 10, 20, None, 0.5, 20), # last not available - uses bid
('bid', 4, 5, None, 0.5, 5), # last not available - uses bid
('bid', 4, 5, None, 1, 5), # last not available - uses bid
('bid', 4, 5, None, 0, 5), # last not available - uses bid
])
def test_get_buy_rate(mocker, default_conf, ask, last, last_ab, expected) -> None:
def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
last, last_ab, expected) -> None:
default_conf['bid_strategy']['ask_last_balance'] = last_ab
default_conf['bid_strategy']['price_side'] = side
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
MagicMock(return_value={'ask': ask, 'last': last}))
MagicMock(return_value={'ask': ask, 'last': last, 'bid': bid}))
assert freqtrade.get_buy_rate('ETH/BTC', True) == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
assert freqtrade.get_buy_rate('ETH/BTC', False) == expected
assert log_has("Using cached buy rate for ETH/BTC.", caplog)
# Running a 2nd time with Refresh on!
caplog.clear()
assert freqtrade.get_buy_rate('ETH/BTC', True) == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
@@ -1309,7 +1334,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
stoploss_order_mock.assert_not_called()
assert freqtrade.handle_trade(trade) is False
assert trade.stop_loss == 0.00002344 * 0.95
assert trade.stop_loss == 0.00002346 * 0.95
# setting stoploss_on_exchange_interval to 0 seconds
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
@@ -1317,10 +1342,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
assert freqtrade.handle_stoploss_on_exchange(trade) is False
cancel_order_mock.assert_called_once_with(100, 'ETH/BTC')
stoploss_order_mock.assert_called_once_with(amount=85.25149190110828,
stoploss_order_mock.assert_called_once_with(amount=85.32423208191126,
pair='ETH/BTC',
order_types=freqtrade.strategy.order_types,
stop_price=0.00002344 * 0.95)
stop_price=0.00002346 * 0.95)
# price fell below stoploss, so dry-run sells trade.
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
@@ -1502,12 +1527,12 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
assert freqtrade.handle_stoploss_on_exchange(trade) is False
# stoploss should be set to 1% as trailing is on
assert trade.stop_loss == 0.00002344 * 0.99
assert trade.stop_loss == 0.00002346 * 0.99
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
stoploss_order_mock.assert_called_once_with(amount=2131074.168797954,
stoploss_order_mock.assert_called_once_with(amount=2132892.491467577,
pair='NEO/BTC',
order_types=freqtrade.strategy.order_types,
stop_price=0.00002344 * 0.99)
stop_price=0.00002346 * 0.99)
def test_enter_positions(mocker, default_conf, caplog) -> None:
@@ -2290,6 +2315,7 @@ def test_handle_timedout_limit_buy(mocker, default_conf, limit_buy_order) -> Non
Trade.session = MagicMock()
trade = MagicMock()
trade.pair = 'LTC/ETH'
limit_buy_order['remaining'] = limit_buy_order['amount']
assert freqtrade.handle_timedout_limit_buy(trade, limit_buy_order)
assert cancel_order_mock.call_count == 1
@@ -2313,6 +2339,7 @@ def test_handle_timedout_limit_buy_corder_empty(mocker, default_conf, limit_buy_
Trade.session = MagicMock()
trade = MagicMock()
trade.pair = 'LTC/ETH'
limit_buy_order['remaining'] = limit_buy_order['amount']
assert freqtrade.handle_timedout_limit_buy(trade, limit_buy_order)
assert cancel_order_mock.call_count == 1
@@ -2380,12 +2407,12 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.172e-05,
'amount': 90.99181073703367,
'amount': 91.07468123861567,
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.172e-05,
'profit_amount': 6.126e-05,
'profit_percent': 0.0611052,
'open_rate': 1.098e-05,
'current_rate': 1.173e-05,
'profit_amount': 6.223e-05,
'profit_ratio': 0.0620716,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.ROI.value,
@@ -2429,12 +2456,12 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.044e-05,
'amount': 90.99181073703367,
'amount': 91.07468123861567,
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.044e-05,
'profit_amount': -5.492e-05,
'profit_percent': -0.05478342,
'open_rate': 1.098e-05,
'current_rate': 1.043e-05,
'profit_amount': -5.406e-05,
'profit_ratio': -0.05392257,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.STOP_LOSS.value,
@@ -2485,12 +2512,12 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.08801e-05,
'amount': 90.99181073703367,
'amount': 91.07468123861567,
'order_type': 'limit',
'open_rate': 1.099e-05,
'current_rate': 1.044e-05,
'profit_amount': -1.498e-05,
'profit_percent': -0.01493766,
'open_rate': 1.098e-05,
'current_rate': 1.043e-05,
'profit_amount': -1.408e-05,
'profit_ratio': -0.01404051,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.STOP_LOSS.value,
@@ -2675,7 +2702,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
assert not trade.is_open
assert trade.close_profit == 0.0611052
assert trade.close_profit == 0.0620716
assert rpc_mock.call_count == 2
last_msg = rpc_mock.call_args_list[-1][0][0]
@@ -2685,12 +2712,12 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.172e-05,
'amount': 90.99181073703367,
'amount': 91.07468123861567,
'order_type': 'market',
'open_rate': 1.099e-05,
'current_rate': 1.172e-05,
'profit_amount': 6.126e-05,
'profit_percent': 0.0611052,
'open_rate': 1.098e-05,
'current_rate': 1.173e-05,
'profit_amount': 6.223e-05,
'profit_ratio': 0.0620716,
'stake_currency': 'BTC',
'fiat_currency': 'USD',
'sell_reason': SellType.ROI.value,
@@ -3712,29 +3739,55 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order
assert freqtrade.handle_trade(trade) is True
def test_get_sell_rate(default_conf, mocker, ticker, order_book_l2) -> None:
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_order_book=order_book_l2,
fetch_ticker=ticker,
)
@pytest.mark.parametrize('side,ask,bid,expected', [
('bid', 10.0, 11.0, 11.0),
('bid', 10.0, 11.2, 11.2),
('bid', 10.0, 11.0, 11.0),
('bid', 9.8, 11.0, 11.0),
('bid', 0.0001, 0.002, 0.002),
('ask', 10.0, 11.0, 10.0),
('ask', 10.11, 11.2, 10.11),
('ask', 0.001, 0.002, 0.001),
('ask', 0.006, 1.0, 0.006),
])
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, expected) -> None:
default_conf['ask_strategy']['price_side'] = side
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'bid': bid})
pair = "ETH/BTC"
# Test regular mode
ft = get_patched_freqtradebot(mocker, default_conf)
rate = ft.get_sell_rate(pair, True)
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float)
assert rate == 0.00001098
assert rate == expected
# Use caching
rate = ft.get_sell_rate(pair, False)
assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
@pytest.mark.parametrize('side,expected', [
('bid', 0.043936), # Value from order_book_l2 fiture - bids side
('ask', 0.043949), # Value from order_book_l2 fiture - asks side
])
def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, order_book_l2):
# Test orderbook mode
default_conf['ask_strategy']['price_side'] = side
default_conf['ask_strategy']['use_order_book'] = True
default_conf['ask_strategy']['order_book_min'] = 1
default_conf['ask_strategy']['order_book_max'] = 2
# TODO: min/max is irrelevant for this test until refactoring
pair = "ETH/BTC"
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
ft = get_patched_freqtradebot(mocker, default_conf)
rate = ft.get_sell_rate(pair, True)
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float)
assert rate == 0.043936
assert rate == expected
rate = ft.get_sell_rate(pair, False)
assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
def test_startup_state(default_conf, mocker):
@@ -3763,30 +3816,6 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker):
assert reinit_mock.call_count == 0
def test_process_i_am_alive(default_conf, mocker, caplog):
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
ftbot = get_patched_freqtradebot(mocker, default_conf)
message = r"Bot heartbeat\. PID=.*"
ftbot.process()
assert log_has_re(message, caplog)
assert ftbot._heartbeat_msg != 0
caplog.clear()
# Message is not shown before interval is up
ftbot.process()
assert not log_has_re(message, caplog)
caplog.clear()
# Set clock - 70 seconds
ftbot._heartbeat_msg -= 70
ftbot.process()
assert log_has_re(message, caplog)
@pytest.mark.usefixtures("init_persistence")
def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order, caplog):
default_conf['dry_run'] = True

View File

@@ -6,7 +6,7 @@ from unittest.mock import MagicMock
import pytest
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.converter import ohlcv_to_dataframe
from freqtrade.misc import (datesarray_to_datetimearray, file_dump_json,
file_load_json, format_ms_time, pair_to_filename,
plural, render_template,
@@ -19,9 +19,9 @@ def test_shorten_date() -> None:
assert shorten_date(str_data) == str_shorten_data
def test_datesarray_to_datetimearray(ticker_history_list):
dataframes = parse_ticker_dataframe(ticker_history_list, "5m", pair="UNITTEST/BTC",
fill_missing=True)
def test_datesarray_to_datetimearray(ohlcv_history_list):
dataframes = ohlcv_to_dataframe(ohlcv_history_list, "5m", pair="UNITTEST/BTC",
fill_missing=True)
dates = datesarray_to_datetimearray(dataframes['date'])
assert isinstance(dates[0], datetime.datetime)

View File

@@ -3,15 +3,16 @@ from copy import deepcopy
from pathlib import Path
from unittest.mock import MagicMock
import pandas as pd
import plotly.graph_objects as go
import pytest
from plotly.subplots import make_subplots
from freqtrade.commands import start_plot_dataframe, start_plot_profit
from freqtrade.configuration import TimeRange
from freqtrade.data import history
from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
from freqtrade.exceptions import OperationalException
from freqtrade.commands import start_plot_dataframe, start_plot_profit
from freqtrade.plot.plotting import (add_indicators, add_profit,
create_plotconfig,
generate_candlestick_graph,
@@ -48,17 +49,17 @@ def test_init_plotscript(default_conf, mocker, testdatadir):
default_conf['trade_source'] = "file"
default_conf['ticker_interval'] = "5m"
default_conf["datadir"] = testdatadir
default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
ret = init_plotscript(default_conf)
assert "tickers" in ret
assert "ohlcv" in ret
assert "trades" in ret
assert "pairs" in ret
default_conf['pairs'] = ["TRX/BTC", "ADA/BTC"]
ret = init_plotscript(default_conf)
assert "tickers" in ret
assert "TRX/BTC" in ret["tickers"]
assert "ADA/BTC" in ret["tickers"]
assert "ohlcv" in ret
assert "TRX/BTC" in ret["ohlcv"]
assert "ADA/BTC" in ret["ohlcv"]
def test_add_indicators(default_conf, testdatadir, caplog):
@@ -266,15 +267,16 @@ def test_generate_profit_graph(testdatadir):
trades = load_backtest_data(filename)
timerange = TimeRange.parse_timerange("20180110-20180112")
pairs = ["TRX/BTC", "ADA/BTC"]
trades = trades[trades['close_time'] < pd.Timestamp('2018-01-12', tz='UTC')]
data = history.load_data(datadir=testdatadir,
pairs=pairs,
timeframe='5m',
timerange=timerange)
tickers = history.load_data(datadir=testdatadir,
pairs=pairs,
timeframe='5m',
timerange=timerange
)
trades = trades[trades['pair'].isin(pairs)]
fig = generate_profit_graph(pairs, tickers, trades, timeframe="5m")
fig = generate_profit_graph(pairs, data, trades, timeframe="5m")
assert isinstance(fig, go.Figure)
assert fig.layout.title.text == "Freqtrade Profit plot"
@@ -283,13 +285,15 @@ def test_generate_profit_graph(testdatadir):
assert fig.layout.yaxis3.title.text == "Profit"
figure = fig.layout.figure
assert len(figure.data) == 4
assert len(figure.data) == 5
avgclose = find_trace_in_fig_data(figure.data, "Avg close price")
assert isinstance(avgclose, go.Scatter)
profit = find_trace_in_fig_data(figure.data, "Profit")
assert isinstance(profit, go.Scatter)
profit = find_trace_in_fig_data(figure.data, "Max drawdown 0.00%")
assert isinstance(profit, go.Scatter)
for pair in pairs:
profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}")
@@ -314,7 +318,7 @@ def test_start_plot_dataframe(mocker):
def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
default_conf['trade_source'] = 'file'
default_conf["datadir"] = testdatadir
default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
default_conf['indicators1'] = ["sma5", "ema10"]
default_conf['indicators2'] = ["macd"]
default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
@@ -370,7 +374,7 @@ def test_start_plot_profit_error(mocker):
def test_plot_profit(default_conf, mocker, testdatadir, caplog):
default_conf['trade_source'] = 'file'
default_conf["datadir"] = testdatadir
default_conf['exportfilename'] = str(testdatadir / "backtest-result_test.json")
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
profit_mock = MagicMock()

View File

@@ -5,7 +5,7 @@ from unittest.mock import MagicMock, PropertyMock
from freqtrade.data.dataprovider import DataProvider
from freqtrade.state import State
from freqtrade.worker import Worker
from tests.conftest import get_patched_worker, log_has
from tests.conftest import get_patched_worker, log_has, log_has_re
def test_worker_state(mocker, default_conf, markets) -> None:
@@ -38,15 +38,13 @@ def test_worker_running(mocker, default_conf, caplog) -> None:
def test_worker_stopped(mocker, default_conf, caplog) -> None:
mock_throttle = MagicMock()
mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle)
mock_sleep = mocker.patch('time.sleep', return_value=None)
worker = get_patched_worker(mocker, default_conf)
worker.freqtrade.state = State.STOPPED
state = worker._worker(old_state=State.RUNNING)
assert state is State.STOPPED
assert log_has('Changing state to: STOPPED', caplog)
assert mock_throttle.call_count == 0
assert mock_sleep.call_count == 1
assert mock_throttle.call_count == 1
def test_throttle(mocker, default_conf, caplog) -> None:
@@ -57,14 +55,14 @@ def test_throttle(mocker, default_conf, caplog) -> None:
worker = get_patched_worker(mocker, default_conf)
start = time.time()
result = worker._throttle(throttled_func, min_secs=0.1)
result = worker._throttle(throttled_func, throttle_secs=0.1)
end = time.time()
assert result == 42
assert end - start > 0.1
assert log_has('Throttling throttled_func for 0.10 seconds', caplog)
assert log_has_re(r"Throttling with 'throttled_func\(\)': sleep for \d\.\d{2} s.*", caplog)
result = worker._throttle(throttled_func, min_secs=-1)
result = worker._throttle(throttled_func, throttle_secs=-1)
assert result == 42
@@ -74,8 +72,54 @@ def test_throttle_with_assets(mocker, default_conf) -> None:
worker = get_patched_worker(mocker, default_conf)
result = worker._throttle(throttled_func, min_secs=0.1, nb_assets=666)
result = worker._throttle(throttled_func, throttle_secs=0.1, nb_assets=666)
assert result == 666
result = worker._throttle(throttled_func, min_secs=0.1)
result = worker._throttle(throttled_func, throttle_secs=0.1)
assert result == -1
def test_worker_heartbeat_running(default_conf, mocker, caplog):
message = r"Bot heartbeat\. PID=.*state='RUNNING'"
mock_throttle = MagicMock()
mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle)
worker = get_patched_worker(mocker, default_conf)
worker.freqtrade.state = State.RUNNING
worker._worker(old_state=State.STOPPED)
assert log_has_re(message, caplog)
caplog.clear()
# Message is not shown before interval is up
worker._worker(old_state=State.RUNNING)
assert not log_has_re(message, caplog)
caplog.clear()
# Set clock - 70 seconds
worker._heartbeat_msg -= 70
worker._worker(old_state=State.RUNNING)
assert log_has_re(message, caplog)
def test_worker_heartbeat_stopped(default_conf, mocker, caplog):
message = r"Bot heartbeat\. PID=.*state='STOPPED'"
mock_throttle = MagicMock()
mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle)
worker = get_patched_worker(mocker, default_conf)
worker.freqtrade.state = State.STOPPED
worker._worker(old_state=State.RUNNING)
assert log_has_re(message, caplog)
caplog.clear()
# Message is not shown before interval is up
worker._worker(old_state=State.STOPPED)
assert not log_has_re(message, caplog)
caplog.clear()
# Set clock - 70 seconds
worker._heartbeat_msg -= 70
worker._worker(old_state=State.STOPPED)
assert log_has_re(message, caplog)