Event Callbacks in Blueshift ============================= Blueshift is an event-driven engine. The core engine implements the event loop. The user strategy implements a set of callback functions ( i.e. event handlers) that are called by the event loop at defined events. The whole strategy logic is structured by responding to events as and when they occur. Blueshift strategies can respond to three types of events during its run. - Lifetime Events These are events generated by the Blueshift event loop based on the lifetime epochs of the strategy. They are summarised under the :ref:`Main Callback Functions
` below. - Trade and Data Events These are events that are triggered once an order is (potentially partially) filled or on arrival of new data points. They work differently in backtest or real-time trading. See :ref:`Trade and Data Callbacks` below. - Time-based Events These are events scheduled by the user (using one of the `scheduling` functions). These are summarised under the :ref:`Scheduled Callback Functions` section. Main Callback Functions ----------------------- Lifetime event handlers are based on the epoch of the strategy run. This includes the following: Initialize ++++++++++ .. function:: initialize(context) This is the first entry point function. ``initialize`` is called when at the beginning of an algorithm run, only once. For backtest, the call time is at midnight of the start date of the backtest run. For live trading, this is called at the start of the execution as soon as possible. :param context: The algorithm context. :type context: :ref:`context ` object. User program should utilise this function to set up the algorithm initialization (parameters, trading universe, function scheduling, risk limits etc). .. warning:: A valid strategy script/ module must have this function defined. Before Trading Start ++++++++++++++++++++ .. function:: before_trading_start(context, data) This function is called at the beginning of every trading session (day). For backtest, this is called 30 minutes before the regular market open hour everyday. For live trading, this is called 30 minutes before the market opens each day, or just after (around a minute) of the ``initialize`` call at the start on the execution start day (in case it is already trading hours or less than 30 minutes remaining from the opening hour). :param context: The algorithm context. :type context: :ref:`context ` object. :param data: The algorithm data object. :type data: :ref:`data ` object. User program should utilise this function to set up daily initialization ( e.g. profit and loss, model re-evaluation etc.) .. warning:: User strategy should not depend on the exact time of invocation of this handler function. The only thing that is guaranteed is that it will be called only once per session and will be called before any handle data or scheduled functions. Handle Data +++++++++++ .. function:: handle_data(context, data) This function is called at every clock beat, i.e. every iteration of the ``event loop`` (usually at every minute bar). :param context: The algorithm context. :type context: :ref:`context ` object. :param data: The algorithm data object. :type data: :ref:`data ` object. User program should utilise this function to run their core algo logic if the algorithm needs to respond to every trading bar (every minute). For algorithms (or functions) that need to respond at a lower scheduled frequency, it is more efficient to use the :ref:`scheduling ` API function to handle such cases. .. note:: If the clock frequency is one minute (which is the case at present on Blueshift), this function is equivalent to a scheduled function with ``time_rule`` as ``every_nth_minute(1)``. After Trading Hours +++++++++++++++++++ .. function:: after_trading_hours(context, data) This function is called at the end of every session (every day) around 5 minutes after the last trading minute (market close). :param context: The algorithm context. :type context: :ref:`context ` object. :param data: The algorithm data object. :type data: :ref:`data ` object. User program should utilise this function to do their end-of-day activities (e.g. update profit and loss, reconcile, set up for next day). .. warning:: User strategy should not depend on the exact time of invocation of this handler function. The only thing that is guaranteed is that it will be called only once per session and will be called after the end of the regular market hours. Analyze +++++++ .. function:: analyze(context, performance) This function is called only once, at the end of an algorithm run, as soon as possible. :param context: The algorithm context. :type context: :ref:`context ` object. :param performance: The algorithm performance. :type performance: pandas.DataFrame .. note:: The ``performance`` object is a DataFrame with algo performance captured at daily intervals. The dataframe timestamps are the timestamps for ``after_trading_hours`` calls. The columns include some of the algo account fields as well the profit and loss (pnls) metrics. User program can implement this method to add custom analysis of backtest results. Trade and Data Callbacks ------------------------ .. attention:: only available for live trading, for brokers supporting streaming for data and trade updates. These callback APIs on Blueshift allows the user strategy to respond to the markets events as it happens (in real-time for live mode). On Data ++++++++ .. function:: on_data(context, data) This event handler is invoked at arrival of new data points. This function is called at every clock tick, i.e. every iteration of the ``event loop`` (usually at every minute bar) in backtest mode. In realtime mode, this is invoked only if the broker supports real-time data streaming and a new data point has been made available. In the case of the latter, only instruments that the algo has subscribed to can trigger a call to this function. :param context: The algorithm context. :type context: :ref:`context ` object. :param data: The algorithm data object. :type data: :ref:`data ` object. .. important:: The `on_data` event handler is called only when triggered by the underlying broker. For that to happen, the broker must support streaming data (market data websockets or socketIO API), as well as the strategy must have subscribed to at least one instrument for streaming data. There is no explicit API to subscribe to streaming data, but :ref:`curret` or :ref:`history` will automatically trigger data subscriptions for the queried assets. If you are using this handler, make sure you have triggered a call to one of these methods to enable subscription. Without it, the handler will never be called. .. warning:: The callback function must be short and quick to avoid creating a backlog. Adding long running funtions may lead to the algo crashing. Also, a temporary network disconnection will cause this event handler to stop triggering (till the connection is restored). To make sure your strategy logic is robust, you can also put the same logic inside :ref:`handle_data` to make sure the strategy logic is triggered at least once every minute, even if there is a disconnection. Of course, this depends on the specific need of a given strategy. .. seealso:: See available callback types :py:class:`blueshift.api.AlgoCallBack`. On Trade +++++++++ .. function:: on_trade(context, data) This event handler is invoked when an order placed by the strategy is filled. This function is called when the simulator fills an order in backtest mode or paper trading mode. In realtime mode, this is invoked only if the broker supports real-time order update streaming and an order has been filled. :param context: The algorithm context. :type context: :ref:`context ` object. :param data: The algorithm data object. :type data: :ref:`data ` object. .. seealso:: See available callback types :py:class:`blueshift.api.AlgoCallBack`. .. warning:: The callback function must be short and quick to avoid creating a backlog. Adding long running funtions, may lead to the algo crashing. Scheduled Callback Functions ---------------------------- The scheduled event callbacks provide a way to schedule a callback function (with signature ``func(context, data)``) based on a date and time based rule. There is no limit on how many callbacks can be scheduled in such a manner. But only one callback can be scheduled at each call of the ``schedule_function``. .. py:module:: blueshift.algorithm.algorithm :noindex: Schedule Once +++++++++++++ .. automethod:: TradingAlgorithm.schedule_once Schedule Later ++++++++++++++ .. automethod:: TradingAlgorithm.schedule_later Schedule Function +++++++++++++++++ .. automethod:: TradingAlgorithm.schedule_function .. py:module:: blueshift.api :noindex: Date Rules ^^^^^^^^^^ .. autoclass:: date_rules Time Rules ^^^^^^^^^^ .. autoclass:: time_rules Scheduling Examples -------------------- Repetitive Logic with Scheduling +++++++++++++++++++++++++++++++++ The below code shows examples of setting up a monthly callback function, to be called on the first business day of each month, 30 minutes before the market close. .. code-block:: python from blueshift.api import schedule_function, date_rules, time_rules from blueshift.api import get_datetime def initialize(context): schedule_function(myfunc, date_rule=date_rules.month_start(), time_rule=time_rules.market_open(minutes=30)) def myfunc(context, data): print(f'scheduled function called at {get_datetime()}') Responsive Strategy with Scheduling ++++++++++++++++++++++++++++++++++++ The below code shows examples of placing a limit order and then updating the order to optimize time to fill and fill price. The algo terminates once the order is executed. .. code-block:: python from blueshift.api import schedule_later, schedule_once, symbol, terminate from blueshift.api import terminate, order, update_order, get_order def initialize(context): context.asset = symbol('AAPL') context.order_id = None schedule_once(myfunc) # call as soon as ready def myfunc(context, data): if context.order_id is None: context.order_id = order(context.asset, 1) if not context.order_id: raise ValueError(f'something went wrong.') schedule_later(myfunc, 1) # call again after one minute return o = get_order(context.order_id) if o.is_open(): px = data.current(context.asset, 'close') update_order(context.order_id, price=px) schedule_later(myfunc, 1) # call again after one minute else: # we are done terminate(f'order executed, terminate now.') If you are using ``schedule_once`` or ``schedule_later`` recursively, carefully follow your logic and make sure the recursion ends where it needs to. User Order Confirmation Callback --------------------------------- .. attention:: only available for live trading, when running in oneclick execution mode. On Oneclick +++++++++++ .. function:: on_oneclick(notifications) This callback function is invoked when notifications (raised when a order notification is generated in oneclick mode) are either confirmed by the user or expired or some error occured. The input parameter `notifications` is a dictionary with notification ID (returnd from the ordering functions in oneclick mode) as key and the final state of that notification as value. This is a global handler, i.e. it is called only in the parent algo context, and not any sub-contexts. .. seealso:: See available status types :py:class:`blueshift.protocol.OneClickState`. See execution mode :py:class:`blueshift.api.ExecutionMode`.