在 MetaEditor 實際跑一次
一個最小可行 EA:新 K 線閘門、magic number、一次進場
存成 MQL5/Experts/Lesson10FirstEA.mq5。僅限模擬帳戶——它會在第一根新 K 線開出一張真實買單。在 Experts 分頁看 retcode。
//+------------------------------------------------------------------+
//| Lesson 10 - First EA: new-bar gate, magic number, one entry |
//| WARNING: sends real market orders. Use a DEMO account. |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version "1.00"
#property strict
#include <Trade/Trade.mqh>
input double Lots = 0.10;
input int StopPoints = 200;
input ulong MagicNumber = 10101010;
CTrade trade;
datetime lastBarTime = 0;
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFillingBySymbol(_Symbol);
Print("Lesson 10 EA ready on ", _Symbol, " / ", EnumToString((ENUM_TIMEFRAMES)_Period));
return(INIT_SUCCEEDED);
}
void OnTick()
{
// 1) Only act on the first tick of a new bar.
if(!IsNewBar())
return;
// 2) Never stack positions - check first.
if(HasPosition(_Symbol, MagicNumber))
{
Print("Already in a position on ", _Symbol, " - standing down.");
return;
}
// 3) Send the order and always log the retcode.
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = NormalizeDouble(ask - StopPoints * _Point, _Digits);
bool ok = trade.Buy(Lots, _Symbol, 0.0, sl, 0.0, "lesson10");
Print("Buy request ", ok ? "accepted" : "FAILED",
" retcode=", trade.ResultRetcode(),
" (", trade.ResultRetcodeDescription(), ")");
if(ok)
Print("Deal: ", trade.ResultDeal(), " volume: ", trade.ResultVolume(),
" price: ", DoubleToString(trade.ResultPrice(), _Digits));
}
//+------------------------------------------------------------------+
//| Has the current bar's open time changed since we last looked? |
//+------------------------------------------------------------------+
bool IsNewBar()
{
datetime t = iTime(_Symbol, _Period, 0);
if(t == lastBarTime)
return false;
lastBarTime = t;
return true;
}
//+------------------------------------------------------------------+
//| Is there a position on this symbol carrying our magic number? |
//+------------------------------------------------------------------+
bool HasPosition(const string sym, const ulong magic)
{
if(!PositionSelect(sym))
return false;
return ((ulong)PositionGetInteger(POSITION_MAGIC) == magic);
}
如何執行
- 1. 從 MT5 開啟 MetaEditor(按 F4), 選擇這支程式該放的資料夾——Experts、Indicators 或 Scripts。
- 2. 開新檔案、把這段程式貼上去,然後按 F7 編譯。Errors 分頁顯示的錯誤 都修掉。
- 3. 回到 MT5,把它拖到圖表上——如果是 EA,就打開 Strategy Tester。
當你能交易時,什麼改變了
一個指標只畫圖。一個 EA 會行動,而行動意味著三個新的責任:決定何時行動、避免行動兩次,以及只為自己的部位負責。
新 K 線閘門
OnTick 在每次價格更新時觸發。如果你的邏輯是以 K 線為基礎——而多數順勢邏輯都是——你必須忽略每根新 K 線第一個 tick 以外的所有 tick。記住目前這根 K 線的開盤時間;如果它沒變,就回傳。少了這個閘門,一個 EA 可以在某個條件剛成立的一分鐘之內開出幾十個部位。
magic number
magic number 只是一個你貼在每一張單上的整數。它是讓一個 EA 分辨「自己的部位」與「你的部位」,以及同一個帳戶上其他 EA 部位的唯一可靠方法。在關閉或修改任何東西之前先檢查它。挑一個有辨識度的;不要重複用 0。
先檢查有沒有已存在的部位
在每一次進場之前,先問自己是不是已經在場內。用 PositionSelect(symbol) 然後驗證 POSITION_MAGIC。在避險帳戶上,同一個商品可以有多個部位,所以你可能需要迭代 PositionsTotal() 並逐一比對 ticket——這裡的簡化版假設一個商品只有一個部位,而那也正好是淨額 CFD 帳戶強制的行為。
把 retcode 記下來
每一次交易呼叫都會回傳一個結果碼。把它印出來。「它沒交易」不是一個診斷;retcode 10019(沒錢)與 10016(無效停損)是兩個完全不同的問題,修法也完全不同。
只在模擬帳戶上執行這個
這堂課的 EA 會送出一張真實的市價單。那就是重點——但在你讀完第三部分並理解風險那堂課之前,請在模擬帳戶上跑它。