English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
Expert Adviso 개발 기초부터(22부): 새로운 주문 시스템(V)

Expert Adviso 개발 기초부터(22부): 새로운 주문 시스템(V)

MetaTrader 5트레이딩 | 3 11월 2023, 10:05
299 0
Daniel Jose
Daniel Jose

소개

새로운 시스템을 구현하는 것은 그리 쉬운 일이 아닙니다. 프로세스를 복잡하게 만드는 문제가 종종 발생하기 때문입니다. 이러한 문제가 나타나면 우리는 개발 방향을 멈추고 다시 분석하여 그대로 두어도 되는지, 아니면 새롭게 바꿔야 하는지 결정해야 합니다.

특히 마감일이나 예산이 명확하지 않은 경우 이러한 결정이 자주 내려질 수 있습니다. 압박감이 없을 때는 개발 및 개선 측면에서 안정성을 보장하기 위해 최선을 다해 테스트하고 조정할 수 있습니다.

중요한 프로그램, 특히 특정 예산이나 기한에 따라 개발된 상업용 프로그램에는 일반적으로 나중에 수정해야 하는 오류가 많이 포함되어 있습니다. 시스템을 개발하는 사람들은 종종 시스템에 유용한 솔루션을 구현하거나 배울 시간이 없습니다. 프로그래머가 만들 수 있는 수준에는 미치지 못하는 프로그램이 대부분 이런 방식으로 만들어집니다. 회사에서 사소한 개선 사항이나 버그 수정이 포함된 버전을 연이어 출시하는 경우가 있습니다. 이는 프로그램 출시 후 버그가 발견되었기 때문이 아니라 출시 전에 프로그래머들이 압박을 받았기 때문입니다.

이는 실제로 전체 생산 과정에서 발생합니다. 그러나 솔루션을 개발하는 과정에서 저희는 최선의 방법, 가급적 가장 쉬운 방법을 찾고 있습니다. 또한 실현 가능한 모든 솔루션을 모색할 수 있는 여유가 있습니다. 필요한 경우 시스템 개발 프로세스를 수정하고 개선하기 위해 잠시 멈추고 돌아갈 수 있습니다. 종종 작은 멈춤과 방향 전환이 원하는 시스템을 개발하는 데 큰 도움이 될 수 있습니다.

트레이딩 시스템 처음부터 개발하기(21부) 에서는 시스템이 거의 완성된 상태였습니다. 이 시스템은 차트에서 주문을 이동하는 기능이 부족했습니다. 이 부분을 구현하기 위해 코드에서 몇 가지 이상한 점을 발견했습니다: 반복되는 부분이 많다는 것이었습니다. 불필요한 반복을 피하기 위해 매우 신중을 기했습니다. 더 큰 문제는 다른 에셋을 사용할 때 일부 기능이 정상적으로 작동하지 않는다는 점이었습니다. EA를 설계하기 시작했을 때, 그리고 여기에 게시된 글에서 이를 문서화하기 시작했을 때, 저는 주로 B3 거래소에서 선물 거래에 이 시스템을 사용할 생각이었습니다. 사실 저는 달러 선물 또는 WDO와 WIN이라는 지수를 거래하기 위해 이 시스템을 개발한 것이었습니다. 하지만 제가 원래 사용하고자 했던 시스템을 완성하면서 다른 시장이나 자산에도 이 시스템을 사용할 수 있다는 것을 깨달았습니다. 바로 여기서 문제가 발생했습니다.


1.0. 클립보드로 돌아가기

문제를 이해하려면 많은 사람들이 Expert Advisor를 개발할 때 간과하는 중요한 세부 사항 한 가지에 주목하세요. 트레이딩 시스템에서 계약은 어떻게 정의되나요? MetaTrader 5는 이에 대한 정보를 제공하며 MQL5는 이 정보에 액세스할 수 있습니다. 따라서 계산을 일반화하고 가장 완벽한 시스템을 얻기 위한 수학식을 만들려면 데이터를 이해해야 합니다.

현재 개발 중인 주문 시스템은 다른 외부 리소스나 향후 생성될 수 있는 어떤 것도 필요 없이 차트에서 직접 거래할 수 있도록 설계되었습니다. 이 시스템 개발 과정은 플랫폼에서 사용되는 것과 매우 유사한 거래 시스템을 만들려는 시도이지만 완전히 오픈소스로 제공되므로 필요한 정보를 여러분이 지정하고 추가할 수 있습니다.

