//+------------------------------------------------------------------+
//| AI RSI BB SCALPER.mq5                                            |
//| Alfr5d                                                           |
//| https://www.49design.co.za                                       |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Alfred Mandala. "
#property description "www.billionairebulls.co.za\n"
#property version "1.5"
#property description "RSI GRID SCALPING EA com Stochastic\n"

// Incluindo bibliotecas necessárias
#include <Trade/Trade.mqh>
#include <NeuralNetwork.mqh>

// ---- INPUTS ----
input group "--- Trading Inputs ---";
input double Lots = 0.01; // Tamanho do Lote
input int TpPoints = 50; // Take Profit em pontos [m5-50p, h1-200p]
input int StopLossPoints = 500; // Stop Loss em pontos [Ex: 20 pontos]
input ENUM_TIMEFRAMES RsiTimeFrame = PERIOD_CURRENT; // Timeframe

input group "--- RSI Inputs ---";
input int RsiPeriods = 18; // Periodo do RSI
input ENUM_APPLIED_PRICE RsiAppPrice = PRICE_CLOSE; // Preço aplicado pelo RSI
input double RsiSellTrigger = 70; // Zona de Venda do RSI
input double RsiBuyTrigger = 30; // Zona de Compra do RSI

input group "--- Bollinger Bands Inputs ---";
input int BbPeriods = 23; // Períodos das BB
input double BbDeviation = 2.0; // Desvio
input double BbShift = 0; // Deslocamento

input group "--- Stochastic Inputs ---";
input int StochKPeriod = 5; // Período K do Stoch
input int StochDPeriod = 3; // Período D do Stoch
input int StochSlowing = 3; // Atraso do Stoch
input double StochOverbought = 80.0; // Nível de sobrecompra
input double StochOversold = 20.0; // Nível de sobrevenda

input group "OTHER INPUTS";
input int NumTrainingBars = 5000;
input int RandomSeed = 42;
enum ENUM_ACTIVATIONFX { Sigmoid_AF = AF_SIGMOID, HyperbolicTan_AF = AF_TANH, LeakyRELU_AF = AF_LRELU, RELU_AF = AF_RELU };
enum ENUM_LOSSFX { BinaryCrossEntropy = LOSS_BCE, CategoricalCrossEntropy = LOSS_CCE, MeanSquaredError = LOSS_MSE, Hinge = LOSS_HINGE };
input ENUM_ACTIVATIONFX ActivationFx = LeakyRELU_AF;
input ENUM_LOSSFX LossFunction = MeanSquaredError;
input uint Epochs = 1000;
input double LearningRate = 0.0001;
input double PercTrainingSize = 0.7;
input string HiddenLayers = "15,10,7";

input group "*** Trading Inputs ***";
input double LotSize = 0.01; // Lot size for trading
input int Tppoints = 800; // Take Profit (10 points = 1 pip)
input int Slpoints = 800; // Stoploss Points (10 points = 1 pip)
input int TslTriggerPoints = 35; // Points in profit before Trailing SL is activated (10 points = 1 pip)
input int TslPoints = 10; // Trailing Stoploss (10 points = 1 pip)
input ENUM_TIMEFRAMES Timeframe = PERIOD_CURRENT; // Time frame to run
input int InpMagic = 891245; // EA identification no
input string TradeComment = "Wendel Cassiano";
input int MaxSpread = 100; // Maximum spread allowed (in points)
input int Slippage = 50; // Slippage in points
input int MaxOrders = 2; // Máximo de ordens permitidas simultaneamente

enum StarHour { Inactive=0, _0100=1, _0200=2, _0300=3, _0400=4, _0500=5, _0600=6, _0700=7, _0800=8, _0900=9, _1000=10, _1100=11, _1200=12, _1300=13, _1400=14, _1500=15, _1600=16, _1700=17, _1800=18, _1900=19, _2000=20, _2100=21, _2200=22, _2300=23 };
input StarHour SHInput = 0; // Start Hour

enum EndHour { Inactive=0, _0100=1, _0200=2, _0300=3, _0400=4, _0500=5, _0600=6, _0700=7, _0800=8, _0900=9, _1000=10, _1100=11, _1200=12, _1300=13, _1400=14, _1500=15, _1600=16, _1700=17, _1800=18, _1900=19, _2000=20, _2100=21, _2200=22, _2300=23 };
input EndHour EHInput = 0; // End Hour

// ---- GLOBAL VARIABLES ----
int handleRsi, handleBb, handleStochastic;
double rsi[], bbUpper[], bbLower[], stochMain[];

