Как тестируете стратегии для биржи? - страница 6

fxsaber #:
Валидная (за редким исключением) цена - последняя в серии last-цен. Промежуточные никакого отношения к возможному совершению сделок не имеют.

Да, но заранее нельзя определить какой тик последний в серии.

Мой вариант тоже рабочий, просто на несколько тиков больше будет и проверок на SL>=Last. Чуть медленнее тестирование будет, чем с одним самым последним Last.

Если только для сохранения в кастом или для мат. режима, то можно через промежуточный массив сделать и еще 20-50% тиков Last выкинуть. Но я делаю вариант, который и для сохранения годится и на лету будет отклонять ненужные Last тики и в штатной работе тестера они будут отклоняться сразу на входе в OnTick() и тоже давать ускорение.

Aleksei Kuznetsov #:

Сделал такой корректировщик Bid/Ask по Last выбираемым из стакана

Корректирую Bid/Ask только первого тика на новом уровне, а не последнего, так как заранее неизвестно какой тик будет последним и по цене уровня и в серии.

Заодно сделал пропуск Last-ов по одинаковой цене, для стратегий не использующих объемы.

Вызов такой:

Почистил тики с одинаковым Last так как volume не использую, объем файлов для мат. режима с 240 до 107 мб сократился, т.е. будет в 2+ раза быстрее тестироваться. Кастомный символ примерно так же сократится.

Усовершенствовал и еще около 20% тиков выкинул:
Добавил в флаги метку Ask или Bid.
Часто после тика Ask или Bid следовал первый тик Last с ценой Ask или Bid. В итоге были полные дубликаты по ценам. Их можно пропустить.
bool Correct_last(MqlTick &Tick, bool skip_same_last_){// skip_same_last_ - пропустить тики с той же ценой, если объемы не важны для стратегии, это ускорит работу тестера
  static double prev_ask=-1,prev_bid=-1;
  if((Tick.flags&TICK_FLAG_LAST)>0){//Last тик
    if((Tick.flags&TICK_FLAG_SELL)>0){//Last Sell тик
      if(prev_bid==Tick.last){return skip_same_last_;}//пропустить тики с той же ценой, если skip_same_last_==true
      Tick.bid=prev_bid=Tick.last;//корректировать только 1-й тик на уровне
      Tick.flags|=TICK_FLAG_BID;
    }
    else if((Tick.flags&TICK_FLAG_BUY)>0){//Last Buy тик
      if(prev_ask==Tick.last){return skip_same_last_;}//пропустить тики с той же ценой, если skip_same_last_==true
      Tick.ask=prev_ask=Tick.last;//корректировать только 1-й тик на уровне
      Tick.flags|=TICK_FLAG_ASK;
    }
  }else{// для пропуска первого Last по цене bid или ask, сократит число тиков на 15-20%
    if((Tick.flags&TICK_FLAG_BID)>0){prev_bid=Tick.bid;}
    else if((Tick.flags&TICK_FLAG_ASK)>0){prev_ask=Tick.ask;}
  }
  return false;
}

Результат чистки и правки тиков для одной из серий:(нашел большую серию, большинство намного меньше)

Bid менялся от 15 до 11 и в работу уйдет всего 5 тиков вместо 21
Стобцы: time,bid,ask,last,volume,volume dbl,flag

Все тики   Скорректированные
2025.09.04 15:37:48.220 131.15 131.16 131.14 1 1.0 2
2025.09.04 15:37:50.424 131.15 131.16 131.15 5 5.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.15 75 75.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.15 18 18.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.15 10 10.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 264 264.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 80 80.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 95 95.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 300 300.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 1 1.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 1 1.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 10 10.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 80 80.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 60 60.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 150 150.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 80 80.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 60 60.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 150 150.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 200 200.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 5 5.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 25 25.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 200 200.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 136 136.0 344
2025.09.04 15:37:55.681 131.11 131.16 131.11 136 136.0 2
2025.09.04 15:37:48.220 131.15 131.16 131.14 1 1.0 2
2025.09.04 15:37:55.680 131.14 131.16 131.14 264 264.0 346
2025.09.04 15:37:55.680 131.13 131.16 131.13 10 10.0 346
2025.09.04 15:37:55.680 131.12 131.16 131.12 80 80.0 346
2025.09.04 15:37:55.680 131.11 131.16 131.11 5 5.0 346
2025.09.04 15:37:55.681 131.11 131.16 131.11 136 136.0 2

