//+------------------------------------------------------------------+
//| Stoch Diff 2 Pair EAsq.mq4
//+------------------------------------------------------------------+

#property copyright "roundrock"
#property link      ""

string VERSION = "Stoch Diff 2 Pair EAsq 2011.03.01";

/* This EA trades the "Hedge and Correlation Strategy" thread at FF:
 * http://www.forexfactory.com/showthread.php?t=160912
 * 
 * This EA was initially posted by "roundrock" here:
 * http://www.forexfactory.com/showthread.php?p=4351329#post4351329
 *
 * Squalou main mods:
 * [2011.03.01:]
 * - made trade closing even more reliable: it now retries until both trades are definitely closed (out of the open trades list)
 * - it should also succeed if one of the trades was closed manually;
 * - Slippage is now a "true pips" value, whatever broker/pair digits is being handled;
 *
 * [2011.02.16:]
 * - made trade opening and closing calls more reliable (retries 10 times);
 *
 * [2011.02.11:]
 * - added CashStopLoss input: trades will close if DD reaches this amount of cash;
 *
 * [2011.02.07:]
 * - added RiskPerCent input: adjust LotSize to % of FreeMargin;
 * - added MaxLots to limit LotSize;
 * - when "Pair2" input is left empty, the EA will set it to the "Pair2" Label object it finds on the chart;
 *   The "Pair2" object is set by the "Basket Profit 2 Pairs sq.mq4" indicator if it loaded on the chart;
 *
 * [2011.02.01:]
 * - added optional "ProfitTarget" input: will close both trades if the combined profit reaches this value (including swap and commission)
 *   (value in your account's currency: 10.0 means 10USD is your account is in USD, or 10EUR if EUR account);
 * - visual cosmetic changes:
 *   - trades opening is marked with a blue vertical line on the chart;
 *   - trades closure is marked with a red vertical line on the chart;
 *   - the Comment area displays various useful information:
 *     - EA start time and uptime;
 *     - Stoch values for both pairs, and Difference;
 *     - currently or last opened trades: pairs, direction, current individual profit/loss in account's currency units,
 *     - combined profit/loss, and MAX DRAWDOWN -- this will be helpful to determine inputs tuning;
 *     - last trades closure time
 * - auto-MagicNumber generation to ensure multiple EAs running concurrently will not mix-up trades:
 *   set to 0 to auto generate;
 * - some "cosmetic" code changes (inputs names, tabs, etc)
 * 
 * [2011.03.07:]
 * - correlation filter added
 */

#include <stderror.mqh>
#include <stdlib.mqh>

extern string Pair1 = "";
extern string Pair2 = "";
extern bool   BuySellMode = true; // true: BUY+SELL mode (for POSITIVELY CORRELATED pairs), or false: BUY+BUY / SELL+SELL mode (for NEGATIVELY CORRELATED pairs)
extern int    EnterAboveCorr = 75;
extern string OpenTradeStoch_notes = "--- Open Trades if stoch difference is above this number ---";
extern double OpenTradeStoch = 85;
extern bool   OpenTradeAtBarOpen  = false; // false can open trades at each tick; true will open trades only at bar open based on stoch of previous bar;
extern string CloseTradesStoch_notes = "--- Close Trades if stoch difference is below this number ---";
extern double CloseTradesStoch = 5;
extern bool   CloseTradesAtBarOpen = false; // false can close trades at each tick; true will close trades only at bar open based on stoch of previous bar;
extern string ProfitTarget_notes = "--- Close Trades if Profit target reached ---";
extern double ProfitTarget = 10.00;
extern double CashStopLoss = 0; // SL in cash amount; will close trades when DD reaches this amount;
extern int    K = 100;
extern int    D = 1;
extern int    S = 1;
extern int    Slippage = 3;
extern double LotSize = 0.1;
extern string _RiskPerCent = "--- Risk % of Free Margin:";
extern string _RiskPerCent2= "--- 0 will use fixed LotSize input:";
extern double RiskPerCent = 0;
extern double MaxLots = 9;
extern int    MagicNumber = 3331; // keep 0 to auto-generate different magicnumbers based on the 2 pairs and the timeframe;
extern string NAME = "Stoch Diff 2 Pair EAsq";


