Recording dividend income

Hi everyone!

I want to compare some strategies based on dividend income. I know dividends are added to portfolio cash on the payout date and I wonder if someone can recommend (or share) some code that records dividends?

Thanks!

4 responses

Hi all,

Here are the two solutions I came up with. One attempts to count the cash that is accumulating. The other attempts to track based on the dividends per share metric. They produce significantly different results, but I do understand why. The cash accumulating method is probably more accurate, and it's definitely easier. It also shows "bigger" results, which is the only "downside". (I prefer to underestimate vs overestimate).

In the end, my use case is to compare strategies using the "dividend returns" metric and i'll be calculating it the same way for all strategies so, it will be an apples to apples comparison.

Ill attach. both algos here. I'd be interested to see what people think, and if anyone has some helpful ideas or better methods.

Thanks,
Brett

Method 1: Since dividends are paid out as cash additions to the account, I just count the accumulating cash before I rebalance once a year.

1
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
from quantopian.algorithm import order_optimal_portfolio
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.filters import QTradableStocksUS, Q500US
import quantopian.optimize as opt
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.data.morningstar import Fundamentals as msfun

def set_algo_params(context):
#########################################################################
#
# Algo parameters to tweak
#
context.target_stock_qty = 25

#
# End of algo parmaters
#
#########################################################################

def initialize(context):

set_algo_params(context)

# Schedule our rebalance function to run at the start of
# each year, shortly after the market opens.
schedule_function(
my_rebalance,
date_rules.month_start(),
time_rules.market_open(minutes = 65)
)

# Record tracking variables at the end of each day.
context.dividend_income = 0.0
context.accumulated_cash = -1
schedule_function(
record_vars,
date_rules.every_day(),
time_rules.market_close(),
)

# Create our pipeline and attach it to our algorithm.
attach_pipeline(make_pipeline(context), 'my_pipeline')

# Second pipeline needed because stocks are screened out by the time
# dividends are paid.
#attach_pipeline(make_dps_pipeline(context), 'dps_pipeline')

def s_and_p_universe(context):

# Universe factors
close_price = USEquityPricing.close.latest
market_cap = msfun.market_cap.latest

# Universe Filters
top_500_market_cap = market_cap.top(500)
has_pricing_data = close_price.notnull()

s_and_p_universe = QTradableStocksUS() & top_500_market_cap & has_pricing_data

return s_and_p_universe

def make_pipeline(context):

filtered_universe = s_and_p_universe(context)

# Algo factors
dy = msfun.trailing_dividend_yield.latest

# Algo filters
is_tradeable = dy.top(context.target_stock_qty, mask=filtered_universe)

return Pipeline(
columns={
'dy': dy
},
screen=is_tradeable
)

def make_dps_pipeline(context):

dps = msfun.dividend_per_share_earnings_reports.latest

filtered_universe = s_and_p_universe(context) \
& dps.notnull() & dps.notnan() & dps.isfinite()

return Pipeline(
columns={
'dps': dps
},
screen=filtered_universe
)

def compute_target_weights(context, data):
"""
Compute ordering weights.
"""

# Initialize empty target weights dictionary.
# This will map securities to their target weight.
weights = {}

# If there are securities in our longs,
# compute even target weights for each security.
if context.longs:
long_weight = 1.0 / len(context.longs)
else:
return weights

# Exit positions in our portfolio if they are not
# in our longs or shorts lists.
for security in context.portfolio.positions:
if security not in context.longs and data.can_trade(security):
weights[security] = 0

for security in context.longs:
weights[security] = long_weight

return weights

def before_trading_start(context, data):
"""
Get pipeline results.
"""

# Gets our pipeline output every day.
context.pipe_results = pipeline_output('my_pipeline')
#context.dps_pipe = pipeline_output('dps_pipeline')

# Go long in securities and check if they can be traded.
context.longs = []

for sec in context.pipe_results.index:
if data.can_trade(sec):
context.longs.append(sec)

def my_rebalance(context, data):
"""
Rebalance yearly
"""

if get_datetime().month not in [1] or get_open_orders(): return

# Before rebalancing, calculate the dividend income.
if context.accumulated_cash == -1:
context.accumulated_cash = 0.0
else:
context.accumulated_cash = context.portfolio.cash

context.dividend_income += context.accumulated_cash

