Virtual Trade

//+------------------------------------------------------------------+

//|                                                 VirtualTrend.mqh |

//|                               Copyright © Evgeniy Trofimov, 2010 |

//|                                  http://forum.mql4.com/ru/16793/ |

//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

//|                 V I R T U A L       T R E N D                    |

//|               Copyright: Evgeniy Trofimov, 2010                  |

//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

// This library contains the following functions:

//Main:

// + VirtualSend

// + VirtualSelect

// + VirtualClose - cancels pending orders and closes positions

// + VirtualModify

//Auxiliary:

// + VirtualCopyPosition - copies position into the other region of array

// + VirtualHighTicket - searches for a young ticket

// + VirtualFileLoad - loads array of trades from a file

// + VirtualFileSave - saves array of trades to a file

// + VirtualUpdate - updates information from market

// + VirtualFilter - creates list of indexes the of array of found positions

// + VirtualProfitHistory - calculates value of moving average of balance

// + VirtualProfit - calculates value of profit of open position at the current moment

// + VirtualRating - calculates rating of profitable TS in percentage terms

// + VirtualExist  - this function is used for restricting of opening 2 trades in succession by a single signal

//+------------------------------------------------------------------+

extern bool RatingON=true;       // - switch of rating (if it is disable, trades in the file must comply with real trading)

extern bool FastTest=true;       // - don't create the file while testing

#define  VIRT_TRADES        1    // - trade orders and positions

#define  VIRT_HISTORY       0    // - closed positions and canceled pending orders

int      Virt.Count;             // - number of positions in array (from 0 to 999)

int      Virt.Index;             // - position of selected trade in the base of functions VirtualSelect()

int      VirtBufferSize  =  16;  // - number of elements in one line of the base (next lines)

int      Virt.Ticket[1000];      // - Number of order

datetime Virt.OpenTime[1000];    // - Time of opening

int      Virt.Type[1000];        // - Type of trade

double   Virt.Lots[1000];        // - Volume

string   Virt.Symbol[1000];      // - Symbol

double   Virt.OpenPrice[1000];   // - Open price

double   Virt.StopLoss[1000];

double   Virt.TakeProfit[1000];

datetime Virt.CloseTime[1000];   // - Tim of closing

double   Virt.ClosePrice[1000];  // - Close price

double   Virt.Swap[1000];        // - Swap

double   Virt.Profit[1000];      // - Profit in pipslots

string   Virt.Comment[1000];     // - Comments

int      Virt.MagicNumber[1000]; // - Magic number

datetime Virt.Expiration[1000];  // - Date of canceling a pending order

int      Virt.Status[1000];      // - Status of order: 1 - open position/pending order; 0 - closed position/canceled order

int      Virt.Filter[1000];

int      Virt.Filter.Count;

int      Err.Number;             // - Error number

string   Err.Description;        // - Description of last

//+------------------------------------------------------------------+

double VirtualRating(int fMagic, string fSymbol, int period, int applied_price, string filename="virtual.csv"){

   //The function returns rating of positive trades 

   //created by one or another trading strategy

   //at a certain symbol (100% is most profitable TS, 0% - neither fish, nor fowl)

   //fMagic - magic number, which is used for telling one trading system from another

   //fSymbol - symbol, the rating is considered for

   //period - number of last trades

   //applied_price -  Price used: 0 - in the deposit currency, 1 - in points;

   //filename - file of trades

   if(!RatingON) return(100);

   int MagicNum[];

   double Profit[];

   //double Rating[];

   int i, j;

   bool MagicExist;

   VirtualFileLoad(filename);

   if(VirtualFilter(VIRT_HISTORY, -1, -1, fSymbol)<1) return (0);

   for(i=0; i<Virt.Filter.Count; i++){

      MagicExist=false;

      for(j=0; j<ArraySize(MagicNum); j++){

         if(MagicNum[j]==Virt.MagicNumber[Virt.Filter[i]]){

            MagicExist=true;

            break;

         }

      }//Next j

      if(!MagicExist){

         ArrayResize(MagicNum, ArraySize(MagicNum)+1);

         MagicNum[ArraySize(MagicNum)-1]=Virt.MagicNumber[Virt.Filter[i]];

      }

   }//Next i

   ArrayResize(Profit, ArraySize(MagicNum));

   for(i=0; i<ArraySize(MagicNum); i++){

      Profit[i]=VirtualProfitHistory(applied_price, period, 0, -1, -1, fSymbol, MagicNum[i], true)+

                VirtualProfit(       applied_price,            -1, -1, fSymbol, MagicNum[i], true);

      if(Profit[i]<0) Profit[i]=0;

   }//Next i

   j=ArrayMaximum(Profit); // 100%

   if(Profit[j]==0) return (0);

   //ArrayResize(Rating, ArraySize(MagicNum));

   for(i=0; i<ArraySize(MagicNum); i++){

      if(fMagic==MagicNum[i]){

         return(100*Profit[i]/Profit[j]);

      }

   }//Next i

   return(0);

}//VirtualRating()

