基礎から学ぶシステムトレード
シストレブログ人気ランキング  

シストレ徹底攻略シストレマスターへの道ワールド・トレーディング・エッジ基礎から学ぶシステムトレードシストレニュースシストレツールシストレナビTOPへ

 

著書のご紹介

2010年09月21日(火)

MQL5でEA(4) [MetaTrader5]

今回は、簡単な売買システムをEAとして作成してみます。

作成する売買システムは、終値が移動平均線を上回ったら買い、下回ったら売りという単純なルールとします。またポジションは反対シグナルが出たところで途転売買し、常にどちらかのポジションが建っているものとします。

このシステムで必要なテクニカル指標は移動平均だけです。まずは、移動平均をiMA()という組込み関数を使って実現してみます。

考え方は、カスタム指標プログラムでテクニカル指標を表示させたのとほぼ同じです。

まずは、外部変数として

// テクニカル指標ハンドル
int hMA;
// iMA()用配列
double BufMA[];

を宣言します。

次に、初期化関数である OnInit()で、

// テクニカル指標の初期化
hMA = iMA(NULL, 0, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);

と記述し、iMA()を初期化します。

同じく OnInit()で、
ArraySetAsSeries(BufMA, true);

として、BufMA[]という配列を時系列配列に設定します。ここは、カスタム指標プログラムの場合と違うところです。

EAでは最新のバーを基準にして過去の数本のバーにおけるテクニカル指標の値を使います。時系列配列に設定するのは、最新のバーをBufMA[0]、1本前のバーをBufMA[1]、2本前のバーをBufMA[2] という形で使えた方が便利だからです。

但し、この状態ではまだBufMA[]は使えません。BufMA[]として使えるようにするには、CopyBuffer()でデータをコピーする必要があります。

ここでもカスタム指標プログラムと違う点があります。カスタム指標プログラムでは、チャート全体分のBufMA[]をコピーしたのですが、EAではチャート全体のデータは必要なく、せいぜい数本のバーだけで構いません。

ここでは、最新のバーから3個のデータのみをコピーすることにしてみます。

テクニカル指標のコピーは、Bid、Askなどと同じくRefreshRates()関数の中に次のように書いておきます。
int count = 3;
CopyBuffer(hMA, 0, 0, count, BufMA);

これで、BufMA[0]、BufMA[1]、BufMA[2]の3個の値を使うことができるようになりました。

最後にOnDeinit()関数でiMA()用のメモリを解放しておきます。
// テクニカル指標の解放
IndicatorRelease(hMA);

ここまで準備ができれば、後は簡単です。

前回作成したオープンポジションのロット数を返す関数 OpenLots()と、成行オーダーを送信する関数OrderSendMarket()を利用すると、売買ルールは次のようにOnTick()関数中に書くことができます。

// ティック関数
void OnTick()
{
if(!RefreshRates()) return;
double pos = OpenLots();
double lots = MathAbs(pos) + Lots;
if(pos <= 0 && Close[1] > BufMA[1]) OrderSendMarket(ORDER_TYPE_BUY, lots);
if(pos >= 0 && Close[1] < BufMA[1]) OrderSendMarket(ORDER_TYPE_SELL, lots);
}

これは最も簡単な書き方で、ポジションがないか売りポジションでかつ、1本前の終値が1本前の移動平均線より上のときに買いオーダーを実行します。

また、ポジションがないか買いポジションでかつ、1本前の終値が1本前の移動平均線より下のときに買いオーダーを実行します。

ここで、売買ロット数lotsがMathAbs(pos) + Lots となっているのは、MQL5ではオープンポジションが合算されるので、途転売買の際に、一旦ポジションを決済してからオープンし直さなくても、2倍のロット数で反対売買を行えばよいからです。

こう書いておくと、ポジションがない場合は、lots=Lots、ポジションがある場合は、lots=2*Lots となり、ちょうど途転売買に対応できるのです。

