両建てグリッドEAをゼロから作る(第8回・番外編:⑥ゾーン・リカバリー型)

この連載は研究用です。 読み物として楽しんでもらえれば。番外編なので読まなくてよいw

前回のおさらい

第7回の④挟み直し型は、ロットを増やさずに、勝った側の利益で負けた側を少しずつ削る型でした。2025年1月6日に残った売り0.05ロットが初日のうちに削れなくなり、最後まで残りました。

番外編の最後は ⑥ゾーン・リカバリー型 です。挟み直し型とは逆に、負けるたびにロットを大きくします。

 

 

 

第8回:2本の線の間を行き来するたびに、2倍で反対に建てる

項目設定
最初の1本第1回と同じ ADX(1分足と30分足で ADX(14)≥22、DI の向きがそろったらその向き。0.01ロット)
2本の線入った価格が1本目の線。そこから入った向きと逆へ 5ドルのところが2本目の線
反対に建てる価格が反対側の線をまたぐたびに、前の玉の 2倍のロットで、前と反対向きに1本
上限1サイクル 8本まで(0.01 → 0.02 → 0.04 … → 1.28ロット)。8本建てたら、あとは待つ(損切りはしない)
まとめて決済そのサイクルの決済済みの損益と、残っている玉の含み損益(スワップ込み)の合計が +5ドル以上で全部決済
ゾーン・リカバリー型の動き

最後に建てた玉がいちばん大きいので、どちらかの線の外へ抜ければ、その玉の利益でそれまでの負けを埋められる、という考え方です。

 

 

 

結果:3か月半で口座がなくなった

条件は第0回の表のとおりです(XAUUSD、2025-01-01〜2026-07-01、リアルティック、最初のロット 0.01、初期証拠金 50,000 USD)。

項目第7回(挟み直し型)0.01ロット換算第8回(ゾーン・リカバリー型)
1サイクル(始めてから全部決済まで)6回55回
うちプラスで終わったサイクル5回(合計 +25 USD)54回(合計 +288 USD)
決済した回数482回(勝率 98%)133回(勝率 65%)
損益合計−407 USD−50,032 USD
最大DD(含み損込み)−1,550 USD−50,312 USD
含み損込みの一番低いところ初期証拠金から −456 USD−39 USD(2025-04-16、口座がなくなった)
同時に持った玉0.02ロット最大8本・2.55ロット
試行回数 N2930
DSR0.0000.000(N=100 換算 0.000)
資産の推移

最初の3週間で54回のサイクルが +5ドル前後で終わり、残高は 50,267 USD まで増えました。8本目まで建てたサイクルは、それまで1回もありません。7本目まで行ったサイクルは3回ありましたが、どれも抜けて +5ドルで終わっています。

そして55回目のサイクルが、テストを終わらせました。

 

 

 

何が起きたか:1月20日の12時間

2025-01-20 08:46、Goldが2,710.94ドルのところで買いから入りました。2本目の線は5ドル下の2,705.94ドル付近です。この日、Goldはこの2本の線の間を、ちょうど行き来しました。

時刻(サーバー時間)建てた玉価格
08:46買い 0.012,710.94
09:20売り 0.022,705.53
13:08買い 0.042,710.84
13:33売り 0.082,705.44
16:08買い 0.162,710.97
17:02売り 0.322,705.41
21:00買い 0.642,711.97
21:25売り 1.282,705.47

12時間半で8本。最後の玉は 1.28ロットで、最初の玉の128倍です。合わせると、買い0.85ロット・売り1.70ロット、差し引き 売り0.85ロット になりました。

このサイクルを +5ドルで終えるには、Goldが 2,699ドル付近まで下がる必要がありました。2本目の線から、さらに7ドルほど下です。

Goldはそこへは戻りませんでした。1月20日の夜から上がり始め、4月には3,200ドル台に乗ります。売り0.85ロットは、Goldが1ドル上がるごとに 85 USD ずつ損が増えます。3か月で550ドル上がり、含み損込みの資産はゼロに向かって下がり続けました。

