Trang chủ Kiến thức OrderCalcProfit() - Tính Toán Lợi Nhuận Trước Khi Giao Dịch
Knowledge

OrderCalcProfit() - Tính Toán Lợi Nhuận Trước Khi Giao Dịch

14 tháng 11, 2025

Hướng dẫn sử dụng hàm OrderCalcProfit() để ước tính lợi nhuận của giao dịch trước khi thực hiện.

Giới thiệu OrderCalcProfit()

Hàm OrderCalcProfit() cho phép bạn tính toán lợi nhuận dự kiến của một giao dịch trước khi thực sự thực hiện nó. Đây là công cụ quan trọng cho việc quản lý rủi ro và tối ưu hóa chiến lược giao dịch.

Cú pháp

bool OrderCalcProfit(
   ENUM_ORDER_TYPE  action,      // loại lệnh
   const string     symbol,       // tên symbol
   double          volume,        // khối lượng
   double          price_open,    // giá mở
   double          price_close,   // giá đóng
   double          &profit        // biến nhận kết quả lợi nhuận
);

Tham Số

  • action: ORDER_TYPE_BUY hoặc ORDER_TYPE_SELL
  • symbol: Tên cặp tiền (ví dụ: "EURUSD")
  • volume: Khối lượng giao dịch tính bằng lots
  • price_open: Giá mở vị thế
  • price_close: Giá đóng vị thế (dự kiến)
  • &profit: Biến nhận giá trị lợi nhuận (theo đồng tiền tài khoản)

Ví dụ 1: Tính Lợi Nhuận Cơ Bản

