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