上位足を考慮した移動平均線のバックテスト|日経225 FX 4時間足

上位足を考慮した移動平均線のバックテスト|日経225 FX 4時間足

この記事を読むと、次のことがわかるようになります。

上位足を確認することの有効性
エッジのあるトレードの一例
・バックテストのやり方
・単利で計算した、この手法の想定利益

「エントリーするときは、上位足を必ず確認した方が良い」

こんな話をよく聞くと思います。そして、この話を信じて実行している人も多いことでしょう。

しかし、ふとあることに気付きました。
それは、「上位足うんぬん~」に関するデータの裏付けをみたことがないということです。

と、いうわけでやってみました、上位足を考慮したバックテスト!

手法は千差万別なので、この記事の結果がすべてではありませんが、ひとつの、それなりの結果は提示できると思います。

それでは、まずは目次を――

※ 今後は、自分自身の備忘録もかねて、ボク自身のバックテストを記録していく記事を定期的に更新していこうと思います^^ 乞うご期待!

上位足を考慮した移動平均線のバックテスト|日経225 FX 4時間足

  1. バックテストの方法
  2. 検証する手法
    1. 仮説
    2. エントリー
    3. 決済1
    4. 決済2
    5. 検証するパターン
  3. 検証を実施
    1. バックテストの関数を作成
    2. バックテストの実行と分析
    3. 銘柄ごとの損益曲線
    4. ストラテジーごとの損益曲線
  4. まとめ
    1. この手法の想定される利益は
    2. 4時間足以外で行うと
    3. 他に、こんな検証の記事を読みたい
  5. プレゼント

1.バックテストの方法

どんなツールを使用して、どんな方法でバックテストを行ったのかの記録です。

使用したツール

バックテストには以下のツールを使用しました。

Python(Google Colaboratory

  • Pandas(価格データの整形・抽出)
  • Numpy(データの計算処理)
  • Ta-Lib(テクニカル分析)
  • Plotly(グラフ描画)
  • Matplotlib(グラフ描画)
  • datetime(日付の操作)
  • openpyxl(excelファイル関連)
  • tpdm

Pythonを手軽に試したいなら、Google Colaboratoryがめちゃくちゃオススメです。Google Driveでドキュメントやスプレッドシートを作る感覚でPythonを試すことができます。今回のような、ちゃちゃっと検証したいときに非常に便利です。

使用した価格データ

価格データは以下を使用しました。

1分毎の価格データを含むcsvファイル
日経225
MT4のhstファイル
USDJPY、EURJPY、GBPJPY、AUDJPY、EURUSD、GBPUSD、AUDUSD

日経225は独自に保有しているもので、hstファイルはFXDDさんのものを使用しています。

目次へ

2.検証する手法

検証した手法の詳細です。

2-1.仮説

このバックテストをするに至った仮説。

4時間足のトレードをする際に、上位足である日足の動向を考慮した方が良い成績になるのではないか

例)日足で上昇トレンドのときだけ、4時間足の買いエントリーを行う等

目次へ

2-2.エントリー

今回の検証の基本的なコンセプトは以下の通りです。

日足のトレンドを、4時間足のエントリーのフィルターとする

バックテスト エントリー

日足のトレンドを確認すると言っても毎回チャートを切り替えるのは手間なので、次の考え方を用います。以下は4時間足であっても、120本の移動平均線は20日相当、240本は40日相当になるということです。

  • 4時間 ✕ 6 = 1日(24時間)
  • 4時間 ✕ 120 = 20日
  • 4時間 ✕ 240 = 40日

また、移動平均のもみ合い時の「だまし」を軽減するために、小次郎講師による移動平均線大循環分析の「ステージ」の考えを用います。

4時間足のトレンド
EMA5とEMA20、EMA40の関係でステージを判断する
日足のトレンド
4時間足のEMA5とEMA120、EMA240の関係でステージを判断する

※ EMAは指数平滑移動平均線のこと、EMA5はローソク足5本のEMAを指す

