Рынок и физика его глобальных закономерностей

24 декабря 2020, 07:19
Evgeniy Ilin
32
5 647

Введение

В этой статье я попытаюсь дать ответ на вопрос как использовать физику рынка для автоматической торговли. Язык математики — это переход от абстрактности и неопределенности к прогнозу, что позволяет оперировать не какими-то приблизительными и расплывчатыми величинами, а четкими формулами или критериями, для того чтобы повысить качество создаваемых систем. Я не буду придумывать какие-то теории или законы, а буду размышлять только исходя из известных всех фактов, постепенно переводя известные нам факты на язык математического анализа. Физика рынка сама по себе невозможна без математики, потому что сигналы, которые мы генерируем, являются математической субстанцией и никакой другой. Очень многие люди пытаются создавать какие-то теории и формулы без какого либо анализа статистики, либо данная статистика имеет очень ограниченный характер, недостаточный для столь смелых выводов. Только практика является критерием истины. Сначала я постараюсь немного поразмышлять, а потом на основе этих размышлений буду делать советник. После всего этого будет тест советника.


Цена и что она нам дает

В контексте любого рынка есть разный товар, на валютном рынке товаром является сама валюта. Валюта означает право на владение неким товаром или информацией, которая во всем мире ставится в эталон. Возьмём к примеру пару EURUSD и текущее значение её графика. Текущее значение графика будет при этом означать USD/EUR = CurrentPrice, еще можно вот так: USD =  EUR*CurrentPrice, что означает количество долларов, содержащихся в одном евро. Иначе говоря, показывает отношение веса каждой валюты, при этом, конечно, предполагается, что есть некий общий эквивалент обмена для каждой валюты, некий общий товар или что-то иное, представляющее ценность. Цена формируется в стакане, динамика стакана определяет движение цены. При этом всегда стоит помнить, что мы никогда не сможем учесть все факторы формирования цены, к примеру, FORTS завязан на FOREX и оба рынка влияют друг на друга. Я не очень разбираюсь в этом многообразии, но все же способен понять, что все завязано и чем больше каналов данных, тем лучше. Мне видится, что лучше не лезть глубоко в такие дебри, а сфокусироваться на простых вещах, которые двигают цену.

Любую зависимость можно представить в качестве функции многих переменных, как и любую котировку. Изначально цена это:

  • P=P(t)

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

  • P'(t)=Pa'(t)+Pu'(t)

Здесь первое слагаемое отражает ту часть первой, которую можно как-либо проанализировать с помощью математического анализа, вторая составляющая является непредсказуемой частью. Из данной записи сразу же следует то, что невозможно со 100 процентной точностью спрогнозировать величину и направление. Можно не думать о том, каково окажется последнее слагаемое, так как мы не сможем его определить, а вот первое можем. Можно предположить, что это слагаемое можно представить в ином виде, предварительно учтя, что функция цены дискретная и мы не можем применять дифференциальные операции. Но вместо этого мы можем взять среднюю производную за промежуток времени "st". Применительно к цене это будет длительностью бара, применительно к тикам это будет минимальное время между двумя тиками.

  • PaM(t)=(Pa(t)-Pa(t-st))/st - среднее движение цены(производная по времени) за фиксированный промежуток времени
  • Ma(t)=Ma(P(t),P(t-st) + ... + P(t-N*st), D(t),D(t-st) + ... + D(t-N*st),U[1](),U[2](t) + ... + U[N](t) )
  • P(t[i]) - значения старых цен(данные баров или тики)
  • D(t[i]) - значения старых цен на других валютных парах
  • U[i](t) - другие неизвестные или известные величины, влияющие на рынок
  • Ma(t) - математическое ожидание величины PaM(t) в данной точки времени 

Иначе говоря, мы предполагаем что предсказуемая часть цены может зависеть от предыдущих баров или тиков, а также от ценовых данных остальных валютных пар, данных других бирж и мировых событий, при этом нужно понимать, что даже эту часть цены невозможно предсказать стопроцентно, мы лишь можем вычислить некоторые ее характеристики. Такой характеристикой может являться лишь какая-то вероятность или параметры случайной величины, такие, как математическое ожидание, дисперсия, среднеквадратичное отклонение и прочие величины теории вероятностей. Для получения прибыльной торговли нам достаточно оперировать математическими ожиданиями. После всех этих выводов, если не торопиться и подумать хорошенько, то можно прийти к мысли, что анализировать рынок можно и не только с помощью подобной логики. Все дело в том, что предсказуемая часть цены развивается исходя из активности игроков в основном. Можно отбросить те параметры рынка, кроме факторов, которые создают сами игроки. Конечно это все приведёт к понижению достоверности нашего метода анализа, но зато мы упростим себе модель. Только нужно понимать, что чем меньше величина "st", тем точнее наши формулы описывают рынок.

  • VMa(t)=VMa(P(t),P(t-st) + ... + P(t-N*st))
  • VMa(t)=VBuy(t)-VSell(t)
  • VMa(t) - итоговые объёмы
  • VBuy(t) - объёмы открытых ордеров на покупку
  • VSell(t) - объёмы открытых ордеров на продажу

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

Волновая природа рынка на самом деле связана именно с этим простым фактом, просто это лишь один частный случай более общего процесса колебания объёмов позиций, быков и медведей если хотите.

Применительно к барам, можно также учитывать что внутри бара есть 4 цены, это даст нам более качественные формулы. Больше данных, значит, точнее анализ, вот почему важно учитывать все ценовые данные. Единственное, мне очень не нравится учитывать каждый тик, ведь от этих данных скорость наших алгоритмов в десятки раз понижается, не говоря уже о том что тики у всех разные. Главное, что цены открытия и закрытия баров практически идентичны у большинства брокеров. Перепишем функцию объёмов так, чтобы она учитывала все ценовые данные:

  • VMa(t)=VMa(O(t),O(t-st) +...+ O(t-N*st) + C(t),C(t-st) + C(t-N*st),H(t),H(t-st)...H(t-N*st),L(t),L(t-st)...L(t-N*st))

В данную функцию можно добавить ещё и переменные связанные с временем, днями недели, месяцами и неделями, но тогда у нас может получиться много формул, которые привязаны к конкретным участкам рынка, а нам нужна физика всего рынка. Мы будем знать, что сломать её не получится, и сможем использовать до тех пор, пока существует рынок. Ещё одним плюсом будет её мультивалютность.

В практическом плане использование подобного рода представлений не имеет смысла, так как нам нужно знать точно, как построить эту функцию и исходя из чего. Мы не можем просто написать с ходу вид этой функции и определить, что и от чего зависит, но эти выражения могут помочь нам обрести какое-то начальное понимание, как же все это анализировать и перейти к следующим допущениям. Любой набор логических условий в конечном итоге можно представить в виде вот такой функции и, наоборот, саму функцию можно превратить в набор условий. Это не важно, какую форму мы будем использовать, важно просто понимать это. Любой алгоритм можно свести к какой либо единственной формуле. Просто иногда проще описать сигналы в качестве условий или системы условий, чем городить сверхсложную функцию. Да и как ее построить, тоже большой вопрос.

В реальной торговой системе мы не сможем анализировать всю историю сразу, а лишь фиксированный отрезок времени. При этом возможны 4 подхода. Я дам им свои названия и объясню почему названия именно такие:

  • Формулы(индикаторы или свои функции)
  • Симуляция
  • Общая математика
  • Виды машинного обучения

Первый вариант подразумевает что мы используем одну какую-то величину, или набор величин, примером могут служить некоторые индикаторы или собственные формулы. Плюсом данного подхода является наличие достаточно большого инструментария в терминалах MetaTrader 4/5. Также на просторах интернета, да и в Маркете, полно индикаторов, сделанных по популярным теориям рынка. Минус данного подхода в том, что мы в большинстве случаев не будем понимать, на основе каких соображений работает индикатор, да и даже если поймём, то не факт, что эти соображения имеют какую то ценность.

