/*Strategy suggested by Stealth1977 on Forex Factory 

long Entry - 1HR Time Frame

1. ADX = Between 25 - 30
2. +DM = Between 20 - 30
3. -DM = Between 1 - 15
4. SL = 70 PIPS
5. TP = ADX above 40

Short Entry - 1HR Time Frame

1. ADX = Between 25 - 30
2. -DM = Between 20 - 30
3. +DM = Between 1 - 15
4. SL = 70 PIPS
5. TP = ADX above 40

Note: Only 1 trade at a time per Currency pair*/

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class Stealth1977 : Robot
    {
        private DirectionalMovementSystem adx;

        [Parameter("Robot Name", DefaultValue = "Stealth1977")]
        public string robotName { get; set; }
        [Parameter("Risk Percentage Per Trade", DefaultValue = 0.01, MinValue = 0.01, Step = 0.01)]
        public double riskPercentage { get; set; }
        [Parameter("Max Positions", DefaultValue = 1)]
        public int maxPositions { get; set; }
        [Parameter("ADX Timeframe", DefaultValue = "Hour")]
        public TimeFrame adxTimeFrame { get; set; }
        [Parameter("ADX Take Profit", DefaultValue = 40)]
        public int adxTakeProfit { get; set; }
        [Parameter("ADX Range A", DefaultValue = 25)]
        public int adxRangeA { get; set; }
        [Parameter("ADX Range B", DefaultValue = 30)]
        public int adxRangeB { get; set; }
        [Parameter("DI High A", DefaultValue = 20)]
        public int DIHighA { get; set; }
        [Parameter("DI High B", DefaultValue = 30)]
        public int DIHighB { get; set; }
        [Parameter("DI Low A", DefaultValue = 1)]
        public int DILowA { get; set; }
        [Parameter("DI Low B", DefaultValue = 15)]
        public int DILowB { get; set; }
        [Parameter("OnBars Trade Management", DefaultValue = "true")]
        public bool OnBars { get; set; }
        [Parameter("Stop Loss", DefaultValue = 70, MinValue = 1, Step = 1)]
        public double stopLoss { get; set; }
        [Parameter("Break Even Stop Loss", DefaultValue = 50, MinValue = 0, Step = 1)]
        public double breakEvenTrigger { get; set; }

        protected override void OnStart()
        {
            var seriesADX = MarketData.GetSeries(adxTimeFrame);
            adx = Indicators.DirectionalMovementSystem(seriesADX, 14);
        }

        protected override void OnTick()
        {
            if (!OnBars)
            {
                manageOpenTrades();
                checkIfNewTrades();
            }
        }

        private void manageOpenTrades()
        {
            if (breakEvenTrigger > 0)
            {
                MoveToBreakEven();
            }
            var currentADX = Math.Round(adx.ADX.LastValue, 1);
            var currentDIP = adx.DIPlus.LastValue;
            var currentDIM = adx.DIMinus.LastValue;
            if (currentADX >= adxTakeProfit && currentDIP > currentDIM)
            {
                closeAllOrdersByType(robotName, TradeType.Buy);
            }
            else if (currentADX >= adxTakeProfit && currentDIM > currentDIP)
            {
                closeAllOrdersByType(robotName, TradeType.Sell);
            }
        }

        private void closeAllOrdersByType(string robotName, TradeType tradeType)
        {
            var activePositions = Positions.FindAll(robotName, Symbol, tradeType);
            foreach (var position in activePositions)
            {
                ClosePosition(position);
            }
        }

        protected override void OnBar()
        {
            if (OnBars)
            {
                manageOpenTrades();
                checkIfNewTrades();
            }
        }

        protected override void OnStop()
        {
            // Not used.
        }

        private void checkIfNewTrades()
        {
            var positionsActive = Positions.FindAll(robotName, Symbol);
            if (positionsActive.Length < maxPositions)
            {
                var direction = checkTradeDirection();
                switch (direction)
                {
                    case TradeDirection.None:
                        break;
                    case TradeDirection.Buy:
                        executeTrade(TradeType.Buy);
                        break;
                    case TradeDirection.Sell:
                        executeTrade(TradeType.Sell);
                        break;
                }
            }
        }

        private void executeTrade(TradeType tradeType)
        {
            var buyVolume = calculateVolume();
            ExecuteMarketOrder(tradeType, Symbol, buyVolume, robotName, stopLoss, null);
        }

        private TradeDirection checkTradeDirection()
        {
            var currentADX = Math.Round(adx.ADX.LastValue, 1);
            var currentDIP = adx.DIPlus.LastValue;
            var currentDIM = adx.DIMinus.LastValue;
            if (currentADX >= adxRangeA && currentADX <= adxRangeB && currentDIP >= DIHighA && currentDIP <= DIHighB && currentDIM >= DILowA && currentDIM <= DILowB)
            {
                return TradeDirection.Buy;
            }
            else if (currentADX >= adxRangeA && currentADX <= adxRangeB && currentDIM >= DIHighA && currentDIM <= DIHighB && currentDIP >= DILowA && currentDIP <= DILowB)
            {
                return TradeDirection.Sell;
            }
            return TradeDirection.None;
        }

        public enum TradeDirection
        {
            None,
            Buy,
            Sell
        }

        private double calculateVolume()
        {
            var riskPerTrade = (Account.Equity) * riskPercentage;
            double totalSLPipValue = (stopLoss + Symbol.Spread) * Symbol.PipValue;
            double calculatedVolume = riskPerTrade / totalSLPipValue;
            double normalizedCalculatedVolume = Symbol.NormalizeVolumeInUnits(calculatedVolume, RoundingMode.ToNearest);
            return normalizedCalculatedVolume;
        }

        private void MoveToBreakEven()
        {
            foreach (var position in Positions.FindAll(robotName, Symbol))
            {
                int factor = position.TradeType == TradeType.Buy ? 1 : -1;
                double newStopLoss = position.EntryPrice + (Symbol.PipSize * factor);
                if (position.StopLoss != position.EntryPrice && position.Pips >= breakEvenTrigger && newStopLoss != position.StopLoss)
                {
                    ModifyPosition(position, newStopLoss, position.TakeProfit);
                    Print("Break even stop loss set to {0} on position ID {1}", newStopLoss, position.Id);
                }
            }
        }
    }
}
