МТ4 или МТ5. Какие преимущества и недостатки? - страница 33

 
Nikolai Semko:

Я думаю причина в том, что MT5 в отличии от MT4, использует многопоточность. Из-за этого происходит данное рассогласование. Если упростить слегка ваш код (и кстати убыстрить), не считывая одну и ту же информацию два раза, то все заработает корректно. Причем, это даже алгоритмическая ошибка, т.к. вы считываете позицию курсора в разные моменты времени, но подразумеваете, что это одно и тоже значение. И в данном случае MT5 ведет себя даже более логично.

Было:

Стало:


спасибо, как то я немного с другой стороны подумал об этом видимо) 
правда всё равно поведение разное в 4 фигуры синхронно движутся.. а в 5 этого нет

 
Renat Fatkhullin:

1) Это не ошибка.

В MetaTrader 5 у стандартных индикаторов мы убрали встроенные костыли с лишними параметрами. Кроме того, индикаторные iXXX функции в MQL5 стали конструкторами и потеряли возможность выдавать фактические данные, так как возвращают хендл.

Сами данные индикаторов извлекаются через массовые CopyBuffer функции с указанием хендла индикатора и требуемых буферов. Упор делается именно на возможность сразу извлечь большой массив (100, 1000, 1000) результатов, а не лазать затратно за каждым значением вглубь iXXX функций, как это делается в МТ4.


2) ADX вычисляется на основе +DI, -DI, которые в свою очередь фиксированны по логике вычислений (high, low, close) и не имеют возможности выбирать от чего считать.

В поставке MetaTrader 5 есть полный исходный код ADX в MQL5\Indicators\Examples\ADX.mq5


спасибо , у меня собственно один вопрос остался,
что выбрать в 4 в кнопке "приметь к ценам" что бы и в 5 и в 4 они строились одинаково.

 
Pavel Verveyko:

я тут подумал , может я просто не занимался "кроссплатформенностью" целенаправленно отсюда такое отношение.

....

я вот всерьёз задумался а может и правда продумать все различия в языках и вписать на уровне препроцессора.
....

я так понимаю это нужно вписать в двух вариантах- организация предопределённых переменных, направлений массивов в индикаторах если нужно конечно (я привык как в 4), получение данных из индикаторов, функции которых нет в 5, и использование одинаковых торговых классов из библиотеки. и тогда будет кросс платформенный код, верно или что то ещё нужно? и не использовать функции ObjectGet то есть без типа в имени так как в 5 такой нет.

Лично я, как поклонник виртуальных интерфейсов - всегда весь код стараюсь обернуть именно в класс-наследник виртуального интерфейса.

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

Вот, скажем, мой виртуальный интерфейс переносимой позиции (хорошая иллюстрация для применения кроссплатформенности):

// СTradePositionI переносимый интерфейс торговой позиции 

// Позиция состоит из компонент-наследников интерфейса CTradePosComponentI
// 
// Реально для МТ4 имплементацией интерфейса является объект CMT4PositionInfo, для МТ5 - объект CMT5PositionInfo
// CMT4PositionInfo представляет из себя массив объектов CMT4OrderInfo, наследников интерфейса CTradePosComponentI.
// Фактически это массив МТ4-ордеров.
// CMT5PositionInfo представляет из себя массив объектов CMT5PositionInfoCore, наследников интерфейса CTradePosComponentI.
// Объект CMT5PositionInfoCore не имеет прямого аналога в МТ5-терминах, это два массива ордеров и сделок, а также структура данных,
// имеющуая идентификатор позиции (m_lPosID или POSITION_IDENTIFIER), тикет магик, вид позиции, и так далее, по аналогии с МТ4-ордером,
// фактически, массивы ордеров и сделок - это аналоги ордеров и сделок обычной неттинговой МТ5-позиции, а структура данных - относится
// к хеджевой позиции, и имеет аналог МТ4-ордера. 
//
// Реально при запросе у позиции компоненты CTradePosComponentI в МТ4 мы получаем указатель на ордер (класс CMT4OrderInfo),
// а в МТ5 - на ядро позиции (класс CMT5PositionInfoCore) 



#include <MyLib\DebugOrRelease\DebugSupport.mqh>
#include <MyLib\Common\MyObject.mqh>
#include <MyLib\Common\CurSymEnum.mq5>
#include <MyLib\Common\TrendEnums.mqh>
#include <MyLib\Trade\TradePosComponentI.mqh>

class CTradePositionI: public CMyObject
{
public:
   void CTradePositionI() {    SetMyObjectType(MOT_TRADE_POSITION_I); };
   virtual void ~CTradePositionI() {};
   
   // Выбор существующей позиции. 
   // Указывается магик и символ, по которому выбираются действующие ордера.
   // Если ulMagic = 0 - выбираются все позиции по всем магикам.
   // Если ECurrencySymbol = CS_UNKNOWN - выбираются все позиции по всем символам
   // Если ECurrencySymbol = CS_CURRENT - запрашивается функция Symbol(), и выбираются все позиции по этому символу
   // Возвращает число компонент позиции внутри позиции (может быть нулевым если позиции нет) или WRONG_VALUE в случае ошибок
   // Каждая фабрика (наследник CEAPartsFactoryT) имеет одну позицию, которая на каждом CEAPartsFactoryT::OnRefresh()
   // обновляется в соответствии с магиком и рабочим символом фабрики. 
   virtual int Select(ulong ulMagic = 0,ECurrencySymbol csSymbol = CS_CURRENT) = 0;

   virtual uint GetTotalComponents() const = 0;  // Получение общего числа компонент
   virtual uint GetNumOfComponentsOnDirection(ENUM_POSITION_TYPE etDirection) const = 0; // Получение числа компонент указанного направления (если tdDirection = TD_FLAT - то всех компонент)  и интерфейс отдельной компоненты
   virtual CTradePosComponentI* GetComponent(uint uiComponentIdx) const = 0;
   
   // Расширенный интерфейс
   
   // Функция исследует позицию, и возвращает ее направление:
   // Если все компоненты - лонги, то вверх.
   // Если все компоненты - шорты, то вниз.
   // Если компонент нет - то флет. 
   // Если компоненты обоих типов, то смотрим на флаг bFlatIfBoth. 
   // Если этот флаг установлен - то возвращаем флет.
   // Если этот флаг сброшен - то смотрим на флаг bConsiderVolume.
   // Если этот флаг установлен - то сравниваем общие объемы лонгов и шортов. Если сброшен - сравниваем количество лонгов и шортов.
   // Каких позиций (или объемов) больше - то направление и возвращаем. 
   // Если позиций (или объемов) одинаково - возвращаем флет.
   // NOTE !!! Функция не проверяет магик и символ компонент !
   virtual ETrendDirection GetDirection(bool bFlatIfBoth = true,bool bConsiderVolume = true) const = 0;
   
   // Функция ищет внутри позиции компоненту с указанным тикетом. 
   // В случае, если ее нет - возвращается false.
   // Если компонента найдена - возвращается true, и uiComponentIdx устанавливается на индекс компоненты внутри позиции.
   virtual bool FindComponentByTicket(long lTicket,uint &uiComponentIdx) const = 0;
};

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

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

// СTradePositionComponentI переносимый интерфейс компоненты торговой позиции 

#include <MyLib\DebugOrRelease\DebugSupport.mqh>
#include <MyLib\Common\MyObject.mqh>
#include <MyLib\Common\CurSymEnum.mq5>
#include <MyLib\Common\EnumsMT5ForMT4.mqh>

