Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.574
Tracking Error
0.103
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
namespace QuantConnect.Algorithm.CSharp
{
    public class VentralQuantumRadiator : QCAlgorithm
    {
        private HeikinAshi hac;
        private URSI ursi;
        private SRSI srsi;
        private string ticker = "ETHUSD";

        private RelativeStrengthIndex rsi;
        private RelativeStrengthIndex rsi_2;
        private MovingAverageConvergenceDivergence _macd;
        private MovingAverageConvergenceDivergence _macdex;
        private ExponentialMovingAverage _ema200;
        private ExponentialMovingAverage _ema200ex;
        private TradeBarConsolidator consolidator;
        bool invested = false;

        // in minutes
        int consolidation_period = 5;


        // candle counter
        private int max_period = 2;
        private int candle_counter = 0;
        private bool enable_candle_counter = false;
        private OrderTicket order = null;

        public override void Initialize()
        {
            SetStartDate(2021, 4, 15);  //Set Start Date
            SetCash(100000);             //Set Strategy Cash

            var _security = AddCrypto(ticker, Resolution.Minute, Market.Bitfinex);
            _security.SetFeeModel(new CustomFeeModel(this));
            //AddEquity(ticker, Resolution.Minute);
            consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(consolidation_period));
            consolidator.DataConsolidated += OnDataConsolidated;

            // SetBrokerageModel(BrokerageName.Bitfinex);

            hac = new HeikinAshi();
            _macd = MACD(ticker, 12, 26, 9);
            _ema200 = EMA(ticker, 200);
            _ema200ex = _ema200.Of(hac);
            _macdex = _macd.Of(hac);
            RegisterIndicator(ticker, hac, consolidator);
            RegisterIndicator(ticker, _macdex, consolidator);
            RegisterIndicator(ticker, _ema200ex, consolidator);

            ursi = new URSI(this, 14, 21, 20, 3);
            srsi = new SRSI(this, 14, 14, 14, 3, 3);
            SetWarmup(200);
        }

        public override void OnWarmupFinished()
        {
            // load via historical data
            // value is 21 since largest value in URSI/SRSI is 21
            var history = History(ticker, consolidation_period * 21, Resolution.Minute);
            foreach (var bar in history)
                consolidator.Update(bar);
        }

        public bool entry()
        {
            bool c1 = ursi.rsi1.getRSI() > ursi.rsi2.getRSI();
            bool c2 = ursi.rsi1.getRSI() > ursi.bb_middle[0];
            bool c3 = ursi.bb_middle[0] > ursi.bb_middle[2];
            bool c4 = srsi.getK() > srsi.getD();
            bool c5 = hac.Close > _macdex.Current.Value;
            bool c6 = _ema200ex.Current.Value < hac.Close;
            // do not trade on March 9th, 2018 for innaccurate data
            DateTime dt = DateTime.Now;
            //bool c5 = ((UtcTime.Month == 3) || (UtcTime.Month == 4)) && (UtcTime.Year == 2018);
            //Debug("Zulu");
            return c1 && c2 && c3 && c4;
        }

        public bool exit()
        {
            bool c1 = ursi.bb_middle[0] < ursi.bb_middle[2];
            bool c2 = srsi.getD() > 90;
            bool c3 = ursi.rsi1.getRSI() < ursi.rsi2.getRSI();
            bool c4 = Securities[ticker].Holdings.UnrealizedProfitPercent > 0.03m;
            bool c5 = Securities[ticker].Holdings.UnrealizedProfitPercent < -0.03m;
            bool c6 = _macdex.Signal.Current.Value > 0;
            return (c3);
        }

