#property show_inputs
#include <stdlib.mqh> // string ErrorDescription(int error_code);
#define PAUSE 100   // Ïàóçà â ìèëëèñåêóíäàõ ìåæäó ðàñ÷åòàìè
#define ALPHA 0.001 // Äëÿ ñðàâíåíèÿ îáúåìîâ
#define STR_SHABLON "<!--STR-->"
#define MAX_CURRENCY 20  // Ìàêñèìàëüíîå êîëè÷åñòâî âàëþò (íå ïàð)
#define MAX_REALSYMBOLS 380  // Ìàêñèìàëüíîå êîëè÷åñâî ó÷èòûâàåìûõ ðåàëüíûõ ñèâîëîâ èç Market Watch (== (MAX_CURRENCY * (MAX_CURRENCY - 1)))
#define MAX_ALLSYMBOLS 380  // Ìàêñèìàëüíîå êîëè÷åñòâî âîçìîæíûõ ñèìâîëîâ (== (MAX_CURRENCY * (MAX_CURRENCY - 1)))
#define MAX_VARIANTSYMBOLS 74 // Ìàêñèìàëüíîå êîëè÷åñòâî âàðèàíòîâ ïîëó÷åíèÿ ñèìâîëà (== (4 * MAX_CURRENCY - 6))
#define MAX_VARIANTPAIRS 5402 // Ìàêñèìàëüíîå êîëè÷åñòâî ñî÷åòàíèé ïàð ñèìâîëîâ (== (MAX_VARIANTSYMBOLS * (MAX_VARIANTSYMBOLS - 1))
extern string Currencies = "AUD, EUR, USD, CHF, JPY, NZD, GBP, CAD, SGD, NOK, SEK, DKK, ZAR, MXN, HKD, HUF, CZK, PLN, RUR, TRY";
extern double MinPips = 0.5; // Ìèíèìàëüíàÿ ó÷èòûâàåìàÿ ðàçíèöà àðáèòðàæà â "ñòàðûõ" ïóíêòàõ
extern int SlipPage = 0; // Äîïóñòèìûé SlipPage äëÿ Market-çàïðîñîâ (íå âñåãäà ðàáîòàåò)
extern bool Lock = FALSE; // Ðàçðåøàòü ëîêè èëè íåò
extern double Lots = 1; // Îáúåì ïîçèöèè ïî ñãåíåðèðîâàííîìó ñèìâîëó
extern double MaxLot = 20; //
extern double MinLot = 0.1; // Íåîáõîäèìî çàäàâàòü èç-çà íåêîððåêòíîãî âîçâðàùàåìîãî çíå÷åíèÿ MarketInfo
extern bool Monitoring = TRUE; // Ïðîèçâîäèòü çàïèñü âñåõ ñëó÷àþùèõñÿ àðáèòðàæåé â ôàéë èëè íåò (çàïèñü çàíèìàåò âðåìÿ, êîòîðîå êðèòè÷íî äëÿ àðáèòðàæà)
extern int TimeToWrite = 5; // ×åðåç êàêîå âðåìÿ (â ìèíóòàõ) áóäóò çàïèñûâàòüñÿ â ôàéë ñòàòèñòè÷åñêèå äàííûå îá àðáèòðàæå
int DigitsLot; // Êîëè÷åñòâî öèôð ïîñëå çàïÿòîé â äåñÿòè÷íîé çàïèñè ëîòîâ
string SymbolPrefix = "";  // Ïðåôèêñ, êîòîðûé äîáàâëåí â Market Watch ïîñëå ñòàíäàðòíîãî íàïèñàíèÿ ïàðû
string Shablon = "<!--STR-->, <!--STR-->";  // Øàáëîí äëÿ âûäèðàíèÿ ÷åãî óãîäíî èç ñòðîêè. Íóæåí äëÿ Currencies
int AmountCurrency;  // Îáùåå êîëè÷åñòâî ó÷èòûâàåìûõ âàëþò
string Currency[MAX_CURRENCY]; // Ó÷èòûâàåìûå âàëþòû
int AmountRealSymbols;  // Îáùåå êîëè÷åñòâî ó÷èòûâàåìûõ ðåàëüíûõ ñèìâîëîâ èç Market Watch
string RealSymbols[MAX_REALSYMBOLS];  // Õðàíèò ñòàíäàðòíûå (áåç ïðåôèêñîâ) íàçâàíèÿ ðåàëüíûõ ñèìâîëîâ èç Market Watch
string REALSymbols[MAX_REALSYMBOLS];  // Õðàíèò ÐÅÀËÜÍÛÅ (ñ ó÷åòîì ïðåôèêñîâ) íàçâàíèÿ ðåàëüíûõ ñèìâîëîâ èç Market Watch
int AmountAllSymbols; // Îáùåå êîëè÷åñòâî ñãåíåðèðîâàííûõ ïàð (ðåàëüíûå + èñêóññòâåííûå)
int AllSymbols[MAX_ALLSYMBOLS][MAX_VARIANTSYMBOLS]; // [k][m] - õðàíèò íîìåðà ðåàëüíûõ ñèìâîëîâ äëÿ ñîçäàíèÿ k-ãî ñèìâîëà
int Math[MAX_ALLSYMBOLS][MAX_VARIANTSYMBOLS]; // [k][m] - õðàíèò ìàò. äåéñòâèå ñîçäàíèÿ k-ãî ñèìâîëà èç m-õ ðåàëüíûõ ñèìâîëîâ
int Count[MAX_ALLSYMBOLS];  // [k] - êîëè÷åñòâî âàðèàíòîâ ïîëó÷åíèÿ k-ãî ñèìâîëà.
double PointD[MAX_ALLSYMBOLS]; // [k] - õðàíèò ðàçìåð (ñòàðîãî) ïóíêòà  k-ãî ñèìâîëà
double MinPipsD[MAX_ALLSYMBOLS]; // [k] - õðàíèò MinPips ñòàðûõ ïóíêòîâ  k-ãî ñèìâîëà
double Bids[MAX_ALLSYMBOLS][MAX_VARIANTSYMBOLS];  // [k][m] - õðàíèò Bid ê-ãî ñèìâîëà ïîëó÷åííîãî èç m-õ ðåàëüíûõ ñèìâîëîâ
double Asks[MAX_ALLSYMBOLS][MAX_VARIANTSYMBOLS];  // [k][m] - õðàíèò Ask ê-ãî ñèìâîëà ïîëó÷åííîãî èç m-õ ðåàëüíûõ ñèìâîëîâ
double BidsReal[MAX_REALSYMBOLS]; // Bid-öåíû ðåàëüíûõ ñèìâîëîâ
double AsksReal[MAX_REALSYMBOLS]; // Ask-öåíû ðåàëüíûõ ñèìâîëîâ
double Position[MAX_REALSYMBOLS];  // Õðàíèò íàïðàâëåíèÿ è îáúåì ðåàëüíûõ ñèìâîëîâ äëÿ îòêðûòèÿ
double XPosition[MAX_ALLSYMBOLS][MAX_VARIANTPAIRS];  // [k][] - Õðàíèò àðáèòðàæíîå íàïðàâëåíèå (+/-) è îáúåì ñðàçó äâóõ âàðèàíòîâ ïîëó÷åíèÿ k-ãî ñèìâîëà 
bool XTrade[MAX_ALLSYMBOLS][MAX_VARIANTPAIRS];  // [k][] - Õðàíèò ðàçðåøåíèå/çàïðåò íà îñóùåñòâëåíèå àðáèòðàæà ìåæäó äâóìÿ âàðèàíòàìè ïîëó÷åíèÿ k-ãî ñèìâîëà
// Äëÿ ñáîðà ñòàòèñòèêè
int CountArbitrage[MAX_ALLSYMBOLS][MAX_VARIANTPAIRS];  // [k][] - Õðàíèò êîëè÷åñòâî àðáèòðàæíåé ñðàçó äâóõ âàðèàíòîâ ïîëó÷åíèÿ k-ãî ñèìâîëà 
int MaxCountArbitrage;
int PrevTime, CurrentTime; // Ïðåäûäóùåå (äëÿ çàïèñè ñòàòèñòèêè) è êðàéíåå âðåìÿ ñåðâåðà
string StrOut = ""; // Âûâîä â ëîã  
//------------------------------------------------------------------------------------------------------------------------------
/************************************************* BEGIN BLOCK_1 *************************************************/
// ÁËÎÊ ÔÓÍÊÖÈÉ, ÈÑÏÎËÜÇÓÞÙÈÕÑß Â ÎÑÍÎÂÍÎÌ ÄËß ÈÍÈÖÈÀËÈÇÀÖÈÈ:
// string StrDelSpaces( string Str );
// int StrToStringS( string Str, string Razdelitel, string &Output[] );
// bool RealSymbol( string Str );
// void GetRealSymbols();
// int GetNumRealSymbol( string Str );
// void GetAllSymbols();
// void GetRealBidAsk();
// string SymbolToStr( int i, int j );
// void GetDataLot( string Symb );
// void GetSymbolPrefix( string Symb );
// void GetXTrade( string FileName );
// void GetPipsD();
// void InitArbitrage();
// void PrintXTrade();
// void PrintBeginInfo();
/************************************************* BEGIN BLOCK_1 *************************************************/
string StrDelSpaces( string Str )
{
  int Pos, Length;
  Str = StringTrimLeft(Str);
  Str = StringTrimRight(Str);
  Length = StringLen(Str) - 1;
  Pos = 1;
  while (Pos < Length)
    if (StringGetChar(Str, Pos) == ' ')
    {  
      Str = StringSubstr(Str, 0, Pos) + StringSubstr(Str, Pos + 1, 0);
      Length--;
    }
    else 
      Pos++;
  return(Str);
}
int StrToStringS( string Str, string Razdelitel, string &Output[] )
{
  int Pos, LengthSh;
  int Count = 0;
  Str = StrDelSpaces(Str);
  Razdelitel = StrDelSpaces(Razdelitel);
  LengthSh = StringLen(Razdelitel);
  while (TRUE)
  {
    Pos = StringFind(Str, Razdelitel);
    Output[Count] = StringSubstr(Str, 0, Pos);
    Count++;
 
    if (Pos == -1)
      break;
 
    Pos += LengthSh;
    Str = StringSubstr(Str, Pos);
  }
  return(Count);
}
// Ïðîâåðêà íà ðåàëüíîñòü (åñòü ëè â Market Watch) ñèìâîëà
// Âîçâðàùàåò: TRUE - ðåàëüíûé, FALSE - èñêóññòâåííûé
bool RealSymbol( string Str )
{
  return(MarketInfo(Str + SymbolPrefix, MODE_BID) != 0);
}
// Ïîëó÷àåò â RealSymbols[] âñå ó÷èòûâàåìûå ðåàëüíûå ñèìâîëû èç Market Watch
void GetRealSymbols()
{
  int i, j;
  string Str;
  AmountRealSymbols = 0;
  for (i = 0; i < AmountCurrency; i++)
    for (j = 0; j < AmountCurrency; j++)
      if (i != j)  // ïàðû äîëæíû áûòü èç ðàçíûõ âàëþò
      {
        Str = Currency[i] + Currency[j];  // îáðàçóåì ðàçëè÷íûå êîìáèíàöèè ïàð
     
        if (RealSymbol(Str))
        {
          RealSymbols[AmountRealSymbols] = Str;
          REALSymbols[AmountRealSymbols] = Str + SymbolPrefix;  // â äàëüíåéøåì ïîíàäîáèòñÿ äëÿ óñêîðåíèÿ ðàñ÷åòîâ
          AmountRealSymbols++;
        }
      }
  return;
}
// Âîçâðàùàåò íîìåð ïîçèöèè, ãäå íàõîäèòñÿ ñèìâîë Str â RealSymbols[]. Ïðè íåóäà÷å âîçâðàùàåò -1.
int GetNumRealSymbol( string Str )
{
  int i;
  for (i = 0; i < AmountRealSymbols; i++)
    if (RealSymbols[i] == Str)
      return(i);
  return(-1);
}
// Ïîëó÷àåò â AllSymbols[][] âàðèàíòû ñãåíåðèðîâàííûõ ïàð
void GetAllSymbols()
{
  int i, j, k, m;
  string Str, Str1[4], Str2[4];
  AmountAllSymbols = 0; // èíèöèàëèçèðîâàëè êîëè÷åñòâî ó÷èòûâàåìûõ ñãåíåðèðîâàííûõ ïàð
  
  for (i = 0; i < MAX_ALLSYMBOLS; i++)
    Count[i] = 0;  // èíèöèàëèçèðîâàëè êîëè÷åñòâî âàðèàíòîâ ïîëó÷åíèÿ ó÷èòûâàåìûõ ñãåíåðèðîâàííûõ ïàð
  for (i = 0; i < AmountCurrency; i++) // Ñòðîèì ïàðó èìåííî ij (íå íàîáîðîò!!!)
    for (j = 0; j < AmountCurrency; j++)
      if (i != j)  // Ïàðà äîëæíà ñîñòîÿòü èç ðàçíûõ âàëþò
      {
        Str = Currency[i] + Currency[j];  // Ñîçäàëè ïàðó ij
     
        if (RealSymbol(Str)) // Åñëè ïàðà ðåàëüíàÿ, òî (ïåðâûé) âàðèàíò åå ïîëó÷åíèÿ - îíà ñàìà.
        {
          AllSymbols[AmountAllSymbols][Count[AmountAllSymbols]] = GetNumRealSymbol(Str);
          Math[AmountAllSymbols][Count[AmountAllSymbols]] = -1; // -2 - "1 / S"; -1 - "S"; 0 - "S1 / S2"; 1 - "S1 * S2"; 2 - "1 / (S1 * S2)"; 3 - "S2 / S1"
          Count[AmountAllSymbols]++;
        }
        Str = Currency[j] + Currency[i];  // Ñîçäàëè îáðàòíóþ ïàðó - ji
        if (RealSymbol(Str)) // Åñëè îáðàòíàÿ ïàðà ðåàëüíàÿ, òî âàðèàíò ïîëó÷åíèÿ ïðÿìîé ïàðû - 1 / îáðàòíóþ.
        {
            AllSymbols[AmountAllSymbols][Count[AmountAllSymbols]] = GetNumRealSymbol(Str);
            Math[AmountAllSymbols][Count[AmountAllSymbols]] = -2; // -2 - "1 / S"; -1 - "S"; 0 - "S1 / S2"; 1 - "S1 * S2"; 2 - "1 / (S1 * S2)"; 3 - "S2 / S1"
            Count[AmountAllSymbols]++;
        }
        for (k = 0; k < AmountCurrency; k++)
          if ((k != i) && (k != j))
          {
            // Îñòàëîñü ïðîâåðèòü 4-å âàðèàíòà ïîëó÷åíèÿ ij-ïàðû
            
            Str1[0] = Currency[i] + Currency[k];
            Str1[1] = Currency[i] + Currency[k];
            Str1[2] = Currency[k] + Currency[i];
            Str1[3] = Currency[k] + Currency[i];
         
            Str2[0] = Currency[j] + Currency[k];
            Str2[1] = Currency[k] + Currency[j];
            Str2[2] = Currency[j] + Currency[k];
            Str2[3] = Currency[k] + Currency[j];
         
            for (m = 0; m < 4; m++)
              if (RealSymbol(Str1[m]) && RealSymbol(Str2[m]))
              {
                // Òàêîé ñïîñîá õðàíåíèÿ ñðàçó äâóõ ïàð (Str1 è Str2) îäíèì ÷èñëîì
                AllSymbols[AmountAllSymbols][Count[AmountAllSymbols]] = GetNumRealSymbol(Str1[m]) * AmountRealSymbols + GetNumRealSymbol(Str2[m]);
                Math[AmountAllSymbols][Count[AmountAllSymbols]] = m; // 0 - "S1 / S2"; 1 - "S1 * S2"; 2 - "1 / (S1 * S2)"; 3 - "S2 / S1"
                Count[AmountAllSymbols]++;
              }
          }
          
        if (Count[AmountAllSymbols] >= 2) // Åñëè âàðèàíòîâ ïîëó÷åíèÿ ij-ïàðû íå ìåíüøå äâóõ 
                                          // (îïòèìèçàöèÿ äëÿ âàðèàíòà àðáèòðàæíîãî èñïîëüçîâàíèÿ, èíà÷å - ïðîâåðêà íà >= 1),
                                          // ó÷èòûâàåì ýòó ïàðó äàëüøå
          AmountAllSymbols++; // Óâåëè÷èëè êîëè÷åñòâî ó÷èòûâàåìûõ ñãåíåðèðîâàííûõ ïàð
        else  // Èíà÷å - íå ó÷èòûâàåì
          Count[AmountAllSymbols] = 0;
      }
  return;
}
void GetRealBidAsk()
{
  for (int i = 0; i < AmountRealSymbols; i++)
  {
    BidsReal[i] = MarketInfo(REALSymbols[i], MODE_BID);
    AsksReal[i] = MarketInfo(REALSymbols[i], MODE_ASK);
  }
  
  return;
}
// Âîçâðàùàåò íàçâàíèå j-ãî âàðèàíòà ïîëó÷åíèÿ i-ãî ñãåíåðèðîâàííîãî ñèìâîëà
string SymbolToStr( int i, int j )
{
  string Str = "", S1, S2;
  if (Math[i][j] == -1) // 
    Str = RealSymbols[AllSymbols[i][j]];
  else if (Math[i][j] == -2)
    Str = "1 / " + RealSymbols[AllSymbols[i][j]];
  else
  {
    S1 = RealSymbols[AllSymbols[i][j] / AmountRealSymbols];  // ïîëó÷èëè íàçâàíèå ïåðâîé ïàðû
    S2 = RealSymbols[AllSymbols[i][j] % AmountRealSymbols];  // ïîëó÷èëè íàçâàíèå âòîðîé ïàðû
     
    switch (Math[i][j])
    {
      case 0: // 0 - "S1 / S2"
        Str = S1 + " / " + S2;
        break;
      case 1: // 1 - "S1 * S2"
        Str = S1 + " * " + S2;
        break;
      case 2: // 2 - "1 / (S1 * S2)";
        Str = "1 / (" + S1 + " * " + S2 + ")";
        break;
      case 3: // 3 - "S2 / S1"
        Str = S2 + " / " + S1;
        break;
     }
   }
 
  return(Str);
}  
void GetDataLot( string Symb )
{
  int Tmp = 1 / MarketInfo(Symb, MODE_LOTSTEP) + 0.1;
  
  Tmp /= 10;
  DigitsLot = 0;
  
  while (Tmp > 0)
  {
    DigitsLot++;
    Tmp /= 10;
  }
  
  if (MaxLot > MarketInfo(Symb, MODE_MAXLOT))
    MaxLot = MarketInfo(Symb, MODE_MAXLOT);
  if (MinLot < MarketInfo(Symb, MODE_MINLOT))
    MinLot = MarketInfo(Symb, MODE_MINLOT);
  
  if (MinLot > MarketInfo(Symb, MODE_LOTSTEP) + ALPHA)
    Alert(WindowExpertName(), " - WARNING: MinLot (", MinLot, ") > LotStep (", MarketInfo(Symb, MODE_MINLOT), ")");
    
  return;
}
void GetSymbolPrefix( string Symb )
{
  SymbolPrefix = StringSubstr(Symb, 6);
  
  return;
}
// Îïðåäåëåíèå àðáèòðàæíûõ ñèìâîëîâ äëÿ òîðãîâëè
void GetXTrade( string FileName )
{
  int i, j, k, m, Pos;
  int handle;
  int AmountStrings = 0;
  string Str1, Str2, Str3, Str[MAX_VARIANTPAIRS];
  
  handle = FileOpen(FileName, FILE_READ);
  
  while (!FileIsEnding(handle))
  {
    Str[AmountStrings] = FileReadString(handle);
    AmountStrings++;
  }
  
  FileClose(handle);
  
  for (i = 0; i < AmountAllSymbols; i++)
  {
    Pos = 0;
    
    for (j = 0; j < Count[i] - 1; j++)
    {
      Str1 = SymbolToStr(i, j);
      Pos += j + 1;
  
      for (k = j + 1; k < Count[i]; k++)
      {
        Str2 = Str1 + " && " + SymbolToStr(i, k);
        Str3 = SymbolToStr(i, k) + " && " + Str1;
        
        for (m = 0; m < AmountStrings; m++)
          if ((Str[m] == Str2) || (Str[m] == Str3))
            break;
        
        if (m == AmountStrings)
          XTrade[i][Pos] = FALSE;   
        else
          XTrade[i][Pos] = TRUE;
          
        XPosition[i][Pos] = 0;
        CountArbitrage[i][Pos] = 0;
        
        Pos++;
      }
    }
  }
  
  return;
}
void GetPipsD()
{
  GetRealBidAsk();
  
  for (int i = 0; i < AmountAllSymbols; i++)
  {
    GetBidAsk(i, 0);           
     
    // Îïðåäåëÿåì ðàçìåð (ñòàðîãî) ïóíêòà
    if (Bids[i][0] > 10)
    {
      PointD[i] = 0.01;
      MinPipsD[i] = MinPips * 0.01;
    }
    else
    {
      PointD[i] = 0.0001;
      MinPipsD[i] = MinPips * 0.0001;
    }
  }
  
  return;
}
void InitArbitrage()
{
  GetDataLot(Symbol());
  GetSymbolPrefix(Symbol());
  
  AmountCurrency = StrToStringS(Currencies, ",", Currency);
  GetRealSymbols();
  GetAllSymbols();
  GetPipsD();
  
  MaxCountArbitrage = 0;
  for (int i = 0; i < AmountRealSymbols; i++)
    Position[i] = 0;
    
  GetXTrade("Trade-Arbitrage.txt");
  return;
}
void PrintXTrade()
{
  int i, j, k, Pos;
  
  for (i = 0; i < AmountAllSymbols; i++)
  {
    Pos = 0;
    
    for (j = 0; j < Count[i] - 1; j++)
    {
      Pos += j + 1;
  
      for (k = j + 1; k < Count[i]; k++)
      {
        if  (XTrade[i][Pos])
          Print(SymbolToStr(i, j) + " && " + SymbolToStr(i, k));
        Pos++;
      }
    }
  }
  
  return;
}
void PrintBeginInfo()
{
  int i, Max;
  
  Print("MAX_CURRENCY = " + AmountCurrency);
  Print("MAX_REALSYMBOLS = " + AmountRealSymbols);
  Print("MAX_ALLSYMBOLS = " + AmountAllSymbols);
   
  Max = 0;
  
  for (i = 0; i < AmountAllSymbols; i++)
    if (Count[i] > Max)
      Max = Count[i];
  Print("MAX_VARIANTSYMBOLS = " + Max);
  Max = 0;
  
  for (i = 0; i < AmountAllSymbols; i++)
    if (Count[i] * (Count[i] - 1) > Max)
      Max = Count[i] * (Count[i] - 1);
      
  Print("MAX_VARIANTPAIRS = " + Max);
  
  Max = 0;
  
  for (i = 0; i < AmountAllSymbols; i++)
    Max += Count[i];
      
  Print("SumAllCounts = " + Max);
  Max = 0;
  
  for (i = 0; i < AmountAllSymbols; i++)
    Max += Count[i] * (Count[i] - 1);
  Print("SumAllVariants = " + Max);
  PrintXTrade();
  
  return;
}
/************************************************* END BLOCK_1 *************************************************/
// ÁËÎÊ ÔÓÍÊÖÈÉ, ÈÑÏÎËÜÇÓÞÙÈÕÑß Â ÎÑÍÎÂÍÎÌ ÄËß ÈÍÈÖÈÀËÈÇÀÖÈÈ:
// string StrDelSpaces( string Str );
// int StrToStringS( string Str, string Razdelitel, string &Output[] );
// bool RealSymbol( string Str );
// void GetRealSymbols();
// int GetNumRealSymbol( string Str );
// void GetAllSymbols();
// void GetRealBidAsk();
// string SymbolToStr( int i, int j );
// void GetDataLot( string Symb );
// void GetSymbolPrefix( string Symb );
// void GetXTrade( string FileName );
// void GetPipsD();
// void InitArbitrage();
// void PrintXTrade();
// void PrintBeginInfo();
/************************************************* END BLOCK_1 *************************************************/
//------------------------------------------------------------------------------------------------------------------------------
/************************************************* BEGIN BLOCK_2 *************************************************/
// ÁËÎÊ ÈÍÔÎÐÌÀÖÈÎÍÍÛÕ ÔÓÍÊÖÈÉ:
// string ArbitragePositions();
// string SymbolToFile( int i, int j );
// void StringToFile( string FileName, string Str );
// void MonitoringArbitrage( int NumSymbol, int Variant1, int Variant2 );
// void WriteStatistic( string FileName );
/************************************************* BEGIN BLOCK_2 *************************************************/
       
