2015年4月17日金曜日

クラスを作成する。注文クラス 改造版

EAのサンプルを作成してみようと思いましたが、前回アップしたクラスがあまりにバグだらけだったため、とりあえずバックテストを通して一応^^;;動作したクラスをアップしなおします。
以降のEA関係の記事は、このファイルがInclude/Customフォルダの下にTradingWrapper.mqhというファイル名で保存されている前提です。

//------------------------------------------------------------------
// 注文関係ラッパークラス
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.10"
#property strict

#include 
#include 
#include 

//------------------------------------------------------------------
// 注文関係ラッパークラス
class CTradingWrapper
{
private:
// マジックナンバー
int m_magicNumber;

// 最大ポジション数
int m_maxPosition;

// Pips倍率
int m_pipsRate;

// 対象通貨ペア
string m_symbol;

// 保有ポジション
CArrayInt m_positions ;

// タイムアウト値
uint m_timeout;

// 少数桁数
int m_digit;
public:
//------------------------------------------------------------------
// コンストラクタ
CTradingWrapper(
string symbol,      //対象通貨ペア
int magicNumber,    //マジックナンバー
int maxPosition,    //最大ポジション数
uint timeout        //タイムアウト
);

//------------------------------------------------------------------
// デストラクタ
~CTradingWrapper();

//------------------------------------------------------------------
// 発注する
// Return   発注成功時インデックス それ以外-1
int SendOrder(
int cmd,                //売買種別
double volume,          //売買ロット
double price,           //価格
uint slippage,          //許容スリッピング(Pips単位)
uint stoploss,          //ストップロス(Pips単位)
uint takeprofit,        //利確値(Pips単位)
string comment,         //コメント
datetime expiration,    //注文有効期限
color arrowColor        //注文矢印の色
);

//------------------------------------------------------------------
// 成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CloseOrder(
int index,                    //決済するインデックス
uint slippage,                //許容スリッピング
color arrowColor = clrNONE    //注文矢印の色
);

//------------------------------------------------------------------
// 全ポジションを成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CloseOrderAll(
uint slippage,                //許容スリッピング
color arrowColor = clrNONE    //注文矢印の色
);

//------------------------------------------------------------------
// 注文を変更する。
// Return   発注成功時ture それ以外false
bool ModifyOrder(
int index,              //決済するインデックス
double price,           //価格
uint stoploss,          //ストップロス(Pips単位)
uint takeprofit,        //利確値(Pips単位)
datetime expiration,    //注文有効期限
color arrowColor        //注文矢印の色
);

//------------------------------------------------------------------
// すべての注文を変更する。
bool ModifyOrderAll(
double price,           //価格
uint stoploss,          //ストップロス(Pips単位)
uint takeprofit,        //利確値(Pips単位)
datetime expiration,    //注文有効期限
color arrowColor        //注文矢印の色
);

//------------------------------------------------------------------
// 注文を変更する。
// Return   発注成功時ture それ以外false
bool ModifyOrder(
int index,              //決済するインデックス
uint stoploss,          //ストップロス(Pips単位)
uint takeprofit,        //利確値(Pips単位)
color arrowColor        //注文矢印の色
);

//------------------------------------------------------------------
// すべての注文を変更する。
bool ModifyOrderAll(
uint stoploss,          //ストップロス(Pips単位)
uint takeprofit,        //利確値(Pips単位)
color arrowColor        //注文矢印の色
);

//------------------------------------------------------------------
// 現在のポジション数を取得する。
// Return   現在のポジション数を取得する。
int GetPositionCount(){ return m_positions.Total(); };

//------------------------------------------------------------------
// 最大のポジション数を取得する。
// Return   最大のポジション数を取得する。
int GetMaxPosition() { return m_maxPosition; } ;

//------------------------------------------------------------------
// 1pipsあたりの値を取得する。
// Return   1pipあたりの値
double GetPipsValue() { return Point() * m_pipsRate; } ;

//------------------------------------------------------------------
// ポジションリストを更新する。
void RefreshPositions();

private:

//------------------------------------------------------------------
// 設定パラメータが安全かどうか
// Return 安全ture それ以外false
bool IsSafeParameter();

};

