#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_6104_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="indicatorShortPeriods" type="Integer" default="50">The number of periods in the short-term indicator.</param>
## <param name="indicatorMediumPeriods" type="Integer" default="100">The number of periods in the medium-term indicator.</param>
## <param name="indicatorLongPeriods" type="Integer" default="200">The number of periods in the long-term indicator.</param>
## <param name="priceActionConvergencePeriods" type="Integer" default="3" min="0">The number of periods used to determine if the price action has converged with the moving averages.</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 closes above the long-term indicator
## for long trades and closes below the long-term indicator for short trades.</param>
def OnInitialize(self,
indicatorShortPeriods,
indicatorMediumPeriods,
indicatorLongPeriods,
priceActionConvergencePeriods,
enableShorting,
enableLonging,
enableEntry2):
# Set the script parameters to script variables.
self._enableShorting = enableShorting
self._enableLonging = enableLonging
self._enableEntry2 = enableEntry2
# Remove all of the indicators from the chart so that we don't get duplicates.
ChartIndicatorRemoveAll(SymbolIndex())
# Create the short-term EMA indicator over the bar close indicator of the underlying symbol.
self._indicatorShort = IndicatorEMA(self, IndicatorCLOSE(self, SymbolIndex()), indicatorShortPeriods)
# Plot the indicator on the underlying symbol's chart.
indicatorShortItemID = ChartIndicatorPlot(SymbolIndex(), self._indicatorShort, "", - 1, 1)
# Set the indicator pen.
ChartIndicatorSetPenByIndex(SymbolIndex(), indicatorShortItemID, 0, C_Color.LAWN_GREEN, C_DashStyle.SOLID, 2)
# Set the indicator style.
ChartIndicatorSetPlotStyle(SymbolIndex(), indicatorShortItemID, C_PlotStyle.LINE)
# Create the medium-term EMA indicator over the bar close indicator of the underlying symbol.
self._indicatorMedium = IndicatorEMA(self, IndicatorCLOSE(self, SymbolIndex()), indicatorMediumPeriods)
# Plot the indicator on the underlying symbol's chart.
indicatorMediumItemID = ChartIndicatorPlot(SymbolIndex(), self._indicatorMedium, "", - 1, 1)
# Set the indicator pen.
ChartIndicatorSetPenByIndex(SymbolIndex(), indicatorMediumItemID, 0, C_Color.ORANGE, C_DashStyle.SOLID, 2)
# Set the indicator style.
ChartIndicatorSetPlotStyle(SymbolIndex(), indicatorMediumItemID, C_PlotStyle.LINE)
# Create the long-term EMA indicator over the bar close indicator of the underlying symbol.
self._indicatorLong = IndicatorEMA(self, IndicatorCLOSE(self, SymbolIndex()), indicatorLongPeriods)
# Plot the indicator on the underlying symbol's chart.
indicatorLongItemID = ChartIndicatorPlot(SymbolIndex(), self._indicatorLong, "", - 1, 1)
# Set the indicator pen.
ChartIndicatorSetPenByIndex(SymbolIndex(), indicatorLongItemID, 0, C_Color.BLUE_VIOLET, C_DashStyle.SOLID, 2)
# Set the indicator style.
ChartIndicatorSetPlotStyle(SymbolIndex(), indicatorLongItemID, C_PlotStyle.LINE)
# Check whether it is necessary to create and graph the Bearish Double Death Cross pattern.
if self._enableShorting:
# Create the Bearish Double Death Cross pattern for the underlying symbol.
self._bearishDDCPattern = PatternDDDC(self, SymbolIndex(), indicatorShortPeriods, indicatorMediumPeriods, indicatorLongPeriods, priceActionConvergencePeriods)
# Plot the pattern on the underlying symbol's chart to highlight instances of the pattern.
ChartPatternPlot(SymbolIndex(), self._bearishDDCPattern, "Bearish Double Death Cross", -1, 1)
# Check whether it is necessary to create and graph the Bullish Double Death Cross pattern.
if self._enableLonging:
# Create the Bullish Double Death Cross pattern for the underlying symbol.
self._bullishDDCPattern = PatternUDDC(self, SymbolIndex(), indicatorShortPeriods, indicatorMediumPeriods, indicatorLongPeriods, priceActionConvergencePeriods)
# Plot the pattern on the underlying symbol's chart to highlight instances of the pattern.
ChartPatternPlot(SymbolIndex(), self._bullishDDCPattern, "Bullish Double Death Cross", -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 the strategy is allowed to short, the Bearish Double Death Cross holds true, and there are no open positions and pending orders.
if self._enableShorting and self._bearishDDCPattern[0] != 0 and not PositionExists(C_PositionStatus.OPEN) and not OrderExists(C_Status.PENDING, None):
# Generate a sell market order for Entry 1 while assuming that a position sizing script will assign the quantity.
BrokerMarket(C_ActionType.SELL_SHORT, 0, C_TIF.GTC, "Entry 1")
# Generate a stop loss order one tick above the medium-term indicator while assuming that a position sizing script will assign the quantity.
BrokerStop(C_ActionType.BUY_TO_COVER, 0, C_TIF.GTC, self._indicatorMedium[0] + SymbolTickSize(), "Stop loss")
# Check whether the strategy is allowed to long, the Bullish Double Death Cross holds true, and there are no open positions and pending orders.
if self._enableLonging and self._bullishDDCPattern[0] != 0 and not PositionExists(C_PositionStatus.OPEN) and not OrderExists(C_Status.PENDING, None):
# Generate a buy market order for Entry 1 while assuming that a position sizing script will assign the quantity.
BrokerMarket(C_ActionType.BUY, 0, C_TIF.GTC, "Entry 1")
# Generate a stop loss order one tick below the medium-term indicator while assuming that a position sizing script will assign the quantity.
BrokerStop(C_ActionType.SELL, 0, C_TIF.GTC, self._indicatorMedium[0] - SymbolTickSize(), "Stop loss")
# Check whether a position exists in the underlying symbol.
if PositionExists(C_PositionStatus.OPEN):
# Create variable to hold whether the pending profit target order exists.
profitTargetOrderExists = False
# Create an array to hold all pending orders for the underlying symbol.
pendingOrders = OrderByStatus(C_Status.PENDING)
# Iterate through all pending orders for the underlying symbol.
for pendingOrder in pendingOrders:
# Check whether the current order type is the same as that of a profit target order.
if OrderPriceType(pendingOrder) == C_OrderType.LIMIT:
# Record the existence of pending profit target order and exit the iteration.
profitTargetOrderExists = True
break
# Check whether there is currently a pending profit target order.
if not profitTargetOrderExists:
# Check whether the short-term indicator crosses below the long-term indicator.
if self._indicatorShort[0] < self._indicatorLong[0] and self._indicatorShort[1] >= self._indicatorLong[1]:
# Generate profit target order one tick above the high of the current bar while assuming that a position sizing script will assign the quantity.
BrokerLimit(C_ActionType.BUY_TO_COVER, 0, C_TIF.GTC, DataHigh(0) + SymbolTickSize(), "Profit target")
# Check whether the short-term indicator crosses above the long-term indicator.
elif self._indicatorShort[0] > self._indicatorLong[0] and self._indicatorShort[1] <= self._indicatorLong[1]:
# Generate profit target order one tick below the low of the current bar while assuming that a position sizing script will assign the quantity.
BrokerLimit(C_ActionType.SELL, 0, C_TIF.GTC, DataLow(0) - SymbolTickSize(), "Profit target")
# Check whether Entry 2 has been enabled and it has not already been generated.
if self._enableEntry2 and not self._entry2Fired:
# Check whether the underlying symbol closes below the long-term indicator and a short position already exists.
if DataClose(0) < self._indicatorLong[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 closes above the long-term indicator and a long position already exists.
elif DataClose(0) > self._indicatorLong[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 the order 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