def CryptoArbBinanceOrderBookProcess(stopProcessesEvent):
    try:
        # Init Kafka producer
        kafka_producer = KafkaProducer(
            bootstrap_servers=config['kafka']['address'],
            value_serializer=lambda v: json.dumps(v).encode('utf-8'))

        credentials = getCredentials()
        client = Client(api_key=credentials['api_key'],
                        api_secret=credentials['api_secret'])

        # Init CloudWatch metrics
        metrics = CWMetrics(config['exchange']['name'])

        bm = BinanceSocketManager(client)
        bm.start_multiplex_socket(
            pairList, partial(process_message, kafka_producer, metrics))
        bm.start()
        logger.info('BinanceSocketManager started')
        stopProcessesEvent.wait()
        bm.close()
        logger.info('BinanceSocketManager closed')
    except Exception as error:
        logger.error("Error CryptoArbBinanceOrderBookProcess: " +
                     type(error).__name__ + " " + str(error.args))
        metrics.putError()
Beispiel #2
0
class BinanceKlines(Thread):

    def __init__(self, symbol, interval, subscribers):
        Thread.__init__(self)
        self.symbol = symbol
        self.interval = interval
        self.subscribers = subscribers
        self.rest_client = Client(None, None)
        self.ws_client = BinanceSocketManager(self.rest_client)
        self.restart_count = 1

    def run(self):
        conn_key = self.ws_client.start_kline_socket(self.symbol, self.on_message, self.interval)
        self.ws_client.start()
        self.ws_client.join()

    def on_message(self, msg):
        #to recieve notifications, subscibers should have an array called klines and an event called kline_event
        #kline = Kline(msg)
        kline = KLineEvent.object_from_dictionary(msg)
        for subscriber in self.subscribers:
            subscriber.klines.append(kline)
            subscriber.kline_event.set()

    def __del__(self):
        self.ws_client = None
        logger.info("Klines stopping")

    def close(self):
        self.keep_running = False
        self.ws_client.close()
        reactor.stop()
Beispiel #3
0
class DataCatcher:
    active = 0

    def callback(self, query):
        global run
        if not self.norm['norm']:
            self.finish()
            return

        price = (float(query['data']['asks'][0][0]) +
                 float(query['data']['bids'][0][0])) / 2
        record = Record.objects.create(price=price,
                                       run_id=run,
                                       asks=str(query['data']['asks']),
                                       bids=str(query['data']['bids']))

    def __init__(self, run_id, timeout=600):
        self.manager = multiprocessing.Manager()
        self.norm = self.manager.dict()
        self.norm['norm'] = True
        self.streams = ['btcusdt@depth5']
        self.timeout = timeout
        self.manager = multiprocessing.Manager()
        self.client = init_client()

        global run
        run = run_id

        self.main_socket = BinanceSocketManager(self.client)
        self.connection_key = self.main_socket.start_multiplex_socket(
            self.streams, self.callback)
        self.socket_process = multiprocessing.Process(target=self.process_func)
        self.timeout_timer = None

    def finish(self):
        self.main_socket.stop_socket(self.connection_key)
        self.main_socket.close()
        reactor.stop()
        print('Datacatcher process ended')

    def stop(self):
        self.norm['norm'] = False

    def process_func(self):
        print('Datacatcher process started')
        self.timeout_timer = threading.Timer(self.timeout, self.finish)
        self.timeout_timer.start()
        self.main_socket.run()

    def start(self):
        DataCatcher.active = 1
        self.socket_process.start()
        return timezone.now(), self.timeout
Beispiel #4
0
class BinanceStream:
    """
    This class defines a binance data stream that's setup using BinanceSocketManager
    """

    bm = None  # type: BinanceSocketManager
    symbol = None  # type: str
    key = None  # type: str
    fresh = True  # type: bool

    def __init__(self, client, symbol):
        # type: (Client, SupportedSymbols) -> None
        self.bm = BinanceSocketManager(client)
        self.symbol = symbol.value

    def start_kline(self, callback=None, interval=KLINE_INTERVAL_1HOUR):
        # type: (Callable, str) -> None
        if not callback:
            callback = self._store_data
        self.key = self.bm.start_kline_socket(self.symbol,
                                              callback,
                                              interval=interval)

    def kline_with_firebase(self, interval=Intervals.H1):
        # type: (Intervals) -> None
        self.key = self.bm.start_kline_socket(self.symbol,
                                              self._update_firebase,
                                              interval=interval)

    def _update_firebase(self):
        # type: () -> None
        raise NotImplementedError

    def refresh(self, client):
        # type: (Client) -> None
        self.bm = BinanceSocketManager(client)
        self.fresh = True

    def start(self):
        # type: () -> None
        if self.fresh:
            self.fresh = False
            self.bm.start()
        else:
            raise StreamNotFreshError

    def close(self):
        # type: () -> None
        self.bm.close()

    def _store_data(self, data):
        # type: (Dict[str, Any]) -> None
        self.data = data
Beispiel #5
0
class BINANCE_ORDER_BOOK(threading.Thread):
    def __init__(self, threadId, name, order_book, api_details):
        threading.Thread.__init__(self)
        self.threadId = threadId
        self.name = name
        self.order_book = order_book
        self.process_functions = {
            pair: functools.partial(self.p_data, symbol=pair)
            for pair, _ in order_book.items()
        }
        self.api_details = api_details
        self.reset_time = 108000
        self.client = Client(self.api_details['API_KEY'],
                             self.api_details['API_SECRET'])
        self.bm = BinanceSocketManager(self.client)
        self.conn_keys = None

    def run(self):

        while True:
            self.bm_init()
            sleep(self.reset_time)
            self.bm_reset()

    def convert_order_data(self, res):
        conv_data = []
        for r in res:
            conv_data.append([Decimal(r[0]), Decimal(r[1])])
        return conv_data

    def p_data(self, msg, symbol=None):
        self.order_book[symbol]['sell'] = self.convert_order_data(msg['asks'])
        self.order_book[symbol]['buy'] = self.convert_order_data(msg['bids'])
        self.order_book[symbol]['lastUpdate'] = time()

    def bm_init(self):
        print("***START***")
        self.client = Client(self.api_details['API_KEY'],
                             self.api_details['API_SECRET'])
        self.bm = BinanceSocketManager(self.client)
        self.conn_keys = [
            self.bm.start_depth_socket(
                pair, f, depth=BinanceSocketManager.WEBSOCKET_DEPTH_20)
            for pair, f in self.process_functions.items()
        ]
        self.bm.start()

    def bm_reset(self):
        print("***STOP***")
        self.bm.close()
        self.bm = None
        self.conn_keys = None
        self.client = None
Beispiel #6
0
class BinanceSocket(ClientSetup):
    def __init__(self, symbol):
        self.symbol = symbol
        self.bsm = None
        self.conn_key = None
        self.new_data_available = True
        self.ticker = None
        self.condition = None
        self.trans_range = None

    def connect(self):
        self.bsm = BinanceSocketManager(self.client)
        self.conn_key = self.bsm.start_kline_socket(self.symbol,
                                                    self.process_msg)
        self.bsm.start()

    def disconnect(self):
        self.bsm.stop_socket(self.conn_key)
        self.bsm.close()
        self.reactor.stop()

    def process_msg(self, msg):
        self.ticker = KlineTicker(msg)
        print(self.ticker.__str__())
        self.new_data_available = True
        self.process_order()

    def process_order(self):
        close = float(self.ticker.close)
        if self.condition == 'buy':
            if close >= self.trans_range:
                print(datetime.now(), 'BUY', self.condition, self.trans_range)
        elif self.condition == 'sell':
            if close <= self.trans_range:
                print(datetime.now(), 'SELL', self.condition, self.trans_range)
        else:
            print(datetime.now(), 'NO ORDERS', self.condition,
                  self.trans_range)

    def set_new_data_available(self, bol_value):
        self.new_data_available = bol_value

    def get_new_data_available(self):
        return self.new_data_available

    def set_market_conditions(self, condition):
        self.condition = condition

    def set_trans_range(self, trans_range):
        self.trans_range = trans_range
def exercise08():
    '''
        Using the below linked documentation from Binance, use websockets to pull and stream back ANY streaming market data
        (like limit order book) using websockets. The stream should be displayed to console. Feel free to import binance python libs.

        Relevant docs
        https://python-binance.readthedocs.io/en/latest/overview.html
        https://python-binance.readthedocs.io/en/latest/websockets.html
        https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md
    '''
    
    #states secret and public key
    secret_key = 'AhYt4B9YJsH6cVCqUhmxgOpHvvmoXYLaITkYIJZKRvo4HNK03Wu1JxOKfBi3N0a5'
    public_key = 'B9zEhWB5ODpEYcuoBdur2XupW0cqyqsDi1N1S0KWKvJU4NEeVfD9kxgKRtwoMnlg'
    
    #initializes client
    client = Client(public_key,secret_key)
    
    #creates message processor function to handle payload from websocket
    def process_message(msg):
        print(msg)
    
    
    #initializes socket manager
    bm = BinanceSocketManager(client)
    
    #creates connection to fetch Etherium trade data
    conn_key = bm.start_trade_socket('ETHBTC',process_message)
    
    
    #starts socket
    bm.start()
    
    #wait 10 seconds to let data come in
    time.sleep(10)
    
    #stops socket connection
    bm.stop_socket(conn_key)
    
    #closes socket manager
    bm.close()
class BinanceStream(object):
    def __init__(self):
        self.client = Client(api_key=config.api_key,
                             api_secret=config.api_secret)
        self.manager = BinanceSocketManager(self.client, user_timeout=30)
        self.user_socket = self.manager.start_user_socket(self.process_order)
        self.ticker_socket = self.manager.start_symbol_book_ticker_socket(
            config.symbol, self.process_tick)
        self.r = redis.Redis(host='localhost', port=6379, db=0)
        self.buy_orders = []
        self.sell_orders = []

    def process_order(self, msg):
        if msg['e'] == 'error':
            # close and restart the socket
            print("ERROR:", msg)
        else:
            print("message type: {}".format(msg['e']))

            if msg['e'] == 'executionReport' and msg['s'] == config.symbol:
                self.r.set(msg['c'], json.dumps(msg))
                print("Saved: ", msg)

    def process_tick(self, msg):
        if msg['s'] == config.symbol:
            name = 'bookTicker_' + msg['s']
            json_msg = json.dumps(msg)
            self.r.set(name, json_msg)
            self.r.xadd("stream_" + name, msg, maxlen=1000)
            print("Saved: ", json_msg)

    def start(self):
        self.manager.start()

    def stop(self):
        self.manager.close()
Beispiel #9
0
 def stop_trading(self):
     bm = BinanceSocketManager(self.client)
     bm.stop_socket(self.conn_key)
     bm.close()
     reactor.stop()
Beispiel #10
0
class BinanceExchangeService(ExchangeClientBase):
    """
    Binance Exchange Services
    (Depends on 3rd party binance websockets library, python-binanc)e
    https://github.com/sammchardy/python-binance

    binance_response_mapping:
        ("s", "symbol"),
    ("E", "timestamp"),
        ("h", "high"),
        ("l", "low"),
        ("b", "bid"),
        ("B", "bid_volume"),
        ("a", "ask"),
        ("A", "ask_volume"),
        ("w", "w_av_price"),
        ("o", "open"),
        ("c", "close"),
        ("x", "previous_close"),
        ("p", "change"),
        ("P", "percentage"),
        ("v", "base_volume"),
        ("q", "quote_volume")
    """

    _binance_item_getter, _binance_quote_keys = zip(
        *[("E", "timestamp"), ("h", "high"), ("l", "low"), (
            "b", "bid"), ("B", "bid_volume"), ("a", "ask"), (
                "A", "ask_volume"), ("v", "base_volume"), ("q",
                                                           "target_volume"),
          ("o",
           "open"), ("c",
                     "close"), ("x",
                                "previous_close"), ("p",
                                                    "change"), ("s",
                                                                "ticker")])

    _binance_item_getter = itemgetter(*_binance_item_getter)

    def __init__(self, **kwargs):
        ExchangeClientBase.__init__(self, "binance", **kwargs)

        symbol_infos = requests.get(
            "https://api.binance.com/api/v1/exchangeInfo").json()["symbols"]

        self.binance_symbol_mapping = {
            info["symbol"]: (info["baseAsset"], info["quoteAsset"])
            for info in symbol_infos
        }

        self.binance_socket_manager = BinanceSocketManager(
            Client(KeyChain.get_key("binance"),
                   KeyChain.get_secret("binance")))

    def process_ticker_response(self, tickers_response):
        self.logger.debug(
            "Received ticker response {}".format(tickers_response),
            truncate=480)

        quotes = []
        append_to_quotes = quotes.append
        for ticker_response in tickers_response:
            quote = dict(
                zip(self._binance_quote_keys,
                    self._binance_item_getter(ticker_response)))
            quote["base"], quote["target"] = self.get_base_target(
                quote["ticker"])
            append_to_quotes(quote)

        self.save_quotes(quotes)
        self.logger.info("Saved {} tickers".format(len(quotes)))

    def get_base_target(self, ticker):
        return self.binance_symbol_mapping[ticker]

    def start(self):
        super().start()
        self.binance_socket_manager.start_ticker_socket(
            self.process_ticker_response)
        self.binance_socket_manager.start()

        while not self.EXCHANGE_STOPPED:
            pass

    def stop(self):
        try:
            reactor.stop()  # pylint: disable=E1101
            self.binance_socket_manager.close()
        except ReactorNotRunning:
            pass

        super().stop()
Beispiel #11
0
class BinanceWebSocket(ExchangeWebSocket):
    def __init__(self, pairs_n_streams):
        super().__init__('Binance', pairs_n_streams)

        self.client = Client('', '')
        self.bm = BinanceSocketManager(self.client)
        self.streams = []
        self.possible_streams = ['trade', 'depth20', 'arr']

    def init_streams(self):
        for pair, stream in self.pairs_n_streams.items():
            if self.has_stream(stream):

                for sub_stream in stream.split(','):
                    self.streams.append('{0}@{1}'.format(pair, sub_stream))
            else:
                logger.warning('%s. There is no %s stream', pair, stream)

        logger.info('%s streams are initialized: %s', len(self.streams),
                    self.streams)

    def close_socket(self):
        logger.debug('Closing socket')
        self.exited = True
        self.bm.close()

    def start_multiple_websocket(self, init_streams=True):
        super().start_multiple_websocket(init_streams=init_streams)

        if len(self.streams) > 0:
            self.bm.start_multiplex_socket(self.streams,
                                           self.multiple_data_handle)

            self.bm.start()

            logger.info('{} socket is started:\n{}\n{}'.format(
                self.exchange, self.node, '\n'.join(self.streams)))

    def save_depth_to_file(self, msg):
        stream = msg.get('stream', '')
        stream_parts = stream.split('@')
        pair = stream_parts[0]
        data = msg.get('data')

        last_update_id = data.get('lastUpdateId', 0)

        if last_update_id >= self.depth_last_update.get(pair, 0):
            self.depth_last_update[pair] = last_update_id
            bids = data.get('bids', [])
            asks = data.get('asks', [])

            if len(bids) == 0 and len(asks) == 0:
                bids = data.get('b', [])
                asks = data.get('a', [])

            if len(bids) > 0 or len(asks) > 0:
                time_now = int(time.time())
                data_to_append = ["{},{},{},{}\n".format(time_now, 'B', bid[0], bid[1]) for bid in bids] + \
                                 ["{},{},{},{}\n".format(time_now, 'A', ask[0], ask[1]) for ask in asks]

                data_to_append = "".join(data_to_append)

                self.file_manager.save_data_to_file(
                    self.exchange,
                    stream_parts[1],
                    pair,
                    data_to_append,
                    columns='time,side,price,qty')
            else:
                if len(bids) == 0:
                    logger.debug('%s. Bids are empty', pair)

                if len(asks) == 0:
                    logger.debug('%s. Asks are empty', pair)
        else:
            logger.debug('%s. last_update_id is whether none or not new', pair)

    def save_trade_to_file(self, msg):
        stream = msg.get('stream', '')
        stream_parts = stream.split('@')
        pair = stream_parts[0]
        data = msg.get('data')

        del data['e']
        del data['s']

        data_to_insert = "{},{},{},{},{},{},{},{},{}\n".format(
            data['m'], data['E'], data['M'], data['q'], data['T'], data['t'],
            data['b'], data['p'], data['a'])

        self.file_manager.save_trades_to_file(self.exchange,
                                              stream_parts[1],
                                              pair,
                                              data_to_insert,
                                              columns="m,E,M,q,T,t,b,p,a")

    def save_min_ticker_to_file(self, msg):
        stream = msg.get('stream', '')
        stream_parts = stream.split('@')
        pair = stream_parts[0]
        data = msg.get('data')

        res = ''
        for cur in data:
            res += "{0},{1},{2},{3},{4}," \
                   "{5},{6},{7},{8}\n".format(cur['e'],
                                              cur['E'],
                                              cur['s'],
                                              cur['c'],
                                              cur['o'],
                                              cur['h'],
                                              cur['l'],
                                              cur['v'],
                                              cur['q'])

        self.file_manager.save_trades_to_file(self.exchange,
                                              stream_parts[1],
                                              pair,
                                              res,
                                              columns='e,E,s,c,o,h,l,v,q')

    def multiple_data_handle(self, msg):
        stream = msg.get('stream', '')

        try:
            if 'depth' in stream:
                self.save_depth_to_file(msg)
            elif 'trade' in stream:
                self.save_trade_to_file(msg)
            elif '!miniTicker' in stream:
                self.save_min_ticker_to_file(msg)
            else:
                logger.debug('%s data handler not implemented', stream)

            self.last_msg_time = int(time.time())
        except Exception as e:
            logger.error(str(e))
