# XJO quant ORB strategy



## sinner (4 April 2013)

Hi guys,

I found this interesting blog which I didn't know existed until today. They share backtest results and ideas for a lot of strategies on the XJO. One I found particularly interesting (since we have discussed it here previously) was a page on an Open Range Breakout strategy. The twist which got my attention was that it was long only. I thought I would share it for discussion,

http://asxiq.com/blog/xjo-opening-range-breakout-trade-backtest-performance-summary/

The author shows the results as quite profitable. The calculation table provided looks a little confusing (why is the daily stretch value 0 sometimes?). Are there backtest result issues since it seems to be using XJO (untradeable) data? Wondering if anyone has done any similar tests on SPI that can confirm?


----------



## sinner (4 April 2013)

*Re: Long only ORB*

Hmmm my mistake, there appears to be a part 2 of the article with shorts 

http://asxiq.com/blog/xjo-opening-range-breakout-trade-part-ii/

with the following caveat



> before you get too exited to implement the above trading strategy , beaware that
> 
> short selling is risky
> bid ask spreads and fills at the exact open-stretch values may not be possible on all the days
> the transaction costs are not included in the above calculations


----------



## Gringotts Bank (4 April 2013)

The "matching high" strategy (from his book) doesn't look anything like what he posted.  I chose that one because he says it's the best performing strategy.  

For Amibroker:

a = abs(Ref(H,-1)-H)<1;
BuyPrice = C;
Buy = a;
SellPrice = C;
Sell = C>0;

Run this on XJO and this is how badly it performs.  It's a mess.  I think he's having a lend.  Can't be bothered looking at the other strategies.


----------



## asxiq (5 April 2013)

sinner said:


> why is the daily stretch value 0 sometimes?





-> because the daily stretch value is the Min of ABS(open-low,high-open) of previous day ..


----------



## sinner (5 April 2013)

asxiq said:


> -> because the daily stretch value is the Min of ABS(open-low,high-open) of previous day ..




Thanks for the direct reply asxiq.

So if there are 10 days with the stretch value at 0, do you buy market on open in this strategy? Is the listed "open" price for XJO tradeable?


----------



## asxiq (5 April 2013)

Gringotts Bank said:


> The "matching high" strategy (from his book) doesn't look anything like what he posted.  I chose that one because he says it's the best performing strategy.
> 
> For Amibroker:
> 
> ...




Never said any of the strategies are best performing strategies BTW .. 

here is the back test performance summary since Jan 2009
http://asxiq.com/blog/when-xjo-posts-a-matching-high-candle-stick/
let me know what differences you had found with Amibroker back test results ..


----------



## asxiq (5 April 2013)

sinner said:


> Thanks for the direct reply asxiq.
> 
> So if there are 10 days with the stretch value at 0, do you buy market on open in this strategy? Is the listed "open" price for XJO tradeable?




1) I know in that hypothetical case you may be not be execute the hypothetical trade on the XJO index on open ,  because of the staggered open

2) OTOH every day's open which is given by S&P is a open which is calculated with stocks in A-C basket ..


----------



## Gringotts Bank (5 April 2013)

asxiq said:


> Never said any of the strategies are best performing strategies BTW ..
> 
> here is the back test performance summary since Jan 2009
> http://asxiq.com/blog/when-xjo-posts-a-matching-high-candle-stick/
> let me know what differences you had found with Amibroker back test results ..




No point me doing that yet.

In your data, 7/3/2011 an obvious 'matching high' on XJO isn't included.  Why not? There's heaps of such examples missing from your backtest.  AB returns a lot more trades than 26 trades.


----------



## Trembling Hand (5 April 2013)

The data is useless using the XJO some of the time the open is only a tick away from yesterdays close because the ASX doesn't always open at 10:00:00


----------



## asxiq (5 April 2013)

Gringotts Bank said:


> No point me doing that yet.
> 
> In your data, 7/3/2011 an obvious 'matching high' on XJO isn't included.  Why not? There's heaps of such examples missing from your backtest.  AB returns a lot more trades than 26 trades.




the OHLC for 7th and 4th are

7- Mar 2011 4,850.70	4,852.40	4,792.30	4,797.90
4 Mar 2011	4,821.50	4,864.30	4,821.20	4,864.30

How is that a matching high ??


----------



## Gringotts Bank (5 April 2013)

asxiq said:


> the OHLC for 7th and 4th are
> 
> 7- Mar 2011 4,850.70	4,852.40	4,792.30	4,797.90
> 4 Mar 2011	4,821.50	4,864.30	4,821.20	4,864.30
> ...




This what I have.  It also matches on Google finance.

