| Overall Statistics |
|
Total Trades 139 Average Win 1.69% Average Loss -1.85% Compounding Annual Return 6.300% Drawdown 53.900% Expectancy 0.276 Net Profit 84.318% Sharpe Ratio 0.415 Loss Rate 33% Win Rate 67% Profit-Loss Ratio 0.91 Alpha 0.081 Beta -0.069 Annual Standard Deviation 0.191 Annual Variance 0.037 Information Ratio 0.216 Tracking Error 0.301 Treynor Ratio -1.158 Total Fees $249.58 |
using System;
using QuantConnect.Orders;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
namespace QuantConnect.RsiMovingAverage
{
public partial class RsiMovingAverageAlgorithm
{
bool IsExitSignal(decimal currentPrice)
{
if (!rsi.IsReady || currentOrder == null || !currentOrder.Status.IsFill())
{
return false;
}
var elapsedTime = Time - purchaseDate;
var isPastHoldPeriod = elapsedTime >= holdPeriod;
var marketInversion = isShortPosition ? IsLongSignal() : IsShortSignal();
return marketInversion;// || isPastHoldPeriod;
}
bool IsLongSignal()
{
return IsUptrend() && IsOverSold();
}
bool IsShortSignal()
{
return IsDowntrend() && IsOverBought();
}
bool IsDowntrend()
{
if (!shortMovingAverage.IsReady || !longMovingAverage.IsReady)
{
return false;
}
return shortMovingAverage < longMovingAverage;
}
bool IsOverBought()
{
if (!rsi.IsReady)
{
return false;
}
return rsi > 92.5m;
}
bool IsUptrend()
{
if (!shortMovingAverage.IsReady || !longMovingAverage.IsReady)
{
return false;
}
return shortMovingAverage > longMovingAverage;
}
bool IsOverSold()
{
if (!rsi.IsReady)
{
return false;
}
return rsi < 7.5m;
}
}
}using System;
using QuantConnect.Orders;
namespace QuantConnect.RsiMovingAverage
{
public partial class RsiMovingAverageAlgorithm
{
int GetPositionSize(decimal currentPrice)
{
// Fixed Fractional Position Sizing
// N = M * R / | p_0 - p_1 | = M * R / S_i
var S_i = 0.02m * currentPrice;
var N = Portfolio.Cash * 0.02m / S_i;
return (int)Math.Floor(N);
}
void EnterLong(decimal currentPrice)
{
int quantity = GetPositionSize(currentPrice);
currentOrder = Buy(symbol, quantity);
profitTarget = LimitOrder(symbol, -quantity, currentPrice * 1.02m);
stopLoss = StopMarketOrder(symbol, -quantity, currentPrice * 0.98m);
highPrice = lowPrice = currentPrice;
isShortPosition = false;
purchaseDate = Time;
}
void EnterShort(decimal currentPrice)
{
int quantity = GetPositionSize(currentPrice);
currentOrder = Sell(symbol, quantity);
profitTarget = LimitOrder(symbol, quantity, currentPrice * 0.98m);
stopLoss = StopMarketOrder(symbol, quantity, currentPrice * 1.02m);
highPrice = lowPrice = currentPrice;
isShortPosition = true;
purchaseDate = Time;
}
void ExitMarket()
{
var quantity = Portfolio[symbol].Quantity;
if (profitTarget != null)
{
profitTarget.Cancel();
}
if (stopLoss != null)
{
stopLoss.Cancel();
}
if (isShortPosition)
{
currentOrder = Buy(symbol, quantity);
}
else
{
currentOrder = Sell(symbol, quantity);
}
}
void UpdateStopLoss(decimal currentPrice)
{
var newHigh = currentPrice > highPrice;
var newLow = currentPrice < lowPrice;
if (newHigh)
{
highPrice = currentPrice;
}
if (newLow)
{
lowPrice = currentPrice;
}
if (isShortPosition && newLow)
{
//stopLoss.Update(new UpdateOrderFields{ StopPrice = currentPrice * 1.02m });
}
if (!isShortPosition && newHigh)
{
//stopLoss.Update(new UpdateOrderFields{ StopPrice = currentPrice * 0.98m });
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (!orderEvent.Status.IsClosed())
{
return;
}
var filledOrderId = orderEvent.OrderId;
if (profitTarget != null && stopLoss != null)
{
if (profitTarget.OrderId == filledOrderId)// && stopLoss.Status.IsOpen())
{
Log(Time+": Cancelling stop loss");
stopLoss.Cancel();
}
if (stopLoss.OrderId == filledOrderId)// && profitTarget.Status.IsOpen())
{
Log(Time+": Cancelling profit target");
profitTarget.Cancel();
}
}
base.OnOrderEvent(orderEvent);
}
}
}using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using System;
using QuantConnect.Orders;
using QuantConnect.Data.Market;
namespace QuantConnect.RsiMovingAverage
{
public partial class RsiMovingAverageAlgorithm : QCAlgorithm
{
string symbol = "SPY";
// string symbol = "C";
// string symbol = "SBUX";
// string symbol = "MSFT";
SimpleMovingAverage shortMovingAverage;
SimpleMovingAverage longMovingAverage;
RelativeStrengthIndex rsi;
DateTime purchaseDate;
TimeSpan holdPeriod;
OrderTicket currentOrder;
OrderTicket profitTarget;
OrderTicket stopLoss;
decimal highPrice = decimal.MinValue;
decimal lowPrice = decimal.MaxValue;
bool isShortPosition;
public override void Initialize()
{
SetStartDate(2000, 1, 1);
SetEndDate(2010, 1, 1);
SetCash(25000);
AddSecurity(SecurityType.Equity, symbol, Resolution.Daily);
shortMovingAverage = SMA(symbol, 50, Resolution.Daily);
longMovingAverage = SMA(symbol, 200, Resolution.Daily);
rsi = RSI(symbol, 2, MovingAverageType.Simple, Resolution.Daily);
holdPeriod = TimeSpan.FromDays(5);
}
public void OnData(TradeBars data)
{
var currentOrderIsFilled = true;
if (currentOrder != null)
{
currentOrderIsFilled = currentOrder.Status.IsFill();
}
var inMarket = Portfolio.Securities[symbol].HoldStock;
if (stopLoss != null)
{
inMarket = inMarket && stopLoss.Status.IsOpen();
}
if (profitTarget != null)
{
inMarket = inMarket && profitTarget.Status.IsOpen();
}
var currentPrice = data[symbol].Price;
if (currentOrderIsFilled && !inMarket)
{
TryEnterMarket(currentPrice);
}
else if (currentOrderIsFilled && inMarket)
{
TryExitMarket(currentPrice);
}
}
private void TryEnterMarket(decimal currentPrice)
{
if (IsLongSignal())
{
EnterLong(currentPrice);
}
else if (IsShortSignal())
{
EnterShort(currentPrice);
}
}
private void TryExitMarket(decimal currentPrice)
{
if (IsExitSignal(currentPrice))
{
ExitMarket();
highPrice = decimal.MinValue;
lowPrice = decimal.MaxValue;
}
else
{
UpdateStopLoss(currentPrice);
}
}
}
}