/*
   Generated by EX4-TO-MQ4 decompiler V4.0.224.1 []
   Website: http://purebeam.biz
   E-mail : purebeam@gmail.com
*/
#property copyright "Copyright © 2010-2012 MT4i.com"
#property link      "http://www.mt4i.com/"

#import "MT4iQuickChannel.dll"
   int QC_StartSender(string a0);
   int QC_ReleaseSender(int a0);
   int QC_SendMessage(int a0, string a1, bool a2);
   int SetupTimedTicks(int a0, int a1);
   int RemoveTimedTicks(int a0);
   int TimeUTC();
   int CreateHelper2(int a0, int a1, int a2, int a3, int a4, int a5, string a6, int a7);
   int RemoveHelper(int a0);
#import "kernel32.dll"
   int LoadLibraryA(string a0);
   int FreeLibrary(int a0);
   int CreateFileA(string a0, int a1, int a2, int a3, int a4, int a5, int a6);
   int WriteFile(int a0, string a1, int a2, int& a3[], int a4);
   int SetFilePointer(int a0, int a1, int a2, int a3);
   int CloseHandle(int a0);
#import

extern string s_Server = "=== Server settings ===";
extern string Channel = "MT4iTradeCopy";
extern int HeartbeatPeriodSeconds = 10;
extern string s_OrdersToInclude = "=== Orders to include ===";
extern string IncludeSymbols = "";
extern string IncludeMagicNumbers = "";
extern string IncludeOrderComments = "";
extern bool IncludePendingOrders = FALSE;
extern string s_Lotsize = "=== Lot sizing and pricing ===";
extern double UseFixedLotSize = 0.0;
extern double LotSizeMultiplier = 1.0;
extern bool BroadcastAllOrdersAtMarket = FALSE;
extern bool HideSLandTP = FALSE;
extern double AddPipsToSL = 0.0;
extern double AddPipsToTP = 0.0;
extern string s_Broadcast = "=== Broadcast settings ===";
extern int RetryAttempts = 2;
extern string FuturesSymbol = "#";
extern string ForexSymbolSuffix = "";
extern string s_Warnings = "=== Warnings ===";
extern bool SendWarningsByEmail = TRUE;
extern string WarningEmailSubjectLine = "Signal Broadcaster warning";
extern string s_Advanced = "=== Advanced settings ===";
extern int TradeWatchFrequencyMS = 50;
extern int Alert_Pips=20;
int g_index_240 = 0;
int gia_244[1000];
int gi_248 = 0;
int gi_252 = -1;
bool gi_256 = FALSE;
int gi_260 = 0;
int gia_264[];
int g_datetime_268 = 0;
int gi_272 = 0;
int gi_276 = 0;
int gi_280 = 0;
bool gi_284 = TRUE;
int gi_288 = -9999;
int g_datetime_292 = 0;
int g_datetime_296 = 0;
int gia_300[1000];
int g_index_304 = 0;
string gsa_unused_308[];
bool gi_312 = FALSE;
double gd_316 = 0.0;
string gs_324 = "!";


double point;
int digits,Q;



int init() {

   
   if(Digits==5||Digits==3)Q=10;
   else Q=1;                            
   if(Digits<4)
   {
      point=0.01;
      digits=2;
   }
   else
   {
      point=0.0001;
      digits=4;
   }
   
   
   
   
   string ls_unused_0;
   string lsa_8[];
   Log("");
   Log("--------------------------------------------------------------------------------");
   Log(StringConcatenate("MT4i TradeCopy Sender", " v", DoubleToStr(1.38, 2), " starting up at ", TimeToStr(TimeLocal(), TIME_DATE|TIME_SECONDS), " (local time)"));
   if (!IsDllsAllowed()) {
      Log("The Allow DLL Imports setting is not turned on. The EA will not trade.");
      DisplayStatus("\"Allow DLL imports\" must be turned on", 1);
      ObjectsRedraw();
      return (0);
   }
   if (!DoesDllExist("MT4iQuickChannel.dll")) {
      Log("MT4iQuickChannel.dll is not installed");
      DisplayStatus("MT4iQuickChannel.dll cannot be found", 1);
      ObjectsRedraw();
      return (0);
   }
   if (Channel == "") {
      Log("The configuration is not valid (channel etc)");
      DisplayStatus("Invalid configuration", 1);
      ObjectsRedraw();
      return (0);
   }
   if (AccountProfit() != 0.0 && OrdersTotal() == 0) {
      Log("Floating P/L is non-zero but the broker reports no open orders");
      DisplayStatus("Bad order data from the broker", 1);
      ObjectsRedraw();
      return (0);
   }
   StringSplit(Channel, ",", lsa_8);
   g_index_240 = 0;
   for (int l_index_12 = 0; l_index_12 < ArraySize(lsa_8); l_index_12++) {
      if (lsa_8[l_index_12] != "") {
         gia_244[g_index_240] = QC_StartSender(lsa_8[l_index_12]);
         if (gia_244[g_index_240] == 0) {
            Log("Failed to initialise QuickChannel");
            DisplayStatus("Failed to initialise QuickChannel", 1);
            ObjectsRedraw();
            return (0);
         }
         g_index_240++;
      }
   }
   Log(StringConcatenate("Server channel: ", Channel));
   Log(StringConcatenate("Included symbols: ", iif_s(IncludeSymbols == "", "(all)", IncludeSymbols)));
   Log(StringConcatenate("Included magic numbers: ", iif_s(IncludeMagicNumbers == "", "(all)", IncludeMagicNumbers)));
   gi_256 = TRUE;
   DisplayStatus(StringConcatenate("Channel: ", Channel), 0);
   gi_276 = TimeLocal() - (HeartbeatPeriodSeconds - 5);
   ObjectsRedraw();
   LoadAdvertising();
   gi_284 = TRUE;
   return (0);
}

