-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmpt.py
More file actions
79 lines (68 loc) · 2.85 KB
/
mpt.py
File metadata and controls
79 lines (68 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import os
import pandas as pd
from pypfopt.expected_returns import mean_historical_return
from pypfopt.risk_models import risk_matrix
from pypfopt.efficient_frontier import EfficientFrontier
def load_data(args):
dfs, symbols = [], []
dirpath = os.path.join(args.data_dir, 'dow30')
for filename in os.listdir(dirpath):
symbols.append(filename[:-4])
df = pd.read_csv(os.path.join(dirpath, filename), index_col='Date')
dfs.append(df)
data = pd.concat([df.Close for df in dfs], axis=1).dropna()
data.index = pd.to_datetime(data.index)
data.columns = symbols
return data
def portfolio_return(args, method='max_sharpe'):
# constants
start_date = args.start_test
lookback = 252 # days
transaction_cost = args.transaction_cost # ratio to the trading volume
initial_cash = args.initial_balance
data = load_data(args)
portfolio = None
tc = transaction_cost
net_value = initial_cash
wallet = {}
net_values = {}
start_idx = data.index.tolist().index(data[start_date:].index[0]) + 27 # align trading days with RL (DJIANew uses past 25 days as lookback)
for idx in range(start_idx, len(data)):
_data = data.iloc[idx - lookback:idx]
exp_return = mean_historical_return(_data, frequency=lookback)
cov_matrix = risk_matrix(_data, frequency=lookback)
frontier = EfficientFrontier(exp_return, cov_matrix)
# make initial portfolio
if portfolio is None:
portfolio = getattr(frontier, method)()
prices = data.iloc[idx]
for symbol, weight in portfolio.items():
price = prices[symbol]
quantity = net_value * weight // price
wallet[symbol] = quantity
cost = price * abs(quantity) * tc
net_value -= cost
else:
prev_prices = data.iloc[idx - 1]
prices = data.iloc[idx]
# recalculate current net value
for symbol, weight in portfolio.items():
prev_price = prev_prices[symbol]
new_price = prices[symbol]
gain = wallet[symbol] * (new_price - prev_price)
net_value += gain
# rebalance
new_portfolio = getattr(frontier, method)()
for symbol, weight in new_portfolio.items():
prev_weight = portfolio[symbol]
price = prices[symbol]
quantity = net_value * (weight - prev_weight) // price
if wallet[symbol] < -quantity:
quantity = -wallet[symbol]
new_portfolio[symbol] = 0
wallet[symbol] += quantity
cost = price * abs(quantity) * tc
net_value -= cost
portfolio = new_portfolio
net_values[data.index[idx]] = net_value
return pd.Series(net_values)