English Русский Deutsch
preview
MQL5でマーケットメイク系アルゴリズムを作成する

MQL5でマーケットメイク系アルゴリズムを作成する

MetaTrader 5トレーディング | 13 5月 2024, 17:36
131 0
Yevgeniy Koshtenko
Yevgeniy Koshtenko

流動性とは何か

金融市場の流動性とは、注文やポジションという形で市場に資金が「飽和」することです。これにより、トレーダーは株式(または通貨)を素早く大量に売ることができます。市場の流動性が高ければ高いほど、スリッページによって大きな損失を被ることなく、多額の資産を売り買いすることが容易になります。 

スリッページは大口プレーヤーの主な弊害です。最大手のファンドは、大きなポジションを処理するのはそれほど容易ではなく、しばしば注文の「スリッページ」のためだけに損失が発生して取引が終了することがあると指摘しています。注文のスリッページは、ある価格で取引が開始され、予想された価格とは異なる別の価格で約定した場合に発生します。トレーダーが数百ドルしか持っていない場合、通常、流動性に問題はありません(三流暗号通貨の全く流動性のない市場の深さを除く)。しかし、数億ドルを扱うとなると、ポジションのオープンとクローズを同時におこなうのは難しいです。これは市場の流動性に直結します。

市場の流動性はマーケットメーカーのおかげで満たされています。彼らの主な仕事は流動性を維持することです。これらの市場参加者は、人々の取引が可能な限りスムーズになるようあらゆる努力を払っており、相場に急激なギャップが生じることはなく、買い手と売り手の双方が常に自分たちに適した価格を受け取ることができるようにします。

マーケットメーカーのいない市場では、価格が一方向に大きく変動したり、資産が大きく変動したり、気配値がずれたりすることがよくあります。


マーケットメーカーはどのように機能し、なぜ「操り人形師」ではないのか

多くのトレーダーは、マーケットメーカーは操り人形師のようなもので、自分の望むところに価格を動かし、ストップレベルを破り、群衆を騙してストップ注文を出させるなどの操作をしていると確信しています。

実際、マーケットメーカーには「群衆」を負けさせる必要はまったくありません。市場の「群衆」は、スプレッド、手数料、スワップによって自ら損をします。 

マーケットが正しい方向にシフトすることに関しても、これはマーケットメーカーの仕事ではありません。取引所との契約上、マーケットメーカーに義務付けられているのは、買い手には買い気配を、売り手には売り気配を提供し、必要であれば空いた「市場の深さ」を埋めることだけです。

マーケットメーカーがいなければ、市場はまったく違ったものになっていたでしょう。価格ギャップ、気配値ギャップ、両方向への絶え間ないスクイーズ、両方向への大幅な価格ジャンプを常に目にすることになります。このようなことは、マーケットメーカーが存在しても採算が合わない市場、たとえば米国のペニー株の多くで、今日でも見られます。


暗号市場におけるAMMの新技術

しかし、参加者をスマートコントラクトに置き換えたらどうなるでしょうか。言い換えれば、マーケットメーカーの代わりに、需給と一般相場を調整する自動システムを設けたらどうでしょう。

これが分散型取引所(DEX)が登場したおおよその経緯です。DEXはAMM(自動マーケットメイク)の仕組みを初めて採用しました。AMMアルゴリズムは、参加者間の取引に参加者のリソースを使用する特別な流動性プールを通じて機能します。取引所の価格と取引量は常にアルゴリズムによってコントロールされています。これにより、すべての売り手とすべての買い手を、参加者が損をすることなく一堂に会させることができるはずです。しかし実際には、どのDEXにも大きな価格変動があります。取引量が多い場合、トークン取引所で大きな割合を失うことが保証されています。

その上、この技術革新によって市場操作がなくなったわけではありません。DEXにはたくさんあります。DEX上のトークン作成者でさえ、トークンを簡単にポンピングし、トークンの流動性プール全体を現金化することができます。

マーケットメーカーが価格操作に対抗する方法

これはマーケットメーカーの責任ではありませんが、マーケットメーカーはしばしば、不正な参加者によって価格が吊り上げられようとしているときに、その芽を摘みます。このような初期段階では、マーケットメーカーは「市場」価格を押し上げようとするプレーヤーに大量の指値注文を出します。これにより需要が消滅するため、ポンプスキームの新規参入者はマーケットメーカーに歯を折られることが非常に多いです。しかし、ポンプがうまく計画されていれば、多くの成行注文が流入し、強力に価格を動かすため、マーケットメーカーは一時的に市場から退場せざるを得なくなります。


マーケットメーカーはいつ市場から去るのか

ほとんどのマーケットメーカーは、取引所との契約の中で、休日、異常な取引がおこなわれている期間、重要なニュースが発表される期間には、アルゴリズムをオフにし、市場から退出することを定めています。これは、MMが資本を維持しようとするためです。 

