Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_TradingCompetition2022-main/models/TradingModel.py
5925 views
1
from decimal import Decimal
2
from models.ModelChecks import ModelChecks
3
import yfinance as yf
4
import pandas as pd
5
from logger.LoggerFactory import LoggerFactory
6
from logger.BusinessLogger import BusinessLogger
7
8
9
class TradingModel:
10
""" Main class for developing trading model
11
"""
12
13
def __init__(self, ticker, config):
14
self._ticker = ticker
15
self._config = config
16
self._prev_price: Decimal = Decimal() # ToDo Change to list of price (history)
17
self._last_price: Decimal = Decimal()
18
self._stop_price = None # Calc stop loos price
19
self._avg_price: Decimal = Decimal() # Avg price of orders (buy)
20
self._stock_limit: Decimal = Decimal() # Limit for lot
21
self._possible_lot: int = 0 # Possible lot for buy
22
self._stock_drop_limit: int = 10 # Percent of drop limit
23
self._count_in_lot = 0
24
self._check_handler: ModelChecks = ModelChecks()
25
26
# This model IS NOT available only for any type of orders
27
self._long_available = False
28
self._short_available = False
29
30
self._long_open_ordered = False
31
self._long_close_ordered = False
32
33
self._long_open_done = False
34
self._long_close_done = False
35
36
self._hist_data: pd = None
37
self._stock_lot_limit = None
38
39
def custom_init(self):
40
self._generate_check()
41
42
@property
43
def config(self):
44
return self._config
45
46
@config.setter
47
def config(self, config):
48
self._config = config
49
50
def get_config(self, options):
51
return self.config.get(section='TradingStrategy', option=options)
52
53
def _check_model(self):
54
return True
55
56
def _calculate_model(self):
57
""" Method for calculate model (should be implemented in each models ) """
58
pass
59
60
def _download_hist_data(self, start_date, end_date):
61
""" Down load hist data for stock """
62
# ToDo This data can be loaded few times (To avoid this, it should be stored in dict)
63
ticker = self._ticker + '.ME'
64
self._hist_data = yf.download(ticker, start=start_date, end=end_date, interval="1d")
65
66
def _generate_check(self):
67
""" Add all checks to self._check_handler (class ModelChecks) """
68
pass
69
70
def _run_checks(self):
71
""" Run all checks for this model """
72
for group, check_key, check in self._check_handler:
73
if check[ModelChecks.IMPL_METHOD]() is True:
74
self._check_handler.set_ready(group, check_key)
75
76
def _is_all_true_in_group(self, group) -> bool:
77
""" Is all check in group eq true? Group = OPEN_LONG, CLOSE_LONG, etc. """
78
is_all_checks_true = False
79
for check in self._check_handler.checks[group].values():
80
is_all_checks_true = check[ModelChecks.IS_READY]
81
if is_all_checks_true is False:
82
break
83
return is_all_checks_true
84
85
@property
86
def is_ready_to_open_long(self) -> bool:
87
""" Check all specified checks, if all checks is true, it is time to open long order """
88
if self._long_available is not True:
89
return False
90
91
# Is it already opened?
92
if self.is_long_open_done is True:
93
return False
94
95
# Is it already ordered?
96
if self._long_open_ordered is True:
97
return False
98
99
# Check possible lot to buy?
100
if self.possible_lot == 0:
101
return False
102
103
104
105
return self._is_all_true_in_group(ModelChecks.OPEN_LONG)
106
107
def calc_possible_lot(self):
108
"""
109
Calc how many lot model can buy
110
:return:
111
"""
112
price_one_lot = self._last_price * self._count_in_lot
113
if price_one_lot != 0:
114
self._possible_lot = int(self._stock_limit // price_one_lot)
115
116
if self._possible_lot == 0:
117
self._possible_lot = self._stock_lot_limit
118
else:
119
self._possible_lot = min(self._possible_lot, self.stock_lot_limit)
120
121
@property
122
def stock_limit(self):
123
return self._stock_limit
124
125
@stock_limit.setter
126
def stock_limit(self, limit):
127
self._stock_limit = limit
128
129
@property
130
def count_in_lot(self):
131
return self._count_in_lot
132
133
@count_in_lot.setter
134
def count_in_lot(self, count):
135
self._count_in_lot = count
136
137
@property
138
def possible_lot(self):
139
return self._possible_lot
140
141
@property
142
def stock_lot_limit(self) -> int:
143
return int(self._stock_lot_limit)
144
145
@stock_lot_limit.setter
146
def stock_lot_limit(self, limit):
147
self._stock_lot_limit = limit
148
149
@property
150
def is_ready_to_close_long(self) -> bool:
151
""" Check all specified checks, if all checks is true, it is time to close long order """
152
if self._long_available is not True:
153
return False
154
155
# Is it already close?
156
if self.is_long_close_done is True:
157
return False
158
159
# Is it already ordered?
160
if self._long_close_ordered is True:
161
return False
162
163
# Is open already done?
164
if self._long_open_done is False:
165
return False
166
167
# Is it triggered STOP,
168
if self.is_price_stop is True:
169
return True
170
171
return self._is_all_true_in_group(ModelChecks.CLOSE_LONG)
172
173
@property
174
def is_ready_to_open_short(self) -> bool:
175
""" Check all specified checks, if all checks is true, it is time to open short order """
176
if self._short_available is not True:
177
return False
178
return self._is_all_true_in_group(ModelChecks.OPEN_SHORT)
179
180
@property
181
def is_ready_to_close_short(self) -> bool:
182
""" Check all specified checks, if all checks is true, it is time to close short order """
183
if self._short_available is not True:
184
return False
185
return self._is_all_true_in_group(ModelChecks.CLOSE_SHORT)
186
187
@property
188
def is_long_open_done(self) -> bool:
189
return self._long_open_done
190
191
@is_long_open_done.setter
192
def is_long_open_done(self, val: bool):
193
self._long_open_done = val
194
195
@property
196
def is_long_close_done(self) -> bool:
197
return self._long_close_done
198
199
@is_long_close_done.setter
200
def is_long_close_done(self, val: bool):
201
self._long_close_done = val
202
203
@property
204
def is_long_open_ordered(self) -> bool:
205
return self._long_open_ordered
206
207
@property
208
def is_long_close_ordered(self) -> bool:
209
return self._long_close_ordered
210
211
def long_open_ordered(self):
212
self._long_open_ordered = True
213
214
def long_close_ordered(self):
215
self._long_close_ordered = True
216
217
@property
218
def last_price(self):
219
return self._last_price
220
221
def set_last_price(self, last_price):
222
"""Set last price into model"""
223
self._prev_price = self._last_price
224
self._last_price = Decimal(last_price)
225
self.calc_possible_lot()
226
self._run_checks()
227
228
@property
229
def is_long_available(self):
230
return self._long_available
231
232
@property
233
def stock_drop_limit(self):
234
return self._stock_drop_limit
235
236
@stock_drop_limit.setter
237
def stock_drop_limit(self, stock_drop_limit):
238
self._stock_drop_limit = stock_drop_limit
239
240
@property
241
def is_price_stop(self) -> bool:
242
"""
243
Check price for STOP
244
:return:
245
"""
246
if self._stop_price is None:
247
# Stop price is no ready
248
return False
249
if self._last_price <= self._stop_price:
250
# Log data
251
LoggerFactory.get_business_logger_instance().add_event(BusinessLogger.STOP_LOOS,
252
self._ticker, self._last_price)
253
return True
254
255
def set_average_position_price(self, avg_price):
256
self._avg_price = avg_price
257
self.calc_stop_price()
258
259
def calc_stop_price(self):
260
if self._avg_price != 0:
261
max_loos = self._avg_price * self._stock_drop_limit / 100
262
self._stop_price = self._avg_price - max_loos
263
# Log data
264
LoggerFactory.get_business_logger_instance().add_event(BusinessLogger.CALC_STOP,
265
self._ticker, self._stop_price)
266
267