Merge branch 'develop' into move_datadownload

This commit is contained in:
Matthias
2022-08-31 10:23:45 +00:00
101 changed files with 3154 additions and 1523 deletions

View File

@@ -23,7 +23,8 @@ from freqtrade.data.dataprovider import DataProvider
from freqtrade.enums import (BacktestState, CandleType, ExitCheckTuple, ExitType, RunMode,
TradingMode)
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.exchange import (amount_to_contract_precision, price_to_precision,
timeframe_to_minutes, timeframe_to_seconds)
from freqtrade.mixins import LoggingMixin
from freqtrade.optimize.backtest_caching import get_strategy_run_id
from freqtrade.optimize.bt_progress import BTProgress
@@ -257,7 +258,7 @@ class Backtesting:
funding_rates_dict = history.load_data(
datadir=self.config['datadir'],
pairs=self.pairlists.whitelist,
timeframe=self.exchange._ft_has['mark_ohlcv_timeframe'],
timeframe=self.exchange.get_option('mark_ohlcv_timeframe'),
timerange=self.timerange,
startup_candles=0,
fail_without_data=True,
@@ -269,12 +270,12 @@ class Backtesting:
mark_rates_dict = history.load_data(
datadir=self.config['datadir'],
pairs=self.pairlists.whitelist,
timeframe=self.exchange._ft_has['mark_ohlcv_timeframe'],
timeframe=self.exchange.get_option('mark_ohlcv_timeframe'),
timerange=self.timerange,
startup_candles=0,
fail_without_data=True,
data_format=self.config.get('dataformat_ohlcv', 'json'),
candle_type=CandleType.from_string(self.exchange._ft_has["mark_ohlcv_price"])
candle_type=CandleType.from_string(self.exchange.get_option("mark_ohlcv_price"))
)
# Combine data to avoid combining the data per trade.
unavailable_pairs = []
@@ -524,12 +525,16 @@ class Backtesting:
# Check if we should increase our position
if stake_amount is not None and stake_amount > 0.0:
pos_trade = self._enter_trade(
trade.pair, row, 'short' if trade.is_short else 'long', stake_amount, trade)
if pos_trade is not None:
self.wallets.update()
return pos_trade
check_adjust_entry = True
if self.strategy.max_entry_position_adjustment > -1:
entry_count = trade.nr_of_successful_entries
check_adjust_entry = (entry_count <= self.strategy.max_entry_position_adjustment)
if check_adjust_entry:
pos_trade = self._enter_trade(
trade.pair, row, 'short' if trade.is_short else 'long', stake_amount, trade)
if pos_trade is not None:
self.wallets.update()
return pos_trade
if stake_amount is not None and stake_amount < 0.0:
amount = abs(stake_amount) / current_rate
@@ -540,7 +545,8 @@ class Backtesting:
if remaining < min_stake:
# Remaining stake is too low to be sold.
return trade
pos_trade = self._exit_trade(trade, row, current_rate, amount)
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT)
pos_trade = self._get_exit_for_signal(trade, row, exit_, amount)
if pos_trade is not None:
order = pos_trade.orders[-1]
if self._get_order_filled(order.price, row):
@@ -560,12 +566,7 @@ class Backtesting:
# Check if we need to adjust our current positions
if self.strategy.position_adjustment_enable:
check_adjust_entry = True
if self.strategy.max_entry_position_adjustment > -1:
entry_count = trade.nr_of_successful_entries
check_adjust_entry = (entry_count <= self.strategy.max_entry_position_adjustment)
if check_adjust_entry:
trade = self._get_adjust_trade_entry_for_candle(trade, row)
trade = self._get_adjust_trade_entry_for_candle(trade, row)
enter = row[SHORT_IDX] if trade.is_short else row[LONG_IDX]
exit_sig = row[ESHORT_IDX] if trade.is_short else row[ELONG_IDX]
@@ -580,14 +581,15 @@ class Backtesting:
return t
return None
def _get_exit_for_signal(self, trade: LocalTrade, row: Tuple,
exit_: ExitCheckTuple) -> Optional[LocalTrade]:
def _get_exit_for_signal(
self, trade: LocalTrade, row: Tuple, exit_: ExitCheckTuple,
amount: Optional[float] = None) -> Optional[LocalTrade]:
exit_candle_time: datetime = row[DATE_IDX].to_pydatetime()
if exit_.exit_flag:
trade.close_date = exit_candle_time
exit_reason = exit_.exit_reason
amount_ = amount if amount is not None else trade.amount
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
try:
close_rate = self._get_close_rate(row, trade, exit_, trade_dur)
@@ -596,7 +598,8 @@ class Backtesting:
# call the custom exit price,with default value as previous close_rate
current_profit = trade.calc_profit_ratio(close_rate)
order_type = self.strategy.order_types['exit']
if exit_.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT):
if exit_.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT,
ExitType.PARTIAL_EXIT):
# Checks and adds an exit tag, after checking that the length of the
# row has the length for an exit tag column
if (
@@ -624,22 +627,23 @@ class Backtesting:
# Confirm trade exit:
time_in_force = self.strategy.order_time_in_force['exit']
if (exit_.exit_type != ExitType.LIQUIDATION and not strategy_safe_wrapper(
self.strategy.confirm_trade_exit, default_retval=True)(
pair=trade.pair,
trade=trade, # type: ignore[arg-type]
order_type=order_type,
amount=trade.amount,
rate=close_rate,
time_in_force=time_in_force,
sell_reason=exit_reason, # deprecated
exit_reason=exit_reason,
current_time=exit_candle_time)):
if (exit_.exit_type not in (ExitType.LIQUIDATION, ExitType.PARTIAL_EXIT)
and not strategy_safe_wrapper(
self.strategy.confirm_trade_exit, default_retval=True)(
pair=trade.pair,
trade=trade, # type: ignore[arg-type]
order_type=order_type,
amount=amount_,
rate=close_rate,
time_in_force=time_in_force,
sell_reason=exit_reason, # deprecated
exit_reason=exit_reason,
current_time=exit_candle_time)):
return None
trade.exit_reason = exit_reason
return self._exit_trade(trade, row, close_rate, trade.amount)
return self._exit_trade(trade, row, close_rate, amount_)
return None
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
@@ -647,7 +651,10 @@ class Backtesting:
self.order_id_counter += 1
exit_candle_time = sell_row[DATE_IDX].to_pydatetime()
order_type = self.strategy.order_types['exit']
amount = amount or trade.amount
# amount = amount or trade.amount
amount = amount_to_contract_precision(amount or trade.amount, trade.amount_precision,
self.precision_mode, trade.contract_size)
rate = price_to_precision(close_rate, trade.price_precision, self.precision_mode)
order = Order(
id=self.order_id_counter,
ft_trade_id=trade.id,
@@ -661,12 +668,12 @@ class Backtesting:
side=trade.exit_side,
order_type=order_type,
status="open",
price=close_rate,
average=close_rate,
price=rate,
average=rate,
amount=amount,
filled=0,
remaining=amount,
cost=amount * close_rate,
cost=amount * rate,
)
trade.orders.append(order)
return trade
@@ -812,7 +819,17 @@ class Backtesting:
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
self.order_id_counter += 1
base_currency = self.exchange.get_pair_base_currency(pair)
amount = round((stake_amount / propose_rate) * leverage, 8)
precision_price = self.exchange.get_precision_price(pair)
propose_rate = price_to_precision(propose_rate, precision_price, self.precision_mode)
amount_p = (stake_amount / propose_rate) * leverage
contract_size = self.exchange.get_contract_size(pair)
precision_amount = self.exchange.get_precision_amount(pair)
amount = amount_to_contract_precision(amount_p, precision_amount, self.precision_mode,
contract_size)
# Backcalculate actual stake amount.
stake_amount = amount * propose_rate / leverage
is_short = (direction == 'short')
# Necessary for Margin trading. Disabled until support is enabled.
# interest_rate = self.exchange.get_interest_rate()
@@ -841,9 +858,10 @@ class Backtesting:
trading_mode=self.trading_mode,
leverage=leverage,
# interest_rate=interest_rate,
amount_precision=self.exchange.get_precision_amount(pair),
price_precision=self.exchange.get_precision_price(pair),
amount_precision=precision_amount,
price_precision=precision_price,
precision_mode=self.precision_mode,
contract_size=contract_size,
orders=[],
)
@@ -853,7 +871,8 @@ class Backtesting:
pair=pair,
open_rate=propose_rate,
amount=amount,
leverage=leverage,
stake_amount=trade.stake_amount,
wallet_balance=trade.stake_amount,
is_short=is_short,
))