エンジニアの備忘録

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

前日の高値・安値を使ったブレイクアウト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.も別で作る
  • 逆張りと順張りのポートフォリオ運用