Hướng dẫn OnTick() event handler - main trading logic, new bar detection, performance optimization và tick filtering.
OnTick() là event handler được gọi mỗi khi có tick mới (thay đổi giá). Đây là nơi chứa main trading logic của EA.
void OnTick() {
// This function is called on every price tick
// Main trading logic goes here
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
Print("New tick: Ask=", ask, " Bid=", bid);
}
// ⚠️ OnTick() is called VERY frequently (thousands of times per day)
// Must be optimized for performance!
// Global variable
datetime g_lastBarTime = 0;
void OnTick() {
// Get current bar time
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
// Check if new bar appeared
if(currentBarTime != g_lastBarTime) {
Print("New bar detected!");
g_lastBarTime = currentBarTime;
// Execute logic ONLY on new bar
CheckForTradeSignals();
}
}
// Alternative: Using static variable
void OnTick() {
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime != lastBarTime) {
lastBarTime = currentBarTime;
OnNewBar(); // Custom function
}
}
void OnNewBar() {
Print("Processing new bar at ", TimeToString(TimeCurrent()));
// Your trading logic here
}
void OnTick() {
// Early exit conditions
if(!IsNewBar()) return;
if(!IsTradingTime()) return;
if(!IsSpreadOK()) return;
// Get signal
int signal = CheckTradingSignal();
// Execute based on signal
if(signal == 1) { // Buy signal
if(CountPositions(POSITION_TYPE_BUY) == 0) {
OpenBuy();
}
} else if(signal == -1) { // Sell signal
if(CountPositions(POSITION_TYPE_SELL) == 0) {
OpenSell();
}
}
// Manage existing positions
ManagePositions();
}
bool IsNewBar() {
static datetime lastTime = 0;
datetime currentTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentTime != lastTime) {
lastTime = currentTime;
return true;
}
return false;
}
int CheckTradingSignal() {
// Get indicators
double ma50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_SMA, PRICE_CLOSE);
double ma200 = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE);
double rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
// Buy: MA50 > MA200 AND RSI < 70
if(ma50 > ma200 && rsi < 70) {
return 1;
}
// Sell: MA50 < MA200 AND RSI > 30
if(ma50 < ma200 && rsi > 30) {
return -1;
}
return 0; // No signal
}
void OnTick() {
// Update positions every tick
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
// Check if position belongs to this EA
if(PositionGetInteger(POSITION_MAGIC) != EA_MAGIC) continue;
// Get position info
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
double profit = PositionGetDouble(POSITION_PROFIT);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// Trailing stop
if(profit > 0) {
double newSL = CalculateTrailingStop(type, openPrice, currentPrice);
if(newSL > 0) {
ModifyPosition(ticket, newSL);
}
}
// Breakeven
double pips = MathAbs(currentPrice - openPrice) / _Point;
if(pips >= 50) { // Move to breakeven after 50 pips
MoveToBreakeven(ticket, openPrice);
}
}
}
// ❌ BAD: Heavy calculations every tick
void OnTick() {
// This runs THOUSANDS of times per day!
double ma50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_SMA, PRICE_CLOSE);
double rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
// Indicator functions called every tick - VERY slow!
}
// ✅ GOOD: Calculate only on new bar
void OnTick() {
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
static double ma50 = 0;
static double rsi = 0;
// Update indicators only on new bar
if(currentBarTime != lastBarTime) {
lastBarTime = currentBarTime;
ma50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_SMA, PRICE_CLOSE);
rsi = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
}
// Use cached values for quick checks
if(ma50 > 1.12000 && rsi < 70) {
// Trading logic
}
}
// ✅ BETTER: Use indicator handles (initialized in OnInit)
int g_maHandle = INVALID_HANDLE;
int g_rsiHandle = INVALID_HANDLE;
int OnInit() {
g_maHandle = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_SMA, PRICE_CLOSE);
g_rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
return INIT_SUCCEEDED;
}
void OnTick() {
if(!IsNewBar()) return;
double maBuffer[];
double rsiBuffer[];
ArraySetAsSeries(maBuffer, true);
ArraySetAsSeries(rsiBuffer, true);
// Fast: Copy from handle
CopyBuffer(g_maHandle, 0, 0, 1, maBuffer);
CopyBuffer(g_rsiHandle, 0, 0, 1, rsiBuffer);
if(maBuffer[0] > 1.12000 && rsiBuffer[0] < 70) {
// Trading logic
}
}
void OnTick() {
// Protect against errors
if(_Symbol == "") {
Print("Error: Invalid symbol");
return;
}
// Check if market is open
if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE)) {
return; // Market closed
}
// Reset error code
ResetLastError();
// Get price
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int error = GetLastError();
if(error != 0) {
Print("Error getting price: ", error);
return;
}
if(ask <= 0) {
Print("Invalid price: ", ask);
return;
}
// Safe to proceed with trading logic
}
void OnTick() {
if(!IsNewBar()) return;
// Check trend on higher timeframes
double h4_ma = iMA(_Symbol, PERIOD_H4, 200, 0, MODE_SMA, PRICE_CLOSE);
double d1_ma = iMA(_Symbol, PERIOD_D1, 50, 0, MODE_SMA, PRICE_CLOSE);
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// Determine overall trend
bool h4Bullish = currentPrice > h4_ma;
bool d1Bullish = currentPrice > d1_ma;
// Only trade with the trend
if(h4Bullish && d1Bullish) {
// Look for buy signals on M15
int signal = CheckM15BuySignal();
if(signal == 1) {
OpenBuy();
}
} else if(!h4Bullish && !d1Bullish) {
// Look for sell signals on M15
int signal = CheckM15SellSignal();
if(signal == 1) {
OpenSell();
}
}
}
enum ENUM_EA_STATE {
STATE_IDLE, // No positions
STATE_WAITING_ENTRY, // Signal detected, waiting for entry
STATE_IN_POSITION, // Position open
STATE_MANAGING_EXIT // Managing position exit
};
ENUM_EA_STATE g_currentState = STATE_IDLE;
void OnTick() {
switch(g_currentState) {
case STATE_IDLE:
// No positions - look for signals
if(CheckForSignal()) {
g_currentState = STATE_WAITING_ENTRY;
}
break;
case STATE_WAITING_ENTRY:
// Signal exists - wait for entry conditions
if(ConfirmEntry()) {
OpenPosition();
g_currentState = STATE_IN_POSITION;
} else if(SignalExpired()) {
g_currentState = STATE_IDLE;
}
break;
case STATE_IN_POSITION:
// Position open - manage it
ManagePosition();
if(PositionsTotal() == 0) {
g_currentState = STATE_IDLE;
}
break;
}
}