Пользовательские свойства ордера - страница 3

 
Я задал свои вопросы потому, что после внимательного прочтения справки и нескольких веток этого и соседнего форумов у меня возникло вполне естественное недоумение: а на кой дьявол вообще этот самый комментарий нужен? После ответа на него в этой ветке сомнения в основном рассеялись: лично мне для хранения какой-то моей информации на стороне сервера, скорее всего, вообще не нужен и даже небезопасен, а нужен брокеру, если он хочет что-либо мне оперативно сообщить. Рискну предположить, что так оно и было задумано. Посему нагородил те самые сорок бочек арестантов с глобальными переменными, и благополучно забыл про эту заморочку (вероятно, до выявления новых косяков).
 
Rosh, я попытался протестировать на скорость обсуждавшиеся варианты сопровождения ордеров - это важно для меня, поскольку я использую MQL главным образом на истории. Привожу код и лог. Будут замечания по коду и параметрам? А то я глобальными переменными редко пользуюсь - недолюбливаю.
#property show_inputs

extern int Steps = 100000;
extern int MaxOrdNum = 8;

int rnd;
int ticket;
int OrdNum;
int OrdInd;
int LastOrdInd;
int OrdID[];
int OrdTicket[];
int OrdDay[];
int OrdHour[];
double OrdData1[];
double OrdData2[];
double OrdData3[];
double OrdData4[];

double Data1;
double Data2;
double Data3;
double Data4;

int ResizeOrdArrays() {
  ArrayResize(OrdID,OrdNum);
  ArrayResize(OrdTicket,OrdNum);
  ArrayResize(OrdDay,OrdNum);
  ArrayResize(OrdHour,OrdNum);
  ArrayResize(OrdData1,OrdNum);
  ArrayResize(OrdData2,OrdNum);
  ArrayResize(OrdData3,OrdNum);
  ArrayResize(OrdData4,OrdNum);
  
  return(0);
}

int AddOrder() {
  OrdInd=OrdNum;
  OrdNum++;
  ResizeOrdArrays();
  OrdID[OrdInd] = 1;
  OrdTicket[OrdInd] = ticket;
  OrdDay[OrdInd] = 1;
  OrdHour[OrdInd] = 1;
  OrdData1[OrdInd] = 123.45;
  OrdData2[OrdInd] = 123.45;
  OrdData3[OrdInd] = 123.45;
  OrdData4[OrdInd] = 123.45;

  return(0);
}

int CutOrder() {
  OrdNum--;
  LastOrdInd=OrdNum;
  OrdID[OrdInd] = OrdID[LastOrdInd];
  OrdTicket[OrdInd] = OrdTicket[LastOrdInd];
  OrdDay[OrdInd] = OrdDay[LastOrdInd];
  OrdHour[OrdInd] = OrdHour[LastOrdInd];
  OrdData1[OrdInd] = OrdData1[LastOrdInd];
  OrdData2[OrdInd] = OrdData2[LastOrdInd];
  OrdData3[OrdInd] = OrdData3[LastOrdInd];
  OrdData4[OrdInd] = OrdData4[LastOrdInd];

  return(0);
}

int AddOrderGV() {
  OrdNum++;
  GlobalVariableSet("1"+ticket+"ID",1);
  GlobalVariableSet("1"+ticket+"OrdDay",1);
  GlobalVariableSet("1"+ticket+"OrdHour",1);
  GlobalVariableSet("1"+ticket+"OrdData1",123.45);
  GlobalVariableSet("1"+ticket+"OrdData2",123.45);
  GlobalVariableSet("1"+ticket+"OrdData3",123.45);
  GlobalVariableSet("1"+ticket+"OrdData4",123.45);

  return(0);
}

int CutOrderGV() {
  OrdNum--;
  GlobalVariableDel("1"+ticket+"ID");
  GlobalVariableDel("1"+ticket+"OrdDay");
  GlobalVariableDel("1"+ticket+"OrdHour");
  GlobalVariableDel("1"+ticket+"OrdData1");
  GlobalVariableDel("1"+ticket+"OrdData2");
  GlobalVariableDel("1"+ticket+"OrdData3");
  GlobalVariableDel("1"+ticket+"OrdData4");

  return(0);
}

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start() {
//---- 
  MathSrand(LocalTime());
  int Norma = 32768/MaxOrdNum;
  int DelTicket = 0;
  int BTime;
  
  OrdNum = 0;
  BTime = GetTickCount();
  for (ticket=0;ticket<Steps;ticket++) {
    AddOrder();
    if (OrdNum >= MaxOrdNum) {
// Тикет вообще-то должен получаться через OrderSelect().
// Для моделирования выберем ордер случайно
      rnd = MathRand()/Norma;
      DelTicket = OrdTicket[rnd];
      for (OrdInd=0; OrdInd<OrdNum;OrdInd++) {
        if (OrdTicket[OrdInd] == DelTicket) break; 
      }
// Пользоваться данными можно непосредственно, имитируем только изменение.
      OrdData1[OrdInd] = 123.45;
      OrdData2[OrdInd] = 123.45;
      OrdData3[OrdInd] = 123.45;
      OrdData4[OrdInd] = 123.45;
      CutOrder();
      ResizeOrdArrays();
    }
  }
  Print ("Первый вариант: ",GetTickCount()-BTime," мс");
  
  AddOrder();

  OrdNum = 0;
  BTime = GetTickCount();
  for (ticket=0;ticket<Steps;ticket++) {
    AddOrderGV();
    if (OrdNum >= MaxOrdNum) {
// Эти две строчки добавлены для справедливости, в предыдущем варианте они выполняют вспомогательную роль.
      rnd = MathRand()/Norma;
      DelTicket = OrdTicket[rnd];
// Теперь считаем значения переменных, без этого ими нельзя будет пользоваться 
      Data1 = GlobalVariableGet("1"+ticket+"OrdData1");
      Data1 = GlobalVariableGet("1"+ticket+"OrdData2");
      Data1 = GlobalVariableGet("1"+ticket+"OrdData3");
      Data1 = GlobalVariableGet("1"+ticket+"OrdData4");
// И запишем
      GlobalVariableSet("1"+ticket+"OrdData1",123.45);
      GlobalVariableSet("1"+ticket+"OrdData2",123.45);
      GlobalVariableSet("1"+ticket+"OrdData3",123.45);
      GlobalVariableSet("1"+ticket+"OrdData4",123.45);
      CutOrderGV();
    }
  }
  Print ("Второй вариант: ",GetTickCount()-BTime," мс");

//----
  return(0);
}
//+------------------------------------------------------------------+



