MetaTrader 5をダウンロードする

METATRADER 5で外部仮想通貨取引を行う方法

12 1月 2018, 08:20
o_o
0
1 212
最近、カスタム銘柄やチャートを開発する最新機能がMQL5の開発者によって導入されました。このイノベーションの力はトレーダーコミュニティではまだ幅広く理解されていませんが、カスタム銘柄に秘められた巨大な可能性は、簡単かつ控えめなブレインストーミングを行うことだけで示すことができます。他のMQLツールと共に、最も大胆で興味深いアイデアの多くを実装することが可能なのです。

これからのMetaTrader 5は単一のDCと対話する端末ではありません。その代わり、それは、APIを介してさまざまな取引所に接続するだけでなく、物価変動や取引フローを視覚化することができる自立型の分析プラットフォームです。この数個の新機能は、限られた数の取引ツールを含むツールボックスではなく、端末をオープンシステムに変えるものです。私の意見では、カスタムツールも強力な分析機能になります。

ここでは、仮想通貨の一般的なテーマを例にとって新しい言語機能を説明していきます。私は、コミュニティのカスタム銘柄への関心はさらに高まると信じています。

本稿は以下の人々に役立つことでしょう。

  • 仮想通貨取引のトレーダー
  • MetaTrader 5およびポートフォリオ投資に精通している投資家
  • フリーランスプログラマー(より単純かつ安価な方法で顧客の仮想通貨取引関連の注文を実行できる)
  • 新しいMetaTrader 5とMQL5の言語機能をフォローしている人

まず、Web APIを提供する仮想通貨取引所を選択する必要があります。

コードベース製品の1つを開発するときに私はBTC-eを使用しましたが、これはもう適切ではありません。したがって、私は別の方法のに切り替えることにしました。そのAPI 機能は、ダウンロードバー、価格の流れ、市場の深さ、現在の口座の注文とポジション、注文履歴と取引履歴を表示するなど、MQLの新機能と既存の機能をうまく説明するのに十分です。

次の計画に固執しましょう。

  1. Webリクエストによって返されるすべてのデータ構造を記述する。
  2. 取引所に接続するためのクラスを開発する。ポイントにアクセスするっためにこれらのクラスはWebRequestを実装する必要がある。
  3. MetaTrader 5でバーを受け取って仮想通貨の価格を更新するエキスパートアドバイザーを開発する。
  4. 注文とポジションを扱うスクリプトとEAを開発する。

1. 取引所データAPIの構造


他の仮想通貨取引所と同様、BITFINEXには2つのアクセスポイントがあります。

  • 1番目(リクエストのセットと形式)は、取引所の価格データ(バー、ティック、市場の深さ)を提供します。データは一般化され匿名であるため、署名や認証なしでリクエストされます。
  • 2番目のアクセスポイントは、口座の状態、ポジション、未決注文、取引履歴など、口座に関するデータを提供します。これらのデータはプライベートなので、取引所の個人的「トレーダーの部屋」で受け取ったAPIキーを使用して、リクエストヘッダーにSHA384-HMACデータ署名を送信する必要があります。これにより、リクエストがあなたの名前を使った攻撃者ではなくあなたによって行われたことが保証されます。

どちらのアクセスポイントもJSON形式を使用してRESTスタイル(注: WebSocketはここでは考慮されない)で動作します。これは、データの読み取りとAPIの書き込みの両方に便利です。

取引所には2つのプロトコルバージョンがあります。2番目のバージョンは現在ベータモードです。この外観には2つの理由があります。まず、新しい機能があります(バーのリクエスト、アラートの操作、注文の魔法番号など)。2番目の理由は、JSON構造の形式ではっきりと表示されます。これはトラフィックの節約です。ティッカーリクエスト(現在の銘柄データ)に対するレスポンスを第1プロトコルバージョンと第2プロトコルバージョンで比較してみましょう。

 バージョン1
バージョン2
{

  "mid":"244.755",
  "bid":"244.75",
  "ask":"244.76",
  "last_price":"244.82",
  "low":"244.2",
  "high":"248.19",
  "volume":"7842.11542563",
  "timestamp":"1444253422.348340958"

}
[
  BID, 
  BID_SIZE, 
  ASK, 
  ASK_SIZE, 
  DAILY_CHANGE, 
  DAILY_CHANGE_PERC, 
  LAST_PRICE, 
  VOLUME, 
  HIGH, 
  LOW
]

ご覧のように、2番目のバージョンではフィールドの名前付けは避けられますが、位置は固定されているので、オブジェクトの代わりに配列が使用されます。本稿では、両方の取引所APIバージョンの例を示します。