// CTradePosComponentI - компонента позиции. Имеет определенный магик, определенный символ, определенный тикет.  
// Для МТ4 компонента позиции - это МТ4-ордер.
// Для МТ5 компонента позиции - это МТ5-позиция (одна для каждого символа при неттинге, и много для каждого символа при хедже).

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
   
   // Основной интерфейс
   virtual long               GetTPCTicket()       const = 0;
   virtual long               GetTPCMagic()        const = 0;
   virtual ECurrencySymbol    GetTPCSymbol()       const = 0;
   virtual ENUM_POSITION_TYPE GetTPCType()         const = 0;
   virtual datetime           GetTPCOpenTime()     const = 0;
   virtual double             GetTPCVolume()       const = 0;
   virtual double             GetTPCOpenPrice()    const = 0;
   virtual double             GetTPCStopLoss()     const = 0;
   virtual double             GetTPCTakeProfit()   const = 0;
   virtual string             GetTPCCommentary()   const = 0;
   
   virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };
   virtual double             GetTPDistance() const { if(GetTPCTakeProfit() == 0 || GetTPCTakeProfit() == EMPTY_VALUE) return(EMPTY_VALUE); if(GetTPCType() == POSITION_TYPE_BUY) return(GetTPCTakeProfit() - GetTPCOpenPrice()); return(GetTPCOpenPrice() - GetTPCTakeProfit());  };
   virtual double             GetSLDistance() const { if(GetTPCStopLoss() == 0 || GetTPCStopLoss() == EMPTY_VALUE) return(EMPTY_VALUE); if(GetTPCType() == POSITION_TYPE_BUY) return(GetTPCOpenPrice()- GetTPCStopLoss()); return(GetTPCStopLoss() - GetTPCOpenPrice());  };
};


// Тип закрытия торговой компоненты
enum ETPCCloseType
{
   TCT_UNKNOWN = WRONG_VALUE,
   TCT_EA,
   TCT_SL,
   TCT_TP
};

class CHistoryPosComponentI: public CTradePosComponentI
{
public:
   void CHistoryPosComponentI() {    SetMyObjectType(MOT_HISTORYPOS_COMPONENT_I); };
   virtual void ~CHistoryPosComponentI() {};

   virtual datetime           GetTPCCloseTime()    const = 0;
   virtual double             GetTPCClosePrice()   const = 0;
   virtual double             GetTPCProfit()       const = 0;  // Возвращает профит по исторической позиции
   
   virtual bool               IsProfitClosePrice() const = 0;   // Возвращает true, если цена зарытия отличается от цены открытия в прибыльную сторону   
   virtual ETPCCloseType      GetCloseType()       const = 0;   // Возвращает тип закрытия компоненты.
   
   // Возвращает профит исторической позиции для случая, когда бы ход цены (в сторону профита) был бы равен dPriceMove, а лот был бы единичным.
   // Функция используется для расчета лота для такой же позиции с нужным ходом цены 
   // Рекомендуется отнимать от цены двойной спред.
   virtual double             CalculateOneLotProfit(double dPriceMove) const = 0;  
  
};

Конкретная реализация - скрыта в реальном классе CTradePosition, поддерживающий общий виртуальный интерфейс предка, который содержит непосредственно платформозависимые классы CMT4PositionInfo или CMT5PositionInfo (это существенно различные классы), разделенные через дефайны на МТ4 и МТ5:

#include <MyLib\Trade\TradePositionI.mqh>

#ifdef __MQL5__
#include <MyLib\Trade\MT5PositionInfo.mq5>
#else // __MQL5__
#include <MyLib\Trade\MT4PositionInfo.mq5>
#endif // __MQL5__


class CTradePosition : public CTradePositionI
{
protected:

#ifdef __MQL5__
   CMT5PositionInfo  m_piMT5Position;
#else // __MQL5__
// Для МТ4 позиция состоит из одного класса CMT4PositionInfo
   CMT4PositionInfo  m_piMT4Position;
   
#endif // __MQL5__

public:
   void CTradePosition();
   void ~CTradePosition() {};
   
   // Выбор существующей совокупной позиции. 
   // Указывается магик и символ, по которому выбираются действующие позиции.
   // Если ulMagic = 0 - выбираются все позиции по всем магикам
   // Если ECurrencySymbol = CS_UNKNOWN - выбираются все позиции по всем символам
   // Если ECurrencySymbol = CS_CURRENT - запрашивается функция Symbol(), и выбираются все позиции по этому символу
   // Возвращает число выбранных компонент позиции (может быть нулевым) или WRONG_VALUE в случае ошибок
   virtual int Select(ulong ulMagic = 0,ECurrencySymbol csSymbol = CS_CURRENT);

   // Получение числа компонент и интерфейса отдельной компоненты
   virtual uint GetTotalComponents() const;
   virtual uint GetNumOfComponentsOnDirection(ENUM_POSITION_TYPE etDirection) const;
   
   virtual CTradePosComponentI* GetComponent(uint uiComponentIdx) const;
   
   // Расширенный интерфейс
   
   // Функция исследует позицию, и возвращает ее направление:
   // Если все компоненты - лонги, то вверх.
   // Если все компоненты - шорты, то вниз.
   // Если компонент нет - то флет. 
   // Если компоненты обоих типов, то смотрим на флаг bFlatIfBoth. 
   // Если этот флаг установлен - то возвращаем флет.
   // Если этот флаг сброшен - то смотрим на флаг bConsiderVolume.
   // Если этот флаг установлен - то сравниваем общие объемы лонгов и шортов. Если сброшен - сравниваем количество лонгов и шортов.
   // Каких позиций (или объемов) больше - то направление и возвращаем. 
   // Если позиций (или объемов) одинаково - возвращаем флет.
   virtual ETrendDirection GetDirection(bool bFlatIfBoth = true,bool bConsiderVolume = true) const;
   
   // Функция ищет внутри позиции компоненту с указанным тикетом. 
   // В случае, если ее нет - возвращается false.
   // Если компонента найдена - возвращается true, и uiComponentIdx устанавливается на индекс компоненты внутри позиции.
   virtual bool FindComponentByTicket(long lTicket,uint &uiComponentIdx) const;
};

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

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

 
George Merts:

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

Да нет же. Все как раз наоборот.

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

 
Andrei:

Да нет же. Все как раз наоборот.

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

Нет. Это ошибка. Как раз блокировка ограничивает тебя от этих самых ошибок.

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

Где тут по-твоему, место для ошибки ?

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

Нужна позиция ? Запросил, получил интерфейс, который тебе не дает ничего лишнего. Ты не имеешь доступа ни к объекту, который эту позицию содержит, ни к каким промежуточным буфферам, вобще ни к чему, кроме конкретно того, в чем заключался твой запрос. Ты даже не можешь сказать, на какой именно платформе работаешь, что конкретно у тебя открыто - то ли МТ4-ордер, то ли МТ5-Хедж-позиция, то ли МТ5-Нетт-позиция. Ты это можешь узнать, запросив соответствующие интерфейсы в другом месте, но там - ты не будешь иметь доступа к позициям.

Сам я когда-то тоже был сторонником прямого доступа к любым структурам данным программы. Однако, по мере роста проектов - я убедился в ошибочности такого подхода.

Считаю очень верным принцип: пользователь имеет доступ исключительно только к тому, что запросил, и ни к биту больше.

 
fxsaber:

MT4 так популярен, потому что это был ПЕРВЫЙ терминал большинства трейдеров. MT5 же - не первый терминал большинства трейдеров. В этом и причина.

Разве были серьёзные проблемы с переходом на новый терминал? Проблемы - с переходом на новый язык MQL5. Представьте что первым был бы 5 - переход с него на MQL4 прошёл бы со свистом. Причина не в трейдерах, а в языке - эта революция не была ориентирована на масс-пользователя MQL или хотя бы адаптирована для него. Поэтому ущемлённый масс-пользователь и запищал

 
Alexander Puzanov:

Разве были серьёзные проблемы с переходом на новый терминал? Проблемы - с переходом на новый язык MQL5. Представьте что первым был бы 5 - переход с него на MQL4 прошёл бы со свистом. Причина не в трейдерах, а в языке - эта революция не была ориентирована на масс-пользователя MQL или хотя бы адаптирована для него. Поэтому ущемлённый масс-пользователь и запищал

Да нету никаких проблем "с переходом на новый язык", поскольку все конструкции языка MQL4 - прекрасно работают в MQL5.

Разница - лишь в некоторых функциях, и в протоколах работы с торговыми приказами.

 
Alexander Puzanov:

