176 lines
6.2 KiB
MQL5
176 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));
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|