Overall Statistics
Total Trades
78
Average Win
0.26%
Average Loss
-0.25%
Compounding Annual Return
-0.230%
Drawdown
3.800%
Expectancy
-0.120
Net Profit
-1.129%
Sharpe Ratio
-0.125
Probabilistic Sharpe Ratio
0.120%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
1.05
Alpha
-0.006
Beta
0.047
Annual Standard Deviation
0.012
Annual Variance
0
Information Ratio
-0.538
Tracking Error
0.171
Treynor Ratio
-0.032
Total Fees
$78.00
Estimated Strategy Capacity
$19000000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
using Accord;
using Accord.Math;
using QLNet;
using QuantConnect.Algorithm.CSharp.Benchmarks;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Transactions;

namespace QuantConnect.Algorithm.CSharp
{
    public class Verifyrsi : QCAlgorithm
    {
        private Symbol _symbol;
        private RelativeStrengthIndex _rsi;
        private decimal OverBoughtLevel = 70m;
        private decimal OverSoldLevel = 30m;

        private RollingWindow<decimal> _rsiLastValue;

        private const decimal Cash = 100000m;
        private const decimal PercentageOfAssetsPerOrder = 0.05m;

        private ConcurrentDictionary<int, OrderWithStopLoss> _orders;


        private Chart _rsiChart;

        public override void Initialize()
        {
            SetStartDate(2018, 1, 1); // Set Start Date
            SetEndDate(2022, 12, 2); // Set End Date
            SetCash(Cash); // Set Strategy Cash

            _symbol = AddEquity("SPY", Resolution.Daily, dataNormalizationMode: DataNormalizationMode.Raw).Symbol;
            EnableAutomaticIndicatorWarmUp = true;

            _rsi = RSI(_symbol, 14, MovingAverageType.Wilders, Resolution.Daily);
            _rsiLastValue = new RollingWindow<decimal>(2);
            _orders = new ConcurrentDictionary<int, OrderWithStopLoss>();

            SetBenchmark("SPY");
            SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);

            // plotting
            _rsiChart = new Chart("RSI");
            _rsiChart.AddSeries(new Series("RSI", SeriesType.Line, "", Color.Green));
            AddChart(_rsiChart);
        }

        /// 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)
        {
            if (!data.Bars.ContainsKey(_symbol))
                return;

            var rsiCurrentValue = _rsi.Current.Value;
            _rsiLastValue.Add(rsiCurrentValue);

            if (!_rsi.IsReady)
                return;

            if (!_rsiLastValue.IsReady)
                return;

            if (IsSell())
            {
                Trade(false);
            }

            if (IsBuy())
            {
                Trade(true);
            }
        }

        public void Trade(bool isBuy)
        {
            var stocksCount = PortfolioTarget.Percent(this, _symbol, PercentageOfAssetsPerOrder).Quantity;

            if (!isBuy)
            {
                stocksCount = -stocksCount;
            }

            var ticket = MarketOrder(_symbol, stocksCount);

            _orders.TryAdd(ticket.OrderId, new OrderWithStopLoss(ticket));
        }

        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            if (orderEvent.Status == OrderStatus.Canceled)
            {
                Debug($"Order with id {orderEvent.Id} was cancelled and orderId {orderEvent.OrderId}");

                if (_orders.TryRemove(orderEvent.OrderId, out var cancelledOrder))
                {
                    Debug($"Order wan't executed");

                    cancelledOrder.StopLossOrder.Cancel();
                }

                var cancelledStopLossOrder = _orders.FirstOrDefault(c => c.Value.StopLossOrder.OrderId == orderEvent.OrderId);

                if (_orders.TryRemove(cancelledStopLossOrder.Key, out var stopLossCancelledOrder))
                {
                    Debug("Stop loss order was cancelled");

                    stopLossCancelledOrder.StopLossOrder.Cancel();
                }
            }

            if (orderEvent.Status == OrderStatus.Filled)
            {
                if (_orders.TryGetValue(orderEvent.OrderId, out var filledOrder))
                {
                    var ticket = filledOrder.CurrentOrder;
                    var fillPrice = ticket.AverageFillPrice;
                    var direction = ticket.Quantity > 0
                        ? Position.Type.Long
                        : Position.Type.Short;

                    decimal stopPrice = direction == Position.Type.Long
                        ? ticket.AverageFillPrice - ticket.AverageFillPrice * 0.02m
                        : ticket.AverageFillPrice + ticket.AverageFillPrice * 0.02m;

                    var stocksCount = -ticket.Quantity;

                    var stopLossTicket = StopMarketOrder(_symbol, stocksCount, stopPrice);
                    filledOrder.AddStopLossTicket(stopLossTicket);

                    Debug($"{direction}. Fill price {fillPrice}. StopLoss price {stopPrice}");
                }

                var executedStopLossOrder = _orders.FirstOrDefault(c => c.Value.StopLossOrder?.OrderId == orderEvent.OrderId);
                if (!executedStopLossOrder.Equals(default(KeyValuePair<int, OrderWithStopLoss>)) && _orders.TryRemove(executedStopLossOrder.Key, out var stopLossExecutedOrder))
                {
                    Debug($"Initial order {stopLossExecutedOrder.CurrentOrder.OrderId} with filled price {stopLossExecutedOrder.CurrentOrder.AverageFillPrice} was closed by {stopLossExecutedOrder.StopLossOrder.AverageFillPrice} with orderId {stopLossExecutedOrder.StopLossOrder.OrderId}");
                }
            }

            base.OnOrderEvent(orderEvent);
        }

        public override void OnEndOfDay()
        {
            if (!_rsi.IsReady)
                return;

            Plot("RSI", "RSI", _rsi.Current.Value);
        }

        private bool IsBuy()
        {
            var rsiCurrentValue = _rsi.Current.Value;
            var rsiLastValue = _rsiLastValue[1];

            var isBuy = rsiLastValue <= OverSoldLevel
                && rsiCurrentValue > OverSoldLevel;

            if (isBuy)
            {
                Debug($"RSI last: {rsiLastValue} RSI current: {rsiCurrentValue}");
                return true;
            }

            return false;
        }

        private bool IsSell()
        {
            var rsiCurrentValue = _rsi.Current.Value;
            var rsiLastValue = _rsiLastValue[1];

            var isSell = rsiLastValue >= OverBoughtLevel
                && rsiCurrentValue < OverBoughtLevel;

            if (isSell)
            {
                Debug($"RSI last: {rsiLastValue} RSI current: {rsiCurrentValue}");
                return true;
            }

            return false;
        }
    }

    class OrderWithStopLoss
    {
        public OrderTicket CurrentOrder { get; private set; }
        public OrderTicket StopLossOrder { get; private set; }

        public OrderWithStopLoss(OrderTicket currentOrder)
        {
            CurrentOrder = currentOrder;
        }

        public void AddStopLossTicket(OrderTicket stopLossOrder)
        {
            StopLossOrder = stopLossOrder;
        }
    }
}