[해결됨] 다른 작업 시간 프레임의 표시기에서 호출/생성될 때 표시기가 올바르게 인스턴스화되지 않습니다.

 

업데이트: 아래 해결 방법을 참조하세요.

CopyBuffer()는 표시기 코드 내에서 다른 시간 프레임으로 표시기를 호출할 때 4806(표시기 데이터에 액세스할 수 없음) 오류를 발생시킵니다. 현재 작업 시간 프레임과 다른 시간 프레임에 유효한 표시기 핸들을 호출할 때 발생합니다. 버그는 초기화 및 첫 번째 틱 데이터 이전에 OnCalculate()에 대한 첫 번째 호출 중에만 나타납니다. 버그를 분리하기 위해 다음 방법이 적용되었습니다.

이것은 스크립트, EA 및 표시기에서 호출될 때 CopyBuffer()의 출력을 테스트하는 데 사용되는 코드 블록입니다.

#include <Indicators\Trend.mqh>


   CiMA ima;
   ima.Create( _Symbol , PERIOD_H1 , 20 , 0 , MODE_SMA , PRICE_CLOSE );
   ima.Refresh();
  
   CIndicatorBuffer *buff = ima.At( 0 );
   int total = buff.Total();
   Print ( __LINE__ ," ", __FUNCSIG__ ," ",buff.Name()," Buffer size = ",total);
   for ( int i= 0 ;i<total;i++){
       if (i> 2 ) break ;
       else {
         Print ( __LINE__ ," ", __FUNCSIG__ ," ",ima.PeriodDescription()," iMA (",i,") value = ", DoubleToString (ima.Main(i), _Digits ));  
      }
   }

전체 표시기 코드:

#property indicator_chart_window

#include <Indicators\Trend.mqh>
#include <errordescription.mqh>

CiMA ima;
int m_bufferSize = - 1 ;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit ()
  {
   static int iCnt = 0 ;
//--- indicator buffers mapping
       Print ( "-----------------------" , TimeCurrent (), "--------------------------" );
//---
   return ( INIT_SUCCEEDED );
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate ( const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[])
  {
//---
   if (rates_total != prev_calculated || m_bufferSize < 1 ){
       ResetLastError ();
      ima.Create( _Symbol , PERIOD_H1 , 20 , 0 , MODE_SMA , PRICE_CLOSE );
      ima.Refresh();
      
      CIndicatorBuffer *buff = ima.At( 0 );
      m_bufferSize = buff.Total();
       Print ( __LINE__ , " " , __FUNCSIG__ , " " ,buff.Name(), " Buffer size = " ,m_bufferSize);
       if (m_bufferSize < 1 ){
         Print (ErrorDescription( GetLastError ()));
      } else {
         for ( int i= 0 ;i<m_bufferSize;i++){
             if (i> 2 ) break ;
             else {
               Print ( __LINE__ , " " , __FUNCTION__ , " " ,ima.PeriodDescription(), " iMA(" ,i, ") value = " , DoubleToString (ima.Main(i), _Digits ));  
            }
         }
      }
   }
//--- return value of prev_calculated for next call
   return (rates_total);
}
//+------------------------------------------------------------------+

오류를 받지 않는 유일한 방법은 H1 차트(동일한 TF)에서 시작하는 것입니다.

동일한 문제가 있는 포럼 게시물 링크:

https://www.mql5.com/en/forum/73274

https://www.mql5.com/en/forum/13676

https://www.mql5.com/en/forum/30958

https://www.mql5.com/en/forum/16614

해결 방법:

해결 방법은 OnInit()에서 표시기를 만들고 EventSetMillisecondTimer를 1ms로 설정하는 것입니다. 이를 통해 OnCalculate()가 첫 번째 패스 후에 반환되고 두 번째 패스를 위해 OnTimer를 빠르게 호출할 수 있습니다. 이 문제를 해결하기 위해 OnTimer 이벤트에 대한 한 번의 호출만 필요했으며 계산에 더 이상의 시간 지연이 필요하지 않았습니다.

//+------------------------------------------------------------------+
//|                                                    THROWAWAY.mq5 |
//|                                                      nicholishen |
//|                                   www.reddit.com/u/nicholishenFX |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link        " www.reddit.com/u/nicholishenFX "
#property version    "1.00"
#property indicator_chart_window

#include <Indicators\Trend.mqh>
#include <errordescription.mqh>

CiMA ima;
int m_bufferSize = - 1 ;
bool timedEvent = false ;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit ()
  {
       int waitMS = 1 ;
       Print ( "-----------------------" , TimeCurrent (), "--------------------------" );
  
      ima.Create( _Symbol , PERIOD_H1 , 20 , 0 , MODE_SMA , PRICE_CLOSE );
       EventSetMillisecondTimer (waitMS);
       Print ( "OnTimer set to " ,waitMS, " ms" );
      
//---
   return ( INIT_SUCCEEDED );
  }

void OnTimer ()
  {
//---
   ima.Refresh();
   EventKillTimer ();
   timedEvent = true ;
  
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate ( const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[])
  {

   static int tickCnt = 0 ;
   tickCnt++;
  
   if (!timedEvent) return rates_total;
//---
   if (rates_total != prev_calculated || m_bufferSize < 1 ){
       ResetLastError ();
      CIndicatorBuffer *buff = ima.At( 0 );
      m_bufferSize = buff.Total();
       if (m_bufferSize <= 0 ) ima.Refresh();
       // try wait with looping  
      
       if (m_bufferSize < 1 ){
         Print (ErrorDescription( GetLastError ()));
        
      } else {
         for ( int i= 0 ;i<m_bufferSize;i++){
             if (i> 2 ) break ;
             else {
               Print ( __LINE__ , " " , __FUNCTION__ ,buff.Name(),
                     " Buffer size = " ,m_bufferSize,
                     " | " ,ima.PeriodDescription(), " iMA(" ,i, ") value = " ,
                     DoubleToString (ima.Main(i), _Digits ),
                     " | Tick-count = " ,tickCnt
                     );  
            }
         }
      }
   }
//--- return value of prev_calculated for next call
   return (rates_total);
}
//+------------------------------------------------------------------+
Error 4806 while copying buffers
Error 4806 while copying buffers
  • www.mql5.com
com/en/articles/100, but tried to change it to use the CCI indicator only.
 
누구든지 해결 방법을 알고 있습니까?
 

이것은 MT5의 버그가 아니라 코드의 버그입니다. 그건 그렇고, 당신이보고 한 모든 주제처럼. 당신이 생각하거나 그렇게 되기를 바라는 것이 아니라 설계된 대로 플랫폼/언어를 다루어야 합니다.

OnCalculate()에서 ima.Create()를 사용하는 이유는 무엇입니까? 핸들을 얻었지만 데이터를 아직 사용할 수 없으며 오류가 발생하면 코드가 다시 호출되지 않습니다.

추신: "데이터 액세스 없이 작동"의 의미는 무엇입니까? ?
 
Alain Verleyen :

이것은 MT5의 버그가 아니라 코드의 버그입니다. 그건 그렇고, 당신이보고 한 모든 주제처럼. 당신이 생각하거나 그렇게 되기를 바라는 것이 아니라 설계된 대로 플랫폼/언어를 다루어야 합니다.

OnCalculate()에서 ima.Create()를 사용하는 이유는 무엇입니까? 핸들을 얻었지만 데이터를 아직 사용할 수 없으며 오류가 발생하면 코드가 다시 호출되지 않습니다.

추신: "데이터 액세스 없이 작동"의 의미는 무엇입니까? ?
나는 이것이 실제로 플랫폼의 버그라고 생각합니다. 나는 Expert의 OnInit()에서 동일한 정확한 코드 블록을 실행하고 있으며 오류가 발생하지 않는 반면 표시기의 OnInit()에서 오류가 발생합니다. 데이터 액세스 없이 작동한다는 것은 오프라인, 테스터 시간 또는 시장 시간 외에서 작동함을 의미합니다. 어떤 지표에 대한 호출은 언제 어디서나 그것을 인스턴스화해야 하며, 그런 점에서 플랫폼이 일관성이 없다는 사실은 이것이 기능이 아니라 버그라는 것을 의미합니다. oncalculate 내부에 ima.Create가 있는 것은 첫 번째 틱(데이터 업데이트) 이전에 표시기 내에서 호출한 모든 다른 시간 프레임에서 표시기를 인스턴스화하지 못하기 때문에 예시일 뿐입니다. 무한대로 새로 고칠 수 있지만 oncalculate가 정확히 한 번 실행되고 반환될 때까지 표시기 데이터에 액세스하지 않습니다. 새 틱이 들어올 때 후속 패스에서 작동합니다. 버그입니다.

다시 한 번 강조하고 싶습니다. 전문가와 스크립트의 어느 곳에서나 올바르게 작동하지만 지표에서 어떻게 든 깨진 것입니다.

 
nicholishen :
나는 이것이 실제로 플랫폼의 버그라고 생각합니다. 나는 Expert의 OnInit()에서 동일한 정확한 코드 블록을 실행하고 있으며 오류가 발생하지 않는 반면 표시기의 OnInit()에서 오류가 발생합니다. 데이터 액세스 없이 작동한다는 것은 오프라인, 테스터 시간 또는 시장 시간 외에서 작동함을 의미합니다. 어떤 지표에 대한 호출은 언제 어디서나 그것을 인스턴스화 해야 하며, 그런 점에서 플랫폼이 일관성 이 없다는 사실은 이것이 기능이 아니라 버그라는 것을 의미합니다. oncalculate 내부에 ima.Create가 있는 것은 첫 번째 틱(데이터 업데이트) 이전에 표시기 내에서 호출한 모든 다른 시간 프레임에서 표시기 를 인스턴스화하지 못하기 때문에 예시일 뿐입니다. 다시 한 번 강조하고 싶습니다. 전문가와 스크립트의 어느 곳에서나 올바르게 작동하지만 지표에서 어떻게 든 깨진 것입니다.

그래, 넌 날 믿지 않아, 그건 네 말이 맞지만 넌 틀렸어

ServiceDesk에 글을 쓰라고 제안할 수만 있으며 여기에서 답변을 보고해 주십시오.

 
Alain Verleyen :

그래, 넌 날 믿지 않아, 그건 네 말이 맞지만 넌 틀렸어

ServiceDesk에 글을 쓰라고 제안할 수만 있으며 여기에서 답변을 보고해 주십시오.

감사합니다. 내가 틀렸다면 왜 전문가와 스크립트에서는 정확히 작동하지만 지표에서는 작동하지 않습니까?
 
nicholishen :
감사합니다. 내가 틀렸다면 왜 전문가와 스크립트에서는 정확히 작동하지만 지표에서는 작동하지 않습니까?

심볼에 대한 모든 표시기가 동일한 스레드에서 실행되기 때문입니다. Strategy Tester , EA와 스크립트는 다른 상황입니다.

ServiceDesk 답변을 보겠습니다. 제가 틀렸을 수도 있어요 :-)

 
Alain Verleyen :

심볼에 대한 모든 표시기가 동일한 스레드에서 실행되기 때문입니다. Strategy Tester , EA와 스크립트는 다른 상황입니다.

ServiceDesk 답변을 보겠습니다. 제가 틀렸을 수도 있어요 :-)

이런 경우라고 생각하자...

  • 동일한 시간 프레임의 지표를 인스턴스화하고 해당 데이터에 즉시 액세스할 수 있지만 다른 시간 프레임은 사용할 수 없는 이유는 무엇입니까?
 
nicholishen :
  • 동일한 기간의 지표를 인스턴스화하고 해당 데이터에 즉시 액세스할 수 있는 이유는 무엇입니까?

이것은 실제로 데이터가 이미 사용 가능하다는 것이 운이 좋다는 것을 의미합니다. 이것은 보장되지 않습니다. 실패할 수도 있습니다.
 
Stanislav Korotky :
이것은 실제로 데이터가 이미 사용 가능하다는 것이 운이 좋다는 것을 의미합니다. 이것은 보장되지 않습니다. 실패할 수도 있습니다.

즉, 데이터가 스크립트나 EA에서 즉시 사용 가능하면 Indicator에서도 동일하게 사용할 수 있습니다(여기에서는 데이터 가용성 문제가 아님). 표시기는 OnCalculate()의 두 번째 패스(첫 번째 틱이라고도 함) 전에 단순히 인스턴스화에 실패합니다.

진단할 때 이 점을 고려했습니다. 만일의 경우를 대비하여 루프와 대기 기간을 통합했습니다. 나 이전의 모든 사람들과 마찬가지로 나도 이 특정 버그에 대해 동일한 문제를 겪고 있습니다.

 
nicholishen :

즉, 데이터가 스크립트나 EA에서 즉시 사용 가능하면 Indicator에서도 동일하게 사용할 수 있습니다(여기에서는 데이터 가용성 문제가 아님). 표시기는 OnCalculate()의 두 번째 패스(첫 번째 틱이라고도 함) 전에 단순히 인스턴스화에 실패합니다 .

진단할 때 이 점을 고려했습니다. 만일의 경우를 대비하여 루프와 대기 기간을 통합했습니다. 나 이전의 모든 사람들과 마찬가지로 나도 이 특정 버그에 대해 동일한 문제를 겪고 있습니다.

"인스턴스화 실패"를 반복하고 있지만 정확하지 않습니다. 표시기는 모든 경우에 인스턴스화됩니다.

문제는 데이터를 동기적으로 사용할 수 없다는 것입니다. 처리해야 합니다. MT5 버그가 아니라 기능입니다.

나는 토론을 중단하고 SD 응답을 기다릴 것을 제안합니다.