//+------------------------------------------------------------------+
//| ML_Adaptive_DMI.mq5                                              |
//| Translated from AlgoAlpha Pine Script (Mozilla Public License 2.0)|
//+------------------------------------------------------------------+
#property copyright "Translated from AlgoAlpha"
#property version   "1.00"
#property indicator_separate_window

#property indicator_plots 15
#property indicator_buffers 26

// Plot 0: OscTopFill1
#property indicator_label1  "Upper Zone 40"
#property indicator_type1   DRAW_FILLING
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

// Plot 1: OscTopFill2
#property indicator_label2  "Upper Zone 50"
#property indicator_type2   DRAW_FILLING

// Plot 2: OscBotFill1
#property indicator_label3  "Lower Zone 40"
#property indicator_type3   DRAW_FILLING

// Plot 3: OscBotFill2
#property indicator_label4  "Lower Zone 50"
#property indicator_type4   DRAW_FILLING

// Plot 4: OscHist
#property indicator_label5  "DMI Oscillator"
#property indicator_type5   DRAW_COLOR_HISTOGRAM
#property indicator_style5  STYLE_SOLID
#property indicator_width5  4

// Plot 5: AdxHist
#property indicator_label6  "ADX Center"
#property indicator_type6   DRAW_COLOR_HISTOGRAM2
#property indicator_style6  STYLE_SOLID
#property indicator_width6  6

// Plot 6: OscBullArrow
#property indicator_label7  "Bullish Oscillator Return"
#property indicator_type7   DRAW_ARROW
#property indicator_width7  2

// Plot 7: OscBearArrow
#property indicator_label8  "Bearish Oscillator Return"
#property indicator_type8   DRAW_ARROW
#property indicator_width8  2

// Plot 8: AdxSquares
#property indicator_label9  "ADX Strength"
#property indicator_type9   DRAW_COLOR_ARROW
#property indicator_width9  2

// Plot 9: PlusDiLine
#property indicator_label10 "Adaptive +DI"
#property indicator_type10  DRAW_LINE
#property indicator_width10 2

// Plot 10: MinusDiLine
#property indicator_label11 "Adaptive -DI"
#property indicator_type11  DRAW_LINE
#property indicator_width11 2

// Plot 11: DiCloudFill
#property indicator_label12 "DI Cloud Fill"
#property indicator_type12  DRAW_FILLING

// Plot 12: CrossPoint
#property indicator_label13 "DMI Cross"
#property indicator_type13  DRAW_COLOR_ARROW
#property indicator_width13 1

// Plot 13: PlusDiCloudLine
#property indicator_label14 "+DI Cloud Edge"
#property indicator_type14  DRAW_LINE
#property indicator_style14 STYLE_SOLID
#property indicator_width14 7

// Plot 14: MinusDiCloudLine
#property indicator_label15 "-DI Cloud Edge"
#property indicator_type15  DRAW_LINE
#property indicator_style15 STYLE_SOLID
#property indicator_width15 7

//--- Enums
enum ENUM_TABLE_SIZE {
    TS_TINY,   // Tiny
    TS_SMALL,  // Small
    TS_MEDIUM, // Medium
    TS_LARGE   // Large
};

enum ENUM_TABLE_PLACE {
    TP_TOP_LEFT,      // Top Left
    TP_TOP_CENTER,    // Top Center
    TP_TOP_RIGHT,     // Top Right
    TP_MIDDLE_LEFT,   // Middle Left
    TP_MIDDLE_CENTER, // Middle Center
    TP_MIDDLE_RIGHT,  // Middle Right
    TP_BOTTOM_LEFT,   // Bottom Left
    TP_BOTTOM_CENTER, // Bottom Center
    TP_BOTTOM_RIGHT   // Bottom Right
};

enum ENUM_REWARD_METRIC {
    RM_BALANCED,    // Balanced
    RM_RETURN,      // % Return
    RM_CLEAN_MOVE,  // Clean Move
    RM_WIN_RATE     // Win Rate
};

//--- Input groups
input int    InpSweepFrom        = 5;       // From | Expert Sweep
input int    InpSweepTo          = 40;      // To | Expert Sweep
input int    InpSweepInterval    = 5;       // Step | Expert Sweep
input int    InpMemorySize       = 500;     // Memory | Machine Learning
input ENUM_REWARD_METRIC InpRewardMetric = RM_BALANCED; // How To Score
input double InpRecencyDecay     = 0.985;   // Forget Old Trades | Machine Learning
input double InpSoftmaxSharpness = 1.5;     // Favor Winners | Machine Learning
input double InpMfeWeight        = 0.25;    // Reward Big Moves | Machine Learning
input double InpMaeWeight        = 0.50;    // Punish Pullbacks | Machine Learning
input int    InpMinBars          = 3;       // Too Fast If Under | Machine Learning
input double InpWhipsawPenalty   = 0.25;    // Fast Flip Penalty | Machine Learning
input int    InpAdxSmoothing     = 14;      // ADX Smoothing | DMI
input bool   InpOscillatorMode   = true;    // Oscillator Mode | Appearance
input color  InpBullColor        = C'0, 255, 187'; // Bullish | Visuals
input color  InpBearColor        = C'255, 17, 0';  // Bearish | Visuals
input color  InpAdxColor         = C'245, 0, 87';  // ADX | Visuals
input bool   InpNormalizeAdx     = true;    // Boost ADX Contrast | Visuals
input bool   InpShowTable        = true;    // Show Table | Table
input ENUM_TABLE_SIZE  InpTableTextSize = TS_SMALL;    // Table Size | Table
input ENUM_TABLE_PLACE InpTablePlace    = TP_TOP_RIGHT;// Table Place | Table

//--- Constants
#define MAX_EXPERTS 40
#define MAX_MEMORY_SIZE 5000
#define OBJ_PREFIX  "MLADMI_"

//--- Indicator buffers
double OscTopFill1_1[], OscTopFill1_2[];
double OscTopFill2_1[], OscTopFill2_2[];
double OscBotFill1_1[], OscBotFill1_2[];
double OscBotFill2_1[], OscBotFill2_2[];
double OscHist[], OscHistColor[];
double AdxHist1[], AdxHist2[], AdxHistColor[];
double OscBullArrow[];
double OscBearArrow[];
double AdxSquares[], AdxSquaresColor[];
double PlusDiLine[];
double MinusDiLine[];
double DiCloudFill1[], DiCloudFill2[];
double CrossPoint[], CrossPointColor[];
double PlusDiCloudLine[];
double MinusDiCloudLine[];
double AdxHistoryBuffer[]; 

//--- State struct for managing tick execution
struct SState {
    double plusSmooth[MAX_EXPERTS];
    double minusSmooth[MAX_EXPERTS];
    double trSmooth[MAX_EXPERTS];
    double adxSmooth[MAX_EXPERTS];
    int    direction[MAX_EXPERTS];
    double entryPrice[MAX_EXPERTS];
    double entryAtr[MAX_EXPERTS];
    int    barsHeld[MAX_EXPERTS];
    double maxFav[MAX_EXPERTS];
    double maxAdv[MAX_EXPERTS];
    
    int    memLengths[MAX_MEMORY_SIZE];
    double memRewards[MAX_MEMORY_SIZE];
    int    memBars[MAX_MEMORY_SIZE];
    int    memCount;

    bool   adaptInit;
    double adaptPlusDm;
    double adaptMinusDm;
    double adaptTr;
    double adaptAdx;

    bool   atrInit;
    double atr14;

    double prevPlusDi;
    double prevMinusDi;
    
    double lastAdaptLen;
    int    lastAdaptIntLen;
    double lastPlusDi;
    double lastMinusDi;
};

//--- Global variables
int    g_expertLengths[MAX_EXPERTS];
int    g_expertCount;
SState g_state;
SState g_savedState;

//+------------------------------------------------------------------+
//| Color Mixing Helpers                                             |
//+------------------------------------------------------------------+
color MakeColor(uchar r, uchar g, uchar b) { return (color)(r | (g << 8) | (b << 16)); }
uchar colorToR(color c) { return (uchar)(c & 0xFF); }
uchar colorToG(color c) { return (uchar)((c >> 8) & 0xFF); }
uchar colorToB(color c) { return (uchar)((c >> 16) & 0xFF); }

color MixColor(color c, double t, color bg)
{
    t = MathMax(0.0, MathMin(1.0, t));
    
    // Simulating TradingView Dark Background
    if(bg == clrBlack) bg = C'19, 23, 34'; 
    
    double r = colorToR(c) * (1.0 - t) + colorToR(bg) * t;
    double g = colorToG(c) * (1.0 - t) + colorToG(bg) * t;
    double b = colorToB(c) * (1.0 - t) + colorToB(bg) * t;
    return MakeColor((uchar)r, (uchar)g, (uchar)b);
}

//+------------------------------------------------------------------+
//| Helper: safe division                                            |
//+------------------------------------------------------------------+
double SafeDiv(double x, double y)
{
    if(y == 0.0 || !MathIsValidNumber(y)) return 0.0;
    return x / y;
}

//+------------------------------------------------------------------+
//| Helper: true range for bar i                                     |
//+------------------------------------------------------------------+
double TrueRangeBar(const double &high[], const double &low[], const double &close[], int i)
{
    double hl  = high[i] - low[i];
    double hpc = MathAbs(high[i] - close[i-1]);
    double lpc = MathAbs(low[i]  - close[i-1]);
    return MathMax(hl, MathMax(hpc, lpc));
}

//+------------------------------------------------------------------+
//| Helper: phase reward calculation                                 |
//+------------------------------------------------------------------+
double PhaseReward(double finalMove, double mfe, double mae, double atrEntry, int heldBars)
{
    double normReturn = SafeDiv(finalMove, atrEntry);
    double normMfe    = SafeDiv(mfe,       atrEntry);
    double normMae    = SafeDiv(mae,       atrEntry);
    double duration   = MathSqrt(MathMax(1, heldBars));
    double raw;

    if(InpRewardMetric == RM_RETURN)
        raw = normReturn;
    else if(InpRewardMetric == RM_CLEAN_MOVE)
        raw = normReturn - normMae;
    else if(InpRewardMetric == RM_WIN_RATE)
        raw = normReturn > 0 ? 1.0 : -1.0;
    else // RM_BALANCED
        raw = normReturn + InpMfeWeight * normMfe - InpMaeWeight * normMae;

    double adjusted = SafeDiv(raw, duration);
    return heldBars < InpMinBars ? adjusted - InpWhipsawPenalty : adjusted;
}

//+------------------------------------------------------------------+
//| Prepend to memory arrays (shift right, insert at 0)              |
//+------------------------------------------------------------------+
void StoreReward(int expertLen, double reward, int heldBars)
{
    int cap = MathMin(InpMemorySize, MAX_MEMORY_SIZE);
    if(g_state.memCount < cap)
    {
        g_state.memCount++;
    }
    for(int j = g_state.memCount - 1; j > 0; j--)
    {
        g_state.memLengths[j] = g_state.memLengths[j-1];
        g_state.memRewards[j] = g_state.memRewards[j-1];
        g_state.memBars[j]    = g_state.memBars[j-1];
    }
    g_state.memLengths[0] = expertLen;
    g_state.memRewards[0] = reward;
    g_state.memBars[0]    = heldBars;
}

//+------------------------------------------------------------------+
//| Score for a given expert length using memory                     |
//+------------------------------------------------------------------+
double ScoreForLength(int expertLen)
{
    double wScore = 0.0, wTotal = 0.0;
    for(int j = 0; j < g_state.memCount; j++)
    {
        if(g_state.memLengths[j] == expertLen)
        {
            double ageW = MathPow(InpRecencyDecay, j);
            wScore += g_state.memRewards[j] * ageW;
            wTotal += ageW;
        }
    }
    return wTotal > 0.0 ? wScore / wTotal : 0.0;
}

//+------------------------------------------------------------------+
//| Build expert length list from sweep parameters                   |
//+------------------------------------------------------------------+
void InitExperts()
{
    int startLen = MathMin(InpSweepFrom, InpSweepTo);
    int endLen   = MathMax(InpSweepFrom, InpSweepTo);
    int stepLen  = MathMax(1, InpSweepInterval);

    g_expertCount = 0;
    for(int l = startLen; l <= endLen && g_expertCount < MAX_EXPERTS; l += stepLen)
    {
        g_expertLengths[g_expertCount] = l;
        g_expertCount++;
    }
}

//+------------------------------------------------------------------+
//| Reset all running state for full recalculation                   |
//+------------------------------------------------------------------+
void ResetAllState()
{
    InitExperts();

    ZeroMemory(g_state);

    for(int e=0; e<MAX_EXPERTS; e++) {
        g_state.plusSmooth[e]  = -1.0; 
        g_state.minusSmooth[e] = -1.0;
        g_state.trSmooth[e]    = -1.0;
        g_state.adxSmooth[e]   = -1.0;
    }

    g_state.lastAdaptLen    = 14.0;
    g_state.lastAdaptIntLen = 14;

    g_savedState = g_state;

    ObjectsDeleteAll(0, OBJ_PREFIX);
}

//+------------------------------------------------------------------+
//| Compute softmax-weighted adaptive length from scored experts     |
//+------------------------------------------------------------------+
double ComputeAdaptiveLength()
{
    double wLen = 0.0, wTotal = 0.0;
    for(int i = 0; i < g_expertCount; i++)
    {
        double score   = ScoreForLength(g_expertLengths[i]);
        double clamped = MathMax(-50.0, MathMin(50.0, score * InpSoftmaxSharpness));
        double rawW    = MathExp(clamped);
        wLen   += g_expertLengths[i] * rawW;
        wTotal += rawW;
    }
    double adapt = wTotal > 0.0 ? wLen / wTotal : 14.0;

    double lo = MathMin(InpSweepFrom, InpSweepTo);
    double hi = MathMax(InpSweepFrom, InpSweepTo);
    return MathMax(lo, MathMin(hi, adapt));
}

//+------------------------------------------------------------------+
//| Update all experts for bar i                                     |
//+------------------------------------------------------------------+
void UpdateExperts(int i,
                   double plusDmRaw, double minusDmRaw, double trueRange,
                   double atr, double closePrice, double highPrice, double lowPrice)
{
    double adxAlpha = 1.0 / InpAdxSmoothing;

    for(int e = 0; e < g_expertCount; e++)
    {
        double alpha = 1.0 / g_expertLengths[e];

        double prevPlus  = g_state.plusSmooth [e];
        double prevMinus = g_state.minusSmooth[e];
        double prevTr    = g_state.trSmooth   [e];
        double prevAdx   = g_state.adxSmooth  [e];

        double plusSm  = (prevPlus  < 0.0) ? plusDmRaw  : prevPlus  + alpha * (plusDmRaw  - prevPlus);
        double minusSm = (prevMinus < 0.0) ? minusDmRaw : prevMinus + alpha * (minusDmRaw - prevMinus);
        double trSm    = (prevTr    < 0.0) ? trueRange  : prevTr    + alpha * (trueRange  - prevTr);

        double plusDi  = 100.0 * SafeDiv(plusSm,  trSm);
        double minusDi = 100.0 * SafeDiv(minusSm, trSm);
        double dx      = 100.0 * SafeDiv(MathAbs(plusDi - minusDi), plusDi + minusDi);
        double adxSm   = (prevAdx < 0.0) ? dx : prevAdx + adxAlpha * (dx - prevAdx);

        g_state.plusSmooth [e] = plusSm;
        g_state.minusSmooth[e] = minusSm;
        g_state.trSmooth   [e] = trSm;
        g_state.adxSmooth  [e] = adxSm;

        int oldDir = g_state.direction[e];
        int newDir = (plusDi > minusDi) ? 1 : (minusDi > plusDi) ? -1 : oldDir;

        if(oldDir == 0 && newDir != 0)
        {
            g_state.direction [e] = newDir;
            g_state.entryPrice[e] = closePrice;
            g_state.entryAtr  [e] = atr;
            g_state.barsHeld  [e] = 0;
            g_state.maxFav    [e] = 0.0;
            g_state.maxAdv    [e] = 0.0;
        }
        else if(oldDir != 0)
        {
            int    held = g_state.barsHeld[e] + 1;
            double ep   = g_state.entryPrice[e];
            double fav  = (oldDir == 1) ? highPrice - ep : ep - lowPrice;
            double adv  = (oldDir == 1) ? ep - lowPrice  : highPrice - ep;

            g_state.barsHeld[e] = held;
            if(fav > g_state.maxFav[e]) g_state.maxFav[e] = fav;
            if(adv > g_state.maxAdv[e]) g_state.maxAdv[e] = adv;

            if(newDir != oldDir && newDir != 0)
            {
                double finalMove = (oldDir == 1) ? closePrice - ep : ep - closePrice;
                double reward    = PhaseReward(finalMove, g_state.maxFav[e], g_state.maxAdv[e], g_state.entryAtr[e], held);
                StoreReward(g_expertLengths[e], reward, held);

                g_state.direction [e] = newDir;
                g_state.entryPrice[e] = closePrice;
                g_state.entryAtr  [e] = atr;
                g_state.barsHeld  [e] = 0;
                g_state.maxFav    [e] = 0.0;
                g_state.maxAdv    [e] = 0.0;
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Create or update a signal arrow object on the main chart         |
//+------------------------------------------------------------------+
void CreateSignalArrow(string name, datetime t, double price, bool isBull)
{
    if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);
    ENUM_OBJECT arrowType = isBull ? OBJ_ARROW_UP : OBJ_ARROW_DOWN;
    ObjectCreate(0, name, arrowType, 0, t, price);
    ObjectSetInteger(0, name, OBJPROP_COLOR,  isBull ? InpBullColor : InpBearColor);
    ObjectSetInteger(0, name, OBJPROP_WIDTH,  2);
    ObjectSetInteger(0, name, OBJPROP_ANCHOR, isBull ? ANCHOR_TOP : ANCHOR_BOTTOM);
    ObjectSetInteger(0, name, OBJPROP_BACK,   false);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
}

//+------------------------------------------------------------------+
//| Update Table UI                                                  |
//+------------------------------------------------------------------+
void UpdateTable(double adaptLen, int adaptIntLen, int memCount, string metric, string trend)
{
    if(!InpShowTable) {
        ObjectsDeleteAll(0, OBJ_PREFIX + "TBL_");
        return;
    }

    int fontSize = 8;
    if(InpTableTextSize == TS_TINY) fontSize = 6;
    else if(InpTableTextSize == TS_SMALL) fontSize = 8;
    else if(InpTableTextSize == TS_MEDIUM) fontSize = 10;
    else if(InpTableTextSize == TS_LARGE) fontSize = 14;

    int rowH = fontSize * 2 + 4;
    int col1W = fontSize * 14;
    int col2W = fontSize * 10;
    int tableW = col1W + col2W;
    int tableH = rowH * 5;

    int baseX = 10, baseY = 10;
    long chartW = ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
    long chartH = ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);

    switch(InpTablePlace) {
        case TP_TOP_LEFT:      baseX = 10; baseY = 10; break;
        case TP_TOP_CENTER:    baseX = (int)(chartW - tableW)/2; baseY = 10; break;
        case TP_TOP_RIGHT:     baseX = (int)(chartW - tableW - 10); baseY = 10; break;
        case TP_MIDDLE_LEFT:   baseX = 10; baseY = (int)(chartH - tableH)/2; break;
        case TP_MIDDLE_CENTER: baseX = (int)(chartW - tableW)/2; baseY = (int)(chartH - tableH)/2; break;
        case TP_MIDDLE_RIGHT:  baseX = (int)(chartW - tableW - 10); baseY = (int)(chartH - tableH)/2; break;
        case TP_BOTTOM_LEFT:   baseX = 10; baseY = (int)(chartH - tableH - 30); break; 
        case TP_BOTTOM_CENTER: baseX = (int)(chartW - tableW)/2; baseY = (int)(chartH - tableH - 30); break;
        case TP_BOTTOM_RIGHT:  baseX = (int)(chartW - tableW - 10); baseY = (int)(chartH - tableH - 30); break;
    }

    color bg = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
    color fg = (color)ChartGetInteger(0, CHART_COLOR_FOREGROUND);
    
    color tableBg = MixColor(fg, 0.90, bg); 
    color borderCol = MixColor(fg, 0.70, bg);

    string cells[5][2] = {
        {"Metric", "Value"},
        {"Optimal length", StringFormat("%.2f (%d)", adaptLen, adaptIntLen)},
        {"Memory", IntegerToString(memCount)},
        {"Goal", metric},
        {"Trend", trend}
    };

    for(int r=0; r<5; r++) {
        for(int c=0; c<2; c++) {
            int cW = (c == 0) ? col1W : col2W;
            int xOffset = (c == 0) ? 0 : col1W;
            int yOffset = r * rowH;

            string bgName = OBJ_PREFIX + "TBL_BG_" + IntegerToString(r) + "_" + IntegerToString(c);
            if(ObjectFind(0, bgName) < 0) {
                ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
                ObjectSetInteger(0, bgName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
                ObjectSetInteger(0, bgName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
                ObjectSetInteger(0, bgName, OBJPROP_WIDTH, 1);
                ObjectSetInteger(0, bgName, OBJPROP_SELECTABLE, false);
            }
            ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, tableBg);
            ObjectSetInteger(0, bgName, OBJPROP_COLOR, borderCol);
            ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, baseX + xOffset);
            ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, baseY + yOffset);
            ObjectSetInteger(0, bgName, OBJPROP_XSIZE, cW);
            ObjectSetInteger(0, bgName, OBJPROP_YSIZE, rowH);

            string cellName = OBJ_PREFIX + "TBL_TXT_" + IntegerToString(r) + "_" + IntegerToString(c);
            if(ObjectFind(0, cellName) < 0) {
                ObjectCreate(0, cellName, OBJ_LABEL, 0, 0, 0);
                ObjectSetInteger(0, cellName, OBJPROP_FONTSIZE, fontSize);
                ObjectSetString(0, cellName, OBJPROP_FONT, "Arial");
                ObjectSetInteger(0, cellName, OBJPROP_SELECTABLE, false);
                ObjectSetInteger(0, cellName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
                ObjectSetInteger(0, cellName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
            }

            ObjectSetInteger(0, cellName, OBJPROP_XDISTANCE, baseX + xOffset + 5);
            ObjectSetInteger(0, cellName, OBJPROP_YDISTANCE, baseY + yOffset + 2); // padding

            color txtCol = fg;
            if(r == 4 && c == 1) {
                txtCol = (trend == "Bullish") ? InpBullColor : InpBearColor;
            }

            ObjectSetInteger(0, cellName, OBJPROP_COLOR, txtCol);
            ObjectSetString(0, cellName, OBJPROP_TEXT, cells[r][c]);
        }
    }
}

//+------------------------------------------------------------------+
//| OnChartEvent                                                     |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
    if(id == CHARTEVENT_CHART_CHANGE)
    {
        if(InpShowTable && g_expertCount > 0)
        {
            string trend = (g_state.lastPlusDi > g_state.lastMinusDi) ? "Bullish" : "Bearish";
            string metricStr = "Balanced";
            if (InpRewardMetric == RM_RETURN) metricStr = "% Return";
            else if (InpRewardMetric == RM_CLEAN_MOVE) metricStr = "Clean Move";
            else if (InpRewardMetric == RM_WIN_RATE) metricStr = "Win Rate";

            UpdateTable(g_state.lastAdaptLen, g_state.lastAdaptIntLen, g_state.memCount, metricStr, trend);
            ChartRedraw();
        }
    }
}

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
{
    SetIndexBuffer(0, OscTopFill1_1, INDICATOR_DATA);
    SetIndexBuffer(1, OscTopFill1_2, INDICATOR_DATA);
    SetIndexBuffer(2, OscTopFill2_1, INDICATOR_DATA);
    SetIndexBuffer(3, OscTopFill2_2, INDICATOR_DATA);
    SetIndexBuffer(4, OscBotFill1_1, INDICATOR_DATA);
    SetIndexBuffer(5, OscBotFill1_2, INDICATOR_DATA);
    SetIndexBuffer(6, OscBotFill2_1, INDICATOR_DATA);
    SetIndexBuffer(7, OscBotFill2_2, INDICATOR_DATA);
    SetIndexBuffer(8, OscHist, INDICATOR_DATA);
    SetIndexBuffer(9, OscHistColor, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(10, AdxHist1, INDICATOR_DATA);
    SetIndexBuffer(11, AdxHist2, INDICATOR_DATA);
    SetIndexBuffer(12, AdxHistColor, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(13, OscBullArrow, INDICATOR_DATA);
    SetIndexBuffer(14, OscBearArrow, INDICATOR_DATA);
    SetIndexBuffer(15, AdxSquares, INDICATOR_DATA);
    SetIndexBuffer(16, AdxSquaresColor, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(17, PlusDiLine, INDICATOR_DATA);
    SetIndexBuffer(18, MinusDiLine, INDICATOR_DATA);
    SetIndexBuffer(19, DiCloudFill1, INDICATOR_DATA);
    SetIndexBuffer(20, DiCloudFill2, INDICATOR_DATA);
    SetIndexBuffer(21, CrossPoint, INDICATOR_DATA);
    SetIndexBuffer(22, CrossPointColor, INDICATOR_COLOR_INDEX);
    SetIndexBuffer(23, PlusDiCloudLine, INDICATOR_DATA);
    SetIndexBuffer(24, MinusDiCloudLine, INDICATOR_DATA);
    SetIndexBuffer(25, AdxHistoryBuffer, INDICATOR_CALCULATIONS);

    ArrayInitialize(OscTopFill1_1, EMPTY_VALUE); ArrayInitialize(OscTopFill1_2, EMPTY_VALUE);
    ArrayInitialize(OscTopFill2_1, EMPTY_VALUE); ArrayInitialize(OscTopFill2_2, EMPTY_VALUE);
    ArrayInitialize(OscBotFill1_1, EMPTY_VALUE); ArrayInitialize(OscBotFill1_2, EMPTY_VALUE);
    ArrayInitialize(OscBotFill2_1, EMPTY_VALUE); ArrayInitialize(OscBotFill2_2, EMPTY_VALUE);
    ArrayInitialize(OscHist, EMPTY_VALUE);
    ArrayInitialize(AdxHist1, EMPTY_VALUE); ArrayInitialize(AdxHist2, EMPTY_VALUE);
    ArrayInitialize(OscBullArrow, EMPTY_VALUE); ArrayInitialize(OscBearArrow, EMPTY_VALUE);
    ArrayInitialize(AdxSquares, EMPTY_VALUE);
    ArrayInitialize(PlusDiLine, EMPTY_VALUE); ArrayInitialize(MinusDiLine, EMPTY_VALUE);
    ArrayInitialize(DiCloudFill1, EMPTY_VALUE); ArrayInitialize(DiCloudFill2, EMPTY_VALUE);
    ArrayInitialize(CrossPoint, EMPTY_VALUE);
    ArrayInitialize(PlusDiCloudLine, EMPTY_VALUE); ArrayInitialize(MinusDiCloudLine, EMPTY_VALUE);
    ArrayInitialize(AdxHistoryBuffer, EMPTY_VALUE);

    color bg = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);

    PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, MixColor(InpBearColor, 0.80, bg));
    PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, MixColor(InpBearColor, 0.80, bg));
    PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0, MixColor(InpBearColor, 0.64, bg));
    PlotIndexSetInteger(1, PLOT_LINE_COLOR, 1, MixColor(InpBearColor, 0.64, bg));

    PlotIndexSetInteger(2, PLOT_LINE_COLOR, 0, MixColor(InpBullColor, 0.80, bg));
    PlotIndexSetInteger(2, PLOT_LINE_COLOR, 1, MixColor(InpBullColor, 0.80, bg));
    PlotIndexSetInteger(3, PLOT_LINE_COLOR, 0, MixColor(InpBullColor, 0.64, bg));
    PlotIndexSetInteger(3, PLOT_LINE_COLOR, 1, MixColor(InpBullColor, 0.64, bg));

    PlotIndexSetInteger(4, PLOT_COLOR_INDEXES, 32); 
    for(int i=0; i<16; i++) {
        double fade = 0.85 - (i / 15.0) * 0.85; 
        PlotIndexSetInteger(4, PLOT_LINE_COLOR, i, MixColor(InpBearColor, fade, bg));
        PlotIndexSetInteger(4, PLOT_LINE_COLOR, i+16, MixColor(InpBullColor, fade, bg));
    }

    PlotIndexSetInteger(5, PLOT_COLOR_INDEXES, 16);
    PlotIndexSetInteger(8, PLOT_COLOR_INDEXES, 16);
    for(int i=0; i<16; i++) {
        double fade = 0.85 - (i / 15.0) * 0.85;
        PlotIndexSetInteger(5, PLOT_LINE_COLOR, i, MixColor(InpAdxColor, fade, bg));
        PlotIndexSetInteger(8, PLOT_LINE_COLOR, i, MixColor(InpAdxColor, fade, bg));
    }

    PlotIndexSetInteger(6, PLOT_ARROW, 233);
    PlotIndexSetInteger(6, PLOT_LINE_COLOR, 0, InpBullColor);
    PlotIndexSetInteger(7, PLOT_ARROW, 234);
    PlotIndexSetInteger(7, PLOT_LINE_COLOR, 0, InpBearColor);

    PlotIndexSetInteger(8, PLOT_ARROW, 110);
    
    PlotIndexSetInteger(9, PLOT_LINE_COLOR, 0, InpBullColor);
    PlotIndexSetInteger(10, PLOT_LINE_COLOR, 0, InpBearColor);

    PlotIndexSetInteger(11, PLOT_LINE_COLOR, 0, MixColor(InpBullColor, 0.7, bg));
    PlotIndexSetInteger(11, PLOT_LINE_COLOR, 1, MixColor(InpBearColor, 0.7, bg));

    PlotIndexSetInteger(12, PLOT_COLOR_INDEXES, 2);
    PlotIndexSetInteger(12, PLOT_ARROW, 159);
    PlotIndexSetInteger(12, PLOT_LINE_COLOR, 0, InpBearColor);
    PlotIndexSetInteger(12, PLOT_LINE_COLOR, 1, InpBullColor);

    PlotIndexSetInteger(13, PLOT_LINE_COLOR, 0, MixColor(InpBullColor, 0.7, bg));
    PlotIndexSetInteger(14, PLOT_LINE_COLOR, 0, MixColor(InpBearColor, 0.7, bg));

    PlotIndexSetInteger(0, PLOT_SHOW_DATA, false);
    PlotIndexSetInteger(1, PLOT_SHOW_DATA, false);
    PlotIndexSetInteger(2, PLOT_SHOW_DATA, false);
    PlotIndexSetInteger(3, PLOT_SHOW_DATA, false);
    PlotIndexSetInteger(11, PLOT_SHOW_DATA, false);
    PlotIndexSetInteger(13, PLOT_SHOW_DATA, false);
    PlotIndexSetInteger(14, PLOT_SHOW_DATA, false);

    if(InpOscillatorMode) {
        PlotIndexSetInteger(8, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(9, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(10, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(11, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(12, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(13, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(14, PLOT_DRAW_TYPE, DRAW_NONE);
        
        PlotIndexSetInteger(8, PLOT_SHOW_DATA, false);
        PlotIndexSetInteger(9, PLOT_SHOW_DATA, false);
        PlotIndexSetInteger(10, PLOT_SHOW_DATA, false);
        PlotIndexSetInteger(12, PLOT_SHOW_DATA, false);
    } else {
        PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(4, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(5, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(6, PLOT_DRAW_TYPE, DRAW_NONE);
        PlotIndexSetInteger(7, PLOT_DRAW_TYPE, DRAW_NONE);
        
        PlotIndexSetInteger(4, PLOT_SHOW_DATA, false);
        PlotIndexSetInteger(5, PLOT_SHOW_DATA, false);
        PlotIndexSetInteger(6, PLOT_SHOW_DATA, false);
        PlotIndexSetInteger(7, PLOT_SHOW_DATA, false);
    }

    IndicatorSetString(INDICATOR_SHORTNAME, "ML Adaptive DMI");
    IndicatorSetInteger(INDICATOR_DIGITS, 2);

    ResetAllState();
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| OnDeinit                                                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, OBJ_PREFIX);
}

//+------------------------------------------------------------------+
//| OnCalculate                                                      |
//+------------------------------------------------------------------+
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 < 2) return 0;

    ArraySetAsSeries(time,  false);
    ArraySetAsSeries(high,  false);
    ArraySetAsSeries(low,   false);
    ArraySetAsSeries(close, false);

    int start = prev_calculated;
    if(prev_calculated == 0)
    {
        ResetAllState();
        start = 1;
    }
    else if(prev_calculated > 1)
        start = prev_calculated - 1;

    // VERY IMPORTANT: Restore the state to the exact state at the close of the 
    // PREVIOUS bar to prevent "repainting" or variables exploding to infinity 
    // due to multiple intra-bar ticks on the live bar!
    g_state = g_savedState;

    for(int i = start; i < rates_total; i++)
    {
        double upMove    = high[i]  - high[i-1];
        double downMove  = low[i-1] - low[i];
        double plusDmRaw  = (upMove > downMove && upMove > 0)     ? upMove   : 0.0;
        double minusDmRaw = (downMove > upMove && downMove > 0)   ? downMove : 0.0;
        double trueRange  = TrueRangeBar(high, low, close, i);

        if(!g_state.atrInit)
        {
            g_state.atr14   = trueRange;
            g_state.atrInit = true;
        }
        else
            g_state.atr14 += (1.0 / 14.0) * (trueRange - g_state.atr14);

        UpdateExperts(i, plusDmRaw, minusDmRaw, trueRange, g_state.atr14, close[i], high[i], low[i]);

        double adaptLen    = ComputeAdaptiveLength();
        double lo          = MathMin(InpSweepFrom, InpSweepTo);
        double hi          = MathMax(InpSweepFrom, InpSweepTo);
        adaptLen           = MathMax(lo, MathMin(hi, adaptLen));
        int    adaptIntLen = (int)MathRound(adaptLen);
        adaptIntLen        = (int)MathMax(lo, MathMin(hi, adaptIntLen));

        double adaptAlpha  = 1.0 / adaptIntLen;
        double adxAlpha    = 1.0 / InpAdxSmoothing;

        if(!g_state.adaptInit)
        {
            g_state.adaptPlusDm  = plusDmRaw;
            g_state.adaptMinusDm = minusDmRaw;
            g_state.adaptTr      = trueRange;
            g_state.adaptAdx     = 0.0;
            g_state.adaptInit    = true;
        }
        else
        {
            g_state.adaptPlusDm  += adaptAlpha * (plusDmRaw  - g_state.adaptPlusDm);
            g_state.adaptMinusDm += adaptAlpha * (minusDmRaw - g_state.adaptMinusDm);
            g_state.adaptTr      += adaptAlpha * (trueRange  - g_state.adaptTr);
        }

        double adaptPlusDi  = 100.0 * SafeDiv(g_state.adaptPlusDm,  g_state.adaptTr);
        double adaptMinusDi = 100.0 * SafeDiv(g_state.adaptMinusDm, g_state.adaptTr);
        double adaptDx      = 100.0 * SafeDiv(MathAbs(adaptPlusDi - adaptMinusDi), adaptPlusDi + adaptMinusDi);
        g_state.adaptAdx   += adxAlpha * (adaptDx - g_state.adaptAdx);

        AdxHistoryBuffer[i] = g_state.adaptAdx;

        double adxLowest = g_state.adaptAdx;
        double adxHighest = g_state.adaptAdx;
        int startIdx = MathMax(0, i - 99);
        for(int k = startIdx; k <= i; k++) {
            if(AdxHistoryBuffer[k] < adxLowest) adxLowest = AdxHistoryBuffer[k];
            if(AdxHistoryBuffer[k] > adxHighest) adxHighest = AdxHistoryBuffer[k];
        }

        double adxForFade = InpNormalizeAdx ? 100.0 * SafeDiv(g_state.adaptAdx - adxLowest, adxHighest - adxLowest) : g_state.adaptAdx;
        double adxNorm = MathMax(0.0, MathMin(100.0, adxForFade));
        int adxIdx = (int)MathRound(adxNorm / 100.0 * 15.0); 
        double adxColorIndex = (double)MathMax(0, MathMin(15, adxIdx));

        double diOsc = adaptPlusDi - adaptMinusDi;
        int oscIdxBase = (int)MathRound(MathMin(MathAbs(diOsc), 40.0) / 40.0 * 15.0);
        double oscColorIndex = (diOsc >= 0) ? (16.0 + oscIdxBase) : (double)oscIdxBase;

        bool bullFlip = (g_state.prevPlusDi <= g_state.prevMinusDi) && (adaptPlusDi > adaptMinusDi);
        bool bearFlip = (g_state.prevPlusDi >= g_state.prevMinusDi) && (adaptPlusDi < adaptMinusDi);

        string name = OBJ_PREFIX + "SIG_" + (string)time[i];
        if(i > 1 && (bullFlip || bearFlip))
        {
            double sigPrice = low[i];
            if(bullFlip) {
                sigPrice = MathMin(low[i], MathMin(low[i>0?i-1:0], low[i>1?i-2:0]));
            } else {
                sigPrice = MathMax(high[i], MathMax(high[i>0?i-1:0], high[i>1?i-2:0]));
            }
            CreateSignalArrow(name, time[i], sigPrice, bullFlip);
        }
        else if (i == rates_total - 1)
        {
            // Crucial: Clean up false signals on the live bar if the price retraces and breaks the crossover condition!
            if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);
        }

        double prevOsc = g_state.prevPlusDi - g_state.prevMinusDi;
        bool oscBullReturn = (prevOsc <= -30.0) && (diOsc > -30.0);
        bool oscBearReturn = (prevOsc >= 30.0)  && (diOsc < 30.0);

        if(InpOscillatorMode)
        {
            OscTopFill1_1[i] = 40.0; OscTopFill1_2[i] = 30.0;
            OscTopFill2_1[i] = 50.0; OscTopFill2_2[i] = 40.0;
            OscBotFill1_1[i] = -30.0; OscBotFill1_2[i] = -40.0;
            OscBotFill2_1[i] = -40.0; OscBotFill2_2[i] = -50.0;

            OscHist[i] = diOsc;
            OscHistColor[i] = oscColorIndex;

            AdxHist1[i] = 2.0;
            AdxHist2[i] = -2.0;
            AdxHistColor[i] = adxColorIndex;

            OscBullArrow[i] = oscBullReturn ? -55.0 : EMPTY_VALUE;
            OscBearArrow[i] = oscBearReturn ?  55.0 : EMPTY_VALUE;

            PlusDiLine[i] = EMPTY_VALUE;
            MinusDiLine[i] = EMPTY_VALUE;
            DiCloudFill1[i] = EMPTY_VALUE;
            DiCloudFill2[i] = EMPTY_VALUE;
            AdxSquares[i] = EMPTY_VALUE;
            CrossPoint[i] = EMPTY_VALUE;
            PlusDiCloudLine[i] = EMPTY_VALUE;
            MinusDiCloudLine[i] = EMPTY_VALUE;
        }
        else
        {
            PlusDiLine[i] = adaptPlusDi;
            MinusDiLine[i] = adaptMinusDi;

            DiCloudFill1[i] = adaptPlusDi;
            DiCloudFill2[i] = adaptMinusDi;

            PlusDiCloudLine[i] = adaptPlusDi;
            MinusDiCloudLine[i] = adaptMinusDi;

            AdxSquares[i] = 0.0;
            AdxSquaresColor[i] = adxColorIndex;

            if(bullFlip || bearFlip) {
                CrossPoint[i] = (adaptPlusDi + adaptMinusDi + g_state.prevPlusDi + g_state.prevMinusDi) / 4.0;
                CrossPointColor[i] = (adaptPlusDi > adaptMinusDi) ? 1 : 0;
            } else {
                CrossPoint[i] = EMPTY_VALUE;
            }

            OscTopFill1_1[i] = EMPTY_VALUE; OscTopFill1_2[i] = EMPTY_VALUE;
            OscTopFill2_1[i] = EMPTY_VALUE; OscTopFill2_2[i] = EMPTY_VALUE;
            OscBotFill1_1[i] = EMPTY_VALUE; OscBotFill1_2[i] = EMPTY_VALUE;
            OscBotFill2_1[i] = EMPTY_VALUE; OscBotFill2_2[i] = EMPTY_VALUE;
            OscHist[i] = EMPTY_VALUE;
            AdxHist1[i] = EMPTY_VALUE; AdxHist2[i] = EMPTY_VALUE;
            OscBullArrow[i] = EMPTY_VALUE;
            OscBearArrow[i] = EMPTY_VALUE;
        }

        g_state.prevPlusDi  = adaptPlusDi;
        g_state.prevMinusDi = adaptMinusDi;

        g_state.lastAdaptLen    = adaptLen;
        g_state.lastAdaptIntLen = adaptIntLen;
        g_state.lastPlusDi      = adaptPlusDi;
        g_state.lastMinusDi     = adaptMinusDi;
        
        // If this bar is completely finished, permanently lock its state in as the saved state
        if(i < rates_total - 1) {
            g_savedState = g_state;
        }

        if(i == rates_total - 1) {
            string trend = (g_state.lastPlusDi > g_state.lastMinusDi) ? "Bullish" : "Bearish";
            string metricStr = "Balanced";
            if (InpRewardMetric == RM_RETURN) metricStr = "% Return";
            else if (InpRewardMetric == RM_CLEAN_MOVE) metricStr = "Clean Move";
            else if (InpRewardMetric == RM_WIN_RATE) metricStr = "Win Rate";

            UpdateTable(g_state.lastAdaptLen, g_state.lastAdaptIntLen, g_state.memCount, metricStr, trend);
        }
    }

    return rates_total;
}
//+------------------------------------------------------------------+
