// 
// Anchored VWAP | indicator
// Generated by Z.ai/GML-5.2
// 
// ### How to use:
// 1. Drag the indicator onto your chart. It will automatically drop a
// dashed vertical line at the current candle.
// 2. Double-click that vertical line to select.
// 3. Drag the line left or right and drop it on any candle you want. 
// 4. The VWAP will **instantly** redraw itself to anchor exactly at the candle
// you dropped the line on.
// 5. If new ticks come in, it will continue calculating from the anchor point
// you selected.

#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots   1

#property indicator_label1  "Anchored VWAP"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

input ENUM_APPLIED_VOLUME VolumeType = VOLUME_TICK; // Volume to use (Tick or Real)

double         VWAPBuffer[];
double         CumPVBuffer[];  // Hidden buffer for Cumulative Price * Volume
double         CumVolBuffer[]; // Hidden buffer for Cumulative Volume

string         AnchorLineName = "AnchoredVWAP_VerticalLine";

int OnInit()
{
    SetIndexBuffer(0,VWAPBuffer,INDICATOR_DATA);
    SetIndexBuffer(1,CumPVBuffer,INDICATOR_CALCULATIONS);
    SetIndexBuffer(2,CumVolBuffer,INDICATOR_CALCULATIONS);
    
    IndicatorSetString(INDICATOR_SHORTNAME,"Anchored VWAP");
    IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    
    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
    
    if(ObjectFind(0, AnchorLineName) < 0)
    {
        ObjectCreate(0, AnchorLineName, OBJ_VLINE, 0, TimeCurrent(), 0);
        ObjectSetInteger(0, AnchorLineName, OBJPROP_COLOR, clrDodgerBlue);
        ObjectSetInteger(0, AnchorLineName, OBJPROP_STYLE, STYLE_DASH);
        ObjectSetInteger(0, AnchorLineName, OBJPROP_WIDTH, 1);
        ObjectSetString(0, AnchorLineName, OBJPROP_TEXT, "VWAP Anchor (Drag me)");
        ObjectSetInteger(0, AnchorLineName, OBJPROP_BACK, true);
        ObjectSetInteger(0, AnchorLineName, OBJPROP_SELECTABLE, true);
    }

    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    ObjectDelete(0, AnchorLineName);
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    // If the user drags the vertical line, recalculate instantly
    if(id == CHARTEVENT_OBJECT_DRAG)
    {
        if(sparam == AnchorLineName)
        {
            RecalculateVWAP();
        }
    }
}

void RecalculateVWAP()
{
    int rates_total = Bars(_Symbol, _Period);
    if(rates_total == 0) return;
    
    MqlRates rates[];
    ArraySetAsSeries(rates, false); // Index 0 is oldest, matching indicator buffers
    if(CopyRates(_Symbol, _Period, 0, rates_total, rates) <= 0) return;
    
    datetime anchorTime = (datetime)ObjectGetInteger(0, AnchorLineName, OBJPROP_TIME);
    
    int start_idx = 0;
    while(start_idx < rates_total && rates[start_idx].time < anchorTime)
        start_idx++;

    // If the anchor time is in the future, wait for more bars
    if(start_idx >= rates_total) return;
    
    // Clear buffers
    ArrayInitialize(VWAPBuffer, 0.0);
    ArrayInitialize(CumPVBuffer, 0.0);
    ArrayInitialize(CumVolBuffer, 0.0);
    
    double cumPV = 0;
    double cumVol = 0;

    for(int i = start_idx; i < rates_total; i++)
    {
        double tp = (rates[i].high + rates[i].low + rates[i].close) / 3.0;
        double vol = (VolumeType == VOLUME_TICK) ? rates[i].tick_volume : rates[i].real_volume;
        if(VolumeType == VOLUME_REAL && vol == 0) vol = rates[i].tick_volume;
        
        cumPV += tp * vol;
        cumVol += vol;
        
        if(cumVol > 0)
            VWAPBuffer[i] = cumPV / cumVol;
        else
            VWAPBuffer[i] = (i > 0) ? VWAPBuffer[i-1] : tp;

        CumPVBuffer[i] = cumPV;
        CumVolBuffer[i] = cumVol;
    }
   
    ChartRedraw(0);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if(rates_total == 0) return(0);
    
    datetime anchorTime = (datetime)ObjectGetInteger(0, AnchorLineName, OBJPROP_TIME);
    
    static datetime last_anchor = 0;
    bool recalc_all = false;
    
    // Force a full recalculation if it's the first run OR if the user moved the line
    if(prev_calculated == 0 || anchorTime != last_anchor)
    {
        recalc_all = true;
        last_anchor = anchorTime;
    }

    int start_index = 0;
   
    if(recalc_all)
    {
        // Clear buffers
        ArrayInitialize(VWAPBuffer, 0.0);
        ArrayInitialize(CumPVBuffer, 0.0);
        ArrayInitialize(CumVolBuffer, 0.0);
        
        // Find the first bar that is >= AnchorTime
        while(start_index < rates_total && time[start_index] < anchorTime)
            start_index++;
        
        // If the anchor time is in the future, wait for more bars
        if(start_index >= rates_total) return(0);
        
        // Initialize the first bar
        double tp = (high[start_index] + low[start_index] + close[start_index]) / 3.0;
        long vol = (VolumeType == VOLUME_TICK) ? tick_volume[start_index] : volume[start_index];
        if(VolumeType == VOLUME_REAL && vol == 0) vol = tick_volume[start_index];
        
        CumPVBuffer[start_index] = tp * vol;
        CumVolBuffer[start_index] = vol;
        VWAPBuffer[start_index] = (vol > 0) ? tp : 0.0;
        
        start_index++;
    }
    else
    {
        // Continue from the previously calculated bar
        start_index = prev_calculated - 1;
        if(start_index < 0) start_index = 0;
    }

    //--- Main calculation loop
    for(int i = start_index; i < rates_total; i++)
    {
        // If anchor is after this bar, keep it empty
        if(time[i] < anchorTime)
        {
            VWAPBuffer[i] = 0.0;
            continue;
        }
        
        double tp = (high[i] + low[i] + close[i]) / 3.0;
        
        long vol = (VolumeType == VOLUME_TICK) ? tick_volume[i] : volume[i];
        if(VolumeType == VOLUME_REAL && vol == 0) vol = tick_volume[i];

        // If this is the exact anchor bar, initialize cumulative from scratch
        if(i > 0 && time[i-1] < anchorTime)
        {
            CumPVBuffer[i] = tp * vol;
            CumVolBuffer[i] = vol;
        }
        else
        {
            CumPVBuffer[i] = CumPVBuffer[i-1] + (tp * vol);
            CumVolBuffer[i] = CumVolBuffer[i-1] + vol;
        }

        if(CumVolBuffer[i] > 0)
            VWAPBuffer[i] = CumPVBuffer[i] / CumVolBuffer[i];
        else
            VWAPBuffer[i] = VWAPBuffer[i-1]; // Prevent division by zero
    }

    return(rates_total);
}
 