MT5自動売買ソフトの作成8 自動売買ソフトサンプルを見てみる、その1
自動売買ソフトウェアを作る、とは?
MT5ではエキスパートアドバイザと言います。
裁量トレードをするときには、インジケータを参考にしてオーダーします。自動売買ソフトウェアでも同様でインジケータを参考にしてオーダーしますが、ソフトウェアが自動でオーダーすることだけが違います。
こう書くと簡単ですが、インジケータよりもソフトウェアはだいぶ面倒になります。
- インジケータからのシグナルで売買もしくはクローズします。
- オーダーがある場合は売買しません。
- オーダー時にはロット、ストップロス、テイクプロフィットを計算もしくは指定します。
いろいろFX用語が出てきたので簡単に解説します。
用語 | 意味 |
---|---|
裁量トレード | 手動売買です |
トレード | 売り買いすることです |
売買 | 売り/買い、buy/sell、ロング/ショートなどと言います。 |
クローズ | オーダーを決済します。 |
ストップロス | 損失を確定する値です。オーダー時に設定します。 |
テイクプロフィット | 利益を確定する値です。オーダー時に設定します。 |
ロット | 取引量をオーダー時に設定します。(いくら売買するかということです) |
この中で特にロットを決めるのが面倒です。
まずは面倒なことは後回しにして固定ロットで考えてしまうのが良いと思いますが、なぜ面倒なのかは知っておきましょう。
売買は口座残高=証拠金が無いとオーダーできません。当然ですね、何を買うにしてもお金が必要です。
国内FXブローカーでは個人の場合、レバレッジが25倍上限です。またゼロカットシステムは法律で禁じられています。
海外FXブローカーではブローカーによりますが、レバレッジが500倍前後です。またゼロカットシステムが適用されるところもあります。
レバレッジとは口座残高を担保として信用取引を行う掛け率という感じでしょうか。
具体的に書いた方がわかりやすいので、例えば1万円口座にあるとします。500倍であれば、500万円の取引ができます。
ただし!500万円分のドルを買ったとします。もし0.001円でも下がったら破産します・・・マージンがありませんからね。
破産することをロスカット(ブローカーによって限界の金額が違います)と言いますが、ロスカットになっても追証(マイナス分の支払い)が無いのがゼロカットシステムです。
ロスカットにならないようにロットを決めるにはレバレッジ、口座残高、ストップロスから計算する必要があります。
複数オーダーを扱いたい場合は、扱うロット数やロットごとのオーバー値、ストップロス値を計算して、適切なロットを決めなければいけません。
面倒ですよね。
まずはロット、ストップロス、テイクプロフィットを入力パラメータにしてしまいましょう。
そうすると自動売買ソフトウェアで処理するのは、インジケータのタイミングで売買、決済をするだけになります。
MT5自動売買のサンプルコード
Experts\Examples\Moving Average.mq5
を見てみましょう。長いのでロット計算関数を端折って入力にします。
#include <Trade\Trade.mqh> input double MaximumRisk = 0.02; // Maximum Risk in percentage input double DecreaseFactor = 3; // Descrease factor input int MovingPeriod = 12; // Moving Average period input int MovingShift = 6; // Moving Average shift input double Rots = 1.0; // ロット数(サンプルには無いです) //--- int ExtHandle=0; bool ExtHedging=false; CTrade ExtTrade; #define MA_MAGIC 1234501 //+------------------------------------------------------------------+ //| Calculate optimal lot size | //+------------------------------------------------------------------+ double TradeSizeOptimized(void) { // : // ロット数を計算しています。 // : return Rots; } //+------------------------------------------------------------------+ //| Check for open position conditions | //+------------------------------------------------------------------+ void CheckForOpen(void) { MqlRates rt[2]; //--- go trading only for first ticks of new bar if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates of ",_Symbol," failed, no history"); return; } if(rt[1].tick_volume>1) return; //--- get current Moving Average double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer from iMA failed, no data"); return; } //--- check signals ENUM_ORDER_TYPE signal=WRONG_VALUE; if(rt[0].open>ma[0] && rt[0].close<ma[0]) signal=ORDER_TYPE_SELL; // sell conditions else { if(rt[0].open<ma[0] && rt[0].close>ma[0]) signal=ORDER_TYPE_BUY; // buy conditions } //--- additional checking if(signal!=WRONG_VALUE) { if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100) ExtTrade.PositionOpen(_Symbol,signal,TradeSizeOptimized(), SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK), 0,0); } //--- } //+------------------------------------------------------------------+ //| Check for close position conditions | //+------------------------------------------------------------------+ void CheckForClose(void) { MqlRates rt[2]; //--- go trading only for first ticks of new bar if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates of ",_Symbol," failed, no history"); return; } if(rt[1].tick_volume>1) return; //--- get current Moving Average double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer from iMA failed, no data"); return; } //--- positions already selected before bool signal=false; long type=PositionGetInteger(POSITION_TYPE); if(type==(long)POSITION_TYPE_BUY && rt[0].open>ma[0] && rt[0].close<ma[0]) signal=true; if(type==(long)POSITION_TYPE_SELL && rt[0].open<ma[0] && rt[0].close>ma[0]) signal=true; //--- additional checking if(signal) { if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100) ExtTrade.PositionClose(_Symbol,3); } //--- } //+------------------------------------------------------------------+ //| Position select depending on netting or hedging | //+------------------------------------------------------------------+ bool SelectPosition() { bool res=false; //--- check position in Hedging mode if(ExtHedging) { uint total=PositionsTotal(); for(uint i=0; i<total; i++) { string position_symbol=PositionGetSymbol(i); if(_Symbol==position_symbol && MA_MAGIC==PositionGetInteger(POSITION_MAGIC)) { res=true; break; } } } //--- check position in Netting mode else { if(!PositionSelect(_Symbol)) return(false); else return(PositionGetInteger(POSITION_MAGIC)==MA_MAGIC); //---check Magic number } //--- result for Hedging mode return(res); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- prepare trade class to control positions if hedging mode is active ExtHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); ExtTrade.SetExpertMagicNumber(MA_MAGIC); ExtTrade.SetMarginMode(); ExtTrade.SetTypeFillingBySymbol(Symbol()); //--- Moving Average indicator ExtHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE); if(ExtHandle==INVALID_HANDLE) { printf("Error creating MA indicator"); return(INIT_FAILED); } //--- ok return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(void) { //--- if(SelectPosition()) CheckForClose(); else CheckForOpen(); //--- } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+
さてコードを見ても全くわかりませんね。
エキスパートアドバイザで必須な関数は3つです。
int OnInit(void) void OnTick(void) void OnDeinit(const int reason)
OnInit関数はエキスパートアドバイザがチャートにドラッグドロップされた時に1回だけ実行される、初期化関数です。
(入力パラメータを変更されても実行されます)
OnTick関数はチャートが動いたとき=通貨ペアの値が変化したときに呼び出されます。
OnDeinit関数はエキスパートアドバイザが終了するとき=チャートから削除された時に呼び出されます。
要はOnTick関数で売買と決済を繰り返せば良いということですね。
サンプルを見ると・・・その通りです。
if(SelectPosition()) CheckForClose(); else CheckForOpen();
CheckForOpen関数で売買を、CheckForClose関数で決済をしています。
SelectPosition関数は現在オーダーしているかをチェックしています。(オーダーするとポジションを持つことになるのでPositionという名称を使っています)
CheckForOpen関数を見てみましょう。
移動平均が始値と終値の間にあると売買するようです。陽線(上昇)では買い、陰線(下降)では売りをしています。
if(rt[0].open>ma[0] && rt[0].close<ma[0]) signal=ORDER_TYPE_SELL; // sell conditions else { if(rt[0].open<ma[0] && rt[0].close>ma[0]) signal=ORDER_TYPE_BUY; // buy conditions }
CheckForClose関数では決済をします。
if(type==(long)POSITION_TYPE_BUY && rt[0].open>ma[0] && rt[0].close<ma[0]) signal=true; if(type==(long)POSITION_TYPE_SELL && rt[0].open<ma[0] && rt[0].close>ma[0]) signal=true;
買いポジションを持っているときは移動平均が始値よりも小さく終値よりも大きい場合(陰線)、売りポジションを持っているときは移動平均が始値よりも大きく終値よりも小さい場合(陽線)にクローズします。
買いは移動平均が陽線に突っ込んだらオーダー、陰線に突っ込んだらクローズして、売りは移動平均が陰線に突っ込んだらオーダー、陽線に突っ込んだらクローズするということでしょう。
動かして売買を確認できます。
次回
次回は自動売買ソフトウェアを動かしてみましょう。