def dump_balance_log(self): print_(['margin dumping balance']) with open(make_get_filepath(self.balance_log_filepath), 'a') as f: self.balance['timestamp'] = self.cc.milliseconds() line = json.dumps(self.balance) + '\n' f.write(line) self.time_keepers['dump_balance_log'] = time()
def update_open_orders(self, symbol: str): ''' may be called threaded ''' print_(['margin updating open orders', symbol]) if symbol == 'all': symbols = set() self.my_asks = {s: [] for s in self.symbols} self.my_bids = {s: [] for s in self.symbols} for oo in sorted(self.fetch_margin_open_orders(), key=lambda x: (x['symbol'], x['price'])): symbols.add(oo['symbol']) if oo['side'] == 'buy': if oo['symbol'] not in self.my_bids: self.my_bids[oo['symbol']] = [] self.my_bids[oo['symbol']].append(oo) else: if oo['symbol'] not in self.my_asks: self.my_asks[oo['symbol']] = [] self.my_asks[oo['symbol']].append(oo) else: symbols = [symbol] open_orders = self.fetch_margin_open_orders(symbol) self.my_bids[symbol], self.my_asks[symbol] = [], [] for oo in sorted(open_orders, key=lambda x: x['price']): if oo['side'] == 'buy': self.my_bids[symbol].append(oo) else: self.my_asks[symbol].append(oo) now = time() for symbol in symbols: self.time_keepers['update_open_orders'][symbol] = now
def on_update(self, symbol: str, update: dict): try: if symbol not in self.symbols_set: return if update['bids'][-1]['price'] < self.cm.previous_updates[symbol][ 'bids'][-1]['price']: # highest bid became lower. was my bid taken if self.my_bids[symbol]: if update['bids'][-1]['price'] <= self.my_bids[symbol][-1][ 'price']: # maybe or yes print_(['margin bid taken', symbol]) self.previous_ts_bid_taken[symbol] = time() self.time_keepers['update_balance'] = 0 if update['asks'][0]['price'] > self.cm.previous_updates[symbol][ 'asks'][0]['price']: # lowest ask became higher. was my ask taken if self.my_asks[symbol]: if update['asks'][0]['price'] >= self.my_asks[symbol][0][ 'price']: # maybe or yes print_(['margin ask taken', symbol]) self.previous_ts_ask_taken[symbol] = time() self.time_keepers['update_balance'] = 0 if all([ self.time_keepers[key][symbol] > 10 for key in ['update_open_orders', 'update_my_trades'] ]): self.try_wrapper(self.update_ideal_orders, (symbol, )) self.try_wrapper(self.execute_to_exchange, (symbol, )) self.last_stream_tick_ts = time() except (Exception) as e: print('\n\nERROR in on_update', e)
def update_loan_history(self, coin: str, side: str): assert side in ['borrow', 'repay'] if coin == self.quot: return elif coin == 'BNB': getattr(self, f'{side}_history')[coin] = [] return print_([f'margin update {side} history', coin]) if coin not in getattr(self, f'{side}_history'): self.init_loan_history(coin, side) else: old_loans = getattr(self, f'{side}_history')[coin] fetched = new_loans = getattr(self, f'fetch_{side}_history')(coin) prev_fetch = [] most_recent_id = old_loans[-1]['id'] if old_loans else 0 current = 2 while fetched and fetched != prev_fetch and fetched[0][ 'id'] > most_recent_id: prev_fetch = fetched fetched = getattr(self, f'fetch_{side}_history')(coin, current) new_loans = fetched + new_loans current += 1 condition = lambda x: x['id'] > most_recent_id cache_filepath = f'cache/binance/{self.user}/{side}_history/{coin}/' written_loans = self.write_cache(cache_filepath, new_loans, condition) getattr(self, f'{side}_history')[coin] += written_loans self.time_keepers[f'update_{side}_history'][coin] = time()
def print_update(self, update: dict): print_([ 'margin', { k: update[k] for k in [ 'coin', 'symbol', 'side', 'type', 'status', 'price', 'amount', 'tranId' ] if k in update } ])
def repay(self, coin: str, amount: float): amount = round(amount, 8) if amount <= 0.0: return print_([f'repaying {amount} {coin}']) repaid = self.cc.sapi_post_margin_repay(params={ 'asset': coin, 'amount': amount }) repaid['coin'] = coin repaid['amount'] = amount repaid['side'] = 'repay' repaid['timestamp'] = self.cc.milliseconds() return repaid
def borrow(self, coin: str, amount: float): amount = round(amount, 8) if amount <= 0.0: return print_([f'borrowing {amount} {coin}']) borrowed = self.cc.sapi_post_margin_loan(params={ 'asset': coin, 'amount': amount }) borrowed['coin'] = coin borrowed['amount'] = amount borrowed['side'] = 'borrow' borrowed['timestamp'] = self.cc.milliseconds() return borrowed
def update_my_trades(self, symbol: str): print_(['margin updating my trades', symbol]) if symbol not in self.my_trades: self.init_my_trades(symbol) else: limit = 100 new_my_trades = fetched_my_trades = self.fetch_margin_my_trades( symbol, limit=limit, from_id=self.my_trades[symbol][-1]['id'] + 1 if self.my_trades[symbol] else 0) prev_fetch = [] while fetched_my_trades != prev_fetch and len( fetched_my_trades) == limit: limit = 1000 prev_fetch = fetched_my_trades fetched_my_trades = self.fetch_margin_my_trades( symbol, limit=limit, from_id=fetched_my_trades[-1]['id'] + 1) new_my_trades += fetched_my_trades if new_my_trades: no_dash = symbol.replace('/', '_') cache_filepath = f'cache/binance/{self.user}/my_trades/{no_dash}/' condition = lambda x: x['id'] > self.my_trades[symbol][-1]['id' ] written_my_trades = self.write_cache(cache_filepath, new_my_trades, condition) self.my_trades[symbol], self.my_trades_analyses[symbol] = \ analyze_my_trades(self.my_trades[symbol] + written_my_trades) age_limit_millis = self.cc.milliseconds( ) - self.hyperparams['max_memory_span_millis'] my_trades = [ e for e in self.my_trades[symbol] if e['timestamp'] > age_limit_millis ] self.my_trades[symbol] = my_trades self.time_keepers['update_my_trades'][symbol] = time()
def lock_execute_release(self, symbol: str, fns: [Callable], args_list: [tuple], kwargs_list: [dict]): try: self.locks[symbol].acquire() self.time_keepers['locks'][symbol] = time() self.cm.add_ts_to_lists(len(fns)) future_results = [] for i in range(len(fns)): args = args_list[i] if len(args_list) > i else () kwargs = kwargs_list[i] if len(kwargs_list) > i else {} future_result = threaded(fns[i])(*args, **kwargs) future_results.append(future_result) for future_result in future_results: if future_result.exception(): if 'Unknown order sent' in future_result.exception( ).args[0]: print_([ 'ERROR', 'margin', symbol, 'trying to cancel non-existing order; schedule open orders update' ]) self.time_keepers['update_open_orders'][symbol] = 0 elif 'Account has insufficient balance' in future_result.exception( ).args[0]: print_([ 'ERROR', 'margin', symbol, fns[i].__name__, args, 'insufficient funds; schedule balance update' ]) self.time_keepers['update_balance'] = 0 else: print_([ 'ERROR', 'margin', symbol, future_result.exception(), fns, args_list ]) else: self.try_wrapper(self.update_after_execution, (future_result.result(), )) self.locks[symbol].release() except (Exception) as e: print('\n\nERROR with lock_execute_release', e)
def update_balance(self): ''' may be called threaded ''' print_(['margin updating balance']) fetched = self.cc.sapi_get_margin_account() new_balance = {} onhand_sum_quot = 0.0 debt_sum_quot = 0.0 borrowable_quot = self.fetch_borrowable(self.quot) for e in fetched['userAssets']: c = e['asset'] if c not in self.all_coins_set: continue new_balance[c] = { 'free': float(e['free']), 'used': float(e['locked']), 'borrowed': float(e['borrowed']), 'interest': float(e['interest']), 'equity': float(e['netAsset']), 'free': float(e['free']), 'borrowable': (self.cm.convert_amount(borrowable_quot, self.quot, c) if c in self.do_borrow else 0.0), } new_balance[c][ 'onhand'] = new_balance[c]['free'] + new_balance[c]['used'] new_balance[c]['debt'] = new_balance[c]['interest'] + new_balance[ c]['borrowed'] onhand_sum_quot += self.cm.convert_amount(new_balance[c]['onhand'], c, self.quot) debt_sum_quot += self.cm.convert_amount(new_balance[c]['debt'], c, self.quot) for c in self.all_coins_set: if c not in new_balance: new_balance[c] = { key: 0.0 for key in [ 'free', 'used', 'borrowed', 'interest', 'onhand', 'equity', 'debt' ] } new_balance[c]['borrowable'] = \ (self.cm.convert_amount(borrowable_quot, self.quot, c) if c in self.do_borrow else 0.0) new_balance[c]['account_onhand'] = self.cm.convert_amount( onhand_sum_quot, self.quot, c) new_balance[c]['account_debt'] = self.cm.convert_amount( debt_sum_quot, self.quot, c) new_balance[c]['account_equity'] = \ new_balance[c]['account_onhand'] - new_balance[c]['account_debt'] try: if self.balance[c]['onhand'] != new_balance[c]['onhand']: symbol = f'{c}/{self.quot}' if symbol in self.symbols: self.time_keepers['update_open_orders'][symbol] = 0 self.time_keepers['update_my_trades'][symbol] = 0 except (KeyError): pass self.tradable_bnb = max( 0.0, new_balance['BNB']['onhand'] - self.hyperparams['bnb_buffer']) self.balance = new_balance self.time_keepers['update_balance'] = time()