Beispiel #12
0
    def socket_manager(self):
        with sqlite3.connect('TAdb.db') as conn:

            db = conn.cursor()
            bot = telebot.TeleBot(self.token)

            def telegram_sender():
                percent_difference_for_pep8 = str(
                    round(((100 * (self.previous_price - self.current_price)) /
                           self.previous_price), 3)) + '%'
                bot.send_message(
                    self.room, 'Current Bitcoin Price is : ' +
                    str(self.current_price) + '$'
                    '\n' + 'Previous Price is : ' + str(self.previous_price) +
                    '$'
                    '\n' + 'The difference is : ' +
                    percent_difference_for_pep8)

            # Proccesing messages from socket binance

            def process_message(msg):
                if msg['e'] == 'error':
                    bm.close()
                    bm.start()
                else:
                    self.current_price = float(msg['p'])

            # Counting difference in percent between previous and current price

            def counting_difference():
                if float(((100 * (self.previous_price - self.current_price)) /
                          self.previous_price)) > self.percent:
                    return True
                else:
                    return False

            # Just database SQL

            def adding_to_database(event):
                self.dict_for_database['time'] = int(time.time())
                self.dict_for_database['price'] = str(self.current_price)
                self.dict_for_database['event'] = str(
                    str(event) + '{}%'.format(self.percent))
                db.execute(
                    'INSERT INTO price(time, price) VALUES ("{database[time]}", "{database[price]}")'
                    .format(database=self.dict_for_database))
                db.execute(
                    'INSERT INTO event(time, event) VALUES ("{database[time]}", "{database[event]}")'
                    .format(database=self.dict_for_database))
                conn.commit()

            # creating variables for current and previous prices, then in infinity loop adding events to database and if
            # necessary send to the Telegram

            def main_res():
                while True:
                    if self.current_price is not None:
                        self.previous_price = self.current_price
                        break
                    else:
                        time.sleep(0.1)
                while True:
                    if counting_difference():
                        adding_to_database('Price changed for more than ')
                        telegram_sender()
                        self.previous_price = self.current_price
                        time.sleep(self.timing)
                    elif not counting_difference():
                        adding_to_database('Price changed less than ')
                        self.previous_price = self.current_price
                        time.sleep(self.timing)

            # Just creating sockets
            bm = BinanceSocketManager([self.login, self.password])
            bm.start_trade_socket('BTCUSDT', process_message)
            bm.start()
            main_res()
            bm.close()
Beispiel #13
0
class BinanceTradeEngine:
    def __init__(self, market='BTCUSDT'):
        self.api_key = os.getenv("binance_api_key")
        self.api_secret = os.getenv("binance_api_secret")
        self.client = Client(self.api_key, self.api_secret)
        logger.info("Starting Binance Trading Engine.")
        self.market = market
        self.__process_initial_book_state()
        logger.info("Book state initialized.")
        self.trader = TestTrader()
        #self.trader = Playbook.OGTrader_BTC()
        self.event_buffer = queue.Queue()
        self.event_thread = threading.Thread(target=self.event_loop)

        self.socketManager = BinanceSocketManager(self.client, user_timeout=60)
        self.book_diff = self.socketManager.start_depth_socket(
            market, self.on_depth_message)
        self.trade_stream = self.socketManager.start_trade_socket(
            market, self.on_trade_message)
        self.PRINT_BOOK = True
        self.grapher = Grapher()
        self.graphing = False
        self.tradingOn = True
        self.alive = True

    def start(self):
        self.socketManager.start()
        logger.info("Socket Manager started.")
        self.event_thread.start()
        logger.info("Event thread started.")

    def stop(self):
        self.socketManager.close()
        logger.info("Socket Manager closed.")
        self.alive = False
        self.event_thread.join()
        logger.info("Event thread killed.")

    def __process_initial_book_state(self):
        self.market_state = MarketState(self.market)

        book = self.client.get_order_book(symbol=self.market)
        # print(book, type(book))
        self.order_book = self.market_state.order_book

        for bid in book['bids']:
            self.order_book.add_modify_delete(Order(float(bid[0]),
                                                    float(bid[1])),
                                              side='b')
        for ask in book['asks']:
            self.order_book.add_modify_delete(Order(float(ask[0]),
                                                    float(ask[1])),
                                              side='a')
        print(self.order_book)

    def event_loop(self):
        global done
        while self.alive:
            if not self.event_buffer.empty():
                (event_type, msg) = self.event_buffer.get()
                if event_type == EventType.DEPTH:
                    self.depthUpdateHandler(msg)
                elif event_type == EventType.TRADE:
                    self.tradeUpdateHandler(msg)

    def on_depth_message(self, msg):
        self.event_buffer.put((EventType.DEPTH, msg))

    def on_trade_message(self, msg):
        self.event_buffer.put((EventType.TRADE, msg))

    def depthUpdateHandler(self, msg):
        try:
            self.order_book.binance_incremental_book_update_handler(msg)
            self.market_state.binance_incremental_depth_update_handler(msg)

            print(self.trader)
            if self.PRINT_BOOK:
                print(self.market_state)
            if self.tradingOn:
                self.trader.on_depth_update(self.market_state)
        except Exception as e:
            print("Exception caught in depth update")
            print(e)
            print(e.args)
            print("LAST TRADE: " + str(self.market_state.last_trade))
            print()
            print(self.market_state)
            logger.critical("Exception caught in depth update")
            logger.critical(msg=e)
            logger.critical(msg="LAST TRADE: " +
                            str(self.market_state.last_trade))
            logger.critical(msg=self.market_state)
            self.socketManager.close()
            traceback.print_exc()

    done = False

    def tradeUpdateHandler(self, msg):
        try:
            self.market_state.binance_incremental_trade_update_handler(msg)
            if self.graphing:
                self.grapher.update_graph(float(msg['p']), msg['m'])
            if self.tradingOn:
                self.trader.on_trade_update(self.market_state)

            #self.done = True
            print(self.trader)
            if self.PRINT_BOOK:
                print(self.market_state)
        except Exception as e:
            print("Exception caught in trade update")
            print(e)
            print(e.args)
            print("LAST TRADE: " + str(self.market_state.last_trade))
            print()
            print(self.market_state)
            logger.critical("Exception caught in trade update")
            logger.critical(msg=e)
            logger.critical(msg="LAST TRADE: " +
                            str(self.market_state.last_trade))
            logger.critical(msg=self.market_state)
            self.socketManager.close()
            traceback.print_exc()
                #logger.info('Updating analysis database.')
                logger.info('Creating new analysis document.')

                #update_result = db[collections['analysis']].update_one({'_id': user_market}, {'$set': analysis_document}, upsert=True)
                #logger.debug('update_result.matched_count: ' + str(update_result.matched_count))
                #logger.debug('update_result.modified_count: ' + str(update_result.modified_count))
                inserted_id = db[collections['analysis']].insert_one(
                    analysis_document).inserted_id
                logger.debug('inserted_id: ' + str(inserted_id))

            else:
                logger.error('Error while analyzing trade data.')

    except Exception as e:
        logger.exception(e)

    except KeyboardInterrupt:
        logger.info('Exit signal received.')

    finally:
        if reactor.running:
            logger.info('Closing Binance socket manager.')
            binance_ws.close()

            logger.info('Stopping reactor.')
            reactor.stop()
        else:
            logger.info('No websocket connected or reactor running.')

        logger.info('Exiting.')
Beispiel #15
0
class LiveTrading:
    def __init__(self, client:object, strategy:object, klines:dict={}):

        self.client:object = client
        self.strategy = strategy
        self.trades = []
        self.verbose = False
        self.debug = False

        self.precision:dict = {}
        self.klines:dict = klines

        self.message_no:int = 0

        for coin in self.strategy.tradeCoins:
            if self.verbose: print(f"Getting precision for {coin}")
            self.precision[coin] = self.get_precision(coin)
            self.klines[coin] = []

        self.active_order:bool = False
        self.open_trade:bool = False

        self.balance:float = self.get_balance(self.strategy.baseCoin)
        self.starting_balance:float = self.balance

        print(f"\nStarting trading with the following:")
        print(f"Trade coins: {self.strategy.tradeCoins}")
        print(f"Starting {self.strategy.baseCoin}: {self.balance}")
        print(f"Indicator: {self.strategy.indicator}")
        print(f"Strategy: {self.strategy.strategy}")

        self.open_kline_sockets(self.strategy.tradeCoins)

    def get_balance(self, coin):
        return float(self.client.get_asset_balance(asset=coin)["free"])

    def open_kline_sockets(self, coins:list):
        print(f"\nOpening kline socket for {coins}")
        self.bm = BinanceSocketManager(self.client)

        sockets = []

        for coin in coins:
            sockets.append(f"{coin.lower()}{self.strategy.baseCoin.lower()}@kline_{self.strategy.interval}")

        self.kline_socket = self.bm.start_multiplex_socket(sockets, self.process_message)
        self.bm.start()
    
    @staticmethod
    def print_with_timestamp(message):
        print(f"\n{time.strftime('%H:%M:%S', time.localtime())} | {message}")

    def process_message(self, msg):
        msg = msg["data"]
        if self.verbose: self.print_with_timestamp(f"Recieved kline for {msg['s']}")
        if self.debug: print(msg)

        if (msg['e'] == "kline"): self.process_kline(msg['k'])
        elif msg['e'] == 'error':
            print("Socket error... restarting socket")
            self.bm.close()
            self.open_kline_sockets(self.strategy.tradeCoins)

    def process_kline(self, kline):
        if kline['x'] == True:
            self.message_no += 1
            trade_coin = f"{kline['s'][:len(kline['s']) - len(self.strategy.baseCoin)]}"
            self.klines[trade_coin].append(self.kline_to_ohlcv(kline, self.verbose, self.debug))

            if (self.verbose): 
                self.print_with_timestamp(f"Obtained closed kline and converted to ohlcv. Count for {kline['s']}: {len(self.klines[trade_coin])}")
            
            if (self.debug): print(self.klines)

            if self.message_no == len(self.strategy.tradeCoins):
                self.message_no = 0
                self.print_with_timestamp("Checking for any actions...")
                self.strategy.refresh(self.klines)
                
                # If the strategy is returning more trades than we have, there must be new ones
                if len(self.strategy.trades) > len(self.trades):
                    new_trades = self.strategy.trades[len(self.trades):]
                    print(f"New trades: {new_trades}")
                    self.process_trades(new_trades)
                    self.trades += new_trades

    def process_trades(self, trades):
        for trade in trades:
            if not trade.completed:
                if trade.action == "BUY": self.place_buy(trade.trade_coin, trade.price)
                elif trade.action == "SELL": self.place_sell(trade.trade_coin, trade.price)
                trade.completed = True
        

    def place_buy(self, coin, price):
        self.balance = self.get_balance(self.strategy.baseCoin)
        quantity = self.round_down((self.balance * 0.99) / float(price), self.precision[coin])
        print(f"Buying {quantity} {coin} at {price}")
        order = self.client.create_order(
            symbol=f"{coin}{self.strategy.baseCoin}",
            side=SIDE_BUY,
            type=ORDER_TYPE_LIMIT,
            timeInForce=TIME_IN_FORCE_GTC,
            quantity=quantity,
            price=price
        )
        print(order)

    def place_sell(self, coin, price):
        balance = self.get_balance(coin)
        quantity = self.round_down(balance, self.precision[coin])
        if (quantity != 0):
            print(f"\nSelling {quantity} {coin} at {price}")
            order = self.client.create_order(
                symbol=f"{coin}{self.strategy.baseCoin}",
                side=SIDE_SELL,
                type=ORDER_TYPE_LIMIT,
                timeInForce=TIME_IN_FORCE_GTC,
                quantity=quantity,
                price=price
            )
            print(order)
        else:
            print(f"Something went wrong. Bot is trying to sell 0 {coin}")

    def get_precision(self, coin):
        for filt in self.client.get_symbol_info(f"{coin}{self.strategy.baseCoin}")['filters']:
            if filt['filterType'] == 'LOT_SIZE':
                return int(round(-math.log(float(filt['stepSize']), 10), 0))

    @staticmethod
    def round_down(n, decimals=0):
        return math.floor(n * (10 ** decimals)) / 10 ** decimals

    @staticmethod
    def kline_to_ohlcv(kline, verbose, debug):

        # This converts the kline from the socket stream to the ohclv data
        # so it is the same as the backtesting historical data returned from API
        # which is what a Strategy accepts
        ohlcv = [
            # Open time
            kline['t'],
            # Open
            kline['o'],
            # High
            kline['h'],
            # Low
            kline['l'],
            # Close
            kline['c'],
            # Volume
            kline['v'],
            # Close time
            kline['T'],
            # Quote asset volume
            kline['q'],
            # Number of trades
            kline['n'],
            # Taker buy base asset volume
            kline['V'],
            # Taker buy quote asset volume
            kline['Q'],
            # Ignore
            kline['B']
        ]

        if verbose: print("\nUnpacked closed kline to ohlcv")
        if debug: print(ohlcv)

        return ohlcv
quantity = 0.0021

while True:
    h = raw_input('Pres enter')
    s = raw_input('symbol: ')
    s = s.upper()

    if s == 'BTC':
        symbol = s + "USDT"
    else:
        symbol = s + "BTC"

    bm = BinanceSocketManager(client)
    conn_key = bm.start_symbol_ticker_socket(
        symbol, process_message)  # Connect to price socket from Binance
    bm.start()  # Start connection to price socket from Binance

    # Calculate step size for price and quantity
    for x in range(len(data['symbols'])):
        if (symbol in data['symbols'][x]['symbol']):
            min_price = float(data['symbols'][x]['filters'][0]
                              ['tickSize'])  # minimum price step
            min_Qty = float(data['symbols'][x]['filters'][1]
                            ['stepSize'])  # minimum quantity step

    # Start listening to your key presses
    lis = keyboard.Listener(on_press=on_pres)
    lis.start()
    lis.join()
    bm.close()
