Author: Copyright 2015, MetaQuotes Software Corp.
arbitrage

This script is a specialized trading tool designed to visualize and identify potential "arbitrage" opportunities by comparing the price behavior of multiple currency pairs simultaneously. Instead of looking at just one chart, it performs a mathematical "cross-check" between different markets to see if they are moving in sync or drifting apart.

Core Logic and Functionality

1. Multi-Pair Correlation The tool is designed to work by comparing your current trading pair against two other related currency pairs. For example, if you are viewing a pair involving the Euro (EUR), the script automatically identifies and calculates data for corresponding pairs involving the Japanese Yen (JPY) or the US Dollar (USD). It looks for relationships between these different markets to find discrepancies.

2. Flexible Technical Analysis The script can be configured to use various well-known technical indicators to gather this data. Users can choose from:

  • Moving Average Convergence/Divergence (MACD)
  • Stochastic Oscillator
  • Relative Strength Index (RSI)
  • Commodity Channel Index (CCI)
  • Relative Vigor Index (RVI)
  • DeMarker Oscillator

Regardless of which indicator you choose, the script applies the same logic to all three related currency pairs at once.

3. The Arbitrage Calculation The "logic" of the arbitrage happens in how the script processes these indicators:

  • It gathers data from three different symbols based on your selected timeframe.
  • It normalizes this data (adjusting for certain currency types like Yen to ensure a fair comparison).
  • It creates two distinct values: a Base Index and a Quote Index.
  • These indices represent the "synthetic" price relationship between the markets. When these two lines move in opposite directions or show significant divergence, it suggests that one market might be undervalued or overvalued relative to the others—the fundamental concept of arbitrage.

4. Visual Representation The tool provides a clear visual output in a separate window:

  • Two Lines: It plots the Base Index and the Quote Index as two distinct lines (usually different colors).
  • Dynamic Coloring: If "Fill Buffers" is enabled, the space between these lines changes color. The color shifts automatically depending on which index is currently higher than the other.
  • On-Chart Labels: It places text labels on your chart to identify the currencies being compared so you can keep track of which markets are being analyzed.

5. Alerts and Notifications The script includes a built-in notification system. If the relationship between the Base and Quote indices "crosses over" (meaning one moves from being higher than the other to lower, or vice versa), the tool can trigger an automated alert. This notifies the trader in real-time that a potential discrepancy—and thus a potential trade opportunity—has appeared on the charts.

Summary for the User In simple terms, this script acts as a "comparison engine." It watches three different markets at once, calculates their relative strengths using your favorite technical indicators, and draws two lines on your screen. When those lines cross or move away from each other, it signals a price imbalance between global currencies that you can use to identify trading opportunities.

Indicators Used
MACD HistogramStochastic oscillatorRelative strength indexCommodity channel indexRelative Vigor indexDeMarker indicator
Miscellaneous
Implements a curve of type %1It issuies visual alerts to the screen
19 Views
0 Downloads
0 Favorites
arbitrage
//+------------------------------------------------------------------+
//|                                                    Arbitrage.mq4 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright  "Copyright 2015, MetaQuotes Software Corp."
#property link       "https://www.mql5.com"
#property description"Arbitrage by pipPod"
#property version    "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot Base
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrLimeGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot Quote
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrFireBrick
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2
ushort indicator_width3;
//--- indicator levels
#property indicator_levelcolor clrLightSlateGray
//---
double indicator_level1=   0;
double indicator_level2=  .2;
double indicator_level3= -.2;
double indicator_level4=  20;
double indicator_level5= -20;
double indicator_level6=  30;
double indicator_level7= -30;
double indicator_level8= 100;
double indicator_level9=-100;
//---
enum indicators
  {
   INDICATOR_MACD,         //Moving Average Convergence/Divergence
   INDICATOR_STOCHASTIC,   //Stochastic Oscillator
   INDICATOR_RSI,          //Relative Strength Index
   INDICATOR_CCI,          //Commodity Channel Index
   INDICATOR_RVI,          //Relative Vigor Index
   INDICATOR_DEMARK,       //DeMarker Oscillator
  };
//--- indicator to show
input indicators  Indicator=INDICATOR_MACD;
//--- index timeframe
input ENUM_TIMEFRAMES TimeFrame=0;
//--- indicator parameters
input string MACD;
input int FastEMA=12;                           //Fast EMA Period
input int SlowEMA=26;                           //Slow EMA Period
input int SignalSMA=9;                          //Signal SMA Period
input ENUM_APPLIED_PRICE MACDPrice=PRICE_CLOSE; //MACD Price
//---
input string Stochastic;
input int Kperiod=7;                            //K Period
input int Dperiod=3;                            //D Period
input int Slowing=3;    
input ENUM_STO_PRICE PriceField=STO_LOWHIGH;    //Price Field
//---
input string RSI;
input int  RSIPeriod=14;                        //RSI Period
input ENUM_APPLIED_PRICE RSIPrice=PRICE_CLOSE;  //RSI Price
//---
input string CCI;
input int  CCIPeriod=14;                        //CCI Period
input ENUM_APPLIED_PRICE CCIPrice=PRICE_CLOSE;  //CCI Price
//---
input string RVI;
input int  RVIPeriod=14;                        //RVI Period
//---
input string DeMarker;
input int  DeMarkerPeriod=14;                   //DeMarker Period
input string _;//---
input bool ColorBars=true;    //Color Bars
input bool FillBuffers=true;  //Fill Buffers
input string __;//---
input bool AlertsOn=false;
//---index buffers for drawing
double   BaseBuffer[];
double   QoteBuffer[];
//---currency variables for calculation
#define  USDJPY   "USDJPY"
//---
string   base,
         quote,
         symbol1,
         symbol2,
         symbol3;
//---
double   index1,
         index2,
         index3,
         baseIndex,
         qoteIndex;
//---
bool     baseUSD=false,
         quoteUSD=false,
         quoteJPY=false,
         quoteJPY1=false,
         quoteJPY2=false,
         quoteJPY3=false;
//---set index colors
color    color1 = indicator_color1,
         color2 = indicator_color2,
         colorFill;
//---
string   shortName;
long     chartID=ChartID();
short    window;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---currencies to show
   base = StringSubstr(_Symbol,0,3);  //Base currency name
   quote= StringSubstr(_Symbol,3,3);  //Quote currency name
   if(_Symbol=="EURUSD")  
     {
      color1 = clrRoyalBlue;
      color2 = clrLimeGreen;
      quoteUSD = true;
      symbol1 = _Symbol;
      symbol2 = "EURJPY";
      quoteJPY2 = true;
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol=="EURJPY")  
     {
      color1 = clrRoyalBlue;
      color2 = clrYellow;
      quoteJPY = true;
      symbol1 = _Symbol;
      quoteJPY1 = true;
      symbol2 = "EURUSD";
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol=="GBPUSD")  
     {
      color1 = clrSilver;
      color2 = clrLimeGreen;
      quoteUSD = true;
      symbol1 = _Symbol;
      symbol2 = "GBPJPY";
      quoteJPY2 = true;
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol=="GBPJPY")  
     {
      color1 = clrSilver;
      color2 = clrYellow;
      quoteJPY = true;
      symbol1 = _Symbol;
      quoteJPY1 = true;
      symbol2 = "GBPUSD";
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol=="AUDUSD")  
     {
      color1 = clrDarkOrange;
      color2 = clrLimeGreen;
      quoteUSD = true;
      symbol1 = _Symbol;
      symbol2 = "AUDJPY";
      quoteJPY2 = true;
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol=="USDCAD")
     {
      color1 = clrLimeGreen;
      color2 = clrWhiteSmoke;
      baseUSD = true;
      symbol1 = _Symbol;
      symbol2 = "CADJPY";
      quoteJPY2 = true;
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol=="USDCHF")
     {
      color1 = clrLimeGreen;
      color2 = clrFireBrick;
      baseUSD = true;
      symbol1 = _Symbol;
      symbol2 = "CHFJPY";
      quoteJPY2 = true;
      symbol3 = USDJPY;
      quoteJPY3 = true;
     }
   if(_Symbol==USDJPY)  
     {
      color1 = clrLimeGreen;
      color2 = clrYellow;
      quoteJPY = true;
      symbol1 = _Symbol;
      quoteJPY1 = true;
      symbol2 = "EURUSD";
      symbol3 = "EURJPY";
      quoteJPY3 = true;
     }
//---set timeframes
   string timeFrame = StringSubstr(EnumToString(TimeFrame),7);
   if(timeFrame=="CURRENT")
      timeFrame = "";
//---
   switch(Indicator)
     {
      case INDICATOR_MACD:
         shortName=StringFormat("Arbitrage MACD %s(%d,%d,%d)",
                                timeFrame,FastEMA,SlowEMA,SignalSMA);
         IndicatorSetInteger(INDICATOR_DIGITS,5);
         IndicatorSetInteger(INDICATOR_LEVELS,1);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,0,indicator_level1);
         break;
      case INDICATOR_STOCHASTIC:
         shortName=StringFormat("Arbitrage Stochastic %s(%d,%d,%d)",
                                timeFrame,Kperiod,Dperiod,Slowing);
         IndicatorSetInteger(INDICATOR_DIGITS,0);
         IndicatorSetInteger(INDICATOR_LEVELS,3);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,0,indicator_level1);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,1,indicator_level6);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,2,indicator_level7);
         break;
      case INDICATOR_RSI:
         shortName=StringFormat("Arbitrage RSI %s(%d)",
                                timeFrame,RSIPeriod);
         IndicatorSetInteger(INDICATOR_DIGITS,0);
         IndicatorSetInteger(INDICATOR_LEVELS,3);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,0,indicator_level1);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,1,indicator_level4);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,2,indicator_level5);
         break;
      case INDICATOR_CCI:
         shortName=StringFormat("Arbitrage CCI %s(%d)",
                                timeFrame,CCIPeriod);
         IndicatorSetInteger(INDICATOR_DIGITS,0);
         IndicatorSetInteger(INDICATOR_LEVELS,3);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,0,indicator_level1);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,1,indicator_level8);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,2,indicator_level9);
         break;
      case INDICATOR_RVI:
         shortName=StringFormat("Arbitrage RVI %s(%d)",
                                timeFrame,RVIPeriod);
         IndicatorSetInteger(INDICATOR_DIGITS,3);
         IndicatorSetInteger(INDICATOR_LEVELS,3);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,0,indicator_level1);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,1,indicator_level2);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,2,indicator_level3);
         break;
      case INDICATOR_DEMARK:
         shortName=StringFormat("Arbitrage DeMarker %s(%d)",
                                timeFrame,DeMarkerPeriod);
         IndicatorSetInteger(INDICATOR_DIGITS,3);
         IndicatorSetInteger(INDICATOR_LEVELS,3);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,0,indicator_level1);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,1,indicator_level2);
         IndicatorSetDouble(INDICATOR_LEVELVALUE,2,indicator_level3);
     }  
   IndicatorSetString(INDICATOR_SHORTNAME,shortName);
   window = ChartWindowFind(chartID,shortName);
   int xStart = 4;       //label coordinates
   int xIncrement = 25;
   int yStart = 16;
   ObjectCreate(base,xStart,yStart,color1);
   xStart += xIncrement;
   ObjectCreate(quote,xStart,yStart,color2);
//---index buffers
   SetIndexBuffer(0,BaseBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,QoteBuffer,INDICATOR_DATA);
   SetIndexStyle(0,EMPTY,EMPTY,EMPTY,color1);
   SetIndexStyle(1,EMPTY,EMPTY,EMPTY,color2);
   SetIndexLabel(0,base);
   SetIndexLabel(1,quote);