string ArbitragePositions()
{
  string Str = WindowExpertName() + ": MinPips = " + DoubleToStr(MinPips, 1);
  int i, j, k, Pos;  
  for (i = 0; i < AmountAllSymbols; i++)
  {
    Pos = 0;
    
    for (j = 0; j < Count[i] - 1; j++)
    {
      Pos += j + 1;
  
      for (k = j + 1; k < Count[i]; k++)
      {
        if  (XTrade[i][Pos])
        {
          if (XPosition[i][Pos] < -ALPHA)
            Str = Str + "\n" + SymbolToStr(i, j) + " (SELL) && " + SymbolToStr(i, k) + " (BUY)";
          else if (XPosition[i][Pos] > ALPHA)
            Str = Str + "\n" + SymbolToStr(i, j) + " (BUY) && " + SymbolToStr(i, k) + " (SELL)";
        }
        
        Pos++;
      }
    }
  }
  
  return(Str);
}
// Ãîòîâèò äàííûå äëÿ çàïèñè â ôàéë j-ãî âàðèàíòà ïîëó÷åíèÿ i-ãî ñãåíåðèðîâàííîãî ñèìâîëà
string SymbolToFile( int i, int j )
{
  string Str = "";
  int S1, S2;
  if (Math[i][j] < 0)
  {
    S1 = AllSymbols[i][j];
    Str = RealSymbols[S1] + ": " + DoubleToStr(BidsReal[S1], MarketInfo(REALSymbols[S1], MODE_DIGITS)) + " " +
                                   DoubleToStr(AsksReal[S1], MarketInfo(REALSymbols[S1], MODE_DIGITS));
  } 
  else
  {
    S1 = AllSymbols[i][j] / AmountRealSymbols;
    S2 = AllSymbols[i][j] % AmountRealSymbols;
    Str = RealSymbols[S1] + ": " + DoubleToStr(BidsReal[S1], MarketInfo(REALSymbols[S1], MODE_DIGITS)) + " " +
                                   DoubleToStr(AsksReal[S1], MarketInfo(REALSymbols[S1], MODE_DIGITS)) + "\n" +
          RealSymbols[S2] + ": " + DoubleToStr(BidsReal[S2], MarketInfo(REALSymbols[S2], MODE_DIGITS)) + " " +
                                   DoubleToStr(AsksReal[S2], MarketInfo(REALSymbols[S2], MODE_DIGITS));
  }
  return(Str);
}
void StringToFile( string FileName, string Str )
{
  int handle;
  
  handle = FileOpen(FileName, FILE_READ|FILE_WRITE, "\t");
  FileSeek(handle, 0, SEEK_END);
  FileWrite(handle, Str);
  FileClose(handle);
  return;
}
void MonitoringArbitrage( int NumSymbol, int Variant1, int Variant2 )
{
  int V;
  string Str;
  if (Variant1 < Variant2)
    V = Variant1 * Count[NumSymbol] + Variant2;
  else
    V = Variant2 * Count[NumSymbol] + Variant1;
  CountArbitrage[NumSymbol][V]++;
  
  if (CountArbitrage[NumSymbol][V] > MaxCountArbitrage)
    MaxCountArbitrage = CountArbitrage[NumSymbol][V];
  
  if (Monitoring)
  {
    Str = "Time = " + TimeToStr(CurrentTime, TIME_DATE|TIME_SECONDS) + "\n";
    Str = Str + "Bid \"" + SymbolToStr(NumSymbol, Variant1) + "\" (" + DoubleToStr(Bids[NumSymbol][Variant1], 5) +
          ") > (" + DoubleToStr(Asks[NumSymbol][Variant2], 5) + ") Ask \"" + SymbolToStr(NumSymbol, Variant2) +
          "\", Difference = " + DoubleToStr((Bids[NumSymbol][Variant1] - Asks[NumSymbol][Variant2]) / PointD[NumSymbol], 1) + " pips";
    Str = Str + "\n" + SymbolToFile(NumSymbol, Variant1) + "\n" + SymbolToFile(NumSymbol, Variant2) + "\n";
    Str = Str + "Count = " + CountArbitrage[NumSymbol][V];
    StringToFile("Arbitrage.txt", Str); 
  }
  return;
}
void WriteStatistic( string FileName )
{
  string Str = WindowExpertName() + ": MinPips = " + DoubleToStr(MinPips, 1);
  int i, j, k, Pos;
  int V1, V2;
  int handle, Cnt;
  int BenchTime = GetTickCount();
  
  Cnt = MaxCountArbitrage;
  
  while (Cnt >= 2) // Äëÿ çàêðûòèÿ è îòêðûòèÿ íóæíî, êàê ìèíèìóì, 2 ðàçà
  {
    for (i = 0; i < AmountAllSymbols; i++)
    {
      Pos = 0;
    
      for (j = 0; j < Count[i] - 1; j++)
      {
        Pos += j + 1;
  
        for (k = j + 1; k < Count[i]; k++)
        {
          if  (CountArbitrage[i][Pos] == Cnt)
            Str = Str + "\n" + CountArbitrage[i][Pos] + ": " + SymbolToStr(i, j) + " && " + SymbolToStr(i, k);
            
          Pos++;
        }
      }
    }
    
    Cnt--;
  }
    
  handle = FileOpen(FileName, FILE_WRITE, "\t");
  FileWrite(handle, Str);
  FileClose(handle);
  
  Print("MaxCountArbitrage = " + MaxCountArbitrage + ", Write Time Statistic = " +
        DoubleToStr((GetTickCount() - BenchTime) / 1000, 0) + " s.");
  
  return;
}
/************************************************* END BLOCK_2 *************************************************/
// ÁËÎÊ ÈÍÔÎÐÌÀÖÈÎÍÍÛÕ ÔÓÍÊÖÈÉ:
// string ArbitragePositions();
// string SymbolToFile( int i, int j );
// void StringToFile( string FileName, string Str );
// void MonitoringArbitrage( int NumSymbol, int Variant1, int Variant2 );
// void WriteStatistic( string FileName );
/************************************************* END BLOCK_2 *************************************************/
//------------------------------------------------------------------------------------------------------------------------------
/************************************************* BEGIN BLOCK_3 *************************************************/
// ÁËÎÊ ÎÑÍÎÂÍÛÕ ÒÎÐÃÎÂÛÕ ÔÓÍÊÖÈÉ:
// int OrderSlipPage( double OriginalPrice );
// double GetTradeVolume( int PrevTicket );
// int _OrderSend( string _symbol, int _cmd, double _volume, double _price, int _slippage, double _stoploss, double _takeprofit);
// double _OrderClose( int _ticket, double _lots, double _price, int _slippage);
/************************************************* BEGIN BLOCK_3 *************************************************/
int OrderSlipPage( double OriginalPrice )
{
  double Tmp;
  int Res;
  
  if (OrderCloseTime() == 0)
  {
    if (OrderType() == OP_BUY)
      Tmp = OriginalPrice - OrderOpenPrice();
    else if (OrderType() == OP_SELL)
      Tmp = OrderOpenPrice() - OriginalPrice;
  }
  else
  {
    if (OrderType() == OP_BUY)
      Tmp = OrderClosePrice() - OriginalPrice;
    else if (OrderType() == OP_SELL)
      Tmp = OriginalPrice - OrderClosePrice();
  }
  
  if (Tmp > 0)
    Res = Tmp / MarketInfo(OrderSymbol(), MODE_POINT) + 0.1;
  else
    Res = Tmp / MarketInfo(OrderSymbol(), MODE_POINT) - 0.1;
  
  return(Res);
}
double GetTradeVolume( int PrevTicket )
{
  if (OrderTicket() == PrevTicket) // Íåò íîâûõ îðäåðîâ
    return(0);
    
  return(OrderLots());
}
// Îáðàáàòûâàåò ñèòóàöèè Partial Fills. Ïðè àñèíõðîííîé îáðàáîòêå áðîêåðîì òîðãîâûõ ïðèêàçîâ ìîæåò ðàáîòàòü íåêîððåêòíî
int _OrderSend( string _symbol, int _cmd, double _volume, double _price, int _slippage, double _stoploss, double _takeprofit)
{
  static string OrderTypeToString[7] = {"OP_BUY", "OP_SELL", "OP_BUYLIMIT", "OP_SELLLIMIT", "OP_BUYSTOP", "OP_SELLSTOP", "Balance"};
  int PrevTicket;
  int Ticket = -1;
  int _GetLastError;
  int SP = 0;
  double Vol = 0;
      
  OrderSelect(OrdersTotal() - 1, SELECT_BY_POS, MODE_TRADES);
  PrevTicket = OrderTicket();
  
  OrderSend(_symbol, _cmd, _volume, _price, _slippage, _stoploss, _takeprofit); 
  _GetLastError = GetLastError();
  OrderSelect(OrdersTotal() - 1, SELECT_BY_POS, MODE_TRADES);
                     
  if (GetTradeVolume(PrevTicket) > ALPHA)
    if (OrderSymbol() == _symbol) // íåîáõîäèìî èç-çà ñâÿçàííûõ ñ ðàçðûâàìè ñâÿçè ïðîáëåì
    {
      SP = OrderSlipPage(_price);
      Ticket = OrderTicket();
      Vol = OrderLots();
    }
    
  Print(Ticket, " = OrderSend(", _symbol, ", ", OrderTypeToString[_cmd], ", ", _volume, ", ", DoubleToStr(_price, MarketInfo(_symbol, MODE_DIGITS)),
        ", ", _slippage, ", ", _stoploss, ", ", _takeprofit, ") - ", ErrorDescription(_GetLastError), ", SlipPage = ", SP, ", Lots = ", Vol);
        
  return(Ticket);
}
// Îáðàáàòûâàåò ñèòóàöèè Partial Fills. Ïðè àñèíõðîííîé îáðàáîòêå áðîêåðîì òîðãîâûõ ïðèêàçîâ ìîæåò ðàáîòàòü íåêîððåêòíî
double _OrderClose( int _ticket, double _lots, double _price, int _slippage)
{
  int Ticket = -1;
  int PrevTicket;
  int _GetLastError;
  int SP = 0;
  double Vol = 0;
  string _symbol;
  double PrevLots;
  
  OrderSelect(_ticket, SELECT_BY_TICKET);
  _symbol = OrderSymbol();
  PrevLots = OrderLots();
  OrderSelect(OrdersHistoryTotal() - 1, SELECT_BY_POS, MODE_HISTORY);
  PrevTicket = OrderTicket();
  OrderClose(_ticket, _lots, _price, _slippage);
  _GetLastError = GetLastError();
//  Sleep(1000) // äëÿ MBTrading äàííàÿ ïàóçà íåîáõîäèìà è íå ðåøàåò íà 100% ïðîáëåìû àñèíõðîííîé îáðàáîòêè ïðèêàçîâ
  OrderSelect(OrdersHistoryTotal() - 1, SELECT_BY_POS, MODE_HISTORY);
  
  if (GetTradeVolume(PrevTicket) > ALPHA)
    if (OrderSymbol() == _symbol) // íåîáõîäèìî èç-çà ñâÿçàííûõ ñ ðàçðûâàìè ñâÿçè ïðîáëåì
    {
      SP = OrderSlipPage(_price);
      Ticket = OrderTicket();
      Vol = OrderLots();
    }
  
  Print(Ticket, "(", _symbol, ") = OrderClose(", _ticket, ", ", _lots, "(", PrevLots, "), ", DoubleToStr(_price, MarketInfo(_symbol, MODE_DIGITS)),
        ", ", _slippage, ") - ", ErrorDescription(_GetLastError), ", SlipPage = ", SP, ", Lots = ", Vol);
  
  return(Vol);
}
/************************************************* END BLOCK_3 *************************************************/
// ÁËÎÊ ÎÑÍÎÂÍÛÕ ÒÎÐÃÎÂÛÕ ÔÓÍÊÖÈÉ:
// int OrderSlipPage( double OriginalPrice );
// double GetTradeVolume( int PrevTicket );
// int _OrderSend( string _symbol, int _cmd, double _volume, double _price, int _slippage, double _stoploss, double _takeprofit);
// double _OrderClose( int _ticket, double _lots, double _price, int _slippage);
/************************************************* END BLOCK_3 *************************************************/
//------------------------------------------------------------------------------------------------------------------------------
/************************************************* BEGIN BLOCK_4 *************************************************/
// ÁËÎÊ ÂÑÏÎÌÎÃÀÒÅËÜÍÛÕ ÒÎÐÃÎÂÛÕ ÔÓÍÊÖÈÉ:
// int GetOrderTicket( string Symb, int Type, bool FlagMax );
// bool MyOrderSend( string Symb, int Type, double& Vol, int SlipPage, double MaxLot, bool Lock );
// void CloseLock();
// void RefreshPositions() 
/************************************************* BEGIN BLOCK_4 *************************************************/
int GetOrderTicket( string Symb, int Type, bool FlagMax )
{
  int Ticket, TicketMax = -1, TicketMin = -1;
  double Max = 0;
  double Min = 9999;
  int Pos = OrdersTotal() - 1;
  
  while (Pos >= 0)
  {
    OrderSelect(Pos, SELECT_BY_POS);
    if (OrderSymbol() == Symb)
       if (OrderType() == Type)
       {
         if (OrderLots() > Max) // Íàõîäèì íàèáîëüøèé îðäåð äëÿ ìèíèìèçàöèè êîëè÷åñòâà òîðãîâûõ çàïðîñîâ
         {
           Max = OrderLots();
           TicketMax = OrderTicket();
         }
         if (OrderLots() < Min)  // Íàõîäèì íàèìåíüøèé îðäåð äëÿ óäà÷íîãî CloseBy (ïðîáëåìû MinLot)
         {
           Min = OrderLots();
           TicketMin = OrderTicket();
         }
       }
    Pos--;
  }
  
  if (FlagMax)
    Ticket = TicketMax;
  else
    Ticket = TicketMin;
  
  if (Ticket > 0)
    OrderSelect(Ticket, SELECT_BY_TICKET);
  
  return(Ticket);
}
// Îáðàáîòêà ëîêîâ è Partial Fills
bool MyOrderSend( string Symb, int Type, double& Vol, int SlipPage, double MaxLot, bool Lock )
{
  int TypeReverse;
  double Price;
  double VolTmp;
  
  if (Type == OP_BUY)
  {
    Price = MarketInfo(Symb, MODE_ASK);
//    Price = AsksReal[GetNumRealSymbol(Symb)]; // Ìîãóò áûòü ïðîáëåìû ñ öåíîé èç-çà îãðàíè÷åíèé MT4
    TypeReverse = OP_SELL;
  }
  else // (Type == OP_SELL)
  {
    Price = MarketInfo(Symb, MODE_BID);
//    Price = BidsReal[GetNumRealSymbol(Symb)]; // Ìîãóò áûòü ïðîáëåìû ñ öåíîé èç-çà îãðàíè÷åíèé MT4
    TypeReverse = OP_BUY;
  }
    
  if (!Lock) // Òîðãîâëÿ áåç ëîêà (êðèòè÷íî äëÿ àðáèòðàæà èç-çà áîëüøîãî êîëè÷åñòâà "ëèøíèõ" òîðãîâûõ çàïðîñîâ)
    while ((Vol > MinLot - ALPHA) && (GetOrderTicket(Symb, TypeReverse, TRUE) > 0)) // Vol >= MinLot
    {
      if (Vol > OrderLots() + MinLot - ALPHA) // Vol >= OrderLots() + MinLot
        VolTmp = OrderLots();
      else if (Vol < OrderLots() - MinLot + ALPHA) // Vol <= OrderLots() - MinLot
        VolTmp = Vol;
      else if (Vol > OrderLots() + ALPHA)// Vol > OrderLots()
        VolTmp = OrderLots() - MinLot;
      else if (Vol < OrderLots() - ALPHA) // Vol < OrderLots()
        VolTmp = MathMin(OrderLots() - MinLot, Vol - MinLot);
      else // Vol == OrderLots()
        VolTmp = OrderLots();
      if (VolTmp < MinLot - ALPHA) // VolTmp < MinLot
      {
        Alert("Cannot close Order ", OrderSymbol(), " ", OrderTicket(), " ", OrderLots(), " by ", Vol, " lots! Lock is needed!");
        
        break;
      }
      
      // Ñ òàêèì âîçâðàùàåìûì ðåçóëüòàòîì ìîæíî ïðèêðóòèòü è àñèíõðîííóþ îáðàáîòêó îðäåðîâ áðîêåðà
      VolTmp = _OrderClose(OrderTicket(), NormalizeDouble(VolTmp, DigitsLot), Price, SlipPage);
      
      if (VolTmp < ALPHA)
        return(FALSE);
        
      Vol -= VolTmp;
    }
  
  while (Vol - MaxLot > ALPHA) // Vol > MaxLot
  {
    if (_OrderSend(Symb, Type, NormalizeDouble(MathMin(MaxLot, Vol - MinLot), DigitsLot), Price, SlipPage, 0, 0) < 0)
      return(FALSE);
          
    Vol -= OrderLots();
  }
 
  while (Vol > MinLot - ALPHA) // Vol >= MinLot
  {
    if (_OrderSend(Symb, Type, NormalizeDouble(Vol, DigitsLot), Price, SlipPage, 0, 0) < 0)
      return(FALSE);
    
    Vol -= OrderLots();
  }
  
  return(TRUE);
}
// Çàêðûòèå ëîêèðîâàííûõ ïîçèöèé
void CloseLock()
{
  int BuyTicket, SellTicket;
  double SellLots, Tmp;
  string Symb;
  bool FlagMax = TRUE;
  bool FlagRepeat = FALSE;
  for (int i = 0; i < AmountRealSymbols; i++)
  {
    Symb = REALSymbols[i];
    
    BuyTicket = GetOrderTicket(Symb, OP_BUY, FlagMax);
    SellTicket = GetOrderTicket(Symb, OP_SELL, !FlagMax);
  
    while ((BuyTicket != -1) && (SellTicket != -1))
    {
      SellLots = OrderLots();
      
      OrderSelect(BuyTicket, SELECT_BY_TICKET);
      
      Tmp = MathAbs(OrderLots() - SellLots);
      
      if ((ALPHA < Tmp) && (Tmp < MinLot - ALPHA))
      {
        if (FlagRepeat)
          break;
          
        FlagRepeat = TRUE;
        FlagMax = !FlagMax;
      }
      else
      {
        if (!OrderCloseBy(BuyTicket, SellTicket))
          return;
        
        FlagRepeat = FALSE;
      }
      BuyTicket = GetOrderTicket(Symb, OP_BUY, FlagMax);
      SellTicket = GetOrderTicket(Symb, OP_SELL, !FlagMax);
    }
  }
  
  return;
}
// Îòêðûâàåò ïîñ÷èòàííûå ïîçèöèè - Position[]
void RefreshPositions() 
{
  bool Flag = FALSE;  // Ôëàã íà íàëè÷èå èçìåíåíèé
  double Vol;
  
  for (int i = 0; i < AmountRealSymbols; i++)
  {
    Position[i] = NormalizeDouble(Position[i], DigitsLot); // Íå ñàìîå ëó÷øåå (ïî ñêîðîñòè èñïîëíåíèÿ) ðåøåíèå
    
    if (Position[i] > ALPHA) // ïîêóïêà. Âîçìîæíû ïðîáëåìû èç-çà íåíîðìàëèçîâàííîñòè Position[i]
    {
      Vol = Position[i];
       
      if (!MyOrderSend(REALSymbols[i], OP_BUY, Vol, SlipPage, MaxLot, Lock))
        Print("Vol = ", Vol);
      
      Position[i] = Vol; // Íå ïðîøåäøèé îáúåì èç-çà òîðãîâûõ îøèáîê
      Flag = TRUE;
    }
    else if (Position[i] < -ALPHA) // ïðîäàæà
    {
      Vol = -Position[i];
      if (!MyOrderSend(REALSymbols[i], OP_SELL, Vol, SlipPage, MaxLot, Lock))
        Print("Vol = ", Vol);
       
      Position[i] = -Vol; // Íå ïðîøåäøèé îáúåì èç-çà òîðãîâûõ îøèáîê
      Flag = TRUE;
    }
  }
    
  if (Flag) // Ïå÷àòàåì èçìåíåíèÿ
    Comment(ArbitragePositions());
    
  CloseLock(); // Ïðè Lock = FALSE èç-çà íþàíñîâ MinLot ÌÎÃÓÒ áûòü ëîêèðîâàííûå ïîçèöèè
  
  return;
}
/************************************************* END BLOCK_4 *************************************************/
// ÁËÎÊ ÂÑÏÎÌÎÃÀÒÅËÜÍÛÕ ÒÎÐÃÎÂÛÕ ÔÓÍÊÖÈÉ:
// int GetOrderTicket( string Symb, int Type, bool FlagMax );
// bool MyOrderSend( string Symb, int Type, double& Vol, int SlipPage, double MaxLot, bool Lock );
// void CloseLock();
// void RefreshPositions() 
/************************************************* END BLOCK_4 *************************************************/
//------------------------------------------------------------------------------------------------------------------------------
/************************************************* BEGIN BLOCK_5 *************************************************/
// ÁËÎÊ ÔÓÍÊÖÈÉ ÀÐÁÈÒÐÀÆÀ:
// void SymbolDone( double Vol, int Symb );
// void OpenSymbolPosition( int NumSymbol, int Variant, int Type, double Vol );
// void OpenArbitragePosition( int NumSymbol, int Variant1, int Variant2, double Vol );
// void GetBidAsk( int i, int j );
// void TradeArbitrage();
/************************************************* BEGIN BLOCK_6 *************************************************/
void SymbolDone( double Vol, int Symb )
{
  if (Vol == 0)
    return;
  Position[Symb] += Vol;
  
  if (Vol > 0)
    StrOut = StrOut + "; BUY " + RealSymbols[Symb] + "(" + DoubleToStr(Vol, DigitsLot) + ") = " +
                      DoubleToStr(AsksReal[Symb], MarketInfo(REALSymbols[Symb], MODE_DIGITS)) + " Ask";
  else // Vol < 0
    StrOut = StrOut + "; SELL " + RealSymbols[Symb] + "(" + DoubleToStr(Vol, DigitsLot) + ") = " +
                      DoubleToStr(BidsReal[Symb], MarketInfo(REALSymbols[Symb], MODE_DIGITS)) + " Bid";
          
  return;
}
// Îòêðûòèå Type-òèïà ïîçèöèè ïî ñãåíåðèðîâàííîìó ñèìâîëó AllSymbols[NumSymbol][Variant]
void OpenSymbolPosition( int NumSymbol, int Variant, int Type, double Vol )
{
  int S1, S2;
  double Tmp = 0, Tmp1 = 0, Tmp2 = 0;
  int Symb = AllSymbols[NumSymbol][Variant];
  int Mth = Math[NumSymbol][Variant];
 
  if (Type == OP_SELL)
  {
    if (Mth == -2) // -2 - "1 / S"
      Tmp = Vol / AsksReal[Symb];
    else if (Mth == -1)  //  -1 - "S"
      Tmp = -Vol;
    else
    {
      S1 = Symb / AmountRealSymbols;
      S2 = Symb % AmountRealSymbols;
      switch (Mth)
      {
        case 0: // 0 - "S1 / S2"
          Tmp1 = -Vol;
          Tmp2 = Vol * Bids[NumSymbol][Variant];
          break;
        case 1: // 1 - "S1 * S2"
          Tmp1 = -Vol;
          Tmp2 = -Vol * BidsReal[S1];
          break;
        case 2: // 2 - "1 / (S1 * S2)";
          Tmp1 = Vol / AsksReal[S1];
          Tmp2 = Vol * Bids[NumSymbol][Variant];
          break;
        case 3: // 3 - "S2 / S1"
          Tmp1 = Vol / AsksReal[S1];
          Tmp2 = -Tmp1;
          break;
      }
    }   
  }
  else // (Type == OP_BUY)
  {
    if (Mth == -2) // -2 - "1 / S"
      Tmp = -Vol / BidsReal[Symb];
    else if (Mth == -1)  //  -1 - "S"
      Tmp = Vol;
    else
    {
      S1 = Symb / AmountRealSymbols;
      S2 = Symb % AmountRealSymbols;
      switch (Mth)
      {
        case 0: // 0 - "S1 / S2"
          Tmp1 = Vol;
          Tmp2 = -Vol * Asks[NumSymbol][Variant];
          break;
        case 1: // 1 - "S1 * S2"
          Tmp1 = Vol;
          Tmp2 = Vol * AsksReal[S1];
          break;
        case 2: // 2 - "1 / (S1 * S2)";
          Tmp1 = -Vol / BidsReal[S1];
          Tmp2 = -Vol * Asks[NumSymbol][Variant];
          break;
        case 3: // 3 - "S2 / S1"
          Tmp1 = -Vol / BidsReal[S1];
          Tmp2 = -Tmp1;
          break;
      }
    }   
  }
  
  SymbolDone(Tmp, Symb);
  SymbolDone(Tmp1, S1);
  SymbolDone(Tmp2, S2);
  return;    
}
// Bids[NumSymbol][Variant1] > Asks[NumSymbol][Variant2]
void OpenArbitragePosition( int NumSymbol, int Variant1, int Variant2, double Vol )
{
  int V;
  double XPos;
  int j, k;
  
  // Íè÷åãî íå äåëàåì, åñëè ïî òåêóùåìó àðáèòðàæó óæå îòêðûòà ïîçèöèÿ
  if (Variant1 < Variant2)
  {
    V = Variant1 * Count[NumSymbol] + Variant2;  // Òàêèì ñïîñîáîì õðàíèì íîìåð
    XPos = XPosition[NumSymbol][V];
    
    if (XPos < -ALPHA)
      return;
    XPos += Vol; // ó÷åò îáúåìà ïðåäûäóùåé ñäåëêè
    XPosition[NumSymbol][V] = -Vol;
  }    
  else
  {
    V = Variant2 * Count[NumSymbol] + Variant1;
    XPos = XPosition[NumSymbol][V];
    
    if (XPos > ALPHA)
      return;
      
    XPos = Vol - XPos;  // ó÷åò îáúåìà ïðåäûäóùåé ñäåëêè
    XPosition[NumSymbol][V] = Vol;
  }
  
  MonitoringArbitrage(NumSymbol, Variant1, Variant2);
  if (XTrade[NumSymbol][V]) // Òîðãóåì òîëüêî çàäàííûå êîìáèíàöèè
  {
    OpenSymbolPosition(NumSymbol, Variant1, OP_SELL, XPos);  // Ïðîäàëè ïåðâûé âàðèàíò
    OpenSymbolPosition(NumSymbol, Variant2, OP_BUY, XPos);   // Êóïèëè âòîðîé âàðèàíò
    Print("Variant1 = " + SymbolToStr(NumSymbol, Variant1) + " (Bid = " + DoubleToStr(Bids[NumSymbol][Variant1], 6) + 
          "), Variant2 = " + SymbolToStr(NumSymbol, Variant2) + " (Ask = " + DoubleToStr(Asks[NumSymbol][Variant2], 6) +
          "), Difference = " + DoubleToStr((Bids[NumSymbol][Variant1] - Asks[NumSymbol][Variant2]) / PointD[NumSymbol], 1) + " pips");
    Print(StrOut);
 
    StrOut = "";
  }
  return;
}
// Âû÷èñëÿåò â Bids[i][j] è Asks[i][j] öåíû j-ãî âàðèàíòà ïîëó÷åíèÿ i-ãî ñãåíåðèðîâàííîãî ñèìâîëà
void GetBidAsk( int i, int j )
{
  double Bid1, Bid2;
  double Ask1, Ask2;
  int Mth, Symb;
  int S1, S2;
  Symb = AllSymbols[i][j];
  Mth = Math[i][j];
  
  if (Mth == -2) // -2 - "1 / S"
  {
    Bids[i][j] = 1 / AsksReal[Symb];
    Asks[i][j] = 1 / BidsReal[Symb];
  }
  else if (Mth == -1)  //  -1 - "S"
  {
    Bids[i][j] = BidsReal[Symb];
    Asks[i][j] = AsksReal[Symb];
  }
  else
  {
    S1 = Symb / AmountRealSymbols;
    S2 = Symb % AmountRealSymbols;
    Bid1 = BidsReal[S1];
    Bid2 = BidsReal[S2];
    Ask1 = AsksReal[S1];
    Ask2 = AsksReal[S2];
     
    switch (Mth)
    {
      case 0: // 0 - "S1 / S2"
        Bids[i][j] = Bid1 / Ask2;
        Asks[i][j] = Ask1 / Bid2;
        break;
      case 1: // 1 - "S1 * S2"
        Bids[i][j] = Bid1 * Bid2;
        Asks[i][j] = Ask1 * Ask2;
        break;
      case 2: // 2 - "1 / (S1 * S2)";
        Bids[i][j] = 1 / (Ask1 * Ask2);
        Asks[i][j] = 1 / (Bid1 * Bid2);
        break;
      case 3: // 3 - "S2 / S1"
        Bids[i][j] = Bid2 / Ask1;
        Asks[i][j] = Ask2 / Bid1;
    }
  }
  return;
}
void TradeArbitrage()
{
  int i, j, k;
  double Bid1, Bid2, Ask1, Ask2;
  GetRealBidAsk();
 
  for (i = 0; i < AmountAllSymbols; i++)
  {
    for (j = 0; j < Count[i]; j++)
      GetBidAsk(i, j);           
 
    for (j = 0; j < Count[i] - 1; j++)
    {
      Bid1 = Bids[i][j] - MinPipsD[i];
      Ask1 = Asks[i][j] + MinPipsD[i];
  
      for (k = j + 1; k < Count[i]; k++)
      {
        Bid2 = Bids[i][k];
        Ask2 = Asks[i][k];
                       
        if (Bid1 > Ask2)
          OpenArbitragePosition(i, j, k, Lots);
        else if (Ask1 < Bid2)
          OpenArbitragePosition(i, k, j, Lots);
      }
    }
  }
  RefreshPositions();
  return;
}
/************************************************* END BLOCK_5 *************************************************/
// ÁËÎÊ ÔÓÍÊÖÈÉ ÀÐÁÈÒÐÀÆÀ:
// void SymbolDone( double Vol, int Symb );
// void OpenSymbolPosition( int NumSymbol, int Variant, int Type, double Vol );
// void OpenArbitragePosition( int NumSymbol, int Variant1, int Variant2, double Vol );
// void GetBidAsk( int i, int j );
// void TradeArbitrage();
/************************************************* END BLOCK_5 *************************************************/
//------------------------------------------------------------------------------------------------------------------------------
void init()
{
  Comment(WindowExpertName() + ": MinPips = " + DoubleToStr(MinPips, 1)) ;
  
  InitArbitrage();
  PrintBeginInfo();
  TimeToWrite *= 60;
  PrevTime = TimeCurrent();
  return;
}
void deinit()
{
  Comment("");
   
  return;
}
 
void start()
{
  while(!IsStopped())
  {
    RefreshRates();
    CurrentTime = TimeCurrent();
    
    TradeArbitrage();
    if (CurrentTime - PrevTime > TimeToWrite)
    {
      PrevTime = CurrentTime;
      
      WriteStatistic("ArbitrageStatistic.txt");
    }
    
    Sleep(PAUSE);
  }
  return;
}
             
            
            
            
Comments