int deinit() {
   Log(StringConcatenate("MT4i TradeCopy Sender", " being unloaded for reason #", UninitializeReason()));
   DeleteObjects();
   for (int l_index_0 = 0; l_index_0 < g_index_240; l_index_0++) {
      QC_ReleaseSender(gia_244[l_index_0]);
      gia_244[l_index_0] = 0;
   }
   g_index_240 = 0;
   RemoveTimedTicks(gi_248);
   gi_248 = 0;
   RemoveAdvertising();
   return (0);
}

int start() {
   if (!gi_256) return (0);
   Comment(StringConcatenate("Last chart tick: ", TimeToStr(TimeLocal(), TIME_DATE|TIME_SECONDS), "  (local time)"));
   if (gi_248 == 0) gi_248 = SetupTimedTicks(WindowHandle(Symbol(), Period()), MathMax(TradeWatchFrequencyMS, 10));
   LoadAdvertising();
   g_index_304 = 0;
   if (!IsConnected()) {
      DisplayStatus("MT4 has lost its broker connection", 1);
      switch (gi_252) {
      case -1:
         Log("No broker connection on startup");
         break;
      case 1:
         Log("Broker connection LOST");
      }
      gi_252 = FALSE;
      return (0);
   }
   if (AccountProfit() != 0.0 && OrdersTotal() == 0) {
      Log("Floating P/L is non-zero but the broker reports no open orders");
      DisplayStatus("Bad order data from the broker", 1);
      ObjectsRedraw();
      return (0);
   }
   if (AccountNumber() == 0) {
      Log("MT4 reports a connection to the broker, but the account number is zero");
      DisplayStatus("Broker reports a/c as 0", 1);
      ObjectsRedraw();
      return (0);
   }
   if (AccountBalance() == 0.0) {
      Log("Broker reports the account balance as zero");
      DisplayStatus("Broker reports account balance as 0", 1);
      ObjectsRedraw();
      return (0);
   }
   switch (gi_252) {
   case -1:
      Log("Connected to broker");
      DisplayStatus(StringConcatenate("Channel: ", Channel), 0);
      break;
   case 0:
      Log("Broker connection re-established");
      DisplayStatus(StringConcatenate("Channel: ", Channel), 0);
   }
   gi_252 = TRUE;
   GetBrokerUTCAdjustment();
   GetAccountEquityInDollars();
   if (gi_284) BuildOpenOrderList();
   ScanForNewOpenOrders();
   ScanForClosedOrders();
   DoHeartbeat();
   gi_284 = FALSE;
   return (0);
}

bool SendMessageToServer(string as_0, int ai_unused_8)
{
   double price;
   if (last_type()==0)
   {
      price=last_open(OP_BUY)-Bid;
   }
   else
   {
      price=last_open(OP_SELL)-Bid;
   }
   Comment("   price = ",price/point);
   
   if (MathAbs(price/point)>=Alert_Pips)
   {
   
   int li_16;
   for (int l_index_12 = 0; l_index_12 < g_index_240; l_index_12++) 
   {
      li_16 = QC_SendMessage(gia_244[l_index_12], as_0, 2);
      if (li_16 == 0) 
      {
         Alert("QuickChannel message failed");
         return (FALSE);
      }
   }
   }
   return (TRUE);
  
}

void LoadAdvertising() {
   if (!gi_312) {
      if (WindowHandle(Symbol(), Period()) != 0)
         if (AccountServer() != "") gi_312 = CreateHelper2(1000, WindowHandle(Symbol(), Period()), 20, 120, 0, 30, AccountServer(), 2);
   }
}

void RemoveAdvertising() {
   if (gi_312) {
      RemoveHelper(WindowHandle(Symbol(), Period()));
      gi_312 = FALSE;
   }
}

void ScanForClosedOrders() {
   for (int l_index_0 = 0; l_index_0 < gi_260; l_index_0++) {
      if (OrderSelect(gia_264[l_index_0], SELECT_BY_TICKET)) {
         if (OrderCloseTime() > 0) {
            DoClosedOrderAlert();
            for (int li_4 = l_index_0 + 1; li_4 < gi_260; li_4++) gia_264[li_4 - 1] = gia_264[li_4];
            gi_260--;
            l_index_0--;
         }
      }
   }
}

int FindTicketOnOpenOrderList(int ai_0) {
   bool li_ret_4 = FALSE;
   for (int l_index_8 = 0; l_index_8 < gi_260; l_index_8++) {
      if (gia_264[l_index_8] == ai_0) {
         l_index_8 = gi_260;
         li_ret_4 = TRUE;
      }
   }
   return (li_ret_4);
}

void ScanForNewOpenOrders() {
   bool li_0;
   for (int l_pos_4 = 0; l_pos_4 < OrdersTotal(); l_pos_4++) {
      if (OrderSelect(l_pos_4, SELECT_BY_POS)) {
         switch (OrderType()) {
         case OP_BUY:
         case OP_SELL:
            li_0 = FindTicketOnOpenOrderList(OrderTicket());
            if (!li_0) {
               gi_260++;
               ArrayResize(gia_264, gi_260);
               gia_264[gi_260 - 1] = OrderTicket();
               DoOpenOrderAlert();
            }
            break;
         case OP_BUYLIMIT:
         case OP_SELLLIMIT:
         case OP_BUYSTOP:
         case OP_SELLSTOP:
            if (IncludePendingOrders) {
               li_0 = FindTicketOnOpenOrderList(OrderTicket());
               if (!li_0) {
                  gi_260++;
                  ArrayResize(gia_264, gi_260);
                  gia_264[gi_260 - 1] = OrderTicket();
                  DoOpenOrderAlert();
               }
            }
         }
      }
   }
}

void BuildOpenOrderList() {
   Log("Building list of orders which are already open (no alerts will be sent to subscribers)...");
   gi_260 = 0;
   for (int l_pos_0 = 0; l_pos_0 < OrdersTotal(); l_pos_0++) {
      if (OrderSelect(l_pos_0, SELECT_BY_POS)) {
         switch (OrderType()) {
         case OP_BUY:
         case OP_SELL:
            gi_260++;
            ArrayResize(gia_264, gi_260);
            gia_264[gi_260 - 1] = OrderTicket();
            Log(StringConcatenate("   Found ticket #", OrderTicket()));
            break;
         case OP_BUYLIMIT:
         case OP_SELLLIMIT:
         case OP_BUYSTOP:
         case OP_SELLSTOP:
            if (IncludePendingOrders) {
               gi_260++;
               ArrayResize(gia_264, gi_260);
               gia_264[gi_260 - 1] = OrderTicket();
               Log(StringConcatenate("   Found ticket #", OrderTicket()));
            }
         }
      }
   }
   if (!gi_260) Log("...No open orders");
}

void DoHeartbeat() {
   string l_str_concat_0;
   int li_12;
   string l_str_concat_16;
   if (TimeLocal() - gi_276 >= CollarHeartbeatPeriod(HeartbeatPeriodSeconds)) {
      if (AccountProfit() != 0.0 && OrdersTotal() == 0) {
         Log("Floating P/L is non-zero but the broker reports no open orders");
         DisplayStatus("Bad order data from the broker", 1);
         ObjectsRedraw();
         gi_276 = TimeLocal();
      } else {
         if (AccountNumber() <= 0) {
            Log("Broker reports account number as zero");
            DisplayStatus("Bad a/c number from the broker", 1);
            ObjectsRedraw();
            gi_276 = TimeLocal();
         } else {
            l_str_concat_0 = "";
            for (int l_pos_8 = 0; l_pos_8 < OrdersTotal(); l_pos_8++) {
               if (OrderSelect(l_pos_8, SELECT_BY_POS)) {
                  if (MeetsOrderCriteria(0)) {
                     if (l_str_concat_0 != "") l_str_concat_0 = StringConcatenate(l_str_concat_0, "|");
                     l_str_concat_0 = StringConcatenate(l_str_concat_0, GetOrderDetailsForMessage("^", 1));
                  }
               }
            }
            li_12 = ConvertBrokerTimeToUTC(TimeCurrent());
            l_str_concat_16 = StringConcatenate(li_12, ",HEARTBEAT,", l_str_concat_0);
            if (SendMessageToServer(l_str_concat_16, 0)) {
               gi_272 = li_12;
               DisplayStatus(StringConcatenate("Channel: ", Channel), 0);
            } else {
               Log("Failed to send heartbeat message");
               DisplayStatus(GetEAQueueErrorMessage(gi_280), 1);
               ObjectsRedraw();
            }
            gi_276 = TimeLocal();
         }
      }
   }
}

int CollarHeartbeatPeriod(int ai_0) {
   if (ai_0 < 5) return (5);
   return (ai_0);
}

