Multiple Strategies in a Single Algo
Sometimes we may want to run multiple strategies with different logic and
order/ positions management within a single algo. This maybe useful when
we are running an ensemble of strategies (with a capital allocation rule)
in a single algorithm. There are other cases too. Blueshift API support
such use cases by allowing to dynamically add or remove sub-strategies
in a single algo.
Sub-Strategies
An algo run on Blueshift with the main context as defined by the context
variable available through the main callback
functions. This is the main context of the algo run.
However, by using the sub-strategies API, we can add a sub-strategy
to the
algo (and remove it when required). Each such sub-strategy runs within its
own context - also known as sub-context
. Since each such sub-contexts
are maintained independently, a sub-strategy can maintain its independent
orders and positions tracking. We can generally use the API functions as is
in either in the context of the main strategy or any sub-strategies. Blueshift
will automatically apply the correct context. This allows the main and the
sub-strategies to work independetly from each other, without being aware of
each others presence.
Defining a sub-strategy
A sub-strategy can be defined by sub-classing the Strategy class and then overriding the event handlers as required.
- class blueshift.core.algorithm.strategy.Strategy
Interface for class based strategy on Blueshift. Subclass this strategy to create a sub-strategy. This class can also be used to create the main strategy (instead of defining the main callback functions directly in the strategy code).
Sub-strategy methods and attributes
- Strategy.name
Name (and the context name) of this strategy.
- Strategy.initialize(context)
override this function to run at initialization.
- Strategy.before_trading_start(context, data)
override this function to run at start of the day.
- Strategy.handle_data(context, data)
override this function to run at every cycle.
- Strategy.on_data(context, data)
override this function to run at new data arrival.
- Strategy.on_trade(context, data)
override this function to run at any order fill event.
- Strategy.after_trading_hours(context, data)
override this function to run at end of the day.
- Strategy.analyze(context, perf)
override this function to run at end of the run.
- Strategy.on_error(context, error)
override this function to run before the strategy exit on error.
- Strategy.on_cancel(context)
override this function to run before the strategy exit on user cancel.
The callbacks are similar in functionality as the main event callbacks. These will be automatically called (on appropriate events) once a sub-strategy is added in the algo run. Once a sub-strategy is removed, its callbacks will not be invoked anymore.
Adding a sub-strategy
- TradingAlgorithm.add_strategy(strategy)
Add a sub strategy to the current algo. Sub strategies allow a modular approach to incorporate multiple independent rules in a single strategy. For more see :ref: sub-strategies<Sub-Strategies>.
Note
Sub strategies can only be added in regular modes (not in EXECUTION mode).
- Parameters:
strategy (Strategy.) – The strategy to add.
This API method must be called from the main strategy, as a sub-strategy can be added from the main context only.
Cancelling a sub-strategy
- TradingAlgorithm.cancel_strategy(name, cancel_orders=True, square_off=False)
Cancel (remove) a sub strategy previously added. For more on sub-strategies, see :ref: sub-strategies<Sub-Strategies>.
This API method must be called from the main strategy, or from the sub-strategy itself that is being cancelled.
Sub-strategy order and position tracking
Each sub-strategies, and the main strategy, will maintain their independent
version of orders and positions tracking. That means order APIs
(trading APIs), including order placement,
order management, algo order and stoploss/ take-profit APIs will maintain
and track their own contexts. Simulation APIs (simulation APIs)
will also affect behaviour of only the respective contexts. For example, calling
get_open_orders
from a sub-context will return only the open orders
placed from the corresponding sub-strategy.
However, a few sets of APIs are global, in the sense that they will affect all contexts - the main context as well as any sub-contexts created. This inclues the squareoff API, the pipeline APIs (pipeline) and some risk management APIs). This means, for example, if we call square-off from any context, all positions in all active contexts will be squared-off by default. See more in the documentation of the respective APIs.
Below code snippet shows how to add and cancel of sub-strategies in an algo run. Sub-strategies are added in the main context, but can be removed from itself or from the main context.
from blueshift.api import order, symbol, schedule_once, cancel_strategy
from blueshift.api import add_strategy, exit_when_done, get_context
from blueshift.protocol import Strategy
class Strategy1(Strategy):
def initialize(self, context):
# this context refers to the sub-context for this sub-strategy
schedule_once(self.strategy)
def strategy(self, context, data):
order(symbol('MSFT'), 1)
for oid in context.orders:
# prints only MSFT order
print(context.orders[oid].to_dict())
# cancelling sub-strategy from itself
cancel_strategy(self.name)
class Strategy2(Strategy):
def initialize(self, context):
# this context refers to the sub-context for this sub-strategy
schedule_once(self.strategy)
def strategy(self, context, data):
order(symbol('AAPL'), 1)
for oid in context.orders:
# prints only AAPL order
print(context.orders[oid].to_dict())
def initialize(context):
# this context refers to the main context
# add the sub-strategies
add_strategy(Strategy1('strategy1', 15000))
add_strategy(Strategy2('strategy2', 25000))
schedule_once(strategy)
def strategy(context, data):
order(symbol('AMZN'), 1)
for oid in context.orders:
# prints only AMZN order
print(context.orders[oid].to_dict())
schedule_once(wrap_up)
def wrap_up(context, data):
# cancelling sub-strategy from main context
cancel_strategy('strategy2')
# exit after all orders, (AAPL, MSFT and AMZN) are done
exit_when_done()
def analyze(context, perf):
# print the performance for AMZN from the main context
print(context.blotter.performance)
# print the performance for AAPL from the sub-context "strategy1"
ctx_1 = get_context('strategy1')
print(ctx1.blotter.performance)
The sub-strategy APIs provide a powerful way to create ensemble strategies, run multiple algos more efficiently and otherwise write clean modular algos and trading logic.