Trading Strategy#
strategies/*.py
Trading strategies are built as class objects, and must follow a few
simple rules to function properly with AutoTrader. At a minimum, a
strategy is required to have two methods. The first is the __init__
method,
which is used to instantiate the strategy. The second is the generate_signal
method, which contains the strategy logic and ouputs any trading signals which
may present themselves. Read more about these methods below and take a look at
the sample strategies in the
demo repository. It might
also be helpful to review the tutorial,
Building a Strategy.
Strategy Overview#
Tip
A strategy template can be found in the templates folder of the Github repository. You can also use the command line interface to download strategies from the repo.
The code block below provides some boilerplate for a strategy in
AutoTrader. The methods shown are the only things required
of a strategy. Note the differences in the arguments to the
generate_signal
method between periodic and continuous update
mode. Read about these modes in the
AutoTrader docs.
from autotrader import Order
class Strategy:
def __init__(self, parameters, data, instrument, **kwargs):
"""Define all attributes of the strategy.
"""
self.name = "Template Strategy"
self.data = data
self.params = params
self.instrument = instrument
# Define any indicators used in the strategy
...
# Construct indicators dict for plotting
self.indicators = {'Indicator Name': {'type': 'indicatortype',
'data': 'indicatordata'},}
def generate_signal(self, data):
"""Define strategy logic to determine entry signals.
Parameters
----------
data : pd.DataFrame|dict
The strategy data.
"""
# Example long market order
order = Order(direction=1)
return order
from autotrader import Order
class Strategy:
def __init__(self, parameters, data, instrument, **kwargs):
"""Define all attributes of the strategy.
"""
self.name = "Template Strategy"
self.data = data
self.params = params
self.instrument = instrument
# Define any indicators used in the strategy
...
# Construct indicators dict for plotting
self.indicators = {'Indicator Name': {'type': 'indicatortype',
'data': 'indicatordata'},}
def generate_signal(self, i):
"""Define strategy logic to determine entry signals.
Parameters
----------
i : int
An integer index used to specify the location in the
dataset.
"""
# Example long market order
order = Order(direction=1)
return order
Initialisation#
The __init__
method always initialises a strategy with the following named arguments:
parameters
: a dictionary containing the strategy parameters from your strategy configuration file.data
: the strategy data, which may be a DataFrame, or a dictionary of different datasets.instrument
: a string with the trading instrument’s name (as it appears in the watchlist), or a list of instruments if the strategy hasPORTFOLIO: True
in its configuration.
Broker Access#
In some cases, you may like to directly connect with the broker from your
strategy module. In this case, you must include INCLUDE_BROKER: True
in your
strategy configuration. This will tell AutoTrader to instantiate
your strategy with the broker API and broker utilities. You will therefore need
to include these as named arguments to your __init__
method, as shown below. Now
you can access the methods of the broker directly from your strategy!
def __init__(self, parameters, data, instrument, broker, broker_utils, **kwargs):
"""Define all attributes of the strategy.
"""
self.data = data
self.parameters = parameters
self.instrument = instrument
self.broker = broker
self.utils = broker_utils
Data Stream Access#
It may also be of interest to include the data stream object
when your strategy is instantiated, particularly if you are using a custom data
stream. As above, this can be achieved by specifying INCLUDE_STREAM: True
in
your strategy configuration.
def __init__(self, parameters, data, instrument, data_stream, **kwargs):
"""Define all attributes of the strategy.
"""
self.data = data
self.parameters = parameters
self.instrument = instrument
self.data_stream = data_stream
Indicators Dictionary#
If you wish to include any indicators your strategy uses when visualising
backtest results, you must define an indicators
attribute. This attribute
takes the form of a dictionary with the indicators which you would like to
include. This dictionary then gets passed to AutoPlot.
The general form of this dictionary is shown below, but read more in the
docs for more information.
self.indicators = {'indicator 1 name': {'type': 'indicator 1 type',
'data': self.indicator1_data},
'indicator 2 name': {'type': 'indicator 2 type',
'data': self.indicator2_data},
}
Signal Generation#
Signals are generated using the generate_signal
method. This method contains
the logic behind your strategy and returns trading signals in the form of
Order
s. This method must always return something, so either:
Return an
Order
,Return multiple
Order
s grouped in alist
,Return a blank
Order
usingOrder()
, or an empty list[]
.
The details you provide in each order will depend on the
order type. The most basic (and default) order is a
market
order, which only requires you to specify the direction
: 1 for a
long trade, and -1 for a short trade. Note that you do not have to provide
the instrument
to the order; AutoTrader will do that for you based on the
instruments defined in the WATCHLIST
of the strategy’s configuration.
In some cases, however, you may want to provide it directly (when trading
multiple instruments from the same strategy, for example).
The input arguments to this method will depend on the
run mode of AutoTrader. In
continuous update mode (the default mode),
the most-recent strategy data is passed in. When backtesting, this dataset
will evolve over the backtest period. When livetrading, the latest data
available will be passed in. This mode is less prone to lookahead.
In periodic update mode, the data indexing
parameter i
is passed in as the first argument. Since the entire dataset
is passed to the strategy in this mode, the indexing parameter i
must be
used to act on the correct datapoints.
Shutdown Routine#
If you have a process you would like to exectue after your strategy
has finished running, you may use the shutdown routine functionality
to do so. This involves creating a method involving your shutdown routine
and specifying it to AutoTrader via the
add_strategy
method.
To explain this functionality, consider you are livetrading with a strategy which maintains many open trades at once. If you have this bot deployed in continuous update mode and would like to terminate it by deleting its instance file, you likely would like it to safely close all open trades before terminating. Else, you may have unmanaged trades left open on your account. To prevent this, you may create a shutdown routine as shown below, which cancels any pending orders and closes all remaining open positions. Of course, this method should be more strategic than simply closing all positions and cancelling any orders.
def safe_exit_strategy(self):
# Cancel all pending orders
pending_orders = self.broker.get_orders(self.instrument, 'pending')
for order_id in pending_orders:
self.broker.cancel_order(order_id)
# Close all open trades
close_order = Order(instrument=self.instrument, order_type='close')
self.broker.place_order(close_order)
If you provide the name of your shutdown routine - in the example above this
is ‘safe_exit_strategy’ - to AutoTrader via the shutdown_method
argument
of the add_strategy
method, it will be called
when the bot is terminated.