이 아이디어는 사용자가 포지션을 보는 것만으로도 해당 포지션에 어떤 일이 일어나고 있는지 알 수 있도록 하는 것입니다. 아이디어는 매우 좋은 것 같지만 거래 시스템은 B3와 같은 외환 및 주식 시장을 위한 시스템을 만들려는 사람들에게는 그리 쉽지 않습니다. 사용 가능한 정보 수준은 하나의 시장 또는 다른 시장에 대한 주문을 커스터마이징 하기에 충분하지만 일반적인 것을 만드는 것은 거의 개인적인 모욕이 될 정도로 문제가 많았습니다. 하지만 저는 문제를 직시하고 보편적인 시스템을 만들기로 결심했습니다.

실제로 가능할지는 모르겠지만 적어도 제가 여러분에게 필요한 정보를 찾는 방법을 보여드릴 수 있으므로 로컬 시스템에 맞게 EA를 커스터마이징 해야 하는 경우 여러분은 그 방법에 대한 경험과 지식을 얻을 수 있습니다. 시스템에서 원래 모델링을 처리할 수 없더라도 여러분은 필요에 따라 조정할 수 있습니다.

참고: 데이터는 MQL5 코드를 사용하여 얻을 수 있지만 작업을 단순화하기 위해 MetaTrader 5를 사용하여 데이터의 위치를 표시하겠습니다.


1.0.1. B3에서 거래되는 회사 자산(주식)

다음 두 이미지를 살펴보세요:

     

강조 표시된 부분은 포지션이 어떻게 배치될지 계산하는 데 필요한 데이터를 보여줍니다.

재무적 가치를 기준으로 포지션을 생성하는 경우 손절매 및 이익실현 값을 계산하려면 이 값이 필요합니다. OCO 주문을 사용하지 않는다면 더 쉬워지겠지만 여기서는 모든 주문이 OCO 주문 또는 포지션이라고 가정합니다.

중요: 자산이 부분 주식으로 거래되는 경우(B3의 시장마다 차이가 있음) 최소 볼륨은 지정된 값의 1%가 되므로 100 대 100이 아닌 1 대 1로 거래하게 됩니다. 계산이 달라지므로 잊지 마세요.


1.0.2. B3 선물 계약

선물 계약의 규칙은 주식 거래 규칙과 다르며 전체 계약으로 거래하는지 미니 계약으로 거래하는지에 따라 볼륨이 달라지고 자산별로도 볼륨이 달라집니다. 예를 들어 BOI를 거래하려면 레버리지(승수)가 어떻게 채워지는지 살펴봐야 하는데 우리는 여기서 레버리지가 어떻게 채워지는지 살펴볼 것입니다. 이제 계약에 대해 자세히 알아 보겠습니다. 경우에 따라 전체 계약은 25개의 미니 계약과 같지만 이는 다를 수 있으므로 항상 거래소의 볼륨 규정을 확인해야 합니다.

이제 미니 달러의 다음 이미지를 살펴보겠습니다:

     

이러한 계약에는 만료일이 있지만 이 기사에서는 Expert Advisor 개발 기초부터 (11 부)에서 직접 선물 계약을 거래하는 교차 주문 시스템을 만드는 방법을 알아 보았습니다. 그러므로 이 기사에서는 중요하지 않으며 이 방법을 사용하면 현재 어떤 계약이 거래되고 있는지 걱정할 필요가 없습니다. EA가 자동으로 수행하기 때문입니다. 표시된 부분들은 주식 시스템과 다르므로 주의하세요. 이 부분은 비록 직관적이지는 않지만 때때로 문제를 발생시킬 수 있습니다. 그러나 EA는 이 문제를 처리하고 있습니다. 어쨌든 시간이 지남에 따라 여러분들은 특정 볼륨을 거래하기 위해 레버리지에 사용해야 하는 값에 익숙해 질 것입니다.

하지만 저는 더 높은 목표를 세우기로 했습니다. 여기서부터 어려움이 시작됩니다.


1.0.3. FOREX

저의 문제는 외환 시스템과 관련된 문제였는데 EA가 이 문제를 해결하지 못했습니다. 제가 외환 레버리지 문제를 처리하게 되었을 때 B3 EA의 동일한 코드를 사용할 수 없다는 것을 알았습니다. 이 부분에만 두 개의 다른 EA가 있어야 하는 이유를 알 수 없었고 이 문제는 상당히 짜증나는 문제였습니다. 무슨 일이 일어나고 있는지 이해하려면 다음 이미지를 살펴보세요:

     

