Skip to content
All library documents

Cash Dividends and Corporate Actions in Equity Backtests

Article Strategy library · Author: QuantConnect

Summary

This example demonstrates how cash dividends are represented in an equity backtest when data normalization is set to raw. It subscribes to daily Microsoft data, sets a starting portfolio, and takes a holdings position. The algorithm checks incoming data for dividend and split events and logs their details alongside portfolio cash and the security price, making corporate actions visible during a simulation.

The example also places stop and limit orders and selects brokerage behavior for splits. Its order event handler logs order updates, illustrating that open orders may be adjusted after a split to preserve order value, while reverse splits can lead to cancellation under the chosen brokerage model. The configured simulation spans 1998 to early 2006, but the document provides no performance results. Its main lesson is about data handling and event behavior: dividend accounting and split adjustments depend on normalization and brokerage settings, so this demonstration alone does not establish a trading strategy or validate returns.

Key ideas

  • With raw data normalization, dividend distributions are credited as cash in the portfolio.
  • Dividend and split events can be detected and logged during the data handler.
  • The selected brokerage model determines how open orders respond to forward and reverse splits.
  • The example illustrates corporate-action handling in a backtest and reports no strategy performance.

Tags

Full text
# DividendAlgorithm


# DividendAlgorithm









Demonstration of payments for cash dividends in backtesting. When data normalization mode is set to "Raw" the dividends are paid as cash directly into your portfolio.

## Source (Apache-2.0)

```python
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from AlgorithmImports import *

### <summary>
### Demonstration of payments for cash dividends in backtesting. When data normalization mode is set
### to "Raw" the dividends are paid as cash directly into your portfolio.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="data event handlers" />
### <meta name="tag" content="dividend event" />
class DividendAlgorithm(QCAlgorithm):

    def initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
        self.set_start_date(1998,1,1)  #Set Start Date
        self.set_end_date(2006,1,21)    #Set End Date
        self.set_cash(100000)           #Set Strategy Cash
        # Find more symbols here: http://quantconnect.com/data
        equity = self.add_equity("MSFT", Resolution.DAILY)
        equity.set_data_normalization_mode(DataNormalizationMode.RAW)

        # this will use the Tradier Brokerage open order split behavior
        # forward split will modify open order to maintain order value
        # reverse split open orders will be cancelled
        self.set_brokerage_model(BrokerageName.TRADIER_BROKERAGE)


    def on_data(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.'''
        bar = data["MSFT"]
        if self.transactions.orders_count == 0:
            self.set_holdings("MSFT", .5)
            # place some orders that won't fill, when the split comes in they'll get modified to reflect the split
            quantity = self.calculate_order_quantity("MSFT", .25)
            self.debug(f"Purchased Stock: {bar.price}")
            self.stop_market_order("MSFT", -quantity, bar.low/2)
            self.limit_order("MSFT", -quantity, bar.high*2)

        if data.dividends.contains_key("MSFT"):
            dividend = data.dividends["MSFT"]
            self.log(f"{self.time} >> DIVIDEND >> {dividend.symbol} - {dividend.distribution} - {self.portfolio.cash} - {self.portfolio['MSFT'].price}")

        if data.splits.contains_key("MSFT"):
            split = data.splits["MSFT"]
            self.log(f"{self.time} >> SPLIT >> {split.symbol} - {split.split_factor} - {self.portfolio.cash} - {self.portfolio['MSFT'].price}")

    def on_order_event(self, order_event):
        # orders get adjusted based on split events to maintain order value
        order = self.transactions.get_order_by_id(order_event.order_id)
        self.log(f"{self.time} >> ORDER >> {order}")

```

Shown in full with attribution under the source's licence. Licence: Apache-2.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.