Второй вариант подразумевает отказ от данных, которые мы не понимаем, или от данных, полезность которых стоит под вопросом. Вместо этого мы можем попытаться имитировать ордера на рынке и тем самым мы будем знать, что наша система, хоть и от части, но сможет описывать, сколько позиций открыто в одну сторону, и сколько в другую. Эта информация даст нам необходимые прогнозы и позволит с максимально доступным качеством описать рынок в глобальной перспективе. Это единственная альтернатива машинному обучению.

Под математикой подразумевается понимание каких-то фундаментальных законов или знание определенных математических принципов, которые позволяют эксплуатировать любую котировку вне зависимости от того, какая сейчас ситуация на рынке. Дело в том, что любым функциям, в том числе дискретным, присущи определенные характеристики, которые можно эксплуатировать. Все это, конечно, при условии, что зависимость не носит хаотичный характер (в нашем случае форекс не хаотичен, поэтому любую котировку можно эксплуатировать). В следующей статье я разберу один из таких принципов, которые знают практически все. Но знать и уметь пользоваться — это разные вещи. Плюс данного подхода в том, что в случае построения удачной системы нам не нужно будет беспокоиться о том, как она себя поведет в будущем, так как мы будем знать, что для изменений не может быть никаких причин. 

Четвертый подход — самый прогрессивный, так как с помощью машинного обучения можно выжать максимум из любых данных. Чем больше у вас вычислительных мощностей, тем выше будет качество анализа. Минусом данного подхода будет то, что мы не сможем понять физику рынка. Плюс в простоте подхода, максимальном качестве результатов при минимуме затраченного времени. Но в рамках данной статьи данный подход не применим.


О закономерностях

В повседневной жизни трейдера существует много терминов, некоторые из них менее важные, другие крайне важны. Есть такие термины, которые мы используем постоянно, но мне почему-то кажется, что лишь небольшой процент трейдеров понимает, что на самом деле означают эти термины и что они нам дают. Одним из таких терминов является закономерность. Я попробую объяснить, что это такое, языком математики. Закономерность всегда привязана к конкретному промежутку времени и конкретной валютной паре и периоду графика. Есть более сильные закономерности. Такие закономерности носят мультивалютный либо глобальный характер, либо оба варианта. Идеальной закономерностью называют Грааль. Есть некоторые утверждения, которые справедливы для любой закономерности:

  • Наличие формулы или набора условий, символизирующие закономерность
  • Минимальные показатели теста в тестере стратегий либо показатели демо счета либо реального счета
  • Классификация закономерности по работоспособности относительно всех валютных пар и периодов графиков
  • Промежуток истории, на котором найдена закономерность
  • Промежуток времени в будущем, на котором закономерность сохраняет работоспособность
  • Второй промежуток времени в будущем после первого, на котором исходная закономерность сохраняет какие-либо параметры, либо инвертирует их как её использовать

Если внимательно прочитать каждое свойство, то можно понять, что закономерность — это формула или набор условий, которые максимально точно описывают движение цены на выбранных промежутках. Закономерность может оказаться случайностью, особенно если у нас слишком маленький промежуток и слишком космические показатели системы на нем. Очень важно понимать, что тестируя систему на коротких участках, ваши шансы найти глобальные закономерности стремятся к нулю. Все дело в размере выборки. Чем меньше выборка, тем больше случайных результатов вы получите. 

После того как мы определили, что такое закономерность, встаёт вопрос, как как эффективно использовать эти закономерности. Все зависит от того, каким способом найдена эта закономерность и от ее качества. Если абстрагироваться от методов анализа, которые используют вычислительные возможности машин, то приходим к аналитике. На мой взгляд, аналитика неспособна конкурировать с любыми видами машинного анализа, хотя бы по той причине, что даже хорошая команда аналитиков не способна обработать те данные, которые способна обработать одна, даже не очень мощная, машина. Все равно вопрос о поиске глобальных закономерностей требует вычислительных мощностей, ну, конечно, кроме тех случаев, когда вы случайно увидели какие-то очевидные вещи своими глазами и смогли понять их физику.


Пишем простейший симулятор позиций

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

Логика условно будет делиться на 3 отдельных симуляции и их любую возможную смешанную комбинацию:

  • Симуляция стоп ордеров
  • Симуляция лимит ордеров
  • Симуляция рыночных ордеров
  • Любые возможные комбинации

Логика выставления ордеров будет вот такой:

Orders Grid Logic

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

Предусмотрено 2 вида распределения относительных объемов, с затуханием и равномерное, но только для стоп и лимит ордеров. У рыночных распределение равномерное. При желании можно типы распределения расширять, если в этом будет просматриваться перспектива. Проиллюстрирую на рисунке:

Relative Volumes Filling Types

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

В данном случае грамотно все можно сделать с помощью объектно ориентированного подхода. Начнем с описания нумерованных списков:

enum CLOSE_MODE//как закрывать ордера
   {
   CLOSE_FAST,//быстро
   CLOSE_QUALITY//ждать противоположного сигнала
   };

enum WORK_MODE//режим работы
   {
   MODE_SIMPLE,//медленный режим
   MODE_FAST//быстрый режим
   };

enum ENUM_GRID_WEIGHT//тип заполнения веса лимитных и стоп ордеров
   {
   WEIGHT_DECREASE,//на затухание если двигаться от цены
   WEIGHT_SAME//одинаков для всей сетки
   };

enum ENUM_STATUS_ORDER//статусы ордеров или позиций
   {
   STATUS_VIRTUAL,//стоп ордер либо лимит
   STATUS_MARKET,//рыночный ордер
   STATUS_ABORTED//отмененный стоп или лимит ордер
   };

Симулятор сможет работать в медленном и быстром режиме. Медленный режим нужен в основном для стартового анализа. Стартовый анализ предполагает расчет на первых "n" ближайших свечках к рынку, а быстрый предполагает расчет только лишь на новой появившейся свече. Оказалось, что простого подхода недостаточно, и пришлось дополнить функционал для увеличения скорости работы алгоритма. При инициализации советника должен происходить расчет, он очень емкий. Зато потом нам лишь нужно будет обновлять симуляцию, исходя из всего 1 новой появившейся свечки на каждой свече. Так же здесь будет 2 типа распределения объемов лимитных и стоп ордеров, в зависимости от удаления от текущей цены на рынке, для каждого бара это Open[i]. Я исходил из того, что на каждом баре открывается сетка из стоп лимит и рыночных ордеров, с разными распределениями и весами. Стоп и лимит ордера через некоторое время превращаются в рыночные, как и должно быть, исходя из специфики. Либо стоп и лимит ордера отменяются, если цена не достигла требуемой цены за отведенный промежуток времени.

Начнем строить нашу симуляцию от простого к сложному постепенно собирая все воедино. Для начала определим что такое ордер:

struct Order//структура символизирующая ордер игрока
   {
   public:
   double WantedPrice;//желаемая цена открытия
   int BarsExpirationOpen;//Если ордер висит определенные бары то терпение у игрока кончается и он его убирает(время которое игрок может ждать)
   int BarsExpirationClose;//Если ордер уже рыночный и игрок не готов ждать то он закроет позицию
   double UpPriceToClose;//суммарное движение цены вверх при которой игрок закроет ордер(пункты)
   double LowPriceToClose;//суммарное движение цены вниз при которой игрок закроет ордер
   double VolumeAlpha;//текущий эквивалент объема [0...1]
   double VolumeStart;//стартовый эквивалент объема [0...1]
   int IndexMarket;//индекс бара на котором виртуальный ордер стал рыночным
   ENUM_STATUS_ORDER Status;//статус ордера
   Order(ENUM_STATUS_ORDER S)//конструктор создающий определенный ордер
      {
      Status=S;
      }
   };

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

