def test_build_websocket_url_w_heartbeat(mocker):
    connect_websocket = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.connect_websocket')
    socket = GdaxWebsocket()
    socket.heartbeatEnabled = True
    url = socket.build_websocket_url('https://testnet.bitmex.com/api/v1/')

    assert url == 'wss://testnet.bitmex.com/realtime?heartbeat=true'
def test_connect_should_connect_ws(mocker):
    connect_websocket = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.connect_websocket')

    socket = GdaxWebsocket()
    socket.connect()

    connect_websocket.assert_called_once()
def test_subscribe_action_handler(mocker):
    """
    When calling GdaxWebsocket.subscribe_action(), ensure a proper subscriptionMsg
    message is sent and a subscription event is received when the channel is
    subscribed.
    """
    send_message_mock = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.send_message')
    on_subscribe_mock = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.on_subscribe')
    socket = GdaxWebsocket()
    message = {
        "success": "true",
        "subscribe": "instrument:XBTH17",
        "request": {
            "op": "subscribe",
            "args": ["instrument:XBTH17"]
        }
    }
    partial_action_handler = mocker.stub()
    socket.subscribe_action('partial', 'orderBookL2', 'XBTH17',
                            partial_action_handler)
    socket.on_message(json.dumps(message))
    send_message_mock.assert_called_once()
    on_subscribe_mock.assert_called_once()
    partial_message = orderBookL2_data['partial']
    socket.on_message(json.dumps(partial_message))
    partial_action_handler.assert_called_once_with(partial_message)
def test_on_subscribe_called_on_sub_error_message(mocker):
    '''
    on_message should call on_subscribe when subscription error is received
    '''
    error = mocker.patch('gdax_websocket.websocket.GdaxWebsocket.error')
    socket = GdaxWebsocket()
    message = {
        "status": 400,
        "error": "Unknown table: instrument_",
        "meta": {},
        "request": {
            "op": "subscribe",
            "args": ["instrument_:XBTH17"]
        }
    }

    socket.on_message(json.dumps(message))
    error.assert_called_with("Unknown table: instrument_")
Beispiel #5
0
    def __init__(self,
                 symbol='BTC-USD',
                 channels=[],
                 shouldAuth=False,
                 max_table_length=constants.MAX_TABLE_LEN,
                 websocket=None):

        EventEmitter.__init__(self)
        self.channels = channels

        if max_table_length > 0:
            self.max_table_length = max_table_length
        else:
            self.max_table_length = constants.MAX_TABLE_LEN

        self.shouldAuth = shouldAuth
        self.symbol = symbol
        self.websocket = websocket

        self.data = {
            'orderBookL2': [],
            'instrument': []
        }

        channels = self.channels
        symbol = self.symbol
        shouldAuth = self.shouldAuth
        websocket = self.websocket

        self.websocket = GdaxWebsocket()

        self.websocket.connect(
           shouldAuth=shouldAuth,
           websocket=websocket
        )

        self.websocket.on('subscribe', self.on_subscribe)
        self.websocket.on('latency', self.on_latency)
        self.channels = []
        self.subscribe_to_channels(channels)
        self.subscribe_to_instrument_channels(symbol, channels)
        self.secureChannels = []
        if shouldAuth:
            self.subscribe_to_secure_instrument_channels(symbol, channels)
def test_subscribe_instrument_on_message(mocker):
    socket = GdaxWebsocket()
    message = {
        "success": "true",
        "subscribe": "instrument:XBTH17",
        "request": {
            "op": "subscribe",
            "args": ["instrument:XBTH17"]
        }
    }
    subscribe_handler = mocker.stub()
    # socket.on('subscribe', subscribe_handler)

    @socket.on('subscribe')
    def handler(message):
        subscribe_handler(message)

    socket.on_message(json.dumps(message))

    subscribe_handler.assert_called_once_with(message)
def test_on_partial_action_message_data(mocker):
    socket = GdaxWebsocket()
    message = orderBookL2_data['partial']
    partial_event_handler = mocker.stub()
    event_name = socket.gen_action_event_key(message['action'],
                                             message['data'][0]['symbol'],
                                             message['table'])
    socket.on(event_name, partial_event_handler)
    socket.on_message(json.dumps(message))

    partial_event_handler.assert_called_once_with(message)
def test_on_action_triggers_events(mocker):
    socket = GdaxWebsocket()

    message = orderBookL2_data['partial']
    price_level = message['data'][0]
    mock_event_handler = mocker.stub(name='on_order_book_partial_handler')
    partial_event_name = socket.gen_action_event_key(
        price_level['symbol'],
        message['table'],
        message['action'],
    )

    socket.on(partial_event_name, mock_event_handler)
    socket.emit(partial_event_name, message)
    mock_event_handler.assert_called_with(message)
def test_subscribe_to_channel(mocker):
    socket = GdaxWebsocket()
    socket.heartbeatEnabled = False
    message = {
        "success": "true",
        "subscribe": "instrument:XBTH17",
        "request": {
            "op": "subscribe",
            "args": ["instrument:XBTH17"]
        }
    }

    handler = mocker.stub()
    socket.on('subscribe', handler)
    socket.on_message(json.dumps(message))
    handler.assert_called_once()
def test_re_connect_on_error(mocker):
    '''
    Ensure heartbeat is disabled on the websocket.
    '''
    connect_websocket_mock = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.connect_websocket')
    socket = GdaxWebsocket()
    socket.shouldAuth = False
    socket.heartbeatEnabled = False

    socket.re_connect()

    connect_websocket_mock.assert_called_once()
def test_on_message_receive_ping(mocker):
    """
    Bitmex websocket uses Primus websocket lib which uses the
    following heartbeat scheme:

          client will disconnect
            if not recv within
              `pingTimeout`

         primus::pong::{timestamp}
        +----------------------+
        |                      |
    +---v----+            +---------+
    | server |            |  client |
    +--------+            +----^----+
        |                      |
        +----------------------+
         primus::ping::{timestamp}

          sent at `pingInterval`
          server will disconnect
          if no response since
               last ping
    """
    connect_websocket = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.connect_websocket')
    send_message_mock = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.send_message')
    socket = GdaxWebsocket()
    ping_handler = mocker.stub()
    socket.on('ping', ping_handler)
    latency_handler = mocker.stub()
    socket.on('latency', latency_handler)

    ping_message = "primus::ping::%s" % (time.time() * 1000)
    socket.on_message(ping_message)
    ping_handler.assert_called_once_with(ping_message)
    latency_handler.assert_called_once()
def test_on_subscribe_success(mocker):
    error = mocker.patch('gdax_websocket.websocket.GdaxWebsocket.error')
    socket = GdaxWebsocket()
    message = {
        "success": "true",
        "subscribe": "instrument:XBTH17",
        "request": {
            "op": "subscribe",
            "args": ["instrument:XBTH17"]
        }
    }
    subscribe_handler = mocker.stub()
    socket.on('subscribe', subscribe_handler)
    socket.emit('subscribe', message)

    error.assert_not_called()
    subscribe_handler.assert_called_once_with(message)
def test_connect_websocket_without_heartbeat(mocker):
    '''
    Ensure heartbeat is disabled on the websocket.
    '''
    websocket_run_forever = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.websocket_run_forever')
    init_websocket = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.init_websocket')
    wait_for_connection = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.wait_for_connection')
    socket = GdaxWebsocket()
    socket.shouldAuth = False
    socket.heartbeatEnabled = False
    socket.connect_websocket()

    # neither ping_timeout or ping_interval are passed as args
    websocket_run_forever.assert_called_with(
        {'sslopt': {
            "cert_reqs": ssl.CERT_NONE
        }})
    init_websocket.assert_called_once()
    wait_for_connection.assert_called_once()
