Merge pull request #1018 from freqtrade/feat/sell_reason
Record sell reason
This commit is contained in:
@@ -17,6 +17,7 @@ from freqtrade.arguments import Arguments, TimeRange
|
||||
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
||||
start)
|
||||
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||
from freqtrade.strategy.interface import SellType
|
||||
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||
|
||||
|
||||
@@ -406,6 +407,35 @@ def test_generate_text_table(default_conf, mocker):
|
||||
assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str
|
||||
|
||||
|
||||
def test_generate_text_table_sell_reason(default_conf, mocker):
|
||||
"""
|
||||
Test Backtesting.generate_text_table_sell_reason() method
|
||||
"""
|
||||
patch_exchange(mocker)
|
||||
backtesting = Backtesting(default_conf)
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
|
||||
'profit_percent': [0.1, 0.2, 0.3],
|
||||
'profit_abs': [0.2, 0.4, 0.5],
|
||||
'trade_duration': [10, 30, 10],
|
||||
'profit': [2, 0, 0],
|
||||
'loss': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
)
|
||||
|
||||
result_str = (
|
||||
'| Sell Reason | Count |\n'
|
||||
'|:--------------|--------:|\n'
|
||||
'| roi | 2 |\n'
|
||||
'| stop_loss | 1 |'
|
||||
)
|
||||
assert backtesting._generate_text_table_sell_reason(
|
||||
data={'ETH/BTC': {}}, results=results) == result_str
|
||||
|
||||
|
||||
def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
||||
"""
|
||||
Test Backtesting.start() method
|
||||
@@ -514,7 +544,9 @@ def test_backtest(default_conf, fee, mocker) -> None:
|
||||
'trade_duration': [240, 50],
|
||||
'open_at_end': [False, False],
|
||||
'open_rate': [0.104445, 0.10302485],
|
||||
'close_rate': [0.105, 0.10359999]})
|
||||
'close_rate': [0.105, 0.10359999],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI]
|
||||
})
|
||||
pd.testing.assert_frame_equal(results, expected)
|
||||
data_pair = data_processed[pair]
|
||||
for _, t in results.iterrows():
|
||||
@@ -660,7 +692,9 @@ def test_backtest_record(default_conf, fee, mocker):
|
||||
"open_index": [1, 119, 153, 185],
|
||||
"close_index": [118, 151, 184, 199],
|
||||
"trade_duration": [123, 34, 31, 14],
|
||||
"open_at_end": [False, False, False, True]
|
||||
"open_at_end": [False, False, False, True],
|
||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||
SellType.ROI, SellType.FORCE_SELL]
|
||||
})
|
||||
backtesting._store_backtest_result("backtest-result.json", results)
|
||||
assert len(results) == 4
|
||||
@@ -673,7 +707,7 @@ def test_backtest_record(default_conf, fee, mocker):
|
||||
# Below follows just a typecheck of the schema/type of trade-records
|
||||
oix = None
|
||||
for (pair, profit, date_buy, date_sell, buy_index, dur,
|
||||
openr, closer, open_at_end) in records:
|
||||
openr, closer, open_at_end, sell_reason) in records:
|
||||
assert pair == 'UNITTEST/BTC'
|
||||
assert isinstance(profit, float)
|
||||
# FIX: buy/sell should be converted to ints
|
||||
@@ -682,6 +716,7 @@ def test_backtest_record(default_conf, fee, mocker):
|
||||
assert isinstance(openr, float)
|
||||
assert isinstance(closer, float)
|
||||
assert isinstance(open_at_end, bool)
|
||||
assert isinstance(sell_reason, str)
|
||||
isinstance(buy_index, pd._libs.tslib.Timestamp)
|
||||
if oix:
|
||||
assert buy_index > oix
|
||||
|
@@ -20,6 +20,7 @@ from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc import RPCMessageType
|
||||
from freqtrade.state import State
|
||||
from freqtrade.strategy.interface import SellType, SellCheckTuple
|
||||
from freqtrade.tests.conftest import log_has, patch_coinmarketcap, patch_exchange
|
||||
|
||||
|
||||
@@ -1369,7 +1370,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
|
||||
get_ticker=ticker_sell_up
|
||||
)
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'])
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
@@ -1421,7 +1422,8 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
|
||||
get_ticker=ticker_sell_down
|
||||
)
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'])
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
|
||||
sell_reason=SellType.STOP_LOSS)
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
@@ -1474,7 +1476,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
||||
)
|
||||
freqtrade.config = {}
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'])
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
@@ -1524,7 +1526,8 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
||||
)
|
||||
|
||||
freqtrade.config = {}
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'])
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
|
||||
sell_reason=SellType.STOP_LOSS)
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
@@ -1577,6 +1580,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||
|
||||
|
||||
def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
||||
@@ -1612,6 +1616,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||
|
||||
|
||||
def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, markets, mocker) -> None:
|
||||
@@ -1640,7 +1645,8 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.stop_loss_reached = \
|
||||
lambda current_rate, trade, current_time, current_profit: False
|
||||
lambda current_rate, trade, current_time, current_profit: SellCheckTuple(
|
||||
sell_flag=False, sell_type=SellType.NONE)
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
@@ -1684,6 +1690,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||
|
||||
|
||||
def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, mocker) -> None:
|
||||
@@ -1724,6 +1731,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m
|
||||
# Test if buy-signal is absent (should sell due to roi = true)
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
assert trade.sell_reason == SellType.ROI.value
|
||||
|
||||
|
||||
def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, mocker) -> None:
|
||||
@@ -1762,6 +1770,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog,
|
||||
assert log_has(
|
||||
f'HIT STOP: current price at 0.000001, stop loss is {trade.stop_loss:.6f}, '
|
||||
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples)
|
||||
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
||||
|
||||
|
||||
def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets,
|
||||
@@ -1825,6 +1834,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
|
||||
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
|
||||
f'stop loss is {trade.stop_loss:.6f}, '
|
||||
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples)
|
||||
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
||||
|
||||
|
||||
def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
||||
@@ -1867,6 +1877,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
||||
# Test if buy-signal is absent
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
assert trade.sell_reason == SellType.STOP_LOSS.value
|
||||
|
||||
|
||||
def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
|
||||
|
@@ -465,6 +465,9 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
||||
assert trade.max_rate == 0.0
|
||||
assert trade.stop_loss == 0.0
|
||||
assert trade.initial_stop_loss == 0.0
|
||||
assert trade.sell_reason is None
|
||||
assert trade.strategy is None
|
||||
assert trade.ticker_interval is None
|
||||
assert log_has("trying trades_bak1", caplog.record_tuples)
|
||||
assert log_has("trying trades_bak2", caplog.record_tuples)
|
||||
|
||||
|
Reference in New Issue
Block a user