[Strategy] Opening Range Breakout

Back

Hey all!

Here's an algorithm I threw together to try and showcase various features and techniques available within QuantConnect. This algorithm tries to detect an opening breakout in the direction of the short term trend and takes a position in that direction. The opening breakout is nominally defined as trading outside of the range established in the first 3 minutes of trading. Orders are always submitted with accompanying stop market orders which are gradually tightened using PSAR after some initial profit condition is met.

I've tried to keep it fairly simple but still provide a good framework. As an algorithm grows in complexity, it's typically beneficial to split the pieces out into stand-alone components that can be reused between algorithms, but for the sake of simplicity and understandability, I've left all the code in one file.

In this algorithm you'll see various features and techniques including:
* Scheduled Daily Events
* History function to warm up indicators manually
* StopMarket order updates
* Dynamic position sizing using ATR/allowable losses
* Custom indicator on custom (30 min) interval
* Custom leverage settings
* Decent logging functionality
* Plotting considerations made for backtest and live

There's still plenty of work to be done here before it is made into a good, reliably profitable algorithm. Some things that could be worked on is:

* Setting the stop loss % based on the position size instead of a constant, this way we decide to lose a max% of our portfolio per trade.

* Better entry execution could be done by not entering the position once the signal is generated, but to wait for a reversion to VWAP or for a price point that is ~2 standard deviations from the mean in the beneficial direction.

* Better exit execution, currently it exits based on the stop loss criteria and the encroaching PSAR.

* There's also work that could be done to allow the algorithm to enter a position in the opposite direction of the trend, maybe given a larger breakout threshold from the opening range.

These are just a few areas of the algorithm that could be improved. If you make improvements, please share the algorithm back to this thread and we as a community can learn together!

EDIT: Looks like a previous edit removed the attached algorithm, here it is, enjoy!
Update Backtest






The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.



 
0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Thank you for putting together these examples!
Nik
0

Hey @Michael great work! I'm learning a lot from this example.

I have a rather silly question, why do you use PSARMin.Update((TradeBar)Security.GetLastData()); in line 236 instead of register the indicator?

JJ
0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Hey @JJ, not silly at all! There's no solid reason for it being like that, it's purely the result of me iterating on the design. At one point I was only computing it during certain conditions, such as open position, ect... but ended up moving it there so I could see plots to better understand how a second level PSAR behaves.
0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Thanks Michael!
0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Hi there,

I think there is a mistake in the definition of the STD14 and ATR14 indicators. In line 135 and 13 the long term indicators are defined with daily resolution:// define our longer term indicators
STD14 = STD(Symbol, 14, Resolution.Daily);
ATR14 = ATR(Symbol, 14, resolution: Resolution.Daily);

But when the indicators are smoothed, the EMA period is in hours:// smooth our ATR over a week, we'll use this to determine if recent volatilty warrants entrance
var oneWeekInMarketHours = (int)(5 * 6.5);
SmoothedATR14 = new ExponentialMovingAverage("Smoothed_" + ATR14.Name, oneWeekInMarketHours).Of(ATR14);
// smooth our STD over a week as well
SmoothedSTD14 = new ExponentialMovingAverage("Smoothed_" + STD14.Name, oneWeekInMarketHours).Of(STD14);

So I test the algorithm with the long term indicators in hours and the results become negatives!

Another question, in the algorithm two schedules are used:// schedule an event to run every day at five minutes after our Symbol's market open
Schedule.Event("MarketOpenSpan")
.EveryDay(Symbol)
.AfterMarketOpen(Symbol, minutesAfterOpen: OpeningSpanInMinutes)
.Run(MarketOpeningSpanHandler);

Schedule.Event("MarketOpen")
.EveryDay(Symbol)
.AfterMarketOpen(Symbol, minutesAfterOpen: -1)
.Run(() => PSARMin.Reset());

Why the PSARMin indicator is reseted just a minute before market opens? Is the same to reset the indicator in the OnEndOFDay method?

