From e9f4b6be02f736027887d79a5e5211822ba56d67 Mon Sep 17 00:00:00 2001 From: Charlie Date: Sun, 29 Sep 2024 11:43:29 -0700 Subject: [PATCH] ready for dockerize --- marketsim.py | 163 ----------------------------------------------- requirements.txt | Bin 924 -> 1962 bytes 2 files changed, 163 deletions(-) delete mode 100644 marketsim.py diff --git a/marketsim.py b/marketsim.py deleted file mode 100644 index 91c0b38..0000000 --- a/marketsim.py +++ /dev/null @@ -1,163 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Feb 5 21:56:42 2020 - -@author: cpan -""" - -"""MC2-P1: Market simulator.""" - -import pandas as pd -import numpy as np -# import datetime as dt -# import os -from util import get_data, plot_data - -def normalize_data(df): - return df/df.iloc[0,:] - -def fill_missing_values(df_data): - '''First fill forward and then fill backward''' - df_data.fillna(method="ffill", inplace=True) - df_data.fillna(method="bfill", inplace=True) - -def get_orders(orders_file): - if isinstance(orders_file, pd.DataFrame): - # orders_df = orders_file.set_index('Date') - orders_df = orders_file - else: - orders_df = pd.read_csv(orders_file, index_col = 'Date', parse_dates = True, - na_values = ['nan']) - orders_df = orders_df.dropna() - orders_df = orders_df.sort_index() - return orders_df - -def compute_daily_returns(df): - daily_returns = df.copy() - daily_returns[1:] = (df[1:] / df[:-1].values) - 1 - daily_returns.iloc[0, :] = 0 - return daily_returns - -def compute_portfolio_stats(price, allocs=[0.1,0.2,0,3,0.4], rfr=0.0, sf=252.0): - norm_price = normalize_data(price) - norm_positions_val = norm_price * allocs - if len(norm_positions_val.columns) == 1: - norm_portfolio_val = norm_positions_val - else: - norm_portfolio_val = norm_positions_val.sum(axis=1).to_frame('PORTFOLIO') - cr = norm_portfolio_val.iloc[-1] / norm_portfolio_val.iloc[0] -1 - daily_returns = compute_daily_returns(norm_portfolio_val) - daily_returns = daily_returns[1:] # remove first row (all zeros) - adr = daily_returns.mean() - sddr = daily_returns.std() - sr = np.sqrt(sf) * (adr - rfr)/sddr - return cr, adr, sddr, sr - -def plot_against_SPY(df): - df_temp = df.copy() - if 'SPY' not in df_temp.columns: - df_SPY = get_data(['SPY'], pd.to_datetime(df_temp.index.values)) - df_temp['SPY'] = df_SPY.values - else: - df_SPY = df_temp['SPY'] - df_temp = normalize_data(df_temp) - plot_data(df_temp) - return df_SPY - -def compute_portvals(orders_file = "./orders/orders.csv", start_val = 1000000, - commission=9.95, impact=0.005): - #read in order data - orders_df = get_orders(orders_file) - #scan symbols - symbols = list(set(orders_df['Symbol'].values)) - #get date range - dates = pd.date_range(orders_df.index.values[0], orders_df.index.values[-1]) - #read in prices - prices = get_data(symbols, dates) - # fill_missing_values(prices) # included in get_data() function - prices = prices[symbols] - #add an extra column 'Cash' and initialize it to all ones - prices['Cash'] = np.ones(prices.shape[0]) - - #duplicate price df into a units df, intialize it to all zeros - positions=prices*0.0 - #initialize cash position with starting value - positions.loc[positions.index.values[0],['Cash']]=start_val - - #adjust positions to show how stock units and cash are changing over time with orders - for index, row in orders_df.iterrows(): - stock_sym = row['Symbol'] - order_price = prices.loc[index, stock_sym] - order_shrs = row['Shares'] - - if row['Order'].upper() == 'BUY': - if positions.loc[index, 'Cash'] < order_shrs*order_price +\ - commission + order_shrs*order_price*impact: - # print('Not enough cash to excute the order:\n', row) - pass - else: - #update positions on transaction days - positions.loc[index, stock_sym] += order_shrs - positions.loc[index, "Cash"] -= order_shrs*order_price - #deduct commission - positions.loc[index,"Cash"] -= commission - #impact = no. of orders in transaction * price of each share * impact. - positions.loc[index,"Cash"] -= order_shrs*order_price*impact - elif row['Order'].upper() == 'SELL': - if positions.loc[index, stock_sym] < order_shrs: - # print('Not enough shares to sell to fill the order:\n', row) - pass - else: - positions.loc[index, stock_sym] -= order_shrs - positions.loc[index, "Cash"] += order_shrs*order_price - #deduct commission - positions.loc[index,"Cash"] -= commission - #impact = no. of orders in transaction * price of each share * impact. - positions.loc[index,"Cash"] -= order_shrs*order_price*impact - - # propagate positions beyond transaction days - start_row = positions.index.get_loc(index) + 1 - positions.iloc[start_row:, :] = positions.iloc[start_row-1].values - - #calculate port_vals - port_vals=prices*positions - port_vals.insert(0, 'Portfolio', port_vals.sum(axis=1)) - - return port_vals - -def test_code(): - of = "./orders/orders-05.csv" - sv = 1000000 - - # Process orders - portvals = compute_portvals(orders_file = of, start_val = sv) - if isinstance(portvals, pd.DataFrame): - portvals = portvals[portvals.columns[0]].to_frame() # just get the first column - else: - print("warning, code did not return a DataFrame") - - # Get portfolio stats - start_date = pd.to_datetime(portvals.index.values[0]) - end_date = pd.to_datetime(portvals.index.values[-1]) - price_SPY = plot_against_SPY(portvals) - #portfolio stats calculated similar to assess_portfolio - rfr=0 - sf=252 - - cr, adr, sddr, sr = compute_portfolio_stats(portvals, [1.0], rfr, sf) - crSP,adrSP,sddrSP,srSP = compute_portfolio_stats(price_SPY, [1.0], rfr, sf) - # Compare portfolio against $SPX - print("\nDate Range: {} to {}".format(start_date.date(), end_date.date())) - print() - print("Sharpe Ratio: {}, {}".format(sr, srSP)) - print() - print("Cumulative Return: {}, {}".format(cr, crSP)) - print() - print("Standard Deviation: {}, {}".format(sddr, sddrSP)) - print() - print("Average Daily Return: {}, {}".format(adr, adrSP)) - print() - print("Final Portfolio Value: {:.2f}".format(portvals['Portfolio'].iloc[-1])) - -if __name__ == "__main__": - test_code() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0767ca9f3e75a1d1d296de3c956c998cc37a2d70..b8fc103d671468888776c48e827f182c9bc716ea 100644 GIT binary patch literal 1962 zcmb7_OK;Oq6ol^@iJ#&MJJ7;{6%q?1RxDUnN$NC#v<|UDoBnuUzBBjcrtGT9P2)4? zF>@aM`)8hZ`ngG)v`=IDsGl}nq_^?BPOs7;^=Y0CX%b?k&#h2{K6k=^2HB=gzh}w# zt&sQWr~LM(c<*KJvVF{+Z}g7G@0ZH5Olx`JzX(sP9|A?>Wkhrxo3MBC@1g?Y&-Bf> zwPL}hSJjPb8T9Q=?}+?#s%sWLH9yv+>V_5Z(BUda9OO%sdBg!hZE(NLh*9~-xYe^% z-l8PRxUL&{k79_0w{6Zs&BfNpmpr}d=C3>HJcvQBXOegEwRX=v*FxMW|I0lmRkn!A ziT^GtE>*8&qnC|(4nH)w&Gi(Hc(kFzN|s)1c0#!Wo`K68v(32A%7z+Nn@+&U6I5+Y z`Xm%n!9?Aj*c%V~jdI-!LnXXzv(}re++Lb^p04$Vd6zLxDUS1Il2$Ehg3*j_O0y8PArE|*SS&AyI8Ux z#lr09y7#HtxH|L$jAz8^mbu%B_b=&$wX!3BjWKC5*p4Rmv=^THusx~a>v5r6Ln!J^ zB=)3BVCBmV@f~9KoR>?&+=~BUNIZZX3bJ3sa*9@`t|9z9;;I>9Gzm* zn8YbpTGVy^NxI#r0{na#iSCKUW0$A!T9F2dP^5 z7FJ#C2UuqOK{{KJ!pFO2+dSCNA+9$F?`qNOL6(IqLu?X#G>SV@y7yfecAEbzhkT_< h6!-Ti4ovExNnM2sWFi9h2Nm7vC)wDGQpfqe{R0Ze8nyrc literal 924 zcmZ`%J(JrY5bgOdVvHnz6{<3GMP^c^NO^gAp?2E^Avf~$L%`^-rspL_Z;6l=~=0Ru_Cg)6!OkvO#lSiHJ z7%SCLs(NTlNQBQEgC}300cD?s&ArU-e{V%!91X+1R$ui*&5|kU8-_609`mn6V`SbS zdqAC1;?3y{wjEC0c4)pqy!AH*#C=Q(fV;*rO;3x$kKEz0c*nrK}xPB~@{^6|U9()6Bgv5Yx z%lnrx_s+~Re?>9S0reOB?Jd4 zM|5CrzReC6cUTGtbB%r0diI3#n=SpR#_dHRuZ=C3cS5>aQ9j0*(|tGqsjLyW@AO1f zw!s6U<{o9QZFiqamQ(>5q&-M=7LTq*pS@M$p>0W&(ajmzi3eSBFcYzIDD1^AO4ExL b6YNvsWn-*CID_P0i}Dqm