CTrade trade;
CPositionInfo pos;
COrderInfo ord;
class CMatrix { public: matrix Matrix; };
class CTensor {
    CMatrix* matrices[];
public:
    CTensor(uint size);
    ~CTensor(void);
    uint TensorSize;
    bool Add(matrix& mat, ulong index);
    matrix Get(ulong index);
};
CTensor::CTensor(uint size) {
    TensorSize = size;
    ArrayResize(matrices, TensorSize);
    for (uint i = 0; i < TensorSize; i++) matrices[i] = new CMatrix;
    for (uint i = 0; i < TensorSize; i++) {
        if (CheckPointer(matrices[i]) == POINTER_INVALID) {
            printf("Can't create a tensor, Invalid Matrix pointer. ERROR code = %d", GetLastError());
            return;
        }
    }
}
CTensor::~CTensor(void) {
    for (uint i = 0; i < TensorSize; i++) {
        if (CheckPointer(matrices[i]) != POINTER_INVALID) delete matrices[i];
    }
    ArrayFree(matrices);
}
bool CTensor::Add(matrix& mat, ulong index) {
    if (index >= TensorSize) {
        printf("Index stated is greater than the tensor size in the function %s", __FUNCTION__);
        return false;
    }
    this.matrices[index].Matrix = mat;
    return true;
}
matrix CTensor::Get(ulong index) {
    if (index >= TensorSize) {
        printf("%s index %d out of range, Tensor size = %d", __FUNCTION__, index, TensorSize);
        matrix mat = {};
        return (mat);
    }
    return(this.matrices[index].Matrix);
}
CTensor* WeightsTensor;
CTensor* BiasTensor;
CTensor* InputsTensor;
CTensor* OutPutsTensor;
vector HiddenLayer;
bool InBackPropagation = false;
int SHChoice;
int EHChoice;
int BarsN = 5;
int ExpirationBars = 100;
int OrderDistPoints = 100;
int OldSignal = 0, Signal;
bool IsTrained = false;

int OnInit() {
    handleRsi = iRSI(_Symbol, RsiTimeFrame, RsiPeriods, RsiAppPrice);
    handleBb = iBands(_Symbol, PERIOD_CURRENT, BbPeriods, BbShift, BbDeviation, PRICE_CLOSE);
    handleStochastic = iStochastic(_Symbol, PERIOD_CURRENT, StochKPeriod, StochDPeriod, StochSlowing, MODE_SMA, STO_LOWHIGH);

    trade.SetExpertMagicNumber(InpMagic);
    ChartSetInteger(0, CHART_SHOW_GRID, false);
    ushort Sep = StringGetCharacter(",", 0);
    string Layers[];
    int size = StringSplit(HiddenLayers, Sep, Layers);
    HiddenLayer.Resize(size);
    for (int i = 0; i < size; i++) {
        HiddenLayer[i] = (int)Layers[i];
    }
    return (INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
    IndicatorRelease(handleBb);
    IndicatorRelease(handleRsi);
    IndicatorRelease(handleStochastic);
    if (CheckPointer(WeightsTensor) != POINTER_INVALID) delete WeightsTensor;
    if (CheckPointer(BiasTensor) != POINTER_INVALID) delete BiasTensor;
    if (CheckPointer(InputsTensor) != POINTER_INVALID) delete InputsTensor;
    if (CheckPointer(OutPutsTensor) != POINTER_INVALID) delete OutPutsTensor;
}

void OnTick() {
    TrailStop();
    if (!IsNewBar()) return;

    MqlDateTime time;
    TimeToStruct(TimeCurrent(), time);
    int Hournow = time.hour;
    SHChoice = SHInput;
    EHChoice = EHInput;

    if (Hournow < SHChoice) {
        CloseAllOrders();
        return;
    }
    if (Hournow >= EHChoice && EHChoice != 0) {
        CloseAllOrders();
        return;
    }
    
    double spread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / _Point;
    if (spread > MaxSpread) return;

    int BuyTotal = 0;
    int SellTotal = 0;
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        pos.SelectByIndex(i);
        if (pos.PositionType() == POSITION_TYPE_BUY && pos.Symbol() == _Symbol && pos.Magic() == InpMagic) BuyTotal++;
        if (pos.PositionType() == POSITION_TYPE_SELL && pos.Symbol() == _Symbol && pos.Magic() == InpMagic) SellTotal++;
    }
    for (int i = OrdersTotal() - 1; i >= 0; i--) {
        ord.SelectByIndex(i);
        if (ord.OrderType() == ORDER_TYPE_BUY_STOP && ord.Symbol() == _Symbol && ord.Magic() == InpMagic) BuyTotal++;
        if (ord.OrderType() == ORDER_TYPE_SELL_STOP && ord.Symbol() == _Symbol && ord.Magic() == InpMagic) SellTotal++;
    }
    int TotalOrders = BuyTotal + SellTotal;
    if (TotalOrders >= MaxOrders) return;

    if (!IsTrained) {
        CollectTrainTest(1, NumTrainingBars);
        IsTrained = true;
    }
    
    vector inputs(1);
    inputs[0] = (iClose(_Symbol, Timeframe, 1) > iOpen(_Symbol, Timeframe, 1)) ? 1 : 0;
    BPMinMaxNormalization(inputs);
    Signal = SingleForwardPass(inputs);

    if (Signal == 1 && BuyTotal <= 0) {
        double high = findHigh();
        if (high > 0) {
            SendBuyOrder(high);
        }
    }
    if (Signal == 0 && SellTotal <= 0) {
        double low = findLow();
        if (low > 0) {
            SendSellOrder(low);
        }
    }
}

double findHigh() {
    double highestHigh = 0;
    for (int i = 0; i < 200; i++) {
        double high = iHigh(_Symbol, Timeframe, i);
        if (i > BarsN && iHighest(_Symbol, Timeframe, MODE_HIGH, BarsN * 2 + 1, i - BarsN) == i) {
            if (high > highestHigh) {
                return high;
            }
        }
        highestHigh = MathMax(high, highestHigh);
    }
    return highestHigh;  // Adicionando retorno apropriado
}

double findLow() {
    double LowestLow = DBL_MAX;
    for (int i = 0; i < 200; i++) {
        double Low = iLow(_Symbol, Timeframe, i);
        if (i > BarsN && iLowest(_Symbol, Timeframe, MODE_LOW, BarsN * 2 + 1, i - BarsN) == i) {
            if (Low < LowestLow) {
                return Low;
            }
        }
        LowestLow = MathMin(Low, LowestLow);
    }
    return LowestLow;  // Adicionando retorno apropriado
}

bool IsNewBar() {
    static datetime previousTime = 0;
    datetime currentTime = iTime(_Symbol, Timeframe, 0);
    if (previousTime != currentTime) {
        previousTime = currentTime;
        return true;
    }
    return false;
}

void SendBuyOrder(double entry) {
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    if (ask > entry - OrderDistPoints * _Point) return;
    double tp = entry + Tppoints * _Point;
    double sl = entry - Slpoints * _Point;
    double lots = LotSize;
    datetime expiration = iTime(_Symbol, Timeframe, 0) + ExpirationBars * PeriodSeconds(Timeframe);
    trade.BuyStop(lots, entry, _Symbol, sl, tp, ORDER_TIME_SPECIFIED, expiration, Slippage);
}

void SendSellOrder(double entry) {
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    if (bid < entry + OrderDistPoints * _Point) return;
    double tp = entry - Tppoints * _Point;
    double sl = entry + Slpoints * _Point;
    double lots = LotSize;
    datetime expiration = iTime(_Symbol, Timeframe, 0) + ExpirationBars * PeriodSeconds(Timeframe);
    trade.SellStop(lots, entry, _Symbol, sl, tp, ORDER_TIME_SPECIFIED, expiration, Slippage);
}

void TrailStop() {
    double sl = 0;
    double tp = 0;
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        if (pos.SelectByIndex(i)) {
            ulong ticket = pos.Ticket();
            if (pos.Magic() == InpMagic && pos.Symbol() == _Symbol) {
                if (pos.PositionType() == POSITION_TYPE_BUY) {
                    if (bid - pos.PriceOpen() > TslTriggerPoints * _Point) {
                        tp = pos.TakeProfit();
                        sl = bid - (TslPoints * _Point);
                        if (sl > pos.StopLoss() && sl != 0) {
                            trade.PositionModify(ticket, sl, tp);
                        }
                    }
                } else if (pos.PositionType() == POSITION_TYPE_SELL) {
                    if (ask + (TslTriggerPoints * _Point) < pos.PriceOpen()) {
                        tp = pos.TakeProfit();
                        sl = ask + (TslPoints * _Point);
                        if (sl < pos.StopLoss() && sl != 0) {
                            trade.PositionModify(ticket, sl, tp);
                        }
                    }
                }
            }
        }
    }
}

void CloseAllOrders() {
    for (int i = OrdersTotal() - 1; i >= 0; i--) {
        ord.SelectByIndex(i);
        ulong ticket = ord.Ticket();
        if (ord.Symbol() == _Symbol && ord.Magic() == InpMagic) {
            trade.OrderDelete(ticket);
        }
    }
}

