Обсуждение статьи "Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXI): Торговые классы - Базовый кроссплатформенный торговый объект"

 

Опубликована статья Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXI): Торговые классы - Базовый кроссплатформенный торговый объект:

В статье начнём новый раздел библиотеки - торговые классы, и рассмотрим создание единого базового торгового объекта для платформ MetaTrader 5 и MetaTrader 4. Такой торговый объект будет подразумевать при отправке запроса на сервер, что в него переданы уже проверенные и корректные параметры торгового запроса.

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

  • Нам необходимо иметь возможность отправлять любые торговые приказы из любой платформы — будь то MetaTrader 5, будь то MetaTrader 4. При этом совершенно не задумываясь о том, в какой именно платформе мы отправляем торговый приказ — всё будет одинаково.
  • Нам необходимо предварительно проверить корректность торговых запросов — чтобы не нагружать сервер заведомо ошибочными запросами.
  • Нам необходимо учитывать и правильно обрабатывать коды возврата торгового сервера. Ведь что делает советник отправляя приказ на сервер? Он ведёт диалог с сервером в виде запрос-ответ. И чтобы советник с сервером мог общаться — наша задача правильно организовать такой "канал общения", т.е., создать методы обработки ответов торгового сервера.
  • Нам необходимо создать несколько вариантов обработки ответов сервера — ведь иногда нам необходимо открыть позицию "желательно любой ценой". Для этого нужно организовать повтор отправки приказа на сервер в случае отказа в постановке ордера — мы можем либо подкорректировать параметры торгового приказа и заново его послать, либо все параметры оставить неизменными, но дождаться подходящего момента, при котором приказ с этими параметрами пройдёт, и сразу же его отослать. При всём при этом нам нужно будет учитывать и уровень цены — чтобы не отсылать повторно ордер по заведомо худшей цене.
    Но иногда нам требуется просто отослать торговый приказ, и независимо от результата запроса продолжить работу.
  • Нам необходимо сделать работу с торговыми классами ещё и так, чтобы при размещении программы, созданной на основе библиотеки,
    в mql5-маркет, не было никаких проблем — все проверки такая программа должна проходить без каких-либо осложнений.
Такие вот минимальные планы у нас на данный момент относительно торговых классов.

Автор: Artyom Trishkin

 

Прочитал более половины очень внимательно. Далее, сознание начало отключаться. Объем статьи чрезвычайно велик. 

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

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

//--- Устанавливает отложенный ордер (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit
   bool                 PlaceBuyStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- Устанавливает отложенный ордер (1) SellStop, (2) SellLimit, (3) SellStopLimit
   bool                 PlaceSellStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- 
                        

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

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

 
Реter Konow:

Прочитал более половины очень внимательно. Далее, сознание начало отключаться. Объем статьи чрезвычайно велик. 

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

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

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

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

Спасибо за оценку, Пётр. Использовать необходимо лишь то, что доступно из CEngine, а оно в свою очередь - из программы.

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

Естественно, что всё будет описано. И структура, и все доступные пользователю методы. И будут созданы пользовательские функции, скрывающие ООП-реализацию, и не требующие использовать явно обращение к объектам библиотеки.

Но всё впереди - нужно составить основной функционал, который можно будет описать как структурно, так и таблично так, чтобы описание было полным и не требовало доработок.

С каждой последующей статьёй разница в методах будет сильно увеличиваться. В данной статье лишь самое начало работы над торговыми классами. И, поглядите пожалуйста на OrderSend() для каждого из типов отсылаемых ордеров - какие она требует параметры. Все эти параметры и передаются в методы. Можно конечно было и заморочиться на передачу параметров через описываемые структуры как в индикаторах, но это точно будет лишним.
И можно было заставить пользователя самого заполнять необходимые структуры, но проще и дружелюбнее - передать параметры сразу в метод, чем заполнять предварительно структуру, а потом её передавать в метод. Делается как раз для удобства использования конечным пользователем, а не удобства разбора составляющих библиотеки.
 
Artyom Trishkin:

Спасибо за оценку, Пётр. Использовать необходимо лишь то, что доступно из CEngine, а оно в свою очередь - из программы.

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

Естественно, что всё будет описано. И структура, и все доступные пользователю методы. И будут созданы пользовательские функции, скрывающие ООП-реализацию, и не требующие использовать явно обращение к объектам библиотеки.

Но всё впереди - нужно составить основной функционал, который можно будет описать как структурно, так и таблично так, чтобы описание было полным и не требовало доработок.

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

Чтобы не быть голословным, я бы предложил следующую меру по ограничиванию "заворачивания":

Метод " CTradeObj::SetOrder" переписать таким образом, чтобы он получал один доп. параметр  - тип ордера который нужно установить. Далее, метод сам определит как это сделать. Инициализирует нужную структуру, установит нужные значения и вызовет нужные методы. Таким образом, Вы сожмете все его обертки до одного дополнительного параметра посылаемого в метод  " CTradeObj::SetOrder". Он будет получать  PlaceBuyStop, PlaceSellStopLimit, или PlaceBuyLimit в качестве параметра и далее - осуществлять нужную работу.

Безусловно, этот вариант сложнее реализовать, но Вы обеспечите "сжатие" функционала библиотеки и облегчение его использования.

