FXで売買タイミングとして使うローソク足の上ヒゲ・下ヒゲ等ローソク足の形状を見てエントリーする、簡単なMT4のEAサンプルです。
最近の値動きとして、上ヒゲをつけて一気に下がっていく事や、下ヒゲをつけて一気に上がっていく事がよくあるように感じたので検証用も兼ねて作ってみました。
作っていて思った事は、どれが上ヒゲ、下ヒゲなのかを判断するのがわりと難しかったです。前回ローソク足の値幅以上のヒゲになったらヒゲとする・・・、実体+下ヒゲより上ヒゲが大きかったらヒゲとして扱う等、色々考えましたが結局pips数で判断するようにしました。
今回も最終的にドル円で検証しましたが、上ヒゲ・下ヒゲを根拠にするエントリーは十分使えるのではないかという結果になりました。
主な構文
iHigh()、iLow()、iOpen()、iClose()
【使用例】 rosokuValueHighP = iHigh(NULL, 0, 2); rosokuValueLowP = iLow(NULL, 0, 2); rosokuValueOpenP = iOpen(NULL, 0, 2); rosokuValueCloseP = iClose(NULL, 0, 2);
引数はいずれも以下の通りです。
- 通貨ペア(NULLで当該通貨)
- 時間軸(0で当該時間軸)
- バーシフト
今回は、インジケータを使わないのでローソク足の基本4情報(iHigh:高値、iLow:安値、iOpen:始値、iClose:終値)を取得し、引き算で計算する簡単な式で構成されています。
ヒゲの求め方
上ヒゲ、下ヒゲのサイズを求める際、陽線と陰線かをまず判断する必要があります。
陽線は、iOpen()よりiClose()の方が大きくなる場合です。
陰線は、iOpen()よりiClose()の方が小さくなる場合です。
これを踏まえて、陽線の上ヒゲの長さを求めたい場合はiHigh() – iClose()という感じになります。陽線の下ヒゲの長さを求めたい場合はiOpen() – iLow()です。
また、陰線の下ヒゲの長さを求めたい場合はiClose() – iLow()で、陰線の上ヒゲの長さを求めたい場合はiHigh() – iOpen()です。

