How to calculate Stop Loss pips from amount of money given

 

Stop Loss calculated from money given is useful in two cases:

1. Fixing a Stop Loss to a predefined value to risk. Let's say $80.

2. Stop Loss as percentage of the current account balance.


Here is the formula for calculating stop pips from account percentage:


///
/// The Account Percent Stop limits the loss to a 
/// predefined percent of the account balance. 
/// Returns the number of pips for the StopLoss.
double AccountPercentStopPips(string symbol, double percent, double lots)
{
    double balance  = AccountBalance();
    double exchrate = AccountExchangeRate(symbol);
    double lotsize  = MarketInfo(symbol, MODE_LOTSIZE);
    double point    = MarketInfo(symbol, MODE_POINT);
    double spread   = MarketInfo(symbol, MODE_SPREAD);

    double stopLossPips = percent * balance * exchrate / (lots * lotsize * point) - spread;

    return (stopLossPips);
}


There is a challenge in this simple at first task - Exchange Rate. The input parameter "Money to risk" is in the account currency but the result of the trade (profit or loss) is in the quoted currency, so there is three possibilities:

- Equal account and quoted currency: Example Account in USD and symbol EURUSD. Here the Exchange Rate = 1 because the profit is in the currency of the account.

- Account currency is the base currency: Account in USD and symbol EURUSD. Here the exchange rate is the current bid price.

- The account currency doesn't take part of the instrument being traded. Example: Account in EUR and instrument USDJPY. Here the Exchange rate has to be the current EURJPY rate.


Here is the code for implementing this formulas:


/// Gets the Account Exchange Rate
/// AccountExchangeRate = AccountCurrency / QuotedCurrency
/// It serves to convert the profit of a deal in account currency.
/// This code has to be improved!
double AccountExchangeRate(string symbol)
{
    if (StringLen(symbol) != 6)
        return (1);

    string accCurrency    = AccountCurrency();
    string baseCurrency   = StringSubstr(symbol, 0, 3);
    string quotedCurrency = StringSubstr(symbol, 3, 3);

    if (accCurrency == quotedCurrency)
        return (1);
    else if (accCurrency == baseCurrency)
        return (MarketInfo(symbol, MODE_BID));
    else
    {
        string pair = StringConcatenate(accCurrency, quotedCurrency);
        double rate = MarketInfo(pair, MODE_BID);
        
        LastError = GetLastError();
        if (LastError == 4106)
        {
            pair = StringConcatenate(quotedCurrency, accCurrency);
            rate = MarketInfo(pair, MODE_BID);
            LastError = GetLastError();
            if (LastError == 0)
                rate = 1 / rate;
            else 
                rate = 1;
        }
        else if (LastError != 0)
            rate = 1;
        
        return (rate);
    }
        
    return (1);
}

This code works fine for the majority of the forex symbol but has two drawbacks:

1. It doesn't work for indexes, shares, metals..

2. Its too complicated and slow.


Mu question is if there is any MaketInfo magic for converting account currency to pips that works in general. I mean for all possible account currencies and instruments.




 

Miroslav_Popov wrote >>

This code works fine for the majority of the forex symbol but has two drawbacks:

1. It doesn't work for indexes, shares, metals..

2. Its too complicated and slow.


Mu question is if there is some MaketInfo magic for converting account currency to pips that works in general. I mean for all possible account currencies and instruments.

MarketInfo(symbol,MODE_TICKVALUE) is what u r looking for... It's the value of one tick of the quote currency in the deposit currency. So in your original function it should be something like this (I am not home so can't verify this...):


double AccountPercentStopPips(string symbol, double percent, double lots)
{
    double balance   = AccountBalance();
    double tickvalue = MarketInfo(symbol, MODE_TICKVALUE);
    double lotsize   = MarketInfo(symbol, MODE_LOTSIZE);
    double spread    = MarketInfo(symbol, MODE_SPREAD);

    double stopLossPips = percent * balance / (lots * lotsize * tickvalue) - spread;

    return (stopLossPips);
}
 

Thank you Gordon, I'll check it.

It will be perfect if it works in all the cases available.

 

Seconded.

One point - I now always implement tickvalue/ticksize as a ratio as I have (rarely) seen tickvalue change in magnitude. In this case there was also a compensatory change in ticksize.


CB

 
cloudbreaker:

Seconded.

One point - I now always implement tickvalue/ticksize as a ratio as I have (rarely) seen tickvalue change in magnitude. In this case there was also a compensatory change in ticksize.


CB

