Библиотеки: Expert

 

Expert:

Библиотека чтения/записи параметров произвольных советников.


К описанию прикреплены примеры/сценарии ее использования.

  • ExpertsRemove.mq5

    // Снимает запущенные советники со всех чартов
  • ExpertsReopen.mq5

    // Перезапускает работающие советники
  • ChartsClose.mq5

    // Закрывает все чарты, где отсутствуют советники (полезно для VPS)
  • ExpertLoader_Example.mq5 (в исходнике можно увидеть, как запускается не только советник, но и скрипт (сам себя) под видом советника)

    // Запуск советника с заданными входными параметрами
  • ExpertsChange_Example.mq5

    // Перезапускает работающие советники, изменяя входные параметры

Это самые простые в реализации сценарии применения библиотеки. Так же она может быть полезна при написании различных управляющих панелей чартами/советниками и т.п.

Для лучшего понимания работы с библиотекой короткий исходник:

// Запуск советника с заданными входными параметрами
#include <fxsaber\Expert.mqh>

void OnStart()
{
  MqlParam Params[2];
  
  // Путь к советнику
  Params[0].string_value = "Experts\\Advisors\ExpertMACD.ex5";

  // Первый входной параметр советника
  Params[1].type = TYPE_STRING;
  Params[1].string_value = "Hello World!";

  // На новом чарте запускаем советник
  EXPERT::Run(ChartOpen(_Symbol, _Period), Params);
}

Автор: fxsaber

 

Пояснение к этому

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Вопросы от начинающих MQL4 MT4 MetaTrader 4

fxsaber, 2017.09.08 13:52

При использовании ChartApplyTemplate требуется обязательная синхронизация, которую в библе делаю так

  static bool TemplateApply( const long Chart_ID, const string &Str, const bool Sync = true )
  {
    string TmpStr = Str;

    const bool SyncFlag = (Sync && Chart_ID && (Chart_ID != ::ChartID()) && !::IsStopped());

    if (SyncFlag)
    {
      const color ColorStopLevel = (color)::ChartGetInteger(Chart_ID, CHART_COLOR_STOP_LEVEL);

      if ((bool)(ColorStopLevel >> 24))
        ::ChartSetInteger(Chart_ID, CHART_COLOR_STOP_LEVEL, ColorStopLevel & 0xFFFFFF);

      const int NewColorStopLevel = (int)EXPERT::StringBetween(TmpStr, EXPERT_STOPLEVEL, STRING_END) | (0x01 << 24);

      TmpStr = Str;
      EXPERT::StringReplace(TmpStr, EXPERT_STOPLEVEL, STRING_END, EXPERT_STOPLEVEL + (string)NewColorStopLevel + STRING_END);
    }

    short Data[];
    const bool Res = ::StringToShortArray(TmpStr, Data, 0, ::StringLen(TmpStr)) &&
                     ::FileSave(FILENAME, Data) && ::ChartApplyTemplate((ulong)Chart_ID, FILENAME);

    if (Res && SyncFlag)
    {
      long Value;

      while ((!::IsStopped() && ::ChartGetInteger(Chart_ID, CHART_COLOR_STOP_LEVEL, 0, Value) && (!(bool)((int)Value >> 24))))
        ::Sleep(0);

      ::ChartSetInteger(Chart_ID, CHART_COLOR_STOP_LEVEL, (int)Value & 0xFFFFFF);
    }

    return(Res);
  }

ChartApply не сразу срабатывает. А какие-либо дальнейшие действия можно делать только после срабатывания.


Чтобы понять, что шаблон применился, нужно изменить сам шаблон (в библе изменяется цвет одной характеристики чарта - 4-й байт, отвечающий за прозрачность) и через Sleep дождаться (ChartGetInterger), когда это значение станет свойством чарта. После этого задать ему нормальное значение через ChartSetInteger.


Если же нужно еще и советник запустить на том же чарте, куда кидается скрипт, то нужно открывать новый чарт и на нем через шаблон запускать себя же (скрипт), а оттуда запускать на нужном уже нам чарте советник, закрыв вспомогательный. Это проделывает ExpertLoader_Example.mq5.

 

может быть полезна при написании различных управляющих панелей чартами/советниками и т.п.

Библиотека работает без DLL - полностью удовлетворяет требованиям Маркета.

 

Небольшой лайфхак - запуск советников/скриптов на OBJ_CHART-объектах.

Так запущенные советники висят мертво - никак не выполняются. А вот скрипты работают отлично. Поэтому это открывает некоторые возможности.

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


Компилируем скрипт Scripts\OrderSend.mq5

#include <MT4Orders.mqh>       // https://www.mql5.com/ru/code/16006
#include <GlobalVariables.mqh> // https://www.mql5.com/ru/forum/189649#comment_4854618

struct ORDERSEND
{
  int Type;
  double Volume;
  double Price;
  int SlipPage;
  double SL;
  double TP;
  long Magic;
  datetime Expiration;
  color Arrow_Color;
};

void OrderSend()
{
  const ORDERSEND Order = _GlobalVariableGet<ORDERSEND>("ORDERSEND");
  const string Symb = _GlobalVariableGet<string>("Symbol");
  const string comment = _GlobalVariableGet<string>("Comment");
      
  _GlobalVariableSet(__FUNCTION__, OrderSend(Symb, Order.Type, Order.Volume, Order.Price, Order.SlipPage, Order.SL, Order.TP, comment, Order.Magic, Order.Expiration, Order.Arrow_Color));
  
  _GlobalVariableDel("ORDERSEND");
  _GlobalVariableDel("Symbol");
  _GlobalVariableDel("Comment");  
}

void OnStart()
{
  OrderSend();
}


И запускаем индикатор, который "умеет торговать"

#include <fxsaber\Expert.mqh>  // https://www.mql5.com/ru/code/19003
#include <GlobalVariables.mqh> // https://www.mql5.com/ru/forum/189649#comment_4854618

struct ORDERSEND
{
  int Type;
  double Volume;
  double Price;
  int SlipPage;
  double SL;
  double TP;
  long Magic;
  datetime Expiration;
  color Arrow_Color;
};

template <typename T>
long _GlobalVariableGet2( const string Name, const ulong MaxTime = 1e6 )
{
  const ulong StartTime = GetMicrosecondCount();
  
  while (!IsStopped() && !GlobalVariableCheck(Name) && (GetMicrosecondCount() - StartTime < MaxTime))
    Sleep(0);
      
  return(_GlobalVariableGet<T>(Name));
}

// OrderSend, который работает даже из индикатора хоть на чарте с советником
long OrderSend( string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SL, const double TP,
                string comment = NULL, const long magic = 0, const datetime dExpiration = 0, color arrow_color = clrNONE )
{
  MqlParam Params[1];    
  Params[0].string_value = "Scripts\\OrderSend.ex5";

  ORDERSEND Order;

  Order.Type = Type;
  Order.Volume = dVolume;
  Order.Price = Price;
  Order.SlipPage = SlipPage;
  Order.SL = SL;
  Order.TP = TP;
  Order.Magic = magic;
  Order.Expiration = dExpiration;
  Order.Arrow_Color = arrow_color;

  const long Res = ObjectCreate(0, __FILE__, OBJ_CHART, 0, 0, 0) && _GlobalVariableSet("ORDERSEND", Order) &&
                   _GlobalVariableSet("Symbol", Symb) && _GlobalVariableSet("Comment", comment) &&
                   EXPERT::Run(ObjectGetInteger(0, __FILE__, OBJPROP_CHART_ID), Params) &&
                   ObjectDelete(0, __FILE__) ? _GlobalVariableGet2<long>(__FUNCTION__) : -1;  
  
  _GlobalVariableDel(__FUNCTION__);

  return(Res);
}

void OnInit()
{  
  Print(OrderSend(_Symbol, ORDER_TYPE_BUY, 1, SymbolInfoDouble(_Symbol, SYMBOL_ASK), 100, 0, 0, "From Indicator", 9));
}

int OnCalculate( const int, const int, const int, const double &[] )
{
  return(0);
}
 
Нет технических ограничений, не позволяющих доработать библиотеку до кроссплатформенного состояния - работать и в MT4.
 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Управление советниками через глобальный алгоритм

Totosha16, 2018.02.07 18:57

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

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

void OnStart()
{    
  const long CurrentChart = ChartID();  
  long Chart = ChartFirst();

  while (Chart != -1)
  {
    if (Chart != CurrentChart)
      EXPERT::Remove(Chart);

    Chart = ChartNext(Chart);
  }
}
 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

Vladislav Andruschenko, 2018.02.09 10:14

как получить список внешних переменных внутри эксперта? чтобы не перечислять их в массиве повторно? тоесть при установке на график эксперт читает сам себя и смотрит свои внешние настройки.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

fxsaber, 2018.02.09 12:44

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

input string Input1 = "Hello World!";
input int Input2 = 123;

string GetExpertData( const ulong Chart = 0 ) 
{ 
  string Str = NULL; 

  MqlParam Parameters[]; 
  string Names[]; 

  if (EXPERT::Parameters(Chart, Parameters, Names)) 
  { 
    Str += "\n" + ChartSymbol(Chart) + " " + EnumToString(ChartPeriod(Chart)) + " " + Parameters[0].string_value + "\n"; 

    const int Amount = ArraySize(Names); 

    for (int i = 0; i < Amount; i++) 
      Str += (string)i + ": "+ Names[i] + " = " + Parameters[i + 1].string_value + "\n"; 
  } 

  return(Str); 
}

void OnInit()
{
  Print(GetExpertData());
}


Результат

0: Input1 = Hello World!
1: Input2 = 123


или так

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

input string Input1 = "Hello World!";
input int Input2 = 123;

void OnInit()
{
  MqlParam Parameters[];
  string Names[];   
  
  if (EXPERT::Parameters(0, Parameters, Names))
    ArrayPrint(Parameters);
}


Результат

    [type] [integer_value] [double_value]      [string_value]
[0]    ...               0        0.00000 "Experts\Test2.ex5"
[1]    ...               0        0.00000 "Hello World!"     
[2]    ...             123      123.00000 "123"              
 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

fxsaber, 2018.02.22 23:53

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

Скрипт Scripts\PlaySound.mq5

#include <GlobalVariables.mqh> // https://www.mql5.com/ru/forum/189649#comment_4854618

void OnStart()
{
  const string SoundName = "SOUND";

  if (GlobalVariableCheck(SoundName))
  {
    PlaySound(_GlobalVariableGet<string>(SoundName));
  
    _GlobalVariableDel(SoundName);
  }
}


Индикатор

#property indicator_chart_window

#property indicator_buffers 0
#property indicator_plots indicator_buffers

#include <fxsaber\Expert.mqh>  // https://www.mql5.com/ru/code/19003
#include <GlobalVariables.mqh> // https://www.mql5.com/ru/forum/189649#comment_4854618

class PLAYER
{
public:
  const string Name;
  const long Chart;
  const long chartID;
  
  PLAYER( const long iChart = 0 ) : Name(__FILE__), Chart(iChart ? iChart : ::ChartID()),
                                    chartID(::ObjectCreate(this.Chart, this.Name, OBJ_CHART, 0, 0, 0)   &&
                                            ::ObjectSetInteger(this.Chart, this.Name, OBJPROP_XSIZE, 0) &&
                                            ::ObjectSetInteger(this.Chart, this.Name, OBJPROP_YSIZE, 0) ?
                                            ::ObjectGetInteger(this.Chart, this.Name, OBJPROP_CHART_ID) : this.Chart)
  {
  }
  
  ~PLAYER()
  {
    if (this.chartID != this.Chart)
      ::ObjectDelete(this.Chart, this.Name);
  }
  
  void PlaySound( string FileName, const string ScriptName = "Scripts\\PlaySound.ex5" ) const
  {
    static const string SoundName = "SOUND";
    
    if (_GlobalVariableSet(SoundName, FileName))
    {
      MqlParam Params[1];
      
      Params[0].string_value = ScriptName;
      
      if (!EXPERT::Run(this.chartID, Params))      
        _GlobalVariableDel(SoundName);
    }    
  }
};

int OnCalculate( const int rates_total , const int prev_calculated, const int, const double& [] )
{  
  if (!prev_calculated)
  {
    const PLAYER Player;
    
    Player.PlaySound("email.wav");
  }

  return(rates_total);
}
 

