进阶 · 代码逐项

海龟 · 代码逐项

对着 Turtle4h.py 从上往下走。突破通道买,跌破通道或 2N 卖。

Turtle4h.py 4h 20 日=120 根 2N

1怎么走

日线海龟搬到 4h:1 天 6 根,所以 20 日是 120 根,10 日是 60 根。通道用 shift(1) 不含当前 K。真正止损是入场那根的 2 个 ATR,-0.25 只是地板。

2逐项

每一项是文件里的原文,不是口诀。先看黑底那几行,再听人话,再答三问。

class Turtle4h(IStrategy)
class Turtle4h(IStrategy):

向引擎登记这份 4 小时海龟。对照选的是类名 Turtle4h。

它是什么
策略类。
不管它会怎样
没有这个类,回测选不中这份文件。
拧大 / 拧小 / 拧反
改名以后配置里的策略名要对上。
INTERFACE_VERSION = 3
    INTERFACE_VERSION = 3

按第三版接口读 enter / exit,后面 custom_stoploss 也走这套。

它是什么
接口版本号。
不管它会怎样
版本对不上,进出场字段和自定义止损对不齐。
拧大 / 拧小 / 拧反
拧成 2:新字段名会被旧约定忽略。
timeframe = "4h"
    timeframe = "4h"

每根 K 是 4 小时。后面 120 根才等于 20 个日历日,不是 20 根。

它是什么
采样间隔。海龟的「日」按日历折算。
不管它会怎样
落成 1d,120 就变成 120 天,通道会极懒。
拧大 / 拧小 / 拧反
拧成 1h:同一套 120 变成 5 天,通道会极敏。
startup_candle_count = 130
    startup_candle_count = 130

先丢掉大约 130 根。通道要 120 根,再加 shift(1),前面几根数不满。

它是什么
暖机根数。通道和 ATR 算稳之前不交易。
不管它会怎样
暖机太短,前一段 donchian / ATR 是空的,入场条件里 atr>0 会挡掉,或用半残的通道。
拧大 / 拧小 / 拧反
拧到 20:前几根通道是假的。拧到 2000:白白丢掉很长一段。
process_only_new_candles = True
    process_only_new_candles = True

只在 4h 收盘时判断突破。突破看的是收盘价,不是影线乱晃。

它是什么
只处理收盘。
不管它会怎样
未收盘就喊突破,假突破会更多,回测也对不齐。
拧大 / 拧小 / 拧反
拧成 False:盘中刺一下新高就会提前上车。
can_short = False
    can_short = False

这版只做多。经典海龟有空头系统,这份对照没有。

它是什么
空仓开关。关着。
不管它会怎样
有人按教科书去等跌破做空。文件里没有空头入场。
拧大 / 拧小 / 拧反
拧成 True:还要自己写 enter_short,光开开关不会自动做空。
use_exit_signal = True
    use_exit_signal = True

跌破 10 日低点那行 exit_long,引擎会听。

它是什么
出信号门开着。
不管它会怎样
门关着,通道出场作废,只剩 2N 和地板。
拧大 / 拧小 / 拧反
拧成 False:结构卖会消失,只靠止损出门。
exit_profit_only = False
    exit_profit_only = False

亏着也能按信号卖。假突破可以认赔离场。

它是什么
出信号是否只许盈利时触发。这里不限制。
不管它会怎样
拧成 True:亏着跌破 10 日低点也不走,假突破会拖成深亏。
拧大 / 拧小 / 拧反
拧反 True:通道出场只在赚钱时有效,和海龟「认错」相反。
use_custom_stoploss = True
    use_custom_stoploss = True

打开自定义止损。真正的 2N 在文件最下面那一截。

它是什么
告诉引擎去调用 custom_stoploss。
不管它会怎样
关着,2N 函数写了也不会跑,只剩地板 -0.25。
拧大 / 拧小 / 拧反
拧成 False:海龟少了按波动喘气的那一扇门。
minimal_roi = {"0": 10}
    minimal_roi = {"0": 10}

一上车要涨 10 倍,ROI 才出手。等于关掉按时间止盈,让通道和 2N 负责出场。