I have never myself seen changes in ticksize (and a corresponding change in tickvalue). My current projects implement client-side stoploss (some of my partners are paranoid about brokers stoploss-hunting their ass), hence no need to use tickvalue to calculate the stops (the expert just looks directly at equity).


Anyway, here's the amended function for this rare occasion of ticksize and tickvalue fluctuations:

double AccountPercentStopPips(string symbol, double percent, double lots)
{
    double balance   = AccountBalance();
    double tickvalue = MarketInfo(symbol, MODE_TICKVALUE);
    double lotsize   = MarketInfo(symbol, MODE_LOTSIZE);
    double spread    = MarketInfo(symbol, MODE_SPREAD);
    double point     = MarketInfo(symbol, MODE_POINT);
    double ticksize  = MarketInfo(symbol, MODE_TICKSIZE);
    
    // fix for extremely rare occasion when a change in ticksize leads to a change in tickvalue
    double reliable_tickvalue = tickvalue * point / ticksize;           
    
    double stopLossPips = percent * balance / (lots * lotsize * reliable_tickvalue ) - spread;

    return (stopLossPips);
}

Popov, u can fnd more info on CB's suggestion in this thread -> https://www.mql5.com/en/forum/109552/page3.


I have (rarely) seen tickvalue change in magnitude. In this case there was also a compensatory change in ticksize.

CB, isn't it the other way around - a change of magnitude in ticksize brings the change in tickvalue?

Anyway, I completely agree with your method, but don't u find this thread disturbing -> https://www.mql5.com/en/forum/122784 ?

 
Yes - I wasn't commenting at all on which causes which ;-) On your other point, even though you use a client side SL, you may wish to use TL and TS when calculating lot size as a % of balance to risk based on the SL you've set. CB
 
cloudbreaker:
Yes - I wasn't commenting at all on which causes which ;-) On your other point, even though you use a client side SL, you may wish to use TL and TS when calculating lot size as a % of balance to risk based on the SL you've set. CB

Agreed.


p.s. I imagine TL is supposed to be TV = tickvalue, TS = ticksize... Cause usually TL = trendline and TS = trailing stop. For a Second there I had no idea what u were talking about :-)

 

CB I'm not sure I understand what you mean with this *you may wish to use TL and TS when calculating lot size as a % of balance to risk based on the SL you've set*.

Please, can you clarify?


The final picture must be something like this:


double AccountPercentStopPips(string symbol, double percent, double lots)
{
    double balance      = AccountBalance();
    double moneyrisk    = balance * percent / 100;
    double spread       = MarketInfo(symbol, MODE_SPREAD);
    double point        = MarketInfo(symbol, MODE_POINT);
    double ticksize     = MarketInfo(symbol, MODE_TICKSIZE);
    double tickvalue    = MarketInfo(symbol, MODE_TICKVALUE);
    double tickvaluefix = tickvalue * point / ticksize; // A fix for an extremely rare occasion when a change in ticksize leads to a change in tickvalue
    
    double stoploss = moneyrisk / (lots * tickvaluefix ) - spread;
    
    if (stoploss < MarketInfo(symbol, MODE_STOPLEVEL))
        stoploss = MarketInfo(symbol, MODE_STOPLEVEL); // This may rise the risk over the requested
        
    stoploss = NormalizeDouble(stoploss, 0);
    
    return (stoploss);
}
 
