Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_tinkoff_trader-master/trader.py
5918 views
1
## 19.05.2020 Учимся покупать несколько акций #Buy_several_stocks
2
## 21.05.2020 Комиссию из баланса вычитаем и при покупке и при продаже #Commission_during_buying
3
## 11.08.2020 Засыпать, если Too Many Requests #TooManyRequests_Sleep
4
## 21.12.2020 seconds->total_seconds: if (datetime.now()-r['time']).total_seconds() > 60*60*24: # Expires after 24 hours
5
## 03.09.2021 Skip 'min_price_increment': None #Skip 'min_price_increment': None
6
## #SellStocksWithLoss
7
## Mind spread #MindSpread
8
9
from openapi_client import openapi
10
from datetime import datetime, timedelta
11
from pytz import timezone
12
import time
13
import os
14
15
16
def check_find_curve_c(figi, v_days, period, price, descent_perc = 2, advance_perc = 0.5, times = 1):
17
time_to = datetime.now()
18
time_from = time_to + timedelta(days=-1 * v_days)
19
20
try:
21
response = client.market.market_candles_get(figi,time_from.strftime(g_fmt),time_to.strftime(g_fmt),period)
22
except Exception as err:
23
output(figi + ' ' + str(err))
24
log(figi + ' market_candles_get: ' + str(err), 'error_log.txt')
25
return None
26
27
28
candles = getattr(getattr(response, 'payload'), 'candles')
29
30
res = {}
31
res['0 current_value'] = price
32
t = 1
33
stage = 'Advance ' + str(t)
34
for i in (sorted(candles, key=lambda d: getattr(d, 'time'), reverse=True)):
35
if stage == 'Advance ' + str(t) and t > 1 and getattr(i, 'c') > res[str(t-1) + '_high_value']:
36
res[str(t-1) + '_high_value'] = getattr(i, 'c')
37
res[str(t-1) + '_high_time'] = getattr(i, 'time')
38
elif stage == 'Advance ' + str(t) and getattr(i, 'c') < res['0 current_value'] / 100 * (100 - advance_perc):
39
res[str(t) + ' low_value'] = getattr(i, 'c')
40
res[str(t) + ' low_time'] = getattr(i, 'time')
41
stage = 'Descent ' + str(t)
42
elif stage == 'Descent ' + str(t) and getattr(i, 'c') < res[str(t) + ' low_value']:
43
res[str(t) + ' low_value'] = getattr(i, 'c')
44
res[str(t) + ' low_time'] = getattr(i, 'time')
45
elif stage == 'Descent ' + str(t) and getattr(i, 'c') > res['0 current_value'] / 100 * (100 + descent_perc) and t < times:
46
res[str(t) + '_high_value'] = getattr(i, 'c')
47
res[str(t) + '_high_time'] = getattr(i, 'time')
48
t += 1
49
stage = 'Advance ' + str(t)
50
elif stage == 'Descent ' + str(t) and getattr(i, 'c') > res['0 current_value'] / 100 * (100 + descent_perc) and t == times:
51
res[str(t) + '_high_value'] = getattr(i, 'c')
52
res[str(t) + '_high_time'] = getattr(i, 'time')
53
stage = 'Found'
54
elif stage == 'Found ' + str(t) and getattr(i, 'c') > res[str(t) + '_high_value'] and t == times:
55
res[str(t) + '_high_value'] = getattr(i, 'c')
56
res[str(t) + '_high_time'] = getattr(i, 'time')
57
58
if stage == 'Found':
59
return res
60
61
def check_find_curve(figi, v_days, period, price, descent_perc = 2, advance_perc = 0.5, times = 1):
62
time_to = datetime.now()
63
time_from = time_to + timedelta(days=-1 * v_days)
64
65
try:
66
response = client.market.market_candles_get(figi,time_from.strftime(g_fmt),time_to.strftime(g_fmt),period)
67
except Exception as err:
68
output(figi + ' ' + str(err))
69
log(figi + ' market_candles_get: ' + str(err), 'error_log.txt')
70
return None
71
72
73
candles = getattr(getattr(response, 'payload'), 'candles')
74
75
res = {}
76
res['0 current_value'] = price
77
t = 1
78
stage = 'Advance ' + str(t)
79
for i in (sorted(candles, key=lambda d: getattr(d, 'time'), reverse=True)):
80
if stage == 'Advance ' + str(t) and t > 1 and getattr(i, 'h') > res[str(t-1) + '_high_value']:
81
res[str(t-1) + '_high_value'] = getattr(i, 'h')
82
res[str(t-1) + '_high_time'] = getattr(i, 'time')
83
# Several conditions may be true
84
if stage == 'Advance ' + str(t) and getattr(i, 'l') < res['0 current_value'] / 100 * (100 - advance_perc):
85
res[str(t) + ' low_value'] = getattr(i, 'l')
86
res[str(t) + ' low_time'] = getattr(i, 'time')
87
stage = 'Descent ' + str(t)
88
elif stage == 'Descent ' + str(t) and getattr(i, 'l') < res[str(t) + ' low_value']:
89
res[str(t) + ' low_value'] = getattr(i, 'l')
90
res[str(t) + ' low_time'] = getattr(i, 'time')
91
# Several conditions may be true
92
if stage == 'Descent ' + str(t) and getattr(i, 'h') > res['0 current_value'] / 100 * (100 + descent_perc) and t < times:
93
res[str(t) + '_high_value'] = getattr(i, 'h')
94
res[str(t) + '_high_time'] = getattr(i, 'time')
95
t += 1
96
stage = 'Advance ' + str(t)
97
elif stage == 'Descent ' + str(t) and getattr(i, 'h') > res['0 current_value'] / 100 * (100 + descent_perc) and t == times:
98
res[str(t) + '_high_value'] = getattr(i, 'h')
99
res[str(t) + '_high_time'] = getattr(i, 'time')
100
stage = 'Found'
101
elif stage == 'Found ' + str(t) and getattr(i, 'h') > res[str(t) + '_high_value'] and t == times:
102
res[str(t) + '_high_value'] = getattr(i, 'h')
103
res[str(t) + '_high_time'] = getattr(i, 'time')
104
105
if stage == 'Found':
106
return res
107
108
def check_level(figi, start_period_days, end_period_days, period, high_level, p_high_level_qty, low_level, p_low_level_qty):
109
time_to = datetime.now() + timedelta(days=-1 * end_period_days)
110
time_from = datetime.now() + timedelta(days=-1 * start_period_days)
111
v_high_level_qty = 0
112
v_low_level_qty = 0
113
114
try:
115
response = client.market.market_candles_get(figi,time_from.strftime(g_fmt),time_to.strftime(g_fmt),period)
116
except Exception as err:
117
output(figi + ' ' + str(err))
118
log(figi + ' market_candles_get: ' + str(err), 'error_log.txt')
119
return None
120
121
candles = getattr(getattr(response, 'payload'), 'candles')
122
123
res = {}
124
res['1 start_time'] = time_from
125
res['2 end_time'] = time_to
126
res['3 period'] = period
127
res['4 high_level'] = high_level
128
res['5 low_level'] = low_level
129
res['6 high_level_qty'] = 0
130
res['7 low_level_qty'] = 0
131
t = 1
132
for i in (sorted(candles, key=lambda d: getattr(d, 'time'), reverse=True)):
133
if getattr(i, 'h') >= high_level:
134
res['6 high_level_qty'] = res['6 high_level_qty'] + 1
135
if getattr(i, 'l') <= low_level:
136
res['7 low_level_qty'] = res['7 low_level_qty'] + 1
137
138
if res['6 high_level_qty'] >= p_high_level_qty and res['7 low_level_qty'] >= p_low_level_qty:
139
return res
140
141
def should_i_stop():
142
with open('delete_to_stop.txt', 'r') as stp_file:
143
None
144
145
def log(message, file_name='log.txt'):
146
147
if file_name =='log.txt' or file_name =='error_log.txt':
148
try:
149
trial_str = g_trial + ' '
150
except NameError:
151
trial_str = ''
152
else:
153
trial_str = ''
154
155
print(trial_str + str(message))
156
157
if str(message).find('Reason: Too Many Requests') > 0 and file_name =='error_log.txt': #TooManyRequests_Sleep
158
print('Sleep')
159
time.sleep(5)
160
else:
161
f = open(file_name, 'a')
162
f.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S')+ ' ' + trial_str + str(message) + '\n')
163
f.close()
164
165
166
def output(message):
167
print(g_trial + ' ' + str(message))
168
169
def buy(ticker, figi, lot_qty, lot, currency, price):
170
v_lot_qty = int(lot_qty)
171
with open(g_trial + '/bought.txt', 'a') as g:
172
g.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S') +
173
' ' + str(ticker).ljust(12, ' ') +
174
' ' + str(figi).ljust(12, ' ') +
175
' ' + str(v_lot_qty).ljust(5, ' ') +
176
' ' + str(lot).ljust(7, ' ') +
177
' ' + str(currency).ljust(4, ' ') +
178
' ' + str(price) + '\n')
179
return price*v_lot_qty*lot
180
181
def get_bought():
182
b = []
183
try:
184
with open(g_trial + '/bought.txt', 'r') as f:
185
for item in f:
186
b.append({'time':datetime(int(item[0:4]), int(item[5:7]), int(item[8:10]), int(item[11:13]), int(item[14:16]), int(item[17:19])),
187
'ticker':item[20:33].rstrip(),
188
'figi':item[33:46].rstrip(),
189
'lot_qty':int(float(item[46:52])),
190
'lot':int(item[52:60]),
191
'currency':item[60:63],
192
'price':float(item[65:].rstrip())
193
})
194
except FileNotFoundError:
195
return b
196
return b
197
198
def get_comission(p_amount):
199
v_amount = round(p_amount * float(g_trial_params['COMMISSION']), 2)
200
if v_amount < 0.01:
201
v_amount = 0.01
202
return v_amount
203
204
def sell(ticker, lot_qty, price):
205
part_qty = 0
206
bb = get_bought()
207
with open(g_trial + '/bought.txt', 'w') as f:
208
for b in (bb):
209
if b['ticker'] == ticker and b['lot_qty'] <= lot_qty-part_qty:
210
part_qty = part_qty+b['lot_qty']
211
with open(g_trial + '/sold.txt', 'a') as sf:
212
sf.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +
213
' ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S') +
214
' ' + str(b['ticker']).ljust(12, ' ') +
215
' ' + str(b['figi']).ljust(12, ' ') +
216
' ' + str(b['lot_qty']).ljust(5, ' ') +
217
' ' + str(b['lot']).ljust(7, ' ') +
218
' ' + str(b['currency']).ljust(4, ' ') +
219
' ' + str(b['price']).ljust(10, ' ') +
220
' ' + str(price).ljust(10, ' ') +
221
' ' + str(round(price*b['lot_qty']*b['lot'] - get_comission(price*b['lot_qty']*b['lot']) - b['price']*b['lot_qty']*b['lot'] - get_comission(b['price']*b['lot_qty']*b['lot']),2)) # Profit
222
+ '\n')
223
elif b['ticker'] == ticker:
224
with open(g_trial + '/sold.txt', 'a') as sf:
225
if lot_qty-part_qty != 0:
226
sf.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +
227
' ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S') +
228
' ' + str(b['ticker']).ljust(12, ' ') +
229
' ' + str(b['figi']).ljust(12, ' ') +
230
' ' + str(lot_qty-part_qty).ljust(5, ' ') +
231
' ' + str(b['lot']).ljust(7, ' ') +
232
' ' + str(b['currency']).ljust(4, ' ') +
233
' ' + str(b['price']).ljust(10, ' ') +
234
' ' + str(price).ljust(10, ' ') +
235
' ' + str(round(price*(lot_qty-part_qty)*b['lot'] - get_comission(price*(lot_qty-part_qty)*b['lot']) - b['price']*(lot_qty-part_qty)*b['lot'] - get_comission(b['price']*(lot_qty-part_qty)*b['lot']) ,2)) # Profit
236
+ '\n')
237
238
f.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +
239
' ' + str(b['ticker']).ljust(12, ' ') +
240
' ' + str(b['figi']).ljust(12, ' ') +
241
' ' + str(b['lot_qty']-lot_qty+part_qty).ljust(5, ' ') +
242
' ' + str(b['lot']).ljust(7, ' ') +
243
' ' + str(b['currency']).ljust(4, ' ') +
244
' ' + str(b['price']) + '\n')
245
part_qty = lot_qty
246
else:
247
f.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +
248
' ' + str(b['ticker']).ljust(12, ' ') +
249
' ' + str(b['figi']).ljust(12, ' ') +
250
' ' + str(b['lot_qty']).ljust(5, ' ') +
251
' ' + str(b['lot']).ljust(7, ' ') +
252
' ' + str(b['currency']).ljust(4, ' ') +
253
' ' + str(b['price']) + '\n')
254
return part_qty
255
256
def get_sold():
257
b = []
258
try:
259
with open(g_trial + '/sold.txt', 'r') as f:
260
for item in f:
261
b.append({'buy_time':datetime(int(item[0:4]), int(item[5:7]), int(item[8:10]), int(item[11:13]), int(item[14:16]), int(item[17:19])),
262
'sell_time':datetime(int(item[21:25]), int(item[26:28]), int(item[29:31]), int(item[32:34]), int(item[35:37]), int(item[38:40])),
263
'ticker':item[41:54].rstrip(),
264
'figi':item[54:67].rstrip(),
265
'lot_qty':int(item[67:73]),
266
'lot':int(item[73:81]),
267
'currency':item[81:84],
268
'buy_price':float(item[86:96]),
269
'sell_price':float(item[97:108]),
270
'profit':float(item[108:].rstrip())
271
})
272
except FileNotFoundError:
273
return b
274
return b
275
276
def print_dict(v_dict, prefix = ''):
277
res = ''
278
for i in sorted(list(v_dict.keys())):
279
res = res + prefix + str(i) + ': ' + str(v_dict[i]) + '\n'
280
return res
281
282
def update_balance(amount, currency):
283
b = {}
284
try:
285
f = open(g_trial+'/balances.txt', 'r')
286
for line in f:
287
b[line[0:3]] = line[4:].strip()
288
f.close()
289
except FileNotFoundError:
290
b['RUB'] = 0
291
b['USD'] = 0
292
b['EUR'] = 0
293
try:
294
b[currency] = round(float(b[currency]) + amount, 2)
295
except KeyError:
296
b[currency] = amount
297
298
with open(g_trial+'/balances.txt', 'w') as g:
299
for curr in b.keys():
300
g.write(curr + '=')
301
g.write(str(b[curr])+'\n')
302
return b[currency]
303
304
def get_balance(currency):
305
b = {}
306
try:
307
f = open(g_trial+'/balances.txt', 'r')
308
for line in f:
309
b[line[0:3]] = line[4:].strip()
310
f.close()
311
except FileNotFoundError:
312
b['RUB'] = 0
313
b['USD'] = 0
314
b['EUR'] = 0
315
return b[currency]
316
317
def get_statistic():
318
global g_bougth_value
319
b = {}
320
try:
321
f = open(g_trial+'/balances.txt', 'r')
322
for line in f:
323
b['Balance ' + line[0:3]] = line[4:].strip()
324
f.close()
325
except FileNotFoundError:
326
b['Balance RUB'] = 0
327
b['Balance USD'] = 0
328
b['Balance EUR'] = 0
329
b['Bought RUB'] = 0
330
b['Bought USD'] = 0
331
b['Bought EUR'] = 0
332
b['Bought value RUB'] = 0
333
b['Bought value USD'] = 0
334
b['Bought value EUR'] = 0
335
for i in (get_bought()):
336
b['Bought '+i['currency']] = b['Bought '+i['currency']] + i['price'] * i['lot_qty']* i['lot']
337
try:
338
b['Bought value '+i['currency']] = b['Bought value '+i['currency']] + g_bougth_value[i['ticker']] * i['lot_qty']* i['lot']
339
except KeyError:
340
None
341
b['Balance&Bought RUB'] = float(b['Balance RUB']) + float(b['Bought RUB'])
342
b['Balance&Bought USD'] = float(b['Balance USD']) + float(b['Bought USD'])
343
b['Balance&Bought EUR'] = float(b['Balance EUR']) + float(b['Bought EUR'])
344
b['Profit RUB'] = 0
345
b['Profit USD'] = 0
346
b['Profit EUR'] = 0
347
for i in (get_sold()):
348
b['Profit '+i['currency']] = b['Profit '+i['currency']] + i['profit']
349
return b
350
351
def update_statistic (stat_dict, event, qty=1):
352
try:
353
stat_dict[event] = stat_dict[event] + qty
354
except KeyError:
355
stat_dict[event] = qty
356
return stat_dict
357
358
def find_and_buy():
359
result_statistic = {}
360
try:
361
mkt = client.market.market_stocks_get()
362
except Exception as err:
363
output('Can''t get stocks list: ' + str(err))
364
log('Can''t get stocks list: ' + str(err), 'error_log.txt')
365
return result_statistic
366
367
bought_list = get_bought()
368
sold_list = get_sold()
369
result_statistic['Go to checks'] = 0
370
371
try: #MindSpread
372
v_max_spread = float(g_trial_params['MAX_SPREAD'])
373
except KeyError:
374
v_max_spread = 1000000
375
376
# Get my portfolio
377
try:
378
portfolio_response = client.portfolio.portfolio_get()
379
except Exception as err:
380
output('Can''t get my portfolio: ' + str(err))
381
log('Can''t get my portfolio: ' + str(err), 'error_log.txt')
382
return result_statistic
383
384
# Cycle on stocks
385
for i in (getattr(getattr(mkt, 'payload'), 'instruments')):
386
should_i_stop()
387
update_statistic(result_statistic, 'Total')
388
389
#Skip old Tickers
390
if getattr(i, 'ticker')[-3:] == 'old':
391
output(getattr(i, 'ticker') + ' old ticker')
392
update_statistic(result_statistic, 'Old ticker')
393
continue
394
395
#Skip 'min_price_increment': None
396
if getattr(i, 'min_price_increment') == None:
397
output(getattr(i, 'ticker') + ' min_price_increment: None')
398
update_statistic(result_statistic, 'Min_price_increment: None')
399
continue
400
401
# Check for already requested and bought
402
if getattr(i, 'ticker') in [c['ticker'] for c in bought_list]:
403
output(getattr(i, 'ticker') + ' already bought')
404
update_statistic(result_statistic, 'Already bought')
405
continue
406
if getattr(i, 'ticker') in [c['ticker'] for c in get_request()]:
407
output(getattr(i, 'ticker') + ' already requested')
408
update_statistic(result_statistic, 'Already requested')
409
continue
410
# Check for my portfolio
411
if g_trial_params['ENVIRONMENT'] == 'PROD' \
412
and getattr(i, 'figi') in [getattr(c, 'figi') for c in getattr(getattr(portfolio_response, 'payload'), 'positions')]:
413
output(getattr(i, 'ticker') + ' in my investment portfolio')
414
update_statistic(result_statistic, 'In my investment portfolio')
415
continue
416
417
#Past experienced checks
418
if getattr(i, 'ticker') in g_not_available:
419
output(getattr(i, 'ticker') + ' NotAvailableForTrading (Past experience)')
420
update_statistic(result_statistic, 'NotAvailableForTrading (Past experience)')
421
continue
422
423
try:
424
if (g_stock_price[getattr(i, 'ticker')] > float(g_trial_params['EXPENSIVE_USD']) and getattr(i, 'currency') in ['USD','EUR']) \
425
or (g_stock_price[getattr(i, 'ticker')] > float(g_trial_params['EXPENSIVE_RUB']) and getattr(i, 'currency') == 'RUB'):
426
output(getattr(i, 'ticker') + ' Too expensive ' + getattr(i, 'currency') + ' (Past experience)')
427
update_statistic(result_statistic, 'Too expensive ' + getattr(i, 'currency') + ' (Past experience)')
428
continue
429
except KeyError:
430
None
431
432
# After all offline checks: one pause every four processed stocks
433
if result_statistic['Total'] % int(g_params['SLEEP_PERIOD']) == 0: #TBD Go to checks
434
time.sleep(1)
435
436
# Let's pause to sell PROD
437
if result_statistic['Total'] % int(g_params['SELL_PROD_PERIOD']) == 0:
438
sell_prod()
439
if g_trial_params['ENVIRONMENT'] == 'PROD':
440
sold_list = get_sold()
441
442
try:
443
response = client.market.market_orderbook_get(getattr(i, 'figi'), 2)
444
except Exception as err:
445
output(getattr(i, 'ticker') + ' market_orderbook_get: ' + str(err))
446
log(getattr(i, 'ticker') + ' market_orderbook_get: ' + str(err), 'error_log.txt')
447
update_statistic(result_statistic, 'Error')
448
continue
449
450
if getattr(getattr(response, 'payload'), 'trade_status') != 'NormalTrading':
451
output(getattr(i, 'ticker') + ' ' + getattr(getattr(response, 'payload'), 'trade_status'))
452
update_statistic(result_statistic, getattr(getattr(response, 'payload'), 'trade_status'))
453
if getattr(getattr(response, 'payload'), 'trade_status') == 'NotAvailableForTrading':
454
g_not_available.append(getattr(i, 'ticker'))
455
continue
456
457
lot_qty = 1
458
lot = int(getattr(i, 'lot'))
459
# The Cheapest offer in orderbook
460
try:
461
ask_price = float(getattr(getattr(getattr(response, 'payload'), 'asks')[0], 'price'))
462
g_stock_price[getattr(i, 'ticker')] = ask_price*lot
463
ask_qty = int(getattr(getattr(getattr(response, 'payload'), 'asks')[0], 'quantity'))
464
bid_price = float(getattr(getattr(getattr(response, 'payload'), 'bids')[0], 'price')) #MindSpread
465
except IndexError:
466
output(getattr(i, 'ticker') + ' IndexError: list index out of range')
467
##print(getattr(i, 'ticker') + ' ' + str(response))
468
update_statistic(result_statistic, 'IndexError: list index out of range')
469
continue
470
471
if not ask_price:
472
output('No price')
473
print(str(response))
474
update_statistic(result_statistic, 'No price')
475
continue
476
477
478
479
if (ask_price * lot > float(g_trial_params['EXPENSIVE_USD']) and getattr(i, 'currency') in ['USD','EUR']) \
480
or (ask_price * lot > float(g_trial_params['EXPENSIVE_RUB']) and getattr(i, 'currency') == 'RUB'):
481
output(getattr(i, 'ticker') + ' ' + str(ask_price) + '*' + str(lot) + ' ' + getattr(i, 'currency') + ' Too expensive')
482
update_statistic(result_statistic, 'Too expensive ' + getattr(i, 'currency'))
483
continue
484
485
if ask_price * lot > float(get_balance(getattr(i, 'currency'))):
486
output(getattr(i, 'ticker') + ' ' + str(ask_price) + '*' + str(lot) + ' ' + getattr(i, 'currency') + ' Not enough money')
487
update_statistic(result_statistic, 'Not enough money')
488
continue
489
490
if (ask_price-bid_price)/ask_price > v_max_spread: #MindSpread
491
output(getattr(i, 'ticker') + ': ' + str(ask_price) + '-' + str(bid_price) + ' Spread too wide')
492
update_statistic(result_statistic, 'Spread too wide')
493
continue
494
495
#Buy_several_stocks
496
if ask_price * lot * 2 <= float(get_balance(getattr(i, 'currency'))) \
497
and ask_price * lot * 2 <= float(get_balance(getattr(i, 'currency'))) \
498
and ask_price * lot * 2 <= float(g_trial_params['EXPENSIVE_USD']) and getattr(i, 'currency') in ['USD','EUR'] \
499
and ask_qty >= 2:
500
lot_qty = 2
501
502
# Check for already sold
503
v_already_sold_str = getattr(i, 'ticker') + ' already sold\n'
504
already_sold_flag = 'N'
505
for sold in sold_list:
506
## if sold['figi'] == getattr(i, 'figi'):
507
## v_already_sold_str = v_already_sold_str + ' ' +str(sold['sell_time']) + ' ' + str(ask_price) + ' ' + str(ask_price + 2 * get_comission(ask_price)) + '<>' + str(sold['sell_price']) + '\n'
508
if sold['figi'] == getattr(i, 'figi') \
509
and (datetime.now() - sold['sell_time']).total_seconds() < float(g_trial_params['SELL_TRACKING_HOURS']) * 60 * 60 \
510
and ask_price + 2 * get_comission(ask_price) > sold['sell_price']:
511
v_already_sold_str = v_already_sold_str + ' ' +str(sold['sell_time']) + ' ' + str(ask_price) + ' ' + str(ask_price + 2 * get_comission(ask_price)) + '>' + str(sold['sell_price']) + '\n'
512
already_sold_flag = 'Y'
513
if already_sold_flag == 'Y':
514
## v_already_sold_str = v_already_sold_str + ' Already sold\n'
515
## if g_trial_params['ENVIRONMENT'] == 'PROD':
516
## log(v_already_sold_str, g_trial+'/Already_sold_log.txt')
517
log(v_already_sold_str)
518
update_statistic(result_statistic, 'Already sold')
519
continue
520
521
# Apply checks
522
update_statistic(result_statistic, 'Go to checks')
523
checks_pass = ''
524
try:
525
with open(g_trial+'/check_curve_c.txt', 'r') as check_file:
526
check_params = {line.split('=')[0] : line.split('=')[1].strip() for line in check_file}
527
q = check_find_curve_c(getattr(i, 'figi'),
528
int(check_params['DAYS']),
529
check_params['PERIOD'],
530
ask_price,
531
int(check_params['DESCENT_PERC']),
532
int(check_params['ADVANCE_PERC']),
533
int(check_params['TIMES']))
534
if q:
535
checks_pass = checks_pass + 'check_curve_c passed:\n' + print_dict(q, ' ') + '\n'
536
v_high_level = float(q['1_high_value'])
537
v_low_level = float(q['1 low_value'])
538
else:
539
checks_pass = ''
540
continue
541
except FileNotFoundError:
542
None
543
544
try:
545
with open(g_trial+'/check_curve.txt', 'r') as check_file:
546
check_params = {line.split('=')[0] : line.split('=')[1].strip() for line in check_file}
547
q = check_find_curve(getattr(i, 'figi'),
548
int(check_params['DAYS']),
549
check_params['PERIOD'],
550
ask_price,
551
int(check_params['DESCENT_PERC']),
552
int(check_params['ADVANCE_PERC']),
553
int(check_params['TIMES']))
554
if q:
555
checks_pass = checks_pass + 'check_curve passed:\n' + print_dict(q, ' ') + '\n'
556
v_high_level = float(q['1_high_value'])
557
v_low_level = float(q['1 low_value'])
558
else:
559
checks_pass = ''
560
continue
561
except FileNotFoundError:
562
None
563
564
try:
565
with open(g_trial+'/check_level.txt', 'r') as check_file:
566
check_params = {line.split('=')[0] : line.split('=')[1].strip() for line in check_file}
567
q = check_level(getattr(i, 'figi'),
568
int(check_params['START_PEROD_DAYS']),
569
int(check_params['END_PEROD_DAYS']),
570
check_params['PERIOD'],
571
v_high_level,
572
int(check_params['HIGH_LEVEL_QTY']),
573
v_low_level,
574
int(check_params['LOW_LEVEL_QTY']),)
575
if q:
576
checks_pass = checks_pass + 'check_level passed:\n' + print_dict(q, ' ') + '\n'
577
else:
578
checks_pass = ''
579
continue
580
except FileNotFoundError:
581
None
582
583
584
if checks_pass:
585
## log('Go to request to buy: ' + getattr(i, 'ticker') + ', ' + str(ask_qty), g_trial+'/log.txt')
586
requested_qty = request(getattr(i, 'ticker'), getattr(i, 'figi'), lot_qty, lot, getattr(i, 'currency'), ask_price, 'Buy')
587
if requested_qty > 0:
588
log('Request to buy: ' + getattr(i, 'ticker') + '\n' + checks_pass + '\n', g_trial+'/log.txt')
589
update_statistic(result_statistic, 'Buy requests events')
590
update_statistic(result_statistic, 'Buy requests stocks', requested_qty)
591
# Update balance before request execution
592
log_str = 'Update balance: ' + str(get_balance(getattr(i, 'currency'))) + ' - ' + str(lot_qty*lot*ask_price + get_comission(lot_qty*lot*ask_price))
593
update_balance(-1*(lot_qty*lot*ask_price + get_comission(lot_qty*lot*ask_price)), getattr(i, 'currency')) #Commission_during_buying
594
log_str = log_str + ' = ' + str(get_balance(getattr(i, 'currency')))
595
log(log_str, g_trial+'/log.txt')
596
## if g_trial_params['ENVIRONMENT'] == 'PROD':
597
## log(v_already_sold_str, g_trial+'/Already_sold_log.txt')
598
return result_statistic
599
600
def check_and_sell(profit):
601
global g_bougth_value
602
g_bougth_value = {}
603
result_statistic = {}
604
n = 0
605
606
try: #SellStocksWithLoss
607
v_loss_threshold = float(g_trial_params['LOSS'])
608
except KeyError:
609
v_loss_threshold = 0
610
611
try: #SellStocksWithLoss
612
v_max_loss = float(g_trial_params['MAX_LOSS'])
613
except KeyError:
614
v_max_loss = 0
615
616
try: #MindSpread
617
v_max_spread = float(g_trial_params['MAX_SPREAD'])
618
except KeyError:
619
v_max_spread = 1000000
620
621
for stock in get_bought():
622
n = n + 1
623
try:
624
response = client.market.market_orderbook_get(stock['figi'], 2)
625
except Exception as err:
626
output(stock['ticker'] + ' market_orderbook_get: ' + str(err))
627
log(stock['ticker'] + ' market_orderbook_get: ' + str(err), 'error_log.txt')
628
continue
629
try:
630
bid_price = float(getattr(getattr(getattr(response, 'payload'), 'bids')[0], 'price'))
631
g_bougth_value[stock['ticker']] = bid_price
632
ask_price = float(getattr(getattr(getattr(response, 'payload'), 'asks')[0], 'price')) #MindSpread
633
except IndexError:
634
output('IndexError: list index out of range')
635
print(stock['ticker'] + ' ' + str(response) + '\n')
636
g_bougth_value[stock['ticker']] = float(getattr(getattr(response, 'payload'), 'last_price'))
637
continue
638
639
if (stock['price'] * stock['lot_qty'] * stock['lot'] + get_comission(stock['price'] * stock['lot_qty'] * stock['lot'])) * (1+float(g_trial_params['PROFIT'])) <= \
640
bid_price * stock['lot_qty'] * stock['lot'] - get_comission(bid_price * stock['lot_qty'] * stock['lot']):
641
requested_qty = request(stock['ticker'], stock['figi'], stock['lot_qty'], stock['lot'], stock['currency'], stock['price'], 'Sell', bid_price)
642
if requested_qty > 0:
643
log('Request to sell: ' + stock['ticker'] + ' ' + str(stock['price']) + ' ' + str(bid_price), g_trial+'/log.txt')
644
update_statistic(result_statistic, 'Sell requests events')
645
update_statistic(result_statistic, 'Sell requests stocks', requested_qty)
646
#SellStocksWithLoss
647
elif v_loss_threshold > 0 and \
648
(stock['price'] * stock['lot_qty'] * stock['lot'] - get_comission(stock['price'] * stock['lot_qty'] * stock['lot'])) > \
649
(bid_price * stock['lot_qty'] * stock['lot'] + get_comission(bid_price * stock['lot_qty'] * stock['lot'])) * (1+v_loss_threshold) and \
650
(stock['price'] * stock['lot_qty'] * stock['lot'] - get_comission(stock['price'] * stock['lot_qty'] * stock['lot'])) < \
651
(bid_price * stock['lot_qty'] * stock['lot'] + get_comission(bid_price * stock['lot_qty'] * stock['lot'])) * (1+v_max_loss) and \
652
(ask_price-bid_price)/ask_price <= v_max_spread:
653
#log(stock['ticker'] + ' SellStocksWithLoss: old_price=' + str(stock['price']) + ', new=' + str(bid_price) + ' n%=' + str((bid_price * stock['lot_qty'] * stock['lot'] + get_comission(bid_price * stock['lot_qty'] * stock['lot'])) * (1+v_loss_threshold)), g_trial+'/log.txt')
654
requested_qty = request(stock['ticker'], stock['figi'], stock['lot_qty'], stock['lot'], stock['currency'], stock['price'], 'Sell', bid_price)
655
if requested_qty > 0:
656
log('Request to sell with loss: ' + stock['ticker'] + ' ' + str(stock['price']) + ' ' + str(bid_price), g_trial+'/log.txt')
657
update_statistic(result_statistic, 'Sell requests events')
658
update_statistic(result_statistic, 'Sell requests stocks', requested_qty)
659
if n % int(g_params['SLEEP_PERIOD']) == 0:
660
time.sleep(1)
661
return result_statistic
662
663
def request(ticker, p_figi, lot_qty, lot, currency, buy_price, req_type, sell_price=''):
664
order_id = ''
665
order_status = ''
666
if g_trial_params['ENVIRONMENT'] == 'PROD':
667
v_price = buy_price if req_type == 'Buy' else sell_price
668
try:
669
order_response = client.orders.orders_limit_order_post(figi=p_figi,
670
limit_order_request={"lots": lot_qty,
671
"operation": req_type,
672
"price": v_price})
673
order_id = getattr(getattr(order_response, 'payload'), 'order_id')
674
order_status = getattr(getattr(order_response, 'payload'), 'status')
675
log(order_response, g_trial+'/log.txt')
676
except Exception as err:
677
output('Reqest error. ' + ticker + ' ' + req_type + ' ' + str(lot_qty) + ' lots: ' + str(err))
678
log('Reqest error. ' + ticker + ' ' + req_type + ' ' +str(lot_qty) + ' lots: ' + str(err), 'error_log.txt')
679
return 0
680
elif g_trial_params['ENVIRONMENT'] == 'TEST':
681
order_status = 'New'
682
683
if order_status == 'New':
684
with open(g_trial + '/request.txt', 'a') as g:
685
g.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S') +
686
' ' + str(ticker).ljust(12, ' ') +
687
' ' + str(p_figi).ljust(12, ' ') +
688
' ' + str(lot_qty).ljust(5, ' ') +
689
' ' + str(lot).ljust(7, ' ') +
690
' ' + str(currency).ljust(4, ' ') +
691
' ' + str(buy_price).ljust(10, ' ') +
692
' ' + str(req_type).ljust(4, ' ') +
693
' ' + str(sell_price).ljust(10, ' ') + '\n')
694
return lot_qty
695
else:
696
log(ticker + ' ' + req_type + ' request status: ' + order_status, g_trial+'/log.txt')
697
return 0
698
699
def get_request():
700
b = []
701
try:
702
with open(g_trial + '/request.txt', 'r') as f:
703
for item in f:
704
r = {'time':datetime(int(item[0:4]), int(item[5:7]), int(item[8:10]), int(item[11:13]), int(item[14:16]), int(item[17:19])),
705
'ticker':item[20:33].rstrip(),
706
'figi':item[33:46].rstrip(),
707
'lot_qty':int(float(item[46:52])),
708
'lot':int(item[52:60]),
709
'currency':item[60:63],
710
'buy_price':float(item[65:76].rstrip()),
711
'type':item[76:81].rstrip()
712
}
713
if item[81:].rstrip():
714
r['sell_price'] = float(item[81:].rstrip())
715
b.append(r)
716
except FileNotFoundError:
717
return b
718
return b
719
720
721
def check_requests():
722
#log('In check_requests', 'debug.txt') # TBD
723
res = {}
724
rr = get_request()
725
bought = {} # Already bought
726
for i in get_bought():
727
try:
728
bought[i['figi']] = bought[i['figi']] + i['lot_qty']
729
except KeyError:
730
bought[i['figi']] = i['lot_qty']
731
if g_trial_params['ENVIRONMENT'] == 'PROD':
732
try:
733
response = client.portfolio.portfolio_get()
734
my_portfolio = {getattr(c, 'figi'):int(getattr(c, 'balance')) for c in getattr(getattr(response, 'payload'), 'positions')}
735
except Exception as err:
736
output('Can''t get portfolio: ' + str(err))
737
log('Can''t get portfolio: ' + str(err), 'error_log.txt')
738
return res
739
740
with open(g_trial + '/request.txt', 'w') as f:
741
for r in rr:
742
if (datetime.now()-r['time']).total_seconds() > 60*60*24: # Expires after 24 hours
743
with open(g_trial + '/rejected_requests.txt', 'a') as rf:
744
try:
745
sell_price_str = ' ' + str(r['sell_price']).ljust(10, ' ')
746
except KeyError:
747
sell_price_str = ''
748
rf.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +
749
' ' + str(r['ticker']).ljust(12, ' ') +
750
' ' + str(r['figi']).ljust(12, ' ') +
751
' ' + str(r['lot_qty']).ljust(5, ' ') +
752
' ' + str(r['lot']).ljust(7, ' ') +
753
' ' + str(r['currency']).ljust(4, ' ') +
754
' ' + str(r['buy_price']).ljust(10, ' ') +
755
' ' + str(r['type'].ljust(4, ' ')) + sell_price_str + '\n')
756
update_statistic(res, 'Rejected requests')
757
# Money returning
758
if r['type'] == 'Buy':
759
update_balance(r['lot_qty'] * r['lot'] * r['buy_price'] + get_comission(r['lot_qty'] * r['lot'] * r['buy_price']), r['currency']) #Commission_during_buying
760
elif r['type'] == 'Buy':
761
if g_trial_params['ENVIRONMENT'] == 'PROD':
762
try:
763
already_bougth = bought[r['figi']]
764
except KeyError:
765
already_bougth = 0
766
try:
767
buy_qty = int(my_portfolio[r['figi']] / r['lot'] - already_bougth)
768
except KeyError:
769
buy_qty = 0
770
771
if buy_qty > r['lot_qty']:
772
update_statistic(res, 'Requests with error')
773
log(r['ticker'] + ' bougth more than requested: ' + str(buy_qty) + ' > ' + str(r['lot_qty']))
774
elif g_trial_params['ENVIRONMENT'] == 'TEST':
775
buy_qty = r['lot_qty']
776
777
if buy_qty > 0:
778
with open(g_trial + '/bought.txt', 'a') as sf:
779
sf.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +
780
' ' + str(r['ticker']).ljust(12, ' ') +
781
' ' + str(r['figi']).ljust(12, ' ') +
782
' ' + str(buy_qty).ljust(5, ' ') +
783
' ' + str(r['lot']).ljust(7, ' ') +
784
' ' + str(r['currency']).ljust(4, ' ') +
785
' ' + str(r['buy_price']).ljust(10, ' ') + '\n')
786
update_statistic(res, 'Buy requests completed')
787
update_statistic(res, 'Stocks bought', buy_qty)
788
log(r['ticker'] + ' bougth: ' + str(buy_qty), g_trial+'/log.txt')
789
if r['lot_qty'] > buy_qty:
790
f.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +
791
' ' + str(r['ticker']).ljust(12, ' ') +
792
' ' + str(r['figi']).ljust(12, ' ') +
793
' ' + str(r['lot_qty']-buy_qty).ljust(5, ' ') +
794
' ' + str(r['lot']).ljust(7, ' ') +
795
' ' + str(r['currency']).ljust(4, ' ') +
796
' ' + str(r['buy_price']).ljust(10, ' ') +
797
' ' + str(r['type']) + '\n')
798
elif r['type'] == 'Sell':
799
if g_trial_params['ENVIRONMENT'] == 'PROD':
800
try:
801
sell_qty = r['lot_qty'] - int(my_portfolio[r['figi']] / r['lot'])
802
except KeyError:
803
sell_qty = r['lot_qty']
804
elif g_trial_params['ENVIRONMENT'] == 'TEST':
805
sell_qty = r['lot_qty']
806
807
808
if sell_qty > 0:
809
sold_qty = sell(r['ticker'], sell_qty, r['sell_price'])
810
if sold_qty != sell_qty:
811
log('Error! Faild to sell necessary amount. ' + r['ticker'] + ', sold=' + str(sold_qty) + ', necessary=' + str(sell_qty))
812
update_statistic(res, 'Sell requests completed')
813
update_statistic(res, 'Stocks sold', sold_qty)
814
log_str = 'Update balance: ' + str(get_balance(r['currency'])) + ' + ' + str(sold_qty * r['lot'] * r['sell_price'] - get_comission(sold_qty * r['lot'] * r['sell_price']))
815
update_balance(sold_qty * r['lot'] * r['sell_price'] -
816
## get_comission(sold_qty * r['lot'] * r['buy_price']) - #Commission_during_buying
817
get_comission(sold_qty * r['lot'] * r['sell_price'])
818
, r['currency'])
819
log(r['ticker'] + ' sold: ' + str(sold_qty), g_trial+'/log.txt')
820
log_str = log_str + ' = ' + str(get_balance(r['currency']))
821
log(log_str, g_trial+'/log.txt')
822
if r['lot_qty'] > sell_qty:
823
f.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +
824
' ' + str(r['ticker']).ljust(12, ' ') +
825
' ' + str(r['figi']).ljust(12, ' ') +
826
' ' + str(r['lot_qty']-sell_qty).ljust(5, ' ') +
827
' ' + str(r['lot']).ljust(7, ' ') +
828
' ' + str(r['currency']).ljust(4, ' ') +
829
' ' + str(r['buy_price']).ljust(10, ' ') +
830
' ' + str(r['type']).ljust(4, ' ') +
831
' ' + str(r['sell_price']) + '\n')
832
return res
833
834
def show_all_stat():
835
global g_trial
836
with open('trials.txt', 'r') as trials_file:
837
trials = [line.strip() for line in trials_file]
838
for trial in trials:
839
if not trial.rstrip(): #Skip empty rows
840
continue
841
g_trial = trial
842
output('\n' + 'Statistic:\n' + print_dict(get_statistic(), ' '))
843
844
def sell_prod():
845
global g_trial, g_trial_params, client
846
tmp_trial = g_trial
847
tmp_trial_params = g_trial_params
848
849
for trial in set(trials):
850
if not trial.rstrip(): #Skip empty rows
851
continue
852
with open(trial+'/trial_params.txt', 'r') as trial_params_file:
853
g_trial_params = {line.split('=')[0] : line.split('=')[1].strip() for line in trial_params_file}
854
g_trial = trial
855
if g_trial_params['ENVIRONMENT'] != 'PROD':
856
continue
857
# Sell
858
v_dict = {}
859
v_dict = check_and_sell(g_trial_params['PROFIT'])
860
if v_dict:
861
log('\n' + 'check_and_sell=\n' + print_dict(v_dict, ' '))
862
# Requests process
863
v_dict = {}
864
v_dict = check_requests()
865
if v_dict:
866
log('\n' + 'check_requests=\n' + print_dict(v_dict, ' '))
867
868
# Return original environment
869
g_trial = tmp_trial
870
g_trial_params = tmp_trial_params
871
872
def trade():
873
global g_trial, g_params, g_trial_params, client, g_fmt, g_not_available, g_stock_price, trials
874
875
with open('delete_to_stop.txt', 'w') as stp_file:
876
stp_file.write(str(datetime.now())+'\n')
877
with open('trials.txt', 'r') as trials_file:
878
trials = [line.strip() for line in trials_file]
879
# Reading common parameters
880
try:
881
with open('params.txt', 'r') as params_file:
882
g_params = {line.split('=')[0] : line.split('=')[1].strip() for line in params_file}
883
except FileNotFoundError:
884
with open('params.txt', 'w') as params_file:
885
params_file.write('PARAM=VALUE')
886
print('params.txt created')
887
exit(0)
888
last_iteration_start_time = datetime(2019, 12, 21, 15, 33, 0)
889
log('Starting')
890
f = open('token.txt', 'r')
891
token = f.read()
892
f.close()
893
g_fmt = '%Y-%m-%dT%H:%M:%S.%f+03:00'
894
g_not_available = []
895
g_stock_price = {}
896
while 2 > 1:
897
# No work at night
898
if datetime.now().hour < int(g_params['START_TIME']) and datetime.now().hour > int(g_params['END_TIME']):
899
print('No work at night')
900
time.sleep(60)
901
should_i_stop()
902
continue
903
904
# Wait for time gap
905
sec_between = (datetime.now() - last_iteration_start_time).total_seconds()
906
if sec_between < int(g_params['TIME_GAP'])*60:
907
should_i_stop()
908
print('Pause for ' + str(int(g_params['TIME_GAP'])*60 - sec_between) + ' sec.')
909
time.sleep(60)
910
continue
911
last_iteration_start_time = datetime.now()
912
913
#Sandbox or PROD
914
client = openapi.api_client(token)
915
# Process trials
916
for trial in trials:
917
if not trial.rstrip(): #Skip empty rows
918
continue
919
g_trial = trial
920
should_i_stop()
921
# Reading common parameters
922
with open('params.txt', 'r') as params_file:
923
g_params = {line.split('=')[0] : line.split('=')[1].strip() for line in params_file}
924
# Reading trial parameters
925
if not os.path.exists(trial): os.makedirs(trial)
926
try:
927
with open(trial+'/trial_params.txt', 'r') as trial_params_file:
928
g_trial_params = {line.split('=')[0] : line.split('=')[1].strip() for line in trial_params_file}
929
except FileNotFoundError:
930
with open(trial+'/trial_params.txt', 'w') as trial_params_file:
931
trial_params_file.write('PARAM=VALUE')
932
output('trial_params.txt created')
933
continue
934
# Sell
935
v_dict = {}
936
v_dict = check_and_sell(g_trial_params['PROFIT'])
937
if v_dict:
938
log('\n' + 'check_and_sell=\n' + print_dict(v_dict, ' '))
939
# Requests process
940
v_dict = {}
941
v_dict = check_requests()
942
if v_dict:
943
log('\n' + 'check_requests=\n' + print_dict(v_dict, ' '))
944
945
if float(get_balance('USD'))<1 and float(get_balance('EUR'))<1 and float(get_balance('RUB'))<50:
946
output('No money left')
947
elif datetime.now().hour < int(g_params['START_BUY_TIME']) and datetime.now().hour > int(g_params['END_TIME']):
948
output('We are not buying in the morning')
949
g_not_available = []
950
g_stock_price = {}
951
elif g_trial_params['STOP_BUYING'] == 'Y':
952
output('Buying is stopped')
953
else:
954
log('\n' + 'find_and_buy=\n' + print_dict(find_and_buy(), ' '))
955
# Requests process
956
v_dict = {}
957
v_dict = check_requests()
958
if v_dict:
959
log('\n' + 'check_requests=\n' + print_dict(v_dict, ' '))
960
log('\n' + 'Statistic:\n' + print_dict(get_statistic(), ' '))
961
962
if __name__ == "__main__":
963
trade()
964
965