int DoOpenOrderAlert() {
   string l_str_concat_0;
   string ls_8;
   int l_ticket_16;
   double ld_20;
   int l_ticket_28;
   string l_str_concat_32;
   Log(StringConcatenate("Found new open order with ticket #", OrderTicket()));
   if (MeetsOrderCriteria(1)) {
      l_str_concat_0 = "";
      ls_8 = "";
      l_ticket_16 = GetOldTicketFromPartialClose();
      if (l_ticket_16 > 0) {
         ls_8 = GetOrderDetailsForMessage();
         ld_20 = 0;
         l_ticket_28 = OrderTicket();
         if (OrderSelect(l_ticket_16, SELECT_BY_TICKET)) {
            ld_20 = ChangeLotSizeToNominal(OrderSymbol(), OrderLots());
            OrderSelect(l_ticket_28, SELECT_BY_TICKET);
         } else {
            Log(StringConcatenate("Unable to get original size of partially closed order: ticket #", l_ticket_16, " can no longer be selected"));
            ld_20 = 0;
         }
         l_str_concat_0 = ConvertBrokerTimeToUTC(TimeCurrent());
         l_str_concat_0 = StringConcatenate(l_str_concat_0, ",PARTIALCLOSE");
         l_str_concat_0 = StringConcatenate(l_str_concat_0, ",", l_ticket_16);
         l_str_concat_0 = StringConcatenate(l_str_concat_0, ",", ls_8);
         l_str_concat_0 = StringConcatenate(l_str_concat_0, ",", DoubleToStr(ld_20, 6));
         gia_300[g_index_304] = l_ticket_16;
         g_index_304++;
      } else {
         ls_8 = GetOrderDetailsForMessage();
         if (ls_8 == "") return;
         l_str_concat_0 = StringConcatenate(ConvertBrokerTimeToUTC(TimeCurrent()), ",", ls_8);
      }
      if (SendMessageToServer(l_str_concat_0, 1)) {
         Log(StringConcatenate("Successfully sent open message for ticket #", OrderTicket()));
         if (l_ticket_16 != 0) Log(StringConcatenate("   Partial close from #", l_ticket_16));
         Log(StringConcatenate("   Type: ", GetOrderTypeCaption(OrderType())));
         Log(StringConcatenate("   Symbol: ", OrderSymbol()));
         Log(StringConcatenate("   Lots: ", FLOTS(OrderLots())));
         Log(StringConcatenate("   Volume: ", FLOTS(OrderLots() * MarketInfo(OrderSymbol(), MODE_LOTSIZE)), " at ", FLOTS(MarketInfo(OrderSymbol(), MODE_LOTSIZE)), " nominal per lot"));
         Log(StringConcatenate("   Open price: ", FP(OrderSymbol(), OrderOpenPrice())));
         Log(StringConcatenate("   S/L: ", FP(OrderSymbol(), OrderStopLoss())));
         Log(StringConcatenate("   T/P: ", FP(OrderSymbol(), OrderTakeProfit())));
      } else {
         Log(StringConcatenate("FAILED to send trade-open message for ticket #", OrderTicket()));
         l_str_concat_32 = StringConcatenate("MT4i TradeCopy Sender", " failed to broadcast trade-open message for ticket #", OrderTicket());
         l_str_concat_32 = StringConcatenate(l_str_concat_32, "\r\n\r\n", "Error from hub was #", gi_280, ": ", GetEAQueueErrorMessage(gi_280));
         l_str_concat_32 = StringConcatenate(l_str_concat_32, "\r\n\r\n", "This trade WILL be included in subsequent heartbeat messages. Subscribers may still trade it if CreateOrdersFromHeartbeat is turned on, and the heartbeat message arrives within RequireSignalsWithinSeconds.");
         DisplayWarning(l_str_concat_32);
      }
      g_datetime_268 = TimeCurrent();
   }
   return (0);
}

string GetTypeOfOrder(int ai_0) {
   string ls_ret_4;
   switch (ai_0) {
   case 0:
      ls_ret_4 = "BUY";
      break;
   case 1:
      ls_ret_4 = "SELL";
      break;
   case 2:
      ls_ret_4 = "BUYLIMIT";
      break;
   case 4:
      ls_ret_4 = "BUYSTOP";
      break;
   case 3:
      ls_ret_4 = "SELLLIMIT";
      break;
   case 5:
      ls_ret_4 = "SELLSTOP";
   }
   return (ls_ret_4);
}

string GetOrderTypeCaption(int ai_0) {
   string ls_ret_4;
   switch (ai_0) {
   case 0:
      ls_ret_4 = "Buy";
      break;
   case 1:
      ls_ret_4 = "Sell";
      break;
   case 2:
      ls_ret_4 = "Buy-limit";
      break;
   case 4:
      ls_ret_4 = "Buy-stop";
      break;
   case 3:
      ls_ret_4 = "Sell-limit";
      break;
   case 5:
      ls_ret_4 = "Sell-stop";
   }
   return (ls_ret_4);
}

double ChangeLotSizeToNominal(string a_symbol_0, double ad_8) {
   double ld_ret_16 = NormalizeDouble(ad_8 * LotSizeMultiplier, 2);
   ld_ret_16 *= MarketInfo(a_symbol_0, MODE_LOTSIZE);
   return (ld_ret_16);
}

string GetOrderDetailsForMessage(string as_0 = ",", bool ai_8 = FALSE) {
   int l_ticket_36;
   double ld_12 = OrderLots();
   if (UseFixedLotSize > 0.0) ld_12 = UseFixedLotSize;
   else ld_12 = NormalizeDouble(ld_12 * LotSizeMultiplier, 2);
   ld_12 *= MarketInfo(OrderSymbol(), MODE_LOTSIZE);
   int l_datetime_20 = OrderOpenTime();
   double l_ord_open_price_24 = OrderOpenPrice();
   int l_ticket_32 = GetOldTicketFromPartialClose();
   if (l_ticket_32 > 0) {
      if (!ai_8) Log(StringConcatenate("Current order is a partial close of ticket #", l_ticket_32, ""));
      l_ticket_36 = OrderTicket();
      l_ord_open_price_24 = 0;
      if (OrderSelect(l_ticket_32, SELECT_BY_TICKET)) {
         l_datetime_20 = OrderCloseTime();
         if (OrderSelect(l_ticket_36, SELECT_BY_TICKET) == 0) {
            DisplayWarning("Unable to restore current ticket context when processing partial close");
            return ("");
         }
      } else {
         DisplayWarning(StringConcatenate("Unable to get details of partially-closed ticket #", l_ticket_32, " for current ticket #", l_ticket_36));
         return ("");
      }
   }
   string ls_40 = AdjustSymbol(OrderSymbol());
   string l_str_concat_48 = GetTypeOfOrder(OrderType());
   l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, OrderTicket());
   l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, ls_40);
   if (BroadcastAllOrdersAtMarket) l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, "0");
   else l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, FP(OrderSymbol(), l_ord_open_price_24));
   if (HideSLandTP) {
      l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, "0");
      l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, "0");
   } else {
      l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, FP(OrderSymbol(), AdjustStopLoss(OrderStopLoss(), OrderType(), OrderSymbol())));
      l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, FP(OrderSymbol(), AdjustTakeProfit(OrderTakeProfit(), OrderType(), OrderSymbol())));
   }
   l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, ld_12);
   l_str_concat_48 = StringConcatenate(l_str_concat_48, as_0, ConvertBrokerTimeToUTC(l_datetime_20));
   return (StringConcatenate(l_str_concat_48, as_0, GetAccountEquityInDollars()));
}

double AdjustStopLoss(double ad_0, int ai_8, string as_12) {
   if (ad_0 <= 0.0 || AddPipsToSL <= 0.0) return (ad_0);
   switch (ai_8) {
   case 0:
   case 2:
   case 4:
      return (ad_0 - AdjustUserPipsToPriceDiff(AddPipsToSL, as_12));
   case 1:
   case 3:
   case 5:
      return (ad_0 + AdjustUserPipsToPriceDiff(AddPipsToSL, as_12));
   }
   return (ad_0);
}

double AdjustTakeProfit(double ad_0, int ai_8, string as_12) {
   if (ad_0 <= 0.0 || AddPipsToTP <= 0.0) return (ad_0);
   switch (ai_8) {
   case 0:
   case 2:
   case 4:
      return (ad_0 + AdjustUserPipsToPriceDiff(AddPipsToSL, as_12));
   case 1:
   case 3:
   case 5:
      return (ad_0 - AdjustUserPipsToPriceDiff(AddPipsToSL, as_12));
   }
   return (ad_0);
}

double AdjustUserPipsToPriceDiff(double a_pips_0, string a_symbol_8) {
   int l_digits_16 = MarketInfo(a_symbol_8, MODE_DIGITS);
   double l_point_20 = MarketInfo(a_symbol_8, MODE_POINT);
   if ((l_digits_16 == 3 && l_point_20 == 0.001) || (l_digits_16 == 5 && l_point_20 == 0.00001)) return (10.0 * (a_pips_0 * l_point_20));
   return (a_pips_0 * l_point_20);
}

string AdjustSymbol(string as_0) {
   if (StringFind(as_0, FuturesSymbol) == 0 && FuturesSymbol != "") return (StringConcatenate("/", StringSubstr(as_0, StringLen(FuturesSymbol))));
   if (StringLen(as_0) > 6) return (as_0);
   return (StringSubstr(as_0, 0, 6));
}

int GetOldTicketFromPartialClose() {
   int l_str2int_12;
   string ls_0 = OrderComment();
   int li_8 = StringFind(ls_0, "from #");
   if (li_8 >= 0) {
      l_str2int_12 = StrToInteger(StringSubstr(ls_0, li_8 + 6));
      return (l_str2int_12);
   }
   return (0);
}

int DoClosedOrderAlert() {
   string l_str_concat_4;
   string l_str_concat_12;
   Log(StringConcatenate("Found closed order with ticket #", OrderTicket()));
   if (MeetsOrderCriteria(1)) {
      for (int l_index_0 = 0; l_index_0 < g_index_304; l_index_0++) {
         if (OrderTicket() == gia_300[l_index_0]) {
            Log("Not sending close message because this is a partial close which is handled separately via a new-position message");
            return;
         }
      }
      if (StringFind(OrderComment(), "to #") >= 0) {
         if (TimeCurrent() - OrderCloseTime() < 3) {
            Log(StringConcatenate("Ignoring ticket ", OrderTicket(), " because it appears to be a partial close. Order will be swept up in next heartbeat."));
            Sleep(500);
            return (0);
         }
      }
      l_str_concat_4 = ConvertBrokerTimeToUTC(TimeCurrent());
      l_str_concat_4 = StringConcatenate(l_str_concat_4, ",CLOSE");
      l_str_concat_4 = StringConcatenate(l_str_concat_4, ",", OrderTicket());
      l_str_concat_4 = StringConcatenate(l_str_concat_4, ",", GetOrderTypeCaption(OrderType()));
      l_str_concat_4 = StringConcatenate(l_str_concat_4, ",", FP(OrderSymbol(), OrderOpenPrice()));
      l_str_concat_4 = StringConcatenate(l_str_concat_4, ",", FP(OrderSymbol(), OrderClosePrice()));
      l_str_concat_4 = StringConcatenate(l_str_concat_4, ",", OrderTicket());
      if (SendMessageToServer(l_str_concat_4, 1)) Log(StringConcatenate("Successfully sent closure message for ticket #", OrderTicket()));
      else {
         Log(StringConcatenate("FAILED to send closure message for ticket #", OrderTicket()));
         l_str_concat_12 = StringConcatenate("MT4i TradeCopy Sender", " failed to broadcast trade-close message for ticket #", OrderTicket());
         l_str_concat_12 = StringConcatenate(l_str_concat_12, "\r\n\r\n", "Error from hub was #", gi_280, ": ", GetEAQueueErrorMessage(gi_280));
         l_str_concat_12 = StringConcatenate(l_str_concat_12, "\r\n\r\n", "Subscribers will close this trade at the next successful heartbeat message.");
         DisplayWarning(l_str_concat_12);
      }
      g_datetime_268 = TimeCurrent();
   }
   return (0);
}

