Advertisement
den4ik2003

Untitled

Mar 10th, 2025
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.03 KB | None | 0 0
  1. from threading import Thread
  2. from itertools import accumulate
  3. import time
  4. from rabbit_client import RabbitLoggingClient
  5.  
  6.  
  7. def proper_round(x, precision):
  8. return float(f"%.{precision}f" % round(x, precision))
  9.  
  10. def str_round(x, precision):
  11. return f"%.{precision}f" % round(x, precision)
  12.  
  13.  
  14. class MarketMaking:
  15. def __init__(self, cooldown = 2.5, ema_gamma = 0., prev_mid = 0, price_mode='by_mid_price', trades_impact_coeff=1.):
  16. self.cooldown = cooldown
  17. self.params = []
  18. self.ema_gamma = 0.0
  19. self.prev_mid = prev_mid
  20. self.zi = 0
  21. self.price_mode = price_mode
  22. if self.price_mode == 'by_trades':
  23. self.trades_impact_coeff = trades_impact_coeff
  24.  
  25. self.rabbit_logging_client = RabbitLoggingClient()
  26.  
  27. def log(self, params, name, obj):
  28. params['log'].write(f'{int(time.time() * 1000)} {name} {obj}\n')
  29. params['log'].flush()
  30. self.rabbit_logging_client._log(f'{int(time.time() * 1000)} {name} {obj}')
  31.  
  32. def make_zone(self,
  33. bound = 1500,
  34. total_base = None,
  35. total_quote = None,
  36. qty_step = 2000,
  37. n_levels = 20,
  38. protection = False
  39. ):
  40. zone = {}
  41. zone['zone'] = float(bound)
  42. zone['n_levels'] = int(n_levels)
  43. zone['total_base'] = float(total_base) if total_base != None else None
  44. zone['total_quote'] = float(total_quote) if total_quote != None else None
  45. zone['protection'] = bool(protection)
  46. zone['qty_step'] = float(qty_step) / 10000
  47. return zone
  48.  
  49. # def add_market(self, market, levels, margins, bal_cut, zero_pos, min_bal, n_sup=4, ask_sup_cut=0.05, bid_sup_cut=0.05, cancel_on_start=False, ask_bal_cut=None, bid_bal_cut=None):
  50. def add_market(self,
  51. market,
  52. spread_size = 200,
  53. zero_base = None,
  54. zero_quote = None,
  55. taker_buyback_price = None,
  56. taker_buyback_funds = 0,
  57. drop_base_offset = 0,
  58. drop_quote_offset = 0,
  59. ask_zones = [],
  60. bid_zones = [],
  61. cancel_on_start = False,
  62. retain = False,
  63. cashout_order_size = 0,
  64. min_order_size = 5):
  65. params = {}
  66. info = market.get_info()
  67. if len(ask_zones) == 0:
  68. ask_zones = [self.make_zone(total_base = info['balance']['base'] * 0.2)]
  69. if len(bid_zones) == 0:
  70. bid_zones = [self.make_zone(total_quote = info['balance']['quote'] * 0.2)]
  71. ask_zones = sorted(ask_zones, key = lambda x: x['zone'])
  72. bid_zones = sorted(bid_zones, key = lambda x: x['zone'])
  73. params['market'] = market
  74.  
  75. add = spread_size // 2
  76. params['spread_size'] = spread_size
  77. params['min_order_size'] = min_order_size
  78. params['cashout_order_size'] = cashout_order_size
  79. params['retain'] = retain
  80. params['drop_base_offset'] = params['min_pos_base'] = drop_base_offset
  81. params['taker_buyback_price'] = taker_buyback_price
  82. params['taker_buyback_funds'] = taker_buyback_funds
  83. params['total_sell_base'] = 0
  84. sm = 0
  85. for zone in ask_zones:
  86. zone['total_base'] -= sm
  87. sm += zone['total_base']
  88. if not 'protection' in zone or zone['protection'] != True:
  89. params['min_pos_base'] += zone['total_base']
  90. params['total_sell_base'] += zone['total_base']
  91. print(zone['total_base'])
  92. zone['min_level_base'] = zone['total_base'] / (zone['n_levels'] * (1 + zone['qty_step'] * (zone['n_levels'] - 1) / 2))
  93. zone['price_step'] = (zone['zone'] - add + spread_size) // zone['n_levels']
  94. zone['levels'] = [add + zone['price_step'] * i for i in range(zone['n_levels'])]
  95. zone['prev'] = [0 for lvl in zone['levels']]
  96. zone['margins'] = [zone['price_step'] // 2 for i in range(zone['n_levels'])]
  97. add = zone['zone'] + zone['price_step']
  98. if zero_base == None or zero_base == 0:
  99. zero_base = info['balance']['base']
  100. params['zero_base'] = zero_base
  101. params['ask_zones'] = ask_zones
  102. params['stopped'] = False
  103.  
  104. add = spread_size // 2
  105. params['drop_quote_offset'] = params['max_pos_quote'] = drop_quote_offset
  106. params['total_buy_quote'] = 0
  107. sm = 0
  108. for zone in bid_zones:
  109. zone['total_quote'] -= sm
  110. sm += zone['total_quote']
  111. if not 'protection' in zone or zone['protection'] != True:
  112. params['max_pos_quote'] += zone['total_quote']
  113. params['total_buy_quote'] += zone['total_quote']
  114. zone['min_level_quote'] = zone['total_quote'] / (zone['n_levels'] * (1 + zone['qty_step'] * (zone['n_levels'] - 1) / 2))
  115. zone['price_step'] = (zone['zone'] - add + spread_size) // zone['n_levels']
  116. zone['levels'] = [add + zone['price_step'] * i for i in range(zone['n_levels'])]
  117. zone['prev'] = [0 for lvl in zone['levels']]
  118. zone['margins'] = [zone['price_step'] // 2 for i in range(zone['n_levels'])]
  119. if add == spread_size // 2:
  120. zone['margins'][0] = 0
  121. add = zone['zone'] + zone['price_step']
  122. if zero_quote == None or zero_quote == 0:
  123. zero_quote = info['balance']['quote']
  124. params['zero_quote'] = zero_quote
  125. params['bid_zones'] = bid_zones
  126. params['cnt'] = 0
  127.  
  128. params['cancel_on_start'] = cancel_on_start
  129. params['log'] = open(f"/mnt/mm_telemetry/{market.symbol[1].upper()}_{market.symbol[2].upper()}_{market.market}", "a")
  130. self.log(params, "alert", {'op': 'start'})
  131. self.params.append(params)
  132.  
  133. def build_grid(self, params, mid_price):
  134. info = params['market'].get_info()
  135. self.log(params, 'book', info['book'])
  136. self.log(params, 'balance', info['balance'])
  137. self.log(params, 'public_trades', params['market'].get_new_public_trades())
  138. self.log(params, 'private_trades', params['market'].get_new_private_trades())
  139. #print(info)
  140. print(f"{params['market'].market}: cur balances: {info['balance']}, zero base: {params['zero_base']}, zero quote: {params['zero_quote']}")
  141. print(f'mid price: {mid_price}')
  142. precision = params['market'].symbol[3]
  143. real_ask = info['book']['ask_price']
  144. real_bid = info['book']['bid_price']
  145. if real_ask == 0:
  146. real_ask = mid_price
  147. # ask_mid = max(mid_price, real_bid)
  148. # bid_mid = min(mid_price, real_ask)
  149. # print(f'ask_mid={ask_mid} bid_mid={bid_mid}')
  150.  
  151. price_step = proper_round(0.1 ** precision, precision)
  152. asks = []
  153. bids = []
  154.  
  155. base_balance = info['balance']['base']
  156. quote_balance = info['balance']['quote']
  157. ask_bound = bid_bound = mid_price
  158. cur_pos_base = (base_balance - params['zero_base'])
  159. print(f"{cur_pos_base} ~~~ {-params['min_pos_base']}")
  160. if cur_pos_base < -params['min_pos_base']:
  161. params['stopped'] = True
  162. raise Exception(f"{params['market'].market} stopped by default maxpos checker due to base")
  163.  
  164. cur_pos_quote = (params['zero_quote'] - quote_balance)
  165. print(f"{cur_pos_quote} -- {params['max_pos_quote']}")
  166. if cur_pos_quote > params['max_pos_quote']:
  167. params['stopped'] = True
  168. raise Exception(f"{params['market'].market} stopped by default maxpos checker due to quote")
  169.  
  170. bound = mid_price
  171. if base_balance > params['zero_base'] + 1 / mid_price:
  172. bound = ask_bound = max(mid_price, (params['zero_quote'] - quote_balance) / (base_balance - params['zero_base']))
  173. elif base_balance < params['zero_base'] - 1 / mid_price:
  174. bound = bid_bound = min(mid_price, (quote_balance - params['zero_quote']) / (params['zero_base'] - base_balance))
  175. print(f'ask_bound={ask_bound} bid_bound={bid_bound} <<<<<<')
  176. position_info = {'bound': bound, 'zero_base': params['zero_base'], 'zero_quote': params['zero_quote']}
  177. self.log(params, 'position', position_info)
  178.  
  179. available_sell = []
  180. cur_total_sell = max(0, params['total_sell_base'] + min(0, cur_pos_base + params['drop_base_offset']))
  181. sell_coef = cur_total_sell / params['total_sell_base']
  182. norm_coef = min(base_balance, cur_total_sell) / cur_total_sell
  183. for zone in reversed(params['ask_zones']):
  184. # if zone['protection']:
  185. # delta = zone['total_base']
  186. # else:
  187. # delta = min(cur_total_sell, zone['total_base'])
  188. # cur_total_sell -= delta
  189. available_sell.append(zone['total_base'])
  190. available_sell = list(reversed(available_sell))
  191.  
  192. zi = 0
  193. for zone in params['ask_zones']:
  194. min_qty = zone['min_level_base']
  195. qty_step = zone['qty_step']
  196. if min_qty * zone['n_levels'] >= available_sell[zi]:
  197. min_qty = available_sell[zi] / zone['n_levels']
  198. qty_step = 0
  199. else:
  200. qty_step = (available_sell[zi] / (min_qty * zone['n_levels']) - 1) * 2 / (zone['n_levels'] - 1)
  201. for i in range(zone['n_levels']):
  202. suggest_ask = proper_round(mid_price * (1 + zone['levels'][i] / 10000), precision)
  203. if abs(zone['prev'][i] / suggest_ask - 1) <= zone['margins'][i] / 10000:
  204. suggest_ask = zone['prev'][i]
  205. if len(asks) > 0 and asks[-1][0] >= suggest_ask:
  206. suggest_ask = asks[-1][0] + price_step
  207. zone['prev'][i] = suggest_ask
  208. qty = min_qty * (1 + i * qty_step) * norm_coef
  209. if not zone['protection']:
  210. qty *= sell_coef
  211. asks.append([float(str_round(suggest_ask, precision)), qty])
  212.  
  213. zi += 1
  214.  
  215. available_buy = []
  216. cur_total_buy = params['total_buy_quote'] - max(0, cur_pos_quote - params['drop_quote_offset'])
  217. buy_coef = cur_total_buy / params['total_buy_quote']
  218. norm_coef = min(quote_balance, cur_total_buy) / cur_total_buy
  219. for zone in reversed(params['bid_zones']):
  220. # if zone['protection']:
  221. # delta = zone['total_quote']
  222. # else:
  223. # delta = min(cur_total_buy, zone['total_quote'])
  224. # cur_total_buy -= delta
  225. available_buy.append(zone['total_quote'])
  226. available_buy = list(reversed(available_buy))
  227.  
  228. zi = 0
  229. for zone in params['bid_zones']:
  230. min_qty = zone['min_level_quote']
  231. qty_step = zone['qty_step']
  232. if min_qty * zone['n_levels'] >= available_buy[zi]:
  233. min_qty = available_buy[zi] / zone['n_levels']
  234. qty_step = 0
  235. else:
  236. qty_step = (available_buy[zi] / (min_qty * zone['n_levels']) - 1) * 2 / (zone['n_levels'] - 1)
  237.  
  238. for i in range(zone['n_levels']):
  239. suggest_bid = proper_round(mid_price * (1 - zone['levels'][i] / 10000), precision)
  240. if abs(zone['prev'][i] / suggest_bid - 1) <= zone['margins'][i] / 10000:
  241. suggest_bid = zone['prev'][i]
  242. if len(bids) > 0 and bids[-1][0] <= suggest_bid:
  243. suggest_bid = bids[-1][0] - price_step
  244. zone['prev'][i] = suggest_bid
  245. qty = (min_qty * (1 + i * qty_step)) / suggest_bid * norm_coef
  246. if not zone['protection']:
  247. qty *= buy_coef
  248. bids.append([float(str_round(suggest_bid, precision)), qty])
  249.  
  250. zi += 1
  251.  
  252. if not params['retain']:
  253. ask_bound = max(ask_bound, real_bid + price_step)
  254. bid_bound = min(bid_bound, real_ask - price_step)
  255. min_ask_price = 1e9
  256. for ask in asks:
  257. min_ask_price = min(min_ask_price, ask[0])
  258. if min_ask_price < ask_bound:
  259. for ask in asks:
  260. ask[0] += ask_bound - min_ask_price
  261. ask[0] = float(str_round(ask[0], precision))
  262. max_bid_price = 0
  263. for bid in bids:
  264. max_bid_price = max(max_bid_price, bid[0])
  265. if max_bid_price > bid_bound:
  266. for bid in bids:
  267. bid[0] -= max_bid_price - bid_bound
  268. bid[0] = float(str_round(bid[0], precision))
  269.  
  270. print('asks', asks)
  271. print('bids', bids)
  272.  
  273. actual_asks, actual_bids = [], []
  274. asks_less_than_min_size, bids_less_than_min_size = 0, 1 # в quote из-за умножения больше неточность
  275. for ask in asks:
  276. if ask[0] * ask[1] >= params['min_order_size'] + 0.1:
  277. actual_asks.append(ask)
  278. else:
  279. asks_less_than_min_size += ask[1]
  280.  
  281. for bid in bids:
  282. if bid[0] * bid[1] >= params['min_order_size'] + 0.1:
  283. actual_bids.append(bid)
  284. else:
  285. bids_less_than_min_size += (bid[0] * bid[1])
  286.  
  287. print('asks_less_than_min_size:', asks_less_than_min_size)
  288. print('bids_less_than_min_size:', bids_less_than_min_size)
  289.  
  290. if cur_pos_base <= -params['min_pos_base'] + asks_less_than_min_size:
  291. params['stopped'] = True
  292. raise Exception(f"{params['market'].market} stopped by updated maxpos checker due to base")
  293.  
  294. if cur_pos_quote + bids_less_than_min_size >= params['max_pos_quote']:
  295. params['stopped'] = True
  296. raise Exception(f"{params['market'].market} stopped by updated maxpos checker due to quote")
  297.  
  298. if params['cashout_order_size'] != 0:
  299. if cur_pos_base > 0: # открыта long позиция -> закрываем продажей
  300. """ вместо real_ask можно использовать вместо min(real_ask, user_ask_new), т.к.
  301. плохой случай, это когда мы сейчас выставимся более узко, чем было до этого,
  302. но т.к. мм после этого сужения на следующей итерации не будет сужаться сильнее, то значит на следующей
  303. итерации всё выставится как надо.
  304. и ещё плохой случай, когда цена постоянно уходит вниз, но раз цена идет вниз, значит давление на продажу,
  305. а значит наш кэшаут вряд ли исполнится, и некритично подождать пока всё сбалансируется
  306. """
  307. self.log(params, 'cashout', f'{cur_pos_base} > 0') # debug
  308. price = real_ask - price_step
  309. if price <= real_bid:
  310. price = real_ask
  311.  
  312. qty = min(cur_pos_base, params['cashout_order_size'] / price)
  313.  
  314. if qty * price >= params['min_order_size'] + 0.1 and price >= ask_bound:
  315. actual_asks.append([float(str_round(price, precision)), qty])
  316. self.log(params, 'cashout', actual_asks[-1]) # debug
  317.  
  318. elif cur_pos_base < 0: # открыта short позиция -> закрываем покупкой
  319. self.log(params, 'buyback', f'{cur_pos_base} < 0') # debug
  320. price = real_bid + price_step
  321. if price >= real_ask:
  322. price = real_bid
  323.  
  324. qty = min(-cur_pos_base, params['cashout_order_size'] / price)
  325.  
  326. if qty * price >= params['min_order_size'] + 0.1 and price <= bid_bound:
  327. actual_bids.append([float(str_round(price, precision)), qty])
  328. self.log(params, 'buyback', actual_asks[-1]) # debug
  329.  
  330. return [actual_asks, actual_bids]
  331.  
  332. def process_grid(self, params, mid_price):
  333. new_grid = self.build_grid(params, mid_price)
  334. print('built')
  335. print('new grid', new_grid)
  336. new_ask_grid = {float(ask[0]): ask[1] for ask in new_grid[0]}
  337. new_bid_grid = {float(bid[0]): bid[1] for bid in new_grid[1]}
  338. orders = params['market'].get_orders()
  339. self.log(params, 'orders', orders)
  340. params['cnt'] += 1
  341. if params['cnt'] % 5 == 0:
  342. depth = params['market'].get_depth()
  343. ask_price = depth['asks'][0][0] if len(depth['asks']) > 0 else 1e9
  344. bid_price = depth['bids'][0][0] if len(depth['bids']) > 0 else 0
  345. organic = {'ask': [0 for i in range(30)], 'bid': [0 for i in range(30)]}
  346. user = {'ask': [0 for i in range(30)], 'bid': [0 for i in range(30)]}
  347. for ask in depth['asks']:
  348. percent = int(100 * (ask[0] / ask_price - 1))
  349. if percent >= 30:
  350. break
  351. organic['ask'][percent] += ask[1] * ask[0]
  352. for bid in depth['bids']:
  353. percent = int(100 * (1 - bid[0] / bid_price))
  354. if percent >= 30:
  355. continue
  356. organic['bid'][percent] += bid[1] * bid[0]
  357. if ask_price == 1e9 and len(orders['ask']) > 0:
  358. ask_price = min([order['price'] for order in orders['ask']])
  359. if bid_price == 0 and len(orders['bid']) > 0:
  360. bid_price = max([order['price'] for order in orders['bid']])
  361. for order in orders['ask']:
  362. qty = order['quantity']
  363. price = order['price']
  364. if price < ask_price:
  365. continue
  366. percent = int(100 * (price / ask_price - 1))
  367. if percent >= 30:
  368. continue
  369. organic['ask'][percent] -= qty * price
  370. user['ask'][percent] += qty * price
  371. for order in orders['bid']:
  372. qty = order['quantity']
  373. price = order['price']
  374. if price > bid_price:
  375. continue
  376. percent = int(100 * (1 - price / bid_price))
  377. if percent >= 30:
  378. continue
  379. organic['bid'][percent] -= qty * price
  380. user['bid'][percent] += qty * price
  381. organic['ask'] = list(accumulate(organic['ask']))
  382. organic['bid'] = list(accumulate(organic['bid']))
  383. user['ask'] = list(accumulate(user['ask']))
  384. user['bid'] = list(accumulate(user['bid']))
  385. self.log(params, 'organic_depth', organic)
  386. self.log(params, 'user_depth', user)
  387. step = 0.1 ** params['market'].symbol[3]
  388. if params['taker_buyback_price'] != None and params['taker_buyback_funds'] >= 5 and min(new_ask_grid.keys()) >= ask_price + step and ask_price < mid_price:
  389. delta = min(depth['asks'][0][1], params['taker_buyback_funds'] / ask_price)
  390. print(f'LOL {ask_price} {delta}')
  391. params['taker_buyback_funds'] -= delta * ask_price
  392. params['market'].new_limit(ask_price, delta, True)
  393. print(params['cnt'])
  394. print(f"{params['market'].market} asks: {orders['ask']}")
  395. print(f"{params['market'].market} bids: {orders['bid']}")
  396. # print(new_ask_prices)
  397. # print(new_bid_prices)
  398. # print(orders[0].keys())
  399. to_cancel = []
  400. to_create = [[], []]
  401. for order in orders['ask']:
  402. if order['price'] in new_ask_grid:
  403. new_ask_grid.pop(order['price'])
  404. else:
  405. to_cancel.append(order['id'])
  406. for order in orders['bid']:
  407. if order['price'] in new_bid_grid:
  408. new_bid_grid.pop(order['price'])
  409. else:
  410. to_cancel.append(order['id'])
  411. for price in new_ask_grid:
  412. ask = [price, new_ask_grid[price]]
  413. if not ask[0] in orders['ask']:
  414. to_create[0].append(ask)
  415. for price in new_bid_grid:
  416. bid = [price, new_bid_grid[price]]
  417. if not bid[0] in orders['bid']:
  418. to_create[1].append(bid)
  419. threads = []
  420. print(f'New len: {len(to_create[0])} + {len(to_create[1])}, cancel len: {len(to_cancel)}')
  421. print(to_create[0])
  422. for oid in to_cancel:
  423. # print(f'Start #{self.zi}: cancel {oid}')
  424. self.zi += 1
  425. threads.append(Thread(target=params['market'].cancel, args=(oid,)))
  426. threads[-1].start()
  427. for ask in to_create[0]:
  428. # print(f'Start #{self.zi}: create ask {ask}')
  429. self.zi += 1
  430. threads.append(Thread(target=params['market'].new_limit_maker, args=(ask[0], ask[1], False)))
  431. threads[-1].start()
  432. for bid in to_create[1]:
  433. # print(f'Start #{self.zi}: create bid {bid}')
  434. self.zi += 1
  435. threads.append(Thread(target=params['market'].new_limit_maker, args=(bid[0], bid[1], True)))
  436. threads[-1].start()
  437. return threads
  438.  
  439.  
  440. def calculate_mid_price_by_trades(self, target_params, mid_price):
  441. trades_buy_pressure_amount = 0
  442. trades_sell_pressure_amount = 0
  443.  
  444. if not target_params['stopped']:
  445. trades = target_params['market'].get_new_private_trades(reset=False)
  446. else:
  447. """
  448. т.к. когда бот остановлен, то трейды не логгируются,
  449. поэтому get_new_private_trades(True) не вызывается,
  450. поэтому не обновляется положение последнего обработанного трейда
  451. """
  452. trades = target_params['market'].get_new_private_trades(reset=True)
  453.  
  454. print('trades during cd:', trades)
  455.  
  456. for trade in trades:
  457. if trade['side'] == 'buy':
  458. trades_buy_pressure_amount += trade['price'] * trade['quantity']
  459. if trade['side'] == 'sell':
  460. trades_sell_pressure_amount += trade['quantity']
  461.  
  462. mp_up_delta = trades_sell_pressure_amount / (target_params['min_pos_base'] - target_params['drop_base_offset']) * target_params['spread_size']
  463. mp_down_delta = trades_buy_pressure_amount / (target_params['max_pos_quote'] - target_params['drop_quote_offset']) * target_params['spread_size']
  464.  
  465. print('mp_up_delta', mp_up_delta)
  466. print('mp_down_delta', mp_down_delta)
  467.  
  468. total_delta = (mp_up_delta - mp_down_delta) / self.trades_impact_coeff
  469.  
  470. return mid_price * (1 + total_delta / 10000)
  471.  
  472.  
  473. def run_event_loop(self, target=None):
  474. for i in range(len(self.params)):
  475. if self.params[i]['cancel_on_start']:
  476. self.params[i]['market'].cancel_open_orders()
  477. # for market in self.markets:
  478. # market.cancel_open_orders()
  479.  
  480. for params in self.params:
  481. print(params['market'].get_info()['balance'])
  482.  
  483. while True:
  484. try:
  485. info = self.params[0]['market'].get_info()
  486. if target == None:
  487. print(info)
  488. mid = (info['book']['ask_price'] + info['book']['bid_price']) / 2
  489. if self.prev_mid > 0:
  490. if self.price_mode == 'by_trades':
  491. mid = self.calculate_mid_price_by_trades(self.params[0], self.prev_mid)
  492. else:
  493. mid = self.prev_mid * self.ema_gamma + (1 - self.ema_gamma) * mid
  494. mid = max(self.prev_mid * 0.9995, min(self.prev_mid * 1.0005, mid))
  495. else:
  496. self.pred_mid = mid
  497. mid_2 = mid
  498.  
  499. else:
  500. mid = target()
  501. mid_2 = (info['book']['ask_price'] + info['book']['bid_price']) / 2
  502. self.prev_mid = mid
  503. print(f'{int(time.time() * 1000)} - {mid}')
  504. self.zi = 0
  505. threads = []
  506. i = 0
  507. for params in self.params:
  508. i += 1
  509. if not params['stopped']:
  510. try:
  511. threads.extend(self.process_grid(params, mid if i == 1 else mid_2))
  512. except Exception as e:
  513. print(f'exception: {e}')
  514. self.log(params, 'orders', params['market'].get_orders())
  515.  
  516. if params['stopped']:
  517. self.log(params, 'alert', {'op': 'stop'})
  518.  
  519. for t in threads:
  520. t.join()
  521. except Exception as e:
  522. print(f'exception: {e}')
  523. finally:
  524. time.sleep(self.cooldown)
  525.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement