XJO quant ORB strategy - Aussie Stock Forums

# Thread: XJO quant ORB strategy

1. ## XJO quant ORB strategy

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-ra...mance-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?

2. ## Re: Long only ORB

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

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

3. ## Re: XJO quant ORB strategy

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

4. ## Re: XJO quant ORB strategy

Originally Posted by sinner
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 ..

5. ## Re: XJO quant ORB strategy

Originally Posted by asxiq
-> 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?

6. ## Re: XJO quant ORB strategy

Originally Posted by Gringotts Bank
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;
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.
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...-candle-stick/
let me know what differences you had found with Amibroker back test results ..

7. ## Re: XJO quant ORB strategy

Originally Posted by sinner
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 ..

8. ## Re: XJO quant ORB strategy

Originally Posted by asxiq
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...-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.

9. ## Re: XJO quant ORB strategy

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

10. ## Re: XJO quant ORB strategy

Originally Posted by Gringotts Bank
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 ??

11. ## Re: XJO quant ORB strategy

Originally Posted by asxiq
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 ??
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 -

12. ## Re: XJO quant ORB strategy

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.

13. ## Re: XJO quant ORB strategy

Originally Posted by skyQuake
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.

14. ## Re: XJO quant ORB strategy

Originally Posted by Gringotts Bank
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?

15. ## Re: XJO quant ORB strategy

Originally Posted by sinner
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.

16. ## Re: XJO quant ORB strategy

Originally Posted by sinner
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
 Year Dollars Profit Max closed DD 2009 (1755) 6085 2010 10455 5760 2011 7655 5765 2012 3265 4690

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.

17. ## Re: XJO quant ORB strategy

Good stuff elbee, thanks for your input.

18. ## Re: XJO quant ORB strategy

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

Code:
``` #region Using declarations
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml.Serialization;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
#endregion

// This namespace holds all strategies and is required. Do not change it.
{
/// <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 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;
#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);

if (openRange_filter)
{
}

if (_dailyAtrFilter)
{
if (this.BarsPeriod.BasePeriodType == PeriodType.Day && BarsPeriod.Value == 1)
{
_dailyBarInProgress = 0;
}
else
{
_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;

}

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

_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
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

// target
if (OrderIsActive(ST))
{
ChangeOrder(ST,o.Filled,ST.LimitPrice,ST.StopPrice);
}
else

}

// cancel the other side if a profit is hit
{
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]);
}

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")]
{
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")]
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")]
{
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

}
}```

19. ## Re: XJO quant ORB strategy

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.

20. ## Re: XJO quant ORB strategy

Originally Posted by CanOz
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?