def test_connect_websocket_with_heartbeat(mocker):
    '''
    Ensure heartbeat is enabled on the websocket.
    '''
    websocket_run_forever = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.websocket_run_forever')
    init_websocket = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.init_websocket')
    wait_for_connection = mocker.patch(
        'gdax_websocket.websocket.GdaxWebsocket.wait_for_connection')
    socket = GdaxWebsocket()
    socket.shouldAuth = False
    socket.heartbeatEnabled = True
    socket.connect_websocket()

    websocket_run_forever.assert_called_with({
        'sslopt': {
            "cert_reqs": ssl.CERT_NONE
        },
        'ping_timeout': 20,
        'ping_interval': 60
    })
    init_websocket.assert_called_once()
    wait_for_connection.assert_called_once()
Beispiel #15
0
#! /home/jose/.pyenv/shims/python

from __future__ import absolute_import
from gdax_websocket.websocket import GdaxWebsocket
import logging
import websocket
import asyncio

_logger = logging.getLogger('websocket')
_logger.setLevel(logging.DEBUG)
websocket.enableTrace(True)

ws = GdaxWebsocket()
ws.connect()
ws.subscribe_action('instrument_')

loop = asyncio.get_event_loop()
loop.run_forever()
from __future__ import absolute_import
from gdax_websocket.websocket import GdaxWebsocket
from time import sleep
import logging
import websocket

_logger = logging.getLogger('websocket')
_logger.setLevel(logging.DEBUG)
websocket.enableTrace(True)

ws = GdaxWebsocket()
ws.connect(shouldAuth=True)
ws.subscribe_action('instrument')

while True:
    sleep(1)
