forked from wbpowell328/stochastic-optimization
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAssetSellingModel_Q3.py
162 lines (119 loc) · 5.66 KB
/
AssetSellingModel_Q3.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""
Asset selling model class
Adapted from code by Donghun Lee (c) 2018
"""
from collections import namedtuple
import numpy as np
class AssetSellingModel():
"""
Base class for model
"""
def __init__(self, state_variable, decision_variable, state_0, exog_0,T=10, gamma=1,exog_info_fn=None, transition_fn=None,
objective_fn=None, seed=20180529):
"""
Initializes the model
:param state_variable: list(str) - state variable dimension names
:param decision_variable: list(str) - decision variable dimension names
:param state_0: dict - needs to contain at least the information to populate initial state using state_names
:param exog_info_fn: function - calculates relevant exogenous information
:param transition_fn: function - takes in decision variables and exogenous information to describe how the state
evolves
:param objective_fn: function - calculates contribution at time t
:param seed: int - seed for random number generator
"""
self.initial_args = {'seed': seed,'T': T,'exog_params':exog_0,'gamma':gamma}
exog_params = self.initial_args['exog_params']
biasdf = exog_params['biasdf']
biasdf = biasdf.cumsum(axis=1)
self.initial_args['exog_params'].update({'biasdf':biasdf})
#print(self.initial_args['exog_params']['biasdf'])
#print("\n")
#print(self.initial_args)
self.prng = np.random.RandomState(seed)
self.initial_state = state_0
self.state_variable = state_variable
self.decision_variable = decision_variable
self.State = namedtuple('State', state_variable)
self.state = self.build_state(state_0)
self.Decision = namedtuple('Decision', decision_variable)
self.objective = 0.0
def build_state(self, info):
"""
this function gives a state containing all the state information needed
:param info: dict - contains all state information
:return: namedtuple - a state object
"""
return self.State(*[info[k] for k in self.state_variable])
def build_decision(self, info):
"""
this function gives a decision
:param info: dict - contains all decision info
:return: namedtuple - a decision object
"""
return self.Decision(*[info[k] for k in self.decision_variable])
def exog_info_fn(self):
"""
this function gives the exogenous information that is dependent on a random process (in the case of the the asset
selling model, it is the change in price)
:return: dict - updated price
"""
# we assume that the change in price is normally distributed with mean bias and variance 2
exog_params = self.initial_args['exog_params']
biasdf = exog_params['biasdf'].T
biasprob = biasdf[self.state.bias]
coin = self.prng.random_sample()
if (coin < biasprob['Up']):
new_bias = 'Up'
bias = exog_params['UpStep']
elif (coin>=biasprob['Up'] and coin<biasprob['Neutral']):
new_bias = 'Neutral'
bias = 0
else:
new_bias = 'Down'
bias = exog_params['DownStep']
#####
prev_price2 = self.state.prev_price
prev_price = self.state.price
#####
price_delta = self.prng.normal(bias, exog_params['Variance'])
updated_price = self.state.price + price_delta
# we account for the fact that asset prices cannot be negative by setting the new price as 0 whenever the
# random process gives us a negative price
new_price = 0.0 if updated_price < 0.0 else updated_price
print("coin ",coin," curr_bias ",self.state.bias," new_bias ",new_bias," price_delta ", price_delta, " new price ",new_price)
#####
return {"price": new_price,"bias":new_bias,
"prev_price":prev_price, "prev_price2":prev_price2}
#####
def transition_fn(self, decision, exog_info):
"""
this function takes in the decision and exogenous information to update the state
:param decision: namedtuple - contains all decision info
:param exog_info: any exogenous info (in this asset selling model,
the exogenous info does not factor into the transition function)
:return: dict - updated resource
"""
new_resource = 0 if decision.sell is 1 else self.state.resource
return {"resource": new_resource}
def objective_fn(self, decision, exog_info):
"""
this function calculates the contribution, which depends on the decision and the price
:param decision: namedtuple - contains all decision info
:param exog_info: any exogenous info (in this asset selling model,
the exogenous info does not factor into the objective function)
:return: float - calculated contribution
"""
sell_size = 1 if decision.sell is 1 and self.state.resource != 0 else 0
obj_part = self.state.price * sell_size
return obj_part
def step(self, decision):
"""
this function steps the process forward by one time increment by updating the sum of the contributions, the
exogenous information and the state variable
:param decision: namedtuple - contains all decision info
:return: none
"""
exog_info = self.exog_info_fn()
self.objective += self.objective_fn(decision, exog_info)
exog_info.update(self.transition_fn(decision, exog_info))
self.state = self.build_state(exog_info)