Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

This strategy was originally developed and discussed by Domenico D’Errico in the

June 2017 issue of “Technical Analyses of Stocks & Commodities.”

Trading Rules:
Long Entry: A buy market order is generated when the overnight volume is greater
than the SMA of overnight volume, price trades above the High price made in the
first hour of the regular trading session, and there is at least 30 minutes
remaining in the regular trading session.
Long Exit: A sell market order is generated one minute prior to the close of the
regular trading session.

Short Entry: A sell short market order is generated when the overnight volume is
greater than the SMA of overnight volume, price trades below the Low price made in
the first hour of the regular trading session, and there is at least 30 minutes
remaining in the regular trading session.
Short Exit: A buy market order is generated one minute prior to the close of the
regular trading session.

#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_6112_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="overnightStartTime" type="Integer" default="0" min="0"
max="2400">The start of the overnight period in 24-hr hhmm notation in the time
zone of the underlying symbol.</param>
## <param name="tradingStartTime" type="Integer" default="830" min="0"
max="2400">The start of the trading session in 24-hr hhmm notation in the time zone
of the underlying symbol.</param>
## <param name="tradingEndTime" type="Integer" default="1515" min="0"
max="2400">The end of the trading session in 24-hr hhmm notation in the time zone
of the underlying symbol.</param>
## <param name="enableVolatilityBiasFilter" type="Boolean"
default="True">Indicates whether to enable the volatility bias filter.</param>
## <param name="overnightVolumeSMAPeriods" type="Integer" default="5"
min="1">The number of periods used to calculate overnight volume SMA.</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>
def OnInitialize(self,
overnightStartTime,
tradingStartTime,
tradingEndTime,
enableVolatilityBiasFilter,
overnightVolumeSMAPeriods,
enableShorting,
enableLonging):
# Set the script parameters to script variables.
self._enableVolatilityBiasFilter = enableVolatilityBiasFilter
self._overnightVolumeSMAPeriods = overnightVolumeSMAPeriods
self._enableShorting = enableShorting
self._enableLonging = enableLonging
# Calculate the hour value of the trading start time.
self._tradingStartTimeHour = tradingStartTime / 100
# Calculate the minute value of the trading start time.
self._tradingStartTimeMinute = tradingStartTime % 100
# Calculate the hour value of the trading end time.
self._tradingEndTimeHour = tradingEndTime / 100
# Calculate the minute value of the trading end time.
self._tradingEndTimeMinute = tradingEndTime % 100
# Create for holding the overnight volume values.
self._overnightVolumeValues = []
# Set the time zone to the time zone of the underlying symbol.
DateTimeSetTimeZone(SymbolTimeZone())
# Remove all of the indicators from the chart so that we don't get
duplicates.
ChartIndicatorRemoveAll(SymbolIndex())
# Create the overnight volume indicator.
self._overnightVolume = IndicatorOV(self, SymbolIndex(),
overnightStartTime, tradingStartTime)
# Plot the indicator on the underlying symbol's chart.
indicatorItemID = ChartIndicatorPlot(SymbolIndex(), self._overnightVolume,
"Overnight Volume", - 1, 2)
# Set the indicator pen.
ChartIndicatorSetPenByIndex(SymbolIndex(), indicatorItemID, 0,
C_Color.LIGHT_BLUE, C_DashStyle.SOLID, 1)
# Set the indicator style.
ChartIndicatorSetPlotStyle(SymbolIndex(), indicatorItemID,
C_PlotStyle.BOX_ZERO)
# Create a variable to hold whether the collection of overnight
volume values has been initialized.
self._initiallyLoaded = False
# Create a variable to hold whether a volatility bias has been detected.
self._volatilityBiasDetected = False
# Create a variable to hold whether a closing order has been fired but not
yet filled.
self._waitingToClose = 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):
# Create a variable to hold the start of the current day's trading
session.
tradingStartTime = DateTimeCreate(DateTimeYear(DateTimeCurrent()),
DateTimeMonth(DateTimeCurrent()), DateTimeDay(DateTimeCurrent()),
self._tradingStartTimeHour, self._tradingStartTimeMinute, 0)
# Create a variable to hold the end of the current day's trading
session.
tradingEndTime = DateTimeCreate(DateTimeYear(DateTimeCurrent()),
DateTimeMonth(DateTimeCurrent()), DateTimeDay(DateTimeCurrent()),
self._tradingEndTimeHour, self._tradingEndTimeHour, 0)

# Initialize the collection of overnight volume values if necessary.


self.InitializeIfNecessary(tradingStartTime)
# Check whether it is currently the period to monitor the high and
low values.
if tradingStartTime < DateTimeCurrent() and DateTimeCurrent() <=
DateTimeAddMinutes(tradingStartTime, 60):
# Check whether it is time to calculate the overnight volume
SMA and check for a volatility bias.
if DataEndDateTime(1) <= tradingStartTime and DataIsComplete(0):
# Reset the value of the high during the first hour of
trading.
self._firstHourHigh = -float("inf")
# Reset the value of the low during the first hour of
trading.
self._firstHourLow = float("inf")
# Record whether a volatility bias has occurred
provided that enought time has passed to calculate the overnight volume SMA.
self._volatilityBiasDetected = self._overnightVolume[0] >
(sum(self._overnightVolumeValues) / len(self._overnightVolumeValues)) \
if len(self._overnightVolumeValues)
== self._overnightVolumeSMAPeriods else False
# Add the most recent overnight volume value to the
collection of values.
self._overnightVolumeValues.append(self._overnightVolume[0])
# Check whether the collection of volume values is
greater than the SMA lookback period.
if len(self._overnightVolumeValues) >
self._overnightVolumeSMAPeriods:
# Remove the oldest overnight volume value.
self._overnightVolumeValues.pop(0)

