Code for risk per trade? - page 2

 

Always good to compare notes so would be great to see.... I have my own version. I include a flag for whether it should compound, weightings based on time frame and a weighting based on an assesment of probability, which includes current market volatility and the mix of key metrics that triggered the trade. So "safe" trades get the full 2% but those with a higher chance of loss have the amount risked reduced accordingly. I will try and tidy mine up and post it.

V

 
Woah, we get to see codes ... how great is this !! :)
 

OK, this code does a few things. If you know your stoploss price and the equity you are willing to lose if your stops are hit then you want to calculate the maximum lotsize for which this will occur. Additionally you want the lotsize itself to be normalized to the granularity that your broker operates with (min lotsize combined with min lotstep). And finally you want to determine what the actual equity at risk ended up being once you are operating with the normalized lotsize.

First lets walk through the steps for you to integrate the functions into your existing EA's.

Step 1: Place all the file attachments in this post in your include path (...\experts\include\*.mqh)

Step 2: Add the following to the top of your EA so we have access to the call functions contained in the attached files

#include <Analyze_Currency_Symbol_2010.06.07.mqh>
#include <Trade_Position_Management_2010.06.07.mqh>
#include <StopLoss_Manager_2010.05.24.mqh>

Step 3: Add the following global variable declarations to the top of your EA, these will be used for sending parameters to the called functions later on

int      CurrentSymbolType,CurrentOrderType;
double   CurrentStopLossPrice,CurrentLotSize,CurrentEquityAtRisk;
string   CurrentBasePairForCross,CurrentCounterPairForCross;

Step 4: In your EA's init() routine add the following function calls so we analyze, characterize and define the needed information regarding the type of currency pair our EA is currently attached to when trading is occuring

   CurrentSymbolType=SymbolType();
   CurrentBasePairForCross=BasePairForCross();
   CurrentCounterPairForCross=CounterPairForCross();

Step 5: We need three pieces of information, we need to know (1) how much money the EA is to put at risk of loss, (2) the stoploss price, and (3) the whether ordertype is going to be long or short.
For this example let's assume we are using fixed fractional equity at risk, you already know the stoploss price based on a fixed stoploss or some indicator like ATR, and you define your ordertype from some indicator such that we can define the following variables:

extern double MaxPercentEquityAtRisk=0.5  // user defines global variable such that max VaR is 0.5% per trade
.
<snip>
.
// Section of code that determines whether a new trade is going to be long or short
CurrentOrderType=OP_BUY;   // in this case the order type is to be a Buy order
.
<snip>
.
// Section of code that determines the stoploss price
CurrentStopLossPrice=StopLossPricebyATR(CurrentATRTimeFrame,CurrentATRPeriod,CurrentOrderType,true);  // user has setup their preferred stoploss calculation, substitute your own here
.
<snip>
.
// right before we make our OrderSend call
CurrentEquityAtRisk=(MaxPercentEquityAtRisk/100.)*AccountBalance();  // user has already defined MaxPercentEquityAtRisk by extern param or other

Step 6: Now go to the place in your EA code in the start() routine where you would normally define the lotsize of a position before making your ordersend and add the following

// all the parameters being sent to LotSize() function have previously been defined in steps above
 
CurrentLotSize=LotSize(CurrentEquityAtRisk,CurrentStopLossPrice,CurrentOrderType,CurrentSymbolType,CurrentCounterPairForCross);
 
// the returned value of LotSize is captured by CurrentLotSize variable, it is currently not quantized to the broker lot increment requirement
// if we wanted the returned value to be properly quantized then we'd add another parameter to the end with a value of "true" as follows:
// CurrentLotSize=LotSize(CurrentEquityAtRisk,CurrentStopLossPrice,CurrentOrderType,CurrentSymbolType,CurrentCounterPairForCross,true);
// However for debugging purposes it is nice to first see the unquantized value just to check and confirm we are getting the results we expected:
 
Print("Max EquityAtRisk = $",DoubleToStr(CurrentEquityAtRisk,2)," and Max Lotsize = ",CurrentLotSize);
 
// now we can easily quantize the lot size so that it conforms to broker requirements
// the returned value will always be the maximum lotsize that is less than or equal to the initial lotsize specification
// such that your specified max VaR is not exceeded
 
CurrentLotSize=NormalizeLotSize(CurrentLotSize);
 
// do some checking to ensure we aren't smaller or larger than broker settings, it is up to you what you want your EA to do here if these conditions are met
 
