라이브러리: 싱글 테스터 캐시

 

싱글 테스터 캐시:

테스터의 단일 패스 데이터.

싱글 테스터 캐시

Author: fxsaber

 

단일 실행 보고서에 있는 모든 통계를 사용할 수 있습니다. 이전에는 전문가 어드바이저의 소스를 가지고 있어도 이 중 많은 부분을 얻을 수 없었습니다.

  double            ghpr;                    // 기하 평균 트랜잭션 
  double            ghprpercent;             // 기하평균 트랜잭션(퍼센트) 
  double            ahpr;                    // 트랜잭션의 산술 평균 
  double            ahprpercent;             // 거래의 산술 평균(퍼센트) 
  double            zscore;                  // 시리즈 테스트 
  double            zscorepercent;           // 퍼센트 단위의 직렬 테스트 
  double            lrcorr;                  // 선형 회귀의 상관 계수 
  double            lrstderror;              // 선형 회귀에서 균형 편차의 표준 오차 
  double            corr_prf_mfe;            // MFE와 수익 간의 상관 관계 
  double            corr_prf_mae;            // MAE와 수익 간의 상관 관계 
  double            corr_mfe_mae;            // MAE와 MFE의 상관 관계 
  double            mfe_a;                   // mfe와 수익 간의 상관 관계, 선형 회귀 선의 계수 
  double            mfe_b;                   // mfe와 수익 간의 상관 관계, 선형 회귀 선의 계수 
  double            mae_a;                   // mae와 수익 간의 상관 관계, 선형 회귀 선의 계수 
  double            mae_b;                   // mae와 수익 간의 상관 관계, 선형 회귀 선의 계수 
  UINT              in_per_hours[24];        // 시간별 입력 분포 
  UINT              in_per_week_days[7];     // 요일별 입력 분포 
  UINT              in_per_months[12];       // 월별 입력 분포 
  double            out_per_hours[24][2];    // 시간별 입력 분포 
  double            out_per_week_days[7][2]; // 요일별 입력 분포 
  double            out_per_months[12][2];   // 월별 입력 분포 
  INT64             holding_time_min;        // 최소 위치 유지 시간 
  INT64             holding_time_max;        // 최대 위치 유지 시간 
  INT64             holding_time_avr;        // 평균 위치 유지 시간
 

현재 버전의 tst 형식에는 다음 데이터가 없습니다.

  • 시간(밀리초).
  • PositionID.
  • 매직넘버.
이로 인해 사용 시나리오에 제한이 있습니다.
 

몇 가지 버그 재현. 헤지 계좌에서 테스터에서 Expert Advisor를 실행합니다.

#include <MT4Orders.mqh> // https://www.mql5.com/ko/code/16006

#define  Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

#define  PAUSE 100000

void OnTick()
{
  static bool FirstRun = true;
  
  if (FirstRun)
  {
    OrderSend(_Symbol, OP_BUY, 1, Ask, 0, 0, 0);
    Sleep(PAUSE);
    
    OrderSend(_Symbol, OP_BUY, 2, Ask, 0, 0, 0);
    Sleep(PAUSE);

    if (OrderSelect(0, SELECT_BY_POS))
      OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0);
    Sleep(PAUSE * 2);
    
    if (OrderSelect(0, SELECT_BY_POS))
      OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0);
    Sleep(PAUSE * 2);

    TesterWithdrawal(100);    
    
    FirstRun = false;
  }
}

void OnDeinit( const int )
{
  const int Total = OrdersHistoryTotal();
  
  for (int i = 0; i < Total; i++)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
    {
      OrderPrint();
      
      Print(OrderTicketID()); // MT5-PositionID
    }
}


다음과 같은 결과를 얻습니다.

