エンジニアの備忘録

学んだ事をアウトプットしていきます

前日の高値・安値を使ったブレイクアウトEAを作ってみる

作成前に懸念点を明らかにする

懸念点は往復エントリー。

「安値をブレイクで売り」がメインなのに、ブレイク後安値ラインを上抜けでエントリーされるのは望ましくない。

複数エントリー無しにしても、ブレイク後に決済して戻ってきた場合だとエントリーされてしまう。

今のレベルで思いつく解決法は

①インジケータの高値と安値の区別化

移動平均線でフィルターをかける

③条件文を追加する

お手軽さに優先順位を付けるならこうなる ② > ③ > ① 

②なら移動平均より下であればロングエントリーしない、という内容で防げそう(チャート画面は5分足想定)

しかしこれはこれでMAの数値次第でロングして欲しいときにエントリーしない状況も出てくる(-_-;)

③はどうだろうか?例えば、今日既にエントリーしていた場合はエントリーしない。

これなら1回目のブレイクだけはちゃんとエントリー可能、もしその後にライン付近で上抜け下抜けを繰り返す値動きが起きても本来望んでいないエントリーは発生しないはず。

一旦は③採用で進めます。

 

EAの概要

最初はできるだけシンプルに基盤を作成し、それを元に改良を加えていきます。

【エントリー基準】

  • 前日の高値価格より、現在の終値価格が高いなら買い
  • 前日の安値価格より、現在の終値価格が安いなら売り

【クローズ基準】

【資金管理】

  • 口座残高の2%を1トレードの損失額になるようにロットを算出

【備考】

  • 複数ポジションを持たない
  • エントリーは1日1回まで
  • クロス円

 

定義ができたので、明日はエントリー基準の判定に使う高値安値のオブジェクトをEAに組み込めるように準備します。(以下、高値=H 安値=L)

 

 

HLラインをオブジェクト表示したインジケータを使う予定でしたが、半日かけてもスキル不足で上手くEAに組み込めそうになかったので諦めました。

オブジェクト名を取得し、そこから価格を取得したら出来るらしいが・・・上手く動かなかった(-_-;)

結局オブジェクトのEA化は諦めて、インジケータバッファを用いたHLインジケータを作ったので、それをiCustm関数を使ってEAにしようと思います。

エントリー判定

//+------------------------------------------------------------------+
//| エントリー判定
//+------------------------------------------------------------------+
ENUM_HL_Break HLBreak_Judge(){
   
   ENUM_HL_Break ret = MAC_NULL;
   
   //安値ラインの価格を取得
   double low_line = iCustom(Symbol(),PERIOD_CURRENT,"EA用HLインジ",0,1);
   //高値ラインの価格を取得
   double high_line = iCustom(Symbol(),PERIOD_CURRENT,"EA用HLインジ",1,1);
   //終値を取得
   double temp_close1 = iClose(Symbol(),PERIOD_CURRENT,1); //足1個前
   double temp_close2 = iClose(Symbol(),PERIOD_CURRENT,2); //足2個前
   
   //高値を上抜け
   if(high_line > temp_close2 && high_line < temp_close1){
      ret = MAC_UP_CHANGE;
   //安値を下抜け  
   } else if(low_line < temp_close2 && low_line > temp_close1){
      ret = MAC_DOWN_CHANGE;
   }
   return ret;
}

iCustom関数について補足:

引数3の"ファイル名"はMQL4>indicator内にいれておかないと、カスタムインジケータ値を引っ張ってくる事が出来ません。

例えばindicatorファイル内にフォルダを作って、その別フォルダにiCustom関数で使いたいインジケータを保存している場合使えません。

 

  • 前日の高値価格より、現在の終値価格が高いなら買い
  • 前日の安値価格より、現在の終値価格が安いなら売り

足1本前と2本前の終値価格を使ってブレイクしたかのエントリーを判定しています。

クローズ判定