B3에서는 4개의 값이 있는 반면 Forex에는 2개의 값만 있습니다. 두 개의 누락된 값으로 인해 두 시장 간의 계산이 일치하지 않습니다.

이로 인해 EA가 계산한 값이 너무 높거나 승수가 최소 예상 볼륨에 비해 너무 작아 거래 시스템이 주문을 거부하거나 잘못된 포지션으로 인해 OCO 한도 포인트가 허용되지 않는 등 실제로 수행되는 작업을 이해하기가 매우 어려웠습니다. 그래서 끝없는 혼란이 있었습니다.

저는 이를 알게 된 후 EA를 수정하기로 결정했습니다. 우리는 이전 방식으로 값을 계산하지 않고 다른 방법을 사용할 것입니다. 실제로 이 값은 이제 주식 시장에서 거래하든 외환 시장에서 거래하든 상관없이 거래하는 자산에 따라 조정됩니다. 따라서 EA는 데이터에 적응하고 계산을 올바르게 수행할 것입니다. 하지만 승수를 올바르게 설정하려면 거래하는 상품이 무엇인지 알아야 합니다. 지금은 계산상의 이유로 볼륨에 부동 소수점 값을 사용하지만 이렇게 하지 말아야 합니다. 실제로 사용 중인 레버링을 나타내는 정수 값을 사용해야 합니다.

따라서 우리는 EA에게 우리가 얼마나 거래하고자 하는지를 알리지 않습니다. 대신 최소 허용 볼륨에 적용할 승수를 지정합니다. 이것이 제가 문제를 해결한 방법입니다. 여러분들이 직접 테스트하고 작동 방식을 직접 확인할 수 있습니다.

보다 쉽게 이해하고 작업을 줄이기 위해 다음 주제에서는 결과를 간략하게 설명하도록 하겠습니다.


2.0. 데이터 시각화

먼저 주식 시장 특히 브라질 주식 시장(B3)의 경우를 살펴봅시다. 이것이 5.에서 EA에 데이터가 표시되는 방식입니다.


2.0.1. B3 자산

브라질 증권거래소에 상장된 회사 주식의 경우 최소 볼륨은 100입니다. 1을 표시해서 EA가 최소 허용 볼륨을 거래하도록 설정합니다. 이렇게 하면 볼륨을 처리하기 훨씬 쉬워집니다 - 최소 랏을 정확히 알 필요가 없으며 승수를 사용하기 만하면 EA가 에러 없는 주문을 생성하는 데 필요한 계산을 수행합니다.

분수 값을 사용하는 경우 사용할 분수의 개수 즉 50은 50개의 분수를 사용한다는 의미이며 15를 지정하면 15개의 분수가 사용되는 식으로 표시됩니다.

결과는 MetaTrader 5 도구 상자 창에 아래와 같이 표시되며 최소 랏 값을 가진 주문이 어떻게 생성되었는지 보여줍니다. 테이크 및 스톱 값을 분석하면 차트에 표시된 값과 일치하는 것을 볼 수 있으며 이는 EA가 여기에서 작동했음을 의미합니다.


2.0.2. 미니 달러

여기서 볼륨은 1 대 1이지만 전체 계약의 경우 값이 다릅니다. 차트 트레이딩을 살펴보면 테이크와 스톱으로 표시된 값이 위에 표시된 값과 동일합니다. 그러나 EA는 값을 정확하게 조정합니다. 따라서 우리가 고려해야 할 값은 차트 지표에서 찾은 값이며 차트 트레이드 값들은 무시해야 하지만 이 값들은 차트에 표시된 값에 가깝습니다.


도구 상자에 표시되는 데이터는 다음과 같습니다:

이전 자산에서와 마찬가지로 손절매 및 이익실현 레벨을 확인하기 위해 계산을 수행해 보면 이들 레벨이 EA가 지정한 레벨과 일치하는 것을 볼 수 있습니다. 즉 EA는 코드를 다시 컴파일 할 필요없이 자산을 변경하는 것만으로 이 단계를 통과했습니다.


2.0.3. 외환

