//+------------------------------------------------------------------+
//|                                            Sell Stop Bracket.mq4 |
//|                            Copyright 2020, Niclas Hummel Trading |
//|                                        https://niclashummel.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, Niclas Hummel Trading"
#property link      "https://niclashummel.com/"
#property version   "1.21"
#property strict
#property show_inputs
#define INDEX uint   // Zero based.
#define COUNT uint   // One based.
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum enum_SwitchOnOff
  {
   ON=1,OFF=0
  };
double res1[];
double res2[][1000];
double corr[];
string currencies[];
input double StDMultiplier = 1.0;            // Stop Multiplier (1.0 = unchanged)
input double LotMultiplier = 1.0;            // Lotsize Multiplier
input double TargetMultiplier = 2.0;         // Risk to Reward Multiplier (1:x Target)
input int stDPeriod = 30;                    // Standard Deviation Period for Stoploss
input int lookBack = 90;                     // Lookback Period for Correlation Coefficient
input enum_SwitchOnOff RiskPercentage = ON;  // Risk In Percentage ON/OFF
input double RiskPercent = 5.0;              // Riskpercentage of Accountbalance
input double RiskAmount = 50.00;             // Riskamount if Riskpercentage OFF
input int MagicNumber = 213891;              // Magic Number                     
double riskInCurr = 0.0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //--- resize Array to number of lookback period
   ArrayResize(res1,lookBack);
   //--- execute long order
   _longOrder();
  }
//+------------------------------------------------------------------+
void _longOrder()
  {
   //--- prepare variables for OrderSend()
   double stD = iStdDev(NULL,0,stDPeriod,0,MODE_SMMA,PRICE_CLOSE,1) * StDMultiplier;
   double stD2 = iStdDev(NULL,0,stDPeriod,0,MODE_SMMA,PRICE_CLOSE,1);
   double StopBPS = stD/Close[1];
   double stoplevel = MarketInfo(NULL,MODE_STOPLEVEL)*Point;
   double bid = MarketInfo(NULL,MODE_BID);
   double tv = MarketInfo(NULL,MODE_TICKVALUE);
   double ts = MarketInfo(NULL,MODE_TICKSIZE);
   riskInCurr = RiskPercentage ? ((RiskPercent/100)*AccountBalance()) : RiskAmount;
   double _lotSize=NormalizeDouble(riskInCurr/(bid*StopBPS*(tv/ts)),(int)MarketInfo(NULL,MODE_DIGITS));
   
   //--- check for minimum and maximum lot size
   if(_lotSize < SymbolInfoDouble(NULL,SYMBOL_VOLUME_MIN))
      _lotSize = SymbolInfoDouble(NULL,SYMBOL_VOLUME_MIN);
   if(_lotSize > SymbolInfoDouble(NULL,SYMBOL_VOLUME_MAX))
      _lotSize = SymbolInfoDouble(NULL,SYMBOL_VOLUME_MAX);
   
   //--- _lotSize gets multiplied by correlation coefficients
   _lotSize *= calcCCs();
   
   //--- _lotSize gets adjusted to Lotstep
   _lotSize = NormalizeDouble(MathFloor(_lotSize / MarketInfo(NULL,MODE_LOTSTEP)) * MarketInfo(NULL,MODE_LOTSTEP) * LotMultiplier,2);
   if(_lotSize == 0)
     {
      Alert("Lotsize can't be applied, because Minimum Lotsize risk goes over risk threshold. Reduce risk threshold and start again");
      ExpertRemove();
     }
   if(CheckMoneyForTrade(_lotSize,OP_SELLSTOP))
     {
      if(!OrderSend(NULL,OP_SELLSTOP,_lotSize,NormalizeDouble(bid-stD2,Digits),2,
                    NormalizeDouble(bid-stD2+stD,Digits),
                    NormalizeDouble(bid-stD2-stD*TargetMultiplier,Digits),(string)stD,MagicNumber))
         GetLastError();
     }
  }
//+------------------------------------------------------------------+
//| Checks if enough margin is available for opening the trade       |
//+------------------------------------------------------------------+
bool CheckMoneyForTrade(double lots,int type)
  {
   if(lots < 0) {
      Alert("Can't open trade because correlations are too high");
      return false;
   }
   if(type%2==0)
      type=0;
   else
      type = 1;
   double free_margin=AccountFreeMarginCheck(Symbol(),type,lots);
//-- if not enough money
   if(free_margin<0)
     {
      Alert("Not enough money for " + (string)lots + " Lots in " + (string)Symbol() + " Error code = " + (string)GetLastError());
      return false;
     }
//-- check successfull
   return true;
  }
