Final conversion for 2015-11-14-welcome

This commit is contained in:
Bradlee Speice 2024-10-22 01:29:11 +00:00
parent 212d213359
commit 35a91cd6d3
4 changed files with 278 additions and 62 deletions

View File

@ -0,0 +1,59 @@
Title: Welcome, and an algorithm
Date: 2015-11-19
Tags: introduction, trading
Modified: 2015-12-05
Category: Blog
Hello! Glad to meet you. I'm currently a student at Columbia University
studying Financial Engineering, and want to give an overview of the projects
I'm working on!
To start things off, Columbia has been hosting a trading competition that
myself and another partner are competing in. I'm including a notebook of the
algorithm that we're using, just to give a simple overview of a miniature
algorithm.
The competition is scored in 3 areas:
- Total return
- [Sharpe ratio](1)
- Maximum drawdown
Our algorithm uses a basic momentum strategy: in the given list of potential
portfolios, pick the stocks that have been performing well in the past 30
days. Then, optimize for return subject to the drawdown being below a specific
level. We didn't include the Sharpe ratio as a constraint, mostly because
we were a bit late entering the competition.
I'll be updating this post with the results of our algorithm as they come along!
---
**UPDATE 12/5/2015**: Now that the competition has ended, I wanted to update
how the algorithm performed. Unfortunately, it didn't do very well. I'm planning
to make some tweaks over the coming weeks, and do another forward test in January.
- After week 1: Down .1%
- After week 2: Down 1.4%
- After week 3: Flat
And some statistics for all teams participating in the competition:
| | |
|--------------------|--------|
| Max Return | 74.1% |
| Min Return | -97.4% |
| Average Return | -.1% |
| Std Dev of Returns | 19.6% |
---
{% notebook 2015-11-14-welcome.ipynb %}
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\(','\)']]}});
</script>
<script async src='https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML'></script>
[1]: https://en.wikipedia.org/wiki/Sharpe_ratio

View File

@ -1,4 +1,6 @@
**Goal: Max return given maximum Sharpe and Drawdown**
# Trading Competition Optimization
### Goal: Max return given maximum Sharpe and Drawdown
```python
@ -15,7 +17,7 @@ data = {tick: Quandl.get('YAHOO/{}'.format(tick))[-lookback:] for tick in ticker
market = Quandl.get(market_ticker)
```
## Calculating the Return
# Calculating the Return
We first want to know how much each ticker returned over the prior period.
@ -25,16 +27,16 @@ returns = {tick: data[tick][d_col].pct_change() for tick in tickers}
display({tick: returns[tick].mean() for tick in tickers})
```
```
{'CLB': -0.0016320202164526894,
'CVX': 0.0010319531629488911,
'OXY': 0.00093418904454400551,
'SLB': 0.00098431254720448159,
'XOM': 0.00044165797556096868}
```
## Calculating the Sharpe ratio
Sharpe: $R - R_M \over \sigma$
# Calculating the Sharpe ratio
Sharpe: ${R - R_M \over \sigma}$
We use the average return over the lookback period, minus the market average return, over the ticker standard deviation to calculate the Sharpe. Shorting a stock turns a negative Sharpe positive.
@ -48,15 +50,15 @@ sharpes = {tick: sharpe(returns[tick]) for tick in tickers}
display(sharpes)
```
```
{'CLB': -0.10578734457846127,
'CVX': 0.027303529817677398,
'OXY': 0.022622210057414487,
'SLB': 0.026950946344858676,
'XOM': -0.0053519259698605499}
```
## Calculating the drawdown
# Calculating the drawdown
This one is easy - what is the maximum daily change over the lookback period? That is, because we will allow short positions, we are not concerned strictly with maximum downturn, but in general, what is the largest 1-day change?
@ -67,25 +69,23 @@ drawdowns = {tick: drawdown(returns[tick]) for tick in tickers}
display(drawdowns)
```
```
{'CLB': 0.043551495607375035,
'CVX': 0.044894389686214398,
'OXY': 0.051424517867144637,
'SLB': 0.034774627850375328,
'XOM': 0.035851524605672758}
```
## Performing the optimization
$$
\begin{align*}
# Performing the optimization
$\begin{align}
max\ \ & \mu \cdot \omega\\
s.t.\ \ & \vec{1} \omega = 1\\
& \vec{S} \omega \ge s\\
& \vec{D} \cdot | \omega | \le d\\
& \left|\omega\right| \le l\\
\end{align*}
$$
\end{align}$
We want to maximize average return subject to having a full portfolio, Sharpe above a specific level, drawdown below a level, and leverage not too high - that is, don't have huge long/short positions.
@ -156,12 +156,18 @@ display("Expected Max Drawdown: {0:.2f}%".format(expected_drawdown))
# TODO: Calculate expected Sharpe
```
```
'Optimization terminated successfully.'
"Holdings: [('XOM', 5.8337945679814904), ('CVX', 42.935064321851307), ('CLB', -124.5), ('OXY', 36.790387773552119), ('SLB', 39.940753336615096)]"
'Expected Return: 32.375%'
'Expected Max Drawdown: 4.34%'
```

