Writing an mql5 function that calculates the profit and loss of a position in cash

I would like to make an EA that calculates the loss in cash in the account’s currency base. This function is considered as an essential function for all the profitable multi-currency trading EAs that they do trade based on risk, but the symbol points alas. I’ve already written the first part of the code. I think that it’s the first ever open-source EA that calculates the StopLoss in Cash!!! Later we can add NetLossPerOnePrecentLotPerPoint into the code also, but at first, we should improve its calculation functions therefore it never gives us a wrong answer. It calculates everything perfectly right now when one side of the forex currency pairs is USD like EURUSD. But when USD is not on any sides then it calculates a wrong answer. Does anybody have any ideas to improve it? Here is the code:

#property copyright "Gerald Mann (P. Ghasemi), and other forum members of the babypips.com community"
#property link      "https://twitter.com/PeimanGhasemi"
#property version   "1.00"

void OnTick()
{
   if (PositionsTotal() > 0) //Check if there is any position available to calculate
   {
      double ContractSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE);
      // Check Handle
      if(!PositionSelect(_Symbol))
      {
         PrintFormat("PositionSelect(%s) failed. Error %d",_Symbol, GetLastError());
         return;
      }
      ResetLastError();
      long ticket=PositionGetInteger(POSITION_TICKET);
      //Check ticket
      if(ticket==0)
      {
         PrintFormat("Failed to get %s position ticket. Error %d", _Symbol, GetLastError());
         return;
      }
      // Position info
      double EntryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double Price = PositionGetDouble(POSITION_PRICE_OPEN);
      double StopLossLevel = PositionGetDouble(POSITION_SL);
      double LotSize = PositionGetDouble(POSITION_VOLUME);
      
      //Calculations
      double Spent = LotSize * ContractSize;
      double Distance = MathAbs(Price - StopLossLevel);
      double PositionSlLossValueInCurrencyBaseValue = -(Spent * Distance);
      
      Print("EntryPrice:", EntryPrice);
      Print("StopLossLevel:", StopLossLevel);
      Print("Spent:", Spent);
      Print("PositionSlLossValueInCurrencyBaseValue:", PositionSlLossValueInCurrencyBaseValue);
   }
}

I assume your account is in dollars, which is why it only works for dollars. If it’s not a symbol containing USD, you need to use the base currency’s symbol with USD price to convert the number into USD.

But your stop loss won’t be perfect because as the USD moves, the value of your stop loss will too.

Spread betting is much easier to do all of this because a pip has a fixed price in your account currency. And I can do this without an EA

It’s important to add logic that detects the base currency of the account and adjusts for any pairs involving USD. If USD isn’t part of the pair, you’ll want to use a conversion rate—like EUR/USD for an EUR-based account—to calculate the loss in the account’s base currency. This ensures that even when USD isn’t involved, the loss is properly adjusted based on the current exchange rate. Essentially, you’re dynamically adapting to non-USD pairs by fetching and applying the right conversion rate. Try this out:


void OnTick()
{
   if (PositionsTotal() > 0) // Check if there is any position available to calculate
   {
      double ContractSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE);

      // Check Handle
      if(!PositionSelect(_Symbol))
      {
         PrintFormat("PositionSelect(%s) failed. Error %d",_Symbol, GetLastError());
         return;
      }
      ResetLastError();
      long ticket = PositionGetInteger(POSITION_TICKET);

      // Check ticket
      if(ticket == 0)
      {
         PrintFormat("Failed to get %s position ticket. Error %d", _Symbol, GetLastError());
         return;
      }

      // Position info
      double EntryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double StopLossLevel = PositionGetDouble(POSITION_SL);
      double LotSize = PositionGetDouble(POSITION_VOLUME);
      
      // Get Account Base Currency
      string accountBaseCurrency = AccountInfoString(ACCOUNT_CURRENCY);

      // Default calculation in case USD is involved
      double Spent = LotSize * ContractSize;
      double Distance = MathAbs(EntryPrice - StopLossLevel);
      double PositionSlLossValueInCurrencyBaseValue = -(Spent * Distance);

      // If USD is not involved in the pair, we need to convert the stop loss loss value into the account's base currency
      if (StringFind(_Symbol, "USD") == -1) // No USD in pair, like EURGBP
      {
         double conversionRate = 1.0;

         // Get the current exchange rate for the pair to the account's base currency
         if (accountBaseCurrency != "USD") // If the account base currency is not USD
         {
            conversionRate = GetConversionRate(accountBaseCurrency);
         }

         // Calculate StopLoss value in account's base currency
         PositionSlLossValueInCurrencyBaseValue *= conversionRate;
      }

      // Print results
      Print("EntryPrice:", EntryPrice);
      Print("StopLossLevel:", StopLossLevel);
      Print("Spent:", Spent);
      Print("PositionSlLossValueInCurrencyBaseValue:", PositionSlLossValueInCurrencyBaseValue);
   }
}

// Function to get conversion rate from Symbol to Account's base currency
double GetConversionRate(string baseCurrency)
{
   double conversionRate = 1.0;
   
   // Here, you can implement logic to fetch conversion rates
   // For example, if your base currency is EUR, you may want to use EUR/USD rate
   if (baseCurrency == "EUR")
   {
      conversionRate = SymbolInfoDouble("EURUSD", SYMBOL_BID); // Get EUR/USD bid price
   }
   else if (baseCurrency == "GBP")
   {
      conversionRate = SymbolInfoDouble("GBPUSD", SYMBOL_BID); // Get GBP/USD bid price
   }

   // Return the conversion rate
   return conversionRate;
}