但し、このままでは、一番最初のオーダーが移動平均と終値の交差を待たずに勝手に入ってしまいます。そこで、交差をチェックして売買するために、もう1本前の Close[2]とBufMA[2]を見て

if(pos <= 0 && Close[2] <= BufMA[2] && Close[1] > BufMA[1])
OrderSendMarket(ORDER_TYPE_BUY, lots);
if(pos >= 0 && Close[2] >= BufMA[2] && Close[1] < BufMA[1])
OrderSendMarket(ORDER_TYPE_SELL, lots);

のように書くこともできます。

全体のプログラムリストを以下に示します。

// パラメータ
input int MAPeriod = 20;
input double Lots = 0.1;
input int Slippage = 2;

// レート
double Bid, Ask;
double Close[];

// テクニカル指標ハンドル
int hMA;

// iMA()用配列
double BufMA[];

// 初期化関数
int OnInit()
{
// テクニカル指標の初期化
hMA = iMA(NULL, 0, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);
ArraySetAsSeries(BufMA, true);
ArraySetAsSeries(Close, true);

return(0);
}

// 終了関数
void OnDeinit(const int reason)
{
// テクニカル指標の解放
IndicatorRelease(hMA);
}

// ティック関数
void OnTick()
{
if(!RefreshRates()) return;
double pos = OpenLots();
double lots = MathAbs(pos) + Lots;
if(pos <= 0 && Close[2] <= BufMA[2] && Close[1] > BufMA[1]) OrderSendMarket(ORDER_TYPE_BUY, lots);
if(pos >= 0 && Close[2] >= BufMA[2] && Close[1] < BufMA[1]) OrderSendMarket(ORDER_TYPE_SELL, lots);
}

// レートの更新
bool RefreshRates()
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick)) return(false);
Bid = tick.bid;
Ask = tick.ask;

int count = 3;
if(CopyClose(_Symbol, _Period, 0, count, Close) < count) return(false);
if(CopyBuffer(hMA, 0, 0, count, BufMA) < count) return(false);
return(true);
}

// 成行オーダー
void OrderSendMarket(ENUM_ORDER_TYPE type, double lots)
{
MqlTradeRequest request;
MqlTradeResult result;

request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
if(type == ORDER_TYPE_BUY) request.price = Ask;
else if(type == ORDER_TYPE_SELL) request.price = Bid;
request.deviation = Slippage;
request.volume = lots;
request.type = type;

OrderSend(request,result);
Print("OrderSendRetCode = ", result.retcode);
}

// オープンポジションのロット数
double OpenLots()
{
double lots = 0.0;
if(PositionSelect(_Symbol))
{
lots = PositionGetDouble(POSITION_VOLUME);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) lots = -lots;
}
return(lots);
}

Posted at 14時39分 パーマリンク


2010年09月13日(月)

MQL5でEA(3) [MetaTrader5]

前回はトレード関数OrderSend()の簡単な使い方について説明しました。

今回は、EAに組み込む際に使いやすいように、OrderSend()を実行する手続きをまとめて関数の形にしてみます。

その前にMQL5というかMT5になってオープンポジションの扱いが大きく変わったので、それについて説明しておきましょう。

MQL4では、OrderSend()で送信した成行や指値、逆指値などのオーダーは、個別にチケット番号が振られ、別々に扱われていました。

オープンポジションについても同じで、次のように同じ通貨ペアで複数のポジションを個別に見ることができました。また買いポジションと売りポジションを同時に取る両建ても可能でした。

MQL4のオープンポジション
ポジション1:USD/JPY 0.1 ロット 買いポジション
ポジション2:USD/JPY 0.1 ロット 買いポジション
ポジション3:USD/JPY 0.1 ロット 買いポジション
ポジション4:USD/JPY 0.1 ロット 売りポジション


ところが、MQL5では、指値や逆指値の待機オーダーは別々に扱われますが、一旦約定してオープンポジションになってしまうと、それらは通貨ペア毎にまとめて扱われます。

