File size: 5,010 Bytes
3d0e51e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/usr/local/bin/python3

# Author: Pranab Ghosh
# 
# Licensed under the Apache License, Version 2.0 (the "License"); you
# may not use this file except in compliance with the License. You may
# obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0 
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.

# Package imports
import os
import sys
import random
import statistics 
import numpy as np
import matplotlib.pyplot as plt 
import argparse
from matumizi.util import *
from matumizi.sampler import *
from matumizi.mcsim import *

"""
Balances portfolio with Monte Carlo simulation and Sharpe ratio
"""

class PortFolio():
	"""
	portfolio
	"""
	def __init__(self):
		"""
		
		"""
		self.stocks = list()
		self.srets = list()
		self.rcovar = None
		self.nstock = None
		self.weights = None
		self.metric = -sys.float_info.max
		self.rfret = None
		self.spred = list()
	
	
	def loadStData(self, sdfPath, exfac):
		"""
		load and process stock data
		"""
		e1 = 1 - exfac
		e2 = e1 * e1
		files = getAllFiles(sdfPath)
		print(files)
		
		returns = list()
		for ss, qn, pp in self.stocks:
			print("next stock ", ss)
			for fp in files:
				fname = os.path.basename(fp)
				stname = fname.split("_")[0]
				#print("stock nane from file name ", stname)
				
				if stname == ss:
					#daily prices
					print("loading ", ss)
					prices = getFileColumnAsString(fp, 1)
					prices = prices [1:]
					prices = list(map(lambda p : float(p[1:]), prices))
			
					#predicted price and retuen
					sppred = exfac * prices[0] + exfac * e1 * prices[1] + exfac * e2 * prices[2]
					self.spred.append(sppred)
					up = pp / qn
					sret = (sppred - up) / up
					r = (ss, sret)
					self.srets.append(r)
			
					#daily returns
					bp = prices[-1]
					sdret = list(map(lambda p : (p - bp) / bp, prices))
					#print("daily return size ",  len(sdret))
					returns.append(sdret)
					break
			
		returns = np.array(returns)
		print("daily returns shape ",returns.shape)
		self.rcovar = np.cov(returns)
		print("covar shape ", self.rcovar.shape)


	def optimize(self):
		"""
		balance i.e make buy, sell recommendations
		
		"""
		tamount = 0
		amounts = list()
		for ss, qn , pp in self.stocks:
			amnt = pp
			amounts.append(amnt)
			tamount += amnt
		
		namounts = list(map(lambda w : w * tamount, self.weights))
		quantities = list()
		for am, nam, ppr in zip(amounts, namounts, self.spred):
			#no of stocks to buy or sell for each
			tamount = nam - am
			qnt = int(tamount / ppr)
			quantities.append(qnt)
		
		trans = list()	
		for s, q in zip(self.stocks, quantities):
			tr = (s[0], q)
			trans.append(tr)
			
		return trans

# portfolio object
pfolio = PortFolio()
		
def balance(args):
	"""
	callback for portfolio weights
	"""
	weights = args[:pfolio.nstock]
	#print("wieights ", weights)
	weights = scaleBySum(weights)
	#print("scaled wieights ", weights)
	
	#weighted return
	wr = 0
	for r, w in zip(pfolio.srets, weights):
		wr += (r[1] - pfolio.rfret) * w
	
	wrcv = 0	
	for i in range(pfolio.nstock):
		for j in range(pfolio.nstock):
			wrcv += pfolio.rcovar[i][j] * weights[i] * weights[j]
	
	metric = wr / wrcv
	print("score {:.3f}".format(metric))
	if metric > pfolio.metric:
		pfolio.metric = metric
		pfolio.weights = weights		
	

if __name__ == "__main__":
	parser = argparse.ArgumentParser()
	parser.add_argument('--op', type=str, default = "none", help = "operation")
	parser.add_argument('--niter', type=int, default = "none", help = "num of iterations")
	parser.add_argument('--sdfpath', type=str, default = "none", help = "stock data file directory path")
	parser.add_argument('--spdpath', type=str, default = "none", help = "path of file containing purchase data")
	parser.add_argument('--exfac', type=float, default = 0.9, help = "exponential factor for prediction")
	parser.add_argument('--rfret', type=float, default = 0.2, help = "risk free return")
	args = parser.parse_args()
	op = args.op

	if op == "simu":
		tdata = getFileLines(args.spdpath)
		for rec in tdata:
			#stock symbol, quantity, purchase price
			sname = rec[0]
			quant = int(rec[1])
			pcost = float(rec[2])
			t = (sname, quant, pcost)
			pfolio.stocks.append(t)
		
		#create and run simulator	
		numIter = args.niter
		lfp = "./log/mcsim.log"
		simulator = MonteCarloSimulator(numIter, balance, lfp, "info")
		nstock = len(pfolio.stocks)
		for _ in range(nstock):
			simulator.registerUniformSampler(0.0, 1.0)
		pfolio.nstock = nstock
		pfolio.rfret = args.rfret
		pfolio.loadStData(args.sdfpath, args.exfac)	
		simulator.run()
		
		print("best score {:.3f}".format(pfolio.metric))
		print("weights ", pfolio.weights)
		print("buy and sell recommendations")
		trans = pfolio.optimize()
		for tr in trans:
			print(tr)