「ステージ」の詳細については、権利の関係がありそうなので本家の記事を御覧ください。また、本家では、単純にステージだけでエントリーやイグジットを判断するのではなく、ローソク足の形やもみ合い放れ等も考慮して判断するようです(つまり、本家はこの検証結果よりも良い成績になると思われる)

上昇トレンド
ステージ6:早仕掛け、ステージ1:本仕掛け
下降トレンド
ステージ3:早仕掛け、ステージ4:本仕掛け

今回のバックテストは、エントリーを次のように組み合わせていくつかパターンを作成し行いました。

ENTRY_CycleStage14_FILTER_UpperCycleStage14
通常のパターン(エントリーが遅くなりがち)
ENTRY_CycleStage63_FILTER_UpperCycleStage63
早仕掛けのパターン(エントリーは早いが”だまし”が多くなりがち)
ENTRY_CycleStage14_FILTER_UpperCycleStage63
組み合わせたパターン(ハイブリットを目指す)

※ ENTRYは4時間足のステージを、FILTERは日足のステージを表します

目次へ

2-3.決済1

決済1は、同じく小次郎講師による移動平均線大循環分析の「ステージ」の考えに基づくものです。主に、「利益確定」や、「トレンドがなくなったことを確認した損切り」の決済です。

今回のバックテストでは、ステージによる決済は以下で統一しています。

EXIT1_CycleStage36
通常のパターン(エントリーが遅くなりがち)
買いエントリー(ステージ6か1)はステージ3で決済
売りエントリー(ステージ3か4)はステージ6で決済

「ステージ」の詳細については、権利の関係がありそうなので本家の記事を御覧ください。また、本家では、単純にステージだけでイグジットを判断するのではなく、形成されたトレンドの強弱等も考慮して判断するようです(つまり、本家はこの検証結果よりも良い成績になると思われる)

バックテスト 決済

目次へ

2-4.決済2

決済2は、ATR(その銘柄の平均的な最大変動幅)をもとにした「ロスカット」や「トレイリングストップ」の決済です。

今回のバックテストでは、以下の2パターンを組み替えて行います。

EXIT2_SO_Entry
ATRによるロスカット
エントリーの価格から3-ATRマイナス方向にロスカットを設置(固定)
EXIT2_SO_Close
ATRによるトレイリングストップ
3-ATRマイナス方向にロスカットを設置し、終値がプラス方向に推移するのに追随させる

ATRについてはコチラの記事をご覧ください

バックテスト 決済

目次へ

2-5.検証するパターン

今回のバックテストでは、ここまで解説してきたENTRYやFILTER、EXIT1、EXIT2を組み合わせた以下の6パターンの検証を行いました(FILTER_noneは4時間足だけで判断した場合)

ENTRY_CycleStage14_FILTER_none__EXIT1_CycleStage36__EXIT2_SO_Entry
ENTRY_CycleStage14_FILTER_UpperCycleStage14__EXIT1_CycleStage36__EXIT2_SO_Entry
ENTRY_CycleStage14_FILTER_UpperCycleStage14__EXIT1_CycleStage36__EXIT2_SO_Close
ENTRY_CycleStage63_FILTER_UpperCycleStage63__EXIT1_CycleStage36__EXIT2_SO_Entry
ENTRY_CycleStage63_FILTER_UpperCycleStage63__EXIT1_CycleStage36__EXIT2_SO_Close

目次へ

3.検証を実施

3-1.バックテストの関数を作成