// Definições das funções de redes neurais
void CollectTrainTest(int StartBar, int TotalBars) {
    matrix DataSet(NumTrainingBars, 1);
    vector y(NumTrainingBars);
    for (int i = 0; i < NumTrainingBars; i++) {
        DataSet[i][0] = (iClose(_Symbol, Timeframe, i + 1) > iOpen(_Symbol, Timeframe, i + 1)) ? 1 : 0;
        y[i] = (iClose(_Symbol, Timeframe, i) > iOpen(_Symbol, Timeframe, i)) ? 1 : 0;
    }
    matrix xTrain, xTest;
    vector yTrain, yTest;
    TrainTestSplitMatrices(DataSet, xTrain, yTrain, xTest, yTest, PercTrainingSize);
    Print("\n-----> Training the NN\n");
    MinMaxNormalization(xTrain);
    BackPropagation(xTrain, yTrain, Epochs, LearningRate);
    Print("\n------> Testing the NN\n");
    BPMinMaxNormalization(xTest);
    vector preds = BatchForwardPass(xTest); // Cria previsões para o conjunto de teste
    Print("Actual Values: ", yTest, "\nPredictions\n", preds); // Corrigindo ConfusionMatrix
    vector DataClasses = Classes(y); // Classes no conjunto de dados
    ConfusionMatrix(yTest, preds, DataClasses, true);
}

double Random(double mini, double maxi) {
    return mini + double((MathRand()/32767.0) * (maxi-mini));
}

void TrainTestSplitMatrices(matrix& matrix_, matrix& x_train, vector& y_train, matrix& x_test, vector& y_test, double TrainSampleSize = 0.7) {
    ulong total = matrix_.Rows(), cols = matrix_.Cols();
    ulong last_col = cols - 1;
    Randomize(matrix_);
    int TrainSize = (int)MathFloor(total * TrainSampleSize);
    int TestSize = (int)total - TrainSize;
    x_train.Resize(TrainSize, cols - 1);
    x_test.Resize(TestSize, cols - 1);
    y_train.Resize(TrainSize);
    y_test.Resize(TestSize);
    int TrainCount = 0, TestCount = 0;
    Copy(matrix_.Col(last_col), y_train, 0, TrainSize);
    Copy(matrix_.Col(last_col), y_test, TrainSize);
    for (ulong i = 0; i < matrix_.Rows(); i++) {
        if (i < (ulong)TrainSize) {
            x_train.Row(matrix_.Row(i), TrainCount);
            TrainCount++;
        } else {
            x_test.Row(matrix_.Row(i), TestCount);
            TestCount++;
        }
    }
}

void Randomize(matrix& matrix_) {
    MathSrand(RandomSeed);
    int ROWS = (int)matrix_.Rows(), COLS = (int)matrix_.Cols();
    int SwapIndex;
    matrix temp_m = matrix_;
    vector temp_v(COLS);
    for (int i = 0; i < ROWS; i++) {
        SwapIndex = MathRand() % ROWS;
        temp_v = matrix_.Row(i);
        matrix_.Row(matrix_.Row(SwapIndex), i);
        matrix_.Row(temp_v, SwapIndex);
    }
}

bool Copy(const vector& CopyFrom, vector& CopyTo, ulong StartFrom, ulong Total = WHOLE_ARRAY) {
    if (Total == WHOLE_ARRAY) Total = CopyFrom.Size() - StartFrom;
    if (Total <= 0 || CopyFrom.Size() == 0) {
        printf("%s Can't copy a vector | Size %d total %d StartFrom %d", __FUNCTION__, CopyFrom.Size(), Total, StartFrom);
        return false;
    }
    CopyTo.Resize(Total);
    CopyTo.Fill(0);
    for (ulong i = StartFrom, index = 0; i < Total + StartFrom; i++) {
        CopyTo[index] = CopyFrom[i];
        index++;
    }
    return true;
}

void BackPropagation(matrix& xTrain, vector& yTrain, uint epochs, double learningRate) {
    // Função de Backpropagation da rede neural
}

vector BatchForwardPass(matrix& xTest) {
    // Função para execução do forward pass em batch
}

int SingleForwardPass(vector& inputs) {
    // Função para execução do forward pass em um único dado
}

vector Classes(vector& y) {
    // Função para extrair classes únicas do vetor y
}




erros:

possible loss of data due to type conversion	Scalping_RSI_NEURAL_001_.mq5	137	59
implicit conversion from 'number' to 'string'	Scalping_RSI_NEURAL_001_.mq5	268	83
implicit conversion from 'number' to 'string'	Scalping_RSI_NEURAL_001_.mq5	278	84
'}' - not all control paths return a value	Scalping_RSI_NEURAL_001_.mq5	407	1
'}' - not all control paths return a value	Scalping_RSI_NEURAL_001_.mq5	411	1
'}' - not all control paths return a value	Scalping_RSI_NEURAL_001_.mq5	415	1