//------------------------------------------------------------------
// コンストラクタ
CTradingWrapper::CTradingWrapper(
string symbol,          //対象通貨ペア
int magicNumber = 0,    //マジックナンバー
int maxPosition = 3,    //最大ポジション数
uint timeout = 1000     //タイムアウト(ms)
)
{
m_symbol = symbol;
m_magicNumber = magicNumber;
m_maxPosition = maxPosition;
m_timeout = timeout;
m_positions.Resize(m_maxPosition);

//Pips計算 小数点桁数が3or5の場合、Point()*10=1pips
m_digit =  (int)MarketInfo(m_symbol, MODE_DIGITS);

m_pipsRate = m_digit == 3 || m_digit == 5 ? 10 : 1;

for( int i = 0 ; i < OrdersTotal(); i++)
{
if( OrderSelect(i, SELECT_BY_POS) )
{
if( OrderMagicNumber() == m_magicNumber && OrderSymbol() == m_symbol)
{
m_positions.Add(OrderTicket());
}
}
}
}

//------------------------------------------------------------------
// デストラクタ
CTradingWrapper::~CTradingWrapper()
{
}

//------------------------------------------------------------------
// 発注する
// Return   発注成功時インデックス それ以外-1
int CTradingWrapper::SendOrder(
int cmd,                      //売買種別
double volume,                //売買ロット
double price,                 //価格(成行き時は現在値を自動設定)
uint slippage,                //許容スリッピング(Pips単位)
uint stoploss = 0,            //ストップロス(Pips単位)
uint takeprofit = 0,          //利確値(Pips単位)
string comment = NULL,        //コメント
datetime expiration = 0,      //注文有効期限
color arrowColor = clrNONE    //注文矢印の色
)
{
if( !this.IsSafeParameter() ) return -1;
if( m_positions.Total() >= m_positions.Max() ) return -1 ;

// 計算用 負数フラグ
int flag = cmd == OP_BUY || cmd == OP_BUYLIMIT || cmd == OP_BUYSTOP ? 1 : -1;

uint start = GetTickCount();
while(true)
{
if( (GetTickCount() - start ) > m_timeout ) return -1;

double bid = MarketInfo(m_symbol, MODE_BID);
double ask = MarketInfo(m_symbol, MODE_ASK);

if( cmd == OP_SELL ) price = bid;
if( cmd == OP_BUY ) price = ask;

double closeTarget = cmd == OP_BUY || cmd == OP_BUYLIMIT || cmd == OP_BUYSTOP ? bid : ask;

//pips値からストップロス値、利益確定値を取得する。
double stoplossValue = 0 ;
double takeprofitValue = 0;
if( stoploss != 0 )
{
stoplossValue = NormalizeDouble(closeTarget - this.GetPipsValue() * stoploss * flag, m_digit );
}
if( takeprofit != 0)
{
takeprofitValue = NormalizeDouble(closeTarget + this.GetPipsValue() * takeprofit * flag, m_digit );
}

if( IsTradeAllowed() )
{
if( AccountFreeMarginCheck(m_symbol, cmd, volume) < 0 )
{
return -1;
}

int tiket = ::OrderSend(m_symbol, cmd, volume, price,
slippage * m_pipsRate, stoplossValue, takeprofitValue,
comment, m_magicNumber, expiration, arrowColor);

if( tiket >= 0 )
{
m_positions.Add(tiket);
return m_positions.Total() - 1;
}
else
{
int errorCode = GetLastError();

// リトライしても仕方がないエラーの時は終了してしまう。
if( errorCode == ERR_INVALID_PRICE ||
errorCode == ERR_INVALID_STOPS ||
errorCode == ERR_INVALID_TRADE_VOLUME ||
errorCode == ERR_NOT_ENOUGH_MONEY )
{
return -1;
}
}
}
Sleep(100);
}
Print("SendOrder Timeout");
return -1;
}

//------------------------------------------------------------------
// 成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CTradingWrapper::CloseOrder(
int index,                    //決済するインデックス
uint slippage,                //許容スリッピング
color arrowColor = clrNONE    //注文矢印の色
)
{
uint start = GetTickCount();
while(true)
{
if( (GetTickCount() - start ) > m_timeout ) return false;

if( IsTradeAllowed() )
{
// オーダーが選択できない状態(すでに存在しない)場合は、リストから削除して終了する。
int targetTicket = m_positions.At(index);
if( !OrderSelect(targetTicket, SELECT_BY_TICKET) )
{
m_positions.Delete(index);
return false;
}

if( OrderClose(OrderTicket(),OrderLots(), OrderClosePrice(), slippage * m_pipsRate, arrowColor) )
{
m_positions.Delete(index);
return true;
}
else
{
int errorCode = GetLastError();
Print("Close Error[",  errorCode, "] ");

// リトライしても仕方がないエラーの時は終了してしまう。
if( errorCode == ERR_INVALID_TICKET)
{
m_positions.Delete(index);
return false;
}
}
}
Sleep(100);
}

Print("CloseOrder Timeout");

return false;
}

//------------------------------------------------------------------
// 全ポジションを成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CTradingWrapper::CloseOrderAll(
uint slippage,                // 許容スリッピング
color arrowColor = clrNONE    // 注文矢印の色
)
{
RefreshPositions();
bool result = true;
for( int i = m_positions.Total() - 1; i >= 0; i--)
{
result &= this.CloseOrder(i, slippage, arrowColor);
}

return result;
}

//------------------------------------------------------------------
// 注文を変更する。
// Return   発注成功時ture それ以外false
bool CTradingWrapper::ModifyOrder(
int index,                 //決済するインデックス
double price,              //価格
uint stoploss = 0,         //ストップロス(Pips単位)
uint takeprofit = 0,       //利確値(Pips単位)
datetime expiration = 0,   //注文有効期限
color arrowColor = clrNONE // 注文矢印の色
)
{
uint start = GetTickCount();
while(true)
{
if( (GetTickCount() - start ) > m_timeout ) return false;

if( IsTradeAllowed() )
{
// オーダーが選択できない状態(すでに存在しない)場合は、リストから削除して終了する。
int targetTicket = m_positions.At(index);
if( !OrderSelect(targetTicket, SELECT_BY_TICKET) )
{
m_positions.Delete(index);
return false;
}

double bid = MarketInfo(m_symbol, MODE_BID);
double ask = MarketInfo(m_symbol, MODE_ASK);

int cmd = OrderType();
// 計算用 負数フラグ
int flag = cmd == OP_BUY || cmd == OP_BUYLIMIT || cmd == OP_BUYSTOP ? 1 : -1;

double closeTarget = cmd == OP_BUY || cmd == OP_BUYLIMIT || cmd == OP_BUYSTOP ? bid : ask;
//pips値からストップロス値、利益確定値を取得する。
double stoplossValue = 0 ;
double takeprofitValue = 0;
if( stoploss != 0 )
{
stoplossValue = NormalizeDouble(closeTarget - this.GetPipsValue() * stoploss * flag, m_digit );
}
if( takeprofit != 0)
{
takeprofitValue = NormalizeDouble(closeTarget + this.GetPipsValue() * takeprofit * flag, m_digit );
}

if( OrderModify(targetTicket, price, stoplossValue, takeprofitValue, expiration, arrowColor ))
{
return true;
}
else
{
int errorCode = GetLastError();
Print("Order Error[",  errorCode, "] ");

// リトライしても仕方がないエラーの時は終了してしまう。
if( errorCode == ERR_INVALID_PRICE ||
errorCode == ERR_INVALID_STOPS ||
errorCode == ERR_INVALID_TRADE_VOLUME ||
errorCode == ERR_NOT_ENOUGH_MONEY)
{
return false;
}
}
}
}
Print("ModifyOrder Timeout");
return false;
}

//------------------------------------------------------------------
// すべての注文を変更する。
bool CTradingWrapper::ModifyOrderAll(
double price,              //価格
uint stoploss = 0,         //ストップロス(Pips単位)
uint takeprofit = 0,       //利確値(Pips単位)
datetime expiration = 0,   //注文有効期限
color arrowColor = clrNONE // 注文矢印の色
)
{
RefreshPositions();
bool result = true;
for( int i = m_positions.Total() - 1; i >= 0; i--)
{
result &= this.ModifyOrder(i, price, stoploss, takeprofit, expiration, arrowColor);
}

return result;
}

//------------------------------------------------------------------
// 注文を変更する。
// Return   発注成功時ture それ以外false
bool CTradingWrapper::ModifyOrder(
int index,                 //決済するインデックス
uint stoploss,             //ストップロス(Pips単位)
uint takeprofit = 0,       //利確値(Pips単位)
color arrowColor = clrNONE //注文矢印の色
)
{
return this.ModifyOrder(index, 0, stoploss, takeprofit, 0, arrowColor);
}

//------------------------------------------------------------------
// すべての注文を変更する。
bool CTradingWrapper::ModifyOrderAll(
uint stoploss,             //ストップロス(Pips単位)
uint takeprofit = 0,       //利確値(Pips単位)
color arrowColor = clrNONE //注文矢印の色
)
{
RefreshPositions();
return this.ModifyOrderAll(0, stoploss, takeprofit, 0, arrowColor);
}