Mar 7, 2011 	4,864.30 	4,864.30 	4,792.30 	4,797.90 	-
Mar 4, 2011 	4,806.40 	4,864.30 	4,821.10 	4,864.30 	-


----------



## skyQuake (5 April 2013)

My iress data is same as asxiq. 

imo the argument is moot as OHL for XJO is generally garbage due to staggered open. The close is the only non distorted value.


----------



## Gringotts Bank (5 April 2013)

skyQuake said:


> My iress data is same as asxiq.
> 
> imo the argument is moot as OHL for XJO is generally garbage due to staggered open. The close is the only non distorted value.




Alright my apologies to asxiq, maybe my data is out, and so is Google Finance.

The close is the Buyprice.  I assume he would buy the asx200 cfd at close.


----------



## sinner (5 April 2013)

Gringotts Bank said:


> Alright my apologies to asxiq, maybe my data is out, and so is Google Finance.
> 
> The close is the Buyprice.  I assume he would buy the asx200 cfd at close.




Buying on close in an ORB system?


----------



## Gringotts Bank (5 April 2013)

sinner said:


> Buying on close in an ORB system?




I was referring to his "matching highs" system, which is one of his better performing systems outlined.  Buyprice and Sellprice are at close.  Doesn't trade very often by the looks.

It's in the ebook link on his site.


----------



## elbee (5 April 2013)

sinner said:


> Are there backtest result issues since it seems to be using XJO (untradeable) data? Wondering if anyone has done any similar tests on SPI that can confirm?




Some years ago I was running a similar strategy on the SPI, but differentiating between up and down "stretches" ie selling at open less MA of open-low or buying at open plus MA of high-open.

I still have the code on file and running it over the last 4 calender years gives results of

YearDollars ProfitMax closed DD2009(1755)60852010104555760201176555765201232654690

These results are for 1 contract using a 10 point stop loss and allowing $10 round-trip brokerage. Because the results are highly variable the system is perhaps best used as part of a portfolio of systems or across a number of markets.


----------



## sinner (6 April 2013)

Good stuff elbee, thanks for your input.


----------



## CanOz (22 June 2016)

I just thought i'd drop off some Ninjascript here for an ORB strategy i had coded up....


