V2 与 V3 之间的策略迁移¶
为支持新市场类型和交易类型(即做空交易/杠杆交易),接口层面必须进行一些调整。 如果您计划使用除现货市场以外的其他市场类型,请将您的策略迁移至新格式。
我们已全力保持与现有策略的兼容性,因此若您仅需在__现货市场__中继续使用 freqtrade,目前无需进行任何更改。
您可将快速摘要作为核对清单使用。完整迁移细节请参阅下方详细章节。
快速摘要 / 迁移核对清单¶
注意:forcesell、forcebuy、emergencysell 分别更名为 force_exit、force_enter、emergency_exit。
- 策略方法:
- 数据框列名:
- 交易对象新增以下属性:
is_shortentry_sideexit_sidetrade_direction- 重命名:
sell_reason->exit_reason
- 重命名
trade.nr_of_successful_buys为trade.nr_of_successful_entries(主要与adjust_trade_position()相关) - 引入新的
leverage回调函数 - 信息对现在可以在元组中传递第三个元素,用于定义蜡烛类型
@informative装饰器新增可选参数candle_type- 辅助方法
stoploss_from_open和stoploss_from_absolute新增is_short参数 INTERFACE_VERSION应设置为 3- 策略/配置设置
order_time_in_forcebuy -> entry, sell -> exitorder_typesbuy -> entry, sell -> exitunfilledtimeoutbuy -> entry, sell -> exitignore_buying_expired_candle_after-> 移至根级别而非 "ask_strategy/exit_pricing"
- 术语变更
- 卖出原因更改为反映新的"退出"命名而非卖出。如果策略中使用
exit_reason检查,请谨慎处理并最终更新策略sell_signal->exit_signalcustom_sell->custom_exitforce_sell->force_exitemergency_sell->emergency_exit
- 订单定价
bid_strategy->entry_pricingask_strategy->exit_pricingask_last_balance->price_last_balancebid_last_balance->price_last_balance
- Webhook 术语从"卖出"改为"退出",从"买入"改为"入场"
webhookbuy->entrywebhookbuyfill->entry_fillwebhookbuycancel->entry_cancelwebhooksell->exitwebhooksellfill->exit_fillwebhooksellcancel->exit_cancel
- Telegram 通知设置
buy->entrybuy_fill->entry_fillbuy_cancel->entry_cancelsell->exitsell_fill->exit_fillsell_cancel->exit_cancel
- 策略/配置设置:
use_sell_signal->use_exit_signalsell_profit_only->exit_profit_onlysell_profit_offset->exit_profit_offsetignore_roi_if_buy_signal->ignore_roi_if_entry_signalforcebuy_enable->force_entry_enable
- 卖出原因更改为反映新的"退出"命名而非卖出。如果策略中使用
详细说明¶
populate_buy_trend¶
在 populate_buy_trend() 中,您需要将分配的列从 'buy' 改为 'enter_long',同时将方法名从 populate_buy_trend 改为 populate_entry_trend。
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30
(dataframe['tema'] <= dataframe['bb_middleband']) & # Guard
(dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['buy', 'buy_tag']] = (1, 'rsi_cross')
return dataframe
之后:
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30
(dataframe['tema'] <= dataframe['bb_middleband']) & # Guard
(dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['enter_long', 'enter_tag']] = (1, 'rsi_cross')
return dataframe
请参考策略文档了解如何进入和退出空头交易。
populate_sell_trend¶
与 populate_buy_trend 类似,populate_sell_trend() 将被重命名为 populate_exit_trend()。
我们还将列从 'sell' 改为 'exit_long'。
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70
(dataframe['tema'] > dataframe['bb_middleband']) & # Guard
(dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['sell', 'exit_tag']] = (1, 'some_exit_tag')
return dataframe
之后
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70
(dataframe['tema'] > dataframe['bb_middleband']) & # Guard
(dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard
(dataframe['volume'] > 0) # Make sure Volume is not 0
),
['exit_long', 'exit_tag']] = (1, 'some_exit_tag')
return dataframe
请参考策略文档了解如何进入和退出空头交易。
custom_sell¶
custom_sell 已重命名为 custom_exit。
现在它会在每次迭代中被调用,与当前利润和 exit_profit_only 设置无关。
class AwesomeStrategy(IStrategy):
def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
# ...
class AwesomeStrategy(IStrategy):
def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
# ...
custom_entry_timeout¶
check_buy_timeout() 已重命名为 check_entry_timeout(),check_sell_timeout() 已重命名为 check_exit_timeout()。
class AwesomeStrategy(IStrategy):
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict,
current_time: datetime, **kwargs) -> bool:
return False
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict,
current_time: datetime, **kwargs) -> bool:
return False
class AwesomeStrategy(IStrategy):
def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order',
current_time: datetime, **kwargs) -> bool:
return False
def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order',
current_time: datetime, **kwargs) -> bool:
return False
custom_stake_amount¶
新增字符串参数 side - 可以是 "long" 或 "short"。
class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: Optional[float], max_stake: float,
entry_tag: Optional[str], **kwargs) -> float:
# ...
return proposed_stake
class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float | None, max_stake: float,
entry_tag: str | None, side: str, **kwargs) -> float:
# ...
return proposed_stake
confirm_trade_entry¶
新增字符串参数 side - 可以是 "long" 或 "short"。
class AwesomeStrategy(IStrategy):
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: Optional[str],
**kwargs) -> bool:
return True
之后:
class AwesomeStrategy(IStrategy):
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: str | None,
side: str, **kwargs) -> bool:
return True
confirm_trade_exit¶
将参数 sell_reason 更改为 exit_reason。
为保持兼容性,sell_reason 将在有限时间内继续提供。
class AwesomeStrategy(IStrategy):
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, sell_reason: str,
current_time: datetime, **kwargs) -> bool:
return True
变更后:
class AwesomeStrategy(IStrategy):
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, exit_reason: str,
current_time: datetime, **kwargs) -> bool:
return True
custom_entry_price¶
新增字符串参数 side - 可选值为 "long" 或 "short"。
class AwesomeStrategy(IStrategy):
def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
entry_tag: Optional[str], **kwargs) -> float:
return proposed_rate
变更后:
class AwesomeStrategy(IStrategy):
def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float,
entry_tag: str | None, side: str, **kwargs) -> float:
return proposed_rate
调整交易仓位变更¶
虽然 adjust-trade-position 本身未改变,但您不应再使用 trade.nr_of_successful_buys - 而应使用 trade.nr_of_successful_entries,该参数将同时包含空头开仓。
辅助方法¶
为 stoploss_from_open 和 stoploss_from_absolute 添加参数 "is_short"。
该参数应赋值为 trade.is_short。
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
# once the profit has risen above 10%, keep the stoploss at 7% above the open price
if current_profit > 0.10:
return stoploss_from_open(0.07, current_profit)
return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate)
return 1
变更后:
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> float | None:
# once the profit has risen above 10%, keep the stoploss at 7% above the open price
if current_profit > 0.10:
return stoploss_from_open(0.07, current_profit, is_short=trade.is_short)
return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate, is_short=trade.is_short, leverage=trade.leverage)
策略/配置设置¶
order_time_in_force¶
order_time_in_force 属性从 "buy" 改为 "entry","sell" 改为 "exit"。
order_time_in_force: dict = {
"buy": "gtc",
"sell": "gtc",
}
变更后:
order_time_in_force: dict = {
"entry": "GTC",
"exit": "GTC",
}
order_types¶
order_types 已将所有 buy 相关表述改为 entry - sell 相关表述改为 exit。
且两个单词之间用 _ 连接。
order_types = {
"buy": "limit",
"sell": "limit",
"emergencysell": "market",
"forcesell": "market",
"forcebuy": "market",
"stoploss": "market",
"stoploss_on_exchange": false,
"stoploss_on_exchange_interval": 60
}
变更后:
order_types = {
"entry": "limit",
"exit": "limit",
"emergency_exit": "market",
"force_exit": "market",
"force_entry": "market",
"stoploss": "market",
"stoploss_on_exchange": false,
"stoploss_on_exchange_interval": 60
}
策略层级设置¶
use_sell_signal->use_exit_signalsell_profit_only->exit_profit_onlysell_profit_offset->exit_profit_offsetignore_roi_if_buy_signal->ignore_roi_if_entry_signal
# These values can be overridden in the config.
use_sell_signal = True
sell_profit_only = True
sell_profit_offset: 0.01
ignore_roi_if_buy_signal = False
变更后:
# These values can be overridden in the config.
use_exit_signal = True
exit_profit_only = True
exit_profit_offset: 0.01
ignore_roi_if_entry_signal = False
unfilledtimeout¶
unfilledtimeout 已将所有 buy 相关表述改为 entry - sell 相关表述改为 exit。
unfilledtimeout = {
"buy": 10,
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
}
变更后:
unfilledtimeout = {
"entry": 10,
"exit": 10,
"exit_timeout_count": 0,
"unit": "minutes"
}
order pricing¶
订单定价在两个方面发生了变化。bid_strategy 更名为 entry_pricing,ask_strategy 更名为 exit_pricing。
属性 ask_last_balance -> price_last_balance 和 bid_last_balance -> price_last_balance 也进行了重命名。
此外,价格侧现在可以定义为 ask、bid、same 或 other。
更多信息请参阅定价文档。
{
"bid_strategy": {
"price_side": "bid",
"use_order_book": true,
"order_book_top": 1,
"ask_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"price_side": "ask",
"use_order_book": true,
"order_book_top": 1,
"bid_last_balance": 0.0
"ignore_buying_expired_candle_after": 120
}
}
之后:
{
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing":{
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0
},
"ignore_buying_expired_candle_after": 120
}
FreqAI 策略¶
populate_any_indicators() 方法已拆分为 feature_engineering_expand_all()、feature_engineering_expand_basic()、feature_engineering_standard() 和 set_freqai_targets()。
对于每个新函数,交易对(以及必要的时间框架)将自动添加到列中。 因此,使用新逻辑后,特征的定义变得更加简单。
关于每个方法的完整说明,请前往相应的 freqAI 文档页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
- 特征 - 移至
feature_engineering_expand_all - 基础特征,不跨
indicator_periods_candles扩展 - 移至feature_engineering_expand_basic()。 - 不应扩展的标准特征 - 移至
feature_engineering_standard()。 - 目标 - 将此部分移至
set_freqai_targets()。
freqai - 特征工程扩展全部¶
功能现在会自动扩展。因此,需要移除扩展循环以及 {pair} / {timeframe} 部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |
Freqai - 特征工程基础¶
基础特征。请确保从你的特征中移除 {pair} 部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
FreqAI - 特征工程标准¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
FreqAI - 设置目标¶
目标现在拥有其专用的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
FreqAI - 新数据管道¶
如果你创建了带有自定义 train()/predict() 函数的自定义 IFreqaiModel,并且你仍然依赖 data_cleaning_train/predict(),那么你需要迁移到新的数据管道。如果你的模型不依赖 data_cleaning_train/predict(),那么你无需担心此次迁移。这意味着本迁移指南仅与极少部分的高级用户相关。如果你误入了本指南,欢迎在 Freqtrade 的 Discord 服务器中详细询问你的问题。
转换过程首先需要移除 data_cleaning_train/predict(),并将其替换为添加到你的 IFreqaiModel 类中的 define_data_pipeline() 和 define_label_pipeline() 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
- 数据归一化与清洗现已通过新的流水线定义实现统一。这由新增的
define_data_pipeline()和define_label_pipeline()函数创建。data_cleaning_train()与data_cleaning_predict()函数不再使用。如需创建自定义流水线,可重写define_data_pipeline()函数。 - 数据归一化与清洗现已通过新的流水线定义实现统一。这由新增的
define_data_pipeline()和define_label_pipeline()函数创建。data_cleaning_train()与data_cleaning_predict()函数不再使用。如需创建自定义流水线,可重写define_data_pipeline()函数。 - 数据反归一化现通过新流水线完成。请用以下代码行替换原有实现。