它是什么
ROI 表。10 是 1000%,几乎碰不到。
不管它会怎样
默认小 ROI 会先于通道把仓平了,大行情坐不住。
拧大 / 拧小 / 拧反
拧小到 0.05:涨 5% 就走,海龟不再吃趋势。
stoploss = -0.25
    stoploss = -0.25

硬地板:亏 25% 必须走。注释写了,真正止损由 custom_stoploss 按 2N 算。

它是什么
引擎保命地板,不是主止损。
不管它会怎样
没有地板,自定义止损万一算不出,仓会裸奔。
拧大 / 拧小 / 拧反
拧到 -0.05:地板比 2N 更近,波动大时地板先打掉。拧到 -0.99:地板形同虚设。
trailing_stop = False
    trailing_stop = False

不用跟踪止盈。出场交给通道和 2N。

它是什么
跟踪止损开关。关着。
不管它会怎样
开着会在回撤里提前剪掉趋势。
拧大 / 拧小 / 拧反
拧成 True:和 2N、通道抢出场,趋势派会变碎。
entry_period = 120
    entry_period = 120  # 20 日

入场通道看过去 120 根最高。4h 上 120 根就是 20 个日历日。

它是什么
唐奇安上轨的窗口。
不管它会怎样
不管它,后面 rolling 没有长度,新高这道门不存在。
拧大 / 拧小 / 拧反
拧大:更懒,更少上船。拧小:更敏,假突破更多。拧回 20:那是 20 根 4h,只有大约 3 天。
exit_period = 60
    exit_period = 60    # 10 日

出场通道看过去 60 根最低。4h 上 60 根就是 10 个日历日。

它是什么
唐奇安下轨的窗口。比入场短一截,所以出场比入场更敏。
不管它会怎样
不管它,结构卖没有尺子,只剩 2N。
拧大 / 拧小 / 拧反
拧大:拿得更久,回撤更大。拧小:走得更勤,大行情更容易坐不住。
atr_period = 120
    atr_period = 120    # 20 日 ATR

N 用 120 根 ATR,和入场窗口同一段路的晃动。

它是什么
算 N 的窗口。
不管它会怎样
不管它,2N 没有尺子,自定义止损会退回地板。
拧大 / 拧小 / 拧反
拧小:N 更跟近期暴晃,止损乱跳。拧大:N 更钝,2N 距离几乎不变。
stop_n = 2.0
    stop_n = 2.0

止损放在入场价下方 2 个 N。这就是 2N。

它是什么
ATR 的倍数。
不管它会怎样
不管它,stop_price 那一行没有距离。
拧大 / 拧小 / 拧反
拧大:更不容易被晃出去,单笔亏更宽。拧小:更脆。拧成负数:止损跑到入场价上方,逻辑反了。
donchian_high 不含当前 K
        dataframe["donchian_high"] = dataframe["high"].rolling(self.entry_period).max().shift(1)

过去 120 根的最高价,再往后挪一格。当前这根的高点不算进通道,避免自己跟自己比。

它是什么
入场上轨。shift(1) 是为了不含当前棒,防止看未来。
不管它会怎样
不 shift:当前高点参与 max,收盘很难「突破自己」,或者用到还没走完的高点。
拧大 / 拧小 / 拧反
去掉 shift:通道含本棒,突破条件会失真。窗口跟 entry_period 走。
donchian_low 不含当前 K
        dataframe["donchian_low"] = dataframe["low"].rolling(self.exit_period).min().shift(1)

过去 60 根的最低价,同样挪一格。跌破的是「已经走完的低点」。

它是什么
出场下轨。
不管它会怎样
不 shift:当前低点参与 min,跌破会算乱。
拧大 / 拧小 / 拧反
窗口跟 exit_period。shift 拿掉,结构卖会看进本棒。
atr = ta.ATR(..., 120)
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=self.atr_period)

用 TA-Lib 算 120 根 ATR,存成一列。后面入场要它大于 0,止损要用入场那一格的值。

它是什么
N。量这段路典型晃多少,不是涨跌方向。
不管它会怎样
没有这列,入场 atr>0 永不成立,自定义止损也取不到 N。
拧大 / 拧小 / 拧反
周期跟 atr_period。把它当「看多看空」的箭头,2N 就没法喘气。
入场:收盘突破上轨
                (dataframe["close"] > dataframe["donchian_high"])
                & (dataframe["volume"] > 0)
                & (dataframe["atr"] > 0)

