
가격 상관 관계 통계 데이터를 기반으로 신호 필터링
여기에 이르기까지
이 기사를 쓰게 된 계기는 세계 최고 투자 기록 보유자(1987년 자본금 11,000% 증가)가 이론이 풍부하고 가난한 대학 교수들과 다른 학자들의 신화를 완전히 떨쳐버리고 있다는 래리 윌리엄스의 책 "단기 트레이딩의 장기 비밀" 을 읽고 난 후 떠올랐습니다. 시장을 잘 알고 있어서.."과거 가격 행태와 미래의 추세 사이에 상관관계가 없는 것에 대해 설명합니다.
만약 동전을 100번 던지면, 50번은 앞면이, 50번은 뒷면이 위로 떨어질겁니다. 연속해서 동전을 던질 때, 앞면이 나올 확률은 50%, 뒷면도 마찬가지입니다. 모든 것은 우연에 의존하며 기록되지도 않기에 몇 번을 던져도 확률은 바뀌지 않습니다. 시장이 코인의 혼돈과 같다고 가정해보십시오.
따라서 새 막대가 나타나면 가격이 오르내릴 기회가 동일하며 이전 막대는 현재 막대에 전혀 영향을 미치지 않습니다. 맘마미아! 거래 시스템을 생성하고, 이익실현을 정지 손절매보다 크게 설정하면(즉, 예상 계산 설정을 양수값의 영역으로), 그러면 끝납니다. 약간의 숨고르기. 하지만 문제는 시장의 행동에 대한 우리의 가정은 사실이 아니라는 것입니다. 솔직히 말하면, 괴상할 정도죠! 이에 대해 입증하겠습니다.
MQL5 Wizard를 이용하여 Expert Advisor 템플릿을 만들고 간단한 영숫자 개입을 사용하여 과제 수행에 적합한 조건으로 제시합니다. Expert Advisor를 인코딩하여 1, 2, 3 막대를 닫은 후의 매수를 시뮬레이션합니다. 시뮬레이션은 프로그램이 분석된 막대의 패러미터를 단순히 기억한다는 것을 의미합니다. 스프레드 및 스왑이 수신된 정보의 신뢰성이 의심받을 수 있기 때문에 이 경우 주문 전송(일반적인 방식)이 작동하지 않습니다.
코드는 다음과 같습니다:
//+------------------------------------------------------------------+ //| explorer.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //---Variables--- double profit_percent,open_cur,close_cur; double profit_trades=0,loss_trades=0,day_cur,hour_cur,min_cur,count; double open[],close[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { /* Calculate percent of closures with increase from the total number */ profit_percent=NormalizeDouble(profit_trades*100/(profit_trades+loss_trades),2); Print("Percent of closures with increase ",profit_percent,"%"); // Enter data to the Journal } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //---find out the time--- MqlDateTime time; // Create a structure to store time TimeToStruct(TimeCurrent(),time); // Structuring the data day_cur=time.day_of_week; // Receive the value of the current day hour_cur=time.hour; // Receive the current hour min_cur=time.min; // Receive the current minute //---Find out the prices--- CopyOpen(NULL,0,0,4,open);ArraySetAsSeries(open,true); CopyClose(NULL,0,0,4,close);ArraySetAsSeries(close,true); if(close[1]<open[1]/*&&close[2]<open[2]&&close[3]<open[3]*/ && count==0) // If it closed with a loss { open_cur=open[0]; // Remember open price of the current bar count=1; } if(open_cur!=open[0] && count==1) // The current bar has closed { close_cur=close[1]; // Remember the close price of the formed bar count=0; if(close_cur>=open_cur)profit_trades+=1; // If the close price is higher than open, else loss_trades+=1; // +1 to closures with profit, otherwise +1 to closures with loss } } //+------------------------------------------------------------------+
테스트는 EUR/USD로, 2000년 1월 1일부터 2010년 12월 31일까지의 구간을 대상으로 실시됩니다.
1번 그림. 증가한 청산 비율
(첫 번째 열은 단일, 이중, 삼중 청산 후 두 번째, 세 번째, 네 번째 전체 기간에 대한 데이터를 표시합니다)
이것이 제가 말하고자했던 것입니다! 매매가 항상 손실을 만회하려 하기 때문에 이전의 막대는 현재 막대에 상당히 큰 영향을 미칩니다.
일 보 전진입니다
좋습니다! 각격 변동이 우연이 아니라는 것을 깨달았으니 우리는 이 놀라운 사실을 활용해야합니다. 물론, 독립 매매 시스템 기준으로는 충분치 않습니다만, 끈질기고 종종 오류가 섞이기까지하는 신호들에서 자유롭게 해준다는 점 만으로도 가치가 있습니다. 구현해보도록 하죠!
다음이 필요합니다:
- 최소한 지난 1년간 긍정적인 결과를 낳은 자동 매매 시스템.
- 가격변동에 상관관계가 있음을 확인하는 재미있는 예.
L. Williams가 저작한 책에서 많은 아이디어를 얻었습니다. 그 중 하나를 공유해드리겠습니다.
매매일(TDW, Trade Day of Week) 전략.만약 한 주의 며칠간은 매수만 할 것이고, 다른 날들엔 숏 포지션만 연다면 어떤 일이 벌어지는지 보게 될 것입니다. . 결국, 우리는 하루 안에 가격이 다른 날보다 더 큰 비율로 오른다고 가정할 수 있습니다. 이유가 뭘까요? 지정학적 상황, 거시경제 통계 또는 A. Elder가 자신의 책에 저술한 대로. 월요일과 화요일은 개미들의 날이고 목요일과 금요일은 전문가들의 시간이라고요? 이해해보도록 노력해봅시다.
먼저, 우리는 매 주의 특정 일에만 할 것이고, 그 다음엔 매도만 할 것입니다. 연구를 마치면 최상의 결과를 얻을 수 있으며 이는 거래 시스템에 대한 필터가 될 것입니다. 그나저나, 그에 관해 첨언하고 싶은 것이 몇 있습니다. 아주 당연하죠!
이 시스템은 두 개의 MAs와 MACDake에 기반을 두고 있습니다. 신호:
- 빠른 이동 평균이 느린 이동 평균을 아래에서 위로 가로지르고 MACD 히스토그램이 0 선보다 아래라면 매수.
- 빠른 이동 평균이 느린 이동 평균을 위에서 아래로 가로지르고 MACD 히스토그램이 0 선보다 위라면 매도.
한 지점에서 후행 정지를 사용하여 위치를 종료합니다. 랏이 고정되었습니다 - 0,1.
편의 목적으로 제가 별도의 헤더 파일에 Expert Advisor 클래스를 넣어 두었습니다:
//+------------------------------------------------------------------+ //| moving.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Класс my_expert | //+------------------------------------------------------------------+ class my_expert { // Creating a class // Closed class members private: int ma_red_per,ma_yel_per; // Periods of MAs int ma_red_han,ma_yel_han,macd_han; // Handles double sl,ts; // Stop orders double lots; // Lot double MA_RED[],MA_YEL[],MACD[]; // Arrays for the indicator values MqlTradeRequest request; // Structure of a trade request MqlTradeResult result; // Structure of a server response // Open class members public: void ma_expert(); // Constructor void get_lot(double lot){lots=lot;} // Receiving a lot void get_periods(int red,int yel){ma_red_per=red;ma_yel_per=yel;} // Receiving the periods of MAs void get_stops(double SL,double TS){sl=SL;ts=TS;} // Receiving the values of stops void init(); // Receiving the indicator values bool check_for_buy(); // Checking for buy bool check_for_sell(); // Checking for sell void open_buy(); // Open buy void open_sell(); // Open sell void position_modify(); // Position modification }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* Function definition */ //---Constructor--- void my_expert::ma_expert(void) { //--- Reset the values of variables ZeroMemory(ma_red_han); ZeroMemory(ma_yel_han); ZeroMemory(macd_han); } //---The function for receiving the indicator values--- void my_expert::init(void) { ma_red_han=iMA(_Symbol,_Period,ma_red_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the slow MA ma_yel_han=iMA(_Symbol,_Period,ma_yel_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the fast MA macd_han=iMACD(_Symbol,_Period,12,26,9,PRICE_CLOSE); // Handle of MACDaka //---Copy data into arrays and set indexing like in a time-series--- CopyBuffer(ma_red_han,0,0,4,MA_RED); CopyBuffer(ma_yel_han,0,0,4,MA_YEL); CopyBuffer(macd_han,0,0,2,MACD); ArraySetAsSeries(MA_RED,true); ArraySetAsSeries(MA_YEL,true); ArraySetAsSeries(MACD,true); } //---Function to check conditions to open buy--- bool my_expert::check_for_buy(void) { init(); //Receive values of indicator buffers /* If the fast MA has crossed the slow MA from bottom up between 2nd and 3rd bars, and there was no crossing back. MACD-hist is below zero */ if(MA_RED[3]>MA_YEL[3] && MA_RED[1]<MA_YEL[1] && MA_RED[0]<MA_YEL[0] && MACD[1]<0) { return(true); } return(false); } //----Function to check conditions to open sell--- bool my_expert::check_for_sell(void) { init(); //Receive values of indicator buffers /* If the fast MA has crossed the slow MA from up downwards between 2nd and 3rd bars, and there was no crossing back. MACD-hist is above zero */ if(MA_RED[3]<MA_YEL[3] && MA_RED[1]>MA_YEL[1] && MA_RED[0]>MA_YEL[0] && MACD[1]>0) { return(true); } return(false); } //---Open buy--- /* Form a standard trade request to buy */ void my_expert::open_buy(void) { request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; request.volume=lots; request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK); request.sl=request.price-sl*_Point; request.tp=0; request.deviation=10; request.type=ORDER_TYPE_BUY; request.type_filling=ORDER_FILLING_FOK; OrderSend(request,result); return; } //---Open sell--- /* Form a standard trade request to sell */ void my_expert::open_sell(void) { request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; request.volume=lots; request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID); request.sl=request.price+sl*_Point; request.tp=0; request.deviation=10; request.type=ORDER_TYPE_SELL; request.type_filling=ORDER_FILLING_FOK; OrderSend(request,result); return; } //---Position modification--- void my_expert::position_modify(void) { if(PositionGetSymbol(0)==_Symbol) { //If a position is for our symbol request.action=TRADE_ACTION_SLTP; request.symbol=_Symbol; request.deviation=10; //---If a buy position--- if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { /* if distance from price to stop loss is more than trailing stop and the new stop loss is not less than the previous one */ if(SymbolInfoDouble(Symbol(),SYMBOL_BID)-PositionGetDouble(POSITION_SL)>_Point*ts) { if(PositionGetDouble(POSITION_SL)<SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts) { request.sl=SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts; request.tp=PositionGetDouble(POSITION_TP); OrderSend(request,result); } } } //---If it is a sell position--- else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { /* if distance from price to stop loss is more than the trailing stop value and the new stop loss is not above the previous one. Or the stop loss from the moment of opening is equal to zero */ if((PositionGetDouble(POSITION_SL)-SymbolInfoDouble(Symbol(),SYMBOL_ASK))>(_Point*ts)) { if((PositionGetDouble(POSITION_SL)>(SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts)) || (PositionGetDouble(POSITION_SL)==0)) { request.sl=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts; request.tp=PositionGetDouble(POSITION_TP); OrderSend(request,result); } } } } } //+------------------------------------------------------------------
"MQL5의 객체 지향 접근으로 Expert Advisor 작성하기" 문서의 저자분께 경의를 표합니다. 그게 없었더라면 전 아무것도 할 수 없었을 것입니다! 만약 악랄하지만서도 몹시 쓸모있는 객체 지향 프로그래밍에 익숙치 않으신 분은 이 문서를 읽는 것을 추천드립니다.
Expert Advisor의 메인 코드에 클래스와 파일을 추가한다? 객체를 생성하고 함수를 초기화합니다:
//+------------------------------------------------------------------+ //| Moving.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //---Include a file with the class--- #include <moving.mqh> //---External Variables--- input int MA_RED_PERIOD=7; // The period of a slow MA input int MA_YEL_PERIOD=2; // The period of a fast MA input int STOP_LOSS=800; // Stop loss input int TRAL_STOP=800; // Trailing stop input double LOTS=0.1; // Lot //---Create an object--- my_expert expert; //---Initialize the MqlDataTime structure--- MqlDateTime time; int day_of_week; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //---Initialize the EA expert.get_periods(MA_RED_PERIOD,MA_YEL_PERIOD); // Set the MA periods expert.get_lot(LOTS); // Set the lot expert.get_stops(STOP_LOSS,TRAL_STOP); // Set stop orders return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { TimeToStruct(TimeCurrent(),time); day_of_week=time.day_of_week; if(PositionsTotal()<1) { if(day_of_week==5 && expert.check_for_buy()==true){expert.open_buy();} else if(day_of_week==1 && expert.check_for_sell()==true){expert.open_sell();} } else expert.position_modify(); } //+------------------------------------------------------------------+
끝났네요! 몇가지 특수 기능을 언급하고 넘어가고 싶습니다. 소프트웨어 단에서 한 주의 요일들을 구분하기 위해 저는 MqlDateTime 구조를 사용했습니다. 첫째, 현재 서버 시간을 구조화된 형식으로 변환합니다. 현재(1-월, ...일, 5-금)에 대한 인덱스를 입수하여 설정한 값과 비교합니다.
시도해보세요! 지루한 조사와 추가 자릿수로 부담을 주지 않기 위해 모든 결과를 표로 가져오겠습니다.
여기에 있습니다:
테이블 1. 주중 매수 요약
테이블 2. 주중 매도 요약
최고의 결과는 녹색으로, 최악의 결과는 주황색으로 표기되어 있습니다.
위에서 설명한 조치 후에 시스템이 낮은 상대적 하락과 함께 수익을 보장해야 하며, 매매마다 고수익을 올리면서 상당한 비율의 거래를 성사시켜야 한다고 예약합니다 (여기선 매매가 적을수록 좋음).
보다시피 가장 효과적인 시스템은 금요일에 매수하고 월요일에 매도하는 것입니다. 이 두가지 조건을 합치면:
if(PositionsTotal()<1){ if(day_of_week==5&&expert.check_for_buy()==true){expert.open_buy();} else if(day_of_week==1&&expert.check_for_sell()==true){expert.open_sell();}} else expert.position_modify();
이제 Expert Advisor가 포지션을 양쪽으로 열지만 딱 정해진 날에만 하게 됩니다 이해하기 쉬우시도록 필터를 넣은, 그리고 넣지 않은 도표를 각각 그리겠습니다:
2번 그림. 필터를 사용하지 않은 EA 테스트 결과(EURUSD, H1, 01.01.2010-31.12.2010,)
3번 그림. 필터를 사용한 EA 테스트 결과(EURUSD, H1, 01.01.2010-31.12.2010,)
결과가 어떻습니까? 필터를 사용하는 것으로 매매 시스템이 더 안정적으로 변합니다. 수정하기 전에 Expert Advisor는 주로 테스트 기간의 상반기에 잔고를 증가시켰으며, "업그레이드" 후에는 전체 기간 동안 잔고를 증가시켰습니다.
이제 리포트들을 비교합니다:
테이블 3. 필터를 사용하기 전과 후의 결과
유일한 골칫거리는 순이익이 거의 1000달러(26%)나 감소한 것입니다. 하지만 거래 건수는 거의 3, 5분의 1으로 줄이고 있습니다. 첫째, 손실을 보는 매매를 시도할 가능성을 크게 줄이고 둘째, 스프레드 비용(218*2-62*2=312 USD, EUR/USD에 한함). 을 얻어낼 가능성이 57%로 상승했는데, 이는 꽤나 의미있습니다. 매매 당 이득은 14% 올라 113 USD가 되었고요. L. Williams라면 이리 말했을 겁니다: "이거야 말로 매매할 가치가 있는 액수지!"
마치며
가격변동은 랜덤이 아닙니다 - 이것은 명백한 사실입니다. 이 사실은 충분히 이용할 수 있으며, 이용해야만 합니다. 거래 시스템의 성능을 향상시킬 수 있는 무수한 변형과 기법의 극히 일부에 불과한 예를 하나 드렸습니다. 그러나 이러한 다양성엔 결점이 있습니다. 모든 필터를 통합할 수 있는 것은 아니므로 모든 가능한 시나리오를 고려하여 신중하게 선택해야 합니다.
필터가 아무리 뛰어나더라도 수익을 올릴 매매 또한 솎아내는 경우가 있단 것을 잊지 마세요... 행운을 빕니다!
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/269



