1) extracting edge_conf to a fixture
2) test cased adjusted to Backtesting 3) Formatted backtesting_details a bit
This commit is contained in:
parent
aefc20738a
commit
12e735e831
@ -156,21 +156,6 @@ def default_conf():
|
||||
"NEO/BTC"
|
||||
]
|
||||
},
|
||||
"edge": {
|
||||
"enabled": False,
|
||||
"process_throttle_secs": 1800,
|
||||
"calculate_since_number_of_days": 14,
|
||||
"total_capital_in_stake_currency": 0.5,
|
||||
"allowed_risk": 0.01,
|
||||
"stoploss_range_min": -0.01,
|
||||
"stoploss_range_max": -0.1,
|
||||
"stoploss_range_step": -0.01,
|
||||
"maximum_winrate": 0.80,
|
||||
"minimum_expectancy": 0.20,
|
||||
"min_trade_number": 15,
|
||||
"max_trade_duration_minute": 1440,
|
||||
"remove_pumps": True
|
||||
},
|
||||
"telegram": {
|
||||
"enabled": True,
|
||||
"token": "token",
|
||||
@ -794,3 +779,23 @@ def buy_order_fee():
|
||||
'status': 'closed',
|
||||
'fee': None
|
||||
}
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def edge_conf(default_conf):
|
||||
default_conf['edge'] = {
|
||||
"enabled": False,
|
||||
"process_throttle_secs": 1800,
|
||||
"calculate_since_number_of_days": 14,
|
||||
"total_capital_in_stake_currency": 0.5,
|
||||
"allowed_risk": 0.01,
|
||||
"stoploss_range_min": -0.01,
|
||||
"stoploss_range_max": -0.1,
|
||||
"stoploss_range_step": -0.01,
|
||||
"maximum_winrate": 0.80,
|
||||
"minimum_expectancy": 0.20,
|
||||
"min_trade_number": 15,
|
||||
"max_trade_duration_minute": 1440,
|
||||
"remove_pumps": False
|
||||
}
|
||||
|
||||
return default_conf
|
||||
|
@ -1,17 +1,20 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
# pragma pylint: disable=missing-docstring, C0103, C0330
|
||||
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
|
||||
|
||||
import pytest
|
||||
import logging
|
||||
from freqtrade.tests.conftest import get_patched_freqtradebot
|
||||
from freqtrade.edge import Edge
|
||||
from pandas import DataFrame, to_datetime
|
||||
from freqtrade.strategy.interface import SellType
|
||||
from freqtrade.tests.optimize import (BTrade, BTContainer, _build_backtest_dataframe,
|
||||
_get_frame_time_from_offset)
|
||||
import arrow
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
# Cases to be tested:
|
||||
# 1) Open trade should be removed from the end
|
||||
# 2) Two complete trades within dataframe (with sell hit for all)
|
||||
@ -25,6 +28,101 @@ ticker_interval_in_minute = 60
|
||||
_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7}
|
||||
|
||||
|
||||
# Open trade should be removed from the end
|
||||
tc0 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle)
|
||||
stop_loss=-0.99, roi=float('inf'), profit_perc=0.00,
|
||||
trades=[]
|
||||
)
|
||||
|
||||
# Two complete trades within dataframe(with sell hit for all)
|
||||
tc1 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle)
|
||||
[2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open
|
||||
[3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action
|
||||
[4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade
|
||||
[5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action
|
||||
[6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell
|
||||
],
|
||||
stop_loss=-0.99, roi=float('inf'), profit_perc=0.00,
|
||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2),
|
||||
BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=4, close_tick=6)]
|
||||
)
|
||||
|
||||
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
|
||||
tc2 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit
|
||||
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
],
|
||||
stop_loss=-0.01, roi=float('inf'), profit_perc=-0.01,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
# 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss
|
||||
tc3 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit
|
||||
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
],
|
||||
stop_loss=-0.03, roi=float('inf'), profit_perc=-0.03,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
#5) Stoploss and sell are hit. should sell on stoploss
|
||||
tc4=BTContainer(data = [
|
||||
# D O H L C V B S
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal
|
||||
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
],
|
||||
stop_loss = -0.03, roi = float('inf'), profit_perc = -0.03,
|
||||
trades = [BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
TESTS = [
|
||||
tc0,
|
||||
tc1,
|
||||
tc2,
|
||||
tc3,
|
||||
tc4
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data", TESTS)
|
||||
def test_edge_results(edge_conf, mocker, caplog, data) -> None:
|
||||
"""
|
||||
run functional tests
|
||||
"""
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
frame = _build_backtest_dataframe(data.data)
|
||||
caplog.set_level(logging.DEBUG)
|
||||
edge.fee = 0
|
||||
|
||||
trades = edge._find_trades_for_stoploss_range(frame, 'TEST/BTC', [data.stop_loss])
|
||||
results = edge._fill_calculable_fields(DataFrame(trades)) if trades else DataFrame()
|
||||
|
||||
print(results)
|
||||
|
||||
assert len(trades) == len(data.trades)
|
||||
|
||||
if not results.empty:
|
||||
assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3)
|
||||
|
||||
for c, trade in enumerate(data.trades):
|
||||
res = results.iloc[c]
|
||||
assert res.exit_type == trade.sell_reason
|
||||
assert res.open_time == _get_frame_time_from_offset(trade.open_tick)
|
||||
assert res.close_time == _get_frame_time_from_offset(trade.close_tick)
|
||||
|
||||
|
||||
def test_adjust(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
@ -94,10 +192,10 @@ def _time_on_candle(number):
|
||||
minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms')
|
||||
|
||||
|
||||
def test_edge_heartbeat_calculate(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
heartbeat = default_conf['edge']['process_throttle_secs']
|
||||
def test_edge_heartbeat_calculate(mocker, edge_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
heartbeat = edge_conf['edge']['process_throttle_secs']
|
||||
|
||||
# should not recalculate if heartbeat not reached
|
||||
edge._last_updated = arrow.utcnow().timestamp - heartbeat + 1
|
||||
@ -148,15 +246,15 @@ def test_edge_process_downloaded_data(mocker, default_conf):
|
||||
assert edge._last_updated <= arrow.utcnow().timestamp + 2
|
||||
|
||||
|
||||
def test_process_expectancy(mocker, default_conf):
|
||||
default_conf['edge']['min_trade_number'] = 2
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
def test_process_expectancy(mocker, edge_conf):
|
||||
edge_conf['edge']['min_trade_number'] = 2
|
||||
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
|
||||
|
||||
def get_fee():
|
||||
return 0.001
|
||||
|
||||
freqtrade.exchange.get_fee = get_fee
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
trades = [
|
||||
{'pair': 'TEST/BTC',
|
||||
@ -210,157 +308,3 @@ def test_process_expectancy(mocker, default_conf):
|
||||
assert round(final['TEST/BTC'].risk_reward_ratio, 10) == 306.5384615384
|
||||
assert round(final['TEST/BTC'].required_risk_reward, 10) == 2.0
|
||||
assert round(final['TEST/BTC'].expectancy, 10) == 101.5128205128
|
||||
|
||||
# 1) Open trade should be removed from the end
|
||||
|
||||
|
||||
def test_case_1(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
stoploss = -0.99 # we don't want stoploss to be hit in this test
|
||||
ticker = [
|
||||
# D=Date, B=Buy, O=Open, H=High, L=Low, C=Close, S=Sell
|
||||
# D, B, O, H, L, C, S
|
||||
[3, 1, 12, 25, 11, 20, 0], # ->
|
||||
[4, 0, 20, 30, 19, 25, 1], # -> should enter the trade
|
||||
]
|
||||
|
||||
ticker_df = _build_dataframe(ticker)
|
||||
trades = edge._find_trades_for_stoploss_range(ticker_df, 'TEST/BTC', [stoploss])
|
||||
|
||||
# No trade should be found
|
||||
assert len(trades) == 0
|
||||
|
||||
|
||||
# 2) Two complete trades within dataframe (with sell hit for all)
|
||||
def test_case_2(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
stoploss = -0.99 # we don't want stoploss to be hit in this test
|
||||
ticker = [
|
||||
# D=Date, B=Buy, O=Open, H=High, L=Low, C=Close, S=Sell
|
||||
# D, B, O, H, L, C, S
|
||||
[0, 1, 15, 20, 12, 17, 0], # -> no action
|
||||
[1, 0, 17, 18, 13, 14, 1], # -> should enter the trade as B signal recieved on last candle
|
||||
[2, 0, 14, 15, 11, 12, 0], # -> exit the trade as the sell signal recieved on last candle
|
||||
[3, 1, 12, 25, 11, 20, 0], # -> no action
|
||||
[4, 0, 20, 30, 19, 25, 0], # -> should enter the trade
|
||||
[5, 0, 25, 27, 22, 26, 1], # -> no action
|
||||
[6, 0, 26, 36, 25, 35, 0], # -> should sell
|
||||
]
|
||||
|
||||
ticker_df = _build_dataframe(ticker)
|
||||
trades = edge._find_trades_for_stoploss_range(ticker_df, 'TEST/BTC', [stoploss])
|
||||
|
||||
# Two trades must have occured
|
||||
assert len(trades) == 2
|
||||
|
||||
# First trade check
|
||||
assert trades[0]['open_time'] == _time_on_candle(1)
|
||||
assert trades[0]['close_time'] == _time_on_candle(2)
|
||||
assert trades[0]['open_rate'] == ticker[1][_ohlc['open']]
|
||||
assert trades[0]['close_rate'] == ticker[2][_ohlc['open']]
|
||||
assert trades[0]['exit_type'] == SellType.SELL_SIGNAL
|
||||
##############################################################
|
||||
|
||||
# Second trade check
|
||||
assert trades[1]['open_time'] == _time_on_candle(4)
|
||||
assert trades[1]['close_time'] == _time_on_candle(6)
|
||||
assert trades[1]['open_rate'] == ticker[4][_ohlc['open']]
|
||||
assert trades[1]['close_rate'] == ticker[6][_ohlc['open']]
|
||||
assert trades[1]['exit_type'] == SellType.SELL_SIGNAL
|
||||
##############################################################
|
||||
|
||||
|
||||
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
|
||||
def test_case_3(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
stoploss = -0.01 # we don't want stoploss to be hit in this test
|
||||
ticker = [
|
||||
# D=Date, B=Buy, O=Open, H=High, L=Low, C=Close, S=Sell
|
||||
# D, B, O, H, L, C, S
|
||||
[0, 1, 15, 20, 12, 17, 0], # -> no action
|
||||
[1, 0, 14, 15, 11, 12, 0], # -> enter to trade, stoploss hit
|
||||
[2, 1, 12, 25, 11, 20, 0], # -> no action
|
||||
]
|
||||
|
||||
ticker_df = _build_dataframe(ticker)
|
||||
trades = edge._find_trades_for_stoploss_range(ticker_df, 'TEST/BTC', [stoploss])
|
||||
|
||||
# Two trades must have occured
|
||||
assert len(trades) == 1
|
||||
|
||||
# First trade check
|
||||
assert trades[0]['open_time'] == _time_on_candle(1)
|
||||
assert trades[0]['close_time'] == _time_on_candle(1)
|
||||
assert trades[0]['open_rate'] == ticker[1][_ohlc['open']]
|
||||
assert trades[0]['close_rate'] == (stoploss + 1) * trades[0]['open_rate']
|
||||
assert trades[0]['exit_type'] == SellType.STOP_LOSS
|
||||
##############################################################
|
||||
|
||||
|
||||
# 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss
|
||||
def test_case_4(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
stoploss = -0.03 # we don't want stoploss to be hit in this test
|
||||
ticker = [
|
||||
# D=Date, B=Buy, O=Open, H=High, L=Low, C=Close, S=Sell
|
||||
# D, B, O, H, L, C, S
|
||||
[0, 1, 15, 20, 12, 17, 0], # -> no action
|
||||
[1, 0, 17, 22, 16.90, 17, 0], # -> enter to trade
|
||||
[2, 0, 16, 17, 14.4, 15.5, 0], # -> stoploss hit
|
||||
[3, 0, 17, 25, 16.9, 22, 0], # -> no action
|
||||
]
|
||||
|
||||
ticker_df = _build_dataframe(ticker)
|
||||
trades = edge._find_trades_for_stoploss_range(ticker_df, 'TEST/BTC', [stoploss])
|
||||
|
||||
# Two trades must have occured
|
||||
assert len(trades) == 1
|
||||
|
||||
# First trade check
|
||||
assert trades[0]['open_time'] == _time_on_candle(1)
|
||||
assert trades[0]['close_time'] == _time_on_candle(2)
|
||||
assert trades[0]['open_rate'] == ticker[1][_ohlc['open']]
|
||||
assert trades[0]['close_rate'] == (stoploss + 1) * trades[0]['open_rate']
|
||||
assert trades[0]['exit_type'] == SellType.STOP_LOSS
|
||||
##############################################################
|
||||
|
||||
|
||||
# 5) Stoploss and sell are hit. should sell on stoploss
|
||||
def test_case_5(mocker, default_conf):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
edge = Edge(default_conf, freqtrade.exchange, freqtrade.strategy)
|
||||
|
||||
stoploss = -0.03 # we don't want stoploss to be hit in this test
|
||||
ticker = [
|
||||
# D=Date, B=Buy, O=Open, H=High, L=Low, C=Close, S=Sell
|
||||
# D, B, O, H, L, C, S
|
||||
[0, 1, 15, 20, 12, 17, 0], # -> no action
|
||||
[1, 0, 17, 22, 16.90, 17, 0], # -> enter to trade
|
||||
[2, 0, 16, 17, 14.4, 15.5, 1], # -> stoploss hit and also sell signal
|
||||
[3, 0, 17, 25, 16.9, 22, 0], # -> no action
|
||||
]
|
||||
|
||||
ticker_df = _build_dataframe(ticker)
|
||||
trades = edge._find_trades_for_stoploss_range(ticker_df, 'TEST/BTC', [stoploss])
|
||||
|
||||
# Two trades must have occured
|
||||
assert len(trades) == 1
|
||||
|
||||
# First trade check
|
||||
assert trades[0]['open_time'] == _time_on_candle(1)
|
||||
assert trades[0]['close_time'] == _time_on_candle(2)
|
||||
assert trades[0]['open_rate'] == ticker[1][_ohlc['open']]
|
||||
assert trades[0]['close_rate'] == (stoploss + 1) * trades[0]['open_rate']
|
||||
assert trades[0]['exit_type'] == SellType.STOP_LOSS
|
||||
##############################################################
|
||||
|
@ -1,4 +1,4 @@
|
||||
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument
|
||||
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, C0330, unused-argument
|
||||
import logging
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@ -34,15 +34,15 @@ tc0 = BTContainer(data=[
|
||||
# TC2: Stop-Loss Triggered 3% Loss
|
||||
tc1 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
|
||||
[3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit
|
||||
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
|
||||
[3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit
|
||||
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.03, roi=1, profit_perc=-0.03,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Test 3 Candle drops 4%, Recovers 1%.
|
||||
@ -127,7 +127,7 @@ tc6 = BTContainer(data=[
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.02, roi=0.03, profit_perc=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
||||
)
|
||||
)
|
||||
|
||||
TESTS = [
|
||||
tc0,
|
||||
|
Loading…
Reference in New Issue
Block a user