进阶 · 代码逐项

MultiMa · 代码逐项

对着 MultiMa4h.py 从上往下走。TEMA 多头排列买,相邻线扭头或官方 ROI 卖。

MultiMa4h.py 4h 买 4×15 卖 12×68 官方 ROI

1怎么走

官方 MultiMa 会预计算 20×100 条 TEMA,最长 1881 根,4h 上要暖机大半年。这份只算买 4×15、卖 12×68 用到的线。列名就是周期数字。排列用 AND,扭头用 OR。

2逐项

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

class MultiMa4h(IStrategy)
class MultiMa4h(IStrategy):

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

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

按第三版接口读 enter / exit。

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

每根 K 是 4 小时。官方 ROI 表的分钟数,要按这根时钟去读。

它是什么
采样间隔。
不管它会怎样
落成 5m:同一套 15/68 周期和 ROI 分钟都会错配。
拧大 / 拧小 / 拧反
拧周期却不改 ROI 分钟:闹钟和均线不在同一套时间里。
startup_candle_count = 800
    startup_candle_count = 800

先丢掉大约 800 根。最长卖线是 11×68=748,再留一点余量。

它是什么
暖机根数。最长 TEMA 算稳之前不交易。
不管它会怎样
暖机短于 748:最慢那根 TEMA 是空的,扭头条件缺人。
拧大 / 拧小 / 拧反
拧到 100:长线是假的。拧到 5000:白白丢掉很长一段。官方全算 1881 根,4h 上要暖机大半年,这份故意不算。
process_only_new_candles = True
    process_only_new_candles = True

只在 4h 收盘时看排列齐没齐、相邻线扭没扭。

它是什么
只处理收盘。
不管它会怎样
未收盘就判排列,队伍会在一根 K 里齐了又散。
拧大 / 拧小 / 拧反
拧成 False:盘中均线微晃就会提前买卖。
can_short = False
    can_short = False

只做多。相邻线扭头是平多,不是开空。

它是什么
空仓开关。关着。
不管它会怎样
有人看见扭头就想做空。这份没有 enter_short。
拧大 / 拧小 / 拧反
拧成 True:还要自己写空头条件。
use_exit_signal = True
    use_exit_signal = True

相邻线扭头那行 exit_long,引擎会听。官方 ROI 是另一扇门,不靠这行。

它是什么
出信号门开着。
不管它会怎样
门关着,扭头也不卖,只剩 ROI 和 -34.5% 止损。
拧大 / 拧小 / 拧反
拧成 False:均线派少了「旁边两人换位就走」。
官方 ROI 表
    minimal_roi = {
        "0": 0.523,
        "1553": 0.123,
        "2332": 0.076,
        "3169": 0,
    }

官方 hyperopt 出来的时间止盈,对照时也沿用。0 分钟就要 52.3%;1553 分钟降到 12.3%;2332 分钟 7.6%;3169 分钟降到 0,时间到了即使没赚也开门。单位是分钟,不是 4h 根数。

它是什么
按持仓时间止盈的表。这版没有焊死 ROI。
不管它会怎样
改成 {"0": 10}:闹钟关了,大行情或许能多坐一会,就不再是这份对照。
拧大 / 拧小 / 拧反
拧紧:刚起就响,更坐不住。拧松:信号扭头才是主出门。不要另编一套数冒充优化。
stoploss = -0.345
    stoploss = -0.345

硬止损 34.5%。也是官方 hyperopt 留下的,对照沿用。

它是什么
引擎保命地板。比海龟、一目那档 12% 宽。
不管它会怎样
不管它,ROI 和扭头都没碰到时,亏没有底。
拧大 / 拧小 / 拧反
拧到 -0.1:地板先于排列说话。拧到 -0.99:几乎只靠 ROI 和扭头。不要另编。
trailing_stop = False
    trailing_stop = False

不用跟踪止盈。出门交给扭头、官方 ROI 和地板。

它是什么
跟踪止损开关。关着。
不管它会怎样
开着会和 ROI 抢着剪仓,大行情更坐不住。
拧大 / 拧小 / 拧反
拧成 True:均线派会更碎。
买:4 条,间隔 15
    buy_ma_count = 4
    buy_ma_gap = 15

买侧循环 0、1、2、3,乘 15 得到周期 0、15、30、45。0 后面会被跳过,真正用到的买线是 15、30、45。

它是什么
买侧队伍有多长、隔多远。
不管它会怎样
不管它们,后面 periods 和排列条件没有数字。
拧大 / 拧小 / 拧反
count 拧大:要更多条排齐,更难买。gap 拧大:线隔得更开。拧小:更敏,队伍一天齐散好几次。
卖:12 条,间隔 68
    sell_ma_count = 12
    sell_ma_gap = 68

