
EA化に成功したのはどっち?天底を当てるMT4インジケーター「ASCTrend」と「フラクタルズ」をEAにしてみた!
やりました!天底を当てる人気のMT4インジケーター「ASCTrend」と「フラクタルズ」のEA化に成功しました。
完成したEAは無料でプレゼントします。
記事の後半で、EAのコードを公開しています。
本日は、「ChatGPTでMT4のインジケーターを開発する」シリーズということで、MT4インジケーター「ASCTrend」と「フラクタルズ」のロジックをEAに変換してバックテストするやり方を徹底解説したいと思います。
「ASCTrend」と「フラクタルズ」のEA化については、以前ankimo6666さんからリクエストのあったお題になります。

MT4を使っている人なら、「ASCTrend」と「フラクタルズ」をご存じの方も多いかと思います。
いわゆる、天底を当てるインジケーターになります。
GPTsのリンクはこちら(GPT Store)
■Masayan EA Generator for MT4
https://chatgpt.com/g/g-VYNEupgws-masayan-ea-generator-for-mt4-ver1-01
■Masayan Indicator Generator for MT4
https://chatgpt.com/g/g-t1m5iewUY-masayan-indicator-generator-for-mt4
■ゴゴジャンでEA・インジケーター販売中!
https://www.gogojungle.co.jp/users/626040/products
■ブログURL(MT4初心者向け!GPTsのMasayan EA Generatorを使った自動売買講座)
https://fx.reform-network.net/2024/10/16/mt4初心者向け!gptsのmasayan-ea-generatorを使った自動売買講座/
■ブログURL【無料】MT4対応の新ツール登場!ノーコードでインジケーター作成!
https://fx.reform-network.net/2024/10/30/【無料】mt4対応の新ツール登場!ノーコードでイ/
まず初めに、インジケーターの特徴を簡単に説明します。
ASCTrendは、トレンドフォロー型のシグナルインジケーターで、相場の波が切り替わるポイントでシグナルを表示します。

ラリー・ウィリアムズが考案したWilliams %Rを基にしており、WPRが特定の値を超えると買いシグナル、下回ると売りシグナルが出現します。
フラクタルズは、ビル・ウィリアムズ氏が考案したインジケーターで、相場の高値や安値を特定する際に使用されます。

5本のローソク足を比較し、中央のローソク足が最も高い場合に「Fractal Up」、最も低い場合に「Fractal Down」として表示されます。
トレンドの押し目や戻りのポイントを視覚的に捉えることができるインジケーターになります。
以前作成したEAでは以下の2つの問題があり、EA化に失敗しました。
1、フラクタルズの高値安値が拾えない問題。
2、ASCTrendのレートが拾えない問題。
この問題は、フラクタルズやASCTrendのレートが取得できないことが原因で起こった問題でした。
しかしながら、フラクタルズやASCTrendの関数をEA化する場合、シフトの数、つまり現在のバーとか1本前のバーの値を取得するのではなく、2本以上前のバーで値を取得することでEAが機能することが分かりました。

試しに現在のバーの値0を2にしてみます。
2本前のローソク足でフラクタルズやASCTrendの値を取得することで、きちんとエントリーするEAが出来ます。
ですが、逆指値の注文の無限ループが発生し、EAが止まります。
複雑なロジックはChatGPTは苦手なようなので、今回は手動でコードを書き変えて調整します。
以下がロジック部分になります。
if (ascValue > -30 && Close[1] > fractalHigh) {// ロングエントリー条件
LongSign = true;
ShortSign = false;
} else if (ascValue < -70 && Close[1] < fractalLow) {// ショートエントリー条件
LongSign = false;
ShortSign = true;
} else {
LongSign = false;
ShortSign = false;
}
ロングエントリーの条件は、ASCトレンドが-30以上で1本前の終値がフラクタルズの高値を上に抜けたタイミングでエントリーとなります。
ショートエントリーの条件は、ASCトレンドが-70以下で1本前の終値がフラクタルズの安値を下に抜けたタイミングでエントリーとなります。
ですが、この条件でバックテストをまわすと、ロングのサインしか出ないEAになってしまいます。
ショートの条件にあてはまらない理由を調べます。
チャートの左上のコメント部分に、フラクタルズとASCトレンドの値を表示できるようにします。
値が取得できるとascValueとfractalHighとfractalLowにレートが表示されます。
原因はascValueの値が正しく取得できていないことだと分かりました。
ASCトレンドのバーの位置を5つ前のローソク足にしてみます。
なるほど、一瞬ですがASCトレンドの値が表示されました。
売買サインが点灯した瞬間だけ数字が入るみたいです。
ですが、またしてもここで問題が発生。
ASCトレンドの値が0からマイナス100になるはずが、なぜかレートが表示されているではありませんか。
ASCトレンドの値がドル円のレートになってしまう問題が発生しました。
それにしても、ASCトレンドの値は -30とか-70になるはずですが、ChatGPTに以下の関数の意味について聞いてみます。
ascValue = iCustom(NULL, PERIOD_H1, "ASCTrend", ASCTrendRisk, 0, 5);
なるほど、, 0, 5);の部分の0がバッファ番号なのですね。
ここを1とか2にするとどうなるの?と聞いてみます。
バッファ番号1は上昇トレンドデータの可能性があり、バッファ番号2は下降トレンドデータの可能性があるとの返答が。
バックテストして詳しく調べてみます。
と、ここでもまたしても問題が発生、ASC Trendの関数を使用すると、処理が多いからなのか、バックテストが重すぎてテストできないという問題に直面。
試しに、ASC Trendを消してフラクタルズ単体でバックテストしたところ、なんと普通にトレードするではありませんか!
ということは、パラメーターを調整することでフラクタルズを使用したEAを作ることが出来ます。
2008年10月24日のリーマンショック後のセリングクライマックスの時のバックテストです。
ドル円のバックテストなのですが、相場の下落局面でもロングのポジションでエントリーしており、順張りなのか逆張りなのかよくわかりません。
最初は順張り方向にショートを打ちましたが、その次は逆張りでロングしています。
ストップとリミットの値を10、つまり1000pipsに拡大してみます。
やっぱり順張りなのか逆張りなのか特徴つかめません。
コードを見てみると、フラクタルズの値は4時間足をベースにしていることが原因だと分かりました。
ということで、時間軸は表示しているチャートの時間軸にすべく、ここのところはゼロにします。
なるほど、フラクタルズの高値安値は、2本前のバーの値を参照することから、サインが出てから2本バーが過ぎてから売買することになっています。
ですが、このようにバーの値を1にすると、EAではフラクタルズの高値安値は拾えない問題が発生してしまい、トレードしないEAになってしまいます。
つまり、EAでフラクタルズを使用するということは、ローソク足2本分遅れてエントリーすることになり、なんだか変なEAになってしまうわけです。
2007年からのTDSによる全ティックバックテストの結果はこちらです。

