排查回测挣钱,干跑赔钱的问题, 对两边交易结果进行逐条校验

This commit is contained in:
zhangkun9038@dingtalk.com 2025-05-13 01:59:59 +00:00
parent de6c47c3f8
commit c712c242a7

View File

@ -1,27 +1,27 @@
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import numpy as np # noqa
import pandas as pd # noqa
import numpy as np
import pandas as pd
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class TheForceV7(IStrategy):
INTERFACE_VERSION = 3 # 升级到 V3 接口
INTERFACE_VERSION = 3
minimal_roi = {
"0": 10
"0": 10
}
stoploss = -0.1
trailing_stop = False
timeframe = '5m'
process_only_new_candles = True
use_exit_signal = True # 替换 use_sell_signal
ignore_roi_if_entry_signal = True # 替换 ignore_roi_if_buy_signal
use_exit_signal = True
ignore_roi_if_entry_signal = True
startup_candle_count: int = 30
order_types = {
'entry': 'limit', # 替换 buy
'exit': 'limit', # 替换 sell
'entry': 'limit',
'exit': 'limit',
'stoploss': 'market',
'stoploss_on_exchange': False
}
@ -46,10 +46,17 @@ class TheForceV7(IStrategy):
}
}
def __init__(self, config: dict) -> None:
super().__init__(config)
self.trade_data = {} # 用于缓存 K 线数据以计算 min_rate 和 max_rate
def informative_pairs(self):
return []
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 缓存 K 线数据
self.trade_data[metadata['pair']] = dataframe
stoch = ta.STOCH(dataframe)
dataframe['slowd'] = stoch['slowd']
dataframe['slowk'] = stoch['slowk']
@ -79,32 +86,32 @@ class TheForceV7(IStrategy):
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(
(
(
(dataframe['slowk'] >= 20) & (dataframe['slowk'] <= 80)
&
(dataframe['slowd'] >= 20) & (dataframe['slowd'] <= 80)
)
|
(
(
(dataframe['slowk'] < 30) & (dataframe['slowd'] < 30) &
(qtpylib.crossed_above(dataframe['slowk'], dataframe['slowd']))
)
)
&
(
(
(dataframe['macd'] > dataframe['macd'].shift(1))
&
(dataframe['macdsignal'] > dataframe['macdsignal'].shift(1))
)
&
(
(
(dataframe['close'] > dataframe['close'].shift(1))
&
(dataframe['open'] > dataframe['open'].shift(1))
(dataframe['open'] > dataframe['open'].shift(1))
)
&
(
(
(dataframe['ema5c'] >= dataframe['ema5o'])
|
(dataframe['open'] < dataframe['ema5l'])
@ -114,7 +121,7 @@ class TheForceV7(IStrategy):
(dataframe['volume'] > dataframe['volvar'])
)
|
(
(
(dataframe['slowk'] >= 20) & (dataframe['slowk'] <= 80)
&
(dataframe['slowd'] >= 20) & (dataframe['slowd'] <= 80)
@ -126,8 +133,8 @@ class TheForceV7(IStrategy):
)
)
|
(
(dataframe['close'] > dataframe['ema200c'])
(
(dataframe['close'] > dataframe['ema200c'])
&
(dataframe['rsi7'] < 35)
)
@ -138,37 +145,37 @@ class TheForceV7(IStrategy):
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(
(
(
(dataframe['slowk'] <= 80) & (dataframe['slowd'] <= 80)
(dataframe['slowk'] <= 80) & (dataframe['slowd'] <= 80)
)
|
(
(
(qtpylib.crossed_above(dataframe['slowk'], 70))
|
(qtpylib.crossed_above(dataframe['slowd'], 70))
)
)
&
(
(
(dataframe['macd'] < dataframe['macd'].shift(1))
&
(dataframe['macdsignal'] < dataframe['macdsignal'].shift(1))
)
&
(
(
(dataframe['ema5c'] < dataframe['ema5o'])
|
(dataframe['open'] >= dataframe['ema5h'])
(dataframe['open'] >= dataframe['ema5h'])
)
|
(
(
(dataframe['slowk'] <= 80)
&
(dataframe['slowd'] <= 80)
&
(
(
(dataframe['close'] >= dataframe['bb_upperband'])
|
(dataframe['open'] >= dataframe['bb_upperband'])
@ -180,8 +187,40 @@ class TheForceV7(IStrategy):
&
(((dataframe['high'] - dataframe['bb_upperband']) * 100 / dataframe['bb_upperband']) > 1)
)
),
'exit_long'] = 1
return dataframe
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time: str, current_time: 'datetime', **kwargs) -> bool:
log_path = '/freqtrade/user_data/dryrun_results/dry_run_trades.csv'
# 自动创建目录
os.makedirs(os.path.dirname(log_path), exist_ok=True)
# 写入日志
with open(log_path, 'a') as f:
f.write(f"{pair},{current_time},,,{rate},,{amount},,,,,,,,\n")
return True
def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: float,
rate: float, time: str, exit_reason: str, current_time: 'datetime', **kwargs) -> bool:
log_path = '/freqtrade/user_data/dryrun_results/dry_run_trades.csv'
# 自动创建目录
os.makedirs(os.path.dirname(log_path), exist_ok=True)
# 计算盈亏
profit_ratio = trade.calc_profit_ratio(rate)
profit_abs = trade.calc_profit(rate)
trade_duration = (current_time - trade.open_date).total_seconds() / 60
# 获取 min_rate 和 max_rate
df = self.trade_data.get(pair)
min_rate = max_rate = rate
if df is not None:
trade_period = df[(df['date'] >= trade.open_date) & (df['date'] <= current_time)]
min_rate = trade_period['low'].min() if not trade_period.empty else rate
max_rate = trade_period['high'].max() if not trade_period.empty else rate
# 写入日志
with open(log_path, 'a') as f:
f.write(f"{pair},{trade.open_date},{current_time},{trade.open_rate},{rate},"
f"{amount},{profit_ratio},{profit_abs},{exit_reason},"
f"{trade.fee_open},{trade.fee_close},{trade_duration},{min_rate},{max_rate}\n")
return True