//+------------------------------------------------------------------+
//| 決済オーダー条件の判定
//+------------------------------------------------------------------+
void JudgeClose(ENUM_HL_Break in_hl_break){
   
   bool close_bool    = false;  //決済判定のスイッチ
   double entry_rate  = OrderOpenPrice();
   double sell_close  = 0;
   double buy_close   = 0;
   
   sell_close = entry_rate - 0.500;
   buy_close  = entry_rate + 0.500;
   
   double set_sell_close = NormalizeDouble(sell_close,Digits);
   double set_buy_close  = NormalizeDouble(buy_close,Digits);
   
  if(_StPositionInfoDate.ticket_no > 0){  //ポジション保有
     if(_StPositionInfoDate.entry_dir == OP_SELL) {  //売りポジなら
         if(set_sell_close > Close[1])
         close_bool = true;
         
     } else if(_StPositionInfoDate.entry_dir == OP_BUY){  //買いポジなら
         if(set_buy_close < Close[1])
         close_bool = true;
     }

 

取得したエントリー価格から指定したpips幅をオフセットした値と、足1本前を比較してクローズ判定をしています。

OrderModify関数を使ってストップ・リミットを注文変更しているので、二重チェックで同条件のクローズ判定が執行されます。

ないとは思いますが、なんらかのバグで片方の決済が行われなかった場合、もう片方が執行してくれるので安心です。

金管

//+------------------------------------------------------------------+
//| 新規エントリー                                                                 |
//+------------------------------------------------------------------+
bool EA_EntryOrder(bool in_long)    
  {
  //ロット取得
   double temp_lot = Teiritu_CalcLots(50,2);

新規エントリー情報で取得しておきます。

  • 口座残高の2%を1トレードの損失額になるようにロットを算出

自作関数です。引数1にpips幅、引数2に%値入力でこの条件のロットが毎トレード算出されます。

 

複数ポジションを持たない条件は以前作ったBBと一目EAとほとんど同じ作りです。

ここで一旦動作テストをします。

f:id:pmpmcherry:20220113002721p:plain

安値を下抜けした終値で売られ、ストップ設定値で損切りされています。

高値を上抜けした終値で買われ、リミット設定値で利着されています。

エントリー、クローズ判定は大丈夫そうです。

f:id:pmpmcherry:20220113003026p:plain

ログ出力の内容です。

ロット数に変化が起きているので、変動制ロットになっています。

表示チャートの足更新時に注文変更が発生、パット見で計算しても±50pipsで設定されているのが分かります。

これで大体は完成しました。

 

このまま深刻なバグが見つからなければ、明日には完成しそう。

明日は「エントリーは1日1回まで」の条件を付けくわえていこうと思います。

▲2022/01/13-0:38 更新▲

 

OrdersHistoryTotalという関数がクローズ済み注文数を返してくれます。

しかし、口座履歴で指定した設定に依存してるようなのでリアルトレードではこれを使えば制御できますが、バックテストでは制御できませんでした(バックテストでは口座履歴が設定できないため)

なので今回はGogojungleの書き込みで見つけた、getTradeCountDailyという関数を使います。

引数にEAのマジックナンバーを入れると、当日にエントリーした回数を返してくれます。

これをエントリー判定の中間処理に書いて

int order_history = getTradeCountDaily(MAGIC_NO);
//本日既に決済済み注文があった場合は注文しない
   if(order_history > 0){
      entry_bool = false;
   }

すると↓

f:id:pmpmcherry:20220113235203p:plain

売りエントリー損切り後、もう一度安値を終値で割りましたが追加でエントリーされませんでした!

ひとまず、仕様書の内容は取り込めたので完成です。

これからやること

【最適化】

【その他】

  • 一日一回エントリー条件を高値ブレイクと安値ブレイクで分ける
  • ブレイクしたら逆張り
  • ドルストレートVer.も別で作る
  • 逆張りと順張りのポートフォリオ運用

 

 

 

pythonで重回帰分析

重回帰分析について:

単回帰分析と違い独立変数が複数ある。また、当てはまりの良い直線ではなく最も当てはまりの良いモデルを探すことが目的。

決定係数、自由度修正済み決定係数、P値から独立変数が従属変数に与える影響が低いと判断できた場合、分析に必要のない独立変数データは削除する。

(例:データAの単回帰分析と重回帰分析を比較した。後者のR²値は下がり説明力も

   下がった。また、独立変数BのP値は異常に高く従属変数に影響を与えている可能 

   性が低いので削除した)  

 

前回の不動産データに独立変数yearを追加して重回帰分析を行った結果↓

OLS Regression Results
Dep. Variable: price R-squared: 0.776
Model: OLS Adj. R-squared: 0.772
Method: Least Squares F-statistic: 168.5
Date: Fri, 10 Dec 2021 Prob (F-statistic): 2.77e-32
Time: 06:31:19 Log-Likelihood: -1191.7
No. Observations: 100 AIC: 2389.
Df Residuals: 97 BIC: 2397.
Df Model: 2    
Covariance Type: nonrobust    
  coef std err t P>|t| [0.025 0.975]
const -5.772e+06 1.58e+06 -3.647 0.000 -8.91e+06 -2.63e+06
size 227.7009 12.474 18.254 0.000 202.943 252.458
year 2916.7853 785.896 3.711 0.000 1357.000 4476.571
Omnibus: 10.083 Durbin-Watson: 2.250
Prob(Omnibus): 0.006 Jarque-Bera (JB): 3.678
Skew: 0.095 Prob(JB): 0.159
Kurtosis: 2.080 Cond. No. 9.41e+05

決定係数はどちらも約+0.03上がっており、ばらつきへの説明力は上昇。

独立変数yearのP値は低く、従属変数に与える影響は高い。

結論、価格と築年数には関係性があるといえる。

python (jupyter)で単回帰分析

①分析に必要なライブラリをインポート

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
import seaborn as sns
sns.set()

csvデータを読み込み

date = pd.read_csv("フルパス入力")

date

 

 

データフレームをフルで表示したい場合↓ 

pd.set_option ('display.max_rows', 行数指定)

pd.set_option ('display.max_columns', 列数指定)

 

③要約統計量を取得

date.describe ( )

各列ごとに平均や標準偏差、最大値、最小値、最頻値などの要約統計量を取得できる。

 

(例:値段と床面積)

   price size
count 100.000000 100.000000
mean 292289.470160 853.024200
std 77051.727525 297.941951
min 154282.128000 479.750000
25% 234280.148000 643.330000
50% 280590.716000 696.405000
75% 335723.696000 1029.322500
max 500681.128000 1842.510000
  • count: 要素の個数
  • mean: 算術平均
  • std: 標準偏差
  • min: 最小値
  • max: 最大値
  • 50%: 中央値(median)
  • 25%75%: 1/4分位数、3/4分位数

 

 

④散布図を作成

従属変数と独立変数を定義する

従属: y  = date["Price"]
独立: x1 = date["Size"]

 

散布図を表示

plt.scatter(x1,y)
plt.xlabel('Size',fontsize=20)
plt.ylabel('Price',fontsize=20)
plt.show()

↓出力結果

f:id:pmpmcherry:20211210020649p:plain

plt.scatter( )の引数の種類

x, y グラフに出力するデータ
s サイズ (デフォルト値: 20)
c 色、または、連続した色の値
marker マーカーの形 (デフォルト値: ‘o’= 円)
cmap カラーマップ。c が float 型の場合のみ利用可能です。
norm c を float 型の配列を指定した場合のみ有効。正規化を行う場合の Normalize インスタンスを指定。
vmin, vmax 正規化時の最大、最小値。 指定しない場合、データの最大・最小値となります。norm にインスタンスを指定した場合、vmin, vmax の指定は無視されます。
alpha 透明度。0(透明)~1(不透明)の間の数値を指定。
linewidths 線の太さ。
edgecolors 線の色。

 

 

⑤回帰の実行(最小二乗法)

x = sm.add_constant(x1)  //statmodelでの変数追加
results = sm.OLS(y,x).fit()    //最小二乗回帰式の計算結果が代入

              //fit( )は特定のモデルにおいて一番適合した結果を得る
results.summary()       //結果を表示

↓出力結果

OLS Regression Results

(モデルの要約に関する表)
Dep. Variable: price R-squared: 0.745
Model: OLS Adj. R-squared: 0.742
Method: Least Squares F-statistic: 285.9
Date: Fri, 10 Dec 2021 Prob (F-statistic): 8.13e-31
Time: 02:08:09 Log-Likelihood: -1198.3
No. Observations: 100 AIC: 2401.
Df Residuals: 98 BIC: 2406.
Df Model: 1    
Covariance Type: nonrobust    

OLS(最小二乗):残差の合計が最小になるような直線を決める

        =全てのデータに対して最も近くなるような直線を引く  

R²(決定係数):1に近い程、回帰のばらつきについて説明可能

F値F検定に使う数値、0に近いほどそのモデルは有意ではない

    

          

(係数に関する表)

  coef std err t P>|t| [0.025 0.975]
const 1.019e+05 1.19e+04 8.550 0.000 7.83e+04 1.26e+05
size 223.1787 13.199 16.909 0.000 196.986 249.371

左2行は切片で回帰式の a(1019)とb(223.1787)に当たる

最小二乗法 y^(ハット) = 223.1787 + 101900x₁

std(SSE):標準誤差(ばらつき)が小さいほど正しく予測がされている良い回帰

 

(検定などに使える表)

Omnibus: 6.262 Durbin-Watson: 2.267
Prob(Omnibus): 0.044 Jarque-Bera (JB): 2.938
Skew: 0.117 Prob(JB): 0.230
Kurtosis: 2.194 Cond. No. 2.75e+03

 

回帰直線を散布図の上に表示する

plt.scatter(x1,y)
yhat = x1*223.1787 + 101900
fig = plt.plot(x1, yhat, lw=4, c="orange", label="regression line" )
plt.xlabel('Size', fontsize = 20)
plt.ylabel('Price', fontsize = 20)
plt.show()

↓出力結果

f:id:pmpmcherry:20211210021744p:plain

 

補足

最小二乗法が求めている値とは、残差の二乗の合計の値を最小にしたもの。

 

 

 

 

 

 

 

 

 

 

pythonとcsvファイルのあれこれ

前提:jupyterを使いpandasで読み込むので、読み込んだデータは自動的にdateframeという形に処理される

date = pd.read_csv("   .csv")

フルパスで指定すること

エラーが出た場合とりあえず試す

① パス名の前に r を付ける(ファイル名がエスケープシーエンスとして処理されているパターン)

②後ろに ,encodaing="UTF-8"を付ける、またはshift-jis

 

[データフレームをフル表示したい場合]

pd.set_option ('display.max_rows', 行数指定)

pd.set_option ('display.max_columns', 列数指定)

統計解析で潜在リスクと潜在リターンを予測して知っておく

 

リスクを予測

標準偏差を使った予測

必要な材料は、直近20回分の1トレード損益結果。そこから資金変動幅を推定。

「過去20回のトレードで、危険率0.1%の最大DD」

= STDEV(A1:A20) * 2 * NORMSINV(1 - 0.1% / 2)

その時点での最大値をシステムのリスクと見る。

 

連敗数を使って予測

必要な材料は勝率。確率的連敗最悪値 = 最大DDと予想する。

 = CEILING(LOG(1%, 1 - B2), 1)

1%は危険率、セル(B2)にはシステムの勝率。

最大DD = SL注文の金額ORpips数 * 取引通貨量 * N回(整数のCEILINGが入る)

増し玉、複数注文するシステムはその回数分を金額かpipsに乗算する

★システム運用が1本やリスク分散なしの場合は危険率を0.1%に落として、より安定性を高めたほうが無難

↓早見表

  50% 60% 70% 80% 90%
危険率(1%) 7 6 4 3 2
危険率(0.1%) 10 8 6 5 3

リターンを予測

回帰分析

①相関関係の高い期間を見つける

=CORREL(A1:A15, B1:B15)を使って相関係数を調べる。

左のセルには過去Nヶ月分の平均損益、右には未来のNヶ月分の平均損益を入れる。

係数値は高0.7以上・中0.4以上・低0.2以上。

=CORRELに←Nヶ月間の平均損益 = 合計利益 / トレード数 − 合計損失 / トレード数

f:id:pmpmcherry:20211120202150p:plain

当然未来は分からないので、このように疑似的な未来を用いる。
相関係数の高かった期間から期待利益(1トレード当たり)の下限値を出す

下限値はExcelの回帰分析ツールでXトレード結果とYトレード回数を使って出す。

③月の累積利益を出す

相関係数が高かった期間とその下限値2つの材料が揃えば、大体の月リターンが分かる。

累積利益(pips/月)(金額/月) = 期待利益(1トレード当たり)の下限値 * トレード数 / 期間 

 

EAを停止させる為の判断条件は数値に頼った方が良い

前置き

いくつかシステムの動きを見ていて気付いた事は、全ての相場状況に適したシステムは存在しないという事。

一般的に相場は大きく分けるとレンジ相場とトレンド相場に分類されるといわれている。

レンジでは損を受け入れる代わりに、トレンド相場で利益を取るトレンドフォロー型のシステムで全てのトレンド相場で利益を取れる方法は存在するが、そこは複雑な兼ね合いがまた必要になってくる。

その2つに分類された相場の中にも、異なるレンジとトレンドの形があるから。

f:id:pmpmcherry:20211118164828p:plain

4つの相場

基本的に相場はこの4つのタイプに当てはまる。

乱高下といわれるのは①に近いし、もっとも手を出したくない相場は②で、皆が望む理想のトレンドは④。

こういうサイクルをずっと繰り返してきているのが為替相場

システムトレードとは相場に合わせてシステムを使い分ける作業なのではないかと思う。

その為には「このシステムは最近調子が悪くなってきたな」という危険シグナルをいち早く察知して、システムを停止させる明確な基準が必要。

最近までは底が2度割れたら一旦様子見というシンプルな方法しかないと思っていたが、確率統計によってそれをいち早く明確に、数値で基準化できる方法があるらしい。

数値という根拠で基準化できるメリットは計り知れない。

いい加減なルールではいざというとき破る可能性がある。

実際に相場に身を置いて危機的状況に陥ったら「Xが発生したらYをする」というルールを持っていたとしても、心が弱ったときには合理的にルール通り動けない人の方が多いから。

これは身を持って分かっているので「このシステムはもう駄目だ」という判断をするときは積極的に活用していきたい。

参考にした本はパンローリング社出版の売買システム判別法。

T検定

異なる2種類の母集団の平均値を比較して変調を検出すること。

A「直近10トレードの結果から推定される母集団の平均値」とB「それ以前の結果から推定される母集団の平均値」という風に比較する。

Aのサンプル数が多いと、より精度の高い母集団の平均値が推定できる。

Aのサンプル数が多いほど統計的推定の精度は上がるが、変調に気付く検出スピードが落ちる。

 

=TTEST(P47:P225,P227:P245,2,3) 

#P値 

左2つに過去トレードの結果が入ったセルを指定。

右2つに直近10トレードの結果が入ったセルを指定。

P値が0.05(危険率5%の判定をする場合)よりも小さくなったとき、システムに変調があったと判断する。

※母集団が正規分布と仮定しているので、損失がSL指定により一定値に集中する場合は、発生頻度がそこに集中するため正規分布との乖離が大きくなる

 

二項分布

システムの直近トレード数の勝率だけを使い変調を検出する。

f:id:pmpmcherry:20211118210741p:plain

計算式=BINOMDIST(勝ち数,サンプル数,勝率,TRUE)

勝率90%~40%のシステムが10回中、勝ち数がN回の発生確率をグラフにした。

(ある勝ち数以下の発生する確率)

勝率50%のシステムで10回中に勝ち数5回以下の確率は62%、

勝率80%のシステムで10回中に勝ち数5回以下の確率は15%、

サンプル数を増やせば精度はあがるが(ry

変調と判断する基準値は次のようになる。

勝率 50% 60% 70% 80% 90%
基準値 1 2 4 5 6
  危険率1%        
勝率 50% 60% 70% 80% 90%
基準値 0 1 2 4 5

 

 

 

 

確率と統計を使ってEAのパフォーマンスチェック

読んでみた個人的な感想

著者、山本克二さんの「売買システム判別法」の本の内容が面白かったので、重要な内容を自分用にまとめておく。

書いてある事は非常に為になる内容だった。ハズレの多いトレードの本では間違いなく良書。

著者はトレード会社(他人)などが作ったシステムを使っていて、それらを使えるEAなのかを確率統計を使い判別する術を教えてくれている。

他人の物を使うという事は、正体が分からないブラックボックスを使う事になるのだが、本人いわくシステムの中身がどういった物かを知る必要は無いらしい。

大事なのは、そのシステムが利益を生み出す事が可能なのかを判別する力

確かにその力があればブラックボックスを知らなくても利益は出せるという事実は本当なのかも知れない。

しかし誰かのシステムで稼ぐという事はその「誰か」に依存している事になる。

じゃあその複数の誰かがいつか消えたりなど何かあった場合どうするのか?

共倒れのリスクが一生付き纏う以上、やっぱり他人に依存するのは自分はしたくない。

ブラックボックスを知る事は、どういった内容のシステムが機能して機能しないのか本質を理解しないといけないので、自らシステム開発するトレーダーにとっては必要な過程なのだと思う。

ただ10年以上前の本だから環境は違ったのかもしれない。

現在では依存しようとする人をカモにする投資分野でのビジネスや悪質な商材販売が増えすぎてるから余計厳しそう。

信頼区間

自分の成績から信頼区間を求めることで、95%の確率で将来の成績はこの幅に収まるだろう、という事が分かる。

直近30回の95%信頼区間が1000円 <= µ <= 2000円 →次の30回の平均損益はこの値の範囲内に収まる。

中央値 =( 下限値 + 上限値 ) / 2

サンプル回数が増える程、大数の法則によって信頼区間の精度が上がる。

f:id:pmpmcherry:20211116181004p:plain

計算式

①サンプル数のトレード結果をB1,B30に入力

②マイナスなら下限値、プラスなら上限値を求める

③95%信頼区間(1-95%)(90%なら0.10)

④サンプル数(今回は30回)

↑損益と回数から損益の下限値・上限値を演算

 

↓勝率のパターン

上限: A3+1.96*SQRT(A3*(1-A3)/(B3-1))
下限: A3-1.96*SQRT(A3*(1-A3)/(B3-1))
 
A3=勝率(例:0.4)
B3=トレード数(例:100回)
 

仮説検定

信頼区間では95%に焦点が当たっていたが、仮説検定では5%の間違える可能性を計算式に取り入れている(計算式は省く)

↓偶然に起こりうるPFの範囲

PF(5%) 4.26 2.56 2.11 1.90 1.77 1.68 1.61 1.56 1.52 1.49 1.46 1.44 1.42 1.40 1.38
PF(1%) 6.57 3.17 2.48 2.16 1.98 1.86 1.77 1.70 1.65 1.61 1.57 1.54 1.51 1.49 1.47
サンプル数 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150

損益プラマイ0に収束するPF1のシステムを使っても、最初の10回はビギナーズラック(偶然)によって大儲けが出来てしまう事が起きてしまう。

余談だがFXを始めた当初は数回程度のトレードで50万円儲けた事がある。

稼げると錯覚したものだが、まさしくそれは少ない試行回数で起きた偶然の出来事であり、その後はしっかりと実力値であるPFに収束して破産している。

話を戻して、この仮説検定のメインは偶然の外で起きた事に重きを置いている。

システムの結果が偶然の範囲外を示した=実力があるのではないか

といった風に捉える。

短い時間で回数の多い裁量トレードをした場合なんかは有効活用できると思った。