Trang chủ Kiến thức OnTick() trong MQL5: Xử Lý Mỗi Tick Giá Mới
Knowledge

OnTick() trong MQL5: Xử Lý Mỗi Tick Giá Mới

14 tháng 11, 2025

Hướng dẫn OnTick() event handler - main trading logic, new bar detection, performance optimization và tick filtering.

OnTick(): Main Trading Logic

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.

Cú Pháp Cơ Bản

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!

New Bar Detection

// 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
}

Trading Signal Check

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
}

Position Management in OnTick()

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);
        }
    }
}

Performance Optimization

// ❌ 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
    }
}

Error Handling in OnTick()

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
}

Multi-Timeframe Analysis

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();
        }
    }
}

State Machine Pattern

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;
    }
}

Best Practices

  • ✅ Optimize for performance - OnTick() called very frequently
  • ✅ Use new bar detection để avoid unnecessary calculations
  • ✅ Cache indicator values trong static variables
  • ✅ Use indicator handles initialized trong OnInit()
  • ✅ Early return để skip unnecessary code
  • ✅ Handle errors gracefully
  • ⛔ Tránh heavy calculations mỗi tick
  • ⛔ Không call indicator functions (iMA, iRSI) trực tiếp mỗi tick
  • ⛔ Tránh blocking operations (Sleep, loops lớn)

Bài Tập Thực Hành

  1. Implement OnTick() với new bar detection và print bar count
  2. Tạo OnTick() check MA crossover và print signal
  3. Viết OnTick() quản lý trailing stop cho positions

Tài Liệu Tham Khảo