Skip to content

Commit add5fd4

Browse files
committed
Add simple function to benchmark a series of porfolios over time (as a back test).
1 parent 102a09b commit add5fd4

File tree

1 file changed

+108
-4
lines changed

1 file changed

+108
-4
lines changed

source/account_manager.py

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Various scripts to view and manage an account.
33
"""
44

5-
import sys
5+
import math
66
import database_handler as dbh
77

88
import datetime as dt
@@ -24,6 +24,110 @@ def read_account(account_name):
2424
account = load_account(account_name)
2525

2626

27+
def benchmark2(account_name, benchmark_symbol="SPY"):
28+
account = load_account(account_name)
29+
portfolios = account.portfolios
30+
portfolio_dates = sorted(portfolios.keys())
31+
32+
data_manager = DataManager(db_file="close.pkl")
33+
SPY_prices = data_manager.get_prices(benchmark_symbol)
34+
35+
benchmark_quantity = 0
36+
account_history, history_dates, benchmark_history = [], [], []
37+
38+
last_valid_price = defaultdict(float)
39+
last_portfolio_value = 0
40+
41+
for date_ix, portfolio_date in enumerate(portfolio_dates):
42+
current_portfolio = portfolios[portfolio_date]
43+
if not current_portfolio.assets:
44+
continue
45+
46+
assets_data = data_manager.get_prices(current_portfolio.assets)
47+
pd_date = pd.Timestamp(year=portfolio_date.year,
48+
month=portfolio_date.month,
49+
day=portfolio_date.day)
50+
51+
for asset in assets_data.columns:
52+
price = assets_data[asset].loc[pd_date]
53+
if not math.isnan(price):
54+
last_valid_price[asset] = price
55+
56+
benchmark_price_on_date = SPY_prices.loc[pd_date]
57+
if not math.isnan(benchmark_price_on_date):
58+
last_valid_price[benchmark_symbol] = benchmark_price_on_date
59+
60+
portfolio_value = sum(
61+
current_portfolio.get_position(asset) * last_valid_price[asset]
62+
for asset in current_portfolio.assets
63+
)
64+
65+
benchmark_quantity += (portfolio_value - last_portfolio_value) / \
66+
last_valid_price[benchmark_symbol]
67+
last_portfolio_value = portfolio_value
68+
69+
start_date = dt.datetime(portfolio_date.year,
70+
portfolio_date.month,
71+
portfolio_date.day)
72+
end_date = portfolio_dates[date_ix + 1] if date_ix + 1 < len(
73+
portfolio_dates) else dt.datetime.today()
74+
end_date = dt.datetime(end_date.year, end_date.month, end_date.day)
75+
76+
assets_data = assets_data[(assets_data.index >= start_date) &
77+
(assets_data.index <= end_date)]
78+
79+
for d in assets_data.index:
80+
total_assets_d = 0
81+
prices_d = assets_data.loc[d]
82+
for asset in prices_d.index:
83+
asset_price_at_d = prices_d.loc[asset]
84+
if not math.isnan(asset_price_at_d):
85+
last_valid_price[asset] = asset_price_at_d
86+
total_assets_d += last_valid_price[asset] * \
87+
current_portfolio.get_position(asset)
88+
89+
account_history.append(total_assets_d)
90+
91+
benchmark_price_on_date = SPY_prices.loc[d]
92+
if not math.isnan(benchmark_price_on_date):
93+
last_valid_price[benchmark_symbol] = benchmark_price_on_date
94+
95+
benchmark_history.append(last_valid_price[benchmark_symbol] *
96+
benchmark_quantity)
97+
history_dates.append(d)
98+
99+
net_transactions = {}
100+
balance = 0
101+
for op_date, op_value in account.operations_history():
102+
balance += op_value
103+
pandas_date = pd.Timestamp(year=op_date.year, month=op_date.month,
104+
day=op_date.day)
105+
net_transactions[pandas_date] = balance
106+
107+
transaction_dates = sorted(net_transactions.keys())
108+
cummulative_transactions = [0] * len(history_dates)
109+
110+
for d_ix, d in enumerate(history_dates):
111+
for d_transaction in transaction_dates:
112+
if d > d_transaction:
113+
cummulative_transactions[d_ix] = net_transactions[
114+
d_transaction]
115+
116+
today = dt.datetime.today()
117+
cummulative_transactions = [net_transactions[d] for d in transaction_dates]
118+
transaction_dates.append(pd.Timestamp(year=today.year, month=today.month,
119+
day=today.day))
120+
cummulative_transactions.append(cummulative_transactions[-1])
121+
122+
fig, axes = plt.subplots(ncols=1, figsize=(12, 4))
123+
axes.step(transaction_dates, cummulative_transactions, where="post",
124+
color="lightblue", alpha=0.7)
125+
axes.plot(history_dates, account_history, color="blue")
126+
axes.plot(history_dates, benchmark_history, color="red")
127+
128+
plt.show()
129+
130+
27131
def benchmark(account_name, benchmark_symbol="SPY"):
28132
"""
29133
Benchmarks an account against `benchmark_symbol`.
@@ -148,8 +252,8 @@ def piechart(account_name):
148252
plt.show()
149253

150254

151-
def rebalance_account(
152-
account,
255+
def rebalance_porfolio(
256+
portfolio,
153257
additional_cash,
154258
start_date,
155259
end_date,
@@ -162,7 +266,7 @@ def rebalance_account(
162266
Return the orders to be executed to rebalance the current portfolio in
163267
the `account`.
164268
"""
165-
base_portfolio = account.portfolio
269+
base_portfolio = portfolio
166270

167271
# Data preparation specific to `base_portfolio`. In particular
168272
# if an asset in the portfolio is no longer listed, it is considered

0 commit comments

Comments
 (0)