Beispiel #17
0
class Instrument(EventEmitter):
    def __init__(self,
                 symbol='BTC-USD',
                 channels=[],
                 shouldAuth=False,
                 max_table_length=constants.MAX_TABLE_LEN,
                 websocket=None):

        EventEmitter.__init__(self)
        self.channels = channels

        if max_table_length > 0:
            self.max_table_length = max_table_length
        else:
            self.max_table_length = constants.MAX_TABLE_LEN

        self.shouldAuth = shouldAuth
        self.symbol = symbol
        self.websocket = websocket

        self.data = {
            'orderBookL2': [],
            'instrument': []
        }

        channels = self.channels
        symbol = self.symbol
        shouldAuth = self.shouldAuth
        websocket = self.websocket

        self.websocket = GdaxWebsocket()

        self.websocket.connect(
           shouldAuth=shouldAuth,
           websocket=websocket
        )

        self.websocket.on('subscribe', self.on_subscribe)
        self.websocket.on('latency', self.on_latency)
        self.channels = []
        self.subscribe_to_channels(channels)
        self.subscribe_to_instrument_channels(symbol, channels)
        self.secureChannels = []
        if shouldAuth:
            self.subscribe_to_secure_instrument_channels(symbol, channels)

    def on_latency(self, message):
        alog.debug("# on_latency")
        alog.debug(message)

        latency = []
        if 'latency' not in self.data:
            self.data['latency'] = []

        if len(self.data['latency']) > self.max_table_length - 1:
            self.data['latency'].pop()

        latency.append(message)

        self.data['latency'] = latency

        # calculate average latency
        avg_latency = sum(latency)/len(latency)
        self.emit('latency', avg_latency)
        alog.debug("## avg latency: %s" % (avg_latency))

    def get_latency(self):
        return self.data['latency']

    def subscribe_to_channels(self, channels):
        # Subscribe to all channels by default
        for channel in constants.CHANNELS:
            if len(channels) > 0 and channel not in channels:
                channel = None

            if channel:
                handler_name = "on_%s" % (channel)
                handler = {}
                if hasattr(self, handler_name):
                    handler = getattr(self, handler_name)
                else:
                    handler = self.on_channel

                self.websocket.subscribe(channel, handler)

    def subscribe_to_secure_channels(self, channels):
        # Subscribe to all channels by default
        for channel in constants.SECURE_CHANNELS:
            if len(channels) > 0 and channel not in channels:
                channel = None

            if channel:
                handler_name = "on_%s" % (channel)
                handler = {}
                if hasattr(self, handler_name):
                    handler = getattr(self, handler_name)
                else:
                    handler = self.on_channel

                self.websocket.subscribe(channel, handler)

    def subscribe_to_instrument_channels(self, symbol, channels):
        # Subscribe to all channels by default
        for channel in constants.INSTRUMENT_CHANNELS:
            if len(channels) > 0 and channel not in channels:
                channel = None

            if channel:
                self.subscribe_actions_for_channel(channel, symbol)

    def subscribe_to_secure_instrument_channels(self, symbol, channels):
        # Subscribe to all channels by default
        for channel in constants.SECURE_INSTRUMENT_CHANNELS:
            if len(channels) > 0 and channel not in channels:
                channel = None

            if channel:
                self.subscribe_actions_for_channel(channel, symbol)

    def subscribe_actions_for_channel(self, channel, symbol):
        for action in constants.ACTIONS:
            handler_name = "on_%s" % (channel)
            handler = {}
            if hasattr(self, handler_name):
                handler = getattr(self, handler_name)
            else:
                handler = self.on_action

            self.websocket.subscribe_action(action,
                                            channel,
                                            symbol,
                                            handler)

    def on_subscribe(self, channel):
        self.channels.append(channel)

    def all_channels(self):
        allChannels = []
        for channel in self.channels:
            allChannels.append(channel)

        for channel in self.secureChannels:
            allChannels.append(channel)

        return allChannels

    def on_channel(self, message):
        alog.debug("#on_channel")
        alog.debug(message)
        for item in message['data']:
            self.prepend_to_table(message['table'], item)

    def on_action(self, message):
        self.emit('action', message)
        return
        table = message['table']
        data = message['data']
        alog.debug("on_action")
        action = message['action']

        if action == 'delete':
            for item in data:
                self.delete_from_table(table, item)
        elif action == 'update' and 'id' in data[0]:
            for item in data:
                self.update_item_in_table(table, item)
        elif action == 'partial' and 'id' not in data[0]:
            self.data[table] = data[0]
        elif action == 'insert' and 'id' not in data[0]:
            self.update_keys_in_table(table, data[0])
        elif action == 'partial' or action == 'insert':
            for item in data:
                self.prepend_to_table(table, item)
        else:
            self.update_keys_in_table(table, data[0])

        self.emit(table, table, self.get_table(table))

    def update_keys_in_table(self, table, update):
        self.data[table].update(update)

    def delete_from_table(self, table, item):
        alog.debug('#delete_from_table:%s' % (table))
        alog.debug(item)
        if table not in self.data:
            self.data[table] = []
        delete_item = next(_item for _item in self.data['orderBookL2']
                           if _item['id'] == item['id'])
        if delete_item:
            self.data[table].remove(delete_item)

    def prepend_to_table(self, table, item):
        if table not in self.data:
            self.data[table] = []
        isMaxLength = len(self.data[table]) == self.max_table_length
        if isMaxLength and 'orderBook' not in table:
            self.data[table].pop()

        self.data[table].insert(0, item)
        alog.debug('#prepend_to_table')
        alog.debug(self.data[table])

    def update_item_in_table(self, table, update):
        alog.debug("# update_item_in_table")
        alog.debug(json.dumps(update))

        item_to_update = next(item for item in self.data[table]
                              if item['id'] == update['id'])

        item_to_update.update(update)

    def get_table(self, table):
        return self.data[table]

    def update_instrument(self, action, data):
        alog.debug(data)
        self.data['instrument'] = data[0]