Верхняя и нижняя цена закрытия выполняют роль стопов, но вместе с тем мы будем предполагать, что ордер сетки — не совсем ордер, а в этом ордере еще целая куча ордеров, просто они сливаются в 1 ордер, который открыт определенным объемом по определенной цене. Насколько важны ордера на конкретном уровне конкретного бара, говорят нам переменные стартового объема и текущего объема.

Стартовый объем — это объем при выставлении ордера. Текущий объем — это его объем в процессе развития событий. Нам не так важны будут прибыли конкретных ордеров, сколько соотношение объемов ордеров которые сидят в покупке и в продаже. Сигналы для торговли будут формироваться исходя из этих соображений. Можно, конечно, придумать и иные сигналы, но для этого нужны какие-то иные соображения. Еще стоит сказать, что ордера не будут закрываться, достигнув определенных уровней, а все это будет происходить постепенно на каждом баре по чуть-чуть, в попытке максимально имитировать реальное развитие событий.

Дальше нужно определить хранилище каждого бара. Бар будет хранить ордера которые открылись на нем:

class OrderBox//коробка ордеров конкретного бара
   {
   public:
   Order BuyStopOrders[];
   Order BuyLimitOrders[];
   Order BuyMarketOrders[];
   Order SellStopOrders[];
   Order SellLimitOrders[];
   Order SellMarketOrders[];
   
   OrderBox(int OrdersToOneBar)
      {
      ArrayResize(BuyStopOrders,OrdersToOneBar);
      ArrayResize(BuyLimitOrders,OrdersToOneBar);
      ArrayResize(BuyMarketOrders,OrdersToOneBar);
      ArrayResize(SellStopOrders,OrdersToOneBar);
      ArrayResize(SellLimitOrders,OrdersToOneBar);
      ArrayResize(SellMarketOrders,OrdersToOneBar);      
      for ( int i=0; i<ArraySize(BuyStopOrders); i++ )//зададим типы всем ордерам
         {
         BuyStopOrders[i]=Order(STATUS_VIRTUAL);
         BuyLimitOrders[i]=Order(STATUS_VIRTUAL);
         BuyMarketOrders[i]=Order(STATUS_MARKET);
         SellStopOrders[i]=Order(STATUS_VIRTUAL);
         SellLimitOrders[i]=Order(STATUS_VIRTUAL);
         SellMarketOrders[i]=Order(STATUS_MARKET);         
         }
      }
   };

Здесь все достаточно просто. Шесть типов ордеров, которые описаны массивами. Сделано это во избежание путаницы. В голом виде этот класс мы не будем использоваться конечно же, это лишь кирпичик в постройке.

Далее определим общее хранилище всех баров как один объект, от которого потом будем наследоваться. Приемы здесь достаточно простые и не больно-то экзотичные, но тут уже у кого как фантазия работает и кто как любит писать программы.

class BarBox//хранилище всех ордеров
   {
   protected:
   OrderBox BarOrders[];
   
   BarBox(int OrdersToOneBar,int BarsTotal)
      {
      ArrayResize(BarOrders,BarsTotal);
      for ( int i=0; i<ArraySize(BarOrders); i++ )//зададим типы всем ордерам
         {
         BarOrders[i]=OrderBox(OrdersToOneBar);
         }
      }   
   };

Это просто хранилище с данными баров (ордерами) и ничего более. Пока все было просто. Дальше все намного сложнее.

После того как мы определили удобное хранилище данных для ордеров, мы должны теперь определить, как и по каким правилам создаются ордера, важность конкретных типов ордеров и т. д. Для этого я создал вот такой класс:

