Initial backtesting support. This does make it rather slow.

This commit is contained in:
Reigo Reinmets 2021-12-09 23:21:35 +02:00
parent 00366c5c88
commit b2c2852f86
2 changed files with 76 additions and 5 deletions

View File

@ -24,7 +24,7 @@ from freqtrade.mixins import LoggingMixin
from freqtrade.optimize.bt_progress import BTProgress
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results,
store_backtest_stats)
from freqtrade.persistence import LocalTrade, PairLocks, Trade
from freqtrade.persistence import LocalTrade, PairLocks, Trade, Order
from freqtrade.plugins.pairlistmanager import PairListManager
from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@ -350,8 +350,64 @@ class Backtesting:
else:
return sell_row[OPEN_IDX]
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:
current_rate = sell_row[OPEN_IDX]
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
current_profit = trade.calc_profit_ratio(current_rate)
amount_to_adjust = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)(
pair=trade.pair, trade=trade, current_time=sell_candle_time,
current_rate=current_rate, current_profit=current_profit)
# Check if we should increase our position
if amount_to_adjust is not None and amount_to_adjust > 0.0:
return self._execute_trade_position_change(trade, sell_row, amount_to_adjust)
return trade
def _execute_trade_position_change(self, trade: LocalTrade, row: Tuple,
amount_to_adjust: float) -> Optional[LocalTrade]:
current_price = row[OPEN_IDX]
stake_amount = current_price * amount_to_adjust
propose_rate = min(max(current_price, row[LOW_IDX]), row[HIGH_IDX])
available_amount = self.wallets.get_available_stake_amount()
try:
min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair, propose_rate, -0.05) or 0
stake_amount = self.wallets.validate_stake_amount(trade.pair, stake_amount, min_stake_amount)
stake_amount = self.wallets._check_available_stake_amount(stake_amount, available_amount)
except DependencyException:
logger.debug(f"{trade.pair} adjustment failed, wallet is smaller than asked stake {stake_amount}")
return trade
amount = stake_amount / current_price
if amount <= 0:
logger.debug(f"{trade.pair} adjustment failed, amount ended up being zero {amount}")
return trade
order = Order(
ft_is_open=False,
ft_pair=trade.pair,
symbol=trade.pair,
ft_order_side="buy",
side="buy",
order_type="market",
status="closed",
price=propose_rate,
average=propose_rate,
amount=amount,
cost=stake_amount
)
trade.orders.append(order)
trade.recalc_trade_from_orders()
self.wallets.update();
return trade
def _get_sell_trade_entry_for_candle(self, trade: LocalTrade,
sell_row: Tuple) -> Optional[LocalTrade]:
trade = self._get_adjust_trade_entry_for_candle(trade, sell_row)
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX], # type: ignore
sell_candle_time, sell_row[BUY_IDX],
@ -476,6 +532,21 @@ class Backtesting:
buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None,
exchange='backtesting',
)
order = Order(
ft_is_open=False,
ft_pair=trade.pair,
symbol=trade.pair,
ft_order_side="buy",
side="buy",
order_type="market",
status="closed",
price=trade.open_rate,
average=trade.open_rate,
amount=trade.amount,
cost=trade.stake_amount + trade.fee_open
)
trade.orders = []
trade.orders.append(order)
return trade
return None

View File

@ -1475,8 +1475,8 @@ def test_recalc_trade_from_orders(fee):
assert trade.amount == o1_amount + o2_amount + o3_amount
assert trade.stake_amount == o1_cost + o2_cost + o3_cost
assert trade.open_rate == avg_price
assert round(trade.fee_open_cost, 8) == round(o1_fee_cost + o2_fee_cost + o3_fee_cost, 8)
assert round(trade.open_trade_value, 8) == round(o1_trade_val + o2_trade_val + o3_trade_val, 8)
assert pytest.approx(trade.fee_open_cost) == o1_fee_cost + o2_fee_cost + o3_fee_cost
assert pytest.approx(trade.open_trade_value) == o1_trade_val + o2_trade_val + o3_trade_val
# Just to make sure sell orders are ignored, let's calculate one more time.
sell1 = Order(
@ -1503,8 +1503,8 @@ def test_recalc_trade_from_orders(fee):
assert trade.amount == o1_amount + o2_amount + o3_amount
assert trade.stake_amount == o1_cost + o2_cost + o3_cost
assert trade.open_rate == avg_price
assert round(trade.fee_open_cost, 8) == round(o1_fee_cost + o2_fee_cost + o3_fee_cost, 8)
assert round(trade.open_trade_value, 8) == round(o1_trade_val + o2_trade_val + o3_trade_val, 8)
assert pytest.approx(trade.fee_open_cost) == o1_fee_cost + o2_fee_cost + o3_fee_cost
assert pytest.approx(trade.open_trade_value) == o1_trade_val + o2_trade_val + o3_trade_val
def test_recalc_trade_from_orders_ignores_bad_orders(fee):