이 부분은 조금 더 복잡합니다. 외환에 익숙하지 않은 사람들에게는 포인트가 다소 이상하지만 여전히 EA가이를 파악할 수 있습니다.


MetaTrader 5는 펜딩 중인 거래에 대해 다음과 같이 알려줍니다:


외환 레버리지 레벨은 위에 표시된 것과 다르다는 점을 기억하세요. 그러나 계산을 수행해 보면 EA가 정확한 포인트를 제공하고 주문은 완벽하게 생성되었다는 것을 알 수 있습니다.

이 모든 것은 제가 구현 부분에서 보여줄 것을 제외하고는 추가 변경 없이 수행 되지만 이 외에도 많은 다른 변경 사항이 적용되었으므로 EA는 재 컴파일 할 필요없이 주식 시장과 외환 시장 모두에 사용하기 적합합니다. 그래서 여기서는 이전 기사를 마무리합니다. 차트에서 바로 손절매 및 이익실현 레벨을 이동하는 방법을 살펴 보겠습니다.


3.0. 구현

코드의 몇 가지 변경 사항부터 살펴보겠습니다.

첫째, 3~4 버전 전에 적용되었던 한도 시스템을 제거했습니다. 이는 EA가 주식 시장과 외환 사이의 랏 계산을 잘못 조정하는 경우가 있기 때문입니다.

EA가 외환 및 주식 시장에서 똑같이 잘 작동할 수 있도록 새로운 계산 모델을 추가했습니다. 이 버전 이전에는 이 기능이 불가능했습니다. 처음에 EA는 주식 시장 작업에 중점을 두었지만 거래 방법이 크게 다르지 않기 때문에 저는 EA의 기능을 외환 거래로도 확장하기로 결정했습니다.

이전 버전에서는 EA가 처리할 수 없었던 랏 계산과 관련된 문제가 있었지만 이제 변경 사항을 적용하였고 그래서 코드를 크게 변경하지 않고도 외환 및 주식 시장에서 모두 사용할 수 있습니다. 하지만 외환 및 주식 시장과의 호환성을 유지하기 위해 저는 여러 가지 변경 작업을 수행해야 했습니다.

이러한 변경 사항 중 하나는 코드의 맨 처음 부분에 있습니다:

int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(user20 * Terminal.GetVolumeMinimal(), user21, user22, user23);
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        TradeView.Initilize();
                
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}

강조 표시된 부분은 이전에는 존재하지 않았습니다. 그 밖에도 많은 변경 사항이 있습니다. 하지만 이들 변경 사항의 구현에 중점을 두지는 않을 것입니다. 대신 EA를 사용하여 다른 트릭을 사용하지 않고 테이크 및 스톱 레벨을 차트에서 직접 이동하는 방법을 살펴 보겠습니다. 이제 이 부분으로 넘어가 보겠습니다.


3.0.1 차트에서 바로 주문 이동하기

이전 버전에서는 이 부분이 매우 어려웠습니다. 시간이 갈수록 해결되지 않은 여러 가지 문제가 발생하였습니다. 그 이유 중 하나는 코드가 매우 산만하여 차트에서 직접 주문을 이동하는 시스템을 효과적으로 구현하기 어려웠기 때문입니다. 원래는 시스템이 그렇게 돌아가지 않기로 되어 있었습니다.

EA에 필요한 변경 사항의 정도를 파악하기 위해 이전에 EA가 어떤 모습 이었는지 알려 드리기 위해(마우스를 사용하여 차트에서 펜딩 주문 및 포지션을 이동하여 여러분이 마우스를 사용하여 차트에서 직접 리밋 오더의 이동을 제어 할 수 있는 등) 이전의 EA는 어떠 했는지 살펴 봅시다.

문제는 오브젝트들이 생성될 때 이들이 C_HLineTrade 클래스를 대체했다는 것입니다. 이는 트레이딩 EA 처음부터 개발하기(20부) 글에서 설명했습니다. 이제 시스템은 훨씬 더 복잡한 구조를 가지므로 위의 전체 그림을 다시 복습하지 않고 무슨 일이 있었는지만 살펴 보겠습니다.