まず、各プロトコルバージョンで使用する必要があるリクエストとオープンアクセスポイントの構造を見てみましょう。

バージョン1:

バージョン2:

最初にSymbolDetailsリクエストを使ってこれらの構造がどのように作られるかを説明しましょう。その他のデータ構造体はBFxDefine.mqhファイルに示されています。記事に含まれていない他のAPIリクエストが必要な場合は、同様の方法でチェックアウトできます。

ドキュメント
BFxDefine.mqh
[
  {
    "pair":"btcusd",
    "price_precision":5,
    "initial_margin":"30.0",
    "minimum_margin":"15.0",
    "maximum_order_size":"2000.0",
    "minimum_order_size":"0.01",
    "expiration":"NA"
  },

  ...

]
struct bfxSymbolDetails
{
  string pair;
  int price_precision;
  double initial_margin;
  double minimum_margin;
  double maximum_order_size;
  double minimum_order_size;
  string expiration;
};
struct bfxSymbolsDetails
{
  bfxSymbolDetails symbols[];
};

ドキュメントはリクエストがオブジェクト配列を返すことを示しているため、ここでは2つの構造体を作成しました。1番目(bfxSymbolDetails)は、単一の配列オブジェクトにデータを解析して保存します。2番目(bfxSymbolsDetails )は、受信したbfxSymbolDetailsオブジェクト配列を格納し、Webリクエストで直接使用されます。言い換えれば、JSON <-> MQLのマッチングフォーマットはシンプルで鏡のようであり、すべてのドキュメントに拡張されています。

取引所APIを使用する場合は、共通のCBFx親を持つ2つのクラスを使用します。その目的は、共通のデータフィールドと一般的なWebリクエスト機能をカプセル化することです。

//------------------------------------------------------------------    class CBFx
class CBFx
  {
protected:
   string            m_answer;        // リクエスト結果(JSON非直列化前)
   enBFxRequestResult m_lastrr;       // リクエスト結果のコード
   CJAVal            m_lastjs;        // リクエストからの逆シリアル化されたレスポンス
public:
                     CBFx() { }

protected:
   string URL() { return "https://api.bitfinex.com/"; }
   string Answer() { return m_answer; }
   enBFxRequestResult Request(string mode,string url_request,string head,string body,char &result[])
     {
      char data[]; int n=StringLen(body); if(n>0) StringToCharArray(body,data,0,n); string res_header="";
      int r=WebRequest(mode, url_request, head, 10000, data, result, res_header);
      if(r<=0) return rrErrorWebRequest;
      return r==200?rrOK:rrBadWebRequest;
     }
  };

CBFxクラスには2つのの子孫があります。

  • CBFxPublic — パブリックAPIへのリクエスト(バー、クオーツ、市場の深さ)
  • CBFxTrade — プライベートAPIへのリクエスト(取引、口座データ)

これらの子孫については、次の2つのセクションで説明します。

取引所APIを使用したすべての準備作業が完了したので、MetaTrader 5プラットフォームで実装しましょう。

2. CustomSymbol

現時点でカスタム銘柄はどのような状況にあるでしょうか。小さな機能セットによって管理されていることを考慮しても、それらの操作は通常の銘柄とほぼ同一です。

ここでの目的は、取引所銘柄を作成し、バーの履歴をアップロードし、定期的に価格を送信することです。したがって、次のMQL関数が必要です。

残念ながら、WebRequestは指標から呼び出すことはできないので、定期的に銘柄バーと現在の価格をリクエストしてMetaTrader 5で更新するEAを開発します。

取引所とMetaTrader 5の相互作用は次のとおりです。