class PositionGenerator:public BarBox//унаследуем класс от коробки чтобы не включать ее как внутренний член и не плодить ссылки
   {
   protected:
   double VolumeAlphaStop;//важность объемов STOP ордеров
   double VolumeAlphaLimit;//важность объемов LIMIT ордеров
   double VolumeAlphaMarket;//важность объемов MARKET ордеров
   double HalfCorridorLimitStop;//шаг корридора лимитных и стоп ордеров в пунктах
   int ExpirationOpenLimit;//через сколько баров произойдет полное затухание объемов сетки лимит ордеров для открытия
   int ExpirationOpenStop;//через сколько баров произойдет полное затухание объемов сетки стоп ордеров для открытия
   int ExpirationClose;//через сколько баров произойдет полное затухание объемов ордеров для закрытия
   int ProfitPointsCorridorPart;//размер половинки корридора для профита всех ордеров
   int LossPointsCorridorPart;//размер половинки корридора для проигрыша всех ордеров
   int OrdersToOneBar;//ордеров одного типа на 1 бар
   ENUM_GRID_WEIGHT WeightStopLimitFillingType;
   
   PositionGenerator( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
                     ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0
                     ,int ExpirationOpenLimit0,int ExpirationOpenStop0
                     ,int ExpirationClose0
                     ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0
                     ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) 
                     : BarBox(OrdersToOneBar0,BarsTotal0)
      {
      VolumeAlphaStop=VolumeAlphaStop0;
      VolumeAlphaLimit=VolumeAlphaLimit0;
      VolumeAlphaMarket=VolumeAlphaMarket0;
      OrdersToOneBar=OrdersToOneBar0;
      HalfCorridorLimitStop=double(HalfCorridorLimitStop0)/double(OrdersToOneBar);
      ExpirationOpenLimit=ExpirationOpenLimit0;
      ExpirationOpenStop=ExpirationOpenStop0;
      ExpirationClose=ExpirationClose0;
      ProfitPointsCorridorPart=ProfitPointsCorridorPart0;
      LossPointsCorridorPart=LossPointsCorridorPart0;
      OrdersToOneBar=OrdersToOneBar0;
      WeightStopLimitFillingType=WeightStopLimitFillingType0;
      }
   private:
   
   double CalcVolumeDecrease(double TypeWeight,int i,int size)//объем на затухание
      {
      if ( size > 1 )
         {
         double K=1.0/(1.0-size);
         double C=1.0;
         return TypeWeight*K*i+C;
         }
      else return 0.0;
      }
      
   double CalcVolumeSimple(double TypeWeight)//ровный объем
      {
      return TypeWeight;
      }
   
   void RebuildStops()//перестроим стоп ордера
      {
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
         {
         for ( int i=0; i<size; i++ )//сбросим все
            {
            BarOrders[j].BuyStopOrders[i].Status=STATUS_VIRTUAL;//сбросим статус на начальный
            BarOrders[j].BuyStopOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);//вес каждого элемента сетки
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);//текущий вес каждого элемента сетки
            BarOrders[j].BuyStopOrders[i].VolumeStart=BarOrders[j].BuyStopOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
            BarOrders[j].BuyStopOrders[i].UpPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//верхняя граница для закрытия
            BarOrders[j].BuyStopOrders[i].LowPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;//нижняя граница для закрытия
            BarOrders[j].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
            BarOrders[j].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
       
            BarOrders[j].SellStopOrders[i].Status=STATUS_VIRTUAL;
            BarOrders[j].SellStopOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);//вес каждого элемента сетки
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);//текущий вес каждого элемента сетки
            BarOrders[j].SellStopOrders[i].VolumeStart=BarOrders[j].SellStopOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
            BarOrders[j].SellStopOrders[i].UpPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;//верхняя граница для закрытия
            BarOrders[j].SellStopOrders[i].LowPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;//нижняя граница для закрытия    
            BarOrders[j].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
            BarOrders[j].SellStopOrders[i].BarsExpirationClose=ExpirationClose;                    
            }         
         }      
      }
      
   void RebuildLimits()//перестроим лимитные ордера
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
         {
         for ( int i=0; i<size; i++ )//сбросим все
            {
            BarOrders[j].BuyLimitOrders[i].Status=STATUS_VIRTUAL;//сбросим статус на начальный
            BarOrders[j].BuyLimitOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);//вес каждого элемента сетки
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);//текущий вес каждого элемента сетки
            BarOrders[j].BuyLimitOrders[i].VolumeStart=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
            BarOrders[j].BuyLimitOrders[i].UpPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//верхняя граница для закрытия
            BarOrders[j].BuyLimitOrders[i].LowPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;//нижняя граница для закрытия
            BarOrders[j].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
            BarOrders[j].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;            
       
            BarOrders[j].SellLimitOrders[i].Status=STATUS_VIRTUAL;
            BarOrders[j].SellLimitOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);//вес каждого элемента сетки
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);//текущий вес каждого элемента сетки
            BarOrders[j].SellLimitOrders[i].VolumeStart=BarOrders[j].SellLimitOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
            BarOrders[j].SellLimitOrders[i].UpPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;//верхняя граница для закрытия
            BarOrders[j].SellLimitOrders[i].LowPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;//нижняя граница для закрытия  
            BarOrders[j].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
            BarOrders[j].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
            }         
         }      
      }
      
   void RebuildMarkets()//перестроим рыночные ордера
      {
      int size=ArraySize(BarOrders[0].BuyMarketOrders);
      double MarketStep;
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         MarketStep=(High[j+1]-Low[j+1])/double(OrdersToOneBar);
            
         for ( int i=0; i<size; i++ )//сбросим все
            {
            BarOrders[j].BuyMarketOrders[i].Status=STATUS_MARKET;//сбросим статус на начальный
            BarOrders[j].BuyMarketOrders[i].WantedPrice=Low[j+1]+MarketStep*i;//цены сетки ордеров
            BarOrders[j].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);//текущий вес каждого элемента сетки
            BarOrders[j].BuyMarketOrders[i].VolumeStart=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
            BarOrders[j].BuyMarketOrders[i].UpPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//верхняя граница для закрытия
            BarOrders[j].BuyMarketOrders[i].LowPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;//нижняя граница для закрытия
            BarOrders[j].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
               
            BarOrders[j].SellMarketOrders[i].Status=STATUS_MARKET;
            BarOrders[j].SellMarketOrders[i].WantedPrice=High[j+1]-MarketStep*i;//цены сетки ордеров
            BarOrders[j].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);//текущий вес каждого элемента сетки
            BarOrders[j].SellMarketOrders[i].VolumeStart=BarOrders[j].SellMarketOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
            BarOrders[j].SellMarketOrders[i].UpPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;//верхняя граница для закрытия
            BarOrders[j].SellMarketOrders[i].LowPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;//нижняя граница для закрытия
            BarOrders[j].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
            } 
         }      
      }

   /////быстрые методы
   void RebuildStopsFast()//перестроим стоп ордера
      {
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )//сдвиг ордеров
            {
            BarOrders[j].BuyStopOrders[i]=BarOrders[j-1].BuyStopOrders[i];
            BarOrders[j].SellStopOrders[i]=BarOrders[j-1].SellStopOrders[i];
            BarOrders[j].SellStopOrders[i].IndexMarket++;
            }
         }
         
      for ( int i=0; i<size; i++ )//создадим новую сетку на новом баре
         {
         BarOrders[0].BuyStopOrders[i].Status=STATUS_VIRTUAL;//сбросим статус на начальный
         BarOrders[0].BuyStopOrders[i].WantedPrice=Close[1]+HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);//вес каждого элемента сетки
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);//текущий вес каждого элемента сетки
         BarOrders[0].BuyStopOrders[i].VolumeStart=BarOrders[0].BuyStopOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
         BarOrders[0].BuyStopOrders[i].UpPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//верхняя граница для закрытия
         BarOrders[0].BuyStopOrders[i].LowPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;//нижняя граница для закрытия
         BarOrders[0].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
         BarOrders[0].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
       
         BarOrders[0].SellStopOrders[i].Status=STATUS_VIRTUAL;
         BarOrders[0].SellStopOrders[i].WantedPrice=Close[1]-HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);//вес каждого элемента сетки
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);//текущий вес каждого элемента сетки
         BarOrders[0].SellStopOrders[i].VolumeStart=BarOrders[0].SellStopOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
         BarOrders[0].SellStopOrders[i].UpPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;//верхняя граница для закрытия
         BarOrders[0].SellStopOrders[i].LowPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;//нижняя граница для закрытия    
         BarOrders[0].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
         BarOrders[0].SellStopOrders[i].BarsExpirationClose=ExpirationClose;                    
         }               
      }
      
   void RebuildLimitsFast()//перестроим лимитные ордера
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )//сдвиг ордеров
            {
            BarOrders[j].BuyLimitOrders[i]=BarOrders[j-1].BuyLimitOrders[i];
            BarOrders[j].SellLimitOrders[i]=BarOrders[j-1].SellLimitOrders[i];
            BarOrders[j].SellLimitOrders[i].IndexMarket++;
            }         
         }
      
      for ( int i=0; i<size; i++ )//создадим новую сетку на новом баре
         {
         BarOrders[0].BuyLimitOrders[i].Status=STATUS_VIRTUAL;//сбросим статус на начальный
         BarOrders[0].BuyLimitOrders[i].WantedPrice=Open[1]-HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);//вес каждого элемента сетки
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);//текущий вес каждого элемента сетки
         BarOrders[0].BuyLimitOrders[i].VolumeStart=BarOrders[0].BuyLimitOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
         BarOrders[0].BuyLimitOrders[i].UpPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//верхняя граница для закрытия
         BarOrders[0].BuyLimitOrders[i].LowPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;//нижняя граница для закрытия
         BarOrders[0].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
         BarOrders[0].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;            
       
         BarOrders[0].SellLimitOrders[i].Status=STATUS_VIRTUAL;
         BarOrders[0].SellLimitOrders[i].WantedPrice=Open[1]+HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);//вес каждого элемента сетки
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);//текущий вес каждого элемента сетки
         BarOrders[0].SellLimitOrders[i].VolumeStart=BarOrders[0].SellLimitOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
         BarOrders[0].SellLimitOrders[i].UpPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;//верхняя граница для закрытия
         BarOrders[0].SellLimitOrders[i].LowPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;//нижняя граница для закрытия  
         BarOrders[0].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
         BarOrders[0].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
         }        
      }
      
   void RebuildMarketsFast()//перестроим рыночные ордера
      {
      int size=ArraySize(BarOrders[0].BuyMarketOrders);
      double MarketStep;
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )//сдвиг ордеров
            {
            BarOrders[j].BuyMarketOrders[i]=BarOrders[j-1].BuyMarketOrders[i];
            BarOrders[j].SellMarketOrders[i]=BarOrders[j-1].SellMarketOrders[i];
            }         
         }
      MarketStep=(High[1]-Low[1])/double(OrdersToOneBar);
      for ( int i=0; i<size; i++ )//создадим новую сетку на новом баре
         {
         BarOrders[0].BuyMarketOrders[i].Status=STATUS_MARKET;//сбросим статус на начальный
         BarOrders[0].BuyMarketOrders[i].WantedPrice=Low[1]+MarketStep*i;//цены сетки ордеров
         BarOrders[0].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);//текущий вес каждого элемента сетки
         BarOrders[0].BuyMarketOrders[i].VolumeStart=BarOrders[0].BuyMarketOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
         BarOrders[0].BuyMarketOrders[i].UpPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//верхняя граница для закрытия
         BarOrders[0].BuyMarketOrders[i].LowPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;//нижняя граница для закрытия
         BarOrders[0].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
               
         BarOrders[0].SellMarketOrders[i].Status=STATUS_MARKET;
         BarOrders[0].SellMarketOrders[i].WantedPrice=High[1]-MarketStep*i;//цены сетки ордеров
         BarOrders[0].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);//текущий вес каждого элемента сетки
         BarOrders[0].SellMarketOrders[i].VolumeStart=BarOrders[0].SellMarketOrders[i].VolumeAlpha;//стартовый вес каждого элемента сетки
         BarOrders[0].SellMarketOrders[i].UpPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;//верхняя граница для закрытия
         BarOrders[0].SellMarketOrders[i].LowPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;//нижняя граница для закрытия
         BarOrders[0].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
         }         
      }   
   
   protected:
   void CreateNewOrders()//создаем новые ордера на каждой свече
      {
      if ( VolumeAlphaStop != 0.0 ) RebuildStops();
      if ( VolumeAlphaLimit != 0.0 ) RebuildLimits();
      if ( VolumeAlphaMarket != 0.0 ) RebuildMarkets();
      }
   
   void CreateNewOrdersFast()//
      {
      if ( VolumeAlphaStop != 0.0 ) RebuildStopsFast();
      if ( VolumeAlphaLimit != 0.0 ) RebuildLimitsFast();
      if ( VolumeAlphaMarket != 0.0 ) RebuildMarketsFast();      
      }
   
   public:   
   virtual void Update()//функция обновления состояния (будем ее расширять в наследниках)
      {
      CreateNewOrders();
      }
      
   virtual void UpdateFast()//быстрое обновление состояния
      {
      CreateNewOrdersFast();
      }      
   };

По сути этот класс занимается только тем, что создает реализацию методов Update() и UpdateFast(), которые аналогичны, за тем лишь исключением, что последний работает быстрее более чем на порядок. Эти методы создают новые ордера на каждом баре и удаляют старые, тем самым подготавливая данные для следующего класса, который будет симулировать их жизненный цикл. В данном классе назначаются все необходимые параметры ордеров, их тип, цены открытия, объемы и все прочие важные параметры которые нужны будут в дальнейшем.

Следующий класс реализует процесс симуляции ордеров, а также вычисления необходимых для торговли параметров, на основе которых генерируются сигналы:

class Simulation:public PositionGenerator //дальше собираем наш симулятор позиций(наследуемся от генератора позиций)
   {//в нем же будем производить вычисления параметров рынка
   protected:
   double BuyPercent;//процент открытых позиций на покупку
   double SellPercent;//процент открытых позиций на продажу
   double StartVolume;//стартовый суммарный объем открытых позиций на покупку(одинаков для покупок и продаж)
   double RelativeVolume;//относительный объем
   double SummVolumeBuy;//суммарный объем для покупок
   double SummVolumeSell;//суммарный объем для продаж
   
   public:   
   Simulation( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
                     ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0
                     ,int ExpirationOpenLimit0,int ExpirationOpenStop0
                     ,int ExpirationClose0
                     ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0
                     ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) 
   :PositionGenerator(WeightStopLimitFillingType0
                     ,HalfCorridorLimitStop0,OrdersToOneBar0,BarsTotal0
                     ,ExpirationOpenLimit0,ExpirationOpenStop0
                     ,ExpirationClose0
                     ,ProfitPointsCorridorPart0,LossPointsCorridorPart0
                     ,VolumeAlphaStop0,VolumeAlphaLimit0,VolumeAlphaMarket0) 
      {
      CreateNewOrders();
      CalculateStartVolume();//вычислим стартовые объемы
      UpdateVirtual();//сначала обновим виртуальные чтобы потом обработать часть из них как рыночные в следующей функции
      UpdateMarket();//теперь обновим состояние всех рыночных ордеров
      CalculateCurrentVolume();//посчитаем текущие объемы всех открытых ордеров
      CalculatePercent();//посчитаем проценты позиций
      CalculateRelativeVolume();//посчитаем относительный объем      
      }

   double GetBuyPercent()//получим процент открытых сделок на покупку
      {
      return BuyPercent;
      }
      
   double GetSellPercent()//получим процент открытых сделок на продажу
      {
      return SellPercent;
      }
      
   double GetRelativeVolume()//получим относительный объем
      {
      return RelativeVolume;
      }

   virtual void Update() override
      {
      PositionGenerator::Update();//вызовем все что было до этого
      UpdateVirtual();//сначала обновим виртуальные чтобы потом обработать часть из них как рыночные в следующей функции
      UpdateMarket();//теперь обновим состояние всех рыночных ордеров
      CalculateCurrentVolume();//посчитаем текущие объемы всех открытых ордеров
      CalculatePercent();//посчитаем проценты позиций
      CalculateRelativeVolume();//посчитаем относительный объем
      }
      
   virtual void UpdateFast() override
      {
      PositionGenerator::UpdateFast();//вызовем все что было до этого
      UpdateVirtualFast();//сначала обновим виртуальные чтобы потом обработать часть из них как рыночные в следующей функции
      UpdateMarketFast();//теперь обновим состояние всех рыночных ордеров      
      CalculateCurrentVolume();//посчитаем текущие объемы всех открытых ордеров
      CalculatePercent();//посчитаем проценты позиций
      CalculateRelativeVolume();//посчитаем относительный объем      
      }   
      
   private:
   
   void UpdateVirtual()//обновим статус виртуальных ордеров
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int i=SizeBarOrders; i>0; i-- )//обновим состояние лимитных ордеров имитируя каждую свечу
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )//обновим состояние всех свечей которые были до этой
               {
               for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[i] )//если ордер виртуальный и он находится внутри свечи то он превращается в рыночный
                     {
                     BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].BuyLimitOrders[k].IndexMarket = i;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[i] )//то же самое
                     {
                     BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].SellLimitOrders[k].IndexMarket = i;
                     } 
                 
                  ///////проверка на истечение интереса лимит игроков
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
                     BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                        }
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
                     BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;                  
                        }
                     } 
                  }
               }         
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int i=SizeBarOrders; i>0; i-- )//обновим состояние лимитных ордеров имитируя каждую свечу
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )//обновим состояние всех свечей которые были до этой
               {
               for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
                  {
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].SellStopOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[i] )//то же самое
                     {
                     BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].SellStopOrders[k].IndexMarket = i;
                     }
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[i] )//то же самое
                     {
                     BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].BuyStopOrders[k].IndexMarket = i;
                     }
                  
                  ///////проверка на истечение интереса стоп и лимит игроков
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
                     BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;                  
                        } 
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
                     BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;                     
                        }
                     }                                                                       
                  }
               }         
            }
         }               
      }
      
   void UpdateMarket()//обновим статус рыночных ордеров
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )//обновим состояние ордеров имитируя каждую свечу
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )//обновим состояние всех свечей которые были до этой
               {
               for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
                  {
                  //блок закрытия в при изменении цен
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);//в профит
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);//в убыток
                        }
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);//в профит
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);//в убыток
                        }
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                     } 
                  ///конец блока закрытия при изменении цен
               
                  //блок закрытия при изменении времени*******************************************************
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                     } 
                  }
               }         
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )//обновим состояние ордеров имитируя каждую свечу
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )//обновим состояние всех свечей которые были до этой
               {
               for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
                  {
                  //блок закрытия при изменении цен
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);//в профит
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);//в убыток
                        }
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);//в профит
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);//в убыток
                        }
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                     }
               
                  ///конец блока закрытия при изменении цен
               
                  //блок закрытия при изменении времени*******************************************************
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                     }
                  }
               }         
            }
         }
         
      if ( VolumeAlphaMarket != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )//обновим состояние ордеров имитируя каждую свечу
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )//обновим состояние всех свечей которые были до этой
               {
               for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
                  {
                  //блок закрытия в при изменении цен
                  ///для заведомо рыночных позиций
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);//в профит
                     BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);//в убыток
                     }
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                              
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);//в профит
                     BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);//в убыток
                     }
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;                  
                  ///конец блока закрытия при изменении цен
               
                  //блок закрытия при изменении времени*******************************************************
              
                  ///для заведомо рыночных позиций
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;               
                  //
                  }
               }         
            }
         }
      }
      
   ///быстрые методы****   
   void UpdateVirtualFast()//обновим статус виртуальных ордеров
      {
      int SizeBarOrders=ArraySize(BarOrders);
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
               {
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[1] )//если ордер виртуальный и он находится внутри свечи то он превращается в рыночный
                  {
                  BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].BuyLimitOrders[k].IndexMarket = 1;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[1] )//то же самое
                  {
                  BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].SellLimitOrders[k].IndexMarket = 1;
                  } 
                  
               ///////проверка на истечение интереса лимит игроков
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
                  BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
                  BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;                  
                     }
                  } 
               }
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {       
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
               {
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].SellStopOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[1] )//то же самое
                  {
                  BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].SellStopOrders[k].IndexMarket = 1;
                  }
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[1] )//то же самое
                  {
                  BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].BuyStopOrders[k].IndexMarket = 1;
                  }
                  
               ///////проверка на истечение интереса стоп игроков
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
                  BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;                  
                     } 
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
                  BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;                     
                     }
                  }                                                                       
               }
            }
         }         
      }
      
   void UpdateMarketFast()//обновим статус рыночных ордеров
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
               {
               //блок закрытия в при изменении цен
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);//в профит
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);//в убыток
                     }
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);//в профит
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);//в убыток
                     }
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                  } 
               ///конец блока закрытия при изменении цен
               
               //блок закрытия при изменении времени*******************************************************
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                  } 
               //
               }
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
               {
               //блок закрытия в при изменении цен
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);//в профит
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);//в убыток
                     }
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);//в профит
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);//в убыток
                     }
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                  }
               
               ///конец блока закрытия при изменении цен
               
               //блок закрытия при изменении времени*******************************************************
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                  }
               //
               }
            }
         }
         
       if ( VolumeAlphaMarket != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )//обновим состояние внутри каждой из предшествующих свечей
               {
               ///для заведомо рыночных позиций
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                  {
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);//в профит
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);//в убыток
                  }
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                              
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                  {
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);//в профит
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);//в убыток
                  }
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;                  
               ///конец блока закрытия при изменении цен
               
               //блок закрытия при изменении времени*******************************************************
             
               ///для заведомо рыночных позиций
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
               BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
               BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;               
               //
               }
            }
         }                          
      }      
   ///******
      
   void CalculateStartVolume()//посчитаем стартовый суммарный объем всех позиций(относительно него будем считать наполненность рынка)
      {
      StartVolume=0;
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyStopOrders[i].VolumeStart;
               }
            }        
         }
         
      if ( VolumeAlphaLimit != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyLimitOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyLimitOrders[i].VolumeStart;
               }         
            }
         }
         
      if ( VolumeAlphaMarket != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyMarketOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyMarketOrders[i].VolumeStart;
               }         
            }
         }         
      }
      
   void CalculateCurrentVolume()//посчитаем текущий суммарный объем всех позиций
      {
      SummVolumeBuy=0;
      SummVolumeSell=0;
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               if ( BarOrders[j].BuyStopOrders[i].Status == STATUS_MARKET )
               SummVolumeBuy+=BarOrders[j].BuyStopOrders[i].VolumeAlpha;
               if ( BarOrders[j].SellStopOrders[i].Status == STATUS_MARKET )
               SummVolumeSell+=BarOrders[j].SellStopOrders[i].VolumeAlpha;            
               }         
            }
         }
      
      if ( VolumeAlphaLimit != 0.0 )
         {   
         size=ArraySize(BarOrders[0].BuyLimitOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               if ( BarOrders[j].BuyLimitOrders[i].Status == STATUS_MARKET )
               SummVolumeBuy+=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;
               if ( BarOrders[j].SellLimitOrders[i].Status == STATUS_MARKET )
               SummVolumeSell+=BarOrders[j].SellLimitOrders[i].VolumeAlpha;            
               }         
            }
         }
      
      if ( VolumeAlphaMarket != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyMarketOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               SummVolumeBuy+=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;
               SummVolumeSell+=BarOrders[j].SellMarketOrders[i].VolumeAlpha;
               }         
            }
         }         
      }
      
   void CalculatePercent()//посчитаем проценты покупок и продаж относительно всех позиций
      {
      if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) BuyPercent=100.0*SummVolumeBuy/(SummVolumeBuy+SummVolumeSell);
      else BuyPercent=50;
      if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) SellPercent=100.0*SummVolumeSell/(SummVolumeBuy+SummVolumeSell);
      else SellPercent=50;
      }      
      
   void CalculateRelativeVolume()//посчитаем относительные объемы покупок и продаж (считаем только нескомпенсированную часть позиций)
      {
      if ( SummVolumeBuy >= SummVolumeSell ) RelativeVolume=(SummVolumeBuy-SummVolumeSell)/StartVolume;
      else RelativeVolume=(SummVolumeSell-SummVolumeBuy)/StartVolume;
      }                   
   
   };

