Skip to content

Commit 0193f6f

Browse files
author
chrisrobc
committed
listings
1 parent 07a38bc commit 0193f6f

File tree

46 files changed

+2272
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2272
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from typing import List, Dict, Tuple, Any
2+
import numpy as np
3+
import datetime
4+
5+
# A list of floating point numbers
6+
v: List[float] = [i * 1.23 for i in range(10)]
7+
8+
# A list of mixed type values
9+
v: List[Any] = ['apple', 123, 'banana', None]
10+
11+
# A dictionary of floats indexed by dates
12+
v: Dict[datetime.date, float] = {
13+
datetime.date.today(): 123.456,
14+
datetime.date(2000, 1, 1): 234.567,
15+
}
16+
17+
# A dictionary of lists of strings indexed by tuples of integers
18+
v: Dict[Tuple[int, int], List[str]] = {
19+
(2, 3): [
20+
'apple',
21+
'banana',
22+
],
23+
(4, 7): [
24+
'orange',
25+
'pineapple',
26+
]
27+
}
28+
29+
# An incorrect type hint
30+
# Your compiler or IDE might complain about this
31+
v: List[str] = [1, 2, 3]
32+
33+
# A possibly incorrect type hint
34+
# There is no concensus on whether or not this is correct
35+
v: List[float] = [1, None, 3, None, 5]
36+
37+
# This is non-descript but correct
38+
v: List = [(1,2,'a'), (4,5,'b')]
39+
40+
# This is more descriptive
41+
v: List[Tuple[int, int, str]] = [(1,2,'a'), (4,5,'b')]
42+
43+
# Custom types are supported
44+
from typing import NewType
45+
StockTicker = NewType('StockTicker', np.float64)
46+
ticker: StockTicker = 'AAPL'
47+
48+
# Functions can define input and return types
49+
def convert_to_string(value: Any) -> str:
50+
return str(value)
51+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pandas as pd
2+
import datetime
3+
4+
data = {
5+
'SPY': {
6+
datetime.date(2000, 1, 4): 100,
7+
datetime.date(2000, 1, 5): 101,
8+
},
9+
'AAPL': {
10+
datetime.date(2000, 1, 4): 300,
11+
datetime.date(2000, 1, 5): 303,
12+
},
13+
}
14+
df: pd.DataFrame = pd.DataFrame(data=data)
15+
print(df)
16+
# Returns ...
17+
# SPY AAPL
18+
# 2000-01-04 100 300
19+
# 2000-01-05 101 303
20+
21+
# Index by column
22+
aapl_series: pd.Series = df['AAPL']
23+
print(aapl_series)
24+
# Returns ...
25+
# 2000-01-04 300
26+
# 2000-01-05 303
27+
# Name: AAPL, dtype: int64
28+
29+
# Index by row
30+
start_of_year_row: pd.Series = df.loc[datetime.date(2000, 1, 4)]
31+
print(start_of_year_row)
32+
# Returns ...
33+
# SPY 100
34+
# AAPL 300
35+
# Name: 2000-01-04, dtype: int64
36+
37+
# Index by both
38+
start_of_year_price: pd.Series = df['AAPL'][datetime.date(2000, 1, 4)]
39+
print(start_of_year_price)
40+
# Returns ...
41+
# 300
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import pandas as pd
2+
import datetime
3+
4+
data = {
5+
'SPY': {
6+
datetime.date(2000, 1, 4): 100,
7+
datetime.date(2000, 1, 5): 101,
8+
},
9+
'AAPL': {
10+
datetime.date(2000, 1, 4): 300,
11+
datetime.date(2000, 1, 5): 303,
12+
},
13+
}
14+
15+
### Begin listing
16+
17+
# Create a series
18+
series = pd.Series(data=data['SPY'])
19+
print(series)
20+
# Returns ...
21+
# 2000-01-04 100
22+
# 2000-01-05 101
23+
# dtype: int64
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import pandas as pd
2+
import datetime
3+
4+
### Begin listing
5+
6+
dates = [datetime.date(2000, 1, i) for i in range(1, 11)]
7+
values = [i**2 for i in range(1, 11)]
8+
series = pd.Series(data=values, index=dates)
9+
10+
# O(n) time complexity search through a list
11+
print(datetime.date(2000, 1, 5) in dates)
12+
# Returns ...
13+
# True
14+
15+
# O(1) time complexity search through an index
16+
print(datetime.date(2000, 1, 5) in series.index)
17+
# Returns ...
18+
# True
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import Dict, Any, Callable
2+
3+
DRAWDOWN_EVALUATORS: Dict[str, Callable] = {
4+
'dollar': lambda price, peak: peak - price,
5+
'percent': lambda price, peak: -((price / peak) - 1),
6+
'log': lambda price, peak: np.log(peak) - np.log(price),
7+
}
8+
9+
def calculate_drawdown_series(series: pd.Series, method: str='log') -> pd.Series:
10+
"""
11+
Returns the drawdown series
12+
"""
13+
assert method in DRAWDOWN_EVALUATORS, \
14+
f'Method "{method}" must by one of {list(DRAWDOWN_EVALUATORS.keys())}'
15+
16+
evaluator = DRAWDOWN_EVALUATORS[method]
17+
return evaluator(series, series.cummax())
18+
19+
def calculate_max_drawdown(series: pd.Series, method: str='log') -> float:
20+
"""
21+
Simply returns the max drawdown as a float
22+
"""
23+
return calculate_drawdown_series(series, method).max()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
def calculate_max_drawdown_with_metadata(series: pd.Series,
2+
method: str='log') -> Dict[str, Any]:
3+
"""
4+
Calculates max_drawdown and stores metadata about when and where. Returns
5+
a dictionary of the form
6+
{
7+
'max_drawdown': float,
8+
'peak_date': pd.Timestamp,
9+
'peak_price': float,
10+
'trough_date': pd.Timestamp,
11+
'trough_price': float,
12+
}
13+
"""
14+
15+
assert method in DRAWDOWN_EVALUATORS, \
16+
f'Method "{method}" must by one of {list(DRAWDOWN_EVALUATORS.keys())}'
17+
18+
evaluator = DRAWDOWN_EVALUATORS[method]
19+
20+
max_drawdown = 0
21+
local_peak_date = peak_date = trough_date = series.index[0]
22+
local_peak_price = peak_price = trough_price = series.iloc[0]
23+
24+
for date, price in series.iteritems():
25+
26+
# Keep track of the rolling max
27+
if price > local_peak_price:
28+
local_peak_date = date
29+
local_peak_price = price
30+
31+
# Compute the drawdown
32+
drawdown = evaluator(price, local_peak_price)
33+
34+
# Store new max drawdown values
35+
if drawdown > max_drawdown:
36+
max_drawdown = drawdown
37+
38+
peak_date = local_peak_date
39+
peak_price = local_peak_price
40+
41+
trough_date = date
42+
trough_price = price
43+
44+
return {
45+
'max_drawdown': max_drawdown,
46+
'peak_date': peak_date,
47+
'peak_price': peak_price,
48+
'trough_date': trough_date,
49+
'trough_price': trough_price
50+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def calculate_log_max_drawdown_ratio(series: pd.Series) -> float:
2+
log_drawdown = calculate_max_drawdown(series, method='log')
3+
log_return = np.log(series.iloc[-1]) - np.log(series.iloc[0])
4+
return log_return - log_drawdown
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
def calculate_calmar_ratio(series: pd.Series, years_past: int=3) -> float:
2+
"""
3+
Return the percent max drawdown ratio over the past three years using
4+
CAGR as the numerator, otherwise known as the Calmar Ratio
5+
"""
6+
7+
# Filter series on past three years
8+
last_date = series.index[-1]
9+
three_years_ago = last_date - pd.Timedelta(days=years_past*365.25)
10+
series = series[series.index > three_years_ago]
11+
12+
# Compute annualized percent max drawdown ratio
13+
percent_drawdown = calculate_max_drawdown(series, method='percent')
14+
cagr = calculate_cagr(series)
15+
return cagr / percent_drawdown
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from sklearn.linear_model import LinearRegression
2+
3+
def calculate_pure_profit_score(price_series: pd.Series) -> float:
4+
"""
5+
Calculates the pure profit score
6+
"""
7+
cagr = calculate_cagr(price_series)
8+
9+
# Build a single column for a predictor, t
10+
t: np.ndarray = np.arange(0, price_series.shape[0]).reshape(-1, 1)
11+
12+
# Fit the regression
13+
regression = LinearRegression().fit(t, price_series)
14+
15+
# Get the r-squared value
16+
r_squared = regression.score(t, price_series)
17+
18+
return cagr * r_squared
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
def calculate_jensens_alpha(return_series: pd.Series,
2+
benchmark_return_series: pd.Series) -> float:
3+
"""
4+
Calculates jensens alpha. Prefers input series have the same index. Handles
5+
NAs.
6+
"""
7+
8+
# Join series along date index and purge NAs
9+
df = pd.concat([return_series, benchmark_return_series], sort=True, axis=1)
10+
df = df.dropna()
11+
12+
# Get the appropriate data structure for scikit learn
13+
clean_returns: pd.Series = df[return_series.name]
14+
clean_benchmarks = pd.DataFrame(df[benchmark_return_series.name])
15+
16+
# Fit a linear regression and return the alpha
17+
regression = LinearRegression().fit(clean_benchmarks, y=clean_returns)
18+
return regression.intercept_

0 commit comments

Comments
 (0)