//------------------------------------------------------------------
// 設定パラメータが安全かどうか
// Return 安全ture それ以外false
bool CTradingWrapper::IsSafeParameter()
{
if( m_symbol == NULL ) return false;
if( m_maxPosition == 0 ) return false;
return true;
}

//------------------------------------------------------------------
// ポジションリストを更新する。
void CTradingWrapper::RefreshPositions()
{
m_positions.Clear();
for( int i = 0 ; i < OrdersTotal(); i++)
{
if( OrderSelect(i, SELECT_BY_POS) )
{
if( OrderMagicNumber() == m_magicNumber && OrderSymbol() == m_symbol)
{
m_positions.Add(OrderTicket());
}
}
}
}


2015.4.17 クラスのコードを修正しました。
2015.5.15 新しい記事をアップしています。


2015年4月16日木曜日

EAを作ってみよう2 テンプレートを確認する。

ウィザードから作成されたテンプレートコードを確認したいと思います。

//+------------------------------------------------------------------+
//|                                                      Sample1.mq4 |
//|                                         Copyright 2015,  Daisuke |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//--- create timer
EventSetTimer(60);

//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- destroy timer
EventKillTimer();

}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---

}
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
//---

}
//+------------------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
{
//---
double ret=0.0;
//---

//---
return(ret);
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
//---

}
//+------------------------------------------------------------------+


int OnInit()
初期化関数です。EA初期化処理を記述します。ウィザードではEventSetTimer(60);とOnTimerを60秒に一回起動する設定が記述されています。
戻り値などは、インジケータと同じようです。
インジケータウィザード 生成コードを見てみよう

void OnDeinit(const int reason)
インジケータのウィザードでは自動生成されなかった、終了処理が自動生成されています。
reason引数には終了理由が入ってきます。
Uninitialization Reason Codes
タイマーの停止やバッファの解放、ポジションの解放などEA終了時処理を記述します。ウィザードでは、EventKillTimer()というタイマーを停止する関数が記述されています。

void OnTick()
EAではOnTickに売買シグナルによる注文判断処理を記述するのが一般的なようです。
OnTickは気配値の変更があった場合に呼び出される関数です。

void OnTimer()
一定時間ごとに呼び出される関数です。呼び出し間隔はEventSetTimer(秒)で指定します。

double OnTester()
バックテスト時に呼び出される関数です。
OnDeinitの直前に1回呼び出されます。ここでテスト結果の独自評価式を記述することができます。パラメータ最適化を行う際、ここでの戻り値が最大になるように最適化できるそうです。たとえば利益の出た取引回数を戻すようにすると、利益が出る取引回数が最大になるような最適化が実行可能ということになります。詳しくはEAをバックテストする記事を書く際に調査したいと思います。

void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
EAが登録されているチャート上のイベントを取得する関数です。マウスクリックやオブジェクトの登録・削除などUIに関するイベントが取得可能です。EAを作成する分にはあまり関係なさそうです。

テンプレートで出来上がったコードはなんとなくわかりましたので、次回まずはEAを作ってみたいと思います。

2015年4月15日水曜日

EAを作ってみよう1 ウィザードを確認します。

ちょこっとEA作成してみたいと思います。

まず、ナビゲーターウィンドウのExpertsというを選択して右クリックを押します。New Fileを選択すると作成ウィザードが開始されます。
eaMenu.PNG
今回はあらかじめExpertsの下にテストフォルダを作成しておき、その中にSample1というEAを作成します。

newEa1.png
ExpertsフォルダでNewFileを選択すると自動的に Expert Advisorが選択されています。
このまま次へボタンを押します。

newEa2.png
インジケーター作成と同様に名前やパラメーターを入力する画面となります。ここではNameの欄にSample1と追加して次へボタンをおします。
パラメーターは後からコードで追加したいと思います。

newEa3.png
テンプレートとして作成されるソースコード上に、どのような関数を追加するかのチェックボックスです。とりあえず、どのようなテンプレートが作成されるか確認するためにチェックをつけれるものはすべてつけます。チェックをつけることができない関数はMT5用の関数でMT4では使用できない物です。
MT4 イベントハンドラ
OnTimer:一定時間ごとに呼び出される関数です。呼び出し間隔はEventSetTimer関数で指定します。
OnChartEvent:チャート上の操作イベントで呼び出される関数です。

newEa4.png
テスト用関数を追加するかの選択です。こちらもどのようなメソッドが追加されるか見る為にチェックします。チェックをつけることができないものはMT4では使用できない関数です。
OnTester:バックテストやパラメータ最適化の際、DeInitコールの直前に呼び出されます。テスト結果を数値化したい場合に、ここに評価式を入力しておくと結果として出力されます。

