MT4サンプルコード01(修正①)
※未検証、勉強の記録として残しているだけです。(レンジ相場用のループ注文)
買い注文の決済(TP)価格がマイナスになってしまう原因として、いくつか考えられます。
1. EAのシンボル依存性とグローバル変数の残存
- EAはチャートにアタッチされたシンボルで動作します。
例えば、最初にZARJPYのチャートにEAをアタッチしてテストしていた場合、グローバル変数(LastPairBuyEntry など)や OrderCount などがZARJPY時点の値のまま残っている可能性があります。 - その状態でチャートをUSDJPYに切り替えても、EAは内部で保持している古い値をもとにTP計算を行ってしまうため、誤った(場合によってはマイナスの)決済価格が設定されることがあります。
対策
- 通貨ペアを変更する際は、必ず現在のEAを削除し、対象のチャート(USDJPY)に再度アタッチしてください。
- また、EA内で Symbol() 関数を使っているか、ハードコーディングされていないかを確認してください。
2. pips補正の計算ロジックの問題
- USDJPYの場合、表示桁数(Digits)や Point の値がブローカーごとに異なり、CalcPipValue() の結果が期待通りになっていない可能性があります。
たとえば、USDJPYが2桁表示の場合、CalcPipValue() が「point」だけを返す設計なら、1pip が 0.01 となりますが、以前ZARJPY用に作ったコードがそのまま使われていると、内部で想定している値とズレる場合もあります。
対策
- CalcPipValue() 内で、現在のシンボルに応じた補正が正しく行われているかを確認してください。
- USDJPYの場合、Digits が2の場合の処理が正しいか(1pip=0.01になるか)を再確認しましょう。
3. 再初期化のタイミング
- EAのOnInit()関数は、EAを新たにアタッチするか、再起動することで実行されます。
シンボル変更時に再初期化されないと、古い通貨ペアの値がそのまま残るため、計算結果がおかしくなることがあります。
対策
- シンボル変更時には、EAを一度削除してから新しいチャートに再アタッチするか、EA側でシンボルが変わった際にグローバル変数をリセットする処理を追加するとよいでしょう。
以下は、USDJPY用のループイフダンEAに、証拠金チェック(必要証拠金の200%以上ある場合のみ注文発注する)を組み込んだサンプルコードになります。
//+——————————————————————+
//| USDJPY専用 ループイフダンEA (証拠金チェック付き) |
//+——————————————————————+
#property strict
// ◆ パラメータ設定 ◆
input double StepPips = 10.0; // 決済(TP)間隔(pips単位)
input int MaxSteps = 9; // 最大ステップ数(ペア数)
input double LotSize = 0.1; // ロットサイズ
input int Slippage = 3; // 許容スリッページ
input double EntryStepPips = 5.0; // エントリー間隔(pips単位)
// ◆ グローバル変数 ◆
int OrderCount = 0; // 発注済みのステップ数
double LastPairBuyEntry = 0; // 最後の買いエントリー価格
double LastPairSellEntry = 0; // 最後の売りエントリー価格
//+——————————————————————+
//| OnInit: 初期化 |
//+——————————————————————+
int OnInit()
{
Print(“USDJPY用 ループイフダンEA(証拠金チェック付き) 起動”);
return(INIT_SUCCEEDED);
}
//+——————————————————————+
//| OnTick: メイン処理 |
//+——————————————————————+
void OnTick()
{
// 全てのポジションが決済されている場合、カウンタとエントリー価格をリセット
if(OrdersTotal() == 0)
{
OrderCount = 0;
LastPairBuyEntry = 0;
LastPairSellEntry = 0;
}
// 最大ステップ未満の場合のみ新規発注を試みる
if(OrderCount < MaxSteps)
{
double ask = MarketInfo(Symbol(), MODE_ASK);
double bid = MarketInfo(Symbol(), MODE_BID);
// USDJPY用の1pipの大きさを算出
double onePip = CalcPipValueUSDJPY();
bool canPlaceBuy = false;
bool canPlaceSell = false;
// 初回は無条件で発注
if(OrderCount == 0)
{
canPlaceBuy = true;
canPlaceSell = true;
}
else
{
// 前回の買いエントリー価格からEntryStepPips分上昇していれば買いOK
if(ask >= LastPairBuyEntry + (EntryStepPips * onePip))
canPlaceBuy = true;
// 前回の売りエントリー価格からEntryStepPips分下落していれば売りOK
if(bid <= LastPairSellEntry – (EntryStepPips * onePip))
canPlaceSell = true;
}
// 両方の条件が満たされたらペア注文を発注
if(canPlaceBuy && canPlaceSell)
{
double tpBuy = ask + (StepPips * (OrderCount + 1) * onePip);
double tpSell = bid – (StepPips * (OrderCount + 1) * onePip);
// 証拠金チェック付きで注文発注
bool buyPlaced = PlaceOrderWithMarginCheck(OP_BUY, ask, tpBuy, LotSize);
bool sellPlaced = PlaceOrderWithMarginCheck(OP_SELL, bid, tpSell, LotSize);
if(buyPlaced && sellPlaced)
{
OrderCount++;
LastPairBuyEntry = ask;
LastPairSellEntry = bid;
}
}
}
}
//+——————————————————————+
//| PlaceOrderWithMarginCheck: 証拠金チェック付き注文発注関数 |
//+——————————————————————+
bool PlaceOrderWithMarginCheck(int type, double price, double tp, double lot)
{
// 必要証拠金を概算する
double marginRequired = CalcMarginRequired(lot, price);
double freeMargin = AccountFreeMargin();
// 余裕証拠金として必要証拠金の200%がなければ注文をスキップ
if(freeMargin < marginRequired * 2.0)
{
Print(“証拠金不足のため注文スキップ: 必要証拠金=”, marginRequired,
“, フリー証拠金=”, freeMargin);
return false;
}
int ticket = OrderSend(Symbol(), type, lot, price, Slippage, 0, tp, “LoopIfDone”, 0, 0, clrNONE);
if(ticket < 0)
{
Print(“注文失敗 (Type:”, type, “) Error:”, GetLastError());
return false;
}
return true;
}
//+——————————————————————+
//| CalcMarginRequired: 必要証拠金を概算する (MQL4用) |
//+——————————————————————+
double CalcMarginRequired(double lot, double price)
{
double contractSize = MarketInfo(Symbol(), MODE_LOTSIZE); // 1ロットあたりの通貨量
double leverage = AccountLeverage(); // 口座レバレッジ
// 概算の必要証拠金:(lot × contractSize × price) / leverage
double margin = (lot * contractSize * price) / leverage;
return margin;
}
//+——————————————————————+
//| CalcPipValueUSDJPY: USDJPY用の1pipの大きさを返す関数 |
//+——————————————————————+
double CalcPipValueUSDJPY()
{
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
double point = MarketInfo(Symbol(), MODE_POINT);
// USDJPYの場合、通常1pip = 0.01
if(digits == 2)
return point; // 例: 0.01
else if(digits == 3)
return point * 10.0; // 例: 0.001 * 10 = 0.01
else
return point;
}
解説
- 証拠金チェック付き注文
- 関数 CalcMarginRequired() で概算の必要証拠金を算出します。
(計算式: (lot × コントラクトサイズ × エントリープライス) / レバレッジ) - PlaceOrderWithMarginCheck() で、AccountFreeMargin() と比較し、余裕証拠金が必要証拠金の200%以上ある場合にのみ注文を発注します。
- 関数 CalcMarginRequired() で概算の必要証拠金を算出します。
- USDJPY用1pipの計算
- CalcPipValueUSDJPY() は、チャートの Digits に応じて1pipの値を返します。
通常USDJPYは1pip=0.01となるため、2桁表示の場合はそのまま、3桁表示の場合は10倍しています。
- CalcPipValueUSDJPY() は、チャートの Digits に応じて1pipの値を返します。
- ループ注文ロジック
- 前回エントリー価格との差が EntryStepPips 分動いていれば新規ペア注文を発注し、TPは StepPips × (OrderCount+1) に応じた値に設定します。
※プログラミング詳細内容はEAの専門家にお尋ねください。