int pipMultTab[]={1,10,1,10,1,10,100}; // multiplier to convert pips to Points;
int barsTotal = 0;
string ccy;
int tf;
double pnt;
string m = "";

datetime time_EA_started;
string buyTradeStatus,sellTradeStatus,orderclosetime;
bool TradeExist;
double current_UPL, max_DD;
int ticket1,ticket2;
string  CloseWhen,OpenWhen;
string last_comment = "";
double corr;

//+------------------------------------------------------------------+
int init()
//+------------------------------------------------------------------+
{
  string m = StringSubstr(Symbol(), 6, StringLen(Symbol()) - 6);
  if (Pair1 == "") Pair1 = Symbol();
  else Pair1 = StringSubstr(Pair1,0,6) + m;

  // update LotSize based on input Risk per-cent of free-margin
  if (RiskPerCent>0) LotSize = NormalizeDouble(AccountFreeMargin() * AccountLeverage() / 1000000 * RiskPerCent, 0) * MarketInfo(Symbol(), MODE_MINLOT);
  LotSize = MathMin(LotSize,MaxLots);

  set_Pair2(Pair2);

  time_EA_started = TimeCurrent();

  if (MagicNumber==0) MagicNumber = create_MagicNumber(Period()+NAME+Pair1+Pair2);

  Print("StochDiff+"+Pair1+"-"+Pair2+" "+MagicNumber+" M"+Period());

  if (CloseTradesAtBarOpen) CloseWhen = "(Bar)"; else CloseWhen = "(tick)";
  if (OpenTradeAtBarOpen)  OpenWhen = "(Bar)"; else OpenWhen = "(tick)";

  // make sure we rescan trades history when restarting the EA
  ticket1=0;
  ticket2=0;
  
  start_robot();
  
  return (0);
}

//+------------------------------------------------------------------+
int deinit()
//+------------------------------------------------------------------+
{
  return (0);
}

bool robot_stopped=false;
string robot_stopped_reason;

void start_robot()
{
  robot_stopped=false;
  robot_stopped_reason="";
}

int stop_robot(string reason)
{
  string comment = "\n\n"
    +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
    +"!!!                       !!!\n"
    +"!!!        WARNING        !!!\n"
    +"!!!                       !!!\n"
    +"!!!>> "+reason+" <<\n"
    +"!!!                       !!!\n"
    +"!!!      ROBOT STOPPED    !!!\n"
    +"!!!                       !!!\n"
    +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";
  Comment(comment);
  robot_stopped=true;
  if (reason!="") robot_stopped_reason=reason;
  return(-1);
}