わかりずらい場合、上記の図のように実際に視覚化してみるとわかりやすいと思います。
ということで以下サンプルソースは、前述した式を使ってヒゲの長さを求めてエントリーしています。
ソースコード
//+------------------------------------------------------------------+ //| HIGE_SEARCH.mq4 | //| Copyright 2020, mef Software. | //| https://fx-prog.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, mef Software." #property link "https://fx-prog.com/" #property version "1.00" input int HIGE_SIZE = 170; input int SL_GOSA = -60; input int A_SPREAD = 10; input double Lots = 0.01; double rosokuValueHighP,rosokuValueLowP,rosokuValueOpenP,rosokuValueCloseP; datetime prevtime; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int init() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ int start() { int orderPtn=0; //0:何もしない 1:買い 2:売り int total=0; double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート bool OrderKekka; //--- //新しい足ができた時だけやる if(Time[0] != prevtime){ prevtime = Time[0]; }else{ return(0); } //ローソク足の2つ前の情報を取得しておく rosokuValueHighP = iHigh(NULL, 0, 2); rosokuValueLowP = iLow(NULL, 0, 2); rosokuValueOpenP = iOpen(NULL, 0, 2); rosokuValueCloseP = iClose(NULL, 0, 2); //***売買判断箇所***// //ヒゲ情報の取得 int higeT = funcHigeType(PERIOD_M15,2,HIGE_SIZE); //バータイプの取得 int BarT = funcBarType(PERIOD_M15,1); Print("higeT:",higeT,"BarT:",BarT); //【higeT】3:陽線(下ヒゲ〇Pips以上) 4:陰線(下ヒゲ〇Pips以上) if(higeT == 3 || higeT == 4){ //【BarT】3:陽線(実+下より上ヒゲ長) 6:陰線(上ヒゲ長) 8:陰線(実+下より上ヒゲ長) if(BarT != 3 && BarT != 6 && BarT != 8){ //ヒゲローソク足の終値が直近の安値よりも高い事 //if(funcHighLowGet(2,60,3) < rosokuValueCloseP){ //ヒゲローソク足の安値が直近で最も安い事 if(funcHighLowGet(2,60,3) > rosokuValueLowP){ orderPtn=1; } //} } } //【higeT】1:陽線(上ヒゲ〇Pips以上) 2:陰線(上ヒゲ〇Pips以上) else if(higeT == 1 || higeT == 2){ //【BarT】4:陽線(実+上より下ヒゲ長) 7:陰線(下ヒゲ長) 9:陰線(実+上より下ヒゲ長) if(BarT != 4 && BarT != 7 && BarT != 9){ //ヒゲローソク足の終値が直近の高値よりも安い事 //if(funcHighLowGet(1,60,3) > rosokuValueCloseP){ //ヒゲローソク足の高値が直近で最も高い事 if(funcHighLowGet(1,60,3) < rosokuValueHighP){ orderPtn=2; } //} } } else { orderPtn=0; } //***売買判断箇所***// //***決済判断箇所***// total=OrdersTotal(); if(total ==0 && orderPtn > 0) { if(orderPtn == 1) { ea_order_stop_price = rosokuValueLowP - SL_GOSA * Point; ea_order_good_price = Ask + ((Ask - ea_order_stop_price) * 1000 * Point); } else if(orderPtn == 2) { ea_order_stop_price = rosokuValueHighP + SL_GOSA * Point; ea_order_good_price = Bid - ((ea_order_stop_price - Bid) * 1000 * Point); } //新規注文 OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0); } return(0); } //+------------------------------------------------------------------+ //|【関数】直近の高値安値を取得する //| //|【引数】 Type=(1:高値 2:安値) Kikan=確認する期間 //| //|【戻値】最高値又は最安値 //| double funcHighLowGet(int Type,int Kikan,int Start) { double Kakaku=0; if(Type==1){ Kakaku = iHigh(NULL, 0, iHighest(NULL,0,MODE_HIGH,Kikan,Start)); } else if(Type==2){ Kakaku = iLow(NULL, 0, iLowest(NULL,0,MODE_LOW,Kikan,Start)); } return Kakaku; } //+------------------------------------------------------------------+ //|【関数】バーの陰陽線の状態を確認する //| //|【引数】 時間足(PERIOD_M15,PERIOD_H1,PERIOD_D1等) //| //|【戻値】1:陽線(上ヒゲ長) 2:陽線(下ヒゲ長) 3:陽線(実+下より上ヒゲ長) 4:陽線(実+上より下ヒゲ長) 5:陽線(普通) //|【戻値】6:陰線(上ヒゲ長) 7:陰線(下ヒゲ長) 8:陰線(実+下より上ヒゲ長) 9:陰線(実+上より下ヒゲ長)10:陰線(普通) //| int funcBarType(int period,int shift) { int Kekka=0; double UwaHige,Zittai,SitaHige; //陽線の場合 if(iOpen(NULL, period, shift) < iClose(NULL, period, shift)){ Zittai = iClose(NULL, period, shift) - iOpen(NULL, period, shift); UwaHige = iHigh(NULL, period, shift) - iClose(NULL, period, shift); SitaHige = iOpen(NULL, period, shift) - iLow(NULL, period, shift); if(UwaHige > SitaHige){ if(UwaHige > Zittai + SitaHige){ Kekka = 3; }else{ Kekka = 1; } }else if(SitaHige > Zittai){ if(SitaHige > Zittai + UwaHige){ Kekka = 4; }else{ Kekka = 2; } }else{ Kekka = 5; } } //陰線の場合 else if(iOpen(NULL, 0, shift) > iClose(NULL, 0, shift)){ Zittai = iOpen(NULL, 0, shift) - iClose(NULL, 0, shift); UwaHige = iHigh(NULL, 0, shift) - iOpen(NULL, 0, shift); SitaHige = iClose(NULL, 0, shift) - iLow(NULL, 0, shift); if(UwaHige > SitaHige){ if(UwaHige > Zittai + SitaHige){ Kekka = 8; }else{ Kekka = 6; } }else if(SitaHige > Zittai){ if(SitaHige > Zittai + UwaHige){ Kekka = 9; }else{ Kekka = 7; } }else{ Kekka = 10; } //条件合わずで抜ける(オープンとクローズが同じ値ならここに来る) }else{ Kekka = 0; } return Kekka; } //+------------------------------------------------------------------+ //|【関数】バーの陰陽線の状態を確認する //| //|【引数】 Period:時間足(PERIOD_M15,PERIOD_H1,PERIOD_D1等) //|【引数】 shift:バーシフト //|【引数】 higePips:長いヒゲを判断するpips //| //|【戻値】1:陽線(上ヒゲ長) 2:陰線(上ヒゲ長) 0:ひげ短い //|【戻値】3:陽線(下ヒゲ長) 4:陰線(下ヒゲ長) 5:両ヒゲ長 //| int funcHigeType(int period,int shift,int higePips) { int Kekka=0; double UwaHige,Zittai,SitaHige; //陽線の場合 if(iOpen(NULL, period, shift) < iClose(NULL, period, shift)){ Zittai = iClose(NULL, period, shift) - iOpen(NULL, period, shift); UwaHige = iHigh(NULL, period, shift) - iClose(NULL, period, shift); SitaHige = iOpen(NULL, period, shift) - iLow(NULL, period, shift); //ヒゲのpipsサイズを0にした場合は、1つ前のローソク足のHigh-Lowのサイズをヒゲのpipsサイズとする if(higePips==0){ higePips = (iHigh(NULL, period, shift+1) - iLow(NULL, period, shift+1)) * 1000; } if(UwaHige > SitaHige * 2){ if(UwaHige * 1000 > higePips){ Kekka = 1; }else{ Kekka = 0; } }else if(SitaHige > UwaHige * 2){ if(SitaHige * 1000 > higePips){ Kekka = 3; }else{ Kekka = 0; } }else{ if(UwaHige * 1000 > higePips && SitaHige * 1000 > higePips){ Kekka = 5; }else{ Kekka = 0; } } } //陰線の場合 else if(iOpen(NULL, 0, shift) > iClose(NULL, 0, shift)){ Zittai = iOpen(NULL, 0, shift) - iClose(NULL, 0, shift); UwaHige = iHigh(NULL, 0, shift) - iOpen(NULL, 0, shift); SitaHige = iClose(NULL, 0, shift) - iLow(NULL, 0, shift); if(UwaHige > SitaHige * 2){ if(UwaHige * 1000 > higePips){ Kekka = 2; }else{ Kekka = 0; } }else if(SitaHige > UwaHige * 2){ if(SitaHige * 1000 > higePips){ Kekka = 3; }else{ Kekka = 0; } }else{ if(UwaHige * 1000 > higePips && SitaHige * 1000 > higePips){ Kekka = 5; }else{ Kekka = 0; } } //条件合わずで抜ける(オープンとクローズが同じ値ならここに来る) }else{ Zittai = iClose(NULL, 0, shift); UwaHige = iHigh(NULL, 0, shift) - iClose(NULL, 0, shift); SitaHige = iClose(NULL, 0, shift) - iLow(NULL, 0, shift); if(UwaHige > SitaHige * 2){ if(UwaHige * 1000 > higePips){ Kekka = 2; }else{ Kekka = 0; } }else if(SitaHige > UwaHige * 2){ if(SitaHige * 1000 > higePips){ Kekka = 4; }else{ Kekka = 0; } }else{ if(UwaHige * 1000 > higePips && SitaHige * 1000 > higePips){ Kekka = 5; }else{ Kekka = 0; } } } return Kekka; } //+------------------------------------------------------------------+ //|【関数】新規注文関数 //| //|【引数】 ea_order_entry_Type:売買(0:買 1:売) //|【引数】 ea_order_stop_price:損切値 ea_order_good_price:利確値 //|【引数】 orderComment:オーダーコメント ea_order_MagicNo:マジックNo //| //|【戻値】True:成功 //| //| bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo) { int order_resend_num; // エントリー試行回数 int ea_ticket_res; // チケットNo int errorcode; // エラーコード double ea_order_entry_price; // エントリーレート color order_Color; bool kekka; for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) { // エントリー試行回数上限:10回 if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD){ if(ea_order_entry_Type == OP_BUY){ ea_order_entry_price = Ask; // 現在の買値でエントリー order_Color = clrBlue; }else if(ea_order_entry_Type == OP_SELL){ ea_order_entry_price = Bid; // 現在の売値でエントリー order_Color = clrRed; } // FXCMでは新規エントリー時にストップ/リミットを設定出来ない。 ea_ticket_res = OrderSend( // 新規エントリー注文 NULL, // 通貨ペア ea_order_entry_Type, // オーダータイプ[OP_BUY / OP_SELL] Lots, // ロット[0.01単位](FXTFは1=10Lot) ea_order_entry_price, // オーダープライスレート 20, // スリップ上限 (int)[分解能 0.1pips] ea_order_stop_price, // ストップレート ea_order_good_price, // リミットレート orderComment, // オーダーコメント ea_order_MagicNo, // マジックナンバー(識別用) 0, // オーダーリミット時間 order_Color // オーダーアイコンカラー ); } if ( ea_ticket_res == -1) { // オーダーエラー errorcode = GetLastError(); // エラーコード取得 if( errorcode != ERR_NO_ERROR) { // エラー発生 printf("エラー"); } Sleep(2000); // 1000msec待ち RefreshRates(); // レート更新 printf("再エントリー要求回数:%d, 更新エントリーレート:%g",order_resend_num+1 ,ea_order_entry_price); } else { // 注文約定 Print("新規注文約定。 チケットNo=",ea_ticket_res," レート:",ea_order_entry_price); Sleep(300); // 300msec待ち(オーダー要求頻度が多過ぎるとエラーになる為) break; } } return kekka; }
自作funcHigeType()関数で、ヒゲの長さのチェックをしています。第三引数でヒゲの長さpips数を渡せるようにしており、この渡した数値未満だと長いヒゲとならずエントリー対象外となります。
※設定値HIGE_SIZE(初期値17pips)がこの第三引数の値です
また、funcBarType()関数でローソク足の形状を見ています。厳密にはもう少し分けれそうですが、今回は関数のコメントにもあるように10種類で見ています。
あとは、funcHighLowGet()関数で直近の最高値、最安値を見ています。
エントリータイミング
ロングエントリー
- ①下ヒゲのサイズがHIGE_SIZEより大きい
- ②下ヒゲの出た次のローソク足の状態が良い
- ③ヒゲの最高値が直近(60バー以内)で最安値
※ローソク足の状態が悪い=陽線で実体+下ヒゲより上ヒゲが長い、陰線で上ヒゲが長い、陰線で実体+下ヒゲより上ヒゲが長い
この3つ条件がそろった場合、ロングエントリーします。
ショートエントリー
- ①上ヒゲのサイズがHIGE_SIZEより大きい
- ②上ヒゲの出た次のローソク足の状態良い
- ③ヒゲの最高値が直近(60バー以内)で最高値
※ローソク足の状態が悪い=陽線で実体+上ヒゲより下ヒゲが長い、陰線で下ヒゲが長い、陰線で実体+上ヒゲより下ヒゲが長い
この3つ条件がそろった場合、ショートエントリーしています。