Весь код, который приведен здесь, подходит и для MetaTrader 4 и для MetaTrader 5. Эти классы скомпилируются в обеих платформах, конечно, при условии что в MetaTrader 5 вы заранее реализуете предопределенные массивы, такие же как в MQL4. Этот код я приводить не буду. Не хочу повторяться. В случае необходимости посмотрите в исходниках, как это делается у меня. Там не очень оригинально, но работает. Все, что вам останется, это реализовать переменные, отвечающие за торговлю, и сам торговый функционал, который я тоже приводить не буду. Советники для обоих терминалов будут приложены к статье.

Что хочется сказать, так это то, что код очень прожорливый, и для этого я сделал медленную реализацию логики для стартового анализа и быструю для работы по барам. Все мои советники работают по барам во избежание зависимости от искусственной генерации тиков и прочих нежелательных последствий потикового тестирования. Можно было, конечно, обойтись без медленных функций, но тогда пришлось бы отказаться от стартового анализа. Кроме всего прочего, я не люблю выносить функционал из тела класса, на мой взгляд так теряется целостность картины и можно запутаться.

Конструктор последнего класса будет принимать следующие параметры, которые будут входными, но при желании таких симуляций можно делать много, ведь это всего лишь экземпляр класса:

input bool bPrintE=false;//печатать параметры рынка
input CLOSE_MODE CloseModeE=CLOSE_FAST;//режим закрытия ордеров
input WORK_MODE ModeE=MODE_SIMPLE;//режим симуляции
input ENUM_GRID_WEIGHT WeightFillingE=WEIGHT_SAME;//тип распределения веса
input double LimitVolumeE=0.5;//значимость лимитных ордеров
input double StopVolumeE=0.5;//значимость стоп ордеров
input double MarketVolume=0.5;//значимость рыночных ордеров
input int ExpirationBars=100;//бары для полного истечения открытых ордеров
input int ExpirationOpenStopBars=1000;//терпение стоп игрока в барах чтобы убрать ордер
input int ExpirationOpenLimitBars=1000;//терпение лимит игрока в барах чтобы убрать ордер
input int ProfitPointsCloseE=200;//пункты закрытия на профит
input int LossPointsCloseE=400;//пункты закрытия на убыток
input int HalfCorridorE=500;//полукорридор для лимит и стоп ордеров
input int OrdersToOneBarE=50;//ордеров на пол сетки на 1 бар
input int BarsE=250;//баров для анализа
input double MinPercentE=60;//минимальный перевес в процентах одной из сторон торгов
input double MaxPercentE=80;//максимальный процент
input double MinRelativeVolumeE=0.0001;//минимальное наполнение рынка[0...1]
input double MaxRelativeVolumeE=1.00;//максимальное наполнение рынка[0...1]