```
#region Using declarations
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Data;
using NinjaTrader.Indicator;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Strategy;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
#endregion

// This namespace holds all strategies and is required. Do not change it.
namespace NinjaTrader.Strategy
{
    /// <summary>
    /// By TradingCoders.com for AsiaOnTheBid - takes a bracket trade from the opening range of the day
    /// </summary>
    [Description("By TradingCoders.com for AsiaOnTheBid - takes a bracket trade from the opening range of the day")]
    public class saOpeningRangeBreakoutV2 : Strategy
    {
        #region Variables
        // EXTERNAL INPUT PARAMETERS
        private int entryOffsetTicks = 4; // Default setting for EntryOffsetTicks
        private int positionSize = 1; // Default setting for PositionSize
        private int stopLossOffsetTicks = 0; // Default setting for StopLossOffsetTicks
        private double profitTargetRangeMultiplier = 4; // Default setting for ProfitTargetRangeMultiplier
        private double triggerTrailStopAtRangeMultiplier = 2; // Default setting for ProfitTargetRangeMultiplier
        private bool profitCeasesTrading = false; // Default setting for ProfitCeasesTrading

        private TimeSpan _rangeDuration = new TimeSpan(2, 0, 0);
        private TimeSpan getFlatTime = new TimeSpan(15,0,0);
		private bool getFlatTime_Enabled = true;

        #region anaChandeKrollStop
        private int anaChandeKrollStop_length = 20;
        private int anaChandeKrollStop_periodATR = 10;
        private double anaChandeKrollStop_multiplier = 3.0;
        private anaCKCalcMode anaChandeKrollStop_calcMode = anaCKCalcMode.Arithmetic;
        private anaCKShiftMode anaChandeKrollStop_shiftMode = anaCKShiftMode.Prior_Bar;

        private anaChandeKrollStop _anaChandeKrollStop;
        #endregion

        #region g3ASR
        private g3ASR _g3ASR;
        private int g3ASR_period = 20;
        private bool g3ASR_updateIntraday = false;
        #endregion

        #region Filters
        private bool narrowRange_Filter = true; // requires yesterday to be the narrowest range in the last X days.
        private int narrowRange_Days = 3;
        private double openRange_avgDailyRangePercent = 0.7;
        private bool openRange_filter = true;
        private bool gapFilter = true;
        private double gapFilterPercent = 0.001;

        private bool _dailyAtrFilter = true;
        private int _dailyAtrPeriod = 14;        
        private double _dailyAtrLevel = 0.01;
        private double _lastDailyAtrValue = 0;
        private double _dailyAtrValue = 0;
        private int _dailyBarInProgress = 0;
        #endregion

        // INTERNAL VARIABLES		
        private bool myHistorical = true;
        private int myHistoricalBar = -10;        
        private List<double> dayRanges;
        private double _openingRangeHigh = 0, _openingRangeLow = 0;
        private DateTime _openingRangeFromTime = DateTime.MinValue, _openingRangeToTime = DateTime.MinValue;
        private double _currentSessionOpenPrice = 0;
        private double _previousSessionClosePrice = 0;

        private IOrder LE,SE,LS,LT,SS,ST;
        private TCStrategyPlotMulti orderPlots;
        private bool _triggerTrailStop = false;
        #endregion

        /// <summary>
        /// This method is used to configure the strategy and is called once before any strategy method is called.
        /// </summary>
        protected override void Initialize()
        {
            CalculateOnBarClose = true;
			TraceOrders = true;
			Unmanaged = true;
			MaximumBarsLookBack = MaximumBarsLookBack.Infinite;
            
            _anaChandeKrollStop=anaChandeKrollStop(anaChandeKrollStop_calcMode, anaChandeKrollStop_length, anaChandeKrollStop_multiplier, anaChandeKrollStop_periodATR, anaChandeKrollStop_shiftMode);
            Add(_anaChandeKrollStop);


            if (openRange_filter)
            {
                _g3ASR = g3ASR(g3ASR_period, g3ASR_updateIntraday);
                Add(_g3ASR);
            }

            if (_dailyAtrFilter)
            {
                if (this.BarsPeriod.BasePeriodType == PeriodType.Day && BarsPeriod.Value == 1)
                {
                    _dailyBarInProgress = 0;
                }
                else
                {
                    this.Add(PeriodType.Day, 1);
                    _dailyBarInProgress = 1;
                }
            }

			orderPlots = TCStrategyPlotMulti(1,4,true);
			orderPlots.Name = "OrderPlots";
			orderPlots.Plots[0].Name = "StopLoss";
			orderPlots.Plots[0].Pen.Color = Color.Red;
			orderPlots.Plots[0].PlotStyle = PlotStyle.Hash;
			orderPlots.Plots[1].Name = "Target";
			orderPlots.Plots[1].Pen.Color = Color.Blue;
			orderPlots.Plots[1].PlotStyle = PlotStyle.Hash;
			orderPlots.Plots[2].Name = "LongEntry";
			orderPlots.Plots[2].Pen.Color = Color.Gold;
			orderPlots.Plots[2].PlotStyle = PlotStyle.Hash;
			orderPlots.Plots[3].Name = "ShortEntry";
			orderPlots.Plots[3].Pen.Color = Color.Gold;
			orderPlots.Plots[3].PlotStyle = PlotStyle.Hash;
			Add(orderPlots);
			
        }

        protected override void OnStartUp()
        {
            // do-once
            ClearOutputWindow();

            if (BarsPeriod.Id != PeriodType.Minute)
            {
                if (CurrentBar < BarsRequired + 2)
                    DrawTextFixed("pterror", this.Name + " requires Minute bars", TextPosition.Center, Color.Red, new Font("Tahoma", 12), Color.Red, Color.Silver, 10);
                return;
            }

            dayRanges = new List<double>();
        }

        /// <summary>
        /// Called on each bar update event (incoming tick)
        /// </summary>
        protected override void OnBarUpdate()
        {
            if (_dailyAtrFilter && (_dailyBarInProgress== this.BarsInProgress))
            {
                //daily data
                _lastDailyAtrValue = _dailyAtrValue;
				_dailyAtrValue = ATR(_dailyAtrPeriod)[0];
				
                //Print(Time[0] + " Daily ATR = " + _dailyAtrValue);

                return;
            }

			// myHistorical
			if ((!Historical) || (myHistoricalBar == Count-1 && myHistoricalBar == CurrentBar))
			{				
				myHistorical = false;
			}
			myHistoricalBar = CurrentBar;					
			
			if (CurrentBar < BarsRequired)
				return;
            // ------------             

            if (Bars.FirstBarOfSession)
            {
                //new session detected
                BuildDayRangeData();

                //
                _openingRangeHigh = High[0];
                _openingRangeLow = Low[0];
                _openingRangeFromTime = Time[0];
                _openingRangeToTime = _openingRangeFromTime.Add(this._rangeDuration);

                _currentSessionOpenPrice = Open[0];
                _previousSessionClosePrice = Close[1];

                // ensure nothing funny going on with pre-existing conditions
                if (getFlatTime_Enabled)
                {
                    CancelAllOrders(true, true);
                    if (Position.MarketPosition != MarketPosition.Flat)
                        Position.Close();
                }
                else
                {
                    CancelAllOrders(true, false);
                }
            }

            if (_openingRangeHigh > 0)
            {
                if (Time[0].CompareTo(_openingRangeFromTime) >= 0 && Time[0].CompareTo(_openingRangeToTime) < 0)
                {
                    if (High[0] > _openingRangeHigh) _openingRangeHigh = High[0];
                }
            }

            if (_openingRangeLow > 0)
            {
                if (Time[0].CompareTo(_openingRangeFromTime) >= 0 && Time[0].CompareTo(_openingRangeToTime) < 0)
                {
                    if (Low[0] < _openingRangeLow) _openingRangeLow = Low[0];
                }
            }

            // detect the moment we want to put a new trade on   
            var startTrading = (Time[1].CompareTo(_openingRangeToTime) < 0 && Time[0].CompareTo(_openingRangeToTime) >= 0);

            if (AllOrdersInactive() && startTrading && Position.MarketPosition == MarketPosition.Flat)
            {
                //reset all variables
                _triggerTrailStop = false;
                PlaceBracketOrders();
            }
            else if (getFlatTime_Enabled &&
                        (Time[0].TimeOfDay == getFlatTime
                        || (Time[1].TimeOfDay < getFlatTime && Now.TimeOfDay > getFlatTime)
                        )
                    )
            {
                GetFlat("GetFlatTime");
            }
            
            if (Position.MarketPosition == MarketPosition.Long)
            {                
                if (High[0]-Position.AvgPrice>=TriggerTrailStopAtRangeMultiplier*(_openingRangeHigh-_openingRangeLow))
                {
                    _triggerTrailStop = true;
                }
                if (_triggerTrailStop)
                {
                    if (Close[0] > _anaChandeKrollStop.BullStop[0] && _anaChandeKrollStop.BullStop[0] > Position.AvgPrice)
                    {
                        string oco = "";
                        if (OrderIsActive(LT))
                        {
                            oco = LT.Oco;
                        }

                        // stoploss
                        if (OrderIsActive(LS))
                        {
                            ChangeOrder(LS, Position.Quantity, LS.LimitPrice, _anaChandeKrollStop.BullStop[0]);
                        }
                        else
                            LS = SubmitOrder(0, OrderAction.Sell, OrderType.Stop, Position.Quantity, 0, _anaChandeKrollStop.BullStop[0], oco, "LS");
                    }
                }
            }

            if (Position.MarketPosition == MarketPosition.Short)
            {
                if (Position.AvgPrice - Low[0]>= TriggerTrailStopAtRangeMultiplier * (_openingRangeHigh - _openingRangeLow))
                {
                    _triggerTrailStop = true;
                }

                if (_triggerTrailStop)
                {
                    if (Close[0] < _anaChandeKrollStop.BearStop[0] && _anaChandeKrollStop.BearStop[0]<Position.AvgPrice)
                    {
                        string oco = "";
                        if (OrderIsActive(ST))
                        {
                            oco = ST.Oco;
                        }

                        // stoploss
                        if (OrderIsActive(SS))
                        {
                            ChangeOrder(SS, Position.Quantity, SS.LimitPrice, _anaChandeKrollStop.BearStop[0]);
                        }
                        else
                            SS = SubmitOrder(0, OrderAction.BuyToCover, OrderType.Stop, Position.Quantity, 0, _anaChandeKrollStop.BearStop[0], oco, "SS");
                    }
                }
            }
            // show orders
            ShowOrders();
        }
		
		
		// =============================================================================================================================================================================================================================
		
		private void PlaceBracketOrders()
		{
			if (_openingRangeLow<=0 || _openingRangeHigh<=0)
			{
				TCPrint("Cannot place bracket orders - incomplete range data");
				return;
			}
			
			bool narrowRangeFilterOK = GetNarrowRangeFilterApproval();
			if (!narrowRangeFilterOK)
			{
				Print(Time[0]+" Narrow Range filter prohibits trade today.");
				return;
			}
            
            double range = (_openingRangeHigh - _openingRangeLow) / TickSize;

            if (this.openRange_filter)
            {
                
                if (range>=this.openRange_avgDailyRangePercent*_g3ASR.AverageDailyRange[0])
                {
                    Print(Time[0] + " anaIBRangeBandsV42MTF Filter prohibits trade today.");
                    return;
                }
            }

            if (this.gapFilter)
            {
                double gap = (Math.Abs(_currentSessionOpenPrice - _previousSessionClosePrice))/ TickSize;
                
                if (gap/ _previousSessionClosePrice <= gapFilterPercent)
                {
                    Print(Time[0] + " GAP Filter prohibits trade today.");
                    Print(String.Format("current session open {0}, prev. session close {1} , {2} <= {3}", _currentSessionOpenPrice, _previousSessionClosePrice, gap / _previousSessionClosePrice, gapFilterPercent));
                    return;
                }
            }

            if (this._dailyAtrFilter)
            {
                if (_dailyAtrValue/ _currentSessionOpenPrice < _dailyAtrLevel)
                {
                    Print(Time[0] + " Daily ATR Filter prohibits trade today.");
                    Print(String.Format("Current daily ATR {0} / Session open price {1} < {2}", _dailyAtrValue, _currentSessionOpenPrice, _dailyAtrLevel));
                    return;
                }
            }
            var longEntryPrice 	= rnd(_openingRangeHigh + entryOffsetTicks * TickSize);
            var shortEntryPrice = rnd(_openingRangeLow - entryOffsetTicks * TickSize);
			TCPrint("Placing new bracket orders to enter Long at "+longEntryPrice+" and Short at "+shortEntryPrice);
			// not OCO
			LE = SubmitOrder(0,OrderAction.Buy,OrderType.Stop,positionSize,0,Math.Max(longEntryPrice,GetCurrentAsk()+TickSize),GetAtmStrategyUniqueId(),"LE");
			SE = SubmitOrder(0,OrderAction.SellShort,OrderType.Stop,positionSize,0,Math.Min(shortEntryPrice,GetCurrentBid()-TickSize),GetAtmStrategyUniqueId(),"SE");
		}
		
		private void GetFlat(string reason)
		{
			if (!AllOrdersInactive())
				CancelAllOrders(true,true);
			if (Position.MarketPosition != MarketPosition.Flat)
			{
				TCPrint("Closing position because "+reason);
				Position.Close();
			}
		}
		
		protected override void OnExecution(IExecution e)
		{
			if (e==null || e.Order == null)
				return;
			IOrder o = e.Order;
			Print(Now+ " Execution : "+e.ToString());
			if (LE!=null && o==LE)
			{
				double stopPrice = rnd(_openingRangeLow - stopLossOffsetTicks * TickSize);
				double targetPrice = rnd(_openingRangeHigh + (_openingRangeHigh - _openingRangeLow) *profitTargetRangeMultiplier);
				string oco = GetAtmStrategyUniqueId();
				// stoploss
				if (OrderIsActive(LS))
				{
					oco = LS.Oco;
					ChangeOrder(LS,o.Filled,LS.LimitPrice,LS.StopPrice);
				}
				else
					LS =SubmitOrder(0,OrderAction.Sell,OrderType.Stop,o.Filled,0,Math.Min(stopPrice,o.AvgFillPrice-TickSize),oco,"LS");
				
				// target
				if (OrderIsActive(LT))
				{
					ChangeOrder(LT,o.Filled,LT.LimitPrice,LT.StopPrice);
				}
				else
					LT =SubmitOrder(0,OrderAction.Sell,OrderType.Limit,o.Filled,targetPrice,0,oco,"LT");
				
			}
	
			if (SE!=null && o==SE)
			{
				double stopPrice = rnd(_openingRangeHigh + stopLossOffsetTicks * TickSize);
				double targetPrice = rnd(_openingRangeLow - (_openingRangeHigh - _openingRangeLow) *profitTargetRangeMultiplier);
				string oco = GetAtmStrategyUniqueId();
				// stoploss
				if (OrderIsActive(SS))
				{
					oco = SS.Oco;
					ChangeOrder(SS,o.Filled,SS.LimitPrice,SS.StopPrice);
				}
				else
					SS =SubmitOrder(0,OrderAction.BuyToCover,OrderType.Stop,o.Filled,0,Math.Max(stopPrice,o.AvgFillPrice+TickSize),oco,"SS");
				
				// target
				if (OrderIsActive(ST))
				{
					ChangeOrder(ST,o.Filled,ST.LimitPrice,ST.StopPrice);
				}
				else
					ST =SubmitOrder(0,OrderAction.BuyToCover,OrderType.Limit,o.Filled,targetPrice,0,oco,"ST");
				
			}
			
			// cancel the other side if a profit is hit
			if (profitCeasesTrading)
			{
				if (ST!=null && o==ST && OrderIsActive(LE))
					CancelOrder(LE);
				if (LT!=null && o==LT && OrderIsActive(SE))
					CancelOrder(SE);
			}
		}
		
		private bool OrderIsActive(IOrder o)
		{
			if (	o != null
				&&	o.OrderState != OrderState.Cancelled 
				//&&	o.OrderState != OrderState.PendingCancel
				&&	o.OrderState != OrderState.Rejected
				&&	o.OrderState != OrderState.Filled
				)
				return true;
			return false;
		}

		private bool AllOrdersInactive()
		{
			return !(OrderIsActive(LE) || OrderIsActive(SE) || OrderIsActive(LS) || OrderIsActive(SS) || OrderIsActive(LT) || OrderIsActive(ST));	
		}
		
		private void ShowOrders()
		{
			if (OrderIsActive(LS))
				orderPlots.Values[0].Set(LS.StopPrice);
			if (OrderIsActive(SS))
				orderPlots.Values[0].Set(SS.StopPrice);
			if (OrderIsActive(LT))
				orderPlots.Values[1].Set(LT.LimitPrice);
			if (OrderIsActive(ST))
				orderPlots.Values[1].Set(ST.LimitPrice);
			if (OrderIsActive(LE))
				orderPlots.Values[2].Set(LE.StopPrice);
			if (OrderIsActive(SE))
				orderPlots.Values[3].Set(SE.StopPrice);
		}
		
		#region Narrow Range Filter
		
		private void BuildDayRangeData()
		{
			double yesterday =rnd( PriorDayOHLC().PriorHigh[1] - PriorDayOHLC().PriorLow[1]);
			dayRanges.Add(yesterday);
		}
		
		private bool GetNarrowRangeFilterApproval()
		{
			if (!narrowRange_Filter)
				return true;
			
			if (dayRanges.Count < narrowRange_Days)
				return false;
			double yesterday = dayRanges[dayRanges.Count-1];
			
			double narrowest = double.MaxValue;
			Print(Time[0]+" range yesterday is "+yesterday);
			for (int a = 0; a< narrowRange_Days; a++)
			{
				
				double thisDay = dayRanges[dayRanges.Count -1 - a];
				Print("\t range on day "+a+" is "+thisDay);
				if (thisDay < narrowest)
					narrowest = thisDay;
			}
			return (yesterday) == (narrowest);
		}
		
		#endregion 
		
		#region Miscellaneous
		
		
		/// <summary>
		/// formats a nice looking price
		/// </summary>
		/// <param name="iVal"></param>
		/// <returns></returns>
		private string FormatPrice(double price)
		{
			return Bars.Instrument.MasterInstrument.FormatPrice(price) ;
		}
		
		private void TCPrint(string info)
		{
			Print(Now+" "+this.Name+" "+Instrument.FullName+" "+BarsPeriod.ToString()+" "+info);
		}
		
		private double rnd(double price)
		{
			return (Instrument.MasterInstrument.Round2TickSize(price));
		}
		
		protected DateTime Now
		{
			get {
				DateTime now;
				if (Bars != null && Bars.MarketData != null &&
						Bars.MarketData.Connection.Options.Provider == Cbi.Provider.Replay) {
					now = Bars.MarketData.Connection.Now;
				}
				else {
					now = myHistorical ? Time[0] : DateTime.Now;
				}
				return now;
			}
		}

		
		
		
		
		
		// from anaIBRangeBandsV42MTF
		public class TZListConverter : TypeConverter
		{
			public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
			{
				return true;
			}
			
			public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
			{
				return new StandardValuesCollection((TimeZoneInfo.GetSystemTimeZones()).Select(x=>x.DisplayName).ToList());
			}
		}



        #endregion

        #region 01. Basic
        ///<summary
        ///</summary>
        [Description("Range duration from the session open time")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("01. Range duration (h:min)")]
        public string S_rangeDuration
        {
            get
            {
                return string.Format("{0:D2}:{1:D2}", Math.Abs(_rangeDuration.Hours), Math.Abs(_rangeDuration.Minutes));
            }
            set
            {
                char[] delimiters = new char[] { ':' };
                string[] values = ((string)value).Split(delimiters, StringSplitOptions.None);
                _rangeDuration = new TimeSpan(Convert.ToInt16(values[0]), Convert.ToInt16(values[1]), 0);
            }
        }

        [Description("Price must break the opening range by THIS many ticks")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("02. Entry offset ticks")]
        public int EntryOffsetTicks
        {
            get { return entryOffsetTicks; }
            set { entryOffsetTicks = Math.Max(1, value); }
        }

        [Description("Number of contracts/shares/currency")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("03. Position size")]
        public int PositionSize
        {
            get { return positionSize; }
            set { positionSize = Math.Max(1, value); }
        }

        [Description("StopLoss THIS many ticks beyond other side of opening range. (negative values allowed for 'inside' the range)")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("04. Stoploss offset ticks")]
        public int StopLossOffsetTicks
        {
            get { return stopLossOffsetTicks; }
            set { stopLossOffsetTicks = value; }
        }

        [Description("Profit target is THIS factor of the opening range")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("05. Profit target range multiplier")]
        public double ProfitTargetRangeMultiplier
        {
            get { return profitTargetRangeMultiplier; }
            set { profitTargetRangeMultiplier = Math.Max(0.0100, value); }
        }

        [Description("Trigger trailstop when reached at X times of range in profit")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("06. Trigger trailstop range multiplier")]
        public double TriggerTrailStopAtRangeMultiplier
        {
            get { return triggerTrailStopAtRangeMultiplier; }
            set { triggerTrailStopAtRangeMultiplier = Math.Max(0.0100, value); }
        }

        

        [Description("If a profit on the first trade is achieved, the second trade on the other side of the opening range is not taken.")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("07. Profit ceases trading")]
        public bool ProfitCeasesTrading
        {
            get { return profitCeasesTrading; }
            set { profitCeasesTrading = value; }
        }

        [Description("Get flat at a set time each trading day.")]
        [GridCategory("01. Basic")]
        [Gui.Design.DisplayName("08. GetFlatTime Enabled")]
        public bool GetFlatTime_Enabled
        {
            get { return getFlatTime_Enabled; }
            set { getFlatTime_Enabled = value; }
        }

       
		///<summary
		///</summary>
		[Description("Enter a chart bar time when any open trade must close.")]
		[GridCategory("01. Basic")]
		[Gui.Design.DisplayName("09. GetFlatTime (h:min)")]
		public string S_GetFlatTime
		{
			get 
			{ 	
				return string.Format("{0:D2}:{1:D2}", Math.Abs(getFlatTime.Hours), Math.Abs(getFlatTime.Minutes));
			}
			set 
			{ 
				char[] delimiters = new char[] {':'};
				string[]values =((string)value).Split(delimiters, StringSplitOptions.None);
				getFlatTime = new TimeSpan(Convert.ToInt16(values[0]),Convert.ToInt16(values[1]),0);
			}
		}
		
		

        #endregion

        #region 02. Filters
        [Description("Requires yesterday to be the narrowest range within the last X days")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("01.01 Use NarrowRange Filter")]
        public bool NarrowRange_Filter
        {
            get { return narrowRange_Filter; }
            set { narrowRange_Filter = value; }
        }

        [Description("Requires yesterday to be the narrowest range within the last THIS many days")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("01.02 NarrowRange Filter Days")]
        public int NarrowRange_Days
        {
            get { return narrowRange_Days; }
            set { narrowRange_Days = Math.Max(2, value); }
        }

        [Description("Filter out trades where the 'open range' is greater than a certain % of the Average Daily Range")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("02.01 Use Open Range Filter")]
        public bool OpenRange_Filter
        {
            get { return openRange_filter; }
            set { openRange_filter = value; }
        }

        [Description("Filter out trades where the 'open range' is greater than a certain % of the Average Daily Range")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("02.02 % Average Daily Range")]
        public double OpenRange_AverageTrueRangePercent
        {
            get { return this.openRange_avgDailyRangePercent; }
            set { openRange_avgDailyRangePercent = value; }
        }

        [Description("filter trades based on the % GAP open")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("03.01 Use GAP Filter")]
        public bool GAP_Filter
        {
            get { return gapFilter; }
            set { gapFilter = value; }
        }

        [Description("% GAP open (GAP as defined from prior close to open).")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("03.02 % GAP Filter")]
        public double GAP_Filter_Percent
        {
            get { return gapFilterPercent; }
            set { gapFilterPercent = value; }
        }

        [Description("")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("04.01 Use Daily ATR Filter")]
        public bool DailyAtrFilter
        {
            get { return _dailyAtrFilter; }
            set { _dailyAtrFilter = value; }
        }

        [Description("")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("04.02 Daily ATR Period")]
        public int DailyAtrPeriod
        {
            get { return _dailyAtrPeriod; }
            set { _dailyAtrPeriod = Math.Max(1,value); }
        }

        [Description("")]
        [GridCategory("02. Filters")]
        [Gui.Design.DisplayName("04.03 Daily ATR Minimum Level %")]
        public double DailyAtrLevel
        {
            get { return _dailyAtrLevel; }
            set { _dailyAtrLevel = value; }
        }
        

        #endregion        

        #region 03. Average Daily Range        
        [Description("Period for Average")]
        [GridCategory("03. Average Daily Range")]
        [NinjaTrader.Gui.Design.DisplayName("Period for average")]
        public int G3ASR_Period
        {
            get { return g3ASR_period; }
            set { g3ASR_period = Math.Max(1, value); }
        }

        [Description("If true updates average session during day, false only at the beginning of the session")]
        [GridCategory("03. Average Daily Range")]
        [NinjaTrader.Gui.Design.DisplayName("Update average intraday?")]
        public bool G3ASR_UpdateIntraday
        {
            get { return g3ASR_updateIntraday; }
            set { g3ASR_updateIntraday = value; }
        }
        #endregion

        #region 04. Trailstop		        
        /// <summary>
        /// </summary>
        [Description("ATR period")]
        [GridCategory("04. Trailstop")]
        [Gui.Design.DisplayName("ATR period")]
        public int AnaChandeKrollStop_PeriodATR
        {
            get { return anaChandeKrollStop_periodATR; }
            set { anaChandeKrollStop_periodATR = Math.Max(1, value); }
        }

        /// <summary>
        /// </summary>
        [Description("Lookback period for trailing stop")]
        [GridCategory("04. Trailstop")]
        [Gui.Design.DisplayName("Reference period")]
        public int AnaChandeKrollStop_Length
        {
            get { return anaChandeKrollStop_length; }
            set { anaChandeKrollStop_length = Math.Max(1, value); }
        }

        /// <summary>
        /// </summary>
        [Description("ATR multiplier")]
        [GridCategory("04. Trailstop")]
        [Gui.Design.DisplayName("ATR multiplier")]
        public double AnaChandeKrollStop_Multiplier
        {
            get { return anaChandeKrollStop_multiplier; }
            set { anaChandeKrollStop_multiplier = Math.Max(0.0, value); }
        }

        /// <summary>
        /// </summary>
        [Description("Selects the formula for calculating the average true range.")]
        [GridCategory("04. Trailstop")]
        [Gui.Design.DisplayName("ATR formula")]
        public anaCKCalcMode AnaChandeKrollStop_CalcMode
        {
            get { return anaChandeKrollStop_calcMode; }
            set { anaChandeKrollStop_calcMode = value; }
        }

        /// <summary>
        /// </summary>
        [Description("Selects the bars for which the average true range is calculated")]
        [GridCategory("04. Trailstop")]
        [Gui.Design.DisplayName("Stops calculated from")]
        public anaCKShiftMode AnaChandeKrollStop_ShiftMode
        {
            get { return anaChandeKrollStop_shiftMode; }
            set { anaChandeKrollStop_shiftMode = value; }
        }

        #endregion

        

    }
}
```


