Bibliotecas: Expert

 

Expert:

Biblioteca de leitura/gravação de parâmetros de EAs arbitrários.

Autor: fxsaber

 

Explicação sobre isso

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

Perguntas dos iniciantes em MQL4 MT4 MetaTrader 4

fxsaber, 2017.09.08 13:52

Ao usar o ChartApplyTemplate, é necessária uma sincronização obrigatória, que eu faço assim na Bíblia

  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 não é acionado imediatamente. E qualquer outra ação pode ser realizada somente após o acionamento.


Para entender que o modelo foi aplicado, você precisa alterar o próprio modelo (a cor de uma característica do gráfico é alterada na Bíblia - o 4º byte, responsável pela transparência) e aguardar por meio do Sleep (ChartGetInterger) quando esse valor se tornar uma propriedade do gráfico. Depois disso, defina seu valor normal por meio de ChartSetInteger.


Se precisarmos executar um Expert Advisor no mesmo gráfico em que o script é lançado, precisaremos abrir um novo gráfico e executar o mesmo (script) nele por meio do modelo e, a partir daí, executar o Expert Advisor no gráfico de que precisamos, depois de fechar o auxiliar. Isso é feito pelo ExpertLoader_Example.mq5.

 

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

A biblioteca é executada sem DLLs, atendendo plenamente aos requisitos do mercado.

 

Um pequeno hack - execução de EAs/scripts em objetos OBJ_CHART.

Dessa forma, os Expert Advisors lançados ficam parados - eles não são executados de forma alguma. Mas os scripts funcionam perfeitamente bem. Portanto, isso abre algumas possibilidades.

Por exemplo, você pode usar as funções de ordem dos indicadores nos gráficos, onde já existe um Expert Advisor em execução. E você não precisa abrir nenhum novo gráfico auxiliar.


Compile o script Scripts\OrderSend.mq5

#include <MT4Orders.mqh>       // https://www.mql5.com/pt/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();
}


E execute o indicador, que "pode negociar".

#include <fxsaber\Expert.mqh>  // https://www.mql5.com/pt/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 = 1 e6 )
{
  const ulong StartTime = GetMicrosecondCount();
  
  while (!IsStopped() && !GlobalVariableCheck(Name) && (GetMicrosecondCount() - StartTime < MaxTime))
    Sleep(0);
      
  return(_GlobalVariableGet<T>(Name));
}

// OrderSend, que funciona mesmo a partir do indicador, mesmo em um gráfico com um Expert Advisor
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);
}
 
Não há limitações técnicas que não permitam refinar a biblioteca para um estado de plataforma cruzada - para funcionar também no MT4.
 

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

Gerenciando Expert Advisors por meio de um algoritmo global

Totosha16, 2018.02.07 18:57

No momento, estou tentando resolver um problema simples usando sua biblioteca, que se resume ao seguinte: fechar todos os Expert Advisors em todos os gráficos, exceto o atual (aquele a partir do qual o script ExpertRemove é executado). Você pode me dizer como fazer isso?

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

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

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

    Chart = ChartNext(Chart);
  }
}
 

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

Erros, bugs, perguntas

Vladislav Andruschenko, 2018.02.09 10:14 AM

Como obter a lista de variáveis externas dentro do Expert Advisor? para que eu não tenha que listá-las na matriz repetidamente? ou seja, ao configurá-lo em um gráfico, o Expert Advisor lê a si mesmo e olha para suas configurações externas.

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação.

Erros, bugs, perguntas

fxsaber, 2018.02.09 12:44 pm.

#include <fxsaber\Expert.mqh> // https://www.mql5.com/pt/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());
}


Resultado

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


ou assim

#include <fxsaber\Expert.mqh> // https://www.mql5.com/pt/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);
}


Resultado

    [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"              
 

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

Erros, bugs, perguntas

fxsaber, 2018.02.22 23:53

Reproduzir um arquivo de som de qualquer duração a partir do indicador.

Scripts\PlaySound.mq5 script.

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


Indicador

#property indicator_chart_window

#property indicator_buffers 0
#property indicator_plots indicator_buffers

#include <fxsaber\Expert.mqh>  // https://www.mql5.com/pt/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);
}
 

O MT4, como se viu, está faltando muito mais do que FileSave e FileLoad (que são escritos em 3 linhas cada):

  1. Não há CHART_EXPERT_NAME (e nada para substituí-lo, exceto depois de corrigir todas as outras nuances - a tag de nome).
  2. O Frontal FileLoad não é adequado, pelo que entendi, devido à codificação ANSI do modelo salvo.
    Tive de escrever um TemplateToString análogo que lê o arquivo no modo de texto.
  3. STRING_END deve estar vazio, não há "\r\n" em modelos quádruplos.
  4. A tag <expert> no MT4 também é usada para indicadores, portanto, mesmo depois de todas essas edições, você pode descobrir onde estão os indicadores e onde está o EA apenas confiando no fato de que o EA é especificado por último (e é sempre assim?). Bem, e você precisa chegar até ele.
Em geral, uma coisa muito necessária e conveniente. Obrigado pela implementação!
 

Andrey Khatimlianskii:

A tag <expert> no MT4 também é usada para indicadores, portanto, mesmo depois de todas essas edições, só é possível descobrir onde estão os indicadores e onde está o Expert Advisor confiando no fato de que o Expert Advisor é especificado por último (e é sempre assim?). Bem, e você precisa chegar até ele.

Este código-fonte pode ajudar a entender a questão.

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação.

Como descobrir a cor atual da linha do indicador em um indicador?

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/pt/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:

Essa fonte pode ajudar a entender o problema.

Obrigado! Como sempre, um pouco mais complicado do que eu gostaria ;)

Seus códigos têm uma vantagem inegável: eles podem ser pegos e usados. Mas é muito difícil ajustar qualquer coisa neles, isso é uma desvantagem.