Set the stop loss based on the market. Set the lot size based on risk. Tickvalue changes if the account currency isn't in the symbol.
//+------------------------------------------------------------------+
//| Lot size computation.                                            |
//+------------------------------------------------------------------+
double  LotSize(double SL_points, bool modifying = false) {
    /* This function computes the lot size for a trade.
     * Explicit inputs are SL relative to bid/ask (E.G. SL=30*points,) and if
     * returned lot size will be used in a new order or be used to modify an
     * pending order (trade count-1.) Implicit inputs are the MM mode, the MM
     * multiplier and currently filled or pending trade count (used to reduce
     * available balance.) Mode, multiplier, adjusted account balance determined
     * the maximum dollar risk allowed. StopLoss determines the maximum dollar
     * risk possible per lot. Lots=maxRisk/maxRiskPerLot
     **************************************************************************/
    double  ab  = AccountBalance();
    switch(MMMode_F0M1G2) {
    case MMMODE_FIXED:
        atRisk  = MMMultplier;
        break;
    case MMMODE_MODERATE:
        // See https://www.mql5.com/en/articles/1526 Fallacies, Part 1: Money
        // Management is Secondary and Not Very Important.
        atRisk  = MathSqrt(MMMultplier * ab)/ab;    // % used/trade ~const.
        atRisk  = MathSqrt(MMMultplier * ab
                            * MathPow( 1 - atRisk, OrdersTotal()-modifying ));
        break;
    case MMMODE_GEOMETRICAL:
        atRisk  = MMMultplier * ab
                            * MathPow(1 - MMMultplier, OrdersTotal()-modifying);
        break;
    }
    double  maxLossPerLot   = SL_points/Point
                            * MarketInfo( Symbol(), MODE_TICKVALUE );
    // IBFX demo/mini       EURUSD TICKVALUE=0.1 MAXLOT=50 LOTSIZE=10,000
    // In tester I had a sale: open=1.35883 close=1.35736 (0.00147)
    // gain$=97.32/6.62 lots/147 points=$0.10/point or $1.00/pip.
    // IBFX demo/standard   EURUSD TICKVALUE=1.0 MAXLOT=50 LOTSIZE=100,000
    //                                  $1.00/point or $10.00/pip.
    double  LotStep = MarketInfo( Symbol(), MODE_LOTSTEP ),
    // atRisk / maxLossPerLot = number of lots wanted. Must be rounded/truncated
    // to nearest lotStep size.
    //
    // However, the broker doesn't care about the atRisk/account balance. They
    // care about margin. margin used=lots used*marginPerLot and that must be
    // less than free margin available.
    marginFree   = AccountFreeMargin(),                     // Free Margin
    marginPerLot = MarketInfo( Symbol(), MODE_MARGINREQUIRED ),
    // So I use, the lesser of either.

    // I don't use MODE_MAXLOT as OpenNow handles that.
    size =  MathFloor(  MathMin ( marginFree / marginPerLot
                                , atRisk     / maxLossPerLot )
                        / LotStep )*LotStep;        // truncate
    if (size < MarketInfo( Symbol(), MODE_MINLOT )) {
        // Multiple orders -> no free margin -> LotSize(SL=4.1)=0
        // [risk=9.48USD/40.80, margin=10.62/1334.48, MMM=1x1, coat=0]
        //       0.23                  0.007
        Print(
            "LotSize(SL=", DoubleToStr(SL_points/pips2dbl, Digits.pips), ")=",
            size, " [risk=",    atRisk, AccountCurrency(),  "/", maxLossPerLot,
                    ", margin=",    marginFree,             "/", marginPerLot,
                    ", MMM=",       MMMode_F0M1G2,          "x", MMMultplier,
                    ", coat=",      OrdersTotal(),  // Count Of active trades
                "]" );
        return(0.0);    // Risk limit.
    }
    atRisk  = size * maxLossPerLot; // Export for Comment()
    return(size);
}   // LotSize
 
gordon:

I have never myself seen changes in ticksize (and a corresponding change in tickvalue). My current projects implement client-side stoploss (some of my partners are paranoid about brokers stoploss-hunting their ass), hence no need to use tickvalue to calculate the stops (the expert just looks directly at equity).

Anyway, here's the amended function for this rare occasion of ticksize and tickvalue fluctuations:

Popov, u can fnd more info on CB's suggestion in this thread -> https://www.mql5.com/en/forum/109552/page3.

CB, isn't it the other way around - a change of magnitude in ticksize brings the change in tickvalue?

Anyway, I completely agree with your method, but don't u find this thread disturbing -> https://www.mql5.com/en/forum/122784 ?



In case you already know your stoploss pips (SL), this may serve your strategy...
  double Lots(string symbol, double percent, int SL)
{
    double balance   = AccountFreeMargin();// more conservative... may use equity or balance
    double tickvalue = MarketInfo(symbol, MODE_TICKVALUE);
    int spread    = MarketInfo(symbol, MODE_SPREAD);
    double point     = MarketInfo(symbol, MODE_POINT);
    double ticksize  = MarketInfo(symbol, MODE_TICKSIZE);
    
    double CBtickvalue = tickvalue * point / ticksize;           
    
    double lots  = percent * balance /  ( SL + spread ) * CBtickvalue  ;
      
    lots = NormalizeDouble(lots,1);
    
    return (lots);

}
 
cloudbreaker:
One point - I now always implement tickvalue/ticksize as a ratio as I have (rarely) seen tickvalue change in magnitude. In this case there was also a compensatory change in ticksize.

Must be implemented as a ratio. Here's an example where ticksize=0.5

Also pending order prices must be a multiple of ticksize.

Also do NOT normalizeDouble(lots,1) It MUST be a multiple of lotStep.

Reason: