Я задал свои вопросы потому, что после внимательного прочтения справки и нескольких веток этого и соседнего форумов у меня возникло вполне естественное недоумение: а на кой дьявол вообще этот самый комментарий нужен? После ответа на него в этой ветке сомнения в основном рассеялись: лично мне для хранения какой-то моей информации на стороне сервера, скорее всего, вообще не нужен и даже небезопасен, а нужен брокеру, если он хочет что-либо мне оперативно сообщить. Рискну предположить, что так оно и было задумано. Посему нагородил те самые сорок бочек арестантов с глобальными переменными, и благополучно забыл про эту заморочку (вероятно, до выявления новых косяков).
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() сохранены будут только они. Поэтому для сохранения специфических характеристик (хоть для той же максимальной просадки отдельного ордера) я пришёл именно к такой конструкции. К тому же я всё же стараюсь использовать конструкции двойного назначения - то есть принципиально пригодные и для работы в реальном времени. Разумеется, в этом случае необходимо дополнительное тестирование.
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); } //+------------------------------------------------------------------+В общем выходит, что я не зря их недолюбливаю
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)Приведенный код носит чисто иллюстративный характер (то есть приготовлен вырезанием из рабочего всего, не относящегося к теме, без последующей проверки в работе). Ну и комментарии не стал писать - названия переменных вроде "говорящие", да и не статья всё же.
Если делать полную имитацию глобальных переменных через массивы- то думаю выигрыша по времени не будет. Ведь необходимо еще сделать в цикле перебор всех значений массива для поиска нужного по имени.
Хм, я поначалу воспринял эту фразу только в контексте данной темы. Если же речь идёт об имитации глобальных переменных вообще, то это это бессмысленно. Поскольку они уже есть :). Речь о том, что для реализации некоторых идей стандартных свойств ордера недостаточно. Встроенных средств для такой работы в MQL нет (с чего тема и началась). Но если всё равно хочется, некоторым громоздким способом это реализовать можно. Даже двумя, как выяснилось. Следующий вопрос - какой из способов предпочесть. Это, конечно, во многом дело вкуса и привычки. Мой вывод таков: способ с массивами требует дополнительного цикла для связи стандартного объекта "ордер" с дополнительной "структурой", но зато позволяет экономить код при обращении к этим дополнительным свойствам. По быстродействию же, он превосходит реализацию через глобальные переменные на порядок.