//+------------------------------------------------------------------+

double VirtualProfit(int applied_price=1, int fTicket=-1, int fType=-1, string fSymbol="", int fMagic=-1, bool dh = false, string fComment=""){

   //The function returns value of profit of open positions

   //applied_price -  Price used: 0 - in the deposit currency, 1 - in points;

   //dh = true - Divide by the number of days to calculate the relative profit

   //Other parameters correspond to the ones of the VirtualFilter() function

   //Note: file with positions should be loaded to call this function.

   double Profit, plus, deltaDay;

   datetime OldDay = TimeCurrent();

   if(VirtualFilter(VIRT_TRADES, fTicket, fType, fSymbol, fMagic, fComment)>0){

      for(int i=0; i<Virt.Filter.Count; i++){

         if(Virt.Type[Virt.Filter[i]]<2){

            if(Virt.OpenTime[Virt.Filter[i]]<OldDay) OldDay=Virt.OpenTime[Virt.Filter[i]];

            if(applied_price==0){//In the deposit currency

               plus=Virt.Profit[Virt.Filter[i]];

            }else{//In points

               if(Virt.Type[Virt.Filter[i]]==OP_BUY){

                  plus=(Virt.ClosePrice[Virt.Filter[i]]-Virt.OpenPrice[Virt.Filter[i]])/MarketInfo(Virt.Symbol[Virt.Filter[i]],MODE_POINT);

               }else{

                  plus=(Virt.OpenPrice[Virt.Filter[i]]-Virt.ClosePrice[Virt.Filter[i]])/MarketInfo(Virt.Symbol[Virt.Filter[i]],MODE_POINT);

               }

            }

            Profit=Profit+plus;

         }

      }//Next i

   }

   if(dh){

      deltaDay=(TimeCurrent()-OldDay)/(24*60*60);

      if(deltaDay<1){

         deltaDay=1;

      }

      Profit=Profit/deltaDay;

   }

   return(Profit);

}//VirtualProfit()

//+------------------------------------------------------------------+

double VirtualProfitHistory(int applied_price=1, int period=0, int shift=0, 

int fTicket=-1, int fType=-1, string fSymbol="", int fMagic=-1, bool dh = false, string fComment=""){

   //The function returns value of profit of closed trades

   //applied_price -  Price used: 0 - in the deposit currency, 1 - in points;

   //period   -   Period of averaging for calculation of profit. If it is equal to 0, then for all the trades;

   //shift    -   Index of obtained value from the array of trades (shift back relatively to the last closed trade on the specified number of trades). 

   //dh = true - Divide by the number of days to calculate the relative profit

   //Other parameters correspond to the ones of the VirtualFilter() function

   //Note: file with positions should be loaded to call this function.

   double Profit, plus, deltaDay;

   datetime beginDay=TimeCurrent(), endDay;

   int j, k;

   if(VirtualFilter(VIRT_HISTORY, fTicket, fType, fSymbol, fMagic, fComment)>0){

      for(int i=Virt.Filter.Count-1; i>=0; i--){

         if(Virt.Type[Virt.Filter[i]]<2){

            k++;

            if(k>shift){

               j++;

               if(j>period && period>0) break;

               if(Virt.OpenTime[Virt.Filter[i]]<beginDay) beginDay=Virt.OpenTime[Virt.Filter[i]];

               if(Virt.CloseTime[Virt.Filter[i]]>endDay) endDay=Virt.CloseTime[Virt.Filter[i]];

               if(applied_price==0){//In the deposit currency

                  plus=Virt.Profit[Virt.Filter[i]];

               }else{//In points

                  if(Virt.Type[Virt.Filter[i]]==OP_BUY){

                     plus=(Virt.ClosePrice[Virt.Filter[i]]-Virt.OpenPrice[Virt.Filter[i]])/MarketInfo(Virt.Symbol[Virt.Filter[i]],MODE_POINT);

                  }else{

                     plus=(Virt.OpenPrice[Virt.Filter[i]]-Virt.ClosePrice[Virt.Filter[i]])/MarketInfo(Virt.Symbol[Virt.Filter[i]],MODE_POINT);

                  }

               }

               Profit=Profit+plus;

            }

         }

      }//Next i

   }

   if(dh){

      deltaDay=(endDay-beginDay)/(24*60*60);

      //Print(TimeToStr(endDay)+" - "+TimeToStr(beginDay)+" = "+DoubleToStr(deltaDay,2)+" äíåé");

      if(deltaDay<1){

         deltaDay=1;

      }

      Profit=Profit/deltaDay;

   }

   return(Profit);

}//VirtualProfitHistory()

//+------------------------------------------------------------------+