Удачи.  

 
Реter Konow:

Чтобы не быть голословным, я бы предложил следующую меру по ограничиванию "заворачивания":

Метод " CTradeObj::SetOrder" переписать таким образом, чтобы он получал один доп. параметр  - тип ордера который нужно установить. Далее, метод сам определит как это сделать. Инициализирует нужную структуру, установит нужные значения и вызовет нужные методы. Таким образом, Вы сожмете все его обертки до одного дополнительного параметра посылаемого в метод  " CTradeObj::SetOrder". Он будет получать  PlaceBuyStop, PlaceSellStopLimit, или PlaceBuyLimit в качестве параметра и далее - осуществлять нужную работу.

Безусловно, этот вариант сложнее реализовать, но Вы обеспечите "сжатие" функционала библиотеки и облегчение его использования.

Удачи.  

Этот метод не нужен пользователю библиотеки. Он необходим самой библиотеке.

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

 
Artyom Trishkin:

...

И, поглядите пожалуйста на OrderSend() для каждого из типов отсылаемых ордеров - какие она требует параметры. Все эти параметры и передаются в методы. Можно конечно было и заморочиться на передачу параметров через описываемые структуры как в индикаторах, но это точно будет лишним.

И можно было заставить пользователя самого заполнять необходимые структуры, но проще и дружелюбнее - передать параметры сразу в метод, чем заполнять предварительно структуру, а потом её передавать в метод. Делается как раз для удобства использования конечным пользователем, а не удобства разбора составляющих библиотеки.
С моей точки зрения, самым дружелюбным был бы вариант всего одной обертки для OrderSend(). Эта обертка принимала бы параметры от пользователя и сама решала как оформить вызов. Я бы делал так. Но, Вам виднее.
 
Реter Konow:
С моей точки зрения, самым дружелюбным был бы вариант всего одной обертки для OrderSend(). Эта обертка принимала бы параметры от пользователя и сама решала как оформить вызов. Я бы делал так. Но, Вам виднее.

У меня и сделано так - методы принимают параметры от пользователя.

 
Artyom Trishkin:

У меня и сделано так - методы принимают параметры от пользователя.

Да, понял. Это такая "многослойная" реализация обработки ордеров. Проблема в "толще" оберток. Я запутался, какие методы относятся к механизмам, а какие к пользовательскому интерфейсу библиотеки. Сорри. Если бы оберток было меньше, понять было бы легче, но - это ваш выбор.  
 
Реter Konow:
Да, понял. Это такая "многослойная" реализация обработки ордеров. Проблема в "толще" оберток. Я запутался, какие методы относятся к механизмам, а какие к пользовательскому интерфейсу библиотеки. Сорри. Если бы оберток было меньше, понять было бы легче, но - это ваш выбор.  

Понимаете, Пётр, я стараюсь дать не просто один рабочий метод, а даю множество возможностей.

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

Это не удобно. Но такой доступ есть. Кому нужно, тот сам всё проверит и отправит торговый приказ. Никаких обработок ответа сервера нету. Это первое, что сделано.

В следующей статье будет описан торговый класс, в котором будет тот же набор торговых методов с теми же параметрами, но теперь всё будет проверяться на корректность и возвращаться в программу если параметры не корректны (или один из них, или нет возможности для торговли). Это второй этап - тут уже есть два способа - первый, который уже есть, и второй - когда библиотека сама проверяет корректность параметров, и если они все верные, то отсылается приказ, а если хоть один не верен, или нет возможности для торговли, то осуществляется просто возврат в управляющую программу. Тут уже будет организован более высокоуровневый доступ - с проверками корректности.

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

А далее будет ещё более высокоуровневый подход - всё будет работать в автоматическом режиме в соответствии с требуемыми настройками. И дойдёт дело и до отложенных запросов - когда советник будет ждать подходящего момента для повторной отсылки нужного приказа, либо просто будет знать в какой ситуации ему необходимо отправить запрос. Все такие отложенные запросы будут в его копилке находиться и ждать подходящего момента для отсылки запроса.

Поэтому, Пётр, судить пока преждевременно что-либо.

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

 

Artyom Trishkin:

...

Мне понравилось Ваше пояснение, и в связи с этим, возникло внутреннее противоречие.

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

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

//-----------------------------------

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

 
Artyom Trishkin:

...

А далее будет ещё более высокоуровневый подход - всё будет работать в автоматическом режиме в соответствии с требуемыми настройками. И дойдёт дело и до отложенных запросов - когда советник будет ждать подходящего момента для повторной отсылки нужного приказа, либо просто будет знать в какой ситуации ему необходимо отправить запрос. Все такие отложенные запросы будут в его копилке находиться и ждать подходящего момента для отсылки запроса.

Поэтому, Пётр, судить пока преждевременно что-либо.

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

Разбитие библиотеки на уровни и предоставление возможности на них работать пользователям - очень хорошо. Но, не забывайте, что количество может погубить качество. Чем больше методов получит пользователь, тем больше вероятность что он в них запутается. Это нужно учитывать.

В остальном, я согласен. Многоуровневый подход к торговым функциям - это хорошо.

Причина обращения: