Overall Statistics
Total Trades
128
Average Win
7.53%
Average Loss
-0.51%
Compounding Annual Return
1.704%
Drawdown
26.300%
Expectancy
1.647
Net Profit
14.330%
Sharpe Ratio
0.185
Probabilistic Sharpe Ratio
0.311%
Loss Rate
83%
Win Rate
17%
Profit-Loss Ratio
14.88
Alpha
-0.013
Beta
0.325
Annual Standard Deviation
0.082
Annual Variance
0.007
Information Ratio
-0.59
Tracking Error
0.123
Treynor Ratio
0.047
Total Fees
$128.59
Estimated Strategy Capacity
$4900000000.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;
using VerifyRsi;

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

        private RollingWindow<decimal> _rsiLastValue;

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

        // market-order with stop-loss
        private ConcurrentDictionary<OrderTicket, OrderTicket> _orders;

        private Chart _rsiChart;

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

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

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

            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;

            if (!_rsi.IsReady)
                return;

            if (!_rsiLastValue.IsReady)
                return;

            if (IsSell())
            {
                if (Portfolio[_symbol].IsLong)
                {
                    Debug($"It's long. Liquidating. Profit: {Portfolio[_symbol].Profit}");
                    Liquidate(_symbol);
                }

                Trade(false);
            }

            if (IsBuy())
            {
                if (Portfolio[_symbol].IsShort)
                {
                    Debug($"It's short. Liquidating. Profit: {Portfolio[_symbol].Profit}");
                    Liquidate(_symbol);
                }

                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, null);
        }

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

                // the main order was cancelled
                var cancelledOrder = _orders.FirstOrDefault(c => c.Key.OrderId == orderEvent.OrderId);
                if (cancelledOrder.Value != null) // if there is stop-loss
                {
                    Debug($"Order wan't executed");

                    cancelledOrder.Value.Cancel();
                    _orders.TryRemove(cancelledOrder);
                }

                // the stop-loss order was cancelled
                var cancelledStopLossOrder = _orders.FirstOrDefault(c => c.Value?.OrderId == orderEvent.OrderId);
                if (_orders.TryRemove(cancelledStopLossOrder.Key, out var stopLossCancelledOrder))
                {
                    Debug("Stop loss order was cancelled");

                    stopLossCancelledOrder.Cancel();
                }
            }

            if (orderEvent.Status == OrderStatus.Filled)
            {
                var filledOrder = _orders.FirstOrDefault(c => c.Key.OrderId == orderEvent.OrderId);

                if (!filledOrder.Equals(default(KeyValuePair<OrderTicket, OrderTicket>)))
                {
                    var ticket = filledOrder.Key;
                    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);
                    _orders[filledOrder.Key] = stopLossTicket;

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

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

            base.OnOrderEvent(orderEvent);
        }

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

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

            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;
        }
    }
}
using QuantConnect.Orders;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace VerifyRsi
{
    public sealed class OrderTicketEqualityComparer : IEqualityComparer<OrderTicket>
    {
        public bool Equals(OrderTicket x, OrderTicket y)
        {
            return x.OrderId == y.OrderId;
        }

        public int GetHashCode([DisallowNull] OrderTicket obj)
        {
            return obj.OrderId;
        }
    }
}