First Hour Breakout

#region Namespaces 
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
#endregion

namespace ScriptCode {
	/// <summary> 
	/// This is an intraday breakout strategy that is based on the idea that a larger than average volume of shares/contracts traded prior to the start of the regular trading session is indicative of short term price momentum. 
	/// The strategy monitors the user-defined overnight period to determine the total volume traded and compares it the average amount traded using a simple moving average (SMA) indicator of the overnight volume. 
	/// If there was an above average amount of volume in the overnight period, the strategy will monitor the High and Low prices made in the first hour of the user-defined regular trading session to determine a price range.
	/// Positions are opened if price breaks out of the range and there is at least 30 minutes left in the regular trading session. Positions are closed at the end of the regular trading session.
	/// 
	/// This strategy should be traded on symbols with overnight or pre-market trading sessions and on intraday data.
	/// 
	/// This strategy was originally developed and discussed by Domenico D’Errico in the June 2017 issue of “Technical Analyses of Stocks and Commodities.”
	/// 
	/// Trading Rules: 
	/// 
	/// Long Entry: A buy market order is generated when the overnight volume is greater than the SMA of overnight volume, price trades above the High price made in the first hour of the regular trading session, and there is at least 30 minutes remaining in the regular trading session.
	/// Long Exit: A sell market order is generated one minute prior to the close of the regular trading session.
	/// 
	/// Short Entry: A sell short market order is generated when the overnight volume is greater than the SMA of overnight volume, price trades below the Low price made in the first hour of the regular trading session, and there is at least 30 minutes remaining in the regular trading session.
	/// Short Exit: A buy market order is generated one minute prior to the close of the regular trading session.
	/// </summary>
	public partial class MyTradingStrategy : TradingStrategyScriptBase  // NEVER CHANGE THE CLASS NAME 
	{
#region Variables
		// Indicates whether to enable the volatility bias filter.
		private bool _enableVolatilityBiasFilter;
		// The number of periods used to calculate overnight volume SMA.
		private int _overnightVolumeSMAPeriods;
		// The overnight volume indicator.
		private Indicator _overnightVolume;
		// Indicates whether a volatility bias had been detected.
		private bool _volatilityBiasDetected;
		// The hour value of the trading start time.
		private int _tradingStartTimeHour;
		// The minute value of the trading start time.
		private int _tradingStartTimeMinute;
		// The hour value of the trading end time.
		private int _tradingEndTimeHour;
		// The minute value of the trading end time.
		private int _tradingEndTimeMinute;
		// The high at which the underlying symbol traded during the first hour of trading.
		private double _firstHourHigh;
		// The low at which the underlying symbol traded during the first hour of trading.
		private double _firstHourLow;
		// Use for holding the overnight volume values.
		private List<double> _overnightVolumeValues;
		// Indicates whether the strategy is waiting for an open position to close.
		private bool _waitingToClose;
		// Indicates whether to enable the trading strategy to short symbols.
		private bool _enableShorting;
		// Indicates whether to enable the trading strategy to long symbols.
		private bool _enableLonging;
		// Indicates whether the collection of overnight volume values has been initialized.
		private bool _initiallyLoaded;
#endregion

#region OnInitialize
		/// <summary>
		/// This function is used for accepting the script parameters and for initializing the script prior to all other function calls.
		/// Once the script is assigned to a Desktop, its parameter values can be specified by the user and can be selected for optimization. 
		/// </summary>
		/// --------------------------------------------------------------------------------------------------
		/// PLEASE USE THE SCRIPT WIZARD (CTRL+W) TO ADD, EDIT AND REMOVE THE SCRIPT PARAMETERS
		/// --------------------------------------------------------------------------------------------------
		/// YOU MUST SET A PARAM TAG FOR EACH PARAMETER ACCEPTED BY THIS FUNCTION.
		/// ALL PARAM TAGS SHOULD BE SET IN THE 'OnInitialize' REGION, RIGHT ABOVE THE 'OnInitialize' FUNCTION.
		/// THE ORDER OF THE TAGS MUST MATCH THE ORDER OF THE ACTUAL PARAMETERS.