エントリー例



決済タイミング
エントリー時の逆指値、指値でのみ管理。
逆指値はヒゲの最大値から設定値SL_GOSAの値分を調整しています。初期値はヒゲの6pips手前が逆指値となります。
指値は、公平性も兼ねて逆指値と同じ分をプラス側で指値としています。
その他の仕様
設定値は以下の通りです。
- HIGE_SIZE = 170 ⇒ 何pips分をヒゲとするか(左記の場合だと17pips)
- SL_GOSA = -60 ⇒ ヒゲから何pipsの所に逆指値を置くか。
- A_SPREAD = 10 ⇒ エントリー時に許容するスプレッドの値です(10 = 10pips)
- Lots = 0.01 ⇒ ロット数です(0.01=1000通貨)
既にポジションがある場合は追加でエントリーしません。
新しい足(バー)が出来た際に1度だけ処理を行います。※1時間足の場合だと1時間に1回処理する
EAレポート結果

2005年~2020年の期間の15分足でバックテスト確認してみました。エントリー数は若干少なめですがそこそこのプラス収支になっています。
最近の値動きとして、ダマシのような感じでヒゲを付けて逆行する事が多いと感じたので検証がてらに作ってみました。
最近の成績が良いところをみると、そんな傾向になってきているのかもしれませんね。
あと、トレード結果を見ていくと、指値で決済された後もしっかりと伸びているトレードもたくさんあったので、工夫すれば自分が今使っているEAにも組み込めるかなと思いました。
※ EAのサンプルソースを一覧表にまとめました
※ オススメ記事(EAが使える国内FX業者の一覧)
※ 1からEAの作り方を学びたい人はこちら
コメント