Complete formula for calculating forex pip value for XAUUSD with account funded in euros

 

Hello,

I'm currently developping forex robot using Python API for Metatrader 5. Among different things, this robot places trades given by external signals (with price, SL and TP).


I use euro funded account, trade XAUUSD, and have a 1:100 leverage ratio.
In order to implement 1% strategy (meaning never risk more than 1% of capital in one trade), I would like to calculate precise SL/TP level in euro, my account money, and fail to compute same results as given in my MT5 terminal.

If I take a trade as example (informations sourced from MT5): 
- SELL XAUUSD, price $1849.32
- lot size : 0.01
- date : 2022.05.31 13:31:05
- closed at price $1848.03
- profit announced by MT5 : 2,4€

What's the formula used to calculate this amount, placing leverage and exchange rate from USD to EUR ?

My method so far:

AFAIK, pip value for an USD account with USD as last currency in pair (i.e XAUUSD) is: 


pipValue = (pipStep/ currentPrice) * lotSize


Applied to given trade above (result in dollars):


pipValue = (0.001/1849.32)*100000*0.01 = 0.005407393


Total trade value (in dollars, without any leverage)

tradeValue = 0.005407393 * (1849.32 - 1848.03)/0.01 = 0.6976


From this point, I can't find any way to include leverage and exchange rate for finding this 2,4€ profit.


Thank you for those who can help on this theorical point.

Forex Market – App Store of MetaTrader 5 trading robots, Expert Advisors and technical indicators
Forex Market – App Store of MetaTrader 5 trading robots, Expert Advisors and technical indicators
  • www.mql5.com
An official showcase of applications for trading from the terminal
 
overflaw: What's the formula used to calculate this amount, placing leverage and exchange rate from USD to EUR ?

Risk depends on your initial stop loss, lot size, and the value of the symbol. It does not depend on margin and leverage. No SL means you have infinite risk (on leveraged symbols). Never risk more than a small percentage of your trading funds, certainly less than 2% per trade, 6% total.

  1. You place the stop where it needs to be — where the reason for the trade is no longer valid. E.g. trading a support bounce, the stop goes below the support.

  2. AccountBalance * percent/100 = RISK = OrderLots * (|OrderOpenPrice - OrderStopLoss| * DeltaPerLot + CommissionPerLot) (Note OOP-OSL includes the spread, and DeltaPerLot is usually around $10/PIP but it takes account of the exchange rates of the pair vs. your account currency.)

  3. Do NOT use TickValue by itself - DeltaPerLot and verify that MODE_TICKVALUE is returning a value in your deposit currency, as promised by the documentation, or whether it is returning a value in the instrument's base currency.
              MODE_TICKVALUE is not reliable on non-fx instruments with many brokers - MQL4 programming forum (2017)
              Is there an universal solution for Tick value? - Currency Pairs - General - MQL5 programming forum (2018)
              Lot value calculation off by a factor of 100 - MQL5 programming forum (2019)

  4. You must normalize lots properly and check against min and max.

  5. You must also check FreeMargin to avoid stop out

  6. For MT5, see 'Money Fixed Risk' - MQL5 Code Base (2017)

Most pairs are worth about $10 per PIP. A $5 risk with a (very small) 5 PIP SL is $5/$10/5 or 0.1 Lots maximum.

 

@William Roeder,

Great thanks for all those precise data, and links.


Thanks to you I finally managed to find matching numbers.

 
overflaw #:

@William Roeder,

Great thanks for all those precise data, and links.


Thanks to you I finally managed to find matching numbers.



I am facing a similar issue and have spent hours trying to work out where I've gone wrong. Did you work out what your formula needs to be to calculate an accurate lot size for XAUUSD type orders?

 
TraderLloyd #: I am facing a similar issue and have spent hours trying to work out where I've gone wrong. Did you work out what your formula needs to be to calculate an accurate lot size for XAUUSD type orders?

In python, use the order_calc_profit function and apply it in a way similar to the following MQL5 equivalent ...

Forum on trading, automated trading systems and testing trading strategies

SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) sometimes zero

Fernando Carreiro, 2022.08.23 16:51

Instead of using the Tick Value directly, consider using the function OrderCalcProfit, because it will apply the correct profit calculation method depending on the CALC_MODE for that symbol.

Forum on trading, automated trading systems and testing trading strategies

SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) sometimes zero

Fernando Carreiro, 2022.08.23 17:41

