#!/usr/bin/env python # coding: utf-8 # # Financial Investment Planning # # ## Introduction # # A financial strategy produces orders. These orders are transmitted to a broker. The broker checks the validity of the order and sends each valid order to the market and sends back a transaction. Every time we buy some shares the broker opens a position with the buy price. If we sell some shares this should be related to a position in order to calculate the profit. Therefore the position information is important mainly in order to calculate profit. We call a buy/sell matching a trade. The sum of all positions are called the portfolio. # # # # #### Assumptions for this notebook # # + no user account information # + stock market (consisting of 23 symbols), # + only market orders, # + no short positions, # + the price is determined by the historical *daily* candle data. # # TableOfContents # + [Order and Transaction](#OrderAndTransaction) # + [Portfolio](#Portfolio) # + [Dividend and Split](#DividendAndSplit) # + [Rebalance](#Rebalance) # + [Backtest](#Backtest) # + [Index Calculation](#IndexCalculation) # + [Risk Calculation](#RiskCalculation) # + [Visualization](#Visualization) # + [To Do](#ToDo) # ## Symbol, the price of a symbol # *Symbols* are the financial instrument that we can invest in. At a given point, there is no known price but the price depends on the buyers' and sellers' bid prices. The price of a symbol is **calculated** by the market for a given order. Therefore the price of a symbol may change constantly. Usually symbols have *candle data* (5min, 1day) etc.. storing open, close, low and high values. # In[2]: import pandas as pd import pandas_datareader.data as web from pandas.tseries.offsets import BDay import datetime import warnings from copy import deepcopy import numpy as np get_ipython().run_line_magic('matplotlib', 'inline') from scipy.stats import linregress import matplotlib.pyplot as plt from math import sqrt import scipy.stats as st # In[3]: symbols = ['AEFES.IS', 'AKENR.IS', 'AKSA.IS', 'AKBNK.IS', 'GARAN.IS', 'MGROS.IS', 'THYAO.IS', 'TRKCM.IS', 'ZOREN.IS', 'XU100.IS', 'ALARK.IS', 'CIMSA.IS', 'HALKB.IS', 'TKFEN.IS', 'FROTO.IS', 'DOAS.IS', 'KARSN.IS', 'TMSN.IS', 'YKBNK.IS', 'VAKBN.IS', 'ULKER.IS', 'TCELL.IS', 'SISE.IS', 'EREGL.IS', 'PETKM.IS', 'BJKAS.IS', 'AFYON.IS', 'KOZAL.IS', 'GOODY.IS', 'VAKFN.IS'] start = datetime.datetime(2015,1,1) end = datetime.datetime.now() # In[4]: prices = pd.DataFrame() for symbol in symbols: f = web.DataReader(symbol, 'yahoo', start, end) f['symbol'] = symbol prices = pd.concat([prices, f]) prices; # How do I calculae the adjusted closing price? # http://www.investopedia.com/ask/answers/06/adjustedclosingprice.asp # # - **cash dividend (d)**: closing_price - d # - **share dividend (x:y)**: closing_price* (y+x)/x # - **share split (x:y)**: closing_price * y/x # In[5]: prices = prices.pivot(columns="symbol", values="Adj Close") # In[6]: prices.tail() # In[7]: dividends_and_splits= pd.DataFrame() for symbol in symbols: f = web.DataReader(symbol, 'yahoo-actions') if len(f) > 0: f['symbol'] = symbol dividends_and_splits = pd.concat([dividends_and_splits, f]) # In[8]: Splits = dividends_and_splits[dividends_and_splits["action"] == 'SPLIT'] Dividends = dividends_and_splits[dividends_and_splits["action"] == 'DIVIDEND'] Splits # In[9]: Dividends.head() # ## OrderAndTransaction # [TOC](#TableOfContents) # # We give an *order* buy/sell a symbol of some amount of *shares* to a *brocker* which accepts this order and transmit to the market. The response of an order is a *transaction* and it contains the status of the orders, the price and the commissions. # In[10]: order = {'symbol':'AKBNK.IS', 'shares':100, 'action':'buy', 'time':'2016-05-12'} transaction = {'status':'accepted', 'symbol': 'GARAN', 'action':'buy', 'shares':100, 'price':5.2, 'commision':2, 'time':'2016-05-13'} # In real situation, when we pass the order to a brocker, the brocker responds with a transaction. We will simulate this situation with the following function. # In[11]: # TODO: check user account's free cash and update it accordingly def execute_order(order, holdings): 'Simulates the broker/market and creates a transaction.' transaction = deepcopy(order) symbol, shares, time = order['symbol'], order['shares'], order['time'] if order['action']=='sell': if (holdings is None or not (symbol in holdings.index) or holdings.loc[symbol]['shares'] < order['shares']): warnings.warn('Your order ' + str(order) + ' is rejected.') transaction['status'] = 'rejected' return transaction transaction['status'] = 'accepted' price = get_price(symbol, time) commision = commision_calculation(order, price) transaction['price'], transaction['commision'] = price, commision return transaction def get_price(symbol, time = None, price_source=prices): 'Returns the last price of the symbol. We assume that prices is a pandas object.' if time is None: time = str(datetime.datetime.now()) try: closest_time = (price_source.index[time >= price_source.index]).sort_values()[-1] return price_source[symbol].loc[closest_time] except ValueError: raise ValueError("We don't have historical data for an order at " + order_time) except IndexError: raise IndexError("We don't have historical data for an order at " + order_time) def commision_calculation(order, price, commission_ratio=0.0001): 'Calculates commision to be paid.' return order['shares']*price*commission_ratio def current_time(prices=prices): 'Returns the maximum date from the prices table.' return max(prices.index).strftime("%Y-%m-%d") # In[12]: execute_order(order, []) # TODO: make a real example # # ## Positions and Trades # # Every time we have an accepted buy order we create a *position*. A trade is a *matching* of buy/sell transactions of the same symbol. We need these trade objects in order to calculate the *current* cost of the portfolio and realized profits. # # Example # --- # Transactions # + $t_1$: (GARAN, buy, 100, 7.02) # + $t_2$: (GARAN, buy, 200, 7.05) # + $t_3$: (GARAN, sell, 150, 7.10) # # Cost # + $t_1$: 702 # + $t_2$: 702 + 1410 # + $t_3$: 702 + 1410 - $x $ # # In order to calculate $x $, we need to match the sell order at *t3* with the buy orders *t1* and *t2*. For the matching we assume first-in first-out rule. We first match the sell order at *t3* with the buy order at *t1* and then the remaining with the order at *t2*. # In[13]: def apply_transaction(transaction, positions, trades): 'Update positions and trades with the transaction.' if transaction["status"] == "accepted": symbol, action, shares = transaction["symbol"], transaction["action"], transaction["shares"] price, time = transaction["price"], transaction['time'] if action == "buy": position = {'type':'long', 'symbol':symbol, 'initial_shares':shares, 'shares':shares, 'price': price, 'cost': price*shares, 'time': time} positions.append(position) else: match_sell_transaction_to_position(transaction, positions, trades) def match_sell_transaction_to_position(transaction, positions, trades): """We will select the positions with the same symbol and order them according to time desc. Until we finish the order's volume we match it with a position. If a position is fulfilled, we continue with the next one in the order. We create a trade, we remove the amount traded from the position. If the position's volume is empty we remove the position from the list. Assumption: Since the broker rejects sells orders greater then our holdings, we assume that transaction.shares <= sum(positions.shares).""" sorted(positions, key=lambda k: k['time']) shares, time, symbol = transaction["shares"], transaction["time"], transaction["symbol"] for position in positions: if not shares > 0: break if position["symbol"] == transaction["symbol"] and position["shares"] > 0: traded_shares = min(shares, position['shares']) buy_price, sell_price = position["price"], transaction["price"] profit = (sell_price - buy_price) * traded_shares trade = {'symbol':symbol, 'shares':traded_shares, 'time':time, 'buy_price': buy_price, 'sell_price': sell_price, 'profit': profit} trades.append(trade) shares -= traded_shares position["shares"] = position["shares"] - traded_shares position["cost"] = position["shares"] * buy_price # In[14]: def calculate_portfolio(positions): if len(positions) == 0: return [] else: temp = pd.DataFrame(positions) return temp.pivot_table(index='symbol', aggfunc='sum', values=["cost", 'shares']) # In[15]: orders = [ {'symbol':'GARAN.IS', 'shares':100, 'action':'buy', 'time':'2016-05-15 10:00:00'}, {'symbol':'GARAN.IS', 'shares':100, 'action':'buy', 'time':'2016-05-20 11:00:00'}, {'symbol':'GARAN.IS', 'shares':120, 'action':'sel', 'time':'2016-05-22 09:50:00'}, {'symbol':'AKBNK.IS', 'shares':100, 'action':'buy', 'time':'2016-05-20 16:30:00'}, {'symbol':'AKBNK.IS', 'shares':100, 'action':'buy', 'time':'2016-05-21 10:57:23'}, {'symbol':'AKBNK.IS', 'shares':220, 'action':'buy', 'time':'2016-05-24 10:00:00'}, {'symbol':'AKBNK.IS', 'shares':200, 'action':'sell', 'time':'2016-05-24 12:00:00'}, {'symbol':'AKBNK.IS', 'shares':110, 'action':'sell', 'time':'2016-05-27 00:00:00'}, {'symbol':'ZOREN.IS', 'shares':50, 'action':'sell', 'time':'2016-05-28 00:00:00'}, ] pd.DataFrame(orders) # # Account # In[16]: def execute_orders(orders, transactions, positions, trades): """Calls execute_order function for each order in the order list. Updates transaction, position and trade lists. """ portfolio = calculate_portfolio(positions) for order in orders: transaction = execute_order(order, portfolio) transactions.append(transaction) apply_transaction(transaction, positions, trades) portfolio = calculate_portfolio(positions) # In[17]: transactions, positions, trades = [], [], [] execute_orders(orders, transactions, positions, trades); # In[18]: pd.DataFrame(transactions) # In[19]: def summary(transactions, positions, trades): print "***Transactions***".center(70) print '-' * 70 print pd.DataFrame(transactions) print '' print "***Positions***".center(70) print '-' * 70 print pd.DataFrame(positions) print '' print "***Trades***".center(70) print '-' * 70 print pd.DataFrame(trades) summary(transactions, positions, trades) # ## Portfolio # [TOC](#TableOfContents) # # An *asset* is financial instruments that we own. The assets can be calculated using transaction reports. *Market value* is the current value of an asset. It represent the value as if we want to sell the asset right now. It is calculated by some estimation of the current price of the corresponding symbol. The collection of assets that we own are called our *portfolio*. # # ##### Portfolio Market Value # In[20]: portfolio = calculate_portfolio(positions) portfolio # In[21]: x = pd.Series({"GARAN.IS": get_price("GARAN.IS"), "AKBNK.IS": get_price("AKBNK.IS")}) # In[22]: portfolio["current_price"] = x # In[23]: portfolio["shares"] * portfolio["current_price"] # In[24]: def get_prices(assets, time = None): return {symbol: get_price(symbol, time) for symbol in assets} pd.Series(get_prices(portfolio.index)) # In[25]: portfolio["shares"] / 2 # In[26]: def current_performance(portfolio, time=None): portfolio["current_price"] = pd.Series(get_prices(portfolio.index, time)) portfolio["market_value"] = portfolio["shares"] * portfolio["current_price"] portfolio["profit"] = portfolio["market_value"] - portfolio["cost"] total_market_value = portfolio.market_value.sum() portfolio["weight"] = portfolio["market_value"] / total_market_value # In[27]: current_performance(portfolio) portfolio # In[28]: def historical_performance(transactions, prices): "Returns historical market value of a portfolio." starting_date = min(transactions, key=lambda x: x["time"])["time"] ending_date = datetime.datetime.now() symbols = set([t["symbol"] for t in transactions if t["status"]=="accepted"] ) symbol_price = (prices.loc[starting_date:ending_date][list(symbols)]) holdings_list = holdings(transactions) return (holdings_list .fillna(method='ffill') .reindex(symbol_price.index.union(holdings_list.index)) .fillna(method='ffill') .reindex(symbol_price.index) .mul(symbol_price).sum(axis=1) ) def holdings(transactions): t = pd.DataFrame(transactions) t["sign"] = t["action"].apply(lambda x: 1 if x=="buy" else -1) t["signed_shares"] = t["sign"] * t["shares"] t["time"] = pd.to_datetime(t["time"]) return (t.query("status == 'accepted'") .pivot_table(values="signed_shares", columns="symbol", index="time") .cumsum() ) # In[29]: symbols = set([t["symbol"] for t in transactions if t["status"]=="accepted"] ) starting_date = min(transactions, key=lambda x: x["time"])["time"] ending_date = datetime.datetime.now() symbol_price = (prices.loc[starting_date:ending_date][list(symbols)]) # In[30]: def compare_with_benchmark(historical_value, benchmark_symbol="XU100.IS"): algo = (historical_value[historical_value > 0].pct_change() + 1).cumprod() algo.name = "Algorithm" algo.plot(legend=True) print "Algorithm performance:", (algo.ix[-1] - 1)*100 start_date = historical_value.index[0] benchmark = (prices[benchmark_symbol].dropna().pct_change().loc[start_date:] + 1).cumprod() benchmark.name = "Benchmark" benchmark.plot(legend = True, figsize=[15,10]) print "Benchmark performance:", (benchmark.ix[-1] - 1)*100 # In[31]: historical_performance(transactions, prices).plot(figsize= [15,10]) # ## DividendAndSplit # [TOC](#TableOfContents) # In[32]: def paid_dividends(transactions, dividends): start_date = min(transactions, key=lambda x: x["time"])["time"] end_date = datetime.datetime.now() H= holdings(transactions) H.index = pd.to_datetime(H.index) H = (H.reindex(pd.date_range(start=start_date, end=end_date, freq='D')) .fillna(method='ffill') .fillna(0)) return (pd.melt(Dividends[Dividends.symbol.apply(lambda x: x in H.columns)] .reset_index() .rename(columns={'index':'time'}) .pivot_table(values="value", columns="symbol", index = 'time') .reindex(H.index) .mul(H) .reset_index() .rename(columns={'index':'time'}) , id_vars = 'time', var_name= 'symbol', value_name='paid_dividend' ) ).query("paid_dividend > 0") # In[33]: paid_dividends(transactions, Dividends) # ## Rebalance # [TOC](#TableOfContents) # In[34]: def rebalance(portfolio, target_weights, target_value = None, time = None): """New weights is a dictionary of {symbol: weights}. Creates orders to make portfolio's weight 'new weights'. Time indicates execution of rebalance operation. """ if time is None: time = current_time() if target_value is None: target_value = portfolio.market_value.sum() if len(portfolio)>0: target_weights = {s:target_weights[s] if s in target_weights.keys() else 0 for s in set(portfolio.index | target_weights.keys())} # check if the weights sum up to 1. assert(sum(target_weights.values()) == 1) rebalance_orders = [] target_market_value = {s:target_weights[s]*target_value for s in target_weights.keys()} for symbol, weight in target_weights.iteritems(): if len(portfolio)>0 and symbol in portfolio.index: delta_value = target_market_value[symbol] - portfolio["market_value"].loc[symbol] if weight: delta_lot = int(abs(delta_value / get_price(symbol, time))) else: delta_lot = portfolio["shares"].loc[symbol] if delta_lot: if delta_value > 0: action = 'buy' else: action = 'sell' order = {'symbol': symbol, 'shares': delta_lot, 'action': action, 'time': time} rebalance_orders.append(order) else: delta_lot = int(target_market_value[symbol] / get_price(symbol, time)) order = {'symbol': symbol, 'shares': delta_lot, 'action': 'buy', 'time': time} rebalance_orders.append(order) return rebalance_orders # In[35]: portfolio # In[36]: portfolio["market_value"].loc['AKBNK.IS'] # In[37]: orders_to_rebalance = rebalance(portfolio, {"AKBNK.IS":0.50, "GARAN.IS": 0.20, "ZOREN.IS": 0.30 }) pd.DataFrame(orders_to_rebalance) # In[38]: execute_orders(orders_to_rebalance, transactions, positions, trades) summary(transactions, positions, trades) # In[39]: portfolio = calculate_portfolio(positions) portfolio # In[40]: current_performance(portfolio) portfolio # In[41]: portfolio, transactions, positions, trades = [], [], [], [] orders_to_rebalance = rebalance([], {'AKBNK.IS': 0.50, 'GARAN.IS': 0.50}, 1000000) pd.DataFrame(orders_to_rebalance) # In[42]: execute_orders(orders_to_rebalance, transactions, positions, trades) summary(transactions, positions, trades) # In[43]: portfolio = calculate_portfolio(positions) current_performance(portfolio) portfolio # # Backtest # [TOC](#TableOfContents) # # # ## Algoritm Example: Beta # # - Repeat each month the following procedure: # - Calculate beta values of each stocks against BIST 100 index. **time window** *for beta calculation?* # - Choose 10 stocks with the highest beta value. # - Rebalance the portfolio to be equal weight. # In[44]: capital_base = 100000 start, end = np.datetime64('2016-01-20 00:00:00'), np.datetime64('2016-07-18 00:00:00') running_points = [date for date in prices.index if start <= date <= end] # http://markets.ft.com/data/lexicon/term/beta # # We use Beta 5Y from the Multex Ratios and Statistics table, which is provided to us by Reuters. The Market Guide Beta is the slope of the 60 month regression line of the percentage price change of the stock relative to the percentage price change of the S&P 500. Beta values are not calculated if less than 24 months of pricing is available. # In[45]: def beta(price_data, start, end): returns = (prices.loc[start:end] .resample('3BMS', 'first') .pct_change() .dropna()) beta_values = {symbol: linregress(returns["XU100.IS"], returns[symbol])[0] for symbol in price_data.columns if symbol != 'XU100.IS'} return beta_values # In[46]: portfolio, transactions, positions, trades = [], [], [], [] for t in running_points: time = t.strftime("%Y-%m-%d") if t in pd.date_range(start=running_points[0], periods=12, freq='BM'): print t print '-'*20 beta_values = beta(prices, prices.index[0], t) for s in sorted(beta_values, key=beta_values.get, reverse=True): print s, beta_values[s] n = 5 new_weights = {s: 1./n for s in sorted(beta_values, key=beta_values.get, reverse=True)[:n]} print new_weights target_value = None if len(portfolio) else capital_base if len(portfolio) > 0: current_performance(portfolio, time) rebalance_orders = rebalance(portfolio, new_weights, target_value, time) print "Before Orders" print "-"*20 print portfolio print " " print "Orders" print "-"*20 print pd.DataFrame(rebalance_orders) print " " execute_orders(rebalance_orders, transactions, positions, trades) portfolio = calculate_portfolio(positions) current_performance(portfolio, time) print "After Orders" print "-"*20 print portfolio print " " portfolio = calculate_portfolio(positions) # In[47]: summary(transactions, positions, trades) # In[48]: current_performance(portfolio, time = '2016-08-04') portfolio # In[49]: portfolio.market_value.sum() # In[50]: paid_dividends(transactions, Dividends) # In[51]: dividends_and_splits.query("symbol == 'GOODY.IS'") # In[52]: temp = historical_performance(transactions, prices) compare_with_benchmark(temp) # ## Momentum Strategy # # **Initialization** # 1. Calculate absolute momentum (return) of funds and select *n* stocks with the highest moment. # 2. Equal weight # # **Every month** # 1. If the moment of a stock in the portfolio is negative then replace it with the best option # 2. Rebalance # In[53]: # TODO: fill nan values def momentum(price_data, start, end): momentum_values = {symbol: (price_data[symbol].loc[end] - price_data[symbol].loc[start]) for symbol in price_data.columns if symbol != 'XU100.IS'} return momentum_values # In[54]: pd.date_range(start=running_points[0], periods=12, freq='BM') # In[55]: portfolio, transactions, positions, trades = [], [], [], [] for t in running_points: time = t.strftime("%Y-%m-%d") if t in pd.date_range(start=running_points[0], periods=12, freq='BM'): print t print '-'*20 momentum_values = momentum(prices, t - pd.DateOffset(months=5) - 1*BDay(), t - 1*BDay()) for s in sorted(momentum_values, key=momentum_values.get, reverse=True): print s, momentum_values[s] n = 5 if len(portfolio) == 0: new_weights = {s: 1./n for s in sorted(momentum_values, key=momentum_values.get, reverse=True)[:n]} print new_weights target_value = capital_base rebalance_orders = rebalance(portfolio, new_weights, target_value, time) else: potentials = {key:value for key, value in momentum_values.iteritems() if key not in portfolio.index } new_weights = {} for symbol in portfolio.index: if portfolio["market_value"].loc[symbol]: if momentum_values[symbol] < 0: s = sorted(potentials, key=potentials.get, reverse=True)[0] print symbol + "out" + s + "in" new_weights[s] = 1./n del potentials[s] else: new_weights[symbol] = 1./n current_performance(portfolio, time) print new_weights rebalance_orders = rebalance(portfolio, new_weights, None, time) print "Before Orders" print "-"*20 print portfolio["market_value"].sum() if len(portfolio) else 0 print portfolio print " " print "Orders" print "-"*20 print pd.DataFrame(rebalance_orders) print " " print sum([order["shares"]*get_price(order["symbol"], time) for order in rebalance_orders if order["action"]=="buy"]) print sum([order["shares"]*get_price(order["symbol"], time) for order in rebalance_orders if order["action"]=="sell"]) execute_orders(rebalance_orders, transactions, positions, trades) portfolio = calculate_portfolio(positions) current_performance(portfolio, time) print "After Orders" print "-"*20 print portfolio print portfolio["market_value"].sum() print " " portfolio = calculate_portfolio(positions) # In[56]: summary(transactions, positions, trades) # In[57]: current_performance(portfolio, time = '2016-07-18') portfolio # In[58]: portfolio["market_value"].sum() # In[59]: paid_dividends(transactions, Dividends) # In[60]: temp = historical_performance(transactions, prices) compare_with_benchmark(temp) # ## IndexCalculation # [TOC](#TableOfContents) # # We can withdraw / deposit money into the portfolio or while rebalancing some amount of free cash can left. In this kind of situations it is hard to calculate the performance of a portfolio. To overcome this situation one way is to calculate an index value and to calculate performance over this index value instead of market value. # In[61]: historical_performance(pd.DataFrame(transactions)[0:5].to_dict("records"), prices)["2016-02-25":"2016-03-02"] # In[62]: historical_performance(transactions, prices)["2016-02-25":"2016-03-02"] # In[63]: pd.pivot_table(pd.DataFrame(transactions), values="shares", index="time", columns="symbol") # ## RiskCalculation # [TOC](#TableOfContents) # ### Value-at-Risk # # The value-at-risk is a measure of how much money can be lost for a period of time. Of course there is a (very small) probability that we can lost **all** our money. We can talk about the distrubition of the money that we can lost. For the value-at-risk we choose a probability say 99% and say that we will not loose money more then $ x $ with probability 99%. # # # There can be different methods to *estimate* the *risk*: # * Historical method: # * The Variance-Covariance Method: # * Simulation: # # # One way of chosing a methods is to test against historical data which is called *backtesting*. # In[64]: from matplotlib.dates import AutoDateLocator from matplotlib.ticker import MultipleLocator # In[65]: # Historical method returns = historical_performance(transactions, prices).pct_change().dropna() returns.hist(bins=50, figsize=(15,10)) var_historic = returns.quantile(q = 0.01) var_historic # In[66]: # The Variance-Covariance Method mu = np.mean(returns) sigma = np.std(returns) var_parametric = st.norm.ppf(0.01) * sigma var_parametric # In[67]: # Monte Carlo Simulation samples = np.random.normal(mu, sigma, 100) var_montecarlo = np.percentile(samples, 0.01) var_montecarlo # In[68]: ax = returns.plot(kind='bar', title ="V comp",figsize=(15,10),legend=True, fontsize=12) ax.set_xlabel("Hour",fontsize=12) AutoDateLocator() loc = MultipleLocator(base=10.0) ax.xaxis.set_major_locator(loc) ax.set_ylabel("V",fontsize=12) plt.axhline(y=var_historic, color='r', linestyle='-') plt.axhline(y=var_parametric, color='r', linestyle='-') plt.axhline(y=var_montecarlo, color='r', linestyle='-') plt.show() # ### Stress Testing # ### Risk Profile # # The risk profile is a way of categorizing portfolios according their risk. Risk profile of a portfolio depends on its *yearly volatility*. The yearly volatily is calculated by multiplying the standard deviation of weekly returns by square root of 52 (number of weeks in a year). # # Cutoff points for yearly volatility is: [0, 0.5, 2, 5, 10, 15, 25] # In[69]: historical_performance(transactions, prices).pct_change().resample("W-MON", label="left" ,closed="left").sum().plot() # In[70]: historical_performance(transactions, prices).pct_change().resample("W-MON", label="left" ,closed="left").sum().cumsum().plot() # In[71]: def calculate_risk_value(historical_prices): return np.std((historical_prices.pct_change() + 1).resample("W-MON", label="left" ,closed="left").prod() - 1) * sqrt(52) risk_value = calculate_risk_value(historical_performance(transactions, prices)) * 100 # In[72]: from bisect import bisect RiskLevels = [0, 0.5, 2, 5, 10, 15, 25] bisect(RiskLevels, risk_value) # ## Visualization # [TOC](#TableOfContents) # In[73]: #sparkline historical_prices = historical_performance(transactions, prices) weekly_returns = ((historical_prices.pct_change() + 1).resample("W-MON", label="left" ,closed="left").prod() - 1) plt.plot(weekly_returns) plt.axis("off"); plt.savefig("trend.png") # ## ToDo # [TOC](#TableOfContents) # # ###### Overview # # - Order lifecycle # - Transaction and position # - Strategies: Technical vs Fundemantal # - Trading algorithms from interactive brokers # # ###### Dividend and Split # - Implied transactions # # ###### Portfolio Calculation # # - plot stock prices annotated by dividend and splits # - compute dividend and split automatically: # + get close prices (not adjusted) # + create split and dividend transactions? # + calculate event-adjusted share count # + calculate paid dividend # # ###### Algorithm # # - blacklist for algorithms # - parameter search # # ###### Index Calculation # - Overview # - index normalization # # ###### Risk Calculation # - Value at Risk # # ###### Misc # - styling