//+------------------------------------------------------------------+
//|注文コピー　コピー先
//+------------------------------------------------------------------+
#define VERSION     "1.00"
#define COPYRIGHT   "(C) 2020 funchi"

#property version   VERSION
#property copyright COPYRIGHT
#property strict


extern string  comment01                  = "-----";        //----- System Settings -----
extern string  P_Pairing_id               = "Trade_copy";   //Pairing ID
extern int     P_Interval_Millisecond     = 100;            //Synchronization Interval (ms)
extern int     P_IgnoreDelayedTradeOrders = 300;            //Ignore delayed trade orders (ms)
extern int     P_IgnorePastTradeOrders    = 1;              //Ignore past trade orders (s)
extern int     P_MagicNumber              = 123456;         //Trade Copy System MagicNumber
extern bool    P_OperatingEaAfterStartup  = true;           //Operating EA after MT4 start-up

extern string  comment02                  = "-----";        //----- Symbol Convert Settings -----
extern string  P_Symbol_Convert           = "";             //Symbol Convert (src=des,src=des ...)
extern string  P_Remove_Suffix_src        = "";             //Remove Suffix of Copy Source
extern string  P_Add_Suffix_des           = "";             //Add Suffix of Copy Destination

extern string  comment03                  = "-----";        //----- Lot Settings -----
extern double  conversionRatio            = 1.0;            //(r)...Conversion Ratio
extern string  comment031                 = "(A) = (a) * (r) * (B) / (b)"; //-----
extern string  comment033                 = "(A)...Lot of Copy Destination";       //-----
extern string  comment032                 = "(a)...Lot of Copy Source";            //-----
extern string  comment035                 = "(B)...Balance of Copy Destination";   //-----
extern string  comment034                 = "(b)...Balance of Copy Source";        //-----

string   Pairing_id2                      = "_destination_result_";
string   P_Pairing_id2                    = "";
bool     EnableEA                         = false;

bool isSourceWorking = false;
int CsvReadCount = 0;
int SourceSize = 0;
int RealSize = 0;
double   SourceBalance = 0;
double   RealBalance = 0;
double   OrdDT;
double   OrdDT2;

#define  ARRAY_RESERVE_SIZE   10


int      OrdId[];
string   OrdSym[];
int      OrdTyp[];
double   OrdLot[];
double   OrdPrice[];
double   OrdSL[];
double   OrdTP[];
double   OrdOpDt[];

int      RealOrdId[];
string   RealOrdSym[];
int      RealOrdTyp[];
double   RealOrdLot[];
double   RealOrdPrice[];
double   RealOrdSL[];
double   RealOrdTP[];
string   RealOrdOrig[];

string s[];
string symbol_convert_src[];
string symbol_convert_des[];


string   BUTTON_NAME             = "Trade_copy_Button";
#define  MESSAGE_HEIGHT          (18)
#define  MESSAGE_X_OFFSET        (18)
#define  MESSAGE_Y               (-1)
string   FONT_TYPE               = "MS Gothic";
int      FONT_SIZE_NORMAL        = 10;
int      FONT_SIZE_SMALL         = 11;
color    COLOR_MESSAGE_OK        = clrWhite;
color    COLOR_MESSAGE_ERROR     = clrTomato;

color    ButtonTextEnableColor   = clrWhite;
color    ButtonBgEnableColor     = clrGreen;
color    ButtonTextDisableColor  = clrNONE;
color    ButtonBgDisableColor    = clrNONE;
color    ButtonBorderColor       = clrNONE;


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
//
   P_Pairing_id2 = Pairing_id2 + IntegerToString(AccountNumber());
   EnableEA = false;
   if (P_OperatingEaAfterStartup == true) {
      EnableEA = true;
   }
   isSourceWorking = false;
   CsvReadCount = 0;
   SourceSize = 0;
   RealSize = 0;
   SourceBalance = 0;
   RealBalance = 0;

   ButtonTextDisableColor = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
   ButtonBgDisableColor = (color)ChartGetInteger(0, CHART_COLOR_FOREGROUND);
   ButtonBorderColor = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);

   symbol_convert_init();

   reset_receiver_file();

   createMessage();
   CheckMT4Server(true);
   CreateButton();

//--- create timer
   EventSetMillisecondTimer(P_Interval_Millisecond);

//---
   return(INIT_SUCCEEDED);
}


