v5 · 阶段 B

不看未来

回测一次喂完全部历史。用了当时还没发生的价格,数字会好看,实盘会崩。

shift(1) vs shift(-1) warmup rolling 含不含当前 K

1一句话

回测一次把全部历史喂给你。如果你用了当时还没发生的价格,数字会好看,实盘会崩。

2生活类比

考试允许看已做完的前一题,不允许翻后面的卷子。shift(1) 是看前一题。shift(-1) 是偷看下一题。

3搬到这版

不改买卖逻辑金叉还是金叉。这版改的是:你拿哪一根已经走完的 K 来做决定。
体检命令freqtrade lookahead-analysis。故意错位未来数据,看信号变不变。
暖机也算startup 不够,等于用没算热的线做决定,前几笔不可信。
对照页提过不能 shift(-1),也不能用一目的迟行线(它本身就看未来)。

4每个新参数

shift(1) vs shift(-1)

整列挪一格。正的是看已经走完的上一根,负的是看还没发生的下一根。

它是什么
shift(1) 拿到上一根的值。shift(-1) 拿到下一根的值。
不管它会怎样
不管它:你可能拿当前这根还不稳定的值,或直接拿未来。
拧大 / 拧小 / 拧反了
拧成 -1:典型偷看,回测虚高。保持 1:用已经走完的上一根。不要用「看起来更准」当理由去拧负。
startup 不够会怎样

炉子没热就上菜。前几道看起来像菜,其实是生的。

它是什么
均线、ATR 开头是空的,或用过短的窗口硬算出来。
不管它会怎样
不管它:回测前几笔金叉不可信,数字被开头的假信号污染。
拧大 / 拧小 / 拧反了
拧太小:假信号。够了再拧大:只是少一段样本。至少 ≥ 最慢那条线的周期。
rolling 窗口含不含当前 K = 默认含

「过去 20 根最高价」如果把当前这根自己算进去,等于已经知道这根会不会破高。

它是什么
pandas 的 rolling 默认包含当前行。突破类条件必须先 shift(1),只用已经走完的 20 根。
不管它会怎样
不管它:突破策略会自嗨,回测里「刚好突破」特别准,实盘对不上。
拧大 / 拧小 / 拧反了
金叉用的是两条均线交叉,均线本身已含当前收盘,这是当下能看到的。危险的是去拿「下一根」或「包含当前根的过去极值」来当已经发生的事。
lookahead-analysis

体检。故意把未来数据弄乱,看你的信号会不会跟着变。

它是什么
Freqtrade 自带的检查:如果信号依赖未来,报告会标出来。
不管它会怎样
不管它:你不知道自己有没有偷看。回测很好看也不敢上。
拧大 / 拧小 / 拧反了
没有「拧大拧小」。要做的是跑它,读它,改掉被标出来的写法。

5假懂

人们会说:「回测和实盘用同一份代码就不会偷看」
更短的一句:同一份代码也可以 shift(-1)。偷看是数据怎么对齐的问题,不是文件有没有同步。

6你来讲

答得出来,这课才过关。最好指着一行代码讲,不要只背名词。

  1. shift(1) 和 shift(-1) 各拿到哪一根的价格?
  2. 20 根最高价如果不 shift,为什么算偷看?
  3. startup 不够时,你看到的第一笔金叉可信吗?

7这版代码

只放对和错的对照。这版不是加买卖条件,是改你怎么拿「已经发生」的值。

# 对:用已经走完的上一根
prev_high = dataframe["high"].shift(1)

# 错:偷看下一根
# next_close = dataframe["close"].shift(-1)

# 对:过去 20 根最高价,不含当前这根
donchian = dataframe["high"].rolling(20).max().shift(1)

# 错:把当前这根自己算进「已经突破的高点」
# donchian = dataframe["high"].rolling(20).max()

8过关

会跑 freqtrade lookahead-analysis,并能指出哪类写法是偷看。

freqtrade lookahead-analysis --userdir /workspace/ft_userdata/user_data \
  --config user_data/config_fair.json --strategy FirstSma