@@ -9,14 +9,18 @@ from collections import defaultdict
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from numpy import nan
|
from numpy import isnan, nan
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame, Series
|
||||||
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
from freqtrade.configuration import TimeRange, validate_config_consistency
|
from freqtrade.configuration import TimeRange, validate_config_consistency
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config, IntOrInf, LongShort
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config, IntOrInf, LongShort
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe
|
from freqtrade.data.btanalysis import (
|
||||||
|
find_existing_backtest_stats,
|
||||||
|
get_significant_digits_over_time,
|
||||||
|
trade_list_to_dataframe,
|
||||||
|
)
|
||||||
from freqtrade.data.converter import trim_dataframe, trim_dataframes
|
from freqtrade.data.converter import trim_dataframe, trim_dataframes
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.data.metrics import combined_dataframes_with_rel_mean
|
from freqtrade.data.metrics import combined_dataframes_with_rel_mean
|
||||||
@@ -35,7 +39,7 @@ from freqtrade.exchange import (
|
|||||||
price_to_precision,
|
price_to_precision,
|
||||||
timeframe_to_seconds,
|
timeframe_to_seconds,
|
||||||
)
|
)
|
||||||
from freqtrade.exchange.exchange import Exchange
|
from freqtrade.exchange.exchange import TICK_SIZE, Exchange
|
||||||
from freqtrade.ft_types import (
|
from freqtrade.ft_types import (
|
||||||
BacktestContentType,
|
BacktestContentType,
|
||||||
BacktestContentTypeIcomplete,
|
BacktestContentTypeIcomplete,
|
||||||
@@ -121,6 +125,7 @@ class Backtesting:
|
|||||||
self.order_id_counter: int = 0
|
self.order_id_counter: int = 0
|
||||||
|
|
||||||
config["dry_run"] = True
|
config["dry_run"] = True
|
||||||
|
self.price_pair_prec: dict[str, Series] = {}
|
||||||
self.run_ids: dict[str, str] = {}
|
self.run_ids: dict[str, str] = {}
|
||||||
self.strategylist: list[IStrategy] = []
|
self.strategylist: list[IStrategy] = []
|
||||||
self.all_bt_content: dict[str, BacktestContentType] = {}
|
self.all_bt_content: dict[str, BacktestContentType] = {}
|
||||||
@@ -189,7 +194,6 @@ class Backtesting:
|
|||||||
self.fee = max(fee for fee in fees if fee is not None)
|
self.fee = max(fee for fee in fees if fee is not None)
|
||||||
logger.info(f"Using fee {self.fee:.4%} - worst case fee from exchange (lowest tier).")
|
logger.info(f"Using fee {self.fee:.4%} - worst case fee from exchange (lowest tier).")
|
||||||
self.precision_mode = self.exchange.precisionMode
|
self.precision_mode = self.exchange.precisionMode
|
||||||
self.precision_mode_price = self.exchange.precision_mode_price
|
|
||||||
|
|
||||||
if self.config.get("freqai_backtest_live_models", False):
|
if self.config.get("freqai_backtest_live_models", False):
|
||||||
from freqtrade.freqai.utils import get_timerange_backtest_live_models
|
from freqtrade.freqai.utils import get_timerange_backtest_live_models
|
||||||
@@ -316,6 +320,9 @@ class Backtesting:
|
|||||||
|
|
||||||
self.progress.set_new_value(1)
|
self.progress.set_new_value(1)
|
||||||
self._load_bt_data_detail()
|
self._load_bt_data_detail()
|
||||||
|
self.price_pair_prec = {}
|
||||||
|
for pair in self.pairlists.whitelist:
|
||||||
|
self.price_pair_prec[pair] = get_significant_digits_over_time(data[pair])
|
||||||
return data, self.timerange
|
return data, self.timerange
|
||||||
|
|
||||||
def _load_bt_data_detail(self) -> None:
|
def _load_bt_data_detail(self) -> None:
|
||||||
@@ -385,6 +392,22 @@ class Backtesting:
|
|||||||
else:
|
else:
|
||||||
self.futures_data = {}
|
self.futures_data = {}
|
||||||
|
|
||||||
|
def get_pair_precision(self, pair: str, current_time: datetime) -> tuple[float, int]:
|
||||||
|
"""
|
||||||
|
Get pair precision at that moment in time
|
||||||
|
:param pair: Pair to get precision for
|
||||||
|
:param current_time: Time to get precision for
|
||||||
|
:return: tuple of price precision, precision_mode_price for the pair at that given time.
|
||||||
|
"""
|
||||||
|
precision_series = self.price_pair_prec.get(pair)
|
||||||
|
if precision_series is not None:
|
||||||
|
precision = precision_series.asof(current_time)
|
||||||
|
|
||||||
|
if not isnan(precision):
|
||||||
|
# Force tick size if we define the precision
|
||||||
|
return precision, TICK_SIZE
|
||||||
|
return self.exchange.get_precision_price(pair), self.exchange.precision_mode_price
|
||||||
|
|
||||||
def disable_database_use(self):
|
def disable_database_use(self):
|
||||||
disable_database_use(self.timeframe)
|
disable_database_use(self.timeframe)
|
||||||
|
|
||||||
@@ -793,7 +816,7 @@ class Backtesting:
|
|||||||
)
|
)
|
||||||
if rate is not None and rate != close_rate:
|
if rate is not None and rate != close_rate:
|
||||||
close_rate = price_to_precision(
|
close_rate = price_to_precision(
|
||||||
rate, trade.price_precision, self.precision_mode_price
|
rate, trade.price_precision, trade.precision_mode_price
|
||||||
)
|
)
|
||||||
# We can't place orders lower than current low.
|
# We can't place orders lower than current low.
|
||||||
# freqtrade does not support this in live, and the order would fill immediately
|
# freqtrade does not support this in live, and the order would fill immediately
|
||||||
@@ -926,6 +949,7 @@ class Backtesting:
|
|||||||
trade: LocalTrade | None,
|
trade: LocalTrade | None,
|
||||||
order_type: str,
|
order_type: str,
|
||||||
price_precision: float | None,
|
price_precision: float | None,
|
||||||
|
precision_mode_price: int,
|
||||||
) -> tuple[float, float, float, float]:
|
) -> tuple[float, float, float, float]:
|
||||||
if order_type == "limit":
|
if order_type == "limit":
|
||||||
new_rate = strategy_safe_wrapper(
|
new_rate = strategy_safe_wrapper(
|
||||||
@@ -941,9 +965,7 @@ class Backtesting:
|
|||||||
# We can't place orders higher than current high (otherwise it'd be a stop limit entry)
|
# We can't place orders higher than current high (otherwise it'd be a stop limit entry)
|
||||||
# which freqtrade does not support in live.
|
# which freqtrade does not support in live.
|
||||||
if new_rate is not None and new_rate != propose_rate:
|
if new_rate is not None and new_rate != propose_rate:
|
||||||
propose_rate = price_to_precision(
|
propose_rate = price_to_precision(new_rate, price_precision, precision_mode_price)
|
||||||
new_rate, price_precision, self.precision_mode_price
|
|
||||||
)
|
|
||||||
if direction == "short":
|
if direction == "short":
|
||||||
propose_rate = max(propose_rate, row[LOW_IDX])
|
propose_rate = max(propose_rate, row[LOW_IDX])
|
||||||
else:
|
else:
|
||||||
@@ -1036,7 +1058,7 @@ class Backtesting:
|
|||||||
pos_adjust = trade is not None and requested_rate is None
|
pos_adjust = trade is not None and requested_rate is None
|
||||||
|
|
||||||
stake_amount_ = stake_amount or (trade.stake_amount if trade else 0.0)
|
stake_amount_ = stake_amount or (trade.stake_amount if trade else 0.0)
|
||||||
precision_price = self.exchange.get_precision_price(pair)
|
precision_price, precision_mode_price = self.get_pair_precision(pair, current_time)
|
||||||
|
|
||||||
propose_rate, stake_amount, leverage, min_stake_amount = self.get_valid_price_and_stake(
|
propose_rate, stake_amount, leverage, min_stake_amount = self.get_valid_price_and_stake(
|
||||||
pair,
|
pair,
|
||||||
@@ -1049,6 +1071,7 @@ class Backtesting:
|
|||||||
trade,
|
trade,
|
||||||
order_type,
|
order_type,
|
||||||
precision_price,
|
precision_price,
|
||||||
|
precision_mode_price,
|
||||||
)
|
)
|
||||||
|
|
||||||
# replace proposed rate if another rate was requested
|
# replace proposed rate if another rate was requested
|
||||||
@@ -1124,7 +1147,7 @@ class Backtesting:
|
|||||||
amount_precision=precision_amount,
|
amount_precision=precision_amount,
|
||||||
price_precision=precision_price,
|
price_precision=precision_price,
|
||||||
precision_mode=self.precision_mode,
|
precision_mode=self.precision_mode,
|
||||||
precision_mode_price=self.precision_mode_price,
|
precision_mode_price=precision_mode_price,
|
||||||
contract_size=contract_size,
|
contract_size=contract_size,
|
||||||
orders=[],
|
orders=[],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user