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

 
Pavel Verveyko:

а вот пример - код компилируется в обоих компиляторах. 

(белая рамка перемещается мышью за ней синхронно двигается чёрный квадрат)
но в MT5 так не работает расстояние нарушается  (значит где то есть разница в обработке функций)

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

Было:

case CHARTEVENT_MOUSE_MOVE:
      {
         if (ObjectGetInteger(0,Common_List,OBJPROP_SELECTED))
         {                   
         
            long x_div = ObjectGetInteger(0,Common_List,OBJPROP_XDISTANCE,0)-x_memory;
            long y_div = ObjectGetInteger(0,Common_List,OBJPROP_YDISTANCE,0)-y_memory;     
                  
            ObjectSetInteger(0,Other_List,OBJPROP_XDISTANCE,ObjectGetInteger(0,Other_List,OBJPROP_XDISTANCE)+x_div);
            ObjectSetInteger(0,Other_List,OBJPROP_YDISTANCE,ObjectGetInteger(0,Other_List,OBJPROP_YDISTANCE)+y_div);

            x_memory = ObjectGetInteger(0,Common_List,OBJPROP_XDISTANCE,0);
            y_memory = ObjectGetInteger(0,Common_List,OBJPROP_YDISTANCE,0);

            ChartRedraw(0);
         }      
         break;
      }

Стало:

case CHARTEVENT_MOUSE_MOVE:
      {
         if (ObjectGetInteger(0,Common_List,OBJPROP_SELECTED))
         {                   
         
            long x_div = ObjectGetInteger(0,Common_List,OBJPROP_XDISTANCE,0);
            long y_div = ObjectGetInteger(0,Common_List,OBJPROP_YDISTANCE,0);     
                  
            ObjectSetInteger(0,Other_List,OBJPROP_XDISTANCE,ObjectGetInteger(0,Other_List,OBJPROP_XDISTANCE)+x_div-x_memory);
            ObjectSetInteger(0,Other_List,OBJPROP_YDISTANCE,ObjectGetInteger(0,Other_List,OBJPROP_YDISTANCE)+y_div-y_memory);

            x_memory = x_div;
            y_memory = y_div;

            ChartRedraw(0);
         }      
         break;
      }
 

ILNUR777:
 1. Так запросов основных за 7 лет глобально лишь 2 было. Тики с вощможностью делать из них то о чём говорят выше. И уважить локеров с их разными позициями по сделкам. Всё. Хотелку локеров ведь реализовали со временем. Осталась одна. Больше запросов глобальных и нет.

1. Не правы. Запросов выше крыши.

2. Вы говорили о кастомных таймфреймах. То есть, не правы в исходном посыле.

3. Мое объяснение важно для понимания причин. Попытка свести "вот же только одна осталась" не принимается, так как ничем не отличается от предыдущих тем.


Конкуренты почти все поумирали, кстати. Сейчас будет тяжело оставшимся, после того, как мы в полный рост выпустим встроенные датафиды.

Сейчас основная проблема с отсутствием знаний и понимания возможностей MetaTrader 5 у трейдеров. Над этим и работаем.

 
Renat Fatkhullin:



1. Конкуренты почти все поумирали, кстати. Сейчас будет тяжело оставшимся, после того, как мы в полный рост выпустим встроенные датафиды.

2. Сейчас основная проблема с отсутствием знаний и понимания возможностей MetaTrader 5 у трейдеров. Над этим и работаем.

1.Конкурентов и так можно сказать нет почти. Не беру номинально конкурирующих. 

2. А вот это хорошая новость. 

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

 
ILNUR777:

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

Распространить статью по расходам равно распространению терминала.

Вы думаете, что кто-то читает статьи?

Да на каждое прочтение статьи надо потратить 3-5 долларов рекламного бюджета. Когда осознаете это, то вся ваша картина мира "как надо дела делать", перевернется.

 
Renat Fatkhullin:

Распространить статью по расходам равно распространению терминала.

Вы думаете, что кто-то читает статьи?

Да на каждое прочтение статьи надо потратить 3-5 долларов рекламного бюджета.

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

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

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

И в этой фразе MT4/5 можно заменить на любой другой конкурирующий терминал, суть не изменится. Освоение первого инструмента не вызывает столько лени, как освоение второго. Особенно, когда СРАЗУ не очевидны преимущества второго. А MT5 внешне идентичен MT4. Те же идиотские скины GUI могли бы кардинально изменить отношение к терминалу, но их нет. Встречают по внешней одежке, а не по внутреннему функционалу.

 

Не правы в обоих вопросах, но спорить нет желания.

Учитывайте, что из любого набора советов/идей/объяснений 80-90% будут категорически ошибочны из-за банальности. Ну а по оставшимся тоже еще спорить надо. Но с цифрами на руках, безусловно.

 
Stanislav Korotky:

По этому поводу задавал вопрос в СД (2017.05.03 18:23, #1734753). Ответа нет до сих пор.


у меня тоже были заявки о которых приходилось напоминать через другие заявки о ни наверно могут из за большого количества теряться (

 
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;
};

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

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