class BinanceAccount(Account):
    def __init__(self, get_data=True):
        #self.last_updated = 'xxx'
        #self.sandbox = sandbox
        #gdax sandbox cred
        #config = configparser.ConfigParser()
        #cur_dir = os.path.dirname(__file__)
        #config.read(os.path.join(cur_dir,"config.txt"))
        #self.api_key = config.get("binance","key")
        #self.api_secret = config.get("binance","secret")
        #self.api_pass = ''
        self._init_client(get_data)
        self._stop_loss = None
        self._take_profit = None

    def _init_client(self, get_data=True):
        self.client = Client('key', 'secret', get_data=get_data)
        self.socket_manager = BinanceSocketManager(self.client)

    def get_price(self, symbol):
        if symbol.upper() == "BTC":
            symbol = "BTCUSDT"
        elif symbol.upper() == "ETH":
            symbol = "ETHUSDT"
        elif symbol.upper() == "USD":
            symbol = "USDBTCT"
        order_book = self.client.get_orderbook_ticker(symbol=symbol)
        price = float(order_book['askPrice'])
        return price

    def get_net_value(self, itemized=False):
        """Get value of all owned assets in USD.
        params:
            itemized : bool : if False (default) return total account value in dollars (int)
                              if True, return dictionary of each assets value`{asset:usd}`
        """

        base_asset = "BTCUSDT"
        current_base_price = self.get_price(base_asset)
        # is_held = lambda x: float(x["free"]) + float(x["locked"]) > 0
        # all_assets = self.client.get_account()
        # owned_assets = filter(is_held,all_assets["balances"])
        owned_assets = self.get_balance()
        asset_usd = {}
        for asset, asset_value in owned_assets.items():
            asset_name = asset + base_asset[:3]
            try:
                asset_base_price = self.get_price(asset_name)
                usd_amt = asset_value * asset_base_price * current_base_price
                asset_usd[asset] = usd_amt
            except Exception as e:
                asset_usd[asset] = 0

        if itemized:
            total = asset_usd
        else:
            total = sum([i for i in asset_usd.values()])
        return total

    def get_state(self):
        state = []

        money_available = int(self.get_net_value() > 0)
        state.append(money_available)

        short_pos_open = int(self.client.pos_direction < 0)
        state.append(short_pos_open)

        long_pos_open = int(self.client.pos_direction > 0)
        state.append(long_pos_open)

        return state

    def get_balance(self, symbol=None):
        """Get balance of free coins.
        params:
            symbol : str : if None, returns all free coins and their balance in a dictionary.p
                           else returns the balance of inputted coin.
        """
        is_held = lambda x: float(x["free"]) + float(x["locked"]) > 0
        all_assets = self.client.get_account()
        owned_assets = list(filter(is_held, all_assets["balances"]))

        if symbol is not None:
            symbol = symbol.upper(
            )[:3]  # only take first three of symbol i.e. ETH isntead of ETHBTC
            fn = lambda x: x["asset"] == symbol
            asset_value = list(
                filter(fn, owned_assets)
            )  # there could be a bug here? need to set asset_value to owned_assests['free']
            if len(asset_value) > 0:
                asset_value = asset_value[0]["free"]
            else:
                asset_value = 0
            #asset_value.append(0) # add 0 to list in case symbol not owned
            total = float(asset_value)
        else:
            total = {x["asset"]: float(x["free"]) for x in owned_assets}
        return total

    def get_positions(self, symbol):
        """return current positions"""
        pass

    def place_order(self,
                    order_type,
                    quantity,
                    symbol,
                    stop_loss=None,
                    take_profit=None):
        """Places an order.
        params:
            order_type  : str :   if "BUY", Buys quantity amount of symbol.
                                  if "SELL", Sells quantity amount of symbol.
            quantity    : float : how much you want to buy or sell.
            symbol      : str :   ex. ETHBTC
        """
        if order_type.upper() == "BUY":
            order = self.client.order_market_buy(symbol=symbol,
                                                 quantity=quantity)
        elif order_type.upper() == "SELL":
            order = self.client.order_market_sell(symbol=symbol,
                                                  quantity=quantity)
        else:
            order = "Something is wrong!"

        self._stop_loss = stop_loss
        self._take_profit = take_profit

        return order

    def _set_stop_loss(self, price, symbol):
        self._stop_loss = price

    def _set_take_profit(self, price, symbol):
        self._take_profit = price

    def execute_stop_loss_or_take_profit(self):
        if self.cur_pos > 0:
            self.place_order("SELL", abs(cur_pos), 'btc')
        elif self.cur_pos < 0:
            self.place_order("BUY", abs(cur_pos), 'btc')

    def process_message(self, msg):
        os.system('clear')
        print("message type: {}".format(msg['e']))
        print(msg)

    def start_ticker_stream(self, symbol, callback=None):
        if callback is None:
            self.process_message
        self.socket_manager.start_symbol_ticker_socket(symbol, callback)
        self.socket_manager.start()

    def stop_socket_stream(self):
        self.socket_manager.close()

    def start_kline_stream(self, symbol):
        self.socket_manager.start_kline_socket(symbol, self.process_message)
        self.socket_manager.start()

    def start_multi_stream(self, streams, callback=None):
        if callback is None:
            self.process_message
        self.socket_manager.start_multiplex_socket(streams, callback)
        self.socket_manager.start()
Beispiel #18
0
class LiveTrading:
    def __init__(self,
                 client: object,
                 strategy: object,
                 debug: bool,
                 verbose: bool,
                 klines: dict = {}):
        self.client: object = client
        self.strategy: object = strategy
        self.trades: list = strategy.trades
        self.verbose: bool = verbose
        self.debug: bool = debug

        self.precision: dict = {}
        self.klines: dict = klines

        self.message_no: int = 0

        for coin in self.strategy.tradeCoins:
            if self.verbose: print(f"Getting precision for {coin}")
            self.precision[coin] = self.get_precision(coin)
            self.klines[coin] = []

        self.active_order: bool = False
        self.open_trade: bool = False

        self.balance: float = self.get_balance(self.strategy.baseCoin)
        self.starting_balance: float = self.balance

        print("\nStarting trading with the following:")
        print(f"Trade coins: {self.strategy.tradeCoins}")
        print(f"Starting {self.strategy.baseCoin}: {self.balance}")
        print(f"Indicator: {self.strategy.indicator}")
        print(f"Strategy: {self.strategy.strategy}")

        self.open_kline_sockets(self.strategy.tradeCoins)

    def get_balance(self, coin):
        return float(self.client.get_asset_balance(asset=coin)["free"])

    def open_kline_sockets(self, coins: list):
        print(f"\nOpening kline socket for {coins}")
        self.bm = BinanceSocketManager(self.client)

        sockets = []

        for coin in coins:
            sockets.append(
                f"{coin.lower()}{self.strategy.baseCoin.lower()}@kline_{self.strategy.interval}"
            )

        self.kline_socket = self.bm.start_multiplex_socket(
            sockets, self.process_message)
        self.print_with_timestamp("Going live...")
        self.bm.start()

    @staticmethod
    def print_with_timestamp(message):
        print(
            f"{time.strftime('%d/%m/%y %H:%M:%S', time.localtime())} | {message}"
        )

    def process_message(self, msg):
        msg = msg["data"]
        if self.verbose:
            self.print_with_timestamp(f"Recieved kline for {msg['s']}")
        if self.debug: print(msg)

        if (msg['e'] == "kline"): self.process_kline(msg['k'])
        elif msg['e'] == 'error':
            print("Socket error... restarting socket")
            self.bm.close()
            self.open_kline_sockets(self.strategy.tradeCoins)

    def process_kline(self, kline):
        if kline['x']:
            self.message_no += 1
            trade_coin = f"{kline['s'][:len(kline['s']) - len(self.strategy.baseCoin)]}"
            self.klines[trade_coin].append(Data.process_socket_data(kline))

            if (self.verbose):
                self.print_with_timestamp(
                    f"Interval closed for {kline['s']}: {len(self.klines[trade_coin])}"
                )

            if (self.debug): print(self.klines)

            if self.message_no == len(self.strategy.tradeCoins):
                self.message_no = 0
                self.print_with_timestamp(
                    "Got closed interval for all coins. Checking for any actions..."
                )
                self.strategy.refresh(self.klines)

                # If the strategy is returning more trades than we have, there must be new ones
                if len(self.strategy.trades) > len(self.trades):
                    new_trades = self.strategy.trades[len(self.trades):]
                    print(f"New trades: {new_trades}")
                    self.process_trades(new_trades)
                    self.trades += new_trades

    def process_trades(self, trades):
        for trade in trades:
            if not trade.completed:
                if trade.action == "BUY":
                    self.place_buy(trade.trade_coin, trade.price)
                elif trade.action == "SELL":
                    self.place_sell(trade.trade_coin, trade.price)
                trade.completed = True

    def place_buy(self, coin, price):
        self.balance = self.get_balance(self.strategy.baseCoin)
        quantity = self.round_down((self.balance * 0.99) / float(price),
                                   self.precision[coin])
        print(f"Buying {quantity} {coin} at {price}")
        order = self.client.create_order(
            symbol=f"{coin}{self.strategy.baseCoin}",
            side=SIDE_BUY,
            type=ORDER_TYPE_LIMIT,
            timeInForce=TIME_IN_FORCE_GTC,
            quantity=quantity,
            price=price)
        self.print_with_timestamp(order)

    def place_sell(self, coin, price):
        balance = self.get_balance(coin)
        quantity = self.round_down(balance, self.precision[coin])
        if (quantity != 0):
            print(f"\nSelling {quantity} {coin} at {price}")
            order = self.client.create_order(
                symbol=f"{coin}{self.strategy.baseCoin}",
                side=SIDE_SELL,
                type=ORDER_TYPE_LIMIT,
                timeInForce=TIME_IN_FORCE_GTC,
                quantity=quantity,
                price=price)
            self.print_with_timestamp(order)
        else:
            print(f"Something went wrong. Bot is trying to sell 0 {coin}")

    def get_precision(self, coin):
        for filt in self.client.get_symbol_info(
                f"{coin}{self.strategy.baseCoin}")['filters']:
            if filt['filterType'] == 'LOT_SIZE':
                return int(round(-math.log(float(filt['stepSize']), 10), 0))

    @staticmethod
    def round_down(n, decimals=0):
        return math.floor(n * (10**decimals)) / 10**decimals
Beispiel #19
0
class OLDFXConnector(Logger):
    ORDER_STATUS_NEW = 'NEW'
    ORDER_STATUS_PARTIALLY_FILLED = 'PARTIALLY_FILLED'
    ORDER_STATUS_FILLED = 'FILLED'
    ORDER_STATUS_CANCELED = 'CANCELED'
    ORDER_STATUS_PENDING_CANCEL = 'PENDING_CANCEL'
    ORDER_STATUS_REJECTED = 'REJECTED'
    ORDER_STATUS_EXPIRED = 'EXPIRED'

    SIDE_BUY = 'BUY'
    SIDE_SELL = 'SELL'

    ORDER_TYPE_LIMIT = 'LIMIT'
    ORDER_TYPE_MARKET = 'MARKET'
    ORDER_TYPE_STOP_LOSS = 'STOP_LOSS'
    ORDER_TYPE_STOP_LOSS_LIMIT = 'STOP_LOSS_LIMIT'
    ORDER_TYPE_TAKE_PROFIT = 'TAKE_PROFIT'
    ORDER_TYPE_TAKE_PROFIT_LIMIT = 'TAKE_PROFIT_LIMIT'
    ORDER_TYPE_LIMIT_MAKER = 'LIMIT_MAKER'

    TIME_IN_FORCE_GTC = 'GTC'  # Good till cancelled
    TIME_IN_FORCE_IOC = 'IOC'  # Immediate or cancel
    TIME_IN_FORCE_FOK = 'FOK'  # Fill or kill

    ORDER_RESP_TYPE_ACK = 'ACK'
    ORDER_RESP_TYPE_RESULT = 'RESULT'
    ORDER_RESP_TYPE_FULL = 'FULL'

    def __init__(self, key=None, secret=None):
        super().__init__()
        self.__key = key
        self.__secret = secret
        self.client = Client(key, secret)
        self.bs = BinanceSocketManager(self.client)
        # self.connection = None
        self.ticker_connection = None
        self.user_data_connection = None

    def listen_symbols(self, symbols, on_ticker_received, user_data_handler):
        # self.client = Client(self.__key, self.__secret)
        self.bs = BinanceSocketManager(self.client)
        # self.connection = self.bs.start_multiplex_socket(['{}@trade'.format(s.lower()) for s in symbols],
        #                                                  socket_handler)

        self.bs.start_ticker_socket(on_ticker_received)
        # self.ticker_connection = self.bs.start_multiplex_socket(['{}@ticker'.format(s.lower()) for s in symbols],
        #                                                         on_ticker_received)
        self.user_data_connection = self.bs.start_user_socket(
            user_data_handler)
        self.logInfo('Ticker and User WS initialized')
        self.bs.name = 'Binance WS'

    def start_listening(self):
        self.bs.start()
        self.logInfo('WS listening started')

    def stop_listening(self):
        self.bs.close()
        self.logInfo('Socket stopped')
        # if self.ticker_connection:
        #     # self.bs.stop_socket(self.connection)
        #     self.bs.stop_socket(self.ticker_connection)
        #     self.bs.stop_socket(self.user_data_connection)

    @retry(**DEFAULT_RETRY_SETTINGS)
    def cancel_order(self, sym, id):
        return self.client.cancel_order(symbol=sym, orderId=id)

    @retry(**DEFAULT_RETRY_SETTINGS)
    def cancel_open_orders(self, sym):
        orders = self.get_open_orders(sym)
        if orders:
            for order_id in orders:
                self.client.cancel_order(symbol=sym, orderId=order_id)

    def get_server_time(self):
        return self.client.get_server_time()

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_open_orders(self, sym):
        return [o['orderId'] for o in self.client.get_open_orders(symbol=sym)]

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_all_orders(self, sym, limit=500):
        return {
            o['orderId']: {
                'status': o['status'],
                'price': o['price'],
                'stop_price': o['stopPrice'],
                'vol': o['origQty'],
                'vol_exec': o['executedQty']
            }
            for o in self.client.get_all_orders(symbol=sym, limit=limit)
        }

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_all_tickers(self):
        return self.client.get_all_tickers()

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_orderbook_tickers(self):
        return self.client.get_orderbook_tickers()

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_order_status(self, sym, id):
        return self.client.get_order(symbol=sym, orderId=id)

    # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY)
    def create_makret_order(self, sym, side, volume):
        return self.client.create_order(
            symbol=sym,
            side=side,
            type=FXConnector.ORDER_TYPE_MARKET,
            quantity=FXConnector.format_number(volume))

    # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY)
    def create_limit_order(self, sym, side, price, volume):
        return self.client.create_order(
            symbol=sym,
            side=side,
            type=FXConnector.ORDER_TYPE_LIMIT,
            timeInForce=FXConnector.TIME_IN_FORCE_GTC,
            quantity=FXConnector.format_number(volume),
            price=FXConnector.format_number(price))

    # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY)
    def create_stop_order(self, sym, side, stop_price, price, volume):
        return self.client.create_order(
            symbol=sym,
            side=side,
            type=FXConnector.ORDER_TYPE_STOP_LOSS_LIMIT,
            timeInForce=FXConnector.TIME_IN_FORCE_GTC,
            quantity=FXConnector.format_number(volume),
            stopPrice=FXConnector.format_number(stop_price),
            price=FXConnector.format_number(price))

    # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY)
    def create_test_stop_order(self, sym, side, price, volume):
        return self.client.create_test_order(
            symbol=sym,
            side=side,
            type=FXConnector.ORDER_TYPE_STOP_LOSS_LIMIT,
            timeInForce=FXConnector.TIME_IN_FORCE_GTC,
            quantity=FXConnector.format_number(volume),
            stopPrice=FXConnector.format_number(price),
            price=FXConnector.format_number(price))

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_balance(self, asset):
        bal = self.client.get_asset_balance(asset=asset)
        return float(bal['free']), float(bal['locked'])

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_all_balances(self, assets: dict):
        res = self.client.get_account()

        if 'balances' in res:
            for bal in res['balances']:
                if bal['asset'] in assets:
                    assets[bal['asset']] = {
                        'f': float(bal['free']),
                        'l': float(bal['locked'])
                    }

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_all_balances_dict(self):
        res = self.client.get_account()

        if 'balances' in res:
            return {
                bal['asset']: {
                    'f': float(bal['free']),
                    'l': float(bal['locked'])
                }
                for bal in res['balances']
            }

        return {}

    @retry(**DEFAULT_RETRY_SETTINGS)
    def get_exchange_info(self):
        return self.client.get_exchange_info()
        # info = self.client.get_exchange_info()
        #
        # symbol_info = None
        # for s in info['symbols']:
        #     if s['symbol'] == sym:
        #         symbol_info = s
        #         break
        #
        # props = {}
        # for f in symbol_info['filters']:
        #     props.update(f)
        #
        # props.pop('filterType', None)
        #
        # return FXConnector.ExchangeInfo(**props)

    @classmethod
    def format_number(cls, num):
        return '{:.08f}'.format(num)
