100% 勝ってしまった
QuantXでアルゴリズムを書く方法を投稿していたのですが,ふと100%勝てるシグナルが書けるんじゃない?と思って書いてみたら書けたというお話です.
とりあえず結果を.
天国や
Look-Ahead Bias
コレは現実のシグナルでは絶対に起きない, Look-Ahead Biasという間違いです.
株価分析を行う上でのルックアヘッドとは,誤って将来の価格を分析対象に入れて予測を行ってしまうことを言います.
QuantXのバックテストでは,(私の想像がただしければ)指定された全期間のヒストリカルデータを取得して売買シグナルを計算し,その後,該当日の買いシグナルおよび売りシグナルに基づいて損益を計算する仕様になっています.
つまり,コードの書き方を間違うと,シグナル計算時に誤って将来のデータが入る可能性があるわけです.
実際のコードがこちらです.
def initialize(ctx):
# 設定
ctx.target = 0.10
ctx.period = 5
ctx.codes = [1605, 1925, 2503, 4519, 4911, 6301, 6752, 7741, 8001 ]
ctx.symbol_list = ["jp.stock.{}".format(code) for code in ctx.codes]
ctx.configure(
channels={
"jp.stock": {
"symbols":ctx.symbol_list,
"columns": ["close_price_adj",
]}})
def _NOTCORRECT_SIGNAL(data):
df_close = data["close_price_adj"].fillna(method='ffill')
df_cheat_close = df_close.pct_change().shift(-2) ## <----- ここ!!!!!!!!!
buy_sig = df_cheat_close > 0
sell_sig = buy_sig.shift(1) == True
return {
"cheat":df_cheat_close,
"buy:sig": buy_sig,
"sell:sig": sell_sig,
}
# シグナル登録
ctx.regist_signal("NOTCORRECT_SIGNAL", _NOTCORRECT_SIGNAL)
def handle_signals(ctx, date, current):
df = current.copy()
# 買いシグナル
df_long = df[df["buy:sig"]]
if not df_long.empty:
for (sym, val) in df_long.iterrows():
sec = ctx.getSecurity(sym)
msg = "買いシグナル"
sec.order_target_percent(ctx.target, comment= msg)
# 売り(ポジションクローズ)シグナル
df_sell = df[df["sell:sig"]]
if not df_sell.empty:
for (sym, val) in df_sell.iterrows():
sec = ctx.getSecurity(sym)
msg = "売りシグナル"
sec.order_target_percent(0, comment= msg)
上記 df_cheat_close = df_close.pct_change().shift(-2)
が Look-Ahead 部分です.
df_close
は,pandas.DataFrame
型です.メソッドである shift を使うと,データを前後(表形式で見れば上下)に動かすことができます.ただし,Index(ここでは日付)は動きません.
例;
import pandas as pd
rng = pd.date_range('1/1/2018',periods=4,freq='D')
df = pd.DataFrame({'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,120,110,150]}, index=rng)
今回書いたQuantXのコードでは,df_cheat_close = df_close.pct_change().shift(-2)
と書くことで,2日後の変化率を取得し,ポジティブであれば,買いシグナルを出すという方法で100%勝てるシグナルを作りました.
QuantXでは,シグナルの算出タイミングと,実際の取引するタイミングは異なります.
シグナルは,今日の終値が確定した後,つまり午後三時の大引け後算出し,取引は翌日の大引け時に行います.
例を上げると,このような日程になります
8月15日 | 8月16日15時以降 | 8月17日15時 | 8月18日15時 |
---|---|---|---|
Buysig発生 | 買い取引 | 評価損益 |
よって,17日から18日が値上がりすることがわかっていれば,16日にBuyシグナルを出せばよいということになります.
従って,shift(-2)
でその情報を得ると,シグナルが出せるわけです.
ぬかよろこびはできます
look-ahead bias は,現実には起きませんが,バックテストでぬかよろこびすることはできます.
みなさんはこんな間違いはしないと思いますが,複雑なアルゴリズムを書いていると,こういうケアレスミスだけど,致命的なミスは入りがちです.shift()
メソッドはとても便利ですが,注意してお使い下さい.