//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
//--- destroy timer
   EventKillTimer();
   ObjectsDeleteAll(0, BUTTON_NAME);
}


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
//---

}


//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer() {

   if (CheckMT4Server()) {

      if (EnableEA) {

         load_positions();

         if (isSourceWorking) {
            compare_and_orderSend_positions();
            save_positions();
         }
      }
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void symbol_convert_init() {

   string syms[];
   int syms_len = StringSplit(P_Symbol_Convert, ',', syms);
   ArrayResize(symbol_convert_src, syms_len, 100);
   ArrayResize(symbol_convert_des, syms_len, 100);

   for (int i = 0; i < syms_len; i++) {
      string sym[];
      int sym_len = StringSplit(syms[i], '=', sym);
      symbol_convert_src[i] = sym[0];
      symbol_convert_des[i] = sym[1];
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string get_symbol(string aSymbol) {

   int syms_len = ArraySize(symbol_convert_src);

   for (int i = 0; i < syms_len; i++) {
      if (symbol_convert_src[i] == aSymbol) {
         return symbol_convert_des[i];
      }
   }

   string str = aSymbol;
   StringReplace(str, P_Remove_Suffix_src, "");
   str = str + P_Add_Suffix_des;

   return str;
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void load_positions() {

   isSourceWorking = false;
   CsvReadCount = -1;
   RealBalance = AccountInfoDouble(ACCOUNT_BALANCE);

   int handle = FileOpen(P_Pairing_id + ".csv", FILE_CSV | FILE_READ | FILE_SHARE_READ | FILE_COMMON, ";");
   if(handle > 0) {

      if (FileIsEnding(handle) == true) {
         FileClose(handle);
         updateMessage("Copy source is not operating", COLOR_MESSAGE_ERROR);
         reset_receiver_file();
         return;
      }

      string s2 = FileReadString(handle);
      string c2[];
      int Len = StringSplit(s2, ',', c2);
      CsvReadCount = (int)StringToInteger(c2[0]);
      SourceBalance = StringToDouble(c2[1]);
      OrdDT = StringToDouble(c2[2]);
      OrdDT2 = StringToDouble(c2[3]);

      if (CsvReadCount == -1) {
         FileClose(handle);
         updateMessage("Copy source is not operating", COLOR_MESSAGE_ERROR);
         reset_receiver_file();
         return;
      }

      isSourceWorking = true;

      ArrayResize(s, CsvReadCount, ARRAY_RESERVE_SIZE);

      int i = 0;
      while(FileIsEnding(handle) == false) {
         s[i] = FileReadString(handle);
         i++;
      }
      FileClose(handle);

      parse_s();

   } else {
      updateMessage("Copy source is not operating", COLOR_MESSAGE_ERROR);
      reset_receiver_file();
   }
//Print("@@@ load_positions CsvReadCount=", CsvReadCount);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void parse_s() {

   SourceSize = CsvReadCount;
   ArrayResize(OrdId, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdSym, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdTyp, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdLot, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdPrice, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdSL, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdTP, SourceSize, ARRAY_RESERVE_SIZE);
   ArrayResize(OrdOpDt, SourceSize, ARRAY_RESERVE_SIZE);

   for (int i = 0; i < SourceSize; i++) {

      string c[];
      int Len = StringSplit(s[i], ',', c);

      OrdId[i] = (int)StringToInteger(c[0]);
      OrdSym[i] = get_symbol(c[1]);
      OrdTyp[i] = (int)StringToInteger(c[2]);
      OrdLot[i] = LotVol(StringToDouble(c[3]), OrdSym[i]);

      int digits = (int)MarketInfo(OrdSym[i], MODE_DIGITS);

      OrdPrice[i] = NormalizeDouble(StringToDouble(c[4]), digits);
      OrdSL[i] = NormalizeDouble(StringToDouble(c[5]), digits);
      OrdTP[i] = NormalizeDouble(StringToDouble(c[6]), digits);
      OrdOpDt[i] = StringToDouble(c[7]);

   }
//Print("@@@ parse_s SourceSize=", SourceSize);
//Print("@@@ parse_s SourceSize=", SourceSize);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double LotVol(double lot, string symbol) {
//Print("@@@ LotVol symbol=", symbol);

   double ratio = 1.0;
   if (SourceBalance > 0) {
      ratio = conversionRatio * RealBalance / SourceBalance;
   }

   double lotSize = lot * ratio;
   double lotStep = MarketInfo(symbol, MODE_LOTSTEP);
   if (lotStep == 0) lotStep = 0.01;

// ロットステップ単位未満は切り捨て
// 0.123⇒0.12（lotStep=0.01の場合）
// 0.123⇒0.1 （lotStep=0.1の場合）
   lotSize = MathFloor(lotSize / lotStep) * lotStep;

// 証拠金ベースの制限
   double margin = MarketInfo(symbol, MODE_MARGINREQUIRED);
   if(margin > 0.0) {
      double accountMax = RealBalance / margin;
      accountMax = MathFloor(accountMax / lotStep) * lotStep;
      if(lotSize > accountMax) {
         lotSize = accountMax;
      }
   }

// 最大ロット数、最小ロット数対応
   double minLots = MarketInfo(symbol, MODE_MINLOT);
   double maxLots = MarketInfo(symbol, MODE_MAXLOT);
   if(lotSize < minLots) {
      // 仕掛けようとするロット数が最小単位に満たない場合、
      // そのまま仕掛けると過剰リスクになるため、エラーに
      lotSize = 0;
   } else if(lotSize > maxLots) {
      lotSize = maxLots;
   }

   return(lotSize);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void compare_and_orderSend_positions() {

   real_positions();

   int x[];
   ArrayResize(x, RealSize, ARRAY_RESERVE_SIZE);
   if (RealSize > 0) ArrayInitialize(x, 0);
//
   string moto = TimeToString((datetime)OrdDT, TIME_DATE | TIME_SECONDS)
                 + " "
                 + IntegerToString(((long)OrdDT2 + P_IgnoreDelayedTradeOrders), 20, '0');
   string saki = TimeToString(TimeLocal(), TIME_DATE | TIME_SECONDS)
                 + " "
                 + IntegerToString((long)GetTickCount(), 20, '0');
//Print(moto);
//Print(saki);
//
//マスターとリアルの比較
//
   for (int i = 0; i < SourceSize; i++) {

      bool found = false;
      for (int j = 0; j < RealSize; j++) {
//
         if (DoubleToStr(OrdId[i], 0) == RealOrdOrig[j]) {
            found = true;
            x[j] = 1;
            //
            //オーダー変更
            //
            if (OrdTP[i] != RealOrdTP[j]
                  || OrdSL[i] != RealOrdSL[j]) {
               if (OrderSelect(RealOrdId[j], SELECT_BY_TICKET) == true) {
                  bool res = OrderModify(OrderTicket(),
                                         OrderOpenPrice(),
                                         OrdSL[i],
                                         OrdTP[i],
                                         0
                                        );
                  if (res == false)
                     Alert("error : compare_and_orderSend_positions OrderModify code=", GetLastError());
               } else
                  Alert("error : compare_and_orderSend_positions OrderSelect code=", GetLastError());
            }
         }
      }
      //
      //新規オーダー
      //
      if (!found && !have_closed(OrdId[i]) && (moto >= saki)) {
         if ((OrdOpDt[i] + P_IgnorePastTradeOrders) >= TimeLocal()) {
            double Price = MarketPrice(i, OrdTyp[i]);
            int result = OrderSend(OrdSym[i],   // 通貨ペア名
                                   OrdTyp[i],   // 注文タイプ
                                   OrdLot[i],   // ロット数
                                   Price,       // 注文価格
                                   50,          // スリップページ
                                   0,           // ストップロス価格
                                   0,           // リミット価格
                                   DoubleToStr(OrdId[i], 0),   // コメント
                                   P_MagicNumber,  // マジックナンバー
                                   0            // 有効期限
                                  );
            if (result > 0) {
               bool res = OrderModify(result,
                                      OrderOpenPrice(),
                                      OrdSL[i],
                                      OrdTP[i],
                                      0
                                     );
               if (res == false)
                  Alert("error : compare_and_orderSend_positions OrderModify code=", GetLastError());
            } else
               Alert("error : compare_and_orderSend_positions OrderSend code=", GetLastError());
         }
      }
   }
//
//マスターオーダーがないのでチケットを閉じる
//
   for (int j = RealSize - 1; j >= 0; j--) {
      if (x[j] != 1) {
         double Price = MarketPrice(j, RealOrdTyp[j], "close");
         bool result = OrderClose(RealOrdId[j],
                                  RealOrdLot[j],
                                  Price,
                                  50,
                                  CLR_NONE
                                 );
         if (result == false)
            Alert("error : compare_and_orderSend_positions OrderClose code=", GetLastError());
      }
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double MarketPrice(int i, int aOrderType, string typ = "open") {

   RefreshRates();

   if (typ == "open") {
      int digits = (int)MarketInfo(OrdSym[i], MODE_DIGITS);
      if (OrdTyp[i] == OP_BUYLIMIT || OrdTyp[i] == OP_SELLLIMIT
            || OrdTyp[i] == OP_BUYSTOP || OrdTyp[i] == OP_SELLSTOP) {
         return (NormalizeDouble(OrdPrice[i], digits));
      } else if (OrdTyp[i] == OP_BUY) {
         return(NormalizeDouble(MarketInfo(OrdSym[i], MODE_ASK), digits));
      } else {
         return(NormalizeDouble(MarketInfo(OrdSym[i], MODE_BID), digits));
      }
   } else {
//close:
      int digits = (int)MarketInfo(RealOrdSym[i], MODE_DIGITS);
      if (RealOrdTyp[i] == OP_BUYLIMIT || RealOrdTyp[i] == OP_SELLLIMIT
            || RealOrdTyp[i] == OP_BUYSTOP || RealOrdTyp[i] == OP_SELLSTOP) {
         return (NormalizeDouble(RealOrdPrice[i], digits));
      } else if (RealOrdTyp[i] == OP_BUY) {
         return(NormalizeDouble(MarketInfo(RealOrdSym[i], MODE_BID), digits));
      } else {
         return(NormalizeDouble(MarketInfo(RealOrdSym[i], MODE_ASK), digits));
      }
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void real_positions() {

   RealSize = OrdersTotal();
   ArrayResize(RealOrdId, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdSym, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdTyp, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdLot, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdPrice, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdSL, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdTP, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdOrig, RealSize, ARRAY_RESERVE_SIZE);

   int j = 0;
   for (int i = RealSize - 1; i >= 0; i--) {
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) continue;
      if (P_MagicNumber != OrderMagicNumber()) continue;
      RealOrdId[j] = OrderTicket();
      RealOrdSym[j] = OrderSymbol();
      RealOrdTyp[j] = OrderType();
      RealOrdLot[j] = OrderLots();
      RealOrdPrice[j] = OrderOpenPrice();
      RealOrdSL[j] = OrderStopLoss();
      RealOrdTP[j] = OrderTakeProfit();
      RealOrdOrig[j] = OrderComment();
      j++;
   }

   RealSize = j;
   ArrayResize(RealOrdId, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdSym, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdTyp, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdLot, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdPrice, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdSL, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdTP, RealSize, ARRAY_RESERVE_SIZE);
   ArrayResize(RealOrdOrig, RealSize, ARRAY_RESERVE_SIZE);

}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void save_positions() {

   int handle = FileOpen(P_Pairing_id + P_Pairing_id2 + ".csv", FILE_CSV | FILE_WRITE | FILE_SHARE_WRITE | FILE_COMMON, ",");
   if(handle > 0) {

      FileWrite(handle, RealSize, AccountInfoDouble(ACCOUNT_BALANCE));

      for (int i = RealSize - 1; i >= 0; i--) {
         int digits = (int)MarketInfo(RealOrdSym[i], MODE_DIGITS);
         FileWrite(handle,
                   RealOrdId[i],
                   RealOrdSym[i],
                   RealOrdTyp[i],
                   RealOrdLot[i],
                   DoubleToString(RealOrdPrice[i], digits),
                   DoubleToString(RealOrdSL[i], digits),
                   DoubleToString(RealOrdTP[i], digits),
                   RealOrdOrig[i]
                  );
      }
      FileClose(handle);

   } else
      Alert("error : save_positions FileOpen code=", GetLastError());
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void reset_receiver_file() {

   RealSize = -1;

   int handle = FileOpen(P_Pairing_id + P_Pairing_id2 + ".csv", FILE_CSV | FILE_WRITE | FILE_SHARE_WRITE | FILE_COMMON, ",");
   if(handle > 0) {
      FileWrite(handle, -1, -1);
      FileClose(handle);
   } else
      Alert("error : reset_receiver_file FileOpen code=", GetLastError());
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool have_closed(int aTicket) {

   string ticket = IntegerToString(aTicket);
   int ordersTotal = OrdersHistoryTotal();

   for (int i = ordersTotal - 1; i >= 0; i--) {
      if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) == false) continue;
      if (P_MagicNumber != OrderMagicNumber()) continue;
      string comment = OrderComment();
      if (StringFind(comment, ticket, 0) > -1) return true;
   }
   return false;
}


//+------------------------------------------------------------------+
//|ＥＡ有効 <=> ＥＡ無効ボタン状態変更
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long & lparam,
                  const double & dparam,
                  const string & sparam) {
//
   if(id == CHARTEVENT_OBJECT_CLICK) {

      if (sparam == BUTTON_NAME) {
         ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         EnableEA = !EnableEA;
         if (EnableEA == false) {
            reset_receiver_file();
         }
         UpdateButton();

      }
   }
}


//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
bool CheckMT4Server(bool aInit = false) {
//---
   if (!EnableEA) {
      int handle = FileOpen(P_Pairing_id + ".csv", FILE_CSV | FILE_READ | FILE_SHARE_READ | FILE_COMMON, ";");
      if(handle > 0) {
         if (FileIsEnding(handle) == true) {
            FileClose(handle);
            updateMessage("Copy source is not operating", COLOR_MESSAGE_ERROR);
            reset_receiver_file();
            ResetLastError();
            return false;
         }

         string s2 = FileReadString(handle);
         string c2[];
         int Len = StringSplit(s2, ',', c2);
         CsvReadCount = (int)StringToInteger(c2[0]);
         SourceBalance = StringToDouble(c2[1]);
         OrdDT = StringToDouble(c2[2]);
         OrdDT2 = StringToDouble(c2[3]);

         if (CsvReadCount == -1) {
            FileClose(handle);
            updateMessage("Copy source is not operating", COLOR_MESSAGE_ERROR);
            reset_receiver_file();
            ResetLastError();
            return false;
         }
         FileClose(handle);
      } else {
         updateMessage("Copy source is not operating", COLOR_MESSAGE_ERROR);
         reset_receiver_file();
         ResetLastError();
         return false;
      }
   } else {
      if (!isSourceWorking) {
         return true;
      }
   }
   if (!IsConnected()) {
      updateMessage("No connection", COLOR_MESSAGE_ERROR);
      reset_receiver_file();
      ResetLastError();
      return false;
   }
   if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) {
      updateMessage("Terminal Trade not allowed", COLOR_MESSAGE_ERROR);
      reset_receiver_file();
      ResetLastError();
      return false;
   }
   if (!MQLInfoInteger(MQL_TRADE_ALLOWED)) {
      updateMessage("EA Trade not allowed", COLOR_MESSAGE_ERROR);
      reset_receiver_file();
      ResetLastError();
      return false;
   }
   if (!_LastError == ERR_TRADE_DISABLED) {
      updateMessage("Trade is disabled", COLOR_MESSAGE_ERROR);
      reset_receiver_file();
      ResetLastError();
      return false;
   }
   if (!aInit) {
      if (MarketIsClosed()) {
         updateMessage("Market is closed", COLOR_MESSAGE_ERROR);
         reset_receiver_file();
         ResetLastError();
         return false;
      }
   }
//
   updateMessage("Trade Copy Destination (-> Receiver)", COLOR_MESSAGE_OK);
//
   return true;
}


//+------------------------------------------------------------------+
//| Check for Market is closed in MT4                                |
//+------------------------------------------------------------------+
bool MarketIsClosed() {
   return _LastError == ERR_MARKET_CLOSED;
}


//+------------------------------------------------------------------+
//|ＥＡ有効 <=> ＥＡ無効ボタン作成
//+------------------------------------------------------------------+
void CreateButton() {
   string lab_str = BUTTON_NAME;
   int fontsize = 12;
   int xpos = 125;
   int ypos = 16;
   int width = 120;
   int height = 22;
   string text = "NOT OPERATING";
   ObjectCreate(lab_str, OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0, lab_str, OBJPROP_STATE, 0);
   ObjectSetInteger(0, lab_str, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
   ObjectSetInteger(0, lab_str, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, lab_str, OBJPROP_XDISTANCE, xpos);
   ObjectSetInteger(0, lab_str, OBJPROP_YDISTANCE, ypos);
   ObjectSetInteger(0, lab_str, OBJPROP_XSIZE, width);
   ObjectSetInteger(0, lab_str, OBJPROP_YSIZE, height);
   ObjectSetString(0, lab_str, OBJPROP_FONT, FONT_TYPE);
   ObjectSetInteger(0, lab_str, OBJPROP_FONTSIZE, fontsize);
   ObjectSetString(0, lab_str, OBJPROP_TEXT, text);
   UpdateButton();
}


//+------------------------------------------------------------------+
//|ＥＡ有効 <=> ＥＡ無効ボタン更新
//+------------------------------------------------------------------+
void UpdateButton() {
   string lab_str = BUTTON_NAME;
   string text = "NOT OPERATING";
   if (EnableEA) {
      text = "OPERATING";
      ObjectSetInteger(0, lab_str, OBJPROP_COLOR, ButtonTextEnableColor);
      ObjectSetInteger(0, lab_str, OBJPROP_BGCOLOR, ButtonBgEnableColor);
   } else {
      ObjectSetInteger(0, lab_str, OBJPROP_COLOR, ButtonTextDisableColor);
      ObjectSetInteger(0, lab_str, OBJPROP_BGCOLOR, ButtonBgDisableColor);
   }
   ObjectSetInteger(0, lab_str, OBJPROP_STATE, 0);
   ObjectSetInteger(0, lab_str, OBJPROP_BORDER_COLOR, ButtonBorderColor);
   ObjectSetString(0, lab_str, OBJPROP_TEXT, text);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void createMessage() {
//
   string lab_str = BUTTON_NAME + "Message";
   ObjectCreate(0, lab_str, OBJ_EDIT, 0, 0, 0);

   ObjectSet(lab_str, OBJPROP_BACK, false);
   ObjectSetInteger(0, lab_str, OBJPROP_CORNER, 1);
   ObjectSetInteger(0, lab_str, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
   ObjectSetInteger(0, lab_str, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, lab_str, OBJPROP_SELECTED, false);
   ObjectSetInteger(0, lab_str, OBJPROP_READONLY, true);

   ObjectSetInteger(0, lab_str, OBJPROP_XDISTANCE, MESSAGE_X_OFFSET);
   ObjectSetInteger(0, lab_str, OBJPROP_YDISTANCE, MESSAGE_Y);
   ObjectSetInteger(0, lab_str, OBJPROP_XSIZE, 0);
   ObjectSetInteger(0, lab_str, OBJPROP_YSIZE, MESSAGE_HEIGHT);

   color COLOR_MESSAGE_BG = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
   ObjectSetInteger(0, lab_str, OBJPROP_BORDER_COLOR, COLOR_MESSAGE_BG);
   ObjectSetInteger(0, lab_str, OBJPROP_BGCOLOR, COLOR_MESSAGE_BG);
   ObjectSetInteger(0, lab_str, OBJPROP_COLOR, COLOR_MESSAGE_OK);

   ObjectSetString(0, lab_str, OBJPROP_FONT, FONT_TYPE);
   ObjectSetInteger(0, lab_str, OBJPROP_FONTSIZE, FONT_SIZE_SMALL);
   ObjectSetString(0, lab_str, OBJPROP_TEXT, "");
   ObjectSetString(0, lab_str, OBJPROP_TOOLTIP, "\n");
   ObjectSetInteger(0, lab_str, OBJPROP_ALIGN, ALIGN_RIGHT);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void updateMessage(const string pMessage, const color pColor) {
//
   string lab_str = BUTTON_NAME + "Message";
   if (pMessage == "") {
      ObjectSetInteger(0, lab_str, OBJPROP_XDISTANCE, MESSAGE_X_OFFSET);
      ObjectSetInteger(0, lab_str, OBJPROP_XSIZE, 0);
      ObjectSetString(0, lab_str, OBJPROP_TEXT, "");
      return;
   }

   ObjectSetInteger(0, lab_str, OBJPROP_COLOR, pColor);
   ObjectSetString(0, lab_str, OBJPROP_TEXT, pMessage);

   uint w;
   uint h;
   TextSetFont(FONT_TYPE, (int)(-FONT_SIZE_SMALL * 10), FW_NORMAL, 0);
   TextGetSize(pMessage, w, h);
   w = (int)(w * 1.1);
   h = (int)(h * 1.0);
   if (w < 130) {           //ea name が隠れる最小値
      w = 130;
   }
   ObjectSetInteger(0, lab_str, OBJPROP_XDISTANCE, MESSAGE_X_OFFSET + w);
   ObjectSetInteger(0, lab_str, OBJPROP_XSIZE, w);
}

//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