You can! These are the steps I take. I supply the function with a lot size equal to the “Max Lot Size” allowed for the symbol in question, then calculate the ratio needed to achieve the fractional risk that I wish to apply, to get the correct volume for the order. I then align that with the “Lot Step” and finally check it against both the maximum and minimum allowed lots for the symbol.

The reason I use the “maximum” lots instead of just “1.0” lots as a reference value is because there is no guarantee that the value of 1.0 is within the minimum and maximum values allowed. Given that using 1.0, or the maximum, gives equivalent results anyway (by using the ratio method), I choose to use the “max lots” as the reference point which also offers the most precision for the calculation.

Something like this ...

// This code will not compile. It is only a example reference

if( OrderCalcProfit( eOrderType, _Symbol, dbLotsMax, dbPriceOpen, dbPriceStopLoss, dbProfit ) )
{
   dbOrderLots = fmin( fmax( round( dbRiskMax * dbLotsMax / ( -dbProfit * dbLotsStep ) )
               * dbLotsStep, dbLotsMin ), dbLotsMax ); 
      
   // the rest of the code ...
};

Forum on trading, automated trading systems and testing trading strategies

How to calculate lots using multiplier according to number of opened orders?

Fernando Carreiro, 2017.09.01 21:57

Don't use NormalizeDouble(). Here is some guidance (code is untested, just serves as example):

// Variables for Symbol Volume Conditions
double
   dblLotsMinimum = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN  ),
   dblLotsMaximum = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX  ),
   dblLotsStep    = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP );
   
// Variables for Geometric Progression
double
   dblGeoRatio = 2.8,
   dblGeoInit  = dblLotsMinimum;
   
// Calculate Next Geometric Element
double
   dblGeoNext  = dblGeoInit * pow( dblGeoRatio, intOrderCount + 1 );
   
// Adjust Volume for allowable conditions
double
   dblLotsNext = fmin( dblLotsMaximum,                                     // Prevent too greater volume
                   fmax( dblLotsMinimum,                                   // Prevent too smaller volume
                     round( dblGeoNext / dblLotsStep ) * dblLotsStep ) );  // Align to Step value

Forum on trading, automated trading systems and testing trading strategies

Volume Limit Reached - Validation for new Expert Advisor error

Fernando Carreiro, 2022.07.22 18:22

Your EA must be coded to read the broker's contract specifications, such volume limitations, and prevent that from happening.

SYMBOL_VOLUME_MIN

Minimal volume for a deal

double

SYMBOL_VOLUME_MAX

Maximal volume for a deal

double

SYMBOL_VOLUME_STEP

Minimal volume change step for deal execution

double

SYMBOL_VOLUME_LIMIT

Maximum allowed aggregate volume of an open position and pending orders in one direction (buy or sell) for the symbol. For example, with the limitation of 5 lots, you can have an open buy position with the volume of 5 lots and place a pending order Sell Limit with the volume of 5 lots. But in this case you cannot place a Buy Limit pending order (since the total volume in one direction will exceed the limitation) or place Sell Limit with the volume more than 5 lots.

double

 
Fernando Carreiro #:

In python, use the order_calc_profit function and apply it in a way similar to the following MQL5 equivalent ...

I think we're trying to work out what the lot size should be from the amount of risk we are willing to take. What you're sharing looks great but that will calculate the profit made and I don't see how that will help unless it's used inversely. I did give it a quick try in python but it was spitting out "None" results, so I must be doing something wrong.
 
TraderLloyd #: I think we're trying to work out what the lot size should be from the amount of risk we are willing to take. What you're sharing looks great but that will calculate the profit made and I don't see how that will help unless it's used inversely. I did give it a quick try in python but it was spitting out "None" results, so I must be doing something wrong.

I suggest you look at it again. If you trying to calculate your order volume for a specific stop-loss risk, then use the order_calc_profit as described in my previous post.

Please note however, that stop-loss risk and appropriate volume for it has nothing to do with leverage or margin. That is something completely separate, which you can obtain using order_calc_margin.

If however, you just need the tick value, or point value just use the python equivalent of the following ...

Forum on trading, automated trading systems and testing trading strategies

Symbol Point Value

Fernando Carreiro, 2022.05.18 21:05

double
   dbTickSize   = SymbolInfoDouble( _symbol, SYMBOL_TRADE_TICK_SIZE  ), // Tick size
   dbTickValue  = SymbolInfoDouble( _symbol, SYMBOL_TRADE_TICK_VALUE ), // Tick value
   dbPointSize  = SymbolInfoDouble( _symbol, SYMBOL_POINT ),            // Point size
   dbPointValue = dbTickValue * dbPointSize / dbTickSize;               // Point value