void DisplayWarning(string as_0) {
   Alert(as_0);
   if (SendWarningsByEmail) SendMail(WarningEmailSubjectLine, as_0);
}

string GetEAQueueErrorMessage(int ai_0) {
   switch (ai_0) {
   case 2:
      return ("Message is too long");
   case 3:
      return ("Message contains non-textual characters");
   case 30:
      return ("Invalid hub configuration");
   case 31:
      return ("Unable to initialise Winsock services");
   case 32:
      return ("Unable to create a TCP/IP socket");
   case 33:
      return ("Unable to connect to the hub - check parameters");
   case 34:
      return ("Timeout on hub connection");
   case 35:
      return ("Unable to send data to the hub");
   case 36:
      return ("Timeout on hub response");
   case 37:
      return ("No response from hub - check username/password");
   case 38:
      return ("Message declined by hub - check username/password");
   case 39:
      return ("Corrupt data returned from the hub");
   }
   return (StringConcatenate("Unknown error #", ai_0));
}

bool MeetsOrderCriteria(bool ai_0) {
   switch (OrderType()) {
   case OP_BUY: break;
   case OP_SELL: break;
   case OP_BUYLIMIT:
   case OP_SELLLIMIT:
   case OP_BUYSTOP:
   case OP_SELLSTOP:
      if (!IncludePendingOrders) {
         if (ai_0) Log("   Broadcast of pending orders is not switched on");
         return (FALSE);
      }
      break;
   default:
      if (ai_0) Log(StringConcatenate("   Invalid order type on ticket #", OrderTicket()));
      return (FALSE);
   }
   if (!IsSymbolPermitted(OrderSymbol())) {
      if (ai_0) Log(StringConcatenate("   Ticket #", OrderTicket(), " will not be broadcast because ", OrderSymbol(), " is not on the list of symbols to include"));
      return (FALSE);
   }
   if (!IsMagicNumberPermitted(OrderMagicNumber())) {
      if (ai_0) Log(StringConcatenate("   Ticket #", OrderTicket(), " will not be broadcast because its magic number ", OrderMagicNumber(), " is not on the list to include"));
      return (FALSE);
   }
   if (!IsOrderCommentPermitted(OrderComment())) {
      if (ai_0) Log(StringConcatenate("   Ticket #", OrderTicket(), " will not be broadcast because its comment is not on the list to include"));
      return (FALSE);
   }
   return (TRUE);
}

bool IsOrderCommentPermitted(string as_0) {
   string lsa_8[];
   string ls_16;
   if (IncludeOrderComments == "") return (TRUE);
   StringSplit(IncludeOrderComments, ",", lsa_8);
   for (int l_index_12 = 0; l_index_12 < ArraySize(lsa_8); l_index_12++) {
      ls_16 = lsa_8[l_index_12];
      if (ls_16 != "") {
         if (StringSubstr(ls_16, 0, 1) == "\"") {
            ls_16 = StringSubstr(ls_16, 1, StringLen(ls_16) - 2);
            if (as_0 != ls_16) continue;
            return (TRUE);
         }
         if (StringFind(as_0, ls_16) >= 0) return (TRUE);
      }
   }
   return (FALSE);
}

bool IsMagicNumberPermitted(int ai_0) {
   string lsa_4[];
   if (IncludeMagicNumbers == "") return (TRUE);
   StringSplit(IncludeMagicNumbers, ",", lsa_4);
   for (int l_index_8 = 0; l_index_8 < ArraySize(lsa_4); l_index_8++)
      if (StrToInteger(lsa_4[l_index_8]) == ai_0) return (TRUE);
   return (FALSE);
}

bool IsSymbolPermitted(string as_0) {
   string lsa_8[];
   if (IncludeSymbols == "") return (TRUE);
   StringSplit(IncludeSymbols, ",", lsa_8);
   for (int l_index_12 = 0; l_index_12 < ArraySize(lsa_8); l_index_12++)
      if (lsa_8[l_index_12] == as_0) return (TRUE);
   return (FALSE);
}

void GetBrokerUTCAdjustment() {
   if (gi_288 == -9999 || MathFloor(TimeLocal() / 3600) != MathFloor(g_datetime_292 / 3600)) {
      if (SetBrokerTimeAdjustment()) {
         Log(StringConcatenate("Broker assessed as being ", gi_288, " seconds ahead of UTC"));
         Log(StringConcatenate("   Local clock gives UTC as ", TimeToStr(TimeUTC(), TIME_DATE|TIME_MINUTES), " versus broker time of ", TimeToStr(TimeCurrent(), TIME_DATE|TIME_MINUTES)));
      }
   } else {
      if (TimeCurrent() == g_datetime_296)
         if (TimeLocal() - g_datetime_292 > 10) SetBrokerTimeAdjustment();
   }
}

