Интересно стало почему же моя ф-я расчета комиссии по % в USD на лот добавляла аж 60%. 1) Я использовал GetSymbol() для передачи в функцию, а там работа с CharArrayToString - основная часть тормозов была тут, решил просто передавать _Symbol, но 2) оказывается если в функцию передается строка, то она вызывается намного медленнее, чем без неё. Если делать мультивалютник, то надо будет передавать SymbolID.
Новые замеры: без моих ф-й, как ваш вариант, но со всеми ускорителями обнаруженными ранее: shortest pass 0:00:00.152, longest pass 0:00:00.326, average pass 0:00:00.155 EURUSD USD shortest pass 0:00:00.171, longest pass 0:00:00.372, average pass 0:00:00.173 по EURCHF EUR
Все мои функции shortest pass 0:00:00.158, longest pass 0:00:00.323, average pass 0:00:00.160 по EURUSD USD без запроса цен - замедление 3%
Все с сложным расчетом по EURCHF CHF с запросом 2х цен shortest pass 0:00:00.180, longest pass 0:00:00.381, average pass 0:00:00.183 - замедление 6%
Итого: от 3 до 6% замедления за получение комиссии, свопов, контроля времени и перевода прибыли в валюту депозита. Наверное все таки буду пользоваться.
ПС. У вас там тоже есть обращения к GetSymbol() в Order_Base(), но они вроде нечасто вызываются, не смотрел детально. Отказаться бы от CharArrayToString.
Symbol можно хранить в массиве структур символов и их параметров (для свопов, торг. сессии и т.д.), в осн. расчетах использовать только SymbolID. А для комментариев может отдельный строковый массив сделать и использовать ID ордеров для чтения/записи.
Если делать мультивалютник, то надо будет передавать SymbolID.
На мультивалютке море проблем, это только одна из них, к сожалению.
ПС. У вас там тоже есть обращения к GetSymbol() в Order_Base(), но они вроде нечасто вызываются, не смотрел детально. Отказаться бы от CharArrayToString.
GetSymbol не используется. Ускорение работы сделано здесь.
На мультивалютке море проблем, это только одна из них, к сожалению.
Да я тоже хотел было, да передумал)
fxsaber #: Для Тестера можете попробовать вместо _Symbol использовать NULL.
Для определения комиссии и перевода к валюте депозита, я все строковые варианты (BASE_USD, USD_BASE, PROFIT_USD,... ACCOUNT ....) заранее склеил при создании тестера. В работе с тиками строковых операций не провожу, а использую эти готовые склейки по которым и запрашиваю нужные цены из терминала. Т.е. максимально быстро. Могу скинуть, если надо будет. Времени нормально потратил, для подбора точного совпадения с тестером MQ (где-то Ask, где-то Bid, иногда наоборот или только Ask или Bid). Пока в отдельных переменных храню, но можно в массив структур для индексации инструментов все скинуть.
Вернулся к свопам. Возможно вам пригодится. При пересчете их в валюту депозита нужна цена по первому тику в момент начисления каждый день. Расчет в конце теста, дает свою цену. Т.е. совпадения с тестером MQ не получилось. Сделал проверку ролловера на каждом тике и расчет свопов на лету в NewTick( const MqlTick &Tick ):
#ifdef ORDER_SWAP
int Day = TimeDay(Tick.time); //номер дня сменится при первом тике дняif (Day > prewDay_ && TimeSec(Tick.time) >= this.RolloverTime_){//прохождение RolloverTime, считаем свопыthis.prewDay_ = Day;//до смены дня не выполнятьfor (int i = this.AmountOrders-1; i >=0; i--){
if (this.Orders[i].IsPosition() && this.Orders[i].IsNotNull()) {//открытые + //проверка как в AddHistoryOrder() + не открыт на этом тикеdouble newSwap = (this.Orders[i].GetType() == 1 ? this.SwapShort_ : this.SwapLong_) * this.Orders[i].GetLots() * (this.Rollover3Days_ + 1 == TimeDayOfWeek( Tick.time ) ? 3.0 : 1.0);
#ifdef TO_ACCOUNT_CURRENCY
newSwap *= K_CURRENCY_TO_ACC_CURRENCY( this.Orders[i].GetType()==1 ? 0 : 1 );//#endif
this.Orders[i].SetSwap( this.Orders[i].GetSwap() + newSwap );
}
}
}
#endif
и в соответстрвующих местах добавил:
в Order_Base.mqh
void SetSwap( const double NewSwap ){ this.Swap = ::NormalizeDouble(NewSwap, ORDER_BASE::DigitsCurrency); }
в глобале
int TimeDay ( datetime time ){return((int)( time / 86400));}//current Day from on 1 Jan 1971
int TimeDayOfWeek( datetime time ){return((int)((time / 86400 + THURSDAY) % 7));}//current DayOfWeek. 86400 sec in day. 7 days in week. THURSDAY on 1 Jan 1971
Тормозов нет, т.к. на каждом тике всего 1 раз делается time / 86400, и 1 проверка Day > prewDay_. Все остальные расчеты 1 раз в день. Очень быстро. Всего на 1мс или 0,6% медленнее. Для замера скорости, решил исключить влияние увеличения размера массива истории и задал ему сразу 200000 строк, в тесте около 150000 будет использовано. Это дало ускорение на 11%
массив 2000 без всех доп. функций 2023.12.07 18:13:21.984 Statistics shortest pass 0:00:00.184,
массив 200000 без всех доп. функций 2023.12.07 18:23:12.170 Statistics shortest pass 0:00:00.165, 11% быстрее от 184
свопы - массив 200000 2023.12.07 18:27:50.399 Statistics shortest pass 0:00:00.166, на 0,6% медленнее от 165 торг сессия - массив 200000 2023.12.07 18:38:46.890 Statistics shortest pass 0:00:00.165, то же время пересчет в валюту депозита - массив 200000 2023.12.07 18:40:02.505 Statistics shortest pass 0:00:00.175, на 6% медленнее от 165 комиссия в % - массив 200000 2023.12.07 18:41:25.570 Statistics shortest pass 0:00:00.184, на 11% медленнее от 165 всё - массив 200000 2023.12.07 18:42:40.264 Statistics shortest pass 0:00:00.194, на 17% медленнее от 165
Итого свопы на лету и контроль торговой сессии можно добавить без потери скорости. Остальное можно включать по необходимости.
ПС. Столкнулся с тем, что свопы были начислены случайным сделкам открытым сразу после ролловера. Видимо ячейка Swap не переписывается при перемещении строки на новое место в массиве сделок (когда она не задана). Задал в создании ордера Create(...){.... this.Swap = 0.0; ...} После этого лишние свопы не появляются.
Соответствие с тестром MQ точное, кроме инструментов, у которых свопы начисляются в конце теста, если их вычесть, то получится точно. Если исправят тестер MQ к одинаковому поведению, то будет точно совпадать со всеми инструментами.
Например, секунду назад был последний тик EURUSD, а сейчас пришел тик GBPUSD. На этом тике решили открыть позицию по EURUSD - время открытия будет равно тику GBPUSD.
fxsaber #: Архитектурно создал мультивалютный Virtual. Можно запустить у себя на таком примере.
Результат.
Остальное дописать - рутина, т.к. архитектурно все решилось. Реализация довольно простая. SymbolID не понадобился. Критика приветствуется.
Все таки решили мультивалютник сделать. Возможно понадобится и мне, после тестов на одной валюте. Я только начинаю работу с виртуалкой и начну с 1 валюты (и боюсь это минимум на полгода-год), а вы, видимо, уже все возможности 1-валютных экспертов исследовали и хотите к мультикам перейти.
SymbolID полезен был бы для избавления от строковых полей в структурах (Symbol меняем на ID, коммент в отд. массиве хранить) и от медленного CharArrayToString и StringToCharArray при создании каждого ордера (не знаю насколько он медленный). Плюс по SymbolID хранение параметров символа в отд. массиве.
Из критики: По оптимизации скорости: - зачем в MultiTick( void ) каждый раз создавать массив Ticks[], может где-то 1 раз его создать и потом использовать?
- в CalcOrders( void ) не очень понял что она делает... похоже что при изменении истории, коприрует все ордера по всем символам в один OrdersCounter[]. Но не вижу что с этим дальше делается. Может можно только 1 измененный добавлять? Возможно эти копирования будут медленнее, чем просто пересчитать все инструменты в валюту депозита (~17%) Зачем все инструменты пересчитывать? Если собирать мульти тики с индикаторов на нужных инструментах, то можно как-то передать имя инструмента и только ему делать пересчет. По другим-то изменений нет. Если не с индикаторов, а по тикам основного символа, то да - всех пересчитывать.
- в int CorrectTicksTime( MqlTick &Ticks[] ){ const int Amount = ::MathMin(this.Size, ::ArraySize(Ticks)); размер Ticks вроде бы такой же как this.Size, ниже есть ArrayResize(Ticks, this.Size)
- CorrectTicksTime нужен, чтобы всем инструментам присвоить самое последнее время. Если передаем тики с индикаторов, то можно только его одного пересчитывать (см выше) или его время присвоить всем инстр. Если работаем по тикам основного, то можно без поиска старшего присвоить время тика всем инструментам.
По идее: как я понял, для каждого символа делается свой new ORDERS и все расчеты идут в своих массивах. С одной стороны удобно смотреть баланс по конкретному инструменту. Но обычно мультивалютник на одном балансе работает. Как потом сделать общий отчет, общий баланс? Видимо и итоговых отчетов через Report.mqh надо несколько делать? Я думал, что можно сделать мультик в лоб - в одном ORDERS: прописывать символ (не использовать основные _Symbol, _Points... а из заранее заполненных переменных брать) и пересчитывать всё в валюту депозита. Пересчет добавит ~17%, но будет точный результат. Если не пересчитывать, а оставлять в пипсах, то будет конечно несоразмерный общий баланс, но он и с отдельными массивами несоразмерный, да еще и склеивать их как то придется (если по последней цене, то тоже несоразмерно, за полгода стоимость инструментов может и на 10% измениться). Как вариант аналогичный вашему, балансы разным инструментам в пипсах можно и в одном ORDERS считать по SymbolID, чтобы потом анализировать отдельные символы, если надо.
SymbolID полезен был бы для избавления от строковых полей в структурах (Symbol меняем на ID, коммент в отд. массиве хранить) и от медленного CharArrayToString и StringToCharArray при создании каждого ордера (не знаю насколько он медленный).
shortest pass 0:00:00.119,
убрал вызовы CharArrayToString и StringToCharArray вместо них сохраняю NULL и возвращаю "" shortest pass 0:00:00.114, - 4,4 % ускорения
Плюс ускорится, если функция не будет содержать входных параметров типа string .
Для авторизации и пользования сайтом MQL5.com необходимо разрешить использование файлов Сookie.
Пожалуйста, включите в вашем браузере данную настройку, иначе вы не сможете авторизоваться.
Вы вошли в аккаунт. Двухфакторная аутентификация отключена. Настройте её заново и отсканируйте новый QR-код или продолжите с отключённой двухфакторной аутентификацией.
Интересно стало почему же моя ф-я расчета комиссии по % в USD на лот добавляла аж 60%.
1) Я использовал GetSymbol() для передачи в функцию, а там работа с CharArrayToString - основная часть тормозов была тут, решил просто передавать _Symbol, но
2) оказывается если в функцию передается строка, то она вызывается намного медленнее, чем без неё. Если делать мультивалютник, то надо будет передавать SymbolID.
Новые замеры:
без моих ф-й, как ваш вариант, но со всеми ускорителями обнаруженными ранее:
shortest pass 0:00:00.152, longest pass 0:00:00.326, average pass 0:00:00.155 EURUSD USD
shortest pass 0:00:00.171, longest pass 0:00:00.372, average pass 0:00:00.173 по EURCHF EUR
Все мои функции
shortest pass 0:00:00.158, longest pass 0:00:00.323, average pass 0:00:00.160 по EURUSD USD без запроса цен - замедление 3%
Все с сложным расчетом по EURCHF CHF с запросом 2х цен
shortest pass 0:00:00.180, longest pass 0:00:00.381, average pass 0:00:00.183 - замедление 6%
Итого: от 3 до 6% замедления за получение комиссии, свопов, контроля времени и перевода прибыли в валюту депозита.
Symbol можно хранить в массиве структур символов и их параметров (для свопов, торг. сессии и т.д.), в осн. расчетах использовать только SymbolID.Наверное все таки буду пользоваться.
ПС. У вас там тоже есть обращения к GetSymbol() в Order_Base(), но они вроде нечасто вызываются, не смотрел детально. Отказаться бы от CharArrayToString.
А для комментариев может отдельный строковый массив сделать и использовать ID ордеров для чтения/записи.
Если делать мультивалютник, то надо будет передавать SymbolID.
На мультивалютке море проблем, это только одна из них, к сожалению.
ПС. У вас там тоже есть обращения к GetSymbol() в Order_Base(), но они вроде нечасто вызываются, не смотрел детально. Отказаться бы от CharArrayToString.
Для Тестера можете попробовать вместо _Symbol использовать NULL. Честно говоря, не понимаю, почему MQ выбрали дорогой string для SymbolInfo-функций.
На мультивалютке море проблем, это только одна из них, к сожалению.
Для Тестера можете попробовать вместо _Symbol использовать NULL.
Пока в отдельных переменных храню, но можно в массив структур для индексации инструментов все скинуть.
При пересчете их в валюту депозита нужна цена по первому тику в момент начисления каждый день. Расчет в конце теста, дает свою цену. Т.е. совпадения с тестером MQ не получилось.
Сделал проверку ролловера на каждом тике и расчет свопов на лету в NewTick( const MqlTick &Tick ):
Тормозов нет, т.к. на каждом тике всего 1 раз делается time / 86400, и 1 проверка Day > prewDay_. Все остальные расчеты 1 раз в день. Очень быстро. Всего на 1мс или 0,6% медленнее.
Для замера скорости, решил исключить влияние увеличения размера массива истории и задал ему сразу 200000 строк, в тесте около 150000 будет использовано. Это дало ускорение на 11%
массив 2000 без всех доп. функций
2023.12.07 18:13:21.984 Statistics shortest pass 0:00:00.184,
массив 200000 без всех доп. функций
2023.12.07 18:23:12.170 Statistics shortest pass 0:00:00.165, 11% быстрее от 184
свопы - массив 200000
2023.12.07 18:27:50.399 Statistics shortest pass 0:00:00.166, на 0,6% медленнее от 165
торг сессия - массив 200000
2023.12.07 18:38:46.890 Statistics shortest pass 0:00:00.165, то же время
пересчет в валюту депозита - массив 200000
2023.12.07 18:40:02.505 Statistics shortest pass 0:00:00.175, на 6% медленнее от 165
комиссия в % - массив 200000
2023.12.07 18:41:25.570 Statistics shortest pass 0:00:00.184, на 11% медленнее от 165
всё - массив 200000
2023.12.07 18:42:40.264 Statistics shortest pass 0:00:00.194, на 17% медленнее от 165
Итого свопы на лету и контроль торговой сессии можно добавить без потери скорости. Остальное можно включать по необходимости.
ПС.
Столкнулся с тем, что свопы были начислены случайным сделкам открытым сразу после ролловера. Видимо ячейка Swap не переписывается при перемещении строки на новое место в массиве сделок (когда она не задана). Задал в создании ордера Create(...){.... this.Swap = 0.0; ...} После этого лишние свопы не появляются.
Соответствие с тестром MQ точное, кроме инструментов, у которых свопы начисляются в конце теста, если их вычесть, то получится точно. Если исправят тестер MQ к одинаковому поведению, то будет точно совпадать со всеми инструментами.
Если делать мультивалютник, то надо будет передавать SymbolID.
Результат.
Остальное дописать - рутина, т.к. архитектурно все решилось. Реализация довольно простая. SymbolID не понадобился. Критика приветствуется.
Итого свопы на лету и контроль торговой сессии можно добавить без потери скорости. Остальное можно включать по необходимости.
Архитектурно создал мультивалютный Virtual. Можно запустить у себя на таком примере.
...
Остальное дописать - рутина, т.к. архитектурно все решилось. Реализация довольно простая. SymbolID не понадобился. Критика приветствуется.
Класс! Что можно тестировать? Что должно работать, а что нет?
Класс! Что можно тестировать? Что должно работать, а что нет?
Пока не сделан выбор по тикету, поэтому не работают функции, где это используется: OrderModify, OrderDelete, OrderClose, SELECT_BY_TICKET.
Как тикеты будут сделаны, станет полноценным. Сейчас можно (не проверял) выставить маркет/отложку с SL/TP и увидеть все этапы срабатывания.
Работает внутреннее время Обзора рынка.
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
Библиотеки: Virtual
fxsaber, 2023.12.08 00:42
Например, секунду назад был последний тик EURUSD, а сейчас пришел тик GBPUSD. На этом тике решили открыть позицию по EURUSD - время открытия будет равно тику GBPUSD.
Архитектурно создал мультивалютный Virtual. Можно запустить у себя на таком примере.
Результат.
Остальное дописать - рутина, т.к. архитектурно все решилось. Реализация довольно простая. SymbolID не понадобился. Критика приветствуется.
Все таки решили мультивалютник сделать. Возможно понадобится и мне, после тестов на одной валюте. Я только начинаю работу с виртуалкой и начну с 1 валюты (и боюсь это минимум на полгода-год), а вы, видимо, уже все возможности 1-валютных экспертов исследовали и хотите к мультикам перейти.
SymbolID полезен был бы для избавления от строковых полей в структурах (Symbol меняем на ID, коммент в отд. массиве хранить) и от медленного CharArrayToString и StringToCharArray при создании каждого ордера (не знаю насколько он медленный). Плюс по SymbolID хранение параметров символа в отд. массиве.
Из критики:По оптимизации скорости:
- зачем в MultiTick( void ) каждый раз создавать массив Ticks[], может где-то 1 раз его создать и потом использовать?
- в CalcOrders( void ) не очень понял что она делает... похоже что при изменении истории, коприрует все ордера по всем символам в один OrdersCounter[]. Но не вижу что с этим дальше делается. Может можно только 1 измененный добавлять? Возможно эти копирования будут медленнее, чем просто пересчитать все инструменты в валюту депозита (~17%)
Зачем все инструменты пересчитывать? Если собирать мульти тики с индикаторов на нужных инструментах, то можно как-то передать имя инструмента и только ему делать пересчет. По другим-то изменений нет. Если не с индикаторов, а по тикам основного символа, то да - всех пересчитывать.
- в int CorrectTicksTime( MqlTick &Ticks[] ){
const int Amount = ::MathMin(this.Size, ::ArraySize(Ticks));
размер Ticks вроде бы такой же как this.Size, ниже есть ArrayResize(Ticks, this.Size)
- CorrectTicksTime нужен, чтобы всем инструментам присвоить самое последнее время. Если передаем тики с индикаторов, то можно только его одного пересчитывать (см выше) или его время присвоить всем инстр. Если работаем по тикам основного, то можно без поиска старшего присвоить время тика всем инструментам.
По идее:
как я понял, для каждого символа делается свой new ORDERS и все расчеты идут в своих массивах. С одной стороны удобно смотреть баланс по конкретному инструменту.
Но обычно мультивалютник на одном балансе работает. Как потом сделать общий отчет, общий баланс? Видимо и итоговых отчетов через Report.mqh надо несколько делать?
Я думал, что можно сделать мультик в лоб - в одном ORDERS: прописывать символ (не использовать основные _Symbol, _Points... а из заранее заполненных переменных брать) и пересчитывать всё в валюту депозита. Пересчет добавит ~17%, но будет точный результат. Если не пересчитывать, а оставлять в пипсах, то будет конечно несоразмерный общий баланс, но он и с отдельными массивами несоразмерный, да еще и склеивать их как то придется (если по последней цене, то тоже несоразмерно, за полгода стоимость инструментов может и на 10% измениться). Как вариант аналогичный вашему, балансы разным инструментам в пипсах можно и в одном ORDERS считать по SymbolID, чтобы потом анализировать отдельные символы, если надо.
SymbolID полезен был бы для избавления от строковых полей в структурах (Symbol меняем на ID, коммент в отд. массиве хранить) и от медленного CharArrayToString и StringToCharArray при создании каждого ордера (не знаю насколько он медленный).
убрал вызовы CharArrayToString и StringToCharArray вместо них сохраняю NULL и возвращаю ""
shortest pass 0:00:00.114, - 4,4 % ускорения
Плюс ускорится, если функция не будет содержать входных параметров типа string .