Thanks in advance,

JJ
0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Hey @JJ!

Spotted another bug, great catch! Looks like another hold over from me iterating on the design. The indicators are intended to be on the daily time frame and the smoothers are intended to have a half interval of the long term. So The STD14/ATR14 definitions are as intended, but the smoothers should be defined using a period of 7 (half of the 14 period). I did a quick change and didn't see differences.

As for the scheduled events, great question! There's actually a very large difference between the two. OnEndOfDay events will fire 10 minutes before the market closes. This gives the algorithm enough time to do some processing and submit some final orders. The issue is that with a second level indicator those last 10 minutes will completely dominate the indicator's state, so by resetting it just before market open I'm guaranteeing that the PSAR is fresh and only includes data since market open.

For me, scheduled events is my preferred way of performing an indicator reset since I guarantee what time of day it happens.
1

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


did you have a chance to correct the algo?
0

i tired to modify this algorithm by adding AddUniverse, and setting the secuiry inisde CoarseSelectionFunction function, but i keep running into error, that symbol is not set. my goal was to select the stock that's trending and use that to apply this algorithm. how can i correct this error?

 

During the algorithm initialization, the following exception has occurred: The ticker SPY was not found in the SymbolCache. Use the Symbol object as key instead. Accessing the securities collection/slice object by string ticker is only available for securities added with the AddSecurity-family methods. For more details, please check out the documentation

/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
///
/// QCU: Opening Breakout Algorithm
///
/// In this algorithm we attempt to provide a working algorithm that
/// addresses many of the primary algorithm concerns. These concerns
/// are:
///
/// 1. Signal Generation.
/// This algorithm aims to generate signals for an opening
/// breakout move before 10am. Signals are generated by
/// producing the opening five minute bar, and then trading
/// in the direction of the breakout from that bar.
///
/// 2. Position Sizing.
/// Positions are sized using recently the average true range.
/// The higher the recently movement, the smaller position.
/// This helps to reduce the risk of losing a lot on a single
/// transaction.
///
/// 3. Active Stop Loss.
/// Stop losses are maintained at a fixed global percentage to
/// limit maximum losses per day, while also a trailing stop
/// loss is implemented using the parabolic stop and reverse
/// in order to gauge exit points.
///
/// </summary>
/// <meta name="tag" content="strategy example" />
/// <meta name="tag" content="indicators" />
public class OpeningBreakoutAlgorithm : QCAlgorithm
{
// the equity symbol we're trading
private string symbol = "SPY";

// plotting and logging control
private const bool EnablePlotting = true;
private const bool EnableOrderUpdateLogging = false;
private const int PricePlotFrequencyInSeconds = 15;

// risk control
private const decimal MaximumLeverage = 4;
private const decimal GlobalStopLossPercent = 0.001m;
private const decimal PercentProfitStartPsarTrailingStop = 0.0003m;
private const decimal MaximumPorfolioRiskPercentPerPosition = .0025m;

// entrance criteria
private const int OpeningSpanInMinutes = 3;
private const decimal BreakoutThresholdPercent = 0.00005m;
private const decimal AtrVolatilityThresholdPercent = 0.00275m;
private const decimal StdVolatilityThresholdPercent = 0.005m;

// this is the security we're trading
public Security Security;

// define our indicators used for trading decisions
public AverageTrueRange ATR14;
public StandardDeviation STD14;
public AverageDirectionalIndex ADX14;
public ParabolicStopAndReverse PSARMin;

// smoothed values
public ExponentialMovingAverage SmoothedSTD14;
public ExponentialMovingAverage SmoothedATR14;

// working variable to control our algorithm

// this flag is used to run some code only once after the algorithm is warmed up
private bool FinishedWarmup;
// this is used to record the last time we closed a position
private DateTime LastExitTime;
// this is our opening n minute bar
private TradeBar OpeningBarRange;
// this is the ticket from our market order (entrance)
private OrderTicket MarketTicket;
// this is the ticket from our stop loss order (exit)
private OrderTicket StopLossTicket;
// this flag is used to indicate we've switched from a global, non changing
// stop loss to a dynamic trailing stop using the PSAR
private bool EnablePsarTrailingStop;

/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
// initialize algorithm level parameters
SetStartDate(2020, 9, 24);
SetEndDate(2020, 9, 25);
//SetStartDate(2014, 01, 01);
//SetEndDate(2014, 06, 01);
SetCash(100000);

// leverage tradier $1 traders
SetBrokerageModel(BrokerageName.TradierBrokerage);
UniverseSettings.Resolution = Resolution.Second;
// request high resolution equity data
AddUniverse(CoarseSelectionFunction);
//AddSecurity(SecurityType.Equity, symbol, Resolution.Second);

// save off our security so we can reference it quickly later
Security = Securities[symbol];

// Set our max leverage
Security.SetLeverage(MaximumLeverage);

// define our longer term indicators
ADX14 = ADX(symbol, 28, Resolution.Hour);
STD14 = STD(symbol, 14, Resolution.Daily);
ATR14 = ATR(symbol, 14, resolution: Resolution.Daily);
PSARMin = new ParabolicStopAndReverse(symbol, afStart: 0.0001m, afIncrement: 0.0001m);

// smooth our ATR over a week, we'll use this to determine if recent volatilty warrants entrance
var oneWeekInMarketHours = (int)(5*6.5);
SmoothedATR14 = new ExponentialMovingAverage("Smoothed_" + ATR14.Name, oneWeekInMarketHours).Of(ATR14);
// smooth our STD over a week as well
SmoothedSTD14 = new ExponentialMovingAverage("Smoothed_"+STD14.Name, oneWeekInMarketHours).Of(STD14);

// initialize our charts
var chart = new Chart(symbol);
chart.AddSeries(new Series(ADX14.Name, SeriesType.Line, 0));
chart.AddSeries(new Series("Enter", SeriesType.Scatter, 0));
chart.AddSeries(new Series("Exit", SeriesType.Scatter, 0));
chart.AddSeries(new Series(PSARMin.Name, SeriesType.Scatter, 0));
AddChart(chart);

var history = History(symbol, 20, Resolution.Daily);
foreach (var bar in history)
{
ADX14.Update(bar);
ATR14.Update(bar);
STD14.Update(bar.EndTime, bar.Close);
}

// schedule an event to run every day at five minutes after our symbol's market open
Schedule.Event("MarketOpenSpan")
.EveryDay(symbol)
.AfterMarketOpen(symbol, minutesAfterOpen: OpeningSpanInMinutes)
.Run(MarketOpeningSpanHandler);

Schedule.Event("MarketOpen")
.EveryDay(symbol)
.AfterMarketOpen(symbol, minutesAfterOpen: -1)
.Run(() => PSARMin.Reset());
}
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
// sort descending by daily dollar volume
var sortedByDollarVolume = coarse.OrderByDescending(x => x.DollarVolume);

// take the top entries from our sorted collection
var top = sortedByDollarVolume.Take(1);

// we need to return only the symbol objects
symbol = top.FirstOrDefault().Symbol.Value;
return top.Select(x => x.Symbol);
}
/// <summary>
/// This function is scheduled to be run every day at the specified number of minutes after market open
/// </summary>
public void MarketOpeningSpanHandler()
{
// request the last n minutes of data in minute bars, we're going to
// define the opening rang
var history = History(symbol, OpeningSpanInMinutes, Resolution.Minute);

// this is our bar size
var openingSpan = TimeSpan.FromMinutes(OpeningSpanInMinutes);

// we only care about the high and low here
OpeningBarRange = new TradeBar
{
// time values
Time = Time - openingSpan,
EndTime = Time,
Period = openingSpan,
// high and low
High = Security.Close,
Low = Security.Close
};

// aggregate the high/low for the opening range
foreach (var tradeBar in history)
{
OpeningBarRange.Low = Math.Min(OpeningBarRange.Low, tradeBar.Low);
OpeningBarRange.High = Math.Max(OpeningBarRange.High, tradeBar.High);
}

// widen the bar when looking for breakouts
OpeningBarRange.Low *= 1 - BreakoutThresholdPercent;
OpeningBarRange.High *= 1 + BreakoutThresholdPercent;

Log("---------" + Time.Date + "---------");
Log("OpeningBarRange: Low: " + OpeningBarRange.Low.SmartRounding() + " High: " + OpeningBarRange.High.SmartRounding());
}

