Merge branch 'develop' into bt-metrics2

This commit is contained in:
Matthias
2024-01-27 16:24:05 +01:00
88 changed files with 1050 additions and 760 deletions
+8 -7
View File
@@ -772,7 +772,7 @@ def test_download_data_all_pairs(mocker, markets):
pargs = get_args(args)
pargs['config'] = None
start_download_data(pargs)
expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
expected = set(['BTC/USDT', 'ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
assert dl_mock.call_count == 1
@@ -788,7 +788,7 @@ def test_download_data_all_pairs(mocker, markets):
pargs = get_args(args)
pargs['config'] = None
start_download_data(pargs)
expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
expected = set(['BTC/USDT', 'ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
@@ -1445,12 +1445,13 @@ def test_start_list_data(testdatadir, capsys):
start_list_data(pargs)
captured = capsys.readouterr()
assert "Found 2 pair / timeframe combinations." in captured.out
assert ("\n| Pair | Timeframe | Type | From | To |\n"
in captured.out)
assert (
"\n| Pair | Timeframe | Type "
"| From | To | Candles |\n") in captured.out
assert "UNITTEST/BTC" not in captured.out
assert (
"\n| XRP/ETH | 1m | spot | 2019-10-11 00:00:00 | 2019-10-13 11:19:00 |\n"
in captured.out)
"\n| XRP/ETH | 1m | spot | "
"2019-10-11 00:00:00 | 2019-10-13 11:19:00 | 2469 |\n") in captured.out
@pytest.mark.usefixtures("init_persistence")
@@ -1508,7 +1509,7 @@ def test_backtesting_show(mocker, testdatadir, capsys):
pargs['config'] = None
start_backtesting_show(pargs)
assert sbr.call_count == 1
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert "Pairs for Strategy" in out
+109 -48
View File
@@ -3,7 +3,7 @@ import json
import logging
import re
from copy import deepcopy
from datetime import timedelta
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Optional
from unittest.mock import MagicMock, Mock, PropertyMock
@@ -18,13 +18,11 @@ from freqtrade.commands import Arguments
from freqtrade.data.converter import ohlcv_to_dataframe, trades_list_to_df
from freqtrade.edge import PairInfo
from freqtrade.enums import CandleType, MarginMode, RunMode, SignalDirection, TradingMode
from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange import timeframe_to_minutes
from freqtrade.exchange import Exchange, timeframe_to_minutes, timeframe_to_seconds
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import LocalTrade, Order, Trade, init_db
from freqtrade.resolvers import ExchangeResolver
from freqtrade.util import dt_ts
from freqtrade.util.datetime_helpers import dt_now
from freqtrade.util import dt_now, dt_ts
from freqtrade.worker import Worker
from tests.conftest_trades import (leverage_trade, mock_trade_1, mock_trade_2, mock_trade_3,
mock_trade_4, mock_trade_5, mock_trade_6, short_trade)
@@ -107,17 +105,62 @@ def get_args(args):
return Arguments(args).get_parsed_arg()
def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days=5):
np.random.seed(42)
if not start_date:
start_date = datetime(2020, 1, 1, tzinfo=timezone.utc)
# Generate random data
end_date = start_date + timedelta(days=days)
_start_timestamp = start_date.timestamp()
_end_timestamp = pd.to_datetime(end_date).timestamp()
random_timestamps_in_seconds = np.random.uniform(_start_timestamp, _end_timestamp, n_rows)
timestamp = pd.to_datetime(random_timestamps_in_seconds, unit='s')
id = [
f'a{np.random.randint(1e6, 1e7 - 1)}cd{np.random.randint(100, 999)}'
for _ in range(n_rows)
]
side = np.random.choice(['buy', 'sell'], n_rows)
# Initial price and subsequent changes
initial_price = 0.019626
price_changes = np.random.normal(0, initial_price * 0.05, n_rows)
price = np.cumsum(np.concatenate(([initial_price], price_changes)))[:n_rows]
amount = np.random.uniform(0.011, 20, n_rows)
cost = price * amount
# Create DataFrame
df = pd.DataFrame({'timestamp': timestamp, 'id': id, 'type': None, 'side': side,
'price': price, 'amount': amount, 'cost': cost})
df['date'] = pd.to_datetime(df['timestamp'], unit='ms', utc=True)
df = df.sort_values('timestamp').reset_index(drop=True)
assert list(df.columns) == constants.DEFAULT_TRADES_COLUMNS + ['date']
return df
def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'):
np.random.seed(42)
base = np.random.normal(20, 2, size=size)
if timeframe == '1M':
if timeframe == '1y':
date = pd.date_range(start, periods=size, freq='1YS', tz='UTC')
elif timeframe == '1M':
date = pd.date_range(start, periods=size, freq='1MS', tz='UTC')
elif timeframe == '1w':
elif timeframe == '3M':
date = pd.date_range(start, periods=size, freq='3MS', tz='UTC')
elif timeframe == '1w' or timeframe == '7d':
date = pd.date_range(start, periods=size, freq='1W-MON', tz='UTC')
else:
tf_mins = timeframe_to_minutes(timeframe)
date = pd.date_range(start, periods=size, freq=f'{tf_mins}min', tz='UTC')
if tf_mins >= 1:
date = pd.date_range(start, periods=size, freq=f'{tf_mins}min', tz='UTC')
else:
tf_secs = timeframe_to_seconds(timeframe)
date = pd.date_range(start, periods=size, freq=f'{tf_secs}s', tz='UTC')
df = pd.DataFrame({
'date': date,
'open': base,
@@ -962,6 +1005,58 @@ def get_markets():
'maintenance_rate': '0.005',
},
},
'BTC/USDT': {
'id': 'USDT-BTC',
'symbol': 'BTC/USDT',
'base': 'BTC',
'quote': 'USDT',
'settle': None,
'baseId': 'BTC',
'quoteId': 'USDT',
'settleId': None,
'type': 'spot',
'spot': True,
'margin': True,
'swap': False,
'future': False,
'option': False,
'active': True,
'contract': None,
'linear': None,
'inverse': None,
'taker': 0.0006,
'maker': 0.0002,
'contractSize': None,
'expiry': None,
'expiryDateTime': None,
'strike': None,
'optionType': None,
'precision': {
'amount': 4,
'price': 4,
},
'limits': {
'leverage': {
'min': 1,
'max': 100,
},
'amount': {
'min': 0.000221,
'max': None,
},
'price': {
'min': 1e-02,
'max': None,
},
'cost': {
'min': None,
'max': None,
},
},
'info': {
'maintenance_rate': '0.005',
},
},
'LTC/USDT': {
'id': 'USDT-LTC',
'symbol': 'LTC/USDT',
@@ -2386,14 +2481,7 @@ def trades_history_df(trades_history):
@pytest.fixture(scope="function")
def fetch_trades_result():
return [{'info': {'a': 126181329,
'p': '0.01962700',
'q': '0.04000000',
'f': 138604155,
'l': 138604155,
'T': 1565798399463,
'm': False,
'M': True},
return [{'info': ['0.01962700', '0.04000000', '1565798399.4631551', 'b', 'm', '', '126181329'],
'timestamp': 1565798399463,
'datetime': '2019-08-14T15:59:59.463Z',
'symbol': 'ETH/BTC',
@@ -2406,14 +2494,7 @@ def fetch_trades_result():
'amount': 0.04,
'cost': 0.00078508,
'fee': None},
{'info': {'a': 126181330,
'p': '0.01962700',
'q': '0.24400000',
'f': 138604156,
'l': 138604156,
'T': 1565798399629,
'm': False,
'M': True},
{'info': ['0.01962700', '0.24400000', '1565798399.6291551', 'b', 'm', '', '126181330'],
'timestamp': 1565798399629,
'datetime': '2019-08-14T15:59:59.629Z',
'symbol': 'ETH/BTC',
@@ -2426,14 +2507,7 @@ def fetch_trades_result():
'amount': 0.244,
'cost': 0.004788987999999999,
'fee': None},
{'info': {'a': 126181331,
'p': '0.01962600',
'q': '0.01100000',
'f': 138604157,
'l': 138604157,
'T': 1565798399752,
'm': True,
'M': True},
{'info': ['0.01962600', '0.01100000', '1565798399.7521551', 's', 'm', '', '126181331'],
'timestamp': 1565798399752,
'datetime': '2019-08-14T15:59:59.752Z',
'symbol': 'ETH/BTC',
@@ -2446,14 +2520,7 @@ def fetch_trades_result():
'amount': 0.011,
'cost': 0.00021588599999999999,
'fee': None},
{'info': {'a': 126181332,
'p': '0.01962600',
'q': '0.01100000',
'f': 138604158,
'l': 138604158,
'T': 1565798399862,
'm': True,
'M': True},
{'info': ['0.01962600', '0.01100000', '1565798399.8621551', 's', 'm', '', '126181332'],
'timestamp': 1565798399862,
'datetime': '2019-08-14T15:59:59.862Z',
'symbol': 'ETH/BTC',
@@ -2466,14 +2533,8 @@ def fetch_trades_result():
'amount': 0.011,
'cost': 0.00021588599999999999,
'fee': None},
{'info': {'a': 126181333,
'p': '0.01952600',
'q': '0.01200000',
'f': 138604158,
'l': 138604158,
'T': 1565798399872,
'm': True,
'M': True},
{'info': ['0.01952600', '0.01200000', '1565798399.8721551', 's', 'm', '', '126181333',
1565798399872512133],
'timestamp': 1565798399872,
'datetime': '2019-08-14T15:59:59.872Z',
'symbol': 'ETH/BTC',
+84 -1
View File
@@ -17,7 +17,8 @@ from freqtrade.data.history import (get_timerange, load_data, load_pair_history,
validate_backtest_data)
from freqtrade.data.history.idatahandler import IDataHandler
from freqtrade.enums import CandleType
from tests.conftest import generate_test_data, log_has, log_has_re
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from tests.conftest import generate_test_data, generate_trades_history, log_has, log_has_re
from tests.data.test_history import _clean_test_file
@@ -51,6 +52,49 @@ def test_trades_to_ohlcv(trades_history_df, caplog):
assert 'close' in df.columns
assert df.iloc[0, :]['high'] == 0.019627
assert df.iloc[0, :]['low'] == 0.019626
assert df.iloc[0, :]['date'] == pd.Timestamp('2019-08-14 15:59:00+0000')
df_1h = trades_to_ohlcv(trades_history_df, '1h')
assert len(df_1h) == 1
assert df_1h.iloc[0, :]['high'] == 0.019627
assert df_1h.iloc[0, :]['low'] == 0.019626
assert df_1h.iloc[0, :]['date'] == pd.Timestamp('2019-08-14 15:00:00+0000')
df_1s = trades_to_ohlcv(trades_history_df, '1s')
assert len(df_1s) == 2
assert df_1s.iloc[0, :]['high'] == 0.019627
assert df_1s.iloc[0, :]['low'] == 0.019627
assert df_1s.iloc[0, :]['date'] == pd.Timestamp('2019-08-14 15:59:49+0000')
assert df_1s.iloc[-1, :]['date'] == pd.Timestamp('2019-08-14 15:59:59+0000')
@pytest.mark.parametrize('timeframe,rows,days,candles,start,end,weekday', [
('1s', 20_000, 5, 19522, '2020-01-01 00:00:05', '2020-01-05 23:59:27', None),
('1m', 20_000, 5, 6745, '2020-01-01 00:00:00', '2020-01-05 23:59:00', None),
('5m', 20_000, 5, 1440, '2020-01-01 00:00:00', '2020-01-05 23:55:00', None),
('15m', 20_000, 5, 480, '2020-01-01 00:00:00', '2020-01-05 23:45:00', None),
('1h', 20_000, 5, 120, '2020-01-01 00:00:00', '2020-01-05 23:00:00', None),
('2h', 20_000, 5, 60, '2020-01-01 00:00:00', '2020-01-05 22:00:00', None),
('4h', 20_000, 5, 30, '2020-01-01 00:00:00', '2020-01-05 20:00:00', None),
('8h', 20_000, 5, 15, '2020-01-01 00:00:00', '2020-01-05 16:00:00', None),
('12h', 20_000, 5, 10, '2020-01-01 00:00:00', '2020-01-05 12:00:00', None),
('1d', 20_000, 5, 5, '2020-01-01 00:00:00', '2020-01-05 00:00:00', 'Sunday'),
('7d', 20_000, 37, 6, '2020-01-06 00:00:00', '2020-02-10 00:00:00', 'Monday'),
('1w', 20_000, 37, 6, '2020-01-06 00:00:00', '2020-02-10 00:00:00', 'Monday'),
('1M', 20_000, 74, 3, '2020-01-01 00:00:00', '2020-03-01 00:00:00', None),
('3M', 20_000, 100, 2, '2020-01-01 00:00:00', '2020-04-01 00:00:00', None),
('1y', 20_000, 1000, 3, '2020-01-01 00:00:00', '2022-01-01 00:00:00', None),
])
def test_trades_to_ohlcv_multi(timeframe, rows, days, candles, start, end, weekday):
trades_history = generate_trades_history(n_rows=rows, days=days)
df = trades_to_ohlcv(trades_history, timeframe)
assert not df.empty
assert len(df) == candles
assert df.iloc[0, :]['date'] == pd.Timestamp(f'{start}+0000')
assert df.iloc[-1, :]['date'] == pd.Timestamp(f'{end}+0000')
if weekday:
# Weekday is only relevant for daily and weekly candles.
assert df.iloc[-1, :]['date'].day_name() == weekday
def test_ohlcv_fill_up_missing_data(testdatadir, caplog):
@@ -132,6 +176,45 @@ def test_ohlcv_fill_up_missing_data2(caplog):
f"{len(data)} - after: {len(data2)}.*", caplog)
@pytest.mark.parametrize('timeframe', [
'1s', '1m', '5m', '15m', '1h', '2h', '4h', '8h', '12h', '1d', '7d', '1w', '1M', '3M', '1y'
])
def test_ohlcv_to_dataframe_multi(timeframe):
data = generate_test_data(timeframe, 180)
assert len(data) == 180
df = ohlcv_to_dataframe(data, timeframe, 'UNITTEST/USDT')
assert len(df) == len(data) - 1
df1 = ohlcv_to_dataframe(data, timeframe, 'UNITTEST/USDT', drop_incomplete=False)
assert len(df1) == len(data)
assert data.equals(df1)
data1 = data.copy()
if timeframe in ('1M', '3M', '1y'):
data1.loc[:, 'date'] = data1.loc[:, 'date'] + pd.to_timedelta('1w')
else:
# Shift by half a timeframe
data1.loc[:, 'date'] = data1.loc[:, 'date'] + (pd.to_timedelta(timeframe) / 2)
df2 = ohlcv_to_dataframe(data1, timeframe, 'UNITTEST/USDT')
assert len(df2) == len(data) - 1
tfs = timeframe_to_seconds(timeframe)
tfm = timeframe_to_minutes(timeframe)
if 1 <= tfm < 10000:
# minute based resampling does not work on timeframes >= 1 week
ohlcv_dict = {
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
}
dfs = data1.resample(f"{tfs}s", on='date').agg(ohlcv_dict).reset_index(drop=False)
dfm = data1.resample(f"{tfm}min", on='date').agg(ohlcv_dict).reset_index(drop=False)
assert dfs.equals(dfm)
assert dfs.equals(df1)
def test_ohlcv_to_dataframe_1M():
# Monthly ticks from 2019-09-01 to 2023-07-01
+10 -4
View File
@@ -148,19 +148,25 @@ def test_jsondatahandler_ohlcv_load(testdatadir, caplog):
def test_datahandler_ohlcv_data_min_max(testdatadir):
dh = JsonDataHandler(testdatadir)
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '5m', 'spot')
assert len(min_max) == 2
assert len(min_max) == 3
# Empty pair
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '8m', 'spot')
assert len(min_max) == 2
assert len(min_max) == 3
assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc)
assert min_max[0] == min_max[1]
# Empty pair2
min_max = dh.ohlcv_data_min_max('NOPAIR/XXX', '4m', 'spot')
assert len(min_max) == 2
min_max = dh.ohlcv_data_min_max('NOPAIR/XXX', '41m', 'spot')
assert len(min_max) == 3
assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc)
assert min_max[0] == min_max[1]
# Existing pair ...
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '1m', 'spot')
assert len(min_max) == 3
assert min_max[0] == datetime(2017, 11, 4, 23, 2, tzinfo=timezone.utc)
assert min_max[1] == datetime(2017, 11, 14, 22, 59, tzinfo=timezone.utc)
def test_datahandler__check_empty_df(testdatadir, caplog):
dh = JsonDataHandler(testdatadir)
+1 -1
View File
@@ -194,7 +194,7 @@ def test_get_producer_df(default_conf):
assert la == empty_la
# non existent timeframe, empty dataframe
datframe, la = dataprovider.get_producer_df(pair, timeframe='1h')
_dataframe, la = dataprovider.get_producer_df(pair, timeframe='1h')
assert dataframe.empty
assert la == empty_la
+2 -2
View File
@@ -38,7 +38,7 @@ def test_download_data_main_all_pairs(mocker, markets):
"timeframes": ["5m", "1h"]
})
download_data_main(config)
expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
expected = set(['BTC/USDT', 'ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
assert dl_mock.call_count == 1
@@ -50,7 +50,7 @@ def test_download_data_main_all_pairs(mocker, markets):
"include_inactive": True
})
download_data_main(config)
expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
expected = set(['BTC/USDT', 'ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
+48 -7
View File
@@ -2844,10 +2844,17 @@ async def test__async_fetch_trades(default_conf, mocker, caplog, exchange_name,
exchange._api_async.fetch_trades = get_mock_coro(fetch_trades_result)
pair = 'ETH/BTC'
res = await exchange._async_fetch_trades(pair, since=None, params=None)
res, pagid = await exchange._async_fetch_trades(pair, since=None, params=None)
assert isinstance(res, list)
assert isinstance(res[0], list)
assert isinstance(res[1], list)
if exchange._trades_pagination == 'id':
if exchange_name == 'kraken':
assert pagid == 1565798399872512133
else:
assert pagid == '126181333'
else:
assert pagid == 1565798399872
assert exchange._api_async.fetch_trades.call_count == 1
assert exchange._api_async.fetch_trades.call_args[0][0] == pair
@@ -2856,11 +2863,20 @@ async def test__async_fetch_trades(default_conf, mocker, caplog, exchange_name,
assert log_has_re(f"Fetching trades for pair {pair}, since .*", caplog)
caplog.clear()
exchange._api_async.fetch_trades.reset_mock()
res = await exchange._async_fetch_trades(pair, since=None, params={'from': '123'})
res, pagid = await exchange._async_fetch_trades(pair, since=None, params={'from': '123'})
assert exchange._api_async.fetch_trades.call_count == 1
assert exchange._api_async.fetch_trades.call_args[0][0] == pair
assert exchange._api_async.fetch_trades.call_args[1]['limit'] == 1000
assert exchange._api_async.fetch_trades.call_args[1]['params'] == {'from': '123'}
if exchange._trades_pagination == 'id':
if exchange_name == 'kraken':
assert pagid == 1565798399872512133
else:
assert pagid == '126181333'
else:
assert pagid == 1565798399872
assert log_has_re(f"Fetching trades for pair {pair}, params: .*", caplog)
exchange.close()
@@ -2915,8 +2931,9 @@ async def test__async_fetch_trades_contract_size(default_conf, mocker, caplog, e
)
pair = 'ETH/USDT:USDT'
res = await exchange._async_fetch_trades(pair, since=None, params=None)
res, pagid = await exchange._async_fetch_trades(pair, since=None, params=None)
assert res[0][5] == 300
assert pagid is not None
exchange.close()
@@ -2926,13 +2943,17 @@ async def test__async_get_trade_history_id(default_conf, mocker, exchange_name,
fetch_trades_result):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
if exchange._trades_pagination != 'id':
exchange.close()
pytest.skip("Exchange does not support pagination by trade id")
pagination_arg = exchange._trades_pagination_arg
async def mock_get_trade_hist(pair, *args, **kwargs):
if 'since' in kwargs:
# Return first 3
return fetch_trades_result[:-2]
elif kwargs.get('params', {}).get(pagination_arg) == fetch_trades_result[-3]['id']:
elif kwargs.get('params', {}).get(pagination_arg) in (
fetch_trades_result[-3]['id'], 1565798399752):
# Return 2
return fetch_trades_result[-3:-1]
else:
@@ -2948,7 +2969,8 @@ async def test__async_get_trade_history_id(default_conf, mocker, exchange_name,
assert isinstance(ret, tuple)
assert ret[0] == pair
assert isinstance(ret[1], list)
assert len(ret[1]) == len(fetch_trades_result)
if exchange_name != 'kraken':
assert len(ret[1]) == len(fetch_trades_result)
assert exchange._api_async.fetch_trades.call_count == 3
fetch_trades_cal = exchange._api_async.fetch_trades.call_args_list
# first call (using since, not fromId)
@@ -2961,6 +2983,21 @@ async def test__async_get_trade_history_id(default_conf, mocker, exchange_name,
assert exchange._ft_has['trades_pagination_arg'] in fetch_trades_cal[1][1]['params']
@pytest.mark.parametrize('trade_id, expected', [
('1234', True),
('170544369512007228', True),
('1705443695120072285', True),
('170544369512007228555', True),
])
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test__valid_trade_pagination_id(mocker, default_conf_usdt, exchange_name, trade_id, expected):
if exchange_name == 'kraken':
pytest.skip("Kraken has a different pagination id format, and an explicit test.")
exchange = get_patched_exchange(mocker, default_conf_usdt, id=exchange_name)
assert exchange._valid_trade_pagination_id('XRP/USDT', trade_id) == expected
@pytest.mark.asyncio
@pytest.mark.parametrize("exchange_name", EXCHANGES)
async def test__async_get_trade_history_time(default_conf, mocker, caplog, exchange_name,
@@ -2976,6 +3013,9 @@ async def test__async_get_trade_history_time(default_conf, mocker, caplog, excha
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
if exchange._trades_pagination != 'time':
exchange.close()
pytest.skip("Exchange does not support pagination by timestamp")
# Monkey-patch async function
exchange._api_async.fetch_trades = MagicMock(side_effect=mock_get_trade_hist)
pair = 'ETH/BTC'
@@ -3008,9 +3048,9 @@ async def test__async_get_trade_history_time_empty(default_conf, mocker, caplog,
async def mock_get_trade_hist(pair, *args, **kwargs):
if kwargs['since'] == trades_history[0][0]:
return trades_history[:-1]
return trades_history[:-1], trades_history[:-1][-1][0]
else:
return []
return [], None
caplog.set_level(logging.DEBUG)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
@@ -5312,3 +5352,4 @@ def test_price_to_precision_with_default_conf(default_conf, mocker):
patched_ex = get_patched_exchange(mocker, conf)
prec_price = patched_ex.price_to_precision("XRP/USDT", 1.0000000101)
assert prec_price == 1.00000001
assert prec_price == 1.00000001
+16 -1
View File
@@ -10,7 +10,7 @@ from freqtrade.exceptions import OperationalException
from freqtrade.exchange import (amount_to_contract_precision, amount_to_precision,
date_minus_candles, price_to_precision, timeframe_to_minutes,
timeframe_to_msecs, timeframe_to_next_date, timeframe_to_prev_date,
timeframe_to_seconds)
timeframe_to_resample_freq, timeframe_to_seconds)
from freqtrade.exchange.check_exchange import check_exchange
from tests.conftest import log_has_re
@@ -124,6 +124,21 @@ def test_timeframe_to_msecs():
assert timeframe_to_msecs("1d") == 86400000
@pytest.mark.parametrize("timeframe,expected", [
("1s", '1s'),
("15s", '15s'),
("5m", '300s'),
("10m", '600s'),
("1h", '3600s'),
("1d", '86400s'),
("1w", '1W-MON'),
("1M", '1MS'),
("1y", '1YS'),
])
def test_timeframe_to_resample_freq(timeframe, expected):
assert timeframe_to_resample_freq(timeframe) == expected
def test_timeframe_to_prev_date():
# 2019-08-12 13:22:08
date = datetime.fromtimestamp(1565616128, tz=timezone.utc)
@@ -14,7 +14,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers
(0.99, 220 * 0.99, "sell"),
(0.98, 220 * 0.98, "sell"),
])
def test_create_stoploss_order_huobi(default_conf, mocker, limitratio, expected, side):
def test_create_stoploss_order_htx(default_conf, mocker, limitratio, expected, side):
api_mock = MagicMock()
order_id = f'test_prod_buy_{randint(0, 10 ** 6)}'
order_type = 'stop-limit'
@@ -29,7 +29,7 @@ def test_create_stoploss_order_huobi(default_conf, mocker, limitratio, expected,
mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y)
mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'huobi')
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'htx')
with pytest.raises(InvalidOrderException):
order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=190,
@@ -58,7 +58,7 @@ def test_create_stoploss_order_huobi(default_conf, mocker, limitratio, expected,
# test exception handling
with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'huobi')
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'htx')
exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220,
order_types={}, side=side, leverage=1.0)
@@ -69,20 +69,20 @@ def test_create_stoploss_order_huobi(default_conf, mocker, limitratio, expected,
exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=220,
order_types={}, side=side, leverage=1.0)
ccxt_exceptionhandlers(mocker, default_conf, api_mock, "huobi",
ccxt_exceptionhandlers(mocker, default_conf, api_mock, "htx",
"create_stoploss", "create_order", retries=1,
pair='ETH/BTC', amount=1, stop_price=220, order_types={},
side=side, leverage=1.0)
def test_create_stoploss_order_dry_run_huobi(default_conf, mocker):
def test_create_stoploss_order_dry_run_htx(default_conf, mocker):
api_mock = MagicMock()
order_type = 'stop-limit'
default_conf['dry_run'] = True
mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y)
mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'huobi')
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'htx')
with pytest.raises(InvalidOrderException):
order = exchange.create_stoploss(pair='ETH/BTC', amount=1, stop_price=190,
@@ -103,8 +103,8 @@ def test_create_stoploss_order_dry_run_huobi(default_conf, mocker):
assert order['amount'] == 1
def test_stoploss_adjust_huobi(mocker, default_conf):
exchange = get_patched_exchange(mocker, default_conf, id='huobi')
def test_stoploss_adjust_htx(mocker, default_conf):
exchange = get_patched_exchange(mocker, default_conf, id='htx')
order = {
'type': 'stop',
'price': 1500,
+26 -17
View File
@@ -183,19 +183,17 @@ def test_create_stoploss_order_kraken(default_conf, mocker, ordertype, side, adj
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC'
if ordertype == 'limit':
assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_LIMIT_ORDERTYPE
assert api_mock.create_order.call_args_list[0][1]['params'] == {
'trading_agreement': 'agree',
'price2': adjustedprice
}
else:
assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE
assert api_mock.create_order.call_args_list[0][1]['params'] == {
'trading_agreement': 'agree'}
assert api_mock.create_order.call_args_list[0][1]['type'] == ordertype
assert api_mock.create_order.call_args_list[0][1]['params'] == {
'trading_agreement': 'agree',
'stopLossPrice': 220
}
assert api_mock.create_order.call_args_list[0][1]['side'] == side
assert api_mock.create_order.call_args_list[0][1]['amount'] == 1
assert api_mock.create_order.call_args_list[0][1]['price'] == 220
if ordertype == 'limit':
assert api_mock.create_order.call_args_list[0][1]['price'] == adjustedprice
else:
assert api_mock.create_order.call_args_list[0][1]['price'] is None
# test exception handling
with pytest.raises(DependencyException):
@@ -253,7 +251,7 @@ def test_create_stoploss_order_dry_run_kraken(default_conf, mocker, side):
assert 'info' in order
assert 'type' in order
assert order['type'] == STOPLOSS_ORDERTYPE
assert order['type'] == 'market'
assert order['price'] == 220
assert order['amount'] == 1
@@ -265,11 +263,22 @@ def test_create_stoploss_order_dry_run_kraken(default_conf, mocker, side):
def test_stoploss_adjust_kraken(mocker, default_conf, sl1, sl2, sl3, side):
exchange = get_patched_exchange(mocker, default_conf, id='kraken')
order = {
'type': STOPLOSS_ORDERTYPE,
'price': 1500,
'type': 'market',
'stopLossPrice': 1500,
}
assert exchange.stoploss_adjust(sl1, order, side=side)
assert not exchange.stoploss_adjust(sl2, order, side=side)
# Test with invalid order case ...
order['type'] = 'stop_loss_limit'
assert not exchange.stoploss_adjust(sl3, order, side=side)
# diff. order type ...
order['type'] = 'limit'
assert exchange.stoploss_adjust(sl3, order, side=side)
@pytest.mark.parametrize('trade_id, expected', [
('1234', False),
('170544369512007228', False),
('1705443695120072285', True),
('170544369512007228555', True),
])
def test__valid_trade_pagination_id_kraken(mocker, default_conf_usdt, trade_id, expected):
exchange = get_patched_exchange(mocker, default_conf_usdt, id='kraken')
assert exchange._valid_trade_pagination_id('XRP/USDT', trade_id) == expected
+1 -1
View File
@@ -247,7 +247,7 @@ EXCHANGES = {
'timeframe': '1h',
'orderbook_max_entries': 50,
},
'huobi': {
'htx': {
'pair': 'ETH/BTC',
'stake_currency': 'BTC',
'hasQuoteVolume': True,
+6 -1
View File
@@ -1,4 +1,5 @@
import platform
import sys
from copy import deepcopy
from pathlib import Path
from typing import Any, Dict
@@ -15,6 +16,10 @@ from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver
from tests.conftest import get_patched_exchange
def is_py12() -> bool:
return sys.version_info >= (3, 12)
def is_mac() -> bool:
machine = platform.system()
return "Darwin" in machine
@@ -31,7 +36,7 @@ def patch_torch_initlogs(mocker) -> None:
module_name = 'torch'
mocked_module = types.ModuleType(module_name)
sys.modules[module_name] = mocked_module
else:
elif not is_py12():
mocker.patch("torch._logging._init_logs")
+1 -1
View File
@@ -76,7 +76,7 @@ def test_filter_features(mocker, freqai_conf):
freqai, unfiltered_dataframe = make_unfiltered_dataframe(mocker, freqai_conf)
freqai.dk.find_features(unfiltered_dataframe)
filtered_df, labels = freqai.dk.filter_features(
filtered_df, _labels = freqai.dk.filter_features(
unfiltered_dataframe,
freqai.dk.training_features_list,
freqai.dk.label_list,
+6 -7
View File
@@ -1,7 +1,6 @@
import logging
import platform
import shutil
import sys
from pathlib import Path
from unittest.mock import MagicMock
@@ -16,24 +15,24 @@ from freqtrade.optimize.backtesting import Backtesting
from freqtrade.persistence import Trade
from freqtrade.plugins.pairlistmanager import PairListManager
from tests.conftest import EXMS, create_mock_trades, get_patched_exchange, log_has_re
from tests.freqai.conftest import (get_patched_freqai_strategy, is_mac, make_rl_config,
from tests.freqai.conftest import (get_patched_freqai_strategy, is_mac, is_py12, make_rl_config,
mock_pytorch_mlp_model_training_parameters)
def is_py12() -> bool:
return sys.version_info >= (3, 12)
def is_arm() -> bool:
machine = platform.machine()
return "arm" in machine or "aarch64" in machine
def can_run_model(model: str) -> None:
is_pytorch_model = 'Reinforcement' in model or 'PyTorch' in model
if is_py12() and ("Catboost" in model or is_pytorch_model):
pytest.skip("Model not supported on python 3.12 yet.")
if is_arm() and "Catboost" in model:
pytest.skip("CatBoost is not supported on ARM.")
is_pytorch_model = 'Reinforcement' in model or 'PyTorch' in model
if is_pytorch_model and is_mac() and not is_arm():
pytest.skip("Reinforcement learning / PyTorch module not available on intel based Mac OS.")
+1 -1
View File
@@ -734,7 +734,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
'min_rate': [0.10370188, 0.10300000000000001],
'max_rate': [0.10501, 0.1038888],
'is_open': [False, False],
'enter_tag': [None, None],
'enter_tag': ['', ''],
"leverage": [1.0, 1.0],
"is_short": [False, False],
'open_timestamp': [1517251200000, 1517283000000],
@@ -72,7 +72,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
'min_rate': [0.10370188, 0.10300000000000001],
'max_rate': [0.10481985, 0.1038888],
'is_open': [False, False],
'enter_tag': [None, None],
'enter_tag': ['', ''],
'leverage': [1.0, 1.0],
'is_short': [False, False],
'open_timestamp': [1517251200000, 1517283000000],
+10 -10
View File
@@ -254,7 +254,7 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
'is_best': True
}
)
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert all(x in out
for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "00:20:00"])
@@ -333,7 +333,7 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
# Should be called for historical candle data
assert dumper.call_count == 1
@@ -577,7 +577,7 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
result_str = (
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi"'
':{},"stoploss":null,"trailing_stop":null,"max_open_trades":null}'
@@ -624,7 +624,7 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' in out # noqa: E501
# Should be called for historical candle data
assert dumper.call_count == 1
@@ -666,7 +666,7 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert '{"minimal_roi":{},"stoploss":null}' in out
assert dumper.call_count == 1
@@ -704,7 +704,7 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.call_count == 1
assert dumper2.call_count == 1
@@ -777,7 +777,7 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
assert dumper.call_count == 1
@@ -819,7 +819,7 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
parallel.assert_called_once()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
assert dumper.called
assert dumper.call_count == 1
@@ -1051,7 +1051,7 @@ def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> N
hyperopt.start()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert 'max_open_trades = -1' in out
assert 'max_open_trades = inf' not in out
@@ -1070,7 +1070,7 @@ def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> N
hyperopt.start()
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert '"max_open_trades":-1' in out
+9 -9
View File
@@ -143,8 +143,8 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf
instance = LookaheadAnalysis(lookahead_conf, strategy_obj)
instance.current_analysis = analysis
table, headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
_table, _headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
# check row contents for a try that has too few signals
assert data[0][0] == 'strategy_test_v3_with_lookahead_bias.py'
@@ -158,14 +158,14 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf
analysis.false_exit_signals = 10
instance = LookaheadAnalysis(lookahead_conf, strategy_obj)
instance.current_analysis = analysis
table, headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
_table, _headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
assert data[0][2].__contains__("error")
# edit it into not showing an error
instance.failed_bias_check = False
table, headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
_table, _headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
assert data[0][0] == 'strategy_test_v3_with_lookahead_bias.py'
assert data[0][1] == 'strategy_test_v3_with_lookahead_bias'
assert data[0][2] # True
@@ -176,8 +176,8 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf
analysis.false_indicators.append('falseIndicator1')
analysis.false_indicators.append('falseIndicator2')
table, headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
_table, _headers, data = (LookaheadAnalysisSubFunctions.
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
assert data[0][6] == 'falseIndicator1, falseIndicator2'
@@ -185,7 +185,7 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf
assert len(data) == 1
# check amount of multiple rows
table, headers, data = (LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances(
_table, _headers, data = (LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances(
lookahead_conf, [instance, instance, instance]))
assert len(data) == 3
+1 -1
View File
@@ -513,7 +513,7 @@ def test_show_sorted_pairlist(testdatadir, default_conf, capsys):
show_sorted_pairlist(default_conf, bt_data)
out, err = capsys.readouterr()
out, _err = capsys.readouterr()
assert 'Pairs for Strategy StrategyTestV3: \n[' in out
assert 'TOTAL' not in out
assert '"ETH/BTC", // ' in out
+4 -4
View File
@@ -107,8 +107,8 @@ def test_recursive_helper_text_table_recursive_analysis_instances(recursive_conf
instance = RecursiveAnalysis(recursive_conf, strategy_obj)
instance.dict_recursive = dict_diff
table, headers, data = (RecursiveAnalysisSubFunctions.
text_table_recursive_analysis_instances([instance]))
_table, _headers, data = (RecursiveAnalysisSubFunctions.
text_table_recursive_analysis_instances([instance]))
# check row contents for a try that has too few signals
assert data[0][0] == 'rsi'
@@ -119,8 +119,8 @@ def test_recursive_helper_text_table_recursive_analysis_instances(recursive_conf
dict_diff = dict()
instance = RecursiveAnalysis(recursive_conf, strategy_obj)
instance.dict_recursive = dict_diff
table, headers, data = (RecursiveAnalysisSubFunctions.
text_table_recursive_analysis_instances([instance]))
_table, _headers, data = (RecursiveAnalysisSubFunctions.
text_table_recursive_analysis_instances([instance]))
assert len(data) == 0
+24
View File
@@ -0,0 +1,24 @@
import pytest
from freqtrade.persistence import FtNoDBContext, PairLocks, Trade
@pytest.mark.parametrize('timeframe', ['', '5m', '1d'])
def test_FtNoDBContext(timeframe):
PairLocks.timeframe = ''
assert Trade.use_db is True
assert PairLocks.use_db is True
assert PairLocks.timeframe == ''
with FtNoDBContext(timeframe):
assert Trade.use_db is False
assert PairLocks.use_db is False
assert PairLocks.timeframe == timeframe
with FtNoDBContext():
assert Trade.use_db is False
assert PairLocks.use_db is False
assert PairLocks.timeframe == ''
assert Trade.use_db is True
assert PairLocks.use_db is True
+7 -5
View File
@@ -82,7 +82,7 @@ def test_fetch_pairlist_mock_response_html(mocker, rpl_config):
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config['pairlists'][0], 0)
with pytest.raises(OperationalException, match='RemotePairList is not of type JSON, abort.'):
with pytest.raises(OperationalException, match='RemotePairList is not of type JSON.'):
remote_pairlist.fetch_pairlist()
@@ -107,9 +107,11 @@ def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog):
rpl_config['pairlists'][0], 0)
remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
remote_pairlist._init_done = True
pairlist_url = rpl_config['pairlists'][0]['pairlist_url']
pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
pairs, time_elapsed = remote_pairlist.fetch_pairlist()
assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url}", caplog)
assert log_has(f'Error: Was not able to fetch pairlist from: ' f'{pairlist_url}', caplog)
assert log_has("Keeping last fetched pairlist", caplog)
assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
@@ -281,7 +283,7 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config["pairlists"][1], 1)
pairs, time_elapsed = remote_pairlist.fetch_pairlist()
pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
assert pairs == ["XRP/USDT"]
@@ -334,7 +336,7 @@ def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets,
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config["pairlists"][1], 1)
pairs, time_elapsed = remote_pairlist.fetch_pairlist()
pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
assert pairs == ["XRP/USDT"]
+20 -20
View File
@@ -112,7 +112,7 @@ def assert_response(response, expected_code=200, needs_cors=True):
def test_api_not_found(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/invalid_url")
assert_response(rc, 404)
@@ -120,7 +120,7 @@ def test_api_not_found(botclient):
def test_api_ui_fallback(botclient, mocker):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, "/favicon.ico")
assert rc.status_code == 200
@@ -150,7 +150,7 @@ def test_api_ui_fallback(botclient, mocker):
def test_api_ui_version(botclient, mocker):
ftbot, client = botclient
_ftbot, client = botclient
mocker.patch('freqtrade.commands.deploy_commands.read_ui_version', return_value='0.1.2')
rc = client_get(client, "/ui_version")
@@ -230,7 +230,7 @@ def test_api_unauthorized(botclient):
def test_api_token_login(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client.post(f"{BASE_URI}/token/login",
data=None,
headers={'Authorization': _basic_auth_str('WRONG_USER', 'WRONG_PASS'),
@@ -249,7 +249,7 @@ def test_api_token_login(botclient):
def test_api_token_refresh(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_post(client, f"{BASE_URI}/token/login")
assert_response(rc)
rc = client.post(f"{BASE_URI}/token/refresh",
@@ -541,7 +541,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets, is_short):
def test_api_locks(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/locks")
assert_response(rc)
@@ -821,7 +821,7 @@ def test_api_trade_reload_trade(botclient, mocker, fee, markets, ticker, is_shor
def test_api_logs(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/logs")
assert_response(rc)
assert len(rc.json()) == 2
@@ -1228,7 +1228,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
def test_api_version(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/version")
assert_response(rc)
@@ -1236,7 +1236,7 @@ def test_api_version(botclient):
def test_api_blacklist(botclient, mocker):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/blacklist")
assert_response(rc)
@@ -1303,7 +1303,7 @@ def test_api_blacklist(botclient, mocker):
def test_api_whitelist(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/whitelist")
assert_response(rc)
@@ -1558,7 +1558,7 @@ def test_api_pair_candles(botclient, ohlcv_history):
def test_api_pair_history(botclient, mocker):
ftbot, client = botclient
_ftbot, client = botclient
timeframe = '5m'
lfm = mocker.patch('freqtrade.strategy.interface.IStrategy.load_freqAI_model')
# No pair
@@ -1603,9 +1603,9 @@ def test_api_pair_history(botclient, mocker):
assert 'data' in result
data = result['data']
assert len(data) == 289
# analyed DF has 28 columns
assert len(result['columns']) == 28
assert len(data[0]) == 28
# analyed DF has 30 columns
assert len(result['columns']) == 30
assert len(data[0]) == 30
date_col_idx = [idx for idx, c in enumerate(result['columns']) if c == 'date'][0]
rsi_col_idx = [idx for idx, c in enumerate(result['columns']) if c == 'rsi'][0]
@@ -1698,7 +1698,7 @@ def test_api_strategies(botclient, tmp_path):
def test_api_strategy(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/strategy/{CURRENT_TEST_STRATEGY}")
@@ -1717,7 +1717,7 @@ def test_api_strategy(botclient):
def test_api_exchanges(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/exchanges")
assert_response(rc)
@@ -1954,7 +1954,7 @@ def test_list_available_pairs(botclient):
def test_sysinfo(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/sysinfo")
assert_response(rc)
@@ -2234,7 +2234,7 @@ def test_api_patch_backtest_history_entry(botclient, tmp_path: Path):
def test_health(botclient):
ftbot, client = botclient
_ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/health")
@@ -2245,7 +2245,7 @@ def test_health(botclient):
def test_api_ws_subscribe(botclient, mocker):
ftbot, client = botclient
_ftbot, client = botclient
ws_url = f"/api/v1/message/ws?token={_TEST_WS_TOKEN}"
sub_mock = mocker.patch('freqtrade.rpc.api_server.ws.WebSocketChannel.set_subscriptions')
@@ -2268,7 +2268,7 @@ def test_api_ws_subscribe(botclient, mocker):
def test_api_ws_requests(botclient, caplog):
caplog.set_level(logging.DEBUG)
ftbot, client = botclient
_ftbot, client = botclient
ws_url = f"/api/v1/message/ws?token={_TEST_WS_TOKEN}"
# Test whitelist request
+9 -20
View File
@@ -2,7 +2,6 @@
Unit test file for rpc/external_message_consumer.py
"""
import asyncio
import functools
import logging
from datetime import datetime, timezone
from unittest.mock import MagicMock
@@ -302,19 +301,16 @@ async def test_emc_receive_messages_valid(default_conf, caplog, mocker):
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
loop = asyncio.get_event_loop()
def change_running(emc): emc._running = not emc._running
class TestChannel:
async def recv(self, *args, **kwargs):
emc._running = False
return {"type": "whitelist", "data": ["BTC/USDT"]}
async def ping(self, *args, **kwargs):
return asyncio.Future()
try:
change_running(emc)
loop.call_soon(functools.partial(change_running, emc=emc))
emc._running = True
await emc._receive_messages(TestChannel(), test_producer, lock)
assert log_has_re(r"Received message of type `whitelist`.+", caplog)
@@ -349,19 +345,16 @@ async def test_emc_receive_messages_invalid(default_conf, caplog, mocker):
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
loop = asyncio.get_event_loop()
def change_running(emc): emc._running = not emc._running
class TestChannel:
async def recv(self, *args, **kwargs):
emc._running = False
return {"type": ["BTC/USDT"]}
async def ping(self, *args, **kwargs):
return asyncio.Future()
try:
change_running(emc)
loop.call_soon(functools.partial(change_running, emc=emc))
emc._running = True
await emc._receive_messages(TestChannel(), test_producer, lock)
assert log_has_re(r"Invalid message from.+", caplog)
@@ -396,8 +389,8 @@ async def test_emc_receive_messages_timeout(default_conf, caplog, mocker):
dp = DataProvider(default_conf, None, None, None)
emc = ExternalMessageConsumer(default_conf, dp)
loop = asyncio.get_event_loop()
def change_running(emc): emc._running = not emc._running
def change_running():
emc._running = not emc._running
class TestChannel:
async def recv(self, *args, **kwargs):
@@ -407,8 +400,7 @@ async def test_emc_receive_messages_timeout(default_conf, caplog, mocker):
return asyncio.Future()
try:
change_running(emc)
loop.call_soon(functools.partial(change_running, emc=emc))
change_running()
with pytest.raises(asyncio.TimeoutError):
await emc._receive_messages(TestChannel(), test_producer, lock)
@@ -447,19 +439,16 @@ async def test_emc_receive_messages_handle_error(default_conf, caplog, mocker):
emc.handle_producer_message = MagicMock(side_effect=Exception)
loop = asyncio.get_event_loop()
def change_running(emc): emc._running = not emc._running
class TestChannel:
async def recv(self, *args, **kwargs):
emc._running = False
return {"type": "whitelist", "data": ["BTC/USDT"]}
async def ping(self, *args, **kwargs):
return asyncio.Future()
try:
change_running(emc)
loop.call_soon(functools.partial(change_running, emc=emc))
emc._running = True
await emc._receive_messages(TestChannel(), test_producer, lock)
assert log_has_re(r"Error handling producer message.+", caplog)
+33 -21
View File
@@ -599,7 +599,7 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
get_fee=fee,
)
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
# Move date to within day
time_machine.move_to('2022-06-11 08:00:00+00:00')
@@ -1480,7 +1480,7 @@ async def test_telegram_performance_handle(default_conf_usdt, update, ticker, fe
fetch_ticker=ticker,
get_fee=fee,
)
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt)
# Create some test data
create_mock_trades_usdt(fee)
@@ -1655,7 +1655,7 @@ async def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -
async def test_whitelist_static(default_conf, update, mocker) -> None:
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
await telegram._whitelist(update=update, context=MagicMock())
assert msg_mock.call_count == 1
@@ -2008,6 +2008,7 @@ def test_send_msg_enter_notification(default_conf, mocker, caplog, message_type,
'quote_currency': 'BTC',
'base_currency': 'ETH',
'fiat_currency': 'USD',
'sub_trade': False,
'current_rate': 1.099e-05,
'amount': 1333.3333333333335,
'analyzed_candle': {'open': 1.1, 'high': 2.2, 'low': 1.0, 'close': 1.5},
@@ -2016,14 +2017,16 @@ def test_send_msg_enter_notification(default_conf, mocker, caplog, message_type,
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram.send_msg(msg)
leverage_text = f'*Leverage:* `{leverage:.1g}`\n' if leverage and leverage != 1.0 else ''
leverage_text = f' ({leverage:.1g}x)' if leverage and leverage != 1.0 else ''
assert msg_mock.call_args[0][0] == (
f'\N{LARGE BLUE CIRCLE} *Binance (dry):* {enter} ETH/BTC (#1)\n'
f'\N{LARGE BLUE CIRCLE} *Binance (dry):* New Trade (#1)\n'
f'*Pair:* `ETH/BTC`\n'
'*Candle OHLC*: `1.1, 2.2, 1.0, 1.5`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Amount:* `1333.33333333`\n'
f'{leverage_text}'
f'*Direction:* `{enter}'
f'{leverage_text}`\n'
'*Open Rate:* `0.00001099 BTC`\n'
'*Current Rate:* `0.00001099 BTC`\n'
'*Total:* `0.01465333 BTC / 180.895 USD`'
@@ -2114,6 +2117,7 @@ def test_send_msg_entry_fill_notification(default_conf, mocker, message_type, en
'leverage': leverage,
'stake_amount': 0.01465333,
'direction': entered,
'sub_trade': False,
'stake_currency': 'BTC',
'quote_currency': 'BTC',
'base_currency': 'ETH',
@@ -2122,12 +2126,14 @@ def test_send_msg_entry_fill_notification(default_conf, mocker, message_type, en
'amount': 1333.3333333333335,
'open_date': dt_now() - timedelta(hours=1)
})
leverage_text = f'*Leverage:* `{leverage:.1g}`\n' if leverage != 1.0 else ''
leverage_text = f' ({leverage:.1g}x)' if leverage != 1.0 else ''
assert msg_mock.call_args[0][0] == (
f'\N{CHECK MARK} *Binance (dry):* {entered}ed ETH/BTC (#1)\n'
f'\N{CHECK MARK} *Binance (dry):* New Trade filled (#1)\n'
f'*Pair:* `ETH/BTC`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Amount:* `1333.33333333`\n'
f"{leverage_text}"
f'*Direction:* `{entered}'
f"{leverage_text}`\n"
'*Open Rate:* `0.00001099 BTC`\n'
'*Total:* `0.01465333 BTC / 180.895 USD`'
)
@@ -2153,12 +2159,14 @@ def test_send_msg_entry_fill_notification(default_conf, mocker, message_type, en
})
assert msg_mock.call_args[0][0] == (
f'\N{CHECK MARK} *Binance (dry):* {entered}ed ETH/BTC (#1)\n'
f'\N{CHECK MARK} *Binance (dry):* Position increase filled (#1)\n'
f'*Pair:* `ETH/BTC`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Amount:* `1333.33333333`\n'
f"{leverage_text}"
f'*Direction:* `{entered}'
f"{leverage_text}`\n"
'*Open Rate:* `0.00001099 BTC`\n'
'*Total:* `0.01465333 BTC / 180.895 USD`'
'*New Total:* `0.01465333 BTC / 180.895 USD`'
)
@@ -2357,13 +2365,13 @@ def test_send_msg_exit_fill_notification(default_conf, mocker, direction,
'close_date': dt_now(),
})
leverage_text = f'*Leverage:* `{leverage:.1g}`\n' if leverage and leverage != 1.0 else ''
leverage_text = f' ({leverage:.1g}x)`\n' if leverage and leverage != 1.0 else '`\n'
assert msg_mock.call_args[0][0] == (
'\N{WARNING SIGN} *Binance (dry):* Exited KEY/ETH (#1)\n'
'*Profit:* `-57.41% (loss: -0.05746 ETH)`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Exit Reason:* `stop_loss`\n'
f"*Direction:* `{direction}`\n"
f"*Direction:* `{direction}"
f"{leverage_text}"
'*Amount:* `1333.33333333`\n'
'*Open Rate:* `0.00075 ETH`\n'
@@ -2438,6 +2446,7 @@ def test_send_msg_buy_notification_no_fiat(
'open_rate': 1.099e-05,
'order_type': 'limit',
'direction': enter,
'sub_trade': False,
'stake_amount': 0.01465333,
'stake_amount_fiat': 0.0,
'stake_currency': 'BTC',
@@ -2449,12 +2458,14 @@ def test_send_msg_buy_notification_no_fiat(
'open_date': dt_now() - timedelta(hours=1)
})
leverage_text = f'*Leverage:* `{leverage:.1g}`\n' if leverage and leverage != 1.0 else ''
leverage_text = f' ({leverage:.1g}x)' if leverage and leverage != 1.0 else ''
assert msg_mock.call_args[0][0] == (
f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n'
f'\N{LARGE BLUE CIRCLE} *Binance:* New Trade (#1)\n'
'*Pair:* `ETH/BTC`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Amount:* `1333.33333333`\n'
f'{leverage_text}'
f'*Direction:* `{enter}'
f'{leverage_text}`\n'
'*Open Rate:* `0.00001099 BTC`\n'
'*Current Rate:* `0.00001099 BTC`\n'
'*Total:* `0.01465333 BTC`'
@@ -2481,6 +2492,7 @@ def test_send_msg_exit_notification_no_fiat(
'gain': 'loss',
'leverage': leverage,
'direction': direction,
'sub_trade': False,
'order_rate': 3.201e-04,
'amount': 1333.3333333333335,
'order_type': 'limit',
@@ -2498,14 +2510,14 @@ def test_send_msg_exit_notification_no_fiat(
'close_date': dt_now(),
})
leverage_text = f'*Leverage:* `{leverage:.1g}`\n' if leverage and leverage != 1.0 else ''
leverage_text = f' ({leverage:.1g}x)' if leverage and leverage != 1.0 else ''
assert msg_mock.call_args[0][0] == (
'\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n'
'*Unrealized Profit:* `-57.41% (loss: -0.05746 ETH)`\n'
f'*Enter Tag:* `{enter_signal}`\n'
'*Exit Reason:* `stop_loss`\n'
f'*Direction:* `{direction}`\n'
f'{leverage_text}'
f'*Direction:* `{direction}'
f'{leverage_text}`\n'
'*Amount:* `1333.33333333`\n'
'*Open Rate:* `0.00075 ETH`\n'
'*Current Rate:* `0.00032 ETH`\n'
@@ -2635,7 +2647,7 @@ async def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
async def test_change_market_direction(default_conf, mocker, update) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram, _, _msg_mock = get_telegram_testobject(mocker, default_conf)
assert telegram._rpc._freqtrade.strategy.market_direction == MarketDirection.NONE
context = MagicMock()
context.args = ["long"]
+2 -2
View File
@@ -152,7 +152,7 @@ class StrategyTestV3(IStrategy):
(
qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value)
),
'enter_short'] = 1
('enter_short', 'enter_tag')] = (1, 'short_Tag')
return dataframe
@@ -176,7 +176,7 @@ class StrategyTestV3(IStrategy):
(
qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)
),
'exit_short'] = 1
('exit_short', 'exit_tag')] = (1, 'short_Tag')
return dataframe
+28 -1
View File
@@ -105,7 +105,7 @@ def test_returns_latest_signal(ohlcv_history):
_STRATEGY.config['trading_mode'] = 'spot'
def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history):
def test_analyze_pair_empty(mocker, caplog, ohlcv_history):
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
mocker.patch.object(
_STRATEGY, '_analyze_ticker_internal',
@@ -1019,3 +1019,30 @@ def test_auto_hyperopt_interface_loadparams(default_conf, mocker, caplog):
StrategyResolver.load_strategy(default_conf)
assert log_has("Invalid parameter file format.", caplog)
@pytest.mark.parametrize('function,raises', [
('populate_entry_trend', True),
('advise_entry', False),
('populate_exit_trend', True),
('advise_exit', False),
])
def test_pandas_warning_direct(ohlcv_history, function, raises):
df = _STRATEGY.populate_indicators(ohlcv_history, {'pair': 'ETH/BTC'})
if raises:
with pytest.warns(FutureWarning):
# Test for Future warning
# FutureWarning: Setting an item of incompatible dtype is
# deprecated and will raise in a future error of pandas
# https://github.com/pandas-dev/pandas/issues/56503
getattr(_STRATEGY, function)(df, {'pair': 'ETH/BTC'})
else:
getattr(_STRATEGY, function)(df, {'pair': 'ETH/BTC'})
def test_pandas_warning_through_analyze_pair(ohlcv_history, mocker, recwarn):
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
_STRATEGY.analyze_pair('ETH/BTC')
assert len(recwarn) == 0
+2 -2
View File
@@ -15,7 +15,7 @@ from freqtrade.configuration.deprecated_settings import (check_conflicting_setti
process_deprecated_setting,
process_removed_setting,
process_temporary_deprecated_settings)
from freqtrade.configuration.environment_vars import flat_vars_to_nested_dict
from freqtrade.configuration.environment_vars import _flat_vars_to_nested_dict
from freqtrade.configuration.load_config import (load_config_file, load_file, load_from_files,
log_config_error_range)
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL, ENV_VAR_PREFIX
@@ -1419,7 +1419,7 @@ def test_flat_vars_to_nested_dict(caplog):
'chat_id': '2151'
}
}
res = flat_vars_to_nested_dict(test_args, ENV_VAR_PREFIX)
res = _flat_vars_to_nested_dict(test_args, ENV_VAR_PREFIX)
assert res == expected
assert log_has("Loading variable 'FREQTRADE__EXCHANGE__SOME_SETTING'", caplog)
+4 -3
View File
@@ -627,15 +627,16 @@ def test_process_exchange_failures(default_conf_usdt, ticker_usdt, mocker) -> No
mocker.patch.multiple(
EXMS,
fetch_ticker=ticker_usdt,
create_order=MagicMock(side_effect=TemporaryError)
reload_markets=MagicMock(side_effect=TemporaryError),
create_order=MagicMock(side_effect=TemporaryError),
)
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
sleep_mock = mocker.patch('time.sleep')
worker = Worker(args=None, config=default_conf_usdt)
patch_get_signal(worker.freqtrade)
worker._process_running()
assert sleep_mock.has_calls()
assert sleep_mock.called is True
def test_process_operational_exception(default_conf_usdt, ticker_usdt, mocker) -> None: