def get_variable(config): from bokeh.embed import components name, rank, df, decade_df = get_result(config.gender, config.decade, config.name) if config.name <> name: message = "%s not found. Do you mean %s?" % (config.name, name) else: message ="" birth_table = df.pivot(index='Decade', columns='Name', values='Births').fillna(0).to_html() rank_table = df.pivot(index='Decade', columns='Name', values='Rank').fillna(0).to_html() result_table = df[df['Rank']==rank][['Decade','Name','Rank']].sort('Decade').to_html(index=False) top_table = decade_df[(decade_df['Rank']<=8) & (decade_df['Decade']==config.decade)].sort('Rank').to_html(index=False) from bokeh.charts import Line, save, output_file, ColumnDataSource from bokeh.resources import INLINE plot_path = "%s_%s_%i.html" % (config.name,config.gender,config.decade) output_file("output/" + plot_path, mode='inline') tooltips = [(c, '@' + c) for c in df.columns] p = Line(df, x='Decade', y='Rank', title="Rank across Time", color='Name', xlabel="Decade", ylabel="Rank", tooltips=tooltips) p.circle('Decade', 'Rank', color='gray', alpha=0.5, source=ColumnDataSource(df)) save(p) #script, div = components(p) return { 'plot_path': plot_path, 'result_table': result_table, 'rank_table': rank_table, 'birth_table': birth_table, 'top_table': top_table, 'rank': rank, 'config': config, 'name': name, 'message': message }
def port_opt(constants, port_id): # Grab profile and portfolio objects prof = Profile.from_mongo(port_id) port = Portfolio.from_mongo(port_id) # Function for converting any rate to the specified time step def int_rate_convert(annual_int_rate, time_step): eff_rate = (1 + annual_int_rate)**(time_step) - 1 return eff_rate tickers = constants.TICKERS mgmt_fees = constants.MGMT_FEES # annual management fees trans_costs = constants.TRANS_COSTS # transaction costs Y = prof['horizon'][-1] # Entire length of planning horizon (WEBAPP INPUT) T = prof['time_left'] # Number of years left (WEBAPP INPUT) # If the goal was already reached, further optimizaton is meaningless. if Y - T != 0: if port['reached'][-1] >= 1: return None # Further optimization after time left = 0 is meaningless and returns None if T < 0: return None if Y < 2: time_step = 1 / 12 # Trading frequency is monthly elif 2 <= Y <= 5: time_step = 1 / 4 # Trading frequency is quarterly elif 5 < Y < 10: time_step = 1 / 2 # Trading frequency is semi-annually elif Y >= 10: time_step = 1 # Trading frequency is annually N = T / time_step if N % 2 != 0: N = int(N) + 1 else: N = int(N) # Convert the annual rate to any frequency annual_int_rate = constants.INT_RATE # assume constant interest rate for cash investment eff_rate = int_rate_convert(annual_int_rate, time_step) # Convert management fees to one-time expenses at the end of the planning horizon eff_fees = np.zeros((len(mgmt_fees) + 1, 1)) for m in range(0, len(mgmt_fees)): eff_fees[m] = int_rate_convert(mgmt_fees[m], N * time_step) eff_fees[m + 1] = 0 # assume mgmt fees of cash investment is 0 # Prices and returns of assets; nrows x nassets prices = None while prices is None: try: prices, returns = ia(Y, T, tickers, time_step) except: pass means = np.mean(returns.T, axis=1) # mean return vector; nassets x 1 cov_mat = np.cov(returns.T) # covariance matrix; nassets x nassets # Terminate when time left = 0 if T == 0: temp = port['shares1'][-1] shares = np.matrix(np.zeros((6, 1))) for i in range(0, len(shares)): shares[i] = float(temp[i]) # the variable shares is a result from last optimization net_val = np.matrix(np.zeros((len(shares), 1))) for i in range(0, len(shares) - 1): net_val[i] = shares[i] * prices[-1, i] net_val[i + 1] = shares[i + 1] # This is the 'true' net wealth init_con = float(np.sum(net_val)) # Terminal portfolio value! init_alloc = [] nassets = 6 for i in range(0, nassets): init_alloc.append(float(net_val[i] / init_con)) # Terminal asset allocation! inflation = constants.INFLATION goal = prof['goal'] * (1 + inflation)**Y reached = round(init_con / goal, 3) port["reached"].append(reached) port["alloc_percent"].append(init_alloc) Portfolio.update_portfolio( port['port_id'], { "port_id": port['port_id'], "user_email": port['user_email'], "name": prof['name'], "mean_term_wealth": port["mean_term_wealth"], "mean_var_wealth": port["mean_var_wealth"], "alloc_percent": port["alloc_percent"], "shares0": port["shares0"], "shares1": port["shares1"], "cont": port["cont"], "reached": port["reached"], "ambitious": port["ambitious"] }) return None # Simulate stock prices for each asset; each is ntrials x len(Wt) S00 = prices[-1, 0] # Initial stock price for asset 1 S01 = prices[-1, 1] # Initial stock price for asset 2 S02 = prices[-1, 2] # Initial stock price for asset 3 S03 = prices[-1, 3] # Initial stock price for asset 4 S04 = prices[-1, 4] # Initial stock price for asset 5 ntrials = 10 # start_time = clock() Sprices0 = GBM(ntrials, N, time_step, means, cov_mat, S00, 0) Sprices1 = GBM(ntrials, N, time_step, means, cov_mat, S01, 1) Sprices2 = GBM(ntrials, N, time_step, means, cov_mat, S02, 2) Sprices3 = GBM(ntrials, N, time_step, means, cov_mat, S03, 3) Sprices4 = GBM(ntrials, N, time_step, means, cov_mat, S04, 4) # print("--- %s seconds ---" % (clock() - start_time)) Sprices = np.concatenate( (np.mean(Sprices0, axis=0), np.mean(Sprices1, axis=0), np.mean(Sprices2, axis=0), np.mean(Sprices3, axis=0), np.mean(Sprices4, axis=0))) # Calculate returns from the simulated stock prices of each asset Sreturns0 = np.matrix(np.zeros((ntrials, N))) Sreturns1 = np.matrix(np.zeros((ntrials, N))) Sreturns2 = np.matrix(np.zeros((ntrials, N))) Sreturns3 = np.matrix(np.zeros((ntrials, N))) Sreturns4 = np.matrix(np.zeros((ntrials, N))) for k in range(N): Sreturns0[:, k] = (Sprices0[:, k + 1] - Sprices0[:, k]) / Sprices0[:, k] Sreturns1[:, k] = (Sprices1[:, k + 1] - Sprices1[:, k]) / Sprices1[:, k] Sreturns2[:, k] = (Sprices2[:, k + 1] - Sprices2[:, k]) / Sprices2[:, k] Sreturns3[:, k] = (Sprices3[:, k + 1] - Sprices3[:, k]) / Sprices3[:, k] Sreturns4[:, k] = (Sprices4[:, k + 1] - Sprices4[:, k]) / Sprices4[:, k] # Account for coupon rates as part of the bond ETFs' returns cr3 = constants.COUPONS[0] # CSJ average coupon rate is about 1.6% cr4 = constants.COUPONS[1] # BLV average coupon rate is about 4.5% Sreturns3 = Sreturns3 + int_rate_convert(cr3, time_step) Sreturns4 = Sreturns4 + int_rate_convert(cr4, time_step) # Cash investment i.e. savings account with constant interest rate Sreturns5 = np.ones((Sreturns0.shape[0], Sreturns0.shape[1])) * eff_rate ## Example plot of simulated asset returns #plt.plot(Sreturns0.T,alpha=0.5) #plt.show() # Calculate and reorganize total returns matrix # Notes on the matrix Returns # This is the total return matrix. For example, for 2 assets and 2 scenarios: # Row 1: asset 1 under scenario 1 # Row 2: asset 2 under scenario 1 # Row 3: asset 1 under scenario 2 # Row 4: asset 2 under scenario 2 nassets = len(tickers) + 1 temp = 1 + np.concatenate((Sreturns0, Sreturns1, Sreturns2, Sreturns3, Sreturns4, Sreturns5)) # total returns Returns = np.matrix(np.zeros((temp.shape[0], temp.shape[1]))) for i in range(0, ntrials): for j in range(0, nassets): Returns[j + i * nassets, :] = temp[i + j * ntrials, :] ### Solve the problem! lamb = prof['lamb'] if lamb == 0: lamb = 0 elif lamb == 0.25: lamb = 0.01 elif lamb == 0.5: lamb = 0.05 elif lamb == 0.75: lamb = 0.4 elif lamb == 1: lamb = 0.99 dis_inc = prof['dis_inc'][ -1] * 12 * time_step # Investor's disposable income within a single trading period if Y - T == 0: init_con = prof['init_con'] # intial contribution to goal (User input) init_alloc = prof[ 'init_alloc'] # Recommended initial alloc (WEBAPP INPUT) port['alloc_percent'].append(init_alloc) else: temp = port['shares1'][-1] shares = np.matrix(np.zeros((6, 1))) for i in range(0, len(shares)): shares[i] = float(temp[i]) # the variable shares is a result from last optimization net_val = np.matrix(np.zeros((len(shares), 1))) for i in range(0, len(shares) - 1): net_val[i] = shares[i] * prices[-1, i] net_val[i + 1] = shares[i + 1] # This is the 'true' net wealth init_con = float(np.sum(net_val)) init_alloc = [] for i in range(0, nassets): init_alloc.append( float(net_val[i] / init_con)) # New allocation restriction at "t = 0" # Financial goal (target) accounted for inflation (assumed to be constant at 2% annual) inflation = constants.INFLATION goal = prof['goal'] * (1 + int_rate_convert(inflation, time_step))**( (Y - T) / time_step) # Optimize! # start_time = clock() opt_soln, P, q = sp(nassets, ntrials, Y, N, lamb, dis_inc, Returns, init_con, goal, eff_fees, init_alloc) # print("--- %s seconds ---" % (clock() - start_time)) # Measure run time dv = np.matrix(opt_soln['x']) mean_term_wealth = round(float(q.T * dv), 2) # Mean of terminal wealths mean_var_wealth = round(float(dv.T * P * dv)**0.5, 2) # Variance of wealths # Determine if the goal is "ambitious" diff = goal - mean_term_wealth if diff > 0: ambitious = 1 else: ambitious = 0 # If the goal is ambitious, how much extra should the investor be contributing to meet the goal (monthly)? if ambitious == 1 and (N - 1) != 0: extra_dis_inc = (diff / (N - 1)) / (12 * time_step) extra_time = diff / mean_term_wealth * T else: extra_dis_inc = 0 extra_time = 0 importance = int(prof['importance']) if ambitious == 1 and importance == 1: extra_dis_inc = 0 elif ambitious == 1 and importance == 2: extra_dis_inc = 0.25 * extra_dis_inc extra_time = 0.75 * extra_time elif ambitious == 1 and importance == 3: extra_dis_inc = 0.5 * extra_dis_inc extra_time = 0.5 * extra_time elif ambitious == 1 and importance == 4: extra_dis_inc = 0.75 * extra_dis_inc extra_time = 0.25 * extra_time elif ambitious == 1 and importance == 5: extra_time = 0 # t = 0 # of shares shares0 = dv[0:6] shares0[0:-1] = shares0[0:-1] / Sprices[:, 0] # t = 1 average asset allocation across all scenarios # (need this for next optimization) if N > 1: collect = np.matrix(np.zeros((nassets, ntrials))) ctr = 0 for s in range(0, ntrials): ctr = ctr + (nassets + 1) collect[:, s] = dv[ctr:ctr + nassets] avg_alloc = np.mean(collect, axis=1) alloc_percent = avg_alloc / np.sum(avg_alloc) shares1 = avg_alloc shares1[0:-1] = avg_alloc[ 0:-1] / Sprices[:, 1] # Cash investment is left in units of $ for i in range(0, nassets - 1): shares1[i] = shares1[i] * (1 - trans_costs[i] ) # take away transaction costs # Additional contribution required by next time step cont = round(float(dv[nassets:nassets + 1]), 2) else: collect = np.matrix(np.zeros((nassets, ntrials))) ctr = 0 for s in range(0, ntrials): ctr = ctr + nassets collect[:, s] = dv[ctr:ctr + nassets] avg_alloc = np.mean(collect, axis=1) alloc_percent = avg_alloc / np.sum(avg_alloc) shares1 = avg_alloc shares1[0:-1] = avg_alloc[ 0:-1] / Sprices[:, 1] # Cash investment is left in units of $ for i in range(0, nassets - 1): shares1[i] = shares1[i] * (1 - trans_costs[i] ) # take away transaction costs # Additional contribution required by next time step cont = 0 # Since there will no next time period # % reached of financial goal target reached = float(init_con / goal) reached_dollar = round(reached * goal, 2) # In dollar value # Time weighted rate of return (TWRR) if Y - T == 0: hprr = 0 twrr = 0 if Y - T != 0: # holding period return i.e. rate of return on the portfolio in a single time period hprr = round((reached - port['reached'][-1]) / port['reached'][-1] - port['cont'][-1] / (goal * port['reached'][-1]), 3) twrr = round((1 + port['twrr'][-1]) * (1 + hprr) - 1, 3) # Add elements into lists for historical view prof["horizon"].append((float(prof["horizon"][-1]) + extra_time)) prof["dis_inc"].append((prof["dis_inc"][-1] + extra_dis_inc)) # Update profile by changing the length of time remaining Profile.update_profile( prof['port_id'], { "port_id": prof['port_id'], "user_email": prof['user_email'], "name": prof['name'], "goal": prof['goal'], "horizon": prof['horizon'], "time_left": prof['time_left'] - time_step + extra_time, "init_con": prof['init_con'], "dis_inc": prof['dis_inc'], "init_alloc": init_alloc, "lamb": prof['lamb'], "importance": prof['importance'] }) # Update portfolio (export) shares0_ = [] shares1_ = [] alloc_percent_ = [] for i in range(0, nassets): shares0_.append(float(shares0[i])) shares1_.append(float(shares1[i])) alloc_percent_.append(round(float(alloc_percent[i]) * 100)) # Add elements into lists for historical view port["mean_term_wealth"].append(mean_term_wealth) port["mean_var_wealth"].append(mean_var_wealth) port["alloc_percent"].append(alloc_percent_) port["shares0"].append(shares0_) port["shares1"].append(shares1_) port["cont"].append(cont) port["reached"].append(reached) port["reached_dollar"].append(reached_dollar) port["hprr"].append(hprr) port["twrr"].append(twrr) port["ambitious"].append(ambitious) Portfolio.update_portfolio( port['port_id'], { "port_id": port['port_id'], "user_email": port['user_email'], "name": prof['name'], "mean_term_wealth": port["mean_term_wealth"], "mean_var_wealth": port["mean_var_wealth"], "alloc_percent": port["alloc_percent"], "shares0": port["shares0"], "shares1": port["shares1"], "cont": port["cont"], "reached": port["reached"], "reached_dollar": port["reached_dollar"], "hprr": port["hprr"], "twrr": port["twrr"], "ambitious": port["ambitious"] }) temporary = shares1 - shares0 list1 = port['reached_dollar'] list2 = port['cont'] list3 = [i * time_step for i in range(len(list1))] plot_data = pd.DataFrame({ 'reached_dollar': list1, 'cont': list2, 'time': list3 }) list4 = [ 'Large Cap Equity', 'Small Cap Equity', 'International Equity', 'Short-term Bonds', 'Long-term Bonds', 'Cash Investments' ] list5 = port['alloc_percent'][-2] alloc_data = pd.DataFrame({'category': list4, 'percent': list5}) pie_plot = Donut(data=alloc_data, label="category", values="percent", hover_text="percent") line_plot = Line(data=plot_data, x="time", y="reached_dollar", xlabel="Time Steps Passed", ylabel="Dollar Value of Goal Reached") line_plot.circle(plot_data['time'], plot_data['reached_dollar'], size=5, color='red', line_color="red", fill_alpha=0.5) bar_plot = Line(data=plot_data, x="time", y="cont", xlabel="Time Steps Passed", ylabel="Contribution at Every Time Step") bar_plot.circle(plot_data['time'], plot_data['cont'], size=5, color='red', line_color="red", fill_alpha=0.5) return pie_plot, line_plot, bar_plot, temporary