つまり、上の4ポジションは、売りポジションと買いポジションが相殺され、さらに残りの2つの買いポジションもまとめられて1つのポジション
MQL5のオープンポジション
ポジション:USD/JPY 0.2 ロット 買いポジション

だけになってしまいます。

ここでは、MQL5特有のポジションの扱いを頭において、売買システムを考えていくことにします。

まずは成行でポジションを建てる関数を作ってみます。リストを以下に示します。

// 成行オーダー
void OrderSendMarket(ENUM_ORDER_TYPE type, double lots)
{
MqlTradeRequest request;
MqlTradeResult result;

request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
if(type == ORDER_TYPE_BUY) request.price = Ask;
else if(type == ORDER_TYPE_SELL) request.price = Bid;
request.deviation = Slippage;
request.volume = lots;
request.type = type;

OrderSend(request,result);
Print("OrderSendRetCode = ", result.retcode);
}

MQL4のOrderSend()のように引数を何個もとってもよいのですが、ここでは簡単のため、買いか売りかを指定する「type」と、売買ロット数「lots」だけを引数としてみます。

OrderSend()の使い方は前回の記事とだいたい同じです。違うところだけ説明します。

成行オーダーの場合、 request.action に、「TRADE_ACTION_DEAL」を代入します。

引数の「type」は、ENUM_ORDER_TYPEという型になっていますが、この引数はそのまま、request.typeに代入されます。ここでは、成行買いと成行売りのみを指定できるようにしますが、その識別子はそれぞれ、ORDER_TYPE_BUYORDER_TYPE_SELLとなっています。

request.priceは、売りと買いで異なるので、typeがORDER_TYPE_BUYの場合、買いなので、Askを、typeがORDER_TYPE_SELLの場合、売りなので、Bidを代入するようにします。

もう1つ、EAを作成する際に利用する関数として、オープンポジションのロット数を求める関数を作っておきます。ここでは、買いポジションの場合、プラス売りポジションの場合、マイナスの値としてロット数を返す関数にしてみます。

// オープンポジションのロット数
double OpenLots()
{
double lots = 0.0;
if(PositionSelect(_Symbol))
{
lots = PositionGetDouble(POSITION_VOLUME);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) lots = -lots;
}
return(lots);
}

まず、PositionSelect(_Symbol)は、挿入したチャートの通貨ペアでオープンポジションがあるかどうかを判別する関数で、オープンポジションがあれば trueを返し、なければfalse を返します。

オープンポジションがある場合、PositionGetDouble(POSITION_VOLUME)でロット数を求めます。

ただし、売りポジションの場合、マイナスの値として返すために、PositionGetInteger(POSITION_TYPE)でポジションのタイプを求め、それが、POSITION_TYPE_SELL(売りポジション)の場合、lotsをマイナスにします。

以上、二つの関数を作ってみましたが、これらをテストするための簡単なスクリプトプログラムを次に示します。

input double Lots = 0.1;
input int Slippage = 2;
double Bid, Ask;

void OnStart()
{
if(!RefreshRates()) return;

Print("Open Lots = ", OpenLots());

int ret = MessageBox("Buy at market", "TradeTest", MB_OKCANCEL);
if(ret == IDOK) OrderSendMarket(ORDER_TYPE_BUY, Lots);

ret = MessageBox("Sell at market", "TradeTest", MB_OKCANCEL);
if(ret == IDOK) OrderSendMarket(ORDER_TYPE_SELL, Lots);
}

// レートの更新
bool RefreshRates()
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick)) return(false);
Bid = tick.bid;
Ask = tick.ask;
return(true);
}

// 成行オーダー
void OrderSendMarket(ENUM_ORDER_TYPE type, double lots)
{
MqlTradeRequest request;
MqlTradeResult result;

request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
if(type == ORDER_TYPE_BUY) request.price = Ask;
else if(type == ORDER_TYPE_SELL) request.price = Bid;
request.deviation = Slippage;
request.volume = lots;
request.type = type;

OrderSend(request,result);
Print("OrderSendRetCode = ", result.retcode);
}

// オープンポジションのロット数
double OpenLots()
{
double lots = 0.0;
if(PositionSelect(_Symbol))
{
lots = PositionGetDouble(POSITION_VOLUME);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) lots = -lots;
}
return(lots);
}

まず、以前の記事で説明したRefreshRates()でBid、Askを更新します。

次に、OpenLots()でオープンポジションのロット数を表示します。

そして、

OrderSendMarket(ORDER_TYPE_BUY, Lots);
OrderSendMarket(ORDER_TYPE_SELL, Lots);

成行き買いオーダーと、成行き売りオーダーを実行しますが、実行するかどうかはユーザーが選択できるように、MessageBox()関数でメッセージボックスを表示します。ここで、Okボタンを押すと、実際にオーダーが実行されるようになっています。

このプログラムで実際に売買を行ってみて、それぞれの関数の動きを確認してみてください。但し、言うまでもないことですが、このプログラムは必ずデモ口座で行ってくださいね。

これでようやくEAを作成する準備ができました。次回は簡単なEAを作成してみます。

Posted at 11時54分 パーマリンク


2010年09月06日(月)

MQL5でEA(2) [MetaTrader5]

今回はEAに欠かせないトレードに関する関数についてみてみましょう。

MQL4では、トレードに関して以下の4つの関数が用意されていました。

OrderSend() - 成行、指値、逆指値売買オーダーの送信
OrderClose() - オープンポジションの決済
OrderDelete() - 指値、逆指値の待機オーダーのキャンセル
OrderModify() - オーダーの変更

MQL5では、これらのトレードに関する機能は、一つの関数OrderSend()に集約されています。但し、このOrderSend()という関数は、MQL4の場合と同じ名前ですが、使い方は全く違います

まず、関数の宣言ですが、
bool  OrderSend(
MqlTradeRequest& request, // query structure
MqlTradeResult& result // structure of the answer
);

となっています。たったこれだけです。つまり、引数は二つしかないのです。

但し、引数の型は、それぞれ「MqlTradeRequest」と、「MqlTradeResult」となっています。

MqlTradeRequest、MqlTradeResultという型なんて聞いたことがないと思いますが、これはデータ型ではなく、MQL5で予め定義されている構造体の識別名なのです。

それぞれの構造体は以下のように定義されています。

struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; // Trade operation type
ulong magic; // Expert Advisor ID (magic number)
ulong order; // Order ticket
string symbol; // Trade symbol
double volume; // Requested volume for a deal in lots
double price; // Price
double stoplimit; // StopLimit level of the order
double sl; // Stop Loss level of the order
double tp; // Take Profit level of the order
ulong deviation; // Maximal possible deviation from the requested price
ENUM_ORDER_TYPE type; // Order type
ENUM_ORDER_TYPE_FILLING type_filling; // Order execution type
ENUM_ORDER_TYPE_TIME type_time; // Order execution time
datetime expiration; // Order expiration time (for the orders of ORDER_TIME_SPECIFIED type)
string comment; // Order comment
};

struct MqlTradeResult
{
uint retcode; // Operation return code
ulong deal; // Deal ticket, if it is performed
ulong order; // Order ticket, if it is placed
double volume; // Deal volume, confirmed by broker
double price; // Deal price, confirmed by broker
double bid; // Current Bid price
double ask; // Current Ask price
string comment; // Broker comment to operation (by default it is filled by the operation description)
};

OrderSend()の仕組みは、大雑把に言うとこうです。MqlTradeRequestの構造体変数でオーダーの種類や価格、SL、TPを指定してOrderSend()に渡し、その結果がMqlTradeResultの構造体変数に格納されて返ってくるといった感じです。

ここでは、それぞれの構造体の詳しい説明は省略します。とりあえず、指値買いオーダーを送信する例についてみてみます。

まずは、MqlTradeRequest、MqlTradeResultの構造体変数を宣言します。
MqlTradeRequest request;
MqlTradeResult result;

この例では、requestresult というのが変数名ですが、必ずしもこの名前にする必要はありません。

次に request という構造体変数のそれぞれのフィールドに適切な値を代入します。

action」というフィールドで、成行、指値、修正、キャンセルなどの指示を行います。それぞれ識別子が決まっており、指値、逆指値オーダーの場合、「TRADE_ACTION_PENDING」を代入します。
request.action = TRADE_ACTION_PENDING;

symbol」というフィールドには、対象となる通貨ペアを代入します。通常は、
request.symbol = _Symbol;

と指定すれば、プログラムを挿入したチャートの通貨ペアとなります。

volume」は売買ロット数です。ここでは、0.1ロットを指定してみます。
request.volume = 0.1;

price」は指値の価格です。ここでは、USD/JPYを対象として80円に設定してみます。
request.price = 80.00;

type」というフィールドでは、買い、売りなどのオーダーの種類を指定します。指値買いの場合、「ORDER_TYPE_BUY_LIMIT」を指定します。
request.type = ORDER_TYPE_BUY_LIMIT;

これで必要最低限の設定は終わりましたので、OrderSend()を実行します。
OrderSend(request,result);

これで実際に指値オーダーが入ればOKですが、うまくいかない場合には、エラーコードを表示させると原因がつかみやすいでしょう。

OrderSend()のエラーコードは、resultのretcodeフィールドに格納されて戻ってきます。
Print("retcode = ",result.retcode);

のように表示させれば、ToolboxウィンドウのExpertsタブにエラーコードが表示されます。

画像(450x286)・拡大画像(800x510)

このように「retcode = 10009」となればOKです。10009というコードはオーダーが正常に実行されたことを意味します。

ただ、その下の行では、「retcode = 10027」となっています。これは、AutoTdradingが無効になっていて、オーダーが送信できなかったということを表します。スクリプトプログラムでも、オーダーを実行するためにはツールバーのAutoTradingのボタンを押して有効にしておく必要があります。

result.retcode は、他にも様々なエラーに対応したコードを返します。詳しくは、MQL5のレファレンスマニュアルのReturn Codes of the Trade Serverを参照してください。

最後に、今回説明したプログラムリストをつけておきます。

void OnStart()
{
MqlTradeRequest request;
MqlTradeResult result;

request.action = TRADE_ACTION_PENDING;
request.symbol = _Symbol;
request.volume = 0.1;
request.price = 80.0;
request.type = ORDER_TYPE_BUY_LIMIT;

OrderSend(request,result);
Print("retcode = ",result.retcode);
}

Posted at 15時20分 パーマリンク


過去の記事へ

ページのトップへ ページのトップへ

Sponsor AD

シストレナビモバイル

シストレナビモバイル
ケータイでバーコードを読み取りアクセス(詳細はこちら

プロフィール

豊嶋久道

2003年よりFX取引を始め、システムトレードの道へ。最近ではFXオプション取引も含めたトレーディングシステムの研究を行っている。システムトレードを基礎から正しく理解するための情報を発信する予定。

活動状況・Website

ToyolabFX
Toyolab FX-手ぶらで為替取引
FXトレーディング研究所。
FXメタトレーダー入門
FXメタトレーダー入門
最先端システムトレードソフト使いこなし術。
FXメタトレーダー実践プログラミング
FXメタトレーダー実践プログラミング
システム開発過程を段階的に学ぶ。

2010/9

      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

PHOTO

ランダムウォークとランダムトレード(6)

ランダムウォークとランダムトレード(6)

カテゴリーリスト

最近の記事

スポンサードリンク

検索


当サイトコメントについて

当コメントは情報提供のみを目的として作成されたものであり、投資に関してはご自身でご判断くださいますようお願い致します。また、当資料は著作物であり著作権法により保護されております。無断で全文または一部を転載することはできません。

RSS1.0

[Login]


powered by a-blog
Copyright (C) 2008 PhiConcept,Inc. All rights reserved.