考え方とコード
先日ブログにメモとして残したロジック(OS_OF_ver6)をベースに,売買条件を見直すことにした.買い注文&売り決済を文字にするとこんな感じ.カッコ書きは売り注文&買い決済を示す.
<決済注文>
- 移動最小二乗近似値線の傾きが一定以下(以上)になったら決済
- ポジションがあり,損切条件(直近120個の終値の最大値または最小値)に至ったら決済
<新規注文>
- 移動最小二乗近似値線の傾きが一定以上(以下)かつ
最新の終値が一つ前の終値より低い(高い),かつ
最新の終値と最小二乗近似値との差が一定以下,かつ
直近のRSIが55より小さい(大きい) - ポジションがあり,ポジション平均レートに対し,今のレートとの差が一定値以上ならナンピン
定数設定は,全通貨ペア共通でも良いが,バックテストの結果を見て,USD_JPY&CAD_JPY,EUR_JPYと,GBP_JPY,AUD_JPY,NZD_JPYで微妙にわけた.
詳細は割愛するが,移動最小二乗近似値と終値との差が大きすぎると「高値掴み」しやすいので,新規注文の条件から外す設定があるのだが,GBP_JPY,AUD_JPY,NZD_JPYはそのガードを広めにした.つまり,高値掴みで新規注文しても,トレンドが続きやすく,長い目で見ればプラスになることの方が多い,ということだ.
#210521, 8時間足のデータを基に,ゆったり売買.作成中.OF_ver7と同じロジックで,書き方だけ変えてみた.こっちの方が計算早い #210605, ver1_2:ナンピン注文にトレンドの強さの条件を追加. #210605, ver1_3:通貨ごとに定数設定. def OS_Neo8H_ver1_3(CurrencyPair,Data_DF, jNow,Posi_pre,Vp_pre, Balance,Npair,Amount): #jNowは,売買判断に使う最新足の位置を決定.何もなきゃ0.ひとつ前の足なら1 N_RSI=6 #RSIのスレッシュ判定に用いる足の本数 N_Data = len(Data_DF) Settle = 0 Order = 0 #jNowが0ではないなら,行を削除.例えば,jNow=1なら,最新データは不要なので,0番目を削除する if jNow!=0: for jj in range(jNow): Data_DF=Data_DF.drop(jj) N_Data = len(Data_DF) IndexList = list(range(N_Data - 1, -1, -1)) Data_DF.index = IndexList Vc_H = Data_DF.at[1, "VcMax120"] Vc_L = Data_DF.at[1, "VcMin120"] DeltaHL=Vc_H-Vc_L #スレッシュ定義 #Thre_LS2_L = 0.05 Thre_LS2_L = max(Data_DF['Vc'])*0.00037 #0.0003~5 MaxNamPin=5 #各通貨ペアの,新規注文上限.例えば,買い注文は,初回+MaxNamPin-1回分,ナンピン買い出来る #Loss=4 #損切ラインをどう設定するかね.とりあえず2~3円分で,6通貨ペアそこそこの成績だが… dVc = 0*max(Data_DF['Vc']) * 0.01 # 直近の終値差のスレッシュ.デフォルト0.-なら,より有利な側でのポジション確保 Loss = max(Data_DF['Vc'])*0.025 # 損切ラインをどう設定するかね.直近の終値最高値×0.02~0.03あたり. # ↓北米系以外は0.01以上が良さそう.北米系以外は,LS2を上抜け(上昇)または下抜け(下降)でも,トレンドが続きやすいということか. if CurrencyPair=="USD_JPY" or CurrencyPair=="CAD_JPY" or CurrencyPair=="EUR_JPY"\ or CurrencyPair=="USDJPY" or CurrencyPair=="CADJPY" or CurrencyPair=="EURJPY": dV_GRD = max(Data_DF['Vc']) * 0.009 # 終値がLS2に対して,高いか低いか.LS2に対して余りに不利な条件なら発注見送り.USDで1円設定がロバストだった elif CurrencyPair=="GBP_JPY" or CurrencyPair=="AUD_JPY" or CurrencyPair=="NZD_JPY"\ or CurrencyPair=="GBPJPY" or CurrencyPair=="AUDJPY" or CurrencyPair=="NZDJPY": dV_GRD = max(Data_DF['Vc']) * 0.015 # 終値がLS2に対して,高いか低いか.LS2に対して余りに不利な条件なら発注見送り.USDで1円設定がロバストだった else: dV_GRD = max(Data_DF['Vc']) * 0.01 dV_NamPin = max(Data_DF['Vc']) * 0.00125 # For ver6. 0.001~0.003? k_NamPin=0 #-1~1を設定.1なら,傾きスレッシュより大きなトレンドであればナンピン許容.-1の設定で,概ねver1_1と同じ結果を返す. Trend0_L = 0 if Data_DF.at[0, "LS2_120"] - Data_DF.at[1, "LS2_120"] > Thre_LS2_L: Trend0_L = 1 if Data_DF.at[0, "LS2_120"] - Data_DF.at[1, "LS2_120"] < -Thre_LS2_L: Trend0_L = -1 # 実際は回数ではなくて,ポジション量で上限を切る.注文の都度残高が異なるので,×1.1くらいにしてロバストにする. MaxAmount = (MaxNamPin - 1) * Amount #MaxAmount=round(PosiMax/Npair) # 買い玉を売り決済するロジック if Posi_pre > 0: if Trend0_L==-1: Settle=-1 #if Data_DF.at[0, "Vc"] -Vp_pre<-Loss:#損切り if Data_DF.at[0, "Vc"] < Vc_L: # 損切り Settle=-1 # 売り玉を買い決済するロジック if Posi_pre < 0: if Trend0_L==1: Settle = 1 #if Data_DF.at[0, "Vc"]-Vp_pre>Loss:#損切り if Data_DF.at[0, "Vc"] > Vc_H: # 損切り Settle=1 # 買い注文ロジック if (Settle==0 and Posi_pre==0) or (Settle<0 and Posi_pre<0):#新規注文条件 if Trend0_L == 1 \ and Data_DF.at[0, "Vc"] - Data_DF.at[1, "Vc"] < dVc \ and Data_DF.at[0, "Vc"] - Data_DF.at[0, "LS2_120"] < dV_GRD \ and min(Data_DF['RSI'][N_Data - N_RSI:N_Data]) < 55: Order = Amount elif Settle==0 and Posi_pre>0 and Posi_pre <=MaxAmount: #ナンピン注文条件 if Data_DF.at[0, "Vc"] < Vp_pre - dV_NamPin / 2 * (Amount + abs(Posi_pre)) / Amount\ and (Data_DF.at[0, "LS2_120"] - Data_DF.at[3, "LS2_120"])/3>k_NamPin*Thre_LS2_L: Order = Amount # 売り注文ロジック if (Settle==0 and Posi_pre==0) or (Settle>0 and Posi_pre>0):#新規注文条件 if Trend0_L == -1 \ and Data_DF.at[0, "Vc"] - Data_DF.at[1, "Vc"] > -dVc \ and Data_DF.at[0, "Vc"] - Data_DF.at[0, "LS2_120"] > -dV_GRD \ and max(Data_DF['RSI'][N_Data - N_RSI:N_Data]) > 45: Order = -Amount elif Settle==0 and Posi_pre<0 and Posi_pre >= -MaxAmount: #ナンピン注文条件 if Data_DF.at[0, "Vc"] > Vp_pre + dV_NamPin / 2 * (Amount + abs(Posi_pre)) / Amount\ and (Data_DF.at[0, "LS2_120"] - Data_DF.at[3, "LS2_120"])/3<-k_NamPin*Thre_LS2_L: Order = -Amount PosiMax = CalPosiMax_ver1(Balance) Amount_new = round(PosiMax / (Npair * MaxNamPin)) # 決済する際,それが損となるとき #if (Settle == -1 and Vc0 < Vp_pre-1.0) or (Settle == 1 and Vc0 > Vp_pre+1.0): if (Settle == -1 and Data_DF.at[0, "Vc"] < Vp_pre - 0.3) or (Settle == 1 and Data_DF.at[0, "Vc"] > Vp_pre + 0.3): # 決済する際,それが得となるとき # if (Settle==-1 and Vc0>Vp_pre) or (Settle==1 and Vc0<Vp_pre): #ここで,Amountを更新 Amount = max(Amount_new, Amount) Amount=1000 #ここで固定すれば,一応注文量は常に固定となる return (Amount,Settle, Order) # 決済は±1か0で出力,新規注文は±通貨量かoで出力
バックテスト
2005~2020年のデータを使い,バックテストをしてみた.初回口座残高は50,000円,取引通貨量は1,000,スプレッドは0.03円(3銭)としてみた.通貨ペアはクロス円でUSD,CAD,EUR,GBP,AUD,NZDの6通りとした.
<USD_JPY>
ロジック事態は大きく変えていないので,バックテストの結果もほぼ同じだったりする.今回,追加の分析として勝率やリスクリワード,ディスポジション(勝ちポジションの平均保有期間/負けポジションの平均保有期間)を追加した.
決済時の勝率は45%…思ったより高い…か?負け決済の方が単純に条件が広い(トレンド転換または損切)ので,勝率が低いのはしょうがないか.リスクリワードは2弱…2は欲しかったけど,パラメータのファインチューニングには意味を感じないので,まあこんなもんかな.
ディスポジションを見ると,負けポジションに対し,勝ちポジションは3倍の期間保有していることになる.一応ちゃんとトレンドフォロー出来ているらしい.8時間足のトレードで,勝ちポジションは平均19日の保有,ということは,かなりゆったりと取引していることになる…まあ,それがいいよね.売買回数が多くてもいいことはない.
<CAD_JPY>
<EUR_JPY>
<GBP_JPY>
<AUD_JPY>
<NZD_JPY>
こう見ると,定数設定がほぼ同じなせいか,どの通貨ペアでもだいたい同じ勝率,リスクリワードになっている.短期的にはチャートの値動きはランダム性が強いので,チャートの値動きだけで利益を取り続けることができるか疑問に思うところではあるが,長期で見ると,昔から言う「長期トレンドに従い,短期足には逆らえ」は,一応機能しているようだ.
まあ,しょせん私は副業トレーダー…高速売買にしたって,ファンダメンタルズ的な情報の取得&分析力にしたって,どれだけの注文が入っているかの把握にしたって,ガチ勢に勝てるはずがない.長期の波にゆったり乗って,最悪損しなければ良い,くらいの気持ちで,もう暫くこのシステムトレードを続けたい.
コメント