Remember, it's best to use tick size and tick value in your calculations, instead of point size and its value.
 
TraderLloyd #:
I think we're trying to work out what the lot size should be from the amount of risk we are willing to take. What you're sharing looks great but that will calculate the profit made and I don't see how that will help unless it's used inversely. I did give it a quick try in python but it was spitting out "None" results, so I must be doing something wrong.
I feel stupid now, using it inversely is exactly what you're talking about. Deriving the lot size from a ratio of the take profit. Thank you @FernandoCarreiro - will play around with it and see where it takes me
 
TraderLloyd #: I feel stupid now, using it inversely is exactly what you're talking about. Deriving the lot size from a ratio of the take profit. Thank you @FernandoCarreiro - will play around with it and see where it takes me

I think you mean Stop-Loss and not Take-Profit. In my post I talk about "Risk", not "Reward", and hence why I use "- dbProfit" in the ratio, since a loss will be a negative profit.

 
Fernando Carreiro #:

I think you mean Stop-Loss and not Take-Profit. In my post I talk about "Risk", not "Reward", and hence why I use "- dbProfit" in the ratio, since a loss will be a negative profit.

I played around with the example you shared:
// This code will not compile. It is only a example reference

if( OrderCalcProfit( eOrderType, _Symbol, dbLotsMax, dbPriceOpen, dbPriceStopLoss, dbProfit ) )
{
   dbOrderLots = fmin( fmax( round( dbRiskMax * dbLotsMax / ( -dbProfit * dbLotsStep ) )
               * dbLotsStep, dbLotsMin ), dbLotsMax ); 
      
   // the rest of the code ...
};
But this was returning the dbLotsMax and I wasn't sure what it was trying to achieve or whether I was doing something wrong.

But instead, I've tested the following:

dbRiskMax = -abs(mt5.account_info().balance * 0.01) # 1% risk of account balance converted into negative integer
profit = mt5.order_calc_profit(eOrderType, _Symbol, dbLotsMax, dbPriceOpen, dbPriceStopLoss)

dbLotSize = round(( dbRiskMax / profit ) * dbLotsMax, 2)
Its giving me the correct lot sizes for all forex pairs. But then seems to give me unit sizes for US30 and BTCUSD. I could of course add python logic specific to those pairs to convert the units into micro lot sizes. Do you know what I am missing here?

Example:

# BTCUSD
dbRiskMax = -abs(50000.0 * 0.01)
dbRiskMax = -500.0
profit = mt5.order_calc_profit(mt5.ORDER_TYPE_BUY, BTCUSD, 25.0, 21525, 21200)
profit = -8125.0
dbLotSize = round(( -500.0 / -8125.0 ) * 25.0, 2)
dbLotSize = 1.54

# should be 0.002 micro lots

PS. I have spent hours reading over your examples and trying to work out whats wrong. But admittedly I am rather new to Forex and am probably just making a stupid mistake somewhere.
 
TraderLloyd #: I played around with the example you shared: But this was returning the dbLotsMax and I wasn't sure what it was trying to achieve or whether I was doing something wrong. But instead, I've tested the following: Its giving me the correct lot sizes for all forex pairs. But then seems to give me unit sizes for US30 and BTCUSD. I could of course add python logic specific to those pairs to convert the units into micro lot sizes. Do you know what I am missing here? Example: PS. I have spent hours reading over your examples and trying to work out whats wrong. But admittedly I am rather new to Forex and am probably just making a stupid mistake somewhere.
  1. You can also get "LotsMax", when the stop loss is very small or your stop-loss price is in the wrong place.
  2. Don't take the absolute value of the balance as you could have a negative balance. So, simply test that the balance is actually positive before applying the calculation.
  3. You removed the part of the code that aligns the volume to the allowable volume steps. You can't just use any any value you want you have to make it a valid volume as per the allowed steps.
  4. You removed the protections of checking for the maximum and minimum allowable volume. Again, there are limits in place you must adhere to or the order will fail.
  5. The solution you came up with, is distorting the logic and not resolving the root cause of the original calculation not giving you the correct answer.

EDIT: In my example, I am using a positive Risk, not as a negative. If you want to use it as negative then adjust the calculation as such, by removing the "-" in front of "dbProfit".

dbOrderLots = fmin( fmax( round( dbRiskMax * dbLotsMax / ( dbProfit * dbLotsStep ) )
            * dbLotsStep, dbLotsMin ), dbLotsMax );
    Reason: