English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
사전 정의된 위험 및 R/R 비율을 기반으로 인터랙티브 반자동 드래그 앤 드롭 Expert Advisor 구축

사전 정의된 위험 및 R/R 비율을 기반으로 인터랙티브 반자동 드래그 앤 드롭 Expert Advisor 구축

MetaTrader 5 | 4 8월 2021, 16:40
61 0
investeo
investeo

소개

일부 거래자는 모든 거래를 자동으로 실행하고 일부는 여러 지표의 출력을 기반으로 자동 및 수동 거래를 혼합합니다. 후자 그룹의 일원이기 때문에 동적으로 위험을 평가하고 차트에서 직접 가격 수준을 보상 할 수 있는 대화형 도구가 필요했습니다.

자산에 대한 최대 위험을 선언 한 후 차트에 기록한 손절매 수준을 기반으로 실시간 매개 변수를 계산하고 싶었고 계산 된 SL 및 TP 수준을 기반으로 EA에서 직접 거래를 실행해야 했습니다.

이 글에서는 사전 정의된 주식 위험 및 R/R 비율을 사용하여 대화형 반자동 Expert Advisor를 구현하는 방법을 설명합니다. Expert Advisor 위험, R/R 및 랏 크기 매개 변수는 EA 패널에서 런타임 중에 변경할 수 있습니다. 


1. 요구 사항

EA에 대한 요구 사항은 다음과 같습니다.

  • 시작시 위험 수준을 미리 정의하고 런타임 중에 이를 변경하여 포지션 크기에 미치는 영향을 확인하는 기능
  • 보상 비율에 대한 위험을 미리 정의하고 런타임 중에 변경하는 기능
  • 주어진 위험 및 손절매 수준에 대한 실시간 최대 랏 크기를 계산하는 기능
  • 자산 위험 및 보상에 미치는 영향을 보기 위해 런타임에 랏 크기를 변경할 수 있는 기능
  • EA에서 직접 구매/판매 시장 주문을 실행할 수있는 능력
  • 손절매를 설정하고 사전 정의 된 위험에 대한 가격 수준을 보상 수준으로 보기 위한 드래그 앤 드롭 인터페이스 


2. 디자인

런타임 중에 매개 변수를 표시하고 변경하는 EA의 요구 사항으로 인해 CChartObject 클래스와 그 하위 클래스를 사용하여 차트 창에 GUI를 표시하고 사용자 상호 작용을 위해 들어오는 차트 이벤트를 처리하기로 결정했습니다. 따라서 EA는 레이블, 버튼 및 편집 필드가 있는 사용자 인터페이스가 필요했습니다.

처음에는 CChartObjectPanel 객체를 사용하여 패널에서 다른 객체를 그룹화하고 싶었지만 다른 접근 방식을 시도하기로 결정하고 레이블을 보유하고 필드와 버튼을 편집하고 이미지 배경에 표시하는 클래스를 디자인했습니다. 인터페이스의 배경 이미지는 GIMP 소프트웨어를 사용하여 만들어졌습니다. MQL5 생성 ​​개체는 수정 필드, 실시간으로 업데이트 되는 빨간색 라벨 및 버튼입니다.

간단히 차트에 레이블 개체를 놓고 포지션을 기록하고 계산된 출력 표시, CChartObjectEdit 필드의 매개 변수 수신 및 버튼 상태 기록의 모든 기능을 처리하는 CRRDialog 클래스를 구성했습니다. 색상 위험 및 보상 직사각형은 CChartObjectRectangle 클래스의 개체이며 드래그 가능한 손절매 포인터는 CChartObjectBitmap 클래스의 비트 맵 개체입니다.


 

그림 1. Visual EA 스크린 샷

그림 1. Visual EA 스크린 샷

 


3. EA 대화 클래스 구현

CRRDialog 클래스는 EA의 모든 사용자 인터페이스를 처리합니다. 여기에는 표시되는 여러 변수, 변수를 표시하는 데 사용되는 개체 및 변수 값을 가져 오거나 설정하고 대화 상자를 새로 고치는 데 사용되는 개체가 포함됩니다.

배경에는 CChartObjectBmpLabel 개체, 편집 필드에는 CChartObjectEdit 개체, 레이블 표시에는 CChartObjectLabel 개체 및 버튼용으로 CChartObjectButton 개체 사용하고 있습니다: 

class CRRDialog
  {
private:

   int               m_baseX;
   int               m_baseY;
   int               m_fontSize;
   
   string            m_font;
   string            m_dialogName;
   string            m_bgFileName;

   double            m_RRRatio;
   double            m_riskPercent;
   double            m_orderLots;
   double            m_SL;
   double            m_TP;
   double            m_maxAllowedLots;
   double            m_maxTicksLoss;
   double            m_orderEquityRisk;
   double            m_orderEquityReward;
   ENUM_ORDER_TYPE   m_orderType;

   CChartObjectBmpLabel m_bgDialog;

   CChartObjectEdit  m_riskRatioEdit;
   CChartObjectEdit  m_riskValueEdit;
   CChartObjectEdit  m_orderLotsEdit;

   CChartObjectLabel m_symbolNameLabel;
   CChartObjectLabel m_tickSizeLabel;
   CChartObjectLabel m_maxEquityLossLabel;
   CChartObjectLabel m_equityLabel;
   CChartObjectLabel m_profitValueLabel;
   CChartObjectLabel m_askLabel;
   CChartObjectLabel m_bidLabel;
   CChartObjectLabel m_tpLabel;
   CChartObjectLabel m_slLabel;
   CChartObjectLabel m_maxAllowedLotsLabel;
   CChartObjectLabel m_maxTicksLossLabel;
   CChartObjectLabel m_orderEquityRiskLabel;
   CChartObjectLabel m_orderEquityRewardLabel;
   CChartObjectLabel m_orderTypeLabel;

   CChartObjectButton m_switchOrderTypeButton;
   CChartObjectButton m_placeOrderButton;
   CChartObjectButton m_quitEAButton;

public:

   void              CRRDialog(); // CRRDialog constructor
   void             ~CRRDialog(); // CRRDialog destructor

   bool              CreateCRRDialog(int topX,int leftY);
   int               DeleteCRRDialog();
   void              Refresh();
   void              SetRRRatio(double RRRatio);
   void              SetRiskPercent(double riskPercent);
   double            GetRiskPercent();
   double            GetRRRRatio();
   void              SetSL(double sl);
   void              SetTP(double tp);
   double            GetSL();
   double            GetTP();
   void              SetMaxAllowedLots(double lots);
   void              SetMaxTicksLoss(double ticks);
   void              SetOrderType(ENUM_ORDER_TYPE);
   void              SwitchOrderType();
   void              ResetButtons();
   ENUM_ORDER_TYPE   GetOrderType();
   void              SetOrderLots(double orderLots);
   double            GetOrderLots();
   void              SetOrderEquityRisk(double equityRisk);
   void              SetOrderEquityReward(double equityReward);
  };

get/set 변수 메소드는 간단하므로 CreateCRRDialog() 및 Refresh() 메소드에 집중하겠습니다. CreateCRRDialog() 메소드는 배경 이미지, 레이블, 버튼 및 편집 필드를 초기화합니다.

레이블을 초기화하고 필드를 편집하려면 차트에서 개체를 찾는 좌표 매개 변수가 있는 Create() 메소드, 글꼴을 설정하는 Font() 및 FontSize() 메소드, 레이블에 텍스트를 넣는 Description() 메소드를 사용합니다.

버튼의 경우: Create() 메소드 추가 매개 변수는 버튼 크기를 지정하고 BackColor() 메소드는 버튼의 배경색을 지정합니다. 

bool CRRDialog::CreateCRRDialog(int topX,int leftY)
  {
   bool isCreated=false;

   MqlTick current_tick;
   SymbolInfoTick(Symbol(),current_tick);

   m_baseX = topX;
   m_baseY = leftY;

   m_bgDialog.Create(0, m_dialogName, 0, topX, leftY);
   m_bgDialog.BmpFileOn(m_bgFileName);

   m_symbolNameLabel.Create(0, "symbolNameLabel", 0, m_baseX + 120, m_baseY + 40);
   m_symbolNameLabel.Font("Verdana");
   m_symbolNameLabel.FontSize(8);
   m_symbolNameLabel.Description(Symbol());

   m_tickSizeLabel.Create(0, "tickSizeLabel", 0, m_baseX + 120, m_baseY + 57);
   m_tickSizeLabel.Font("Verdana");
   m_tickSizeLabel.FontSize(8);
   m_tickSizeLabel.Description(DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE), Digits()));

   m_riskRatioEdit.Create(0, "riskRatioEdit", 0, m_baseX + 120, m_baseY + 72, 35, 15);
   m_riskRatioEdit.Font("Verdana");
   m_riskRatioEdit.FontSize(8);
   m_riskRatioEdit.Description(DoubleToString(m_RRRatio, 2));

   m_riskValueEdit.Create(0, "riskValueEdit", 0, m_baseX + 120, m_baseY + 90, 35, 15);
   m_riskValueEdit.Font("Verdana");
   m_riskValueEdit.FontSize(8);
   m_riskValueEdit.Description(DoubleToString(m_riskPercent, 2));

   m_equityLabel.Create(0, "equityLabel", 0, m_baseX + 120, m_baseY + 107);
   m_equityLabel.Font("Verdana");
   m_equityLabel.FontSize(8);
   m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2));

   m_maxEquityLossLabel.Create(0, "maxEquityLossLabel", 0, m_baseX + 120, m_baseY + 122);
   m_maxEquityLossLabel.Font("Verdana");
   m_maxEquityLossLabel.FontSize(8);
   m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*m_riskPercent/100.0,2));

   m_askLabel.Create(0, "askLabel", 0, m_baseX + 120, m_baseY + 145);
   m_askLabel.Font("Verdana");
   m_askLabel.FontSize(8);
   m_askLabel.Description("");

   m_bidLabel.Create(0, "bidLabel", 0, m_baseX + 120, m_baseY + 160);
   m_bidLabel.Font("Verdana");
   m_bidLabel.FontSize(8);
   m_bidLabel.Description("");

   m_slLabel.Create(0, "slLabel", 0, m_baseX + 120, m_baseY + 176);
   m_slLabel.Font("Verdana");
   m_slLabel.FontSize(8);
   m_slLabel.Description("");

   m_tpLabel.Create(0, "tpLabel", 0, m_baseX + 120, m_baseY + 191);
   m_tpLabel.Font("Verdana");
   m_tpLabel.FontSize(8);
   m_tpLabel.Description("");

   m_maxAllowedLotsLabel.Create(0, "maxAllowedLotsLabel", 0, m_baseX + 120, m_baseY + 208);
   m_maxAllowedLotsLabel.Font("Verdana");
   m_maxAllowedLotsLabel.FontSize(8);
   m_maxAllowedLotsLabel.Description("");

   m_maxTicksLossLabel.Create(0, "maxTicksLossLabel", 0, m_baseX + 120, m_baseY + 223);
   m_maxTicksLossLabel.Font("Verdana");
   m_maxTicksLossLabel.FontSize(8);
   m_maxTicksLossLabel.Description("");

   m_orderLotsEdit.Create(0, "orderLotsEdit", 0, m_baseX + 120, m_baseY + 238, 35, 15);
   m_orderLotsEdit.Font("Verdana");
   m_orderLotsEdit.FontSize(8);
   m_orderLotsEdit.Description("");

   m_orderEquityRiskLabel.Create(0, "orderEquityRiskLabel", 0, m_baseX + 120, m_baseY + 255);
   m_orderEquityRiskLabel.Font("Verdana");
   m_orderEquityRiskLabel.FontSize(8);
   m_orderEquityRiskLabel.Description("");

   m_orderEquityRewardLabel.Create(0, "orderEquityRewardLabel", 0, m_baseX + 120, m_baseY + 270);
   m_orderEquityRewardLabel.Font("Verdana");
   m_orderEquityRewardLabel.FontSize(8);
   m_orderEquityRewardLabel.Description("");

   m_switchOrderTypeButton.Create(0, "switchOrderTypeButton", 0, m_baseX + 20, m_baseY + 314, 160, 20);
   m_switchOrderTypeButton.Font("Verdana");
   m_switchOrderTypeButton.FontSize(8);
   m_switchOrderTypeButton.BackColor(LightBlue);

   m_placeOrderButton.Create(0, "placeOrderButton", 0, m_baseX + 20, m_baseY + 334, 160, 20);
   m_placeOrderButton.Font("Verdana");
   m_placeOrderButton.FontSize(8);
   m_placeOrderButton.BackColor(LightBlue);
   m_placeOrderButton.Description("Place Market Order");

   m_quitEAButton.Create(0, "quitEAButton", 0, m_baseX + 20, m_baseY + 354, 160, 20);
   m_quitEAButton.Font("Verdana");
   m_quitEAButton.FontSize(8);
   m_quitEAButton.BackColor(LightBlue);
   m_quitEAButton.Description("Quit");

   return isCreated;
  }

Refresh() 메소드는 CRRDialog 변수 및 현재 매수/매도 수준, 계정 자산 및 주식 위험 값으로 모든 레이블 및 버튼 설명을 새로 고칩니다. 

void CRRDialog::Refresh()
  {
   MqlTick current_tick;
   SymbolInfoTick(Symbol(),current_tick);

   m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2));
   m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*
                                         StringToDouble(m_riskValueEdit.Description())/100.0,2));
   m_askLabel.Description(DoubleToString(current_tick.ask, Digits()));
   m_bidLabel.Description(DoubleToString(current_tick.bid, Digits()));
   m_slLabel.Description(DoubleToString(m_SL, Digits()));
   m_tpLabel.Description(DoubleToString(m_TP, Digits()));
   m_maxAllowedLotsLabel.Description(DoubleToString(m_maxAllowedLots,2));
   m_maxTicksLossLabel.Description(DoubleToString(m_maxTicksLoss,0));
   m_orderEquityRiskLabel.Description(DoubleToString(m_orderEquityRisk,2));
   m_orderEquityRewardLabel.Description(DoubleToString(m_orderEquityReward,2));

   if(m_orderType==ORDER_TYPE_BUY) m_switchOrderTypeButton.Description("Order Type: BUY");
   else if(m_orderType==ORDER_TYPE_SELL) m_switchOrderTypeButton.Description("Order Type: SELL");
  }


4. 차트 이벤트

EA는 대화형으로 설계되었으므로 차트 이벤트를 처리해야 합니다.

처리되는 이벤트는 다음과 같습니다.

  • 차트에서 S/L 포인터 (CChartObjectBitmap 클래스의 SL_arrow 개체)를 드래그하면 S/L 레벨을 수집하고 R/R 비율에 따라 T/P 레벨을 계산할 수 있습니다.
  • 오더 유형 전환 (매수/매도) 버튼
  • '시장 주문' 버튼 누르기
  • 위험, R/R 및 주문 랏 필드 편집
  • '종료' 버튼을 누른 후 EA 종료

처리되는 이벤트는 포인터 선택 및 버튼의 경우 CHARTEVENT_OBJECT_CLICK , S/L 포인터 드래그의 경우 CHARTEVENT_OBJECT_DRAG되었고 트레이더에 의해 수정 필드를 업데이트 한 후에는 CHARTEVENT_OBJECT_ENDEDIT가 되었습니다.

처음에 OnChartEvent() 함수를 구현하려면 몇 페이지의 코드가 필요했지만 여러 이벤트 핸들러로 분할하기로 결정했습니다. 이로 인해 OnChartEvent() 함수가 사람이 읽을 수 있는 형태로 변환되었습니다. 

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Check the event by pressing a mouse button
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      string clickedChartObject=sparam;

      if(clickedChartObject==slButtonID)
         SL_arrow.Selected(!SL_arrow.Selected());

      if(clickedChartObject==switchOrderTypeButtonID)
        {
         EA_switchOrderType();
        };

      if(clickedChartObject==placeOrderButtonID)
        {
         EA_placeOrder();
        }

      if(clickedChartObject==quitEAButtonID) ExpertRemove();

      ChartRedraw();
     }

   if(id==CHARTEVENT_OBJECT_DRAG)
     {
      // BUY 
      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
        {
         EA_dragBuyHandle();
        };

      // SELL
      if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
        {
         EA_dragSellHandle();
        };
      ChartRedraw();
     }

   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      if((sparam==riskRatioEditID || sparam==riskValueEditID || sparam==orderLotsEditID) && orderPlaced==false)
        {
         EA_editParamsUpdate();
        }
     }
  }

이벤트 핸들러 구현은 다음 섹션에서 자세히 설명합니다. 주목할 만한 것은 SL_arrow 객체를 선택하는 데 사용한 트릭입니다. 일반적으로 차트에서 개체를 선택하려면 두 번 클릭해야 합니다. 그러나 CHARTEVENT_OBJECT_CLICK 이벤트 핸들러 내의 OnChartEvent() 기능에서 CChartObject 개체 또는 자손의 Selected() 메소드를 한 번 클릭하고 호출하여 선택할 수 있습니다.

      if(clickedChartObject==slButtonID)
         SL_arrow.Selected(!SL_arrow.Selected());

한 번의 클릭 후 이전 상태에 따라 개체가 선택되거나 선택 취소됩니다. 


5. CMoneyFixedRisk를 기반으로 하는 확장된 자금 관리 클래스

ChartEvent 핸들러를 설명하기 전에 자금 관리 클래스를 거쳐야 합니다.

자금 관리를 위해 MetaQuotes에서 제공하는 CMoneyFixedRisk 클래스를 재사용하고 CMoneyFixedRiskExt 클래스를 구현했습니다.

원래 CMoneyFixedRisk 클래스 메소드는 주어진 가격, 손절매 수준 및 브로커가 허용하는 최소 및 최대 랏 크기 사이의 주식 위험에 대해 허용된 주문 랏 금액을 반환합니다. 위험 요구 사항이 충족되지 않으면 CheckOpenLong() 및 CheckOpenShort() 메소드를 변경하여 0.0 랏 크기를 반환하고 GetMaxSLPossible(), CalcMaxTicksLoss(), CalcOrderEquityRisk() 및 CalcOrderEquityReward()의 네 가지 메소드로 확장했습니다.

class CMoneyFixedRiskExt : public CExpertMoney
  {
public:
   //---
   virtual double    CheckOpenLong(double price,double sl);
   virtual double    CheckOpenShort(double price,double sl);
   
   double GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType);
   double CalcMaxTicksLoss();
   double CalcOrderEquityRisk(double price, double sl, double lots);
   double CalcOrderEquityReward(double price, double sl, double lots, double rrratio);
  };

GetMaxSLPossible() 메소드는 주어진 주식 위험 및 최소 허용 거래 크기에 대한 최대 손절매 가격 값을 계산합니다.

예를 들어 계좌 잔고가 계좌 기준 통화 10,000 개이고 리스크가 2 % 인 경우 최대 200 개의 계좌 통화를 위태롭게 할 수 있습니다. 최소 거래 랏 크기가 0.1 랏 인 경우이 메소드는 ORDER_TYPE_BUY 또는 ORDER_TYPE_SELL 주문에 대한 가격 수준을 반환합니다. 0.1 lot의 포지션에 대한 자기 자본 리스크 가치를 충족합니다. 이는 최소 랏 크기 거래를 위해 감당할 수있는 최대 손절매 수준을 추정하는 데 도움이 됩니다. 이것은 주어진 주식 위험 수준에 대해 우리가 교차 할 수 없는 가격 수준입니다. 

double CMoneyFixedRiskExt::GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType)
{
   double maxEquityLoss, tickValLoss, maxTicksLoss;
   double minvol=m_symbol.LotsMin();
   double orderTypeMultiplier;
   
   if(m_symbol==NULL) return(0.0);
   
   switch (orderType)
   {
   case ORDER_TYPE_SELL: orderTypeMultiplier = -1.0; break;
   case ORDER_TYPE_BUY: orderTypeMultiplier = 1.0; break;
   default: orderTypeMultiplier = 0.0;
   }
   
   maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss 
   tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss
   maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss);
 
   return (price - maxTicksLoss*m_symbol.TickSize()*orderTypeMultiplier);
}

CalcMaxTickLoss() 메소드는 주어진 위험과 최소 허용 랏 크기에 대해 느슨하게 감당할 수있는 최대 틱 수를 반환합니다.

처음에 최대 자기 자본 손실은 현재 잔액의 백분율로 계산된 다음 주어진 기호에 대해 허용되는 최소 랏 크기에 대해 한 틱 변경에 대한 틱 값 손실이 계산됩니다. 그런 다음 최대 자기 자본 손실을 틱 가치 손실로 나눈 결과는 다음과 같습니다. MathFloor() 함수를 사용하여 정수 값으로 반올림합니다.

double CMoneyFixedRiskExt::CalcMaxTicksLoss()
{
   double maxEquityLoss, tickValLoss, maxTicksLoss;
   double minvol=m_symbol.LotsMin();
   
   if(m_symbol==NULL) return(0.0);
   
   maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss 
   tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss
   maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss);
   
   return (maxTicksLoss);
}

CalcOrderEquityRisk() 메소드는 주어진 가격, 손절매 수준 및 랏 양에 대한 주식 위험을 반환합니다. 틱 손실 값에 랏 수와 가격을 곱한 다음 현재 가격과 손절매 수준의 차이를 곱하여 계산됩니다.

double CMoneyFixedRiskExt::CalcOrderEquityRisk(double price,double sl, double lots)
{
   double equityRisk;
   
   equityRisk = lots*m_symbol.TickValueLoss()*(MathAbs(price-sl)/m_symbol.TickSize()); 
   
   if (dbg) Print("calcEquityRisk: lots = " + DoubleToString(lots) +
                 " TickValueLoss = " + DoubleToString(m_symbol.TickValueLoss()) +
                 " risk = " + DoubleToString(equityRisk));
   
   return equityRisk;
}

 CalcOrderEquityReward() 메소드는 CalcOrderEquityRisk() 메소드와 유사하지만 TickValueLoss() 메소드 대신 TickValueProfit()을 사용하며 그 결과에 주어진 위험 대 보상 비율이 곱해집니다.  

double CMoneyFixedRiskExt::CalcOrderEquityReward(double price,double sl, double lots, double rrratio)
{
   double equityReward; 
   equityReward = lots*m_symbol.TickValueProfit()*(MathAbs(price-sl)/m_symbol.TickSize())*rrratio; 
   
   if (dbg) Print("calcEquityReward: lots = " + DoubleToString(lots) + 
                   " TickValueProfit = " + DoubleToString(m_symbol.TickValueProfit()) +
                 " reward = " + DoubleToString(equityReward));
   return equityReward;
}

이러한 방법은 최대 손절매 수준을 계산하고 실시간 주식 위험 및 보상을 반환하는 데 충분합니다. CalcMaxTickLoss() 메소드는 위험 사각형 그리기를 수정하는 데 사용됩니다. 거래자가 느슨하게 감당할 수 있는 틱 수의 경계를 넘는 거래를 원하는 경우 사각형은 그가 잃을 수 있는 최대 틱 수까지만 그려집니다.

그것은 삶을 차트에서 직접 쉽게 볼 수 있게 해줍니다. 글 끝의 데모에서 볼 수 있습니다.


6. 차트 이벤트 핸들러 구현

EA_switchOrderType() 핸들러는 m_switchOrderTypeButton 개체에서 CHARTEVENT_OBJECT_CLICK 이벤트를 수신한 후 트리거 됩니다. ORDER_TYPE_BUY 와 ORDER_TYPE_SELL 사이에서 주문 유형을 전환하고, 버튼 상태를 재설정하고, 대화 상자의 변수를 재설정합니다. 차트에서 위험 및 보상 사각형 개체를 삭제합니다.

void EA_switchOrderType()
  {
   symbolInfo.RefreshRates();

   visualRRDialog.SwitchOrderType();
   visualRRDialog.ResetButtons();
   visualRRDialog.SetSL(0.0);
   visualRRDialog.SetTP(0.0);
   visualRRDialog.SetMaxAllowedLots(0.0);
   visualRRDialog.SetOrderLots(0.0);
   visualRRDialog.SetMaxTicksLoss(0);
   visualRRDialog.SetOrderEquityRisk(0.0);
   visualRRDialog.SetOrderEquityReward(0.0);

   if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask());
   else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid());
   SL_arrow.SetInteger(OBJPROP_TIME,0,TimeCurrent());

   rectReward.Delete();
   rectRisk.Delete();

   visualRRDialog.Refresh();

  }

EA_dragBuyHandle() 핸들러는 SL_arrow 개체를 차트에 끌어서 놓은 후 트리거됩니다. 처음에는 차트에서 SL_arrow 객체 드롭 포인트 시간과 가격 매개 변수를 읽고 가격 수준을 우리 거래에 대한 가상의 손절매로 설정합니다.

그런 다음 주식에 대한 주어진 위험에 대해 얼마나 많은 랏을 열 수 있는지 계산합니다. 손절매 값이 해당 심볼에서 가능한 가장 낮은 거래 랏에 대한 위험 목표를 보장 할 수 없는 경우 가능한 최대 SL 레벨로 자동 이동됩니다. 이는 주어진 위험에 대한 손절매 공간을 평가하는 데 도움이 됩니다.

위험과 보상을 계산한 후 직사각형 개체가 차트에서 업데이트됩니다.

void EA_dragBuyHandle()
  {
   SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

// BUY
   double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);
   Print("Allowed lots = "+DoubleToString(allowedLots,2));
   double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY);

   if(SL_price<lowestSLAllowed)
     {
      SL_price=lowestSLAllowed;
      ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());

   if(visualRRDialog.GetTP()<SL_price)
     {
      visualRRDialog.SetSL(0.0);
      visualRRDialog.SetTP(0.0);
      SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask());
      rectReward.Delete();
      rectRisk.Delete();
      return;
     }

   double lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);

   visualRRDialog.SetMaxAllowedLots(lotSize);
   visualRRDialog.SetOrderLots(lotSize);
   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, lotSize));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), 
                                       SL_price, lotSize, visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();

   rectUpdate(visualRRDialog.GetOrderType());

  }

EA_dragSellHandle()은 판매 주문 구성을 위해 트리거됩니다.

계산은 symbolInfo.Bid() 가격을 기반으로 하며 이에 따라 사각형이 그려집니다. 즉, 이익이 현재 가격 수준보다 낮음을 표시하는 녹색 영역입니다. 

void EA_dragSellHandle()
  {
   SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

   double allowedLots=MM.CheckOpenShort(symbolInfo.Bid(),SL_price);
   Print("Allowed lots = "+DoubleToString(allowedLots,2));
   double maxSLAllowed=MM.GetMaxSLPossible(symbolInfo.Bid(),ORDER_TYPE_SELL);

   if(SL_price>maxSLAllowed)
     {
      SL_price=maxSLAllowed;
      SL_arrow.SetDouble(OBJPROP_PRICE,0,maxSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());

   if(visualRRDialog.GetTP()>SL_price)
     {
      visualRRDialog.SetSL(0.0);
      visualRRDialog.SetTP(0.0);
      SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid());
      rectReward.Delete();
      rectRisk.Delete();
      return;
     }

   double lotSize=MM.CheckOpenShort(symbolInfo.Bid(),SL_price);

   visualRRDialog.SetMaxAllowedLots(lotSize);
   visualRRDialog.SetOrderLots(lotSize);
   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Bid(), SL_price, lotSize));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(),
                                       SL_price, lotSize, visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();

   rectUpdate(visualRRDialog.GetOrderType());

  }

EA_placeOrder()는 m_placeOrderButton 객체가 눌러진 후에 트리거됩니다. 계산된 SL 및 TP 수준과 지정된 랏 크기에 대해 매수 또는 매도 시장 주문을 합니다.

CExpertTrade 클래스를 사용하여 시장 주문을 하는 것이 얼마나 쉬운 지에 대해 주목해주세요. 

bool EA_placeOrder()
  {
   symbolInfo.RefreshRates();
   visualRRDialog.ResetButtons();

   if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
      orderPlaced=trade.Buy(visualRRDialog.GetOrderLots(),symbolInfo.Ask(),
                            visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent()));
   else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
      orderPlaced=trade.Sell(visualRRDialog.GetOrderLots(),symbolInfo.Bid(),
                            visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent()));

   return orderPlaced;
  }

EA_editParamsUpdate() 핸들러는 수정 필드 (riskRatioEdit, riskValueEdit 및 orderLotsEdit) 중 하나를 수정 한 후 Enter 키를 누르면 트리거됩니다.

이 경우 허용 된 랏 크기, TP 수준, 최대 틱 손실, 주식 위험 및 보상을 다시 계산해야 합니다.

void EA_editParamsUpdate()
  {
   MM.Percent(visualRRDialog.GetRiskPercent());

   SL_arrow.GetDouble(OBJPROP_PRICE, 0, SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME, 0, startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

   double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);

   double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY);
   if(SL_price<lowestSLAllowed)
     {
      SL_price=lowestSLAllowed;
      ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());

   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), 
                                     SL_price, visualRRDialog.GetOrderLots()));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, 
                                       visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();
   rectUpdate(visualRRDialog.GetOrderType());

   ChartRedraw();
  }

EA_onTick()은 새로운 틱이 도착할 때마다 호출됩니다. 계산은 아직 주문이 이루어지지 않았고 SL_arrow 포인터를 드래그하여 손절매 레벨을 이미 선택한 경우에만 수행됩니다.

주문이 완료되면 리스크와 보상, TP 레벨은 물론 리스크와 보상의 재도출이 필요하지 않습니다. 

void EA_onTick()
  {
   if(SL_price!=0.0 && orderPlaced==false)
     {
      double lotSize=0.0;
      SL_price=visualRRDialog.GetSL();
      symbolInfo.RefreshRates();

      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
         lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);
      else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
         lotSize=MM.CheckOpenShort(symbolInfo.Ask(),SL_price);

      visualRRDialog.SetMaxAllowedLots(lotSize);
      if(visualRRDialog.GetOrderLots()>lotSize) visualRRDialog.SetOrderLots(lotSize);

      visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());

      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
        {
         visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());
         visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), 
                                           SL_price, visualRRDialog.GetOrderLots()));
         visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, 
                                             visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
        }
      else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
        {
         visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());
         visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(
                                           symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots()));
         visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, 
                                             visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
        }
      visualRRDialog.Refresh();
      rectUpdate(visualRRDialog.GetOrderType());
     }

   ChartRedraw(0);
  }

rectUpdate() 함수는 색상 위험 및 보상 사각형을 다시 그리는 역할을 합니다. 제어점은 SL_arrow 개체 시작 시간, 주문 유형에 따른 현재 매도 또는 매도 가격 값, SL 및 TP 수준입니다. 연 분홍색 사각형은 현재가와 SL 등급 사이의 가격대를 나타내고 연한 녹색 사각형은 현재가와 TP 등급 간의 가격대를 보여줍니다.

두 직사각형 모두 SL 및 TP 가격 수준에 대한 위험 대 보상 비율 영향을 관찰하고 거래에 들어가기 전에 위험을 조정하는 데 도움이 되는 훌륭한 도구입니다.

void rectUpdate(ENUM_ORDER_TYPE orderType)
  {
   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   if(orderType==ORDER_TYPE_BUY)
     {
      rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Ask(),currentTime,symbolInfo.Ask()+
                       (symbolInfo.Ask()-visualRRDialog.GetSL())*visualRRDialog.GetRRRRatio());
      rectReward.Color(LightGreen);
      rectReward.Background(true);

      rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Ask());
      rectRisk.Color(LightPink);
      rectRisk.Background(true);
     }
   else if(orderType==ORDER_TYPE_SELL)
     {
      rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Bid(),currentTime,symbolInfo.Bid()-
                        (visualRRDialog.GetSL()-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());
      rectReward.Color(LightGreen);
      rectReward.Background(true);

      rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Bid());
      rectRisk.Color(LightPink);
      rectRisk.Background(true);
     }
  }

 

7. 데모

작동 중인 Expert Advisor의 데모를 아래에서 관찰하십시오. 2010년 1월 11일 월요일 시장이 개장 한 직후 큰 반등 후 매도 주문을 하고 있습니다.

최상의 시청 환경을 위해 비디오를 전체 화면으로 설정하고 화질을 480p로 설정하십시오. 동영상에 댓글이 포함되어 있습니다.

 

결론

다음 글에서는 사전 정의된 위험 및 위험 대 보상 비율을 기반으로 수동 거래를 위한 대화 형 Expert Advisor를 구축하는 방법을 제시했습니다.

차트에 콘텐츠를 표시하기 위해 표준 클래스를 사용하는 방법과 새 데이터를 입력하고 끌어서 놓기 개체를 처리하기 위해 차트 이벤트를 처리하는 방법을 보여주었습니다. 내가 제시한 아이디어가 MQL5에서 다른 구성 가능한 시각적 도구를 구축하는 기초가 되기를 바랍니다.

모든 소스 파일과 비트 맵이 글에 첨부됩니다.

MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/192

파일 첨부됨 |
visualrrea.mq5 (13.84 KB)
crrdialog.mqh (13.95 KB)
visualrrids.mqh (0.8 KB)
images.zip (159.05 KB)
CChartObject 클래스 기반의 새로운 GUI 위젯 설계 및 구현 CChartObject 클래스 기반의 새로운 GUI 위젯 설계 및 구현
GUI 인터페이스가 있는 반자동 Expert Advisor에 대한 이전 글을 작성한 후 더 복잡한 지표와 Expert Advisors를 위한 몇 가지 새로운 기능으로 인터페이스를 향상시키는 것이 바람직하다는 것이 밝혀졌습니다. MQL5 표준 라이브러리 클래스에 익숙해 진 후 새로운 위젯을 구현했습니다. 이 글에서는 표시기 및 Expert Advisor에서 사용할 수 있는 새로운 MQL5 GUI 위젯을 설계하고 구현하는 프로세스를 설명합니다. 글에 제시된 위젯은 CChartObjectSpinner, CChartObjectProgressBar 및 CChartObjectEditTable입니다.
MQL5 트레이딩 시스템과 윌리엄 블라우의 인디케이터. 파트 1: 인디케이터 MQL5 트레이딩 시스템과 윌리엄 블라우의 인디케이터. 파트 1: 인디케이터
이 글은 윌리엄 블라우의 저서 'Momentum, Direction, and Divergence'에서 다루어진 인디케이터에 대한 소개입니다. 윌리엄 블라우의 접근법을 이용하면 수요 곡선 상의 변동을 빠르고 정확하게 계산할 수 있으며, 가격 변동 추세와 전환점을 판단하고, 노이즈를 제거할 수 있습니다. 이와 동시에 시장의 과매수/과매도 상태와 추세의 끝과 가격 움직임의 반전을 나타내는 신호를 감지할 수도 있죠.
MetaTrader 5의 병렬 계산 MetaTrader 5의 병렬 계산
시간은 인류 내역을 통틀어 큰 가치로 여겨져 왔으며, 불필요하게 낭비하지 않도록 노력하고 있습니다. 이 글에서는 컴퓨터에 멀티 코어 프로세서가 있는 경우 Expert Advisor의 작업을 가속화하는 방법에 대해 설명합니다. 또한 제안된 방법의 구현에는 MQL5 외에 다른 언어에 대한 지식이 필요하지 않습니다.
스펙트럼 분석기 구축 스펙트럼 분석기 구축
이 글은 독자들이 MQL5 언어의 그래픽 객체를 사용하는 가능한 변형에 대해 알게 하기 위한 것입니다. 그래픽 개체를 사용하여 간단한 스펙트럼 분석기를 관리하는 패널을 구현하는 표시기를 분석합니다. 이 기사는 MQL5의 기본 사항에 대해 잘 알고 있는 독자를 대상으로 합니다.