if(CurrentLotSize<MarketInfo(CurrentSymbol,MODE_MINLOT)) CurrentLotSize=MarketInfo(CurrentSymbol,MODE_MINLOT);
if(CurrentLotSize>MarketInfo(CurrentSymbol,MODE_MAXLOT)) CurrentLotSize=MarketInfo(CurrentSymbol,MODE_MAXLOT);
 
// Now we want to compute the actual equity place at risk by the trade based on the actual lotsize we are going to use for the trade
 
CurrentEquityAtRisk=EquityAtRisk(CurrentLotSize,CurrentStopLossPrice,CurrentOrderType,CurrentSymbolType,CurrentCounterPairForCross);
Print("Current EquityAtRisk = $",DoubleToStr(CurrentEquityAtRisk,2)," and Current Lotsize = ",CurrentLotSize);

Step 7: Place your order

         switch(CurrentOrderType)
            {
            case OP_BUY    :  ticket = OrderSend(Symbol(),OP_BUY,CurrentLotSize,Ask,AllowedSlip,CurrentStopLossPrice,0,,DoubleToStr(CurrentEquityAtRisk*1.,2),CurrentMagicNumber,0,Aqua); break;
            case OP_SELL   :  ticket = OrderSend(Symbol(),OP_SELL,CurrentLotSize,Bid,AllowedSlip,CurrentStopLossPrice,0,DoubleToStr(CurrentEquityAtRisk*1.,2),CurrentMagicNumber,0,Red); break;
            default        :  Print("Error encountered in the OrderType() routine for opening positions");  break; // The expression did not generate a case value
            }
In summary we use the call functions in a number of ways.

First we use it to determine the maximum lotsize we can open for a given order direction and given stoploss price.

Then we normalize that lotsize (round it down) to the broker's required granularity.

And finally we feed that normalized lotsize back into another call function that tells us what our actual equity at risk is going to be (which will always be something less than our initially defined maximum allowed VaR).
 