Разве были серьёзные проблемы с переходом на новый терминал? Проблемы - с переходом на новый язык MQL5. Представьте что первым был бы 5 - переход с него на MQL4 прошёл бы со свистом. Причина не в трейдерах, а в языке - эта революция не была ориентирована на масс-пользователя MQL или хотя бы адаптирована для него. Поэтому ущемлённый масс-пользователь и запищал

Мы говорим не про меня, и не про Вас. А про массового (среднего) пользователя. Который понятия не имеет об языках.

Если взять среднего пользователя QUICK, то он с большим скрипом пересядет на MT5. Потому что он УЖЕ знает QUICK, но ЕЩЕ не знает MT5. Всему причина лень, а не языки программирования.

И всякая аргументы, что вот в квике есть опционы, а в MT5 - нет. Поэтому не переходят. Эти аргументы ерунда. Причина в банальной лени.

 
George Merts:

Да нету никаких проблем "с переходом на новый язык", поскольку все конструкции языка MQL4 - прекрасно работают в MQL5.

Разница - лишь в некоторых функциях, и в протоколах работы с торговыми приказами.

Все зависит от уровня пользователя.

На самом деле, при переходе проблемы возникают на каждом шагу.

Я пытался переписать свой проект на МТ5, но получил столько предупреждений и ошибок, что временно оставил переход.

Иначе говоря, вся проблема в переходе на МТ5. Он оказывается слишком трудозатратным для большинства.

Все они рассуждают примерно так: "Да, мой код на МТ4 менее быстр и профессионален, и пусть в нем больше помарок и легких ошибок, но он работает! Что будет если я его перепишу? "

И задают схожие вопросы: "Зачем мне переходить если меня все устраивает? Что я получу, если перейду? Сколько времени займет переписывание кода? Будет ли код работать по прежнему?"

Кодо-база под МТ4 богаче. Для многих это очень важно, ведь это способствует их развитию.

Давай будем объективными и составим "портрет" среднестатистического форекс-трейдера.

  1. Кто он?
  2. Каков его уровень владения программированием?
  3. На что ему хочется тратить время - на проверку своих торговых идей, или на повышение профессионализма написания программ?
  4. Зачем ему награмождающаяся сложность инструментария, когда основная задача перед ним - быстрая компоновка и проверка своих стратегий?

Разве не будет он пользоваться тем инструментом, с которым можно легче и быстрее получить результат?

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

  • "Ребята, не надо меня лечить".
  • "Я сам знаю, что для меня лучше".
  • "Возможности нового языка не гарантируют, что я получу с рынка больше денег."
  • " Мои стратегии не требуют использовать инструментарий такой сложности. "
 
Реter Konow:

Все зависит от уровня пользователя.

На самом деле, при переходе проблемы возникают на каждом шагу.

Я пытался переписать свой проект на МТ5, но получил столько предупреждений и ошибок, что временно оставил переход.

Иначе говоря, вся проблема в переходе на МТ5. Он оказывается слишком трудозатратным для большинства.

Все они рассуждают примерно так: "Да, мой код на МТ4 менее быстр и профессионален, и пусть в нем больше помарок и легких ошибок, но он работает! Что будет если я его перепишу? "

И задают схожие вопросы: "Зачем мне переходить если меня все устраивает? Что я получу, если перейду? Сколько времени займет переписывание кода? Будет ли код работать по прежнему?"

Кодо-база под МТ4 богаче. Для многих это очень важно, ведь это способствует их развитию.

Давай будем объективными и составим "портрет" среднестатистического форекс-трейдера.

  1. Кто он?
  2. Каков его уровень владения программированием?
  3. На что ему хочется тратить время - на проверку своих торговых идей, или на повышение профессионализма написания программ?
  4. Зачем ему награмождающаяся сложность инструментария, когда основная задача перед ним - быстрая компоновка и проверка своих стратегий?

Разве не будет он пользоваться тем инструментом, с которым можно легче и быстрее получить результат?

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

  • "Ребята, не надо меня лечить".
  • "Я сам знаю, что для меня лучше".
  • "Возможности нового языка не гарантируют, что я получу с рынка больше денег."
  • " Мои стратегии не требуют использовать инструментарий такой сложности. "

+100!

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