-
Notifications
You must be signed in to change notification settings - Fork 392
/
optimal_trader.py
143 lines (112 loc) · 3.51 KB
/
optimal_trader.py
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
import sys
import operator
import numpy as np
import pandas as pd
from pandas_datareader import DataReader
import datetime
stock = 'XRF'
start_date = datetime.datetime.now() - datetime.timedelta(days=7)
end_date = datetime.date.today()
def find_optimal_action(priceVec, transFeeRate, use_DP=True):
_BUY = 1
_HOLD = 0
_SELL = -1
dataLen = len(priceVec)
actionVec = np.zeros(dataLen)
# Dynamic Programming method
if use_DP:
capital = 1
money = [{'money' : 0, 'from' : 0 } for _ in range(dataLen)]
stock = [{'stock' : 0, 'from' : 1 } for _ in range(dataLen)]
# DP initialization
money[0]['money'] = capital
stock[0]['stock'] = capital * (1 - transFeeRate) / priceVec[0]
# DP recursion
for t in range(1, dataLen):
# find optimal for sell at time t:
hold = money[t - 1]['money']
sell = stock[t - 1]['stock'] * priceVec[t] * (1 - transFeeRate)
if hold > sell:
money[t]['money'] = hold
money[t]['from'] = 0
else:
money[t]['money'] = sell
money[t]['from'] = 1
# find optimal for buy at time t:
hold = stock[t - 1]['stock']
buy = money[t - 1]['money'] * (1 - transFeeRate) / priceVec[t]
if hold > buy:
stock[t]['stock'] = hold
stock[t]['from'] = 1
else:
stock[t]['stock'] = buy
stock[t]['from'] = 0
# must sell at T
prev = 0
actionVec[-1] = _SELL
# DP trace back
record = [money, stock]
for t in reversed(range(1, dataLen)):
prev = record[prev][t]['from']
actionVec[t - 1] = _SELL if prev == 0 else _BUY
# Action smoothing
prevAction = actionVec[0]
for t in range(1, dataLen):
if actionVec[t] == prevAction:
actionVec[t] = _HOLD
elif actionVec[t] == -prevAction:
prevAction = actionVec[t]
return actionVec
# Baseline method
else:
conCount = 3
for ic in range(dataLen):
if ic + conCount + 1 > dataLen:
continue
if all(x > 0 for x in list(map(operator.sub,priceVec[ic+1:ic+1+conCount], priceVec[ic:ic+conCount]))):
actionVec[ic] = _BUY
if all(x < 0 for x in list(map(operator.sub,priceVec[ic+1:ic+1+conCount], priceVec[ic:ic+conCount]))):
actionVec[ic] = _SELL
prevAction = _SELL
for ic in range(dataLen):
if actionVec[ic] == prevAction:
actionVec[ic] = _HOLD
elif actionVec[ic] == -prevAction:
prevAction = actionVec[ic]
return actionVec
def profit_estimate(priceVec, transFeeRate, actionVec):
capital = 1
capitalOrig = capital
dataCount = len(priceVec)
suggestedAction = actionVec
stockHolding = np.zeros((dataCount))
total = np.zeros((dataCount))
total[0] = capital
for ic in range(dataCount):
currPrice = priceVec[ic]
if ic > 0:
stockHolding[ic] = stockHolding[ic-1]
if suggestedAction[ic] == 1:
if stockHolding[ic] == 0:
stockHolding[ic] = capital * (1 - transFeeRate) / currPrice
capital = 0
elif suggestedAction[ic] == -1:
if stockHolding[ic] > 0:
capital = stockHolding[ic] * currPrice * (1 - transFeeRate)
stockHolding[ic] = 0
elif suggestedAction[ic] == 0:
pass
else:
assert False
total[ic] = capital + stockHolding[ic] * currPrice * (1 - transFeeRate)
returnRate = (total[-1] - capitalOrig) / capitalOrig
return returnRate
if __name__ == '__main__':
SEARCH = False
df = DataReader(stock, 'yahoo', start_date, end_date)
transFeeRate = float(0.01)
priceVec = df["Adj Close"].values
print('Optimizing over %i numbers of transactions.' % (len(priceVec)))
actionVec = find_optimal_action(priceVec, transFeeRate)
returnRate = profit_estimate(priceVec, transFeeRate, actionVec)
print('Return rate: ', returnRate)