		/// REQUIRED ATTRIBUTES:
		/// (1) name: The exact parameter name.
		/// (2) type: The type of data to collect from the user: 
		/// Set to "Integer" when the data type is 'int'
		/// Set to "IntegerArray" when the data type is 'int[]'
		/// Set to "DateTime" when the data type is 'long'  
		/// Set to "DateTimeArray" when the data type is 'long[]'  
		/// Set to "Boolean" when the data type is 'bool'
		/// Set to "BooleanArray" when the data type is 'bool[]'
		/// Set to "Double" when the data type is 'double'
		/// Set to "DoubleArray" when the data type is 'double[]'
		/// Set to "String" when the data type is 'string'
		/// Set to "StringArray" when the data type is 'string[]'
		/// Set to "Indicator" when the data type is 'Indicator'
		/// Set to "Pattern" when the data type is 'Pattern'
		/// Set to "Signal" when the data type is 'Signal'
		/// Set to "Drawing" when the data type is 'Drawing'

		/// OPTIONAL ATTRIBUTES:
		/// (3) default: The default parameter value is only valid when the type is Integer, Boolean, Double, String or an API Type. 
		/// (4) min: The minimum parameter value is only valid when the type is Integer or Double.
		/// (5) max: The maximum parameter value is only valid when the type is Integer or Double.