Beispiel #20
0
class BinanceManager:
    def __init__(self, api_key=None, secret_key=None):
        # Binance API Client
        self.client = Client(api_key, secret_key)
        # Binance API websocket
        self.socket = BinanceSocketManager(self.client)
        # Panda dataFrame
        self.df = None
        # Trading asset
        self.symbol = None
        # Interval
        self.interval = None
        # Logger
        self.logger = Logger()
        # File to save data
        self.filename = None

    def start(self, symbol, interval, startTime=None, filename="data.csv"):
        self.logger.success("Manager start")
        self.symbol = symbol
        self.interval = interval
        self.filename = filename
        self.get_historical_klines(self.symbol, interval, startTime)
        self.logger.success("Socket start")
        self.socket.start()
        self.logger.success("Socket kline start")
        self.socket.start_kline_socket(self.symbol,
                                       self.klines_callback,
                                       interval=self.interval)
        return self

    def get_historical_klines(self,
                              symbol="btcusdt",
                              interval="1h",
                              startTime=None,
                              endTime=None,
                              limit=500):
        self.logger.info("Get historical klines")
        # fetch klines
        data = self.client.futures_klines(symbol=symbol,
                                          interval=interval,
                                          startTime=startTime,
                                          endTime=endTime,
                                          limit=limit)
        # Keep only the first 6 columns
        data = np.array(data)[:, 0:6]
        # Create a DataFrame with annotated columns
        df = pd.DataFrame(
            data, columns=["Date", "Open", "High", "Low", "Close", "Volume"])
        # Convert Date from ms to Datetime
        df["Date"] = pd.to_datetime(df["Date"], unit="ms")
        # Convert columns to numeric values
        df["Open"] = pd.to_numeric(df["Open"], errors="coerce")
        df["High"] = pd.to_numeric(df["High"], errors="coerce")
        df["Low"] = pd.to_numeric(df["Low"], errors="coerce")
        df["Close"] = pd.to_numeric(df["Close"], errors="coerce")
        df["Volume"] = pd.to_numeric(df["Volume"], errors="coerce")
        # Set Date column as index
        df.set_index("Date", inplace=True)
        self.df = df
        self.write()
        return df

    def klines_callback(self, msg):
        self.logger.info("Websocket: Updating last kline")
        if msg['e'] == 'error':
            print("[!] ERROR: Klines websocket does not work.")
            return self

        # [Kline start time, Open price, High price, Low price, Close price, Base asset volume
        kline = np.array([
            msg['k']['t'], msg['k']['o'], msg['k']['h'], msg['k']['l'],
            msg['k']['c'], msg['k']['v']
        ])
        # Create a Tableau with annotated columns
        df = pd.DataFrame(
            [kline],
            columns=["Date", "Open", "High", "Low", "Close", "Volume"])
        # Convert Date from ms to Datetime
        df["Date"] = pd.to_datetime(df["Date"], unit="ms")
        # Convert columns to numeric values
        df["Open"] = pd.to_numeric(df["Open"], errors="coerce")
        df["High"] = pd.to_numeric(df["High"], errors="coerce")
        df["Low"] = pd.to_numeric(df["Low"], errors="coerce")
        df["Close"] = pd.to_numeric(df["Close"], errors="coerce")
        df["Volume"] = pd.to_numeric(df["Volume"], errors="coerce")
        df.set_index("Date", inplace=True)
        # If is not a new kline
        if self.df.last_valid_index() == df.last_valid_index():
            # Update last kline value
            self.df.drop(self.df.last_valid_index(), inplace=True)
        # Append df to main df
        self.df = self.df.append(df, ignore_index=False)
        # Update data
        self.write()
        return self

    def write(self):
        if self.filename is not None:
            lock = FileLock(self.filename + '.lock')
            with lock:
                self.df.to_csv(self.filename)

    def stop(self):
        self.logger.success("Manager stop")
        self.socket.close()
        return self
