Path: blob/master/ invest-robot-contest_tinkoff_trader-master/trader.py
5918 views
## 19.05.2020 Учимся покупать несколько акций #Buy_several_stocks1## 21.05.2020 Комиссию из баланса вычитаем и при покупке и при продаже #Commission_during_buying2## 11.08.2020 Засыпать, если Too Many Requests #TooManyRequests_Sleep3## 21.12.2020 seconds->total_seconds: if (datetime.now()-r['time']).total_seconds() > 60*60*24: # Expires after 24 hours4## 03.09.2021 Skip 'min_price_increment': None #Skip 'min_price_increment': None5## #SellStocksWithLoss6## Mind spread #MindSpread78from openapi_client import openapi9from datetime import datetime, timedelta10from pytz import timezone11import time12import os131415def check_find_curve_c(figi, v_days, period, price, descent_perc = 2, advance_perc = 0.5, times = 1):16time_to = datetime.now()17time_from = time_to + timedelta(days=-1 * v_days)1819try:20response = client.market.market_candles_get(figi,time_from.strftime(g_fmt),time_to.strftime(g_fmt),period)21except Exception as err:22output(figi + ' ' + str(err))23log(figi + ' market_candles_get: ' + str(err), 'error_log.txt')24return None252627candles = getattr(getattr(response, 'payload'), 'candles')2829res = {}30res['0 current_value'] = price31t = 132stage = 'Advance ' + str(t)33for i in (sorted(candles, key=lambda d: getattr(d, 'time'), reverse=True)):34if stage == 'Advance ' + str(t) and t > 1 and getattr(i, 'c') > res[str(t-1) + '_high_value']:35res[str(t-1) + '_high_value'] = getattr(i, 'c')36res[str(t-1) + '_high_time'] = getattr(i, 'time')37elif stage == 'Advance ' + str(t) and getattr(i, 'c') < res['0 current_value'] / 100 * (100 - advance_perc):38res[str(t) + ' low_value'] = getattr(i, 'c')39res[str(t) + ' low_time'] = getattr(i, 'time')40stage = 'Descent ' + str(t)41elif stage == 'Descent ' + str(t) and getattr(i, 'c') < res[str(t) + ' low_value']:42res[str(t) + ' low_value'] = getattr(i, 'c')43res[str(t) + ' low_time'] = getattr(i, 'time')44elif stage == 'Descent ' + str(t) and getattr(i, 'c') > res['0 current_value'] / 100 * (100 + descent_perc) and t < times:45res[str(t) + '_high_value'] = getattr(i, 'c')46res[str(t) + '_high_time'] = getattr(i, 'time')47t += 148stage = 'Advance ' + str(t)49elif stage == 'Descent ' + str(t) and getattr(i, 'c') > res['0 current_value'] / 100 * (100 + descent_perc) and t == times:50res[str(t) + '_high_value'] = getattr(i, 'c')51res[str(t) + '_high_time'] = getattr(i, 'time')52stage = 'Found'53elif stage == 'Found ' + str(t) and getattr(i, 'c') > res[str(t) + '_high_value'] and t == times:54res[str(t) + '_high_value'] = getattr(i, 'c')55res[str(t) + '_high_time'] = getattr(i, 'time')5657if stage == 'Found':58return res5960def check_find_curve(figi, v_days, period, price, descent_perc = 2, advance_perc = 0.5, times = 1):61time_to = datetime.now()62time_from = time_to + timedelta(days=-1 * v_days)6364try:65response = client.market.market_candles_get(figi,time_from.strftime(g_fmt),time_to.strftime(g_fmt),period)66except Exception as err:67output(figi + ' ' + str(err))68log(figi + ' market_candles_get: ' + str(err), 'error_log.txt')69return None707172candles = getattr(getattr(response, 'payload'), 'candles')7374res = {}75res['0 current_value'] = price76t = 177stage = 'Advance ' + str(t)78for i in (sorted(candles, key=lambda d: getattr(d, 'time'), reverse=True)):79if stage == 'Advance ' + str(t) and t > 1 and getattr(i, 'h') > res[str(t-1) + '_high_value']:80res[str(t-1) + '_high_value'] = getattr(i, 'h')81res[str(t-1) + '_high_time'] = getattr(i, 'time')82# Several conditions may be true83if stage == 'Advance ' + str(t) and getattr(i, 'l') < res['0 current_value'] / 100 * (100 - advance_perc):84res[str(t) + ' low_value'] = getattr(i, 'l')85res[str(t) + ' low_time'] = getattr(i, 'time')86stage = 'Descent ' + str(t)87elif stage == 'Descent ' + str(t) and getattr(i, 'l') < res[str(t) + ' low_value']:88res[str(t) + ' low_value'] = getattr(i, 'l')89res[str(t) + ' low_time'] = getattr(i, 'time')90# Several conditions may be true91if stage == 'Descent ' + str(t) and getattr(i, 'h') > res['0 current_value'] / 100 * (100 + descent_perc) and t < times:92res[str(t) + '_high_value'] = getattr(i, 'h')93res[str(t) + '_high_time'] = getattr(i, 'time')94t += 195stage = 'Advance ' + str(t)96elif stage == 'Descent ' + str(t) and getattr(i, 'h') > res['0 current_value'] / 100 * (100 + descent_perc) and t == times:97res[str(t) + '_high_value'] = getattr(i, 'h')98res[str(t) + '_high_time'] = getattr(i, 'time')99stage = 'Found'100elif stage == 'Found ' + str(t) and getattr(i, 'h') > res[str(t) + '_high_value'] and t == times:101res[str(t) + '_high_value'] = getattr(i, 'h')102res[str(t) + '_high_time'] = getattr(i, 'time')103104if stage == 'Found':105return res106107def check_level(figi, start_period_days, end_period_days, period, high_level, p_high_level_qty, low_level, p_low_level_qty):108time_to = datetime.now() + timedelta(days=-1 * end_period_days)109time_from = datetime.now() + timedelta(days=-1 * start_period_days)110v_high_level_qty = 0111v_low_level_qty = 0112113try:114response = client.market.market_candles_get(figi,time_from.strftime(g_fmt),time_to.strftime(g_fmt),period)115except Exception as err:116output(figi + ' ' + str(err))117log(figi + ' market_candles_get: ' + str(err), 'error_log.txt')118return None119120candles = getattr(getattr(response, 'payload'), 'candles')121122res = {}123res['1 start_time'] = time_from124res['2 end_time'] = time_to125res['3 period'] = period126res['4 high_level'] = high_level127res['5 low_level'] = low_level128res['6 high_level_qty'] = 0129res['7 low_level_qty'] = 0130t = 1131for i in (sorted(candles, key=lambda d: getattr(d, 'time'), reverse=True)):132if getattr(i, 'h') >= high_level:133res['6 high_level_qty'] = res['6 high_level_qty'] + 1134if getattr(i, 'l') <= low_level:135res['7 low_level_qty'] = res['7 low_level_qty'] + 1136137if res['6 high_level_qty'] >= p_high_level_qty and res['7 low_level_qty'] >= p_low_level_qty:138return res139140def should_i_stop():141with open('delete_to_stop.txt', 'r') as stp_file:142None143144def log(message, file_name='log.txt'):145146if file_name =='log.txt' or file_name =='error_log.txt':147try:148trial_str = g_trial + ' '149except NameError:150trial_str = ''151else:152trial_str = ''153154print(trial_str + str(message))155156if str(message).find('Reason: Too Many Requests') > 0 and file_name =='error_log.txt': #TooManyRequests_Sleep157print('Sleep')158time.sleep(5)159else:160f = open(file_name, 'a')161f.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S')+ ' ' + trial_str + str(message) + '\n')162f.close()163164165def output(message):166print(g_trial + ' ' + str(message))167168def buy(ticker, figi, lot_qty, lot, currency, price):169v_lot_qty = int(lot_qty)170with open(g_trial + '/bought.txt', 'a') as g:171g.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S') +172' ' + str(ticker).ljust(12, ' ') +173' ' + str(figi).ljust(12, ' ') +174' ' + str(v_lot_qty).ljust(5, ' ') +175' ' + str(lot).ljust(7, ' ') +176' ' + str(currency).ljust(4, ' ') +177' ' + str(price) + '\n')178return price*v_lot_qty*lot179180def get_bought():181b = []182try:183with open(g_trial + '/bought.txt', 'r') as f:184for item in f:185b.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])),186'ticker':item[20:33].rstrip(),187'figi':item[33:46].rstrip(),188'lot_qty':int(float(item[46:52])),189'lot':int(item[52:60]),190'currency':item[60:63],191'price':float(item[65:].rstrip())192})193except FileNotFoundError:194return b195return b196197def get_comission(p_amount):198v_amount = round(p_amount * float(g_trial_params['COMMISSION']), 2)199if v_amount < 0.01:200v_amount = 0.01201return v_amount202203def sell(ticker, lot_qty, price):204part_qty = 0205bb = get_bought()206with open(g_trial + '/bought.txt', 'w') as f:207for b in (bb):208if b['ticker'] == ticker and b['lot_qty'] <= lot_qty-part_qty:209part_qty = part_qty+b['lot_qty']210with open(g_trial + '/sold.txt', 'a') as sf:211sf.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +212' ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S') +213' ' + str(b['ticker']).ljust(12, ' ') +214' ' + str(b['figi']).ljust(12, ' ') +215' ' + str(b['lot_qty']).ljust(5, ' ') +216' ' + str(b['lot']).ljust(7, ' ') +217' ' + str(b['currency']).ljust(4, ' ') +218' ' + str(b['price']).ljust(10, ' ') +219' ' + str(price).ljust(10, ' ') +220' ' + 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)) # Profit221+ '\n')222elif b['ticker'] == ticker:223with open(g_trial + '/sold.txt', 'a') as sf:224if lot_qty-part_qty != 0:225sf.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +226' ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S') +227' ' + str(b['ticker']).ljust(12, ' ') +228' ' + str(b['figi']).ljust(12, ' ') +229' ' + str(lot_qty-part_qty).ljust(5, ' ') +230' ' + str(b['lot']).ljust(7, ' ') +231' ' + str(b['currency']).ljust(4, ' ') +232' ' + str(b['price']).ljust(10, ' ') +233' ' + str(price).ljust(10, ' ') +234' ' + 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)) # Profit235+ '\n')236237f.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +238' ' + str(b['ticker']).ljust(12, ' ') +239' ' + str(b['figi']).ljust(12, ' ') +240' ' + str(b['lot_qty']-lot_qty+part_qty).ljust(5, ' ') +241' ' + str(b['lot']).ljust(7, ' ') +242' ' + str(b['currency']).ljust(4, ' ') +243' ' + str(b['price']) + '\n')244part_qty = lot_qty245else:246f.write(b['time'].strftime('%Y-%m-%d %H:%M:%S') +247' ' + str(b['ticker']).ljust(12, ' ') +248' ' + str(b['figi']).ljust(12, ' ') +249' ' + str(b['lot_qty']).ljust(5, ' ') +250' ' + str(b['lot']).ljust(7, ' ') +251' ' + str(b['currency']).ljust(4, ' ') +252' ' + str(b['price']) + '\n')253return part_qty254255def get_sold():256b = []257try:258with open(g_trial + '/sold.txt', 'r') as f:259for item in f:260b.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])),261'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])),262'ticker':item[41:54].rstrip(),263'figi':item[54:67].rstrip(),264'lot_qty':int(item[67:73]),265'lot':int(item[73:81]),266'currency':item[81:84],267'buy_price':float(item[86:96]),268'sell_price':float(item[97:108]),269'profit':float(item[108:].rstrip())270})271except FileNotFoundError:272return b273return b274275def print_dict(v_dict, prefix = ''):276res = ''277for i in sorted(list(v_dict.keys())):278res = res + prefix + str(i) + ': ' + str(v_dict[i]) + '\n'279return res280281def update_balance(amount, currency):282b = {}283try:284f = open(g_trial+'/balances.txt', 'r')285for line in f:286b[line[0:3]] = line[4:].strip()287f.close()288except FileNotFoundError:289b['RUB'] = 0290b['USD'] = 0291b['EUR'] = 0292try:293b[currency] = round(float(b[currency]) + amount, 2)294except KeyError:295b[currency] = amount296297with open(g_trial+'/balances.txt', 'w') as g:298for curr in b.keys():299g.write(curr + '=')300g.write(str(b[curr])+'\n')301return b[currency]302303def get_balance(currency):304b = {}305try:306f = open(g_trial+'/balances.txt', 'r')307for line in f:308b[line[0:3]] = line[4:].strip()309f.close()310except FileNotFoundError:311b['RUB'] = 0312b['USD'] = 0313b['EUR'] = 0314return b[currency]315316def get_statistic():317global g_bougth_value318b = {}319try:320f = open(g_trial+'/balances.txt', 'r')321for line in f:322b['Balance ' + line[0:3]] = line[4:].strip()323f.close()324except FileNotFoundError:325b['Balance RUB'] = 0326b['Balance USD'] = 0327b['Balance EUR'] = 0328b['Bought RUB'] = 0329b['Bought USD'] = 0330b['Bought EUR'] = 0331b['Bought value RUB'] = 0332b['Bought value USD'] = 0333b['Bought value EUR'] = 0334for i in (get_bought()):335b['Bought '+i['currency']] = b['Bought '+i['currency']] + i['price'] * i['lot_qty']* i['lot']336try:337b['Bought value '+i['currency']] = b['Bought value '+i['currency']] + g_bougth_value[i['ticker']] * i['lot_qty']* i['lot']338except KeyError:339None340b['Balance&Bought RUB'] = float(b['Balance RUB']) + float(b['Bought RUB'])341b['Balance&Bought USD'] = float(b['Balance USD']) + float(b['Bought USD'])342b['Balance&Bought EUR'] = float(b['Balance EUR']) + float(b['Bought EUR'])343b['Profit RUB'] = 0344b['Profit USD'] = 0345b['Profit EUR'] = 0346for i in (get_sold()):347b['Profit '+i['currency']] = b['Profit '+i['currency']] + i['profit']348return b349350def update_statistic (stat_dict, event, qty=1):351try:352stat_dict[event] = stat_dict[event] + qty353except KeyError:354stat_dict[event] = qty355return stat_dict356357def find_and_buy():358result_statistic = {}359try:360mkt = client.market.market_stocks_get()361except Exception as err:362output('Can''t get stocks list: ' + str(err))363log('Can''t get stocks list: ' + str(err), 'error_log.txt')364return result_statistic365366bought_list = get_bought()367sold_list = get_sold()368result_statistic['Go to checks'] = 0369370try: #MindSpread371v_max_spread = float(g_trial_params['MAX_SPREAD'])372except KeyError:373v_max_spread = 1000000374375# Get my portfolio376try:377portfolio_response = client.portfolio.portfolio_get()378except Exception as err:379output('Can''t get my portfolio: ' + str(err))380log('Can''t get my portfolio: ' + str(err), 'error_log.txt')381return result_statistic382383# Cycle on stocks384for i in (getattr(getattr(mkt, 'payload'), 'instruments')):385should_i_stop()386update_statistic(result_statistic, 'Total')387388#Skip old Tickers389if getattr(i, 'ticker')[-3:] == 'old':390output(getattr(i, 'ticker') + ' old ticker')391update_statistic(result_statistic, 'Old ticker')392continue393394#Skip 'min_price_increment': None395if getattr(i, 'min_price_increment') == None:396output(getattr(i, 'ticker') + ' min_price_increment: None')397update_statistic(result_statistic, 'Min_price_increment: None')398continue399400# Check for already requested and bought401if getattr(i, 'ticker') in [c['ticker'] for c in bought_list]:402output(getattr(i, 'ticker') + ' already bought')403update_statistic(result_statistic, 'Already bought')404continue405if getattr(i, 'ticker') in [c['ticker'] for c in get_request()]:406output(getattr(i, 'ticker') + ' already requested')407update_statistic(result_statistic, 'Already requested')408continue409# Check for my portfolio410if g_trial_params['ENVIRONMENT'] == 'PROD' \411and getattr(i, 'figi') in [getattr(c, 'figi') for c in getattr(getattr(portfolio_response, 'payload'), 'positions')]:412output(getattr(i, 'ticker') + ' in my investment portfolio')413update_statistic(result_statistic, 'In my investment portfolio')414continue415416#Past experienced checks417if getattr(i, 'ticker') in g_not_available:418output(getattr(i, 'ticker') + ' NotAvailableForTrading (Past experience)')419update_statistic(result_statistic, 'NotAvailableForTrading (Past experience)')420continue421422try:423if (g_stock_price[getattr(i, 'ticker')] > float(g_trial_params['EXPENSIVE_USD']) and getattr(i, 'currency') in ['USD','EUR']) \424or (g_stock_price[getattr(i, 'ticker')] > float(g_trial_params['EXPENSIVE_RUB']) and getattr(i, 'currency') == 'RUB'):425output(getattr(i, 'ticker') + ' Too expensive ' + getattr(i, 'currency') + ' (Past experience)')426update_statistic(result_statistic, 'Too expensive ' + getattr(i, 'currency') + ' (Past experience)')427continue428except KeyError:429None430431# After all offline checks: one pause every four processed stocks432if result_statistic['Total'] % int(g_params['SLEEP_PERIOD']) == 0: #TBD Go to checks433time.sleep(1)434435# Let's pause to sell PROD436if result_statistic['Total'] % int(g_params['SELL_PROD_PERIOD']) == 0:437sell_prod()438if g_trial_params['ENVIRONMENT'] == 'PROD':439sold_list = get_sold()440441try:442response = client.market.market_orderbook_get(getattr(i, 'figi'), 2)443except Exception as err:444output(getattr(i, 'ticker') + ' market_orderbook_get: ' + str(err))445log(getattr(i, 'ticker') + ' market_orderbook_get: ' + str(err), 'error_log.txt')446update_statistic(result_statistic, 'Error')447continue448449if getattr(getattr(response, 'payload'), 'trade_status') != 'NormalTrading':450output(getattr(i, 'ticker') + ' ' + getattr(getattr(response, 'payload'), 'trade_status'))451update_statistic(result_statistic, getattr(getattr(response, 'payload'), 'trade_status'))452if getattr(getattr(response, 'payload'), 'trade_status') == 'NotAvailableForTrading':453g_not_available.append(getattr(i, 'ticker'))454continue455456lot_qty = 1457lot = int(getattr(i, 'lot'))458# The Cheapest offer in orderbook459try:460ask_price = float(getattr(getattr(getattr(response, 'payload'), 'asks')[0], 'price'))461g_stock_price[getattr(i, 'ticker')] = ask_price*lot462ask_qty = int(getattr(getattr(getattr(response, 'payload'), 'asks')[0], 'quantity'))463bid_price = float(getattr(getattr(getattr(response, 'payload'), 'bids')[0], 'price')) #MindSpread464except IndexError:465output(getattr(i, 'ticker') + ' IndexError: list index out of range')466##print(getattr(i, 'ticker') + ' ' + str(response))467update_statistic(result_statistic, 'IndexError: list index out of range')468continue469470if not ask_price:471output('No price')472print(str(response))473update_statistic(result_statistic, 'No price')474continue475476477478if (ask_price * lot > float(g_trial_params['EXPENSIVE_USD']) and getattr(i, 'currency') in ['USD','EUR']) \479or (ask_price * lot > float(g_trial_params['EXPENSIVE_RUB']) and getattr(i, 'currency') == 'RUB'):480output(getattr(i, 'ticker') + ' ' + str(ask_price) + '*' + str(lot) + ' ' + getattr(i, 'currency') + ' Too expensive')481update_statistic(result_statistic, 'Too expensive ' + getattr(i, 'currency'))482continue483484if ask_price * lot > float(get_balance(getattr(i, 'currency'))):485output(getattr(i, 'ticker') + ' ' + str(ask_price) + '*' + str(lot) + ' ' + getattr(i, 'currency') + ' Not enough money')486update_statistic(result_statistic, 'Not enough money')487continue488489if (ask_price-bid_price)/ask_price > v_max_spread: #MindSpread490output(getattr(i, 'ticker') + ': ' + str(ask_price) + '-' + str(bid_price) + ' Spread too wide')491update_statistic(result_statistic, 'Spread too wide')492continue493494#Buy_several_stocks495if ask_price * lot * 2 <= float(get_balance(getattr(i, 'currency'))) \496and ask_price * lot * 2 <= float(get_balance(getattr(i, 'currency'))) \497and ask_price * lot * 2 <= float(g_trial_params['EXPENSIVE_USD']) and getattr(i, 'currency') in ['USD','EUR'] \498and ask_qty >= 2:499lot_qty = 2500501# Check for already sold502v_already_sold_str = getattr(i, 'ticker') + ' already sold\n'503already_sold_flag = 'N'504for sold in sold_list:505## if sold['figi'] == getattr(i, 'figi'):506## 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'507if sold['figi'] == getattr(i, 'figi') \508and (datetime.now() - sold['sell_time']).total_seconds() < float(g_trial_params['SELL_TRACKING_HOURS']) * 60 * 60 \509and ask_price + 2 * get_comission(ask_price) > sold['sell_price']:510v_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'511already_sold_flag = 'Y'512if already_sold_flag == 'Y':513## v_already_sold_str = v_already_sold_str + ' Already sold\n'514## if g_trial_params['ENVIRONMENT'] == 'PROD':515## log(v_already_sold_str, g_trial+'/Already_sold_log.txt')516log(v_already_sold_str)517update_statistic(result_statistic, 'Already sold')518continue519520# Apply checks521update_statistic(result_statistic, 'Go to checks')522checks_pass = ''523try:524with open(g_trial+'/check_curve_c.txt', 'r') as check_file:525check_params = {line.split('=')[0] : line.split('=')[1].strip() for line in check_file}526q = check_find_curve_c(getattr(i, 'figi'),527int(check_params['DAYS']),528check_params['PERIOD'],529ask_price,530int(check_params['DESCENT_PERC']),531int(check_params['ADVANCE_PERC']),532int(check_params['TIMES']))533if q:534checks_pass = checks_pass + 'check_curve_c passed:\n' + print_dict(q, ' ') + '\n'535v_high_level = float(q['1_high_value'])536v_low_level = float(q['1 low_value'])537else:538checks_pass = ''539continue540except FileNotFoundError:541None542543try:544with open(g_trial+'/check_curve.txt', 'r') as check_file:545check_params = {line.split('=')[0] : line.split('=')[1].strip() for line in check_file}546q = check_find_curve(getattr(i, 'figi'),547int(check_params['DAYS']),548check_params['PERIOD'],549ask_price,550int(check_params['DESCENT_PERC']),551int(check_params['ADVANCE_PERC']),552int(check_params['TIMES']))553if q:554checks_pass = checks_pass + 'check_curve passed:\n' + print_dict(q, ' ') + '\n'555v_high_level = float(q['1_high_value'])556v_low_level = float(q['1 low_value'])557else:558checks_pass = ''559continue560except FileNotFoundError:561None562563try:564with open(g_trial+'/check_level.txt', 'r') as check_file:565check_params = {line.split('=')[0] : line.split('=')[1].strip() for line in check_file}566q = check_level(getattr(i, 'figi'),567int(check_params['START_PEROD_DAYS']),568int(check_params['END_PEROD_DAYS']),569check_params['PERIOD'],570v_high_level,571int(check_params['HIGH_LEVEL_QTY']),572v_low_level,573int(check_params['LOW_LEVEL_QTY']),)574if q:575checks_pass = checks_pass + 'check_level passed:\n' + print_dict(q, ' ') + '\n'576else:577checks_pass = ''578continue579except FileNotFoundError:580None581582583if checks_pass:584## log('Go to request to buy: ' + getattr(i, 'ticker') + ', ' + str(ask_qty), g_trial+'/log.txt')585requested_qty = request(getattr(i, 'ticker'), getattr(i, 'figi'), lot_qty, lot, getattr(i, 'currency'), ask_price, 'Buy')586if requested_qty > 0:587log('Request to buy: ' + getattr(i, 'ticker') + '\n' + checks_pass + '\n', g_trial+'/log.txt')588update_statistic(result_statistic, 'Buy requests events')589update_statistic(result_statistic, 'Buy requests stocks', requested_qty)590# Update balance before request execution591log_str = 'Update balance: ' + str(get_balance(getattr(i, 'currency'))) + ' - ' + str(lot_qty*lot*ask_price + get_comission(lot_qty*lot*ask_price))592update_balance(-1*(lot_qty*lot*ask_price + get_comission(lot_qty*lot*ask_price)), getattr(i, 'currency')) #Commission_during_buying593log_str = log_str + ' = ' + str(get_balance(getattr(i, 'currency')))594log(log_str, g_trial+'/log.txt')595## if g_trial_params['ENVIRONMENT'] == 'PROD':596## log(v_already_sold_str, g_trial+'/Already_sold_log.txt')597return result_statistic598599def check_and_sell(profit):600global g_bougth_value601g_bougth_value = {}602result_statistic = {}603n = 0604605try: #SellStocksWithLoss606v_loss_threshold = float(g_trial_params['LOSS'])607except KeyError:608v_loss_threshold = 0609610try: #SellStocksWithLoss611v_max_loss = float(g_trial_params['MAX_LOSS'])612except KeyError:613v_max_loss = 0614615try: #MindSpread616v_max_spread = float(g_trial_params['MAX_SPREAD'])617except KeyError:618v_max_spread = 1000000619620for stock in get_bought():621n = n + 1622try:623response = client.market.market_orderbook_get(stock['figi'], 2)624except Exception as err:625output(stock['ticker'] + ' market_orderbook_get: ' + str(err))626log(stock['ticker'] + ' market_orderbook_get: ' + str(err), 'error_log.txt')627continue628try:629bid_price = float(getattr(getattr(getattr(response, 'payload'), 'bids')[0], 'price'))630g_bougth_value[stock['ticker']] = bid_price631ask_price = float(getattr(getattr(getattr(response, 'payload'), 'asks')[0], 'price')) #MindSpread632except IndexError:633output('IndexError: list index out of range')634print(stock['ticker'] + ' ' + str(response) + '\n')635g_bougth_value[stock['ticker']] = float(getattr(getattr(response, 'payload'), 'last_price'))636continue637638if (stock['price'] * stock['lot_qty'] * stock['lot'] + get_comission(stock['price'] * stock['lot_qty'] * stock['lot'])) * (1+float(g_trial_params['PROFIT'])) <= \639bid_price * stock['lot_qty'] * stock['lot'] - get_comission(bid_price * stock['lot_qty'] * stock['lot']):640requested_qty = request(stock['ticker'], stock['figi'], stock['lot_qty'], stock['lot'], stock['currency'], stock['price'], 'Sell', bid_price)641if requested_qty > 0:642log('Request to sell: ' + stock['ticker'] + ' ' + str(stock['price']) + ' ' + str(bid_price), g_trial+'/log.txt')643update_statistic(result_statistic, 'Sell requests events')644update_statistic(result_statistic, 'Sell requests stocks', requested_qty)645#SellStocksWithLoss646elif v_loss_threshold > 0 and \647(stock['price'] * stock['lot_qty'] * stock['lot'] - get_comission(stock['price'] * stock['lot_qty'] * stock['lot'])) > \648(bid_price * stock['lot_qty'] * stock['lot'] + get_comission(bid_price * stock['lot_qty'] * stock['lot'])) * (1+v_loss_threshold) and \649(stock['price'] * stock['lot_qty'] * stock['lot'] - get_comission(stock['price'] * stock['lot_qty'] * stock['lot'])) < \650(bid_price * stock['lot_qty'] * stock['lot'] + get_comission(bid_price * stock['lot_qty'] * stock['lot'])) * (1+v_max_loss) and \651(ask_price-bid_price)/ask_price <= v_max_spread:652#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')653requested_qty = request(stock['ticker'], stock['figi'], stock['lot_qty'], stock['lot'], stock['currency'], stock['price'], 'Sell', bid_price)654if requested_qty > 0:655log('Request to sell with loss: ' + stock['ticker'] + ' ' + str(stock['price']) + ' ' + str(bid_price), g_trial+'/log.txt')656update_statistic(result_statistic, 'Sell requests events')657update_statistic(result_statistic, 'Sell requests stocks', requested_qty)658if n % int(g_params['SLEEP_PERIOD']) == 0:659time.sleep(1)660return result_statistic661662def request(ticker, p_figi, lot_qty, lot, currency, buy_price, req_type, sell_price=''):663order_id = ''664order_status = ''665if g_trial_params['ENVIRONMENT'] == 'PROD':666v_price = buy_price if req_type == 'Buy' else sell_price667try:668order_response = client.orders.orders_limit_order_post(figi=p_figi,669limit_order_request={"lots": lot_qty,670"operation": req_type,671"price": v_price})672order_id = getattr(getattr(order_response, 'payload'), 'order_id')673order_status = getattr(getattr(order_response, 'payload'), 'status')674log(order_response, g_trial+'/log.txt')675except Exception as err:676output('Reqest error. ' + ticker + ' ' + req_type + ' ' + str(lot_qty) + ' lots: ' + str(err))677log('Reqest error. ' + ticker + ' ' + req_type + ' ' +str(lot_qty) + ' lots: ' + str(err), 'error_log.txt')678return 0679elif g_trial_params['ENVIRONMENT'] == 'TEST':680order_status = 'New'681682if order_status == 'New':683with open(g_trial + '/request.txt', 'a') as g:684g.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S') +685' ' + str(ticker).ljust(12, ' ') +686' ' + str(p_figi).ljust(12, ' ') +687' ' + str(lot_qty).ljust(5, ' ') +688' ' + str(lot).ljust(7, ' ') +689' ' + str(currency).ljust(4, ' ') +690' ' + str(buy_price).ljust(10, ' ') +691' ' + str(req_type).ljust(4, ' ') +692' ' + str(sell_price).ljust(10, ' ') + '\n')693return lot_qty694else:695log(ticker + ' ' + req_type + ' request status: ' + order_status, g_trial+'/log.txt')696return 0697698def get_request():699b = []700try:701with open(g_trial + '/request.txt', 'r') as f:702for item in f:703r = {'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])),704'ticker':item[20:33].rstrip(),705'figi':item[33:46].rstrip(),706'lot_qty':int(float(item[46:52])),707'lot':int(item[52:60]),708'currency':item[60:63],709'buy_price':float(item[65:76].rstrip()),710'type':item[76:81].rstrip()711}712if item[81:].rstrip():713r['sell_price'] = float(item[81:].rstrip())714b.append(r)715except FileNotFoundError:716return b717return b718719720def check_requests():721#log('In check_requests', 'debug.txt') # TBD722res = {}723rr = get_request()724bought = {} # Already bought725for i in get_bought():726try:727bought[i['figi']] = bought[i['figi']] + i['lot_qty']728except KeyError:729bought[i['figi']] = i['lot_qty']730if g_trial_params['ENVIRONMENT'] == 'PROD':731try:732response = client.portfolio.portfolio_get()733my_portfolio = {getattr(c, 'figi'):int(getattr(c, 'balance')) for c in getattr(getattr(response, 'payload'), 'positions')}734except Exception as err:735output('Can''t get portfolio: ' + str(err))736log('Can''t get portfolio: ' + str(err), 'error_log.txt')737return res738739with open(g_trial + '/request.txt', 'w') as f:740for r in rr:741if (datetime.now()-r['time']).total_seconds() > 60*60*24: # Expires after 24 hours742with open(g_trial + '/rejected_requests.txt', 'a') as rf:743try:744sell_price_str = ' ' + str(r['sell_price']).ljust(10, ' ')745except KeyError:746sell_price_str = ''747rf.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +748' ' + str(r['ticker']).ljust(12, ' ') +749' ' + str(r['figi']).ljust(12, ' ') +750' ' + str(r['lot_qty']).ljust(5, ' ') +751' ' + str(r['lot']).ljust(7, ' ') +752' ' + str(r['currency']).ljust(4, ' ') +753' ' + str(r['buy_price']).ljust(10, ' ') +754' ' + str(r['type'].ljust(4, ' ')) + sell_price_str + '\n')755update_statistic(res, 'Rejected requests')756# Money returning757if r['type'] == 'Buy':758update_balance(r['lot_qty'] * r['lot'] * r['buy_price'] + get_comission(r['lot_qty'] * r['lot'] * r['buy_price']), r['currency']) #Commission_during_buying759elif r['type'] == 'Buy':760if g_trial_params['ENVIRONMENT'] == 'PROD':761try:762already_bougth = bought[r['figi']]763except KeyError:764already_bougth = 0765try:766buy_qty = int(my_portfolio[r['figi']] / r['lot'] - already_bougth)767except KeyError:768buy_qty = 0769770if buy_qty > r['lot_qty']:771update_statistic(res, 'Requests with error')772log(r['ticker'] + ' bougth more than requested: ' + str(buy_qty) + ' > ' + str(r['lot_qty']))773elif g_trial_params['ENVIRONMENT'] == 'TEST':774buy_qty = r['lot_qty']775776if buy_qty > 0:777with open(g_trial + '/bought.txt', 'a') as sf:778sf.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +779' ' + str(r['ticker']).ljust(12, ' ') +780' ' + str(r['figi']).ljust(12, ' ') +781' ' + str(buy_qty).ljust(5, ' ') +782' ' + str(r['lot']).ljust(7, ' ') +783' ' + str(r['currency']).ljust(4, ' ') +784' ' + str(r['buy_price']).ljust(10, ' ') + '\n')785update_statistic(res, 'Buy requests completed')786update_statistic(res, 'Stocks bought', buy_qty)787log(r['ticker'] + ' bougth: ' + str(buy_qty), g_trial+'/log.txt')788if r['lot_qty'] > buy_qty:789f.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +790' ' + str(r['ticker']).ljust(12, ' ') +791' ' + str(r['figi']).ljust(12, ' ') +792' ' + str(r['lot_qty']-buy_qty).ljust(5, ' ') +793' ' + str(r['lot']).ljust(7, ' ') +794' ' + str(r['currency']).ljust(4, ' ') +795' ' + str(r['buy_price']).ljust(10, ' ') +796' ' + str(r['type']) + '\n')797elif r['type'] == 'Sell':798if g_trial_params['ENVIRONMENT'] == 'PROD':799try:800sell_qty = r['lot_qty'] - int(my_portfolio[r['figi']] / r['lot'])801except KeyError:802sell_qty = r['lot_qty']803elif g_trial_params['ENVIRONMENT'] == 'TEST':804sell_qty = r['lot_qty']805806807if sell_qty > 0:808sold_qty = sell(r['ticker'], sell_qty, r['sell_price'])809if sold_qty != sell_qty:810log('Error! Faild to sell necessary amount. ' + r['ticker'] + ', sold=' + str(sold_qty) + ', necessary=' + str(sell_qty))811update_statistic(res, 'Sell requests completed')812update_statistic(res, 'Stocks sold', sold_qty)813log_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']))814update_balance(sold_qty * r['lot'] * r['sell_price'] -815## get_comission(sold_qty * r['lot'] * r['buy_price']) - #Commission_during_buying816get_comission(sold_qty * r['lot'] * r['sell_price'])817, r['currency'])818log(r['ticker'] + ' sold: ' + str(sold_qty), g_trial+'/log.txt')819log_str = log_str + ' = ' + str(get_balance(r['currency']))820log(log_str, g_trial+'/log.txt')821if r['lot_qty'] > sell_qty:822f.write(r['time'].strftime('%Y-%m-%d %H:%M:%S') +823' ' + str(r['ticker']).ljust(12, ' ') +824' ' + str(r['figi']).ljust(12, ' ') +825' ' + str(r['lot_qty']-sell_qty).ljust(5, ' ') +826' ' + str(r['lot']).ljust(7, ' ') +827' ' + str(r['currency']).ljust(4, ' ') +828' ' + str(r['buy_price']).ljust(10, ' ') +829' ' + str(r['type']).ljust(4, ' ') +830' ' + str(r['sell_price']) + '\n')831return res832833def show_all_stat():834global g_trial835with open('trials.txt', 'r') as trials_file:836trials = [line.strip() for line in trials_file]837for trial in trials:838if not trial.rstrip(): #Skip empty rows839continue840g_trial = trial841output('\n' + 'Statistic:\n' + print_dict(get_statistic(), ' '))842843def sell_prod():844global g_trial, g_trial_params, client845tmp_trial = g_trial846tmp_trial_params = g_trial_params847848for trial in set(trials):849if not trial.rstrip(): #Skip empty rows850continue851with open(trial+'/trial_params.txt', 'r') as trial_params_file:852g_trial_params = {line.split('=')[0] : line.split('=')[1].strip() for line in trial_params_file}853g_trial = trial854if g_trial_params['ENVIRONMENT'] != 'PROD':855continue856# Sell857v_dict = {}858v_dict = check_and_sell(g_trial_params['PROFIT'])859if v_dict:860log('\n' + 'check_and_sell=\n' + print_dict(v_dict, ' '))861# Requests process862v_dict = {}863v_dict = check_requests()864if v_dict:865log('\n' + 'check_requests=\n' + print_dict(v_dict, ' '))866867# Return original environment868g_trial = tmp_trial869g_trial_params = tmp_trial_params870871def trade():872global g_trial, g_params, g_trial_params, client, g_fmt, g_not_available, g_stock_price, trials873874with open('delete_to_stop.txt', 'w') as stp_file:875stp_file.write(str(datetime.now())+'\n')876with open('trials.txt', 'r') as trials_file:877trials = [line.strip() for line in trials_file]878# Reading common parameters879try:880with open('params.txt', 'r') as params_file:881g_params = {line.split('=')[0] : line.split('=')[1].strip() for line in params_file}882except FileNotFoundError:883with open('params.txt', 'w') as params_file:884params_file.write('PARAM=VALUE')885print('params.txt created')886exit(0)887last_iteration_start_time = datetime(2019, 12, 21, 15, 33, 0)888log('Starting')889f = open('token.txt', 'r')890token = f.read()891f.close()892g_fmt = '%Y-%m-%dT%H:%M:%S.%f+03:00'893g_not_available = []894g_stock_price = {}895while 2 > 1:896# No work at night897if datetime.now().hour < int(g_params['START_TIME']) and datetime.now().hour > int(g_params['END_TIME']):898print('No work at night')899time.sleep(60)900should_i_stop()901continue902903# Wait for time gap904sec_between = (datetime.now() - last_iteration_start_time).total_seconds()905if sec_between < int(g_params['TIME_GAP'])*60:906should_i_stop()907print('Pause for ' + str(int(g_params['TIME_GAP'])*60 - sec_between) + ' sec.')908time.sleep(60)909continue910last_iteration_start_time = datetime.now()911912#Sandbox or PROD913client = openapi.api_client(token)914# Process trials915for trial in trials:916if not trial.rstrip(): #Skip empty rows917continue918g_trial = trial919should_i_stop()920# Reading common parameters921with open('params.txt', 'r') as params_file:922g_params = {line.split('=')[0] : line.split('=')[1].strip() for line in params_file}923# Reading trial parameters924if not os.path.exists(trial): os.makedirs(trial)925try:926with open(trial+'/trial_params.txt', 'r') as trial_params_file:927g_trial_params = {line.split('=')[0] : line.split('=')[1].strip() for line in trial_params_file}928except FileNotFoundError:929with open(trial+'/trial_params.txt', 'w') as trial_params_file:930trial_params_file.write('PARAM=VALUE')931output('trial_params.txt created')932continue933# Sell934v_dict = {}935v_dict = check_and_sell(g_trial_params['PROFIT'])936if v_dict:937log('\n' + 'check_and_sell=\n' + print_dict(v_dict, ' '))938# Requests process939v_dict = {}940v_dict = check_requests()941if v_dict:942log('\n' + 'check_requests=\n' + print_dict(v_dict, ' '))943944if float(get_balance('USD'))<1 and float(get_balance('EUR'))<1 and float(get_balance('RUB'))<50:945output('No money left')946elif datetime.now().hour < int(g_params['START_BUY_TIME']) and datetime.now().hour > int(g_params['END_TIME']):947output('We are not buying in the morning')948g_not_available = []949g_stock_price = {}950elif g_trial_params['STOP_BUYING'] == 'Y':951output('Buying is stopped')952else:953log('\n' + 'find_and_buy=\n' + print_dict(find_and_buy(), ' '))954# Requests process955v_dict = {}956v_dict = check_requests()957if v_dict:958log('\n' + 'check_requests=\n' + print_dict(v_dict, ' '))959log('\n' + 'Statistic:\n' + print_dict(get_statistic(), ' '))960961if __name__ == "__main__":962trade()963964965