From cb72af178108e76158a1aa44b357e5e0dc617e13 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Fri, 9 Oct 2020 10:39:17 -0400 Subject: [PATCH] Implement marketsim without commission and impact --- marketsim/marketsim.py | 83 +++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/marketsim/marketsim.py b/marketsim/marketsim.py index 23f9468..98387a6 100644 --- a/marketsim/marketsim.py +++ b/marketsim/marketsim.py @@ -29,9 +29,11 @@ import pandas as pd import numpy as np import datetime as dt import os +import sys from util import get_data, plot_data from optimize_something.optimization import calculate_stats + def read_orders(orders_file): """ Parser orders into the form: @@ -48,35 +50,78 @@ def read_orders(orders_file): 2011-01-10,AAPL,SELL,1500 """ orders = pd.read_csv(orders_file, + index_col=['Date'], dtype='|str, str, str, i4', - parse_dates=["Date"]) + parse_dates=['Date']) orders.sort_values(by="Date", inplace=True) return orders def get_order_book_info(orders): """Return start_date, end_date, and symbols (as a list).""" - start_date = orders.iloc[0].Date - end_date = orders.iloc[0].Date + start_date = orders.index[0] + end_date = orders.index[-1] symbols = sorted(list((set(orders.Symbol.tolist())))) return start_date, end_date, symbols +def get_portfolio_value(holding, prices): + """Calculate the current portofolio value.""" + value = 0 + for ticker, shares in holding.items(): + if ticker == 'cash': + value += shares + else: + value += shares * prices[ticker] + return value + + +def handle_orders(orders, holding, adj_closing_prices): + """Process the orders.""" + for date, order in orders.iterrows(): + symbol, order, shares = order + adj_closing_price = adj_closing_prices[symbol] + cost = shares * adj_closing_price + if order == "BUY": + # print(f"Buy {shares:6} of {symbol:4} on {date}") + holding['cash'] -= cost + holding[symbol] += shares + elif order == "SELL": + # print(f"Sell {shares:6} of {symbol:4} on {date}") + holding['cash'] += cost + holding[symbol] -= shares + else: + raise Exception("Unexpected order type.") + + def compute_portvals(orders_file="./orders/orders-01.csv", start_val=1000000, commission=9.95, impact=0.005): orders = read_orders(orders_file) start_date, end_date, symbols = get_order_book_info(orders) - # In the template, instead of computing the value of the portfolio, we just - # read in the value of IBM over 6 months - start_date = dt.datetime(2008, 1, 1) - end_date = dt.datetime(2008, 6, 1) - portvals = get_data(['IBM'], pd.date_range(start_date, end_date)) - portvals = portvals[['IBM']] # remove SPY + # Tickers in the orderbook over the date_range in the order book. + prices = get_data(symbols, pd.date_range(start_date, end_date)) + prices['Portval'] = pd.Series(0.0, index=prices.index) + # A dictionary to keep track of the assets we are holding. + holding = {s: 0 for s in symbols} + holding['cash'] = start_val + + orders_processed = 0 + for date, values in prices.iterrows(): + if date in orders.index: + current_orders = orders.loc[date:date] + orders_processed += current_orders.shape[0] + handle_orders(current_orders, holding, values) + + # Compute portfolio value at the end of day. + values['Portval'] = get_portfolio_value(holding, values) + + # Make sure we have processed all orders. If there was an order on a + # non-trading day we would currently not handle it. + assert(orders.shape[0] == orders_processed) + + portvals = prices[['Portval']] return portvals - # Don't know why this was in template. Keep for now. - # rv = pd.DataFrame(index=portvals.index, data=portvals.values) - # return rv def test_code(): @@ -89,20 +134,14 @@ def test_code(): else: raise Exception("warning, code did not return a DataFrame") - # One way of getting the portfolio dates - # print(portvals.index[0]) - - # Get portfolio stats. - start_date = dt.datetime(2008, 1, 1) - end_date = dt.datetime(2008, 6, 1) - - + start_date = portvals.index[0] + end_date = portvals.index[-1] cum_ret, avg_daily_ret, \ - std_daily_ret, sharpe_ratio = calculate_stats(portvals.to_frame(), [1]) + std_daily_ret, sharpe_ratio = calculate_stats(portvals.to_frame(), [1]) spy = get_data(['SPY'], pd.date_range(start_date, end_date)) cum_ret_SPY, avg_daily_ret_SPY, \ - std_daily_ret_SPY, sharpe_ratio_SPY = calculate_stats(spy, [1]) + std_daily_ret_SPY, sharpe_ratio_SPY = calculate_stats(spy, [1]) # Compare portfolio against $SPY print(f"Date Range: {start_date} to {end_date}")