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