次回はウィザードで作成されたテンプレートのコードを確認したいと思います。





2015年4月14日火曜日

クラスを作成する。

Build600以降のMT4では、なんとクラスを作成できます。
プログラマの大好物(?)オブジェクト指向プログラミングが可能です!

とりあえず、コンパイルが通るレベルで書いてみました。

書き方はC++に基本準拠していますが、アロー演算子が無いようですので、参照であろうが実体であろうが、.でメンバーにアクセスします。このあたりはC#っぽい?

サンプルとして売買サポートクラスのさわり部分だけアップします。

#include <Arrays/ArrayInt.mqh>
#include <stdlib.mqh>
#include <stderror.mqh>
//------------------------------------------------------------------
// 注文関係ラッパークラス
class CTradingWrapper
{
private:
// マジックナンバー
int m_magicNumber;

// 最大ポジション数
int m_maxPosition;

// Pips倍率
int m_pipsRate;

// 対象通貨ペア
string m_symbol;

// 保有ポジション
CArrayInt m_positions ;

// タイムアウト値
uint m_timeout;

// 少数桁数
int m_digit;
public:
//------------------------------------------------------------------
// コンストラクタ
CTradingWrapper(
string symbol,      //対象通貨ペア
int magicNumber,    //マジックナンバー
int maxPosition,    //最大ポジション数
uint timeout        //タイムアウト
);

//------------------------------------------------------------------
// デストラクタ
~CTradingWrapper();

//------------------------------------------------------------------
// 発注する
// Return   発注成功時インデックス それ以外-1
int SendOrder(
int cmd,                //売買種別
double volume,          //売買ロット
double price,           //価格
uint slippage,          //許容スリッピング(Pips単位)
uint stoploss,          //ストップロス(Pips単位)
uint takeprofit,        //利確値(Pips単位)
string comment,         //コメント
datetime expiration,    //注文有効期限
color arrowColor        //注文矢印の色
);

//------------------------------------------------------------------
// 成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CloseOrder(
int index,                    //決済するインデックス
uint slippage,                //許容スリッピング
color arrowColor = clrNONE    //注文矢印の色
);

//------------------------------------------------------------------
// 全ポジションを成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CloseOrderAll(
uint slippage,                //許容スリッピング
color arrowColor = clrNONE    //注文矢印の色
);

//------------------------------------------------------------------
// 現在のポジション数を取得する。
// Return   現在のポジション数を取得する。
int GetPositionCount(){ return m_positions.Total(); };

//------------------------------------------------------------------
// 最大のポジション数を取得する。
// Return   最大のポジション数を取得する。
int GetMaxPosition() { return m_maxPosition; } ;

private:

//------------------------------------------------------------------
// 設定パラメータが安全かどうか
// Return 安全ture それ以外false
bool IsSafeParameter();

};

//------------------------------------------------------------------
// コンストラクタ
CTradingWrapper::CTradingWrapper(
string symbol,          //対象通貨ペア
int magicNumber = 0,    //マジックナンバー
int maxPosition = 3,    //最大ポジション数
uint timeout = 1000     //タイムアウト(ms)
)
{
m_symbol = symbol;
m_magicNumber = magicNumber;
m_maxPosition = maxPosition;
m_timeout = timeout;
m_positions.Resize(m_maxPosition);

//Pips計算 小数点桁数が3or5の場合、Point()*10=1pips
m_digit =  (int)MarketInfo(m_symbol, MODE_DIGITS);

m_pipsRate = m_digit == 3 || m_digit == 5 ? 10 : 1;
}

//------------------------------------------------------------------
// デストラクタ
CTradingWrapper::~CTradingWrapper()
{
}