Beispiel #21
0
class BalanceGUI(tk.Frame):
    def __init__(self, parent, coins):
        ''' Initialize the GUI and read the config file '''
        tk.Frame.__init__(self, parent)
        parent.protocol('WM_DELETE_WINDOW', self.on_closing)
        self.parent = parent
        parent.deiconify()
        self.coins = coins
        self.coins_base = coins
        self.queue = queue.Queue()
        self.trades_placed = 0
        self.trades_completed = 0
        self.trades = []
        self.headers = self.column_headers()
        self.read_config()
        self.initalize_records()

        #portfolio display
        self.portfolio_view = tk.LabelFrame(parent, text='Portfolio')
        self.portfolio_view.grid(row=0,
                                 column=0,
                                 columnspan=2,
                                 sticky=tk.E + tk.W + tk.N + tk.S)
        self.portfolio = tkinter.ttk.Treeview(self.portfolio_view,
                                              height=len(self.coins),
                                              selectmode='extended')
        self.portfolio['columns'] = ('Stored', 'Exchange', 'Locked', 'Target',
                                     'Actual', 'Bid', 'Ask', 'Action',
                                     'Status', 'Event')
        for label in self.portfolio['columns']:
            if label == 'Status' or label == 'Event':
                self.portfolio.column(label, width=200)
            elif label == 'Action':
                self.portfolio.column(label, width=120)
            else:
                self.portfolio.column(label, width=100)
            self.portfolio.heading(label, text=label)
        self.portfolio.grid(row=0, column=0)

        for i in range(2):
            self.parent.columnconfigure(i, weight=1, uniform='parent')

        #options display
        self.controls_view = tk.LabelFrame(parent, text='Controls')
        for i in range(4):
            self.controls_view.columnconfigure(i, weight=1, uniform='controls')
        self.controls_view.grid(row=1,
                                column=0,
                                sticky=tk.E + tk.W + tk.N + tk.S)

        self.key_label = tk.Label(self.controls_view,
                                  text='API Key',
                                  relief='ridge')
        self.key_label.grid(row=0, column=0, sticky=tk.E + tk.W)

        self.secret_label = tk.Label(self.controls_view,
                                     text='API Secret',
                                     relief='ridge')
        self.secret_label.grid(row=1, column=0, sticky=tk.E + tk.W)

        self.key_entry = tk.Entry(self.controls_view, show='*')
        self.key_entry.grid(row=0, column=1, columnspan=2, sticky=tk.E + tk.W)

        self.secret_entry = tk.Entry(self.controls_view, show='*')
        self.secret_entry.grid(row=1,
                               column=1,
                               columnspan=2,
                               sticky=tk.E + tk.W)

        self.login = tk.Button(self.controls_view,
                               text='Login',
                               command=self.api_enter)
        self.login.grid(row=0,
                        column=3,
                        rowspan=2,
                        sticky=tk.E + tk.W + tk.N + tk.S)

        #Statistics display
        self.stats_view = tk.LabelFrame(parent, text='Statistics')
        self.stats_view.grid(row=1, column=1, sticky=tk.E + tk.W + tk.N + tk.S)
        for i in range(4):
            self.stats_view.columnconfigure(i, weight=1, uniform='stats')

        self.trade_currency_value_label = tk.Label(self.stats_view,
                                                   text=self.trade_currency +
                                                   ' Value:',
                                                   relief='ridge')
        self.trade_currency_value_label.grid(row=0,
                                             column=0,
                                             sticky=tk.E + tk.W)
        self.trade_currency_value_string = tk.StringVar()
        self.trade_currency_value_string.set('0')
        self.trade_currency_value = tk.Label(
            self.stats_view, textvariable=self.trade_currency_value_string)
        self.trade_currency_value.grid(row=0, column=1, sticky=tk.E + tk.W)

        self.imbalance_label = tk.Label(self.stats_view,
                                        text='Imbalance:',
                                        relief='ridge')
        self.imbalance_label.grid(row=1, column=0, sticky=tk.E + tk.W)
        self.imbalance_string = tk.StringVar()
        self.imbalance_string.set('0%')
        self.imbalance_value = tk.Label(self.stats_view,
                                        textvariable=self.imbalance_string)
        self.imbalance_value.grid(row=1, column=1, sticky=tk.E + tk.W)

        self.messages_queued_label = tk.Label(self.stats_view,
                                              text='Status',
                                              relief='ridge')
        self.messages_queued_label.grid(row=0, column=2, sticky=tk.E + tk.W)

        self.messages_string = tk.StringVar()
        self.messages_string.set('Up to Date')
        self.messages_queued = tk.Label(self.stats_view,
                                        textvariable=self.messages_string)
        self.messages_queued.grid(row=0, column=3, sticky=tk.E + tk.W)

        self.trades_label = tk.Label(self.stats_view,
                                     text='Trades Placed:',
                                     relief='ridge')
        self.trades_label.grid(row=1, column=2, sticky=tk.E + tk.W)
        self.trades_count = tk.IntVar()
        self.trades_count.set(0)
        self.trades_count_display = tk.Label(self.stats_view,
                                             textvariable=self.trades_count)
        self.trades_count_display.grid(row=1, column=3, sticky=tk.E + tk.W)

    def read_config(self):
        s_to_ms = 1000
        config = configparser.RawConfigParser(allow_no_value=False)
        config.read('config.ini')
        self.trade_currency = config.get('trades', 'trade_currency')
        if self.trade_currency != 'BTC':
            self.display_error(
                'Config Error',
                '{0} trading pairs are not supported yet, only BTC'.format(
                    self.trade_currency),
                quit_on_exit=True)
        self.rebalance_time = int(config.get('trades',
                                             'rebalance_period')) * s_to_ms
        if self.rebalance_time <= 0:
            self.display_error(
                'Config Error',
                'Rebalance period must be a positive integer (seconds)',
                quit_on_exit=True)
        self.min_trade_value = float(config.get('trades', 'min_trade_value'))
        if self.min_trade_value <= 0:
            self.min_trade_value = None
        self.trade_type = config.get('trades', 'trade_type')
        if self.trade_type != 'MARKET' and self.trade_type != 'LIMIT':
            self.display_error(
                'Config Error',
                '{0} is not a supported trade type. Use MARKET or LIMIT'.
                format(trade_type),
                quit_on_exit=True)
        self.ignore_backlog = int(config.get('websockets', 'ignore_backlog'))

    def on_closing(self):
        ''' Check that all trades have executed
        before starting the save and exit process
        '''
        if self.trades_placed > 0 and self.trades_completed < self.trades_placed:
            if messagebox.askokcancel(
                    'Quit', 'Not all trades have completed. Quit anyway?'):
                self.save_and_quit()
        else:
            self.save_and_quit()

    def save_and_quit(self):
        '''
        If trades have been executed in the current session,
        save them to file. Stop all websockets and exit the GUI.
        '''
        if self.trades:
            df = pd.DataFrame(self.trades)
            if os.path.isfile('trade_history.csv'):
                with open('trade_history.csv', 'a') as f:
                    df.to_csv(f, sep=',', header=False, index=False)
            else:
                with open('trade_history.csv', 'w') as f:
                    df.to_csv(f, sep=',', header=True, index=False)
        for coin in self.coins['coin']:
            pair = coin + self.trade_currency
            self.records[pair].close()
        try:
            self.bm.close()
            reactor.stop()
        except AttributeError:
            self.parent.destroy()
        else:
            self.parent.destroy()

    def exit_error(self):
        if self.quit_on_exit:
            self.top.destroy()
            self.save_and_quit()
        else:
            self.top.destroy()

    def display_error(self, title, error, quit_on_exit=False):
        self.quit_on_exit = quit_on_exit
        self.top = tk.Toplevel()
        self.top.title('Login Error')
        msg = tk.Message(self.top, text=error)
        msg.grid(row=0, column=0)
        button = tk.Button(self.top, text="Dismiss", command=self.exit_error)
        button.grid(row=1, column=0)
        self.top.attributes('-topmost', 'true')

    def api_enter(self):
        '''
        Log in to Binance with the provided credentials,
        update user portfolio and start listening to price and
        account update websockets.
        '''
        api_key = self.key_entry.get()
        self.key_entry.delete(0, 'end')
        api_secret = self.secret_entry.get()
        self.secret_entry.delete(0, 'end')

        try:
            self.client = Client(api_key, api_secret)
            status = self.client.get_system_status()
        except (BinanceRequestException, BinanceAPIException) as e:
            self.display_error('Login Error', e.message)
        else:
            try:
                self.populate_portfolio()
            except BinanceAPIException as e:
                self.display_error('API Error', e.message, quit_on_exit=True)
            else:
                self.start_websockets()

    def start_websockets(self):
        '''
        Start websockets to get price updates for all coins in the portfolio,
        trade execution reports, and user account balance updates.
        Start the message queue processor.
        '''
        self.bm = BinanceSocketManager(self.client)
        trade_currency = self.trade_currency
        symbols = self.coins['symbol'].tolist()
        symbols.remove(trade_currency + trade_currency)
        self.sockets = {}
        for symbol in symbols:
            self.sockets[symbol] = self.bm.start_symbol_ticker_socket(
                symbol, self.queue_msg)
        self.sockets['user'] = self.bm.start_user_socket(self.queue_msg)
        self.bm.start()
        self.parent.after_idle(self.parent.after, 1, self.process_queue)

    def initalize_records(self):
        self.records = dict()
        for coin in self.coins['coin']:
            pair = coin + self.trade_currency
            self.records[pair] = open(pair + '.csv', 'a+', 1)  #unbuffered

    def populate_portfolio(self):
        '''
        Get all symbol info from Binance needed to
        populate user portfolio data and execute trades
        '''
        self.coins = self.coins_base
        self.portfolio.delete(*self.portfolio.get_children())
        exchange_coins = []
        trade_currency = self.trade_currency
        self.trade_coin = trade_currency

        #update the GUI context
        self.key_label.destroy()
        self.key_entry.destroy()
        self.secret_label.destroy()
        self.secret_entry.destroy()
        self.login.destroy()

        updatetext = tk.StringVar()
        updatetext.set('Initializing')
        self.progresslabel = tk.Label(self.controls_view,
                                      textvariable=updatetext)
        self.progresslabel.grid(row=1,
                                column=0,
                                columnspan=4,
                                sticky=tk.E + tk.W)
        progress_var = tk.DoubleVar()
        progress = 0
        progress_var.set(progress)
        self.progressbar = tkinter.ttk.Progressbar(self.controls_view,
                                                   variable=progress_var,
                                                   maximum=len(self.coins))
        self.progressbar.grid(row=0,
                              column=0,
                              columnspan=4,
                              sticky=tk.E + tk.W)
        for coin in self.coins['coin']:
            self.progressbar.update()
            progress += 1
            progress_var.set(progress)
            updatetext.set('Fetching {0} account information'.format(coin))
            self.progresslabel.update()
            pair = coin + trade_currency
            balance = self.client.get_asset_balance(asset=coin)
            if coin != trade_currency:
                price = float(
                    self.client.get_symbol_ticker(symbol=pair)['price'])
                symbolinfo = self.client.get_symbol_info(
                    symbol=pair)['filters']
                minvalue = float(symbolinfo[3]['minNotional'])
                if self.min_trade_value is not None:
                    minvalue = self.min_trade_value
                row = {
                    'coin': coin,
                    'exchange_balance': float(balance['free']),
                    'locked_balance': float(balance['locked']),
                    'minprice': float(symbolinfo[0]['minPrice']),
                    'maxprice': float(symbolinfo[0]['maxPrice']),
                    'ticksize': float(symbolinfo[0]['tickSize']),
                    'minqty': float(symbolinfo[2]['minQty']),
                    'maxqty': float(symbolinfo[2]['maxQty']),
                    'stepsize': float(symbolinfo[2]['stepSize']),
                    'minnotional': minvalue,
                    'symbol': pair,
                    'askprice': price,
                    'bidprice': price,
                    'price': price,
                    'last_placement': None,
                    'last_execution': None
                }
            else:
                fixed_balance = self.coins.loc[self.coins['coin'] ==
                                               coin]['fixed_balance']
                row = {
                    'coin': coin,
                    'exchange_balance': float(balance['free']),
                    'locked_balance': float(balance['locked']),
                    'minprice': 0,
                    'maxprice': 0,
                    'ticksize': 0,
                    'minqty': 0,
                    'maxqty': 0,
                    'stepsize': 0,
                    'minnotional': 0,
                    'symbol': coin + coin,
                    'askprice': 1.0,
                    'bidprice': 1.0,
                    'price': 1.0,
                    'last_placement': None,
                    'last_execution': None
                }
            exchange_coins.append(row)
        exchange_coins = pd.DataFrame(exchange_coins)
        self.coins = pd.merge(self.coins,
                              exchange_coins,
                              on='coin',
                              how='outer')
        self.coins['value'] = self.coins.apply(
            lambda row: row.price * (row.exchange_balance + row.fixed_balance),
            axis=1)
        self.total = np.sum(self.coins['value'])
        self.coins['actual'] = self.coins.apply(
            lambda row: 100.0 * row.value / self.total, axis=1)
        self.update_status()
        i = 0
        for row in self.coins.itertuples():
            self.portfolio.insert(
                '',
                i,
                iid=row.coin,
                text=row.coin,
                values=(row.fixed_balance, row.exchange_balance,
                        row.locked_balance, '{0} %'.format(row.allocation),
                        '{0:.2f} %'.format(row.actual),
                        round_decimal(row.price, row.ticksize),
                        round_decimal(row.price, row.ticksize), '', ''))
            i += 1
        updatetext.set('Testing connection'.format(coin))
        self.dryrun()
        self.progressbar.destroy()
        self.progresslabel.destroy()

        self.automate = tk.BooleanVar()
        self.automate.set(False)
        self.automate_text = tk.StringVar()
        self.automate_text.set('Start Automation')
        self.toggle_automate = tk.Button(
            self.controls_view,
            textvariable=self.automate_text,
            command=lambda: self.automation(toggle=True))
        self.toggle_automate.grid(row=0,
                                  column=0,
                                  rowspan=2,
                                  columnspan=2,
                                  sticky=tk.E + tk.W + tk.N + tk.S)
        self.sell_button = tk.Button(self.controls_view,
                                     text='Execute Sells',
                                     command=self.execute_sells)
        self.sell_button.grid(row=0,
                              column=2,
                              columnspan=2,
                              sticky=tk.E + tk.W)
        self.buy_button = tk.Button(self.controls_view,
                                    text='Execute Buys',
                                    command=self.execute_buys)
        self.buy_button.grid(row=1, column=2, columnspan=2, sticky=tk.E + tk.W)

    def update_status(self):
        '''Update the statistics frame whenever a change occurs in balance or price'''
        value = '{0:.8f}'.format(self.total)
        diff = np.diff(self.coins['actual'].values -
                       self.coins['allocation'].values)
        imbalance = '{0:.2f}%'.format(np.sum(np.absolute(diff)))
        self.trade_currency_value_string.set(value)
        self.imbalance_string.set(imbalance)

    def queue_msg(self, msg):
        '''
        Whenever a weboscket receives a message, check for errors.
        If an error occurs, restart websockets. If no error, add it to
        the message queue.
        '''
        if msg['e'] == 'error':
            self.bm.close()
            reactor.stop()
            self.start_websockets()
        else:
            self.queue.put(msg)

    def get_msg(self):
        '''Reroute new websocket messages to the appropriate handler'''
        try:
            msg = self.queue.get(block=False)
        except queue.Empty:
            pass
        else:
            if msg['e'] == '24hrTicker':
                self.update_price(msg)
            elif msg['e'] == 'outboundAccountInfo':
                self.update_balance(msg)
            elif msg['e'] == 'executionReport':
                self.update_trades(msg)

    def process_queue(self, flush=False):
        '''
        Check for new messages in the queue periodically.
        Recursively calls itself to perpetuate the process.
        '''
        if flush:
            while not self.queue.empty():
                self.get_msg()
        else:
            self.get_msg()
            self.master.after_idle(self.master.after, 1, self.process_queue)
        n = self.queue.qsize()
        if n > self.ignore_backlog:
            self.messages_string.set('{0} Updates Queued'.format(n))
        else:
            self.messages_string.set('Up to Date')

    def update_trades(self, msg):
        ''' Update balances whenever a partial execution occurs '''
        coin = msg['s'][:-len(self.trade_coin)]
        savemsg = {
            self.headers[key]: value
            for key, value in list(msg.items())
        }
        filled = float(savemsg['cumulative_filled_quantity'])
        orderqty = float(savemsg['order_quantity'])
        side = savemsg['side']
        if filled >= orderqty:
            self.coins.loc[self.coins['coin'] == coin,
                           'last_execution'] = time.mktime(
                               datetime.now().timetuple())
            self.trades_completed += 1
            self.trades_count.set(self.trades_completed)
        self.portfolio.set(coin,
                           column='Event',
                           value='{0} {1}/{2} {3}'.format(
                               side, filled, orderqty,
                               datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
        self.trades.append(savemsg)

    def update_balance(self, msg):
        '''
        Update user balances internally and on the
        display whenever an account update message is received.
        '''
        balances = msg['B']
        coins = self.coins['coin'].values
        for balance in balances:
            coin = balance['a']
            if coin in coins:
                exchange_balance = float(balance['f']) + float(balance['l'])
                locked_balance = float(balance['l'])
                self.portfolio.set(
                    coin,
                    column='Exchange',
                    value=round_decimal(
                        exchange_balance, self.coins.loc[
                            self.coins['coin'] == coin]['stepsize'].values[0]))
                self.portfolio.set(
                    coin,
                    column='Locked',
                    value=round_decimal(
                        locked_balance, self.coins.loc[
                            self.coins['coin'] == coin]['stepsize'].values[0]))
                self.coins.loc[self.coins['coin'] == coin,
                               'exchange_balance'] = exchange_balance
                self.coins.loc[self.coins['coin'] == coin,
                               'locked_balance'] = locked_balance
                ask = self.coins.loc[self.coins['coin'] == coin,
                                     'askprice'].values[0]
                value = (self.coins.loc[self.coins['coin'] == coin,
                                        'exchange_balance'].values[0] +
                         self.coins.loc[self.coins['coin'] == coin,
                                        'fixed_balance'].values[0]) * ask
                self.coins.loc[self.coins['coin'] == coin, 'value'] = value

        self.total = np.sum(self.coins['value'])
        self.coins['actual'] = self.coins.apply(
            lambda row: 100.0 * row.value / self.total, axis=1)
        for row in self.coins.itertuples():
            coin = row.coin
            actual = '{0:.2f}%'.format(
                self.coins.loc[self.coins['coin'] == coin, 'actual'].values[0])
            self.portfolio.set(coin, column='Actual', value=actual)
        self.update_actions()
        self.update_status()

    def update_price(self, msg):
        '''
        Update symbol prices and user allocations internally
        and on the display whenever a price update is received.
        '''
        coin = msg['s'][:-len(self.trade_currency)]
        ask = float(msg['a'])
        bid = float(msg['b'])
        askprice = round_decimal(
            ask, self.coins.loc[self.coins['coin'] == coin,
                                'ticksize'].values[0])
        bidprice = round_decimal(
            bid, self.coins.loc[self.coins['coin'] == coin,
                                'ticksize'].values[0])
        self.portfolio.set(coin, column='Ask', value=askprice)
        self.coins.loc[self.coins['coin'] == coin, 'askprice'] = ask
        self.portfolio.set(coin, column='Bid', value=bidprice)
        self.coins.loc[self.coins['coin'] == coin, 'bidprice'] = bid
        value = (self.coins.loc[self.coins['coin'] == coin,
                                'exchange_balance'].values[0] +
                 self.coins.loc[self.coins['coin'] == coin,
                                'fixed_balance'].values[0]) * ask
        self.coins.loc[self.coins['coin'] == coin, 'value'] = value
        self.total = np.sum(self.coins['value'])
        self.coins['actual'] = self.coins.apply(
            lambda row: 100.0 * row.value / self.total, axis=1)
        for row in self.coins.itertuples():
            coin = row.coin
            actual = '{0:.2f}%'.format(
                self.coins.loc[self.coins['coin'] == coin, 'actual'].values[0])
            self.portfolio.set(coin, column='Actual', value=actual)
        self.update_actions()
        self.update_status()
        self.print_price(msg)

    def print_price(self, msg):
        pair = msg['s']
        avg_price = float(msg['w'])
        time = float(msg['E'])
        mid_price = (float(msg['b']) + float(msg['a'])) / 2.0
        self.records[pair].write('{0},{1},{2}\n'.format(
            time, avg_price, mid_price))

    def update_actions(self):
        '''
        Calcuate required trades and update the main GUI
        '''
        for row in self.coins.itertuples():
            tradecoin_balance = np.squeeze(
                self.coins[self.coins['coin'] ==
                           self.trade_coin]['exchange_balance'].values)
            tradecoin_locked = np.squeeze(
                self.coins[self.coins['coin'] ==
                           self.trade_coin]['locked_balance'].values)
            tradecoin_free = tradecoin_balance - tradecoin_locked
            dif = (row.allocation -
                   row.actual) / 100.0 * self.total / row.price

            if dif < 0:
                side = SIDE_SELL
            if dif > 0:
                side = SIDE_BUY

            status = ''
            coin = row.coin
            pair = coin + self.trade_coin
            balance = float(row.exchange_balance) - float(row.locked_balance)
            actual = row.actual
            qty = np.absolute(dif)

            action = '{0} {1}'.format(side, round_decimal(qty, row.stepsize))
            if side == SIDE_SELL:
                price = row.bidprice
            if side == SIDE_BUY:
                price = row.askprice
            if side == SIDE_SELL and qty > balance and coin != self.trade_coin:
                status = 'Insufficient ' + coin + ' for sale'
            if coin == self.trade_coin:
                status = 'Ready'
            elif qty < row.minqty or qty * price < row.minnotional:
                status = status = 'Trade value too small ({0:.0f}%)'.format(
                    100.0 * qty * price / row.minnotional)
            elif qty > row.maxqty:
                status = 'Trade quantity too large'
            elif side == SIDE_BUY and qty * price > tradecoin_free:
                status = 'Insufficient ' + self.trade_coin + ' for purchase'
            else:
                status = 'Trade Ready'
            self.portfolio.set(coin, column='Status', value=status)
            self.portfolio.set(coin, column='Action', value=action)

    def execute_transactions(self, side, dryrun):
        '''
        Calculate the required trade for each coin and execute
        them if they belong to the appropriate side
        '''
        for row in self.coins.itertuples():
            self.process_queue(flush=True)
            tradecoin_balance = np.squeeze(
                self.coins[self.coins['coin'] ==
                           self.trade_coin]['exchange_balance'].values)
            tradecoin_locked = np.squeeze(
                self.coins[self.coins['coin'] ==
                           self.trade_coin]['locked_balance'].values)
            tradecoin_free = tradecoin_balance - tradecoin_locked
            dif = (row.allocation -
                   row.actual) / 100.0 * self.total / row.price
            if dif < 0 and side == SIDE_BUY:
                continue
            if dif > 0 and side == SIDE_SELL:
                continue
            status = ''
            coin = row.coin
            pair = coin + self.trade_coin
            balance = float(row.exchange_balance) - float(row.locked_balance)
            actual = row.actual
            qty = np.absolute(dif)
            action = '{0} {1}'.format(side, round_decimal(qty, row.stepsize))
            last_placement = np.squeeze(self.coins[self.coins['coin'] == coin]
                                        ['last_placement'].values)
            last_execution = np.squeeze(self.coins[self.coins['coin'] == coin]
                                        ['last_execution'].values)
            if side == SIDE_SELL:
                price = row.bidprice
            if side == SIDE_BUY:
                price = row.askprice
            if side == SIDE_SELL and qty > balance and coin != self.trade_coin:
                status = 'Insufficient ' + coin + ' for sale'
            if coin == self.trade_coin:
                status = 'Ready'
            elif qty < row.minqty or qty * price < row.minnotional:
                status = 'Trade value too small ({0:.0f}%)'.format(
                    100.0 * qty * price / row.minnotional)
            elif qty > row.maxqty:
                status = 'Trade quantity too large'
            elif side == SIDE_BUY and qty * price > tradecoin_free:
                status = 'Insufficient ' + self.trade_coin + ' for purchase'
            elif last_placement == None or last_execution >= last_placement:
                trade_currency = self.trade_coin
                try:
                    self.place_order(coin, pair, self.trade_type, qty, price,
                                     side, dryrun, row.stepsize, row.ticksize)
                except (BinanceRequestException, BinanceAPIException,
                        BinanceOrderException, BinanceOrderMinAmountException,
                        BinanceOrderMinPriceException,
                        BinanceOrderMinTotalException,
                        BinanceOrderUnknownSymbolException,
                        BinanceOrderInactiveSymbolException) as e:
                    self.portfolio.set(coin, column='Event', value=e.message)
                else:
                    status = 'Trade Ready'
                    if not dryrun:
                        self.trades_placed += 1
                        status = 'Trade Placed'
                        self.portfolio.set(coin,
                                           column='Event',
                                           value='Trade Placed')
            self.portfolio.set(coin, column='Status', value=status)
            self.portfolio.set(coin, column='Action', value=action)

    def automation(self, toggle=False):
        if toggle:
            if not self.automate.get():
                self.automate_text.set('Stop Automation')
            else:
                self.automate_text.set('Start Automation')
            self.automate.set(not self.automate.get())
        if self.automate.get():
            self.execute_sells()
            self.execute_buys()
            self.rebalance_callback = self.parent.after(
                self.rebalance_time, self.automation)
        else:
            self.parent.after_cancel(self.rebalance_callback)

    def execute_sells(self):
        '''
        Perform any sells required by overachieving coins
        '''
        self.execute_transactions(side=SIDE_SELL, dryrun=False)

    def execute_buys(self):
        '''
        Perform any buys required by underachieving coins
        '''
        self.execute_transactions(side=SIDE_BUY, dryrun=False)

    def dryrun(self):
        '''
        perform a dry run to list what trades are required
        '''
        self.execute_transactions(side=SIDE_SELL, dryrun=True)
        self.execute_transactions(side=SIDE_BUY, dryrun=True)

    def place_order(self, coin, pair, trade_type, quantity, price, side,
                    dryrun, stepsize, ticksize):
        '''
        Format and place an order using the Binance API
        '''
        if trade_type == 'LIMIT':
            if dryrun:
                order = self.client.create_test_order(
                    symbol=pair,
                    side=side,
                    type=ORDER_TYPE_LIMIT,
                    timeInForce=TIME_IN_FORCE_GTC,
                    quantity=round_decimal(quantity, stepsize),
                    price=round_decimal(price, ticksize))
            else:
                order = self.client.create_order(
                    symbol=pair,
                    side=side,
                    type=ORDER_TYPE_LIMIT,
                    timeInForce=TIME_IN_FORCE_GTC,
                    quantity=round_decimal(quantity, stepsize),
                    price=round_decimal(price, ticksize))
        elif trade_type == 'MARKET':
            if dryrun:
                order = self.client.create_test_order(symbol=pair,
                                                      side=side,
                                                      type=ORDER_TYPE_MARKET,
                                                      quantity=round_decimal(
                                                          quantity, stepsize))
            else:
                order = self.client.create_order(symbol=pair,
                                                 side=side,
                                                 type=ORDER_TYPE_MARKET,
                                                 quantity=round_decimal(
                                                     quantity, stepsize))
        if not dryrun:
            self.coins.loc[self.coins['coin'] == coin,
                           'last_placement'] = time.mktime(
                               datetime.now().timetuple())

    def column_headers(self):
        ''' define human readable aliases for the headers in trade execution reports. '''
        return {
            'e': 'event_type',
            'E': 'event_time',
            's': 'symbol',
            'c': 'client_order_id',
            'S': 'side',
            'o': 'type',
            'O': 'order_creation_time',
            'f': 'time_in_force',
            'q': 'order_quantity',
            'p': 'order_price',
            'P': 'stop_price',
            'F': 'iceberg_quantity',
            'g': 'ignore_1',
            'C': 'original_client_order_id',
            'x': 'current_execution_type',
            'X': 'current_order_status',
            'r': 'order_reject_reason',
            'i': 'order_id',
            'l': 'last_executed_quantity',
            'z': 'cumulative_filled_quantity',
            'Z': 'cumulative_quote_asset_transacted_qty',
            'L': 'last_executed_price',
            'n': 'commission_amount',
            'N': 'commission_asset',
            'T': 'transaction_time',
            't': 'trade_id',
            'I': 'ignore_2',
            'w': 'order_working',
            'm': 'maker_side',
            'M': 'ignore_3',
            'Y': 'last_quote_asset_transacted_qty'
        }
class BinanceStore(with_metaclass(MetaSingleton, object)):
    _GRANULARITIES = {
        (TimeFrame.Minutes, 1): '1m',
        (TimeFrame.Minutes, 3): '3m',
        (TimeFrame.Minutes, 5): '5m',
        (TimeFrame.Minutes, 15): '15m',
        (TimeFrame.Minutes, 30): '30m',
        (TimeFrame.Minutes, 60): '1h',
        (TimeFrame.Minutes, 120): '2h',
        (TimeFrame.Minutes, 240): '4h',
        (TimeFrame.Minutes, 360): '6h',
        (TimeFrame.Minutes, 480): '8h',
        (TimeFrame.Minutes, 720): '12h',
        (TimeFrame.Days, 1): '1d',
        (TimeFrame.Days, 3): '3d',
        (TimeFrame.Weeks, 1): '1w',
        (TimeFrame.Months, 1): '1M',
    }

    BrokerCls = None  # Broker class will autoregister
    DataCls = None  # Data class will auto register

    @classmethod
    def getdata(cls, *args, **kwargs):
        """Returns ``DataCls`` with args, kwargs"""
        return cls.DataCls(*args, **kwargs)

    @classmethod
    def getbroker(cls, *args, **kwargs):
        """Returns broker with *args, **kwargs from registered ``BrokerCls``"""
        return cls.BrokerCls(*args, **kwargs)

    def __init__(self,
                 api_key,
                 api_secret,
                 coin_refer,
                 coin_target,
                 retries=5):
        self.binance = Client(api_key, api_secret)
        self.binance_socket = BinanceSocketManager(self.binance)
        self.coin_refer = coin_refer
        self.coin_target = coin_target
        self.retries = retries

        self._precision = None
        self._step_size = None

        self._cash = 0
        self._value = 0
        self.get_balance()

    def retry(method):
        @wraps(method)
        def retry_method(self, *args, **kwargs):
            for i in range(self.retries):
                time.sleep(500 / 1000)  # Rate limit
                try:
                    return method(self, *args, **kwargs)
                except BinanceAPIException:
                    if i == self.retries - 1:
                        raise

        return retry_method

    @retry
    def cancel_order(self, order_id):
        try:
            self.binance.cancel_order(symbol=self.symbol, orderId=order_id)
        except BinanceAPIException as api_err:
            if api_err.code == -2011:  # Order filled
                return
            else:
                raise api_err
        except Exception as err:
            raise err

    @retry
    def create_order(self, side, type, size, price):
        return self.binance.create_order(symbol=self.symbol,
                                         side=side,
                                         type=type,
                                         timeInForce=TIME_IN_FORCE_GTC,
                                         quantity=self.format_quantity(size),
                                         price=self.strprecision(price))

    @retry
    def close_open_orders(self):
        orders = self.binance.get_open_orders(symbol=self.symbol)
        for o in orders:
            self.cancel_order(o['orderId'])

    def format_quantity(self, size):
        precision = self.step_size.find('1') - 1
        if precision > 0:
            return '{:0.0{}f}'.format(size, precision)
        return floor(int(size))

    @retry
    def get_asset_balance(self, asset):
        balance = self.binance.get_asset_balance(asset)
        return float(balance['free']), float(balance['locked'])

    def get_balance(self):
        free, locked = self.get_asset_balance(self.coin_target)
        self._cash = free
        self._value = free + locked

    def get_interval(self, timeframe, compression):
        return self._GRANULARITIES.get((timeframe, compression))

    def get_precision(self):
        symbol_info = self.get_symbol_info(self.symbol)
        self._precision = symbol_info['baseAssetPrecision']

    def get_step_size(self):
        symbol_info = self.get_symbol_info(self.symbol)
        for f in symbol_info['filters']:
            if f['filterType'] == 'LOT_SIZE':
                self._step_size = f['stepSize']

    @retry
    def get_symbol_info(self, symbol):
        return self.binance.get_symbol_info(symbol)

    @property
    def precision(self):
        if not self._precision:
            self.get_precision()
        return self._precision

    def start_socket(self):
        if self.binance_socket.is_alive():
            return

        self.binance_socket.daemon = True
        self.binance_socket.start()

    @property
    def step_size(self):
        if not self._step_size:
            self.get_step_size()

        return self._step_size

    def stop_socket(self):
        self.binance_socket.close()
        reactor.stop()
        self.binance_socket.join()

    def strprecision(self, value):
        return '{:.{}f}'.format(value, self.precision)

    @property
    def symbol(self):
        return '{}{}'.format(self.coin_refer, self.coin_target)
Beispiel #23
0
class DataCatcher:
    # Функция для обработки потока ордерной книги
    def orderbook_callback(self, query):
        pair = query['stream'].split('@')[0]

        # Записываем все, что можем вытащить из ордербука, в хранилище
        for side in ('asks', 'bids'):
            for level, (price, quantity) in enumerate(query['data'][side]):
                self.storage[pair + '_' + side + '_orderbook_price_level_' +
                             str(level)] = price
                self.storage[pair + '_' + side + '_orderbook_quantity_level_' +
                             str(level)] = quantity

    # Функция для обработки свечей
    def kline_callback(self, query):
        pair = query['stream'].split('@')[0]

        # Если свеча еще не завершена (данные неполные), ничего не делаем
        if not query['data']['k']['x']:
            return

        name_dict = {
            'open_price': 'o',
            'close_price': 'c',
            'high_price': 'h',
            'low_price': 'l',
            'base_volume': 'v',
            'trade_number': 'n',
            'quote_volume': 'q',
            'taker_base_volume': 'V',
            'taker_quote_volume': 'Q',
            'update_time': 'T'
        }
        for here in name_dict:
            self.storage[pair + '_kline_' +
                         here] = query['data']['k'][name_dict[here]]

    # Функция, которая вызывается, когда мы ловим сообщение
    def general_callback(self, query):
        stream_type = query['stream'].split('@')[1]
        # Ниже мы определяем, какой поток какая функция обрабатывает и запускаем
        # обработку в отдельном потоке, чтобы не тормозить
        stream_callbacks = {
            'kline_1m': self.kline_callback,
            'depth20': self.orderbook_callback
        }
        action = threading.Thread(target=stream_callbacks[stream_type],
                                  args=(query, ))
        action.start()

    def init_streams(self):
        # Считываем из настроек, какие мы рассматриваем крипты и потоки и инициализируем потоки
        with open('settings/pairs.txt') as file:
            self.pairs = file.readlines()
        self.pairs = [el[:-1] for el in self.pairs]
        with open('settings/streams.txt') as file:
            streams = file.readlines()
        streams = [el[:-1] for el in streams]
        for pair in self.pairs:
            for stream in streams:
                self.streams.append(pair + stream)

    # saver - функция, вызываемая каждую секунду и сохраняющая данные
    # timeout - через сколько вырубать обработку, period - длина одного тика
    def __init__(self,
                 saver=None,
                 storage=None,
                 timeout=23 * 60 * 60,
                 period=1.,
                 process_inside=True):
        self.timeout = timeout
        self.period = period
        self.manager = multiprocessing.Manager()

        # storage - куда мы сохраняем данные
        if storage is None:
            self.storage = self.manager.dict()
        else:
            self.storage = storage

        self.pairs = []
        self.streams = []
        self.init_streams()
        self.start_time = time.time()
        self.client = init_client()

        # Сокет, который все слушает (запускаем в отдельном процессе)
        self.main_socket = BinanceSocketManager(self.client)
        self.connection_key = self.main_socket.start_multiplex_socket(
            self.streams, self.general_callback)
        self.process_inside = process_inside
        if self.process_inside:
            self.socket_process = multiprocessing.Process(
                target=self.main_socket.run)
        self.data_saver = saver
        self.timer = RepeatTimer(self.period, self.give_data)
        logging.info('DataCatcher initialization successful')

    # Функция завершения работы
    def finish(self):
        logging.info('finishing listening')
        self.timer.cancel()
        self.main_socket.stop_socket(self.connection_key)
        self.main_socket.close()
        self.socket_process.terminate()

    # Функция передачи данных saver-у
    def give_data(self):

        # Смотри комментарии к kline_callback
        for pair in self.pairs:
            self.storage[pair + '_kline_time_since_update'] = time.time() * 1000 - \
                                                        self.storage[pair + '_kline_update_time']
        self.storage['time'] = time.time()

        self.data_saver.push_data(self.storage)

    def start(self):
        logging.info('started listening')
        if self.process_inside:
            self.socket_process.start()

        # Ждем 2 минуты, чтобы все успело прийти
        logging.info('waiting for 2 minutes')
        time.sleep(120.)
        logging.info('started transferring data')
        timeout_timer = threading.Timer(self.timeout, self.finish)
        timeout_timer.start()

        if self.data_saver is not None:
            self.timer.start()

        self.socket_process.join()
Beispiel #24
0
class BinanceConnector(Connector):

    BINANCE_ORDER_STATUS_FILLED = "FILLED"
    BINANCE_ORDER_STATUS_CANCELED = "CANCELED"

    def __init__(self):

        super().__init__()
        self.client: Client = Client("", "")
        self.socket = BinanceSocketManager(self.client)
        self.connected: bool = False

    def connect(self, key: str, secret: str) -> None:

        self.client = Client(api_key=key, api_secret=secret)
        self.socket = BinanceSocketManager(self.client)
        self.connected = True

    def start_listen(self):

        if not self.connected:
            return

        self.socket.start_user_socket(self.user_handler)
        self.socket.start_miniticker_socket(self.ticker_handler)
        self.socket.daemon = True
        self.socket.start()

    def user_handler(self, message: Dict):

        message_type = message["e"]
        if not message_type == "executionReport":
            return

        order_id = message["i"]
        binance_order_status = message["X"]

        if binance_order_status == BinanceConnector.BINANCE_ORDER_STATUS_FILLED:
            self.emit(
                UserEvent({
                    "external_id": order_id,
                    "status": BinanceConnector.ORDER_STATUS_FILLED
                }))
        elif binance_order_status == BinanceConnector.BINANCE_ORDER_STATUS_CANCELED:
            self.emit(
                UserEvent({
                    "external_id": order_id,
                    "status": BinanceConnector.ORDER_STATUS_CANCELED
                }))

    def ticker_handler(self, message: List[Dict]):

        for ticker in message:

            message_type = ticker["e"]
            if not message_type == "24hrMiniTicker":
                continue

        self.emit(TickerEvent({}))

    def stop_listen(self):

        self.socket.close()

    def satisfied(self, execution_order: SingleExecutionOrder) -> bool:

        execution_conditions = execution_order.conditions
        return True

    def submit(self, execution_order: SingleExecutionOrder) -> int:

        execution_params = execution_order.params

        side = self.client.SIDE_BUY
        if execution_params.command == ExecutionParams.CMD_SELL:
            side = self.client.SIDE_SELL

        params = {
            "newClientOrderId": execution_order.order_id,
            "symbol": execution_params.symbol,
            "quantity": execution_params.quantity,
            "side": side,
            "type": self.client.ORDER_TYPE_MARKET
        }

        if not execution_params.price == 0:
            params["type"] = self.client.ORDER_TYPE_LIMIT
            params["price"] = execution_params.price
            params["timeInForce"] = self.client.TIME_IN_FORCE_GTC

        try:
            binance_order = self.client.create_order(**params)
        except (BinanceRequestException, BinanceOrderException,
                BinanceAPIException) as exception:
            raise ConnectorException(exception.message, execution_order)

        return binance_order["orderId"]

    def is_filled(self, execution_order: SingleExecutionOrder) -> bool:

        try:
            binance_order = self.client.get_order(
                symbol=execution_order.params.symbol,
                orderId=execution_order.external_id)
        except (BinanceRequestException, BinanceAPIException) as exception:
            raise ConnectorException(exception.message, execution_order)

        return binance_order["status"] == self.client.ORDER_STATUS_FILLED

    def cancel(self, execution_order: SingleExecutionOrder) -> None:

        try:
            self.client.cancel_order(symbol=execution_order.params.symbol,
                                     orderId=execution_order.external_id)
        except (BinanceRequestException, BinanceAPIException) as exception:
            raise ConnectorException(exception.message, execution_order)
class BinanceExchange(Exchange):
    exchange_name = "Binance"
    isMargin = False

    def __init__(self, apiKey, apiSecret, pairs, name):
        super().__init__(apiKey, apiSecret, pairs, name)

        self.connection = Client(self.api['key'], self.api['secret'])

        symbol_info_arr = self.connection.get_exchange_info()
        dict_symbols_info = {
            item['symbol']: item
            for item in symbol_info_arr["symbols"]
        }
        actual_symbols_info = {
            symbol: dict_symbols_info[symbol]
            for symbol in self.pairs
        }
        self.symbols_info = actual_symbols_info

        self.update_balance()
        self.socket = BinanceSocketManager(self.connection)
        self.socket.start_user_socket(self.on_balance_update)
        self.socket.start()
        self.is_last_order_event_completed = True
        self.step_sizes = {}
        self.balance_updated = True

        for symbol_info in symbol_info_arr['symbols']:
            if symbol_info['symbol'] in self.pairs:
                self.step_sizes[symbol_info['symbol']] = \
                    [f['stepSize'] for f in symbol_info['filters'] if f['filterType'] == 'LOT_SIZE'][0]

    def start(self, caller_callback):
        self.socket.start_user_socket(caller_callback)

    def update_balance(self):
        account_information = self.connection.get_account()
        self.set_balance(account_information['balances'])

    def get_trading_symbols(self):
        symbols = set()
        if not self.symbols_info:
            raise RuntimeError("Cant get exchange info")
        for key, value in self.symbols_info.items():
            symbols.add(value["quoteAsset"])
            symbols.add(value["baseAsset"])
        return symbols

    def set_balance(self, balances):
        symbols = self.get_trading_symbols()
        dict_balances = {item['asset']: item for item in balances}
        actual_balance = {symbol: dict_balances[symbol] for symbol in symbols}
        self.balance = actual_balance

    def on_balance_update(self, upd_balance_ev):
        if upd_balance_ev['e'] == 'outboundAccountPosition':
            balance = []
            for ev in upd_balance_ev['B']:
                balance.append({
                    'asset': ev['a'],
                    'free': ev['f'],
                    'locked': ev['l']
                })
            self.balance.update({item['asset']: item for item in balance})

    def get_open_orders(self):
        orders = self.connection.get_open_orders()
        general_orders = []
        for o in orders:
            quantityPart = self.get_part(o['symbol'], o["origQty"], o['price'],
                                         o['side'])
            general_orders.append(
                Order(o['price'], o["origQty"], quantityPart, o['orderId'],
                      o['symbol'], o['side'], o['type'], self.exchange_name))
        return general_orders

    def _cancel_order(self, order_id, symbol):
        self.connection.cancel_order(symbol=symbol, orderId=order_id)
        self.logger.info(f'{self.name}: Order canceled')

    async def on_cancel_handler(self, event: Actions.ActionCancel):
        try:
            slave_order_id = self._cancel_order_detector(event.price)
            self._cancel_order(slave_order_id, event.symbol)
        except BinanceAPIException as error:
            self.logger.error(f'{self.name}: error {error.message}')
        except:
            self.logger.error(
                f"{self.name}: error in action: {event.name} in slave {self.name}"
            )

    def stop(self):
        self.socket.close()

    def _cancel_order_detector(self, price):
        # detect order id which need to be canceled
        slave_open_orders = self.connection.get_open_orders()
        for ordr_open in slave_open_orders:
            if float(ordr_open['price']) == float(price):
                return ordr_open['orderId']

    def process_event(self, event):
        # return event in generic type from websocket

        # if this event in general type it was send from start function and need call firs_copy
        if 'exchange' in event:
            return event

        if event['e'] == 'outboundAccountPosition':
            self.on_balance_update(event)

        elif event['e'] == 'executionReport':
            if event['X'] == 'FILLED':
                return
            elif event['x'] == 'CANCELED':
                return Actions.ActionCancel(event['s'], event['p'], event['i'],
                                            self.exchange_name, event)
            elif event['X'] == 'NEW':
                order_event = event

                if order_event['s'] not in self.pairs:
                    return

                if order_event[
                        'o'] == 'MARKET':  # if market order, we haven't price and cant calculate quantity
                    order_event['p'] = self.connection.get_ticker(
                        symbol=order_event['s'])['lastPrice']

                # part = self.get_part(order_event['s'], order_event['q'], order_event['p'], order_event['S'])

                # shortcut mean https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#order-update
                order = Order(
                    order_event['p'], order_event['q'],
                    self.get_part(order_event['s'], order_event['q'],
                                  order_event['p'], order_event['S']),
                    order_event['i'], order_event['s'], order_event['S'],
                    order_event['o'], self.exchange_name, order_event['P'])
                return Actions.ActionNewOrder(order, self.exchange_name, event)
            return

    async def on_order_handler(self, event: Actions.ActionNewOrder):
        self.create_order(event.order)

    def create_order(self, order):
        """
        :param order:
        """
        quantity = self.calc_quantity_from_part(order.symbol,
                                                order.quantityPart,
                                                order.price, order.side)
        self.logger.info('Slave ' + self.name + ' ' +
                         str(self._get_quote_balance(order.symbol)) + ' ' +
                         str(self._get_base_balance(order.symbol)) +
                         ', Create Order:' + ' amount: ' + str(quantity) +
                         ', price: ' + str(order.price))
        try:
            if order.type == 'STOP_LOSS_LIMIT' or order.type == "TAKE_PROFIT_LIMIT":
                self.connection.create_order(symbol=order.symbol,
                                             side=order.side,
                                             type=order.type,
                                             price=order.price,
                                             quantity=quantity,
                                             timeInForce='GTC',
                                             stopPrice=order.stop)
            if order.type == 'MARKET':
                self.connection.create_order(symbol=order.symbol,
                                             side=order.side,
                                             type=order.type,
                                             quantity=quantity)
            else:
                self.connection.create_order(symbol=order.symbol,
                                             side=order.side,
                                             type=order.type,
                                             quantity=quantity,
                                             price=order.price,
                                             timeInForce='GTC')
            self.logger.info(f"{self.name}: order created")
        except Exception as e:
            self.logger.error(str(e))

    def _get_quote_balance(self, symbol):
        return self.balance[self.symbols_info[symbol]['quoteAsset']]

    def _get_base_balance(self, symbol):
        return self.balance[self.symbols_info[symbol]['baseAsset']]

    def get_part(self, symbol: str, quantity: float, price: float, side: str):
        # get part of the total balance of this coin

        # if order[side] == sell: need obtain coin balance
        if side == 'BUY':
            get_context_balance = self._get_quote_balance
            market_value = float(quantity) * float(price)
        else:
            get_context_balance = self._get_base_balance
            market_value = float(quantity)

        balance = float(get_context_balance(symbol)['free'])

        # if first_copy the balance was update before
        if self.balance_updated:
            balance += float(get_context_balance(symbol)['locked'])
        # else:
        #     balance += market_value

        part = market_value / balance
        part = part * 0.99  # decrease part for 1% for avoid rounding errors in calculation
        return part

    def calc_quantity_from_part(self, symbol, quantityPart, price, side):
        # calculate quantity from quantityPart

        # if order[side] == sell: need obtain coin balance

        if side == 'BUY':
            get_context_balance = self._get_quote_balance
            buy_koef = float(price)
        else:
            get_context_balance = self._get_base_balance
            buy_koef = 1

        cur_bal = float(get_context_balance(symbol)['free'])

        if self.balance_updated:
            cur_bal += float(get_context_balance(symbol)['locked'])

        quantity = quantityPart * cur_bal / buy_koef

        stepSize = float(self.step_sizes[symbol])
        precision = int(round(-math.log(stepSize, 10), 0))
        quantity = round(quantity, precision)
        return quantity
Beispiel #26
0
class BinanceAPIManagerNew(BinanceAPIManager):
    def __init__(self, config: ConfigNew, db: Database, logger: Logger):
        self.db = db
        self.logger = logger
        self.config = config
        api_key = self.config.BINANCE_API_KEY
        api_secret = self.config.BINANCE_API_SECRET_KEY
        self.binance_client = BinanceClientNew(api_key, api_secret, config,
                                               logger)
        if self.binance_client.is_client_ok:
            self.socket_manager = BinanceSocketManager(self.binance_client)
            self.binance_client.socket_manager = self.socket_manager

    def start_multiplex_socket(self):
        conn_key = self.binance_client.start_multiplex_socket()
        return conn_key

    def start_user_socket(self):
        conn_key = self.binance_client.start_user_socket()
        return conn_key

    def start_sock_manager(self):
        self.socket_manager.start()
        self.logger.info("BinanceSocketManager started")

    def stop_socket(self):
        self.binance_client.stop_multiplex_socket()
        self.binance_client.stop_user_socket()

    def stop_sock_manager(self):
        self.stop_socket()
        self.socket_manager.close()
        reactor.stop()
        self.logger.info("BinanceSocketManager closed")

    def get_all_market_tickers(self) -> AllTickers:
        """
        Get ticker price of all coins
        """
        # return super().get_all_market_tickers()
        return AllTickers(self.binance_client.get_all_tickers())

    def get_all_balances(self):
        try:
            account_info = self.binance_client.get_account()
        except BinanceAPIException as e:
            self.logger.error(f"Error in get_all_balances(): {e}")
            if e.code == -1003:
                pass
            return None

        return account_info["balances"]

    def get_currency_balance(self, currency_symbol: str):
        """
        Get balance of a specific coin
        """
        try:
            account_info = self.binance_client.get_account()
        except BinanceAPIException as e:
            self.logger.error(f"Error in get_currency_balance(): {e}")
            if e.code == -1003:
                pass
            return None

        for currency_balance in account_info["balances"]:
            if currency_balance["asset"] == currency_symbol:
                return float(currency_balance["free"])
        return None

    def get_market_ticker_price(self, ticker_symbol: str):
        """
        Get ticker price of a specific coin
        """
        for ticker in self.binance_client.get_symbol_ticker():
            if ticker["symbol"] == ticker_symbol:
                return float(ticker["price"])
        return None

    def delete_old_data(self, file_dir, file_cnt=1):
        self.binance_client.delete_old_data(file_dir, file_cnt=file_cnt)
class BinanceInfluxdb():
    
    def __init__(self,symbol = "IOTAUSDT",is_new_db = False,database ='binance',measurement= 'minute_tick',host='localhost', port=8086,api_key = "api_key",api_secret = "private_api_key" ):    
    
        self.database = database
        self.client = Client(api_key, api_secret)
        self.InfluxClient = InfluxDBClient(host='localhost', port=8086)
        
        if is_new_db:
            self.InfluxClient.create_database(self.database) 
        
        self.InfluxClient.switch_database(self.database)  
        self.InfluxClient.create_retention_policy(name='coin_policy',duration='INF',database=self.database,replication=1, default=True, shard_duration='4w')
        self.symbol = symbol
        self.measurement_name = measurement
        self.need_data_actualization = True
        
    def online_process_message(self,msg):
        
        measurement = self.measurement_name
        msg_type = "raw"
        self.insert_data_point_influxdb(msg,measurement,msg_type)
        
        if self.need_data_actualization:
            print("##############################one time update #####################################")
            current_time_num=msg["k"]["t"]/1000.0
            self.get_previous_point(current_time_num)
            self.need_data_actualization = False               
            
    def get_previous_point(self,current_time_num):

        count = 1    
      #  current_time_num=previous_msg["k"]["t"]/1000.0
        pre_previous_time = zulu.parse(current_time_num-60).isoformat() #I rest 60 seconds
        
        pre_previous_time_query=self.InfluxClient.query("select * from {2} WHERE time = '{0}' AND pair = '{1}';".format(pre_previous_time,self.symbol,self.measurement_name))
        
        while not len(list(pre_previous_time_query)):
            count = count+1
            pre_previous_time_loopty_loop = zulu.parse(current_time_num-count*60).isoformat()
            pre_previous_time_query=self.InfluxClient.query("select * from {2} WHERE time = '{0}' AND pair = '{1}';".format(pre_previous_time_loopty_loop,self.symbol,self.measurement_name))
            print("testing_time: {0}, count:".format(pre_previous_time_loopty_loop),count)

    
    
        event_type = 'kline'
        interval ='1m'     
        units = 'minute'
        num_of_units = count+2 #Just in case I added 2 units more
        msg_type = 'raw'
        self.insert_offline_tick_data(event_type,interval,units,num_of_units,msg_type)
    
    def insert_data_point_influxdb(self,msg,measurement,msg_type):

        if msg_type == 'raw':
            json_body4 = [
            {
                    "measurement": measurement,
                    "tags": {
                        "event_type": msg["e"],
                        "base_currency": msg["s"][:int(len(msg["s"])/2)],
                        "quote_currency": msg["s"][int(len(msg["s"])/2):],
                        "pair": msg["s"],
                        "interval": msg["k"]["i"]
                    },
                    "time": zulu.parse(msg["k"]["t"]/1000.0).isoformat(),#datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),#+'Z',
                    "fields": {
                        "open": float(msg["k"]["o"]),
                        "close":float(msg["k"]["c"]),
                        "high":float(msg["k"]["h"]),
                        "low":float(msg["k"]["l"]),
                        "high-low":float(msg["k"]["h"])-float(msg["k"]["l"]),
                        "close-open":float(msg["k"]["c"])-float(msg["k"]["o"]),
                        "volume":float(msg["k"]["v"]), #Base asset volume
                        "number_of_trades":int(msg["k"]["n"]),
                        "quote_volume":float(msg["k"]["q"]), #Quote asset volume
                        "active_buy_volume":float(msg["k"]["V"]), #Taker buy base asset volume
                        "active_buy_quote_volume":float(msg["k"]["Q"]), #Taker buy quote asset volume
                        "gain":-1000,
                        "lose":-1000,
                        "avg_gain":-1000,
                        "avg_lose":-1000,
                        "RSI":-1000,
                        "MACD":-1000,
                        "KDJ":-1000,
                        "DMI":-1000,
                        "OBV":-1000,
                        "MTM":-1000,
                        "EMA":-1000,
                        "VWAP":-1000,
                        "AVL":-1000,
                        "TRIX":-1000,
                        "StochRSI":-1000,
                        "EMV":-1000,
                        "WR":-1000,
                        "BOLL":-1000,
                        "SAR":-1000,
                        "CCI":-1000,
                        "MA":-1000,
                        "VOL":-1000
                    
                    
                    }
                }
            ]    
        self.InfluxClient.write_points(points=json_body4,retention_policy='coin_policy')
        print("inserting message with time: {0}, message type: {1}, measurement: {2}".format(json_body4[0]["time"],msg_type,measurement))

    def create_msg_from_history(self,event_type,interval,symbol,units,num_of_units,from_now=True,from_date=0,to_date=0): #update this to accept more ranges
        
        if from_now:
        #http://dateparser.readthedocs.io/en/latest/
            if units == 'hour':        
                raw_data=self.client.get_historical_klines(symbol, interval, "{0} hour ago UTC".format(num_of_units))
            elif units == 'day':
                raw_data=self.client.get_historical_klines(symbol, interval, "{0} day ago UTC".format(num_of_units))        
            elif units == 'week':
                raw_data=self.client.get_historical_klines(symbol, interval, "{0} week ago UTC".format(num_of_units))     
            elif units == 'month':
                raw_data=self.client.get_historical_klines(symbol, interval, "{0} month ago UTC".format(num_of_units))
            elif units == 'minute':
                raw_data=self.client.get_historical_klines(symbol, interval, "{0} minute ago UTC".format(num_of_units))         
    
        else:
            raw_data=self.client.get_historical_klines(symbol, interval,from_date,to_date)
            #klines = client.get_historical_klines("ETHBTC", Client.KLINE_INTERVAL_30MINUTE, "1 Dec, 2017", "1 Jan, 2018")
   
        list_of_msgs = list(np.zeros(len(raw_data),dtype=object))


        for i, raw_msg in enumerate(raw_data):
       
            if i%10000 == 0:
                print(i)
           
            list_of_msgs[i] =  {
                    "measurement": self.measurement_name,
                    "tags": {
                        "event_type": event_type,
                        "base_currency": symbol[:int(len(symbol)/2)],
                        "quote_currency": symbol[int(len(symbol)/2):],
                        "pair": symbol ,
                        "interval": interval
                    },
                    "time": zulu.parse(raw_msg[0]/1000.0).isoformat(),#datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),#+'Z',
                    "fields": {
                        "open": float(raw_msg[1]),
                        "close":float(raw_msg[4]),
                        "high":float(raw_msg[2]),
                        "low":float(raw_msg[3] ),
                        "high-low":float(raw_msg[2])-float(raw_msg[3]),
                        "close-open":float(raw_msg[4])-float(raw_msg[1]),
                        "volume":float(raw_msg[5]), #Base asset volume
                        "number_of_trades":int(raw_msg[8] ),
                        "quote_volume":float(raw_msg[7] ), #Quote asset volume
                        "active_buy_volume":float(raw_msg[9] ), #Taker buy base asset volume
                        "active_buy_quote_volume":float(raw_msg[10]), #Taker buy quote asset volume
                        "gain":-1000,
                        "lose":-1000,
                        "avg_gain":-1000,
                        "avg_lose":-1000,
                        "RSI":-1000,                   
                        "MACD":-1000,
                        "KDJ":-1000,
                        "DMI":-1000,
                        "OBV":-1000,
                        "MTM":-1000,
                        "EMA":-1000,
                        "VWAP":-1000,
                        "AVL":-1000,
                        "TRIX":-1000,
                        "StochRSI":-1000,
                        "EMV":-1000,
                        "WR":-1000,
                        "BOLL":-1000,
                        "SAR":-1000,
                        "CCI":-1000,
                        "MA":-1000,
                        "VOL":-1000                    
                    
                    }
                }
        return list_of_msgs      

    def insert_offline_data(self,list_of_msgs,measurement,msg_type):   
        
        self.InfluxClient.write_points(points=list_of_msgs,retention_policy='coin_policy')
        
    def insert_offline_tick_data(self,event_type,interval,units,num_of_units,msg_type,from_now=True,from_date=0,to_date=0):
        '''
        event_type = 'kline'
        interval ='1m'     
        symbol = "IOTAUSDT"
        units = 'hour'
        num_of_units = 24
        measurement_off ="offline_minute_tick"
        msg_type = 'raw'
        from_now = False
        from_date = "26Jun, 2018"     
        to_date = "27 Jun, 2018"  
        '''
        measurement =self.measurement_name
        symbol = self.symbol
        # forced symbol to be self.symbol because it has to be coherent to the websocket type of messages, this is not pretty but it works.
        list_of_msgs = self.create_msg_from_history(event_type,interval,symbol,units,num_of_units,from_now,from_date,to_date)
        self.insert_offline_data(list_of_msgs,measurement,msg_type)  
        return list_of_msgs      

    def websocket_start(self):
        
        self.bm =BinanceSocketManager(self.client)
        self.bm.start_kline_socket( self.symbol, self.online_process_message)
        self.bm.start()
        
    def websocket_close(self):        
        self.bm.close()
class BinanceStore(with_metaclass(MetaSingleton, object)):
    _GRANULARITIES = {
        (TimeFrame.Minutes, 1): '1m',
        (TimeFrame.Minutes, 3): '3m',
        (TimeFrame.Minutes, 5): '5m',
        (TimeFrame.Minutes, 15): '15m',
        (TimeFrame.Minutes, 30): '30m',
        (TimeFrame.Minutes, 60): '1h',
        (TimeFrame.Minutes, 120): '2h',
        (TimeFrame.Minutes, 240): '4h',
        (TimeFrame.Minutes, 360): '6h',
        (TimeFrame.Minutes, 480): '8h',
        (TimeFrame.Minutes, 720): '12h',
        (TimeFrame.Days, 1): '1d',
        (TimeFrame.Days, 3): '3d',
        (TimeFrame.Weeks, 1): '1w',
        (TimeFrame.Months, 1): '1M'
    }

    BrokerCls = None  # Broker class will autoregister
    DataCls = None  # Data class will auto register

    @classmethod
    def getdata(cls, *args, **kwargs):
        """Returns ``DataCls`` with args, kwargs"""
        return cls.DataCls(*args, **kwargs)

    @classmethod
    def getbroker(cls, *args, **kwargs):
        """Returns broker with *args, **kwargs from registered ``BrokerCls``"""
        return cls.BrokerCls(*args, **kwargs)

    def __init__(self,
                 api_key,
                 api_secret,
                 coin_refer,
                 coin_target,
                 retries=5):
        self.binance = Client(api_key, api_secret)
        self.binance_socket = BinanceSocketManager(self.binance)
        self.coin_refer = coin_refer
        self.coin_target = coin_target
        self.symbol = coin_refer + coin_target
        self.retries = retries

        self.step_size = None
        self.tick_size = None
        self.get_filters()

        self._cash = 0
        self._value = 0
        self.get_balance()

    def _format_value(self, value, step):
        precision = step.find('1') - 1
        if precision > 0:
            return '{:0.0{}f}'.format(value, precision)
        return floor(int(value))

    def retry(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            for attempt in range(1, self.retries + 1):
                time.sleep(60 / 1200)  # API Rate Limit
                try:
                    return func(self, *args, **kwargs)
                except (BinanceAPIException, ConnectTimeout,
                        ConnectionError) as err:
                    if isinstance(err,
                                  BinanceAPIException) and err.code == -1021:
                        # Recalculate timestamp offset between local and Binance's server
                        res = self.binance.get_server_time()
                        self.binance.timestamp_offset = res[
                            'serverTime'] - int(time.time() * 1000)

                    if attempt == self.retries:
                        raise

        return wrapper

    @retry
    def cancel_open_orders(self):
        orders = self.binance.get_open_orders(symbol=self.symbol)
        for o in orders:
            self.cancel_order(o['orderId'])

    @retry
    def cancel_order(self, order_id):
        try:
            self.binance.cancel_order(symbol=self.symbol, orderId=order_id)
        except BinanceAPIException as api_err:
            if api_err.code == -2011:  # Order filled
                return
            else:
                raise api_err
        except Exception as err:
            raise err

    @retry
    def create_order(self, side, type, size, price):
        params = dict()
        if type in [ORDER_TYPE_LIMIT, ORDER_TYPE_STOP_LOSS_LIMIT]:
            params.update({'timeInForce': TIME_IN_FORCE_GTC})
        if type != ORDER_TYPE_MARKET:
            params.update({'price': self.format_price(price)})

        return self.binance.create_order(symbol=self.symbol,
                                         side=side,
                                         type=type,
                                         quantity=self.format_quantity(size),
                                         **params)

    def format_price(self, price):
        return self._format_value(price, self.tick_size)

    def format_quantity(self, size):
        return self._format_value(size, self.step_size)

    @retry
    def get_asset_balance(self, asset):
        balance = self.binance.get_asset_balance(asset)
        return float(balance['free']), float(balance['locked'])

    def get_balance(self):
        free, locked = self.get_asset_balance(self.coin_target)
        self._cash = free
        self._value = free + locked

    def get_filters(self):
        symbol_info = self.get_symbol_info(self.symbol)
        for f in symbol_info['filters']:
            if f['filterType'] == 'LOT_SIZE':
                self.step_size = f['stepSize']
            elif f['filterType'] == 'PRICE_FILTER':
                self.tick_size = f['tickSize']

    def get_interval(self, timeframe, compression):
        return self._GRANULARITIES.get((timeframe, compression))

    @retry
    def get_symbol_info(self, symbol):
        return self.binance.get_symbol_info(symbol)

    def start_socket(self):
        if self.binance_socket.is_alive():
            return
        self.binance_socket.daemon = True
        self.binance_socket.start()

    def stop_socket(self):
        self.binance_socket.close()
        reactor.stop()
Beispiel #29
0
class BinanceStream(AggregateWebsocketApi):
    TAG = ExchangeAbbr.BINANCE

    def __init__(self, api):
        super(BinanceStream, self).__init__(api)
        self._socket_manager: BinanceSocketManager = None

    def subscribe_bar(self, symbol, frequency):
        symbol_exchange = self.api.contracts[symbol].symbol_exchange
        self._socket_manager.start_kline_socket(
            symbol_exchange, self._on_bar_impl(symbol, frequency), frequency)

    def subscribe_depth(self, symbol):
        symbol_exchange = self.api.contracts[symbol].symbol_exchange
        self._socket_manager.start_depth_socket(
            symbol_exchange, self._on_depth_impl(symbol),
            binance.enums.WEBSOCKET_DEPTH_5)

    def _on_bar_impl(self, symbol, frequency):
        bar = BarData()
        bar.symbol = symbol
        bar.frequency = frequency
        bar.datetime = None
        self._bar_dict[(symbol, frequency)] = bar

        def callback(packet):
            origin_dt = bar.datetime
            kline = packet['k']
            dt = datetime.datetime.utcfromtimestamp(kline['t'] / 1000.0)
            if origin_dt is not None and dt > origin_dt:
                self.api.on_bar(copy.copy(bar))
            self._parse_bar(packet, bar)
            bar.datetime = dt

        return callback

    def _parse_bar(self, packet, bar: BarData):
        kline = packet['k']
        bar.open = float(kline['o'])
        bar.high = float(kline['h'])
        bar.low = float(kline['l'])
        bar.close = float(kline['c'])
        bar.volume = float(kline['v'])

    def _parse_depth(self, packet, depth: DepthData):
        depth.datetime = datetime.datetime.utcnow()
        bids = packet['bids']
        for index, (price, volume) in enumerate(bids[:DepthData.N_DEPTH]):
            depth.bid_prices[index] = float(price)
            depth.bid_volumes[index] = float(volume)
        asks = packet['asks']
        for index, (price, volume) in enumerate(asks[:DepthData.N_DEPTH]):
            depth.ask_prices[index] = float(price)
            depth.ask_volumes[index] = float(volume)

    def start(self):
        self._socket_manager = BinanceSocketManager(
            Client(self.api.api_key, self.api.api_secret))
        self.on_connected()
        self.back_fill()
        self._socket_manager.start()

    def stop(self):
        self._socket_manager.close()
        import twisted.internet.error
        from twisted.internet import reactor
        try:
            reactor.stop()
        except twisted.internet.error.ReactorNotRunning:
            pass

    def subscribe_user_stream(self):
        self._socket_manager.start_user_socket(self.on_user_stream)

    def on_user_stream(self, data):
        if data['e'] == 'executionReport':
            logger.debug(f'{self} received raw data {data}')
            if data['C'] != 'null':
                cli_id = data['C']
            else:
                cli_id = data['c']

            order = self.api.oms.clone(cli_id)
            if order is None:
                return

            order.order_id = str(data['i'])
            order.executed_volume = float(data['z'])
            order.executed_notional = float(data['Z'])
            order.status = STATUS_MAP_REVERSE[data['X']]

            if float(data['l']):
                trade = TradeData.from_order(order)
                trade.trade_id = str(data['t'])
                trade.price = float(data['L'])
                trade.volume = float(data['l'])
                trade.commission = float(data['n'])
                trade.commission_asset = '.'.join((data['N'], self.TAG))
                trade.datetime = datetime.datetime.utcfromtimestamp(data['E'] /
                                                                    1000.0)
                trade.strategy_id = order.strategy_id
                trade.client_order_id = order.client_order_id

                contract = trade.contract
                if trade.commission != 0 and trade.commission_asset == 'BNB.BNC':
                    if (trade.commission_asset != contract.asset_base) and \
                            (trade.commission_asset != contract.asset_quote):

                        commission_trade = trade.copy()
                        commission_trade.commission = 0.0
                        commission_trade.symbol = 'BNB' + contract.symbol_quote + '.BNC'
                        commission_trade.direction = EnumOrderDirection.SELL

                        notional = trade.volume * trade.price * 0.00075
                        commission_trade.price = notional / trade.commission
                        commission_trade.volume = notional / commission_trade.price
                        commission_trade.trade_id = generate_id()
                        self.api.on_trade(commission_trade)

                        trade.commission_asset = contract.asset_quote
                        trade.commission = notional

                self.api.on_trade(trade)
                logger.debug("%s: receive %s", self, trade)

            self.api.on_order(order)
            if order.is_closed():
                self.api.oms.pop(order.client_order_id)

            logger.debug("%s: receive %s", self, order)
Beispiel #30
0
def main():
    #READ API CONFIG
    api_config = {}
    with open('api_config.json') as json_data:
        api_config = json.load(json_data)
        json_data.close()

    TOKEN = api_config['telegram_bot_token']
    tb = telebot.TeleBot(TOKEN)	#create a new Telegram Bot object

    def send_message(chat_id, msg):
        try:
            tb.send_message(chat_id, msg)
        except:
            pass

    
    def send_to_all_chat_ids(msg):
        for chat_id in chat_ids:
            send_message(chat_id, msg)

    @tb.message_handler(commands=['start', 'help'])
    def send_welcome(message):
        set_chat_id(message.chat.id)
        tb.reply_to(message, "Welcome to BinancePump Bot, Binance Top Tick Count, Top Price and Volume Change Feeds will be shared with you. One of it could be start of pump or dump, keep an eye on me!")

    def process_message(tickers):
        # print("stream: {} data: {}".format(msg['stream'], msg['data']))
        # print("Len {}".format(len(msg)))
        # print("Currentb Price Of {} is {}".format(msg[0]['s'], msg[0]['c']))
        
        for ticker in tickers:
            symbol = ticker['s']

            if not show_only_pair in symbol:
                continue

            price = float(ticker['c'])
            total_trades = int(ticker['n'])
            open = float(ticker['o'])
            volume = float(ticker['v'])
            event_time = dt.datetime.fromtimestamp(int(ticker['E'])/1000)
            if len(price_changes) > 0:
                price_change = filter(lambda item: item.symbol == symbol, price_changes)
                price_change = list(price_change)
                if (len(price_change) > 0):
                    price_change = price_change[0]
                    price_change.event_time = event_time
                    price_change.prev_price = price_change.price
                    price_change.prev_volume = price_change.volume
                    price_change.price = price
                    price_change.total_trades = total_trades
                    price_change.open = open
                    price_change.volume = volume
                    price_change.isPrinted = False
                else:
                    price_changes.append(PriceChange(symbol, price, price, total_trades, open, volume, False, event_time, volume))
            else:
                price_changes.append(PriceChange(symbol, price, price, total_trades, open, volume, False, event_time, volume))

        price_changes.sort(key=operator.attrgetter('price_change_perc'), reverse=True)
        #print(len(price_changes))
        
        for price_change in price_changes:
            console_color = 'green'
            if price_change.price_change_perc < 0:
                console_color = 'red'

            if (not price_change.isPrinted 
                and abs(price_change.price_change_perc) > min_perc 
                and price_change.volume_change_perc > min_perc):

                price_change.isPrinted = True 
                
                if not price_change.symbol in price_groups:
                    price_groups[price_change.symbol] = PriceGroup(price_change.symbol,                                                                
                                                                1,                                                                
                                                                abs(price_change.price_change_perc),
                                                                price_change.price_change_perc,
                                                                price_change.volume_change_perc,                                                                
                                                                price_change.price,                                                                                                                             
                                                                price_change.event_time,
                                                                price_change.open,
                                                                price_change.volume,
                                                                False,
                                                                )
                else:
                    price_groups[price_change.symbol].tick_count += 1
                    price_groups[price_change.symbol].last_event_time = price_change.event_time
                    price_groups[price_change.symbol].volume = price_change.volume
                    price_groups[price_change.symbol].last_price = price_change.price
                    price_groups[price_change.symbol].isPrinted = False
                    price_groups[price_change.symbol].total_price_change += abs(price_change.price_change_perc)
                    price_groups[price_change.symbol].relative_price_change += price_change.price_change_perc
                    price_groups[price_change.symbol].total_volume_change += price_change.volume_change_perc                

        if len(price_groups)>0:
            anyPrinted = False 
            sorted_price_group = sorted(price_groups, key=lambda k:price_groups[k]['tick_count'])
            if (len(sorted_price_group)>0):
                sorted_price_group = list(reversed(sorted_price_group))
                for s in range(show_limit):
                    header_printed=False
                    if (s<len(sorted_price_group)):
                        max_price_group = sorted_price_group[s]
                        max_price_group = price_groups[max_price_group]
                        if not max_price_group.isPrinted:
                            if not header_printed:
                                msg = "Top Ticks"
                                print(msg)
                                send_to_all_chat_ids(msg)
                                header_printed = True
                            print(max_price_group.to_string(True))
                            send_to_all_chat_ids(max_price_group.to_string(False))
                            anyPrinted = True

            sorted_price_group = sorted(price_groups, key=lambda k:price_groups[k]['total_price_change'])
            if (len(sorted_price_group)>0):
                sorted_price_group = list(reversed(sorted_price_group))
                for s in range(show_limit):
                    header_printed=False
                    if (s<len(sorted_price_group)):
                        max_price_group = sorted_price_group[s]
                        max_price_group = price_groups[max_price_group]
                        if not max_price_group.isPrinted:
                            if not header_printed:
                                msg = "Top Total Price Change"
                                print(msg)
                                send_to_all_chat_ids(msg)
                                header_printed = True
                            print(max_price_group.to_string(True))
                            send_to_all_chat_ids(max_price_group.to_string(False))
                            anyPrinted = True

            sorted_price_group = sorted(price_groups, key=lambda k:abs(price_groups[k]['relative_price_change']))
            if (len(sorted_price_group)>0):
                sorted_price_group = list(reversed(sorted_price_group))
                for s in range(show_limit):
                    header_printed=False
                    if (s<len(sorted_price_group)):
                        max_price_group = sorted_price_group[s]
                        max_price_group = price_groups[max_price_group]
                        if not max_price_group.isPrinted:
                            if not header_printed:
                                msg = "Top Relative Price Change"
                                print(msg)
                                send_to_all_chat_ids(msg)
                                header_printed = True
                            print(max_price_group.to_string(True))
                            send_to_all_chat_ids(max_price_group.to_string(False))
                            anyPrinted = True

            sorted_price_group = sorted(price_groups, key=lambda k:price_groups[k]['total_volume_change'])
            if (len(sorted_price_group)>0):
                sorted_price_group = list(reversed(sorted_price_group))
                for s in range(show_limit):
                    header_printed=False
                    if (s<len(sorted_price_group)):
                        max_price_group = sorted_price_group[s]
                        max_price_group = price_groups[max_price_group]
                        if not max_price_group.isPrinted:
                            if not header_printed:
                                msg = "Top Total Volume Change"
                                print(msg)
                                send_to_all_chat_ids(msg)
                                header_printed = True
                            print(max_price_group.to_string(True))
                            send_to_all_chat_ids(max_price_group.to_string(False))
                            anyPrinted = True

            if anyPrinted:
                print("")

    client = Client(api_config['api_key'], api_config['api_secret'])
    # prices = client.get_all_tickers()
    # pairs = list(pd.DataFrame(prices)['symbol'].values)
    # pairs = [pair for pair in pairs if 'BTC' in pair]
    # print(pairs)    
          
    bm = BinanceSocketManager(client)
    conn_key = bm.start_ticker_socket(process_message)
    bm.start()
    print('bm socket started')

    tb.polling()
    print('tb socket started')
    
    input("Press Enter to continue...")
    bm.stop_socket(conn_key)
    bm.close()
    print('Socket Closed')
    return