Reversal Combined with Volatility Effect in Stocks

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

namespace ScriptCode {
	/// <summary>
	/// This is a reversal and volatility strategy that buys and sells symbols depending on where they fall relative to one another along their return and volatility dimensions. 
	/// Reversal strategies are widely used and effectively invest in symbols with poor recent performance with the intent that their performance will reverse. 
	/// By adding another criteria for volatility (as measured by standard deviation of price), this strategy seeks to achieve above average risk-adjusted returns.
	/// 
	/// To determine which symbols to invest in, the strategy first sorts each symbol by its return. 
	/// From the group of symbols with returns below the specified bottom return percentile, the strategy will long the symbols with the lowest volatility. 
	/// From the group of symbols with returns above the specified top return percentile, the strategy will short the symbols with the lowest volatility.
	/// 
	/// This strategy uses a laddered rebalancing schedule so only a portion of all symbols held are rebalanced at the end of each holding period. 
	/// The portion rebalanced depends on the specified total number of symbols the strategy can invest in as well as the specified holding period.
	/// As an example, if the strategy can invest in 10 symbols and the holding period is 5 bars, 2 symbols are rebalanced at the end of each holding period.
	/// 
	/// Combining the reversal effect and volatility effect was studied by Jason Wei of the University of Toronto in the paper titled, “Do momentum and reversals coexist?”
	/// 
	/// Trading Rules: 
	/// 
	/// Long Entry: A buy market order is generated when the symbol satisfies the return percentile and volatility cutoff criteria for long entries and the return is negative.
	/// Long Exit: A sell market order is generated when the position has been held for the specified number of bars.
	/// 
	/// Short Entry: A sell short market order is generated when the symbol satisfies the return percentile and volatility cutoff criteria for short entries and the return is positive.
	/// Short Exit: A buy 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 return indicators indexed by symbol index.
		private Indicator [] _return;
		// Use for holding volatility indicators indexed by symbol index.
		private Indicator [] _volatility;
		// Create for holding the symbol return and volatility values <symbol index, latest return value for symbol, latest volatility value for symbol>.
		private List<Tuple<int, double, double>> _symbolReturnVolatilityValues;
		// The number of periods used to calculate the return.
		private int _returnPeriods;
		// The number of daily periods used to calculate the volatility.
		private int _volatilityPeriods;
		// The number of symbols to hold for each trade direction (long and short).
		private int _holdSymbols;
		// The number of bars to hold the selected symbols.
		private int _holdBars;
		// 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;
		// The percent distance from the entry price in which to place a stop loss order.
		private double _stopLoss;
		// The percent distance from the entry price in which to place a take profit order. 
		private double _takeProfit;
		// The minimum price a symbol can have to be eligible for trading.
		private double _minimumPrice;
		// The number of symbols to invest each close.
		private int _symbolsToInvestEachClose;
		// The number of symbols to invest each close with a remainder.
		private int _symbolsToInvestWithRemainder;
		// The percentile on the return distribution below which stocks are not considered for trading.
		private double _returnPercentileCutoff;
		// The total numbers of bars processed.
		private int _barsProcessed;
		// The bar in the holding period cycle when the number of symbols to invest is switched from _symbolsToInvestWithRemainder to _symbolsToInvestEachClose.
		private int _barToSwitchNumberOfSymbols;
		// Create for holding the number of bars a position has been held for each symbol.
		private int [] _heldBars;
#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="returnPeriods" type="Integer" default="6" min="1" max="10000000">The number of periods used to calculate the return.</param>
		/// <param name="volatilityPeriods" type="Integer" default="252" min="1" max="10000000">The number of daily periods used to calculate the volatility.</param>
		/// <param name="holdSymbols" type="Integer" default="10" min="1" max="10000">The number of symbols to hold for each trade direction (long/short).</param>
		/// <param name="holdBars" type="Integer" default="1" min="1" max="10000000">The number of bars to hold the selected symbols.</param>
		/// <param name="returnPercentileCutoff" type="Double" default="20" min="1" max="100">The percentile on the return distribution below which stocks are not considered for trading.</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="stopLoss" type="Double" default="0">The percent distance from the entry price in which to place a stop loss order. (0 to ignore). </param>
		/// <param name="takeProfit" type="Double" default="0">The percent distance from the entry price in which to place a take profit order. (0 to ignore). </param>
		/// <param name="minimumPrice" type="Double" default="5">The minimum price a symbol can have to be eligible for trading.</param>
		public void OnInitialize(
			int returnPeriods,
			int volatilityPeriods,
			int holdSymbols,
			int holdBars,
			double returnPercentileCutoff,
			bool enableShorting,
			bool enableLonging,
			double stopLoss,
			double takeProfit,
			double minimumPrice) {
			// Set the script parameters to script variables.
			_returnPeriods = returnPeriods;	
			_volatilityPeriods = volatilityPeriods;	
			_holdSymbols = holdSymbols;
			_holdBars = holdBars;
			_enableShorting = enableShorting;
			_enableLonging = enableLonging;
			_stopLoss = stopLoss;
			_takeProfit = takeProfit;
			_minimumPrice = minimumPrice;
			_returnPercentileCutoff = returnPercentileCutoff;
			// Calculate number of symbols to invest each close.
			_symbolsToInvestEachClose = holdSymbols / holdBars;
			// Calculate the number of symbols to invest if _holdSymbols is not a multiple of _holdBars.
			_symbolsToInvestWithRemainder = _symbolsToInvestEachClose + 1;
			// Record the bar in the holding period cycle when number of symbols to invest is switched from _symbolsToInvestWithRemainder to _symbolsToInvestEachClose.
			_barToSwitchNumberOfSymbols = holdSymbols % holdBars;
			// Create an array large enough to hold a single indicator for each symbol.
			_return = new Indicator[SymbolCount()];
			// Create an array large enough to hold a single indicator for each symbol.
			_volatility = new Indicator[SymbolCount()];
			// Iterate over all of the symbol indexes.
			for (int symbolIndex = 0; symbolIndex < SymbolCount(); symbolIndex++) {
				// Create a copy for the current symbol index.
				_return[symbolIndex] = IndicatorROC(IndicatorCLOSE(symbolIndex), returnPeriods);
			}
			// Switch to the daily data series.
			DataSeriesSwitch(1);
			// Iterate over all of the symbol indexes.
			for (int symbolIndex = 0; symbolIndex < SymbolCount(); symbolIndex++) {
				// Create a copy for the current symbol index.
				_volatility[symbolIndex] = IndicatorSDEV(IndicatorCLOSE(symbolIndex), volatilityPeriods);
			}
			// Switch back to the original data series.
			DataSeriesSwitch(0);
			// Create for holding the symbol return and volatility values <symbol index, latest return value for symbol, latest volatility value for symbol>.
			_symbolReturnVolatilityValues = new List<Tuple<int, double, double>>();
			// Create for holding the number of bars a position has been held for each symbol.
			_heldBars = new int[SymbolCount()];
		}
#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){
				// Switch the API functions to work with the current symbol.
				SymbolSwitch(symbolIndex);
				// Check whether the bar is complete.
				if (DataIsComplete()) {
					// Clear the list of return and volatility values.
					_symbolReturnVolatilityValues.Clear();
					// Create a list to hold the symbols indices of positions that were just closed.
					List<double> symbolIndicesJustClosed = new List<double>();
					// Iterate through each symbol.
					for (int symIndex = 0; symIndex < SymbolCount(); symIndex++) {
						// Switch the API functions to work with the current symbol.
						SymbolSwitch(symIndex);
						// Check whether a position exists.
						if(PositionExists(C_PositionStatus.OPEN)){
							// Increase the number of held bars for the current index.
							_heldBars[symIndex]++;
							// Check whether it is time to close the open position.
							if(_heldBars[symIndex] == _holdBars){
								// Close the open position.
								BrokerClosePosition("Time to close");
								// Record the symbol index of the position that just closed.
								symbolIndicesJustClosed.Add(symIndex);
							}
						}
						// Switch to the daily data series.
						DataSeriesSwitch(1);
						// Record the oldest close needed to calculate volatility.
						double volatilityClose = DataClose(_volatilityPeriods);
						// Switch back to the original data series.
						DataSeriesSwitch(0);
						// Check whether the symbol is trading above the minimum price, there is enough history to calculate the return and volatility, and the symbol is active.
						if(DataClose() > _minimumPrice && DataClose(_returnPeriods) != 0 && volatilityClose != 0 && SymbolIsAvailable()){
							// Get the return and volatility values for the latest bar of the current symbol.
							_symbolReturnVolatilityValues.Add(new Tuple<int, double, double>(symIndex, _return[symIndex][0], _volatility[symIndex][0]));
						}
					}
					// Sort the symbols by ascending return values so that those with lower values come first. Then sort any symbols with the same signal value by symbol index.
					_symbolReturnVolatilityValues = _symbolReturnVolatilityValues.OrderBy(x => x.Item2).ThenBy(x => x.Item1).ToList();
					// Calculate the number of symbols to sort by return.
					int symbolCutoff = (int)(_symbolReturnVolatilityValues.Count() * _returnPercentileCutoff / 100);
					// Create a list of long symbols that satisfy the return percentile cutoff.
					List<Tuple<int, double, double>> longLowVolatilitySymbols = _symbolReturnVolatilityValues.GetRange(0, symbolCutoff);
					// Sort the symbols by ascending volatility values so that those with lower values come first.
					longLowVolatilitySymbols.Sort((x, y) => x.Item3.CompareTo(y.Item3));
					// Calculate the number of orders to send for the current close.
					int ordersToSend = (_barsProcessed % _holdBars < _barToSwitchNumberOfSymbols) ? _symbolsToInvestWithRemainder : _symbolsToInvestEachClose;
					// Check whether the trading strategy can go long.
					if(_enableLonging){
						// Create a variable to hold the number of buy orders sent.
						int buyOrdersSent = 0;
						// Iterate over the number of long symbols with negative returns until enough orders are generated.
						for (int i = 0; i < longLowVolatilitySymbols.Count && buyOrdersSent < ordersToSend && longLowVolatilitySymbols[i].Item2 < 0; i++) {
							// Switch the API functions to work with the current symbol.
							SymbolSwitch(longLowVolatilitySymbols[i].Item1);
							// Check to ensure there is not an open position or an open position was just closed.
							if(!PositionExists(C_PositionStatus.OPEN) && !OrderExists(C_Status.PENDING) || symbolIndicesJustClosed.Contains(longLowVolatilitySymbols[i].Item1)){
								// Buy the current symbol while assuming that a position sizing script will assign the quantity
								int orderIndex = BrokerMarket(C_ActionType.BUY, 0, C_TIF.DAY, "Buy to open.");
								// Increase the number of buy orders sent.
								buyOrdersSent++;
								// Set a stop loss on the order. 
								BrokerSetStopLossPercent(orderIndex, _stopLoss, true, "Stop loss");
								// Set a take profit on the order. 
								BrokerSetTakeProfitPercent(orderIndex, _takeProfit, true, "Profit target");
							}
						}
					}
					// Sort the symbols by descending return values so that those with higher values come first. Then sort any symbols with the same signal value by symbol index.
					_symbolReturnVolatilityValues = _symbolReturnVolatilityValues.OrderByDescending(x => x.Item2).ThenBy(x => x.Item1).ToList();
					// Create a list of short symbols that satisfy the return percentile cutoff.
					List<Tuple<int, double, double>> shortLowVolatilitySymbols = _symbolReturnVolatilityValues.GetRange(0, symbolCutoff);
					// Sort the symbols by ascending volatility values so that those with lower values come first.
					shortLowVolatilitySymbols.Sort((x, y) => x.Item3.CompareTo(y.Item3));
					// Check whether the trading strategy can go short.
					if(_enableShorting){
						// Create a variable to hold the number of sell orders sent.
						int sellOrdersSent = 0;
						// Iterate over the number of short symbols with positive returns until enough orders are generated.
						for (int i = 0; i < shortLowVolatilitySymbols.Count && sellOrdersSent < ordersToSend && shortLowVolatilitySymbols[i].Item2 > 0; i++) {
							// Switch the API functions to work with the current symbol.
							SymbolSwitch(shortLowVolatilitySymbols[i].Item1);
							// Check to ensure there is not an open position or an open position was just closed.
							if(!PositionExists(C_PositionStatus.OPEN) && !OrderExists(C_Status.PENDING) || symbolIndicesJustClosed.Contains(shortLowVolatilitySymbols[i].Item1)){
								// Sell short the current symbol while assuming that a position sizing script will assign the quantity
								int orderIndex = BrokerMarket(C_ActionType.SELL_SHORT, 0, C_TIF.DAY, "Sell short to open.");
								// Increase the number of sell orders sent.
								sellOrdersSent++;
								// Set a stop loss on the order. 
								BrokerSetStopLossPercent(orderIndex, _stopLoss, true, "Stop loss");
								// Set a take profit on the order. 
								BrokerSetTakeProfitPercent(orderIndex, _takeProfit, true, "Profit target");
							}
						}
					}
					// Increase the number of bars processed.
					_barsProcessed++;
				}
			}
		}
#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 underlying symbol index of the order</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 underlying symbol index of the position</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) {
			// Switch the API functions to work with the current symbol.
			SymbolSwitch(symbolIndex);
			// 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[symbolIndex] = false;
				// Reset the number of bars held for the current symbol.
				_heldBars[symbolIndex] = 0;
			}
		}
#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 update 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 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
	}
}