//+------------------------------------------------------------------+
int start()
//+------------------------------------------------------------------+
{
  if (OpenTradeStoch<=CloseTradesStoch) {
    stop_robot("Inconsistent OpenTradeStoch / CloseTradesStoch values");
    return;
  }
  if (ProfitTarget<0) {
    stop_robot("Inconsistent ProfitTarget value");
    return;
  }
  if (MarketInfo(Pair1,MODE_ASK)==0) {
    stop_robot("Invalid Pair1 \""+Pair1+"\"");
    return;
  }
  get_Pair2_from_chart(Pair2);// will update itself if "Pair2" label object was changed (manually or from another indicator/EA)
  if (MarketInfo(Pair2,MODE_ASK)==0) {
    stop_robot("Invalid Pair2 \""+Pair2+"\"");
    return;
  }
  if (Pair1==Pair2) {
    stop_robot("Pair1 and Pair2 must be different");
    return;
  }

  int t=GetTickCount();

  static datetime cur_bar;
  bool new_bar;
  if (cur_bar!=Time[0]) {
    cur_bar = Time[0];
    new_bar=true;
  } else {
    new_bar=false;
  }

  if (CheckTradeExist(Pair1,ticket1, Pair2,ticket2)) {

    // both trades exist: check for closing both
    if (CashStopLoss>0 && current_UPL<=-CashStopLoss) // combined cash Stop Loss hit
    {
      // close existing trades
      last_comment = TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS)+ "SL hit, closing Trades Now"+"\n";
      CloseTrades();
      orderclosetime = "Last Orders Closed at "+TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS);
    }

    if ((CloseTradesAtBarOpen && new_bar) || !CloseTradesAtBarOpen || ProfitTarget>0) {
    
      int shift;
      if (CloseTradesAtBarOpen) shift = 1; else shift = 0;
      double stoch1 = iStochastic(Pair1, 0, K, D, S, 0, 0, 0, shift);
      double stoch2 = iStochastic(Pair2, 0, K, D, S, 0, 0, 0, shift);

      if ( ( CloseTradesStoch>0  && MathAbs(stoch1 - stoch2) < CloseTradesStoch ) // Stoch Diff for closing reached
        || ( ProfitTarget>0 && current_UPL>=ProfitTarget )) // combined Profit target reached
      {
        // close existing trades
        last_comment = TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS)+ "Closing Trades Now"+"\n";
        CloseTrades();
        orderclosetime = "Last Orders Closed at "+TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS);
      }

    }
    
  } else {

    // no trade exist: check for open

    if ((OpenTradeAtBarOpen && new_bar) || !OpenTradeAtBarOpen) {
    
      if (OpenTradeAtBarOpen) shift = 1; else shift = 0;
      stoch1 = iStochastic(Pair1, 0, K, D, S, 0, 0, 0, shift);
      stoch2 = iStochastic(Pair2, 0, K, D, S, 0, 0, 0, shift);

      if (!BuySellMode)
      { // BUY+BUY / SELL+SELL mode for NEGATIVELY CORRELATED pairs
        // invert stoch2 to compute the Stoch Diff
        stoch2 = 100-stoch2;
      }

      corr = 100*cp(Pair1,Pair2,1440,50);
      
      if (MathAbs(stoch1 - stoch2) > OpenTradeStoch && corr > EnterAboveCorr)
      {
        // ready to open trade.
        string comment;
        comment = "StochDiff+"+Pair1+"-"+Pair2+" "+MagicNumber+" M"+Period();

        Print("Ready to place orders. Stoch1 " + DoubleToStr(stoch1, 2) + " stoch2 " + DoubleToStr(stoch2, 2) + " Difference " + DoubleToStr(MathAbs(stoch1 - stoch2), 2)+ " Corr " + DoubleToStr(corr,2));

        if (BuySellMode) // BUY+SELL mode for POSITIVELY CORRELATED pairs
        {
          if (stoch1 < stoch2) { // BUY Pair1 / SELL Pair2
            ticket1 = BUY (Pair1, LotSize, comment);
            ticket2 = SELL(Pair2, LotSize, comment);
          } else {  // SELL Pair1 / BUY Pair2
            ticket1 = SELL(Pair1, LotSize, comment);
            ticket2 = BUY (Pair2, LotSize, comment);
          }

        }
        else // BUY+BUY / SELL+SELL mode (for NEGATIVELY CORRELATED pairs)
        { 
          // stoch1 and mirrored_Stoch2 are far away from each other
          stoch2 = 100-stoch2; // retrieve original stoch2 value
          // if Stochs are on the higher half(>50) =>SELL both, if stochs are on the lower half(<50) =>BUY both
          if ((stoch1+stoch2)/2<50) { // BUY both pairs
            ticket1 = BUY (Pair1,  LotSize, comment);
            ticket2 = BUY (Pair2,  LotSize, comment);
          } else { // SELL both pairs
            ticket1 = SELL(Pair1,  LotSize, comment);
            ticket2 = SELL(Pair2,  LotSize, comment);
          }
        }

        WaitOrdersOpen(ticket1, ticket2);
      }
    }

  }

  static datetime lastcommenttime;
  //if (TimeCurrent()-lastcommenttime>5)
  {
    lastcommenttime = TimeCurrent();
    Comment(WindowExpertName()+" started on "+TimeToStr(time_EA_started)+" (uptime="+DoubleToStr((TimeCurrent()-time_EA_started)/60,0)+"min)\n"
      +"LotSize="+DoubleToStr(LotSize,2)+"\n"
      +"Open at "+DoubleToStr(OpenTradeStoch,0)+OpenWhen+", Close at "+DoubleToStr(CloseTradesStoch,0)+CloseWhen+", or Profit="+DoubleToStr(ProfitTarget,0)+", or Loss="+DoubleToStr(CashStopLoss,0)+"\n"
      + last_comment
      +"Stoch("+Pair1+")="+DoubleToStr(stoch1,2)+"\n"
      +"Stoch("+Pair2+")="+DoubleToStr(stoch2,2)+"\n"
      +"Diff="+DoubleToStr(MathAbs(stoch1-stoch2),2)+"\n"
      +"Corr="+DoubleToStr(corr,2)+"\n"
      +buyTradeStatus+"\n"
      +sellTradeStatus+"\n"
      +"Combined P/L = "+DoubleToStr(current_UPL,2)+", max DD="+DoubleToStr(max_DD,2)+"\n"
      +orderclosetime+"\n"
      //+"(start()="+DoubleToStr(GetTickCount()-t,0),"ms)"
    );
  }

  return (0);
}