//------------------------------------------------------------------
// 発注する
// Return   発注成功時インデックス それ以外-1
int CTradingWrapper::SendOrder(
int cmd,                      //売買種別
double volume,                //売買ロット
double price,                 //価格(成行き時は現在値を自動設定)
uint slippage,                //許容スリッピング(Pips単位)
uint stoploss = 0,            //ストップロス(Pips単位)
uint takeprofit = 0,          //利確値(Pips単位)
string comment = NULL,        //コメント
datetime expiration = 0,      //注文有効期限
color arrowColor = clrNONE    //注文矢印の色
)
{
if( !this.IsSafeParameter() ) return -1;
if( m_positions.Total() >= m_positions.Max() ) return -1 ;

//pips値からストップロス値、利益確定値を取得する。
double stoplossValue = 0 ;
double takeprofitValue = 0;

// 計算用 負数フラグ
int flag = cmd == OP_BUY || cmd == OP_BUYLIMIT || cmd == OP_BUYSTOP ? 1 : -1;

uint start = GetTickCount();
while(true)
{
if( (GetTickCount() - start ) > m_timeout ) return -1;

double bid = MarketInfo(m_symbol, MODE_BID);
double ask = MarketInfo(m_symbol, MODE_ASK);

if( cmd == OP_SELL ) price = bid;
if( cmd == OP_BUY ) price = ask;

double closeTarget = cmd == OP_BUY || cmd == OP_BUYLIMIT || cmd == OP_BUYSTOP ? bid : ask;
if( stoploss != 0 )
{
stoplossValue = NormalizeDouble(closeTarget - Point() * m_pipsRate * stoploss * flag, m_digit );
}
if( takeprofit != 0)
{
takeprofitValue = NormalizeDouble(closeTarget + Point() * m_pipsRate * takeprofit * flag, m_digit );
}

if( IsTradeAllowed() )
{
int tiket = ::OrderSend(m_symbol, cmd, volume, price,
slippage * m_pipsRate, stoplossValue, takeprofitValue,
comment, m_magicNumber, expiration, arrowColor);

if( tiket < 0 )
{
int errorCode = GetLastError();
Print("Order Error[",  errorCode, "] ");

// リトライしても仕方がないエラーの時は終了してしまう。
if( errorCode == ERR_INVALID_PRICE ||
errorCode == ERR_INVALID_STOPS ||
errorCode == ERR_INVALID_TRADE_VOLUME ||
errorCode == ERR_NOT_ENOUGH_MONEY)
{
return -1;
}
}
else
{
m_positions.Add(tiket);
return m_positions.Total() - 1;
}
}
Sleep(100);
}
Print("Order Timeout");
return -1;
}

//------------------------------------------------------------------
// 成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CTradingWrapper::CloseOrder(
int index,                    //決済するインデックス
uint slippage,                //許容スリッピング
color arrowColor = clrNONE    //注文矢印の色
)
{
int targetTicket = m_positions.At(index);
if( !OrderSelect(targetTicket, SELECT_BY_TICKET) ) return false;

uint start = GetTickCount();
while(true)
{
if( (GetTickCount() - start ) > m_timeout ) return false;

if( IsTradeAllowed() )
{
if( OrderClose(OrderTicket(),OrderLots(), OrderClosePrice(), slippage * m_pipsRate, arrowColor) )
{
m_positions.Delete(index);
return true;
}
}
Sleep(100);
}

return false;
}

//------------------------------------------------------------------
// 全ポジションを成行き決済を行う。
// Return   発注成功時ture それ以外false
bool CTradingWrapper::CloseOrderAll(
uint slippage,                // 許容スリッピング
color arrowColor = clrNONE    // 注文矢印の色
)
{
bool result = true;
for( int i = m_positions.Total() - 1; i >= 0; i--)
{
result &= this.CloseOrder(i, slippage, arrowColor);
}

return result;
}

//------------------------------------------------------------------
// 設定パラメータが安全かどうか
// Return 安全ture それ以外false
bool CTradingWrapper::IsSafeParameter()
{
if( m_symbol == NULL ) return false;
if( m_maxPosition == 0 ) return false;
return true;
}


使い方はこんな感じになります。
CTradingWrapper *wrapper;

//------------------------------------------------------------------
//初期化
int OnInit()
{
wrapper = new CTradingWrapper(Symbol(), MagicNumber, 3);
return(INIT_SUCCEEDED);
}

//------------------------------------------------------------------
//終了処理
void OnDeinit(const int reason)  //終了理由
{
delete wrapper;
}

//------------------------------------------------------------------
// 気配値表示処理
void OnTick()
{
wrapper.SendOrder(OP_BUY, 0.1, MarketInfo(Symbol(), MODE_ASK), 2);
}


Tickの度に注文する恐ろしいプログラムですので実行禁止です(笑)

たとえば、通貨ペアごとにCTradingWrapperを宣言して、ポジション管理をクラスに任せることで、実際の売買アルゴリズムの部分を単純化できたりします。

複雑なプログラムになるほどクラス化の効果が表れるといわれています。ただ1からオブジェクト指向について学習するのは、かなり手間ですので初心者の方にはおすすめしません。もしプログラム経験者でクラスの作成に慣れているようでしたら、あらかじめクラス設計をしておくことをお勧めします。さらりと設計しておくだけでも将来の開発速度向上の効果が見込めます。

