def header_print(client): ''' Print Local Time, Timezone and Account Balance Info ''' t_local = time.time() * 1000 print('\n' + barstr(text="", space_size=0)) print(barstr(text='IG Server Connecting Session Info')) print('\n\n\tDemo Server: %s' % str(demo)) print('\tLocal Time at Start: %s, \n\tServer Timezone Offset : UTC%+d' % (timestr(t_local), resp['timezoneOffset'])) print('\n\tAccount Balance info at Start:') try: accounts = client.account_details()['accounts'] for n in range(len(accounts)): info = { str(key): str(value) for key, value in iter(accounts[0].items()) } balance = { str(key): str(value) for key, value in iter(accounts[0]['balance'].items()) } print('\tAccount #{}:'.format(n)) print( "\tId : {accountId:<} \tType : {accountType:<} \tStatus : {status:<} \tCurrency : {currency:<}" .format(**info)) print( "\tBalance \tTotal : {balance:<} \tAvailable : {available:<}". format(**balance)) print('\n') except Exception: print('\tFAIL to connect to client.balance. \n')
def close_stream(self, on_close=None): ''' Call self.unsubcribe() and self.disconnect() ''' self.unsubcribe() self.disconnect() if bool(on_close): on_close() else: print('\n' + barstr(text='Close Data Streaming') + '\n') ###%%%
def open_stream(self, on_open=None): ''' Call self.connect() and self.subcribe() Create message tracker --> self._prevResp : dict ''' conn_status = self.connect() sub_status = self.subcribe() self._prevResp = {} for item in self.subscriptions['item_names']: self._prevResp[item] = {} for field in self.subscriptions['field_names']: self._prevResp[item][field] = None if conn_status == 'OK' and sub_status == 'OK': if bool(on_open): on_open() else: print('\n' + barstr(text='Start Data Streaming') + '\n') else: print('\tERROR ~ \tConnect : ', conn_status, 'Subcribe : ', sub_status)
demo = True if demo: # demo IG username = '' password = '' apikey = '' else: # IG username = '' password = '' apikey = '' """Client to connect to IG Server""" client = Client(username=username, password=password, api_key=apikey, demo=demo) resp = client.create_session(encrypted=True) header_print(client) """Multi-threading function. One can call data_stream() directly""" t1 = threading.Thread(target=data_stream) t1.setDaemon(True) t1.start() t1.join() print('\n\tLocal Time at Close: %s \n' % timestr(time.time() * 1000)) print( barstr(text='Elapsed time = {} seconds'.format( round(time.time() - start_time, 2)))) print(barstr(text="", space_size=0)) os._exit(1)
def main(args): start_time = time.time() testnet = True filename = str(int(time.time())) if testnet: # Testnet # "key": "d782f0d42dcf016c32fd56a96d01bb0e120f78802d9de378cdb9fde2b6c841e8", #"secret": "1ec873bf3137868a9e12e57e8e19cfa5a3a38cec403b741d2f7cd881c51bfd72" apikey = 'd782f0d42dcf016c32fd56a96d01bb0e120f78802d9de378cdb9fde2b6c841e8' ### INSERT your api key here ### scrkey = '1ec873bf3137868a9e12e57e8e19cfa5a3a38cec403b741d2f7cd881c51bfd72' ### INSERT your api secret here ### else: # Binance apikey = '' scrkey = '' if testnet: fileout = "report/testnet-" + filename else: fileout = "report/" + filename insIds = ['BTCUSDT', 'ETHUSDT', 'BCHUSDT', 'BNBUSDT'] # Generate Client object client = Client(apikey, scrkey, testnet=testnet) client.change_position_mode(dualSide='true') # Generate Portfolio object portfolio = Portfolio(client, tradeIns=insIds) long, short = portfolio.equity_distribution(longPct=0.25, shortPct=0.25, currency='USDT', orderPct=0.05) portfolio.position_locks() print_('\n' + barstr('', length=100, space_size=0), fileout) print_(barstr('BINANCE TRADING', length=100, space_size=5), fileout) print_(barstr('', length=100, space_size=0) + '\n', fileout) print_('\n' + barstr('Generating Models', length=100, space_size=5) + '\n', fileout) # Generate Models object models = {} for i in range(len(portfolio.tradeIns)): symbol = portfolio.tradeIns[i] client.change_leverage(symbol, 1) _data = MarketData(testnet=testnet, symbol=symbol) model = TradingModel(symbol=symbol, testnet=testnet, modelType='bollinger', marketData=_data, pdObserve=pd_ob, pdEstimate=pd_es, orderSize=portfolio.orderSize) model.build_initial_input() only_pos = 'BOTH' if symbol in portfolio.locks['BUY']: model.add_signal_lock(slock='BUY') only_pos = 'SELL ONLY' elif symbol in portfolio.locks['SELL']: model.add_signal_lock(slock='SELL') only_pos = 'BUY ONLY' models[symbol] = model print_( '\tFinish generating model for %s - positions: %s' % (symbol, only_pos), fileout) print_( '\n' + barstr('Start Data Streaming', length=100, space_size=5) + '\n', fileout) header_print(testnet, client, portfolio, fileout) print('\n\tPre-processing Time = %f' % (time.time() - start_time)) print_('\nStream updating for {} minutes...'.format(pd_ob * min_in_candle), fileout) signals = wss_run(portfolio, client, testnet, ['@kline_1m'], models, fileout) session_summary(signals, fileout) print_('\n\tLocal Time at Close: %s ' % timestr(time.time() * 1000), fileout) print_( barstr(text='Elapsed time = {} seconds'.format( round(time.time() - start_time, 2))), fileout) print_(barstr(text="", space_size=0), fileout) os._exit(1)
apikey = '' scrkey = '' insIds = ['BTCUSDT', 'ETHUSDT', 'BCHUSDT'] stream = ['@depth5@500ms'] ### PROBLEM 1 Modify stream subscription here ### BidAsk = {} AggTrades = {} SymKlns = {} client = Client(apikey, scrkey, testnet=testnet) client.change_position_mode(dualSide='true') for symbol in insIds: client.change_leverage(symbol, 1) BidAsk[symbol] = [] AggTrades[symbol] = [] SymKlns[symbol] = [] print('\n' + barstr(text='Start Data Streaming') + '\n') header_print(testnet, client) print('\nStream updating...') listen_key = client.get_listen_key() ws = websocket.WebSocketApp(f'{client.wss_way}{listen_key}', on_message=on_message, on_error=on_error, on_close=on_close) ws.on_open = on_open ws.run_forever() client.close_stream() print('\n\tLocal Time at Close: %s \n' % timestr(time.time() * 1000)) print( barstr(text='Elapsed time = {} seconds'.format( round(time.time() - start_time, 2))))
def wss_run(*args): def on_message(ws, message): ''' Control the message received from ''' mess = json.loads(message) if mess['e'] == 'kline': kln = mess['k'] if kln['x'] is True: symbol = kln['s'].upper() new_kln = { '_t': int(kln['t']), '_o': float(kln['o']), '_h': float(kln['h']), '_l': float(kln['l']), '_c': float(kln['c']), '_v': float(kln['q']) } SymKlns[symbol].append(new_kln) print_( '%d. %s\t' % (len(SymKlns[symbol]), symbol) + timestr(new_kln['_t']) + '\t' + \ ''.join(['{:>3}:{:<10}'.format(k, v) for k,v in iter(new_kln.items()) if not k=='_t']), fileout) def on_error(ws, error): ''' Do something when websocket has an error ''' pass def on_close(ws): ''' Do something when websocket closes ''' pass def on_open(ws, *args): ''' Main function to run multi-threading ''' def data_stream(*args): ''' First thread to send subscription to the exchange ''' params = [str.lower(ins) + str(s) for ins in insIds for s in stream] print_(params, fileout) ws.send(json.dumps({"method": "SUBSCRIBE", "params": params, "id": 1 })) t1_idx = 0 while True: if len(SymKlns[insIds[0]]) % 5 == 0 and len(SymKlns[insIds[0]]) > t1_idx and len(SymKlns[insIds[0]]) < models[insIds[0]].pdObserve: client.keepalive_stream() t1_idx = len(SymKlns[insIds[0]]) def strategy(*args): ''' Second thread to generate signals upon the message from the exchange ''' t2_idx = {} for symbol in insIds: t2_idx[symbol] = 0 while len(SymKlns[insIds[0]]) < models[insIds[0]].pdObserve: for symbol in insIds: sym_ = SymKlns[symbol].copy() if len(sym_) > t2_idx[symbol]: if models[symbol].modelType == 'bollinger': data_ob = pd.DataFrame(sym_) model_sig = models[symbol].get_last_signal(dataObserve=data_ob) else: model_sig = None if model_sig is not None: ready = True if ready: side, positionSide, startTime = model_sig['side'], model_sig['positionSide'], model_sig['_t']+60*1000 expTime, price = startTime + 5*60*1000, round(model_sig['_p'], PRICEPRE[symbol]) # stopLoss = model_sig['atr'] takeProfit = model_sig['atr'] new_sig = Signal(symbol=symbol, side=side, size=models[symbol].orderSize, orderType='LIMIT', positionSide=positionSide, price=price, startTime=startTime, expTime=expTime, \ stopLoss=stopLoss, takeProfit=takeProfit, timeLimit=models[symbol].pdEstimate*60, timeInForce='GTC') if in_possition_(Signals[symbol], side='BOTH') or position_count(insIds, Signals, side=side) >= portfolio.equityDist[side]: new_sig.set_expired() else: for sig in Signals[symbol]: if sig.is_waiting(): sig.set_expired() print_('\n\tSet WAITING signal EXPIRED: \n\t' + str(sig), fileout) Signals[symbol].append(new_sig) print_('\n\tFOUND ' + str(new_sig), fileout) t2_idx[symbol] = len(sym_) def book_manager(*args): ''' Third thread to excecute/cancel/track the signals generated in strategy() ''' while len(SymKlns[insIds[0]]) < models[insIds[0]].pdObserve: time.sleep(1) for symbol in insIds: in_position = False last_signal = None for sig in Signals[symbol]: model = models[symbol] sv_time = client.timestamp() if sig.is_waiting(): ### Check for EXPIRED order here ### if sv_time > sig.expTime: sig.set_expired() print_('\n\tSet WAITING signal EXPIRED: \n\t' + str(sig), fileout) else: last_signal = sig elif sig.is_ordered(): ### Set ACTIVE order here ### in_position = True order_update = client.query_order(symbol, sig.orderId) if order_update['status'] == 'FILLED': sig.set_active(excTime=order_update['updateTime'], excPrice=order_update['avgPrice'], excQty=order_update['executedQty']) sig.path_update(lastTime=sig.excTime, lastPrice=sig.excPrice) print_('\n\tSet BOOKED order ACTIVE: \n\t' + str(sig) + '\n\t' + orderstr(order_update), fileout) ### PROBLEM 3 Insert your code to handle EXPIRED and PARTIALLY_FILLED order here ### elif sig.is_active(): ### Control ACTIVE position here ### in_position = True recent_trades = model.marketData.recent_trades(limit=5) for trade in recent_trades: if int(trade['time']) > sig.pricePath[-1]['timestamp']: sig.path_update({'timestamp': trade['time'], 'price': trade['price']}) exit_sign, pos = sig.exit_triggers() if exit_sign: print_('\n\tFound ' + str(exit_sign) + '{}\n'.format(round(pos,4)), fileout) cnt_order = sig.counter_order() order = client.new_order(symbol=symbol, side=cnt_order['side'], orderType='MARKET', quantity=cnt_order['amt'], positionSide=sig.positionSide) #, timeInForce=cnt_order['TIF'], price=lim) sig.set_cnt_ordered(cntorderId=order['orderId'], cntType='MARKET', cntTime=order['updateTime']) print_('\tPlaced COUNTER order: \n\t' + str(sig) + '\n\t' + orderstr(order), fileout) elif sig.is_cnt_ordered(): ### Set CLOSED position here ### in_position = True order_update = client.query_order(symbol, sig.cntorderId) if order_update['status'] == 'FILLED': sig.set_closed(clsTime=order_update['updateTime'], clsPrice=order_update['avgPrice']) print_('\n\tClosed order: \n\t' + str(sig) + '\n\t' + orderstr(order_update), fileout) if (not in_position) and (last_signal is not None): ### Check for ENTRY and place NEW order here ### sig = last_signal if sig.orderType == 'MARKET': order = client.new_order(symbol=symbol, side=sig.side, orderType=sig.orderType, quantity=sig.get_quantity(), positionSide=sig.positionSide) sig.set_ordered(orderId=order['orderId'], orderTime=order['updateTime'], limitPrice=None) print_('\n\tPlaced NEW order: \n\t' + str(sig) + '\n\t' + orderstr(order), fileout) elif sig.orderType=='LIMIT': bids, asks, lim = get_possible_price(model.marketData, sig.side) order = client.new_order(symbol=symbol, side=sig.side, orderType=sig.orderType, quantity=sig.get_quantity(), positionSide=sig.positionSide, timeInForce='GTC', price=lim) sig.set_ordered(orderId=order['orderId'], orderTime=order['updateTime'], limitPrice=lim) print_('\n\tPlaced NEW order: \n\t' + str(sig) + '\n\t' + orderstr(order), fileout) ws.close() t1 = threading.Thread(target=data_stream) t2 = threading.Thread(target=strategy) t3 = threading.Thread(target=book_manager) t1.start() t2.start() t3.start() def position_count(insIds, signal_list, side='BOTH'): ''' Returns number of open positions ''' count = 0 for s in insIds: for sig in signal_list[s]: if sig.side==side or side=='BOTH': if sig.is_ordered() or sig.is_active() or sig.is_cnt_ordered(): count += 1 return count def in_possition_(signal_list, side='BOTH'): ''' Check if there is any open positions ''' in_pos = False for sig in signal_list: if sig.side==side or side=='BOTH': if sig.is_ordered() or sig.is_active() or sig.is_cnt_ordered(): in_pos = True break return in_pos def get_possible_price(mk_data, side): ''' Return a safe limit price available on the market ''' mk_depth = mk_data.order_book(limit=5) bids = list(float(x[0]) for x in mk_depth['bids']) asks = list(float(x[0]) for x in mk_depth['asks']) lim = (side=='BUY')*(bids[0]+bids[1])/2 + (side=='SELL')*(asks[0]+asks[1])/2 lim = round(lim, PRICEPRE[mk_data.symbol.upper()]) return bids, asks, lim start_time = time.time() portfolio, client, testnet, stream, models, fileout = args insIds = portfolio.tradeIns SymKlns = {} Signals = {} for symbol in insIds: SymKlns[symbol] = [] Signals[symbol] = [] listen_key = client.get_listen_key() ws = websocket.WebSocketApp(f'{client.wss_way}{listen_key}', on_message=on_message, on_error=on_error, on_close=on_close) ws.on_open = on_open ws.run_forever() client.close_stream() print_('\n' + barstr('Close Opening Positions', length=100, space_size=5) + '\n', fileout) ### PROBLEM 2 Insert your code to close all positions here ### return Signals