OnInit

  • MetaTrader 5での銘柄の存在を確認する(SymbolSelect)
    • 銘柄が見つからない場合 -> 指定された銘柄のSymbolDetailsをリクエストする
    • 銘柄を作成(CustomSymbolCreate)しプロパティを書き入れる(CustomSymbolSetХХХ
  • М1履歴を確認する
    • ダウンロードが必要な場合 -> 必要な数の最後のバーでCandleHistをリクエストする
    • バーを追加する(CustomRatesUpdate
  • タイマーを開始する

OnTimer

  • Ticker価格をリクエストする -> ティックを更新する(CustomTicksAdd
  • 最後のバー(CandleLast)をリクエストする -> 更新する(CustomRatesUpdate

これが全作業のサイクルです。

より多くの作業例を得るために作業を複雑にしましょう。このために、市場の深さ(OrderBook)と時間と売上(TradesHist )をリクエストして表示します。時間と売上はチャートにコメントとして表示され、市場の深さは対応する価格レベルでセグメントとして表示されます。

パブリックAPIはCBFxPublicクラスで実装されています。

#include "BFxDefine.mqh"
//------------------------------------------------------------------    class CBFxPublic
class CBFxPublic: public CBFx
  {
public:
                     CBFxPublic() {  }
protected:
   virtual enBFxRequestResult Request(string url_request);
public:
   enBFxRequestResult Symbols(bfxSymbols &ret);
   enBFxRequestResult SymbolsDetails(bfxSymbolsDetails &ret);
   enBFxRequestResult OrderBook(string pair,int depth,bool group,bfxOrderBook &ret);

public:
   enBFxRequestResult CandleHist(string pair,enbfcTimeFrame tf,int limit,bfxCandles &ret);
   enBFxRequestResult CandleLast(string pair,enbfcTimeFrame tf,MqlRates &ret);
   enBFxRequestResult TradesHist(string pair,int limit,bfxTrades &ret);
   enBFxRequestResult Ticker(string pair,bfxTicker2 &ret);
  };

CandleHistを見てその動作を説明しましょう

ドキュメントの説明:

一般的なリクエストフォーム: https://api.bitfinex.com/v2/candles/trade::TimeFrame::Symbol/Section

リクエストのレスポンス:

[   [ MTS, OPEN, CLOSE, HIGH, LOW, VOLUME ],   ... ]

リクエストパラメータ: https://docs.bitfinex.com/v2/reference#rest-public-candles

サンプルリクエスト: https://api.bitfinex.com/v2/candles/trade:1m:tBTCUSD/hist?limit=50

MQL CandleHist実装:

//------------------------------------------------------------------    CandleHist
enBFxRequestResult CBFxPublic::CandleHist(string pair,enbfcTimeFrame tf,int limit,bfxCandles &ret)
  {
   Request(URL()+"v2/candles/trade:"+bfcTimeFrame[tf]+":t"+STU(pair)+"/hist"+(limit>0?"?limit="+string(limit):""));
   if(m_lastrr==rrOK) m_lastrr=ret.FromJson(m_lastjs)?rrSuccess:rrErrorStruct;
   return m_lastrr;
  }

CandleHist関数はGETリクエストを形成します。分足に興味があるので、最終的にリクエストラインは次のようになります: https://api.bitfinex.com/v2/candles/trade:1m:tXXXXXX/hist. レスポンスとして、履歴から最後の1000バーを取得します。

APIリクエストを使ったこれらの操作の結果は、バーを構成して価格をチャートに沿って移動させる単純なEAです。

#include "..\BFxAPI\BFxPublicAPI.mqh"

input string Pair="btcusd";
input int ShowBookDepth=0;
input bool ShowTimeSales=false;

CBFxPublic bfx;
string mtPair;
//------------------------------------------------------------------    OnInit
int OnInit()
  {
// MT銘柄の名前を作成する
#ifdef __MQL4__
   mtPair=StringToUpper(Pair);
#endif          
#ifdef __MQL5__
   mtPair=Pair; StringToUpper(mtPair);
#endif
   mtPair+=".bfx";

// 気配値表示の銘柄を選択する
   bool bnew=false;
   if(!SymbolSelect(mtPair,true))
     {
      bfxSymbolsDetails sd;
      enBFxRequestResult brr=bfx.SymbolsDetails(sd);
      if(brr!=rrSuccess) return INIT_FAILED;
      for(int i=0; i<ArraySize(sd.symbols);++i)
        {
         bfxSymbolDetails ss=sd.symbols[i];
         if(ss.pair==Pair)
           {
            bnew=true;
            CustomSymbolCreate(mtPair,"BITFINEX");
            CustomSymbolSetString(mtPair,SYMBOL_ISIN,Pair);
            CustomSymbolSetInteger(mtPair,SYMBOL_DIGITS,ss.price_precision);
            CustomSymbolSetDouble(mtPair,SYMBOL_MARGIN_INITIAL,ss.initial_margin);
            CustomSymbolSetDouble(mtPair, SYMBOL_VOLUME_MAX, ss.maximum_order_size);
            CustomSymbolSetDouble(mtPair, SYMBOL_VOLUME_MIN, ss.minimum_order_size);
            CustomSymbolSetDouble(mtPair,SYMBOL_VOLUME_STEP, ss.minimum_order_size);
           }
        }
      if(!SymbolSelect(mtPair, true)) return INIT_FAILED;
     }
   if(Symbol()!=mtPair) ChartSetSymbolPeriod(0,mtPair,bnew?PERIOD_M1:Period());

// 履歴を読み込む
   datetime adt[]; ArraySetAsSeries(adt,true);
   int limit=1000;
   if(CopyTime(mtPair,PERIOD_M1,0,1,adt)==1)
      limit=(int)fmax(2,fmin((TimeCurrent()-adt[0])/60,1000));

   bfxCandles bars;
   enBFxRequestResult brr=bfx.CandleHist(Pair,tf1m,limit,bars);
   if(brr==rrSuccess) CustomRatesUpdate(mtPair,bars.rates);

// タイマーを開始する
   EventSetTimer(3);
   return INIT_SUCCEEDED;
  }
//------------------------------------------------------------------    OnDeinit
void OnDeinit(const int reason) { EventKillTimer(); ObjectsDeleteAll(0,Pair,0); if(ShowTimeSales) Comment(""); }
//------------------------------------------------------------------    OnTimer
void OnTimer()
  {
// 最後のティックを取得する
   bfxTicker2 bt;
   MqlTick tick[1]={0};
   tick[0].time=TimeCurrent();
   tick[0].time_msc=TimeCurrent();
   if(bfx.Ticker(Pair,bt)==rrSuccess)
     {
      tick[0].flags=TICK_FLAG_BID|TICK_FLAG_ASK|TICK_FLAG_VOLUME|TICK_FLAG_LAST;
      tick[0].bid=bt.bid; tick[0].ask=bt.ask; tick[0].last=bt.last; tick[0].volume=(long)bt.day_vol;
      CustomTicksAdd(mtPair,tick); ChartRedraw();
     }

// 最後のバーを取得する
   MqlRates rate[1];
   if(bfx.CandleLast(Pair,tf1m,rate[0])==rrSuccess)
     {
      rate[0].spread=int((tick[0].ask-tick[0].bid)*MathPow(10,_Digits));
      CustomRatesUpdate(mtPair,rate);
      if(tick[0].flags>0) { tick[0].last=rate[0].close; CustomTicksAdd(mtPair,tick); }
      ChartRedraw();
     }

   if(ShowBookDepth>0) ShowBook(ShowBookDepth);
   if(ShowTimeSales) TimeSales();
  }
//------------------------------------------------------------------ ShowBook
void ShowBook(int depth)
  {
   bfxOrderBook bk;
   if(bfx.OrderBook(Pair, depth, true, bk)!=rrSuccess) return;
   if(ArraySize(bk.asks)>0)
     {
      for(int i=0; i<ArraySize(bk.asks);++i)
         SetLine(Pair+"Asks"+IntegerToString(i),TimeCurrent(),bk.asks[i].price,TimeCurrent()+20*PeriodSeconds(),
                 bk.asks[i].price,clrDodgerBlue,1,STYLE_DOT,i==0?DoubleToString(bk.asks[i].volume,1):"");
     }
   if(ArraySize(bk.bids)>0)
     {
      for(int i=0; i<ArraySize(bk.bids);++i)
         SetLine(Pair+"Bids"+IntegerToString(i),TimeCurrent(),bk.bids[i].price,TimeCurrent()+20*PeriodSeconds(),
                 bk.bids[i].price,clrRed,1,STYLE_DOT,i==0?DoubleToString(bk.bids[i].volume,1):"");
     }
  }
//------------------------------------------------------------------ TimeSales
void TimeSales()
  {
   bfxTrades tr;
   string inf="";
   if(bfx.TradesHist(Pair,10,tr)==rrSuccess)
     {
      for(int i=0; i<ArraySize(tr.trades);++i)
        {
         bfxTrade t=tr.trades[i];
         inf+="\n   "+IntegerToString(t.id)+"  |  "+TimeToString(t.mts,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"  |  "+DoubleToString(t.price,_Digits)+
              "  |  "+DoubleToString(fabs(t.amount),2)+"  |  "+(t.amount>0?"Buy ":"Sell");
        }
     }
   Comment(inf);
  }
//------------------------------------------------------------------ SetLine
void SetLine(string name,datetime dt1,double pr1,datetime dt2,double pr2,color clr,int width,int style,string st)
  {
   ObjectCreate(0,name,OBJ_TREND,0,0,0); ObjectSetInteger(0,name,OBJPROP_RAY,false);
   ObjectSetInteger(0,name,OBJPROP_TIME,0,dt1); ObjectSetDouble(0,name,OBJPROP_PRICE,0,pr1);
   ObjectSetInteger(0,name,OBJPROP_TIME,1,dt2); ObjectSetDouble(0,name,OBJPROP_PRICE,1,pr2);
   ObjectSetInteger(0,name,OBJPROP_WIDTH,width); ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
   ObjectSetString(0,name,OBJPROP_TEXT,st); ObjectSetInteger(0,name,OBJPROP_STYLE,style);
  }
//------------------------------------------------------------------ SetArrow
void SetArrow(string name,datetime dt,double pr,color clr,int width,string st)
  {
   ObjectCreate(0,name,OBJ_ARROW_LEFT_PRICE,0,dt,pr);
   ObjectSetInteger(0,name,OBJPROP_TIME,0,dt); ObjectSetDouble(0,name,OBJPROP_PRICE,0,pr);
   ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
   ObjectSetString(0,name,OBJPROP_TEXT,st); ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
  }
//+------------------------------------------------------------------+

まず、デバッグモードで起動し、すべてのコールを段階的に移動してください。APIが返すもの、応答の解析方法、データの表示方法を確認してください。

MetaTrader 5でのEAの動作

OnInit関数を実行すると、BTCUSD.bfx銘柄がBITFINEX:セクションに作成されます。


最後のバーとティックは、タイマーに従ってアップロードされます。

アルゴリズム全体は以下に要約することができます。

  • -> WebRequestを送信する
  • -> 受信したJSONを解析する
  • -> 銘柄データを更新する

ご覧のように、パブリックAPIで作業するのは複雑ではありません。必要に応じて他の機能も実装することができます。

本稿執筆時点では、プラットフォーム開発者はSYMBOL_FORMULAプロパティで銘柄を処理する機能を備えた別のMQL5アップデートを準備していました。この新機能は、単純な数式を使って新しい交差率を簡単に作成できるようにします。

3. オートメーションツール

ここでは、注文とポジションを扱うためにプライベートAPIにリクエストをを送る方法を示します。アクセスポイントを扱うための関数は、別のCBFxTradeクラスに集約されています。

#include "SHA384.mqh"
#include "BFxDefine.mqh"
//------------------------------------------------------------------    class CBFxTrade
class CBFxTrade : public CBFx
  {
protected:
   string            m_key;
   string            m_secret;
public:
                     CBFxTrade(string key, string secret) { Init(key, secret); }
   void Init(string key, string secret) { m_key=key; m_secret=secret; }

protected:
   virtual enBFxRequestResult Request1(string req, const CJAVal* prm=NULL);
   virtual enBFxRequestResult Request2(string req, const CJAVal* prm=NULL);
   
   ulong Nonce() { ulong n=(ulong)GlobalVariableGet("bfx."+m_key)+1; GlobalVariableSet("bfx."+m_key,n); return n; };
   void ResetNonce() { double n=fmax(GlobalVariableGet("bfx."+m_key)+1,TimeTradeServer()); GlobalVariableSet("bfx."+m_key,n); };

   // v1
public:
   enBFxRequestResult AccountInfo(bfxAccInfo &ret);
   enBFxRequestResult OrdersHist(bfxOrders &ret);
   enBFxRequestResult ActiveOrders(bfxOrders &ret);
   enBFxRequestResult OrderStatus(const long order_id,bfxOrder &ret);
   enBFxRequestResult NewOrder(string symbol,double amount,double price,string side,string type,bfxOrder &ret);
   enBFxRequestResult CancelOrder(const long order_id,bfxOrder &ret);
   enBFxRequestResult ReplaceOrder(const long order_id,string symbol,double amount,double price,string side,string type,bfxOrder &ret);

   enBFxRequestResult Positions(bfxPositions &ret);
   enBFxRequestResult Balances(string currency,bfxBalances &ret);

   // v2
public:
   enBFxRequestResult AlertList(bfxAlerts &ret);
   enBFxRequestResult AlertSet(string symbol,double price,bfxAlert &ret);
   enBFxRequestResult AlertDel(string name/*price:SMB:price*/);
  };

リクエストはCBFxPublicクラスと完全に類推して行います。初心者にとって難しいのはリクエストヘッダーを埋めることだけだと思います。パブリックAPIとは異なり、秘密鍵で署名されたSHA384-HMACリクエストボディハッシュを渡す必要があります。APIバージョン1では、リクエストをさらにBase64に変換する必要があります。

ドキュメントによると署名されたハッシュは次の順序で作成されます。

  • json形式でリクエスト本体を作成する => body
  • bodyをBase64で再エンコードする => bodyBase64
  • bodyBase64ハッシュを秘密鍵で計算する => signature
  • bodyBase64 signatureをリクエストヘッダで送信する

bodyBase64 CryptEncodeで受信されます。これはCRYPT_BASE64パラメータを持つ標準MQL関数です。signatureを見つけるには、本稿に添付されているSHA384クラスを使用します。

したがって、プライベートAPIバージョン1に対するリクエストの機能は、次のようになります。

//------------------------------------------------------------------    Request
enBFxRequestResult CBFxTrade::Request1(string req,const CJAVal *prm)
  {
   CJAVal js; m_lastjs.Clear();
   if(CheckPointer(prm)!=POINTER_INVALID) js=prm;      // 追加フィールドをコピーする
   js["request"]="/v1/"+req;
   js["nonce"]=string(Nonce());
   string body=js.Serialize();                         // JSONにシリアル化する

   uchar cin[],ck[],cout[]; StringToCharArray(body,cin);
   CryptEncode(CRYPT_BASE64,cin,ck,cout);
   string payload=CharArrayToString(cout);
   string signature=SHA384::hmac(payload,m_secret);

   string head="";
   head+="Content-Type: application/json\n";
   head+="Accept: application/json\n";
   head+="X-BFX-APIKEY:"+m_key+"\n";
   head+="X-BFX-PAYLOAD:"+payload+"\n";
   head+="X-BFX-SIGNATURE:"+signature+"\n\n";

   uchar result[];
   m_lastrr=Request("POST", URL()+"v1/"+req, head, body, result);
   m_answer=CharArrayToString(result);
   if(m_lastrr==rrOK) m_lastrr=m_lastjs.Deserialize(result)?rrOK:rrErrorJson;
   else
     {
      Print("req:"+req+", res:"+m_answer);
      if(StringFind(m_answer,"Nonce is too small")>=0) ResetNonce();
     }
   return m_lastrr;
  }


プライベートAPIバージョン2のリクエストは、Base64がなくヘッダーの名前が違いますがほぼ同じ順序で発生します。

//------------------------------------------------------------------    Request
enBFxRequestResult CBFxTrade::Request2(string req,const CJAVal *prm)
  {
   CJAVal js; m_lastjs.Clear();
   if(CheckPointer(prm)!=POINTER_INVALID) js=prm;
   string body=js.Serialize();
   string apiPath="v2/auth/"+req;
   string nonce=string(Nonce());
   string signature="/api/"+apiPath+nonce+body;
   signature=SHA384::hmac(signature,m_secret);

   string head="";
   head+="content-type: application/json\n";
   head+="accept: application/json\n";
   head+="bfx-nonce:"+nonce+"\n";
   head+="bfx-signature:"+signature+"\n";
   head+="bfx-apikey:"+m_key+"\n\n";

   uchar result[];
   m_lastrr=Request("POST", URL()+apiPath, head, body, result);
   m_answer=CharArrayToString(result);
#ifdef DEBUG    
   Print(m_answer);
#endif
   if(m_lastrr==rrOK) m_lastrr=m_lastjs.Deserialize(result)?rrOK:rrErrorJson;
   else
     {
      Print("req:"+req+", res:"+m_answer);
      if(StringFind(m_answer,"Nonce is too small")>=0) ResetNonce();
     }
   return m_lastrr;
  }


Nonceパラメータは別に言及されるべきです。パブリックAPIでは必須ではありませんが、目的はシンプルで、まだ取引所で処理されていない新しいリクエストを送信していることを示します。Nonce は、単純なインクリメンタルカウンタで、各Webリクエストでの値は前よりも常に高いです。

誤って前の値以下のNonceを送信した場合、APIは10100 - Nonce is too smallというエラーを返します。残念ながら、APIは(他の取引所とは異なり)期待値を送信しません。したがって、最初に正しい値にリセットするには、現在の時刻を割り当てて、それ以降のWebリクエストで再度増分します。

下記はここで使用するプライベートアクセスポイントのリクエストです。

// 取引状態:
// 取引:
// アラート:

例1: 口座のポジションと注文に関するデータの受信

ここでは取引を最大限に自動化しようとしているので、ポジションとアクティブ注文に現在何が起こっているのかを見ることは非常に重要です。これはAPIリクエストで行われます。

ご覧のとおり、リクエストは簡単です。単一の必須パラメータ(必要な注文のチケット)は、注文状態のみです。

最も簡単なデモは、チャートのデータを表形式で表示することです。グラフィックオブジェクトの操作は簡単であり本稿の目的とは異なるので行いません。擬似コードでは、リストの受信と表示は次のようになります。

CBFxTrade bfxt(Key, Secret);                     // アクセスキーを使用してプライベートAPIを初期化する
bfxOrders hist;
bfxt.OrdersHist(hist);                           // 履歴から注文をリクエストする
for (int i=0; i<ArraySize(hist.order); ++i)      // 受信したリストを反復処理してチャートに表示する
  {
   // hist.order[i]を表示する
  }

他の配列(bfxt.Positions bfxt.ActiveOrders)は同じように行われます。

下記はスクリプト実行結果です。

これで、注文とポジションに関するすべてのデータが自由に使えるようになり、EAは取引ロジックに従って意思決定を行うことができます。

例2: アラートの使用

ここでしばらく取引については忘れて、プライベートAPIのテストに役立つ例を考えてみましょう。アラート送信メカニズムを使用すると、口座のリスクを伴わずにオブジェクトの作成/削除リクエストを送信できます。たとえば、取引のかわりにアラートを使用することができます。取引所は、PCまたはモバイルアプリケーションにアラートを送信して、行動の兆候を示します。

APIは3つのリクエストを提供します。

それぞれはすでにスクリプトとして実装されており、本稿に添付されたアーカイブにあります。Alert Setによるアラート作成について検討しましょう。ドキュメントによると、リクエストには銘柄と価格を渡す必要があります。これらのパラメータをユーザーからリクエストしないためにチャート自体から銘柄を取得しますが、価格はスクリプトがチャートに適用された時点から取得されます。

#include "..\..\BFxAPI\BFxTradeAPI.mqh"
//------------------------------------------------------------------    OnInit
void OnStart()
  {
   double price=ChartPriceOnDropped();
   string symbol=SymbolInfoString(Symbol(),SYMBOL_ISIN);
   if(StringLen(symbol)<=0)
    {
     symbol=Symbol();
     if(StringFind(symbol,".")>=0) symbol=StringSubstr(symbol,0,StringFind(symbol,"."));
    }

   if(MessageBox("Set new Alert on "+symbol+" with price "+DoubleToString(price,5)+"?", "Set Alert?", MB_YESNO|MB_ICONQUESTION)!=IDYES)
      return;

   CBFxTrade bfxt(MY_KEY,MY_SECRET);
   bfxAlert o;
   if(bfxt.AlertSet(symbol,price,o)!=rrSuccess)
    {
     MessageBox("Error:\n"+bfxt.Answer(),"Error set alert",MB_ICONERROR);
     return;
    }
   MessageBox("Success:\n"+o.symbol+" on "+DoubleToString(o.price,5)+", current "+DoubleToString(o.curprice,5),"Success");
  }

MessageBoxの答えを得てデータを準備した後は、取引所にリクエストを送信するだけです( bfxt.AlertSet)。送信が成功すると、新しく生成されたbfxAlertオブジェクトが受信されます。

MetaTrader 5には、上記の方法に代わる優れた100%代替手段があります。

MetaTrader 5のビルトインアラートシステムでは、ローカルでのアラートだけでなく、電子メール、ファイルまたはプッシュ通知を介してモバイルプラットフォームにアラートを送信することもできます。 言い換えれば、取引プラットフォームは、上記の方法と比較してはるかに有用な機能と設定を提供します。

例3: 注文とポジションの操作

ついに、MetaTrader 5から直接取引所で取引を行うという、最後の最も重要な例に到達しました。

私はこのコードをチャートからのポジション管理をするEAのように実装することに決めました。パネル開発は私たちの主な目的ではありませんが、一連のスクリプトの代わりに便利です。以下のスクリーンショットは、EA動作の一般的なビューを示します。

下記がEAの動作です。

  • 未決注文とポジションとチャート行を表に表示する
  • ラインの移動時に注文価格を修正する
  • 未決注文を削除する
  • ポジションを決済する
  • チャートに新しい水平線を配置するときに、未決指値注文の設定を行う

全体的にみると、すべての処理とAPI呼び出しはOnChartEventに集中しています。

  • OBJECT_CREATEイベントを使用して、新しいチャート行の作成を検出し、新しい制限命令を作成する
  • OBJECT_DRAGイベントを使用して、「私たちの」行の動きを検出し、未決注文を変更する
  • OBJECT_CLICKイベントを使用して、ポジションを決済するか注文を削除する際に、コントロールボタンのクリックを検出し、表を更新する

EAのイベント関数のコードは次のようになります。

//------------------------------------------------------------------ OnInit
int OnInit()
{
 Refresh();
 ChartSetInteger(0,  CHART_EVENT_OBJECT_CREATE, true);
 return INIT_SUCCEEDED;
}
//---------------------------------------------------------------   OnChartEvent
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
 switch (id)
 {
 case CHARTEVENT_OBJECT_CREATE:
  if (ObjectGetInteger(0, sparam, OBJPROP_TYPE)==OBJ_HLINE)
   if (StringLen(ObjectGetString(0, sparam, OBJPROP_TEXT))<=0)
   {
    NewOrder(sparam);
    ObjectDelete(0, sparam); ChartRedraw();
   }
  break;

 case CHARTEVENT_OBJECT_DRAG:
  if (ObjectGetInteger(0, sparam, OBJPROP_TYPE)==OBJ_HLINE)
   if (StringFind(sparam, "_ord_")>0)
    ReplaceOrder(sparam);
  break;

 case CHARTEVENT_OBJECT_CLICK:
  if (ObjectGetInteger(0, sparam, OBJPROP_TYPE)==OBJ_BUTTON)
  {
   if (StringFind(sparam, "_upd_")>0) Refresh();
   if (StringFind(sparam, "_del_")>0) CancelOrder(sparam);
   if (StringFind(sparam, "_cls_")>0) ClosePos(sparam);
   ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
  }
  break;
 }
}

完全なコードはBFxTrading.mq5ファイルにあります。次の短いアニメーションは結果を示します。

完成です。

ツールキットは新機能で強化され、MetaTrader 5は外部交換のプラットフォームになりました。カスタム銘柄を扱い、Webリクエストを使用して外部取引所と統合した経験がある方は、コメントで共有してください。

幸運と大きな利益を!

添付ファイル

 #  名称 種類
 説明
 1  \Experts\BFx\BFxAPI\  
 取引所APIファイル
 1.1
 \Experts\BFx\BFxAPI\BFxDefine.mqh  インクルードファイル  API定数と定義
 1.2  \Experts\BFx\BFxAPI\BFxPublicAPI.mqh  インクルードファイル  パブリックアクセスポイントを使用するためのクラス
 1.3  \Experts\BFx\BFxAPI\BFxTradeAPI.mqh  インクルードファイル  プライベートアクセスポイントを使用するためのクラス
 1.4  \Experts\BFx\BFxAPI\JAson.mqh  インクルードファイル  JSON形式シリアル化クラス
 1.5  \Experts\BFx\BFxAPI\SHA384.mqh  インクルードファイル
 SHA + HMAC関数ハッシュクラス
 2  \Experts\BFx\Private\    プライベートAPIの使用例
 2.1  \Experts\BFx\Private\_BFxTradeInfo.mq5  スクリプト
 ポジションと注文の一覧の取得
 2.2  \Experts\BFx\Private\Alert\_BFxAlertSet.mq5  スクリプト  口座への通知の追加
 2.3  \Experts\BFx\Private\Alert\_BFxAlertList.mq5  スクリプト  口座からの通知の取得
 2.4  \Experts\BFx\Private\Alert\_BFxAlertDeleteAll.mq5  スクリプト  口座のすべての通知の削除
 2.5  \Experts\BFx\Private\Trading\_BFxOrderStatus.mq5  スクリプト  注文データの取得
 2.6  \Experts\BFx\Private\Trading\_BFxOrderNew.mq5  スクリプト  注文の作成
 2.7  \Experts\BFx\Private\Trading\_BFxOrderCancel.mq5  スクリプト  注文の削除
 2.8  \Experts\BFx\Private\Trading\_BFxTrading.mq5  EA  口座での注文とポジションを作成、変更、削除することができる取引EA
 3  \Experts\BFx\Public\    パブリックAPIの使用例
 3.1  \Experts\BFx\Public\_BFxCustomSymbol.mq5  EA  


MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/4160

添付されたファイル |
MQL5.zip (31.52 KB)
通貨ペアバスケットをトレードするときに発生するパターンのテスト パート2 通貨ペアバスケットをトレードするときに発生するパターンのテスト パート2

通貨ペアバスケットをトレードするときに発生するパターンをテストし、トレード通貨ペアバスケットの記事で説明したメソッドを試していきます。 実際には、移動平均のクロスの複合 WPR チャートのパターンを使用できるかどうかを検討してみましょう。 もし使用できる場合は、適切な使用メソッドを検討する必要があります。

カルマンフィルタを用いた価格方向予測 カルマンフィルタを用いた価格方向予測

トレードで成功するには、ノイズ変動と価格変動を分けることができるインジケーターが必要です。 この記事では、最も有望なデジタルフィルタ、カルマンフィルタを検討します。 フィルタを描画して使用する方法について説明します。

MQL5 ウィザードの NRTR に基づく NRTR インジケーターとトレーディングモジュール MQL5 ウィザードの NRTR に基づく NRTR インジケーターとトレーディングモジュール

この記事では、NRTR インジケーターを分析し、このインジケーターに基づいてトレードシステムを作成します。 追加のトレンド確認インジケーターと NRTR の組み合わせに基づいて戦略を作成する際に使用することができるトレードシグナルのモジュールを開発します。

モメンタムピンボールトレーディング戦略 モメンタムピンボールトレーディング戦略

この記事では、Linda B. RaschkeとLaurence A. Connors の "Street Smarts: High Probability Short-Term Trading Strategies" に記載されているトレーディングシステムのコードを記述します。 今回は、モメンタムのピンボールシステムを研究します。また、2つのインジケーター、トレードロボットとシグナルブロックの作成について説明します。