2020.01.08 23:59:58   #1 2020.01.01 00:00:00 balance 0.00 0.00000 0.00000 0.00000 2020.01.01 00:00:00 0.00000 0.00 0.00 100000.00 0
2020.01.08 23:59:58   0
2020.01.08 23:59:58   #4 2020.01.02 06:00:00 buy 1.00 EURUSD 1.12137 0.00000 0.00000 2020.01.02 06:03:20 1.12132 -3.56 0.00 -4.46 0
2020.01.08 23:59:58   2
2020.01.08 23:59:58   #5 2020.01.02 06:01:40 buy 2.00 EURUSD 1.12137 0.00000 0.00000 2020.01.02 06:06:40 1.12129 -7.14 0.00 -14.27 0
2020.01.08 23:59:58   3
2020.01.08 23:59:58   #6 2020.01.02 06:10:00 balance 0.00 0.00000 0.00000 0.00000 2020.01.02 06:10:00 0.00000 0.00 0.00 -100.00 withdrawal 0
2020.01.08 23:59:58   0


다음으로 스크립트로 해당 tst 파일을 읽습니다.

#include <fxsaber\SingleTesterCache\SingleTesterCache.mqh> // https://www.mql5.com/ko/code/27611
#include <fxsaber\MultiTester\MTTester.mqh> // https://www.mql5.com/ko/code/26132

void OnStart()
{  
  uchar Bytes2[];
  
  if (MTTESTER::GetLastTstCache(Bytes2) != -1) // 단일 실행의 마지막 캐시 레코드를 읽을 수 있는 경우
  {
    const SINGLETESTERCACHE SingleTesterCache(Bytes2); // 해당 객체에 드라이브합니다.

    for (int i = 0; i < ArraySize(SingleTesterCache.Positions); i++)
      Print(SingleTesterCache.Positions[i].ToString());
  }
}


포지션에 대한 데이터가 표시됩니다.

id = 0
mfe = 0.0
mae = -8.029999999999999
profit = -4.46
lifetime = 00:03:20

id = 0
mfe = 0.0
mae = -21.4
profit = -14.27
lifetime = 00:05:00

id = 0
mfe = 0.0
mae = 0.0
profit = 0.0
lifetime = 00:00:00


이 게시물의 모든 내용을 비교하면 다음과 같은 버그가 공식화됩니다.

  • 올바른 ID가 아닌 Null ID.
  • 수익 계산 시 커미션과 스왑이 고려되지 않음.
  • 출금 거래가 청산된 거래 포지션 수에 잘못 포함됩니다.
 

이제 저는 설정 파일 대신 tst 파일을 사용합니다. 입력 매개변수뿐만 아니라 전체 백테스트를 통해 매우 빠르게 전환할 수 있습니다.

tst에 밀리초 데이터가 부족하기 때문에 서로 다른 TS를 포트폴리오로 결합하는 것이 불가능하다는 것은 유감입니다.


개발자들이 기존 필드를 최대한 활용하기 시작했으면 좋겠습니다.

INT64             TradeDeal::time_create;             // 레코드 생성 시간

INT64             TradeOrder::time_setup;             // 클라이언트에서 시스템으로 주문이 접수된 시간
INT64             TradeOrder::time_done;              // 주문 취소 시간

시간 값을 초 단위가 아닌 밀리초 단위로 작성하면 됩니다.


일반적으로 tst 사용의 모든 멋짐을 보여주기 위해 실제로는 tst의 아주 작은 결함을 제공하지 않습니다. 수정해야 합니다.

 
단일 패스 잔액 그래프를 더 자세히 살펴볼 필요가 있는 경우가 많습니다.
// 인터랙티브 싱글 패스 잔액 그래프.

#include <fxsaber\SingleTesterCache\SingleTesterCache.mqh> // https://www.mql5.com/ko/code/27611
#include <fxsaber\MultiTester\MTTester.mqh> // https://www.mql5.com/ko/code/26132

#include <..\Files\Graph.mqh> // https://www.mql5.com/ko/code/18801

#import "shell32.dll"
  int ShellExecuteW( int, string, string, string, string, int );
#import

#define  BASEPATH (::TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\")

