【一目均衡表編】FXテクニカル手法をChatGPTでEA化する【ノーコードEA開発】
■YouTubeで動画再生
https://youtu.be/4YXUEZtUKug
ChatGPT担当のナナミです。
前回の記事では、12本の移動平均線を使用するテクニカル指標、GMMA(複合型移動平均線)のEAを開発しました。
GPTsのリンクはこちら(GPT Store)
https://chatgpt.com/g/g-VYNEupgws-masayan-ea-generator-for-mt4-ver1-01
■ブログURL
https://fx.reform-network.net/2024/06/11/gpts「masayan-ea-generator-for-mt4-」条件を指定するだけで高性能なeaを生/
■前回記事
https://note.com/aimjey/n/nd82c00b04def
本日使用するテクニカル指標は、一目均衡表です。
わたしは、一目均衡表を用いたEAの開発経験があります。
Ichimoku SignalTrade GBPJPY(有料EA)
https://www.gogojungle.co.jp/systemtrade/fx/30755
Ichimoku SignalTrade(イチモク・シグナルトレード)のロジックは、いわゆる朝スキャのロジックを採用しています。
一目均衡表を使用したEAの場合、順張りロジックを採用するのか、逆張りロジックを採用するのか決める必要があります。
単純にChatGPTに「一目均衡表を使ったEAを作成して」とプロンプトを送信しただけでは、順張りロジックのEAが生成されます。
■ChatGPTが考えたロジック
転換線が基準線を上抜けした場合、ロングシグナル(買い)
転換線が基準線を下抜けした場合、ショートシグナル(売り)
生成されたMQL4プログラムコード(1時間足チャート用)
//+------------------------------------------------------------------+
//| Masayan EA Generator_1.01.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Masayan."
#property version "1.01"
#property strict
#property description "https://fx.reform-network.net"
extern int Magic = 20240610;// Magic number
extern double Lots = 0.1;//1.0=100000 0.1=10000 0.01=1000
extern double StopLossRequest = 4.0;// StopLoss 0.5=50pips 1.0=100pips 10=1000pips
extern double TakeProfitRequest = 5.0;// TakeProfit 0.5=50pips 1.0=100pips 10=1000pips
extern int MaxSpread = 50;// Max spread (50=5pips)
extern int MaxError = 100;// Continuous order count limit (Max=100)
extern string CommentPositions = "Masayan EA Generator";
//ここから改変可能
extern int TenkanPeriod = 19;
extern int KijunPeriod = 51;
extern int SenkouSpanBPeriod = 52;
//ここまで改変可能
string tmpstr;
string error_msg;
string spread_msg;
string position_msg;
bool LongSign = false;
bool ShortSign = false;
double Pips = 0.01;
int e = 0;
int Adjusted_Slippage= 0;
// ティックが動くごとに処理
void OnTick()
{
if(Bars < 10)
{
return;
}
if(StopLossRequest <= 0.1)
{
StopLossRequest = 0.1;
}
if(TakeProfitRequest <= 0.1)
{
TakeProfitRequest = 0.1;
}
if(MaxError >= 100)
{
MaxError = 100;
}
double Info_Spread = MarketInfo(Symbol(),MODE_SPREAD);
string value = Symbol();
string target = "JPY";
int pos = StringFind(value, target);
double Symbol_RATE = Close[1];
if(pos > 0)
{
Pips = 1.00;// ドルストレートは0.010(100pips)、クロス円は1.0(100pips)で判定
}else if(Symbol_RATE > 10 && Symbol_RATE <= 100)
{
Pips = 0.1;
}else if(Symbol_RATE > 100 && Symbol_RATE <= 1000)
{
Pips = 1.0;
}else if(Symbol_RATE > 1000 && Symbol_RATE <= 10000)
{
Pips = 10.0;
}else if(Symbol_RATE > 10000 && Symbol_RATE <= 100000)
{
Pips = 100.0;
}else if(Symbol_RATE > 100000 && Symbol_RATE <= 1000000)
{
Pips = 1000.0;
}else if(Symbol_RATE > 1000000 && Symbol_RATE <= 10000000)
{
Pips = 10000.0;
}else if(Symbol_RATE > 10000000 && Symbol_RATE <= 100000000)
{
Pips = 100000.0;
}else if(Symbol_RATE > 100000000)
{
Pips = 1000000.0;
}
//ここから改変可能
double tenkanSen = iIchimoku(NULL, 0, TenkanPeriod, KijunPeriod, SenkouSpanBPeriod, MODE_TENKANSEN, 0);
double kijunSen = iIchimoku(NULL, 0, TenkanPeriod, KijunPeriod, SenkouSpanBPeriod, MODE_KIJUNSEN, 0);
if(tenkanSen > kijunSen)
{
LongSign = true;
ShortSign = false;
}
else if(tenkanSen < kijunSen)
{
ShortSign = true;
LongSign = false;
}
//ここまで改変可能
if(MaxSpread < Info_Spread)
{
LongSign = false;
ShortSign = false;
spread_msg = "Max spread Orber\n";
}
else
{
spread_msg = "";
}
if(Hour() == 4 && Minute() == 0 && Seconds() < 6)
{
e = 0;
}
if(Hour() == 11 && Minute() == 0 && Seconds() < 6)
{
e = 0;
}
if(Hour() == 18 && Minute() == 0 && Seconds() < 6)
{
e = 0;
}
if(e > MaxError)
{
LongSign = false;
ShortSign = false;
error_msg = "Continuous order count limit\n";
}
else
{
error_msg = "";
}
if(LongSign == true)
{
position_msg = "LongSign = true\n";
}
else if(ShortSign == true)
{
position_msg = "ShortSign = true\n";
}
else if(LongSign == false && ShortSign == false)
{
position_msg = "No Sign\n";
}
//売買指示
if(LongSign == true)
{
if(CalculateCurrentOrders() == 0)
{
// ポジション保有していない場合
CheckForOpenLong();// 新規ロングオーダー処理を行う
e ++;
}
else
{
// ポジション保有している場合
CloseShortPosition();// ショートポジションがある場合のみ決済ロング処理
}
}
if(ShortSign == true)
{
if(CalculateCurrentOrders() == 0)
{
// ポジション保有していない場合
CheckForOpenShort();// 新規ショートオーダー処理を行う
e ++;
}
else
{
// ポジション保有している場合
CloseLongPosition();// ロングポジションがある場合のみ決済ショート処理
}
}
tmpstr = StringConcatenate(error_msg,spread_msg,position_msg);
Comment(tmpstr);
}
//保有中のポジションを計算
int CalculateCurrentOrders(void)
{
int buys=0;
int sells=0;
int icount;
for(icount = 0 ; icount < OrdersTotal() ; icount++)
{
if(OrderSelect(icount,SELECT_BY_POS,MODE_TRADES) == false)
{
break;
}
if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
{
if(OrderType()==OP_BUY)
{
buys++;
}
if(OrderType()==OP_SELL)
{
sells++;
}
}
}
if(buys > 0)
{
return(buys);
}
else
{
return(-sells);
}
}
//ショートポジション⇒ロングクローズ処理
void CloseShortPosition()
{
int icount;
bool ret;
for(icount = 0; icount < OrdersTotal(); icount++)
{
if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
{
break;
}
if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
{
continue;
}
//ショートポジションの場合⇒ロングエントリー
if(OrderType() == OP_SELL)
{
ret = OrderClose(
OrderTicket(),
OrderLots(),
Ask,
Adjusted_Slippage,
clrBlue);
if(ret == false)
{
Print("オーダークローズエラー:エラーコード=",GetLastError());
}
break;
}
}
}
//ロングポジション⇒ショートクローズ処理
void CloseLongPosition()
{
int icount;
bool ret;
for(icount = 0; icount < OrdersTotal(); icount++)
{
if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
{
break;
}
if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
{
continue;
}
//ロングポジションの場合⇒ショートエントリー
if(OrderType() == OP_BUY)
{
ret = OrderClose(
OrderTicket(),
OrderLots(),
Bid,
Adjusted_Slippage,
clrRed);
if(ret == false)
{
Print("オーダークローズエラー:エラーコード=",GetLastError());
}
break;
}
}
}
//ロングオーダー処理
void CheckForOpenLong()
{
int res;
double entrylot;
entrylot = NormalizeDouble(Lots,2);
res = OrderSend(
Symbol(),
OP_BUY,
entrylot,
Ask,
Adjusted_Slippage,
Ask - (Pips * StopLossRequest),
Ask + (Pips * TakeProfitRequest),
CommentPositions,
Magic,
0,
clrRed);
return;
}
//ショートオーダー処理
void CheckForOpenShort()
{
int res;
double entrylot;
entrylot = NormalizeDouble(Lots,2);
res = OrderSend(
Symbol(),
OP_SELL,
entrylot,
Bid,
Adjusted_Slippage,
Bid + (Pips * StopLossRequest),
Bid - (Pips * TakeProfitRequest),
CommentPositions,
Magic,
0,
clrBlue);
return;
}
一目均衡表で用いる「転換線」「基準線」「先行スパン」の初期値は以下の通りです。
TenkanPeriod = 9;
KijunPeriod = 26;
SenkouSpanBPeriod = 52;
デフォルト設定のままでは好成績は望めません。
設定項目の最適化を行います。
始値のみで2013年~2023年12月まで(通貨ペアUSDJPY)
1分足 NG(期待値マイナス)
5分足 NG(期待値マイナス)
15分足チャート
TenkanPeriod = 9;
KijunPeriod = 61;
プロフィットファクター1.10
1時間足チャート
TenkanPeriod = 19;
KijunPeriod = 51;
プロフィットファクター1.25
4時間足チャート
TenkanPeriod = 5;
KijunPeriod = 16;
プロフィットファクター1.19
トレードのイメージとしては、ゴールデンクロスでロング、デッドクロスでショートと同じ様な感じですね。
2013年1月から2023年12月までの11年間のバックテストではプラスになりました。
ちなみに2007年からのバックテストでは、優位性が確認できませんでした。
プロフィットファクターで言えば、1時間足チャートが一番成績が良いみたいです。
順張りトレードの場合、成績だけで言えば移動平均線を使用したほうが良い結果が得られます。
実は、一目均衡表を使ったトレード手法には、逆張り手法が存在します。
それが、Ichimoku SignalTradeのロジックで朝スキャのEAになります。
Ichimoku SignalTradeのロジックは、ゴゴジャンの販売ページに記載があるので参考にしてプロンプトを考えます。
日本時間の3:00~8:59にエントリーとあります。
ここでEAクリエイターを悩ませる時間の処理が立ちふさがります。
現在のChatGPTをもってしても、簡単にはいきません。
日本にはサマータイムはありません。
ロンドンや米国にはサマータイムはあります。
また、MT4のサーバー時間はキプロス時間を基準としており、GMT+2(冬)/+3(夏)です。
日本時間の何時という指定をするには、時差とサマータイムを考慮しないといけません。
日本を中心に考えると処理が非常に面倒になるので、どうしても日本時間で考えたい人はnoteの記事を参考にEA開発してください。
noteの第5回の記事(夏時間冬時間の計算)
https://note.com/aimjey/n/n1710e9e5bfac
今回は、日本時間にこだわらず、サーバー時間であるキプロス時間のままで開発を進めます。
日本時間の3:00~8:59は夏時間だとマイナス6時間すればサーバー時間に変換できます。
つまり、夏時間だとサーバー時間の21時~2:59になります。
以下が送信するプロンプトです。
一目均衡表を使ったEAを作成して。
転換線の値は「9」
基準線の値は「26」
先行スパンの値は「52」
転換線が基準線の下から上へ抜けたタイミング(ゴールデンクロス達成)かつ1本前のローソク足が陰線でショート、逆に転換線が基準線の上から下へ抜けた時(デッドクロス達成)かつ1本前のローソク足が陽線でロングのポジションを取ります。
サーバー時間の21時から3時までエントリーします。
ゴールデンクロス・デッドクロスは1本前の足と2本前の足を比較して売買判断します。
完成したEAでバックテストをまわしたところ、Ichimoku SignalTradeの成績とは随分とかけ離れたものになりました。
実は、Ichimoku SignalTradeのゴールデンクロス・デッドクロスは1本前の足と2本前の足を比較して売買判断していますが、ChatGPTが生成したコードは、0本前の足と1本前の足でゴールデンクロス・デッドクロスの比較をしていました。
きちんとプロンプトで指示したつもりでしたが、複雑なプロンプトほど正しく理解してもらえず、間違ったコードを生成する確率が上がります。
今回は手動で修正しました。
修正したコードこちらです。
//+------------------------------------------------------------------+
//| Masayan EA Generator_1.01.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Masayan."
#property version "1.01"
#property strict
#property description "https://fx.reform-network.net"
extern int Magic = 20240610;// Magic number
extern double Lots = 0.1;//1.0=100000 0.1=10000 0.01=1000
extern double StopLossRequest = 4.0;// StopLoss 0.5=50pips 1.0=100pips 10=1000pips
extern double TakeProfitRequest = 5.0;// TakeProfit 0.5=50pips 1.0=100pips 10=1000pips
extern int MaxSpread = 50;// Max spread (50=5pips)
extern int MaxError = 100;// Continuous order count limit (Max=100)
extern string CommentPositions = "Masayan EA Generator";
// ここから改変可能
extern int TenkanSenPeriod = 9;
extern int KijunSenPeriod = 26;
extern int SenkouSpanBPeriod = 52;
// ここまで改変可能
string tmpstr;
string error_msg;
string spread_msg;
string position_msg;
bool LongSign = false;
bool ShortSign = false;
double Pips = 0.01;
int e = 0;
int Adjusted_Slippage = 0;
// ティックが動くごとに処理
void OnTick()
{
if (Bars < 52)
{
return;
}
if (StopLossRequest <= 0.1)
{
StopLossRequest = 0.1;
}
if (TakeProfitRequest <= 0.1)
{
TakeProfitRequest = 0.1;
}
if (MaxError >= 100)
{
MaxError = 100;
}
double Info_Spread = MarketInfo(Symbol(), MODE_SPREAD);
string value = Symbol();
string target = "JPY";
int pos = StringFind(value, target);
double Symbol_RATE = Close[1];
if (pos > 0)
{
Pips = 1.00; // ドルストレートは0.010(100pips)、クロス円は1.0(100pips)で判定
}
else if (Symbol_RATE > 10 && Symbol_RATE <= 100)
{
Pips = 0.1;
}
else if (Symbol_RATE > 100 && Symbol_RATE <= 1000)
{
Pips = 1.0;
}
else if (Symbol_RATE > 1000 && Symbol_RATE <= 10000)
{
Pips = 10.0;
}
else if (Symbol_RATE > 10000 && Symbol_RATE <= 100000)
{
Pips = 100.0;
}
else if (Symbol_RATE > 100000 && Symbol_RATE <= 1000000)
{
Pips = 1000.0;
}
else if (Symbol_RATE > 1000000 && Symbol_RATE <= 10000000)
{
Pips = 10000.0;
}
else if (Symbol_RATE > 10000000 && Symbol_RATE <= 100000000)
{
Pips = 100000.0;
}
else if (Symbol_RATE > 100000000)
{
Pips = 1000000.0;
}
// ここから改変可能
double tenkan_sen = iIchimoku(NULL, 0, TenkanSenPeriod, KijunSenPeriod, SenkouSpanBPeriod, MODE_TENKANSEN, 1);
double kijun_sen = iIchimoku(NULL, 0, TenkanSenPeriod, KijunSenPeriod, SenkouSpanBPeriod, MODE_KIJUNSEN, 1);
double prev_tenkan_sen = iIchimoku(NULL, 0, TenkanSenPeriod, KijunSenPeriod, SenkouSpanBPeriod, MODE_TENKANSEN, 2);
double prev_kijun_sen = iIchimoku(NULL, 0, TenkanSenPeriod, KijunSenPeriod, SenkouSpanBPeriod, MODE_KIJUNSEN, 2);
double prev_close = Close[1];
double prev2_close = Close[2];
bool golden_cross = (prev_tenkan_sen <= prev_kijun_sen) && (tenkan_sen > kijun_sen) && (prev_close < Open[1]);
bool dead_cross = (prev_tenkan_sen >= prev_kijun_sen) && (tenkan_sen < kijun_sen) && (prev_close > Open[1]);
int current_hour = Hour();
if ((current_hour >= 21 || current_hour < 3))
{
if (golden_cross)
{
ShortSign = true;
LongSign = false;
}
else if (dead_cross)
{
LongSign = true;
ShortSign = false;
}
}
// ここまで改変可能
if (MaxSpread < Info_Spread)
{
LongSign = false;
ShortSign = false;
spread_msg = "Max spread Order\n";
}
else
{
spread_msg = "";
}
if (Hour() == 4 && Minute() == 0 && Seconds() < 6)
{
e = 0;
}
if (Hour() == 11 && Minute() == 0 && Seconds() < 6)
{
e = 0;
}
if (Hour() == 18 && Minute() == 0 && Seconds() < 6)
{
e = 0;
}
if (e > MaxError)
{
LongSign = false;
ShortSign = false;
error_msg = "Continuous order count limit\n";
}
else
{
error_msg = "";
}
if (LongSign == true)
{
position_msg = "LongSign = true\n";
}
else if (ShortSign == true)
{
position_msg = "ShortSign = true\n";
}
else if (LongSign == false && ShortSign == false)
{
position_msg = "No Sign\n";
}
// 売買指示
if (LongSign == true)
{
if (CalculateCurrentOrders() == 0)
{
// ポジション保有していない場合
CheckForOpenLong(); // 新規ロングオーダー処理を行う
e++;
}
else
{
// ポジション保有している場合
CloseShortPosition(); // ショートポジションがある場合のみ決済ロング処理
}
}
if (ShortSign == true)
{
if (CalculateCurrentOrders() == 0)
{
// ポジション保有していない場合
CheckForOpenShort(); // 新規ショートオーダー処理を行う
e++;
}
else
{
// ポジション保有している場合
CloseLongPosition(); // ロングポジションがある場合のみ決済ショート処理
}
}
tmpstr = StringConcatenate(error_msg, spread_msg, position_msg);
Comment(tmpstr);
}
// 保有中のポジションを計算
int CalculateCurrentOrders(void)
{
int buys = 0;
int sells = 0;
int icount;
for (icount = 0; icount < OrdersTotal(); icount++)
{
if (OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
{
break;
}
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
{
if (OrderType() == OP_BUY)
{
buys++;
}
if (OrderType() == OP_SELL)
{
sells++;
}
}
}
if (buys > 0)
{
return (buys);
}
else
{
return (-sells);
}
}
// ショートポジション⇒ロングクローズ処理
void CloseShortPosition()
{
int icount;
bool ret;
for (icount = 0; icount < OrdersTotal(); icount++)
{
if (OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
{
break;
}
if (OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
{
continue;
}
// ショートポジションの場合⇒ロングエントリー
if (OrderType() == OP_SELL)
{
ret = OrderClose(
OrderTicket(),
OrderLots(),
Ask,
Adjusted_Slippage,
clrBlue);
if (ret == false)
{
Print("オーダークローズエラー:エラーコード=", GetLastError());
}
break;
}
}
}
// ロングポジション⇒ショートクローズ処理
void CloseLongPosition()
{
int icount;
bool ret;
for (icount = 0; icount < OrdersTotal(); icount++)
{
if (OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
{
break;
}
if (OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
{
continue;
}
// ロングポジションの場合⇒ショートエントリー
if (OrderType() == OP_BUY)
{
ret = OrderClose(
OrderTicket(),
OrderLots(),
Bid,
Adjusted_Slippage,
clrRed);
if (ret == false)
{
Print("オーダークローズエラー:エラーコード=", GetLastError());
}
break;
}
}
}
// ロングオーダー処理
void CheckForOpenLong()
{
int res;
double entrylot;
entrylot = NormalizeDouble(Lots, 2);
res = OrderSend(
Symbol(),
OP_BUY,
entrylot,
Ask,
Adjusted_Slippage,
Ask - (Pips * StopLossRequest),
Ask + (Pips * TakeProfitRequest),
CommentPositions,
Magic,
0,
clrRed);
return;
}
// ショートオーダー処理
void CheckForOpenShort()
{
int res;
double entrylot;
entrylot = NormalizeDouble(Lots, 2);
res = OrderSend(
Symbol(),
OP_SELL,
entrylot,
Bid,
Adjusted_Slippage,
Bid + (Pips * StopLossRequest),
Bid - (Pips * TakeProfitRequest),
CommentPositions,
Magic,
0,
clrBlue);
return;
}
今回作成した朝スキャEAとIchimoku SignalTradeは、安定相場で利益が狙えるEAです。
2020年以降のボラティリティの大きな時期は勝てないので、相場が安定してきたら動かすといいです。
最後に、ゴゴジャンで販売中のEA「Ichimoku SignalTrade」と今回作成した朝スキャEAのバックテスト結果を公開します。
有料版のIchimoku SignalTradeの方が成績が良いですが、TDSによるスプレッド変動でバックテストすると似たような成績になりました。
注意点として、計測期間が2010年から2019年ということです。
現在の相場では勝てません。また、2008年のリーマンショックの相場でも勝てません。
安定相場で勝つ、朝スキャEAとなりますので、時期が来たら動かすといいです。
今の様な大荒れ相場では、最初に作成した順張りロジックのEAのほうが好成績を収めることが出来ます。
投資の判断は自己責任でお願いします。
無料のEAにはサポートは付きません。
サポートをご希望のかたは、ゴゴジャンで有料のEAをご購入下さい。
次回の動画では、わたしの得意とするテクニカル指標のボリンジャーバンドを用いたEA開発を行います。
ボリンジャーバンドは順張り手法と逆張り手法の両方で使える数少ないテクニカル指標です。
個人的にはシグマタッチは2σ(シグマ)で判定することが多いです。
順張りトレードの時は2シグマにタッチで順張り方向にエントリーし、逆張りトレードの時も2シグマにタッチでトレンドとは逆方向にエントリーします。
ボリンジャーバンド単体で売買判断しても良いのですが、他のテクニカル指標と組み合わせることが多いです。
Bolivan EAというわたしが開発したEAのロジックを使用し、順張り型のEAと、逆張り型のEAの開発を行いたいと思います。
Bolivan EA
https://www.gogojungle.co.jp/systemtrade/fx/49202
Bolivan EA(ボリバンEA)は、順張り時はボリンジャーバンドと移動平均線を使用し、逆張り時はボリンジャーバンドとRSIを使用したマルチロジックEAです。
また、今後EA化するテクニカル指標は以下の通りです。
MACD
RSI
ストキャスティクス
フィボナッチリトレースメント
ZigZag
【免責事項】
・本GPTsについて、正当性を保証するものではありません。
・本GPTsを利用して損失を被った場合でも一切の責任を負いません。
・投資の決定は、自己判断 自己責任でお願いします。
この記事が気に入ったらサポートをしてみませんか?