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; } } }