My code is below. Obviously there are elements of it that are specific to me, so for sure this is not a turn key function! I have my own expectation calculations that I use to weight the fixed risk (so it's not really a fixed risk!). Nor do I need counter currency calculations as I'm spread betting in my base currency regardless of instrument. (The main reason I haven't got my head around it either).


I'm interested in the comparisons on the 3 methods. I pass a compounding flag to use a fixed baseline account size or compound it based on the current account size. I'm liking WHReoder's more detailed approach, so I may integrate that thinking. I've been undecided on what account balance to base compounding on. I've seen methods using AccountEquity(), but I feel this is on the aggressive side and basing risk on unrealised paper profit. AccountBalance() is the middle option, but doesn't consider ongoing trades that currently have confirmed profit from a trailing stop. Safe option I guess is AccountFreeMargin(). Any opinions? I would like to base it on current balance plus current confirmed profit from trailed stops on ongoing trades. I think I will work this up next.


I note Phil, we have a different way to normalise lot sizing. Although it works for me, perhaps mine is ineffective. Any comments on it?.

I got lost a bit reading your code with the equityatrisk and lotsize function. Clearly they are not, but it looked circular, in that lotsize was dependent on risk which was dependent on lot size.

As ever, comments appreciated.

V

   /* Lot sizing
   *  Pass in stop loss risk in points, trade type, whether to compound and the baseline account balance (for when not compounding)
   *  Applies a fixed risk % based on timeframe and weights that based on a risk assesment of the trade. EG Tradetype 2 would risk 1/2 of normal 
   *  It also applies a weighting based on fast and slow ATR as a measure of volatility (higher chance of triggering the stop)
   *  returns a lot size normalised to the account type.
   */
   double  lotsize(double risk,int tradetype,bool compound,double startbalance)
         {
         double   riskpercent,ttw,equity;
         switch ( Period() )
               {
               case PERIOD_MN1: riskpercent = 0.05; break;
               case PERIOD_W1:  riskpercent = 0.05; break;
               case PERIOD_D1:  riskpercent = 0.05; break;
               case PERIOD_H4:  riskpercent = 0.05; break;
               case PERIOD_H1:  riskpercent = 0.05; break;
               case PERIOD_M30: riskpercent = 0.025; break;
               case PERIOD_M15: riskpercent = 0.02; break;
               case PERIOD_M5:  riskpercent = 0.02; break;
               case PERIOD_M1:  riskpercent = 0.01; break;
               default:    Alert(" NO PERIOD_ID. POTENTIAL FIXED RISK% ISSUE"); riskpercent = 0.02; break;
               }
         switch(tradetype)
            {
            case 1:  ttw=1.0;   break;
            case 2:  ttw=1.0;   break;
            case 3:  ttw=1.0;   break;
            case 4:  ttw=1.0;   break;
            case 5:  ttw=1.0;   break;
            case 6:  ttw=1.0;   break;
            default: ttw=1.0;   break;
            }   
         
         if (compound)
            {
            equity=AccountBalance();
            }
         else
            {
            equity=startbalance;
            }
         double   stoploss             =  risk/Point;
         double   PipValuePerLot       =  MarketInfo(Symbol(),MODE_LOTSIZE) * MarketInfo(Symbol(),MODE_POINT);
         double riskweighting=atr/atr9;
         if (riskweighting>1)
            {
            riskweighting=1;
            }
         double weightedrisk=riskweighting*riskpercent*ttw;
         
         double NeededPipValue = (equity * weightedrisk) / stoploss; // Calculate the needed value per pip to risk correct amount of capital according to sl size
         switch(MarketInfo(Symbol(),MODE_MINLOT))
            {
            case 1.0:   lotdigits=0; break;           //" Standard account";
            case 0.1:   lotdigits=1; break;           //" Mini Lot account"
            case 0.01:  lotdigits=2; break;           //" Micro Lot account";
            default:    lotdigits=1; Alert("Uncoded lot sizeaccount"); break;
            }
         lot = NormalizeDouble((NeededPipValue / PipValuePerLot),lotdigits);         // Calculate the lot size 
         Alert("At risk : £",DoubleToStr(stoploss*lot,2));
         return (lot);
         }
 
Viffer:

My code is below. Obviously there are elements of it that are specific to me, so for sure this is not a turn key function! I have my own expectation calculations that I use to weight the fixed risk (so it's not really a fixed risk!). Nor do I need counter currency calculations as I'm spread betting in my base currency regardless of instrument. (The main reason I haven't got my head around it either).


Yeah, in terms of "what do you use in practice" the VaR itself is an algorithm designed to vary the output (the amount of money to put at risk of losing) based on an input that is a metric of the predictor accuracy.

Remember we are trying to forecast the market here and as such we should all have a system that is keeping tabs on how well our predictions are doing...if the weatherman has been forecasting sunny skies all week but it has rained everyday you would put little faith in his forecast for tomorrow's weather.

When your entry/exit strategies are doing a good job predicting the markets then you are legitimate to place more confidence in them and increase the amount of equity you are willing to place at risk of loss.
I've been undecided on what account balance to base compounding on. I've seen methods using AccountEquity(), but I feel this is on the aggressive side and basing risk on unrealised paper profit. AccountBalance() is the middle option, but doesn't consider ongoing trades that currently have confirmed profit from a trailing stop. Safe option I guess is AccountFreeMargin(). Any opinions? I would like to base it on current balance plus current confirmed profit from trailed stops on ongoing trades. I think I will work this up next.
You want to keep track of the VaR currently allocated to all positions open on account so the next potential trade is not putting a larger percentage of equity at risk than it is entitled to.

I create allocation portfolios. I assign each currency its own allocation that is independent of the other currency pairs. If I have a $10k account and I am trading 4 currency pairs then each pair is assigned $2500 at time-zero. Each pair has its own EA that is monitoring its predictor accuracy and scaling the VaR on its own. If the EURUSD allocation is up $2k that means for the next EURUSD trade the EURUSD EA has $4.5k at its disposal. If the USDJPY allocation is down $1k that means for the next USDJPY trade the USDJPY EA has $1.5k at it disposal to put towards VaR.

This rewards the winners and self-penalizes the losers in the portfolio, within which they are individually penalizing themselves for their own predictor inaccuracies of late.
we have a different way to normalise lot sizing. Although it works for me, perhaps mine is ineffective. Any comments on it?.
From my understanding of your code it appears you are not worried about those situations where a broker has different values for MINLOT and LOTSTEP.

The only downside I see with your current implementation is if for some reason a broker does not have a LOTSTEP value that is equal to 1*lotdigits then you will be rounding to values that the broker will reject. For example a broker that has MINLOT = 0.1 and LOTSTEP = 0.05 would accept lotsizes of 0.10, 0.15, 0.20, etc but because lotdigits is 2 your equation would potentially create lot values that aren't quantized to the broker's requirements for lotstep...you could be sending 0.11 or 0.16 lot requests.
I got lost a bit reading your code with the equityatrisk and lotsize function. Clearly they are not, but it looked circular, in that lotsize was dependent on risk which was dependent on lot size.
Let's do an example.

Let's say I am trading the USDJPY and through whatever means necessary I have decided that I want to open a short position which places $500 at risk of loss if my stops are hit.

So the first thing I do is find out how many lots I can open a short position with, based on current price and my stoploss price, by using the LotSize() function.

Let's say it returns the value 0.16235467...obviously I can't send this unquantized value to the broker, so I feed this value into the NormalizeLotSize() function and let's say it returns the value 0.15 lots because my broker has MINLOT = 0.1 and LOTSTEP = 0.05 (so 0.16 lots would have been unacceptable).

0.15 lots is less than 0.16235467 lots, so if I open my position with 0.15 lots instead of 0.16235467 lots I will actually be placing less equity at risk of loss than I had previously allocated towards the trade. So now I want to determine what my actual equity at risk is going to be for the position I am about to open. It isn't going to be $500, it is going to be something less than $500.

So I feed the quantized lotsize and stoploss price info into the EquityAtRisk() function and it returns the VaR...the actual value at risk of loss for the proposed position parameters. In this example it would return the value of $461.95. Depending on how I setup my money manager to track aggregate VaR for my entire portfolio this value would then be fed into other functions and so on.

Edit: the additional utility of the EquityAtRisk() function is that it enables you to determine the equity at risk of a position at a future time when the stops have moved from their initial value. Thus you can keep an accurate picture of your portfolio's VaR so it is simple matter to compute the allowed equity at risk for the next position that will be opened. (I think you mentioned you wanted something like this in your post above)
 

Philip,


When I test your code, I get some extremely small lot sizes in JPY pairs - do you see the same?

 

I have only tested/operated this code on the following brokers: Alpari(US), CitiFX Pro, CMS, Gain Capital (forex.com), FXCM, FXDD, IBFX, MIG Bank, and ODL. It works for those brokers.

I'm curious how/why the code would actually generate lot sizes that are too small for the JPY pairs. One possible way this would be happening is if the stoploss price that you are sending when calling the LotSize() function is too large or too small.

Are you confident you are computing the stoploss price correctly?

 
Yes I am. I will post my code alteration here tonight, if that is alright, since I had to alter the code to accept a passed in symbol (instead of relying on the symbol from the chart).
 

Thanks for the code phillip, I´ve just implemented it and noticed it does not work if the AccountCurrency() is not included in the pair I trade. Your code then generates this error:


2012.02.10 07:10:53 2011.10.20 23:44 EA Mod 2 AUDNZD,H1: Error encountered in the SWITCH routine for calculating the EquityAtRisk

^In the above case my account currency is EUR and I trade the AUDNZD.


It only works correct if I trade symbols that include my AccountCurrency() in the symbol. Do you have a solution for this? Because otherwise your code works really well!


Thanks.

 
Viffer:

My code is below.

switch(MarketInfo(Symbol(),MODE_MINLOT))
            {
            case 1.0:   lotdigits=0; break;           //" Standard account";
            case 0.1:   lotdigits=1; break;           //" Mini Lot account"
            case 0.01:  lotdigits=2; break;           //" Micro Lot account";
            default:    lotdigits=1; Alert("Uncoded lot sizeaccount"); break;
            }
lot = NormalizeDouble((NeededPipValue / PipValuePerLot),lotdigits);

NormalizeDouble will only work on multiples of 10 (0.1, 0.01) not on any other. You also only trade multiples of minlot not multiples of lot step (0.10, 0.11, 0.12...) Just do it right.
double NormalizeLots(double lots, string pair=""){
    if (pair == "") pair = Symbol();
    double  lotStep     = MarketInfo(pair, MODE_LOTSTEP),
            minLot      = MarketInfo(pair, MODE_MINLOT);
    lots            = MathRound(lots/ls) * ls;
    if (lots < minLot) lots = 0;    // or minLot
    return(lots);
}
double NormalizePrice(double p, string pair=""){
    // https://www.mql5.com/en/forum/135345 zzuegg reports for non-currency DE30:
    // MarketInfo(chart.symbol,MODE_TICKSIZE) returns 0.5
    // MarketInfo(chart.symbol,MODE_DIGITS) return 1
    // Point = 0.1
    // Prices to open must be a multiple of ticksize
    if (pair == "") pair = Symbol();
    double ts = MarketInfo(pair, MODE_TICKSIZE)
    return( MathRound(p/ts) * ts );
}
Reason: