Эмуляция тиков из советника/индикатора

 

Давно задавался целью реализовать автоматическую эмуляцию тиков из советника/индикатора (т.е. чтобы он сам себя мог "тикать"), не прибегая к использованию самописных DLL или запуску скриптов. Чисто спортивный интерес, так сказать :)  Хотя возможно кому-то это будет очень полезно, если стоит задача не выходить за рамки файла MQL, а зацикливать код нельзя (в частности индикаторы).

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

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

 

#import "user32.dll"
  int   RegisterWindowMessageA(string lpString);
  int   SetTimer(int hWnd,int nIDEvent,int uElapse,int& lpTimerFunc[]);
  bool  KillTimer(int hWnd,int uIDEvent);
#import "kernel32.dll"
  int   GetModuleHandleA(string lpModuleName);
  int   GetProcAddress(int hModule,string lpProcName);
  

int TimerId=666;

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

int init()
{
  SetMyTimer(1000);  // интервал в миллисекундах
}
//----------------------------------------------------------------------

int deinit()
{
  KillMyTimer();
}  

//+------------------------------------------------------------------+
//| program start function                                           |
//+------------------------------------------------------------------+
int start()
{
   PlaySound("tick.wav"); 
}
//-------------------------------------------------------------------

int SetMyTimer(int interval)
{    
  int MT4InternalMsg= RegisterWindowMessageA("MetaTrader4_Internal_Message");
  int hWnd= WindowHandle(Symbol(),Period());
  int PostMsgAddr= GetProcAddress(GetModuleHandleA("user32.dll"),"PostMessageA");
  int code[];
  AddBytes(code,0x55);  // push ebp
  AddBytes(code,0x8B);  // move ebp,esp
  AddBytes(code,0xEC);  
  AddBytes(code,0x6A);  // push 01
  AddBytes(code,0x01);
  AddBytes(code,0x6A);  // push 02
  AddBytes(code,0x02);
  AddBytes(code,0x68);  // push MT4InternalMsg
  AddBytes(code,MT4InternalMsg,4);
  AddBytes(code,0x68);  // push hWnd
  AddBytes(code,hWnd,4);
  AddBytes(code,0xB8);  // mov eax, PostMsgAddr
  AddBytes(code,PostMsgAddr,4);
  AddBytes(code,0xFF);  // call eax
  AddBytes(code,0xD0);  
  AddBytes(code,0x33);  // xor eax, eax
  AddBytes(code,0xC0);
  AddBytes(code,0x5D);  // pop ebp
  AddBytes(code,0xC3);  // ret
  
  return (SetTimer(hWnd, TimerId, interval, code) );
}
//---------------------------------------------------

bool KillMyTimer()
{
  return( KillTimer(WindowHandle(Symbol(),Period()), TimerId) );
}
//+------------------------------------------------------------------+

int AddBytes(int& code[], int value, int bytescount=1)
{
  static int pos=0;  // текущая позиция (в байтах)
  if (ArraySize(code)==0) pos=0;
  for (int i=0; i<bytescount; i++, pos++)
  {
    int cell=pos/4;
    if (cell>=ArraySize(code)) ArrayResize(code,cell+1);
    int byte=pos%4;
    code[cell] &= ~(0xFF<<(byte*8));    // обнуляем место под байт
    code[cell] |= value&0xFF<<(byte*8); // записываем байт
    value>>=8;
  }  
  return(pos);
}
 
чем Ваш код лучше "тикалки" из скрипта period_converter ?
 
IgorM:
чем Ваш код лучше "тикалки" из скрипта period_converter ?
Тем, что он не зацикленный, и соответственно может быть использован в индикаторах.
 

сделал так:

#property indicator_chart_window

#import "user32.dll"
  int   RegisterWindowMessageA(string lpString);
  int   SetTimer(int hWnd,int nIDEvent,int uElapse,int& lpTimerFunc[]);
  bool  KillTimer(int hWnd,int uIDEvent);
#import "kernel32.dll"
  int   GetModuleHandleA(string lpModuleName);
  int   GetProcAddress(int hModule,string lpProcName);
int TimerId=666;

//+------------------------------------------------------------------+
int init(){
   SetMyTimer(1000);  // интервал в миллисекундах
return(0);
}
//+------------------------------------------------------------------+
int deinit(){
   KillMyTimer();
   Comment("");
return(0);
}
//+------------------------------------------------------------------+
int start(){
   int i,limit;
   limit = 1000;
   for(i=limit; i>=0; i--){
      Comment("i = ",i);
   }
   Print("цикл завершен....");
return(0);
}

int SetMyTimer(int interval)
{    
  int MT4InternalMsg= RegisterWindowMessageA("MetaTrader4_Internal_Message");
  int hWnd= WindowHandle(Symbol(),Period());
  int PostMsgAddr= GetProcAddress(GetModuleHandleA("user32.dll"),"PostMessageA");
  int code[];
  AddBytes(code,0x55);  // push ebp
  AddBytes(code,0x8B);  // move ebp,esp
  AddBytes(code,0xEC);  
  AddBytes(code,0x6A);  // push 01
  AddBytes(code,0x01);
  AddBytes(code,0x6A);  // push 02
  AddBytes(code,0x02);
  AddBytes(code,0x68);  // push MT4InternalMsg
  AddBytes(code,MT4InternalMsg,4);
  AddBytes(code,0x68);  // push hWnd
  AddBytes(code,hWnd,4);
  AddBytes(code,0xB8);  // mov eax, PostMsgAddr
  AddBytes(code,PostMsgAddr,4);
  AddBytes(code,0xFF);  // call eax
  AddBytes(code,0xD0);  
  AddBytes(code,0x33);  // xor eax, eax
  AddBytes(code,0xC0);
  AddBytes(code,0x5D);  // pop ebp
  AddBytes(code,0xC3);  // ret
  
  return (SetTimer(hWnd, TimerId, interval, code) );
}
//---------------------------------------------------

bool KillMyTimer()
{
  return( KillTimer(WindowHandle(Symbol(),Period()), TimerId) );
}
//+------------------------------------------------------------------+

int AddBytes(int& code[], int value, int bytescount=1)
{
  static int pos=0;  // текущая позиция (в байтах)
  if (ArraySize(code)==0) pos=0;
  for (int i=0; i<bytescount; i++, pos++)
  {
    int cell=pos/4;
    if (cell>=ArraySize(code)) ArrayResize(code,cell+1);
    int byte=pos%4;
    code[cell] &= ~(0xFF<<(byte*8));    // обнуляем место под байт
    code[cell] |= value&0xFF<<(byte*8); // записываем байт
    value>>=8;
  }  
  return(pos);
}

в логе один раз: 

11:02:31 test USDCHF,H1: loaded successfully
11:02:31 test USDCHF,H1: initialized
11:02:31 test USDCHF,H1: цикл завершен....

 

комментарий  остался: i = 0

чёт не так (((( 

 

Не фига себе кода сколько! Для индикатора достаточно одной строки.

#property indicator_chart_window
#include <WinUser32.mqh>
int nCounter = 0;
void start()
 {
  int hwndChart = WindowHandle(Symbol(), 0);
  SendMessageA(hwndChart, WM_COMMAND, 0x822C, NULL); // <-- Этой строки.
  nCounter++;
  Comment(nCounter);
 }
 
Zhunko:

Не фига себе кода сколько! Для индикатора достаточно одной строки.

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

Если же в вашем коде вместо обновления графика сделать именно эмуляцию тика, то это приведёт к зацикливанию (т.е. индикатор будет запускаться без остановки), а соответственно к подвисанию терминала.

 

IgorM, у меня Print выводится каждую секунду, как и положено. Не знаю почему у вас только один раз... Может из-за различий в операционках... У меня XP стоит.

А в журнале не выдаётся какая-нибудь ошибка? И проверьте, чему у вас равен PostMsgAddr, не нулю?

 
Meat:

IgorM, у меня Print выводится каждую секунду, как и положено. Не знаю почему у вас только один раз... Может из-за различий в операционках... У меня XP стоит.

А в журнале не выдаётся какая-нибудь ошибка? И проверьте, чему у вас равен PostMsgAddr, не нулю?


у меня вин7, в журнале нет ошибок, сейчас занят, может чуть позже перезагружу комп в хр - у меня 2 операционки
 

Да можете не напрягаться, я сам только что у себя запустил семёрку (Ultimate). Всё отлично на ней пашет. Так что не знаю, почему не работает у Вас... 

 
Meat:

Да можете не напрягаться, я сам только что у себя запустил семёрку (Ultimate). Всё отлично на ней пашет. Так что не знаю, почему не работает у Вас... 

в хр работает: принты каждую секунду, а вот в вин7 максимальная с обновлениями по сегодняшний день не работает, так что не знаю почему работает у Вас....
 
IgorM:
 ...так что не знаю почему работает у Вас....


Значит нам нужно услышать ещё чей-нибудь отзыв по поводу семёрки, чтоб окончательно определиться, кто прав, кто виноват :)

Кстати, а что насчёт PostMsgAddr, о котором я писал выше? Он не равен нулю?

 
Meat:Кстати, а что насчёт PostMsgAddr, о котором я писал выше? Он не равен нулю?

поставил после

int SetMyTimer(int interval)
{    
  int MT4InternalMsg= RegisterWindowMessageA("MetaTrader4_Internal_Message");
  int hWnd= WindowHandle(Symbol(),Period());
  int PostMsgAddr= GetProcAddress(GetModuleHandleA("user32.dll"),"PostMessageA");
  Print("PostMsgAddr =",PostMsgAddr);
  int code[];
в логе получил: 

23:39:01 test EURUSD,H1: loaded successfully
23:39:01 test EURUSD,H1: PostMsgAddr =1977398342
23:39:01 test EURUSD,H1: initialized
23:39:01 test EURUSD,H1: цикл завершен....
 

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