//+------------------------------------------------------------------+
bool CheckTradeExist(string pair1, int &ticket1, string pair2, int &ticket2)
//+------------------------------------------------------------------+
{
  TradeExist = false;
  current_UPL = 0; // P/L of open orders

  // first see if the given tickets are still our open trades, this saves us looping over all open trades
  if (OrderSelect(ticket1,SELECT_BY_TICKET)) {
    if (OrderCloseTime()==0) { // this trade is still open, check second trade
      update_UPL();
      if (OrderSelect(ticket2,SELECT_BY_TICKET)) {
        if (OrderCloseTime()==0) { // second trade is still open
          // we found our 2 trades, no need to look into the history
          update_UPL();
          TradeExist = true;
          return (TradeExist);
        }
      }
    }
  }

  // tickets are invalid: look for our trades in the open trades history if any
  current_UPL = 0; // P/L of open orders
  ticket1=0; ticket2=0;
  for (int i = 0; i < OrdersTotal(); i++)
  {
    OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    if (OrderMagicNumber() == MagicNumber)
    {
      // Print(" order type "+OrderType() +" ticket "+ OrderTicket());
      if (OrderType()==OP_BUY && (OrderSymbol()==pair1 || OrderSymbol()==pair2) )
      {
        // set ticket number, will help tracking on next ticks
        if (OrderSymbol()==pair1) ticket1 = OrderTicket(); else ticket2 = OrderTicket();
        update_UPL();
        TradeExist = true;
      }
      if (OrderType()==OP_SELL && (OrderSymbol()==pair1 || OrderSymbol()==pair2) )
      {
        // set ticket number, will help tracking on next ticks
        if (OrderSymbol()==pair1) ticket1 = OrderTicket(); else ticket2 = OrderTicket();
        update_UPL();
        TradeExist = true;
      }
    }
  }
  return (TradeExist);
}

void update_UPL()
{
  double profit = OrderProfit()+OrderSwap()+OrderCommission();
  current_UPL += profit;
  string status = OrderSymbol()+" Opened at "+TimeToStr(OrderOpenTime(),TIME_DATE|TIME_SECONDS)+" P/L = "+DoubleToStr(profit,2);
  if (OrderType() == OP_BUY) buyTradeStatus = "Last BUY "+status;
  else sellTradeStatus = "Last SELL "+status;
  if (current_UPL<max_DD) max_DD = current_UPL; // track worst DD
}