/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice data)
{
// we don't need to run any of this during our warmup phase
if (IsWarmingUp) return;

// when we're done warming up, register our indicators to start plotting
if (!IsWarmingUp && !FinishedWarmup)
{
// this is a run once flag for when we're finished warmup
FinishedWarmup = true;

// plot our hourly indicators automatically, wait for them to ready
PlotIndicator("ADX", ADX14);
PlotIndicator("ADX", ADX14.NegativeDirectionalIndex, ADX14.PositiveDirectionalIndex);

PlotIndicator("ATR", true, ATR14);
PlotIndicator("STD", true, STD14);
PlotIndicator("ATR", true, SmoothedATR14);
}

// update our PSAR
PSARMin.Update((TradeBar) Security.GetLastData());

// plot price until an hour after we close so we can see our execution skillz
if (ShouldPlot)
{
// we can plot price more often if we want
Plot(symbol, "Price", Security.Close);
// only plot psar on the minute
if (PSARMin.IsReady)
{
Plot(symbol, PSARMin);
}
}

// first wait for our opening range bar to be set to today
if (OpeningBarRange == null || OpeningBarRange.EndTime.Date != Time.Date || OpeningBarRange.EndTime == Time) return;

// we only trade max once per day, so if we've already exited the stop loss, bail
if (StopLossTicket != null && StopLossTicket.Status == OrderStatus.Filled)
{
// null these out to signal that we're done trading for the day
OpeningBarRange = null;
StopLossTicket = null;
return;
}

// now that we have our opening bar, test to see if we're already in a position
if (!Security.Invested)
{
ScanForEntrance();
}
else
{
// if we haven't exited yet then manage our stop loss, this controls our exit point
if (Security.Invested)
{
ManageStopLoss();
}
else if (StopLossTicket != null && StopLossTicket.Status.IsOpen())
{
StopLossTicket.Cancel();
}
}
}

/// <summary>
/// Scans for a breakout from the opening range bar
/// </summary>
private void ScanForEntrance()
{
// scan for entrances, we only want to do this before 10am
if (Time.TimeOfDay.Hours >= 10) return;

// expect capture 10% of the daily range
var expectedCaptureRange = 0.1m*ATR14;

var allowedDollarLoss = MaximumPorfolioRiskPercentPerPosition * Portfolio.TotalPortfolioValue;

var shares = (int) (allowedDollarLoss/expectedCaptureRange);

// determine a position size based on an acceptable loss in proporton to our total portfolio value
//var shares = (int) (MaximumLeverage*MaximumPorfolioRiskPercentPerPosition*Portfolio.TotalPortfolioValue/(0.4m*ATR14));

// max out at a little below our stated max, prevents margin calls and such
var maxShare = (int) CalculateOrderQuantity(symbol, MaximumLeverage);
shares = Math.Min(shares, maxShare);

// min out at 1x leverage
//var minShare = CalculateOrderQuantity(symbol, MaximumLeverage/2m);
//shares = Math.Max(shares, minShare);

// we're looking for a breakout of the opening range bar in the direction of the medium term trend
if (ShouldEnterLong)
{
// breakout to the upside, go long (fills synchronously)
MarketTicket = MarketOrder(symbol, shares);
Log("Enter long @ " + MarketTicket.AverageFillPrice.SmartRounding() + " Shares: " + shares);
Plot(symbol, "Enter", MarketTicket.AverageFillPrice);

// we'll start with a global, non-trailing stop loss
EnablePsarTrailingStop = false;

// submit stop loss order for max loss on the trade
var stopPrice = Security.Low*(1 - GlobalStopLossPercent);
StopLossTicket = StopMarketOrder(symbol, -shares, stopPrice);
if (EnableOrderUpdateLogging)
{
Log("Submitted stop loss @ " + stopPrice.SmartRounding());
}
}
else if (ShouldEnterShort)
{
// breakout to the downside, go short
MarketTicket = MarketOrder(symbol, - -shares);
Log("Enter short @ " + MarketTicket.AverageFillPrice.SmartRounding());
Plot(symbol, "Enter", MarketTicket.AverageFillPrice);

// we'll start with a global, non-trailing stop loss
EnablePsarTrailingStop = false;

// submit stop loss order for max loss on the trade
var stopPrice = Security.High*(1 + GlobalStopLossPercent);
StopLossTicket = StopMarketOrder(symbol, -shares, stopPrice);
if (EnableOrderUpdateLogging)
{
Log("Submitted stop loss @ " + stopPrice.SmartRounding() + " Shares: " + shares);
}
}
}

