今回は、MT4のEAサンプルソース(FX用)を公開します。
はじめに
たとえEAで自動売買していたとしても、連敗する事はよくあります。
連敗=その日の値動きに合っていない
という事もあるため、3連敗したらその日はエントリーしない(手仕舞いする)ようにする処理を記載します。
今回はChatGPTを使ってソースコード生成し、コードを入れ込んでいく方法も記載しているので参考にしてみてください。
ChatGPTに質問
ChatGPTに以下の質問をします。
『MQL4で3連敗したらその日の取引はしないソースコードを作ってください』
出力されたコードはこちら↓
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int consecutiveLosses = 0;
datetime lastTradeDay = 0;
int OnInit()
{
consecutiveLosses = 0;
lastTradeDay = 0;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 日付が変わったらリセット
if (TimeDay(TimeCurrent()) != TimeDay(lastTradeDay))
{
consecutiveLosses = 0;
lastTradeDay = TimeCurrent();
}
// 勝敗チェック
CheckTradeResults();
// 3連敗していたらその日は取引しない
if (consecutiveLosses >= 3)
{
return;
}
// ↓ここにトレードロジックを書く
if (条件) {
// 例:買い注文
OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Buy", 0, 0, clrBlue);
}
}
//+------------------------------------------------------------------+
//| 勝敗チェック関数 |
//+------------------------------------------------------------------+
void CheckTradeResults()
{
static int lastCheckedTicket = -1;
for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if (OrderSymbol() != Symbol()) continue; // シンボル確認
if (OrderCloseTime() <= lastTradeDay) break; // すでにチェック済み
if (OrderTicket() == lastCheckedTicket) break; // 同じものはスキップ
if (OrderProfit() < 0)
{
consecutiveLosses++;
}
else
{
consecutiveLosses = 0; // 勝ったら連敗リセット
}
lastTradeDay = OrderCloseTime();
lastCheckedTicket = OrderTicket();
break; // 1回のループで1つチェック
}
}
}
近頃のChatGPTはかなり精度が上がっていて今回のように機能を作る場合でもかなり助かります。私が見た感じで、問題箇所は無さそうですので、この出力されたコードを解説しながら別のEA(移動平均線のEA)に入れ込んでいきますね。
ソースコード解説
先頭部
まず、先頭部の解説から。
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int consecutiveLosses = 0;
datetime lastTradeDay = 0;
int OnInit()
{
consecutiveLosses = 0;
lastTradeDay = 0;
return(INIT_SUCCEEDED);
}
ここでは、int型の「consecutiveLosses」変数とdatetime型の「lastTradeDay」を作り、EA起動時に0にしている(初期化)しているという処理になります。
いずれの変数も後々使うのでここは同じように先頭部に宣言し、OnInit()にて初期化するという感じにしましょう。
移動平均線のEAに入れる場合だとこんな感じ。
//~
input double Lots = 0.01;
input int SL_GOSA = 60;
int MAShift = 0;
int MAMode = 1; // 0 = sma, 1 = ema, 2 = smma, 3 = lwma
datetime prevtime;
int consecutiveLosses = 0;
datetime lastTradeDay = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
consecutiveLosses = 0;
lastTradeDay = 0;
//---
return(INIT_SUCCEEDED);
}
//~
メイン処理部
次に、メイン処理部(void OnTick())です。
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 日付が変わったらリセット
if (TimeDay(TimeCurrent()) != TimeDay(lastTradeDay))
{
consecutiveLosses = 0;
lastTradeDay = TimeCurrent();
}
// 勝敗チェック
CheckTradeResults();
// 3連敗していたらその日は取引しない
if (consecutiveLosses >= 3)
{
return;
}
// ↓ここにトレードロジックを書く
if (条件) {
// 例:買い注文
OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Buy", 0, 0, clrBlue);
}
}
ここでは、『// ↓ここにトレードロジックを書く』と書いている箇所の以下はいらないのでその上の部分を入れ込んでいきましょう。
『// 日付が変わったらリセット』と記載している箇所では、まず
『if (TimeDay(TimeCurrent()) != TimeDay(lastTradeDay))』で日付が違う(次の日)になった事をチェックできます。で、日付が違っていたら
・「consecutiveLosses」変数をリセット(負け数のカウントリセット)
・「lastTradeDay」変数に今日の日付をセット
という事で、日付が変わったらまた連敗チェックしますよという感じになっています。
次に『// 勝敗チェック』と書いている箇所では、CheckTradeResults()関数を呼び出しています。
最後に、『// 3連敗していたらその日は取引しない』と書いている箇所では、
『if (consecutiveLosses >= 3)』で負け数が3を超えたら『return;』となっているので負け数が3を超えたらその日は以降トレードしないという作りになっていますね・
『return;』というのいは、以降の処理はせず終わるという感じです。
これを、移動平均線のEAに入れる場合だとこんな感じ。
//~
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;
//---
// 日付が変わったらリセット
if (TimeDay(TimeCurrent()) != TimeDay(lastTradeDay))
{
consecutiveLosses = 0;
lastTradeDay = TimeCurrent();
}
// 勝敗チェック
CheckTradeResults();
// 3連敗していたらその日は取引しない
if (consecutiveLosses >= 3)
{
return;
}
//新しい足ができた時だけやる
if(Time[0] != prevtime){
prevtime = Time[0];
}else{
return;
}
//***売買判断箇所***//
//----MA短期値取得
double fasterMA_ATAI_1 = iMA(NULL, 0, FasterMA, MAShift, MAMode, PRICE_CLOSE, 1); //短期移動平均線で1つ前の値を取得
double fasterMA_ATAI_2 = iMA(NULL, 0, FasterMA, MAShift, MAMode, PRICE_CLOSE, 2); //短期移動平均線で2つ前の値を取得
//~
ポイントは、void OnTick()の一番上の方(ベストは変数宣言の後)に入れ込む事です。こうする事で間違いなく今回の処理は機能します。
典型的な入れ込みNG例としては、トレード処理の後に入れ込んじゃう事です。
個別関数部(CheckTradeResults())
最後は、個別関数部になります。この入れ込みはとても簡単です。一番簡単で確実なのはソースコードの一番最後に入れ込むことです。
//~
}
return kekka;
}
//+------------------------------------------------------------------+
//| 勝敗チェック関数 |
//+------------------------------------------------------------------+
void CheckTradeResults()
{
static int lastCheckedTicket = -1;
for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if (OrderSymbol() != Symbol()) continue; // シンボル確認
if (OrderCloseTime() <= lastTradeDay) break; // すでにチェック済み
if (OrderTicket() == lastCheckedTicket) break; // 同じものはスキップ
if (OrderProfit() < 0)
{
consecutiveLosses++;
}
else
{
consecutiveLosses = 0; // 勝ったら連敗リセット
}
lastTradeDay = OrderCloseTime();
lastCheckedTicket = OrderTicket();
break; // 1回のループで1つチェック
}
}
}
基本的に『int OnInit()』『void OnDeinit()』『void OnTick()』以外の関数は丸々入れ込むという感じでOKです。
入れ込み完了ソースコード
おまけですが、『連敗したらその日はエントリーしなくする処理』の入れ込みが完了したソースコード全てになります。あと、連敗数をパラメータ設定にしています。
//+------------------------------------------------------------------+
//| IMA_TEST.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 FasterMA = 21;
input int MediumMA = 84;
input int A_SPREAD = 20;
input double Lots = 0.01;
input int SL_GOSA = 60;
input int RenpaiStop= 3; //〇連敗でその日の処理を終了する数
int MAShift = 0;
int MAMode = 1; // 0 = sma, 1 = ema, 2 = smma, 3 = lwma
datetime prevtime;
int consecutiveLosses = 0;
datetime lastTradeDay = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
consecutiveLosses = 0;
lastTradeDay = 0;
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;
//---
// 日付が変わったらリセット
if (TimeDay(TimeCurrent()) != TimeDay(lastTradeDay))
{
consecutiveLosses = 0;
lastTradeDay = TimeCurrent();
}
// 勝敗チェック
CheckTradeResults();
// 〇連敗していたらその日は取引しない
if (consecutiveLosses >= RenpaiStop)
{
return;
}
//新しい足ができた時だけやる
if(Time[0] != prevtime){
prevtime = Time[0];
}else{
return;
}
//***売買判断箇所***//
//----MA短期値取得
double fasterMA_ATAI_1 = iMA(NULL, 0, FasterMA, MAShift, MAMode, PRICE_CLOSE, 1); //短期移動平均線で1つ前の値を取得
double fasterMA_ATAI_2 = iMA(NULL, 0, FasterMA, MAShift, MAMode, PRICE_CLOSE, 2); //短期移動平均線で2つ前の値を取得
//----MA中期値取得
double MediumMA_ATAI_1 = iMA(NULL, 0, MediumMA, MAShift, MAMode, PRICE_CLOSE, 1); //中期移動平均線で1つ前の値を取得
double MediumMA_ATAI_2 = iMA(NULL, 0, MediumMA, MAShift, MAMode, PRICE_CLOSE, 2); //中期移動平均線で2つ前の値を取得
//MAのゴールデンクロスでロング(短期MAが上を向いている事)
if(fasterMA_ATAI_1 > MediumMA_ATAI_1 && fasterMA_ATAI_2 < MediumMA_ATAI_2 && fasterMA_ATAI_1 > fasterMA_ATAI_2)
{
orderPtn=1;
}
//MAのデッドクロスでショート(短期MAが下を向いている事)
else if(fasterMA_ATAI_1 < MediumMA_ATAI_1 && fasterMA_ATAI_2 > MediumMA_ATAI_2 && fasterMA_ATAI_1 < fasterMA_ATAI_2)
{
orderPtn=2;
}
else
{
orderPtn=0;
}
//***売買判断箇所***//
//***決済判断箇所***//
total=OrdersTotal();
if(total ==0 && orderPtn > 0)
{
if(orderPtn == 1)
{
ea_order_stop_price = MediumMA_ATAI_1 - SL_GOSA * Point;
ea_order_good_price = Ask + ((Ask - ea_order_stop_price) * 1000 * Point);
}
else if(orderPtn == 2)
{
ea_order_stop_price = MediumMA_ATAI_1 + 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;
}
//+------------------------------------------------------------------+
//|【関数】新規注文関数 |
//| |
//|【引数】 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;
Print(Ask,"でロングするよ!");
}else if(ea_order_entry_Type == OP_SELL){
ea_order_entry_price = Bid; // 現在の売値でエントリー
order_Color = clrRed;
Print(Bid,"でショートするよ!");
}
// 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 // オーダーアイコンカラー
);
Print("結果:"+ea_ticket_res);
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;
}
}else{
Print("スプレッドNG 許容スプレッド:"+A_SPREAD+"pips未満 環境スプレッド:"+MarketInfo(NULL,MODE_SPREAD)+"pips");
Sleep(2000); // 2000msec待ち
RefreshRates();
}
}
return kekka;
}
//+------------------------------------------------------------------+
//| 勝敗チェック関数 |
//+------------------------------------------------------------------+
void CheckTradeResults()
{
static int lastCheckedTicket = -1;
for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if (OrderSymbol() != Symbol()) continue; // シンボル確認
if (OrderCloseTime() <= lastTradeDay) break; // すでにチェック済み
if (OrderTicket() == lastCheckedTicket) break; // 同じものはスキップ
if (OrderProfit() < 0)
{
consecutiveLosses++;
}
else
{
consecutiveLosses = 0; // 勝ったら連敗リセット
}
lastTradeDay = OrderCloseTime();
lastCheckedTicket = OrderTicket();
break; // 1回のループで1つチェック
}
}
}
さいごに
以上、『連敗したらその日はエントリーしなくする処理』でした。
今回はChatGPTを使って処理を作りましたが、めちゃくちゃ簡単に作れてしまうので便利ですよね。最近は、とりあえずベースをChatGPTに作ってもらって手直しするというのが私の作り方のメインになっています。
※ EAのサンプルソースを一覧表にまとめました
※EA作成依頼はこちら
※オンラインレッスンやってます
コメント