MQL5Book/Indicators/p6/MarketBookDisplay.mq5
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

175 lines
6.2 KiB
MQL5

//+------------------------------------------------------------------+
//| MarketBookDisplay.mq5 |
//| Copyright 2022, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2022, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property description "Subscribe to market book of specified symbol and display its updates in the comment."
#property description "Show buy and sell volumes as a histogram in a subwindow."
#property indicator_separate_window
#property indicator_plots 2
#property indicator_buffers 2
// plot settings
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
#property indicator_label1 "Buys"
#property indicator_type2 DRAW_HISTOGRAM
#property indicator_color2 clrOrangeRed
#property indicator_width2 2
#property indicator_label2 "Sells"
#include "..\..\Include\PRTF.mqh"
input string WorkSymbol = ""; // WorkSymbol (if empty, use current chart symbol)
input bool AdvancedMode = false;
input bool ShowVolumeInLots = false;
const string _WorkSymbol = StringLen(WorkSymbol) == 0 ? _Symbol : WorkSymbol;
double buys[], sells[];
int depth, digits;
double tick, contract;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// indicator buffers setup
SetIndexBuffer(0, buys);
SetIndexBuffer(1, sells);
ArraySetAsSeries(buys, true);
ArraySetAsSeries(sells, true);
// obtain symbol properties
depth = (int)PRTF(SymbolInfoInteger(_WorkSymbol, SYMBOL_TICKS_BOOKDEPTH));
digits = (int)PRTF(SymbolInfoInteger(_WorkSymbol, SYMBOL_DIGITS));
tick = PRTF(SymbolInfoDouble(_WorkSymbol, SYMBOL_TRADE_TICK_SIZE));
contract = PRTF(SymbolInfoDouble(_WorkSymbol, SYMBOL_TRADE_CONTRACT_SIZE));
if(tick > 0)
{
// enable market book events
PRTF(MarketBookAdd(_WorkSymbol));
}
return tick > 0 ? INIT_SUCCEEDED : INIT_FAILED;
}
#define VOL(V) (ShowVolumeInLots ? V / contract : V)
//+------------------------------------------------------------------+
//| Market book notification handler |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
if(symbol == _WorkSymbol) // process only book for requested symbol
{
MqlBookInfo mbi[];
if(MarketBookGet(symbol, mbi)) // retrieve current book
{
// clean up all slots (multiplied by 10 to take some
// additional slots which may be affected because of AdvancedMode
for(int i = 0; i <= fmin(depth * 10, Bars(_Symbol, _Period) - 1); ++i)
{
buys[i] = EMPTY_VALUE;
sells[i] = EMPTY_VALUE;
}
int half = ArraySize(mbi) / 2; // estimation of the middle slot
bool correct = true;
// collect all levels into a string to display
string s = "";
for(int i = 0; i < ArraySize(mbi); ++i)
{
s += StringFormat("%02d %s %s %d %g\n", i,
(mbi[i].type == BOOK_TYPE_BUY ? "B" :
(mbi[i].type == BOOK_TYPE_SELL ? "S" : "?")),
DoubleToString(mbi[i].price, digits),
mbi[i].volume, mbi[i].volume_real);
if(i > 0) // find the middle of the book
{
if(mbi[i - 1].type == BOOK_TYPE_SELL
&& mbi[i].type == BOOK_TYPE_BUY)
{
half = i;
}
if(mbi[i - 1].price <= mbi[i].price)
{
correct = false;
}
}
}
// NB: Comment is important for implicit chart refreshing!
Comment(s + (!correct ? "\nINCORRECT BOOK" : ""));
if(!correct) return;
// now fill indicator buffers with data
if(AdvancedMode) // show empty slots
{
for(int i = 0; i < ArraySize(mbi); ++i)
{
if(i < half)
{
const int x = (int)MathRound((mbi[i].price - mbi[half - 1].price) / tick);
if(x < Bars(_Symbol, _Period))
{
sells[x] = -VOL(mbi[i].volume_real);
}
}
else
{
const int x = (int)MathRound((mbi[half].price - mbi[i].price) / tick);
if(x < Bars(_Symbol, _Period))
{
buys[x] = VOL(mbi[i].volume_real);
}
}
}
}
else // standard mode: skip empty slots
{
for(int i = 0; i < ArraySize(mbi); ++i)
{
if(i < half)
{
sells[half - i - 1] = -VOL(mbi[i].volume_real);
}
else
{
buys[i - half] = VOL(mbi[i].volume_real);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function (dummy) |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int, const double &price[])
{
if(prev_calculated == 0)
{
ArrayInitialize(buys, EMPTY_VALUE);
ArrayInitialize(sells, EMPTY_VALUE);
}
return rates_total;
}
//+------------------------------------------------------------------+
//| Finalization handler |
//+------------------------------------------------------------------+
void OnDeinit(const int)
{
Comment("");
PRTF(MarketBookRelease(_WorkSymbol));
}
//+------------------------------------------------------------------+