----------



## CanOz (2 August 2016)

Open Range Breakout Fade with a 'twist'. Need more OOS data. I'll just test it forward for now. This is the DAX, not the XJO.


----------



## Gringotts Bank (2 August 2016)

CanOz said:


> Open Range Breakout Fade with a 'twist'. Need more OOS data. I'll just test it forward for now. This is the DAX, not the XJO.




Canoz, you've got enough data there to split it in half, optimize the first half and walk forward the second.  What does that look like?


----------



## CanOz (2 August 2016)

Gringotts Bank said:


> Canoz, you've got enough data there to split it in half, optimize the first half and walk forward the second.  What does that look like?




I suppose i could optimize the stops or the time series...?


----------



## Gringotts Bank (2 August 2016)

CanOz said:


> I suppose i could optimize the stops or the time series...?




The profit targets, the stops, the opening range itself (start/stop time), the days of the week, the time of exit, etc.  Anything that improves the curve.


----------



## StuDrefus (10 August 2016)

Is an index the best type of market for a breakout strategy of this type? Don't equity indices tend to be fairly mean reverting (versus commodities, currencies etc)?

I only know of the ORB in the form presented in Toby Crabel's books from several decades back, but I'm pretty sure he relied on trading quite a diverse portfolio of commodity futures to smooth returns. Likewise with Larry Williams who used a not dissimilar 'volatility breakout' strategy (not based on the opening range).

It would be interesting to see this strategy tested over a portfolio.


----------



## CanOz (10 August 2016)

StuDrefus said:


> Is an index the best type of market for a breakout strategy of this type? Don't equity indices tend to be fairly mean reverting (versus commodities, currencies etc)?
> 
> I only know of the ORB in the form presented in Toby Crabel's books from several decades back, but I'm pretty sure he relied on trading quite a diverse portfolio of commodity futures to smooth returns. Likewise with Larry Williams who used a not dissimilar 'volatility breakout' strategy (not based on the opening range).
> 
> It would be interesting to see this strategy tested over a portfolio.




In my testing the only open range breakout that works now is on the HKFE index futures. I have another system that works very well fading an open range breakout under certain circumstances (filter). The former work well after a volatility contraction, where as the latter works well if the opening range is already equal to a certain proportion of the average daily range. It works on many different indices to differing degrees of success. I don't use a time series to define the range though.


----------