bool CreateBalanceData( const SINGLETESTERCACHE &SingleTesterCache, const string FileName = "exdat.txt" )
{
  const int handle = FileOpen(FileName, FILE_WRITE | FILE_TXT | FILE_ANSI);
  const bool Res = (handle != INVALID_HANDLE);

  if (Res)
  {
    FileWriteString(handle, "var dat1=[\n");
    
    for (uint i = 0; i < SingleTesterCache.Header.equities_total; i++)
      FileWriteString(handle, "[" + (string)((long)SingleTesterCache.TradeState[i].time * 1000) + "," + ::DoubleToString(SingleTesterCache.TradeState[i].balance, 2) + "],\n");

    FileWriteString(handle, "];\n");
    FileWriteString(handle, "var T1=dat1[0][0];\n");
    FileWriteString(handle, "var T2=dat1[dat1.length-1][0];\n");
    FileWriteString(handle, "var nTrades=dat1.length;\n");
    FileWriteString(handle, "var Balance=" + ::DoubleToString(SingleTesterCache.TradeState[SingleTesterCache.Header.equities_total - 1].balance, 2) + ";\n");
    FileWriteString(handle, "var Currency=\"" + (SingleTesterCache.Header.trade_pips ? "Pips" : SingleTesterCache.Header.trade_currency[]) + "\";\n");

    FileClose(handle);
  }

  return(Res);
}

void OnStart()
{  
  uchar Bytes2[];
  
  if (MTTESTER::GetLastTstCache(Bytes2) != -1) // 단일 실행의 마지막 캐시 레코드를 읽을 수 있는 경우
  {
    const SINGLETESTERCACHE SingleTesterCache(Bytes2); // 해당 객체에 드라이브합니다.
    
    const string FileName = "Report.htm";
    uchar Array[];    
    
    if ((StringToCharArray(StrMQH, Array) > 0) && FileSave(FileName, Array) && CreateBalanceData(SingleTesterCache))
      ShellExecuteW(0, "Open", BASEPATH + FileName, NULL, NULL, 3);      
  }
}


만약

TradeState[i].balance -> TradeState[i].equity

로 바꾸면 주식 그래프가 됩니다.

 

거래/주문량이 tst에 잘못 기록되어 있습니다. 항상 SYMBOL_TRADE_CONTRACT_SIZE = 100,000인 것처럼 계산됩니다.

다른 값을 설정하면 tst의 거래량 값에 영향을 미치지 않습니다.

 
fxsaber:

실제로 TST를 사용하는 모든 멋짐을 보여주기 위해 실제로 TST의 작은 단점에 의해 전혀 주어지지 않습니다. 수정해야 합니다.

첫 번째 삼키기.

TesterPortfolio - портфель ТС
TesterPortfolio - портфель ТС
  • www.mql5.com
Возьмем третий пункт. Допустим, взяли несколько приглянувшихся советников из Маркета. Настроили их для каждого символа. TesterPortfolio запустит все варианты одновременно, показав общую торговую статистику (просадка эквити на реальных тиках и т.д.). Чаще всего использую для оценки диверсификации различных настроек своих ТС. Использование. На...
 
TesterPortfolio - портфель ТС
TesterPortfolio - портфель ТС
  • 2020.01.16
  • www.mql5.com
В приложении советник/робот, который объединяет несколько независимых одиночных проходов MT5-Тестера в один. Сценарии использования. Чужой советник с закрытым исходным кодом не запускается в MT5-Визуализаторе. TesterPortfolio сможет немного помочь. Сбор статистики прямо во время торговли советников с закрытым исходным кодом. Например...
 

거기서 답변했습니다.

 

블로그에서 어떻게 발생하는지 모르겠는데 새 댓글(답글이 아닌 경우)이 표시되면 어떤 식으로든 신호가 전달되나요? 아니면 새 댓글이 표시되는 포럼 스레드 중 하나에 게시하는 것이 더 낫나요?

왜 KB에 게시하지 않았나요? 더 편리 할 것입니다.

그렇다면 신속한 답변을 받으려면 어디에 게시해야 하나요?