将来EAアルゴリズム部分の開発に集中するために、最初に少しだけ手間をかけておくという考え方です。

プログラム的には、豊嶋先生の本を参考にしながら記述しました。注意点が一つあるとすると、
uint start = GetTickCount();
・・・略
if( (GetTickCount() - start ) > m_timeout ) return -1;

です。GetTickCount();を受ける引数はuintという負数をとらない整数を指定してください。
GetTickCountの特性で40日ほど放置すると0に戻ります。intで計算してしまうと、いつまでも終わらない症状が発生する場合があります。uintにしておくと、GetTickCount() - startは必ず整数の差分となります。(詳しく知りたい方は、補数というキーワードで調べてみてください。)

2015.5.15 新しい記事をアップしています。

2015.4.16
サンプルで乗せたクラスのコードですが、実行してみたらバグだらけでした(^^;;;
あくまでクラスの書き方サンプルとしてご参照ください。またデバッグ終わったら乗せなおしたいと思います。

2015年4月13日月曜日

二つの移動平均 移動方向をグラフ化する。

前回の記事で作ったクローズ値の移動速度オシレーターがあまりに意味がないため、もう少し意味があるかもしれないものを作ってみました(汗

長期・短期の移動平均が2本あった際、両方の移動平均の方向がそろった場合、取引したいとします。それが本当に効果があるかどうか含めて検討する為にもインジケーターとして実装して、実際のグラフと比較してどうだろ?考え方足りていないか?などを検討できます。
今回は、移動平均がどちらの方向に移動中かのグラフを2本引きます。両方ともに+or-になった際、取引を開始するという事を考えます。

MASpeed.PNG

先に結果から。下側の子ウィンドウが移動平均線の移動速度です。移動速度が両方+Or-になったということは中央の0の線を両方超えた場合という風にとらえることができます。

チャート右側の長期にトレンドが出ている場合は、有効ですが、左側のチャートが暴れている箇所は、だましとなるシグナルが上下に複数発生している事が見て取れます。
移動平均の方向だけを見て取引をするのは危険ということがわかりました。(^^;;;;

今まで使用してきた関数などを使用してコードを作成しました。新しいことは特にしていないため、今回はコードの解説とばします^^;;

ちなみに、移動平均の種類をEMAやWMAにしたり、平均期間を変えたりすると、また違ったグラフになります。興味のある方はいろいろ試してみてください。もしいい感じのパラメータが見つかったら教えていただけるととてもうれしいです(^^ 個人的には、EMA長期12、短期5でぐらいだともしかして、5分足の判断の一つとして使えるかも?とか思ってたり・・・。

//------------------------------------------------------------------
// 移動平均速度オシレーター
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict

//indicator_separate_windowを指定すると、子ウィンドウを追加する動作
#property indicator_separate_window

//バッファーを指定する。
#property indicator_buffers 2

//プロット数を指定する。
#property indicator_plots   2

//長期移動平均速度
#property indicator_label1  "SpeedLong"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//短期移動平均速度
#property indicator_label2  "SpeedShort"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrIndianRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//入力パラメータ 長期期間
input int   LongPeriod = 10;

//入力パラメータ 短期期間
input int   ShortPeriod = 5;

//入力パラメータ 表示移動
input int   MaShift = 0;

//入力パラメータ 移動平均種別
input ENUM_MA_METHOD MaMethod = MODE_SMA;

//入力パラメータ 適用価格
input ENUM_APPLIED_PRICE MaPrice = PRICE_CLOSE;

//インジケーター バッファ
double         longValues[];
double         shortValues[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
//インジケーターバッファを初期化する。
SetIndexBuffer(0,longValues);
SetIndexBuffer(1,shortValues);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{

//現在チャートの選択時間幅を秒で取得する。
int period = PeriodSeconds(PERIOD_CURRENT) / 60;

//元となる値を計算する。
for( int i = (rates_total - prev_calculated - 1); i>=0 ; i-- )
{
// 最も古い要素は速度算出不可
if( i == (rates_total - 1) )
{
shortValues[i] = 0 ;
longValues[i] = 0 ;
continue;
}
// 現在の価格
double currentPrice = iMA(Symbol(), 0, LongPeriod, MaShift, MaMethod, MaPrice, i);
// ひとつ前の価格
double beforePrice = iMA(Symbol(), 0, LongPeriod, MaShift, MaMethod, MaPrice, i + 1);

//単位がチャート上の通貨ではない単位(この場合 通貨/分)の値を入れる。
longValues[i] = (currentPrice - beforePrice) / period;

// 現在の価格
currentPrice = iMA(Symbol(), 0, ShortPeriod, MaShift, MaMethod, MaPrice, i);
// ひとつ前の価格
beforePrice = iMA(Symbol(), 0, ShortPeriod, MaShift, MaMethod, MaPrice, i + 1);

//単位がチャート上の通貨ではない単位(この場合 通貨/分)の値を入れる。
shortValues[i] = (currentPrice - beforePrice) / period;
}
return(rates_total);
}


子ウィンドウタイプのインジケーターを作成する。

今度は子ウィンドウを表示して、それっぽいデータを描画したいと思います。
意味があるかどうかはわかりませんが、価格移動の分速を求めて表示したいと思います。

speed.PNG

//------------------------------------------------------------------
// 移動速度オシレーター

#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict

//(1)indicator_separate_windowを指定すると、子ウィンドウを追加する動作
#property indicator_separate_window

//バッファーを指定する。
#property indicator_buffers 1

//プロット数を指定する。
#property indicator_plots   1

//移動平均速度
#property indicator_label1  "Speed"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//入力パラメータ 適用価格
input ENUM_APPLIED_PRICE SpeedPrice = PRICE_CLOSE;

//インジケーター バッファ
double         speedValues[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
//インジケーターバッファを初期化する。
SetIndexBuffer(0,speedValues);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{

//現在チャートの選択時間幅を秒で取得する。
int period = PeriodSeconds(PERIOD_CURRENT) / 60;

//(2)元となる値を計算する。
for( int i = (rates_total - prev_calculated - 1); i>=0 ; i-- )
{
// 最も古い要素は速度算出不可
if( i == (rates_total - 1) )
{
speedValues[i] = 0 ;
continue;
}

// 現在の価格
double currentPrice;
// ひとつ前の価格
double beforePrice;

switch( SpeedPrice )
{
case PRICE_CLOSE:
currentPrice = close[i];
beforePrice = close[i + 1];
break;
case PRICE_OPEN:
currentPrice = open[i];
beforePrice = open[i + 1];
break;
case PRICE_HIGH:
currentPrice = high[i];
beforePrice = high[i + 1];
break;
case PRICE_LOW:
currentPrice = low[i];
beforePrice = low[i + 1];
break;
case PRICE_MEDIAN:
currentPrice = (high[i] + low[i]) / 2;
beforePrice = (high[i + 1] + low[i + 1]) / 2;
break;
case PRICE_TYPICAL:
currentPrice = (high[i] + low[i] + close[i]) / 3;
beforePrice = (high[i + 1] + low[i + 1] + close[i + 1]) / 3;
break;
case PRICE_WEIGHTED:
currentPrice = (high[i] + low[i] + close[i] + close[i]) / 4;
beforePrice = (high[i + 1] + low[i + 1] + close[i + 1] + close[i + 1]) / 4;
break;
}

//(3)単位がチャート上の通貨ではない単位(この場合 通貨/分)の値を入れる。
speedValues[i] = (currentPrice - beforePrice) / period;
}
return(rates_total);
}


じつは、チャート上に表示するのも、子ウィンドウに表示するのもコードはほとんど変わりません。
��1)にてindicator_separate_windowを指定していますが、基本これだけで子ウィンドウが表示されてチャートが描画されます。
��2)にて計算のもととなる値を求めて、(3)にてバッファに設定する流れはほぼ同じです。

インジケーターが挿入されているチャートの秒数を取得する為、PeriodSeconds関数を使用しました。これは指定の足を秒単位で戻してくれる関数です。引数を省略すると現在チャートの足の秒数を戻します。
��例:5分足で表示していた場合、300が戻ってきます。)
PeriodSeconds
int PeriodSeconds(
ENUM_TIMEFRAMES period=PERIOD_CURRENT // chart period
);

サブウィンドウには、チャート上と単位が違い扱えないデータを表示するのに向いている仕組みです。この場合は価格/分という速度ベースのデータを表示してみましたが、通常は指数値などオシレーター系のデータを表示するのに使うようです。
オシレーターを自力で開発というと結構数学的知識が必要で大変ですよね。私もあれこれ考えるのは好きですが、どうしても確率論や集合論との格闘になってしまい、頭が固まってしまいます。EA開発の前段階として自分なりのオシレーターが作れるよう日々努力です。