2025-04-16 03:01、証拠金が足りなくなって強制ロスカットになり、テストもそこで終わりました。最後の残高は −32 USD です。

強制決済した玉ロット損益(スワップ込み)
買い4本0.85+41,243 USD
売り4本1.70−91,548 USD
このサイクルの合計−50,320 USD

(スワップの向きと大きさは業者や時期で変わるので、ここは割り引いて見てください。)

 

 

 

番外編を通して見ると

回型損益合計最後まで残ったサイクルの始まり
第5回①同時両建て型−11,973 USD2025-02-03
第6回③段階ヘッジ型−10,055 USD2025-01-20
第7回④挟み直し型(0.01ロット換算)−407 USD2025-01-06
第8回⑥ゾーン・リカバリー型−50,032 USD(口座がなくなった)2025-01-20

4つとも、勝ち方は「小さなサイクルをたくさん」で、負け方は「終わらないサイクル1本」でした。そして4つとも、その1本は2025年1〜2月、Goldが上がり始めた入り口(2,600〜2,900ドル台)で始まっています。

違ったのは、その1本が抱えた量です。ロットを増やさない挟み直し型がいちばん浅く、2倍ずつ増やすゾーン・リカバリー型だけが口座を使い切りました。

 

 

 

第8回のまとめ

  • ゾーン・リカバリー型は、2本の線をまたぐたびに2倍のロットで反対に建て、どちらかに抜けたら全体で +5ドルを取る
  • 今回の期間では、2025年1月20日にGoldがちょうど5ドルの幅を行き来し、12時間半で8本・2.55ロットまで建った
  • 差し引き売り0.85ロットが残り、Goldの上昇で3か月後に口座がなくなった
  • ロットを増やす型は、終わらないサイクルが1本来たときの損を、何倍にも大きくする

 

 

 

次回

次回は、ここまでの結果をもう一度ふりかえって、別のやり方と比べます。タイトルは「損切り+再エントリーと比べる」です。

 

 

 

試行回数について

今回は N=30 です。第7回(N=29)の続きで数えています。

 

 

 

ソースコード

研究用です。リアル口座では使わないでください。ログ出力の hg_log.mqh は第1回と同じです。ファイル名の番号(HG11)は作った順の通し番号で、回の番号とはずれています。

HG11_ZoneRecovery.mq5

//+------------------------------------------------------------------+
//| HG11_ZoneRecovery.mq5                                             |
//| Hedged Grid series, chapter 10 (extra): type 6, zone recovery     |
//| Entry : same as HG01 (ADX on M1 and M30, DI direction agrees).    |
//|         The entry price is one edge of the zone, the other edge   |
//|         is InpZone against the entry direction.                   |
//| Zone  : each time price crosses to the other edge, open the       |
//|         opposite direction with the previous lot x InpMult, up to |
//|         InpMaxLevels positions (then wait, no stop loss).         |
//| Exit  : close everything when the cycle result (closed profit of  |
//|         this cycle + floating profit incl. swap) reaches           |
//|         InpBasketTarget.                                          |
//| Research code for the fxwolf2.com blog. Not for live trading.     |
//+------------------------------------------------------------------+
#property copyright "fxwolf2.com"
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>
#include "hg_log.mqh"

input double InpLot          = 0.01;   // First lot
input int    InpAdxPeriodM1  = 14;     // ADX period (M1)
input double InpAdxLevelM1   = 22.0;   // ADX threshold (M1)
input int    InpAdxPeriodHTF = 14;     // ADX period (higher timeframe)
input double InpAdxLevelHTF  = 22.0;   // ADX threshold (higher timeframe)
input ENUM_TIMEFRAMES InpHTF = PERIOD_M30; // Higher timeframe
input double InpZone         = 5.0;    // Zone width (USD)
input double InpMult         = 2.0;    // Lot multiplier for each new level
input int    InpMaxLevels    = 8;      // Max positions in a cycle
input double InpBasketTarget = 5.0;    // Close all when the cycle result reaches this (USD)
input long   InpMagic        = 260811; // Magic number
input string InpTag          = "ch10"; // Log file tag