화살표는 새 클래스를 위한 공간을 확보하기 위해 C_HLineTrade 클래스가 제거된 연결 지점을 가리킵니다. 이를 통해 이전 글에서 소개한 더 많은 구현이 가능해었습니다. 하지만 C_OrderView 클래스의 존재가 개발에 방해가 되어 결국 이 클래스를 제외해야 했습니다. 하지만 그게 다가 아닙니다. C_TradeGraphics 클래스가 기존 C_OrderView 클래스와 병합되어 C_IndicatorTradeView라는 새로운 클래스가 나타났습니다. 그래서 이 클래스가 두 개의 클래스를 대체했고 이를 통해 주문 이동 시스템을 개발할 수 있었습니다.

여기서 소개할 것은 이 시스템의 첫 번째 버전입니다. 현재 개발 중인 다른 버전이 있는데 이 버전은 다른 글에서 소개할 예정입니다.


3.0.1.1 - 새로운 시스템용 코드 작성하기


병합 후 새로운 시스템의 구성은 다음과 같습니다:


녹색 영역은 무료인 클래스 세트를 나타냅니다. 즉 EA가 아닌 MetaTrader 5에서 관리합니다. 하지만 이것이 어떻게 되는 것일까요? 프로세스를 자세히 살펴보겠습니다. 실제로는 EA는 클래스와 이러한 클래스에 의해 생성된 모든 객체만 생성, 배치 및 삭제합니다. EA 코드 내부를 살펴보면 녹색 영역에서 생성된 오브젝트를 참조하는 구조나 변수를 찾을 수 없습니다. 이렇게 하면 MetaTrader 5가 운영체제에서 메모리를 할당할 수 있는 한 객체를 무제한으로 생성할 수 있습니다. 객체의 수는 EA 내부의 구조나 변수에 의해 제한되지 않습니다.

미친 사람만이 그런 구조를 만들 수 있다고 생각할 수도 있습니다. 어쨋든, 제가 만들었고 작동하니 미쳤다고 해도 좋습니다. 게다가 놀랍게도 시스템에 과부하가 걸리지 않습니다. 사람들은 저를 미쳤다고 하지만 별거 아니죠... 더 자세히 알아봅시다. 여러분은 펜딩 또는 리밋 주문을 이동할 때 속도가 느려지는 것을 느낄 수 있을 것입니다. 이는 코드의 오류나 컴퓨터의 문제가 아닌 MetaTrader 5에서 시스템이 거래 서버 자체에서 펜딩 중인 주문 또는 limit을 이동시켜 이 이동과 서버의 응답 사이에 지연 시간이 발생하기 때문입니다. 하지만 일부 시나리오에서는 이 방법이 가장 안전하고 최선이지만 우리의 입장에서 EA가 했으면 하는 다른 작업을 수행할 수 없습니다. 따라서 다음 기사에서는 EA에 새로운 기능을 추가하여 이 문제를 해결하고 시스템을 더 유동적으로 만들 것입니다. 위의 구조를 수정하지 않고도 가능합니다. 코드를 적절하게 조작할 뿐입니다. 비록 시스템 보안이 약해지지만 그때까지는 이 문제에 대한 좋은 해결책을 찾을 수 있을지도 모릅니다.

새로운 코드의 몇 가지 중요한 사항을 살펴보겠습니다. 짧은 시간 동안 연구한 함수부터 시작하겠습니다. 코드는 다음과 같습니다:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        ulong ticket;
        
        if (trans.symbol == Terminal.GetSymbol()) switch (trans.type)
        {
                case TRADE_TRANSACTION_DEAL_ADD:
                case TRADE_TRANSACTION_ORDER_ADD:
                        ticket = trans.order;
                        ticket = (ticket == 0 ? trans.position : ticket);
                        TradeView.IndicatorInfosAdd(ticket);
                        TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type)));
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                                if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order);
                                else
                                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                                if (!PositionSelectByTicket(trans.position))
                                        TradeView.RemoveIndicator(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_UPDATE:
                        TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type));
                        break;
                case TRADE_TRANSACTION_POSITION:
                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                        break;
        }
        
        
#undef def_IsBuy
}

이 코드는 새로운 포지션이 나타나거나 수정될 때마다 일일이 확인하지 않아도 되므로 매우 흥미롭습니다. 실제로 서버 자체에서 무슨 일이 일어나고 있는지를 우리에게 알려주기 때문에 우리는 EA가 이벤트에 올바르게 응답하는지를 확인하기만 하면 됩니다. 이러한 방식의 코딩 방법이나 OnTradeTransaction 이벤트를 사용해서 이전 버전에서 수행되는 방식으로 모델을 분석하면 확인에 많은 시간이 걸릴 수 있습니다. 이 경우 서버가 모든 작업을 대신 수행하므로 우리는 차트의 값이 서버가 현재 보고 있는 것이라는 사실을 확실히 알 수 있습니다.