通常のEAでは、順張りや逆張り、戻り売りや押し目買いなどの戦略をとることが一般的とされます。
ですがこのEA、フラクタルズの高値安値をブレイクするタイミングというのがEAではうまくとらえることが出来ず、中途半端な成績になってしまいます。
様々なテクニカル指標を追加することで、こうした遅延がいい方向に進む可能性はありますが、このままでは優位性はありません。
そう、一目均衡表の雲のようなイメージですね。

幸い、このフラクタルズのEAは、1分足や5分足チャートで動かすと、ものすごくトレード回数の多いEAになります。
何らかのフィルターを追加することで勝てるEAに大化けする可能性はあります。
完成したフラクタルズのEAのソースコードは、以下よりダウンロードできます。
#property copyright "Copyright 2024, Masayan."
#property version "1.01"
#property strict
#property description "https://fx.reform-network.net"
extern int Magic = 20241120; // 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 10=1000pips
extern double TakeProfitRequest = 5.0;// TakeProfit 0.5=50pips 10=1000pips
extern int MaxSpread = 50; // Max spread (50=5pips)
extern int MaxError = 100; // Continuous order count limit (Max=100)
extern string CommentPositions = "Fractals";
//ここから改変可能
extern int FractalsBuffer = 2; // 何本前のバーの値で測定するか
//ここまで改変可能
bool AscLong = false;
bool AscShort = false;
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 (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;
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) Pips = 100.0;
//ここから改変可能
LongSign = false;
ShortSign = false;
double fractalHigh = 0.0;
double fractalLow = 0.0;
//ここから改変可能
// フラクタルズの高値安値取得
fractalHigh = iFractals(NULL, 0, MODE_UPPER, FractalsBuffer);
fractalLow = iFractals(NULL, 0, MODE_LOWER, FractalsBuffer);
if (fractalHigh) {// ロングエントリー
LongSign = true;
ShortSign = false;
} else if (fractalLow) {// ショートエントリー
LongSign = false;
ShortSign = true;
} else {
LongSign = false;
ShortSign = 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(" FractalHigh ",fractalHigh,"\n FractalLow ",fractalLow,"\n",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 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;
}
// ショートポジション⇒ロングクローズ処理
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;
}
}
}
そのままでは勝てないEAなので、カスタマイズするなどしてご利用いただければと思います。
【次回予告】
遂に完成!マルチロジックFXインジケーター【ダウンロードは無料】というタイトルで動画を撮っていこうと思います。
簡単に内容を説明しますと、EAにはマルチロジックEAってのがありますよね。
いわゆる複数のロジックで動かすタイプのEAです。
例えば、朝は逆張り、夜は順張りみたいなやつです。
インジケーターだと、裁量トレードになるので、このような感じのインジケーターが考えられます。
1、順張りと逆張りのロジックを内蔵している。
2、利用者がプロパティ設定で順張りと逆張りを切り換える。
3、売買サインは順張りか逆張りどちらか片方のロジックを表示。
つまり、トレーダーが自分で環境認識を行い、今は順張り有利とか、反対に逆張り手法が有効などでトレードするといったやり方です。
インジケーターを2つ用意するよりも、1つのインジケーターで切り替えた方が管理が簡単です。
というわけで、次回の動画ではマルチロジックインジケーターなるものを作ってみたいと思います。
他にも、こんな企画やってほしいみたいなリクエストがあれば、コメントお願いします。
・本GPTsについて、正当性を保証するものではありません。
・本GPTsを利用して損失を被った場合でも一切の責任を負いません。
・投資の決定は、自己判断 自己責任でお願いします。