Пример математически правильной Торговой Системы

5 марта 2020, 06:16
fxsaber
12
945

Ниже математически правильная ТС с проверкой, что это так.

Логика ТС следующая: переворачивается вовнутрь, когда цена с запасом пересекает EMA-шку от цены.

// Пример математически правильной ТС с возможностью ее проверки в MT5-Тестере.
// https://www.mql5.com/ru/forum/333746

input group "EA"
input int inPeriod = 100;       // Период EMA-шки
input double inFilter = 0.0005; // Фильтр сделок

input group "Symbol"
input bool inReverse = false;   // Переворот символа
input double inKoef = 1;        // На столько умножаем символ

#include <fxsaber\Virtual\Virtual.mqh> // https://www.mql5.com/ru/code/22577

int OnInit()
{
  return(!MQLInfoInteger(MQL_TESTER) || !VIRTUAL::SelectByHandle(VIRTUAL::Create())); // Создали виртуальное торговое окружение и вошли в него.
}

// Обычная EMA-шка.
struct EMA
{
private:
  double Value;       // Текущее значение EMA.

public:
  const double Alpha; // Коэффициент EMA (обратен периоду).

  EMA( const int period ) : Alpha(1.0 / period), Value(0)
  {
  }

  // Возвращает значение EMA после добавления нового члена в нее.
  double Get( const double NewValue )
  {
    if (this.Value) // Если не первый запуск, вычисляем EMA через классическую итерацию.
      this.Value += (NewValue - this.Value) * this.Alpha;
    else
      this.Value = NewValue; // Если первый запуск, берем пришедшее значение в качестве EMA.

    return(this.Value);
  }
};

// Логарифмирование тика.
const MqlTick LogTick( const MqlTick &Tick )
{
  MqlTick NewTick = Tick;

  // Логарифмируем обе цены.
  NewTick.bid = MathLog(NewTick.bid);
  NewTick.ask = MathLog(NewTick.ask);

  return(NewTick);
}

// Торговая система
void EA()
{
  static const double Filter = MathLog(1 + inFilter); // С таким запасом нужно будет пересечь EMA для переворота.
  static EMA Ema(inPeriod); // Инициализировали EMA с соответствующим периодом.

  MqlTick Tick;

  if (!SymbolInfoTick(_Symbol, Tick)) // Взяли текущий тик.
    return;

  const MqlTick LogTick = LogTick(Tick); // Логарифмировали его.
  const double Price = Ema.Get((LogTick.bid + LogTick.ask) / 2); // Применили EMA к средней цене.

  const int Type = OrderSelect(0, SELECT_BY_POS) ? OrderType() : -1; // Направление текущей открытой позиции.

  if (LogTick.bid > Price + Filter) // Если bid выше EMA-шки с запасом, переворачиваемся в SELL.
  {
    if (Type != OP_SELL) // Если SELL не открыта.
    {
      if (Type == OP_BUY)                                             // Если BUY открыта,
        OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0); // закроем ее.

      OrderSend(_Symbol, OP_SELL, 1, Tick.bid, 0, 0, 0); // Откроем SELL-позицию.
    }
  }
  else if ((LogTick.ask < Price - Filter) && (Type != OP_BUY)) // Если ask ниже EMA-шки с запасом, переворачиваемся в BUY.
  {
    if (Type == OP_SELL)                                            // Если SELL открыта,
      OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0); // закроем ее.

    OrderSend(_Symbol, OP_BUY, 1, Tick.ask, 0, 0, 0); // Откроем BUY-позицию.
  }
}

// Изменения символа, которые должны быть инвариантны для математически правильной ТС.
void ChangeTick( MqlTick &Tick )
{
  if (inReverse) // Обращаем символ.
  {

    const double Tmp = Tick.bid;

    Tick.bid = 1 / Tick.ask;
    Tick.ask = 1 / Tmp;
  }

  // Домножаем на константу
  Tick.bid *= inKoef;
  Tick.ask *= inKoef;
}

void OnTick()
{
  MqlTick Tick;

  if (_V(0, SymbolInfoTick(_Symbol, Tick)) && Tick.bid && Tick.ask) // _V(0, - берем тик из реального торгового окружения (Тестер).
  {
    ChangeTick(Tick); // Изменяем тик для проверки инвариант-условия математически правильной ТС.

    VIRTUAL::NewTick(Tick, EA); // Пробросили измененный тик в виртуальное окружение с выполнением ТС.
  }
}

input group "HTML"
#define REPORT_TESTER             // В тестере будут автоматически записываться отчеты
#define REPORT_TESTER_INPUTS      // В отчете одиночного прохода будут видны входные параметры советника - требует разрешения DLL.
#define REPORT_BROWSER            // Создание отчета с запуском браузера - требует разрешения DLL.
#include <Report.mqh>             // https://www.mql5.com/ru/code/18801

#include <fxsaber\OnTesterCustom\OnTesterCustom.mqh> // https://www.mql5.com/ru/code/27714

input group "OnTester"
sinput double inMarkup = 20; // Комиссия на миллион (одна сторона)
sinput double inRisk = 0.5;  // Фиксированный риск

double OnTester()
{
  const ONTESTERCUSTOM OnTesterCustom(_Symbol); // Считали историю торговли.

  return(OnTesterCustom.TesterStatistics(ONTESTERCUSTOM_GAIN, inRisk, inMarkup)); // Посчитали прибыльность при заданном риске и размере комиссии.
}


Вся система - это лаконичная EA-функция. Остальной функционал - проверка правильности ТС. Специально привел полный листинг, чтобы было понятно, о чем речь. Код простой.

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

Отчет торговли автоматически появляется в браузере в виде HTML (нужно разрешить использование DLL - WinAPI).


Проверка.

Итак, запускаем на EURUSD по реальным тикам с такими настройками.


Исходный символ не меняется (не домножения и/или переворота).


Получаем в браузере торговый отчет с таким графиком баланса.



И смотрим значение прибыльности ТС.


Собственно, именно это значение и должно быть инвариантом для всех изменений исходного символа. Т.е. как бы я его не менял (согласно ранее озвученным правилам), это значение должно оставаться постоянным.


Замена символа.

Попробуем изменить исходный символ.


Выделенное в настройках показыет, что символ теперь равен 7/EURUSD. Т.е. перевернули EURUSD и потом домножили на семь. После запуска Вы увидите, что график торговли и значение OnTester остались неизменны.

На всякий случай сравним каждый вход/выход до изменения и после. Ниже на скрине оба HTML-отчета.


Видим, что входы идентичны (с точностью до миллисекунды).


Зачем это нужно?

По коду видно, что в торговле нигде не используются размеры пунктов, лотов, маржинальных требований и т.д. Имеем просто голый ненормализованный ценовой ряд и торгуем на нем, соблюдая инвариантность к некоторым изменениям этого ряда. Удовлетворение логики ТС таким простым правилам позволяет с помощью нее легко делать масштабные исследования различных синтетических символов. Избавляет от математической ущербности в построении торговых сигналов. А значит уменьшает вероятность самообмана


Чтобы проверить любую ТС, замените просто EA-функцию на другую.