//+------------------------------------------------------------------+ //| OnTester_Sample.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property description "Sample EA with the OnTester() handler" #property description "As a custom optimization criterion, " #property description "the ratio of the balance graph linear regression" #property description "divided by the deviation mean-square error is returned" //-- 取引操作クラスをインクルードする #include <Trade\Trade.mqh> //--- EA入力パラメータ input double Lots = 0.1; // ボリューム input int Slippage = 10; // 許容されるスリッページ input int MovingPeriod = 80; // 移動平均期間 input int MovingShift = 6; // 移動平均シフト //--- グローバル変数 int IndicatorHandle=0; // 指標ハンドル bool IsHedging=false; // アカウントのフラグ CTrade trade; // 取引操作の実行 //--- #define EA_MAGIC 18052018 //+------------------------------------------------------------------+ //| ポジションを開く条件を確認する | //+------------------------------------------------------------------+ void CheckForOpen(void) { MqlRates rt[2]; //--- 新しいバーの始めのみで取引する if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates of ",_Symbol," failed, no history"); return; } //--- ティックボリューム if(rt[1].tick_volume>1) return; //--- 移動平均値を受け取る double ma[1]; if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1) { Print("CopyBuffer from iMA failed, no data"); return; } //--- シグナルの存在を確認する ENUM_ORDER_TYPE signal=WRONG_VALUE; //--- ローソク足は移動平均より高く開いたが、移動平均より低く閉じた if(rt[0].open>ma[0] && rt[0].close<ma[0]) signal=ORDER_TYPE_BUY; // 買いシグナル else // ローソク足は移動平均より低く開いたが、移動平均より高く閉じた { if(rt[0].open<ma[0] && rt[0].close>ma[0]) signal=ORDER_TYPE_SELL;// 売りシグナル } //--- 追加的確認 if(signal!=WRONG_VALUE) { if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100) { double price=SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ?SYMBOL_BID:SYMBOL_ASK); trade.PositionOpen(_Symbol,signal,Lots,price,0,0); } } //--- } //+------------------------------------------------------------------+ //| ポジションを閉じる条件を確認する | //+------------------------------------------------------------------+ void CheckForClose(void) { MqlRates rt[2]; //--- 新しいバーの始めのみで取引する if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates of ",_Symbol," failed, no history"); return; } if(rt[1].tick_volume>1) return; //--- 移動平均値を受け取る double ma[1]; if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1) { Print("CopyBuffer from iMA failed, no data"); return; } //--- ポジションはPositionSelect()を使用してすでに選択されている bool signal=false; long type=PositionGetInteger(POSITION_TYPE); //--- ローソク足は移動平均より高く開いたが、移動平均より低く閉じた - ショートポジションを決済する if(type==(long)POSITION_TYPE_SELL && rt[0].open>ma[0] && rt[0].close<ma[0]) signal=true; //--- ローソク足は移動平均より低く開いたが、移動平均より高く閉じた - ロングポジションを決済する if(type==(long)POSITION_TYPE_BUY && rt[0].open<ma[0] && rt[0].close>ma[0]) signal=true; //--- 追加的確認 if(signal) { if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100) trade.PositionClose(_Symbol,Slippage); } //--- } //+-------------------------------------------------------------------+ //| 口座タイプ(ネッティングまたはヘッジ)を考慮してポジションを選択する | //+-------------------------------------------------------------------+ bool SelectPosition() { bool res=false; //--- ヘッジ口座のポジションを選択する if(IsHedging) { uint total=PositionsTotal(); for(uint i=0; i<total; i++) { string position_symbol=PositionGetSymbol(i); if(_Symbol==position_symbol && EA_MAGIC==PositionGetInteger(POSITION_MAGIC)) { res=true; break; } } } //--- ネッティング口座のポジションを選択する else { if(!PositionSelect(_Symbol)) return(false); else return(PositionGetInteger(POSITION_MAGIC)==EA_MAGIC); //---check Magic number } //--- 実行結果 return(res); } //+------------------------------------------------------------------+ //| エキスパート初期化関数 | //+------------------------------------------------------------------+ int OnInit(void) { //--- 取引タイプ(ネッティングまたはヘッジ)を設定する IsHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); //--- 正しいポジション制御のためにオブジェクトを初期化する trade.SetExpertMagicNumber(EA_MAGIC); trade.SetMarginMode(); trade.SetTypeFillingBySymbol(Symbol()); trade.SetDeviationInPoints(Slippage); //--- 移動平均指標を作成する IndicatorHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE); if(IndicatorHandle==INVALID_HANDLE) { printf("Error creating iMA indicator"); return(INIT_FAILED); } //--- ok return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| エキスパートティック関数 | //+------------------------------------------------------------------+ void OnTick(void) { //--- ポジションが既に開かれている場合は、決済条件を確認する if(SelectPosition()) CheckForClose(); // ポジションを開く条件を確認する CheckForOpen(); //--- } //+------------------------------------------------------------------+ //| テスタ関数 | //+------------------------------------------------------------------+ double OnTester() { //--- カスタム条件最適化の値(高いほど良い) double ret=0.0; //--- 取引結果を配列に入れる double array[]; double trades_volume; GetTradeResultsToArray(array,trades_volume); int trades=ArraySize(array); //--- 10取引未満の場合、肯定的結果がないことをテストする if(trades<10) return (0); //--- 取引あたりの平均結果 double average_pl=0; for(int i=0;i<ArraySize(array);i++) average_pl+=array[i]; average_pl/=trades; //--- 単一テストモード用のメッセージを表示する if(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_OPTIMIZATION)) PrintFormat("%s: Trades=%d, Average profit=%.2f",__FUNCTION__,trades,average_pl); //--- 利益グラフの線形回帰を計算する double a,b,std_error; double chart[]; if(!CalculateLinearRegression(array,chart,a,b)) return (0); //--- 回帰直線からグラフの偏差の誤差を計算する if(!CalculateStdError(chart,a,b,std_error)) return (0); //--- 傾向偏差の標準偏差を計算する ret=(std_error == 0.0) ? a*trades : a*trades/std_error; //--- カスタム条件最適化値を返す return(ret); } //+------------------------------------------------------------------+ //| 取引の利益/損失の配列を得る | //+------------------------------------------------------------------+ bool GetTradeResultsToArray(double &pl_results[],double &volume) { //--- 完全な取引履歴をリクエストする if(!HistorySelect(0,TimeCurrent())) return (false); uint total_deals=HistoryDealsTotal(); volume=0; //--- 証拠金を持つ配列の初期サイズを、履歴の取引数で設定する ArrayResize(pl_results,total_deals); //--- 取引結果を修正する取引のカウンター - 利益または損失 int counter=0; ulong ticket_history_deal=0; //--- 全ての取引を見る for(uint i=0;i<total_deals;i++) { //--- 取引を選択する if((ticket_history_deal=HistoryDealGetTicket(i))>0) { ENUM_DEAL_ENTRY deal_entry =(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_history_deal,DEAL_ENTRY); long deal_type =HistoryDealGetInteger(ticket_history_deal,DEAL_TYPE); double deal_profit =HistoryDealGetDouble(ticket_history_deal,DEAL_PROFIT); double deal_volume =HistoryDealGetDouble(ticket_history_deal,DEAL_VOLUME); //--- 興味があるのは取引操作のみである if((deal_type!=DEAL_TYPE_BUY) && (deal_type!=DEAL_TYPE_SELL)) continue; //--- 損益を固定する取引のみ if(deal_entry!=DEAL_ENTRY_IN) { //--- 取引結果を配列に書き込み、取引のカウンターを増やす pl_results[counter]=deal_profit; volume+=deal_volume; counter++; } } } //--- 配列の最終サイズを設定する ArrayResize(pl_results,counter); return (true); } //+------------------------------------------------------------------+ //| 線形回帰を計算する y=a*x+b | //+------------------------------------------------------------------+ bool CalculateLinearRegression(double &change[],double &chartline[], double &a_coef,double &b_coef) { //--- データが十分か確認する if(ArraySize(change)<3) return (false); //--- 蓄積されたチャート配列を作成する int N=ArraySize(change); ArrayResize(chartline,N); chartline[0]=change[0]; for(int i=1;i<N;i++) chartline[i]=chartline[i-1]+change[i]; //--- 線形回帰を計算する double x=0,y=0,x2=0,xy=0; for(int i=0;i<N;i++) { x=x+i; y=y+chartline[i]; xy=xy+i*chartline[i]; x2=x2+i*i; } a_coef=(N*xy-x*y)/(N*x2-x*x); b_coef=(y-a_coef*x)/N; //--- return (true); } //+------------------------------------------------------------------+ //| 指定されたaとbの平均二乗偏差誤差を計算する | //+------------------------------------------------------------------+ bool CalculateStdError(double &data[],double a_coef,double b_coef,double &std_err) { //--- 誤差の平方和 double error=0; int N=ArraySize(data); if(N<=2) return (false); for(int i=0;i<N;i++) error+=MathPow(a_coef*i+b_coef-data[i],2); std_err=MathSqrt(error/(N-2)); //--- return (true); } |