bool VirtualSelect(int index, int select, int pool=VIRT_TRADES){

   //The function chooses an order for further working with it. Returns TRUE if the function is executed successfully. 

   //index   -   Position of order or number of order depending on the second parameter. 

   //select   -   Flag of mode of choosing. Can have one of the following values:

   //SELECT_BY_POS - the index number of position in the list is passed in the index parameter,

   //SELECT_BY_TICKET - the number of ticket is passed in the index parameter. 

   //pool   -  Data source for choosing. Used when the select parameter is equal to SELECT_BY_POS. Can have one of the following values:

   //VIRT_TRADES (default) - order is chosen from open and pending orders,

   //VIRT_HISTORY - order is chosen from closed and deleted orders 

   //Note: file with positions should be loaded to call this function.

   if(select==SELECT_BY_POS){

      if(VirtualFilter(pool)>0){

         if(index<Virt.Filter.Count){

            Virt.Index=Virt.Filter[index];

            return(true);

         }

      }

   }else{ //select==SELECT_BY_TICKET

      if(VirtualFilter(-1,index)>0){

         Virt.Index=Virt.Filter[0];

         return(true);

      }

   }

   return(false);

}//VirtualSelect()

//+------------------------------------------------------------------+

bool VirtualModify(int ticket, double price, double stoploss, double takeprofit, datetime expiration, string filename="virtual.csv"){

   //Changes parameters of previously opened positions and pending orders. Returns TRUE if the function is executed successfully.

   //Attention!!! There are no checks for the minimum acceptable level of 'price', SL and TP!!!

   int i;

   VirtualFileLoad(filename);

   if(VirtualFilter(VIRT_TRADES, ticket)>0){

      i=Virt.Filter[0];

      if(Virt.Type[i]<2){      

         if(stoploss>0) Virt.StopLoss[i]=stoploss;

         if(takeprofit>0) Virt.TakeProfit[i]=takeprofit;

      }else{

         if(price>0) Virt.OpenPrice[i]=price;

         if(stoploss>0) Virt.StopLoss[i]=stoploss;

         if(takeprofit>0) Virt.TakeProfit[i]=takeprofit;

         if(expiration>0) Virt.Expiration[i]=expiration;

      }

      VirtualFileSave(filename);

      return(true);

   } else {

      Err.Number = 102;

      Err.Description = "Number of position couldn't be found at the attempt to change it";

      return(false);

   }   

}//VirtualModify()

//+------------------------------------------------------------------+

