Donchian Crawling Along Pattern

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

namespace ScriptCode {
	/// <summary>
	/// This is a multiple entry trend following strategy that enters a position when the underlying symbol experiences a pull back in a longer term trend and closes out when a profit target or stop loss has been reached. 
	/// This strategy uses Donchian channels to determine when a trend forms as well as where to place profit target and stop loss orders. 
	/// 
	/// An up-trend is formed when price makes at least the specified amount of new Highs over the specified period and price remains above the middle Donchian band. 
	/// Once an up-trend is confirmed and price crosses below the middle Donchian band, the strategy will take its first long entry. 
	/// If price continues downward and reaches the lower Donchian band, the strategy will take the second long entry.
	/// 
	/// A down-trend is formed when price makes at least the specified amount of new Lows over the specified period and price remains below the middle Donchian band. 
	/// Once a down-trend is confirmed and price crosses above the middle Donchian band, the strategy will take its first short entry. 
	/// If price continues upward and reaches the upper Donchian band, the strategy will take the second short entry.
	/// 
	/// Stop loss and profit target orders are generated alongside entry orders. 
	/// For long entries, the strategy places profit target orders one tick above the most recent upper Donchian level that was flat and stop loss orders one tick below the most recent lower Donchian level that was flat. 
	/// Similarly for short entries, the strategy places profit target orders one tick below the most recent lower Donchian level that was flat and stop loss orders one tick above the most recent upper Donchian level that was flat.
	/// 
	/// Trading Rules: 
	/// 
	/// Long Entry 1: A buy market order is generated when the symbol makes the required amount of new Highs, price remains above the middle Donchian over the lookback period, 
	/// and price has crossed below the middle Donchian band.
	/// 
	/// Long Entry 2: A buy marker order is generated when price trades at or below the lower Donchian band.
	/// Long Exit: A long position is exited when a stop loss or profit target is reached.
	/// 
	/// Short Entry 1: A sell short market order is generated when price makes the required amount of new Lows, price remains below the middle Donchian over the lookback period, and price has crossed above the middle Donchian band.
	/// Short Entry 2: A sell short market order is generated when price trades at or above the upper Donchian band.
	/// Short Exit: A short position is exited when a stop loss or profit target is reached.
	/// </summary>
	public partial class MyTradingStrategy : TradingStrategyScriptBase  // NEVER CHANGE THE CLASS NAME 
	{
#region Variables
		// The upper donchian indicator.
		private Indicator _donchianUpper;
		// The lower donchian indicator.
		private Indicator _donchianLower;
		// The middle donchian indicator.
		private Indicator _donchianMiddle;
		// Indicates whether to enable the trading strategy script to short symbols.  
		private bool _enableShorting;
		// Indicates whether to enable the trading strategy script to long symbols.  
		private bool _enableLonging;
		// Indicates whether to enable Entry 2.
		private bool _enableEntry2;
		// Indicates whether the Entry 2 entry order has been fired.
		private bool _entry2Fired;
		// Pattern object indicating whether the Bearish Donchian Crawling Along pattern holds true.
		private Pattern _bearishDCAPattern;
		// Pattern object indicating whether the Bullish Donchian Crawling Along pattern holds true.
		private Pattern _bullishDCAPattern;
#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="donchianPeriods" type="Integer" default="20" min="1">The number of periods used in calculating the donchian channel. </param>
		/// <param name="patternLookback" type="Integer" default="10" min="1">The number of consecutive bars considered when determining if pattern holds true. </param>
		/// <param name="percentNewHighLowsRequired" type="Double" default="50" min="0" max="100">The minimum required percentage of bars that make new highs/lows throughout the lookback of the pattern. </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>
		/// <param name="enableEntry2" type="Boolean" default="True">Indicates whether to enable Entry 2. Entry 2 occurs when the underlying symbol trades at or below the lower donchian level 
		/// for long trades and at or above the upper donchian level for short trades.</param>
		public void OnInitialize(
			int donchianPeriods,
			int patternLookback,
			double percentNewHighLowsRequired,
			bool enableShorting,
			bool enableLonging,
			bool enableEntry2) {
			// Set the script parameters to script variables. 
			_enableShorting = enableShorting;
			_enableLonging = enableLonging;
			_enableEntry2 = enableEntry2;
			
			// Create the upper donchian indicator for the underlying symbol.
			_donchianUpper = IndicatorDCU(SymbolIndex(), donchianPeriods);
			// Create the lower donchian indicator for the underlying symbol.
			_donchianLower = IndicatorDCL(SymbolIndex(), donchianPeriods);
			// Create the middle donchian indicator for the underlying symbol.
			_donchianMiddle = IndicatorDIVC(IndicatorADD(_donchianLower, _donchianUpper), 2);
			// Remove all of the indicators from the chart so that we don't get duplicates.
			ChartIndicatorRemoveAll(SymbolIndex());
			// Remove the session break markers (personal preference).
			ChartSetProperties(SymbolIndex(), C_ChartProperties.SHOW_SESSION_BREAKS, false);
			
			// Plot the indicator on the underlying symbol's chart.
			int donchianUpperItemID = ChartIndicatorPlot(SymbolIndex(), _donchianUpper, "Upper donchian", - 1, 1);
			// Set the indicator pen.
			ChartIndicatorSetPenByIndex(SymbolIndex(), donchianUpperItemID, 0, C_Color.LIGHT_BLUE, C_DashStyle.SOLID, 2);
			// Plot the indicator on the underlying symbol's chart.
			int donchianMiddleItemID = ChartIndicatorPlot(SymbolIndex(), _donchianMiddle, "Middle donchian", - 1, 1);
			// Set the indicator pen.
			ChartIndicatorSetPenByIndex(SymbolIndex(), donchianMiddleItemID, 0, C_Color.LIGHT_YELLOW, C_DashStyle.SOLID, 2);
			// Plot the indicator on the underlying symbol's chart.
			int donchianLowerItemID = ChartIndicatorPlot(SymbolIndex(), _donchianLower, "Lower donchian", - 1, 1);
			// Set the indicator pen.
			ChartIndicatorSetPenByIndex(SymbolIndex(), donchianLowerItemID, 0, C_Color.LIGHT_BLUE, C_DashStyle.SOLID, 2);
			
			// Check whether it is necessary to create and graph the Bearish Donchian Crawling Along pattern.
			if(enableShorting){
				// Create the Bearish Donchian Crawling Along pattern for the underlying symbol.
				_bearishDCAPattern = PatternDDCA(SymbolIndex(), donchianPeriods, patternLookback, percentNewHighLowsRequired);
				// Plot the pattern on the underlying symbol's chart to highlight instances of the pattern.
				ChartPatternPlot(SymbolIndex(), _bearishDCAPattern, "Bearish Donchian Crawling Along", -1, 1);
			}
			// Check whether it is necessary to create and graph the Bullish Donchian Crawling Along pattern.
			if(enableLonging){
				// Create the Bullish Donchian Crawling Along pattern for the underlying symbol.
				_bullishDCAPattern = PatternUDCA(SymbolIndex(), donchianPeriods, patternLookback, percentNewHighLowsRequired);
				// Plot the pattern on the underlying symbol's chart to highlight instances of the pattern.
				ChartPatternPlot(SymbolIndex(), _bullishDCAPattern, "Bullish Donchian Crawling Along", -1, 1);
			}
		}
#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) {
			// Check whether there are no open positions or pending orders.
			if (_enableLonging && !PositionExists(C_PositionStatus.OPEN) && !OrderExists(C_Status.PENDING)) {
				// Check if the Bullish Donchian Crawling Along pattern holds true.
				if(_bullishDCAPattern[0] != 0){
					// Generate a buy market order while assuming that a position sizing script will assign the quantity. 
					BrokerMarket(C_ActionType.BUY, 0, C_TIF.GTC, "Entry 1");
					
					// Create a variable to hold the number of bar shifts used when finding stop loss level.
					int stopLossBarShift = 0;
					// Iterate backwards until lower donchian is flat and check that indicator is non-zero to avoid infinite loop.
					while(_donchianLower[stopLossBarShift + 1] != _donchianLower[stopLossBarShift]
						&& _donchianLower[stopLossBarShift + 1] != 0){
						// Increase stop loss bar shift index.
						stopLossBarShift++;
					}
					// Generate a stop loss order one tick below flat lower donchian level while assuming that a position sizing script will assign the quantity.
					BrokerStop(C_ActionType.SELL, 0, C_TIF.GTC, _donchianLower[stopLossBarShift] - SymbolTickSize(), "Stop loss");
					
					// Create a variable to hold the number of bar shifts used when finding profit target level.
					int profitTargetBarShift = 0;
					// Iterate backwards until upper donchian is flat and check that indicator is non-zero to avoid infinite loop.
					while(_donchianUpper[profitTargetBarShift + 1] != _donchianUpper[profitTargetBarShift]
						&& _donchianUpper[profitTargetBarShift + 1] != 0){
						// Increase profit target bar shift index.
						profitTargetBarShift++;
					}
					// Generate profit target order one tick above flat upper donchian level while assuming that a position sizing script will assign the quantity.
					BrokerLimit(C_ActionType.SELL, 0, C_TIF.GTC, _donchianUpper[profitTargetBarShift] + SymbolTickSize(), "Profit target");
				}
			}
			
			// Check whether there are no open positions or pending orders.
			if (_enableShorting && !PositionExists(C_PositionStatus.OPEN) && !OrderExists(C_Status.PENDING)) {
				// Check if the Bearish Donchian Crawling Along pattern holds true.
				if(_bearishDCAPattern[0] != 0){
					// 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.GTC, "Entry 1");
					
					// Create variable to hold the number of bar shifts used when finding stop loss level.
					int stopLossBarShift = 0;
					// Iterate backwards until upper donchian is flat and check that indicator is non-zero to avoid infinite loop.
					while(_donchianUpper[stopLossBarShift + 1] != _donchianUpper[stopLossBarShift]
						&& _donchianUpper[stopLossBarShift + 1] != 0){
						// Increase stop loss bar shift index.
						stopLossBarShift++;
					}
					// Generate a stop loss order one tick above flat upper donchian level while assuming that a position sizing script will assign the quantity.
					BrokerStop(C_ActionType.BUY_TO_COVER, 0, C_TIF.GTC, _donchianUpper[stopLossBarShift] + SymbolTickSize(), "Stop loss");
					
					// Create a variable to hold the number of bar shifts used when finding profit target level.
					int profitTargetBarShift = 0;
					// Iterate backwards until lower donchian is flat and check that indicator is non-zero to avoid infinite loop.
					while(_donchianLower[profitTargetBarShift + 1] != _donchianLower[profitTargetBarShift]
						&& _donchianLower[profitTargetBarShift + 1] != 0){
						// Increase profit target bar shift index.
						profitTargetBarShift++;
					}
					// Generate profit target order one tick below flat lower donchian level while assuming that a position sizing script will assign the quantity.
					BrokerLimit(C_ActionType.BUY_TO_COVER, 0, C_TIF.GTC, _donchianLower[profitTargetBarShift] - SymbolTickSize(), "Profit target");
				}
			}
			
