First Algo on Quantopian using the Debt to Equity Ratio

Hi everyone,

I am a new member of the community and I have spent my time here as a mostly as a learner, trying to grasp new concepts through the lectures and tutorials. This algorithm is the result of my first 'hands-on experience' of the Algorithm API. Most of my code is based out of concepts taught in the tutorials and lectures so one might find this extremely rudimentary.

I use the most common financial ratio - the debt to equity ratio to create a long-short equity strategy, targetting the top and bottom 2000 stocks ranked on the basis of the debt/equity value. I noticed that Quantopian ranks the metric based on the numerical value. So a company with practically zero debt to equity ratio was ranked way below than it should ideally be. So I had to short the stocks with the top ranks and vice-versa.

The algorithm is backtested on a 5-year window starting Jan'13 and ending Jan'18. Although I couldn't beat the SPY benchmark in the backtest but I learnt a lot throughout the process of creating it and this definitely serves as a kickstart for many more such algorithms to come!

Please feel free to point out any mistakes that I might have committed and do give your suggestions on how this can be improved.

AJ

24
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
import quantopian.optimize as opt
from quantopian.pipeline.data.factset import Fundamentals
from quantopian.pipeline.factors import CustomFactor, Returns, Latest
from quantopian.pipeline.classifiers.fundamentals import Sector

def initialize(context):
# Schedule our rebalance function to run at the start of
# each week, when the market opens.
schedule_function(
my_rebalance,
date_rules.week_start(),
time_rules.market_close()
)

# Record variables at the end of each day.
schedule_function(
my_record_vars,
date_rules.every_day(),
time_rules.market_close()
)

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

def make_pipeline():

# define our fundamental factor pipeline

pipe = Pipeline()

d_e = Fundamentals.debt_eq_qf.latest

top_d_e = d_e_rank.top(1500)

bottom_d_e = d_e_rank.bottom(1500)

shorts = bottom_d_e

longs  = top_d_e

pipe =  Pipeline(
columns={
'longs': longs,
'shorts': shorts
},
)

return pipe

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 and shorts lists,
# compute even target weights for each security.
if context.longs and context.shorts:
long_weight = -0.5 / len(context.longs)
short_weight = 0.5 / len(context.shorts)

else:

return weights

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

for security in context.shorts:
weights[security] = short_weight

return weights

"""
Get pipeline results.
"""

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

# Go long in securities for which the 'longs' value is True,
# and check if they can be traded.
context.longs = []
for sec in pipe_results[pipe_results['longs']].index.tolist():
context.longs.append(sec)

# Go short in securities for which the 'shorts' value is True,
# and check if they can be traded.
context.shorts = []
for sec in pipe_results[pipe_results['shorts']].index.tolist():
context.shorts.append(sec)

def my_rebalance(context, data):
"""
Rebalance weekly.
"""

# 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 my_record_vars(context, data):
"""
Record variables at the end of each day.
"""

longs = shorts = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
longs += 1
elif position.amount < 0:
shorts += 1

# Record our variables.
record(
leverage=context.account.leverage,
long_count=longs,
short_count=shorts
)
There was a runtime error.
5 responses

Looks great! Just wanted to mention that since your strategy is long/short, the SPY is not the best benchmark since the SPY is long only. Comparing your strategy’s sharpe ratio to the sharpe ratio of the SPY during the same period might be a somewhat better comparison, though not really ideal either since again, the SPY is long only.

Thank you, Joakim for your valuable input. I will surely keep that in mind going forward with my long-short strategies. Could you link any similar algos/notebooks where I could see the implementation of the comparison of sharpe ratios?
Also, it feels awesome to get feedback from someone whose posts I have followed for a good period of time.

No worries AJ! I’m glad you like my posts. :)

Unfortunately I don’t know how to do that in the backtester, but what I tend to do is look at the ‘Volatility matched to benchmark’ graph in the Pyfolio tearsheet (leaving the benchmark to either the SPY or something broader). If the strategy performs better than the SPY, after it’s been adjusted to match the volatility of the SPY, I’m generally happy (though preferably a lot better). That to me is more of an apples to apples comparison.

Arnav, have a look at your strategy's alpha and beta stats. Beta represents how much of the returns can be attributed to long-market exposure. Alpha represents how much of the strategy return is unique to the market.

If you look at your returns chart, you can see it follows the same dips and bumps as SPY does. Since your beta is 0.37 and alpha is close to 0, this means you probably could have achieved more or less same result by simply investing 37% of your portfolio into SPY instead.

Ideally you want a beta as close to 0 as possible and an alpha that is as high as possible.

@ Joakim, thank you once again for this input. I will surely look at the mentioned graph the next time I compare it to the benchmark.

@Virdian, Thank you so much for your feedback...like I said, I am an absolute beginner here and ill be working more on my algos to get as pure an alpha, as I can.