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