Скальперские автоматические системы по праву считаются вершиной алгоритмического трейдинга, но при этом они же являются и самыми сложными для написания кода.
В этой статье мы покажем, как с помощью встроенных средств отладки и визуального тестирования строить стратегии, основанные на анализе поступающих тиков. Для выработки правил входа и выхода зачастую требуются годы ручной торговли. Но с помощью MetaTrader 5 вы можете быстро проверить любую подобную стратегию на реальной истории.
Прежде всего, нам необходимо создать индикатор, который будет строить тиковые графики — то есть графики, на которых можно увидеть каждое изменение цены.
Один из первых таких индикаторов вы можете найти в Библиотеке — https://www.mql5.com/ru/code/89. В отличие от обычных, на тиковых графиках при поступлении нового тика необходимо весь график смещать назад.
За основу проверяемой идеи возьмем ряд изменений цены между двумя последовательными тиками, это будет примерно такая последовательность в пунктах:
+1, 0, +2, -1, 0, +1, -2, -1, +1, -5, -1, +1, 0, -1, +1, 0, +2, -1, +1, +6, -1, +1,...
Закон нормального распределения гласит, что 99 % изменений цены между двумя тиками укладывается в пределах 3-х сигм. Мы попробуем в режиме реального времени вычислять на каждом тике среднеквадратичное отклонение и помечать резкие скачки цены значками красного и синего цвета.
Таким образом мы попытаемся визуально выбрать стратегию для использования таких резких выбросов — торговать в направлении изменения или же использовать «возврат к среднему». Как видите, идея совсем простая, и наверняка по этому пути прошло большинство любителей математики.
В MetaEditor запускаем Мастер MQL, задаем имя и два входных параметра:
Далее отмечаем «Индикатор в отдельном окне» и указываем 2 графических построения, которые будут отображать информацию в подокне: линия для тиков и цветные стрелки для сигналов о появлении резких изменений цены.
Внесем в полученную заготовку изменения, которые отмечены желтым
//+------------------------------------------------------------------+ //| TickSpikeHunter.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 2 //--- plot TickPrice #property indicator_label1 "TickPrice" #property indicator_type1 DRAW_LINE #property indicator_color1 clrGreen #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plot Signal #property indicator_label2 "Signal" #property indicator_type2 DRAW_COLOR_ARROW #property indicator_color2 clrRed,clrBlue,C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0' #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- input parameters input int ticks=50; // количество тиков в расчетах input double gap=3.0; // ширина канала в сигмах //--- indicator buffers double TickPriceBuffer[]; double SignalBuffer[]; double SignalColors[]; //--- счетчик изменений цены int ticks_counter; //--- первый вызов индикатора bool first; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TickPriceBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA); SetIndexBuffer(2,SignalColors,INDICATOR_COLOR_INDEX); //--- укажем пустые значения, которые нужно игнорировать при отрисовке PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); //--- сигналы будем выводить в виде этого значка PlotIndexSetInteger(1,PLOT_ARROW,159); //--- инициализация глобальных переменных ticks_counter=0; first=true; //--- успешная инициализация программы return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Теперь осталось добавить код в предопределенный обработчик поступающих тиков OnCalculate(). При первом вызове функции явно обнулим значения в индикаторных буферах, а также для удобства установим для них признак таймсерии — таким образом индексация у них будет справа налево.
Это позволит обращаться к самому свежему значению индикаторного буфера по индексу ноль, то есть в TickPriceBuffer[0] будет храниться значение последнего тика.
Кроме того, основную обработку тиков мы вынесем в отдельную функцию ApplyTick():
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- при первом вызове обнулим индикаторные буферы и установим признак серии if(first) { ZeroMemory(TickPriceBuffer); ZeroMemory(SignalBuffer); ZeroMemory(SignalColors); //--- массивы серии идут задом наперед, так удобнее в данном случае ArraySetAsSeries(SignalBuffer,true); ArraySetAsSeries(TickPriceBuffer,true); ArraySetAsSeries(SignalColors,true); first=false; } //--- возьмем в качестве цены текущее значение Close double lastprice=close[rates_total-1]; //--- считаем тики ticks_counter++; ApplyTick(lastprice); // проведем вычисления и сдвиг в буферах //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| применяет тик для вычислений | //+------------------------------------------------------------------+ void ApplyTick(double price) { int size=ArraySize(TickPriceBuffer); ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1); ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1); ArrayCopy(SignalColors,SignalColors,1,0,size-1); //--- запишем последнее значение цены TickPriceBuffer[0]=price; //--- }
Функция ApplyTick() пока производит самые простые действия — сдвигает все значения буфера на одну позицию вглубь истории и пишет в TickPriceBuffer[0] последний тик. Запускаем индикатор под отладкой и наблюдаем некоторое время.
Видим, что цена Bid, по которой строится Close текущей свечи, очень часто остается неизменной, и поэтому график рисуется кусками «плато». Немного подправим код, чтобы получать только «пилу» — так глазу более понятно.
//--- вычисляем только если цена изменилась if(lastprice!=TickPriceBuffer[0]) { ticks_counter++; // считаем тики ApplyTick(lastprice); // проведем вычисления и сдвиг в буферах }
Итак, первую версию индикатора мы создали, теперь у нас не бывает нулевых приращений цены.
Для вычисления отклонения нам необходим дополнительный массив, который будет хранить приращения цены на каждом тике. В качестве такого массива добавим еще один индикаторный буфер и добавим соответствующий код в нужных местах:
#property indicator_separate_window #property indicator_buffers 4 #property indicator_plots 2 ... //--- indicator buffers double TickPriceBuffer[]; double SignalBuffer[]; double DeltaTickBuffer[]; double ColorsBuffers[]; ... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TickPriceBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA); SetIndexBuffer(2,SignalColors,INDICATOR_COLOR_INDEX); SetIndexBuffer(3,DeltaTickBuffer,INDICATOR_CALCULATIONS); ... } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const ...) //--- при первом вызове обнулим индикаторные буфера и установим признак серии if(first) { ZeroMemory(TickPriceBuffer); ZeroMemory(SignalBuffer); ZeroMemory(SignalColors); ZeroMemory(DeltaTickBuffer); //--- массивы серии идут задом наперед, так удобнее в данном случае ArraySetAsSeries(TickPriceBuffer,true); ArraySetAsSeries(SignalBuffer,true); ArraySetAsSeries(SignalColors,true); ArraySetAsSeries(DeltaTickBuffer,true); first=false; } ... return(rates_total); } //+------------------------------------------------------------------+ //| применяет тик для вычислений | //+------------------------------------------------------------------+ void ApplyTick(double price) { int size=ArraySize(TickPriceBuffer); ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1); ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1); ArrayCopy(SignalColors,SignalColors,1,0,size-1); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,size-1); //--- запишем последнее значение цены TickPriceBuffer[0]=price; //--- вычислим разницу с предыдущим значением DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1]; //--- получим ср.кв. отклонение double stddev=getStdDev(ticks);
Теперь мы готовы вычислить среднеквадратичное отклонение. Сначала напишем функцию getStdDev(), которая делает все вычисления«в лоб», пробегая по всем элементам массива столько циклов, сколько нужно.
//+------------------------------------------------------------------+ //| вычисляет стандартное отклонение "в лоб" | //+------------------------------------------------------------------+ double getStdDev(int number) { double summ=0,sum2=0,average,stddev; //--- считаем сумму изменений и вычисляем матожидание for(int i=0;i<ticks;i++) summ+=DeltaTickBuffer[i]; average=summ/ticks; //--- теперь считаем среднеквадратичное отклонение sum2=0; for(int i=0;i<ticks;i++) sum2+=(DeltaTickBuffer[i]-average)*(DeltaTickBuffer[i]-average); stddev=MathSqrt(sum2/(number-1)); return (stddev); }
Затем там же допишем блок, который отвечает за выставление сигналов на тиковом графике — установку кружков красного и синего цвета
//+------------------------------------------------------------------+ //| применяет тик для вычислений | //+------------------------------------------------------------------+ void ApplyTick(double price) { int size=ArraySize(TickPriceBuffer); ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1); ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1); ArrayCopy(SignalColors,SignalColors,1,0,size-1); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,size-1); //--- запишем последнее значение цены TickPriceBuffer[0]=price; //--- вычислим разницу с предыдущим значением DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1]; //--- получим ср.кв. отклонение double stddev=getStdDev(ticks); //--- если изменение цены превысило заданный порог if(MathAbs(DeltaTickBuffer[0])>gap*stddev) // при первом тике будет показан сигнал, оставим как фичу { SignalBuffer[0]=price; // поставим точку string col="Red"; // по умолчанию, точка красного цвета if(DeltaTickBuffer[0]>0) // цена резко выросла { SignalColors[0]=1; // тогда точка синего цвета col="Blue"; // запомним для вывода в лог } else // цена резко упала SignalColors[0]=0; // точка красного цвета //--- выведем запись в журнал Экспертов PrintFormat("tick=%G change=%.1f pts, trigger=%.3f pts, stddev=%.3f pts %s", TickPriceBuffer[0],DeltaTickBuffer[0]/_Point,gap*stddev/_Point,stddev/_Point,col); } else SignalBuffer[0]=0; // нет сигнала //--- }
Нажимаем кнопку F5 (Начало отладки/продолжение выполнения) и наблюдаем в терминале MetaTrader 5, как работает наш индикатор.
Теперь пришло время заняться отладкой кода, которая позволит выявить ошибки и ускорить работу программы.
Для программ, работающих в режиме реального времени, критически важна скорость выполнения. Среда разработки MetaEditor позволяет удобно и быстро оценивать затраты времени на выполнение тех или иных участков программы.
Для этого необходимо запустить профилирование кода и дать поработать программе некоторое время. Для профилировки индикатора будет достаточно минуты.
Как видите, большая часть времени (95.21%) ушла на отработку функции ApplyTick(), которая была вызвана 41 раз из функции OnCalculate(). Сама же OnCalculate() вызывалась 143 раза, но только в 41 случае цена в пришедшем тике отличалась от цены предыдущего.
При этом в самой функции ApplyTick() большую часть времени заняли вызовы функции ArrayCopy(), которые выполняют только вспомогательные действия и не производят вычислений, ради которых и был задуман данный индикатор. Вычисление среднеквадратичного отклонения на 111 строке кода заняло только 0.57% общего времени выполнения программы.
Постараемся уменьшить непроизводительные затраты, для этого попробуем копировать не все элементы массивов (TickPriceBuffer и т.д), а только 200 последних. Ведь нам на графике достаточно будет видеть 200 последних значений, к тому же количество тиков за одну торговую сессию может достигать десятков и сотен тысяч.
Просматривать их все нет необходимости. Поэтому введем входной параметр shift=200, который задает количество сдвигаемых значений. Добавьте в код строки, выделенные желтым:
//--- input parameters input int ticks=50; // кол-во тиков в расчетах input int shift=200; // кол-во сдвигаемых значений input double gap=3.0; // ширина канала в сигмах ... void ApplyTick(double price) { //--- сколько элементов сдвигаем в индикаторных буферах на каждом тике int move=ArraySize(TickPriceBuffer)-1; if(shift!=0) move=shift; ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,move); ArrayCopy(SignalBuffer,SignalBuffer,1,0,move); ArrayCopy(SignalColors,SignalColors,1,0,move); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,move);
Запускаем заново профилировку и видим новый результат — время на копирование массивов упало в в сотни или тысячи раз, теперь основное время занимает вызов StdDev(), которая отвечает за вычисление среднеквадратичного отклонения.
Таким образом, мы ускорили работу функции ApplyTick() на несколько порядков, что даст нам существенную экономию при оптимизации стратегии и при работе программы в режиме реального времени. Ведь вычислительных ресурсов никогда не бывает слишком много.
Иногда даже оптимально написанный код можно заставить работать еще быстрее. В данном случае вычисление среднеквадратичного отклонения можно ускорить, если немного переписать формулу.
Таким образом, мы можем просто вычислять квадрат суммы и сумму квадратов приращений цены — это позволит нам выполнять меньше математических операций на каждом тике. На каждом тике мы просто отнимаем выпадающий элемент массива и добавляем входящий элемент массива в переменные, содержащие суммы.
Создадим новую функцию getStdDevOptimized(), в которой применим уже знакомый метод сдвига значений массива внутри себя.
//+------------------------------------------------------------------+ //| вычисляет стандартное отклонение по формулам | //+------------------------------------------------------------------+ double getStdDevOptimized(int number) { //--- static double X2[],X[],X2sum=0,Xsum=0; static bool firstcall=true; //--- первый вызов if(firstcall) { //--- зададим размер динамических массивов на 1 больше количества тиков ArrayResize(X2,ticks+1); ArrayResize(X,ticks+1); //--- гарантируем себе нулевые значения в начале вычислений ZeroMemory(X2); ZeroMemory(X); firstcall=false; } //--- сдвигаем массивы ArrayCopy(X,X,1,0,ticks); ArrayCopy(X2,X2,1,0,ticks); //--- вычислим новые входящие значения сумм X[0]=DeltaTickBuffer[0]; X2[0]=DeltaTickBuffer[0]*DeltaTickBuffer[0]; //--- вычислим новые суммы Xsum=Xsum+X[0]-X[ticks]; X2sum=X2sum+X2[0]-X2[ticks]; //--- квадрат стандартного отклонения double S2=(1.0/(ticks-1))*(X2sum-Xsum*Xsum/ticks); //--- считаем сумму тиков и вычисляем матожидание double stddev=MathSqrt(S2); //--- return (stddev); }
Добавим в функцию ApplyTick() вычисление среднеквадратичного отклонения вторым способом через функцию getStdDevOptimized() и вновь запустим профилировку.
//--- вычислим разницу с предыдущим значением DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1]; //--- получим ср.кв. отклонение double stddev=getStdDev(ticks); double std_opt=getStdDevOptimized(ticks);
Результат выполнения:
Видно, что новая функция getStdDevOptimized() требует в два раза меньше времени — 4.56%, чем лобовой обсчет в getStdDev() — 9.54%. Она выполняется даже быстрее, чем встроенная функция PrintFormat(), которая использовала 4.74% времени работы программы.
Таким образом, использование оптимального способа вычисления дает еще больший выигрыш по скорости работы программы. Рекомендуем также посмотреть статью 3 метода ускорения индикаторов на примере линейной регрессии.
Кстати, о вызове стандартных функций — в данном индикаторе мы получаем цену из таймсерии close[], которая строится по ценам Bid. Есть еще два способа получить эту цену — с помощью функций SymbolInfoDouble() и SymbolInfoTick(). Добавим эти вызовы в код и снова сделаем профилировку.
Как видите, здесь тоже есть разница по скорости работы. И это понятно, так как чтение готовой цены из close[] не требует затрат по сравнению с вызовом универсальных функций.
При написании индикаторов и торговых роботов нельзя предусмотреть все возможные ситуации, которые могут случиться при онлайн-работе. К счастью, MetaEditor позволяет проводить отладку и на исторических данных.
Просто запустите отладку в режиме визуального тестирования, и вы сможете проверить вашу программу на заданном интервале истории. Вы сможете ускорять, останавливать и прокручивать тестирование до нужной даты.
Важно: в окне Отладка укажите режим моделирования "Каждый тик на основе реальных тиков". Это позволит использовать для отладки реальные записанные котировки, которые хранит у себя торговый сервер. При первом запуске тестирования они автоматически загрузятся на ваш компьютер.
Если эти параметры не заданы в MetaEditor, то при в виузальном режиме тестирования будут использоваться текущие настройки тестера. Укажите в них режим «Каждый тик на основе реальных тиков».
Мы видим, что на тиковом графике появляются странные разрывы. Значит, в алгоритме допущена какая-то ошибка. Неизвестно, сколько времени ушло бы на её проявление при тестировании в реальном времени. В данном случае по выводам в Журнал визуального тестирования видно, что странные разрывы возникают в момент появления нового бара.
Точно! — мы забыли, что при переходе на новый бар размер индикаторных буферов автоматически увеличивается на 1. Внесём исправление в код:
void ApplyTick(double price) { //--- будем запоминать размер массива TickPriceBuffer - он равен кол-ву баров на графике static int prev_size=0; int size=ArraySize(TickPriceBuffer); //--- если размер индикаторных буферов не изменился, то сдвинем все элементы на 1 позицию назад if(size==prev_size) { //--- сколько элементов сдвигаем в индикаторных буферах на каждом тике int move=ArraySize(TickPriceBuffer)-1; if(shift!=0) move=shift; ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,move); ArrayCopy(SignalBuffer,SignalBuffer,1,0,move); ArrayCopy(SignalColors,SignalColors,1,0,move); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,move); } prev_size=size; //--- запишем последнее значение цены TickPriceBuffer[0]=price; //--- вычислим разницу с предыдущим значением
Запустим визуальное тестирование и поставим точку остановки, чтобы поймать момент открытия нового бара. Добавим наблюдаемые значения и убедимся, что всё сделали правильно: количество баров на графике увеличилось на единицу, тиковый объем текущего бара равен 1 — это самый первый тик нового бара.
Ну вот, мы провели оптимизацию кода, исправили ошибки, замерили время выполнения различных функций, теперь индикатор готов к работе. Можно запускать визуальное тестирование и наблюдать, что происходит после появления сигналов на тиковом графике. Можно что-то еще улучшить в коде индикатора?
Перфекционист от кодинга скажет — да! Мы еще не попробовали использовать кольцевой буфер для ускорения работы. Желающие могут проверить сами — дает ли это прирост производительности?
Для написания автоматической торговой системы важно иметь не только удобную среду разработки и мощный язык программирования, но и дополнительные инструменты для отладки и калибровки программы. В этой статье мы показали как:
Разработка индикатора, показывающего торговые сигналы, зачастую является первым необходимым шагом для написания торгового робота. Визуализация помогает выработать торговые правила либо отвергнуть идею еще до начала работы над проектом.
Пользуйтесь всеми возможностями среды разработки MetaEditor для создания эффективных торговых роботов!
Статьи по теме:
Мы написали 4 поколения своих собственных компиляторов к тому же.
Парни с другой планеты. Клепают то, что никому не нужно. А вместо критики начинают обвинять, что я на кого-то работаю. Видимо я из компании Арка, создаю Quik :)))
еще и дебаггер прикрутили. Тоже хорошо
Тестер умеет отлично строить модель рынка на реальных тиках с точностью до миллисекунд по любому набору параллельных символов.
Причем все это можно легко визуализировать штатным визуализатором и смотреть как развивается рынок тик за тиком в обзоре рынка, как изменяются чарты и пересчитываются все профиты.
Единственно, что моделирования стакана нет, только best bid/ask.
И это получается:
— для программистов полный набор функционала и документация
— библиотеки открытого кода с тысячами программ
— встроенный маркет с тысячами программ (платными и бесплатными)
— фриланс сервис, где можно заказать все что угодно
— еще много всего вкусного и бесплатного
Спрос огромный, как бы это не нравилось некоторым.
Хотя можно самостоятельно нарезать (или найти готовый скрипт) нужные таймфреймы и отобразить их.
Исходные тики на любую глубину можно получить через CopyTicks функцию.
Одним словом какова тиковая история?
Простой код скрипта:
MqlTick ticks[];
void OnStart(void)
{
PrintFormat(«Ticks: %d»,CopyTicks(_Symbol,ticks,COPY_TICKS_ALL,0,100000000));
}
выдает даже на истекшем Si-9.16: Ticks: 56 192 184
Да, 56 миллионов тиков одним ударом. Описание функции тут: https://www.mql5.com/ru/docs/series/copyticks
Kraftus, к сожалению, нет
Хотя можно самостоятельно нарезать (или найти готовый скрипт) нужные таймфреймы и отобразить их.
Исходные тики на любую глубину можно получить через CopyTicks функцию.
--------------
где найти готовый скрипт...???
ПЫСЫ цены Вам не будет, если Вы добавите с терминал, например, хотя бы один секундный таймфрейм — 5 сек… заранее спасибо… лучше, конечно меньше, например 3 сек… а там потом можно будет параметрами индюка забрать то, что нужно…
и еще забыл совсем!!!!!!
Сделайте как в квике на якорь нажимаешь и якорь на окне графика, чтобы они подхватывались, а то когда инструмент подгружен сразу на 10 графиках, то не получается быстро все графики перевести на другой инструмент… только это держит покупать подписку у трейдинг вью за 155 долларов… Вы сделайте я бы Вам платил по 150 баксов в год за терминал. за один якорь!!!
То же самое по прибыли позиции. Хотелось бы иметь истинную прибыль позиции без учета переоткрытий при клиринге. Ведь нет же никаких клирингов при тестировании. А для масштабирования стратегий удобнее было бы иметь прибыль в пунктах или в пунктах на контракт. Что за прибыль в деньгах, детский сад ей богу. Даже не вычислить ее ретроспективно, т.к. может меняться цена тика. Короче призываю вас сделать язык, а также библиотеку более биржеориентированными.
Подразумевать не надо — вы торгуете на ФОРТС и используете его схему постоянных переоткрытий позиций. Истинную прибыль позиции можно легко посчитать по истории и связанным идентификаторам. Для этого достаточно написать свою функцию.
Прибыль в пунктах тоже элементарно вычислить — это дело одной функции.
Это же программирование, где берешь и пишешь что тебе нужно.
В стандартную библиотеку мы обязательно добавим функции полного связанного пересчета профитов и цен. Это не сложно.
FIX на Фортсе работает поверх инфраструктуры Плазы и никак не быстрее ее. Соответственно и не быстрее МТ5, плюс минус погрешность конечно.
Если выкинем тормозуху в виде закрытой Плазы, то запросто. MOEX все никак полного и открытого единого протокола не дает к себе. Вместо этого какие-то половинчатые протоколы как на зло.
Это не говоря еще о наших мобильных платформах.
Дело не в платформе, а том, закачали историю на конкретный символ или нет.
В МТ5 хоть миллиарды тиков закачай — отдаст и не поперхнется.
Наберите на этой страничке http://ninjatraderecosystem.com/Partners/Add-Ons-Search.php#78 в поиске DiverProfit они нас не считают идиотами и еще порядка 50 компаний со всего мира тоже не считают идиотами.
Более того у нас уже более 150 постоянных клиентов из разных стран.
Причем мультисимвольные стратегии, когда одновременно моделируется развитие каждого инструмента с миллисекундной точностью.
Просто попробуйте.
Но если речь об МТ5, то там тики абсолютно неотключаемая вещь и всегда доступна любом трейдеру.
Вы можете открыть МТ5 демо-счет на сервере MetaQuotes-Demo, выбрать в броузере символов любые символы MOEX, открыть их и использовать тики в тестере или своих роботах.
Дам десятки миллионов тиков по каждому инструменту есть. И все они доступны бесплатно.
Данные MOEX транслируются с 15 минутной задержкой.