예제 #1
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_)
예제 #2
0
 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]])
예제 #3
0
 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 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)
def session_summary(signals, file):
    '''
    Print summary statistics of the trading session
    '''
    gross_profit = 0
    gross_loss = 0
    win, loss, inpos = 0, 0, 0
    exp_signal = 0
    trade_time = 0
    for symbol in signals:
        for sig in signals[symbol]:
            if sig.is_expired(): exp_signal += 1
            elif sig.is_closed():
                if sig.clsPrice is not None:
                    if sig.side == 'BUY': _side = 1.0
                    elif sig.side == 'SELL': _side = -1.0
                    pnl = _side * sig.get_quantity() * (sig.clsPrice -
                                                        sig.excPrice)
                    trade_time += (sig.clsTime - sig.excTime) / (60 * 1000)
                    if pnl > 0:
                        win += 1
                        gross_profit += pnl
                    if pnl <= 0:
                        loss += 1
                        gross_loss += pnl
                else:
                    inpos += 1
    if (win + loss) > 0: timeAv = trade_time / (win + loss)
    else: timeAv = 0.0
    print_(
        '\n####################\tTrading Session Summary\t####################\n',
        file)
    print_('\n\tGross Profit: \t%1.5f' % gross_profit, file)
    print_('\tGross Loss: \t%1.5f' % gross_loss, file)
    print_('\tNet Profit: \t%1.5f' % (gross_profit + gross_loss), file)
    print_('\tAverage time in position: \t%1.2f' % timeAv, file)
    print_('\tNumber of win trades: \t%d' % win, file)
    print_('\tNumber of loss trades: \t%d' % loss, file)
    print_('\tNumber of unfinished trades: \t%d' % inpos, file)
    print_('\tNumber of expired signals: \t%d \n' % exp_signal, file)
def header_print(testnet, client, portfolio, file):
    '''
    Print general information of the trading session
    '''
    t_server, t_local = client.timestamp(), time.time() * 1000
    print_('\tTestnet: %s' % str(testnet), file)
    print_('\tServer Time at Start: %s' % timestr(t_server), file)
    print_(
        '\tLocal Time at Start: %s, \tOffset (local-server): %d ms\n' %
        (timestr(t_local), (t_local - t_server)), file)
    print_(
        '\t#LONG order : %d \t#SHORT order: %d \tOrder Size : %1.2f \n' %
        (portfolio.equityDist['BUY'], portfolio.equityDist['SELL'],
         portfolio.orderSize), file)
    try:
        bal_st = pd.DataFrame(client.balance())
        bal_st['updateTime'] = [timestr(b) for b in bal_st['updateTime']]
        print_('\nBeginning Balance Info: \n', file)
        print_(bal_st, file)
    except Exception:
        print_('\nFail to connect to client.balance: \n', file)
예제 #7
0
        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()
예제 #8
0
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