![시장과 시장이 보여 주는 글로벌 패턴의 물리학](https://c.mql5.com/i/articles/overlay.png)
시장과 시장이 보여 주는 글로벌 패턴의 물리학
소개
이 글에서 우리는 시장 물리학을 자동매매에 어떻게 사용할 수 있는지 알아보겠습니다. 수학은 추상성과 불확실성에서 예측으로의 전환을 의미합니다. 이를 통해 대략적이고 모호한 값이 아닌 명확한 공식이나 기준에 따라 운영을 하며 생성된 시스템의 품질을 개선할 수 있습니다. 저는 어떤 이론이나 패턴을 발명하지 않을 것이고 알려진 사실만을 사용하며 이러한 사실을 점차 수학적인 분석 언어로 번역할 것입니다. 시장 물리학은 수학 없이는 불가능합니다. 왜냐하면 우리가 생성하는 신호는 수학적 실체이기 때문입니다. 많은 사람들이 통계적 분석 없이 또는 매우 제한된 통계를 사용하여 다양한 이론과 공식을 만들려고 하는데 이는 대담한 결론을 내리기에는 충분하지 않은 경우가 많습니다. 연습만이 진실의 기준이 됩니다. 우선은 조금 알라 보고 이를 바탕으로 EA를 만들어 보겠습니다. 이후 EA를 테스트할 것입니다.
가격 및 가격이 제공하는 것
시장은 다양한 제품이 존재한다는 것을 의미합니다. 통화 시장에서는 상품이 곧 통화입니다. 화폐는 전 세계적으로 기준이 되는 특정 상품이나 정보를 소유할 수 있는 권리를 의미하며 예를 들어 EURUSD 쌍과 해당 차트에서의 현재 가치를 생각해 보세요. 현재 차트 값은 USD/EUR = 현재 가격을 의미합니다. 혹은 USD = EUR*현재 가격, 즉 1유로에 포함된 달러의 양을 의미합니다. 즉, 이 값은 각 통화 비중의 비율을 나타내며 물론 각 통화에 대해 공통의 교환 대상, 즉 공통의 상품이나 다른 무언가가 있다고 가정합니다. 가격은 오더 북에서 형성되며 오더 북의 역학 관계에 따라 가격 변동이 결정됩니다. 우리는 가격 형성의 모든 요인을 고려할 수는 없다는 점을 항상 기억해야 합니다. 예를 들어 FORTS는 FOREX와 연결되어 있으며 두 시장은 서로 영향을 주고받습니다. 저는 이 분야의 전문가는 아니지만 모든 것이 연결되어 있고 데이터 채널이 많을수록 좋다는 것을 누구나 이해할 수 있을 것입니다. 우리에게는 그런 세부 사항에 깊이 들어 가지 않고 가격을 움직이는 것에 집중하는 것이 더 나은 접근법일 것입니다.
모든 종속성은 따옴표처럼 여러 변수의 함수로 표현될 수 있습니다. 처음에는 가격입니다:
- P=P(t)
다시 말해 가격은 시간의 함수입니다. 각 통화쌍이나 다른 상품에 대해 함수의 형태를 안정적으로 설정하는 데는 무한한 시간이 걸리므로 함수의 현태를 안정적으로 설정할 수는 없습니다. 하지만 이 가격 표현은 우리에게 아무것도 알려주지 않습니다. 그러나 가격은 예측 가능한 요소와 무작위 요소가 모두 포함되어 있어 이중적인 성격을 띠고 있습니다. 예측 가능한 부분은 함수 자체가 아니라 그 함수의 첫 번째 도함수입니다. 이 함수는 거래의 목적이 없기 때문에 이 함수를 어떤 용어로 표현하는 것은 의미가 없습니다. 그러나 그 첫 번째 파생물을 고려하면 다음과 같은 것이 있습니다:
- P'(t)=Pa'(t)+Pu'(t)
여기서 첫 번째 항은 수학적 분석을 통해 어떤 식으로 든 분석할 수 있는 부분을 반영하고 두 번째 항은 예측할 수 없는 부분을 반영합니다. 이 공식에 따르면 움직임의 크기와 방향을 100% 정확하게 예측하는 것은 불가능하다고 할 수 있습니다. 마지막 항이 언제가 될지 결정할 수 없으므로 미리 생각할 필요가 없습니다. 하지만 우리는 첫 번째 항을 결정할 수 있습니다. 값 함수가 불 연속적이고 미분 연산을 적용할 수 없다는 점을 고려하면 우리는 이 항을 다른 형태로 표현할 수 있다고 가정할 수 있습니다. 대신 시간에 따른 평균 도함수를 'st'로 구할 수 있습니다. 가격에 적용하면 바의 지속 시간이 되고 틱에 적용하면 두 틱 사이의 최소 시간이 됩니다.
- PaM(t)=(Pa(t)-Pa(t-st))/st - 일정 기간 동안의 평균 가격 변동(시간 파생상품)
- Ma(t)=Ma(P(t),P(t-st) + ... + P(t-N*st), D(t),D(t-st) + ... + D(t-N*st),U[1](),U[2](t) + ... + U[N](t) )
- P(t[i]) - 이전 가격 값(막대 또는 틱 데이터)
- D(t[i]) - 다른 통화쌍의 이전 가격값
- U[i](t) - 시장에 영향을 미치는 기타 알려지지 않은 값 또는 알려진 값
- Ma(t ) - 특정 시점의 PaM(t) 값에 대한 수학적 기대치
즉, 우리는 가정하기를 가격의 예측 가능한 부분은 이전 막대 또는 틱 뿐만 아니라 다른 통화쌍의 가격 데이터, 다른 거래소의 데이터 및 전 세계의 이벤트에 따라 달라질 수 있다고 가정합니다. 그러나 우리는 가격의 이 부분조차도 100 %의 정확도로 예측할 수는 없지만 일부 특성을 계산할 수 있다는 점을 알아야 합니다. 이러한 특성은 확률 이론의 수학적 기대치, 분산, 표준편차 및 기타 수량과 같은 확률 또는 무작위 변수의 매개 변수일 수 있습니다. 수학적 기대치를 가지고 운영하면 수익성 있는 트레이딩을 할 수 있습니다. 시간을 갖고 신중하게 생각해 보면 우리가 이러한 논리로만 시장을 분석할 수 없다는 결론에 도달할 수 있습니다. 문제는 가격의 예측 가능한 부분이 시장 참여자들의 활동을 기반으로 발전한다는 것입니다. 우리는 시장 참여자들이 직접 만든 요인을 제외한 다양한 시장 변수를 버릴 수 있습니다. 물론 이러한 작업이 우리의 분석 방법의 신뢰성을 떨어뜨리지만 이는 모델을 크게 단순화합니다. 여기서 "st" 값이 작을수록 우리의 공식을 통해 시장을 더 정확하게 설명할 수 있습니다.
- VMa(t)=VMa(P(t),P(t-st) + ... + P(t-N*st))
- VMa(t)=VBuy(t)-VSell(t)
- VMa(t) - 총 볼륨
- VBuy(t) - 체결 매수 주문 수량
- VSell(t) - 체결 매도 주문 수량
위의 함수는 현재 진입 되어 있는 모든 매수 및 매도 포지션의 볼륨의 차이를 설명합니다. 이러한 포지션 중 일부는 서로를 보상하는 반면 나머지 포지션은 독립적입니다. 포지션이 오픈 되어 있다는 것은 잠시 후 청산하겠다는 약속을 상징합니다. 매수하면 가격이 상승하고 매도하면 가격이 하락한다는 것은 누구나 알고 있는 사실입니다. 가격의 방향을 알 수 있는 유일한 방법은 진입한 포지션의 볼륨을 측정하고 진입한 시장가 주문만 고려하여 포지션의 방향을 추정하는 것입니다.
시장의 파동적 특성은 사실 이 단순한 사실과 관련이 있습니다. 이는 포지션 볼륨의 변동 또는 상승과 하락의 보다 일반적인 과정 중 하나의 특수한 사례일 뿐입니다.
바를 다룰 때는 바 안에 4개의 가격이 있다는 사실을 고려하면 더 나은 공식을 얻을 수 있습니다. 데이터가 많을수록 더 정확한 분석이 가능하므로 모든 가격 데이터를 고려하는 것이 중요합니다. 하지만 모든 틱을 세는 것은 알고리즘을 10배나 느리게 하기 때문에 저는 좋아하지 않습니다. 또한 틱 데이터는 브로커마다 다를 수 있습니다. 반대로 바의 시가와 종가는 대부분의 브로커에서 거의 동일합니다. 이제 모든 가격 데이터를 반영하도록 볼륨 함수를 수정해 보겠습니다:
- VMa(t)=VMa(O(t),O(t-st) +...+ O(t-N*st) + C(t),C(t-st) + C(t-N*st),H(t),H(t-st)...H(t-N*st),L(t),L(t-st)...L(t-N*st))
우리는 이 함수에 시간, 요일, 월, 주 등 더 많은 변수를 추가할 수 있지만 이렇게 하면 특정 시장 영역과 연결된 수 많은 함수가 생성됩니다. 그러나 우리의 목적은 일반적인 시장 물리학을 결정하는 것입니다. 우리는 그것이 깨질 수 없으므로 시장이 존재하는 한 사용할 수 있다는 것을 알게 될 것입니다. 이 공식의 또 다른 장점은 여러 통화를 사용할 수 있다는 점입니다.
실제로는 이 표현 유형을 사용하는 것은 의미가 없습니다. 왜냐하면 이 함수를 구축하는 방법과 데이터를 정확히 알아야 하기 때문입니다. 이 함수의 형식을 작성하고 종속성을 결정하는 것만으로는 부족합니다. 그러나 이러한 표현법은 우리가 분석 방법과 다음 가정으로 넘어가는 방법에 대한 초기 이해를 얻는 데 도움이 될 수 있습니다. 모든 논리 조건 집합은 궁극적으로 이러한 함수로 표현될 수 있습니다. 반대로 함수 자체를 조건 집합으로 바꿀 수도 있습니다. 어떤 방식을 사용하든 상관없습니다. 이러한 과정을 이해하는 것이 중요합니다. 모든 알고리즘은 하나의 공식으로 축소할 수 있습니다. 때로는 복잡한 함수를 만드는 것보다 신호를 조건이나 조건의 시스템으로 설명하는 것이 더 쉬울 때가 있습니다. 또 다른 큰 의문은 이러한 함수를 어떻게 구축하는가 하는 점입니다.
실제 거래 시스템에서 우리는 전체 이력을 한 번에 분석할 수 없고 일정 기간만을 분석할 수 있습니다. 이러한 분석에는 4가지 접근 방식이 있습니다. 4가지 각각에 대해 이름을 짓고 설명해 드리겠습니다:
- 공식(지표 또는 해당 함수)
- 시뮬레이션
- 일반 수학
- 머신 러닝의 유형
첫 번째 옵션은 우리가 하나의 특정 값 또는 값 집합을 사용한다고 가정합니다. 예를 들어 우리는 지표 또는 자체 수식을 사용할 수 있습니다. 이 접근법의 장점은 우리가 MetaTrader 4/5 터미널에서 대규모 툴킷을 사용할 수 있다는 것입니다. 또한 마켓과 웹에는 인기 있는 시장 이론에 기반한 많은 보조지표가 있습니다. 이 접근 방식의 단점은 대부분의 경우 우리가 지표가 작동하는 방식을 이해하지 못한다는 것입니다. 설령 이해한다고 해도 그런 이해는 아무런 가치가 없을 수 있습니다.
두 번째 옵션에서는 우리가 이해하지 못하거나 사용할 수 없는 데이터는 사용하지 않습니다. 대신 시장에서 주문을 시뮬레이션 하면 우리의 시스템은 매수 또는 매도의 방향에 얼마나 많은 포지션이 진입해 있고 다른 방향에 얼마나 많은 포지션이 진입해 있는지 어느 정도 파악할 수 있습니다. 이 정보는 예측을 생성하고 글로벌한 관점에서 시장을 세밀하게 설명할 수 있습니다. 이는 머신 러닝의 유일한 대안입니다.
수학은 현재 시장 상황에 관계없이 모든 시세를 활용할 수 있는 몇 가지 기본 법칙이나 특정한 수학적 원리에 대한 지식을 의미합니다. 사실 불연속 함수를 포함한 모든 함수에는 이용될 수 있는 특정 특성이 있습니다. 물론 여기서는 의존성이 혼란스럽지 않다고 가정합니다(우리의 경우 외환 거래는 혼란스럽지 않으므로 어떤 시세도 이용될 수 있습니다). 다음 기사에서는 거의 모든 사람이 알고 있는 이러한 원칙 중 하나를 분석해 보겠습니다. 하지만 아는 것과 사용할 수 있는 것은 별개입니다. 이 접근 방식의 장점은 성공적인 시스템을 구축하기만 하면 이후 시스템이 어떻게 작동할지 걱정할 필요가 없다는 것입니다.
네 번째 접근 방식은 머신 러닝이 모든 데이터를 최대한 활용할 수 있다는 점에서 가장 진보적인 접근 방식입니다. 컴퓨팅 성능이 높을수록 분석의 품질이 높아집니다. 이 접근법의 단점은 시장 물리학을 이해하는 데 도움이 되지 않는다는 것입니다. 이 방법의 장점은 접근 방식이 간단하고 최소한의 시간으로 최대 결과의 품질을 얻을 수 있다는 점입니다. 그러나 이 글에서는 이 접근 방식을 적용하지 않습니다.
패턴 정보
일상적인 트레이딩에는 수많은 용어가 있으며 각 용어는 중요도가 다릅니다. 일부 용어는 매우 자주 사용되지만 모든 트레이더가 용어의 실제 목적을 이해하는 것은 아닙니다. 이러한 용어 중 하나가 패턴입니다. 수학의 언어로 설명해 보겠습니다. 패턴은 항상 특정 기간, 특정 통화쌍 및 차트 주기와 연결됩니다. 일부 패턴은 강력합니다. 이러한 패턴은 다중 통화 또는 글로벌 패턴일 수 있습니다. 이상적인 패턴은 성배입니다. 모든 패턴에 적용할 수 있는 몇 가지 사실이 있습니다:
- 패턴을 상징하는 공식 또는 조건 집합이 존재
- 전략 테스터의 최소 테스트 값 또는 데모 또는 실계좌의 성능 값
- 모든 통화쌍 및 차트 주기에 대한 성과 측면에서 패턴 분류
- 패턴이 발견된 이전 거래의 기간
- 패턴이 계속 작동할 수 있는 미래의 시간 간격
- 미래의 두 번째 기간은 첫 번째 기간에 이어서 원래 패턴이 일부의 매개 변수를 유지하거나 반전하는 기간입니다.
각 속성을 주의 깊게 읽어 보면 패턴이란 선택한 간격 동안의 가격 변동을 가장 정확하게 설명하는 공식 또는 조건 집합이라는 것을 이해할 수 있을 것입니다. 특히 너무 짧은 기간에 발견되거나 관련 시스템이 지나치게 낙관적인 값을 생성하는 경우 패턴이 무작위인 것으로 판명될 수 있습니다. 단기간에 시스템을 테스트할 때는 글로벌 패턴을 찾을 확률이 0에 가까워진다는 점을 이해하는 것이 매우 중요합니다. 이는 표본의 크기와 관련이 있습니다. 표본이 작을수록 결과의 무작위성이 높아집니다.
우리는 이제 패턴이 무엇인지 확인했습니다. 그렇다면 패턴을 효과적으로 사용하는 방법은 무엇일까요? 그것은 패턴을 발견한 방법과 품질에 따라 달라집니다. 컴퓨팅 파워를 활용한 분석 방법을 고려하지 않으면 우리는 분석에 도달할 수 없습니다. 제 생각에 분석은 어떤 유형의 기계 분석과도 경쟁할 수 없습니다. 아무리 훌륭한 분석가 팀이라도 한 대의 기계가 처리할 수 있는 데이터를 처리할 수는 없습니다. 어쨌든 글로벌 패턴을 찾는 과정에는 컴퓨팅 성능이 필요합니다. 하지만 여러분이 명백한 것을 직접 눈으로 보고 그 물리학을 이해한 경우는 예외입니다.
가장 간단한 포지션 시뮬레이터 작성
글로벌 패턴을 찾기 위해 시장 참여자들의 분위기를 설명할 수 있는 EA를 개발하는 것도 흥미로울 것입니다. 이를 위해 저는 시장 포지션 시뮬레이터를 만들기로 했습니다. 포지션은 시장의 끝 부분에 가까운 바 내에서 시뮬레이션 됩니다. 시장 참여자가 다르고 주문의 비중도 다르다고 가정해야 합니다. 동시에 이것은 간단한 형식으로 제공되어야 합니다. 간단한 프로토타입을 통해 수익성이 입증되면 그 원리를 더 활용할 수 있습니다.
로직은 조건부로 3개의 개별 시뮬레이션과 이들 시뮬레이션의 혼합 조합으로 나뉩니다:
- 스탑 오더 시뮬레이션
- 펜딩 오더 시뮬레이션
- 마켓 오더 시뮬레이션
- 가능한 모든 조합
주문 시에는 다음과 같은 로직이 사용됩니다:
이 그리드는 시장 참여자 중 일부의 분위기를 시뮬레이션하기 위해 새로운 바가 생길 때마다 배치됩니다. 이전 주문 그리드의 상태는 차트에 나타나는 새로운 바를 기준으로 업데이트됩니다. 이 접근 방식은 정확하지는 않지만 틱 단위로 시뮬레이션을 하면 계산이 끝없을 수 있습니다. 게다가 저는 틱을 그다지 신뢰하지 않습니다.
상대 볼륨 분포에는 감쇠와 고른 두 가지 유형이 있지만 스톱 및 리밋 주문에만 적용됩니다. 마켓 오더는 고르게 분포되어 있습니다. 이러한 관점에서 본다면 배포 유형을 확장하는 것도 가능할 것입니다. 다음은 그림입니다:
여기서 주문을 상징하는 선의 길이는 같은 주문의 볼륨에 비례합니다. 그런 그림은 누구나 간단하고 이해하기 쉬운 것입니다.
이 경우 객체 지향적 접근 방식을 사용하여 모든 작업을 수행할 수 있습니다. 이제 번호가 매겨진 목록에 대해 설명하는 것부터 시작하겠습니다:
enum CLOSE_MODE// how to close orders { CLOSE_FAST,// fast CLOSE_QUALITY// wait for an opposite signal }; enum WORK_MODE// operation mode { MODE_SIMPLE,// slow mode MODE_FAST// fast mode }; enum ENUM_GRID_WEIGHT//weight fill type for limit and stop orders { WEIGHT_DECREASE,// with attenuation if moving from the price WEIGHT_SAME// the same for the entire grid }; enum ENUM_STATUS_ORDER// statuses of orders or positions { STATUS_VIRTUAL,// stop or limit order STATUS_MARKET,// market order STATUS_ABORTED// canceled stop or limit order };
시뮬레이터는 느림과 빠름의 두 가지 모드로 작동합니다. 느린 모드는 주로 분석을 시작할 때 필요합니다. 시작 분석에서는 시장 캔들 스틱에 가장 가까운 첫 번째 "n"에서 계산이 수행됩니다. 빠른 모드에서는 새로 나타난 캔들 스틱에 대해서만 계산이 수행됩니다. 하지만 단순한 접근 방식만으로는 충분하지 않다는 것이 밝혀졌습니다. 알고리즘 속도를 높이기 위해 추가 기능이 필요했습니다. EA 초기화 시에 꽤 많은 양의 계산이 수행됩니다. 하지만 그 이후에는 우리는 각 캔들 스틱에서 하나의 새로운 캔들스틱에 대해서만 시뮬레이션을 업데이트하면 됩니다. 펜딩 오더와 스탑 오더의 볼륨 분포는 각 바의 현재 시장 가격인 오픈[i]과의 거리에 따라 두 가지 유형으로 나뉩니다. 이는 각 바에서 서로 다른 분포와 가중치를 가진 스톱 및 펜딩 오더 그리드가 열리기 때문입니다. 일정 시간이 지나면 스탑 및 리밋 주문이 시장가 주문으로 바뀝니다. 지정된 시간 동안 가격이 필요한 가격에 도달하지 않으면 스탑 및 펜딩 오더가 취소됩니다.
이제 이 시뮬레이션을 간단한 것부터 복잡한 것까지 차근차근 구축하면서 모든 것을 조합해 보겠습니다. 먼저 주문이 무엇인지 정의합니다:
struct Order// structure symbolizing a player's order { public: double WantedPrice;// desired open price int BarsExpirationOpen;// If the order remains for certain number of bars, the player can't wait any more and cancels the order int BarsExpirationClose;//If this is a market order and the player does not want to wait, he closes the position double UpPriceToClose;//The total upward price movement at which the player closes the order (points) double LowPriceToClose;//The total downward price movement at which the player closes the order double VolumeAlpha;// current volume equivalent [0...1] double VolumeStart;// starting volume equivalent [0...1] int IndexMarket;// the index of the bar on which the virtual market turned into market ENUM_STATUS_ORDER Status;// order status Order(ENUM_STATUS_ORDER S)// constructor that creates a certain order { Status=S; } };
매개변수는 많지 않으며 각 매개변수는 일반적인 알고리즘에서 중요합니다. 대부분의 필드는 모든 주문에 적용 가능하지만 일부 필드는 리밋 또는 스탑 주문에만 적용됩니다. 예를 들어 원하는 가격은 시장가 주문의 진입가를 의미하며 펜딩 오더와 스탑 오더는 정확히 원하는 가격입니다.
종가의 상한가와 하한가가 스톱 레벨의 역할을 합니다. 동시에 그리드 주문은 주문이 아니며 이 주문에는 전체의 모든 주문이 포함되어 있지만 이들 주문이 모두 특정 가격에 특정 볼륨으로 개설되는 하나의 주문으로 병합된다고 가정합니다. 시작 볼륨과 현재 볼륨이라는 변수를 통해 특정 바의 특정 레벨에서 주문이 얼마나 중요한지 알 수 있습니다.
시작 볼륨은 주문 시점의 볼륨입니다. 현재 볼륨은 이벤트가 더 진행됨에 따른 볼륨입니다. 중요한 것은 특정 주문의 수익보다는 매수 및 매도 주문의 볼륨의 비율입니다. 트레이딩 신호는 이러한 사항을 바탕으로 생성됩니다. 물론 다른 신호를 생각해낼 수도 있지만 여기에는 몇 가지 다른 우리가 고려할 사항들이 필요합니다. 또한 주문이 특정 수준에 도달하면 청산되지 않고 각 바에서 점진적으로 청산이 이루어진다는 점을 알아야 합니다. 이는 이벤트의 실제 진행 상황을 최대한 시뮬레이션 하기 위해서입니다.
다음으로 각 바에 대한 저장소를 정의해야 합니다. 바에는 이 바에서 열리는 주문이 저장됩니다:
class OrderBox// Order box of a specific bar { public: Order BuyStopOrders[]; Order BuyLimitOrders[]; Order BuyMarketOrders[]; Order SellStopOrders[]; Order SellLimitOrders[]; Order SellMarketOrders[]; OrderBox(int OrdersToOneBar) { ArrayResize(BuyStopOrders,OrdersToOneBar); ArrayResize(BuyLimitOrders,OrdersToOneBar); ArrayResize(BuyMarketOrders,OrdersToOneBar); ArrayResize(SellStopOrders,OrdersToOneBar); ArrayResize(SellLimitOrders,OrdersToOneBar); ArrayResize(SellMarketOrders,OrdersToOneBar); for ( int i=0; i<ArraySize(BuyStopOrders); i++ )// Set types for all orders { BuyStopOrders[i]=Order(STATUS_VIRTUAL); BuyLimitOrders[i]=Order(STATUS_VIRTUAL); BuyMarketOrders[i]=Order(STATUS_MARKET); SellStopOrders[i]=Order(STATUS_VIRTUAL); SellLimitOrders[i]=Order(STATUS_VIRTUAL); SellMarketOrders[i]=Order(STATUS_MARKET); } } };
여기서는 모든 것이 아주 간단합니다. 배열로 설명되는 6가지 주문 유형. 이는 혼동을 피하기 위한 것입니다. 이 클래스는 순수한 형태로는 사용되지 않으며 구성을 위한 부분에 불과합니다.
다음으로 모든 바의 공통 저장소를 하나의 객체로 정의하여 나중에 상속이 수행되도록 합니다. 여기서 사용하는 기술은 매우 간단합니다.
class BarBox// Storage for all orders { protected: OrderBox BarOrders[]; BarBox(int OrdersToOneBar,int BarsTotal) { ArrayResize(BarOrders,BarsTotal); for ( int i=0; i<ArraySize(BarOrders); i++ )// Set types for all orders { BarOrders[i]=OrderBox(OrdersToOneBar); } } };
이것은 바 데이터(주문)가 있는 저장소일 뿐입니다. 지금까지는 모든 것이 아주 간단합니다. 그러나 더 나아가 보면 상황은 더 복잡해 집니다.
주문을 위한 사용하기 편한 데이터 저장소를 결정한 후에는 주문이 어떤 규칙에 따라 어떻게 생성되는지, 특정 주문 유형의 중요도는 어느 정도인지 등을 결정해야 합니다. 이를 위해 다음과 같은 클래스를 만들었습니다:
class PositionGenerator:public BarBox// Inherit class from the box to avoid the need to include it as an internal member and to avoid multiple references { protected: double VolumeAlphaStop;// importance of volumes of STOP orders double VolumeAlphaLimit;// importance of volumes of LIMIT orders double VolumeAlphaMarket;// importance of volumes of MARKET orders double HalfCorridorLimitStop;// step of the corridor of Limit and Stop orders in points int ExpirationOpenLimit;// after how many bars the volumes of the grid of limit orders for opening will completely attenuate int ExpirationOpenStop;// after how many bars the volumes of the grid of stop orders for opening will completely attenuate int ExpirationClose;// after how many bars the volumes of orders for closing will completely attenuate int ProfitPointsCorridorPart;// half corridor size for the profit of all orders int LossPointsCorridorPart;// half corridor size for the loss of all orders int OrdersToOneBar;// orders of one type per 1 bar ENUM_GRID_WEIGHT WeightStopLimitFillingType; PositionGenerator( ENUM_GRID_WEIGHT WeightStopLimitFillingType0 ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0 ,int ExpirationOpenLimit0,int ExpirationOpenStop0 ,int ExpirationClose0 ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0 ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) : BarBox(OrdersToOneBar0,BarsTotal0) { VolumeAlphaStop=VolumeAlphaStop0; VolumeAlphaLimit=VolumeAlphaLimit0; VolumeAlphaMarket=VolumeAlphaMarket0; OrdersToOneBar=OrdersToOneBar0; HalfCorridorLimitStop=double(HalfCorridorLimitStop0)/double(OrdersToOneBar); ExpirationOpenLimit=ExpirationOpenLimit0; ExpirationOpenStop=ExpirationOpenStop0; ExpirationClose=ExpirationClose0; ProfitPointsCorridorPart=ProfitPointsCorridorPart0; LossPointsCorridorPart=LossPointsCorridorPart0; OrdersToOneBar=OrdersToOneBar0; WeightStopLimitFillingType=WeightStopLimitFillingType0; } private: double CalcVolumeDecrease(double TypeWeight,int i,int size)// attenuation volume { if ( size > 1 ) { double K=1.0/(1.0-size); double C=1.0; return TypeWeight*K*i+C; } else return 0.0; } double CalcVolumeSimple(double TypeWeight)// equal volume { return TypeWeight; } void RebuildStops()// rebuild stop orders { int size=ArraySize(BarOrders[0].BuyStopOrders); for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ )// reset all { BarOrders[j].BuyStopOrders[i].Status=STATUS_VIRTUAL;// reset status to initial BarOrders[j].BuyStopOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid BarOrders[j].BuyStopOrders[i].VolumeStart=BarOrders[j].BuyStopOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[j].BuyStopOrders[i].UpPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close BarOrders[j].BuyStopOrders[i].LowPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close BarOrders[j].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop; BarOrders[j].BuyStopOrders[i].BarsExpirationClose=ExpirationClose; BarOrders[j].SellStopOrders[i].Status=STATUS_VIRTUAL; BarOrders[j].SellStopOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid BarOrders[j].SellStopOrders[i].VolumeStart=BarOrders[j].SellStopOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[j].SellStopOrders[i].UpPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close BarOrders[j].SellStopOrders[i].LowPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close BarOrders[j].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop; BarOrders[j].SellStopOrders[i].BarsExpirationClose=ExpirationClose; } } } void RebuildLimits()// rebuild limit orders { int size=ArraySize(BarOrders[0].BuyLimitOrders); for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ )// reset all { BarOrders[j].BuyLimitOrders[i].Status=STATUS_VIRTUAL;// reset status to initial BarOrders[j].BuyLimitOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid BarOrders[j].BuyLimitOrders[i].VolumeStart=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[j].BuyLimitOrders[i].UpPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close BarOrders[j].BuyLimitOrders[i].LowPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close BarOrders[j].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit; BarOrders[j].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose; BarOrders[j].SellLimitOrders[i].Status=STATUS_VIRTUAL; BarOrders[j].SellLimitOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid BarOrders[j].SellLimitOrders[i].VolumeStart=BarOrders[j].SellLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[j].SellLimitOrders[i].UpPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close BarOrders[j].SellLimitOrders[i].LowPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close BarOrders[j].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit; BarOrders[j].SellLimitOrders[i].BarsExpirationClose=ExpirationClose; } } } void RebuildMarkets()// rebuild market orders { int size=ArraySize(BarOrders[0].BuyMarketOrders); double MarketStep; for ( int j=ArraySize(BarOrders)-1; j>0; j-- ) { MarketStep=(High[j+1]-Low[j+1])/double(OrdersToOneBar); for ( int i=0; i<size; i++ )// reset all { BarOrders[j].BuyMarketOrders[i].Status=STATUS_MARKET;// reset status to initial BarOrders[j].BuyMarketOrders[i].WantedPrice=Low[j+1]+MarketStep*i;// prices of the order grid BarOrders[j].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid BarOrders[j].BuyMarketOrders[i].VolumeStart=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[j].BuyMarketOrders[i].UpPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close BarOrders[j].BuyMarketOrders[i].LowPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close BarOrders[j].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose; BarOrders[j].SellMarketOrders[i].Status=STATUS_MARKET; BarOrders[j].SellMarketOrders[i].WantedPrice=High[j+1]-MarketStep*i;// prices of the order grid BarOrders[j].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid BarOrders[j].SellMarketOrders[i].VolumeStart=BarOrders[j].SellMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[j].SellMarketOrders[i].UpPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close BarOrders[j].SellMarketOrders[i].LowPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close BarOrders[j].SellMarketOrders[i].BarsExpirationClose=ExpirationClose; } } } ///// Fast methods void RebuildStopsFast()// rebuild stop orders { int size=ArraySize(BarOrders[0].BuyStopOrders); for ( int j=ArraySize(BarOrders)-1; j>0; j-- ) { for ( int i=0; i<size; i++ )// shift orders { BarOrders[j].BuyStopOrders[i]=BarOrders[j-1].BuyStopOrders[i]; BarOrders[j].SellStopOrders[i]=BarOrders[j-1].SellStopOrders[i]; BarOrders[j].SellStopOrders[i].IndexMarket++; } } for ( int i=0; i<size; i++ )// create a new grid at a new bar { BarOrders[0].BuyStopOrders[i].Status=STATUS_VIRTUAL;// reset status to initial BarOrders[0].BuyStopOrders[i].WantedPrice=Close[1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid BarOrders[0].BuyStopOrders[i].VolumeStart=BarOrders[0].BuyStopOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[0].BuyStopOrders[i].UpPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//upper border to close BarOrders[0].BuyStopOrders[i].LowPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close BarOrders[0].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop; BarOrders[0].BuyStopOrders[i].BarsExpirationClose=ExpirationClose; BarOrders[0].SellStopOrders[i].Status=STATUS_VIRTUAL; BarOrders[0].SellStopOrders[i].WantedPrice=Close[1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid BarOrders[0].SellStopOrders[i].VolumeStart=BarOrders[0].SellStopOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[0].SellStopOrders[i].UpPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close BarOrders[0].SellStopOrders[i].LowPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close BarOrders[0].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop; BarOrders[0].SellStopOrders[i].BarsExpirationClose=ExpirationClose; } } void RebuildLimitsFast()// rebuild limit orders { int size=ArraySize(BarOrders[0].BuyLimitOrders); for ( int j=ArraySize(BarOrders)-1; j>0; j-- ) { for ( int i=0; i<size; i++ )// shift orders { BarOrders[j].BuyLimitOrders[i]=BarOrders[j-1].BuyLimitOrders[i]; BarOrders[j].SellLimitOrders[i]=BarOrders[j-1].SellLimitOrders[i]; BarOrders[j].SellLimitOrders[i].IndexMarket++; } } for ( int i=0; i<size; i++ )// create a new grid at a new bar { BarOrders[0].BuyLimitOrders[i].Status=STATUS_VIRTUAL;// reset status to initial BarOrders[0].BuyLimitOrders[i].WantedPrice=Open[1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid BarOrders[0].BuyLimitOrders[i].VolumeStart=BarOrders[0].BuyLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[0].BuyLimitOrders[i].UpPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close BarOrders[0].BuyLimitOrders[i].LowPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close BarOrders[0].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit; BarOrders[0].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose; BarOrders[0].SellLimitOrders[i].Status=STATUS_VIRTUAL; BarOrders[0].SellLimitOrders[i].WantedPrice=Open[1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid BarOrders[0].SellLimitOrders[i].VolumeStart=BarOrders[0].SellLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[0].SellLimitOrders[i].UpPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close BarOrders[0].SellLimitOrders[i].LowPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower order to close BarOrders[0].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit; BarOrders[0].SellLimitOrders[i].BarsExpirationClose=ExpirationClose; } } void RebuildMarketsFast()// rebuild market orders { int size=ArraySize(BarOrders[0].BuyMarketOrders); double MarketStep; for ( int j=ArraySize(BarOrders)-1; j>0; j-- ) { for ( int i=0; i<size; i++ )// shift orders { BarOrders[j].BuyMarketOrders[i]=BarOrders[j-1].BuyMarketOrders[i]; BarOrders[j].SellMarketOrders[i]=BarOrders[j-1].SellMarketOrders[i]; } } MarketStep=(High[1]-Low[1])/double(OrdersToOneBar); for ( int i=0; i<size; i++ )// create a new grid at a new bar { BarOrders[0].BuyMarketOrders[i].Status=STATUS_MARKET;// reset status to initial BarOrders[0].BuyMarketOrders[i].WantedPrice=Low[1]+MarketStep*i;// prices of the order grid BarOrders[0].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid BarOrders[0].BuyMarketOrders[i].VolumeStart=BarOrders[0].BuyMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[0].BuyMarketOrders[i].UpPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close BarOrders[0].BuyMarketOrders[i].LowPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close BarOrders[0].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose; BarOrders[0].SellMarketOrders[i].Status=STATUS_MARKET; BarOrders[0].SellMarketOrders[i].WantedPrice=High[1]-MarketStep*i;// prices of the order grid BarOrders[0].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid BarOrders[0].SellMarketOrders[i].VolumeStart=BarOrders[0].SellMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid BarOrders[0].SellMarketOrders[i].UpPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close BarOrders[0].SellMarketOrders[i].LowPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close BarOrders[0].SellMarketOrders[i].BarsExpirationClose=ExpirationClose; } } protected: void CreateNewOrders()// create new orders at each candlestick { if ( VolumeAlphaStop != 0.0 ) RebuildStops(); if ( VolumeAlphaLimit != 0.0 ) RebuildLimits(); if ( VolumeAlphaMarket != 0.0 ) RebuildMarkets(); } void CreateNewOrdersFast()// { if ( VolumeAlphaStop != 0.0 ) RebuildStopsFast(); if ( VolumeAlphaLimit != 0.0 ) RebuildLimitsFast(); if ( VolumeAlphaMarket != 0.0 ) RebuildMarketsFast(); } public: virtual void Update()// state updating function (will be expanded in child classes) { CreateNewOrders(); } virtual void UpdateFast()// fast state update { CreateNewOrdersFast(); } };
사실 이 클래스는 Update() 및 UpdateFast() 메서드의 구현만 생성하는데 후자가 훨씬 빠르다는 점만 다를 뿐 비슷합니다. 이러한 메서드는 각각의 바에서 새로운 주문을 생성하고 이전 주문을 삭제하여 주문의 수명 주기를 시뮬레이션할 그 다음 클래스를 위한 데이터를 준비합니다. 이 클래스에는 유형, 미체결 호가, 볼륨 및 추가적인 작업에 필요한 기타 중요한 매개변수를 포함한 모든 필수 주문 매개변수가 할당됩니다.
다음 클래스는 주문 시뮬레이션 프로세스와 거래에 필요한 매개변수의 계산을 구현하고 이를 기반으로 신호를 생성합니다:
class Simulation:public PositionGenerator // then assemble a simulator of positions (inherited from the position generator) {// market parameter calculations will also performed in this class protected: double BuyPercent;// percent of open Buy positions double SellPercent;// percent of open Sell positions double StartVolume;// starting total volume of open Buy positions (the same for Buys and Sells) double RelativeVolume;// relative volume double SummVolumeBuy;// total volume for Buys double SummVolumeSell;// total volume for Sells public: Simulation( ENUM_GRID_WEIGHT WeightStopLimitFillingType0 ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0 ,int ExpirationOpenLimit0,int ExpirationOpenStop0 ,int ExpirationClose0 ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0 ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) :PositionGenerator(WeightStopLimitFillingType0 ,HalfCorridorLimitStop0,OrdersToOneBar0,BarsTotal0 ,ExpirationOpenLimit0,ExpirationOpenStop0 ,ExpirationClose0 ,ProfitPointsCorridorPart0,LossPointsCorridorPart0 ,VolumeAlphaStop0,VolumeAlphaLimit0,VolumeAlphaMarket0) { CreateNewOrders(); CalculateStartVolume();// calculate starting volumes UpdateVirtual();// first update virtual orders to process part of them as market orders in the next function UpdateMarket();// now update the state of all market orders CalculateCurrentVolume();// calculate current volumes of all open orders CalculatePercent();// calculate the percentage of positions CalculateRelativeVolume();// calculate relative volume } double GetBuyPercent()// get the percentage of open Buy deals { return BuyPercent; } double GetSellPercent()// get the percentage of open Sell deals { return SellPercent; } double GetRelativeVolume()// get relative volume { return RelativeVolume; } virtual void Update() override { PositionGenerator::Update();// call everything that was before UpdateVirtual();// first update virtual orders to process part of them as market orders in the next function UpdateMarket();// now update the state of all market orders CalculateCurrentVolume();// calculate current volumes of all open orders CalculatePercent();// calculate the percentage of positions CalculateRelativeVolume();// calculate relative volume } virtual void UpdateFast() override { PositionGenerator::UpdateFast();// call everything that was before UpdateVirtualFast();// first update virtual orders to process part of them as market orders in the next function UpdateMarketFast();// now update the state of all market orders CalculateCurrentVolume();// calculate current volumes of all open orders CalculatePercent();// calculate the percentage of positions CalculateRelativeVolume();// calculate relative volume } private: void UpdateVirtual()// update the status of virtual orders { int size=ArraySize(BarOrders[0].BuyLimitOrders); int SizeBarOrders=ArraySize(BarOrders); if ( VolumeAlphaLimit != 0.0 ) { for ( int i=SizeBarOrders; i>0; i-- )// update the state of limit orders simulating each candlestick { for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[i] && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[i] )// if the order is virtual and is inside a candlestick, then it turns into a market one { BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET; BarOrders[j].BuyLimitOrders[k].IndexMarket = i; } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[i] && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[i] )// the same { BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET; BarOrders[j].SellLimitOrders[k].IndexMarket = i; } /////// Check for interest expiration of limit players if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen ) BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen); if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0; } } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen ) BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen); if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0; } } } } } } if ( VolumeAlphaStop != 0.0 ) { for ( int i=SizeBarOrders; i>0; i-- )// update the state of limit orders simulating each candlestick { for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].SellStopOrders[k].WantedPrice <= High[i] && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[i] )// the same { BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET; BarOrders[j].SellStopOrders[k].IndexMarket = i; } if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[i] && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[i] )// the same { BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET; BarOrders[j].BuyStopOrders[k].IndexMarket = i; } /////// Check for interest expiration of stop and limit players if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen ) BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen); if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0; } } if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen ) BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen); if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0; } } } } } } } void UpdateMarket()// update the status of market orders { int size=ArraySize(BarOrders[0].BuyLimitOrders); int SizeBarOrders=ArraySize(BarOrders); if ( VolumeAlphaLimit != 0.0 ) { for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick { for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { // Block for closing when prices change if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);// with profit BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);// with loss } if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);//with profit BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);//with loss } if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0; } // End of lock for closing when prices change // Block for closing when time changes****************************************************** if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose); if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose); if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0; } } } } } if ( VolumeAlphaStop != 0.0 ) { for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick { for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { // Block for closing when prices change if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);// with profit BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);// with loss } if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);//with profit BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);//with loss } if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0; } // End of lock for closing when prices change // Block for closing when time changes****************************************************** if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose); if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose); if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0; } } } } } if ( VolumeAlphaMarket != 0.0 ) { for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick { for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { // Block for closing when prices change /// For obviously market positions if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);// with profit BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);// with loss } if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0; if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);// with profit BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);// with loss } if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0; // End of lock for closing when prices change // Block for closing when time changes****************************************************** /// For obviously market positions if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose); if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0; if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose); if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0; // } } } } } /// fast methods**** void UpdateVirtualFast()// update the status of virtual orders { int SizeBarOrders=ArraySize(BarOrders); int size=ArraySize(BarOrders[0].BuyLimitOrders); if ( VolumeAlphaLimit != 0.0 ) { for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[1] && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[1] )// if the order is virtual and is inside a candlestick, then it turns into a market one { BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET; BarOrders[j].BuyLimitOrders[k].IndexMarket = 1; } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[1] && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[1] )// the same { BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET; BarOrders[j].SellLimitOrders[k].IndexMarket = 1; } /////// Check for interest expiration of limit players if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen ) BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen); if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0; } } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen ) BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen); if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0; } } } } } if ( VolumeAlphaStop != 0.0 ) { for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].SellStopOrders[k].WantedPrice <= High[1] && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[1] )// the same { BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET; BarOrders[j].SellStopOrders[k].IndexMarket = 1; } if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[1] && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[1] )// the same { BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET; BarOrders[j].BuyStopOrders[k].IndexMarket = 1; } /////// Check for interest expiration of stop players if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen ) BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen); if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0; } } if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )// { if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen ) BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED; else { if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen); if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0; } } } } } } void UpdateMarketFast()// update the status of market orders { int size=ArraySize(BarOrders[0].BuyLimitOrders); int SizeBarOrders=ArraySize(BarOrders); if ( VolumeAlphaLimit != 0.0 ) { for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { // Block for closing when prices change if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);// with profit BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);// with loss } if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);// with profit BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);// with loss } if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0; } // End of lock for closing when prices change // Block for closing when time changes****************************************************** if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose); if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose); if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0; } // } } } if ( VolumeAlphaStop != 0.0 ) { for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { // Block for closing when prices change if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);// with profit BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);// with loss } if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);// with profit BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);// with loss } if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0; } // End of lock for closing when prices change // Block for closing when time changes****************************************************** if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose); if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0; } if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )// { if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose); if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0; } // } } } if ( VolumeAlphaMarket != 0.0 ) { for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one { for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick { /// For obviously market positions if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);// with profit BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);// with loss } if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0; if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 ) { BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);// with profit BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);// with loss } if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0; // End of lock for closing when prices change // Block for closing when time changes****************************************************** /// For obviously market positions if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose); if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0; if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose); if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0; // } } } } ///****** void CalculateStartVolume()// calculate the starting total volume of all positions (relative to it we will estimate market fullness) { StartVolume=0; int size=ArraySize(BarOrders[0].BuyStopOrders); if ( VolumeAlphaStop != 0.0 ) { for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ ) { StartVolume+=BarOrders[j].BuyStopOrders[i].VolumeStart; } } } if ( VolumeAlphaLimit != 0.0 ) { size=ArraySize(BarOrders[0].BuyLimitOrders); for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ ) { StartVolume+=BarOrders[j].BuyLimitOrders[i].VolumeStart; } } } if ( VolumeAlphaMarket != 0.0 ) { size=ArraySize(BarOrders[0].BuyMarketOrders); for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ ) { StartVolume+=BarOrders[j].BuyMarketOrders[i].VolumeStart; } } } } void CalculateCurrentVolume()// calculate the current total volume of all positions { SummVolumeBuy=0; SummVolumeSell=0; int size=ArraySize(BarOrders[0].BuyStopOrders); if ( VolumeAlphaStop != 0.0 ) { for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ ) { if ( BarOrders[j].BuyStopOrders[i].Status == STATUS_MARKET ) SummVolumeBuy+=BarOrders[j].BuyStopOrders[i].VolumeAlpha; if ( BarOrders[j].SellStopOrders[i].Status == STATUS_MARKET ) SummVolumeSell+=BarOrders[j].SellStopOrders[i].VolumeAlpha; } } } if ( VolumeAlphaLimit != 0.0 ) { size=ArraySize(BarOrders[0].BuyLimitOrders); for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ ) { if ( BarOrders[j].BuyLimitOrders[i].Status == STATUS_MARKET ) SummVolumeBuy+=BarOrders[j].BuyLimitOrders[i].VolumeAlpha; if ( BarOrders[j].SellLimitOrders[i].Status == STATUS_MARKET ) SummVolumeSell+=BarOrders[j].SellLimitOrders[i].VolumeAlpha; } } } if ( VolumeAlphaMarket != 0.0 ) { size=ArraySize(BarOrders[0].BuyMarketOrders); for ( int j=ArraySize(BarOrders)-1; j>=0; j-- ) { for ( int i=0; i<size; i++ ) { SummVolumeBuy+=BarOrders[j].BuyMarketOrders[i].VolumeAlpha; SummVolumeSell+=BarOrders[j].SellMarketOrders[i].VolumeAlpha; } } } } void CalculatePercent()// calculate the percentage of Buys and Sells relative to all positions { if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) BuyPercent=100.0*SummVolumeBuy/(SummVolumeBuy+SummVolumeSell); else BuyPercent=50; if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) SellPercent=100.0*SummVolumeSell/(SummVolumeBuy+SummVolumeSell); else SellPercent=50; } void CalculateRelativeVolume()// calculate relative volumes of Buys and Sells (calculate only uncovered part of positions) { if ( SummVolumeBuy >= SummVolumeSell ) RelativeVolume=(SummVolumeBuy-SummVolumeSell)/StartVolume; else RelativeVolume=(SummVolumeSell-SummVolumeBuy)/StartVolume; } };
전체 코드는 MetaTrader 4와 MetaTrader 5 모두에 유효합니다. 이러한 클래스는 두 플랫폼 모두에서 컴파일 될 수 있습니다. 물론 MetaTrader 5에서는 MQL4에서와 마찬가지로 미리 정의된 배열을 구현해야 합니다. 저는 여기에서 이 코드를 제공하지 않겠습니다. 여러분은 첨부된 소스 코드에서 확인할 수 있습니다. 제 코드는 그다지 독창적이지 않습니다. 이제 우리에게 남은 일은 거래를 담당하는 변수와 이 변수와 관련된 기능을 구현하기만 하면 됩니다. 두 터미널용의 EA는 아래에 첨부되어 있습니다.
코드가 매우 리소스 집약적이기 때문에 분석을 시작할 때는 느린 로직의 구현을, 바 작업을 할 때는 빠른 구현을 구성했습니다. 모든 Expert Advisor는 인위적인 틱의 생성에 의존하지 않고 모든 틱-테스트의 바람직하지 않은 결과를 피하기 위해 바로 작업합니다. 물론 함수가 느려지는 것을 완전히 피할 수는 없습니다. 하지만 그런 경우 경우에는 분석을 시작하지 않았을 것입니다. 저 같은 경우 클래스의 본문 외부에서 기능을 구현하는 것은 제 생각으로는 전체적인 통합성을 헤치는 것으로 봅니다.
마지막 클래스 생성자는 다음과 같은 매개 변수를 입력으로 받습니다. 여러분이 원하는 경우 이 생성자는 클래스 인스턴스일 뿐이므로 이러한 시뮬레이션을 여러 번 만들 수 있습니다:
input bool bPrintE=false;// print market parameters input CLOSE_MODE CloseModeE=CLOSE_FAST;// order closing mode input WORK_MODE ModeE=MODE_SIMPLE;// simulation mode input ENUM_GRID_WEIGHT WeightFillingE=WEIGHT_SAME;// weight distribution type input double LimitVolumeE=0.5;// significance of limit orders input double StopVolumeE=0.5;// significance of stop orders input double MarketVolume=0.5;// significance of market orders input int ExpirationBars=100;// bars for full expiration of open orders input int ExpirationOpenStopBars=1000;// patience of a stop player in bars, after which the order is canceled input int ExpirationOpenLimitBars=1000;// patience of a limit player in bars, after which the order is canceled input int ProfitPointsCloseE=200;// points to close with profit input int LossPointsCloseE=400;// points to close with loss input int HalfCorridorE=500;// half-corridor for limit and stop orders input int OrdersToOneBarE=50;// orders for half-grid per 1 bar input int BarsE=250;// bars for analysis input double MinPercentE=60;// minimum superiority of one trading side in percentage input double MaxPercentE=80;// maximum percentage input double MinRelativeVolumeE=0.0001;// minimum market filling [0...1] input double MaxRelativeVolumeE=1.00;// maximum market filling [0...1]
모든 것이 간단하고 명확하므로 여기서는 제가 거래와 관련된 변수를 제공하지 않겠습니다.
거래가 구현되는 저의 함수는 다음과 같습니다:
void Trade() { if ( Area0 == NULL ) { CalcAllMQL5Values(); Area0 = new Simulation(WeightFillingE,HalfCorridorE,OrdersToOneBarE,BarsE ,ExpirationOpenLimitBars,ExpirationOpenStopBars,ExpirationBars,ProfitPointsCloseE,LossPointsCloseE ,StopVolumeE,LimitVolumeE,MarketVolume); } switch(ModeE) { case MODE_SIMPLE: Area0.Update();// update simulation case MODE_FAST: Area0.UpdateFast();// fast update simulation } if (bPrintE) { Print("BuyPercent= ",Area0.GetBuyPercent()); Print("SellPercent= ",Area0.GetSellPercent()); Print("RelativeVolume= ",Area0.GetRelativeVolume()); } if ( CloseModeE == CLOSE_FAST && Area0.GetBuyPercent() > 50.0 ) { if ( !bInvert ) CloseBuyF(); else CloseSellF(); } if ( CloseModeE == CLOSE_FAST && Area0.GetSellPercent() > 50.0 ) { if ( !bInvert ) CloseSellF(); else CloseBuyF(); } if ( Area0.GetBuyPercent() > MinPercentE && Area0.GetBuyPercent() < MaxPercentE && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE ) { if ( !bInvert ) { CloseBuyF(); SellF(); } else { CloseSellF(); BuyF(); } } if ( Area0.GetSellPercent() > MinPercentE && Area0.GetSellPercent() < MaxPercentE && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE ) { if ( !bInvert ) { CloseSellF(); BuyF(); } else { CloseBuyF(); SellF(); } } }
원하는 경우 더 나은 거래 조건과 더 복잡한 함수를 만들 수 있지만 현재까지는 그럴 필요가 없어 보입니다. 저는 항상 트레이딩과 논리적 부분을 분리하려고 노력합니다. 로직은 객체에서 구현됩니다. 시뮬레이션 객체는 첫 번째 트리거 시 트레이딩 함수에서 동적으로 생성되며 초기화 중에 삭제됩니다. 이는 MQL5에서 사전 정의된 배열을 사용할 수 없기 때문이며 MQL4와 유사한 클래스 작동을 보장하기 위해 우리는 사전 정의된 배열을 인위적으로 생성해야 합니다.
작업 설정을 어떻게 찾으 수 있을까요?
제 경험에 비추어 볼 때 수동으로 설정을 선택하는 것이 더 나은 거 같습니다. 또한 짧은 차트 주기로 실행되고 수학적 기대치가 작은 EA의 경우 첫 단계에서 스프레드를 무시하는 것이 좋습니다. 이렇게 하면 초반에 수익률 신호를 놓치지 않는 데 도움이 됩니다. MetaTrader 4는 이러한 목적에 매우 적합합니다. 성공적인 검색을 한 이후에는 항상 최종적인 수학적 기대치와 수익률(신호 강도)을 높이기 위해 수정, 편집 등의 작업이 뒤따릅니다. 또한 입력 데이터의 구조는 이상적으로는 독립적인 작동 모드를 허용해야 합니다. 즉, 한 설정은 다른 설정의 값과 상관없이 시스템 성능에 가장 독립적인 영향을 미쳐야 합니다. 이러한 접근 방식은 발견된 모든 신호를 함께 결합하는 균형적인 설정을 통해 전체 신호를 향상시킬 수 있습니다. 이러한 구현이 항상 가능한 것은 아니지만 우리의 경우 각각이 주문 유형을 개별적으로 분석하기 때문에 적용 가능합니다.
Expert Advisor 테스트
이 Expert Advisor는 아무런 준비 없이 처음부터 작성되었습니다. 그래서 이 EA 코드는 저에게 새로운 코드입니다. 비슷한 EA가 있었지만 훨씬 더 단순하고 로직이 완전히 달랐습니다. 저는 이 글의 목적이 시장 물리학을 완전히 설명하지 못하더라도 정확한 기본을 갖춘 아이디어는 어느 정도 성과를 보여줄 가능성이 매우 높다는 것을 보여주기 위한 것이라는 점을 강조하고 싶습니다. 시스템이 기본적인 성능 능력을 보여 주면 우리는 이를 테스트하고 이 시스템에서 무엇이 효과가 있는지 찾아내어 다음 단계의 시장을 이해하는 방향으로 나아갈 수 있습니다. 다음 단계는 여러분이 직접 생성하는 더 나은 품질의 EA를 가정합니다.
저는 EA를 테스트할 때 며칠 동안 인내심을 가지고 거래 매개변수를 선택해야 했습니다. 모든 것이 지루하고 길었지만 잘 작동하는 설정을 찾을 수 있었습니다. 물론 이러한 설정은 매우 약하고 시뮬레이션은 하나의 주문 유형만 사용하여 수행되지만 이는 일부러 그렇게 한 것입니다. 특정 주문 유형이 신호에 어떤 영향을 미치는지 개별적으로 분석한 다음 다른 주문 유형을 시뮬레이션 하는 것이 더 낫기 때문입니다. 다음은 MetaTrader 4의 결과입니다:
먼저 MetaTrader 4용 버전을 만들어 스프레드를 최대한 낮춰서 테스트했습니다. 사실 패턴을 검색하려면 모든 틱을 확인해야 하며 특히 짧은 기간의 틱은 더욱 그렇습니다. MetaTrader 5 버전을 테스트할 때는 MetaTester 5가 시스템 재량으로 조정할 수 있는 스프레드 때문에 이를 확인할 수 없습니다.
이 기능은 스트레스 테스트와 실제 시스템 성능을 평가하는 데 완벽한 도구입니다. 이 기능은 마지막 단계에서 실제 틱에 대해 시스템을 테스트하는 데 사용해야 합니다. 우리의 경우 MetaTrader 4에서 시스템 테스트를 시작하는 것이 좋습니다. 저는 여러분이 이 접근 방식을 사용하도록 권장합니다. 왜냐하면 다섯 번째 버전에서 즉시 테스트를 시작하면 더 나은 품질의 설정의 기초가 될 수 있는 우수한 설정 옵션을 많이 잃을 지도 모르기 때문입니다.
여러 가지 이유로 최적화를 사용하지 않을 것을 권장합니다. 그 이유는 최적화는 단순한 매개변수의 반복이기 때문입니다. 만약 여러분이 매개 변수를 수동으로 시도하지 않으면 어떻게 작동하는지 이해할 수 없습니다. 만약 여러분이 오래된 라디오를 가져 다가 손잡이에 전기 모터를 연결하고 라디오를 틀면 라디오 방송국을 찾지 못할 가능성이 큽니다. 여기에서도 마찬가지입니다. 무언가를 찾았다고 해도 그것이 무엇인지 이해하기는 어려울 것입니다.
또 다른 매우 중요한 측면은 주문의 개수입니다. 바의 개수에 대해 더 많은 주문이 열릴수록 더 강력한 물리학이 발견되고 그러면 향후 성능에 도움이 됩니다. 또한 발견된 패턴이 스프레드 내부에 있을 수 있을수 있습니다. 그런데 이러한 경우를 통제하지 않으면 시스템이 무용지물이 될 수 있다는 점을 기억하세요!
그런 점에서 MetaTrader 5 테스터가 필요합니다. MetaTrader 5에서는 실제 틱을 사용하여 전략을 테스트할 수 있습니다. 안타깝게도 통화쌍과 상품에 대해 비교적 최근 기간의 실제 틱만이 존재합니다. 실제 틱과 매우 엄격한 스프레드 요건을 사용하여 MetaTrader 5가 2020년도에 작동하는지 확인합니다. 하지만 먼저 이전에 사용한 기간의 '모든 틱' 모드에서 시스템을 테스트해 보겠습니다:
이 테스트 모드는 실제 틱을 사용하는 테스트 모드보다 좋지 않습니다. 그러나 초기 결과의 신호 중 극히 일부만 여기에 남아 있는 것은 분명합니다. 그럼에도 불구하고 스프레드를 커버하고 심지어 작은 수익을 제공하는 신호가 있습니다. 테스터가 오래된 기록으로부터 가져온 스프레드에 대해서는 잘 모르겠습니다. 이 테스트에서 랏은 0.01이었는데, 이는 수학적 기대치가 5 포인트 라는 것을 의미하며 차트는 그다지 좋지 않지만 원래 테스트보다 훨씬 더 높습니다. 이 데이터를 신뢰할 수 있는 이유는 초기 테스트에서 100,000건의 거래로 구성된 방대한 샘플이 있기 때문입니다.
이제 지난 한 해를 살펴보겠습니다:
이 테스트에서는 랏을 0.1로 설정했기 때문에 수학적 기대치는 23.4 포인트로 MetaTrader 4의 초기 테스트에서 기대치가 3포인트에 불과했던 것을 감안하면 상당히 좋은 수치입니다. 앞으로 기대치가 낮아질 수도 있지만 큰 폭으로 낮아지지는 않을 것입니다. 따라서 손익분기점 거래에는 여전히 충분할 것입니다.
두 터미널에 사용한 EA는 아래 첨부 파일에서 확인할 수 있습니다. 여러분들은 설정을 변경하고 펜딩 및 마켓 오더에 대한 작동 매개변수를 찾을 수 있을 것입니다. 그런 다음 세트를 결합하고 몇 가지 평균 매개 변수를 설정할 수 있습니다. 안타깝게도 이를 최대한 활용할 시간이 충분하지 않았기 때문에 추가적인 조치가 필요할 수도 있습니다.
물론 이 EA가 차트에서 간단히 실행하여 즐길 수 있는 바로 사용 가능한 EA가 아니라는 점에 유의하세요. 다른 유형의 주문을 사용하여 시뮬레이션을 테스트한 다음 설정을 결합하고 EA가 안정적인 거래 결과를 보여줄 때까지 이 두 가지 테스트를 반복합니다. 일부 필터를 도입하거나 알고리즘 자체를 조정할 수도 있습니다. 테스트에서 볼 수 있듯이 더 많은 개선이 가능합니다. 따라서 여러분은 첨부된 프로그램을 사용하여 안정적인 결과를 얻기 위해 다듬을 수 있습니다. 저는 긴 차트 주기에서 적합한 설정을 찾지 못했습니다. EA는 M5에서 가장 잘 작동하지만 다른 차트 주기를 찾을 수도 있습니다. 저는 다른 통화쌍에서 이 세트가 어떻게 작동하는지 확인할 시간이 없었습니다. 그러나 일반적으로 이러한 평평한 선은 EA가 다른 통화 쌍에서도 작동한다는 것을 의미합니다. 시간이 된다면 EA를 계속 개선하도록 노력하겠습니다. 이 글을 쓰면서 저는 EA에서 몇 가지 결함과 오류를 발견했기 때문에 아직 해야 할 일이 많습니다.
결론
시뮬레이터는 이 분석 방법에 대한 좋은 기대치를 입증했습니다. 첫 번째 결과를 얻은 후 부터는 결과를 개선하는 방법과 알고리즘을 현대화하고 더 좋고 빠르며 가변적으로 만드는 방법에 대해 생각할 때입니다. 이 접근 방식의 가장 큰 장점은 단순하다는 것입니다. 저는 논리 구현에 대해 이야기하는 것이 아니라 물리적 관점에서 이 방법의 기본 논리에 대해 이야기하고 있습니다.
우리가 가격 변동에 영향을 미치는 모든 요인을 고려할 수는 없습니다. 그러나 그러한 요인이 하나 이상 있더라도(이를 확실하게 설명할 수 없을지라도) 부정확한 코드 설명이 특정한 신호를 생성할 수 있습니다. 이러한 신호들의 경우 품질이 매우 높은 것은 아니지만 이러한 아이디어로 수익을 창출하기에는 충분합니다. 시장에 영향을 미치는 모든 요인을 고려할 필요는 없습니다. 그러므로 이상적인 공식을 찾으려고 너무 노력하지 마세요.
또한 저의 접근 방식을 그대로 사용할 필요는 없습니다. 이 글의 목적은 시장을 부분적으로 나마 설명할 수 있는 시장 물리학을 활용하고 그 아이디어를 증명할 수 있는 EA를 작성하는 것이었습니다. 그 결과 저는 그러한 가정이 작동하는 시스템의 기반이 될 수 있다는 것을 보여주는 EA를 개발했습니다.
또한 코드에 크고 작은 결함과 버그가 있다는 사실도 고려하세요. 그럼에도 EA는 작동합니다. 여러분들이 더 다듬어야 합니다. 다음 글에서는 더 간단하고 효과적이며 모두에게 알려진 또 다른 유형의 다중 자산 시장 분석에 대해 알려 드리겠습니다. 이번에도 우리는 EA를 만들 것입니다.
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/8411
![조합론과 트레이딩 확률(4부): 베르누이 논리](https://c.mql5.com/2/44/ieu9.png)
![Expert Advisor 개발 기초부터(28부): 미래를 향해(III)](https://c.mql5.com/2/48/development__4.png)
![조합과 트레이딩을 위한 확률(5부): 곡선 분석](https://c.mql5.com/2/43/bvmb.png)
![트레이딩 전문가 어드바이저를 처음부터 개발하기(27부): 다음을 향해(II)](https://c.mql5.com/2/48/development__3.png)