Donchian Crawling Along Pattern - Python

Donchian Crawling Along Pattern - Python

#region Namespaces
# ---------- DON'T REMOVE OR EDIT THESE LINES -------------------
# These lines are required for integrating Python with our .NET platform.
import clr
clr.AddReference("Tickblaze.Model")
import ScriptCode
from TradingStrategyAPI import *
from AssemblyTradingStrategy_6103_ImportedScripts import *
# ---------------------------------------------------------------
#endregion

## <summary>
## Trading Strategy scripts are used for trading one symbol at a time such that each symbol gets its own strategy instance. 
## Common use-cases include momentum strategies, crossover strategies and overbought / oversold strategies, all of which need to evaluate only a single symbol at a time in order to make trading decisions.
## </summary>
class MyTradingStrategy(ScriptCode.TradingStrategyScriptBase):  # NEVER CHANGE THE CLASS NAME
    #region Variables
    # Variables Content
    #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>
    ## --------------------------------------------------------------------------------------------------
    ##                                 INSTRUCTIONS - PLEASE READ CAREFULLY
    ## --------------------------------------------------------------------------------------------------
    ## 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' (The 'long' data type can only be used for date/time representation)
    ## Set to "DateTimeArray" when the data type is 'long[]' (The 'long' data type can only be used for date/time representation)
    ## 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>
    def OnInitialize(self,
            donchianPeriods,
            patternLookback,
            percentNewHighLowsRequired,
            enableShorting,
            enableLonging,
            enableEntry2):
		# Set the script parameters to script variables. 
        self._enableShorting = enableShorting
        self._enableLonging = enableLonging
        self._enableEntry2 = enableEntry2
		
		# Create the upper donchian indicator for the underlying symbol.
        self._donchianUpper = IndicatorDCU(self, SymbolIndex(), donchianPeriods)
		# Create the lower donchian indicator for the underlying symbol.
        self._donchianLower = IndicatorDCL(self, SymbolIndex(), donchianPeriods)
		# Create the middle donchian indicator for the underlying symbol.
        self._donchianMiddle = IndicatorDIVC(self, IndicatorADD(self, self._donchianLower, self._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.
        donchianUpperItemID = ChartIndicatorPlot(SymbolIndex(), self._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.
        donchianMiddleItemID = ChartIndicatorPlot(SymbolIndex(), self._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.
        donchianLowerItemID = ChartIndicatorPlot(SymbolIndex(), self._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 self._enableShorting:
			# Create the Bearish Donchian Crawling Along pattern for the underlying symbol.
            self._bearishDCAPattern = PatternDDCA(self, SymbolIndex(), donchianPeriods, patternLookback, percentNewHighLowsRequired)
			# Plot the pattern on the underlying symbol's chart to highlight instances of the pattern.
            ChartPatternPlot(SymbolIndex(), self._bearishDCAPattern, "Bearish Donchian Crawling Along", -1, 1)
		# Check whether it is necessary to create and graph the Bullish Donchian Crawling Along pattern.
        if self._enableLonging:
			# Create the Bullish Donchian Crawling Along pattern for the underlying symbol.
            self._bullishDCAPattern = PatternUDCA(self, SymbolIndex(), donchianPeriods, patternLookback, percentNewHighLowsRequired)
			# Plot the pattern on the underlying symbol's chart to highlight instances of the pattern.
            ChartPatternPlot(SymbolIndex(), self._bullishDCAPattern, "Bullish Donchian Crawling Along", -1, 1)
        
        # Create a variable to hold whether the Entry 2 entry order has been fired.
        self._entry2Fired = False
    #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>
    def OnBarUpdate(self, symbolIndex, dataSeries, completedBars):
		# Check whether there are no open positions or pending orders.
        if self._enableLonging and not PositionExists(C_PositionStatus.OPEN) and not OrderExists(C_Status.PENDING, None):
			# Check if the Bullish Donchian Crawling Along pattern holds true.
            if self._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.
                stopLossBarShift = 0
				# Iterate backwards until lower donchian is flat and check that indicator is non-zero to avoid infinite loop.
                while self._donchianLower[stopLossBarShift + 1] != self._donchianLower[stopLossBarShift] and self._donchianLower[stopLossBarShift + 1] != 0:
					# Increase stop loss bar shift index.
                    stopLossBarShift = stopLossBarShift + 1
				# 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, self._donchianLower[stopLossBarShift] - SymbolTickSize(), "Stop loss")
				
				# Create a variable to hold the number of bar shifts used when finding profit target level.
                profitTargetBarShift = 0
				# Iterate backwards until upper donchian is flat and check that indicator is non-zero to avoid infinite loop.
                while self._donchianUpper[profitTargetBarShift + 1] != self._donchianUpper[profitTargetBarShift] and self._donchianUpper[profitTargetBarShift + 1] != 0:
					# Increase profit target bar shift index.
                    profitTargetBarShift = profitTargetBarShift + 1
				# 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, self._donchianUpper[profitTargetBarShift] + SymbolTickSize(), "Profit target")
		
		# Check whether there are no open positions or pending orders.
        if self._enableShorting and not PositionExists(C_PositionStatus.OPEN) and not OrderExists(C_Status.PENDING, None):
			# Check if the Bearish Donchian Crawling Along pattern holds true.
            if self._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.
                stopLossBarShift = 0
				# Iterate backwards until upper donchian is flat and check that indicator is non-zero to avoid infinite loop.
                while self._donchianUpper[stopLossBarShift + 1] != self._donchianUpper[stopLossBarShift] and self._donchianUpper[stopLossBarShift + 1] != 0:
					# Increase stop loss bar shift index.
                    stopLossBarShift = stopLossBarShift + 1
				# 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, self._donchianUpper[stopLossBarShift] + SymbolTickSize(), "Stop loss")
				
				# Create a variable to hold the number of bar shifts used when finding profit target level.
                profitTargetBarShift = 0
				# Iterate backwards until lower donchian is flat and check that indicator is non-zero to avoid infinite loop.
                while self._donchianLower[profitTargetBarShift + 1] != self._donchianLower[profitTargetBarShift] and self._donchianLower[profitTargetBarShift + 1] != 0:
					# Increase profit target bar shift index.
                    profitTargetBarShift = profitTargetBarShift + 1
				# 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, self._donchianLower[profitTargetBarShift] - SymbolTickSize(), "Profit target")
		
		# Check whether a position exists in the underlying symbol and Entry 2 is allowed.
        if PositionExists(C_PositionStatus.OPEN) and self._enableEntry2:
			# Check whether the Entry 2 entry order has been fired.
            if not self._entry2Fired:
				# Check whether the underlying symbol trades at or above the upper donchian level and a short position already exists.
                if DataHigh(0) >= self._donchianUpper[0] and 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.
                    self._entry2Fired = True
				# Check whether the underlying symbol trades at or below the lower donchian level and a long position already exists.
                elif DataLow(0) <= self._donchianLower[0] and 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.
                    self._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>
    def OnOrderFillUpdate(self, symbolIndex, orderIndex, orderFillIndex):
        # OnOrderFillUpdate Content
        pass
    #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>
    def OnOrderUpdate(self, symbolIndex, orderIndex, 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 or OrderActionType(orderIndex) == C_ActionType.BUY_TO_COVER:
				# Create an array to hold all pending orders for the underlying symbol.
                pendingOrders = OrderByStatus(C_Status.PENDING)
				# Cancel each pending order for the underlying symbol.
                for 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.
                self._entry2Fired = False
            else:
				# Check whether the order is an Entry 2 entry order.
                if "Entry 2" in OrderComment(orderIndex):
					# Create an array to hold all pending orders for the underlying symbol.
                    pendingOrders = OrderByStatus(C_Status.PENDING)
					# Update pending stop loss and profit target orders with share quantity of Entry 2 entry order.
                    for pendingOrder in pendingOrders:
                        if OrderPriceType(pendingOrder) == C_OrderType.LIMIT or 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>
    def OnPositionUpdate(self, symbolIndex, positionIndex, status):
        # OnPositionUpdate Content
        pass
    #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>
    def OnSessionUpdate(self, symbolIndex, status):
        # OnSessionUpdate Content
        pass
    #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>
    def OnNewsUpdate(self, symbolIndex, dateTime, title, message, type):
        # OnNewsUpdate Content
        # [NO_NEWS_UPDATES] - Delete this comment to enable news updates to this strategy.
        pass
    #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>
    def OnRSSUpdate(self, symbolIndex, dateTime, title, message, type):
        # OnRSSUpdate Content
        # [NO_RSS_UPDATES] - Delete this comment to enable RSS updates to this strategy.
        pass
    #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>
    def OnAlertUpdate(self, symbolIndex, dateTime, message, type):
        # OnAlertUpdate Content
        # [NO_ALERT_UPDATES] - Delete this comment to enable alert updates to this strategy.
        pass
    #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>
    def OnJournalUpdate(self, symbolIndex, dateTime, title, message, type):
        # OnJournalUpdate Content
        # [NO_JOURNAL_UPDATES] - Delete this comment to enable journal updates to this strategy.
        pass
    #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>
    def OnDataConnectionUpdate(self, symbolIndex, dateTime, message, type):
        # OnDataConnectionUpdate Content
        # [NO_DATA_CONNECTION_UPDATES] - Delete this comment to enable data connection updates to this strategy.
        pass
    #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>
    def OnBrokerConnectionUpdate(self, dateTime, message, type):
        # OnBrokerConnectionUpdate Content
        # [NO_BROKER_CONNECTION_UPDATES] - Delete this comment to enable broker connection updates to this strategy.
        pass
    #endregion

    #region OnShutdown
    ## <summary>
    ## This function is called when the script is shutdown.
    ## </summary>
    def OnShutdown(self):
        # OnShutdown Content
        pass
    #endregion