Merge branch 'develop' into pr/italodamato/6563
This commit is contained in:
@@ -3,7 +3,7 @@ from typing import Dict, List, NamedTuple, Optional
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.enums import SellType
|
||||
from freqtrade.enums import ExitType
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
|
||||
|
||||
@@ -15,10 +15,11 @@ class BTrade(NamedTuple):
|
||||
"""
|
||||
Minimalistic Trade result used for functional backtesting
|
||||
"""
|
||||
sell_reason: SellType
|
||||
exit_reason: ExitType
|
||||
open_tick: int
|
||||
close_tick: int
|
||||
buy_tag: Optional[str] = None
|
||||
enter_tag: Optional[str] = None
|
||||
is_short: bool = False
|
||||
|
||||
|
||||
class BTContainer(NamedTuple):
|
||||
@@ -34,10 +35,11 @@ class BTContainer(NamedTuple):
|
||||
trailing_only_offset_is_reached: bool = False
|
||||
trailing_stop_positive: Optional[float] = None
|
||||
trailing_stop_positive_offset: float = 0.0
|
||||
use_sell_signal: bool = False
|
||||
use_exit_signal: bool = False
|
||||
use_custom_stoploss: bool = False
|
||||
custom_entry_price: Optional[float] = None
|
||||
custom_exit_price: Optional[float] = None
|
||||
leverage: float = 1.0
|
||||
|
||||
|
||||
def _get_frame_time_from_offset(offset):
|
||||
@@ -46,18 +48,18 @@ def _get_frame_time_from_offset(offset):
|
||||
|
||||
|
||||
def _build_backtest_dataframe(data):
|
||||
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell']
|
||||
columns = columns + ['buy_tag'] if len(data[0]) == 9 else columns
|
||||
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'enter_long', 'exit_long',
|
||||
'enter_short', 'exit_short']
|
||||
if len(data[0]) == 8:
|
||||
# No short columns
|
||||
data = [d + [0, 0] for d in data]
|
||||
columns = columns + ['enter_tag'] if len(data[0]) == 11 else columns
|
||||
|
||||
frame = DataFrame.from_records(data, columns=columns)
|
||||
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
|
||||
# Ensure floats are in place
|
||||
for column in ['open', 'high', 'low', 'close', 'volume']:
|
||||
frame[column] = frame[column].astype('float64')
|
||||
if 'buy_tag' not in columns:
|
||||
frame['buy_tag'] = None
|
||||
if 'exit_tag' not in columns:
|
||||
frame['exit_tag'] = None
|
||||
|
||||
# Ensure all candles make kindof sense
|
||||
assert all(frame['low'] <= frame['close'])
|
||||
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from freqtrade.enums import RunMode, SellType
|
||||
from freqtrade.enums import ExitType, RunMode
|
||||
from freqtrade.optimize.hyperopt import Hyperopt
|
||||
from tests.conftest import patch_exchange
|
||||
|
||||
@@ -44,7 +44,7 @@ def hyperopt_results():
|
||||
'profit_abs': [-0.2, 0.4, -0.2, 0.6],
|
||||
'trade_duration': [10, 30, 10, 10],
|
||||
'amount': [0.1, 0.1, 0.1, 0.1],
|
||||
'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.STOP_LOSS, SellType.ROI],
|
||||
'exit_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI],
|
||||
'open_date':
|
||||
[
|
||||
datetime(2019, 1, 1, 9, 15, 0),
|
||||
|
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock
|
||||
import pytest
|
||||
|
||||
from freqtrade.data.history import get_timerange
|
||||
from freqtrade.enums import SellType
|
||||
from freqtrade.enums import ExitType
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from tests.conftest import patch_exchange
|
||||
from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
|
||||
@@ -15,21 +15,21 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
|
||||
# Test 0: Sell with signal sell in candle 3
|
||||
# Test with Stop-loss at 1%
|
||||
tc0 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4986, 4986, 6172, 0, 0], # exit with stoploss hit
|
||||
[3, 5010, 5010, 4980, 5010, 6172, 0, 1],
|
||||
[4, 5010, 5011, 4977, 4995, 6172, 0, 0],
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True,
|
||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_exit_signal=True,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)]
|
||||
)
|
||||
|
||||
# Test 1: Stop-Loss Triggered 1% loss
|
||||
# Test with Stop-loss at 1%
|
||||
tc1 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4600, 4600, 6172, 0, 0], # exit with stoploss hit
|
||||
@@ -37,14 +37,14 @@ tc1 = BTContainer(data=[
|
||||
[4, 4977, 4995, 4977, 4995, 6172, 0, 0],
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
|
||||
# Test 2: Minus 4% Low, minus 1% close
|
||||
# Test with Stop-Loss at 3%
|
||||
tc2 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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],
|
||||
@@ -52,7 +52,7 @@ tc2 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ tc2 = BTContainer(data=[
|
||||
# Trade-A: Stop-Loss Triggered 2% Loss
|
||||
# Trade-B: Stop-Loss Triggered 2% Loss
|
||||
tc3 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4800, 4975, 6172, 0, 0], # exit with stoploss hit
|
||||
@@ -72,8 +72,8 @@ tc3 = BTContainer(data=[
|
||||
[5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit
|
||||
[6, 4950, 4975, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2),
|
||||
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2),
|
||||
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=4, close_tick=5)]
|
||||
)
|
||||
|
||||
# Test 4: Minus 3% / recovery +15%
|
||||
@@ -81,7 +81,7 @@ tc3 = BTContainer(data=[
|
||||
# Test with Stop-loss at 2% ROI 6%
|
||||
# Stop-Loss Triggered 2% Loss
|
||||
tc4 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit
|
||||
@@ -89,13 +89,13 @@ tc4 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain
|
||||
# stop-loss: 1%, ROI: 3%
|
||||
tc5 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4980, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4987, 5025, 4975, 4987, 6172, 0, 0],
|
||||
@@ -103,13 +103,13 @@ tc5 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4962, 4972, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss
|
||||
# stop-loss: 2% ROI: 5%
|
||||
tc6 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss
|
||||
@@ -117,13 +117,13 @@ tc6 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain
|
||||
# stop-loss: 2% ROI: 3%
|
||||
tc7 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
||||
@@ -131,42 +131,42 @@ tc7 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
|
||||
# Test 8: trailing_stop should raise so candle 3 causes a stoploss.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2
|
||||
tc8 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
||||
[2, 5000, 5250, 4750, 4850, 6172, 0, 0],
|
||||
[3, 4850, 5050, 4650, 4750, 6172, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
|
||||
# Test 9: trailing_stop should raise - high and low in same candle.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3
|
||||
tc9 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
||||
[2, 5000, 5050, 4950, 5000, 6172, 0, 0],
|
||||
[3, 5000, 5200, 4550, 4850, 6172, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 10: trailing_stop should raise so candle 3 causes a stoploss
|
||||
# without applying trailing_stop_positive since stoploss_offset is at 10%.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||
tc10 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||
@@ -175,14 +175,14 @@ tc10 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=4)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=4)]
|
||||
)
|
||||
|
||||
# Test 11: trailing_stop should raise so candle 3 causes a stoploss
|
||||
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||
tc11 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||
@@ -191,14 +191,14 @@ tc11 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 12: trailing_stop should raise in candle 2 and cause a stoploss in the same candle
|
||||
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||
tc12 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
||||
@@ -207,55 +207,55 @@ tc12 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 13: Buy and sell ROI on same candle
|
||||
# stop-loss: 10% (should not apply), ROI: 1%
|
||||
tc13 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 4850, 5100, 6172, 0, 0],
|
||||
[3, 4850, 5050, 4750, 4750, 6172, 0, 0],
|
||||
[4, 4750, 4950, 4750, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
# Test 14 - Buy and Stoploss on same candle
|
||||
# stop-loss: 5%, ROI: 10% (should not apply)
|
||||
tc14 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4600, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 4850, 5100, 6172, 0, 0],
|
||||
[3, 4850, 5050, 4750, 4750, 6172, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
|
||||
# Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle
|
||||
# stop-loss: 5%, ROI: 10% (should not apply)
|
||||
tc15 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4900, 5100, 6172, 1, 0],
|
||||
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
||||
[3, 4850, 5050, 4750, 4750, 6172, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1),
|
||||
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=2, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1),
|
||||
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=2, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 16: Buy, hold for 65 min, then forcesell using roi=-1
|
||||
# Causes negative profit even though sell-reason is ROI.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration)
|
||||
tc16 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
||||
@@ -263,7 +263,7 @@ tc16 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 17: Buy, hold for 120 mins, then forcesell using roi=-1
|
||||
@@ -271,7 +271,7 @@ tc16 = BTContainer(data=[
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||
# Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe.
|
||||
tc17 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
|
||||
@@ -279,7 +279,7 @@ tc17 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ tc17 = BTContainer(data=[
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||
# uses open_rate as sell-price
|
||||
tc18 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
||||
@@ -295,14 +295,14 @@ tc18 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 19: Buy, hold for 119 mins, then drop ROI to 1%, causing a sell in candle 3.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||
# uses calculated ROI (1%) as sell rate, otherwise identical to tc18
|
||||
tc19 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
||||
@@ -310,14 +310,14 @@ tc19 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4550, 4975, 4550, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 20: Buy, hold for 119 mins, then drop ROI to 1%, causing a sell in candle 3.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration)
|
||||
# uses calculated ROI (1%) as sell rate, otherwise identical to tc18
|
||||
tc20 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
|
||||
[2, 4987, 5300, 4950, 5200, 6172, 0, 0],
|
||||
@@ -325,7 +325,7 @@ tc20 = BTContainer(data=[
|
||||
[4, 4962, 4987, 4950, 4950, 6172, 0, 0],
|
||||
[5, 4925, 4975, 4925, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 21: trailing_stop ROI collision.
|
||||
@@ -333,7 +333,7 @@ tc20 = BTContainer(data=[
|
||||
# which cannot happen in reality
|
||||
# stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle
|
||||
tc21 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 4650, 5100, 6172, 0, 0],
|
||||
@@ -342,14 +342,14 @@ tc21 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 22: trailing_stop Raises in candle 2 - but ROI applies at the same time.
|
||||
# applying a positive trailing stop of 3% - ROI should apply before trailing stop.
|
||||
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2
|
||||
tc22 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||
@@ -358,17 +358,34 @@ tc22 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 23: trailing_stop Raises in candle 2 (does not trigger)
|
||||
|
||||
# Test 23: trailing_stop Raises in candle 2 - but ROI applies at the same time.
|
||||
# applying a positive trailing stop of 3% - ROI should apply before trailing stop.
|
||||
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2
|
||||
tc23 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5050, 4900, 4900, 6172, 0, 0, 0, 0],
|
||||
[2, 4900, 4900, 4749, 4900, 6172, 0, 0, 0, 0],
|
||||
[3, 4850, 5050, 4650, 4750, 6172, 0, 0, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2, is_short=True)]
|
||||
)
|
||||
|
||||
# Test 24: trailing_stop Raises in candle 2 (does not trigger)
|
||||
# applying a positive trailing stop of 3% since stop_positive_offset is reached.
|
||||
# ROI is changed after this to 4%, dropping ROI below trailing_stop_positive, causing a sell
|
||||
# in the candle after the raised stoploss candle with ROI reason.
|
||||
# Stoploss would trigger in this candle too, but it's no longer relevant.
|
||||
# stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell)
|
||||
tc23 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc24 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||
@@ -377,74 +394,107 @@ tc23 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, profit_perc=0.03, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 24: Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||
# Test 25: Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||
# Stoploss at 1%.
|
||||
# Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle)
|
||||
tc24 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc25 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4986, 4986, 6172, 0, 0],
|
||||
[3, 5010, 5010, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal
|
||||
[4, 5010, 5010, 4977, 4995, 6172, 0, 0],
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_sell_signal=True,
|
||||
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_exit_signal=True,
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 25: Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||
# Test 26: Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||
# Stoploss at 1%.
|
||||
# Sell-signal wins over stoploss
|
||||
tc25 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc26 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4986, 4986, 6172, 0, 0],
|
||||
[3, 5010, 5010, 4986, 5010, 6172, 0, 1],
|
||||
[4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True,
|
||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
||||
stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_exit_signal=True,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)]
|
||||
)
|
||||
|
||||
# Test 26: Sell with signal sell in candle 3 (ROI at signal candle)
|
||||
# Test 27: (copy of test26 with leverage)
|
||||
# Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||
# Stoploss at 1%.
|
||||
# Sell-signal wins over stoploss
|
||||
tc27 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4986, 4986, 6172, 0, 0],
|
||||
[3, 5010, 5010, 4986, 5010, 6172, 0, 1],
|
||||
[4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_exit_signal=True,
|
||||
leverage=5.0,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)]
|
||||
)
|
||||
|
||||
# Test 28: (copy of test26 with leverage and as short)
|
||||
# Sell with signal sell in candle 3 (stoploss also triggers on this candle)
|
||||
# Stoploss at 1%.
|
||||
# Sell-signal wins over stoploss
|
||||
tc28 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5025, 4975, 4987, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5025, 4975, 4987, 6172, 0, 0, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4987, 5012, 4986, 4986, 6172, 0, 0, 0, 0],
|
||||
[3, 5010, 5010, 4986, 5010, 6172, 0, 0, 0, 1],
|
||||
[4, 4990, 5010, 4855, 4995, 6172, 0, 0, 0, 0], # Triggers stoploss + sellsignal acted on
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0, 0, 0]],
|
||||
stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_exit_signal=True,
|
||||
leverage=5.0,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)]
|
||||
)
|
||||
# Test 29: Sell with signal sell in candle 3 (ROI at signal candle)
|
||||
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger)
|
||||
# Sell-signal wins over stoploss
|
||||
tc26 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc29 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4986, 4986, 6172, 0, 0],
|
||||
[3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal
|
||||
[4, 5010, 5010, 4855, 4995, 6172, 0, 0],
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
|
||||
stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_exit_signal=True,
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 27: Sell with signal sell in candle 3 (ROI at signal candle)
|
||||
# Test 30: Sell with signal sell in candle 3 (ROI at signal candle)
|
||||
# Stoploss at 10% (irrelevant), ROI at 5% (will trigger) - Wins over Sell-signal
|
||||
tc27 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc30 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[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, 4986, 4986, 6172, 0, 0],
|
||||
[3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal
|
||||
[4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on
|
||||
[5, 4995, 4995, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.002, use_sell_signal=True,
|
||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)]
|
||||
stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.002, use_exit_signal=True,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)]
|
||||
)
|
||||
|
||||
# Test 28: trailing_stop should raise so candle 3 causes a stoploss
|
||||
# Test 31: trailing_stop should raise so candle 3 causes a stoploss
|
||||
# Same case than tc11 - but candle 3 "gaps down" - the stoploss will be above the candle,
|
||||
# therefore "open" will be used
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||
tc28 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc31 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5100, 4950, 5100, 6172, 0, 0],
|
||||
[2, 5100, 5251, 5100, 5100, 6172, 0, 0],
|
||||
@@ -453,14 +503,33 @@ tc28 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 29: trailing_stop should be triggered by low of next candle, without adjusting stoploss using
|
||||
# Test 32: (Short of test 31) trailing_stop should raise so candle 3 causes a stoploss
|
||||
# Same case than tc11 - but candle 3 "gaps down" - the stoploss will be above the candle,
|
||||
# therefore "open" will be used
|
||||
# stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2
|
||||
tc32 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5050, 4890, 4890, 6172, 0, 0, 0, 0],
|
||||
[2, 4890, 4890, 4749, 4890, 6172, 0, 0, 0, 0],
|
||||
[3, 5150, 5350, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[
|
||||
BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3, is_short=True)
|
||||
]
|
||||
)
|
||||
|
||||
# Test 33: trailing_stop should be triggered by low of next candle, without adjusting stoploss using
|
||||
# high of stoploss candle.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply)
|
||||
tc29 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc33 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5050, 5000, 5000, 6172, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss
|
||||
@@ -468,13 +537,13 @@ tc29 = BTContainer(data=[
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True,
|
||||
trailing_stop_positive=0.03,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 30: trailing_stop should be triggered immediately on trade open candle.
|
||||
# Test 34: trailing_stop should be triggered immediately on trade open candle.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply)
|
||||
tc30 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc34 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
@@ -482,13 +551,13 @@ tc30 = BTContainer(data=[
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
|
||||
trailing_stop_positive=0.01,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
# Test 31: trailing_stop should be triggered immediately on trade open candle.
|
||||
# Test 35: trailing_stop should be triggered immediately on trade open candle.
|
||||
# stop-loss: 10%, ROI: 10% (should not apply)
|
||||
tc31 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc35 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
@@ -497,47 +566,68 @@ tc31 = BTContainer(data=[
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.01, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02,
|
||||
trailing_stop_positive=0.01,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
# Test 32: trailing_stop should be triggered immediately on trade open candle.
|
||||
# Test 36: trailing_stop should be triggered immediately on trade open candle.
|
||||
# stop-loss: 1%, ROI: 10% (should not apply)
|
||||
tc32 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
tc36 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade (signal on last candle) and stop
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade and stop
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02,
|
||||
trailing_stop_positive=0.01, use_custom_stoploss=True,
|
||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
# Test 33: trailing_stop should be triggered immediately on trade open candle.
|
||||
# Test 37: trailing_stop should be triggered immediately on trade open candle.
|
||||
# stop-loss: 1%, ROI: 10% (should not apply)
|
||||
tc33 = BTContainer(data=[
|
||||
# D O H L C V B S BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 'buy_signal_01'],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0, None], # enter trade (signal on last candle) and stop
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, None],
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, None],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, None]],
|
||||
tc37 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 0, 0, 'buy_signal_01'],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None],
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None]],
|
||||
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02,
|
||||
trailing_stop_positive=0.01, use_custom_stoploss=True,
|
||||
trades=[BTrade(
|
||||
sell_reason=SellType.TRAILING_STOP_LOSS,
|
||||
exit_reason=ExitType.TRAILING_STOP_LOSS,
|
||||
open_tick=1,
|
||||
close_tick=1,
|
||||
buy_tag='buy_signal_01'
|
||||
enter_tag='buy_signal_01'
|
||||
)]
|
||||
)
|
||||
# Test 38: trailing_stop should be triggered immediately on trade open candle.
|
||||
# copy of Test37 using shorts.
|
||||
# stop-loss: 1%, ROI: 10% (should not apply)
|
||||
tc38 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0, 'short_signal_01'],
|
||||
[1, 5000, 5049, 4500, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None],
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None]],
|
||||
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
|
||||
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02,
|
||||
trailing_stop_positive=0.01, use_custom_stoploss=True,
|
||||
trades=[BTrade(
|
||||
exit_reason=ExitType.TRAILING_STOP_LOSS,
|
||||
open_tick=1,
|
||||
close_tick=1,
|
||||
enter_tag='short_signal_01',
|
||||
is_short=True,
|
||||
)]
|
||||
)
|
||||
|
||||
# Test 34: Custom-entry-price below all candles should timeout - so no trade happens.
|
||||
tc34 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# Test 39: Custom-entry-price below all candles should timeout - so no trade happens.
|
||||
tc39 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
@@ -547,9 +637,9 @@ tc34 = BTContainer(data=[
|
||||
custom_entry_price=4200, trades=[]
|
||||
)
|
||||
|
||||
# Test 35: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
|
||||
tc35 = BTContainer(data=[
|
||||
# D O H L C V B S
|
||||
# Test 40: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
|
||||
tc40 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Timeout
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
@@ -557,16 +647,30 @@ tc35 = BTContainer(data=[
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
|
||||
custom_entry_price=7200, trades=[
|
||||
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)
|
||||
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)
|
||||
])
|
||||
|
||||
# Test 41: Custom-entry-price above all candles should have rate adjusted to "entry candle high"
|
||||
tc41 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0], # Timeout
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0],
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0],
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]],
|
||||
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01,
|
||||
custom_entry_price=4000,
|
||||
trades=[
|
||||
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True)
|
||||
]
|
||||
)
|
||||
|
||||
# Test 36: Custom-entry-price around candle low
|
||||
# Test 42: Custom-entry-price around candle low
|
||||
# Would cause immediate ROI exit, but since the trade was entered
|
||||
# below open, we treat this as cheating, and delay the sell by 1 candle.
|
||||
# details: https://github.com/freqtrade/freqtrade/issues/6261
|
||||
tc36 = BTContainer(data=[
|
||||
# D O H L C V B S BT
|
||||
tc42 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 4999, 6172, 0, 0], # Enter and immediate ROI
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
@@ -574,14 +678,14 @@ tc36 = BTContainer(data=[
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01,
|
||||
custom_entry_price=4952,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)]
|
||||
)
|
||||
|
||||
# Test 37: Custom-entry-price around candle low
|
||||
# Test 43: Custom-entry-price around candle low
|
||||
# Would cause immediate ROI exit below close
|
||||
# details: https://github.com/freqtrade/freqtrade/issues/6261
|
||||
tc37 = BTContainer(data=[
|
||||
# D O H L C V B S BT
|
||||
tc43 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5400, 5500, 4951, 5100, 6172, 0, 0], # Enter and immediate ROI
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 0],
|
||||
@@ -589,37 +693,65 @@ tc37 = BTContainer(data=[
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01,
|
||||
custom_entry_price=4952,
|
||||
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1)]
|
||||
trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)]
|
||||
)
|
||||
|
||||
# Test 38: Custom exit price below all candles
|
||||
# Test 44: Custom exit price below all candles
|
||||
# Price adjusted to candle Low.
|
||||
tc38 = BTContainer(data=[
|
||||
# D O H L C V B S BT
|
||||
tc44 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
||||
[2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout
|
||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 0],
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01,
|
||||
use_sell_signal=True,
|
||||
use_exit_signal=True,
|
||||
custom_exit_price=4552,
|
||||
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=3)]
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=3)]
|
||||
)
|
||||
|
||||
# Test 39: Custom exit price above all candles
|
||||
# Test 45: Custom exit price above all candles
|
||||
# causes sell signal timeout
|
||||
tc39 = BTContainer(data=[
|
||||
# D O H L C V B S BT
|
||||
tc45 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
||||
[2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout
|
||||
[2, 4950, 5250, 4900, 5100, 6172, 0, 1], # exit - entry timeout
|
||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 0],
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
||||
use_sell_signal=True,
|
||||
use_exit_signal=True,
|
||||
custom_exit_price=6052,
|
||||
trades=[BTrade(sell_reason=SellType.FORCE_SELL, open_tick=1, close_tick=4)]
|
||||
trades=[BTrade(exit_reason=ExitType.FORCE_EXIT, open_tick=1, close_tick=4)]
|
||||
)
|
||||
|
||||
# Test 46: (Short of tc45) Custom short exit price above below candles
|
||||
# causes sell signal timeout
|
||||
tc46 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5000, 4951, 5000, 6172, 0, 0, 0, 0],
|
||||
[2, 4910, 5150, 4910, 5100, 6172, 0, 0, 0, 1], # exit - entry timeout
|
||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
||||
use_exit_signal=True,
|
||||
custom_exit_price=4700,
|
||||
trades=[BTrade(exit_reason=ExitType.FORCE_EXIT, open_tick=1, close_tick=4, is_short=True)]
|
||||
)
|
||||
|
||||
# Test 47: Colliding long and short signal
|
||||
tc47 = BTContainer(data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0],
|
||||
[2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0],
|
||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0]],
|
||||
stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0,
|
||||
use_exit_signal=True,
|
||||
trades=[]
|
||||
)
|
||||
|
||||
|
||||
@@ -664,11 +796,19 @@ TESTS = [
|
||||
tc37,
|
||||
tc38,
|
||||
tc39,
|
||||
tc40,
|
||||
tc41,
|
||||
tc42,
|
||||
tc43,
|
||||
tc44,
|
||||
tc45,
|
||||
tc46,
|
||||
tc47,
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data", TESTS)
|
||||
def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
||||
def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer) -> None:
|
||||
"""
|
||||
run functional tests
|
||||
"""
|
||||
@@ -681,22 +821,27 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
||||
if data.trailing_stop_positive is not None:
|
||||
default_conf["trailing_stop_positive"] = data.trailing_stop_positive
|
||||
default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset
|
||||
default_conf["use_sell_signal"] = data.use_sell_signal
|
||||
default_conf["use_exit_signal"] = data.use_exit_signal
|
||||
|
||||
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)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch("freqtrade.exchange.Binance.get_max_leverage", return_value=100)
|
||||
patch_exchange(mocker)
|
||||
frame = _build_backtest_dataframe(data.data)
|
||||
backtesting = Backtesting(default_conf)
|
||||
# TODO: Should we initialize this properly??
|
||||
backtesting._can_short = True
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
backtesting.required_startup = 0
|
||||
backtesting.strategy.advise_buy = lambda a, m: frame
|
||||
backtesting.strategy.advise_sell = lambda a, m: frame
|
||||
backtesting.strategy.advise_entry = lambda a, m: frame
|
||||
backtesting.strategy.advise_exit = lambda a, m: frame
|
||||
if data.custom_entry_price:
|
||||
backtesting.strategy.custom_entry_price = MagicMock(return_value=data.custom_entry_price)
|
||||
if data.custom_exit_price:
|
||||
backtesting.strategy.custom_exit_price = MagicMock(return_value=data.custom_exit_price)
|
||||
backtesting.strategy.use_custom_stoploss = data.use_custom_stoploss
|
||||
backtesting.strategy.leverage = lambda **kwargs: data.leverage
|
||||
caplog.set_level(logging.DEBUG)
|
||||
|
||||
pair = "UNITTEST/BTC"
|
||||
@@ -715,8 +860,9 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
||||
assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3)
|
||||
|
||||
for c, trade in enumerate(data.trades):
|
||||
res = results.iloc[c]
|
||||
assert res.sell_reason == trade.sell_reason.value
|
||||
assert res.buy_tag == trade.buy_tag
|
||||
res: BTrade = results.iloc[c]
|
||||
assert res.exit_reason == trade.exit_reason.value
|
||||
assert res.enter_tag == trade.enter_tag
|
||||
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
|
||||
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)
|
||||
assert res.is_short == trade.is_short
|
||||
|
@@ -19,27 +19,27 @@ from freqtrade.data.btanalysis import BT_DATA_COLUMNS, evaluate_result_multi
|
||||
from freqtrade.data.converter import clean_ohlcv_dataframe
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.data.history import get_timerange
|
||||
from freqtrade.enums import RunMode, SellType
|
||||
from freqtrade.enums import ExitType, RunMode
|
||||
from freqtrade.exceptions import DependencyException, OperationalException
|
||||
from freqtrade.exchange.exchange import timeframe_to_next_date
|
||||
from freqtrade.misc import get_strategy_run_id
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from freqtrade.persistence import LocalTrade
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re, patch_exchange,
|
||||
patched_configuration_load_config_file)
|
||||
|
||||
|
||||
ORDER_TYPES = [
|
||||
{
|
||||
'buy': 'limit',
|
||||
'sell': 'limit',
|
||||
'entry': 'limit',
|
||||
'exit': 'limit',
|
||||
'stoploss': 'limit',
|
||||
'stoploss_on_exchange': False
|
||||
},
|
||||
{
|
||||
'buy': 'limit',
|
||||
'sell': 'limit',
|
||||
'entry': 'limit',
|
||||
'exit': 'limit',
|
||||
'stoploss': 'limit',
|
||||
'stoploss_on_exchange': True
|
||||
}]
|
||||
@@ -134,12 +134,14 @@ def _trend(signals, buy_value, sell_value):
|
||||
n = len(signals['low'])
|
||||
buy = np.zeros(n)
|
||||
sell = np.zeros(n)
|
||||
for i in range(0, len(signals['buy'])):
|
||||
for i in range(0, len(signals['date'])):
|
||||
if random.random() > 0.5: # Both buy and sell signals at same timeframe
|
||||
buy[i] = buy_value
|
||||
sell[i] = sell_value
|
||||
signals['buy'] = buy
|
||||
signals['sell'] = sell
|
||||
signals['enter_long'] = buy
|
||||
signals['exit_long'] = sell
|
||||
signals['enter_short'] = 0
|
||||
signals['exit_short'] = 0
|
||||
return signals
|
||||
|
||||
|
||||
@@ -154,8 +156,10 @@ def _trend_alternate(dataframe=None, metadata=None):
|
||||
buy[i] = 1
|
||||
else:
|
||||
sell[i] = 1
|
||||
signals['buy'] = buy
|
||||
signals['sell'] = sell
|
||||
signals['enter_long'] = buy
|
||||
signals['exit_long'] = sell
|
||||
signals['enter_short'] = 0
|
||||
signals['exit_short'] = 0
|
||||
return dataframe
|
||||
|
||||
|
||||
@@ -166,7 +170,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--export', 'none'
|
||||
]
|
||||
|
||||
@@ -201,7 +205,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--datadir', '/foo/bar',
|
||||
'--timeframe', '1m',
|
||||
'--enable-position-stacking',
|
||||
@@ -251,7 +255,7 @@ def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog)
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '2'
|
||||
]
|
||||
@@ -262,7 +266,7 @@ def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog)
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '0.5'
|
||||
]
|
||||
@@ -280,7 +284,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None:
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
]
|
||||
pargs = get_args(args)
|
||||
start_backtesting(pargs)
|
||||
@@ -302,8 +306,8 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
|
||||
assert backtesting.config == default_conf
|
||||
assert backtesting.timeframe == '5m'
|
||||
assert callable(backtesting.strategy.advise_all_indicators)
|
||||
assert callable(backtesting.strategy.advise_buy)
|
||||
assert callable(backtesting.strategy.advise_sell)
|
||||
assert callable(backtesting.strategy.advise_entry)
|
||||
assert callable(backtesting.strategy.advise_exit)
|
||||
assert isinstance(backtesting.strategy.dp, DataProvider)
|
||||
get_fee.assert_called()
|
||||
assert backtesting.fee == 0.5
|
||||
@@ -313,7 +317,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
|
||||
def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None:
|
||||
patch_exchange(mocker)
|
||||
del default_conf['timeframe']
|
||||
default_conf['strategy_list'] = ['StrategyTestV2',
|
||||
default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY,
|
||||
'HyperoptableStrategy']
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
|
||||
@@ -350,7 +354,6 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
|
||||
assert len(processed['UNITTEST/BTC']) == 102
|
||||
|
||||
# Load strategy to compare the result between Backtesting function and strategy are the same
|
||||
default_conf.update({'strategy': 'StrategyTestV2'})
|
||||
strategy = StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
processed2 = strategy.advise_all_indicators(data)
|
||||
@@ -494,16 +497,17 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
|
||||
Backtesting(default_conf)
|
||||
|
||||
# Multiple strategies
|
||||
default_conf['strategy_list'] = ['StrategyTestV2', 'TestStrategyLegacyV1']
|
||||
default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'TestStrategyLegacyV1']
|
||||
with pytest.raises(OperationalException,
|
||||
match='PrecisionFilter not allowed for backtesting multiple strategies.'):
|
||||
Backtesting(default_conf)
|
||||
|
||||
|
||||
def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
||||
default_conf['use_sell_signal'] = False
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
default_conf['stake_amount'] = 'unlimited'
|
||||
default_conf['max_open_trades'] = 2
|
||||
@@ -520,7 +524,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
||||
0.0012, # High
|
||||
'', # Buy Signal Name
|
||||
]
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert isinstance(trade, LocalTrade)
|
||||
assert trade.stake_amount == 495
|
||||
|
||||
@@ -528,42 +532,123 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
||||
LocalTrade.trades_open.append(trade)
|
||||
LocalTrade.trades_open.append(trade)
|
||||
backtesting.wallets.update()
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade is None
|
||||
LocalTrade.trades_open.pop()
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade is not None
|
||||
|
||||
backtesting.strategy.custom_stake_amount = lambda **kwargs: 123.5
|
||||
backtesting.wallets.update()
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade
|
||||
assert trade.stake_amount == 123.5
|
||||
|
||||
# In case of error - use proposed stake
|
||||
backtesting.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade
|
||||
assert trade.stake_amount == 495
|
||||
assert trade.is_short is False
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='short')
|
||||
assert trade
|
||||
assert trade.stake_amount == 495
|
||||
assert trade.is_short is True
|
||||
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=300.0)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade
|
||||
assert trade.stake_amount == 300.0
|
||||
|
||||
|
||||
def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
|
||||
default_conf_usdt['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_leverage", return_value=100)
|
||||
patch_exchange(mocker)
|
||||
default_conf_usdt['stake_amount'] = 300
|
||||
default_conf_usdt['max_open_trades'] = 2
|
||||
default_conf_usdt['trading_mode'] = 'futures'
|
||||
default_conf_usdt['margin_mode'] = 'isolated'
|
||||
default_conf_usdt['stake_currency'] = 'USDT'
|
||||
default_conf_usdt['exchange']['pair_whitelist'] = ['.*']
|
||||
backtesting = Backtesting(default_conf_usdt)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
pair = 'UNITTEST/USDT:USDT'
|
||||
row = [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
|
||||
0.001, # Open
|
||||
0.0012, # High
|
||||
0.00099, # Low
|
||||
0.0011, # Close
|
||||
1, # enter_long
|
||||
0, # exit_long
|
||||
1, # enter_short
|
||||
0, # exit_hsort
|
||||
'', # Long Signal Name
|
||||
'', # Short Signal Name
|
||||
'', # Exit Signal Name
|
||||
]
|
||||
|
||||
backtesting.strategy.leverage = MagicMock(return_value=5.0)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_maintenance_ratio_and_amt",
|
||||
return_value=(0.01, 0.01))
|
||||
|
||||
# leverage = 5
|
||||
# ep1(trade.open_rate) = 0.001
|
||||
# position(trade.amount) = 1500000
|
||||
# stake_amount = 300 -> wb = 300 / 5 = 60
|
||||
# mmr = 0.01
|
||||
# cum_b = 0.01
|
||||
# side_1: -1 if is_short else 1
|
||||
# liq_buffer = 0.05
|
||||
#
|
||||
# Binance, Long
|
||||
# liquidation_price
|
||||
# = ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
|
||||
# = ((300 + 0.01) - (1 * 1500000 * 0.001)) / ((1500000 * 0.01) - (1 * 1500000))
|
||||
# = 0.0008080740740740741
|
||||
# freqtrade_liquidation_price = liq + (abs(open_rate - liq) * liq_buffer * side_1)
|
||||
# = 0.0008080740740740741 + ((0.001 - 0.0008080740740740741) * 0.05 * 1)
|
||||
# = 0.0008176703703703704
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert pytest.approx(trade.liquidation_price) == 0.00081767037
|
||||
|
||||
# Binance, Short
|
||||
# liquidation_price
|
||||
# = ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
|
||||
# = ((300 + 0.01) - ((-1) * 1500000 * 0.001)) / ((1500000 * 0.01) - ((-1) * 1500000))
|
||||
# = 0.0011881254125412541
|
||||
# freqtrade_liquidation_price = liq + (abs(open_rate - liq) * liq_buffer * side_1)
|
||||
# = 0.0011881254125412541 + (abs(0.001 - 0.0011881254125412541) * 0.05 * -1)
|
||||
# = 0.0011787191419141915
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='short')
|
||||
assert pytest.approx(trade.liquidation_price) == 0.0011787191
|
||||
|
||||
# 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)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade is None
|
||||
|
||||
# Stake-amount throwing error
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_trade_stake_amount",
|
||||
side_effect=DependencyException)
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert trade is None
|
||||
|
||||
|
||||
def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
|
||||
default_conf['use_sell_signal'] = False
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
default_conf['timeframe_detail'] = '1m'
|
||||
default_conf['max_open_trades'] = 2
|
||||
@@ -572,63 +657,72 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
|
||||
pair = 'UNITTEST/BTC'
|
||||
row = [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=55, tzinfo=timezone.utc),
|
||||
1, # Buy
|
||||
200, # Open
|
||||
201, # Close
|
||||
0, # Sell
|
||||
195, # Low
|
||||
201.5, # High
|
||||
'', # Buy Signal Name
|
||||
195, # Low
|
||||
201, # Close
|
||||
1, # enter_long
|
||||
0, # exit_long
|
||||
0, # enter_short
|
||||
0, # exit_hsort
|
||||
'', # Long Signal Name
|
||||
'', # Short Signal Name
|
||||
'', # Exit Signal Name
|
||||
]
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert isinstance(trade, LocalTrade)
|
||||
|
||||
row_sell = [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc),
|
||||
0, # Buy
|
||||
200, # Open
|
||||
201, # Close
|
||||
0, # Sell
|
||||
195, # Low
|
||||
210.5, # High
|
||||
'', # Buy Signal Name
|
||||
195, # Low
|
||||
201, # Close
|
||||
0, # enter_long
|
||||
0, # exit_long
|
||||
0, # enter_short
|
||||
0, # exit_short
|
||||
'', # long Signal Name
|
||||
'', # Short Signal Name
|
||||
'', # Exit Signal Name
|
||||
|
||||
]
|
||||
row_detail = pd.DataFrame(
|
||||
[
|
||||
[
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc),
|
||||
1, 200, 199, 0, 197, 200.1, '', '',
|
||||
200, 200.1, 197, 199, 1, 0, 0, 0, '', '', '',
|
||||
], [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=1, tzinfo=timezone.utc),
|
||||
0, 199, 199.5, 0, 199, 199.7, '', '',
|
||||
199, 199.7, 199, 199.5, 0, 0, 0, 0, '', '', '',
|
||||
], [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=2, tzinfo=timezone.utc),
|
||||
0, 199.5, 200.5, 0, 199, 200.8, '', '',
|
||||
199.5, 200.8, 199, 200.9, 0, 0, 0, 0, '', '', '',
|
||||
], [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=3, tzinfo=timezone.utc),
|
||||
0, 200.5, 210.5, 0, 193, 210.5, '', '', # ROI sell (?)
|
||||
200.5, 210.5, 193, 210.5, 0, 0, 0, 0, '', '', '', # ROI sell (?)
|
||||
], [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=4, tzinfo=timezone.utc),
|
||||
0, 200, 199, 0, 193, 200.1, '', '',
|
||||
200, 200.1, 193, 199, 0, 0, 0, 0, '', '', '',
|
||||
],
|
||||
], columns=["date", "buy", "open", "close", "sell", "low", "high", "buy_tag", "exit_tag"]
|
||||
], columns=['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long',
|
||||
'enter_short', 'exit_short', 'long_tag', 'short_tag', 'exit_tag']
|
||||
)
|
||||
|
||||
# No data available.
|
||||
res = backtesting._get_sell_trade_entry(trade, row_sell)
|
||||
assert res is not None
|
||||
assert res.sell_reason == SellType.ROI.value
|
||||
assert res.exit_reason == ExitType.ROI.value
|
||||
assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc)
|
||||
|
||||
# Enter new trade
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert isinstance(trade, LocalTrade)
|
||||
# Assign empty ... no result.
|
||||
backtesting.detail_data[pair] = pd.DataFrame(
|
||||
[], columns=["date", "buy", "open", "close", "sell", "low", "high", "buy_tag", "exit_tag"])
|
||||
[], columns=['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long',
|
||||
'enter_short', 'exit_short', 'long_tag', 'short_tag', 'exit_tag'])
|
||||
|
||||
res = backtesting._get_sell_trade_entry(trade, row)
|
||||
assert res is None
|
||||
@@ -638,7 +732,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
|
||||
|
||||
res = backtesting._get_sell_trade_entry(trade, row_sell)
|
||||
assert res is not None
|
||||
assert res.sell_reason == SellType.ROI.value
|
||||
assert res.exit_reason == ExitType.ROI.value
|
||||
# Sell at minute 3 (not available above!)
|
||||
assert res.close_date_utc == datetime(2020, 1, 1, 5, 3, tzinfo=timezone.utc)
|
||||
sell_order = res.select_order('sell', True)
|
||||
@@ -646,9 +740,10 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
|
||||
|
||||
|
||||
def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['use_sell_signal'] = False
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
@@ -686,7 +781,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.value, SellType.ROI.value],
|
||||
'exit_reason': [ExitType.ROI.value, ExitType.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],
|
||||
@@ -694,7 +789,8 @@ 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],
|
||||
'buy_tag': [None, None]
|
||||
'enter_tag': [None, None],
|
||||
"is_short": [False, False],
|
||||
})
|
||||
pd.testing.assert_frame_equal(results, expected)
|
||||
data_pair = processed[pair]
|
||||
@@ -711,9 +807,10 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
|
||||
|
||||
def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['use_sell_signal'] = False
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
@@ -735,6 +832,36 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
|
||||
assert len(results['results']) == 1
|
||||
|
||||
|
||||
def test_backtest_trim_no_data_left(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
timerange = TimeRange('date', None, 1517227800, 0)
|
||||
backtesting.required_startup = 100
|
||||
backtesting.timerange = timerange
|
||||
data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'],
|
||||
timerange=timerange)
|
||||
df = data['UNITTEST/BTC']
|
||||
df.loc[:, 'date'] = df.loc[:, 'date'] - timedelta(days=1)
|
||||
# Trimming 100 candles, so after 2nd trimming, no candle is left.
|
||||
df = df.iloc[:100]
|
||||
data['XRP/USDT'] = df
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
|
||||
backtesting.backtest(
|
||||
processed=deepcopy(processed),
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=10,
|
||||
position_stacking=False,
|
||||
)
|
||||
|
||||
|
||||
def test_processed(default_conf, mocker, testdatadir) -> None:
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
@@ -751,9 +878,10 @@ def test_processed(default_conf, mocker, testdatadir) -> None:
|
||||
|
||||
|
||||
def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['use_sell_signal'] = False
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=100000)
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
@@ -770,7 +898,7 @@ def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadi
|
||||
dp = backtesting.strategy.dp
|
||||
df, _ = dp.get_analyzed_dataframe(pair, backtesting.strategy.timeframe)
|
||||
current_candle = df.iloc[-1].squeeze()
|
||||
assert current_candle['buy'] == 1
|
||||
assert current_candle['enter_long'] == 1
|
||||
|
||||
candle_date = timeframe_to_next_date(backtesting.strategy.timeframe, current_candle['date'])
|
||||
assert candle_date == current_time
|
||||
@@ -802,6 +930,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)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
tests = [
|
||||
['sine', 9],
|
||||
['raise', 10],
|
||||
@@ -809,7 +938,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
||||
['sine', 9],
|
||||
['raise', 10],
|
||||
]
|
||||
# While buy-signals are unrealistic, running backtesting
|
||||
# While entry-signals are unrealistic, running backtesting
|
||||
# over and over again should not cause different results
|
||||
for [contour, numres] in tests:
|
||||
# Debug output for random test failure
|
||||
@@ -836,14 +965,15 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
|
||||
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_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
# While buy-signals are unrealistic, running backtesting
|
||||
# While entry-signals are unrealistic, running backtesting
|
||||
# over and over again should not cause different results
|
||||
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == expected
|
||||
|
||||
|
||||
def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
|
||||
# Override the default buy trend function in our StrategyTestV2
|
||||
# Override the default buy trend function in our StrategyTest
|
||||
def fun(dataframe=None, pair=None):
|
||||
buy_value = 1
|
||||
sell_value = 1
|
||||
@@ -852,14 +982,14 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
backtesting.strategy.advise_buy = fun # Override
|
||||
backtesting.strategy.advise_sell = fun # Override
|
||||
backtesting.strategy.advise_entry = fun # Override
|
||||
backtesting.strategy.advise_exit = fun # Override
|
||||
result = backtesting.backtest(**backtest_conf)
|
||||
assert result['results'].empty
|
||||
|
||||
|
||||
def test_backtest_only_sell(mocker, default_conf, testdatadir):
|
||||
# Override the default buy trend function in our StrategyTestV2
|
||||
# Override the default buy trend function in our StrategyTest
|
||||
def fun(dataframe=None, pair=None):
|
||||
buy_value = 0
|
||||
sell_value = 1
|
||||
@@ -868,14 +998,15 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
backtesting.strategy.advise_buy = fun # Override
|
||||
backtesting.strategy.advise_sell = fun # Override
|
||||
backtesting.strategy.advise_entry = fun # Override
|
||||
backtesting.strategy.advise_exit = fun # Override
|
||||
result = backtesting.backtest(**backtest_conf)
|
||||
assert result['results'].empty
|
||||
|
||||
|
||||
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_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
|
||||
pair='UNITTEST/BTC', datadir=testdatadir)
|
||||
@@ -883,8 +1014,8 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting.required_startup = 0
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
backtesting.strategy.advise_buy = _trend_alternate # Override
|
||||
backtesting.strategy.advise_sell = _trend_alternate # Override
|
||||
backtesting.strategy.advise_entry = _trend_alternate # Override
|
||||
backtesting.strategy.advise_exit = _trend_alternate # Override
|
||||
result = backtesting.backtest(**backtest_conf)
|
||||
# 200 candles in backtest data
|
||||
# won't buy on first (shifted by 1)
|
||||
@@ -915,11 +1046,14 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
||||
multi = 20
|
||||
else:
|
||||
multi = 18
|
||||
dataframe['buy'] = np.where(dataframe.index % multi == 0, 1, 0)
|
||||
dataframe['sell'] = np.where((dataframe.index + multi - 2) % multi == 0, 1, 0)
|
||||
dataframe['enter_long'] = np.where(dataframe.index % multi == 0, 1, 0)
|
||||
dataframe['exit_long'] = np.where((dataframe.index + multi - 2) % multi == 0, 1, 0)
|
||||
dataframe['enter_short'] = 0
|
||||
dataframe['exit_short'] = 0
|
||||
return dataframe
|
||||
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -935,8 +1069,8 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
||||
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
backtesting.strategy.advise_buy = _trend_alternate_hold # Override
|
||||
backtesting.strategy.advise_sell = _trend_alternate_hold # Override
|
||||
backtesting.strategy.advise_entry = _trend_alternate_hold # Override
|
||||
backtesting.strategy.advise_exit = _trend_alternate_hold # Override
|
||||
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
@@ -959,8 +1093,9 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
||||
offset = 1 if tres == 0 else 0
|
||||
removed_candles = len(data[pair]) - offset - backtesting.strategy.startup_candle_count
|
||||
assert len(backtesting.dataprovider.get_analyzed_dataframe(pair, '5m')[0]) == removed_candles
|
||||
assert len(backtesting.dataprovider.get_analyzed_dataframe(
|
||||
'NXT/BTC', '5m')[0]) == len(data['NXT/BTC']) - 1 - backtesting.strategy.startup_candle_count
|
||||
assert len(
|
||||
backtesting.dataprovider.get_analyzed_dataframe('NXT/BTC', '5m')[0]
|
||||
) == len(data['NXT/BTC']) - 1 - backtesting.strategy.startup_candle_count
|
||||
|
||||
backtest_conf = {
|
||||
'processed': deepcopy(processed),
|
||||
@@ -986,7 +1121,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--datadir', str(testdatadir),
|
||||
'--timeframe', '1m',
|
||||
'--timerange', '1510694220-1510700340',
|
||||
@@ -1016,10 +1151,10 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
|
||||
def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
|
||||
default_conf.update({
|
||||
"use_sell_signal": True,
|
||||
"sell_profit_only": False,
|
||||
"sell_profit_offset": 0.0,
|
||||
"ignore_roi_if_buy_signal": False,
|
||||
"use_exit_signal": True,
|
||||
"exit_profit_only": False,
|
||||
"exit_profit_offset": 0.0,
|
||||
"ignore_roi_if_entry_signal": False,
|
||||
})
|
||||
patch_exchange(mocker)
|
||||
backtestmock = MagicMock(return_value={
|
||||
@@ -1043,7 +1178,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
text_table_bt_results=text_table_mock,
|
||||
text_table_strategy=strattable_mock,
|
||||
generate_pair_metrics=MagicMock(),
|
||||
generate_sell_reason_stats=sell_reason_mock,
|
||||
generate_exit_reason_stats=sell_reason_mock,
|
||||
generate_strategy_comparison=strat_summary,
|
||||
generate_daily_stats=MagicMock(),
|
||||
)
|
||||
@@ -1059,7 +1194,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
'--enable-position-stacking',
|
||||
'--disable-max-market-positions',
|
||||
'--strategy-list',
|
||||
'StrategyTestV2',
|
||||
CURRENT_TEST_STRATEGY,
|
||||
'TestStrategyLegacyV1',
|
||||
]
|
||||
args = get_args(args)
|
||||
@@ -1082,7 +1217,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
'Backtesting with data from 2017-11-14 21:17:00 '
|
||||
'up to 2017-11-14 22:58:00 (0 days).',
|
||||
'Parameter --enable-position-stacking detected ...',
|
||||
'Running backtesting for Strategy StrategyTestV2',
|
||||
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
||||
'Running backtesting for Strategy TestStrategyLegacyV1',
|
||||
]
|
||||
|
||||
@@ -1093,10 +1228,10 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
||||
def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys):
|
||||
default_conf.update({
|
||||
"use_sell_signal": True,
|
||||
"sell_profit_only": False,
|
||||
"sell_profit_offset": 0.0,
|
||||
"ignore_roi_if_buy_signal": False,
|
||||
"use_exit_signal": True,
|
||||
"exit_profit_only": False,
|
||||
"exit_profit_offset": 0.0,
|
||||
"ignore_roi_if_entry_signal": False,
|
||||
})
|
||||
patch_exchange(mocker)
|
||||
result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
|
||||
@@ -1112,7 +1247,9 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'stake_amount': [0.01, 0.01],
|
||||
'open_rate': [0.104445, 0.10302485],
|
||||
'close_rate': [0.104969, 0.103541],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI]
|
||||
"is_short": [False, False],
|
||||
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI]
|
||||
})
|
||||
result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
|
||||
'profit_ratio': [0.03, 0.01, 0.1],
|
||||
@@ -1129,7 +1266,8 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'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]
|
||||
"is_short": [False, False, False],
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
|
||||
})
|
||||
backtestmock = MagicMock(side_effect=[
|
||||
{
|
||||
@@ -1168,7 +1306,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'--disable-max-market-positions',
|
||||
'--breakdown', 'day',
|
||||
'--strategy-list',
|
||||
'StrategyTestV2',
|
||||
CURRENT_TEST_STRATEGY,
|
||||
'TestStrategyLegacyV1',
|
||||
]
|
||||
args = get_args(args)
|
||||
@@ -1185,7 +1323,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'Backtesting with data from 2017-11-14 21:17:00 '
|
||||
'up to 2017-11-14 22:58:00 (0 days).',
|
||||
'Parameter --enable-position-stacking detected ...',
|
||||
'Running backtesting for Strategy StrategyTestV2',
|
||||
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
||||
'Running backtesting for Strategy TestStrategyLegacyV1',
|
||||
]
|
||||
|
||||
@@ -1194,22 +1332,128 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert 'BACKTESTING REPORT' in captured.out
|
||||
assert 'SELL REASON STATS' in captured.out
|
||||
assert 'EXIT REASON STATS' in captured.out
|
||||
assert 'DAY BREAKDOWN' in captured.out
|
||||
assert 'LEFT OPEN TRADES REPORT' in captured.out
|
||||
assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out
|
||||
assert 'STRATEGY SUMMARY' in captured.out
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
||||
def test_backtest_start_nomock_futures(default_conf_usdt, mocker,
|
||||
caplog, testdatadir, capsys):
|
||||
# Tests detail-data loading
|
||||
default_conf_usdt.update({
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"use_exit_signal": True,
|
||||
"exit_profit_only": False,
|
||||
"exit_profit_offset": 0.0,
|
||||
"ignore_roi_if_entry_signal": False,
|
||||
"strategy": CURRENT_TEST_STRATEGY,
|
||||
})
|
||||
patch_exchange(mocker)
|
||||
result1 = pd.DataFrame({'pair': ['XRP/USDT', 'XRP/USDT'],
|
||||
'profit_ratio': [0.0, 0.0],
|
||||
'profit_abs': [0.0, 0.0],
|
||||
'open_date': pd.to_datetime(['2021-11-18 18:00:00',
|
||||
'2021-11-18 03:00:00', ], utc=True
|
||||
),
|
||||
'close_date': pd.to_datetime(['2021-11-18 20:00:00',
|
||||
'2021-11-18 05:00:00', ], utc=True),
|
||||
'trade_duration': [235, 40],
|
||||
'is_open': [False, False],
|
||||
'is_short': [False, False],
|
||||
'stake_amount': [0.01, 0.01],
|
||||
'open_rate': [0.104445, 0.10302485],
|
||||
'close_rate': [0.104969, 0.103541],
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI]
|
||||
})
|
||||
result2 = pd.DataFrame({'pair': ['XRP/USDT', 'XRP/USDT', 'XRP/USDT'],
|
||||
'profit_ratio': [0.03, 0.01, 0.1],
|
||||
'profit_abs': [0.01, 0.02, 0.2],
|
||||
'open_date': pd.to_datetime(['2021-11-19 18:00:00',
|
||||
'2021-11-19 03:00:00',
|
||||
'2021-11-19 05:00:00'], utc=True
|
||||
),
|
||||
'close_date': pd.to_datetime(['2021-11-19 20:00:00',
|
||||
'2021-11-19 05:00:00',
|
||||
'2021-11-19 08:00:00'], utc=True),
|
||||
'trade_duration': [47, 40, 20],
|
||||
'is_open': [False, False, False],
|
||||
'is_short': [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],
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
|
||||
})
|
||||
backtestmock = MagicMock(side_effect=[
|
||||
{
|
||||
'results': result1,
|
||||
'config': default_conf_usdt,
|
||||
'locks': [],
|
||||
'rejected_signals': 20,
|
||||
'timedout_entry_orders': 0,
|
||||
'timedout_exit_orders': 0,
|
||||
'final_balance': 1000,
|
||||
},
|
||||
{
|
||||
'results': result2,
|
||||
'config': default_conf_usdt,
|
||||
'locks': [],
|
||||
'rejected_signals': 20,
|
||||
'timedout_entry_orders': 0,
|
||||
'timedout_exit_orders': 0,
|
||||
'final_balance': 1000,
|
||||
}
|
||||
])
|
||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['XRP/USDT']))
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
|
||||
|
||||
patched_configuration_load_config_file(mocker, default_conf_usdt)
|
||||
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--datadir', str(testdatadir),
|
||||
'--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'),
|
||||
'--timeframe', '1h',
|
||||
]
|
||||
args = get_args(args)
|
||||
start_backtesting(args)
|
||||
|
||||
# check the logs, that will contain the backtest result
|
||||
exists = [
|
||||
'Parameter -i/--timeframe detected ... Using timeframe: 1h ...',
|
||||
f'Using data directory: {testdatadir} ...',
|
||||
'Loading data from 2021-11-17 01:00:00 '
|
||||
'up to 2021-11-21 03:00:00 (4 days).',
|
||||
'Backtesting with data from 2021-11-17 21:00:00 '
|
||||
'up to 2021-11-21 03:00:00 (3 days).',
|
||||
'XRP/USDT, funding_rate, 8h, data starts at 2021-11-18 00:00:00',
|
||||
'XRP/USDT, mark, 8h, data starts at 2021-11-18 00:00:00',
|
||||
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
||||
]
|
||||
|
||||
for line in exists:
|
||||
assert log_has(line, caplog)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert 'BACKTESTING REPORT' in captured.out
|
||||
assert 'EXIT REASON STATS' in captured.out
|
||||
assert 'LEFT OPEN TRADES REPORT' in captured.out
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
||||
def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
caplog, testdatadir, capsys):
|
||||
# Tests detail-data loading
|
||||
default_conf.update({
|
||||
"use_sell_signal": True,
|
||||
"sell_profit_only": False,
|
||||
"sell_profit_offset": 0.0,
|
||||
"ignore_roi_if_buy_signal": False,
|
||||
"use_exit_signal": True,
|
||||
"exit_profit_only": False,
|
||||
"exit_profit_offset": 0.0,
|
||||
"ignore_roi_if_entry_signal": False,
|
||||
})
|
||||
patch_exchange(mocker)
|
||||
result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
|
||||
@@ -1222,10 +1466,11 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
'2018-01-30 05:35:00', ], utc=True),
|
||||
'trade_duration': [235, 40],
|
||||
'is_open': [False, False],
|
||||
'is_short': [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]
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI]
|
||||
})
|
||||
result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
|
||||
'profit_ratio': [0.03, 0.01, 0.1],
|
||||
@@ -1239,10 +1484,11 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
'2018-01-30 08:30:00'], utc=True),
|
||||
'trade_duration': [47, 40, 20],
|
||||
'is_open': [False, False, False],
|
||||
'is_short': [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]
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
|
||||
})
|
||||
backtestmock = MagicMock(side_effect=[
|
||||
{
|
||||
@@ -1278,7 +1524,7 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
'--timeframe', '5m',
|
||||
'--timeframe-detail', '1m',
|
||||
'--strategy-list',
|
||||
'StrategyTestV2'
|
||||
CURRENT_TEST_STRATEGY
|
||||
]
|
||||
args = get_args(args)
|
||||
start_backtesting(args)
|
||||
@@ -1292,7 +1538,7 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
'up to 2019-10-13 11:10:00 (2 days).',
|
||||
'Backtesting with data from 2019-10-11 01:40:00 '
|
||||
'up to 2019-10-13 11:10:00 (2 days).',
|
||||
'Running backtesting for Strategy StrategyTestV2',
|
||||
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
||||
]
|
||||
|
||||
for line in exists:
|
||||
@@ -1300,7 +1546,7 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert 'BACKTESTING REPORT' in captured.out
|
||||
assert 'SELL REASON STATS' in captured.out
|
||||
assert 'EXIT REASON STATS' in captured.out
|
||||
assert 'LEFT OPEN TRADES REPORT' in captured.out
|
||||
|
||||
|
||||
@@ -1311,10 +1557,10 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
|
||||
def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testdatadir, run_id,
|
||||
start_delta, cache):
|
||||
default_conf.update({
|
||||
"use_sell_signal": True,
|
||||
"sell_profit_only": False,
|
||||
"sell_profit_offset": 0.0,
|
||||
"ignore_roi_if_buy_signal": False,
|
||||
"use_exit_signal": True,
|
||||
"exit_profit_only": False,
|
||||
"exit_profit_offset": 0.0,
|
||||
"ignore_roi_if_entry_signal": False,
|
||||
})
|
||||
patch_exchange(mocker)
|
||||
backtestmock = MagicMock(return_value={
|
||||
|
@@ -8,15 +8,16 @@ from arrow import Arrow
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.history import get_timerange
|
||||
from freqtrade.enums import SellType
|
||||
from freqtrade.enums import ExitType
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from tests.conftest import patch_exchange
|
||||
|
||||
|
||||
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['use_sell_signal'] = False
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
default_conf.update({
|
||||
"stake_amount": 100.0,
|
||||
@@ -59,7 +60,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
|
||||
'trade_duration': [200, 40],
|
||||
'profit_ratio': [0.0, 0.0],
|
||||
'profit_abs': [0.0, 0.0],
|
||||
'sell_reason': [SellType.ROI.value, SellType.ROI.value],
|
||||
'exit_reason': [ExitType.ROI.value, ExitType.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],
|
||||
@@ -67,7 +68,8 @@ 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],
|
||||
'buy_tag': [None, None],
|
||||
'enter_tag': [None, None],
|
||||
'is_short': [False, False],
|
||||
})
|
||||
pd.testing.assert_frame_equal(results, expected)
|
||||
data_pair = processed[pair]
|
||||
|
@@ -6,7 +6,8 @@ from unittest.mock import MagicMock
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_edge
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.optimize.edge_cli import EdgeCli
|
||||
from tests.conftest import get_args, log_has, patch_exchange, patched_configuration_load_config_file
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, patch_exchange,
|
||||
patched_configuration_load_config_file)
|
||||
|
||||
|
||||
def test_setup_optimize_configuration_without_arguments(mocker, default_conf, caplog) -> None:
|
||||
@@ -15,7 +16,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca
|
||||
args = [
|
||||
'edge',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
]
|
||||
|
||||
config = setup_optimize_configuration(get_args(args), RunMode.EDGE)
|
||||
@@ -44,7 +45,7 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N
|
||||
args = [
|
||||
'edge',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--datadir', '/foo/bar',
|
||||
'--timeframe', '1m',
|
||||
'--timerange', ':100',
|
||||
@@ -78,7 +79,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None:
|
||||
args = [
|
||||
'edge',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
]
|
||||
pargs = get_args(args)
|
||||
start_edge(pargs)
|
||||
|
@@ -10,7 +10,7 @@ from filelock import Timeout
|
||||
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_hyperopt
|
||||
from freqtrade.data.history import load_data
|
||||
from freqtrade.enums import RunMode, SellType
|
||||
from freqtrade.enums import ExitType, RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.hyperopt import Hyperopt
|
||||
from freqtrade.optimize.hyperopt_auto import HyperOptAuto
|
||||
@@ -18,32 +18,32 @@ from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
||||
from freqtrade.optimize.space import SKDecimal
|
||||
from freqtrade.strategy.hyper import IntParameter
|
||||
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re, patch_exchange,
|
||||
patched_configuration_load_config_file)
|
||||
|
||||
|
||||
def generate_result_metrics():
|
||||
return {
|
||||
'trade_count': 1,
|
||||
'total_trades': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 0.01,
|
||||
'duration': 20.0,
|
||||
'wins': 1,
|
||||
'draws': 0,
|
||||
'losses': 0,
|
||||
'profit_mean': 0.01,
|
||||
'profit_total_abs': 0.001,
|
||||
'profit_total': 0.01,
|
||||
'holding_avg': timedelta(minutes=20),
|
||||
'max_drawdown': 0.001,
|
||||
'max_drawdown_abs': 0.001,
|
||||
'loss': 0.001,
|
||||
'is_initial_point': 0.001,
|
||||
'is_random': False,
|
||||
'is_best': 1,
|
||||
}
|
||||
'trade_count': 1,
|
||||
'total_trades': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 0.01,
|
||||
'duration': 20.0,
|
||||
'wins': 1,
|
||||
'draws': 0,
|
||||
'losses': 0,
|
||||
'profit_mean': 0.01,
|
||||
'profit_total_abs': 0.001,
|
||||
'profit_total': 0.01,
|
||||
'holding_avg': timedelta(minutes=20),
|
||||
'max_drawdown': 0.001,
|
||||
'max_drawdown_abs': 0.001,
|
||||
'loss': 0.001,
|
||||
'is_initial_point': 0.001,
|
||||
'is_random': False,
|
||||
'is_best': 1,
|
||||
}
|
||||
|
||||
|
||||
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
|
||||
@@ -145,7 +145,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None
|
||||
args = [
|
||||
'hyperopt',
|
||||
'--config', 'config.json',
|
||||
'--strategy', 'StrategyTestV2',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--stake-amount', '1',
|
||||
'--starting-balance', '0.5'
|
||||
]
|
||||
@@ -331,8 +331,8 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
||||
# Should be called for historical candle data
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hasattr(hyperopt, "max_open_trades")
|
||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
||||
assert hasattr(hyperopt, "position_stacking")
|
||||
@@ -357,9 +357,10 @@ def test_hyperopt_format_results(hyperopt):
|
||||
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"is_open": [False, False, False, True],
|
||||
"is_short": [False, False, False, False],
|
||||
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||
SellType.ROI, SellType.FORCE_SELL]
|
||||
"exit_reason": [ExitType.ROI, ExitType.STOP_LOSS,
|
||||
ExitType.ROI, ExitType.FORCE_EXIT]
|
||||
}),
|
||||
'config': hyperopt.config,
|
||||
'locks': [],
|
||||
@@ -427,9 +428,10 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
|
||||
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"is_open": [False, False, False, True],
|
||||
"is_short": [False, False, False, False],
|
||||
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||
SellType.ROI, SellType.FORCE_SELL]
|
||||
"exit_reason": [ExitType.ROI, ExitType.STOP_LOSS,
|
||||
ExitType.ROI, ExitType.FORCE_EXIT]
|
||||
}),
|
||||
'config': hyperopt_conf,
|
||||
'locks': [],
|
||||
@@ -688,8 +690,8 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hasattr(hyperopt, "max_open_trades")
|
||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
||||
assert hasattr(hyperopt, "position_stacking")
|
||||
@@ -761,8 +763,8 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
||||
assert dumper.called
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hasattr(hyperopt, "max_open_trades")
|
||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
||||
assert hasattr(hyperopt, "position_stacking")
|
||||
@@ -803,8 +805,8 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||
assert dumper.called
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hasattr(hyperopt, "max_open_trades")
|
||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
||||
assert hasattr(hyperopt, "position_stacking")
|
||||
@@ -851,6 +853,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
||||
'spaces': ['all']
|
||||
})
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import rapidjson
|
||||
from freqtrade.constants import FTHYPT_FILEVERSION
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer
|
||||
from tests.conftest import log_has, log_has_re
|
||||
from tests.conftest import CURRENT_TEST_STRATEGY, log_has, log_has_re
|
||||
|
||||
|
||||
# Functions for recurrent object patching
|
||||
@@ -168,9 +168,9 @@ def test__pprint_dict():
|
||||
|
||||
def test_get_strategy_filename(default_conf):
|
||||
|
||||
x = HyperoptTools.get_strategy_filename(default_conf, 'StrategyTestV2')
|
||||
x = HyperoptTools.get_strategy_filename(default_conf, 'StrategyTestV3')
|
||||
assert isinstance(x, Path)
|
||||
assert x == Path(__file__).parents[1] / 'strategy/strats/strategy_test_v2.py'
|
||||
assert x == Path(__file__).parents[1] / 'strategy/strats/strategy_test_v3.py'
|
||||
|
||||
x = HyperoptTools.get_strategy_filename(default_conf, 'NonExistingStrategy')
|
||||
assert x is None
|
||||
@@ -178,7 +178,7 @@ def test_get_strategy_filename(default_conf):
|
||||
|
||||
def test_export_params(tmpdir):
|
||||
|
||||
filename = Path(tmpdir) / "StrategyTestV2.json"
|
||||
filename = Path(tmpdir) / f"{CURRENT_TEST_STRATEGY}.json"
|
||||
assert not filename.is_file()
|
||||
params = {
|
||||
"params_details": {
|
||||
@@ -206,13 +206,13 @@ def test_export_params(tmpdir):
|
||||
}
|
||||
|
||||
}
|
||||
HyperoptTools.export_params(params, "StrategyTestV2", filename)
|
||||
HyperoptTools.export_params(params, CURRENT_TEST_STRATEGY, filename)
|
||||
|
||||
assert filename.is_file()
|
||||
|
||||
with filename.open('r') as f:
|
||||
content = rapidjson.load(f)
|
||||
assert content['strategy_name'] == 'StrategyTestV2'
|
||||
assert content['strategy_name'] == CURRENT_TEST_STRATEGY
|
||||
assert 'params' in content
|
||||
assert "buy" in content["params"]
|
||||
assert "sell" in content["params"]
|
||||
@@ -225,7 +225,7 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker):
|
||||
default_conf['disableparamexport'] = False
|
||||
export_mock = mocker.patch("freqtrade.optimize.hyperopt_tools.HyperoptTools.export_params")
|
||||
|
||||
filename = Path(tmpdir) / "StrategyTestV2.json"
|
||||
filename = Path(tmpdir) / f"{CURRENT_TEST_STRATEGY}.json"
|
||||
assert not filename.is_file()
|
||||
params = {
|
||||
"params_details": {
|
||||
@@ -254,17 +254,17 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker):
|
||||
FTHYPT_FILEVERSION: 2,
|
||||
|
||||
}
|
||||
HyperoptTools.try_export_params(default_conf, "StrategyTestV222", params)
|
||||
HyperoptTools.try_export_params(default_conf, "StrategyTestVXXX", params)
|
||||
|
||||
assert log_has("Strategy not found, not exporting parameter file.", caplog)
|
||||
assert export_mock.call_count == 0
|
||||
caplog.clear()
|
||||
|
||||
HyperoptTools.try_export_params(default_conf, "StrategyTestV2", params)
|
||||
HyperoptTools.try_export_params(default_conf, CURRENT_TEST_STRATEGY, params)
|
||||
|
||||
assert export_mock.call_count == 1
|
||||
assert export_mock.call_args_list[0][0][1] == 'StrategyTestV2'
|
||||
assert export_mock.call_args_list[0][0][2].name == 'strategy_test_v2.json'
|
||||
assert export_mock.call_args_list[0][0][1] == CURRENT_TEST_STRATEGY
|
||||
assert export_mock.call_args_list[0][0][2].name == 'strategy_test_v3.json'
|
||||
|
||||
|
||||
def test_params_print(capsys):
|
||||
|
@@ -12,17 +12,17 @@ from freqtrade.data import history
|
||||
from freqtrade.data.btanalysis import (get_latest_backtest_filename, load_backtest_data,
|
||||
load_backtest_stats)
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.enums import SellType
|
||||
from freqtrade.enums import ExitType
|
||||
from freqtrade.optimize.optimize_reports import (_get_resample_from_period, generate_backtest_stats,
|
||||
generate_daily_stats, generate_edge_table,
|
||||
generate_pair_metrics,
|
||||
generate_exit_reason_stats, generate_pair_metrics,
|
||||
generate_periodic_breakdown_stats,
|
||||
generate_sell_reason_stats,
|
||||
generate_strategy_comparison,
|
||||
generate_trading_stats, show_sorted_pairlist,
|
||||
store_backtest_stats, text_table_bt_results,
|
||||
text_table_sell_reason, text_table_strategy)
|
||||
text_table_exit_reason, text_table_strategy)
|
||||
from freqtrade.resolvers.strategy_resolver import StrategyResolver
|
||||
from tests.conftest import CURRENT_TEST_STRATEGY
|
||||
from tests.data.test_history import _backup_file, _clean_test_file
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ def test_text_table_bt_results():
|
||||
|
||||
|
||||
def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
||||
default_conf.update({'strategy': 'StrategyTestV2'})
|
||||
default_conf.update({'strategy': CURRENT_TEST_STRATEGY})
|
||||
StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
results = {'DefStrat': {
|
||||
@@ -74,9 +74,10 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
||||
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"is_open": [False, False, False, True],
|
||||
"is_short": [False, False, False, False],
|
||||
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||
SellType.ROI, SellType.FORCE_SELL]
|
||||
"exit_reason": [ExitType.ROI, ExitType.STOP_LOSS,
|
||||
ExitType.ROI, ExitType.FORCE_EXIT]
|
||||
}),
|
||||
'config': default_conf,
|
||||
'locks': [],
|
||||
@@ -125,9 +126,10 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
||||
"close_rate": [0.002546, 0.003014, 0.0032903, 0.003217],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"is_open": [False, False, False, True],
|
||||
"is_short": [False, False, False, False],
|
||||
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||
"sell_reason": [SellType.ROI, SellType.ROI,
|
||||
SellType.STOP_LOSS, SellType.FORCE_SELL]
|
||||
"exit_reason": [ExitType.ROI, ExitType.ROI,
|
||||
ExitType.STOP_LOSS, ExitType.FORCE_EXIT]
|
||||
}),
|
||||
'config': default_conf,
|
||||
'locks': [],
|
||||
@@ -262,7 +264,7 @@ def test_generate_trading_stats(testdatadir):
|
||||
assert res['losses'] == 0
|
||||
|
||||
|
||||
def test_text_table_sell_reason():
|
||||
def test_text_table_exit_reason():
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
@@ -273,12 +275,12 @@ def test_text_table_sell_reason():
|
||||
'wins': [2, 0, 0],
|
||||
'draws': [0, 0, 0],
|
||||
'losses': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % |'
|
||||
'| Exit Reason | Exits | Win Draws Loss Win% | Avg Profit % | Cum Profit % |'
|
||||
' Tot Profit BTC | Tot Profit % |\n'
|
||||
'|---------------+---------+--------------------------+----------------+----------------+'
|
||||
'------------------+----------------|\n'
|
||||
@@ -288,9 +290,9 @@ def test_text_table_sell_reason():
|
||||
' -0.2 | -5 |'
|
||||
)
|
||||
|
||||
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
|
||||
exit_reason_stats = generate_exit_reason_stats(max_open_trades=2,
|
||||
results=results)
|
||||
assert text_table_sell_reason(sell_reason_stats=sell_reason_stats,
|
||||
assert text_table_exit_reason(exit_reason_stats=exit_reason_stats,
|
||||
stake_currency='BTC') == result_str
|
||||
|
||||
|
||||
@@ -305,23 +307,23 @@ def test_generate_sell_reason_stats():
|
||||
'wins': [2, 0, 0],
|
||||
'draws': [0, 0, 0],
|
||||
'losses': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI.value, SellType.ROI.value, SellType.STOP_LOSS.value]
|
||||
'exit_reason': [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value]
|
||||
}
|
||||
)
|
||||
|
||||
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
|
||||
exit_reason_stats = generate_exit_reason_stats(max_open_trades=2,
|
||||
results=results)
|
||||
roi_result = sell_reason_stats[0]
|
||||
assert roi_result['sell_reason'] == 'roi'
|
||||
roi_result = exit_reason_stats[0]
|
||||
assert roi_result['exit_reason'] == 'roi'
|
||||
assert roi_result['trades'] == 2
|
||||
assert pytest.approx(roi_result['profit_mean']) == 0.15
|
||||
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
|
||||
assert pytest.approx(roi_result['profit_mean']) == 0.15
|
||||
assert roi_result['profit_mean_pct'] == round(roi_result['profit_mean'] * 100, 2)
|
||||
|
||||
stop_result = sell_reason_stats[1]
|
||||
stop_result = exit_reason_stats[1]
|
||||
|
||||
assert stop_result['sell_reason'] == 'stop_loss'
|
||||
assert stop_result['exit_reason'] == 'stop_loss'
|
||||
assert stop_result['trades'] == 1
|
||||
assert pytest.approx(stop_result['profit_mean']) == -0.1
|
||||
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
|
||||
@@ -397,6 +399,6 @@ def test_show_sorted_pairlist(testdatadir, default_conf, capsys):
|
||||
show_sorted_pairlist(default_conf, bt_data)
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
assert 'Pairs for Strategy StrategyTestV2: \n[' in out
|
||||
assert 'Pairs for Strategy StrategyTestV3: \n[' in out
|
||||
assert 'TOTAL' not in out
|
||||
assert '"ETH/BTC", // ' in out
|
||||
|
Reference in New Issue
Block a user