// ─────────────────────────────────────────────────────────────
//  TradeTrove · NAS100 Mean Reversion v3
//
//  Fades NAS100 extensions during the US session.
//  Entry: price touches a Bollinger Band extreme AND RSI
//  confirms oversold/overbought AND price is on the correct
//  side of the trend EMA. Exits on middle-band reversion or
//  ATR stop.
//
//  Generated for: Trader
//  Account size:  $100000
//  Target firm:   unspecified
//  Generated at:  2026-04-28T12:41:52.164Z
// ─────────────────────────────────────────────────────────────

using System;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None, AddIndicators = true)]
    public class Nas100MeanReversionV3 : Robot
    {
        [Parameter("Session Start UTC", DefaultValue = 14, MinValue = 0, MaxValue = 23)]
        public int SessionStartUtc { get; set; }

        [Parameter("Session End UTC", DefaultValue = 20, MinValue = 0, MaxValue = 23)]
        public int SessionEndUtc { get; set; }

        [Parameter("Bollinger Period", DefaultValue = 20, MinValue = 10, MaxValue = 50)]
        public int BbPeriod { get; set; }

        [Parameter("Bollinger Std Dev", DefaultValue = 2.1, MinValue = 1.0, MaxValue = 3.0, Step = 0.1)]
        public double BbStdDev { get; set; }

        [Parameter("RSI Period", DefaultValue = 14, MinValue = 5, MaxValue = 30)]
        public int RsiPeriod { get; set; }

        [Parameter("RSI Oversold", DefaultValue = 30, MinValue = 10, MaxValue = 40)]
        public int RsiOversold { get; set; }

        [Parameter("RSI Overbought", DefaultValue = 74, MinValue = 60, MaxValue = 90)]
        public int RsiOverbought { get; set; }

        [Parameter("Trend EMA Period", DefaultValue = 100, MinValue = 20, MaxValue = 400)]
        public int TrendEmaPeriod { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, MinValue = 5, MaxValue = 50)]
        public int AtrPeriod { get; set; }

        [Parameter("ATR SL Multiplier", DefaultValue = 1.8, MinValue = 0.5, MaxValue = 3.0, Step = 0.1)]
        public double AtrSlMultiplier { get; set; }

        [Parameter("ATR TP Multiplier", DefaultValue = 1, MinValue = 0.3, MaxValue = 2.0, Step = 0.1)]
        public double AtrTpMultiplier { get; set; }

        [Parameter("Risk % per trade", DefaultValue = 0.4, MinValue = 0.1, MaxValue = 1.5, Step = 0.05)]
        public double RiskPct { get; set; }

        private BollingerBands _bb;
        private RelativeStrengthIndex _rsi;
        private ExponentialMovingAverage _ema;
        private AverageTrueRange _atr;
        private const string Label = "TradeTrove_NAS_MR_v3";

        protected override void OnStart()
        {
            _bb = Indicators.BollingerBands(Bars.ClosePrices, BbPeriod, BbStdDev, MovingAverageType.Simple);
            _rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, RsiPeriod);
            _ema = Indicators.ExponentialMovingAverage(Bars.ClosePrices, TrendEmaPeriod);
            _atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Exponential);
        }

        protected override void OnBar()
        {
            var now = Server.Time;

            // Only trade during US session
            if (now.Hour < SessionStartUtc || now.Hour >= SessionEndUtc) return;
            if (HasOpenPosition()) return;

            var price = Bars.ClosePrices.LastValue;
            var rsi = _rsi.Result.LastValue;
            var ema = _ema.Result.LastValue;
            var upper = _bb.Top.LastValue;
            var lower = _bb.Bottom.LastValue;
            var atr = _atr.Result.LastValue;

            // Long setup: touched lower band, oversold, uptrend filter
            var touchedLower = Bars.LowPrices.LastValue <= lower;
            if (touchedLower && rsi < RsiOversold && price > ema)
            {
                EnterPosition(TradeType.Buy, atr);
                return;
            }

            // Short setup: touched upper band, overbought, downtrend filter
            var touchedUpper = Bars.HighPrices.LastValue >= upper;
            if (touchedUpper && rsi > RsiOverbought && price < ema)
            {
                EnterPosition(TradeType.Sell, atr);
            }
        }

        private bool HasOpenPosition()
        {
            foreach (var p in Positions)
            {
                if (p.Label == Label && p.SymbolName == SymbolName) return true;
            }
            return false;
        }

        private void EnterPosition(TradeType direction, double atr)
        {
            var slPips = (AtrSlMultiplier * atr) / Symbol.PipSize;
            var tpPips = (AtrTpMultiplier * atr) / Symbol.PipSize;
            var volume = VolumeForRisk(slPips);
            if (volume < Symbol.VolumeInUnitsMin) return;

            var result = ExecuteMarketOrder(direction, SymbolName, volume, Label, slPips, tpPips);
            if (result.IsSuccessful)
            {
                Print("{0} MR entry @ {1} SL={2:F1}p TP={3:F1}p",
                      direction, result.Position.EntryPrice, slPips, tpPips);
            }
        }

        private double VolumeForRisk(double slPips)
        {
            var riskAmount = Account.Balance * (RiskPct / 100.0);
            var units = riskAmount / (slPips * Symbol.PipValue);
            return Symbol.NormalizeVolumeInUnits(units, RoundingMode.Down);
        }
    }
}