В МТ4, как оказалось, отсутствует намного больше, чем FileSave и FileLoad (которые пишутся в 3 строках каждая):

  1. Нет CHART_EXPERT_NAME (и нечем заменить, разве что после исправления всех остальных нюансов — тегом name).
  2. Лобовой FileLoad не подходит, насколько я понимаю, из-за ANSI-кодировки сохраняемого шаблона.
    Пришлось написать аналог TemplateToString, читающий файл в текстовом режиме.
  3. STRING_END должен быть пустым, "\r\n" в четверочных шаблонах нет.
  4. Тэг <expert> в МТ4 используется и для индикаторов, поэтому даже после всех этих правок обнаружить — где индикаторы, а где советник, можно только, положившись на то, что советник указывается последним (а всегда ли это так?). Ну, и добраться до него нужно.
А вообще, очень нужная и удобная вещь, благодарю за реализацию!
 

Andrey Khatimlianskii:

Тэг <expert> в МТ4 используется и для индикаторов, поэтому даже после всех этих правок обнаружить — где индикаторы, а где советник, можно только, положившись на то, что советник указывается последним (а всегда ли это так?). Ну, и добраться до него нужно.

Этот исходник может помочь разобраться в вопросе.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Как в индикаторе узнать текущий цвет индикаторной линии?

fxsaber, 2017.05.12 13:45

#property strict

#property indicator_chart_window
#property indicator_buffers 2

#define PATH "MQL4\\indicators\\"

#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

string GetIndicatorName( void )
{
  const string StrName = ::MQLInfoString(MQL_PROGRAM_PATH);
  const int Pos = ::StringFind(StrName, PATH) + ::StringLen(PATH);
  
  return(::StringSubstr(StrName, Pos, ::StringLen(StrName) - Pos - 4));
}

void SeekToString( const int handle, const string Str )
{
  while (!::FileIsEnding(handle))
    if (::FileReadString(handle) == Str)
      break;
  
  return;
}  

struct BUFFER_STRUCT
{
  int Shift;
  int Type;
  color Color;
  ENUM_LINE_STYLE Style;
  int Width;
};

const BUFFER_STRUCT GetBufferProperties( const uint Num = 0, const bool FlagSave = true )
{
  BUFFER_STRUCT Res = {0};
  
  const string FileName = ::WindowExpertName() + ".tpl";

  if (FlagSave ? ::ChartSaveTemplate(0, "..\\MQL4\\Files\\" + FileName) : true)
  {
    const int handle = ::FileOpen(FileName, ::FILE_READ|::FILE_CSV);

    if (handle > 0)
    {
      ::SeekToString(handle, "name=" + ::GetIndicatorName());
      
      if (Num == 0)
        ::SeekToString(handle, "</expert>");
      else
      {
        const string TmpStr = "weight_" + (string)(Num - 1);
        
        while (!::FileIsEnding(handle))
          if (::StringFind(::FileReadString(handle), TmpStr) == 0)
            break;
      }
            
      if (!::FileIsEnding(handle))
      {
        static const string Property[] = {"shift", "draw", "color", "style", "weight"};
        const string StrNum = "_" + (string)Num + "=";
              
        for (int i = 0; i < ::ArraySize(Property); i++)
          _W(Res)[i * sizeof(int)] = (int)::StringToInteger(::StringSubstr(::FileReadString(handle), ::StringLen(Property[i] + StrNum)));
      }
      
      ::FileClose(handle);
    }
  }
  
  return(Res);
}  

void OnInit()
{  
  string Str = "Colors:";
  
  for (int i = 0; i < indicator_buffers; i++)
    Str += " " + (string)i + "-" + (string)::GetBufferProperties(i).Color;
    
  Alert(Str);
}

void start()
{
}
 
fxsaber:

Этот исходник может помочь разобраться в вопросе.

Благодарю! Как всегда, чуть сложнее, чем хотелось бы ;)

У ваших кодов есть одно неоспоримое преимущество — их можно брать, и использовать. Но что-либо подправить в них достаточно сложно, это минус.