//+------------------------------------------------------------------+
//| Tính lợi nhuận cho lệnh Buy                                     |
//+------------------------------------------------------------------+
void CalculateBuyProfit()
{
   string symbol = "EURUSD";
   double volume = 0.1;  // 0.1 lot

   double price_open = 1.10000;
   double price_close = 1.10500;  // Target price
   double profit = 0;

   if(OrderCalcProfit(ORDER_TYPE_BUY, symbol, volume,
                      price_open, price_close, profit))
   {
      Print("Expected profit for Buy: ", profit, " ", AccountInfoString(ACCOUNT_CURRENCY));
      Print("In pips: ", (price_close - price_open) / SymbolInfoDouble(symbol, SYMBOL_POINT));
   }
   else
   {
      Print("Failed to calculate profit: ", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Output example:                                                  |
//| Expected profit for Buy: 50.00 USD                              |
//| In pips: 500                                                     |
//+------------------------------------------------------------------+

Ví dụ 2: So Sánh Lợi Nhuận Buy vs Sell

//+------------------------------------------------------------------+
//| So sánh lợi nhuận của cả hai hướng giao dịch                   |
//+------------------------------------------------------------------+
void CompareBuyVsSellProfit()
{
   string symbol = _Symbol;
   double volume = 0.5;

   double current_ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
   double current_bid = SymbolInfoDouble(symbol, SYMBOL_BID);
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);

   // Giả định di chuyển 100 pips
   double target_distance = 100 * point;

   // Tính lợi nhuận Buy
   double buy_profit = 0;
   if(OrderCalcProfit(ORDER_TYPE_BUY, symbol, volume,
                      current_ask, current_ask + target_distance, buy_profit))
   {
      Print("Buy profit if price moves up 100 pips: ", buy_profit);
   }

   // Tính lợi nhuận Sell
   double sell_profit = 0;
   if(OrderCalcProfit(ORDER_TYPE_SELL, symbol, volume,
                      current_bid, current_bid - target_distance, sell_profit))
   {
      Print("Sell profit if price moves down 100 pips: ", sell_profit);
   }

   // So sánh
   Print("Profit difference: ", MathAbs(buy_profit - sell_profit));
}

Ví dụ 3: Tính Risk/Reward Ratio

//+------------------------------------------------------------------+
//| Tính tỷ lệ Risk/Reward trước khi vào lệnh                      |
//+------------------------------------------------------------------+
bool CheckRiskReward(string symbol, double volume, ENUM_ORDER_TYPE type,
                     double entry, double sl, double tp)
{
   double risk_profit = 0;
   double reward_profit = 0;

   // Tính lỗ nếu chạm SL
   if(!OrderCalcProfit(type, symbol, volume, entry, sl, risk_profit))
   {
      Print("Failed to calculate risk");
      return false;
   }

   // Tính lãi nếu chạm TP
   if(!OrderCalcProfit(type, symbol, volume, entry, tp, reward_profit))
   {
      Print("Failed to calculate reward");
      return false;
   }

   // Tính Risk/Reward ratio
   double risk_amount = MathAbs(risk_profit);
   double reward_amount = MathAbs(reward_profit);
   double rr_ratio = reward_amount / risk_amount;

   Print("=== Risk/Reward Analysis ===");
   Print("Risk: ", risk_amount, " ", AccountInfoString(ACCOUNT_CURRENCY));
   Print("Reward: ", reward_amount, " ", AccountInfoString(ACCOUNT_CURRENCY));
   Print("R:R Ratio: 1:", NormalizeDouble(rr_ratio, 2));

   // Chỉ chấp nhận nếu RR >= 1:2
   if(rr_ratio >= 2.0)
   {
      Print("Good Risk/Reward ratio - Trade accepted");
      return true;
   }
   else
   {
      Print("Poor Risk/Reward ratio - Trade rejected");
      return false;
   }
}

//+------------------------------------------------------------------+
//| Sử dụng trong trading logic                                     |
//+------------------------------------------------------------------+
void OnTick()
{
   string symbol = _Symbol;
   double volume = 0.1;

   double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);

   double entry = ask;
   double sl = entry - 500 * point;   // 50 pips SL
   double tp = entry + 1500 * point;  // 150 pips TP (1:3 RR)

   if(CheckRiskReward(symbol, volume, ORDER_TYPE_BUY, entry, sl, tp))
   {
      // Proceed with OrderSend()
      Print("Risk/Reward acceptable - opening position");
   }
}

Ví dụ 4: Tính Toán Position Sizing Dựa Trên Risk

//+------------------------------------------------------------------+
//| Tính volume dựa trên % risk của account                        |
//+------------------------------------------------------------------+
double CalculateVolumeByRisk(string symbol, ENUM_ORDER_TYPE type,
                             double entry_price, double sl_price,
                             double risk_percent)
{
   double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double risk_amount = account_balance * risk_percent / 100.0;

   Print("Account balance: ", account_balance);
   Print("Risk percentage: ", risk_percent, "%");
   Print("Risk amount: ", risk_amount);

   // Bắt đầu với volume nhỏ và scale up
   double test_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);

   double loss_at_sl = 0;

   // Tìm volume phù hợp
   while(test_volume <= max_volume)
   {
      if(OrderCalcProfit(type, symbol, test_volume, entry_price, sl_price, loss_at_sl))
      {
         double actual_loss = MathAbs(loss_at_sl);

         if(actual_loss <= risk_amount)
         {
            // Volume này OK, thử volume lớn hơn
            test_volume += volume_step;
         }
         else
         {
            // Quá lớn, quay lại volume trước đó
            test_volume -= volume_step;
            break;
         }
      }
      else
      {
         Print("Error calculating profit for volume: ", test_volume);
         return 0;
      }
   }

   // Đảm bảo không vượt quá max volume
   if(test_volume > max_volume)
      test_volume = max_volume;

   // Verify final volume
   if(OrderCalcProfit(type, symbol, test_volume, entry_price, sl_price, loss_at_sl))
   {
      Print("Calculated volume: ", test_volume);
      Print("Max loss at SL: ", MathAbs(loss_at_sl));
      return test_volume;
   }

   return 0;
}

//+------------------------------------------------------------------+
//| Sử dụng trong trading strategy                                  |
//+------------------------------------------------------------------+
void OpenPositionWithRiskManagement()
{
   string symbol = _Symbol;
   double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);

   double entry = ask;
   double sl = entry - 500 * point;  // 50 pips SL

   // Tính volume cho 2% risk
   double volume = CalculateVolumeByRisk(symbol, ORDER_TYPE_BUY, entry, sl, 2.0);

   if(volume > 0)
   {
      Print("Opening position with volume: ", volume);
      // Proceed with OrderSend()
   }
   else
   {
      Print("Failed to calculate appropriate volume");
   }
}

Ví dụ 5: Portfolio Risk Calculator

//+------------------------------------------------------------------+
//| Tính tổng risk của tất cả positions                            |
//+------------------------------------------------------------------+
double CalculateTotalPortfolioRisk()
{
   double total_risk = 0;

   // Duyệt qua tất cả positions
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
      {
         string symbol = PositionGetString(POSITION_SYMBOL);
         double volume = PositionGetDouble(POSITION_VOLUME);
         double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
         double sl = PositionGetDouble(POSITION_SL);
         ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         // Chỉ tính nếu có SL
         if(sl > 0)
         {
            double potential_loss = 0;
            ENUM_ORDER_TYPE order_type = (pos_type == POSITION_TYPE_BUY) ?
                                         ORDER_TYPE_BUY : ORDER_TYPE_SELL;

            if(OrderCalcProfit(order_type, symbol, volume, open_price, sl, potential_loss))
            {
               total_risk += MathAbs(potential_loss);
               Print("Position ", ticket, " risk: ", MathAbs(potential_loss));
            }
         }
      }
   }

   double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double risk_percent = (total_risk / account_balance) * 100;

   Print("=== Portfolio Risk Analysis ===");
   Print("Total Risk: ", total_risk, " ", AccountInfoString(ACCOUNT_CURRENCY));
   Print("Account Balance: ", account_balance);
   Print("Total Risk %: ", NormalizeDouble(risk_percent, 2), "%");

   return risk_percent;
}

//+------------------------------------------------------------------+
//| Check before opening new position                               |
//+------------------------------------------------------------------+
bool CanOpenNewPosition()
{
   double current_risk = CalculateTotalPortfolioRisk();
   double max_portfolio_risk = 10.0; // Maximum 10% total risk

   if(current_risk < max_portfolio_risk)
   {
      Print("Portfolio risk acceptable: ", current_risk, "%");
      return true;
   }
   else
   {
      Print("Portfolio risk too high: ", current_risk, "% - No new positions");
      return false;
   }
}

Lưu Ý Quan Trọng

  • Chỉ dùng trong EA và Scripts: OrderCalcProfit() không thể sử dụng trong indicators.
  • Giá trị trả về: Lợi nhuận được tính theo đồng tiền của tài khoản.
  • Spread và Commission: Hàm không tính spread và commission. Bạn phải tính thêm.
  • Thị trường đóng cửa: Hàm vẫn hoạt động khi thị trường đóng cửa.
  • Symbol phải có sẵn: Symbol phải được load trong Market Watch.

Best Practices

  1. Luôn validate kết quả: Kiểm tra return value của hàm trước khi sử dụng profit.
  2. Tính toán spread: Thêm spread vào calculation cho kết quả chính xác hơn.
  3. Sử dụng cho position sizing: Đây là use case chính của hàm này.
  4. Portfolio risk management: Tính tổng risk của tất cả positions.
  5. Risk/Reward analysis: Đánh giá trước khi vào lệnh.

Kết Luận

OrderCalcProfit() là công cụ thiết yếu cho money management và risk management trong MQL5. Kết hợp với các hàm giao dịch khác, nó giúp bạn xây dựng EA với quản lý vốn chuyên nghiệp và kiểm soát rủi ro hiệu quả.