Developing Strategies on Blueshift娦
Systematic strategy life cycle¶
A typical systematic strategy usually runs in the following manner. First
at the onset, the strategy does some initialization. This includes things
like defining the trading asset universe
, setting up periodic function
calls, and defining strategy parameters. Then onwards, during the market
hours, as every new bar of price arrives, the strategy responds to it.
This typically involves computing signals, computing the target portfolio
based on these signals and current state of the algorithm, and potentially
placing or cancelling orders. At the end of market hours, it does some end-of-day
activities. Once the market hours are over, it goes in to a sleep mode.
The next day, it wakes up again before the market opens. At this point it
may carry out some functions to set itself up for the day. Once the market
opens it gets into the usual routine of taking in the arriving data and
responding to it.
Special functions on Blueshift®¶
On Blueshift®, each of the points in the trading cycle above directly
maps to a particular functions. These functions are called the
main entry-point functions
. They are as below:
-
initialize(context)
: This is the first function that is called once (and only once) at the very start of the strategy run. -
before_trading_start(context, data)
: This is the function that is called at a preset time before the market opens, every day. Your strategy should not depend on the exact time of this function call. -
handle_data(context, data)
: This is the function called at every minute during the market hours. -
analyze(context, performance)
: This is a function called at the end of a strategy run.
We must define the initialize
function in our strategy for it to be a
valid strategy. Other functions are useful but optional.
In the live version, we have a few extra functions
-
after_market_hours(context, data)
: called once everyday, after the market closes. Your strategy should not depend on the exact time of this function call. -
on_trade(context, data)
: called at every trade-related update from the brokers (e.g. a order fill, cancellation etc). -
on_data(context, data)
: called at every data update from the brokers.
The last two functions are only available if the broker for the live run supports live streaming. These two functions are useful to trade at real-time speed on Blueshift. For more, see below.
These are special functions understood by the platform (by their names)
and are called by the platform at the periodicity mentioned above. At
the heart of it, Blueshift® core engine is an elaborate event loop
that manages the complex things about systematic trading (like scheduling,
data management, order management, risk management, logging etc.). The
user strategy code defines these functions above, and the platform
call them at appropriate time inside this event loop
. This is how a
strategy gets executed on the platform. Hence they are collectively
called main entry-point functions
.
For more details, check out the API callback functions.
Special variables¶
As you can see, most of the functions above has two pre-defined variables
that are passed on as arguments. They are context
and data
- these
are special variables and are maintained by the platform itself. The
user program can query them to get information about the current state
of the strategy or fetch data on securities of interest.
The context variable¶
It is a special variable that servers two purposes.
First, it acts as a container for storing user defined variables that
are needed across different functions. All the entry point functions
have defined arguments, and hence it is not possible to pass an user
defined argument directly. Instead, we can define our variables as an
attribute
to this special variable context
. This makes them
automatically accessible from other entry-point functions
through the
context
variable. We have already seen examples of it when we stored
our trading universe as a list of assets in the context
variable.
def initialize(context):
context.universe = [symbol("AAPL"), symbol("MSFT")]
def handle_data(context, data):
# here we can access the universe as we have context as an argument
print(context.universe)
Second, the context
variable is also used by the platform to track the
current state of the strategy. Hence user program can query this variable
anytime to get the information about its current state:
def handle_data(context, data):
print(context.account)
print(context.portfolio)
For more details and a complete list of available attributes, check out the API doc.
The data variable¶
This is another special variable maintained by the platform that enables a user algorithm to query price and other data it has access to. For more on this see the API doc.
The API functions¶
Apart from the main entry-point functions
, Blueshift® also
provides a bunch of API functions, that can be imported from the api
module. Examples:
from blueshift.api import symbol
For asset related API functions, see the section on asset. For ordering and trading related API functions, see the section on placing and cancelling orders. For scheduling related API functions, see the section on it. Other commonly used API functions are:
get_datetime()
: Fetch the current date and time as PandasTimestamp
object.- risk control related functions like
set_max_order_size
,set_max_order_count
,set_max_position_size
etc.
For a complete list of API function refer to the API doc.
Trading real time with live mode¶
In the live
mode, blueshift supports real time trading1. In addition
to the regular events, we
have two sets of event handlers than respond to real time updates. They
have the following signatures:
on_trade(callback) # function `callback` is called for every trade updates
on_data(callback) # function `callback` is called for every data updates
Required broker support for real-time trading.
The broker must support streaming order updates for on_trade
and
streaming data updates for on_data
API functions. Any or both or none
can be avaialble for a broker depending on their API support. See
footnote below for current status.
These functions take an argument, a function (callback
above) that is
called for every update received in real time. The function callback
must have a signature as callback(context, data)
. Note, there is no
enforced limit on the number of handlers per event (trade
or data
).
You can use these on_data
or on_trade
functions to register
multiple event handlers per event. However, adding the same function
twice will call it only once - make sure the callback
function names
are different for multiple handlers for the same event. For example:
from blueshift.api import on_data, on_trade
# a user defined function that checks stoploss hit on every data update
def handle_stoploss(context, data):
# do useful work here
pass
def initialize(context):
# some initialization...
# schedule the function above to be called on every data update
on_data(hande_stoploss)
There is another set of API functions that turns off these event handlers.
off_trade(callback) # remove the callback for trade updates
off_data(callback) # remove the callback for data updates
callback
is optional - if not supplied, all handlers
for that particular event (trade
or data
) will be removed. See the
API doc for details.
-
Only for brokers that support real time updates on trades and data through a streaming API. At present, the following brokers support this features: 1) Both trade and data streaming -FXCM (live and paper), Alpaca (live and paper) 2) Only data update - Mastertrust (live), 3) only trade updates - None. ↩