// ─────────────────────────────────────────────────────────────
//  TradeTrove · XAUUSD London Morning Pullback
//
//  Trades trend continuations on Gold during London morning.
//  Enters on pullbacks to a fast EMA when the slow EMA confirms
//  trend direction. ATR-sized stops and targets. One setup at
//  a time — designed for survivability over a challenge.
//
//  Generated for: Trader
//  Account size:  $100000
//  Target firm:   unspecified
//  Generated at:  2026-04-28T12:42:21.745Z
// ─────────────────────────────────────────────────────────────

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

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

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

        [Parameter("Fast EMA", DefaultValue = 21, MinValue = 5, MaxValue = 50)]
        public int FastEmaPeriod { get; set; }

        [Parameter("Slow EMA", DefaultValue = 100, MinValue = 30, MaxValue = 400)]
        public int SlowEmaPeriod { get; set; }

        [Parameter("Pullback ATR Threshold", DefaultValue = 0.5, MinValue = 0.1, MaxValue = 2.0, Step = 0.1)]
        public double PullbackAtrThreshold { get; set; }

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

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

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

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

        [Parameter("Close before rollover", DefaultValue = true)]
        public bool CloseBeforeRollover { get; set; }

        [Parameter("Rollover Close Hour UTC", DefaultValue = 21, MinValue = 0, MaxValue = 23)]
        public int RolloverCloseHour { get; set; }

        private ExponentialMovingAverage _fast;
        private ExponentialMovingAverage _slow;
        private AverageTrueRange _atr;
        private const string Label = "TradeTrove_XAU_LondonPullback";

        protected override void OnStart()
        {
            _fast = Indicators.ExponentialMovingAverage(Bars.ClosePrices, FastEmaPeriod);
            _slow = Indicators.ExponentialMovingAverage(Bars.ClosePrices, SlowEmaPeriod);
            _atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Exponential);
        }

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

            // Flatten before rollover if enabled (lots of firms hate rollover holds)
            if (CloseBeforeRollover && now.Hour >= RolloverCloseHour)
            {
                CloseAll();
                return;
            }

            // Only trade London morning window
            if (now.Hour < SessionStartUtc || now.Hour >= SessionEndUtc) return;
            if (HasOpenPosition()) return;

            var price = Bars.ClosePrices.LastValue;
            var fast = _fast.Result.LastValue;
            var slow = _slow.Result.LastValue;
            var atr = _atr.Result.LastValue;

            // Uptrend: fast > slow. Pullback: price below fast by < threshold*ATR,
            // then crosses back above fast on this bar.
            var uptrend = fast > slow;
            var downtrend = fast < slow;
            var prevClose = Bars.ClosePrices.Last(1);
            var distToFast = Math.Abs(price - fast);

            if (uptrend
                && prevClose < fast // previous bar was below the fast EMA (the pullback)
                && price > fast     // current bar reclaimed it
                && distToFast < PullbackAtrThreshold * atr)
            {
                EnterPosition(TradeType.Buy, atr);
                return;
            }

            if (downtrend
                && prevClose > fast
                && price < fast
                && distToFast < PullbackAtrThreshold * atr)
            {
                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 CloseAll()
        {
            foreach (var p in Positions)
            {
                if (p.Label == Label && p.SymbolName == SymbolName) ClosePosition(p);
            }
        }

        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;

            ExecuteMarketOrder(direction, SymbolName, volume, Label, 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);
        }
    }
}