ここが一番重要だったりするのですが、ものすごく長くなってしまうので割愛します。こちらのサイトのコードを参考に作成しました((´・ω・`;)ヒィィッ すいません「バックテストを試してみました」

参考までに、ストラテジー部分のみ掲載します。

Google Colab

class Strategy_SycleEMA_Base(StrategyBase) :
  
  def __init__(self ,symbol ,np_arr_dic ,df) :
    
    StrategyBase.__init__(self ,symbol ,np_arr_dic ,df)
    
    self.ATR_N = 3

    self.ema5   = ta.EMA(self.c ,5)
    self.ema20  = ta.EMA(self.c ,20)
    self.ema40  = ta.EMA(self.c ,40)
    self.ema120 = ta.EMA(self.c ,120)
    self.ema240 = ta.EMA(self.c ,240)

    self.stage = np.array(list(map(lambda s ,m ,l :
                                  1 if s > m and m > l else \
                                  2 if m > s and s > l else \
                                  3 if m > l and l > s else \
                                  4 if l > m and m > s else \
                                  5 if l > s and s > m else \
                                  6 if s > l and l > m else \
                                  np.nan
                                  ,self.ema5 ,self.ema20 ,self.ema40))) 
    
    self.upper_stage = np.array(list(map(lambda s ,m ,l :
                                    1 if s > m and m > l else \
                                    2 if m > s and s > l else \
                                    3 if m > l and l > s else \
                                    4 if l > m and m > s else \
                                    5 if l > s and s > m else \
                                    6 if s > l and l > m else \
                                    np.nan
                                    ,self.ema5 ,self.ema120 ,self.ema240))) 
    
    self.atrL ,self.atrH = ta.MINMAX(self.atr20 ,1800)
    self.atrM = (self.atrH + self.atrL) / 2
    self.atr  = np.array(list(map(lambda a ,b :
                                 a if a > b else \
                                 b if a < b else \
                                 a if np.isnan(b)==True and np.isnan(a)==False else \
                                 b if np.isnan(a)==True and np.isnan(b)==False else \
                                 np.nan
                                 ,self.atr20 ,self.atrM))) 

    
  def Base_record_mgmt(self ,i) :

    Pos             = self.count_pos()
    self.count_days = np.nan if Pos==0 else self.count_days + 1
    b4_EntrySig     = False  if Pos==0 else abs(self.mgmt[-1]["EntrySig"])==1
    MTM             = 0      if Pos==0 else ((self.o[i] - self.entry_price) * self.l_s)
    b4_SO_Close     = np.nan if Pos==0 else self.mgmt[-1]["SO_Close"]
    b4_SO_Entry     = np.nan if Pos==0 else self.mgmt[-1]["SO_Entry"]
    pre_SO_Close    = np.nan if Pos==0 and not b4_EntrySig else self.c[i-1] - self.atr[i-1] * self.ATR_N * self.l_s
    pre_SO_Entry    = np.nan if Pos==0 and not b4_EntrySig else self.entry_price - self.atr[i-1] * self.ATR_N * self.l_s
    SO_Close        = np.nan if Pos==0 and not b4_EntrySig else pre_SO_Close if b4_EntrySig or (self.l_s==1 and pre_SO_Close > b4_SO_Close) or (self.l_s==-1 and pre_SO_Close < b4_SO_Close) else b4_SO_Close
    SO_Entry        = np.nan if Pos==0 and not b4_EntrySig else pre_SO_Entry if b4_EntrySig or (self.l_s==1 and pre_SO_Entry > b4_SO_Entry) or (self.l_s==-1 and pre_SO_Entry < b4_SO_Entry) else b4_SO_Entry
    PL              = 0      if len(self.hst)==0 else self.hst[-1]['profit'] if self.mgmt[-1]['ExitSig']==1 else 0
    self.mgmt.append({
        'Time'        : self.df.index[i]
        ,'Open'       : self.o[i]
        ,'High'       : self.h[i]
        ,'Low'        : self.l[i]
        ,'Close'      : self.c[i]
        ,'ATR'        : self.atr[i]
        ,'EMA5'       : self.ema5[i]
        ,'EMA20'      : self.ema20[i]
        ,'EMA40'      : self.ema40[i]
        ,'EMA120'     : self.ema120[i]
        ,'EMA240'     : self.ema240[i]
        ,'Pos'        : Pos
        ,'CountDays'  : self.count_days
        ,'EntrySig'   : 1 if self.opbuy else -1 if self.opsell else 0
        ,'ExitSig'    : 1 if self.clbuy or self.clsell else 0
        ,'L/S'        : self.l_s
        ,'EntryPrice' : self.entry_price
        ,'MTM'        : MTM
        ,'PL'         : PL
        ,'Stage'      : self.stage[i]
        ,'UpperStage' : self.upper_stage[i]
        ,'SO'         : self.SO
        ,'SO_Close'   : SO_Close
        ,'SO_Entry'   : SO_Entry
    })
    
    
    
class ENTRY_CycleStage14_FILTER_UpperCycleStage14__EXIT1_CycleStage36__EXIT2_SO_Entry(Strategy_SycleEMA_Base) :
  
  def __init__(self ,symbol ,np_arr_dic ,df) :
    
    Strategy_SycleEMA_Base.__init__(self ,symbol ,np_arr_dic ,df)
    
    
  def onTick(self ,i) :
    
    if self.is_nan[i] : return

    if any([self.opbuy ,self.opsell ,self.clbuy ,self.clsell]) :
      if self.SO :
        self.order_proccesing(i ,Entry_Price=self.c[i-1] ,Exit_Price=self.mgmt[-2]['SO_Entry'] ,ATR=self.atr[i-1])
        self.SO = False
      else :
        self.order_proccesing(i ,Entry_Price=self.c[i-1] ,Exit_Price=self.c[i-1] ,ATR=self.atr[i-1])
    
    opbuy = opsell = clbuy_1 = clbuy_2 = clsell_1 = clsell_2 = False

    buy_filter  = self.upper_stage[i] == 1
    buy_sig     = self.stage[i]       == 1
    
    sell_filter = self.upper_stage[i] == 4
    sell_sig    = self.stage[i]       == 4
    
    self.opbuy  = buy_filter  and buy_sig  and self.count_buy()==0
    self.opsell = sell_filter and sell_sig and self.count_sell()==0
  
    if self.count_buy() > 0 :
      SO_Entry   = self.entry_price - self.atr[i-1] * self.ATR_N * self.l_s if self.count_days==0 else self.mgmt[-1]['SO_Entry'] 
      clbuy_1    = self.stage[i-1] < 3 and (self.stage[i] == 3 or self.stage[i] == 4)
      clbuy_2    = self.l[i] < SO_Entry
      self.clbuy = any([clbuy_1 ,clbuy_2 ,self.opsell])
      self.SO    = clbuy_2

    if self.count_sell() > 0 :
      SO_Entry    = self.entry_price - self.atr[i-1] * self.ATR_N * self.l_s if self.count_days==0 else self.mgmt[-1]['SO_Entry'] 
      clsell_1    = (self.stage[i-1] < 6 and self.stage[i-1] >= 4) and (self.stage[i] == 6 or self.stage[i] == 1) 
      clsell_2    = self.h[i] > SO_Entry
      self.clsell = any([clsell_1 ,clsell_2 ,self.opbuy])
      self.SO     = clsell_2
    
    self.Base_record_mgmt(i)

目次へ

3-2.バックテストの実行と分析

諸々の準備が整ったら、バックテストを実行します。

結果を分析

各ストラテジーの統計は以下の通りでした。

※ 各番号のストラテジーは以下の通り
①:Entry_SycleStage14_Filter_none__Exit1_Sycle36__Exit2_SO_Entry
②:Entry_SycleStage14_Filter_UpperSycleStage14__Exit1_Sycle36__Exit2_SO_Entry
③:Entry_SycleStage14_Filter_UpperSycleStage14__Exit1_Sycle36__Exit2_SO_Close
④:Entry_SycleStage63_Filter_UpperSycleStage63__Exit1_Sycle36__Exit2_SO_Entry
⑤:Entry_SycleStage63_Filter_UpperSycleStage63__Exit1_Sycle36__Exit2_SO_Close
⑥:Entry_SycleStage14_Filter_UpperSycleStage63__Exit1_Sycle36__Exit2_SO_Entry

取引回数 勝率 平均利益
/atr
平均損失
/atr
累計損益
/atr
RR比率 期待値
/atr
4656 31.06% 2.998 -1.248 348.931 2.40 0.071
2594 31.42% 3.233 -1.342 259.575 2.41 0.095
2870 32.65% 3.008 -1.296 324.621 2.32 0.109
3630 33.25% 3.072 -1.359 436.712 2.26 0.115
3985 33.75% 2.937 -1.322 477.544 2.22 0.115
3435 32.98% 3.104 -1.348 429.895 2.30 0.120

金額にかかる項目はすべてそのときのATRで割った数値を使っています。そうすることで、すべての銘柄の検証結果を同じように扱うことができます。

日足を考慮すると(②)

①は日足を考慮していないもの、②は①に加えて日足を考慮したもの(その他の条件は同じ)ですが、結果をみると勝率とRR比率、期待値のすべてにおいて改善したことがわかります。しかし、累計損益はというと減少しています。これは取引回数の減少によるものだと考えるのが妥当でしょう。

トレイリングストップを導入すると(③)

③は②のEXIT2をトレイリングストップに変更したものです。平均利益とRR比率が低下したものの、その他のすべての項目が改善されました。最終的な成果にあたる累計損益も②から改善しました。しかし、①と比べるとまだ物足りなさがあります。

エントリーを早める(④、⑤、⑥)

③までの結果を受けて、エントリーを早めてみることにしました。すると累計損益が大幅に改善され、①よりも良い結果になりました。もっとも良いものは⑤で、トレイリングストップの効果の高さを確認することができます。

目次へ

3-3.銘柄ごとの損益曲線

バックテスト Nikkei225 バックテスト USDJPY バックテスト EURJPY バックテスト GBPJPY バックテスト AUDJPY バックテスト EURUSD バックテスト GBPUSD バックテスト AUDUSD

目次へ

3-4.ストラテジーごとの損益曲線

バックテスト ストラテジー1 バックテスト ストラテジー2 バックテスト ストラテジー3 バックテスト ストラテジー4 バックテスト ストラテジー5 バックテスト ストラテジー6

目次へ

4.まとめ

さて、いかがでしたでしょうか。
日足を考慮して4時間足でトレードすることは概ね有効だと言えそうですね。注意点として、「どんなに勝率やRR比率があがったとしても取引回数がともなわなければ本末転倒になりかねない」ということがありそうです。また、単一銘柄では資産曲線がマイナスになる時期が長いものがあるので、分散投資の必要性も感じます。

この手法の想定される利益は

もっとも成績がよかった⑤の想定される利益は、以下のように算出することができます。100万円の運用資金だとすると、477万円の利益で、577万円まで資金が増えるであろうことがわかります。

⑤の累計損益/atr:477.544
運用資金:100万円
資金管理:1-ATR=運用資金の1% とする

477.544 ✕ 1万円 = 4,775,440円

4時間足以外で行うと

ちなみに、データは掲載しませんが、メインの足を1時間足に変更すると、一転、成績が良くなるどころか悪化してしまいました。つまり、上位足なら何でも良いということではないのかもしれないですね。とくに日足は、数ある足種のなかでも少し特殊です。他の足種に比べて四本値に多くの意味を持つのが日足ですから、そのあたりも関係しているのかもしれません(いずれにしてもさらなる検証が必要ですが)

他に、こんな検証の記事を読みたい

もし、「こんな検証の記事が読んでみたい」というご要望があれば、下部のコメント欄より、お気軽にお知らせください。可能なもので、時間があれば、ご要望にお答えしたいと思いますので、ぜひ!

注意点

これらの結果は4時間足のものです。トレードする時間足を変えると、これらの結果もガラッと変わることがありますので十分ご注意ください。

取引回数としてのサンプルは十分、リーマンショックの期間も含まれているので、そこそこ参考になる数字だとは思います。しかし、銘柄数や年数のサンプルとしては、正直ちょっと不十分です。銘柄や年の変化は想像以上にトレード結果に影響を及ぼしますので。

また、バックテストには万全を期していますが、結果の完全性や手法を用いた際の利益を保証するものではありません。予めご了承ください。

Back to Top

Investment Tech Hack

Sorry... doesn't support your browser

To get the best possible experience using our site we recommend that you upgrade to a modern web browser. Investment Tech Hackではご利用中のブラウザサポートはしていません。
Internet Explorerのアップグレード行う、もしくはその他のブラウザを使用しての閲覧をお願いします。