CTrade   trade;
int      hAdxM1 = INVALID_HANDLE, hAdxHTF = INVALID_HANDLE;
datetime lastEntryBar = 0;
int      dir = 0, nLevels = 0, lastSide = 0; // cycle direction, positions opened, side of the last one
double   anchor = 0, bal0 = 0, lastLot = 0;

int OnInit()
{
   hAdxM1  = iADX(_Symbol, PERIOD_M1, InpAdxPeriodM1);
   hAdxHTF = iADX(_Symbol, InpHTF, InpAdxPeriodHTF);
   if(hAdxM1 == INVALID_HANDLE || hAdxHTF == INVALID_HANDLE) return INIT_FAILED;
   trade.SetExpertMagicNumber(InpMagic);
   trade.SetTypeFillingBySymbol(_Symbol);
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   HGLog_Deinit(InpTag);
}

int AdxSide(const int handle, const double level)
{
   double adx[1], plus[1], minus[1];
   if(CopyBuffer(handle, 0, 1, 1, adx)   != 1) return 0;
   if(CopyBuffer(handle, 1, 1, 1, plus)  != 1) return 0;
   if(CopyBuffer(handle, 2, 1, 1, minus) != 1) return 0;
   if(adx[0] < level) return 0;
   if(plus[0] > minus[0]) return 1;
   if(minus[0] > plus[0]) return -1;
   return 0;
}

int Scan(double &floating)
{
   int n = 0;
   floating = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong t = PositionGetTicket(i);
      if(t == 0 || PositionGetString(POSITION_SYMBOL) != _Symbol
         || PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
      n++;
      floating += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
   }
   return n;
}

void CloseAll()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong t = PositionGetTicket(i);
      if(t > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol
         && PositionGetInteger(POSITION_MAGIC) == InpMagic) trade.PositionClose(t);
   }
}

bool Open(const int side, const double lot, const MqlTick &tick, const string what)
{
   string c = StringFormat("HG11 %s sp=%.2f", what, tick.ask - tick.bid);
   if(side == 1) return trade.Buy(lot, _Symbol, tick.ask, 0, 0, c);
   return trade.Sell(lot, _Symbol, tick.bid, 0, 0, c);
}

void OnTick()
{
   HGLog_OnTick();
   MqlTick tick;
   if(!SymbolInfoTick(_Symbol, tick)) return;
   double p = tick.bid;

   double floating;
   int n = Scan(floating);

   if(n > 0)
   {
      if(AccountInfoDouble(ACCOUNT_BALANCE) - bal0 + floating >= InpBasketTarget)
      {
         CloseAll();
         return;
      }
      if(nLevels >= InpMaxLevels) return;
      // The entry edge is anchor, the other edge is InpZone against the entry direction
      double other = anchor - dir * InpZone;
      bool cross;
      if(lastSide == dir) cross = (dir == 1) ? (p <= other) : (p >= other);
      else                cross = (dir == 1) ? (p >= anchor) : (p <= anchor);
      if(!cross) return;
      double lot = NormalizeDouble(lastLot * InpMult, 2);
      lot = MathMin(lot, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX));
      if(Open(-lastSide, lot, tick, StringFormat("level%d", nLevels + 1)))
      {
         nLevels++; lastSide = -lastSide; lastLot = lot;
      }
      return;
   }

   // Flat: at most one new entry per M1 bar
   datetime bar = iTime(_Symbol, PERIOD_M1, 0);
   if(bar == lastEntryBar) return;
   int side = AdxSide(hAdxM1, InpAdxLevelM1);
   if(side == 0 || AdxSide(hAdxHTF, InpAdxLevelHTF) != side) return;

   bal0 = AccountInfoDouble(ACCOUNT_BALANCE);
   if(Open(side, InpLot, tick, "entry"))
   {
      lastEntryBar = bar;
      dir = side; nLevels = 1; lastSide = side; lastLot = InpLot;
      anchor = p;
   }
}

コメント

タイトルとURLをコピーしました