【第1回】ChatGPT最新AIモデル GPT-4oを使ったEA開発方法【EA開発】
こんにちは、ChatGPT担当のナナミです。
前回の記事では、Copilotを使いゴールデンクロスでロング、デッドクロスでショートのポジションを持つEAを紹介しました。
ChatGPTを使えば、様々な不具合を修正できるので、今回はその一例をご紹介いたします。
以下のMQL4プログラムは、ゴールデンクロスでロング、デッドクロスでショートのポジションを持つEAのプログラムソースコードです。
ゴールデンクロスとデッドクロスが頻繁に入れ替わる時に、エントリーと決済を短時間で行う不具合があります。
修正するにはどうすればよいですか?とChatGPTに質問してみます。
//+------------------------------------------------------------------+
//| Moving Average.mq4 |
//| Copyright 2005-2014, MetaQuotes Software Corp. |
//| http://www.mql4.com |
//+------------------------------------------------------------------+
#define MAGICMA 20131111
//--- Inputs
input double Lots =0.1;
input double MaximumRisk =0.02;
input double DecreaseFactor=3;
input int MovingPeriod =12;
input int MovingShift =6;
int ShortMovingPeriod = 90;
int LongMovingPeriod = 300;
//+------------------------------------------------------------------+
//| Calculate open positions |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
int buys=0,sells=0;
//---
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)
{
if(OrderType()==OP_BUY) buys++;
if(OrderType()==OP_SELL) sells++;
}
}
//--- return orders volume
if(buys>0) return(buys);
else return(-sells);
}
//+------------------------------------------------------------------+
//| Calculate optimal lot size |
//+------------------------------------------------------------------+
double LotsOptimized()
{
double lot=Lots;
int orders=HistoryTotal(); // history orders total
int losses=0; // number of losses orders without a break
//--- select lot size
lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);
//--- calcuulate number of losses orders without a break
if(DecreaseFactor>0)
{
for(int i=orders-1;i>=0;i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)
{
Print("Error in history!");
break;
}
if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
continue;
//---
if(OrderProfit()>0) break;
if(OrderProfit()<0) losses++;
}
if(losses>1)
lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
}
//--- return lot size
if(lot<0.1) lot=0.1;
return(lot);
}
//+------------------------------------------------------------------+
//| Check for open order conditions |
//+------------------------------------------------------------------+
void CheckForOpen()
{
double shortMA, longMA;
int res;
// Get Moving Averages
shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
// Buy conditions (Golden Cross)
if (shortMA > longMA)
{
res = OrderSend(Symbol(), OP_BUY, LotsOptimized(), Ask, 3, 0, 0, "", MAGICMA, 0, Blue);
return;
}
// Sell conditions (Dead Cross)
if (shortMA < longMA)
{
res = OrderSend(Symbol(), OP_SELL, LotsOptimized(), Bid, 3, 0, 0, "", MAGICMA, 0, Red);
return;
}
}
//+------------------------------------------------------------------+
//| Check for close order conditions |
//+------------------------------------------------------------------+
void CheckForClose()
{
double shortMA, longMA;
// Get Moving Averages
shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
for (int i = 0; i < OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false)
break;
if (OrderMagicNumber() != MAGICMA || OrderSymbol() != Symbol())
continue;
// Check order type
if (OrderType() == OP_BUY)
{
if (shortMA < longMA)
{
if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, White))
Print("OrderClose error ", GetLastError());
}
break;
}
if (OrderType() == OP_SELL)
{
if (shortMA > longMA)
{
if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, White))
Print("OrderClose error ", GetLastError());
}
break;
}
}
}
//+------------------------------------------------------------------+
//| OnTick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- check for history and trading
if(Bars<100 || IsTradeAllowed()==false)
return;
//--- calculate open orders by current symbol
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForClose();
//---
}
//+------------------------------------------------------------------+
すると、改善されたプログラムソースコードを書き出してくれました!
素晴らしい!
以下の点を導入しました:
TradeDelayという変数を導入し、最後のトレードから一定期間(バー数)内では新しいエントリーを行わないようにしました。
ゴールデンクロスとデッドクロスの確認に、前のバーのクロスをチェックする関数PreviousGoldenCrossおよびPreviousDeadCrossを追加しました。
これにより、頻繁なエントリーと決済を防ぎ、より安定したトレードが可能になります。
//+------------------------------------------------------------------+
//| Moving Average.mq4 |
//| Copyright 2005-2014, MetaQuotes Software Corp. |
//| http://www.mql4.com |
//+------------------------------------------------------------------+
#define MAGICMA 20131111
//--- Inputs
input double Lots =0.1;
input double MaximumRisk =0.02;
input double DecreaseFactor=3;
input int MovingPeriod =12;
input int MovingShift =6;
input int ShortMovingPeriod = 90;
input int LongMovingPeriod = 300;
input int TradeDelay = 10; // リトレード防止期間(バー数)
datetime LastTradeTime = 0; // 最後のトレード時間
//+------------------------------------------------------------------+
//| Calculate open positions |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
int buys=0,sells=0;
//---
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)
{
if(OrderType()==OP_BUY) buys++;
if(OrderType()==OP_SELL) sells++;
}
}
//--- return orders volume
if(buys>0) return(buys);
else return(-sells);
}
//+------------------------------------------------------------------+
//| Calculate optimal lot size |
//+------------------------------------------------------------------+
double LotsOptimized()
{
double lot=Lots;
int orders=HistoryTotal(); // history orders total
int losses=0; // number of losses orders without a break
//--- select lot size
lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);
//--- calculate number of losses orders without a break
if(DecreaseFactor>0)
{
for(int i=orders-1;i>=0;i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)
{
Print("Error in history!");
break;
}
if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
continue;
//---
if(OrderProfit()>0) break;
if(OrderProfit()<0) losses++;
}
if(losses>1)
lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
}
//--- return lot size
if(lot<0.1) lot=0.1;
return(lot);
}
//+------------------------------------------------------------------+
//| Check for open order conditions |
//+------------------------------------------------------------------+
void CheckForOpen()
{
double shortMA, longMA;
int res;
// Get Moving Averages
shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
// リトレード防止期間のチェック
if (TimeCurrent() - LastTradeTime < TradeDelay * PeriodSeconds())
return;
// Buy conditions (Golden Cross)
if (shortMA > longMA && PreviousGoldenCross())
{
res = OrderSend(Symbol(), OP_BUY, LotsOptimized(), Ask, 3, 0, 0, "", MAGICMA, 0, Blue);
if(res > 0) LastTradeTime = TimeCurrent(); // 最後のトレード時間を更新
return;
}
// Sell conditions (Dead Cross)
if (shortMA < longMA && PreviousDeadCross())
{
res = OrderSend(Symbol(), OP_SELL, LotsOptimized(), Bid, 3, 0, 0, "", MAGICMA, 0, Red);
if(res > 0) LastTradeTime = TimeCurrent(); // 最後のトレード時間を更新
return;
}
}
/*
return (shortMA1 <= longMA1) は、shortMA1 が longMA1
以下の場合に true を返し、それ以外の場合は false を返します。MQL4 では、true は 1 に、false は 0 にそれぞれ相当します。
この条件式の結果は 1 または 0 として解釈されますが、実際には論理値 (true または false) が返されます。
*/
// ゴールデンクロスが確認されたかどうかをチェック
bool PreviousGoldenCross()
{
double shortMA1 = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double longMA1 = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
return (shortMA1 <= longMA1);
}
// デッドクロスが確認されたかどうかをチェック
bool PreviousDeadCross()
{
double shortMA1 = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double longMA1 = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
return (shortMA1 >= longMA1);
}
//+------------------------------------------------------------------+
//| Check for close order conditions |
//+------------------------------------------------------------------+
void CheckForClose()
{
double shortMA, longMA;
// Get Moving Averages
shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
for (int i = 0; i < OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false)
break;
if (OrderMagicNumber() != MAGICMA || OrderSymbol() != Symbol())
continue;
// Check order type
if (OrderType() == OP_BUY)
{
if (shortMA < longMA)
{
if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, White))
Print("OrderClose error ", GetLastError());
}
break;
}
if (OrderType() == OP_SELL)
{
if (shortMA > longMA)
{
if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, White))
Print("OrderClose error ", GetLastError());
}
break;
}
}
}
//+------------------------------------------------------------------+
//| OnTick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- check for history and trading
if(Bars<100 || IsTradeAllowed()==false)
return;
//--- calculate open orders by current symbol
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForClose();
//---
}
//+------------------------------------------------------------------+
分からない部分があれば、続けて質問してみましょう。
return (shortMA1 <= longMA1)は1か0が入りますか?と質問してみます。
return (shortMA1 <= longMA1)
この条件式の結果は 1 または 0 として解釈されますが、実際には論理値 (true または false) が返されます。
との答えが返ってきました。
なるほど、疑問点があればきちんと教えてくれるので、プログラミングスクールの先生みたいなこともできるわけですね。
上記のソースコードでMT4のバックテストをまわしたところ、理想のトレードになったのですが、成績が悪化しました。
おそらく改善案として導入されたフィルターが悪さをしているようです。
ちなみに私が開発したEA「GoldenCross_USDJPY」では成績が悪化することはありませんでした。
GoldenCross_USDJPY
https://www.gogojungle.co.jp/systemtrade/fx/43098
トレンドフォロー型のEAの場合、ゴールデンクロス・デッドクロスよりもパーフェクトオーダーの方が使い勝手が良いので、次回は3本の移動平均線を使ったロジックをChatGPTに作ってもらいたいと思います。