위 코드의 주요 내용을 살펴보기 전에 다른 스니펫을 살펴보겠습니다.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        TradeView.DispatchMessage(id, lparam, dparam, sparam);
        ChartRedraw();
}

모든 것이 한 곳에서 이루어집니다. 클래스에 들어가면 내부에서 무슨 일이 일어나는지 볼 수 있습니다.


3.1. C_IndicatorTradeView 클래스

이 클래스는 데이터를 표시하고 조작하는 데 사용됩니다. 기본적으로 앞서 언급했듯이 이 클래스에는 이전 C_OrderView 및 C_TradeGraphics 클래스가 포함되어 있습니다. 하지만 완전히 다른 방식으로 데이터를 조작합니다. 이 클래스의 몇 가지 요점을 살펴보겠습니다.

다음과 같은 초기화 함수부터 시작하겠습니다:

void Initilize(void)
{
        int orders = OrdersTotal();
        ulong ticket;
        bool isBuy;
        long info;
        double tp, sl;

        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_OBJECT_DESCR, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false);
        for (int c0 = 0; c0 <= orders; c0++) if ((ticket = OrderGetTicket(c0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                info = OrderGetInteger(ORDER_TYPE);
                isBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(-1, ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), OrderGetDouble(ORDER_SL), OrderGetDouble(ORDER_VOLUME_CURRENT), isBuy);
        }
        orders = PositionsTotal();
        for (int c0 = 0; c0 <= orders; c0++) if (PositionGetSymbol(c0) == Terminal.GetSymbol())
        {
                tp = PositionGetDouble(POSITION_TP);
                sl = PositionGetDouble(POSITION_SL);
                ticket = PositionGetInteger(POSITION_TICKET);
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(1, ticket, PositionGetDouble(POSITION_PRICE_OPEN), tp, sl, PositionGetDouble(POSITION_VOLUME), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
        }
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PENDING);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP);
}

기본적으로 우리가 하는 일은 작업할 필요한 지표를 만들고 포지션이나 펜딩 주문 등 현재 계좌에 존재하는 모든 것을 표시하는 것입니다. 이때 강조 표시된 선이 중요합니다. 교차 주문 시스템을 사용하지 않는 경우 차트에 주문 포인트 (MetaTrader 5에서)가 있고 이 포인트를 클릭하고 드래그 하면 EA가 지표의 변경 사항으로 새 포인트를 업데이트하기 때문입니다. 이 점이 크게 방해가 되지는 않지만 우리가 개발 중인 시스템을 실제로 사용해야 합니다. 그렇지 않으면 개발의 의미가 없는것 아닐까요?

다음으로 다음 코드에 주목하세요:

void UpdateInfosIndicators(char test, ulong ticket, double pr, double tp, double sl, double vol, bool isBuy)
{
        bool isPending;
                                
        isPending = (test > 0 ? false : (test < 0 ? true : (ticket == def_IndicatorTicket0 ? true : OrderSelect(ticket))));
        PositionAxlePrice(ticket, (isPending ? IT_RESULT : IT_PENDING), 0);
        PositionAxlePrice(ticket, (isPending ? IT_PENDING : IT_RESULT), pr);
        SetTextValue(ticket, (isPending ? IT_PENDING : IT_RESULT), vol);
        PositionAxlePrice(ticket, IT_TAKE, tp);
        PositionAxlePrice(ticket, IT_STOP, sl);
        SetTextValue(ticket, IT_TAKE, vol, (isBuy ? tp - pr : pr - tp));
        SetTextValue(ticket, IT_STOP, vol, (isBuy ? sl - pr : pr - sl));
}

이 코드는 데이터를 수신하고 업데이트하여 재무적 가치와 주문이 위치한 지점의 정확한 값을 제시합니다. 기본적으로 펜딩 주문이나 포지션이 있는지 걱정할 필요가 없습니다. 이 함수는 주문이나 포지션을 차트에서 정확하게 볼 수 있도록 합니다.

다음 함수는 다음과 같습니다.

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ticket = PositionGetInteger(POSITION_TICKET);
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

