//+------------------------------------------------------------------+ //| OrderFlowHistogram.mq5 | //| Copyright 2026, Antigravity AI | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Antigravity AI" #property link "https://www.mql5.com" #property version "1.20" #property indicator_chart_window #property indicator_plots 0 #include //--- Input Parameters input int InpPriceStepTicks = 5; // Price Clustering (Ticks per Row) input int InpMaxBars = 30; // Max Bars with History (Limit for performance) input color InpBuyColor = clrAqua; input color InpSellColor = clrMagenta; input int InpOpacity = 120; // Transparency (0-255) input int InpHistWidthPct = 40; // Histogram Width (% of Bar Width) input int InpFontSize = 9; // Label Font Size input color InpTextColor = clrWhite; // Label Color //--- Structures struct LevelData { double price; double buy_vol; double sell_vol; }; struct BarData { datetime time; LevelData levels[]; int levels_count; bool processed; }; //--- Global Variables CCanvas m_canvas; BarData m_bars[]; int m_max_bars = 0; double m_point_step = 0; double m_prev_bid = 0; double m_prev_ask = 0; double m_prev_price = 0; bool m_history_loaded = false; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { m_max_bars = InpMaxBars; ArrayResize(m_bars, m_max_bars); for(int i=0; i 0) { for(int j = 0; j < copied; j++) { ProcessSingleTick(ticks[j], i); } m_bars[i].processed = true; } } Print("OrderFlow: History load complete."); } //+------------------------------------------------------------------+ //| Process a Single Tick into a specific Bar index | //+------------------------------------------------------------------+ void ProcessSingleTick(MqlTick &tick, int bar_history_idx) { double price = (tick.last != 0) ? tick.last : (tick.bid + tick.ask) / 2.0; double vol = (tick.volume_real > 0) ? tick.volume_real : (double)tick.volume; if(vol <= 0) vol = 1.0; // FIXED Bitwise Check (reverted user's logical &&) string dir = "NEUTRAL"; if((tick.flags & TICK_FLAG_BUY) != 0) dir = "BUY"; else if((tick.flags & TICK_FLAG_SELL) != 0) dir = "SELL"; else if(tick.last != 0) { if(tick.last >= tick.ask) dir = "BUY"; else if(tick.last <= tick.bid) dir = "SELL"; } else { // Ask/Bid movement check (fallback for many FX brokers) static double last_bid = 0, last_ask = 0; if(tick.ask > last_ask && last_ask > 0) dir = "BUY"; else if(tick.bid < last_bid && last_bid > 0) dir = "SELL"; else if((tick.flags & TICK_FLAG_ASK) != 0) dir = "BUY"; else if((tick.flags & TICK_FLAG_BID) != 0) dir = "SELL"; last_ask = tick.ask; last_bid = tick.bid; } double level_price = MathFloor(price / m_point_step) * m_point_step; int idx = -1; for(int i=0; i 0; i--) { m_bars[i].time = m_bars[i-1].time; m_bars[i].levels_count = m_bars[i-1].levels_count; m_bars[i].processed = m_bars[i-1].processed; ArrayCopy(m_bars[i].levels, m_bars[i-1].levels); } m_bars[0].time = current_bar_time; m_bars[0].levels_count = 0; m_bars[0].processed = false; ArrayResize(m_bars[0].levels, 0); } //+------------------------------------------------------------------+ //| Rendering Logic | //+------------------------------------------------------------------+ void Render() { int chart_w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); int chart_h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); if(m_canvas.Width() != chart_w || m_canvas.Height() != chart_h) m_canvas.Resize(chart_w, chart_h); m_canvas.Erase(0); for(int i=0; i max_v) max_v = m_bars[i].levels[j].buy_vol; if(m_bars[i].levels[j].sell_vol > max_v) max_v = m_bars[i].levels[j].sell_vol; } // Draw levels for(int j=0; j bar_high) bar_high = m_bars[i].levels[j].price; if(m_bars[i].levels[j].price < bar_low) bar_low = m_bars[i].levels[j].price; } if(bar_high == -1) continue; bar_high += m_point_step; // Account for the top of the highest level int y_text_top, y_text_bottom; if(ChartTimePriceToXY(0, 0, time_val, bar_high, x_center, y_text_top) && ChartTimePriceToXY(0, 0, time_val, bar_low, x_center, y_text_bottom)) { m_canvas.FontSet("Trebuchet MS", -InpFontSize * 10, FW_NORMAL); // Total Volume (Top) string txt_total = StringFormat("%.0f", bar_buy + bar_sell); m_canvas.TextOut(x_center, y_text_top - 15, txt_total, ColorToARGB(InpTextColor), TA_CENTER | TA_BOTTOM); // Buy Volume (Bottom Right) string txt_buy = StringFormat("%.0f", bar_buy); m_canvas.TextOut(x_center + 5, y_text_bottom + 5, txt_buy, ColorToARGB(InpBuyColor), TA_LEFT | TA_TOP); // Sell Volume (Bottom Left) string txt_sell = StringFormat("%.0f", bar_sell); m_canvas.TextOut(x_center - 5, y_text_bottom + 5, txt_sell, ColorToARGB(InpSellColor), TA_RIGHT | TA_TOP); } } m_canvas.Update(); } //+------------------------------------------------------------------+