			// Check whether a position exists in the underlying symbol and Entry 2 is allowed.
			if (PositionExists(C_PositionStatus.OPEN) && _enableEntry2) {
				// Check whether the Entry 2 entry order has been fired.
				if (!_entry2Fired) {
					// Check whether the underlying symbol trades at or above the upper donchian level and a short position already exists.
					if(DataHigh() >= _donchianUpper[0] && PositionExistsInDirection(C_PositionStatus.OPEN, C_Direction.SHORT_SIDE)){
						// Generate a sell market order for Entry 2 while assuming that a position sizing script will assign the quantity.
						BrokerMarket(C_ActionType.SELL_SHORT, 0, C_TIF.GTC, "Entry 2");
						// Record that the Entry 2 entry order has been fired.
						_entry2Fired = true;
					// Check whether the underlying symbol trades at or below the lower donchian level and a long position already exists.
					} else if(DataLow() <= _donchianLower[0] && PositionExistsInDirection(C_PositionStatus.OPEN, C_Direction.LONG_SIDE)){
						// Generate a buy market order for Entry 2 while assuming that a position sizing script will assign the quantity.
						BrokerMarket(C_ActionType.BUY, 0, C_TIF.GTC, "Entry 2");
						// Record that the Entry 2 entry order has been fired.
						_entry2Fired = 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) {
			// Check whether the order is executed.
			if(status == C_Status.EXECUTED){
				// Check whether it is an exit order.
				if(OrderActionType(orderIndex) == C_ActionType.SELL || OrderActionType(orderIndex) == C_ActionType.BUY_TO_COVER){
					// Create an array to hold all pending orders for the underlying symbol.
					int [] pendingOrders = OrderByStatus(C_Status.PENDING);
					// Cancel each pending order for the underlying symbol.
					foreach(int pendingOrder in pendingOrders)
						BrokerCancelOrder(pendingOrder, OrderComment(pendingOrder)+" - No longer needed");
					// Reset variable that holds whether the Entry 2 entry order has been fired back to false.
					_entry2Fired = false;
				} else {
					// Check whether the order is an Entry 2 entry order.
					if (OrderComment(orderIndex).Contains("Entry 2")) {
						// Create an array to hold all pending orders for the underlying symbol.
						int[] pendingOrders = OrderByStatus(C_Status.PENDING);
						// Update pending stop loss and profit target orders with share quantity of Entry 2 entry order.
						foreach (int pendingOrder in pendingOrders)
							if (OrderPriceType(pendingOrder) == C_OrderType.LIMIT || OrderPriceType(pendingOrder) == C_OrderType.STOP)
								BrokerModifyOrder(pendingOrder, OrderQuantityRemaining(pendingOrder) + OrderQuantityFilled(orderIndex), -1, -1);
					}
				}
			}
		}
#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) {
			// OnPositionUpdate Content
		}
#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
	}
}