English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
트레이드미네이터 3: 라이즈 오브 더 트레이딩 머신

트레이드미네이터 3: 라이즈 오브 더 트레이딩 머신

MetaTrader 5트레이딩 | 5 8월 2021, 09:44
56 0
Roman Zamozhnyy
Roman Zamozhnyy

프롤로그

옛날 옛적 멀고도 멀리 떨어진 한 포럼(MQL5)에 두 개의 글이 게시되었습니다. 바로 주(joo)가 쓴 '유전 알고리즘-쉬워요!'와 제가 쓴 '닥터 트레이드러브...'인데요. 첫 번째 글에서는 매매 전략을 포함해 무엇이든 최적화할 수 있는 강력한 도구를 함께 만들었습니다. 바로 MQL5로 구현된 유전 알고리즘이죠.

해당 알고리즘을 이용해 두 번째 글에서는 자기 최적화 기능을 갖춘 엑스퍼트 어드바이저를 만들어 보려고 했는데요. 한 가지 매매 시스템에 대한 매개 변수 최적화는 물론이고, 여러 매매 시스템 가운데 가장 좋은 시스템까지 선택해 주는 엑스퍼트 어드바이저를 만들기로 하면서 끝났었죠. 이게 과연 가능한 일인지, 또 가능하다면 어떻게 가능한지 한번 알아보죠.

트레이딩 로봇 이야기

엑스퍼트 어드바이저가 자기 최적화 기능을 갖추려면 다음의 조건을 충족해야 합니다.

거래 기록을 기반으로 다음의 기능을 수행할 수 있어야 하는데요.

  • 최적의 전략 선택
  • 최적의 금융 상품 선택
  • 레버리지 조정 거래용 예수금 설정
  • 선택된 전략에 필요한 인디케이터에 최적의 매개 변수 설정

또한, 실제 적용을 위해 다음의 기능도 필요하죠.

  • 포지션 오픈 및 청산
  • 포지션 크기 선택
  • 새 최적화 필요성 판단

아래는 다이어그램으로 나타낸 해당 엑스퍼트 어드바이저입니다.


보다 세밀한 도식은 첨부된 Scheme_en 파일에서 찾아볼 수 있습니다.

지금까지 알려진 엑스퍼트 어드바이저의 한계는 다음과 같으니 반드시 숙지하세요. 나는 다음 사항을 인지합니다(중요):

  1. 엑스퍼트 어드바이저는 새로운 바가 나타날 때마다 매매를 결정합니다(선택된 타임 프레임 내).
  2. 또한 T/P, S/L 및 TSL이 설정되지 않은 신호에만 포지션을 청산합니다.
  3. 새로운 최적화는 잔고 드로우다운이 초기화 시 미리 정해진 값보다 높아질 때 일어납니다. 이건 제가 개인적으로 정한 조건이고 여러분도 여러분만의 조건을 정할 수 있습니다.
  4. 피트니스 함수는 매매 기록을 기준으로 거래를 실행해 보며 거래가 실행되는 잔고를 최대화시킵니다. 잔고의 상대적 드로우다운이 미리 정해진 값보다 낮아질 때 말이죠. 이 또한 제가 선택한 피트니스 함수이며 여러분도 여러분만의 피트니스 함수를 선택할 수 있습니다.
  5. 세 가지 일반 변수(전략, 상품 및 예수금)를 제외한 나머지 인디케이터 버퍼 최적화 변수의 개수를 다섯 개로 제한합니다. 내장된 기술적 지표의 최대 인디케이터 버퍼 개수와도 논리적으로 맞습니다. 다수의 인디케이터 버퍼를 갖는 커스텀 인디케이터를 갖는 전략을 이용하는 경우, main.mq5 파일에서 OptParamCount 변수 값을 원하는 개수로 설정해 줍니다.

필요한 세부 사항을 모두 정했으니 이제 구현 코드를 한번 볼까요?

우선 함수부터 살펴보겠습니다.

void OnTick()
{
  if(isNewBars()==true)
  {
    trig=false;
    switch(strat)
    {
      case  0: {trig=NeedCloseMA()   ; break;};                      //The number of case strings must be equal to the number of strategies
      case  1: {trig=NeedCloseSAR()  ; break;};
      case  2: {trig=NeedCloseStoch(); break;};
      default: {trig=NeedCloseMA()   ; break;};
    }
    if(trig==true)
    {
      if(GetRelDD()>maxDD)                                           //If a balance drawdown is above the max allowed value:
      {
        GA();                                                        //Call the genetic optimization function
        GetTrainResults();                                           //Get the optimized parameters
        maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);               //Now count the drawdown not from the balance maximum...
                                                                     //...but from the current balance
      }
    }
    switch(strat)
    {
      case  0: {trig=NeedOpenMA()   ; break;};                       //The number of case strings must be equal to the number of strategies
      case  1: {trig=NeedOpenSAR()  ; break;};
      case  2: {trig=NeedOpenStoch(); break;};
      default: {trig=NeedOpenMA()   ; break;};
    }
    Print(TimeToString(TimeCurrent()),";","Main:OnTick:isNewBars(true)",
          ";","strat=",strat);
  }
}

뭐가 있을까 궁금하군요. 우선 다이어그램에서 보이듯, 새 틱이 발생할 때마다 새로운 바가 형성되는지 확인합니다. 새로운 바가 형성될 경우 포지션 확인 및 청산을 위해 필요한 특정 함수를 호출합니다. 예를 들어 최적의 돌파구 전략이 SAR이라고 가정한다면, NeedCloseSAR 함수를 호출하는 거죠.

bool NeedCloseSAR()
{
  CopyBuffer(SAR,0,0,count,SARBuffer);
  CopyOpen(s,tf,0,count,o);
  Print(TimeToString(TimeCurrent()),";","StrategySAR:NeedCloseSAR",
        ";","SAR[0]=",SARBuffer[0],";","SAR[1]=",SARBuffer[1],";","Open[0]=",o[0],";","Open[1]=",o[1]);
  if((SARBuffer[0]>o[0]&&SARBuffer[1]<o[1])||
     (SARBuffer[0]<o[0]&&SARBuffer[1]>o[1]))
  {
    if(PositionsTotal()>0)
    {
      ClosePosition();
      return(true);
    }
  }
  return(false);
}

모든 포지션 청산 함수는 불리언 자료형이어야 하며 포지션 청산 시 참값을 반환해야 합니다. 참값이 반환되면 다음 코드 블록인 OnTick() 함수가 재최적화의 필요성을 결정합니다.

    if(trig==true)
    {
      if(GetRelDD()>maxDD)                                           //If the balance drawdown is above the max allowed one:
      {
        GA();                                                        //Call the genetic optimization function
        GetTrainResults();                                           //Get optimized parameters
        maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);                   //Now count the drawdown not from the balance maximum...
                                                                     //...but from the current balance
      }
    }

현재 잔고 드로우다운 값과 최대 잔고드로우다운 값을 비교해 보세요. 현재 값이 최대 값보다 큰 경우, 최적화(GA())를 실행합니다. GA() 함수는 엑스퍼트 어드바이저의 피트니스 함수를 호출합니다. 여기서는 GAModule.mqh 모듈 피트니수 함수이죠.