//+------------------------------------------------------------------+
void CloseTrades()
//+------------------------------------------------------------------+
{
  double profit[2];
  int close_price_type[2]={MODE_BID,MODE_ASK};
  string order_type[2]={"BUY","SELL"};
    
  string comment = TimeToStr(TimeCurrent(),TIME_SECONDS)+": Closed ";

  int n=0,found=-1,closed=0;
  while (closed!=found) // make sure we close all relevant open trades
  {
    found=0;
    closed=0;
    for (int i = 0; i < OrdersTotal(); i++)
    {
      OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
      if (OrderMagicNumber() == MagicNumber && OrderType() <= OP_SELL)
      {
        found++;
        Print("In full closure mode.  Closing a ", OrderSymbol(), " ",order_type[OrderType()]," trade...");
        while(IsTradeContextBusy()) Sleep(100);
        RefreshRates();
        int digits = MarketInfo(OrderSymbol(), MODE_DIGITS);
        double closeprice = MarketInfo(OrderSymbol(), close_price_type[OrderType()]);
        if (!OrderClose(OrderTicket(), OrderLots(), closeprice, Slippage*pipMultTab[digits], White)) { // check failure reason
          int err = GetLastError();
          if (err!=ERR_NO_ERROR && err!=ERR_NO_RESULT) {
            Print("StochDiff CLOSE "+order_type[OrderType()]+" failed with error: ", ErrorDescription(err));
            continue;
          }
        }
        // success
        closed++;
        profit[n] = OrderProfit()+OrderSwap()+OrderCommission();
        comment=comment+OrderSymbol()+"="+DoubleToStr(profit[n],2); if(n==0) comment=comment+",";
        n++;
      }
    }
  }

  // update combined P/L for displaying the final trade result
  current_UPL = profit[0]+profit[1];

  comment=comment+"="+DoubleToStr(current_UPL,2)+",max DD="+DoubleToStr(max_DD,2);
  Print(comment);
  if (current_UPL>=0) drawVLine(comment,TimeCurrent(),Lime,STYLE_DOT);
  else drawVLine(comment,TimeCurrent(),Red,STYLE_DOT);
}

//+------------------------------------------------------------------+
void wait_until_order_in_history(int ticket)
//+------------------------------------------------------------------+
{
  if (ticket<0) return;
  // wait until order is entered into the history
  int t=GetTickCount();
  while(!OrderSelect(ticket,SELECT_BY_TICKET)){}
  Print("Order entered into history in ",DoubleToStr(GetTickCount()-t,0),"ms");
}

//+------------------------------------------------------------------+
int BUY(string buySymbol, double LotSize, string comment)
//+------------------------------------------------------------------+
{
  // adjust LotSize based on input Risk per-cent of free-margin
  if (RiskPerCent>0) LotSize = NormalizeDouble(AccountFreeMargin() * AccountLeverage() / 1000000 * RiskPerCent, 0) * MarketInfo(buySymbol, MODE_MINLOT);
  LotSize = MathMin(LotSize,MaxLots);
  for (int k=0;k<10;k++) {
    while(IsTradeContextBusy()) Sleep(100);
    RefreshRates();
    double buyPrice = MarketInfo(buySymbol, MODE_ASK);
    Print("Placing buy order at " + buyPrice + " for " + buySymbol);
    int digits = MarketInfo(buySymbol, MODE_DIGITS);
    int ticketlong = OrderSend(buySymbol, OP_BUY, LotSize, NormalizeDouble(buyPrice, digits), Slippage*pipMultTab[digits], 0, 0, comment, MagicNumber, 0, Blue);
    if (ticketlong >= 0) return(ticketlong);
    int check = GetLastError();
  }
  Print("StochDiff OP_BUY failed with error: ", ErrorDescription(check));
  return(ticketlong);
}