收盘价高于过去 20 日最高,并且这根有量、ATR 已经算出来,才写开多。

它是什么
三道门同时成立才买。核心是收盘突破,不是均线金叉。
不管它会怎样
只留 close>high:零量和 ATR 空洞的棒也可能进。去掉突破:海龟不成海龟。
拧大 / 拧小 / 拧反
改成 high>通道:影线刺一下就算突破,假突破更多。改成 close>=:贴着通道也进。
出场:收盘跌破下轨
                (dataframe["close"] < dataframe["donchian_low"])
                & (dataframe["volume"] > 0)

收盘价低于过去 10 日最低,并且有量,才写平多。这里不管赚亏。

它是什么
结构出场。和 2N 是两扇门。
不管它会怎样
不管它,只靠止损出门,小回撤也会把你震走,或者反过来抱太久。
拧大 / 拧小 / 拧反
改成 low<通道:影线刺一下就走,大行情更坐不住。
custom_stoploss 取分析表
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe is None or dataframe.empty:
            return self.stoploss

先把这对币已经算好的表拿来。表是空的,就退回地板 -0.25。

它是什么
自定义止损的数据入口。算不出 2N 时的退路。
不管它会怎样
不判断空表,后面取 ATR 会崩,或者返回奇怪的数。
拧大 / 拧小 / 拧反
这里没有可拧的倍数。退回的是上面的 stoploss。
对准入场那根 K
        trade_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc)
        candle = dataframe.loc[dataframe["date"] == trade_date]
        if candle.empty:
            candle = dataframe.loc[dataframe["date"] <= trade_date].tail(1)
        if candle.empty:
            return self.stoploss

把开仓时间对齐到 4h 的那根 K。对不齐就取不晚于开仓的最后一根。还没有,再退回地板。

它是什么
2N 要用「进场当时」的 ATR,不是此刻的 ATR。
不管它会怎样
用当前 K 的 ATR:路突然变晃,止损会跟着跑,不再是入场时定下的距离。
拧大 / 拧小 / 拧反
对齐错周期:取到别的棒,N 就不是进场那一格。
N 取入场 ATR
        n = float(candle["atr"].iloc[0])
        if n <= 0:
            return self.stoploss

入场那根的 ATR 就是 N。N 还没算出来或是 0,不硬算 2N,退回地板。

它是什么
这一笔的波动尺子。
不管它会怎样
不检查 n<=0:后面 stop_price 可能等于开仓价,止损贴脸。
拧大 / 拧小 / 拧反
用当前 ATR 替换:止损会随波动伸缩,不是经典 2N。
stop_price = 开仓价 - 2N
        stop_price = trade.open_rate - self.stop_n * n

从买入价往下量 2 个 N,得到一个固定价格。路晃得大,安全距离也大。

它是什么
这一笔的止损价。按入场价和入场 N 钉死。
不管它会怎样
不管它,下一行没有价格可除。
拧大 / 拧小 / 拧反
stop_n 拧大:价更远。拧小:价更近。写成加号:止损跑到上方,逻辑反了。
返回相对当前价的比例
        return (stop_price / current_rate) - 1

引擎要的不是价格,是「现在这笔相对现价还要再跌多少才碰到止损价」。止损价固定,现价在变,这个比例每根都会重算。

它是什么
Freqtrade 自定义止损的返回约定:相对现价的距离。
不管它会怎样
直接 return -0.02:变成固定 2%,2N 白算。return stop_price:单位错了,引擎会当成比例。
拧大 / 拧小 / 拧反
少写 -1:返回的是价格比,不是跌幅。现价越低,比例越接近 0,门会乱。

3假懂

人们会说:「stoploss = -0.25,所以海龟一亏 25% 才走。」
更短的一句:-0.25 是地板。use_custom_stoploss 打开以后,主止损是入场 ATR 的 2 倍。通道跌破是另一扇门。三扇门不是同一把尺子。

4你来讲

对着这份文件讲。口头就行。不要编回测数字。

  1. donchian_high 为什么要 shift(1)?不挪的话,突破比的是谁跟谁?
  2. custom_stoploss 用的是入场那根的 ATR,还是此刻的 ATR?差在哪?
  3. return (stop_price / current_rate) - 1 为什么不是直接返回 -0.25?引擎要的是价格还是比例?