void FitnessFunction(int chromos)                                    //A fitness function for the genetic optimizer:...
                                                                     //...selects a strategy, symbol, deposit share,...
                                                                     //...parameters of indicator buffers;...
                                                                     //...you can optimize whatever you need, but...
                                                                     //...watch carefully the number of genes
{
  double ff=0.0;                                                     //The fitness function
  strat=(int)MathRound(Colony[GeneCount-2][chromos]*StratCount);     //GA selects a strategy
 //For EA testing mode use the following code...
  z=(int)MathRound(Colony[GeneCount-1][chromos]*3);                  //GA selects a symbol
  switch(z)
  {
    case  0: {s="EURUSD"; break;};
    case  1: {s="GBPUSD"; break;};
    case  2: {s="USDCHF"; break;};
    case  3: {s="USDJPY"; break;};
    default: {s="EURUSD"; break;};
  }
//..for real mode, comment the previous code and uncomment the following one (symbols are selected in the MarketWatch window)
/*
  z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1));//GA selects a symbol
  s=SymbolName(z,true);
*/
  optF=Colony[GeneCount][chromos];                                   //GA selects a deposit share
  switch(strat)
  {
    case  0: {ff=FFMA(   Colony[1][chromos],                         //The number of case strings must be equal to the number of strategies
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
    case  1: {ff=FFSAR(  Colony[1][chromos],
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
    case  2: {ff=FFStoch(Colony[1][chromos],
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
    default: {ff=FFMA(   Colony[1][chromos],
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
  }
  AmountStartsFF++;
  Colony[0][chromos]=ff;
  Print(TimeToString(TimeCurrent()),";","GAModule:FitnessFunction",
        ";","strat=",strat,";","s=",s,";","optF=",optF,
        ";",Colony[1][chromos],";",Colony[2][chromos],";",Colony[3][chromos],";",Colony[4][chromos],";",Colony[5][chromos]);
}

현재 적용 전략에 따라 필요한 피트니스 함수 연산 모듈이 호출됩니다. 예를 들어, GA가 스토캐스틱을 선택한 경우, FFStoch() 함수가 호출되며 인디케이터 버퍼 최적화 매개 변수가 전송됩니다.

double FFStoch(double par1,double par2,double par3,double par4,double par5)
{
  int    b;
  bool   FFtrig=false;                                               //Is there an open position?
  string dir="";                                                     //Direction of the open position
  double OpenPrice;                                                  //Position Open price
  double t=cap;                                                      //Current balance
  double maxt=t;                                                     //Maximum balance
  double aDD=0.0;                                                    //Absolute drawdown
  double rDD=0.000001;                                               //Relative drawdown
  Stoch=iStochastic(s,tf,(int)MathRound(par1*MaxStochPeriod)+1,
                         (int)MathRound(par2*MaxStochPeriod)+1,
                         (int)MathRound(par3*MaxStochPeriod)+1,MODE_SMA,STO_CLOSECLOSE);
  StochTopLimit   =par4*100.0;
  StochBottomLimit=par5*100.0;
  dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS));
  leverage=AccountInfoInteger(ACCOUNT_LEVERAGE);
  contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE);
  b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth);
  for(from=b;from>=1;from--)                                         //Where to start copying of history
  {
    CopyBuffer(Stoch,0,from,count,StochBufferMain);
    CopyBuffer(Stoch,1,from,count,StochBufferSignal);
    if((StochBufferMain[0]>StochBufferSignal[0]&&StochBufferMain[1]<StochBufferSignal[1])||
       (StochBufferMain[0]<StochBufferSignal[0]&&StochBufferMain[1]>StochBufferSignal[1]))
    {
      if(FFtrig==true)
      {
        if(dir=="BUY")
        {
          CopyOpen(s,tf,from,count,o);
          if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0;
          if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
          if((maxt>0)&&(aDD/maxt>rDD)) rDD=aDD/maxt;
        }
        if(dir=="SELL")
        {
          CopyOpen(s,tf,from,count,o);
          if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0;
          if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
          if((maxt>0)&&(aDD/maxt>rDD)) rDD=aDD/maxt;
        }
        FFtrig=false;
      }
   }
    if(StochBufferMain[0]>StochBufferSignal[0]&&StochBufferMain[1]<StochBufferSignal[1]&&StochBufferMain[1]>StochTopLimit)
    {
      CopyOpen(s,tf,from,count,o);
      OpenPrice=o[1];
      dir="SELL";
      FFtrig=true;
    }
    if(StochBufferMain[0]<StochBufferSignal[0]&&StochBufferMain[1]>StochBufferSignal[1]&&StochBufferMain[1]<StochBottomLimit)
    {
      CopyOpen(s,tf,from,count,o);
      OpenPrice=o[1];
      dir="BUY";
      FFtrig=true;
    }
  }
  Print(TimeToString(TimeCurrent()),";","StrategyStoch:FFStoch",
        ";","K=",(int)MathRound(par1*MaxStochPeriod)+1,";","D=",(int)MathRound(par2*MaxStochPeriod)+1,
        ";","Slow=",(int)MathRound(par3*MaxStochPeriod)+1,";","TopLimit=",StochTopLimit,";","BottomLimit=",StochBottomLimit,
        ";","rDD=",rDD,";","Cap=",t);
  if(rDD<=trainDD) return(t); else return(0.0);
}

스토캐스틱의 피트니스 함수는 잔고 값을 메인 함수로 반환하여 유전 알고리즘으로 전송합니다. GA가 최적화 종료를 결정하게 되면 GetTrainResults() 함수를 이용해 해당 전략(예: 이동 평균)의 현재 최고값, 심볼, 예수금 및 기본 프로그램 인디케이터 버퍼 매개 변수를 반환하고 실제 거래에 적용 가능한 인디케이터를 작성합니다.

void GetTrainResults()                                               //Get the best parameters
{
  strat=(int)MathRound(Chromosome[GeneCount-2]*StratCount);          //Remember the best strategy
//For EA testing mode use the following code...
  z=(int)MathRound(Chromosome[GeneCount-1]*3);                       //Remember the best symbol
  switch(z)
  {
    case  0: {s="EURUSD"; break;};
    case  1: {s="GBPUSD"; break;};
    case  2: {s="USDCHF"; break;};
    case  3: {s="USDJPY"; break;};
    default: {s="EURUSD"; break;};
  }
//...for real mode, comment the previous code and uncomment the following one (symbols are selected in the MarketWatch window)
/*
  z=(int)MathRound(Chromosome[GeneCount-1]*(SymbolsTotal(true)-1));  //Remember the best symbol
  s=SymbolName(z,true);
*/
  optF=Chromosome[GeneCount];                                        //Remember the best deposit share
  switch(strat)
  {
    case  0: {GTRMA(   Chromosome[1],                                //The number of case strings must be equal to the number of strategies
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
    case  1: {GTRSAR(  Chromosome[1],
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
    case  2: {GTRStoch(Chromosome[1],
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
    default: {GTRMA(   Chromosome[1],
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
  }
  Print(TimeToString(TimeCurrent()),";","GAModule:GetTrainResults",
        ";","strat=",strat,";","s=",s,";","optF=",optF,
        ";",Chromosome[1],";",Chromosome[2],";",Chromosome[3],";",Chromosome[4],";",Chromosome[5]);
}

void GTRMA(double par1,double par2,double par3,double par4,double par5)
{
  MAshort=iMA(s,tf,(int)MathRound(par1*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
  MAlong =iMA(s,tf,(int)MathRound(par2*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
  CopyBuffer(MAshort,0,from,count,ShortBuffer);
  CopyBuffer(MAlong, 0,from,count,LongBuffer );
  Print(TimeToString(TimeCurrent()),";","StrategyMA:GTRMA",
        ";","MAL=",(int)MathRound(par2*MaxMAPeriod)+1,";","MAS=",(int)MathRound(par1*MaxMAPeriod)+1);
}

마지막으로 OnTick() 함수를 이용해 실제 거래에 최적화된 전략이 제대로 작동하는지 확인합니다.

bool NeedOpenMA()
{
  CopyBuffer(MAshort,0,0,count,ShortBuffer);
  CopyBuffer(MAlong, 0,0,count,LongBuffer );
  Print(TimeToString(TimeCurrent()),";","StrategyMA:NeedOpenMA",
        ";","LB[0]=",LongBuffer[0],";","LB[1]=",LongBuffer[1],";","SB[0]=",ShortBuffer[0],";","SB[1]=",ShortBuffer[1]);
  if(LongBuffer[0]>LongBuffer[1]&&ShortBuffer[0]>LongBuffer[0]&&ShortBuffer[1]<LongBuffer[1])
  {
    request.type=ORDER_TYPE_SELL;
    OpenPosition();
    return(false);
  }
  if(LongBuffer[0]<LongBuffer[1]&&ShortBuffer[0]<LongBuffer[0]&&ShortBuffer[1]>LongBuffer[1])
  {
    request.type=ORDER_TYPE_BUY;
    OpenPosition();
    return(false);
  }
  return(true);
}

끝입니다.

이제 직접 사용해 볼까요? 다음은 4가지 통화쌍 EURUSD, GBPUSD, USDHF, USDJPY에 대한 1시간 타임 프레임의 2011년 보고서입니다.

전략 테스터 보고서
InstaForex 서버(빌드 567)
설정
엑스퍼트: 메인
통화쌍: EURUSD
기간: H1(2011.01.01-2011.12.31)
입력 변수: trainDD=0.50000000
maxDD=0.20000000
브로커: InstaForex Companies Group
통화: USD
투자 원금: 10000.00
레버리지: 1:100
결과
기록 퀄리티: 100%
바: 6197 틱: 1321631
총 순이익: -538.74 총이익: 3535.51 총손실: -4 074.25
손익비: 0.87 예상 수익: -89.79 마진 레벨: 85.71%
회수율: -0.08 샤프지수: 0.07 OnTester 결과 0
잔고 드로우다운:
절대적 드로우다운: 4074.25 최대 드로우다운: 4074.25(40.74%) 상대적 드로우다운: 40.74%(4074.25)
Equity Drawdown:
절대적 드로우다운: 4889.56 최대 드로우다운: 6690.90(50.53%) 상대적 드로우다운: 50.53%(6690.90)
총 거래수: 6 숏(수익%): 6(16.67%) 롱(수익%): 0(0.00%)
총 거래수: 12 수익(전체 대비 %): 1(16.67%) 손실(전체 대비 %): 5(83.33%)
최대 수익: 3535.51 최대 손실: -1325.40
평균 수익: 3535.51 평균 손실: -814.85
최대 연속 수익 거래일: 1(3535.51) 최대 연속 손실 거래일: 5(-4074.25)
최대 연속 수익(일수): 3535.51(1) 최대 연속 손실(일수): -4074.25(5)
평균 연속 수익 거래일: 1 평균 연속 손실 거래일: 5


 

주문
개장 시간 주문 통화쌍 형식 거래량 가격 S/L T/P 시간 상태 코멘트
2011.01.03 01:002USDCHF매도28.21/28.210.93212011.01.03 01:00완료
2011.01.03 03:003USDCHF매수28.21/28.210.93652011.01.03 03:00완료
2011.01.03 06:004USDCHF매도24.47/24.470.93522011.01.03 06:00완료
2011.01.03 09:005USDCHF매수24.47/24.470.93722011.01.03 09:00완료
2011.01.03 13:006USDCHF매도22.99/22.990.93522011.01.03 13:00완료
2011.01.03 16:007USDCHF매수22.99/22.990.93752011.01.03 16:00완료
2011.01.03 18:008USDJPY매도72.09/72.0981.572011.01.03 18:00완료
2011.01.03 21:009USDJPY매수72.09/72.0981.662011.01.03 21:00완료
2011.01.04 01:0010USDJPY매도64.54/64.5481.672011.01.04 01:00완료
2011.01.04 02:0011USDJPY매수64.54/64.5481.782011.01.04 02:00완료
2011.10.20 21:0012USDCHF매도56.30/56.300.89642011.10.20 21:00완료
2011.10.21 12:0013USDCHF매수56.30/56.300.89082011.10.21 12:00완료
거래
시간 거래 통화쌍 형식 방향 거래량 가격 주문 수수료 스왑 이익(Profit) 잔고 코멘트
2011.01.01 00:001잔고0.000.0010000.0010000.00
2011.01.03 01:002USDCHF매도28.210.932120.000.000.0010000.00
2011.01.03 03:003USDCHF매수아웃28.210.936530.000.00-1325.408674.60
2011.01.03 06:004USDCHF매도24.470.935240.000.000.008674.60
2011.01.03 09:005USDCHF매수아웃24.470.937250.000.00-522.198152.41
2011.01.03 13:006USDCHF매도22.990.935260.000.000.008152.41
2011.01.03 16:007USDCHF매수아웃22.990.937570.000.00-564.027588.39
2011.01.03 18:008USDJPY매도72.0981.5780.000.000.007588.39
2011.01.03 21:009USDJPY매수아웃72.0981.6690.000.00-794.536793.86
2011.01.04 01:0010USDJPY매도64.5481.67100.000.000.006793.86
2011.01.04 02:0011USDJPY매수아웃64.5481.78110.000.00-868.115925.75
2011.10.20 21:0012USDCHF매도56.300.8964120.000.000.005925.75
2011.10.21 12:0013USDCHF매수아웃56.300.8908130.00-3.783539.299461.26
0.00 -3.78 -534.96 9461.26
(C) 2001-2011, MetaTrader사

차트에 표시된 구간에 대해 설명하겠습니다.

  1. 엑스퍼트 어드바이저 실행 후 유전 알고리즘이 예수금의 28%를 SAR 전략을 이용해 USDCHF 적용하기로 합니다. 1월 3일 저녁 이후 예수금의 20% 이상의 손실이 발생하였으므로 재최적화를 실행합니다.
  2. 이번에는 엑스퍼트 어드바이저가 예수금 전체(98%)를 SAR 전략으로 USDJPY에 투자하기로 합니다. 곧 손실이 발생하며 1월 4일 오전 세 번째 최적화가 실행됩니다.
  3. 한번 더 예수금 전체를 이동 평균의 골든 크로스와 데스 크로스를 기반으로 USDCHF에 투자하기로 합니다. 10월 20일 첫 번째 데드 크로스가 발생에 따라 최대 수익을 창출하며 손실을 모두 메웁니다. 그 후 2011년 말까지 엑스퍼트 어드바이저는 시장에 진입할 조건을 찾지 못합니다.

두고 볼까요?

두고 보기만 해도 될까요? 다음 세대 엑스퍼트 어드바이저는 어떤 모습일까요? 아마 여러 전략을 개발하고 그 중 최고의 전략을 고를 수 있지 않을까 싶습니다. 또 투자 원금도 관리해 주고, 하드웨어도 알아서 사고...

경고

해당 문서는 FX마진거래에 수반되는 전체 위험을 설명하고 있지 않습니다. FX거래 참여에 앞서 투자자 여러분의 신중한 판단이 필요합니다. 경험, 목적 및 자금 등을 고려해 여러분에게 맞는 투자인지 생각해 보십시오.

FX거래는 수익성이 높은 만큼 많은 위험을 내포합니다. 높은 레버리지와 가격변동성으로 단기간에 투자 원금의 전부 또는 상당 부분이 손실될 수 있습니다. 반드시 여유자금 범위 내에서만 투자하십시오. 투자를 결정한 경우 상품 구조 및 위험성을 완전히 이해한 후 시작하십시오. 필요한 경우 추가 조언을 구하십시오.

라이선스

UGAlib.mqh 모듈은 joo(Andrey Dik)가 개발했으며 BSD 라이선스 하에 배포됩니다.

본문에 첨부된 엑스퍼트 어드바이저 및 보조 모듈은 Roman Rich가 개발했으며 BSD 라이선스 하에 배포됩니다. 라이선스 문서는 Lic.txt 파일로 저장되어 있습니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/350

파일 첨부됨 |
lic.txt (1.43 KB)
sheme_en.gif (147.11 KB)
gamodule.mqh (7.24 KB)
main.mq5 (4.99 KB)
musthave.mqh (7.79 KB)
strategyma.mqh (5.13 KB)
strategysar.mqh (4.09 KB)
strategystoch.mqh (6.04 KB)
ugalib.mqh (33.36 KB)
MQL5 코드 보호하기: 보안 암호, 키 생성기, 시간 제한, 원격 라이선스 및 고급 EA 라이선스 키 암호 테크닉 MQL5 코드 보호하기: 보안 암호, 키 생성기, 시간 제한, 원격 라이선스 및 고급 EA 라이선스 키 암호 테크닉
개발자라면 자신이 개발한 프로그램이 안전하게 보호되길 바라죠. 이번 글에서는 MQL5 소프트웨어를 보호할 수 있는 몇 가지 방법을 알아보겠습니다. 어떻게 하면 MQL5 스크립트, 엑스퍼트 어드바이저 및 인디케이터에 대한 라이선스를 획득할 수 있는지 설명해 드릴게요. 보안 암호, 키 생성기, 계정 라이선스, 시간 제한 및 MQL5-RPC 원격 호출에 대해 다룰 겁니다.
엑스퍼트 어드바이저 비주얼 마법사로 엑스퍼트 어드바이저 만들기 엑스퍼트 어드바이저 비주얼 마법사로 엑스퍼트 어드바이저 만들기
MetaTrader 5 의 엑스퍼트 어드바이저 비주얼 마법사는 매우 직관적인 그래픽 환경과 다양한 매매 블록을 제공하여 단 몇 분만에 엑스퍼트 어드바이저를 만들 수 있도록 도와줍니다. 클릭, 드래그 앤드 드롭만 할 줄 알면 종이에 그리는 것처럼 외환 거래 전략을 시각화할 수 있습니다. 이렇게 만들어진 매매 다이어그램은 몰라니스(Molanis) MQL5 코드 생성기로 자동 분석되며 즉시 사용 가능한 엑스퍼트 어드바이저로 완성됩니다. 인터랙티브 그래픽 환경 덕분에 MQL5 코드를 쓰지 않고 간단하게 디자인할 수 있죠.
EX5 라이브러리로 프로젝트 홍보하기 EX5 라이브러리로 프로젝트 홍보하기
클래스 및 함수 구현 세부 사항을 .ex5 파일에 은닉함으로써 다른 개발자들과 노하우를 공유하고 공동 프로젝트 작업을 하며 온라인에서 프로젝트를 홍보할 수도 있습니다. MetaQuotes에서 EX5 라이브러리 클래스의 직접 상속을 가능하게 하기 위해 열심히 개발 중이긴 하지만 우리가 한번 먼저 구현해 보도록 하겠습니다.
지수 평활법을 이용한 시계열 예측(계속) 지수 평활법을 이용한 시계열 예측(계속)
이번 글에서는 이전 글에서 만든 인디케이터를 한층 업그레이드해 보고, 부트스트랩과 분위수를 이용한 예측 신뢰 구간 측정 방법에 대해서도 간단하게 알아보겠습니다. 예측 인디케이터 개발과 예측 정확도 측정에 사용될 스크립트 작성이 목표입니다.