//+------------------------------------------------------------------+
int SELL(string sellSymbol, double LotSize, string comment)
//+------------------------------------------------------------------+
{
  // adjust LotSize based on input Risk per-cent of free-margin
  if (RiskPerCent>0) LotSize = NormalizeDouble(AccountFreeMargin() * AccountLeverage() / 1000000 * RiskPerCent, 0) * MarketInfo(sellSymbol, MODE_MINLOT);
  LotSize = MathMin(LotSize,MaxLots);
  for (int k=0;k<10;k++) {
    while(IsTradeContextBusy()) Sleep(100);
    RefreshRates();
    double sellPrice = MarketInfo(sellSymbol, MODE_BID);
    Print("Placing sell  order at " + sellPrice + " for  " + sellSymbol);
    comment = "StochDiff+"+Pair1+"-"+Pair2+" "+MagicNumber+" M"+Period();
    int digits = MarketInfo(sellSymbol, MODE_DIGITS);
    int ticketshort = OrderSend(sellSymbol, OP_SELL, LotSize, NormalizeDouble(sellPrice, digits), Slippage*pipMultTab[digits], 0, 0, comment, MagicNumber, 0, Red);
    if (ticketshort >= 0) return(ticketshort);
    int check = GetLastError();
  }
  Print("StochDiff OP_SELL failed with error: ", ErrorDescription(check));
  return(ticketshort);
}

//+------------------------------------------------------------------+
void WaitOrdersOpen(int ticket1, int ticket2)
//+------------------------------------------------------------------+
{
  string symbol1,symbol2;
  double price1,price2;
  int type1,type2;
  string sType[]={"Buy","Sell"};
  
  max_DD = 0;// reset max DD for this round

  wait_until_order_in_history(ticket1);
  wait_until_order_in_history(ticket2);

  OrderSelect(ticket1,SELECT_BY_TICKET); price1 = OrderOpenPrice(); symbol1 = OrderSymbol(); type1=OrderType();
  OrderSelect(ticket2,SELECT_BY_TICKET); price2 = OrderOpenPrice(); symbol2 = OrderSymbol(); type2=OrderType();

  last_comment = TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS)+"Placed "+sType[type1]+"("+Pair1+") + "+sType[type2]+"("+Pair2+")"+"\n";

  drawVLine("StochDiff:+"+symbol1+"@"+DoubleToStr(price1,MarketInfo(symbol1,MODE_DIGITS))
     +",-"+symbol2+"@"+DoubleToStr(price2,MarketInfo(symbol2,MODE_DIGITS))
     +" "+TimeToStr(TimeCurrent(),TIME_SECONDS), TimeCurrent(), Blue, STYLE_DOT);
}

//+------------------------------------------------------------------
void drawVLine(string objname, int time, color c, int style=STYLE_SOLID, int width=0)
//+------------------------------------------------------------------
{
  ObjectCreate(objname, OBJ_VLINE, 0, time, 0);
  ObjectSet(objname, OBJPROP_COLOR, c);
  ObjectSet(objname, OBJPROP_STYLE, style);
}

//+------------------------------------------------------------------+
int create_MagicNumber(string s)
//+------------------------------------------------------------------+
{
  return (hash_string(s));
}

//+------------------------------------------------------------------+
int hash_string(string s)
//+------------------------------------------------------------------+
{
  // this is the djb2 string hash algo
  int h = 5381, l = StringLen(s), i;
  for (i=0; i<l; i++) h = h * 33 + StringGetChar(s,i);
  return (h);
} /*hash_string()*/

//+------------------------------------------------------------------+
string __m;
void set_Pair2(string &Pair2)
//+------------------------------------------------------------------+
{
  __m = StringSubstr(Symbol(), 6, StringLen(Symbol()) - 6);
  Pair2 = StringSubstr(Pair2,0,6) + __m;
  if (MarketInfo(Pair2,MODE_ASK)==0) {// Pair2 does not exist, get it from the chart "Pair2" object
    get_Pair2_from_chart(Pair2);
  } else {
    drawFixedLbl("Pair2", Pair2, 1, 8, 20, 14, "Arial", MediumVioletRed, false);
  }
}