        public void OnDataConsolidated(object sender, TradeBar consolidated)
        {
            //Debug(hac.Close);

            ursi.update(hac.Close);
            srsi.update(hac.Close);

            if (entry() && !invested && !Portfolio.Invested)
            {
                int quantity = (int)((Portfolio.Cash * 0.9m) / hac.Open);
                // market order
                Order(ticker, quantity);
                //Debug("Open: " + consolidated.Close + " : " + consolidated.Open + " : " + consolidated.High + " : " + consolidated.Low + " : " + consolidated.Price + " : " + UtcTime);
                // take profit
                // order = LimitOrder(ticker, quantity, Math.Round(hac.Open, 4));
                candle_counter = 0;
                enable_candle_counter = true;
                Log("Position Entered: " + ticker + " at " + UtcTime);
                invested = true;
            }
            else if (exit() && Portfolio[ticker].Invested && order != null)
            // if you want to run market order remove && order !=null
            {
                //Debug("Close: " + consolidated.Close + " : " + consolidated.Open + " : " + consolidated.High + " : " + consolidated.Low + " : " + consolidated.Price + " : " + UtcTime);
                Log("Position Closed: " + ticker + " at " + UtcTime);
                Liquidate(ticker);
                // LimitOrder(ticker, -1 * Securities[ticker].Holdings.Quantity, Math.Round(hac.Open, 4));
                candle_counter = 0;
                enable_candle_counter = true;
                order = null;
                invested = false;
            }

            // candle counter for un-filled candle
            if (enable_candle_counter && order != null && invested)
            {
                // check to see if filled
                bool filled = order.Status == OrderStatus.Filled;
                if (filled)
                {
                    Log("Position Filled: " + ticker + " at " + UtcTime);
                    enable_candle_counter = false;
                    candle_counter = 0;
                }
                else
                {
                    // check to see if hit max limit
                    if (max_period <= candle_counter)
                    {
                        Log("Warning: Order Not Filled at " + UtcTime + " for " + ticker + " (Liquidated)");
                        Liquidate(ticker);
                        order = null;
                        enable_candle_counter = false;
                        candle_counter = 0;
                        invested = false;
                    }
                    else
                    {
                        // increment counter if not hit max
                        candle_counter++;
                    }
                }
            }
        }

        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock data
        public override void OnData(Slice data) { }
    }


    class URSI
    {
        public CorrectRSI rsi1;
        public CorrectRSI rsi2;
        public SimpleMovingAverage bb_sma;
        public RollingWindow<decimal> bb_middle;

        public URSI(QCAlgorithm qc, int rsi1_l, int rsi2_l, int bb_period, int bb_m_period)
        {
            this.rsi1 = new CorrectRSI(rsi1_l);
            this.rsi2 = new CorrectRSI(rsi2_l);
            this.bb_sma = new SimpleMovingAverage(bb_period);
            this.bb_middle = new RollingWindow<decimal>(bb_m_period);
            for (int i = 0; i < bb_m_period; i++)
                bb_middle.Add(0.0m);
        }

        public void update(decimal value)
        {
            rsi1.update(value);
            rsi2.update(value);
            bb_sma.update(rsi1.getRSI());
            bb_middle.Add(bb_sma.getSMA());
        }
    }

    class SRSI
    {
        public RollingWindow<decimal> min;
        public RollingWindow<decimal> max;
        public CorrectRSI rsi;
        public SimpleMovingAverage k;
        public SimpleMovingAverage d;

        public SRSI(QCAlgorithm qc, int rsi_l, int min_l, int max_l, int k, int d)
        {
            min = new RollingWindow<decimal>(min_l);
            max = new RollingWindow<decimal>(max_l);

            this.k = new SimpleMovingAverage(k);
            this.d = new SimpleMovingAverage(d);

            rsi = new CorrectRSI(rsi_l);

            for (int i = 0; i < min_l; i++)
            {
                min.Add(0.0m);
                max.Add(0.0m);
            }
        }

        public void update(decimal value)
        {
            rsi.update(value);
            min.Add(rsi.getRSI());
            max.Add(rsi.getRSI());
            k.update(getValue());
            d.update(getK());
        }

        public decimal getMin()
        {
            decimal min_value = Decimal.MaxValue;
            foreach (decimal i in min)
            {
                if (i < min_value)
                    min_value = i;
            }
            return min_value;
        }

        public decimal getMax()
        {
            decimal max_value = 0.0m;
            foreach (decimal i in max)
            {
                if (i > max_value)
                    max_value = i;
            }
            return max_value;
        }

        public decimal getValue()
        {
            if (getMax() - getMin() == 0)
                return 0.0m;
            return ((rsi.getRSI() - getMin()) / (getMax() - getMin())) * 100;
        }

        public decimal getK()
        {
            return k.getSMA();
        }

        public decimal getD()
        {
            return d.getSMA();
        }
    }

    class CorrectRSI
    {
        public decimal rsi = 0.0m;
        private RollingWindow<decimal> closes;
        private decimal gains;
        private decimal losses;
        private decimal period = 0.0m;

        public CorrectRSI(int period)
        {
            this.period = period;
            closes = new RollingWindow<decimal>(period);
            gains = 0.0m;
            losses = 0.0m;
        }

        public void update(decimal value)
        {
            closes.Add(value);

            if (!closes.IsReady)
                return;

            decimal diff = closes[0] - closes[1];
            if (diff > 0)
            {
                gains = ((gains * (period - 1)) + diff) / period;
                losses = ((losses * (period - 1)) + 0.0m) / period;
            }
            else
            {
                gains = ((gains * (period - 1)) + 0.0m) / period;
                losses = ((losses * (period - 1)) + Math.Abs(diff)) / period;
            }

            if (losses == 0 || gains == 0)
                return;

            decimal rs = gains / losses;

            if (rs + 1 == 0)
                return;

            rsi = 100.0m - (100.0m / (1.0m + rs));
        }

        public decimal getRSI()
        {
            return rsi;
        }
    }


    class MovingAverage
    {
        // variables:

        // QCAlgorithm variable
        QCAlgorithm algorithm { get; set; }
        // period of MovingAverage
        private int period { get; set; }
        // value of moving average
        private decimal ema { get; set; }
        // smoothing variable
        private decimal smoothing { get; set; }

        // constructors:

        // main constructor
        public MovingAverage(QCAlgorithm qc, int period, decimal init, decimal smoothing)
        {
            this.algorithm = qc;
            this.period = period;
            this.ema = init;
            this.smoothing = smoothing;
        }


        // methods:

        // update method
        public void update(decimal data)
        {
            decimal k = smoothing / (1 + period);
            ema = (data * k) + (ema * (1.0m - k));
        }

        // ema get method
        public decimal getEMA()
        {
            return this.ema;
        }
    }

    class SimpleMovingAverage
    {
        private RollingWindow<decimal> values;
        private int period;

        public SimpleMovingAverage(int period)
        {
            this.values = new RollingWindow<decimal>(period);
            this.period = period;

            // init default values
            for (int i = 0; i < period; i++)
                values.Add(0.0m);
        }

        public void update(decimal value)
        {
            values.Add(value);
        }

        public decimal getSMA()
        {
            decimal sma = 0.0m;
            foreach (decimal d in values)
                sma += d;
            return sma / period;
        }
    }
    public class CustomFeeModel : FeeModel
    {
        private readonly QCAlgorithm _algorithm;
        public CustomFeeModel(QCAlgorithm algorithm)
        {
            _algorithm = algorithm;
        }
        public override OrderFee GetOrderFee(OrderFeeParameters parameters)
        {
            // custom fee math
            var fee = Math.Max(
                1m,
                parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.0001m);
            //_algorithm.Log($"CustomFeeModel: {fee}");
            return new OrderFee(new CashAmount(fee, "USD"));
        }
    }
}