//---color bars
   if(ColorBars)
     {
      if(ChartGetInteger(0,CHART_COLOR_CANDLE_BULL)!=color1)
        {
         ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,color1);
         ChartSetInteger(0,CHART_COLOR_CHART_UP,color1);
        }
      if(ChartGetInteger(0,CHART_COLOR_CANDLE_BEAR)!=color2)
        {
         ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,color2);
         ChartSetInteger(0,CHART_COLOR_CHART_DOWN,color2);
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
#define INVERT -1
#define BUY "BUY"
#define SELL "SELL"
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   int limit = rates_total-prev_calculated;
   if(prev_calculated>rates_total || prev_calculated<=0)  
     {
      int barsWindow = (int)ChartGetInteger(chartID,CHART_VISIBLE_BARS)+100;
      if(iBars(symbol1,TimeFrame)<=barsWindow ||
         iBars(symbol2,TimeFrame)<=barsWindow ||
         iBars(symbol3,TimeFrame)<=barsWindow)
         return(0);
      SetIndexDrawBegin(0,rates_total-barsWindow);
      SetIndexDrawBegin(1,rates_total-barsWindow);
      limit = barsWindow;
//---
      if(FillBuffers)
        {
         ushort scale = ChartGetInteger(chartID,CHART_SCALE);
         switch(scale)
           {
            case 5:  indicator_width3 = 24;  break;
            case 4:  indicator_width3 = 12;  break;
            case 3:  indicator_width3 = 6;   break;
            case 2:  indicator_width3 = 3;   break;
            default: indicator_width3 = 1;  
           }
        }
     }  
   if(prev_calculated>0 && NewBar(time[0]))
      limit++;