//+------------------------------------------------------------------+
bool get_Pair2_from_chart(string &Pair2)
//+------------------------------------------------------------------+
{

  /* Import Pair2 input from the "Pair2" Label object if it exists on the chart
   * The "Pair2" Label is also used by "OverLayChart" indicator,
   * and the "Stochastic different pairs 1.6b.mq4" EA,
   * so that they can all be loaded on the same chart and run with the same second-pair
   */
  if (ObjectFind("Pair2")>=0) {
    string p2 = ObjectDescription("Pair2");
    if (p2!=Pair2) {
      Pair2 = StringSubstr(p2,0,6) + __m;
      if (MarketInfo(Pair2,MODE_ASK)==0) Pair2=Symbol();// Pair2 does not exist, default to chart's symbol
      drawFixedLbl("Pair2", Pair2, 1, 8, 20, 14, "Arial", MediumVioletRed, false);
      Print("Found \"Pair2\" object : "+Pair2);
      return(true);
    }
  } else { // "Pair2" object does not exist: create it
    Pair2 = StringSubstr(Pair2,0,6) + __m;
    if (MarketInfo(Pair2,MODE_ASK)==0) Pair2=Symbol();// Pair2 does not exist, default to chart's symbol
    drawFixedLbl("Pair2", Pair2, 1, 8, 20, 14, "Arial", MediumVioletRed, false);
  }
  return(false);
}

//--------------------------------------------------------------------------------------
void drawFixedLbl(string objname, string s, int Corner, int DX, int DY, int FSize, string Font, color c, bool bg)
//--------------------------------------------------------------------------------------
{
   if (ObjectFind(objname) < 0) ObjectCreate(objname, OBJ_LABEL, 0, 0, 0);
   
   ObjectSet(objname, OBJPROP_CORNER, Corner);
   ObjectSet(objname, OBJPROP_XDISTANCE, DX);
   ObjectSet(objname, OBJPROP_YDISTANCE, DY);
   ObjectSet(objname,OBJPROP_BACK, bg);      
   ObjectSetText(objname, s, FSize, Font, c);
} // drawFixedLbl


double   cp(string Symbol1,string Symbol2,int TimeFrame, int periods)
{
	//Comment( Symbol1,"  ",Symbol2,"  ",TimeFrame,"  ",periods);
	datetime closeTime1	= iTime(Symbol1,TimeFrame,0),
				closeTime2	= iTime(Symbol2,TimeFrame,0),
				closeTime	= MathMin(closeTime1,closeTime2);
	int		shift1		= iBarShift(Symbol1,TimeFrame,closeTime),
				shift2		= iBarShift(Symbol2,TimeFrame,closeTime);
	double	Co,
	         close1[],
				close2[];

	ArrayCopySeries(close1,MODE_CLOSE,Symbol1,TimeFrame);
	ArrayCopySeries(close2,MODE_CLOSE,Symbol2,TimeFrame);

	int bars = MathMin(ArraySize(close1)-shift1,ArraySize(close2)-shift2);
	if ( periods > 0 )
	     bars = periods;
   Co = Correlation(close1,close2,shift1,shift2,bars);
   
   return(Co);
}

double Correlation(double x[], double y[], int x_shift = 0, int y_shift = 0, int count = -1)
{
	int n = MathMin(ArraySize(x)-x_shift,ArraySize(y)-y_shift);
	if(n>count && count>0)
		n=count;
	if(n<2)
		return(-2);

	double	sum_sq_x,
				sum_sq_y,
				sum_coproduct,
				mean_x = x[x_shift],
				mean_y = y[y_shift];

	for(int i = 0; i < n; i++)
	{
		double	sweep = i / (i+1.0),
					delta_x = x[i+x_shift] - mean_x,
					delta_y = y[i+y_shift] - mean_y;

		sum_sq_x += delta_x*delta_x * sweep;
		sum_sq_y += delta_y*delta_y * sweep;
    	sum_coproduct += delta_x*delta_y * sweep;
    	mean_x += delta_x / (i+1.0);
    	mean_y += delta_y / (i+1.0);
	}

	double	pop_sd_x = MathSqrt(sum_sq_x/n),
				pop_sd_y = MathSqrt(sum_sq_y/n),
				cov_x_y = sum_coproduct / n;

	if(pop_sd_x*pop_sd_y != 0.0)
		return(cov_x_y / (pop_sd_x*pop_sd_y));

	return(-3);
}

//end