bool SetBrokerTimeAdjustment() {
   int li_0 = gi_288;
   int li_4 = TimeUTC();
   int l_datetime_8 = TimeCurrent();
   double ld_12 = l_datetime_8 - li_4;
   ld_12 = 1800.0 * MathRound(ld_12 / 1800.0);
   gi_288 = ld_12;
   g_datetime_292 = TimeLocal();
   g_datetime_296 = l_datetime_8;
   if (gi_288 != li_0) return (TRUE);
   return (FALSE);
}

int ConvertBrokerTimeToUTC(int ai_0) {
   if (gi_288 == -9999) return (0);
   return (ai_0 - gi_288);
}

bool DoesDllExist(string as_0) {
   int li_8 = LoadLibraryA(StringConcatenate(TerminalPath(), "\\experts\\libraries\\", as_0));
   if (li_8 == 0) {
      li_8 = LoadLibraryA(as_0);
      if (li_8 == 0) return (FALSE);
      FreeLibrary(li_8);
      return (TRUE);
   }
   FreeLibrary(li_8);
   return (TRUE);
}

void DeleteObjects() {
   for (int li_0 = 1; li_0 < 10; li_0++) ObjectDelete("EASender Status " + li_0);
}

void DisplayStatus(string a_text_0, bool ai_8 = FALSE) {
   int l_y_12 = 30;
   string l_name_16 = "EASender Status 1";
   string l_name_24 = "EASender Status 2";
   string l_name_32 = "EASender Status 3";
   string l_name_40 = "EASender Status 4";
   if (ObjectFind(l_name_16) == -1) {
      ObjectCreate(l_name_16, OBJ_LABEL, 0, 0, 0);
      ObjectSet(l_name_16, OBJPROP_XDISTANCE, 20);
      ObjectSet(l_name_16, OBJPROP_YDISTANCE, l_y_12);
      ObjectSet(l_name_16, OBJPROP_CORNER, 0);
   }
   if (ObjectFind(l_name_24) == -1) {
      ObjectCreate(l_name_24, OBJ_LABEL, 0, 0, 0);
      ObjectSet(l_name_24, OBJPROP_XDISTANCE, 20);
      ObjectSet(l_name_24, OBJPROP_YDISTANCE, l_y_12 + 5);
      ObjectSet(l_name_24, OBJPROP_CORNER, 0);
   }
   if (ObjectFind(l_name_32) == -1) {
      ObjectCreate(l_name_32, OBJ_LABEL, 0, 0, 0);
      ObjectSet(l_name_32, OBJPROP_XDISTANCE, 20);
      ObjectSet(l_name_32, OBJPROP_YDISTANCE, l_y_12 + 25);
      ObjectSet(l_name_32, OBJPROP_CORNER, 0);
   }
   if (ObjectFind(l_name_40) == -1) {
      ObjectCreate(l_name_40, OBJ_LABEL, 0, 0, 0);
      ObjectSet(l_name_40, OBJPROP_XDISTANCE, 20);
      ObjectSet(l_name_40, OBJPROP_YDISTANCE, l_y_12 + 45);
      ObjectSet(l_name_40, OBJPROP_CORNER, 0);
   }
   ObjectSetText(l_name_16, StringConcatenate("MT4i TradeCopy Sender", " v" + DoubleToStr(1.38, 2)), 10, "Arial", DodgerBlue);
   ObjectSetText(l_name_24, "_____________________________", 10, "Arial", DodgerBlue);
   if (ai_8) ObjectSetText(l_name_32, a_text_0, 10, "Arial", Red);
   else ObjectSetText(l_name_32, a_text_0, 10, "Arial", DodgerBlue);
   string l_str_concat_48 = "";
   if (gi_272 == 0) l_str_concat_48 = "(none)";
   else l_str_concat_48 = StringConcatenate(TimeToStr(gi_272, TIME_DATE|TIME_SECONDS), " UTC");
   ObjectSetText(l_name_40, StringConcatenate("Heartbeat sent: ", l_str_concat_48), 10, "Arial", DodgerBlue);
}

string iif_s(bool ai_0, string as_4, string as_12) {
   if (ai_0) return (as_4);
   return (as_12);
}

string StringReplace(string as_0, string as_8, string as_16) {
   string ls_ret_24 = as_0;
   int l_str_len_32 = StringLen(as_8);
   int l_str_len_36 = StringLen(as_16);
   int li_40 = 0;
   for (int li_44 = StringFind(ls_ret_24, as_8, li_40); li_44 >= 0; li_44 = StringFind(ls_ret_24, as_8, li_40)) {
      if (li_44 == 0) ls_ret_24 = StringConcatenate(as_16, StringSubstr(ls_ret_24, li_44 + l_str_len_32));
      else ls_ret_24 = StringConcatenate(StringSubstr(ls_ret_24, 0, li_44), as_16, StringSubstr(ls_ret_24, li_44 + l_str_len_32));
      li_40 = li_44 + l_str_len_36;
   }
   return (ls_ret_24);
}

