Merge pull request #4454 from freqtrade/backtest_compound_speed
Backtest compound, wallet, ...
This commit is contained in:
@@ -19,7 +19,7 @@ from freqtrade.data.converter import ohlcv_to_dataframe
|
||||
from freqtrade.edge import Edge, PairInfo
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.persistence import Trade, init_db
|
||||
from freqtrade.persistence import LocalTrade, Trade, init_db
|
||||
from freqtrade.resolvers import ExchangeResolver
|
||||
from freqtrade.worker import Worker
|
||||
from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4,
|
||||
@@ -183,28 +183,34 @@ def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None:
|
||||
freqtrade.exchange.refresh_latest_ohlcv = lambda p: None
|
||||
|
||||
|
||||
def create_mock_trades(fee):
|
||||
def create_mock_trades(fee, use_db: bool = True):
|
||||
"""
|
||||
Create some fake trades ...
|
||||
"""
|
||||
def add_trade(trade):
|
||||
if use_db:
|
||||
Trade.session.add(trade)
|
||||
else:
|
||||
LocalTrade.trades.append(trade)
|
||||
|
||||
# Simulate dry_run entries
|
||||
trade = mock_trade_1(fee)
|
||||
Trade.session.add(trade)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_2(fee)
|
||||
Trade.session.add(trade)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_3(fee)
|
||||
Trade.session.add(trade)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_4(fee)
|
||||
Trade.session.add(trade)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_5(fee)
|
||||
Trade.session.add(trade)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_6(fee)
|
||||
Trade.session.add(trade)
|
||||
add_trade(trade)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -255,6 +261,7 @@ def get_default_conf(testdatadir):
|
||||
"20": 0.02,
|
||||
"0": 0.04
|
||||
},
|
||||
"dry_run_wallet": 1000,
|
||||
"stoploss": -0.10,
|
||||
"unfilledtimeout": {
|
||||
"buy": 10,
|
||||
|
@@ -28,6 +28,7 @@ def mock_trade_1(fee):
|
||||
amount_requested=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
is_open=True,
|
||||
open_rate=0.123,
|
||||
exchange='bittrex',
|
||||
open_order_id='dry_run_buy_12345',
|
||||
@@ -81,6 +82,7 @@ def mock_trade_2(fee):
|
||||
open_rate=0.123,
|
||||
close_rate=0.128,
|
||||
close_profit=0.005,
|
||||
close_profit_abs=0.000584127,
|
||||
exchange='bittrex',
|
||||
is_open=False,
|
||||
open_order_id='dry_run_sell_12345',
|
||||
@@ -140,6 +142,7 @@ def mock_trade_3(fee):
|
||||
open_rate=0.05,
|
||||
close_rate=0.06,
|
||||
close_profit=0.01,
|
||||
close_profit_abs=0.000155,
|
||||
exchange='bittrex',
|
||||
is_open=False,
|
||||
strategy='DefaultStrategy',
|
||||
@@ -180,6 +183,7 @@ def mock_trade_4(fee):
|
||||
amount_requested=124.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
is_open=True,
|
||||
open_rate=0.123,
|
||||
exchange='bittrex',
|
||||
open_order_id='prod_buy_12345',
|
||||
@@ -230,6 +234,7 @@ def mock_trade_5(fee):
|
||||
amount_requested=124.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
is_open=True,
|
||||
open_rate=0.123,
|
||||
exchange='bittrex',
|
||||
strategy='SampleStrategy',
|
||||
@@ -281,6 +286,7 @@ def mock_trade_6(fee):
|
||||
amount_requested=2.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
is_open=True,
|
||||
open_rate=0.15,
|
||||
exchange='bittrex',
|
||||
strategy='SampleStrategy',
|
||||
|
@@ -274,15 +274,17 @@ def test_create_cum_profit1(testdatadir):
|
||||
def test_calculate_max_drawdown(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
drawdown, h, low = calculate_max_drawdown(bt_data)
|
||||
drawdown, hdate, lowdate, hval, lval = calculate_max_drawdown(bt_data)
|
||||
assert isinstance(drawdown, float)
|
||||
assert pytest.approx(drawdown) == 0.21142322
|
||||
assert isinstance(h, Timestamp)
|
||||
assert isinstance(low, Timestamp)
|
||||
assert h == Timestamp('2018-01-24 14:25:00', tz='UTC')
|
||||
assert low == Timestamp('2018-01-30 04:45:00', tz='UTC')
|
||||
assert isinstance(hdate, Timestamp)
|
||||
assert isinstance(lowdate, Timestamp)
|
||||
assert isinstance(hval, float)
|
||||
assert isinstance(lval, float)
|
||||
assert hdate == Timestamp('2018-01-24 14:25:00', tz='UTC')
|
||||
assert lowdate == Timestamp('2018-01-30 04:45:00', tz='UTC')
|
||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||
drawdown, h, low = calculate_max_drawdown(DataFrame())
|
||||
drawdown, hdate, lowdate, hval, lval = calculate_max_drawdown(DataFrame())
|
||||
|
||||
|
||||
def test_calculate_csum(testdatadir):
|
||||
@@ -294,6 +296,10 @@ def test_calculate_csum(testdatadir):
|
||||
assert isinstance(csum_max, float)
|
||||
assert csum_min < 0.01
|
||||
assert csum_max > 0.02
|
||||
csum_min1, csum_max1 = calculate_csum(bt_data, 5)
|
||||
|
||||
assert csum_min1 == csum_min + 5
|
||||
assert csum_max1 == csum_max + 5
|
||||
|
||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||
csum_min, csum_max = calculate_csum(DataFrame())
|
||||
@@ -310,13 +316,16 @@ def test_calculate_max_drawdown2():
|
||||
# sort by profit and reset index
|
||||
df = df.sort_values('profit').reset_index(drop=True)
|
||||
df1 = df.copy()
|
||||
drawdown, h, low = calculate_max_drawdown(df, date_col='open_date', value_col='profit')
|
||||
drawdown, hdate, ldate, hval, lval = calculate_max_drawdown(
|
||||
df, date_col='open_date', value_col='profit')
|
||||
# Ensure df has not been altered.
|
||||
assert df.equals(df1)
|
||||
|
||||
assert isinstance(drawdown, float)
|
||||
# High must be before low
|
||||
assert h < low
|
||||
assert hdate < ldate
|
||||
# High value must be higher than low value
|
||||
assert hval > lval
|
||||
assert drawdown == 0.091755
|
||||
|
||||
df = DataFrame(zip(values[:5], dates[:5]), columns=['profit', 'open_date'])
|
||||
|
@@ -1,6 +1,5 @@
|
||||
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, C0330, unused-argument
|
||||
import logging
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -489,7 +488,8 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
||||
default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset
|
||||
default_conf["ask_strategy"] = {"use_sell_signal": data.use_sell_signal}
|
||||
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_fee", MagicMock(return_value=0.0))
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_fee", return_value=0.0)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
patch_exchange(mocker)
|
||||
frame = _build_backtest_dataframe(data.data)
|
||||
backtesting = Backtesting(default_conf)
|
||||
@@ -503,7 +503,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
||||
min_date, max_date = get_timerange({pair: frame})
|
||||
results = backtesting.backtest(
|
||||
processed=data_processed,
|
||||
stake_amount=default_conf['stake_amount'],
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=10,
|
||||
@@ -514,6 +513,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
||||
|
||||
for c, trade in enumerate(data.trades):
|
||||
res = results.iloc[c]
|
||||
assert res.sell_reason == trade.sell_reason
|
||||
assert res.sell_reason == trade.sell_reason.value
|
||||
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
|
||||
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)
|
||||
|
@@ -9,7 +9,6 @@ import pandas as pd
|
||||
import pytest
|
||||
from arrow import Arrow
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_backtesting
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data import history
|
||||
@@ -19,6 +18,7 @@ from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.data.history import get_timerange
|
||||
from freqtrade.exceptions import DependencyException, OperationalException
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from freqtrade.persistence import LocalTrade
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
from freqtrade.state import RunMode
|
||||
from freqtrade.strategy.interface import SellType
|
||||
@@ -90,7 +90,6 @@ def simple_backtest(config, contour, mocker, testdatadir) -> None:
|
||||
assert isinstance(processed, dict)
|
||||
results = backtesting.backtest(
|
||||
processed=processed,
|
||||
stake_amount=config['stake_amount'],
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=1,
|
||||
@@ -111,7 +110,6 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
|
||||
min_date, max_date = get_timerange(processed)
|
||||
return {
|
||||
'processed': processed,
|
||||
'stake_amount': conf['stake_amount'],
|
||||
'start_date': min_date,
|
||||
'end_date': max_date,
|
||||
'max_open_trades': 10,
|
||||
@@ -233,8 +231,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
|
||||
assert log_has('Parameter --fee detected, setting fee to: {} ...'.format(config['fee']), caplog)
|
||||
|
||||
|
||||
def test_setup_optimize_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
|
||||
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||
def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog) -> None:
|
||||
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
@@ -242,9 +239,21 @@ def test_setup_optimize_configuration_unlimited_stake_amount(mocker, default_con
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'DefaultStrategy',
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '2'
|
||||
]
|
||||
|
||||
with pytest.raises(DependencyException, match=r'.`stake_amount`.*'):
|
||||
conf = setup_optimize_configuration(get_args(args), RunMode.BACKTEST)
|
||||
assert isinstance(conf, dict)
|
||||
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'DefaultStrategy',
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '0.5'
|
||||
]
|
||||
with pytest.raises(OperationalException, match=r"Starting balance .* smaller .*"):
|
||||
setup_optimize_configuration(get_args(args), RunMode.BACKTEST)
|
||||
|
||||
|
||||
@@ -448,9 +457,48 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
|
||||
Backtesting(default_conf)
|
||||
|
||||
|
||||
def test_backtest__enter_trade(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['ask_strategy']['use_sell_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
patch_exchange(mocker)
|
||||
default_conf['stake_amount'] = 'unlimited'
|
||||
backtesting = Backtesting(default_conf)
|
||||
pair = 'UNITTEST/BTC'
|
||||
row = [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
|
||||
1, # Sell
|
||||
0.001, # Open
|
||||
0.0011, # Close
|
||||
0, # Sell
|
||||
0.00099, # Low
|
||||
0.0012, # High
|
||||
]
|
||||
trade = backtesting._enter_trade(pair, row=row, max_open_trades=2, open_trade_count=0)
|
||||
assert isinstance(trade, LocalTrade)
|
||||
assert trade.stake_amount == 495
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, max_open_trades=2, open_trade_count=2)
|
||||
assert trade is None
|
||||
|
||||
# Stake-amount too high!
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0)
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, max_open_trades=2, open_trade_count=0)
|
||||
assert trade is None
|
||||
|
||||
# Stake-amount too high!
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_trade_stake_amount",
|
||||
side_effect=DependencyException)
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, max_open_trades=2, open_trade_count=0)
|
||||
assert trade is None
|
||||
|
||||
|
||||
def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['ask_strategy']['use_sell_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
pair = 'UNITTEST/BTC'
|
||||
@@ -461,7 +509,6 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
min_date, max_date = get_timerange(processed)
|
||||
results = backtesting.backtest(
|
||||
processed=processed,
|
||||
stake_amount=default_conf['stake_amount'],
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=10,
|
||||
@@ -486,7 +533,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
'trade_duration': [235, 40],
|
||||
'profit_ratio': [0.0, 0.0],
|
||||
'profit_abs': [0.0, 0.0],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI],
|
||||
'sell_reason': [SellType.ROI.value, SellType.ROI.value],
|
||||
'initial_stop_loss_abs': [0.0940005, 0.09272236],
|
||||
'initial_stop_loss_ratio': [-0.1, -0.1],
|
||||
'stop_loss_abs': [0.0940005, 0.09272236],
|
||||
@@ -512,6 +559,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['ask_strategy']['use_sell_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
@@ -523,7 +571,6 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
|
||||
min_date, max_date = get_timerange(processed)
|
||||
results = backtesting.backtest(
|
||||
processed=processed,
|
||||
stake_amount=default_conf['stake_amount'],
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=1,
|
||||
@@ -558,6 +605,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
||||
|
||||
default_conf['enable_protections'] = True
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
tests = [
|
||||
['sine', 9],
|
||||
['raise', 10],
|
||||
@@ -589,6 +637,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
|
||||
default_conf['protections'] = protections
|
||||
default_conf['enable_protections'] = True
|
||||
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
# While buy-signals are unrealistic, running backtesting
|
||||
# over and over again should not cause different results
|
||||
@@ -626,6 +675,7 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
|
||||
|
||||
|
||||
def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
|
||||
pair='UNITTEST/BTC', datadir=testdatadir)
|
||||
@@ -658,6 +708,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
||||
dataframe['sell'] = np.where((dataframe.index + multi - 2) % multi == 0, 1, 0)
|
||||
return dataframe
|
||||
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -678,7 +729,6 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
backtest_conf = {
|
||||
'processed': processed,
|
||||
'stake_amount': default_conf['stake_amount'],
|
||||
'start_date': min_date,
|
||||
'end_date': max_date,
|
||||
'max_open_trades': 3,
|
||||
@@ -694,7 +744,6 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
||||
|
||||
backtest_conf = {
|
||||
'processed': processed,
|
||||
'stake_amount': default_conf['stake_amount'],
|
||||
'start_date': min_date,
|
||||
'end_date': max_date,
|
||||
'max_open_trades': 1,
|
||||
@@ -822,6 +871,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'2018-01-30 05:35:00', ], utc=True),
|
||||
'trade_duration': [235, 40],
|
||||
'is_open': [False, False],
|
||||
'stake_amount': [0.01, 0.01],
|
||||
'open_rate': [0.104445, 0.10302485],
|
||||
'close_rate': [0.104969, 0.103541],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI]
|
||||
@@ -838,6 +888,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'2018-01-30 08:30:00'], utc=True),
|
||||
'trade_duration': [47, 40, 20],
|
||||
'is_open': [False, False, False],
|
||||
'stake_amount': [0.01, 0.01, 0.01],
|
||||
'open_rate': [0.104445, 0.10302485, 0.122541],
|
||||
'close_rate': [0.104969, 0.103541, 0.123541],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
|
@@ -12,10 +12,9 @@ import pytest
|
||||
from arrow import Arrow
|
||||
from filelock import Timeout
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_hyperopt
|
||||
from freqtrade.data.history import load_data
|
||||
from freqtrade.exceptions import DependencyException, OperationalException
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.hyperopt import Hyperopt
|
||||
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
|
||||
from freqtrade.state import RunMode
|
||||
@@ -130,8 +129,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
|
||||
assert log_has('Parameter --print-all detected ...', caplog)
|
||||
|
||||
|
||||
def test_setup_hyperopt_configuration_unlimited_stake_amount(mocker, default_conf) -> None:
|
||||
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||
def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None:
|
||||
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
@@ -139,9 +137,20 @@ def test_setup_hyperopt_configuration_unlimited_stake_amount(mocker, default_con
|
||||
'hyperopt',
|
||||
'--config', 'config.json',
|
||||
'--hyperopt', 'DefaultHyperOpt',
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '2'
|
||||
]
|
||||
conf = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
|
||||
assert isinstance(conf, dict)
|
||||
|
||||
with pytest.raises(DependencyException, match=r'.`stake_amount`.*'):
|
||||
args = [
|
||||
'hyperopt',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'DefaultStrategy',
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '0.5'
|
||||
]
|
||||
with pytest.raises(OperationalException, match=r"Starting balance .* smaller .*"):
|
||||
setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
|
||||
|
||||
|
||||
|
@@ -48,7 +48,7 @@ def test_text_table_bt_results():
|
||||
)
|
||||
|
||||
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
|
||||
max_open_trades=2, results=results)
|
||||
starting_balance=4, results=results)
|
||||
assert text_table_bt_results(pair_results, stake_currency='BTC') == result_str
|
||||
|
||||
|
||||
@@ -73,11 +73,13 @@ def test_generate_backtest_stats(default_conf, testdatadir):
|
||||
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"is_open": [False, False, False, True],
|
||||
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||
SellType.ROI, SellType.FORCE_SELL]
|
||||
}),
|
||||
'config': default_conf,
|
||||
'locks': [],
|
||||
'final_balance': 1000.02,
|
||||
'backtest_start_time': Arrow.utcnow().int_timestamp,
|
||||
'backtest_end_time': Arrow.utcnow().int_timestamp,
|
||||
}
|
||||
@@ -100,6 +102,7 @@ def test_generate_backtest_stats(default_conf, testdatadir):
|
||||
# Above sample had no loosing trade
|
||||
assert strat_stats['max_drawdown'] == 0.0
|
||||
|
||||
# Retry with losing trade
|
||||
results = {'DefStrat': {
|
||||
'results': pd.DataFrame(
|
||||
{"pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"],
|
||||
@@ -116,18 +119,31 @@ def test_generate_backtest_stats(default_conf, testdatadir):
|
||||
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
|
||||
"close_rate": [0.002546, 0.003014, 0.0032903, 0.003217],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"open_at_end": [False, False, False, True],
|
||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||
SellType.ROI, SellType.FORCE_SELL]
|
||||
"is_open": [False, False, False, True],
|
||||
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||
"sell_reason": [SellType.ROI, SellType.ROI,
|
||||
SellType.STOP_LOSS, SellType.FORCE_SELL]
|
||||
}),
|
||||
'config': default_conf}
|
||||
'config': default_conf,
|
||||
'locks': [],
|
||||
'final_balance': 1000.02,
|
||||
'backtest_start_time': Arrow.utcnow().int_timestamp,
|
||||
'backtest_end_time': Arrow.utcnow().int_timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
assert strat_stats['max_drawdown'] == 0.0
|
||||
assert strat_stats['drawdown_start'] == datetime(1970, 1, 1, tzinfo=timezone.utc)
|
||||
assert strat_stats['drawdown_end'] == datetime(1970, 1, 1, tzinfo=timezone.utc)
|
||||
assert strat_stats['drawdown_end_ts'] == 0
|
||||
assert strat_stats['drawdown_start_ts'] == 0
|
||||
stats = generate_backtest_stats(btdata, results, min_date, max_date)
|
||||
assert isinstance(stats, dict)
|
||||
assert 'strategy' in stats
|
||||
assert 'DefStrat' in stats['strategy']
|
||||
assert 'strategy_comparison' in stats
|
||||
strat_stats = stats['strategy']['DefStrat']
|
||||
|
||||
assert strat_stats['max_drawdown'] == 0.013803
|
||||
assert strat_stats['drawdown_start'] == datetime(2017, 11, 14, 22, 10, tzinfo=timezone.utc)
|
||||
assert strat_stats['drawdown_end'] == datetime(2017, 11, 14, 22, 43, tzinfo=timezone.utc)
|
||||
assert strat_stats['drawdown_end_ts'] == 1510699380000
|
||||
assert strat_stats['drawdown_start_ts'] == 1510697400000
|
||||
assert strat_stats['pairlist'] == ['UNITTEST/BTC']
|
||||
|
||||
# Test storing stats
|
||||
@@ -189,7 +205,7 @@ def test_generate_pair_metrics():
|
||||
)
|
||||
|
||||
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
|
||||
max_open_trades=2, results=results)
|
||||
starting_balance=2, results=results)
|
||||
assert isinstance(pair_results, list)
|
||||
assert len(pair_results) == 2
|
||||
assert pair_results[-1]['key'] == 'TOTAL'
|
||||
@@ -265,7 +281,7 @@ def test_generate_sell_reason_stats():
|
||||
'wins': [2, 0, 0],
|
||||
'draws': [0, 0, 0],
|
||||
'losses': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
'sell_reason': [SellType.ROI.value, SellType.ROI.value, SellType.STOP_LOSS.value]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -291,6 +307,7 @@ def test_generate_sell_reason_stats():
|
||||
|
||||
def test_text_table_strategy(default_conf):
|
||||
default_conf['max_open_trades'] = 2
|
||||
default_conf['dry_run_wallet'] = 3
|
||||
results = {}
|
||||
results['TestStrategy1'] = {'results': pd.DataFrame(
|
||||
{
|
||||
@@ -323,9 +340,9 @@ def test_text_table_strategy(default_conf):
|
||||
'|---------------+--------+----------------+----------------+------------------+'
|
||||
'----------------+----------------+--------+---------+----------|\n'
|
||||
'| TestStrategy1 | 3 | 20.00 | 60.00 | 1.10000000 |'
|
||||
' 30.00 | 0:17:00 | 3 | 0 | 0 |\n'
|
||||
' 36.67 | 0:17:00 | 3 | 0 | 0 |\n'
|
||||
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
|
||||
' 45.00 | 0:20:00 | 3 | 0 | 0 |'
|
||||
' 43.33 | 0:20:00 | 3 | 0 | 0 |'
|
||||
)
|
||||
|
||||
strategy_results = generate_strategy_metrics(all_results=results)
|
||||
|
@@ -73,9 +73,13 @@ def test_PairLocks(use_db):
|
||||
assert PairLocks.is_pair_locked('XRP/USDT', lock_time + timedelta(minutes=-50))
|
||||
|
||||
if use_db:
|
||||
assert len(PairLock.query.all()) > 0
|
||||
locks = PairLocks.get_all_locks()
|
||||
locks_db = PairLock.query.all()
|
||||
assert len(locks) == len(locks_db)
|
||||
assert len(locks_db) > 0
|
||||
else:
|
||||
# Nothing was pushed to the database
|
||||
assert len(PairLocks.get_all_locks()) > 0
|
||||
assert len(PairLock.query.all()) == 0
|
||||
# Reset use-db variable
|
||||
PairLocks.reset_locks()
|
||||
|
@@ -430,7 +430,8 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
|
||||
'--enable-position-stacking',
|
||||
'--disable-max-market-positions',
|
||||
'--timerange', ':100',
|
||||
'--export', '/bar/foo'
|
||||
'--export', '/bar/foo',
|
||||
'--stake-amount', 'unlimited'
|
||||
]
|
||||
|
||||
args = Arguments(arglist).get_parsed_arg()
|
||||
@@ -463,6 +464,8 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
|
||||
|
||||
assert 'export' in config
|
||||
assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog)
|
||||
assert 'stake_amount' in config
|
||||
assert config['stake_amount'] == 'unlimited'
|
||||
|
||||
|
||||
def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> None:
|
||||
|
@@ -2243,6 +2243,7 @@ def test_check_handle_timedout_sell_usercustom(default_conf, ticker, limit_sell_
|
||||
|
||||
open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
|
||||
open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
||||
open_trade.close_profit_abs = 0.001
|
||||
open_trade.is_open = False
|
||||
|
||||
Trade.session.add(open_trade)
|
||||
@@ -2290,6 +2291,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
|
||||
|
||||
open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime
|
||||
open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime
|
||||
open_trade.close_profit_abs = 0.001
|
||||
open_trade.is_open = False
|
||||
|
||||
Trade.session.add(open_trade)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
import logging
|
||||
from types import FunctionType
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import arrow
|
||||
@@ -8,7 +9,7 @@ from sqlalchemy import create_engine
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.exceptions import DependencyException, OperationalException
|
||||
from freqtrade.persistence import Order, Trade, clean_dry_run_db, init_db
|
||||
from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db
|
||||
from tests.conftest import create_mock_trades, log_has, log_has_re
|
||||
|
||||
|
||||
@@ -1039,14 +1040,18 @@ def test_fee_updated(fee):
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_total_open_trades_stakes(fee):
|
||||
@pytest.mark.parametrize('use_db', [True, False])
|
||||
def test_total_open_trades_stakes(fee, use_db):
|
||||
|
||||
Trade.use_db = use_db
|
||||
res = Trade.total_open_trades_stakes()
|
||||
assert res == 0
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, use_db)
|
||||
res = Trade.total_open_trades_stakes()
|
||||
assert res == 0.004
|
||||
|
||||
Trade.use_db = True
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_get_overall_performance(fee):
|
||||
@@ -1172,3 +1177,25 @@ def test_select_order(fee):
|
||||
assert order.ft_order_side == 'stoploss'
|
||||
order = trades[4].select_order('sell', False)
|
||||
assert order is None
|
||||
|
||||
|
||||
def test_Trade_object_idem():
|
||||
|
||||
assert issubclass(Trade, LocalTrade)
|
||||
|
||||
trade = vars(Trade)
|
||||
localtrade = vars(LocalTrade)
|
||||
|
||||
# Parent (LocalTrade) should have the same attributes
|
||||
for item in trade:
|
||||
# Exclude private attributes and open_date (as it's not assigned a default)
|
||||
if (not item.startswith('_')
|
||||
and item not in ('delete', 'session', 'query', 'open_date')):
|
||||
assert item in localtrade
|
||||
|
||||
# Fails if only a column is added without corresponding parent field
|
||||
for item in localtrade:
|
||||
if (not item.startswith('__')
|
||||
and item not in ('trades', )
|
||||
and type(getattr(LocalTrade, item)) not in (property, FunctionType)):
|
||||
assert item in trade
|
||||
|
Reference in New Issue
Block a user