卖侧循环 0 到 11,乘 68 得到 0、68、136,一直到 748。0 跳过。扭头看的是这些相邻对。

它是什么
卖侧队伍有多长、隔多远。比买侧长、也更慢。
不管它会怎样
不管它们,扭头没有线可比。
拧大 / 拧小 / 拧反
count 拧大:更多相邻对,OR 更容易为真,更早走。gap 拧大:线更慢。拧小:更碎。
收集要用的周期
        periods = set()
        for ma_count in range(self.buy_ma_count):
            periods.add(ma_count * self.buy_ma_gap)
        for ma_count in range(self.sell_ma_count):
            periods.add(ma_count * self.sell_ma_gap)

把买侧和卖侧的周期丢进集合。官方文件会预计算 20×100 全部 TEMA;这里只收集默认参数用到的那些数。

它是什么
待计算的周期集合。0 也在集合里,下一行会跳过。
不管它会怎样
写成 range(1,21) 套 range(1,101):又变回官方全算,4h 暖机大半年。
拧大 / 拧小 / 拧反
漏掉卖侧:扭头比的列不存在。漏掉买侧:排列比的列不存在。
TEMA 列名就是周期
        for period in periods:
            if period > 1:
                dataframe[period] = ta.TEMA(dataframe, timeperiod=int(period))

周期大于 1 才算。算完以后,列名是整数 15、30、45、68……748,不是 ma_15 这种字符串。0 被跳过。

它是什么
三重指数均线,存在以周期为名的列里。
不管它会怎样
period>1 拿掉:TEMA(0) 或 TEMA(1) 没有意义,会出错。改成 SMA:反应更钝,快出的味淡了。
拧大 / 拧小 / 拧反
官方预计算最长 1881。这份最长 748。不要把没算的列当存在。
入场:相邻买线全排齐
        conditions = []
        for ma_count in range(self.buy_ma_count):
            key = ma_count * self.buy_ma_gap
            past_key = (ma_count - 1) * self.buy_ma_gap
            if past_key > 1 and key in dataframe.keys() and past_key in dataframe.keys():
                conditions.append(dataframe[key] < dataframe[past_key])
        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), "enter_long"] = 1

买侧相邻两条,慢的 key 低于快的 past_key,才算这一对排齐。ma_count=0、1 时 past_key 是 -15 或 0,被 past_key>1 跳过。真正比的是 30<15 和 45<30。所有对 AND 在一起,全齐才买。

它是什么
多头排列:短线在上、长线在下。key < past_key 就是慢线低于快线。
不管它会怎样
改成一条线上穿:MultiMa 退回金叉课。少了 AND:一对齐就买,队伍没站好。
拧大 / 拧小 / 拧反
拧成 key > past_key:空头排列时开多,逻辑反了。要求十条全齐:永远在等最后一个人。
出场:相邻卖线任一扭头
        conditions = []
        for ma_count in range(self.sell_ma_count):
            key = ma_count * self.sell_ma_gap
            past_key = (ma_count - 1) * self.sell_ma_gap
            if past_key > 1 and key in dataframe.keys() and past_key in dataframe.keys():
                conditions.append(dataframe[key] > dataframe[past_key])
        if conditions:
            dataframe.loc[reduce(lambda x, y: x | y, conditions), "exit_long"] = 1

卖侧相邻两条,慢的 key 高于快的 past_key,就算这一对扭头。从 136>68 一直比到 748>680。任意一对成立就卖,是 OR,不是 AND。

它是什么
相邻线换位。出门比「全死叉」更早。
不管它会怎样
改成 AND:要很多对都扭完才走,回撤浅的优点会被吐回去。
拧大 / 拧小 / 拧反
拧成 key < past_key:排列还整齐时就卖,逻辑反了。再加更多对:OR 更容易为真,更坐不住。

3假懂

人们会说:「这份和官方 MultiMa 一样,预计算了全部 TEMA。」
更短的一句:官方是 count 1 到 20 乘 gap 1 到 100,最长 1881 根。这份只算买 4×15、卖 12×68 用到的线,列名就是周期。逻辑同一套,算的根数不是同一张表。

4你来讲

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

  1. 买侧为什么实际只用 15、30、45?0 和 past_key>1 各跳过了谁?
  2. 入场是 AND,出场是 OR。你怎么对着 reduce 那两行讲给外行听?
  3. 官方 ROI 的 1553、2332、3169 是分钟还是 4h 根数?和焊死 ROI 的 {"0": 10} 差在哪?