Переменные, связанные с торговлей, я здесь не буду приводить, там все достаточно просто и понятно. 

Функция, в которой будет реализована торговля, у меня выглядит вот так:

void Trade()
   {
   if ( Area0 == NULL )
      {
      CalcAllMQL5Values();
      Area0 = new Simulation(WeightFillingE,HalfCorridorE,OrdersToOneBarE,BarsE
       ,ExpirationOpenLimitBars,ExpirationOpenStopBars,ExpirationBars,ProfitPointsCloseE,LossPointsCloseE
       ,StopVolumeE,LimitVolumeE,MarketVolume);      
      }
   
   switch(ModeE)
      {
      case MODE_SIMPLE:
         Area0.Update();//обновим симуляцию
      case MODE_FAST:
         Area0.UpdateFast();//быстро обновим симуляцию
      }
   
   if (bPrintE)
      {
      Print("BuyPercent= ",Area0.GetBuyPercent());
      Print("SellPercent= ",Area0.GetSellPercent());
      Print("RelativeVolume= ",Area0.GetRelativeVolume());
      }
      
   if ( CloseModeE == CLOSE_FAST && Area0.GetBuyPercent() > 50.0 )
      {
      if ( !bInvert ) CloseBuyF();
      else CloseSellF();
      }
      
   if ( CloseModeE == CLOSE_FAST && Area0.GetSellPercent() > 50.0 )
      {
      if ( !bInvert ) CloseSellF();
      else CloseBuyF();
      }      
      
   if ( Area0.GetBuyPercent() > MinPercentE && Area0.GetBuyPercent() < MaxPercentE 
   && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
      {
      if ( !bInvert )
         {
         CloseBuyF();
         SellF();
         }
      else
         {
         CloseSellF();
         BuyF();
         }   
      }
      
   if ( Area0.GetSellPercent() > MinPercentE && Area0.GetSellPercent() < MaxPercentE 
   && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
      {
      if ( !bInvert )    
         {
         CloseSellF();
         BuyF();
         }  
      else
         {
         CloseBuyF();
         SellF();
         }
      }
   }

При желании условия для торговли можно и получше сделать, и саму функцию сложнее, но пока я не вижу смысла в этом. Я всегда стараюсь отделять часть торговую от логической. Вся логика происходит в объекте. Кстати, создание объекта симуляции происходит динамически в торговой функции при первом срабатывании, а удаляется этот объект при деинициализации. Пришлось так сделать, так как в MQL5 отсутствуют предопределенные массивы и их приходится создавать искусственно для того, чтобы классы работали так же как и в MQL4.


Как искать рабочие настройки?

Исходя из своего опыта я могу сказать, что искать настройки лучше руками. При этом я считаю, что для советников, работающих на низких таймфреймах и с небольшими математическими ожиданиями, крайне важно на первом этапе абстрагироваться от спреда, чтобы не пропустить зачатки работоспособности в самом начале. Для этой цели очень хорошо подходит MetaTrader 4. После успешного поиска всегда следует доработка, какие-то правки и прочее, что повысит нам итоговое математическое ожидание и профит-фактор (силу сигнала). Еще уточню, что структура входных данных в идеале должна давать возможность независимых режимов работы. Иначе говоря, выкручивание одной настройки должно максимально независимо влиять на показатели системы, несмотря на значение остальных настроек. Такой подход может дать усиление общего сигнала за счет выставления неких сбалансированных настроек, которые дадут объединение всех найденных сигналов воедино. Такая реализация далеко не всегда возможна, но в нашем случае она применима, если мы будем анализировать каждый тип ордеров отдельно.


Тестирование советника

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

При тестировании советника мне пришлось потратить несколько дней, терпеливо подбирая параметры торговли. Все было достаточно занудно и долго, но тем не менее мне удалось найти рабочие настройки. Конечно, эти настройки очень хилые и симуляция происходит используя только 1 тип ордеров, но это сделано намеренно. Дело в том, что лучше проанализировать отдельно, как тот или иной тип ордеров влияет на сигнал, а уже после всего этого пытаться симулировать другие типы ордеров. Результат полученный мной на MetaTrader 4 выглядит вот так:

EURUSD 2010.01.01 -2020.11.01

Изначально была сделана версия для MetaTrader 4 и протестирована там с минимально возможным спредом. Все дело в том, что для поиска закономерностей, особенно на низких таймфремах графиков, нам необходимо видеть каждый тик. Если мы протестируем версию для MetaTrader 5, то мы не увидим того, что мы видим здесь из за спредов, которые MetaTester 5 может корректировать на свое усмотрение, если посчитает нужным.

Для стресс тестов и оценки реальной работоспособности это прекрасный механизм. Его нужно использовать на последнем этапе для проверки на реальных тиках. В нашем же случае лучше изначально тестировать систему в MetaTrader 4. Для подобных целей я всем советую использовать именно такой подход, ведь тестируя сразу в пятом терминале, можно потерять кучу прекрасных вариантов настроек, которые могут послужить отправной точкой для поиска новых более качественных настроек.

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

Еще одним очень важным аспектом является количество ордеров. Чем больше количество ордеров относительно количества баров, тем более сильную физику мы нашли — в том плане, что можно не опасаться за ее работоспособность в будущем. Еще следует помнить, что найденные закономерности могут лежать внутри спреда что может сделать нашу систему бесполезной если эти моменты не проконтролировать!

Для того чтобы отделить зерна от плевел нам понадобится тестер MetaTrader 5. Этот тестер дает такую возможность благодаря возможности тестирования по реальным тикам. К сожалению, реальные тики существуют сравнительно недавно для всех валютных пар и инструментов. Последний год я протестирую версию для MetaTrader 5 с реальными тиками и максимально жесткими требованиями к спредам, для того чтобы увидеть — работает ли система в 2020 году и насколько хорошо, если да. Но сначала я протестирую систему в обычном режиме "Все тики" по тому же участку, что был до этого:

EURUSD M5 2010.01.01-2020.11.01

Этот режим тестирования не такой хороший, как по реальным тикам, но все же можно увидеть, что осталась лишь очень маленькая часть сигналов от первоначального результата, но тем не менее там есть сигналы, которые перебивают спред и дают небольшую, но прибыль! Еще у меня довольно большие сомнения в достоверности тех спредов, что тестер берет из глубокой истории. В данном тесте был лот 0.01, из чего выходит что мат ожидание 5 пунктов, что даже выше, чем у исходного теста, хоть график и выглядит очень криво. Все равно можно доверять этим данным, потому что за ними стоит огромная выборка из 100000 сделок первоначального теста.

Теперь посмотрим на последний год:

EURUSD M5 2020.01.01-2020.11.01

В данном тесте лот я выставлял 0.1, тем самым получается, что математическое ожидание 23.4 пункта, что довольно неплохо, учитывая что изначальный тест на MetaTrader 4 дал всего 3 пункта матожидания. Возможно матожидание и понизится в будущем, но вряд ли очень сильно, все равно его будет достаточно для безубыточной торговли.

Советник будет приложен к статье для обоих терминалов. Можете поиграться с настройками и попробовать найти рабочие параметры для лимит ордеров и для рыночных, если выйдет. А потом можно объединить эти сеты, выставив некие средние настройки. К сожалению, у меня не было времени выжимать из него все соки, но простор для маневра остался.

Конечно, еще хотел бы заметить, что это далеко не рабочий советник, который можно сразу вешать на график и радоваться. После этого должно идти тестирование симуляции с использованием иных типов ордеров, а потом объединение настроек и повторение этих двух циклов тестирования до тех пор, пока показатели торговли не примут достаточно безопасные значения, возможно введение фильтров или фикс самих алгоритмов. Как видно из тестов, подобный подход более чем возможен. Я оставлю все это читателю. Единственное, мне не удалось найти рабочие настройки для высоких таймфреймов. Лучше всего работает на M5, но я могу ошибаться. Также, к сожалению, я не успел проверить мультивалютность сета на других валютных парах, но обычно при таких ровных линиях все работает и на остальных валютных парах. Время появится, может я еще поплотнее поработаю над этим советником. Даже в процессе написания было найдено куча недочетов и ошибок, я думаю, что там еще много недоработок.


Заключение

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

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

Кроме всего прочего, необязательно использовать именно мой подход. Целью данной статьи было взять какую либо физику рынка, которая, по моему мнению, должна хотя бы частично описывать его, и написать советник, который бы это доказал. В итоге был написан советник, который показал, что любые предположения, имеющие под собой хотя бы часть правды, могут дать рабочую систему.

Нужно еще учесть тот факт, что в коде, я думаю, еще много недочетов, возможно и грубых ошибок, но несмотря на это все, советник даже в таком варианте говорит нам, что "даже так я буду работать". Вам лишь остается терпеливо его доработать. В следующей статье я представлю иной, более простой и эффективный, вид мультивалютного анализа рынка, который всем до боли знаком. Так же будет сделан работающий советник.

Прикрепленные файлы |
Simulation.zip (26.94 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (32)
Evgeniy Ilin
Evgeniy Ilin | 24 янв 2021 в 22:15
Dmytryi Nazarchuk:

Когда Максим Романов заработает миллиард - посмотрю. 

Какой именно рынок "флейтовых"? Дайте определение этому термину. И прекратите использовать научные термины для придания веса Вашим статьям. Ну, как дети - один квантовую механику приплёл, торой теорию струн притянет?

вот почитайте, подробно описано что такое флет https://www.mql5.com/ru/articles/8274

Dmytryi Nazarchuk
Dmytryi Nazarchuk | 25 янв 2021 в 06:33
Evgeniy Ilin:

вот почитайте, подробно описано что такое флет https://www.mql5.com/ru/articles/8274

Спасибо - прочитал и посмеялся. Особенно позабавило определение "хаотичного рынка". Оказывается вероятность срабатываниях защитных ордеров зависит от рынка, а не от их размера!

Этак что такое флатовый рынок - дайте математическое определение

Evgeniy Ilin
Evgeniy Ilin | 25 янв 2021 в 20:49
Dmytryi Nazarchuk:

Спасибо - прочитал и посмеялся. Особенно позабавило определение "хаотичного рынка". Оказывается вероятность срабатываниях защитных ордеров зависит от рынка, а не от их размера!

Этак что такое флатовый рынок - дайте математическое определение

Я вам уже кинул ссылку на статью. Вы посмеялись. Хорошо в этом я вас упрекнуть не могу. Флет графически определяется. Но можно дать и скалярную характеристику этим процессам. Если мы берем фиксированное количество шагов и считаем эталонное распределение (распределение при условии что рынок хаотичен) считаем среднемодульное движение цены в эталонном распределении и потом считаем среднемодульное движение цены в имеющемся распределения на рынке, то тренд это последнее больше чем эталонное. Флет соответственно обратная ситуация. Среднемодульное движение должно быть меньше чем эталонное. Можно еще коэффициент движения в покупку или продажу придумать аналогично, плюс восходящий, минус нисходящий. Кстати в статье пример индикатора который эти 2 характеристики реализует если вы не поняли. Удовлетворены ? или мне вам формулы накидать в паинте ? Теперь я задам вам вопрос. Что вы ищете в статьях для себя ?

Dmytryi Nazarchuk
Dmytryi Nazarchuk | 25 янв 2021 в 21:27
Evgeniy Ilin:

Я вам уже кинул ссылку на статью. Вы посмеялись. Хорошо в этом я вас упрекнуть не могу. Флет графически определяется. Но можно дать и скалярную характеристику этим процессам. Если мы берем фиксированное количество шагов и считаем эталонное распределение (распределение при условии что рынок хаотичен) считаем среднемодульное движение цены в эталонном распределении и потом считаем среднемодульное движение цены в имеющемся распределения на рынке, то тренд это последнее больше чем эталонное. Флет соответственно обратная ситуация. Среднемодульное движение должно быть меньше чем эталонное. Можно еще коэффициент движения в покупку или продажу придумать аналогично, плюс восходящий, минус нисходящий. Кстати в статье пример индикатора который эти 2 характеристики реализует если вы не поняли. Удовлетворены ? или мне вам формулы накидать в паинте ? Теперь я задам вам вопрос. Что вы ищете в статьях для себя ?

В этой статье я искал объяснение, при чем тут физика. Физика тут не при чем. 

В первой статье меня заинтересовало определение 'хаотичного рынка' - забавная привязка к вероятности срабатывания защитных ордеров. Есть смутное подозрение того, что с снижением размера ордеров "хаотичность" любого рынка повышается....

Evgeniy Ilin
Evgeniy Ilin | 26 янв 2021 в 09:29
Dmytryi Nazarchuk:

В этой статье я искал объяснение, при чем тут физика. Физика тут не при чем. 

В первой статье меня заинтересовало определение 'хаотичного рынка' - забавная привязка к вероятности срабатывания защитных ордеров. Есть смутное подозрение того, что с снижением размера ордеров "хаотичность" любого рынка повышается....

Это только экспериментально проверить можно. Там есть отличие от хаотичности на всех диапазонах, но в целом почти 100 процентов везде сдвиг в сторону флета, потому что люди когда покупают они покупают скопом и продают тоже скопом, вот и волны возникают, вход и выход это две полуволны. Во многих случаях это получается случайно, и мы достоверно не можем определить что было в волне. Те экспериментальные советники что я делал способны были дать около 20-40 пунктов матожидания, при этом работая глобально по всей истории. Эта механика работает везде и мультивалютно. Есл играмотно сделать детект этих волн и еще и фильтрацию сигналов, а если еще и усреднение с мартином добавить то ... Только это уже серьезный проект, на который у меня пока нет времени. Есть аналоги. Как раз У Максима Романова схожий подход, но он использует высшие ТФ. Это и верно кстати говоря. Я промобал в основном На М5

Нейросети — это просто (Часть 9): Документируем проделанную работу Нейросети — это просто (Часть 9): Документируем проделанную работу

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

Работа с ценами в библиотеке DoEasy (Часть 59): Объект для хранения данных одного тика Работа с ценами в библиотеке DoEasy (Часть 59): Объект для хранения данных одного тика

С данной статьи приступим к созданию функционала библиотеки для работы с ценовыми данными. Сегодня создадим класс объекта, который будет хранить в себе все данные цен, пришедшие с очередным тиком.

Самоадаптирующийся алгоритм (Часть III): Отказываемся от оптимизации Самоадаптирующийся алгоритм (Часть III): Отказываемся от оптимизации

Получить по-настоящему стабильный алгоритм невозможно, если для подбора параметров используется оптимизация по историческим данным. Стабильный алгоритм сам должен знать, какие параметры нужны для работы по любому торговому инструменту в любой момент времени. Он не должен предполагать или угадывать, он должен точно знать.

Поиск сезонных закономерностей на валютном рынке с помощью алгоритма CatBoost Поиск сезонных закономерностей на валютном рынке с помощью алгоритма CatBoost

В статье показана возможность создания моделей машинного обучения с временными фильтрами и раскрыта эффективность такого подхода. Теперь можно исключить человеческий фактор, просто сказав модели: "Хочу, чтобы ты торговала в определенный час определенного дня недели". А поиск закономерностей возложить на плечи алгоритма.