//---
   for(int i=limit;i>=0 && !IsStopped();i--)
     {
      int shift = iBarShift(NULL,TimeFrame,time[i]);
      switch(Indicator)
        {
         case INDICATOR_MACD:
            index1 = iMACD(symbol1,TimeFrame,FastEMA,SlowEMA,SignalSMA,MACDPrice,MODE_MAIN,shift);
            index2 = iMACD(symbol2,TimeFrame,FastEMA,SlowEMA,SignalSMA,MACDPrice,MODE_MAIN,shift);
            index3 = iMACD(symbol3,TimeFrame,FastEMA,SlowEMA,SignalSMA,MACDPrice,MODE_MAIN,shift);
            if(quoteJPY1)
               index1 /= 100;
            if(quoteJPY2)
               index2 /= 100;
            if(quoteJPY3)
               index3 /= 100;
            break;
         case INDICATOR_STOCHASTIC:
            index1 = iStochastic(symbol1,TimeFrame,Kperiod,Dperiod,Slowing,MODE_SMA,PriceField,MODE_MAIN,shift)-50;
            index2 = iStochastic(symbol2,TimeFrame,Kperiod,Dperiod,Slowing,MODE_SMA,PriceField,MODE_MAIN,shift)-50;
            index3 = iStochastic(symbol3,TimeFrame,Kperiod,Dperiod,Slowing,MODE_SMA,PriceField,MODE_MAIN,shift)-50;
            break;
         case INDICATOR_RSI:
            index1 = iRSI(symbol1,TimeFrame,RSIPeriod,RSIPrice,shift)-50;
            index2 = iRSI(symbol2,TimeFrame,RSIPeriod,RSIPrice,shift)-50;
            index3 = iRSI(symbol3,TimeFrame,RSIPeriod,RSIPrice,shift)-50;
            break;
         case INDICATOR_CCI:
            index1 = iCCI(symbol1,TimeFrame,CCIPeriod,CCIPrice,shift);
            index2 = iCCI(symbol2,TimeFrame,CCIPeriod,CCIPrice,shift);
            index3 = iCCI(symbol3,TimeFrame,CCIPeriod,CCIPrice,shift);
            break;
         case INDICATOR_RVI:
            index1 = iRVI(symbol1,TimeFrame,RVIPeriod,MODE_MAIN,shift);
            index2 = iRVI(symbol2,TimeFrame,RVIPeriod,MODE_MAIN,shift);
            index3 = iRVI(symbol3,TimeFrame,RVIPeriod,MODE_MAIN,shift);
            break;
         case INDICATOR_DEMARK:
            index1 = iDeMarker(symbol1,TimeFrame,DeMarkerPeriod,shift)-.5;
            index2 = iDeMarker(symbol2,TimeFrame,DeMarkerPeriod,shift)-.5;
            index3 = iDeMarker(symbol3,TimeFrame,DeMarkerPeriod,shift)-.5;
        }
//---
      if(quoteUSD)
        {
         baseIndex = (index1+index2)/2;
         index1 *= INVERT;
         qoteIndex = (index1+index3)/2;          
        }
      if(quoteJPY)
        {
         if(_Symbol==USDJPY)  
            index2 *= INVERT;
         baseIndex = (index1+index2)/2;
         index1 *= INVERT;
         index3 *= INVERT;
         qoteIndex = (index1+index3)/2;          
        }
      if(baseUSD)
        {
         baseIndex = (index1+index3)/2;
         index1 *= INVERT;
         qoteIndex = (index1+index2)/2;          
        }
//---
      BaseBuffer[i] = baseIndex;
      QoteBuffer[i] = qoteIndex;
//---
      if(FillBuffers)
        {
         colorFill = (baseIndex>qoteIndex)?color1:color2;
         ObjectCreate((string)(uint)time[i],time[i],BaseBuffer[i],QoteBuffer[i]);
        }  
//---
      if(AlertsOn && prev_calculated>0)
        {
         static datetime timePrev;
         if(BaseBuffer[i]>QoteBuffer[i] &&
            BaseBuffer[i+1]<QoteBuffer[i+1] &&
            timePrev!=time[0])
           {
            Alert(BUY+_Symbol);
            timePrev = time[0];
           }
         if(BaseBuffer[i]<QoteBuffer[i] &&
            BaseBuffer[i+1]>QoteBuffer[i+1] &&
            timePrev!=time[0])
           {
            Alert(SELL+_Symbol);
            timePrev = time[0];
           }
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // delete labels
   ObjectDelete(chartID,base);
   ObjectDelete(chartID,quote);
   ObjectsDeleteAll(chartID,window,OBJ_TREND);
   return;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void ObjectCreate(string currency,
                  int x,
                  int y,
                  color clr)
  {
   if(!ObjectCreate(chartID,currency,OBJ_LABEL,window,0,0))
     {
      Print(__FUNCTION__,": error ",GetLastError());
      return;
     }
   ObjectSetString(chartID,currency,OBJPROP_TEXT,currency);
   ObjectSetString(chartID,currency,OBJPROP_FONT,"Arial Bold");
   ObjectSetInteger(chartID,currency,OBJPROP_FONTSIZE,8);
   ObjectSetInteger(chartID,currency,OBJPROP_COLOR,clr);
   ObjectSetInteger(chartID,currency,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(chartID,currency,OBJPROP_YDISTANCE,y);
   return;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#define OBJ_NONE -1
void ObjectCreate(string object_name,
                  const datetime &time,
                  const double &high,
                  const double &low)
  {
   if(ObjectFind(chartID,object_name)!=OBJ_NONE)
      ObjectDelete(chartID,object_name);
   if(!ObjectCreate(chartID,object_name,OBJ_TREND,window,time,high,time,low))
     {
      Print(__FUNCTION__,": error ",GetLastError());
      return;
     }
   ObjectSetInteger(chartID,object_name,OBJPROP_WIDTH,indicator_width3);
   ObjectSetInteger(chartID,object_name,OBJPROP_COLOR,colorFill);
   ObjectSetInteger(chartID,object_name,OBJPROP_RAY,false);
   ObjectSetInteger(chartID,object_name,OBJPROP_HIDDEN,true);
   ObjectSetInteger(chartID,object_name,OBJPROP_SELECTABLE,false);
   return;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool NewBar(const datetime &time)
  {
   static datetime currTime1[1],
                   currTime2[1],
                   currTime3[1],
                   prevTime;
   if(CopyTime(symbol1,TimeFrame,time,1,currTime1)!=1 ||
      CopyTime(symbol2,TimeFrame,time,1,currTime2)!=1 ||
      CopyTime(symbol3,TimeFrame,time,1,currTime3)!=1)
      return(false);
   if(prevTime!=currTime1[0] &&
      prevTime!=currTime2[0] &&
      prevTime!=currTime3[0])
     {
      prevTime = currTime1[0];
      return(true);
     }
   return(false);
  }
//-------------------------------------------------------------------+

Comments