void StringSplit(string as_0, string as_8, string &asa_16[]) {
   int li_24;
   int li_28;
   ArrayResize(asa_16, 0);
   int l_str_len_20 = StringLen(as_8);
   while (as_0 != "") {
      li_28 = StringFind(as_0, as_8);
      if (li_28 == -1) {
         li_24 = ArraySize(asa_16) + 1;
         ArrayResize(asa_16, li_24);
         asa_16[li_24 - 1] = as_0;
         as_0 = "";
      } else {
         li_24 = ArraySize(asa_16) + 1;
         ArrayResize(asa_16, li_24);
         asa_16[li_24 - 1] = StringSubstr(as_0, 0, li_28);
         as_0 = StringSubstr(as_0, li_28 + l_str_len_20);
         if (as_0 == "") {
            ArrayResize(asa_16, li_24 + 1);
            asa_16[li_24] = "";
         }
      }
   }
}

string FP(string a_symbol_0, double ad_8) {
   return (DoubleToStr(ad_8, MarketInfo(a_symbol_0, MODE_DIGITS)));
}

string FLOTS(double ad_0) {
   return (DoubleToStr(ad_0, 2));
}

void Log(string as_0) {
   string l_str_concat_8;
   int li_16;
   string l_str_concat_20;
   int lia_28[1];
   Print(as_0);
   if (IsDllsAllowed()) {
      l_str_concat_8 = StringConcatenate(TerminalPath(), "\\experts\\files\\", "MT4i TradeCopy Sender", " ", Channel, " ", StringReplace(TimeToStr(TimeLocal(), TIME_DATE), ".", "-"), ".txt");
      li_16 = CreateFileA(l_str_concat_8, D'10.01.2004 16:37:04', 3, 0, 4, 0, 0);
      if (li_16 == -1) Alert("Unable to open log file");
      else {
         SetFilePointer(li_16, 0, 0, 2);
         l_str_concat_20 = "";
         l_str_concat_20 = StringConcatenate(l_str_concat_20, TimeToStr(TimeLocal(), TIME_DATE|TIME_SECONDS), "     ");
         l_str_concat_20 = StringConcatenate(l_str_concat_20, as_0, "\r\n");
         WriteFile(li_16, l_str_concat_20, StringLen(l_str_concat_20), lia_28, 0);
         CloseHandle(li_16);
      }
   }
}

double GetAccountEquityInDollars() {
   double l_bid_16;
   double ld_ret_0 = AccountEquity();
   string ls_8 = AccountCurrency();
   if (ls_8 != "USD") {
      if (gs_324 == "!") {
         if (ForexSymbolSuffix != "") gs_324 = ForexSymbolSuffix;
         else {
            ForexSymbolSuffix = GetAutoSymbolSuffix();
            Log(StringConcatenate("Auto-detected suffix for forex symbols as \'", ForexSymbolSuffix, "\'"));
            gs_324 = ForexSymbolSuffix;
         }
      }
      l_bid_16 = MarketInfo(ls_8 + "USD" + gs_324, MODE_BID);
      if (l_bid_16 > 0.0) ld_ret_0 *= l_bid_16;
      else {
         l_bid_16 = MarketInfo("USD" + ls_8 + gs_324, MODE_BID);
         if (l_bid_16 > 0.0) ld_ret_0 /= l_bid_16;
         else {
            if (gd_316 > 0.0) {
               Log(StringConcatenate("Unable to convert account equity to USD, and using last-known equity. Make sure that the ", ls_8, "USD or USD", ls_8, " symbol is in the MT4 Market Watch."));
               ld_ret_0 = gd_316;
            } else {
               Log(StringConcatenate("Unable to convert account equity to USD. Make sure that the ", ls_8, "USD or USD", ls_8, " symbol is in the MT4 Market Watch."));
               ld_ret_0 = 0;
            }
         }
      }
   }
   if (ld_ret_0 > 0.0) gd_316 = ld_ret_0;
   return (ld_ret_0);
}

string GetAutoSymbolSuffix() {
   int li_12;
   int li_16;
   string ls_24;
   string ls_ret_0 = "";
   int l_file_8 = FileOpenHistory("symbols.raw", FILE_BIN|FILE_READ);
   if (l_file_8 <= 0) Log("Unable to open symbols file");
   else {
      li_12 = FileSize(l_file_8) / 1936;
      li_16 = 1924;
      for (int l_count_20 = 0; l_count_20 < li_12; l_count_20++) {
         ls_24 = FileReadString(l_file_8, 12);
         if (StringFind(ls_24, "EURUSD") == 0) {
            if (StringLen(ls_24) > 6) ls_ret_0 = StringSubstr(ls_24, 6);
            l_count_20 = li_12 + 1;
         }
         FileSeek(l_file_8, li_16, SEEK_CUR);
      }
      FileClose(l_file_8);
   }
   return (ls_ret_0);
}
int last_type() 
{
   int W = -1;
   for (int i = 0; i < OrdersTotal(); i++) 
   {
      OrderSelect(i, SELECT_BY_POS);
      if (OrderSymbol() == Symbol() && OrderType() <= OP_SELL) W = OrderType();
   }
   return (W);
}

double last_open(int ai_0) 
{
   double W;
   for (int i = 0; i < OrdersTotal(); i++) 
   {
      OrderSelect(i, SELECT_BY_POS);
      if (OrderSymbol() == Symbol() && OrderType() == ai_0) W = OrderOpenPrice();
   }
   return (W);
}