//+------------------------------------------------------------------+
//| Calculates correlation coefficients and takes into account, that |
//| there can be different initial stoplosses, that affect the       |
//| consideration weight of the correlation coefficient              |
//+------------------------------------------------------------------+
double calcCCs()
  {
   //--- first create a series and fill res1 with current symbol for looking back lookBack period (=90)
   createSeriesThisSymbol();
   int count = 0;
   //--- go through all orders
   for(int j = 0; j < OrdersTotal(); j++)
     {
      if(!OrderSelect(j,SELECT_BY_POS,MODE_TRADES))
         GetLastError();
      //--- only if it's not the same symbol, that the trade is made and if not already targeted
      if(OrderCloseTime() == 0 && OrderSymbol() != Symbol() && !isInCurrencyList(OrderSymbol()))
        {
         //--- resize res2 to amount of runs
         ArrayResize(res2,count+1);
         //--- fill res2, depending on the currency with close to close % returns
         for(int i = 0; i < lookBack; i++)
           {
            string Symbol2 = OrderSymbol();
            res2[count][i] = (iClose(Symbol2,0,i)-iClose(Symbol2,0,i+1))/iClose(Symbol2,0,i);
           }
         //--- resize currencies to amount of runs
         ArrayResize(currencies,count+1);
         //--- fill currencies to identifier = amount of runs (= amount of other currencies/symbols with open orders)
         currencies[count] = OrderSymbol();
         count++;
        }
     }
   int l = 0;
   
   int sizeRes2 = (int)ArraySize(res2)/1000;
   
   //--- go through all symbols with open orders
   for(int k = 0; k < sizeRes2; k++)
     {
      //--- calculate correlation coefficient
      double cc = MathAbs(correlation_coefficient(res1,res2,k,lookBack));
      if(cc >= 0.1)
        {
         ArrayResize(corr,l+1);
         //--- save correlation coefficient in corr array
         corr[l] = cc;
         l++;
        }
     }
   double correlation_decrease_factor = 1.0;
   
   if(ArraySize(corr) >= 1)
     {
      //--- sort for highest correlation coefficient
      ArraySort(corr);
      for(int o = ArraySize(corr)-1; o >= 0; o--)
        {
         //--- calculate the amount that the position size should be reduced
         //--- takes into account the riskRatio = relation to initial full position size
         correlation_decrease_factor *= (1-corr[o]*riskRatio(currencies[o]));
        }
     }
   return correlation_decrease_factor;
  }
//+------------------------------------------------------------------+
//| This function calculates the ratio between the full currency     |
//| amount and the already invested initial risk in currency of the  |
//| open positions                                                   |
//+------------------------------------------------------------------+
double riskRatio(string OS)
  {
   return ((totalLots(OS) * stoploss_distance(OS) * MarketInfo(OS,MODE_TICKVALUE)) / MarketInfo(OS,MODE_TICKSIZE)) / riskInCurr;
  }
//+------------------------------------------------------------------+
//| Creates a series of close to close % returns                     |
//+------------------------------------------------------------------+
void createSeriesThisSymbol()
  {
   for(int i = 0; i < lookBack; i++)
     {
      res1[i] = (Close[i]-Close[i+1])/Close[i];
     }
  }
//+------------------------------------------------------------------+
//| Calculates correlation coefficient of two arrays                 |
//+------------------------------------------------------------------+
double correlation_coefficient(double& a[], double& b[][], int k, COUNT length, INDEX iBeg=0)
  {
   INDEX    iEnd  = iBeg + length;
   double   Ex=0.0,  Ex2=0.0,    Ey=0.0,  Ey2=0.0,    Exy=0.0;    // Ex=Sum(x)
   for(; iBeg < iEnd; ++iBeg)
     {
      double   x = a[iBeg],   y = b[k][iBeg];
      Ex += x;
      Ex2 += x * x;
      Ey += y;
      Ey2 += y * y;
      Exy += x * y;
     }
   double   ssxy  = length * Exy - Ex * Ey;
   double   ssxx  = length * Ex2 - Ex * Ex;
   double   ssyy  = length * Ey2 - Ey * Ey;
   double   deno  = MathSqrt(ssxx * ssyy);
   return (deno == 0.0) ? 0.0 : ssxy / deno;
  }