# Check whether a volatility bias has been detected or the


volatility bias filter is disabled.
if (self._enableVolatilityBiasFilter and self._volatilityBiasDetected)
or not self._enableVolatilityBiasFilter:
# Record a new high if the high of the current bar is
greater than the current high of the first hour of trading.
self._firstHourHigh = max(DataHigh(0), self._firstHourHigh)
# Record a new low if the low of the current bar is
less than the current low of the first hour of trading.
self._firstHourLow = min(DataLow(0), self._firstHourLow)
# Check whether it is after the first hour of trading but before the
cutoff time for sending entry orders.
elif DateTimeAddMinutes(tradingStartTime, 60) < DateTimeCurrent() and
DateTimeCurrent() <= DateTimeAddMinutes(tradingEndTime, -30):
# Check whether a volatility bias has been detected or the
volatility bias filter is disabled and there is not already an open position nor
pending order.
if ((self._enableVolatilityBiasFilter and self._volatilityBiasDetected)
or not self._enableVolatilityBiasFilter) and not
PositionExists(C_PositionStatus.OPEN) and not OrderExists(C_Status.PENDING, None):
# Check whether the underlying symbol trades below the
low of the first hour of trading.
if self._enableShorting and DataLow(0) < self._firstHourLow:
# 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.DAY, "Time to
sell short")
# Check whether the underlying symbol trades above the
high of the first hour of trading.
elif self._enableLonging and self._firstHourHigh < DataHigh(0):
# Generate a buy market order while assuming
that a position sizing script will assign the quantity.
BrokerMarket(C_ActionType.BUY, 0, C_TIF.DAY, "Time to buy")

# Check whether it is time to close open positions, an open position


exists, and the strategy has not already requested that the open position be
closed.
elif DateTimeCurrent() >= DateTimeAddMinutes(tradingEndTime, -1) and
PositionExists(C_PositionStatus.OPEN) and not self._waitingToClose:
# Close the open position.
BrokerClosePosition("Time to close.")
# Record that the strategy is waiting for the position to be
closed.
self._waitingToClose = 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):
# OnOrderUpdate Content
pass
#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):
# 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.
self._waitingToClose = False
#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

def InitializeIfNecessary(self, tradingStartTime):


# Check whether it is necessary to initialize the overnight volume values
and it has not already been done.
if (StrategyMode() == C_StrategyMode.LIVE or StrategyMode() ==
C_StrategyMode.PLAYBACK) and not self._initiallyLoaded:
# Check whether it is past the overnight window.
if tradingStartTime < DateTimeCurrent():
# Add the most recent overnight volume value to the collection
of values.
self._overnightVolumeValues.append(self._overnightVolume[0])
# Check whether the strategy needs to go back in time to fill up the
collection of overnight volume values.
if len(self._overnightVolumeValues) != self._overnightVolumeSMAPeriods:
# Create a variable the hold the bar shift index.
tempBarShift = 1
# Iterate backwards until the collection is full or there is
no more volume history.
while len(self._overnightVolumeValues) <
self._overnightVolumeSMAPeriods and self._overnightVolume[tempBarShift] != 0:
# Check whether the overnight volume of the current
barshift is greater than the overnight volume one barshift into the future.
if self._overnightVolume[tempBarShift] >
self._overnightVolume[tempBarShift - 1]:
# Add the current overnight volume value to the
front of the collection.
self._overnightVolumeValues.insert(0,
self._overnightVolume[tempBarShift])
# Increment the bar shift variable.
tempBarShift = tempBarShift + 1

# Check whether it is past the time to check for a volatility bias.


if tradingStartTime < DateTimeCurrent():
# Record whether a volatility bias has occurred provided that
the collection of overnight volume values is full.
self._volatilityBiasDetected = self._overnightVolume[0] >
(sum(self._overnightVolumeValues) / len(self._overnightVolumeValues)) \
if len(self._overnightVolumeValues)
== self._overnightVolumeSMAPeriods else False
# Check whether a volatility bias has been detected or the
volatility bias filter is disabled.
if (self._enableVolatilityBiasFilter and
self._volatilityBiasDetected) or not self._enableVolatilityBiasFilter:
# Initialize the first hour high.
self._firstHourHigh = -float("inf")
# Initialize the first hour low.
self._firstHourLow = float("inf")
# Create a variable the hold the bar shift index.
barShift = 1
# Iterate backwards until the the start time of trading
is reached.
while tradingStartTime < DataEndDateTime(barShift):
# Check whether it is currently the period to
monitor the high and low values.
if DataEndDateTime(barShift) <=
DateTimeAddMinutes(tradingStartTime, 60):
# Record a new high if the high of the
current bar is greater than the current high of the first hour of trading.
self._firstHourHigh = max(DataHigh(barShift),
self._firstHourHigh)
# Record a new low if the low of the
current bar is less than the current low of the first hour of trading.
self._firstHourLow = min(DataLow(barShift),
self._firstHourLow)
# Increment the bar shift variable.
barShift = barShift + 1
# Record that the overnight volume values have been initialized.
self._initiallyLoaded = True

You might also like