/// <summary>
/// Manages our stop loss ticket
/// </summary>
private void ManageStopLoss()
{
// if we've already exited then no need to do more
if (StopLossTicket == null || StopLossTicket.Status == OrderStatus.Filled) return;

// only do this once per minute
//if (Time.RoundDown(TimeSpan.FromMinutes(1)) != Time) return;

// get the current stop price
var stopPrice = StopLossTicket.Get(OrderField.StopPrice);

// check for enabling the psar trailing stop
if (ShouldEnablePsarTrailingStop(stopPrice))
{
EnablePsarTrailingStop = true;
Log("Enabled PSAR trailing stop @ ProfitPercent: " + Security.Holdings.UnrealizedProfitPercent.SmartRounding());
}

// we've trigger the psar trailing stop, so start updating our stop loss tick
if (EnablePsarTrailingStop && PSARMin.IsReady)
{
StopLossTicket.Update(new UpdateOrderFields {StopPrice = PSARMin});
Log("Submitted stop loss @ " + PSARMin.Current.Value.SmartRounding());
}
}

/// <summary>
/// This event handler is fired for each and every order event the algorithm
/// receives. We'll perform some logging and house keeping here
/// </summary>
public override void OnOrderEvent(OrderEvent orderEvent)
{
// print debug messages for all order events
if (LiveMode || orderEvent.Status.IsFill() || EnableOrderUpdateLogging)
{
LiveDebug("Filled: " + orderEvent.FillQuantity + " Price: " + orderEvent.FillPrice);
}

// if this is a fill and we now don't own any stock, that means we've closed for the day
if (!Security.Invested && orderEvent.Status == OrderStatus.Filled)
{
// reset values for tomorrow
LastExitTime = Time;
var ticket = Transactions.GetOrderTickets(x => x.OrderId == orderEvent.OrderId).Single();
Plot(symbol, "Exit", ticket.AverageFillPrice);
}
}

/// <summary>
/// If we're still invested by the end of the day, liquidate
/// </summary>
public override void OnEndOfDay()
{
if (Security.Invested)
{
Liquidate();
}
}

/// <summary>
/// Determines whether or not we should plot. This is used
/// to provide enough plot points but not too many, we don't
/// need to plot every second in backtests to get an idea of
/// how good or bad our algorithm is performing
/// </summary>
public bool ShouldPlot
{
get
{
// always in live
if (LiveMode) return true;
// set in top to override plotting during long backtests
if (!EnablePlotting) return false;
// every 30 seconds in backtest
if (Time.RoundDown(TimeSpan.FromSeconds(PricePlotFrequencyInSeconds)) != Time) return false;
// always if we're invested
if (Security.Invested) return true;
// always if it's before noon
if (Time.TimeOfDay.Hours < 10.25) return true;
// for an hour after our exit
if (Time - LastExitTime < TimeSpan.FromMinutes(30)) return true;

return false;
}
}