В общем выходит, что я не зря их недолюбливаю

2006.11.06 01:14:52	test EURUSD,M30: removed
2006.11.06 01:14:52	test EURUSD,M30: Второй вариант: 12469 мс
2006.11.06 01:14:39	test EURUSD,M30: Первый вариант: 1047 мс
2006.11.06 01:14:36	test EURUSD,M30: loaded successfully


 
В общем-то, я предлагал использовать глобальные переменные джля работы в он-лайне, и тут важна не скорость, а надежность. Для тестирования на истории подойдет любой удовлетворяющий пользователя вариант. Вечером разберусь в Вашем советнике.
 
Конструкцией с массивами я пользовался, она рабочая. А вот с глобальными переменнымы сконструировал по аналогии, она может быть и неоптимальной. Хотя думаю разница в быстродействии объясняется тем, что раз используются строковые имена, парсинг неизбежен. То есть тот же перебор массивов, только многократный. Ну а то, что он спрятан от пользователя, дела не меняет.
 
Если делать полную имитацию глобальных переменных через массивы- то думаю выигрыша по времени не будет. Ведь необходимо еще сделать в цикле перебор всех значений массива для поиска нужного по имени.
 
Кстати, если кому интересно, удаление ордеров из сопутствующей "структуры" я обычно делаю примерно такой конструкцией
  int AccTotal=HistoryTotal();
  if (AccTotal != preAccTotal) {
    for (i= preAccTotal; i<AccTotal; i++) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) {
        Print("Ошибка при доступе к исторической базе (",GetLastError(),")");
      }
      for (OrdInd=0; OrdInd<OrdNum; OrdInd++) {
        if (OrderTicket() == OrdTicket[OrdInd]) break;
      }  //  for (OrdInd=i; OrdInd<OrdNum; OrdInd++)
      if (OrdInd == OrdNum) {
        Print("Ордер отсутстует в OrderList, ордеров в списке: ",OrdNum,", ордеров в истории: ",AccTotal);
      } else {  //  if (OrdInd == OrdNum)
        FileWrite(handle,...);
        CutOrder();
      }  //  if (OrdInd < OrdNum)
    }  //  for (i= preAccTotal; i<AccTotal; i++)
    ResizeOrdArrays();  
    preAccTotal = AccTotal;
  }  //  if (AccTotal != preAccTotal)


Приведенный код носит чисто иллюстративный характер (то есть приготовлен вырезанием из рабочего всего, не относящегося к теме, без последующей проверки в работе). Ну и комментарии не стал писать - названия переменных вроде "говорящие", да и не статья всё же.


 
Candid, гораздо проще снимать кальку с ордеров (в тестере) в deinit(). А поиски данных в HistoryTotal() действительно могут сущесвтенно замедлить тестирование, но все можно обойти написанием специального варианта советника только для тестера.
 
Если я правильно понял мысль, это годится только для стандартных характеристик - поскольку к моменту работы deinit() сохранены будут только они. Поэтому для сохранения специфических характеристик (хоть для той же максимальной просадки отдельного ордера) я пришёл именно к такой конструкции. К тому же я всё же стараюсь использовать конструкции двойного назначения - то есть принципиально пригодные и для работы в реальном времени. Разумеется, в этом случае необходимо дополнительное тестирование.
 
P.S. Да, самое главное. Ради стандартных характеристик вообще ничего из описанного в этом топике городить нет необходимости :).
 
Rosh:
Если делать полную имитацию глобальных переменных через массивы- то думаю выигрыша по времени не будет. Ведь необходимо еще сделать в цикле перебор всех значений массива для поиска нужного по имени.

Хм, я поначалу воспринял эту фразу только в контексте данной темы. Если же речь идёт об имитации глобальных переменных вообще, то это это бессмысленно. Поскольку они уже есть :). Речь о том, что для реализации некоторых идей стандартных свойств ордера недостаточно. Встроенных средств для такой работы в MQL нет (с чего тема и началась). Но если всё равно хочется, некоторым громоздким способом это реализовать можно. Даже двумя, как выяснилось. Следующий вопрос - какой из способов предпочесть. Это, конечно, во многом дело вкуса и привычки. Мой вывод таков: способ с массивами требует дополнительного цикла для связи стандартного объекта "ордер" с дополнительной "структурой", но зато позволяет экономить код при обращении к этим дополнительным свойствам. По быстродействию же, он превосходит реализацию через глобальные переменные на порядок.