SuperCharlie_Aldridge/MarketBook.mqh

429 lines
16 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:27:21 +02:00
//+------------------------------------------------------------------+
//| MarketBook.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link "http://www.mql5.com"
#define LAST_ASK_INDEX 0
#define LAST_BID_INDEX m_depth_total-1
//+------------------------------------------------------------------+
//| Side of MarketBook. |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_SIDE
{
MBOOK_ASK, // Ask side
MBOOK_BID // Bid (offer) side
};
//+------------------------------------------------------------------+
//| Market Book info integer properties. |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_INFO_INTEGER
{
MBOOK_BEST_ASK_INDEX, // Best ask index
MBOOK_BEST_BID_INDEX, // Best bid index
MBOOK_LAST_ASK_INDEX, // Last (worst) ask index
MBOOK_LAST_BID_INDEX, // Last (worst) bid index
MBOOK_DEPTH_ASK, // Depth of ask side
MBOOK_DEPTH_BID, // Depth of bid side
MBOOK_DEPTH_TOTAL, // Total depth
MBOOK_MAX_ASK_VOLUME, // Max ask volume
MBOOK_MAX_ASK_VOLUME_INDEX, // Max ask volume index
MBOOK_MAX_BID_VOLUME, // Max bid volume
MBOOK_MAX_BID_VOLUME_INDEX, // Max bid volume index
MBOOK_ASK_VOLUME_TOTAL, // Total volume on Ask side of MarketBook
MBOOK_BID_VOLUME_TOTAL, // Total volume on Bid side of MarketBook
MBOOK_BUY_ORDERS, // Total orders on sell
MBOOK_SELL_ORDERS, // Total orders on buy
};
//+------------------------------------------------------------------+
//| Market Book info double properties. |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_INFO_DOUBLE
{
MBOOK_BEST_ASK_PRICE, // Best ask price,
MBOOK_BEST_BID_PRICE, // Best bid price,
MBOOK_LAST_ASK_PRICE, // Last (worst) ask price,
MBOOK_LAST_BID_PRICE, // Last (worst) bid price,
MBOOK_AVERAGE_SPREAD, // Average spread for work time
MBOOK_OPEN_INTEREST, // Current Open Interest of Market
MBOOK_BUY_ORDERS_VOLUME, // Total volume of sell orders
MBOOK_SELL_ORDERS_VOLUME, // Total volume of buy orders
};
class CMarketBook;
class CBookCalculation
{
private:
int m_max_ask_index; // Index of maximum Bid volume
long m_max_ask_volume; // Maximum Bid volume
int m_max_bid_index; // Index of maximum Ask volume
long m_max_bid_volume; // Maximum Ask volume
long m_sum_ask_volume; // Total volume of Ask price in DOM
long m_sum_bid_volume; // Total volume of Ask price in DOM.
bool m_calculation; // flag indicating that all necessary calculations are made
CMarketBook* m_book; // Depth of Market indicator
void Calculation(void)
{
// FOR ASK SIDE
int begin = (int)m_book.InfoGetInteger(MBOOK_LAST_ASK_INDEX);
int end = (int)m_book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
//m_ask_best_index
for(int i = begin; i <= end && begin !=-1; i++)
{
if(m_book.MarketBook[i].volume > m_max_ask_volume)
{
m_max_ask_index = i;
m_max_ask_volume = m_book.MarketBook[i].volume;
}
m_sum_ask_volume += m_book.MarketBook[i].volume;
}
// FOR BID SIDE
begin = (int)m_book.InfoGetInteger(MBOOK_BEST_BID_INDEX);
end = (int)m_book.InfoGetInteger(MBOOK_LAST_BID_INDEX);
for(int i = begin; i <= end && begin != -1; i++)
{
if(m_book.MarketBook[i].volume > m_max_bid_volume)
{
m_max_bid_index = i;
m_max_bid_volume = m_book.MarketBook[i].volume;
}
m_sum_bid_volume += m_book.MarketBook[i].volume;
}
m_calculation = true;
}
public:
CBookCalculation(CMarketBook* book)
{
Reset();
m_book = book;
}
void Reset()
{
m_max_ask_volume = 0.0;
m_max_bid_volume = 0.0;
m_max_ask_index = -1;
m_max_bid_index = -1;
m_sum_ask_volume = 0;
m_sum_bid_volume = 0;
m_calculation = false;
}
int GetMaxVolAskIndex()
{
if(!m_calculation)
Calculation();
return m_max_ask_index;
}
long GetMaxVolAsk()
{
if(!m_calculation)
Calculation();
return m_max_ask_volume;
}
int GetMaxVolBidIndex()
{
if(!m_calculation)
Calculation();
return m_max_bid_index;
}
long GetMaxVolBid()
{
if(!m_calculation)
Calculation();
return m_max_bid_volume;
}
long GetAskVolTotal()
{
if(!m_calculation)
Calculation();
return m_sum_ask_volume;
}
long GetBidVolTotal()
{
if(!m_calculation)
Calculation();
return m_sum_bid_volume;
}
};
class CMarketBook
{
private:
string m_symbol; // Market Book symbol
int m_depth_total; // Market depth total
bool m_available; // True if market book available, otherwise false
double m_spread_sum; // Accumulation spread;
int m_count_refresh; // Count call CMarketBook::Refresh()
/* Indexes fields*/
int m_best_ask_index; // Best ask index
int m_best_bid_index; // Best bid index
void SetBestAskAndBidIndex(void);
bool FindBestBid(void);
CBookCalculation Calculation;
public:
MqlBookInfo MarketBook[]; // Array of market book
MqlTick LastTick;
CMarketBook();
CMarketBook(string symbol);
long InfoGetInteger(ENUM_MBOOK_INFO_INTEGER property);
double InfoGetDouble(ENUM_MBOOK_INFO_DOUBLE property);
void Refresh(void);
bool IsAvailable(void);
bool SetMarketBookSymbol(string symbol);
string GetMarketBookSymbol(void);
double GetDeviationByVol(long vol, ENUM_MBOOK_SIDE side);
long GetVolByDeviation(double deviation, ENUM_MBOOK_SIDE side);
double Last();
};
//+------------------------------------------------------------------+
//| Default constructor. |
//+------------------------------------------------------------------+
CMarketBook::CMarketBook(void) : Calculation(GetPointer(this))
{
SetMarketBookSymbol(Symbol());
}
//+------------------------------------------------------------------+
//| Create Market Book and set symbol for it. |
//+------------------------------------------------------------------+
CMarketBook::CMarketBook(string symbol) : Calculation(GetPointer(this))
{
SetMarketBookSymbol(symbol);
}
//+------------------------------------------------------------------+
//| Get symbol for market book. |
//+------------------------------------------------------------------+
string CMarketBook::GetMarketBookSymbol(void)
{
return m_symbol;
}
//+------------------------------------------------------------------+
//| Set symbol for market book. |
//+------------------------------------------------------------------+
bool CMarketBook::SetMarketBookSymbol(string symbol)
{
ArrayResize(MarketBook, 0);
m_available = false;
m_best_ask_index = -1;
m_best_bid_index = -1;
m_depth_total = 0;
bool isSelect = SymbolSelect(symbol, true);
if(isSelect)
m_symbol = symbol;
else
{
if(!SymbolSelect(m_symbol, true) && SymbolSelect(Symbol(), true))
m_symbol = Symbol();
}
if(isSelect)
MarketBookAdd(m_symbol);
Refresh();
return isSelect;
}
//+------------------------------------------------------------------+
//| Refresh Market Book. |
//+------------------------------------------------------------------+
void CMarketBook::Refresh(void)
{
m_available = MarketBookGet(m_symbol, MarketBook);
SymbolInfoTick(Symbol(), LastTick);
m_depth_total = ArraySize(MarketBook);
SetBestAskAndBidIndex();
if(m_depth_total == 0)
return;
m_count_refresh++;
if(m_best_ask_index != -1 && m_best_bid_index != -1)
m_spread_sum += MarketBook[m_best_ask_index].price-MarketBook[m_best_bid_index].price;
Calculation.Reset();
}
//+------------------------------------------------------------------+
//| Returns the Last price, if the last tick was assotiated with |
//| buy or sell |
//+------------------------------------------------------------------+
/*double CMarketBook::Last(void)
{
bool is_buy = (m_last_tick.flags & TICK_FLAG_BUY) == TICK_FLAG_BUY;
}*/
//+------------------------------------------------------------------+
//| Return true if market book is available, otherwise return false |
//+------------------------------------------------------------------+
bool CMarketBook::IsAvailable(void)
{
return m_available;
}
//+------------------------------------------------------------------+
//| Find best ask and bid indexes and set this indexes for |
//| m_best_ask_index and m_best_bid field |
//+------------------------------------------------------------------+
void CMarketBook::SetBestAskAndBidIndex(void)
{
if(!FindBestBid())
{
//Find best ask by slow full search
m_best_ask_index = -1;
int bookSize = ArraySize(MarketBook);
for(int i = 0; i < bookSize; i++)
{
if((MarketBook[i].type == BOOK_TYPE_BUY) || (MarketBook[i].type == BOOK_TYPE_BUY_MARKET))
{
m_best_ask_index = i-1;
FindBestBid();
break;
}
}
}
}
//+------------------------------------------------------------------+
//| Fast find best bid by best ask |
//+------------------------------------------------------------------+
bool CMarketBook::FindBestBid(void)
{
m_best_bid_index = -1;
bool isBestAsk = m_best_ask_index >= 0 && m_best_ask_index < m_depth_total &&
(MarketBook[m_best_ask_index].type == BOOK_TYPE_SELL ||
MarketBook[m_best_ask_index].type == BOOK_TYPE_SELL_MARKET);
if(!isBestAsk && m_best_ask_index != -1)return false;
int bestBid = m_best_ask_index+1;
bool isBestBid = bestBid >= 0 && bestBid < m_depth_total &&
(MarketBook[bestBid].type == BOOK_TYPE_BUY ||
MarketBook[bestBid].type == BOOK_TYPE_BUY_MARKET);
if(isBestBid)
{
m_best_bid_index = bestBid;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Get integer property by ENUM_MBOOK_INFO_INTEGER modifier |
//+------------------------------------------------------------------+
long CMarketBook::InfoGetInteger(ENUM_MBOOK_INFO_INTEGER property)
{
switch(property)
{
case MBOOK_BEST_ASK_INDEX:
return m_best_ask_index;
case MBOOK_BEST_BID_INDEX:
return m_best_bid_index;
case MBOOK_LAST_ASK_INDEX:
if(m_best_ask_index == -1)
return -1;
else
return LAST_ASK_INDEX;
case MBOOK_LAST_BID_INDEX:
if(m_best_bid_index == -1)
return -1;
else
return LAST_BID_INDEX;
case MBOOK_DEPTH_TOTAL:
return m_depth_total;
case MBOOK_DEPTH_BID:
return (m_depth_total - m_best_bid_index);
case MBOOK_DEPTH_ASK:
return m_best_bid_index;
case MBOOK_MAX_ASK_VOLUME:
return Calculation.GetMaxVolAsk();
case MBOOK_MAX_ASK_VOLUME_INDEX:
return Calculation.GetMaxVolAskIndex();
case MBOOK_MAX_BID_VOLUME:
return Calculation.GetMaxVolBid();
case MBOOK_MAX_BID_VOLUME_INDEX:
return Calculation.GetMaxVolBidIndex();
case MBOOK_BUY_ORDERS:
return SymbolInfoInteger(m_symbol, SYMBOL_SESSION_BUY_ORDERS);
case MBOOK_SELL_ORDERS:
return SymbolInfoInteger(m_symbol, SYMBOL_SESSION_SELL_ORDERS);
case MBOOK_ASK_VOLUME_TOTAL:
return Calculation.GetAskVolTotal();
case MBOOK_BID_VOLUME_TOTAL:
return Calculation.GetBidVolTotal();
}
return 0;
}
//+------------------------------------------------------------------+
//| Get double property by ENUM_MBOOK_INFO_DOUBLE modifier |
//+------------------------------------------------------------------+
double CMarketBook::InfoGetDouble(ENUM_MBOOK_INFO_DOUBLE property)
{
switch(property)
{
case MBOOK_BEST_ASK_PRICE:
return MarketBook[m_best_ask_index].price;
case MBOOK_BEST_BID_PRICE:
return MarketBook[m_best_bid_index].price;
case MBOOK_LAST_ASK_PRICE:
return MarketBook[LAST_ASK_INDEX].price;
case MBOOK_LAST_BID_PRICE:
return MarketBook[LAST_BID_INDEX].price;
case MBOOK_AVERAGE_SPREAD:
return (m_spread_sum/m_count_refresh);
case MBOOK_BUY_ORDERS_VOLUME:
return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_BUY_ORDERS_VOLUME);
case MBOOK_SELL_ORDERS_VOLUME:
return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_SELL_ORDERS_VOLUME);
case MBOOK_OPEN_INTEREST:
return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_INTEREST);
}
return 0.0;
}
//+------------------------------------------------------------------+
//| Get deviation value by volume. Return 0.0 if deviation is |
//| infinity (insufficient liquidity) |
//+------------------------------------------------------------------+
double CMarketBook::GetDeviationByVol(long vol, ENUM_MBOOK_SIDE side)
{
if(ArraySize(MarketBook) == 0)return 0.0;
int best_ask = (int)InfoGetInteger(MBOOK_BEST_ASK_INDEX);
int last_ask = (int)InfoGetInteger(MBOOK_LAST_ASK_INDEX);
int best_bid = (int)InfoGetInteger(MBOOK_BEST_BID_INDEX);
int last_bid = (int)InfoGetInteger(MBOOK_LAST_BID_INDEX);
double avrg_price = 0.0;
long volume_exe = vol;
if(side == MBOOK_ASK)
{
for(int i = best_ask; i >= last_ask; i--)
{
long currVol = MarketBook[i].volume < volume_exe ?
MarketBook[i].volume : volume_exe ;
avrg_price += currVol * MarketBook[i].price;
volume_exe -= MarketBook[i].volume;
if(volume_exe <= 0)break;
}
}
else
{
for(int i = best_bid; i <= last_bid; i++)
{
long currVol = MarketBook[i].volume < volume_exe ?
MarketBook[i].volume : volume_exe ;
avrg_price += currVol * MarketBook[i].price;
volume_exe -= MarketBook[i].volume;
if(volume_exe <= 0)break;
}
}
if(volume_exe > 0)
return 0.0;
avrg_price/= (double)vol;
double deviation = 0.0;
if(side == MBOOK_ASK)
deviation = avrg_price - MarketBook[best_ask].price;
else
deviation = MarketBook[best_bid].price - avrg_price;
return deviation;
}