bool VirtualClose(int ticket, string filename="virtual.csv"){

   //Closing position. Returns TRUE if the function is executed successfully. Returns FALSE if the function is function execution failed.

   //Information about errors is stored in the Err.* variables

   int i, j;

   VirtualFileLoad(filename);

   if(VirtualFilter(VIRT_TRADES, ticket)>0){

      i=Virt.Filter[0];

      if(Virt.Type[i]==OP_BUY){

         Virt.CloseTime[i]=TimeCurrent();   // - Time of closing

         Virt.ClosePrice[i]=MarketInfo(Virt.Symbol[i], MODE_BID);  // - Price of closing

         Virt.Swap[i]=MathRound((Virt.CloseTime[i]-Virt.OpenTime[i])/(24*60*60))*MarketInfo(Virt.Symbol[i], MODE_SWAPLONG)*Virt.Lots[i];

         Virt.Profit[i]=(Virt.ClosePrice[i]-Virt.OpenPrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

      }else if(Virt.Type[i]==OP_SELL){

         Virt.CloseTime[i]=TimeCurrent();   // - Time of closing

         Virt.ClosePrice[i]=MarketInfo(Virt.Symbol[i], MODE_ASK);  // - Price of closing

         Virt.Swap[i]=MathRound((Virt.CloseTime[i]-Virt.OpenTime[i])/(24*60*60))*MarketInfo(Virt.Symbol[i], MODE_SWAPSHORT)*Virt.Lots[i];

         Virt.Profit[i]=(Virt.OpenPrice[i]-Virt.ClosePrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

      }else if(Virt.Type[i]>1 && Virt.Type[i]<6){

         Virt.CloseTime[i]=TimeCurrent();   // - Time of canceling

         Virt.Comment[i]=Virt.Comment[i]+"[canceled]";

      }

      for(j=i-1; j>=0; j--){

         if(Virt.Status[j]==0) break;

      }//Next j

      Virt.Status[i]=0;                  

      if(j<i-1){

         j=j+1;

         VirtualCopyPosition(j, 999);

         VirtualCopyPosition(i, j);

         VirtualCopyPosition(999, i);

      }

      VirtualFileSave(filename);

      return(true);

   } else {

      Err.Number = 101;

      Err.Description = "Number of positions couldn't be found at the attempt to close it";

      return(false);

   }

}//VirtualClose()

//+------------------------------------------------------------------+

int VirtualFilter(int fStatus=-1, int fTicket=-1, int fType=-1, string fSymbol="", int fMagic=-1, string fComment=""){

   //The function creates the list of indexes of the array of positions found 

   //according to the existing parameters of filter;

   //and returns size of the list

   //Note: file with positions should be loaded to call this function.

   Virt.Filter.Count=0;

   for(int i = 0; i<Virt.Count; i++){

      if(fTicket==-1 || Virt.Ticket[i]==fTicket){

         if(fType==-1 || Virt.Type[i]==fType){

            if(fSymbol=="" || Virt.Symbol[i]==fSymbol) {

               if(fComment=="" || StringFind(Virt.Comment[i], fComment)>-1) {

                  if(fMagic==-1 || Virt.MagicNumber[i]==fMagic) {

                     if(fStatus==-1 || Virt.Status[i]==fStatus) {

                        Virt.Filter[Virt.Filter.Count]=i;

                        Virt.Filter.Count++;

                     }

                  }

               }

            }

         }

      }

   }//Next i

   return(Virt.Filter.Count);

}//VirtualFilter()

//+------------------------------------------------------------------+

void VirtualUpdate(string filename="virtual.csv"){

   //Procedure of updating the file of trades in accordance with the market changes.

   //The following actions are performed here:

   // + charging of swaps;

   // + virtual closing of trades by preliminary set StopLoss and TakeProfit

   // + updating of close price of open positions, calculating profit;

   // + opening pending orders;

   // + expiration of pending orders that haven't triggered;

   bool is_changed, is_closed;

   int i, j;

   VirtualFileLoad(filename);

   for(i=Virt.Count-1; i>=0; i--){

      is_closed=false;

      if(Virt.Status[i]==1) {

         is_changed=true;

         switch(Virt.Type[i]){

         case OP_BUY:

            Virt.CloseTime[i]=TimeCurrent();   // - Time of closing

            Virt.ClosePrice[i]=MarketInfo(Virt.Symbol[i], MODE_BID);  // - Price of closing

            Virt.Swap[i]=MathRound((Virt.CloseTime[i]-Virt.OpenTime[i])/(24*60*60))*MarketInfo(Virt.Symbol[i], MODE_SWAPLONG)*Virt.Lots[i];

            Virt.Profit[i]=(Virt.ClosePrice[i]-Virt.OpenPrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

            if(Virt.TakeProfit[i]>0){

               if(MarketInfo(Virt.Symbol[i], MODE_BID)>=Virt.TakeProfit[i]){

                  Virt.ClosePrice[i]=Virt.TakeProfit[i];  // - Close price

                  Virt.Profit[i]=(Virt.ClosePrice[i]-Virt.OpenPrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

                  Virt.Comment[i]=Virt.Comment[i]+"[tp]";

                  is_closed=true;

               }

            }//End TakeProfit

            if(Virt.StopLoss[i]>0){

               if(MarketInfo(Virt.Symbol[i], MODE_BID)<=Virt.StopLoss[i]){

                  Virt.ClosePrice[i]=Virt.StopLoss[i];  // - Close price

                  Virt.Profit[i]=(Virt.ClosePrice[i]-Virt.OpenPrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

                  Virt.Comment[i]=Virt.Comment[i]+"[sl]";

                  is_closed=true;

               }

            }//End StopLoss

            break;

         case OP_SELL:

            Virt.CloseTime[i]=TimeCurrent();   // - Time of closing

            Virt.ClosePrice[i]=MarketInfo(Virt.Symbol[i], MODE_ASK);  // - Price of closing

            Virt.Swap[i]=MathRound((Virt.CloseTime[i]-Virt.OpenTime[i])/(24*60*60))*MarketInfo(Virt.Symbol[i], MODE_SWAPSHORT)*Virt.Lots[i];

            Virt.Profit[i]=(Virt.OpenPrice[i]-Virt.ClosePrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

            if(Virt.TakeProfit[i]>0){

               if(MarketInfo(Virt.Symbol[i], MODE_ASK)<=Virt.TakeProfit[i]){

                  Virt.ClosePrice[i]=Virt.TakeProfit[i];  // - Close price

                  Virt.Profit[i]=(Virt.OpenPrice[i]-Virt.ClosePrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

                  Virt.Comment[i]=Virt.Comment[i]+"[tp]";

                  is_closed=true;

               }

            }//End TakeProfit

            if(Virt.StopLoss[i]>0){

               if(MarketInfo(Virt.Symbol[i], MODE_ASK)>=Virt.StopLoss[i]){

                  Virt.ClosePrice[i]=Virt.StopLoss[i];  // - Close price

                  Virt.Profit[i]=(Virt.OpenPrice[i]-Virt.ClosePrice[i])*Virt.Lots[i]*MarketInfo(Virt.Symbol[i],MODE_TICKVALUE)/MarketInfo(Virt.Symbol[i], MODE_POINT)+Virt.Swap[i];

                  Virt.Comment[i]=Virt.Comment[i]+"[sl]";

                  is_closed=true;

               }

            }//End StopLoss

            break;

         case OP_BUYLIMIT:

            if(MarketInfo(Virt.Symbol[i], MODE_ASK)<=Virt.OpenPrice[i]){

               Virt.Type[i]=OP_BUY;

            }

            break;

         case OP_SELLLIMIT:

            if(MarketInfo(Virt.Symbol[i], MODE_BID)>=Virt.OpenPrice[i]){

               Virt.Type[i]=OP_SELL;

            }

            break;

         case OP_BUYSTOP:

            if(MarketInfo(Virt.Symbol[i], MODE_ASK)>=Virt.OpenPrice[i]){

               Virt.Type[i]=OP_BUY;

            }

            break;

         case OP_SELLSTOP:

            if(MarketInfo(Virt.Symbol[i], MODE_BID)<=Virt.OpenPrice[i]){

               Virt.Type[i]=OP_SELL;

            }

            break;

         }//End switch

         if(Virt.Type[i]>1 && Virt.Type[i]<6) {

            if(Virt.Expiration[i]>0) {

               if(TimeCurrent()>Virt.Expiration[i]) {

                  Virt.Comment[i]=Virt.Comment[i]+"[expiration]";

                  is_closed=true;

               }

            }

         }

         if(is_closed){

            for(j=i; j>=0; j--){

               if(Virt.Status[j]==0) break;

            }//Next j

            Virt.Status[i]=0;                  

            if(j<i-1){

               j=j+1;

               VirtualCopyPosition(j, 999);

               VirtualCopyPosition(i, j);

               VirtualCopyPosition(999, i);

            }

         }//End if(is_closed)

      } else {

         break;

      }//End if(Virt.Status[i]==1)

   }//Next i

   if(is_changed) VirtualFileSave(filename);

}//VirtualUpdate()

//+------------------------------------------------------------------+

void VirtualCopyPosition(int FirstPosition, int SecondPosition){

   //Procedure of copying of position to another cell of array

   Virt.Ticket[SecondPosition]=Virt.Ticket[FirstPosition];      // - Number of order

   Virt.OpenTime[SecondPosition]=Virt.OpenTime[FirstPosition];    // - Time of opening

   Virt.Type[SecondPosition]=Virt.Type[FirstPosition];        // - Type of trade

   Virt.Lots[SecondPosition]=Virt.Lots[FirstPosition];        // - Volume

   Virt.Symbol[SecondPosition]=Virt.Symbol[FirstPosition];      // - Symbol

   Virt.OpenPrice[SecondPosition]=Virt.OpenPrice[FirstPosition];   // - Price of opening

   Virt.StopLoss[SecondPosition]=Virt.StopLoss[FirstPosition];

   Virt.TakeProfit[SecondPosition]=Virt.TakeProfit[FirstPosition];

   Virt.CloseTime[SecondPosition]=Virt.CloseTime[FirstPosition];   // - Time of closing

   Virt.ClosePrice[SecondPosition]=Virt.ClosePrice[FirstPosition];  // - Price of closing

   Virt.Swap[SecondPosition]=Virt.Swap[FirstPosition];        // - Swap

   Virt.Profit[SecondPosition]=Virt.Profit[FirstPosition];      // - Profit in pipslots

   Virt.Comment[SecondPosition]=Virt.Comment[FirstPosition];     // - Comments

   Virt.MagicNumber[SecondPosition]=Virt.MagicNumber[FirstPosition]; // - Magic number

   Virt.Expiration[SecondPosition]=Virt.Expiration[FirstPosition];  // - Date of canceling pending order

   Virt.Status[SecondPosition]=Virt.Status[FirstPosition];      // - Status of order: 1 - open position/pending order; 0 - closed position/pending order

}//VirtualCopyPosition()

//+------------------------------------------------------------------+

int VirtualSend(string symbol, int cmd, double volume, double price,

 int slippage, double stoploss, double takeprofit, string comment="",

  int magic=0, datetime expiration=0, string filename="virtual.csv") {

   /* Main function that is used for opening positions and placing pending orders.

   Returns ticket number, which is assigned to order by the trade server, or -1 in case of failing.

   

   Parameters:

   symbol   -   Name of financial instrument a trade operation is performed by. 

   cmd   -   Trade operation. Can have any value of trade operations. 

   volume   -   Number of lots. 

   price   -   Open price. 

   slippage   -  Maximum acceptable deviation of price in points for market orders (buy or sell orders). 

   stoploss   -   Price of closing position at reaching a level of unprofitableness (0 if there is no such level). 

   takeprofit   -   Price of closing the position at reaching a level of profitability (0 if there is no such level). 

   comment   -   Text of comment to the order. The last part of comment can be changed by the trade server. 

   magic   -   Magic number of the order. Can be used as an identifier determined by user. 

   expiration   -   Expiration time of pending order. 

   filename   -  Name of the file of virtual trades from directory TerminalPath()+"\experts\files" */

   

   //---------------------------------------------

   //Block of checks:

   if(cmd==OP_BUY){

      //Open price of buying should be near Ask +- slippage

      if((price>MarketInfo(symbol,MODE_ASK)+slippage*MarketInfo(symbol,MODE_POINT))||

         (price<MarketInfo(symbol,MODE_ASK)-slippage*MarketInfo(symbol,MODE_POINT))){

         Err.Number = 1;

         Err.Description = "Open price of position is too far from market";

         return(-1);

      }

   }else if(cmd==OP_SELL){

      if((price>MarketInfo(symbol,MODE_BID)+slippage*MarketInfo(symbol,MODE_POINT))||

         (price<MarketInfo(symbol,MODE_BID)-slippage*MarketInfo(symbol,MODE_POINT))){

         Err.Number = 1;

         Err.Description = "Open price of position is too far from market";

         return(-1);

      }

   }else if(cmd==OP_BUYSTOP){

      if(price<=MarketInfo(symbol,MODE_ASK)+MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){

         Err.Number = 2;

         Err.Description = "Open price of pending order is too close to market";

         return(-1);

      }

   }else if(cmd==OP_SELLSTOP){

      if(price>=MarketInfo(symbol,MODE_BID)-MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){

         Err.Number = 2;

         Err.Description = "Open price of pending order is too close to market";

         return(-1);

      }

   }else if(cmd==OP_BUYLIMIT){

      if(price>=MarketInfo(symbol,MODE_ASK)-MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){

         Err.Number = 2;

         Err.Description = "Open price of pending order is too close to market";

         return(-1);

      }

   }else if(cmd==OP_SELLLIMIT){

      if(price<=MarketInfo(symbol,MODE_BID)+MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){

         Err.Number = 2;

         Err.Description = "Open price of pending order is too close to market";

         return(-1);

      }

   }

   

   if(stoploss!=0.0){

      if((cmd==OP_BUY) || (cmd==OP_BUYSTOP) || (cmd==OP_BUYLIMIT)){// Buy

         if(price-stoploss<=MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){ 

            Err.Number = 3;

            Err.Description = "StopLoss or TakeProfit level is too close to price";

            return(-1);

         }

      }else if((cmd==OP_SELL) || (cmd==OP_SELLSTOP) || (cmd==OP_SELLLIMIT)){// Sell

         if(stoploss-price<=MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){ 

            Err.Number = 3;

            Err.Description = "StopLoss or TakeProfit level is too close to price";

            return(-1);

         }

      }

   }

   if(takeprofit!=0.0){

      if((cmd==OP_BUY) || (cmd==OP_BUYSTOP) || (cmd==OP_BUYLIMIT)){// Buy

         if(takeprofit-price<=MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){

            Err.Number = 3;

            Err.Description = "StopLoss or TakeProfit level is too close to price";

            return(-1);

         }

      }else if((cmd==OP_SELL) || (cmd==OP_SELLSTOP) || (cmd==OP_SELLLIMIT)){// Sell

         if(price-takeprofit<=MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT)){

            Err.Number = 3;

            Err.Description = "StopLoss or TakeProfit level is too close to price";

            return(-1);

         }

      }      

   }

   

   if((volume<MarketInfo(symbol, MODE_MINLOT))||

      (volume>MarketInfo(symbol, MODE_MAXLOT))){

      Err.Number = 4;

      Err.Description = "Incorrect volume";

      return(-1);

   }

   

   if(expiration<=TimeCurrent() && expiration!=0){

      Err.Number = 5;

      Err.Description = "Incorrect expiration date";

      return(-1);

   }

   

   //End of block of checks

   //---------------------------------------------

   //Protection from array overflow

   int i, j, k;

   VirtualFileLoad(filename);

   

   if(Virt.Count>998){

      //Delete all canceled pending orders

      for(i=0; i<Virt.Count; i++){

         if((Virt.Type[i]>1) && (Virt.Status[i]==0)) {

            //All trades located below this pending order are moved one level up:

            for(j=i; j<Virt.Count-1; j++) {

               Virt.Ticket[j] = Virt.Ticket[j+1];

               Virt.OpenTime[j] = Virt.OpenTime[j+1];

               Virt.Type[j] = Virt.Type[j+1];

               Virt.Lots[j] = Virt.Lots[j+1];

               Virt.Symbol[j] = Virt.Symbol[j+1];

               Virt.OpenPrice[j] = Virt.OpenPrice[j+1];

               Virt.StopLoss[j] = Virt.StopLoss[j+1];

               Virt.TakeProfit[j] = Virt.TakeProfit[j+1];

               Virt.CloseTime[j] = Virt.CloseTime[j+1];

               Virt.ClosePrice[j] = Virt.ClosePrice[j+1];

               Virt.Swap[j] = Virt.Swap[j+1];

               Virt.Profit[j] = Virt.Profit[j+1];

               Virt.Comment[j] = Virt.Comment[j+1];

               Virt.MagicNumber[j] = Virt.MagicNumber[j+1];

               Virt.Expiration[j] = Virt.Expiration[j+1];

               Virt.Status[j] = Virt.Status[j+1];

            }//Next j

            Virt.Count--;

         }

      }//Next i

      //Searching for first closed trade

      for(i=0; i<Virt.Count; i++){

         if((Virt.Type[i]<2) && (Virt.Status[i]==0)) {

            break;

         }

      }//Next i

      if(i==Virt.Count){

         Err.Number = 402;

         Err.Description = "Number of open trades exceeded the maximum allowed level! You should remake the trading system.";

         return(-1);

      }else{

         //Searching for the second closed trade

         for(j=i+1; j<Virt.Count; j++) {

            if(Virt.Status[j]==0) {

               break;

            }

         }//Next j

         if(j==Virt.Count){

            Err.Number = 402;

            Err.Description = "Number of open trades exceeded the maximum allowed level! You should remake the trading system.";

            return(-1);

         }else{

            //Bring the result of 2 trades (1-st and 2-nd) in the first closed trade:

            Virt.Ticket[i] = Virt.Ticket[j];

            Virt.OpenTime[i] = Virt.OpenTime[j];

            Virt.Type[i] = -1;

            Virt.Lots[i] = Virt.Lots[i]+Virt.Lots[j];

            Virt.Symbol[i] = "";

            Virt.OpenPrice[i] = 0;

            Virt.StopLoss[i] = 0;

            Virt.TakeProfit[i] = 0;

            Virt.CloseTime[i] = Virt.CloseTime[j];

            Virt.ClosePrice[i] = 0;

            Virt.Swap[i] = Virt.Swap[i]+Virt.Swap[j];

            Virt.Profit[i] = Virt.Profit[i]+Virt.Profit[j];

            Virt.Comment[i] = "Archive";

            Virt.MagicNumber[i] = Virt.MagicNumber[j];

            Virt.Expiration[i] = Virt.Expiration[j];

            //All trades located below the second closed trade are moved one level upwards:

            for(k=j; k<Virt.Count-1; k++) {

               Virt.Ticket[k] = Virt.Ticket[k+1];

               Virt.OpenTime[k] = Virt.OpenTime[k+1];

               Virt.Type[k] = Virt.Type[k+1];

               Virt.Lots[k] = Virt.Lots[k+1];

               Virt.Symbol[k] = Virt.Symbol[k+1];

               Virt.OpenPrice[k] = Virt.OpenPrice[k+1];

               Virt.StopLoss[k] = Virt.StopLoss[k+1];

               Virt.TakeProfit[k] = Virt.TakeProfit[k+1];

               Virt.CloseTime[k] = Virt.CloseTime[k+1];

               Virt.ClosePrice[k] = Virt.ClosePrice[k+1];

               Virt.Swap[k] = Virt.Swap[k+1];

               Virt.Profit[k] = Virt.Profit[k+1];

               Virt.Comment[k] = Virt.Comment[k+1];

               Virt.MagicNumber[k] = Virt.MagicNumber[k+1];

               Virt.Expiration[k] = Virt.Expiration[k+1];

               Virt.Status[k] = Virt.Status[k+1];

            }//Next k

            Virt.Count--;

         }

      }

   }//End of protection from array overflow

   //---------------------------------------------

   //Adding new position into the array

   Virt.Count++;

   Virt.Ticket[Virt.Count-1] = VirtualHighTicket()+1;

   Virt.OpenTime[Virt.Count-1] = TimeCurrent();

   Virt.Type[Virt.Count-1] = cmd;

   Virt.Lots[Virt.Count-1] = volume;

   Virt.Symbol[Virt.Count-1] = symbol;

   Virt.OpenPrice[Virt.Count-1] = price;

   Virt.StopLoss[Virt.Count-1] = stoploss;

   Virt.TakeProfit[Virt.Count-1] = takeprofit;

   Virt.Comment[Virt.Count-1] = comment;

   Virt.MagicNumber[Virt.Count-1] = magic;

   Virt.Expiration[Virt.Count-1] = expiration;

   Virt.Status[Virt.Count-1] = 1;

   //---------------------------------------------

   //Save changes

   VirtualFileSave(filename);

   return(Virt.Ticket[Virt.Count-1]);

}//VirtualSend()

//+------------------------------------------------------------------+

int VirtualHighTicket(){

   //Searching for a young ticket

   int i, j;

   for(i=0; i<Virt.Count; i++){

      if(Virt.Ticket[i]>j){

         j=Virt.Ticket[i];

      }

   }//Next i

   return(j);

}//VirtualHighTicket()

//+------------------------------------------------------------------+

int VirtualFileLoad(string file) {

   //The function loads trades from a file to the array of trades

   //and returns the number of loaded lines.

   if(FastTest){

      if(IsTesting()) return(0);

   }

   int k, Count;

   string buffer[];

   ArrayResize(buffer,VirtBufferSize*1000);

   int handle=FileOpen(file,FILE_CSV|FILE_READ,';');

   if(handle<1) {

      Err.Number = 401;

      Err.Description = "File of trades is not found";

      return(-1);

   } else {

      Count=0;

      //Read the file

      while(!FileIsEnding(handle)){

         buffer[Count]=FileReadString(handle);

         Count++;

      }

      Count--;

      FileClose(handle);

      

      //Fill the array

      Virt.Count=0;

      k=VirtBufferSize;

      //Print("Loaded ",Count/VirtBufferSize-1," lines");

      while(Virt.Count<Count/VirtBufferSize-1) {

         Virt.Ticket[Virt.Count]= StrToInteger(buffer[k]);        // - Number of order

         Virt.OpenTime[Virt.Count] = StrToTime(buffer[k+1]);      // - Time of opening

         Virt.Type[Virt.Count] = StrToInteger(buffer[k+2]);       // - Type of trade

         Virt.Lots[Virt.Count] = StrToDouble(buffer[k+3]);        // - Volume

         Virt.Symbol[Virt.Count] = buffer[k+4];                   // - Symbol

         Virt.OpenPrice[Virt.Count] = StrToDouble(buffer[k+5]);   // - Price of opening

         Virt.StopLoss[Virt.Count] = StrToDouble(buffer[k+6]);

         Virt.TakeProfit[Virt.Count] = StrToDouble(buffer[k+7]);

         Virt.CloseTime[Virt.Count] = StrToTime(buffer[k+8]);     // - Time of closing

         Virt.ClosePrice[Virt.Count] = StrToDouble(buffer[k+9]);  // - Price of closing

         Virt.Swap[Virt.Count] = StrToDouble(buffer[k+10]);       // - Swap

         Virt.Profit[Virt.Count] = StrToDouble(buffer[k+11]);     // - Profit in pipslots

         Virt.Comment[Virt.Count] = buffer[k+12];                 // - Comments

         Virt.MagicNumber[Virt.Count] = StrToInteger(buffer[k+13]);//- Magic number

         Virt.Expiration[Virt.Count] = StrToTime(buffer[k+14]);   // - Date of canceling pending order

         Virt.Status[Virt.Count] = StrToInteger(buffer[k+15]);    // - State of order: 1 - open; 0 - closed

         k=k+VirtBufferSize;

         Virt.Count++;

      }

      return(Virt.Count);

   }

}//VirtualFileLoad()

//+------------------------------------------------------------------+

void VirtualFileSave(string file) {

//Procedure of saving the array of trades to a specified file.

   if(FastTest){

      if(IsTesting()) return(0);

   }

   int Count=0;

   int handle=FileOpen(file,FILE_CSV|FILE_WRITE,';');

   FileWrite(handle, "Order name","Open time","Type","Volume","Symbol","Open price","S/L","T/P",

   "Close time","Close price","Swap","Profit","Comment","Magic number","Expiration","State");

   //Saving to the file

   while(Count<Virt.Count) {

      //Probably the transformation of data is necessary

      FileWrite(handle,

      Virt.Ticket[Count],

      TimeToStr(Virt.OpenTime[Count]),

      Virt.Type[Count],

      Virt.Lots[Count],

      Virt.Symbol[Count],

      Virt.OpenPrice[Count],

      Virt.StopLoss[Count],

      Virt.TakeProfit[Count],

      TimeToStr(Virt.CloseTime[Count]),

      Virt.ClosePrice[Count],

      Virt.Swap[Count],

      Virt.Profit[Count],

      Virt.Comment[Count],

      Virt.MagicNumber[Count],

      TimeToStr(Virt.Expiration[Count]),

      Virt.Status[Count]

      );

      Count++;

   }

   FileClose(handle);   

   //Print("Written ",Count," lines");   

}//VirtualFileSave()

//+------------------------------------------------------------------+

bool VirtualExist(datetime TimeOpenCandle, int fMagic=0){

   VirtualFilter(VIRT_TRADES, -1, -1, Symbol(), fMagic);

   for(int i=0; i<Virt.Filter.Count; i++){

      if(Virt.OpenTime[Virt.Filter[i]]>=TimeOpenCandle){

         return(true);

      }

   }//Next i

   return(false);

}//VirtualExist()

//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

//+------------------------------------------------------------------+