View File

@ -1,45 +1,32 @@
---
title: Welcome, and an algorithm
date: 2015-11-19
date: 2015-11-19 12:00:00
last_update:
date: 2015-12-05
date: 2015-12-05 12:00:00
slug: 2015/11/welcome
authors: [bspeice]
tags: [trading]
---
import Notebook from './_notebook.md'
Hello! Glad to meet you. I'm currently a student at Columbia University
studying Financial Engineering, and want to give an overview of the projects
I'm working on!
Hello! Glad to meet you. I'm currently a student at Columbia University studying Financial Engineering, and want to give an overview of the projects I'm working on!
<!-- truncate -->
To start things off, Columbia has been hosting a trading competition that
myself and another partner are competing in. I'm including a notebook of the
algorithm that we're using, just to give a simple overview of a miniature
algorithm.
To start things off, Columbia has been hosting a trading competition that myself and another partner are competing in. I'm including a notebook of the algorithm that we're using, just to give a simple overview of a miniature algorithm.
The competition is scored in 3 areas:
- Total return
- [Sharpe ratio](https://en.wikipedia.org/wiki/Sharpe_ratio)
- [Sharpe ratio](1)
- Maximum drawdown
Our algorithm uses a basic momentum strategy: in the given list of potential
portfolios, pick the stocks that have been performing well in the past 30
days. Then, optimize for return subject to the drawdown being below a specific
level. We didn't include the Sharpe ratio as a constraint, mostly because
we were a bit late entering the competition.
Our algorithm uses a basic momentum strategy: in the given list of potential portfolios, pick the stocks that have been performing well in the past 30 days. Then, optimize for return subject to the drawdown being below a specific level. We didn't include the Sharpe ratio as a constraint, mostly because we were a bit late entering the competition.
I'll be updating this post with the results of our algorithm as they come along!
---
**UPDATE 12/5/2015**: Now that the competition has ended, I wanted to update
how the algorithm performed. Unfortunately, it didn't do very well. I'm planning
to make some tweaks over the coming weeks, and do another forward test in January.
**UPDATE 12/5/2015**: Now that the competition has ended, I wanted to update how the algorithm performed. Unfortunately, it didn't do very well. I'm planning to make some tweaks over the coming weeks, and do another forward test in January.
- After week 1: Down .1%
- After week 2: Down 1.4%
@ -47,25 +34,189 @@ to make some tweaks over the coming weeks, and do another forward test in Januar
And some statistics for all teams participating in the competition:
<table>
<tr>
<td>Max Return</td>
<td>74.1%</td>
</tr>
<tr>
<td>Min Return</td>
<td>-97.4%</td>
</tr>
<tr>
<td>Average Return</td>
<td>-.1%</td>
</tr>
<tr>
<td>Std Dev of Returns</td>
<td>19.6%</td>
</tr>
</table>
| Statistic | Value |
|--------------------|--------|
| Max Return | 74.1% |
| Min Return | -97.4% |
| Average Return | -.1% |
| Std Dev of Returns | 19.6% |
---
<Notebook/>
## Trading Competition Optimization
**Goal: Max return given maximum Sharpe and Drawdown**
```python
from IPython.display import display
import Quandl
from datetime import datetime, timedelta
tickers = ['XOM', 'CVX', 'CLB', 'OXY', 'SLB']
market_ticker = 'GOOG/NYSE_VOO'
lookback = 30
d_col = 'Close'
data = {tick: Quandl.get('YAHOO/{}'.format(tick))[-lookback:] for tick in tickers}
market = Quandl.get(market_ticker)
```
## Calculating the Return
We first want to know how much each ticker returned over the prior period.
```python
returns = {tick: data[tick][d_col].pct_change() for tick in tickers}
display({tick: returns[tick].mean() for tick in tickers})
```
```
{'CLB': -0.0016320202164526894,
'CVX': 0.0010319531629488911,
'OXY': 0.00093418904454400551,
'SLB': 0.00098431254720448159,
'XOM': 0.00044165797556096868}
```
## Calculating the Sharpe ratio
Sharpe: ${R - R_M \over \sigma}$
We use the average return over the lookback period, minus the market average return, over the ticker standard deviation to calculate the Sharpe. Shorting a stock turns a negative Sharpe positive.
```python
market_returns = market.pct_change()
sharpe = lambda ret: (ret.mean() - market_returns[d_col].mean()) / ret.std()
sharpes = {tick: sharpe(returns[tick]) for tick in tickers}
display(sharpes)
```
```
{'CLB': -0.10578734457846127,
'CVX': 0.027303529817677398,
'OXY': 0.022622210057414487,
'SLB': 0.026950946344858676,
'XOM': -0.0053519259698605499}
```
## Calculating the drawdown
This one is easy - what is the maximum daily change over the lookback period? That is, because we will allow short positions, we are not concerned strictly with maximum downturn, but in general, what is the largest 1-day change?
```python
drawdown = lambda ret: ret.abs().max()
drawdowns = {tick: drawdown(returns[tick]) for tick in tickers}
display(drawdowns)
```
```
{'CLB': 0.043551495607375035,
'CVX': 0.044894389686214398,
'OXY': 0.051424517867144637,
'SLB': 0.034774627850375328,
'XOM': 0.035851524605672758}
```
# Performing the optimization
$$
\begin{align*}
max\ \ & \mu \cdot \omega\\
s.t.\ \ & \vec{1} \omega = 1\\
& \vec{S} \omega \ge s\\
& \vec{D} \cdot | \omega | \le d\\
& \left|\omega\right| \le l\\
\end{align*}
$$
We want to maximize average return subject to having a full portfolio, Sharpe above a specific level, drawdown below a level, and leverage not too high - that is, don't have huge long/short positions.
```python
import numpy as np
from scipy.optimize import minimize
#sharpe_limit = .1
drawdown_limit = .05
leverage = 250
# Use the map so we can guarantee we maintain the correct order
# So we can write as upper-bound
# sharpe_a = np.array(list(map(lambda tick: sharpes[tick], tickers))) * -1
dd_a = np.array(list(map(lambda tick: drawdowns[tick], tickers)))
# Because minimizing
returns_a = np.array(list(map(lambda tick: returns[tick].mean(), tickers)))
meets_sharpe = lambda x: sum(abs(x) * sharpe_a) - sharpe_limit
def meets_dd(x):
portfolio = sum(abs(x))
if portfolio < .1:
# If there are no stocks in the portfolio,
# we can accidentally induce division by 0,
# or division by something small enough to cause infinity
return 0
return drawdown_limit - sum(abs(x) * dd_a) / sum(abs(x))
is_portfolio = lambda x: sum(x) - 1
def within_leverage(x):
return leverage - sum(abs(x))
objective = lambda x: sum(x * returns_a) * -1 # Because we're minimizing
bounds = ((None, None),) * len(tickers)
x = np.zeros(len(tickers))
constraints = [
{
'type': 'eq',
'fun': is_portfolio
}, {
'type': 'ineq',
'fun': within_leverage
#}, {
# 'type': 'ineq',
# 'fun': meets_sharpe
}, {
'type': 'ineq',
'fun': meets_dd
}
]
optimal = minimize(objective, x, bounds=bounds, constraints=constraints,
options={'maxiter': 500})
# Optimization time!
display(optimal.message)
display("Holdings: {}".format(list(zip(tickers, optimal.x))))
# multiply by -100 to scale, and compensate for minimizing
expected_return = optimal.fun * -100
display("Expected Return: {:.3f}%".format(expected_return))
expected_drawdown = sum(abs(optimal.x) * dd_a) / sum(abs(optimal.x)) * 100
display("Expected Max Drawdown: {0:.2f}%".format(expected_drawdown))
# TODO: Calculate expected Sharpe
```
```
'Optimization terminated successfully.'
"Holdings: [('XOM', 5.8337945679814904),
('CVX', 42.935064321851307),
('CLB', -124.5),
('OXY', 36.790387773552119),
('SLB', 39.940753336615096)]"
'Expected Return: 32.375%'
'Expected Max Drawdown: 4.34%'
```