Merge branch 'develop' into bt-metrics2
This commit is contained in:
@@ -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
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -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
|
||||
|
||||
@@ -247,7 +247,7 @@ EXCHANGES = {
|
||||
'timeframe': '1h',
|
||||
'orderbook_max_entries': 50,
|
||||
},
|
||||
'huobi': {
|
||||
'htx': {
|
||||
'pair': 'ETH/BTC',
|
||||
'stake_currency': 'BTC',
|
||||
'hasQuoteVolume': True,
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.")
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user