スプレッドの拡大により、マーケットメーカーが即座に市場から退出しているのがわかります。強力なグローバルニュースが発表されると、ECNでもスプレッドが広がるのをご覧になったことがありますか。通常のスプレッドの狭さは、マーケットメーカーの努力によって実現されています。したがって、それがなければ、広いスプレッド、大きな価格スリッページ、突然のディップや価格スパイクなど、野放図な市場の楽しみをすべて含む、とりわけ非常に悪い取引条件に直面することになります。


マーケットメーカーの在庫リスクとは

多くの人は、マーケットメーカーはまったくリスクを負わないと考えています。しかし、これはそうではありません。マーケットメーカーの主なリスクは在庫リスクです。このリスクは、ポジションが一方向に急激に動く可能性があり、そのポジションをオフロードしてスプレッドで稼ぐ能力がないことにあります。例えば、熱狂的な群衆がある資産を売ると、マーケットメーカーは全供給を買い取ることを余儀なくされます。その結果、価格はマイナスになり、MMは損失を被ります。 

企業は、特別なスプレッド中心方程式を使い、売買の最適価格を決定することで、このリスクを回避しようとしています。しかし、これは必ずしも実現可能なことではありません。たとえ価格が最適でなかったとしても、MMの仕事は市場に流動性を供給することであり、たとえ一時的に赤字であったとしても、この仕事をこなさなければなりません。 

世界最大のマーケットメーカー、ケネス・グリフィンの会社の記録の分析

ケネス・グリフィンが設立した世界最大のマーケットメーカー、シタデル・セキュリティーズの活動を分析すると、その役割が金融市場においていかに重要であるかが明らかになります。

同社の報告書は、印象的な影響を示しています。米国株式市場の取引の10件中7件は、このマーケットメーカーが提供する流動性に依存しています。この活動は、同社がこの市場の安定性と流動性の維持に重要な役割を果たしていることを示しています。

グリフィンの会社の影響力の大きさを評価するために、毎日約9億ロットの米国株がそのアルゴリズムを通過していることを挙げることができます。この大きな取引量は、同社の米国取引所における高い活動性と影響力を反映しています。

ところで、ケネス・グリフィンがディレクテッド取引からマーケットメイキングに移行していく過程は非常に興味深いものです。グリフィンの会社は世界市場に積極的に進出しており、アジアの取引所を積極的に開拓し、そこで流動性を提供しています。

 

マーケットメーカーEAの準備

理論はわかりました。次に、マーケットメーカーEAを作り始めます。もちろん、私たちのアルゴリズムは非常にシンプルなものになります。特別な方程式に従ってスプレッド取引を構築するつもりはありません。 

その代わりに、2つの指値注文(売り指値と買い指値)を常に開いておくという、最も単純なアルゴリズムを実装します。

最もシンプルなマーケットメイクのMQL5実装

アルゴリズムのコードを分析してみましょう。コードの見出しです。このセクションでは、ロットサイズ、利益レベル、EAマジックナンバー、取引通貨ペアなど、ストラテジーの基本パラメータを設定します。

//+------------------------------------------------------------------+
//|                                                  MarketMaker.mq5 |
//|                                Copyright 2023, Evgeniy Koshtenko |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Evgeniy Koshtenko"
#property link      "https://www.mql5.com/en/users/koshtenko"
#property version   "1.00"

#include <Trade\Trade.mqh>        // Include the CTrade trading class

//--- input parameters
input double Lots       = 0.1;    // lot
input double Profit     = 0.1;    // profit
input double BProfit    = 11;     // buy profit
input double SProfit    = 11;     // sell profit
input int StopLoss      = 0;      // stop loss
input int TakeProfit    = 0;      // take profit
input int    Count      = 5;      // number of orders
input int    Delta      = 55;     // delta
input int    Magic      = 123;    // magic number

input bool   BuyLimit   = 1;      // Buy Limit
input bool   SellLimit  = 1;      // Sell Limit

input string Symbol1    = "EURUSD";
input string Symbol2    = "GBPUSD";
input string Symbol3    = "USDCHF";
input string Symbol4    = "USDJPY";
input string Symbol5    = "USDCAD";
input string Symbol6    = "AUDUSD";
input string Symbol7    = "NZDUSD";
input string Symbol8    = "EURGBP";
input string Symbol9    = "CADCHF";
input int MaxOrders = 20; // Max number of orders
CTrade trade;

datetime t=0;
int delta=0;

注文間のデルタ、決済利益(合計、買い利益、売り利益)、EAマジックナンバー、取引ライブラリのインポート、取引通貨ペアの選択、注文数の制限などの基本設定が含まれています。

初期化関数と初期化解除関数は一般的に標準的なものです。OnInit()関数は EAが開始するときに呼び出され、OnDeinit()関数は EAが終了するときに呼び出されます。OnInit()はEAのマジックナンバーと取引関数のタイマーを設定します。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   // Set a timer with a resolution of 10000 milliseconds (10 seconds) 
   EventSetMillisecondTimer(100000);
   trade.SetExpertMagicNumber(Magic);
//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {// Disable timer
   EventKillTimer();
   Comment("");
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

以下は、未決済注文と未決済ポジションをカウントする関数です。CountOrdersとCountTradesは、EAのマジックナンバーを考慮して、特定の銘柄の未決済注文とポジションをカウントします。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CountOrders(string symbol, ENUM_ORDER_TYPE orderType) {
  int count = 0;
  
  for(int i = OrdersTotal()-1; i >= 0; i--) {
      
    ulong ticket = OrderGetTicket(i);
      
    if(!OrderSelect(ticket)) {
      continue;
    }
      
    if(OrderGetInteger(ORDER_TYPE) != orderType) {
      continue;
    }
    
    if(PositionGetString(POSITION_SYMBOL) != symbol ||
       PositionGetInteger(POSITION_MAGIC) != Magic) {
      continue; 
    }
      
    count++;
  }
  
  return count;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CountTrades(string symbol, ENUM_POSITION_TYPE type) {
  int count = 0;
  
  for(int i=PositionsTotal()-1; i>=0; i--) {
    
    ulong ticket=PositionGetTicket(i);
      
    if(!PositionSelectByTicket(ticket)) {
      continue;
    }
    
    if(PositionGetString(POSITION_SYMBOL)==symbol && 
       PositionGetInteger(POSITION_TYPE)==type) {
        
      count++;
    }
  }
  
  return count;
}

以下は、注文の削除、利益の計算、注文の決済に関する関数です。DelOrderは、マジックナンバーを使用して特定の銘柄の注文をすべて削除します。AllProfitは、マジックナンバーを考慮に入れて、特定の銘柄の売買取引からの利益または利益の合計を計算します。

//+------------------------------------------------------------------+
//|  Position Profit                                                 |
//+------------------------------------------------------------------+
double AllProfit(string symbol, int positionType = -1) {

  double profit = 0;

  for(int i = PositionsTotal()-1; i >= 0; i--) {

    ulong ticket = PositionGetTicket(i);

    if(!PositionSelectByTicket(ticket)) {
      continue;
    }

    if(PositionGetString(POSITION_SYMBOL) != symbol ||
       PositionGetInteger(POSITION_MAGIC) != Magic) {
      continue;
    }

    if(positionType != -1 && 
       PositionGetInteger(POSITION_TYPE) != positionType) {
      continue;
    }

    profit += PositionGetDouble(POSITION_PROFIT);

  }

  return profit;

}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CloseAll(string symbol, int positionType = -1) {

  for(int i = PositionsTotal()-1; i >= 0; i--) {

    ulong ticket = PositionGetTicket(i);

    if(!PositionSelectByTicket(ticket)) {
      continue;
    }

    if(PositionGetString(POSITION_SYMBOL) != symbol ||
       PositionGetInteger(POSITION_MAGIC) != Magic) {
      continue;
    }

    if(positionType != -1 && 
       PositionGetInteger(POSITION_TYPE) != positionType) {
      continue;  
    }

    trade.PositionClose(ticket);

  }

}

最後に、取引関数とティック関数が2つの主要な関数です。取引は、指定されたパラメータを考慮した指値の買い注文と売り注文を出す責任があります。OnTimerはTrade関数を呼び出し、選択された銘柄を取引し、その銘柄の利益情報を表示します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void Trade(string symb)
  {
   double sl = 0, tp = 0;
   double pr=0;
   double Bid=SymbolInfoDouble(symb,SYMBOL_BID);
  
   if(AllProfit(symb)>Profit && Profit>0)
      CloseAll(symb);
   
   if(AllProfit(symb)>Profit && Profit>0)
      CloseAll(symb);
      
   if(AllProfit(symb,0)>BProfit && BProfit>0)
      CloseAll(symb,0);
  
      for(int i=1; i<=Count; i++)
        {
         if(BuyLimit)
           {
           
            if (StopLoss > 0)
                sl = NormalizeDouble(Bid - (StopLoss) * Point(), _Digits);
            if (TakeProfit > 0)
                tp = NormalizeDouble(Bid + (TakeProfit) * Point(), _Digits);
                
            pr=NormalizeDouble(Bid-(Delta+Step)*_Point*i,_Digits);
            trade.BuyLimit(Lots,pr,symb,sl, tp,0,0,"");
           }
         if(SellLimit)
           {
            
            if (StopLoss > 0)
                sl = NormalizeDouble(Bid + (_Point * StopLoss) * Point(), _Digits);
            if (TakeProfit > 0)
                tp = NormalizeDouble(Bid - (_Point * TakeProfit) * Point(), _Digits);
                
            pr=NormalizeDouble(Bid+(Delta+Step)*_Point*i,_Digits);
            trade.SellLimit(Lots,pr,symb,sl, tp,0,0,"");
           }
         
        }
     
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTimer()
  {
   DelOrder();
   Trade(Symbol1);
   Trade(Symbol2);
   Trade(Symbol3);
   Comment("\n All Profit: ",AllProfit(Symbol1),
           "\n Buy Profit: ",AllProfit(Symbol1,0),
           "\n Sell Profit: ",AllProfit(Symbol1,1));
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+--------+

これがこのシンプルなEAのコード全体です。

テスト結果

では、テスターでデフォルト設定のEAを起動してみましょう。2023年2月1日から2024年2月18日までのEURUSD、GBPUSD、EURGBP、USDJPY、EURJPYのEAの結果です。

EAテスト

利益に対するドローダウンが非常に大きいです。エクイティのドローダウンは通常、年間利益よりも大きいです。EAの動作は通常のグリッドEAと大差ありません。以下はテストの統計です。

テスト統計

どうやら、このEAはそのリスクに対して何の見返りもないようです。ストップレベルのないアルゴリズムと同様、これは時限爆弾です。損失を出していないにもかかわらず、市場が1日あたり10~15%の通貨暴落を経験しないとは誰も保証できません。個人的には、この4年間で、市場では絶対に何でも可能であり、信じられないようなシナリオでさえ実現する可能性があることを学びました。このEAは私の評価基準を満たさないので、これを公開することにしました。

結論

最も単純なマーケットメーカーのアルゴリズムの例を作成しました。もちろん、この例は例示であり、非常に単純なものです。明らかに、長い間市場でこのように働いてきたマーケットメーカーは一社もありません。現在では、そのアルゴリズムは時代に対応し、機械学習やニューラルネットワークを使用し、オーダーブックからのストリーミングデータに基づいて深層学習を適用し、多くの変数や価格特性を考慮しています。価格より上や下に注文を出ス人はもう誰もいません。これには在庫リスクが伴います。将来的には、機械学習を使用してマーケットメーカーを作り、注文間の最適なデルタを独自に決定する実験も合理的かもしれません。


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/13897

添付されたファイル |
Experts.zip (33.42 KB)
母集団最適化アルゴリズム:微小人工免疫系(Micro-AIS) 母集団最適化アルゴリズム:微小人工免疫系(Micro-AIS)
この記事では、身体の免疫系の原理に基づいた最適化手法、つまりAISを改良した微小人工免疫系(Micro Artificial Immune System:Micro-AIS)について考察します。Micro-AISは、より単純な免疫系のモデルと単純な免疫情報処理操作を用います。また、この記事では、従来のAISと比較した場合のMicro-AISの利点と欠点についても触れています。
母集団最適化アルゴリズム:細菌採餌最適化-遺伝的アルゴリズム(BFO-GA) 母集団最適化アルゴリズム:細菌採餌最適化-遺伝的アルゴリズム(BFO-GA)
本稿では、細菌採餌最適化(BFO)アルゴリズムのアイデアと遺伝的アルゴリズム(GA)で使用される技術を組み合わせ、ハイブリッドBFO-GAアルゴリズムとして最適化問題を解くための新しいアプローチを紹介します。最適解を大域的に探索するために細菌の群れを使い、局所最適解を改良するために遺伝的演算子を使用します。元のBFOとは異なり、細菌は突然変異を起こし、遺伝子を受け継ぐことができるようになっています。
リプレイシステムの開発(第33回):発注システム(II) リプレイシステムの開発(第33回):発注システム(II)
今日も発注システムの開発を続けます。ご覧のように、他の記事ですでに紹介したものを大量に再利用することになります。とはいえ、この記事にはささやかなご褒美があります。まず、デモ口座からでもリアル口座からでも、取引サーバーで使えるシステムを開発します。MetaTrader 5プラットフォームを幅広く活用し、当初から必要なサポートをすべて提供します。
母集団最適化アルゴリズム:進化戦略、(μ,λ)-ESと(μ+λ)-ES 母集団最適化アルゴリズム:進化戦略、(μ,λ)-ESと(μ+λ)-ES
この記事では、進化戦略(Evolution Strategies:ES)として知られる最適化アルゴリズム群について考察します。これらは、最適解を見つけるために進化原理を用いた最初の集団アルゴリズムの1つです。従来のESバリエーションへの変更を実施し、アルゴリズムのテスト関数とテストスタンドの手法を見直します。