		/// EXAMPLE: <param name="" type="" default="" min="" max="">Enter the parameter description here.</param> 
		/// --------------------------------------------------------------------------------------------------
		/// <param name="overnightStartTime" type="Integer" default="0" min="0" max="2400">The start of the overnight period in 24-hr hhmm notation in the time zone of the underlying symbol.</param>
		/// <param name="tradingStartTime" type="Integer" default="830" min="0" max="2400">The start of the trading session in 24-hr hhmm notation in the time zone of the underlying symbol.</param>
		/// <param name="tradingEndTime" type="Integer" default="1515" min="0" max="2400">The end of the trading session in 24-hr hhmm notation in the time zone of the underlying symbol.</param>
		/// <param name="enableVolatilityBiasFilter" type="Boolean" default="True">Indicates whether to enable the volatility bias filter.</param>
		/// <param name="overnightVolumeSMAPeriods" type="Integer" default="5" min="1">The number of periods used to calculate overnight volume SMA.</param>
		/// <param name="enableShorting" type="Boolean" default="True">Indicates whether to enable the trading strategy to short symbols.</param>
		/// <param name="enableLonging" type="Boolean" default="True">Indicates whether to enable the trading strategy to long symbols.</param>
		public void OnInitialize(
			int overnightStartTime,
			int tradingStartTime,
			int tradingEndTime,
			bool enableVolatilityBiasFilter,
			int overnightVolumeSMAPeriods,
			bool enableShorting,
			bool enableLonging) {
			// Set the script parameters to script variables.
			_enableVolatilityBiasFilter = enableVolatilityBiasFilter;
			_overnightVolumeSMAPeriods = overnightVolumeSMAPeriods;
			_enableShorting = enableShorting;
			_enableLonging = enableLonging;
			// Calculate the hour value of the trading start time.
			_tradingStartTimeHour = tradingStartTime / 100;
			// Calculate the minute value of the trading start time.
			_tradingStartTimeMinute = tradingStartTime % 100;
			// Calculate the hour value of the trading end time.
			_tradingEndTimeHour = tradingEndTime / 100;
			// Calculate the minute value of the trading end time.
			_tradingEndTimeMinute = tradingEndTime % 100;
			// Create for holding the overnight volume values.
			_overnightVolumeValues = new List<double>();
			// Set the time zone to the time zone of the underlying symbol.
			DateTimeSetTimeZone(SymbolTimeZone());
			// Remove all of the indicators from the chart so that we don't get duplicates.
			ChartIndicatorRemoveAll(SymbolIndex());		
			// Create the overnight volume indicator.
			_overnightVolume = IndicatorOV(SymbolIndex(), overnightStartTime, tradingStartTime);
			// Plot the indicator on the underlying symbol's chart.
			int indicatorItemID = ChartIndicatorPlot(SymbolIndex(), _overnightVolume, "Overnight Volume", - 1, 2);
			// Set the indicator pen.
			ChartIndicatorSetPenByIndexRGB(SymbolIndex(), indicatorItemID, 0, new int[] { 35, 145, 255, 255 }, C_DashStyle.SOLID, 1);
			// Set the indicator style.
			ChartIndicatorSetPlotStyle(SymbolIndex(), indicatorItemID, C_PlotStyle.BOX_ZERO);
		}
#endregion

#region OnBarUpdate
		/// <summary>
		/// This function is called after each new bar of each symbol assigned to the Desktop strategy. 
		/// It should evaluate the specified symbol and its new bar in order to determine whether to generate new orders for it. 
		/// Never create indicators, signals or patterns from OnBarUpdate, for performance reasons those should be created from OnInitialize.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The index of the symbol in the strategy symbol table</param>
		/// <param name="dataSeries" type="Integer">The number indicating the data series from which the symbol was updated. 
		/// According to the Desktop strategy data series settings: 0 for the main data series, 1 for the second data series, etc. (See the DataSeriesSwitch function).</param>
		/// <param name="completedBars" type="Integer">The number of completed bars for the specified symbol since the last call to OnBarUpdate.
		/// Always 1 unless the bar type can generate multiple completed bars from a single tick/minute/day update (depending on the underlying bar source).</param>
		public override void OnBarUpdate(
			int symbolIndex,
			int dataSeries,
			int completedBars) {
			// Create a variable to hold the start of the current day's trading session.
			long tradingStartTime = DateTimeCreate(DateTimeYear(DateTimeCurrent()), DateTimeMonth(DateTimeCurrent()), DateTimeDay(DateTimeCurrent()), _tradingStartTimeHour, _tradingStartTimeMinute, 0);
			// Create a variable to hold the end of the current day's trading session.
			long tradingEndTime = DateTimeCreate(DateTimeYear(DateTimeCurrent()), DateTimeMonth(DateTimeCurrent()), DateTimeDay(DateTimeCurrent()), _tradingEndTimeHour, _tradingEndTimeHour, 0);
			
			// Initialize the collection of overnight volume values if necessary.
			InitializeIfNecessary(tradingStartTime);
			
			// Check whether it is currently the period to monitor the high and low values.
			if(tradingStartTime < DateTimeCurrent() && DateTimeCurrent() <= DateTimeAddMinutes(tradingStartTime, 60)){
				// Check whether it is time to calculate the overnight volume SMA and check for a volatility bias.
				if(DataEndDateTime(1) <= tradingStartTime && DataIsComplete()){
					// Reset the value of the high during the first hour of trading.
					_firstHourHigh = double.MinValue;
					// Reset the value of the low during the first hour of trading.
					_firstHourLow = double.MaxValue;
					// Record whether a volatility bias has occurred provided that enought time has passed to calculate the overnight volume SMA.
					_volatilityBiasDetected = _overnightVolumeValues.Count == _overnightVolumeSMAPeriods ? _overnightVolume[0] > _overnightVolumeValues.Average() : false;
					// Add the most recent overnight volume value to the collection of values.
					_overnightVolumeValues.Add(_overnightVolume[0]);
					// Check whether the collection of volume values is greater than the SMA lookback period.
					if(_overnightVolumeValues.Count > _overnightVolumeSMAPeriods){
						// Remove the oldest overnight volume value.
						_overnightVolumeValues.RemoveAt(0);
					}
				}
				// Check whether a volatility bias has been detected or the volatility bias filter is disabled.
				if((_enableVolatilityBiasFilter && _volatilityBiasDetected) || !_enableVolatilityBiasFilter){
					// Record a new high if the high of the current bar is greater than the current high of the first hour of trading.
					_firstHourHigh = Math.Max(DataHigh(), _firstHourHigh);
					// Record a new low if the low of the current bar is less than the current low of the first hour of trading.
					_firstHourLow = Math.Min(DataLow(), _firstHourLow);
				}
			}
			// Check whether it is after the first hour of trading but before the cutoff time for sending entry orders.
			else if(DateTimeAddMinutes(tradingStartTime, 60) < DateTimeCurrent() && DateTimeCurrent() <= DateTimeAddMinutes(tradingEndTime, -30)){
				// Check whether a volatility bias has been detected or the volatility bias filter is disabled and there is not already an open position nor pending order.
				if(((_enableVolatilityBiasFilter && _volatilityBiasDetected) || !_enableVolatilityBiasFilter) && !PositionExists(C_PositionStatus.OPEN) && !OrderExists(C_Status.PENDING)){
					// Check whether the underlying symbol trades below the low of the first hour of trading.
					if(_enableShorting && DataLow() < _firstHourLow){
						// Generate a sell short market order while assuming that a position sizing script will assign the quantity. 
						BrokerMarket(C_ActionType.SELL_SHORT, 0, C_TIF.DAY, "Time to sell short");
					} 
					// Check whether the underlying symbol trades above the high of the first hour of trading.
					else if(_enableLonging && _firstHourHigh < DataHigh()){
						// Generate a buy market order while assuming that a position sizing script will assign the quantity.
						BrokerMarket(C_ActionType.BUY, 0, C_TIF.DAY, "Time to buy");
					}
				}
			} 
			// Check whether it is time to close open positions, an open position exists, and the strategy has not already requested that the open position be closed.
			else if(DateTimeCurrent() >= DateTimeAddMinutes(tradingEndTime, -1) && PositionExists(C_PositionStatus.OPEN) && !_waitingToClose){
				// Close the open position.
				BrokerClosePosition("Time to close.");
				// Record that the strategy is waiting for the position to be closed.
				_waitingToClose = true;
			}
		}
#endregion

#region OnOrderFillUpdate
		/// <summary>
		/// This function is called for each new order fill.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index</param>
		/// <param name="orderIndex" type="Integer">The order index</param>
		/// <param name="orderFillIndex" type="Integer">The order fill index</param>
		public override void OnOrderFillUpdate(
			int symbolIndex,
			int orderIndex,
			int orderFillIndex) {
			// OnOrderFillUpdate Content
		}
#endregion

#region OnOrderUpdate
		/// <summary>
		/// This function is called when an order is executed or cancelled.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index</param>
		/// <param name="orderIndex" type="Integer">The order index</param>
		/// <param name="status" type="C_Status">The updated status of the order</param>
		public override void OnOrderUpdate(
			int symbolIndex,
			int orderIndex,
			C_Status status) {
			// OnOrderUpdate Content
		}
#endregion

#region OnPositionUpdate
		/// <summary>
		/// This function is called when a position is opened or closed. 
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index</param>
		/// <param name="positionIndex" type="Integer">The position index</param>
		/// <param name="status" type="C_PositionStatus">The updated status of the position</param>
		public override void OnPositionUpdate(
			int symbolIndex,
			int positionIndex,
			C_PositionStatus status) {
			// Check whether the position just closed.
			if(status == C_PositionStatus.CLOSED){
				// Record that the strategy is no longer waiting for the position to be closed.
				_waitingToClose = false;
			}
		}
#endregion

#region OnSessionUpdate
		/// <summary>
		/// This function is called when a session is opened or closed.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index whose session is updated</param>
		/// <param name="status" type="C_SessionStatus">The session status</param>
		public override void OnSessionUpdate(
			int symbolIndex,
			C_SessionStatus status) {
		}
#endregion

#region OnNewsUpdate
		/// <summary>
		/// This function is called when a news update is received and only if the NO_NEWS_UPDATES comment is removed.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index for the update</param>
		/// <param name="dateTime" type="DateTime">The date/time in which the update was received by the platform</param>
		/// <param name="title" type="String">The update title</param>
		/// <param name="message" type="String">The update message</param>   
		/// <param name="type" type="C_MessageType">The update message type</param>
		public override void OnNewsUpdate(
			int symbolIndex,
			long dateTime,
			string title,
			string message,
			C_MessageType type) {
			// OnNewsUpdate Content
			// [NO_NEWS_UPDATES] - Delete this comment to enable news updates to this strategy.
		}
#endregion

#region OnRSSUpdate
		/// <summary>
		/// This function is called when an RSS update is received and only if the NO_RSS_UPDATES comment is removed.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index for the update</param>
		/// <param name="dateTime" type="DateTime">The date/time in which the update was received by the platform</param>
		/// <param name="title" type="String">The update title</param>
		/// <param name="message" type="String">The update message</param>   
		/// <param name="type" type="C_MessageType">The message type</param>
		public override void OnRSSUpdate(
			int symbolIndex,
			long dateTime,
			string title,
			string message,
			C_MessageType type) {
			// OnRSSUpdate Content
			// [NO_RSS_UPDATES] - Delete this comment to enable RSS updates to this strategy.
		}
#endregion

#region OnAlertUpdate
		/// <summary>
		/// This function is called when an alert update is received and only if the NO_ALERT_UPDATES comment is removed.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index for the update</param>
		/// <param name="dateTime" type="DateTime">The date/time in which the update was received by the platform</param>
		/// <param name="message" type="String">The update message</param>   
		/// <param name="type" type="C_MessageType">The update message type</param>
		public override void OnAlertUpdate(
			int symbolIndex,
			long dateTime,
			string message,
			C_MessageType type) {
			// OnAlertUpdate Content
			// [NO_ALERT_UPDATES] - Delete this comment to enable alert updates to this strategy.
		}
#endregion

#region OnJournalUpdate
		/// <summary>
		/// This function is called when a journal update is received and only if the NO_JOURNAL_UPDATES comment is removed.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index for the update</param>
		/// <param name="dateTime" type="DateTime">The date/time in which the update was received by the platform</param>
		/// <param name="title" type="String">The update title</param>
		/// <param name="message" type="String">The update message</param>   
		/// <param name="type" type="C_MessageType">The update message type</param>
		public override void OnJournalUpdate(
			int symbolIndex,
			long dateTime,
			string title,
			string message,
			C_MessageType type) {
			// OnJournalUpdate Content
			// [NO_JOURNAL_UPDATES] - Delete this comment to enable journal updates to this strategy.
		}
#endregion

#region OnDataConnectionUpdate
		/// <summary>
		/// This function is called when a data connection update is received and only if the NO_DATA_CONNECTION_UPDATES comment is removed.
		/// </summary>
		/// <param name="symbolIndex" type="Integer">The symbol index for the update</param>
		/// <param name="dateTime" type="DateTime">The date/time in which the update was received by the platform</param>
		/// <param name="message" type="String">The update message</param>   
		/// <param name="type" type="C_MessageType">The update message type</param>
		public override void OnDataConnectionUpdate(
			int symbolIndex,
			long dateTime,
			string message,
			C_MessageType type) {
			// OnDataConnectionUpdate Content
			// [NO_DATA_CONNECTION_UPDATES] - Delete this comment to enable data connection updates to this strategy.
		}
#endregion

#region OnBrokerConnectionUpdate
		/// <summary>
		/// This function is called when a broker connection update is received and only if the NO_BROKER_CONNECTION_UPDATES comment is removed.
		/// </summary>
		/// <param name="dateTime" type="DateTime">The date/time in which the update was received by the platform</param>
		/// <param name="message" type="String">The update message</param>   
		/// <param name="type" type="C_MessageType">The update message type</param>
		public override void OnBrokerConnectionUpdate(
			long dateTime,
			string message,
			C_MessageType type) {
			// OnBrokerConnectionUpdate Content
			// [NO_BROKER_CONNECTION_UPDATES] - Delete this comment to enable broker connection updates to this strategy.
		}
#endregion

#region OnShutdown
		/// <summary>
		/// This function is called when the script is shutdown.
		/// </summary>
		public override void OnShutdown() {
			// OnShutdown Content
		}

		
#endregion	
		
public void InitializeIfNecessary(long tradingStartTime){
	// Check whether it is necessary to initialize the overnight volume values and it has not already been done.
	if((StrategyMode() == C_StrategyMode.LIVE || StrategyMode() == C_StrategyMode.PLAYBACK) && !_initiallyLoaded){
		// Check whether it is past the overnight window.
		if(tradingStartTime < DateTimeCurrent()){
			// Add the most recent overnight volume value to the collection of values.
			_overnightVolumeValues.Add(_overnightVolume[0]);
		}
		// Check whether the strategy needs to go back in time to fill up the collection of overnight volume values.
		if(_overnightVolumeValues.Count() != _overnightVolumeSMAPeriods){
			// Create a variable the hold the bar shift index.
			int tempBarShift = 1;
			// Iterate backwards until the collection is full or there is no more volume history.
			while(_overnightVolumeValues.Count() < _overnightVolumeSMAPeriods && _overnightVolume[tempBarShift] != 0){
				// Check whether the overnight volume of the current barshift is greater than the overnight volume one barshift into the future.
				if(_overnightVolume[tempBarShift] > _overnightVolume[tempBarShift - 1]){
					// Add the current overnight volume value to the front of the collection.
					_overnightVolumeValues.Insert(0, _overnightVolume[tempBarShift]);
				}
				// Increment the bar shift variable.
				tempBarShift++;
			}
		}
		// Check whether it is past the time to check for a volatility bias.
		if(tradingStartTime < DateTimeCurrent()){
			// Record whether a volatility bias has occurred provided that the collection of overnight volume values is full.
			_volatilityBiasDetected = _overnightVolumeValues.Count == _overnightVolumeSMAPeriods ? _overnightVolume[0] > _overnightVolumeValues.Average() : false;
			// Check whether a volatility bias has been detected or the volatility bias filter is disabled.
			if((_enableVolatilityBiasFilter && _volatilityBiasDetected) || !_enableVolatilityBiasFilter){
				// Initialize the first hour high.
				_firstHourHigh = double.MinValue;
				// Initialize the first hour low.
				_firstHourLow = double.MaxValue;
				// Create a variable the hold the bar shift index.
				int barShift = 1;
				// Iterate backwards until the the start time of trading is reached.
				while(tradingStartTime < DataEndDateTime(barShift)){
					// Check whether it is currently the period to monitor the high and low values.
					if(DataEndDateTime(barShift) <= DateTimeAddMinutes(tradingStartTime, 60)){
						// Record a new high if the high of the current bar is greater than the current high of the first hour of trading.
						_firstHourHigh = Math.Max(DataHigh(barShift), _firstHourHigh);
						// Record a new low if the low of the current bar is less than the current low of the first hour of trading.
						_firstHourLow = Math.Min(DataLow(barShift), _firstHourLow);
					}
					// Increment the bar shift variable.
					barShift++;
				}
			}
		}
		// Record that the overnight volume values have been initialized.
		_initiallyLoaded = true;
	}
}

	}
}