Aleksei Kuznetsov #:

Да, но заранее нельзя определить какой тик последний в серии.

MQ в своем Тестере это сделать могут при желании.

Мой вариант тоже рабочий, просто на несколько тиков больше будет и проверок на SL>=Last.

Тогда можно будет совершать сделки по промежуточным ценам, чего в реальности быть не может.

Если только для сохранеия в кастом или для мат режима, то можно через промежуточный массив сделать и еще 20-50% тиков Last выкинуть.

Почти каждая серия last-цен заменятся на один bid/ask. Причем в некоторых случаях нужно делать так.

PrevTick.bid = PrevTick.last - TickSize; // PrevTick.ask = PrevTick.last + TickSize;

Т.е. иногда точно можно сказать, что цена даже хуже последнего last в серии.

fxsaber #:

MQ в своем Тестере это сделать могут при желании.

Могут, будем надеяться что сделают.

Aleksei Kuznetsov #:

Если только для сохранения в кастом или для мат. режима, то можно через промежуточный массив сделать и еще 20-50% тиков Last выкинуть. 

Еще вариант без промежуточного массива: 
задерживать все тики до изменения миллисекунды, и при наступлении новой, отдавать задержанную с последней ценой и сразу же новую. И заодно объем можно суммировать. Для сохранения тиков в кастом или мат. режим - задержка не критична, а вот на лету может быть...

Попробую сделать.

Aleksei Kuznetsov #:

Могут, будем надеяться...

Еще вариант без промежуточного массива: 
задерживать все тики до изменения миллисекунды, и при наступлении новой, отдавать задержанную с последней ценой и сразу же новую. И заодно объем можно суммировать. Для сохранения тиков в кастом или мат. режим - задержка не критична, а вот на лету может быть...

Попробую сделать.

Наверно нет смысла к миллисекунде привязываться - пока идут Last-ы их все и задерживать до первого Ask|Bid. Может несколько серий объединит. 
Но тогда можно просто следующий Ask|Bid использовать, как изначально предлагал.
Aleksei Kuznetsov #:
Но тогда можно просто следующий Ask|Bid использовать, как изначально предлагал.

В таком случае, самое простое и правильное решение для MQ:

сделать триггер для SL не по цене Last как сейчас (для которой записан старый Ask|Bid), а просто по Ask|Bid ценам которые придут после нескольких Last (или без них), тогда автоматически применится актуальный Ask|Bid.

Для записи тиков в кастомный символ или для мат. режима - просто пропускать все Last тики, тогда все будет работать по новым актуальным Ask|Bid.
Aleksei Kuznetsov #:

В таком случае, самое простое и правильное решение для MQ:

сделать триггер для SL не по цене Last как сейчас (для которой записан старый Ask|Bid), а просто по Ask|Bid ценам которые придут после нескольких Last (или без них), тогда автоматически применится актуальный Ask|Bid.

Для записи тиков в кастомный символ или для мат. режима - просто пропускать все Last тики, тогда все будет работать по новым актуальным Ask|Bid.

Вот функция такой чистки Last-ов