이 함수는 온틱 이벤트에 의해 호출되므로 속도와 시스템 부하 측면에서 매우 중요합니다. 이 함수가 하는 일은 체크하는 기능 이외에 강조 표시된 코드로 구현된 차트의 값을 업데이트하는 것이 유일합니다. 여기서 포지션 티켓이 매우 중요하다는 점에 유의하세요.

위에서 강조 표시된 함수를 자세히 살펴 보겠습니다.

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0 / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        break;
        }
}

이것이 올바른 값들이 표시되는 방식입니다. 하지만 문제는 어떻게 이동하느냐는 것입니다. 이 작업은 다른 세 가지 코드로 수행됩니다. 물론 이를 피하고 현재 EA 시스템보다 훨씬 빠른 MetaTrader 5 시스템 자체를 사용할 수도 있습니다. 하지만 앞서 말했듯이 곧 다른 개선 사항이 적용될 예정이므로 저는 EA를 사용하는 것을 선호합니다.

이동을 담당하는 첫 번째 함수는 아래에서 볼 수 있습니다. 그러나 한도 또는 주문 자체와 같이 포인트를 이동하는 데 필요한 조각만 표시합니다. 전체 코드가 훨씬 더 광범위하고 마우스 움직임을 사용하여 이동하는 방법을 이해하는 것은 굳이 필요하지 않기 때문입니다.

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;

// ... Code ....

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse click 
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT press 
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL press 
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        bMounting = true;
                                }
                                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                        }else if (bMounting)
                        {
                                UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick)
                                {
                                        bIsMove = false;
                                        m_TradeLine.SpotLight();
                                }
                                MoveSelection(price, mKeys);
                        }
                        break;

// ... Code ...
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Code ...

                                case EV_MOVE:
                                        if (bIsMove)
                                        {
                                                m_TradeLine.SpotLight();
                                                bIsMove = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                bIsMove = true;
                                        }
                                        break;
                        }
                        break;
        }
}

무슨 일이 일어나고 있는지 이해해 봅시다. 이 글의 마지막에는 이를 수행하는 방법과 실제로 어떤 일이 일어나는지 보여주는 동영상이 있습니다. 하지만 먼저 알아볼 것이 있습니다.

각 표시에는 선택이 가능한 객체가 있습니다(이동할 수 없는 결과 제외). 이 지점을 클릭하면 표시 선이 변경되어 점점 더 굵어집니다. 이 경우 마우스의 움직임이 캡처되고 선택 오브젝트 외부를 새로 클릭할 때까지 이 오브젝트의 새 위치로 변환되어 오브젝트의 이동을 허용합니다. 마우스 버튼을 계속 누르고 있을 필요가 없습니다. 한 번 클릭하고 드래그한 다음 다시 클릭하기만 하면 됩니다.

하지만 지금은 일부 작업만 수행됩니다. 다른 두 가지 함수도 우리의 작업에 도움이 됩니다. 하나는 이미 위에서 보았듯이 계산 된 값을 표시하는 역할을 하고; 다른 하나는 주문 또는 한도 수준 이동 시스템을 사용할 때 EA를 슬러그처럼 보이게 하는 돌의 역할을 합니다: 이 코드는 다음과 같이 표시됩니다:

void MoveSelection(double price, uint keys)
{
        static string memStr = NULL;
        static ulong ticket = 0;
        static eIndicatorTrade it;
        eEventType ev;
        double tp, sl, pr;
        bool isPending;
                                
        string sz0 = m_TradeLine.GetObjectSelected();
        
        if (sz0 != NULL)
        {
                if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                isPending = OrderSelect(ticket);
                switch (it)
                {
                        case IT_TAKE:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                break;
                        case IT_STOP:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                break;
                        case IT_PENDING:
                                pr = macroGetPrice(IT_PENDING);
                                tp = macroGetPrice(IT_TAKE);
                                sl = macroGetPrice(IT_STOP);
                                ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                break;
                }
        };
}