/// <summary>
/// In live mode it's nice to push messages to the debug window
/// as well as the log, this allows easy real time inspection of
/// how the algorithm is performing
/// </summary>
public void LiveDebug(object msg)
{
if (msg == null) return;

if (LiveMode)
{
Debug(msg.ToString());
Log(msg.ToString());
}
else
{
Log(msg.ToString());
}
}

/// <summary>
/// Determines whether or not we should end a long position
/// </summary>
private bool ShouldEnterLong
{
// check to go in the same direction of longer term trend and opening break out
get
{
return IsUptrend
&& HasEnoughRecentVolatility
&& Security.Close > OpeningBarRange.High;
}
}

/// <summary>
/// Determines whether or not we're currently in a medium term up trend
/// </summary>
private bool IsUptrend
{
get { return ADX14 > 20 && ADX14.PositiveDirectionalIndex > ADX14.NegativeDirectionalIndex; }
}

/// <summary>
/// Determines whether or not we should enter a short position
/// </summary>
private bool ShouldEnterShort
{
// check to go in the same direction of longer term trend and opening break out
get
{
return IsDowntrend
&& HasEnoughRecentVolatility
&& Security.Close < OpeningBarRange.Low;
}
}

/// <summary>
/// Determines whether or not we're currently in a medium term down trend
/// </summary>
private bool IsDowntrend
{
get { return ADX14 > 20 && ADX14.NegativeDirectionalIndex > ADX14.PositiveDirectionalIndex; }
}

/// <summary>
/// Determines whether or not there's been enough recent volatility for
/// this strategy to work
/// </summary>
private bool HasEnoughRecentVolatility
{
get
{
return SmoothedATR14 > Security.Close*AtrVolatilityThresholdPercent
|| SmoothedSTD14 > Security.Close*StdVolatilityThresholdPercent;
}
}

/// <summary>
/// Determines whether or not we should enable the psar trailing stop
/// </summary>
/// <param name="stopPrice">current stop price of our stop loss tick</param>
private bool ShouldEnablePsarTrailingStop(decimal stopPrice)
{
// no need to enable if it's already enabled
return !EnablePsarTrailingStop
// once we're up a certain percentage, we'll use PSAR to control our stop
&& Security.Holdings.UnrealizedProfitPercent > PercentProfitStartPsarTrailingStop
// make sure the PSAR is on the right side
&& PsarIsOnRightSideOfPrice
// make sure the PSAR is more profitable than our global loss
&& IsPsarMoreProfitableThanStop(stopPrice);
}

/// <summary>
/// Determines whether or not the PSAR is on the right side of price depending on our long/short
/// </summary>
private bool PsarIsOnRightSideOfPrice
{
get
{
return (Security.Holdings.IsLong && PSARMin < Security.Close)
|| (Security.Holdings.IsShort && PSARMin > Security.Close);
}
}

/// <summary>
/// Determines whether or not the PSAR stop price is better than the specified stop price
/// </summary>
private bool IsPsarMoreProfitableThanStop(decimal stopPrice)
{
return (Security.Holdings.IsLong && PSARMin > stopPrice)
|| (Security.Holdings.IsShort && PSARMin < stopPrice);
}
}
}

 

0

Hi SherpaTrader,

The problem stems from Security = Securities[symbol]; as we are trying to access the Security object for a security we are not subscribed to. Furthermore, we need to move everything in Initialize that is below AddUniverse(CoarseSelectionFunction); into OnSecuritiesChanged, and use the full name indicators (e.g. SimpleMovingAverage instead of SMA) and manually update them.

Best,
Shile Wen

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Update Backtest





0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Loading...

This discussion is closed