bool Skip_last(MqlTick &t, bool add_volume){//Пропустит все Last тики, если add_volume=T, то сохранит сумму объемов и последнюю цену из Last в первые после них Ask/Bid тики. Если тик и Ask и Bid, то будет сумма покупок и продаж. Если только Ask или Bid, то по отдельности.
  static double vB=0,vS=0,lastB=0,lastS=0;
  if((t.flags&TICK_FLAG_LAST)>0){//Last тик
    if(add_volume){//посчитать сумму объемов
      if((t.flags&TICK_FLAG_SELL)>0){vS+=t.volume_real; lastS=t.last;}//Last Sell тик
      if((t.flags&TICK_FLAG_BUY )>0){vB+=t.volume_real; lastB=t.last;}//Last Buy тик
    }
    return true;//пропустить Last тик
  }else{//Ask/Bid тик
    if((t.flags&(TICK_FLAG_BID|TICK_FLAG_ASK))>0){t.volume_real=0;t.volume=0;}//сейчас (2026) там объем от предыдущего Last
    if(vS>0 && (t.flags&TICK_FLAG_BID)>0){t.volume_real+=vS;t.volume=(ulong)t.volume_real; t.flags|=TICK_FLAG_SELL|TICK_FLAG_VOLUME|TICK_FLAG_LAST; t.last=lastS; vS=0;}
    if(vB>0 && (t.flags&TICK_FLAG_ASK)>0){t.volume_real+=vB;t.volume=(ulong)t.volume_real; t.flags|=TICK_FLAG_BUY |TICK_FLAG_VOLUME|TICK_FLAG_LAST; t.last=lastB; vB=0;}
  }
  return false;//обработать Ask/Bid тик
}

Распчатал туже серию, что и выше Bid тиками до и после, объемы просуммировал и добавил к Ask/Bid, к флагам добавились TICK_FLAG_SELL|TICK_FLAG_VOLUME|TICK_FLAG_LAST

Стобцы: time,bid,ask,last,volume,volume dbl,flag

Все тики   Скорректированные
2025.09.04 15:37:48.220 131.15 131.16 131.14 1 1.0 2
2025.09.04 15:37:50.424 131.15 131.16 131.15 5 5.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.15 75 75.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.15 18 18.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.15 10 10.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 264 264.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 80 80.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 95 95.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 300 300.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 1 1.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.14 1 1.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 10 10.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 80 80.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 60 60.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.13 150 150.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 80 80.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 60 60.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 150 150.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.12 200 200.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 5 5.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 25 25.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 200 200.0 344
2025.09.04 15:37:55.680 131.15 131.16 131.11 136 136.0 344
2025.09.04 15:37:55.681 131.11 131.16 131.11 136 136.0 2
2025.09.04 15:37:48.22 131.15 131.16 131.14 16 16.0 90
2025.09.04 15:37:55.681 131.11 131.16 131.11 2005 2005.0 90


И если не сохранять объемы, то 

2025.09.04 15:37:48.22 131.15 131.16 0.0 0 0.0 0
2025.09.04 15:37:55.681 131.11 131.16 0.0 0 0.0 0

Объем файлов сократился еще сильнее до 63 мб (было с 240мб до 107мб в предыдущей версии с 1 Last на каждом уровне)

В мат. режиме с Virtual положительные проскальзывания из SL исчезли, то что и хотел получить, так как это правильно.

Но появились положительные проскальзывания для лимитных ордеров при открытии позиций. А это думаю, плохо, так как в реальности исполнится точно. 
То есть для Stop ордеров нужно учитывать Ask/Bid, что я и сделал. А для лимитных нужен свой тригер в Virtual для биржевого исполнения.

Проскальзывания из Virtual в мат режиме с удаленными Last:

MQ тестер показывает такие проскальзывания (все входы по лимиткам с 0 проскальзыванием, кроме одного +200)


Aleksei Kuznetsov #:

Но появились положительные проскальзывания для лимитных ордеров при открытии позиций. А это думаю, плохо, так как в реальности исполнится точно. 

То есть для Stop ордеров нужно учитывать Ask/Bid, что я и сделал. А для лимитных нужен свой тригер в Virtual для биржевого исполнения.

Так отключите.
// #define VIRTUAL_LIMITS_TP_SLIPPAGE // Лимитники и TP исполняются по первой цене акцепта - положительные проскальзывания
fxsaber #:
Так отключите.

Да, тоже уже нашел. Как раз тестирую. Жаль, что дефайнами сделано, а не через переменные. Отдельную версию для бирж надо держать и отдельную для форекса.

Протестировал, теперь как надо:

Aleksei Kuznetsov #:

Вот функция такой чистки Last-ов

Лучше написать такую.
// Возвращает массив с правильными bid/ask, без last.
int ConvertTicks( const MqlTick &TicksIn[], MqlTick &TicksOut[] );