//+------------------------------------------------------------------+
//| Checks if the OrderSymbol os in the argument is in the currencies|
//| array                                                            |
//+------------------------------------------------------------------+
bool isInCurrencyList(string os)
  {
   for(int i = 0; i < ArraySize(currencies); i++)
     {
      if(os == currencies[i])
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Calculates the stoploss distance between the initial stoploss    |
//| and the current average price                                    |
//+------------------------------------------------------------------+
double stoploss_distance(string OS)
  {
   double avge=1, avge_sl = 1, Avgprice=0, Avgprice_sl=0, lts=0;
   
   //--- saves open buy and sell orders in count variables
   int longcount = countOrders(OP_BUY,OS);
   int shortcount = countOrders(OP_SELL,OS);
   if(longcount == 0 && shortcount == 0)
     {
      longcount = 1;
      shortcount = 1;
     }
     
   string ordercomment = "";
   
   //--- finds the ordercomment with the initial stoploss (when opened by my script with MagicNumber)
   for(int i = 0; i<=OrdersTotal(); i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS))
         GetLastError();
      if(OrderType() < 2  && OrderSymbol() == OS && OrderMagicNumber() == MagicNumber)
        {
         ordercomment = OrderComment();
         break;
        }
     }
   
   //--- if more long orders
   if(longcount > shortcount)
     {
      //--- first calculate total Lots
      for(int i = 0; i<=OrdersTotal(); i++)
        {
         if(!OrderSelect(i,SELECT_BY_POS))
            GetLastError();
         if(OrderType() == OP_BUY  && OrderSymbol() == OS)
           {
            lts+= OrderLots();
           }
        }
      
      //--- then calculate the average entry price and stoploss
      for(int h = 0; h<=OrdersTotal(); h++)
        {
         if(!OrderSelect(h,SELECT_BY_POS))
            GetLastError();
         if(OrderType() == OP_BUY  && OrderSymbol() == OS)
           {
            Avgprice+= OrderOpenPrice() * (OrderLots() / lts);
            Avgprice_sl+= OrderStopLoss() * (OrderLots() / lts);
           }
        }
      avge = NormalizeDouble(Avgprice,(int)MarketInfo(OS,MODE_DIGITS));
      avge_sl = NormalizeDouble(Avgprice_sl,(int)MarketInfo(OS,MODE_DIGITS));
     }
   //--- if more short orders
   else
     {
     //--- first calculate total Lots
      for(int u = 0; u<=OrdersTotal(); u++)
        {
         if(!OrderSelect(u,SELECT_BY_POS))
            GetLastError();
         if(OrderType() == OP_SELL  && OrderSymbol() == OS)
           {
            lts+= OrderLots();
           }
        }
      
      //--- then calculate the average entry price and stoploss
      for(int j = 0; j<=OrdersTotal(); j++)
        {
         if(!OrderSelect(j,SELECT_BY_POS))
            GetLastError();
         if(OrderType() == OP_SELL && OrderSymbol() == OS)
           {
            Avgprice+= OrderOpenPrice() * (OrderLots() / lts);
            Avgprice_sl+= OrderStopLoss() * (OrderLots() / lts);
           }
        }
      avge = NormalizeDouble(Avgprice,(int)MarketInfo(OS,MODE_DIGITS));
      avge_sl = NormalizeDouble(Avgprice_sl,(int)MarketInfo(OS,MODE_DIGITS));
     }
   if(avge == 1)
      avge = 0.0;
   if(avge_sl == 1)
      avge_sl = 0.0;
   
   double oc = (double)ordercomment;
   
   //--- if no order was opened there's no initial stoploss, so take the average stoploss
   if(oc == 0)
     {
      return MathAbs(avge_sl-avge);
     } else {
      return oc;
     }
  }
//+------------------------------------------------------------------+
//| Counts all orders with certain OrderType in certain Symbol       |
//+------------------------------------------------------------------+
int countOrders(int oType,string OS)
  {
   int count=0;
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(OrderSelect(i,SELECT_BY_POS))
        {
         if(OrderSymbol()==OS)
           {
            if(OrderType()==oType || oType<0)
              {
               count++;
              }
           }
        }
     }
   return count;
  }
//+------------------------------------------------------------------+
//| Returns total Lots for certain Symbol of open orders             |
//+------------------------------------------------------------------+
double totalLots(string OS)
  {
   double lts = 0.0;
   for(int i = 0; i<=OrdersTotal(); i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS))
         GetLastError();
      if(OrderType() < 2  && OrderSymbol() == OS)
        {
         lts+= OrderLots();
        }
     }
   return lts;
  }
//+------------------------------------------------------------------+
             
            
            
            
Comments