제가 이 함수를 돌이라고 부르는 이유는 포지셔닝 시스템을 느리게 만들기 때문입니다. 마약 이해가 되지 않는 부분이 있다면 강조 표시된 내용을 살펴보세요. 각각은 C_Router 클래스 내부에 있으며 거래 서버에 요청을 보내는 함수이므로 서버가 어떤 이유로든 응답하는 데 시간이 걸리면 (지연 시간으로 인해 항상 발생) 포지셔닝 시스템이 다소 느려지지만 서버가 빠르게 응답하면 시스템이 유동적이거나 오히려 더 원활하게 진행됩니다. 나중에 이 부분을 수정할 예정입니다. 이 시스템에서는 현재 다른 작업을 할 수 없습니다. 어쨌든 이렇게 하면 가격이 매우 빠르게 움직이는 변동성이 큰 움직임에서 운영하는 것을 좋아하는 사람들, 특히 조금 더 안전한 방식으로 운영 할 수 있지만 한도가 커질 위험이 있다는 점을 명심해야 합니다. 방법은 없습니다. 무언가를 희생해야 합니다. 서버 내부에 정확히 어떤 내용이 있을지 알고 있고 이러한 운영에 동의하는 분들을 위해 EA는 지금 바로 준비되어 있습니다. 그러나 이 부분이 다소 명확하지는 않더라도 EA가 유동적으로 작동하기를 바라는 사람들을 위해 다음 기사에서는 상황이 바뀌고 더 흥미로워질 것입니다.

아래 비디오는 시스템이 실제로 어떻게 작동하는지 보여 줍니다. 값에 주목하세요. ​차트와 도구 상자에 나오는 값에 주목 하세요.


결론

이제 거래를 위한 매우 흥미로운 Expert Advisor가 있습니다. 그러나 EA의 작동 방식에 익숙해지기 위해 잠시 동안 데모 계정에서 사용하는 것이 좋습니다. 더 이상 작동 방식에 큰 변화는 없을 것이라고 약속 드립니다. 이후에는 개선 사항이 있을 뿐이며 다음 기사에서는 이 EA가 제공하는 일부 보안을 희생하는 대신 부족한 몇 가지 사항을 추가할 것입니다. 어쨌든 이것은 트레이딩 시스템의 작동 방식과 플랫폼을 조작하여 필요한 데이터 모델링 유형을 얻는 방법을 배우는 데 좋은 자료가 될 것입니다.

주문 또는한도한 레벨을 이동하는 것이 너무 느리다고 생각되면 기사에서 설명한 포인트를 제거하고 MetaTrader 5 자체를 사용하여 주문이나 한도를 이동하고 데이터 해석에 도움이 되는 방향으로 EA를 사용할 수 있다는 것을 잊지 마십시오. 사용 여부는 여러분의 선택입니다...


MetaQuotes 소프트웨어 사를 통해 포르투갈어가 번역됨
원본 기고글: https://www.mql5.com/pt/articles/10516

모집단 최적화 알고리즘: 물고기 떼 검색(FSS) 모집단 최적화 알고리즘: 물고기 떼 검색(FSS)
물고기 떼 검색(FSS)은 대부분의 물고기(최대 80%)가 친척들로 구성된 집단인 물고기 떼에서 물고기의 행동에서 영감을 얻은 새로운 최적화 알고리즘입니다. 물고기의 떼가 먹이 사냥의 효율성과 포식자로부터 보호하는 데 중요한 역할을 한다는 것은 이미 입증된 사실입니다.
어썸 오실레이터로 트레이딩 시스템 설계하는 방법 알아보기 어썸 오실레이터로 트레이딩 시스템 설계하는 방법 알아보기
이번 글에서는 트레이딩에 유용하게 사용될 수 있는 새로운 기술 도구에 대해 알아보겠습니다. 그것은 어썸 오실레이터(AO) 지표입니다. 이 지표로 거래 시스템을 설계하는 방법에 대해 알아볼 것입니다.
Expert Advisor 개발 기초부터(23부): 새로운 주문 시스템(VI) Expert Advisor 개발 기초부터(23부): 새로운 주문 시스템(VI)
이제 주문 시스템을 더욱 유연하게 만들어 볼 것입니다. 여기서는 포지션 스톱 레벨을 훨씬 더 빠르게 변경할 수 있도록 코드를 더 유연하게 변경하는 방법을 알아보겠습니다.
Relative Vigor Index로 거래 시스템을 설계하는 방법을 알아보세요 Relative Vigor Index로 거래 시스템을 설계하는 방법을 알아보세요
가장 인기 있는 기술 지표를 기반으로 거래 시스템을 설계하는 방법에 대한 시리즈의 새로운 기사입니다. 이 글에서는 상대 활력 지수(Relative Vigor Index) 지표를 사용하여 이를 수행하는 방법을 알아봅니다.