또 다른 MQL5 OOP 클래스
소개
실제로 작동하는 완전한 객체 지향 EA를 구축하는 것은 논리적 추론, 발산적 사고, 분석 및 종합 능력, 상상력 등 많은 기술을 모두 요구하는 도전적인 작업입니다. 우리가 해결해야 하는 자동 거래 시스템이 체스 게임이라면 거래 아이디어는 체스 전략이 될 것입니다. 그리고 전술을 통한 체스 전략의 실행은 기술 지표, 차트 수치, 기본 경제 아이디어 및 개념 공리를 사용하여 로봇을 프로그래밍하는 것입니다.
그림 1. Raffaello Sanzio의 아테네 학파의 디테일. 이 그림에서 철학자 플라톤과 아리스토텔레스가 심도 있게 토론하는 것을 볼 수 있습니다.
여기서 플라톤은 개념적 세계를 나타내고 아리스토텔레스는 경험주의적 세계를 나타냅니다.
저는 이 운동의 어려움을 알고 있습니다. OO EA는 프로그래밍하기가 매우 어렵지는 않지만 애플리케이션 개발 경험이 거의 없는 사람들에게 어느 정도의 어려움을 주는 것은 사실입니다. 다른 학문과 마찬가지로 이것은 경험 자체가 부족하기 때문입니다. 그래서 저는 당신이 이해할 것이라고 확신하는 구체적인 예를 통해 이 주제를 가르치려고 합니다. OOP 개념을 처리하는 데 여전히 불안함을 느끼더라도 낙담하지 마십시오. 예를 들어 처음 5개의 EA를 구현하고 나면 훨씬 더 쉽게 일을 찾을 수 있을 것입니다. 지금은 처음부터 아무것도 만들 필요가 없습니다. 여기에서 설명하는 내용만 이해하면 됩니다.
거래 시스템을 구상하고 실행하는 전체 과정은 여러 사람이 수행할 때 여러 면에서 단순화될 수 있지만 이러한 사실은 의사소통의 문제를 제기합니다. 내 말은 투자 전략을 구상하는 사람이 그들의 대화 상대인 프로그래머를 다루는 프로그래밍 개념을 다룰 필요가 없다는 뜻입니다. 그리고 MQL5 개발자는 처음에는 고객의 거래 전략의 몇 가지 중요한 측면을 이해하지 못할 수 있습니다.
이것은 스크럼, 테스트 주도 개발(TDD), 익스트림 프로그래밍( XP) 등 언어의 함정을 인식하는 것이 중요합니다. 그건 그렇고, Wikipedia는 "소프트웨어 엔지니어링의 소프트웨어 개발 방법론 또는 시스템 개발 방법론은 정보 시스템 개발 프로세스를 구성, 계획 및 제어하는 데 사용되는 프레임워크"라고 말합니다.
우리는 성공적인 거래 아이디어를 신속하게 구상하고 실행할 수 있다고 가정할 것입니다. 또한 우리는 고객이 생각하는 거래 시스템이 이미 모두가 잘 정의하고 이해하는 반복적인 개발 프로세스의 끝에 있다고 가정할 수 있습니다. 당신이 원하는대로 말이죠. 그건 그렇고, 지금부터 이 텍스트에서 MQL5 프로그래밍 문서에서 사용할 수 있는 몇 가지 교육 문서를 참조하겠습니다. 당신이 이 연습을 성공적으로 수행하는 데 필요한 몇 가지 아이디어를 신속하게 확인하고 기억할 수 있게 말이죠. 준비가 되었나요?
1. 새로운 패러다임을 받아들이는 첫걸음
1.1. OOP가 Forex EA 프로그래밍에 좋은 이유는 무엇입니까?
아마도 이 시점에서 왜 이런 일을 해야 하는지 궁금할 것입니다. OOP를 강요받지 않는다는 점을 먼저 말씀드리겠습니다. 어쨌든 자동 거래 시스템 프로그래밍에 대한 지식을 한 단계 더 발전시키려면 OOP 담당자가 되는 것이 좋습니다.
절차적 프로그래밍이라고 하는 응용 프로그램을 개발하는 고전적인 방법에는 다음과 같은 단점이 있습니다.
- 문제를 모델링하기 어렵게 만듭니다. 이 오래된 패러다임에서 주요 문제의 솔루션은 기능적 모듈, 즉 기능 및 절차에 의해 해결되는 더 간단한 하위 문제로 분할되도록 축소됩니다.
- 코드를 재사용하기 어렵게 만들어 비용, 안정성, 유연성 및 유지 관리를 방해합니다.
새로운 객체 지향 스타일을 사용하면 코드 재사용이 더 쉬워집니다. 이건 매우 중요합니다! 많은 Expert들은 코드 재사용이 소프트웨어 개발의 대부분의 문제에 대한 진정한 해결책이라고 믿습니다.
이 시점에서 추상 데이터 유형(ADT)을 언급해야 합니다. OOP를 사용하면 ADT를 생성할 수 있습니다. ADT는 모든 프로그래밍 언어에 존재하는 데이터 유형의 전통적인 개념을 추상화한 것입니다. 주요 용도는 애플리케이션의 데이터 도메인을 편안하게 정의하는 것입니다. Include\Arrays\Array.mqh, Include\Arrays\List.mqh 및 Include\Arrays\Tree.mqh는 MQL5 추상 데이터 유형의 몇 가지 예입니다.
요컨대, 객체 지향 프로그래밍 패러다임은 코드 재사용, 안정성, 유연성 및 유지 관리 용이성의 이점을 누릴 수 있도록 개념적 수준에서 응용 프로그램을 설계하기를 원합니다.
1.2. 당신은 개념적 추론가입니까? UML이 구출에 나섭니다
UML에 대해 들어본 적이 있습니까? UML은 통합 모델링 언어의 약자입니다. 객체 지향 시스템을 설계하기 위한 그래픽 언어입니다. 우리 인간은 먼저 분석 단계에서 시스템을 생각한 다음 프로그래밍 언어로 코딩해야 합니다. 위에서 아래로 내려가는 것이 그나마 개발자들에게는 덜 미친 짓입니다. 그럼에도 불구하고, 분석가로서의 제 경험에 따르면 때때로 여러 가지 이유로 이것이 가능하지 않다고 말합니다. 앱은 매우 짧은 시간에 완료되어야 하고, 팀에 UML에 대한 지식을 신속하게 적용할 수 있는 사람이 없습니다. 아니면 팀 사람들이 UML의 일부분을 모를지도 모르는 일이죠.
제 생각에 UML은 편안하고 프로젝트를 둘러싼 상황이 괜찮다면 사용할 수 있는 좋은 분석 도구입니다. UML 주제를 탐색하는 데 관심이 있는 경우 UML 도구를 사용하여 Expert Advisor를 개발하는 방법을 읽어보세요. 글이 다소 압도적일 수는 있겠으나 전문 소프트웨어 엔지니어가 분석을 수행하는 방법에 대한 큰 그림을 얻는 데 도움이 됩니다. UML을 완전히 이해하려면 몇 주 과정을 완료해야 합니다! 지금은 UML이 무엇인지만 알아도 괜찮습니다. 제 경험에 따르면 이 분석 도구는 실제 세계에 존재하는 여러 상황으로 인해 모든 소프트웨어 프로젝트에서 항상 사용되는 것은 아닙니다.
그림 2. UML 로고.
1.3. Hello World! 첫 번째 OO 클래스
객체 지향 프로그래밍에 대한 완전한 초보자라면 먼저 MQL5 Reference에서 제공되는 OO에 대한 공식 문서인 MQL5 개체를 이용한 Expert Advisor 쓰기 - 기본 사항을 이해하기 위한 지향 프로그래밍 접근 방식을 읽어볼 것을 추천합니다. 이 글을 읽으면서 다른 자료로 정보를 꼭 보완하세요. 이제부터는 OOP를 이미 알고 있다고 가정하겠습니다. 그래야 당신이 MQL5에서 다음과 같은 Person 클래스의 고전적인 예를 쉽게 이해할 수 있을 테니까요.
//+------------------------------------------------------------------+ //| CPerson Class | //+------------------------------------------------------------------+ class CPerson { protected: string m_first_name; string m_surname; datetime m_birth; public: //--- Constructor and destructor methods CPerson(void); ~CPerson(void); //--- Getter methods string GetFirstName(void); string GetSurname(void); datetime GetBirth(void); //--- Setter methods void SetFirstName(string first_name); void SetSurname(string surname); void SetBirth(datetime birth); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPerson::CPerson(void) { Alert("Hello world! I am run when an object of type CPerson is created!"); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPerson::~CPerson(void) { Alert("Goodbye world! I am run when the object is destroyed!"); } //+------------------------------------------------------------------+ //| GetFirstName | //+------------------------------------------------------------------+ string CPerson::GetFirstName(void) { return m_first_name; } //+------------------------------------------------------------------+ //| GetSurname | //+------------------------------------------------------------------+ string CPerson::GetSurname(void) { return m_surname; } //+------------------------------------------------------------------+ //| GetBirth | //+------------------------------------------------------------------+ datetime CPerson::GetBirth(void) { return m_birth; } //+------------------------------------------------------------------+ //| SetFirstName | //+------------------------------------------------------------------+ void CPerson::SetFirstName(string first_name) { m_first_name=first_name; } //+------------------------------------------------------------------+ //| SetSurname | //+------------------------------------------------------------------+ void CPerson::SetSurname(string surname) { m_surname=surname; } //+------------------------------------------------------------------+ //| SetBirth | //+------------------------------------------------------------------+ void CPerson::SetBirth(datetime birth) { m_birth=birth; } //+------------------------------------------------------------------+
다음은 양질의 코드 작성에 집착하는 순수주의 개발자를 위한 것입니다. MQL5 프로그래밍 글 및 코드 베이스에서 사용 가능한 다른 많은 소프트웨어와 달리, 위의 클래스는 MetaQuotes Software Corp에서 적용한 것과 동일한 프로그래밍 규칙을 사용합니다. MQL5 프레임워크를 코딩하기 위해 MetaQuotes처럼 코드를 작성하는 것이 좋습니다. 그건 그렇고, OOP MQL5 프로그램의 규칙 정보라는 제목의 스레드가 이 주제를 다룹니다.
간단히 말해서 Person.mqh 작성에 적용되는 몇 가지 중요한 규칙은 다음과 같습니다.
- 클래스 이름 CPerson은 대문자 C로 시작합니다.
- 메소드 이름은 카멜 케이스이며 대문자로 시작합니다(예: GetFirstName, SetSurname 등).
- 보호 속성 이름 앞에는 m_ 접두사가 붙습니다(예: m_first_name, m_surname 및 m_born).
- 예약어 this는 클래스 자체 내에서 클래스 멤버를 참조하는 데 사용되지 않습니다.
일부 MQL5 프레임워크 파일(예: Include\Arrays\Array.mqh, Include\Arrays\List.mqh, Include\Trade)을 살펴보십시오. \Trade.mqh에서 원래 MQL5 코드가 어떻게 작성되었는지 확인하세요.
2. 첫 번째 객체 지향 EA를 프로그래밍해봅시다
2.1. 거래 시스템의 아이디어
우리의 거래 아이디어는 간단합니다. "변동성 시장의 단기 추세는 거의 무작위입니다." 그게 다입니다. 이것은 어떤 상황에서 여러 Expert에 의해 관찰되었습니다. 이 가설이 사실이라면 Forex 로봇은 필연적으로 작동해야 합니다. 차트의 임의의 지점이 주어지면 다음 움직임은 분명히 위아래로 갈 수 있지만 우리는 그걸 알 수 없습니다. 문제는 설정된 SL과 TP 수준 간의 차이가 충분히 작으면 그 차이가 절대적으로 중요하지 않으므로 수학적 기대치에 도달했다는 것입니다. 이 시스템은 수학적 기대치를 작동하게 할 것입니다. 이 글에서 EA의 코드를 얻고 백테스트를 실행하면 매우 간단한 자금 관리 정책이 필요하다는 것을 알 수 있습니다.
2.2. 로봇의 OOP 스켈레톤
이 섹션에서는 객체 지향 프로그래밍이 요구하는 추상적인 추론을 통해 위의 전략을 개발합니다. 그렇다면 EA를 마치 살아있는 생물처럼 생각하는 것은 어떨까요? 이 비전에 따라 Forex 기계는 세 가지 주요 부분으로 구성될 수 있습니다. 두뇌, 진화라고 하는 것, 차트입니다.
두뇌는 ROM(읽기 전용 메모리)과 같은 작동에 필요한 데이터를 포함하는 로봇의 일부입니다. 차트는 로봇이 작동하는 그래픽을 모방한 정보 조각입니다. 마지막으로 소위 진화는 주어진 순간의 로봇 상태, 수행된 작업의 이력 등과 같은 시간적 정보를 포함하는 데이터 조각입니다. 마치 우리가 건강 부문을 위한 앱을 개발해야 하기 때문에 프랑켄슈타인과 같은 장기를 통해 인간을 디자인하는 것과 같습니다. 이러한 맥락에서 각 기관은 전체의 다른 부분과 관련된 고유한 의미 개념입니다.
우선 사용자 정의 항목을 저장할 MQL5\Include\Mine 폴더를 생성하겠습니다. 이것은 코드를 정리하기 위한 아이디어일 뿐입니다. 개발 과정에서 이 작업을 수행할 수 있다는 것을 아는 것이 좋지만 물론 강제로 수행해야 하는 것은 아닙니다. 그런 다음 우리가 생성한 열거형을 저장하기 위해 MQL5\Include\Mine\Enums.mqh 파일을 생성합니다.
//+------------------------------------------------------------------+ //| Status enumeration | //+------------------------------------------------------------------+ enum ENUM_STATUS_EA { BUY, SELL, DO_NOTHING }; //+------------------------------------------------------------------+ //| Lifetime enumeration | //+------------------------------------------------------------------+ enum ENUM_LIFE_EA { HOUR, DAY, WEEK, MONTH, YEAR }; //+------------------------------------------------------------------+
다음으로 ExpertSimpleRandom.mq5라는 이름의 EA 배아를 만들 차례입니다! 따라서 MQL5\Experts\SimpleRandom 폴더를 만든 다음 ExpertSimpleRandom.mq5 파일 안에 다음 코드를 만드십시오.
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #property copyright "Copyright © 2013, laplacianlab" #property link "https://www.mql5.com/ko/articles" #property version "1.00" #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Indicators\Indicators.mqh> #include <Mine\Enums.mqh> #include <..\Experts\SimpleRandom\CSimpleRandom.mqh> input int StopLoss; input int TakeProfit; input double LotSize; input ENUM_LIFE_EA TimeLife; MqlTick tick; CSimpleRandom *SR=new CSimpleRandom(StopLoss,TakeProfit,LotSize,TimeLife); //+------------------------------------------------------------------+ //| Initialization function | //+------------------------------------------------------------------+ int OnInit(void) { SR.Init(); return(0); } //+------------------------------------------------------------------+ //| Deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { SR.Deinit(); delete(SR); } //+------------------------------------------------------------------+ //| OnTick event function | //+------------------------------------------------------------------+ void OnTick() { SymbolInfoTick(_Symbol,tick); SR.Go(tick.ask,tick.bid); } //+------------------------------------------------------------------+
이것은 가능한 많은 방법 중 하나일 뿐입니다. 이 모든 것은 기본적으로 MQL5에서 OOP가 작동하는 방식을 설명하기 위한 것입니다. 보시다시피 Expert Advisor의 기본 클래스 이름은 CSimpleRandom.mqh입니다. MQL5\Experts\SimpleRandom\CSimpleRandom.mqh에 저장하십시오.
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Mine\Enums.mqh> #include <..\Experts\SimpleRandom\CBrain.mqh> #include <..\Experts\SimpleRandom\CEvolution.mqh> #include <..\Experts\SimpleRandom\CGraphic.mqh> //+------------------------------------------------------------------+ //| CSimpleRandom Class | //+------------------------------------------------------------------+ class CSimpleRandom { protected: CBrain *m_brain; CEvolution *m_evolution; CGraphic *m_graphic; CTrade *m_trade; CPositionInfo *m_positionInfo; public: //--- Constructor and destructor methods CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life); ~CSimpleRandom(void); //--- Getter methods CBrain *GetBrain(void); CEvolution *GetEvolution(void); CGraphic *GetGraphic(void); CTrade *GetTrade(void); CPositionInfo *GetPositionInfo(void); //--- Specific methods of CSimpleRandom bool Init(); void Deinit(void); bool Go(double ask,double bid); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSimpleRandom::CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life) { int lifeInSeconds; switch(time_life) { case HOUR: lifeInSeconds=3600; break; case DAY: lifeInSeconds=86400; break; case WEEK: lifeInSeconds=604800; break; case MONTH: lifeInSeconds=2592000; break; // One year default: lifeInSeconds=31536000; break; } m_brain=new CBrain(TimeLocal(),TimeLocal()+lifeInSeconds,lot_size,stop_loss,take_profit); m_evolution=new CEvolution(DO_NOTHING); m_graphic=new CGraphic(_Symbol); m_trade=new CTrade(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSimpleRandom::~CSimpleRandom(void) { delete(m_brain); delete(m_evolution); delete(m_graphic); delete(m_trade); } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CBrain *CSimpleRandom::GetBrain(void) { return m_brain; } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CEvolution *CSimpleRandom::GetEvolution(void) { return m_evolution; } //+------------------------------------------------------------------+ //| GetGraphic | //+------------------------------------------------------------------+ CGraphic *CSimpleRandom::GetGraphic(void) { return m_graphic; } //+------------------------------------------------------------------+ //| GetTrade | //+------------------------------------------------------------------+ CTrade *CSimpleRandom::GetTrade(void) { return m_trade; } //+------------------------------------------------------------------+ //| GetPositionInfo | //+------------------------------------------------------------------+ CPositionInfo *CSimpleRandom::GetPositionInfo(void) { return m_positionInfo; } //+------------------------------------------------------------------+ //| CSimpleRandom initialization | //+------------------------------------------------------------------+ bool CSimpleRandom::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CSimpleRandom deinitialization | //+------------------------------------------------------------------+ void CSimpleRandom::Deinit(void) { // Deinitialization logic here... delete(m_brain); delete(m_evolution); delete(m_graphic); delete(m_trade); } //+------------------------------------------------------------------+ //| CSimpleRandom Go | //+------------------------------------------------------------------+ bool CSimpleRandom::Go(double ask,double bid) { double tp; double sl; int coin=m_brain.GetRandomNumber(0,1); // Is there any open position? if(!m_positionInfo.Select(_Symbol)) { // If not, we open one if(coin==0) { GetEvolution().SetStatus(BUY); } else { GetEvolution().SetStatus(SELL); } } // If so, let it work the mathematical expectation. else GetEvolution().SetStatus(DO_NOTHING); switch(GetEvolution().GetStatus()) { case BUY: tp = ask + m_brain.GetTakeProfit() * _Point; sl = bid - m_brain.GetStopLoss() * _Point; GetTrade().PositionOpen(_Symbol,ORDER_TYPE_BUY,m_brain.GetSize(),ask,sl,tp); break; case SELL: sl = ask + m_brain.GetStopLoss() * _Point; tp = bid - m_brain.GetTakeProfit() * _Point; GetTrade().PositionOpen(_Symbol,ORDER_TYPE_SELL,m_brain.GetSize(),bid,sl,tp); break; case DO_NOTHING: // Nothing... break; } // If there is some error we return false, for now we always return true return(true); } //+------------------------------------------------------------------+
2.3. 복합 유형의 개체에 CSimpleRandom 바인딩
CBrain, CEvolution 및 CGraphic 유형의 사용자 정의 개체가 CSimpleRandom에 어떻게 연결되어 있는지 확인하십시오.
먼저 해당 보호 속성을 정의합니다.
protected:
CBrain *m_brain;
CEvolution *m_evolution;
CGraphic *m_graphic;
그리고 생성자 내부에서 해당 개체를 인스턴스화한 직후:
m_brain=new CBrain(TimeLocal(), TimeLocal() + lifeInSeconds, lot_size, stop_loss, take_profit); m_evolution=new CEvolution(DO_NOTHING); m_graphic=new CGraphic(_Symbol);
우리가 하는 일은 공식 문서가 개체 포인터에서 설명하는 것처럼 복잡한 유형의 개체를 동적으로 생성하는 것입니다. 이 체계를 사용하면 CSimpleRandom에서 직접 CBrain, CEvolution 및 CGraphic의 기능에 액세스할 수 있습니다. 예를 들어 ExpertSimpleRandom.mq5에서 다음 코드를 실행할 수 있습니다.
//+------------------------------------------------------------------+ //| OnTick event function | //+------------------------------------------------------------------+ void OnTick() { // ... int randNumber=SR.GetBrain().GetRandomNumber(4, 8); // ... }
이제 이 섹션을 마무리하기 위해 CBrain, CEvolution 및 CGraphic 코드를 작성합니다. SimpleRandom을 백테스트하는 데 꼭 필요하지 않기 때문에 코딩되지 않은 부분이 있다는 점에 유의하십시오. 이 클래스의 누락된 부분을 코딩하는 것은 연습으로 남겨두고 원하는 대로 자유롭게 개발하십시오. 예를 들어, m_death는 실제로 사용되지 않지만 그 뒤에 숨어 있는 아이디어는 로봇이 활동을 완료할 날짜를 처음부터 알고 있다는 것입니다.
//+------------------------------------------------------------------+ //| ExpertSimpleRandom | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| CBrain Class | //+------------------------------------------------------------------+ class CBrain { protected: ENUM_TIMEFRAMES m_period; // period must always be initialized to PERIOD_M1 to fit the system's idea datetime m_birth; // The datetime in which the robot is initialized for the first time datetime m_death; // The datetime in which the robot will die double m_size; // The size of the positions int m_stopLoss; // Stop loss int m_takeProfit; // Take profit public: //--- Constructor and destructor methods CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit); ~CBrain(void); //--- Getter methods datetime GetBirth(void); datetime GetDeath(void); double GetSize(void); int GetStopLoss(void); int GetTakeProfit(void); //--- Setter methods void SetBirth(datetime birth); void SetDeath(datetime death); void SetSize(double size); void SetStopLoss(int stopLoss); void SetTakeProfit(int takeProfit); //--- Brain specific logic int GetRandomNumber(int a,int b); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CBrain::CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit) { MathSrand(GetTickCount()); m_period=PERIOD_M1; m_birth=birth; m_death=death; m_size=size; m_stopLoss=stopLoss; m_takeProfit=takeProfit; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CBrain::~CBrain(void) { } //+------------------------------------------------------------------+ //| GetBirth | //+------------------------------------------------------------------+ datetime CBrain::GetBirth(void) { return m_birth; } //+------------------------------------------------------------------+ //| GetDeath | //+------------------------------------------------------------------+ datetime CBrain::GetDeath(void) { return m_death; } //+------------------------------------------------------------------+ //| GetSize | //+------------------------------------------------------------------+ double CBrain::GetSize(void) { return m_size; } //+------------------------------------------------------------------+ //| GetStopLoss | //+------------------------------------------------------------------+ int CBrain::GetStopLoss(void) { return m_stopLoss; } //+------------------------------------------------------------------+ //| GetTakeProfit | //+------------------------------------------------------------------+ int CBrain::GetTakeProfit(void) { return m_takeProfit; } //+------------------------------------------------------------------+ //| SetBirth | //+------------------------------------------------------------------+ void CBrain::SetBirth(datetime birth) { m_birth=birth; } //+------------------------------------------------------------------+ //| SetDeath | //+------------------------------------------------------------------+ void CBrain::SetDeath(datetime death) { m_death=death; } //+------------------------------------------------------------------+ //| SetSize | //+------------------------------------------------------------------+ void CBrain::SetSize(double size) { m_size=size; } //+------------------------------------------------------------------+ //| SetStopLoss | //+------------------------------------------------------------------+ void CBrain::SetStopLoss(int stopLoss) { m_stopLoss=stopLoss; } //+------------------------------------------------------------------+ //| SetTakeProfit | //+------------------------------------------------------------------+ void CBrain::SetTakeProfit(int takeProfit) { m_takeProfit=takeProfit; } //+------------------------------------------------------------------+ //| GetRandomNumber | //+------------------------------------------------------------------+ int CBrain::GetRandomNumber(int a,int b) { return(a+(MathRand()%(b-a+1))); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| ExpertSimpleRandom | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ #include <Indicators\Indicators.mqh> #include <Mine\Enums.mqh> //+------------------------------------------------------------------+ //| CEvolution Class | //+------------------------------------------------------------------+ class CEvolution { protected: ENUM_STATUS_EA m_status; // The current EA's status CArrayObj* m_operations; // History of the operations performed by the EA public: //--- Constructor and destructor methods CEvolution(ENUM_STATUS_EA status); ~CEvolution(void); //--- Getter methods ENUM_STATUS_EA GetStatus(void); CArrayObj *GetOperations(void); //--- Setter methods void SetStatus(ENUM_STATUS_EA status); void SetOperation(CObject *operation); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEvolution::CEvolution(ENUM_STATUS_EA status) { m_status=status; m_operations=new CArrayObj; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CEvolution::~CEvolution(void) { delete(m_operations); } //+------------------------------------------------------------------+ //| GetStatus | //+------------------------------------------------------------------+ ENUM_STATUS_EA CEvolution::GetStatus(void) { return m_status; } //+------------------------------------------------------------------+ //| GetOperations | //+------------------------------------------------------------------+ CArrayObj *CEvolution::GetOperations(void) { return m_operations; } //+------------------------------------------------------------------+ //| SetStatus | //+------------------------------------------------------------------+ void CEvolution::SetStatus(ENUM_STATUS_EA status) { m_status=status; } //+------------------------------------------------------------------+ //| SetOperation | //+------------------------------------------------------------------+ void CEvolution::SetOperation(CObject *operation) { m_operations.Add(operation); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ #include <Trade\SymbolInfo.mqh> #include <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| CGrapic Class | //+------------------------------------------------------------------+ class CGraphic { protected: ENUM_TIMEFRAMES m_period; // Graphic's timeframe string m_pair; // Graphic's pair CSymbolInfo* m_symbol; // CSymbolInfo object CArrayObj* m_bars; // Array of bars public: //--- Constructor and destructor methods CGraphic(string pair); ~CGraphic(void); //--- Getter methods string GetPair(void); CSymbolInfo *GetSymbol(void); CArrayObj *GetBars(void); //--- Setter methods void SetPair(string pair); void SetSymbol(CSymbolInfo *symbol); void SetBar(CObject *bar); }; //+------------------------------------------------------------------+ //| Constuctor | //+------------------------------------------------------------------+ CGraphic::CGraphic(string pair) { m_period=PERIOD_M1; m_pair=pair; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGraphic::~CGraphic(void) { } //+------------------------------------------------------------------+ //| GetPair | //+------------------------------------------------------------------+ string CGraphic::GetPair(void) { return m_pair; } //+------------------------------------------------------------------+ //| GetSymbol | //+------------------------------------------------------------------+ CSymbolInfo *CGraphic::GetSymbol(void) { return m_symbol; } //+------------------------------------------------------------------+ //| GetBars | //+------------------------------------------------------------------+ CArrayObj *CGraphic::GetBars(void) { return m_bars; } //+------------------------------------------------------------------+ //| SetPair | //+------------------------------------------------------------------+ void CGraphic::SetPair(string pair) { m_pair=pair; } //+------------------------------------------------------------------+ //| SetSymbol | //+------------------------------------------------------------------+ void CGraphic::SetSymbol(CSymbolInfo *symbol) { m_symbol=symbol; } //+------------------------------------------------------------------+ //| SetBar | //+------------------------------------------------------------------+ void CGraphic::SetBar(CObject *bar) { m_bars.Add(bar); } //+------------------------------------------------------------------+
3. Backtesting ExpertSimpleRandom.mq5
이 무작위 거래 시스템은 예상대로 특정 손절매 및 이익 수준에 대해서만 유효한 것으로 입증되었습니다. 물론 이러한 승자 SL/TP 간격은 모든 기호에 대해 동일하지 않습니다. 이는 모든 기호가 주어진 시간에 고유한 성격을 나타내기 때문입니다. 즉, 다른 말로 하면 모든 통화 쌍이 나머지에 대해 다르게 움직이기 때문입니다. 따라서 실제 환경에서 ExpertSimpleRandom.mq5를 실행하기 전에 백테스팅에서 이러한 수준을 먼저 식별하십시오.
이 글에서 드러난 아이디어가 승자처럼 보이는 몇 가지 샘플 데이터를 공유합니다. 이것은 MetaTrader 5 Strategy Tester에서 ExpertSimpleRandom.mq5를 여러 번 실행한 후에 추론할 수 있습니다.
2012년 1월 EURUSD에 대한 일부 승자 입력은 다음과 같습니다.
- 손절매: 400
- 이익실현: 600
- 랏 크기: 0.01
- TimeLife: MONTH
실행 번호 1:
실행 번호 2:
Run number 3:
결론
우리는 자동화된 거래 시스템에 객체 지향 프로그래밍을 적용하는 방법을 배웠습니다. 이를 위해 먼저 기계적 거래 전략을 정의해야 했습니다. 우리의 거래 아이디어는 매우 간단합니다. "변동성이 큰 시장의 단기 추세는 거의 무작위에 가깝습니다." 이것은 어떤 상황에서 여러 Expert에 의해 관찰되었습니다.
마치 살아있는 생물처럼 현실 세계에서 EA를 생각한 직후입니다. 이 비전 덕분에 우리는 Forex 기계가 뇌, 진화라고 부르는 것, 차트의 세 가지 주요 부분으로 구성될 수 있다는 것을 알았습니다.
그리고 마지막으로 백테스트를 실행하는 데 필요한 논리를 통합하는 시스템의 Expert 조언자를 프로그래밍했으며 2012년 1월에 로봇을 여러 번 실행했으며 대부분의 경우 시스템이 이기는 것으로 나타났습니다. 이 시스템의 이면에 있는 아이디어는 사실로 입증되었지만 단순성으로 인해 효율성이 그리 높지 않습니다.
MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/703