log.info('dividend income =  ${:.2f}'.format(context.dividend_income)) log.info('dividend returns = {:.2f}%'.format( ((context.dividend_income / context.portfolio.starting_cash) * 100.0))) log.info('ordering {} stocks'.format(len(context.longs))) # Calculate target weights to rebalance target_weights = compute_target_weights(context, data) # If we have target weights, rebalance our portfolio if target_weights: order_optimal_portfolio( objective=opt.TargetWeights(target_weights), constraints=[], ) def record_vars(context, data): """ Plot variables at the end of each day. """ record(cash = context.portfolio.cash) record(dividend_income = context.dividend_income) There was a runtime error. Method 2: Setup a second pipeline and use the dividend_per_share_earnings_reports factor. 0 Loading... Total Returns -- Alpha -- Beta -- Sharpe -- Sortino -- Max Drawdown -- Benchmark Returns -- Volatility --  Returns 1 Month 3 Month 6 Month 12 Month  Alpha 1 Month 3 Month 6 Month 12 Month  Beta 1 Month 3 Month 6 Month 12 Month  Sharpe 1 Month 3 Month 6 Month 12 Month  Sortino 1 Month 3 Month 6 Month 12 Month  Volatility 1 Month 3 Month 6 Month 12 Month  Max Drawdown 1 Month 3 Month 6 Month 12 Month from quantopian.algorithm import order_optimal_portfolio from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline import Pipeline from quantopian.pipeline.data.builtin import USEquityPricing from quantopian.pipeline.factors import SimpleMovingAverage from quantopian.pipeline.filters import QTradableStocksUS, Q500US import quantopian.optimize as opt from quantopian.pipeline.data import morningstar from quantopian.pipeline.data.morningstar import Fundamentals as msfun def set_algo_params(context): ######################################################################### # # Algo parameters to tweak # context.target_stock_qty = 25 # # End of algo parmaters # ######################################################################### def initialize(context): set_algo_params(context) # Schedule our rebalance function to run at the start of # each year, shortly after the market opens. schedule_function( my_rebalance, date_rules.month_start(), time_rules.market_open(minutes = 65) ) # Record tracking variables at the end of each day. context.dividend_income = 0.0 schedule_function( record_vars, date_rules.every_day(), time_rules.market_close(), ) # Create our pipeline and attach it to our algorithm. attach_pipeline(make_pipeline(context), 'my_pipeline') # Second pipeline needed because stocks are screened out by the time # dividends are paid. attach_pipeline(make_dps_pipeline(context), 'dps_pipeline') def s_and_p_universe(context): # Universe factors close_price = USEquityPricing.close.latest market_cap = msfun.market_cap.latest # Universe Filters top_500_market_cap = market_cap.top(500) has_pricing_data = close_price.notnull() s_and_p_universe = QTradableStocksUS() & top_500_market_cap & has_pricing_data return s_and_p_universe def make_pipeline(context): filtered_universe = s_and_p_universe(context) # Algo factors dy = msfun.trailing_dividend_yield.latest # Algo filters is_tradeable = dy.top(context.target_stock_qty, mask=filtered_universe) return Pipeline( columns={ 'dy': dy }, screen=is_tradeable ) def make_dps_pipeline(context): dps = msfun.dividend_per_share_earnings_reports.latest filtered_universe = s_and_p_universe(context) \ & dps.notnull() & dps.notnan() & dps.isfinite() return Pipeline( columns={ 'dps': dps }, screen=filtered_universe ) def compute_target_weights(context, data): """ Compute ordering weights. """ # Initialize empty target weights dictionary. # This will map securities to their target weight. weights = {} # If there are securities in our longs, # compute even target weights for each security. if context.longs: long_weight = 1.0 / len(context.longs) else: return weights # Exit positions in our portfolio if they are not # in our longs or shorts lists. for security in context.portfolio.positions: if security not in context.longs and data.can_trade(security): weights[security] = 0 for security in context.longs: weights[security] = long_weight return weights def before_trading_start(context, data): """ Get pipeline results. """ # Gets our pipeline output every day. context.pipe_results = pipeline_output('my_pipeline') context.dps_pipe = pipeline_output('dps_pipeline') # Go long in securities and check if they can be traded. context.longs = [] for sec in context.pipe_results.index: if data.can_trade(sec): context.longs.append(sec) def my_rebalance(context, data): """ Rebalance yearly """ if get_datetime().month not in [1] or get_open_orders(): return # Before rebalancing, calculate the dividend income. # For each stock, get the yearly dividend paid, times that by the num shares, # then add to dividend income. for sec in context.portfolio.positions: if sec in context.dps_pipe.index: dps = context.dps_pipe.get_value(sec, 'dps') shares = context.portfolio.positions[sec].amount context.dividend_income += dps*shares log.info('dividend income =${:.2f}'.format(context.dividend_income))
log.info('dividend returns = {:.2f}%'.format(
((context.dividend_income / context.portfolio.starting_cash) * 100.0)))
log.info('ordering {} stocks'.format(len(context.longs)))

# Calculate target weights to rebalance
target_weights = compute_target_weights(context, data)

# If we have target weights, rebalance our portfolio
if target_weights:
order_optimal_portfolio(
objective=opt.TargetWeights(target_weights),
constraints=[],
)

def record_vars(context, data):
"""
Plot variables at the end of each day.
"""
record(cash = context.portfolio.cash)
record(dividend_income = context.dividend_income)
There was a runtime error.

Well, now of course, i want to test an algo that trades once a month and both my solutions no longer work. Anyone have any ideas?