Short X Symbols for Y Bars

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

namespace ScriptCode {
	/// <summary>
	/// This is a generalized short-only trading strategy that uses the specified Signal to determine when to trade in the underlying symbols. 
	/// Signals that can be used in this strategy include the Indicator Negative Crossover signal and Indicator Overbought signal. 
	/// 
	/// The strategy goes short the specified number of symbols with the lowest Signal values and rebalances after holding for the specified number of bars.
	/// 
	/// Trading Rules: 
	/// 
	/// Short Entry: A sell short market order is generated when the symbol satisfies the Signal value cutoff criteria.
	/// Short Exit: A buy-to-cover market order is generated when the position has been held for the specified number of bars.
	/// </summary>
	public partial class MyMultiSymbolTradingStrategy : MultiSymbolTradingStrategyScriptBase  // NEVER CHANGE THE CLASS NAME 
	{
#region Variables
		// Use for holding user specified signals indexed by symbol index.
		private Signal[] _sortSignal;
		// The number of symbols to hold.
		private int _holdSymbols;
		// The number of bars to hold the selected symbols.
		private int _holdBars;
		// Use for counting the number of bars the current open positions have been held.
		private int _heldBars;
		// Use for holding the symbol signal values <symbol index, latest signal value for symbol>.
		private List<Tuple<int, double>> _symbolSignals;
		// Indicates whether the strategy is currently in sell mode or buy mode.
		private bool _isSellMode;
#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="sortSignal" type="Signal" default="" min="" max="">The signal to use for sorting the symbols.</param>
		/// <param name="holdSymbols" type="Integer" default="10" min="1" max="10000">The number of symbols to hold.</param>
		/// <param name="holdBars" type="Integer" default="30" min="1" max="10000000">The number of bars to hold the selected symbols.</param>
		public void OnInitialize(
			 Signal sortSignal,
			 int holdSymbols,
			 int holdBars) {
			// Create an array large enough to hold a single signal for each symbol.
			_sortSignal = new Signal[SymbolCount()];
			// Iterate over all of the symbol indexes.
			for (int symbolIndexItem = 0; symbolIndexItem < _sortSignal.Length; symbolIndexItem++) {
				// Create a copy for the current symbol index.
				_sortSignal[symbolIndexItem] = SignalSetSymbol(sortSignal, symbolIndexItem);
				// Plot the signal on the symbol's chart.
				ChartSignalPlot(symbolIndexItem, _sortSignal[symbolIndexItem], "", - 1, 1);
			}
			// Set the script parameter to a variable.
			_holdSymbols = holdSymbols;
			// Set the script parameter to a variable.
			_holdBars = holdBars;
			// Create for holding the symbol signal values <symbol index, latest signal value for symbol>.
			_symbolSignals = new List<Tuple<int, double>>();
		}
#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.</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>
		/// <param name="isLastSymbol" type="Boolean">Indicates whether this is the last symbol to be updated for the current bar. 
		/// The parameter is valid when the bars for different symbols have matching timestamps, e.g. 1m, 5m, 1d, 1w, etc.</param>
		public override void OnBarUpdate(
			int symbolIndex,
			int dataSeries,
			int completedBars,
			bool isLastSymbol) {
			// Check whether all of the symbols have been updated.
			if (isLastSymbol) {
				// Check whether the strategy is in sell mode.
				if (_isSellMode) {
					// Increase the number of held bars.
					_heldBars++;
					// Check whether the number of held bars matches the specified number of hold bars.
					if (_heldBars == _holdBars) {
						// Get the open position indexes for all of the symbols.
						int[] openPositionIndexes = PositionByStatusAll(C_PositionStatus.OPEN);
						// Iterate over the open positions for all of the symbols while closing them one by one.
						for (int i = 0; i < openPositionIndexes.Length; i++) {
							// Swith the API functions to work with the position's symbol.
							SymbolSwitch(PositionSymbolIndex(openPositionIndexes[i]));
							// Check whether the data bar isn't missing.
							if(!DataIsMissing()){
								// Buy to cover the current symbol.
								BrokerMarket(C_ActionType.BUY_TO_COVER, PositionCurrentQuantity(openPositionIndexes[i]), C_TIF.DAY, "");
							}
						}
						// Clear the number of held bars.
						_heldBars = 0;
						// Set to indicate that the strategy is currently is buy mode.
						_isSellMode = false;
					}
				}
				// The strategy is in buy mode.
				else {
					// Clear the list of signals.
					_symbolSignals.Clear();
					// Iterate over the symbols.
					for (int symIndex = 0; symIndex < SymbolCount(); symIndex++) {
						// Get the signal value for the latest bar of the current symbol.
						_symbolSignals.Add(new Tuple<int, double>(symIndex, _sortSignal[symIndex][0]));
					}
					// Sort the symbols by ascending signal values so that those with the lower signal values come first. Then sort any symbols with the same value by symbol index.
					_symbolSignals = _symbolSignals.OrderBy(x => x.Item2).ThenBy(x => x.Item1).ToList();
					// Iterate over the number of symbols to hold.
					for (int i = 0; i < _symbolSignals.Count && i < _holdSymbols; i++) {
						// Swith the API functions to work with the signal's symbol.
						SymbolSwitch(_symbolSignals[i].Item1);
						// Check to ensure there is not an open position or pending order and if market data is available for the current symbol.
						if(!PositionExists(C_PositionStatus.OPEN) && !OrderExists(C_Status.PENDING) && !DataIsMissing()){
							// Short the current symbol while assuming that a position sizing script will assign the quantity.
							BrokerMarket(C_ActionType.SELL_SHORT, 0, C_TIF.DAY, "");
						}
					}
					// Set to indicate that the strategy is currently is sell mode.
					_isSellMode = true;
				}
			}
		}
#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) {
			// OnPositionUpdate Content
		}
#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 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 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 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 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 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
	}
}