Esempio n. 1
0
 def get_marks(self, instrument: ExchangeInstrument) -> Signal:
     if self.mark_type == MarkType.LAST:
         trades = self.mds.get_trades(instrument)
         return Map(self.network, trades, lambda x: Mark(instrument.get_instrument(), x.get_price()))
     else:
         books = self.mds.get_order_books(instrument)
         return Map(self.network, books, lambda x: Mark(instrument.get_instrument(), x.get_mid()))
Esempio n. 2
0
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()

            self.instrument_trades[symbol] = MutableSignal()
            self.instrument_quotes[symbol] = MutableSignal()

            # magic: inject the bare Signal into the graph so we can
            # fire events on it without any downstream connections
            # yet made
            network.attach(self.instrument_trades[symbol])
            network.attach(self.instrument_quotes[symbol])

            subscribe_msg = {
                'id': 1,
                'method': 'trade.subscribe',
                'params': [symbol]
            }

            messages = MutableSignal()
            json_messages = Map(network, messages, lambda x: json.loads(x))
            incr_messages = Filter(network, json_messages,
                                   lambda x: x.get('type', None) == 'incremental')
            trade_lists = Map(network, incr_messages, lambda x: self.__extract_trades(x))
            trades = FlatMap(self.scheduler, trade_lists)

            class TradeScheduler(Event):
                # noinspection PyShadowingNames
                def __init__(self, fh: PhemexFeedHandler, trades: Signal):
                    self.fh = fh
                    self.trades = trades

                def on_activate(self) -> bool:
                    if self.trades.is_valid():
                        trade = self.trades.get_value()
                        trade_symbol = trade.get_instrument().get_exchange_instrument_code()
                        trade_signal = self.fh.instrument_trades[trade_symbol]
                        self.fh.scheduler.schedule_update(trade_signal, trade)
                        return True
                    else:
                        return False

            network.connect(trades, TradeScheduler(self, trades))

            # noinspection PyShadowingNames
            async def do_subscribe(instrument, subscribe_msg, messages):
                async with websockets.connect(self.ws_uri) as sock:
                    subscribe_msg_txt = json.dumps(subscribe_msg)
                    self.logger.info(f'sending subscription request for {instrument.get_exchange_instrument_code()}')
                    await sock.send(subscribe_msg_txt)
                    while True:
                        self.scheduler.schedule_update(messages, await sock.recv())

            asyncio.ensure_future(do_subscribe(instrument, subscribe_msg, messages))

        # we are now live
        self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        symbols = []
        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            symbols.append(f'{symbol}')

            self.instrument_trades[symbol] = MutableSignal()
            self.instrument_quotes[symbol] = MutableSignal()
            self.instrument_order_books[symbol] = MutableSignal()

            # magic: inject the bare Signal into the graph so we can
            # fire events on it without any downstream connections
            # yet made
            network.attach(self.instrument_trades[symbol])
            network.attach(self.instrument_quotes[symbol])
            network.attach(self.instrument_order_books[symbol])

        subscribe_msg = {
            'type': 'subscribe',
            'product_ids': symbols,
            'channels': ['matches', 'heartbeat']
        }

        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        match_messages = Filter(network, json_messages,
                                lambda x: x.get('type', None) == 'match')
        trades = Map(network, match_messages,
                     lambda x: self.__extract_trade(x))

        class TradeScheduler(Event):
            def __init__(self, fh: CoinbaseProFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if trades.is_valid():
                    trade = trades.get_value()
                    trade_symbol = trade.get_instrument(
                    ).get_exchange_instrument_code()
                    trade_signal = self.fh.instrument_trades[trade_symbol]
                    self.fh.scheduler.schedule_update(trade_signal, trade)
                    return True
                else:
                    return False

        network.connect(trades, TradeScheduler(self))

        async with websockets.connect(self.ws_uri) as sock:
            self.logger.info('Sending subscription request for all products')
            await sock.send(json.dumps(subscribe_msg))
            self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
            while True:
                self.scheduler.schedule_update(messages, await sock.recv())
Esempio n. 4
0
async def main():
    scheduler = RealtimeNetworkScheduler()
    network = scheduler.get_network()
    values = From(scheduler, [0.0, 3.2, 2.1, 2.9, 8.3, 5.7])
    mapper = Map(network, values, lambda x: round(x))
    accumulator = Scan(network, mapper)
    Do(network, accumulator, lambda: print(f"{accumulator.get_value()}"))
Esempio n. 5
0
def test_map_reduce():
    scheduler = HistoricNetworkScheduler(0, 30 * 1000)
    network = scheduler.get_network()
    values = From(scheduler, [0.0, 3.2, 2.1, 2.9, 8.3, 5.7])
    mapper = Map(network, values, lambda x: round(x))
    accumulator = Scan(network, mapper)
    Do(network, accumulator, lambda: print(f'{accumulator.get_value()}'))
    scheduler.run()
    assert accumulator.get_value() == 22.0
Esempio n. 6
0
    def init(self, ctx: StrategyContext):
        self.ctx = ctx

        big_print_qty = float(ctx.getenv('BIG_PRINT_QTY'))
        self.trade_qty = float(ctx.getenv('CONTRACT_TRADE_QTY'))

        api_key = ctx.getenv('PHEMEX_API_KEY')
        api_secret = ctx.getenv('PHEMEX_API_SECRET')
        if not api_key:
            raise ValueError('missing PHEMEX_API_KEY')
        if not api_secret:
            raise ValueError('missing PHEMEX_API_SECRET')

        credentials = AuthCredentials(api_key, api_secret)

        exchange_instance = ctx.getenv('PHEMEX_INSTANCE', 'prod')
        if exchange_instance == 'prod':
            self.trading_conn = PhemexConnection(credentials)
        elif exchange_instance == 'test':
            self.trading_conn = PhemexConnection(
                credentials, api_url='https://testnet-api.phemex.com')
        else:
            raise ValueError(
                f'Unknown PHEMEX_INSTANCE value: {exchange_instance}')

        self.logger.info(f'Connected to Phemex {exchange_instance} instance')

        self.spot_feed = ctx.fh_registry.get_feed(
            f'coinbasepro:{exchange_instance}:BTC-USD')
        self.futures_feed = ctx.fh_registry.get_feed(
            f'phemex:{exchange_instance}:BTCUSD')

        self.logger.info(
            f'Connected to spot & futures {exchange_instance} feeds')

        network = self.ctx.get_network()

        # scan the spot market for large trades
        spot_trades = self.spot_feed.get_trades()
        Do(
            network, spot_trades, lambda: self.logger.info(
                f'Spot market trade: {spot_trades.get_value()}'))
        self.big_prints = Filter(network, spot_trades,
                                 lambda x: x.get_qty() >= big_print_qty)

        # compute 5 minute bins for the futures market and extract the volume field
        self.futures_trades = self.futures_feed.get_trades()
        buffer_5min = BufferWithTime(network, self.futures_trades,
                                     timedelta(minutes=5))
        self.ohlc_5min = ComputeOHLC(network, buffer_5min)
        Do(
            network, self.ohlc_5min, lambda: self.logger.info(
                f'OHLC[5min]: {self.ohlc_5min.get_value()}'))
        self.volume = Map(network, self.ohlc_5min, lambda x: x.volume)

        # track the exponentially weighted moving average of the futures volume
        self.ewma = ExponentialMovingAverage(network, self.volume)
    def start(self):
        network = self.scheduler.get_network()
        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        json_messages = Filter(
            network, json_messages,
            lambda x: x.get('type', None) in ['incremental', 'snapshot'])

        class OrderEventScheduler(Event):
            # noinspection PyShadowingNames
            def __init__(self, sub: AccountOrderPositionSubscriber,
                         json_messages: Signal):
                self.sub = sub
                self.json_messages = json_messages

            def on_activate(self) -> bool:
                if self.json_messages.is_valid():
                    msg = self.json_messages.get_value()
                    accounts = msg['accounts']
                    for account in accounts:
                        orders = account['orders']
                        for order in orders:
                            order_event = OrderEvent()
                            self.sub.scheduler.schedule_update(
                                self.sub.order_events, order_event)
                    return True
                else:
                    return False

        network.connect(json_messages,
                        OrderEventScheduler(self, json_messages))

        # noinspection PyShadowingNames
        async def do_subscribe():
            async with websockets.connect(self.ws_uri) as sock:
                self.logger.info(
                    f'sending Account-Order-Position subscription request')
                auth_msg = self.auth.get_user_auth_message()
                await sock.send(auth_msg)
                error_msg = await sock.recv()
                error_struct = json.loads(error_msg)
                if error_struct['error'] is not None:
                    raise ConnectionError(
                        f'Unable to authenticate: {error_msg}')

                aop_sub_msg = {
                    'id': 2,
                    'method': 'aop.subscribe',
                    'params': []
                }
                await sock.send(json.dumps(aop_sub_msg))
                while True:
                    self.scheduler.schedule_update(messages, await sock.recv())

        asyncio.ensure_future(do_subscribe())
Esempio n. 8
0
    def get_marks(self, instrument: ExchangeInstrument) -> Signal:
        economics = instrument.get_instrument().get_economics()
        if isinstance(economics, FutureContract):
            economics = economics.get_underlier().get_economics()
        if isinstance(economics, CurrencyPair):
            ccy_code = economics.get_base_currency().get_currency_code()
            symbol = f'.M{ccy_code}'
            cash_instrument = self.instrument_cache.get_or_create_cash_instrument(
                ccy_code)
        else:
            raise ValueError(
                'Unable to get marks: expected CurrencyPair or FutureContract on CurrencyPair'
            )

        network = self.scheduler.get_network()
        marks = self.instrument_marks.get(symbol)
        if marks is None:
            marks = MutableSignal()
            network.attach(marks)
            self.instrument_marks[symbol] = marks

            subscribe_msg = {
                'id': 1,
                'method': 'tick.subscribe',
                'params': [symbol]
            }

            messages = MutableSignal()
            json_messages = Map(network, messages, lambda x: json.loads(x))
            tick_messages = Filter(network, json_messages,
                                   lambda x: 'tick' in x)
            ticks = Map(
                network, tick_messages, lambda x: self.__extract_tick(
                    x, cash_instrument.get_instrument()))
            Pipe(self.scheduler, ticks, marks)

            asyncio.ensure_future(
                websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                               self.logger, subscribe_msg,
                                               self.scheduler, messages,
                                               symbol, 'ticks'))
        return marks
Esempio n. 9
0
    def init(self, ctx: StrategyContext):
        self.ctx = ctx

        big_print_qty = float(ctx.getenv('BIG_PRINT_QTY'))
        self.trade_qty = float(ctx.getenv('CONTRACT_TRADE_QTY'))

        exchange_instance = ctx.getenv('EXCHANGE_INSTANCE', 'prod')
        op_uri = f'phemex:{exchange_instance}'
        self.order_placer = ctx.get_order_placer_service().get_order_placer(
            op_uri)

        self.logger.info(f'Connected to Phemex {exchange_instance} instance')

        network = self.ctx.get_network()

        # scan the spot market for large trades
        btc_usd_spot = self.ctx.get_instrument_cache(
        ).get_crypto_exchange_instrument('CoinbasePro', 'BTC-USD')
        spot_trades = self.ctx.get_marketdata_service().get_trades(
            btc_usd_spot)
        Do(
            network, spot_trades, lambda: self.logger.info(
                f'Spot market trade: {spot_trades.get_value()}'))
        self.big_prints = Filter(network, spot_trades,
                                 lambda x: x.get_qty() >= big_print_qty)

        # compute 5 minute bins for the futures market and extract the volume field
        btc_usd_future = self.ctx.get_instrument_cache(
        ).get_crypto_exchange_instrument('Phemex', 'BTCUSD')
        self.futures_trades = self.ctx.get_marketdata_service().get_trades(
            btc_usd_future)
        buffer_5min = BufferWithTime(self.ctx.get_scheduler(),
                                     self.futures_trades, timedelta(minutes=5))
        self.ohlc_5min = ComputeOHLC(network, buffer_5min)
        Do(
            network, self.ohlc_5min, lambda: self.logger.info(
                f'OHLC[5min]: {self.ohlc_5min.get_value()}'))
        self.volume = Map(network, self.ohlc_5min, lambda x: x.volume)

        # subscribe to top of book
        self.futures_book = self.ctx.get_marketdata_service().get_order_books(
            btc_usd_future)
        Do(
            network, self.futures_book, lambda: self.logger.info(
                f'Futures bid/ask: '
                f'{self.futures_book.get_value().get_best_bid()} / '
                f'{self.futures_book.get_value().get_best_ask()}'))

        # track the exponentially weighted moving average of the futures volume
        self.ewma = ExponentialMovingAverage(network, self.volume)
Esempio n. 10
0
    def init(self, ctx: StrategyContext):
        scheduler = ctx.get_scheduler()
        network = scheduler.get_network()

        contract_qty = int(ctx.getenv('BBANDS_QTY', 1))
        window = int(ctx.getenv('BBANDS_WINDOW'))
        num_std = int(ctx.getenv('BBANDS_NUM_STD'))
        stop_std = int(ctx.getenv('BBANDS_STOP_STD'))
        bin_minutes = int(ctx.getenv('BBANDS_BIN_MINUTES', 5))
        cooling_period_seconds = int(ctx.getenv('BBANDS_COOL_SECONDS', 15))
        exchange_code, instrument_code = ctx.getenv(
            'TRADING_INSTRUMENT').split(':')
        instrument = ctx.get_instrument_cache().get_crypto_exchange_instrument(
            exchange_code, instrument_code)
        trades = ctx.get_marketdata_service().get_trades(instrument)
        trades_5m = BufferWithTime(scheduler, trades,
                                   timedelta(minutes=bin_minutes))
        prices = ComputeOHLC(network, trades_5m)
        close_prices = Map(network, prices, lambda x: x.close_px)
        bbands = ComputeBollingerBands(network, close_prices, window, num_std)

        op_service = ctx.get_order_placer_service()
        oms = op_service.get_order_manager_service()
        dcs = ctx.get_data_capture_service()

        exchange_id = ctx.getenv('EXCHANGE_ID', 'phemex')
        exchange_instance = ctx.getenv('EXCHANGE_INSTANCE', 'prod')
        account = ctx.getenv('EXCHANGE_ACCOUNT')
        op_uri = f'{exchange_id}:{exchange_instance}'

        # subscribe to marks, position updates and exchange position updates
        marks = ctx.get_mark_service().get_marks(instrument)
        Do(scheduler.get_network(), marks,
           lambda: self.logger.debug(marks.get_value()))

        position = ctx.get_position_service().get_position(account, instrument)
        Do(scheduler.get_network(), position,
           lambda: self.logger.info(position.get_value()))

        exch_position = ctx.get_exchange_position_service(
        ).get_exchange_positions()
        Do(scheduler.get_network(), exch_position,
           lambda: self.logger.info(exch_position.get_value()))

        # capture position and Bollinger Band data
        Do(
            scheduler.get_network(), position, lambda: dcs.capture(
                'Position', {
                    'time': pd.to_datetime(scheduler.get_time(), unit='ms'),
                    'position': position.get_value().get_qty()
                }))
        Do(
            scheduler.get_network(), bbands, lambda: dcs.capture(
                'BollingerBands', {
                    'time': pd.to_datetime(scheduler.get_time(), unit='ms'),
                    'sma': bbands.get_value().sma,
                    'upper': bbands.get_value().upper,
                    'lower': bbands.get_value().lower
                }))

        # debug log basic marketdata
        Do(scheduler.get_network(), prices,
           lambda: self.logger.debug(prices.get_value()))

        class TraderState(Enum):
            GOING_LONG = auto()
            LONG = auto()
            FLATTENING = auto()
            FLAT = auto()

        # order placement logic
        class BollingerTrader(Event):
            # noinspection PyShadowingNames
            def __init__(self, scheduler: NetworkScheduler,
                         op_service: OrderPlacerService,
                         strategy: BollingerBandsStrategy1):
                self.scheduler = scheduler
                self.op = op_service.get_order_placer(f'{op_uri}')
                self.strategy = strategy
                self.last_entry = 0
                self.last_exit = 0
                self.cum_pnl = 0
                self.stop = None
                self.trader_state = TraderState.FLAT
                self.last_trade_time = 0

                self.scheduler.get_network().connect(oms.get_order_events(),
                                                     self)
                self.scheduler.get_network().connect(position, self)

            def on_activate(self) -> bool:
                if self.scheduler.get_network().has_activated(
                        oms.get_order_events()):
                    order_event = oms.get_order_events().get_value()
                    if isinstance(order_event,
                                  ExecutionReport) and order_event.is_fill():
                        order_type = 'stop order' if self.stop is not None and order_event.get_order_id() == \
                                                     self.stop.order_id else 'market order'
                        self.strategy.logger.info(
                            f'Received fill event for {order_type}: {order_event}'
                        )
                        if self.trader_state == TraderState.GOING_LONG:
                            self.last_entry = order_event.get_last_px()
                            if order_event.get_order_status(
                            ) == OrderStatus.FILLED:
                                self.strategy.logger.info(
                                    f'Entered long position: entry price={self.last_entry}'
                                )
                                self.trader_state = TraderState.LONG
                        elif self.trader_state in (TraderState.FLATTENING, TraderState.LONG) and \
                                order_event.get_order_status() == OrderStatus.FILLED:
                            if order_type == 'stop order':
                                self.strategy.logger.info(
                                    f'stop loss filled at {order_event.get_last_px()}'
                                )
                                self.stop = None

                            trade_pnl = (order_event.get_last_px() - self.last_entry) * \
                                        (contract_qty / self.last_entry)
                            self.cum_pnl += trade_pnl
                            self.strategy.logger.info(
                                f'Trade P&L={trade_pnl}; cumulative P&L={self.cum_pnl}'
                            )

                            dcs.capture(
                                'PnL', {
                                    'time':
                                    pd.to_datetime(scheduler.get_time(),
                                                   unit='ms'),
                                    'trade_pnl':
                                    trade_pnl,
                                    'cum_pnl':
                                    self.cum_pnl
                                })
                            self.trader_state = TraderState.FLAT
                    elif isinstance(order_event, Reject):
                        self.strategy.logger.error(
                            f'Order rejected: {order_event.get_message()}')
                        self.trader_state = TraderState.FLAT
                elif self.trader_state == TraderState.FLAT and close_prices.get_value(
                ) < bbands.get_value().lower:
                    if self.last_trade_time != 0 and (scheduler.get_time() - self.last_trade_time) < \
                            cooling_period_seconds * 1000:
                        self.strategy.logger.info(
                            'Cooling off -- not trading again on rapidly repeated signal'
                        )
                        return False

                    self.strategy.logger.info(
                        f'Close below lower Bollinger band, enter long position '
                        f'at {scheduler.get_clock().get_time()}')

                    stop_px = close_prices.get_value() - (
                        (bbands.get_value().sma - bbands.get_value().lower) *
                        (stop_std / num_std))

                    self.strategy.logger.info(
                        f'Submitting orders: last_px = {close_prices.get_value()}, '
                        f'stop_px = {stop_px}')

                    order = self.op.get_order_factory().create_market_order(
                        Side.BUY, contract_qty, instrument)
                    self.stop = self.op.get_order_factory().create_stop_order(
                        Side.SELL, contract_qty, stop_px, instrument)

                    self.op.submit(order)
                    self.op.submit(self.stop)

                    self.last_trade_time = scheduler.get_time()
                    self.trader_state = TraderState.GOING_LONG
                elif self.trader_state == TraderState.LONG and close_prices.get_value(
                ) > bbands.get_value().upper:
                    self.strategy.logger.info(
                        f'Close above upper Bollinger band, exiting long position at '
                        f'{scheduler.get_clock().get_time()}')

                    order = self.op.get_order_factory().create_market_order(
                        Side.SELL, contract_qty, instrument)
                    self.op.submit(order)
                    if self.stop is not None:
                        self.op.cancel(self.stop)
                        self.stop = None

                    self.trader_state = TraderState.FLATTENING
                return False

        network.connect(bbands, BollingerTrader(scheduler, op_service, self))
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        symbols = []
        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            symbols.append(f'{symbol.lower()}@aggTrade')

            self.instrument_trades[symbol] = MutableSignal()
            self.instrument_quotes[symbol] = MutableSignal()

            # magic: inject the bare Signal into the graph so we can
            # fire events on it without any downstream connections
            # yet made
            network.attach(self.instrument_trades[symbol])
            network.attach(self.instrument_quotes[symbol])

        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        trade_messages = Filter(network, json_messages, lambda x: 'data' in x)
        trades = Map(network, trade_messages,
                     lambda x: self.__extract_trade(x))

        class TradeScheduler(Event):
            def __init__(self, fh: BinanceFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if trades.is_valid():
                    trade = trades.get_value()
                    trade_symbol = trade.get_instrument(
                    ).get_exchange_instrument_code()
                    trade_signal = self.fh.instrument_trades[trade_symbol]
                    self.fh.scheduler.schedule_update(trade_signal, trade)
                    return True
                else:
                    return False

        network.connect(trades, TradeScheduler(self))

        async with websockets.connect(self.ws_uri) as sock:
            ndx = 1
            n = 250
            symbols_chunked = [
                symbols[i:i + n] for i in range(0, len(symbols), n)
            ]

            for symbols in symbols_chunked:
                self.logger.info(
                    f'Sending subscription request for {len(symbols)} symbols: {symbols}'
                )
                subscribe_msg = {
                    "method": "SUBSCRIBE",
                    "params": symbols,
                    "id": ndx
                }
                await sock.send(json.dumps(subscribe_msg))
                ndx = ndx + 1

            self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
            while True:
                self.scheduler.schedule_update(messages, await sock.recv())
Esempio n. 12
0
    def init(self, ctx: StrategyContext):
        self.ctx = ctx

        big_print_qty = float(ctx.getenv('BIG_PRINT_QTY'))
        self.trade_qty = float(ctx.getenv('CONTRACT_TRADE_QTY'))

        api_key = ctx.getenv('PHEMEX_API_KEY')
        api_secret = ctx.getenv('PHEMEX_API_SECRET')
        if not api_key:
            raise ValueError('missing PHEMEX_API_KEY')
        if not api_secret:
            raise ValueError('missing PHEMEX_API_SECRET')

        credentials = AuthCredentials(api_key, api_secret)

        exchange_instance = ctx.getenv('PHEMEX_INSTANCE', 'prod')
        if exchange_instance == 'prod':
            self.trading_conn = PhemexConnection(credentials)
        elif exchange_instance == 'test':
            self.trading_conn = PhemexConnection(
                credentials, api_url='https://testnet-api.phemex.com')
        else:
            raise ValueError(
                f'Unknown PHEMEX_INSTANCE value: {exchange_instance}')

        self.logger.info(f'Connected to Phemex {exchange_instance} instance')

        network = self.ctx.get_network()

        # subscribe to AOP messages
        auth = WebsocketAuthenticator(api_key, api_secret)
        aop_sub = AccountOrderPositionSubscriber(auth, ctx.get_scheduler(),
                                                 exchange_instance)
        aop_sub.start()

        # scan the spot market for large trades
        btc_usd_spot = self.ctx.get_instrument_cache().get_exchange_instrument(
            'CoinbasePro', 'BTC-USD')
        spot_trades = self.ctx.get_marketdata_service().get_trades(
            btc_usd_spot)
        Do(
            network, spot_trades, lambda: self.logger.info(
                f'Spot market trade: {spot_trades.get_value()}'))
        self.big_prints = Filter(network, spot_trades,
                                 lambda x: x.get_qty() >= big_print_qty)

        # compute 5 minute bins for the futures market and extract the volume field
        btc_usd_future = self.ctx.get_instrument_cache(
        ).get_exchange_instrument('Phemex', 'BTCUSD')
        self.futures_trades = self.ctx.get_marketdata_service().get_trades(
            btc_usd_future)
        buffer_5min = BufferWithTime(network, self.futures_trades,
                                     timedelta(minutes=5))
        self.ohlc_5min = ComputeOHLC(network, buffer_5min)
        Do(
            network, self.ohlc_5min, lambda: self.logger.info(
                f'OHLC[5min]: {self.ohlc_5min.get_value()}'))
        self.volume = Map(network, self.ohlc_5min, lambda x: x.volume)

        # subscribe to top of book
        self.futures_book = self.ctx.get_marketdata_service().get_order_books(
            btc_usd_future)
        Do(
            network, self.futures_book, lambda: self.logger.info(
                f'Futures bid/ask: '
                f'{self.futures_book.get_value().get_best_bid()} / '
                f'{self.futures_book.get_value().get_best_ask()}'))

        # track the exponentially weighted moving average of the futures volume
        self.ewma = ExponentialMovingAverage(network, self.volume)
Esempio n. 13
0
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        symbols = []
        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            if symbol == self.include_symbol or self.include_symbol == '*':
                symbols.append(f'{symbol}')

                self.instrument_trades[symbol] = MutableSignal()
                self.instrument_order_book_events[symbol] = MutableSignal()
                self.instrument_order_books[symbol] = OrderBookBuilder(
                    network, self.instrument_order_book_events[symbol])

                # magic: inject the bare Signal into the graph so we can
                # fire events on it without any downstream connections
                # yet made
                network.attach(self.instrument_trades[symbol])
                network.attach(self.instrument_order_book_events[symbol])
                network.attach(self.instrument_order_books[symbol])

        subscribe_msg = {
            'type': 'subscribe',
            'product_ids': symbols,
            'channels': ['matches', 'level2', 'heartbeat']
        }

        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        match_messages = Filter(network, json_messages,
                                lambda x: x.get('type', None) == 'match')
        book_messages = Filter(
            network, json_messages,
            lambda x: x.get('type', None) in {'snapshot', 'l2update'})
        trades = Map(network, match_messages,
                     lambda x: self.__extract_trade(x))
        books = Map(network, book_messages,
                    lambda x: self.__extract_order_book_event(x))

        class TradeScheduler(Event):
            def __init__(self, fh: CoinbaseProFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if trades.is_valid():
                    trade = trades.get_value()
                    trade_symbol = trade.get_instrument(
                    ).get_exchange_instrument_code()
                    trade_signal = self.fh.instrument_trades[trade_symbol]
                    self.fh.scheduler.schedule_update(trade_signal, trade)
                    return True
                else:
                    return False

        network.connect(trades, TradeScheduler(self))

        class OrderBookScheduler(Event):
            def __init__(self, fh: CoinbaseProFeedHandler):
                self.fh = fh

            def on_activate(self) -> bool:
                if books.is_valid():
                    obe = books.get_value()
                    obe_symbol = obe.get_instrument(
                    ).get_exchange_instrument_code()
                    obe_signal = self.fh.instrument_order_book_events[
                        obe_symbol]
                    self.fh.scheduler.schedule_update(obe_signal, obe)
                    return True
                else:
                    return False

        network.connect(books, OrderBookScheduler(self))
        asyncio.ensure_future(
            websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                           self.logger, subscribe_msg,
                                           self.scheduler, messages,
                                           'all products', 'global'))

        # we are now live
        self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)
Esempio n. 14
0
    def start(self):
        network = self.scheduler.get_network()
        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))
        json_messages = Filter(network, json_messages,
                               lambda x: x.get('type', None) == 'incremental')

        class OrderEventScheduler(Event):
            # noinspection PyShadowingNames
            def __init__(self, sub: OrderEventSubscriber,
                         json_messages: Signal):
                self.sub = sub
                self.json_messages = json_messages

            def on_activate(self) -> bool:
                if self.json_messages.is_valid():
                    msg = self.json_messages.get_value()
                    orders = msg['orders']
                    for order_msg in orders:
                        order_id = order_msg['orderID']
                        cl_ord_id = order_msg['clOrdID']
                        exec_id = order_msg['execID']
                        last_px = order_msg['execPriceEp'] / 10000
                        last_qty = order_msg['execQty']

                        order = self.sub.oms.get_order_by_cl_ord_id(cl_ord_id)
                        if order is None:
                            if cl_ord_id is None or cl_ord_id == '':
                                self.sub.logger.warning(
                                    f'Received message from exchange with missing clOrdID, '
                                    f'orderID={order_id}')
                            else:
                                self.sub.logger.warning(
                                    f'Received message from exchange for unknown '
                                    f'clOrdID={cl_ord_id}, orderID={order_id}')
                            return False
                        elif order.get_order_id() is None:
                            self.sub.logger.info(
                                f'OMS order missing orderID; patching from clOrdID={cl_ord_id}'
                            )
                            order.set_order_id(order_id)

                        if order_msg['ordStatus'] == 'New':
                            self.sub.oms.new(order, exec_id)
                        elif order_msg['ordStatus'] == 'Canceled':
                            self.sub.oms.apply_cancel(order, exec_id)
                        elif order_msg[
                                'ordStatus'] == 'PartiallyFilled' or order_msg[
                                    'ordStatus'] == 'Filled':
                            self.sub.oms.apply_fill(order, last_qty, last_px,
                                                    exec_id)

                    return True
                else:
                    return False

        network.connect(json_messages,
                        OrderEventScheduler(self, json_messages))

        # noinspection PyShadowingNames,PyBroadException
        async def do_subscribe():
            while True:
                try:
                    async with websockets.connect(self.ws_uri) as sock:
                        self.logger.info(
                            'sending Account-Order-Position subscription request for orders'
                        )
                        auth_msg = self.auth.get_user_auth_message(1)
                        await sock.send(auth_msg)
                        error_msg = await sock.recv()
                        error_struct = json.loads(error_msg)
                        if error_struct['error'] is not None:
                            raise ConnectionError(
                                f'Unable to authenticate: {error_msg}')

                        aop_sub_msg = {
                            'id': 2,
                            'method': 'aop.subscribe',
                            'params': []
                        }
                        await sock.send(json.dumps(aop_sub_msg))
                        while True:
                            try:
                                self.scheduler.schedule_update(
                                    messages, await sock.recv())
                            except BaseException as error:
                                self.logger.error(
                                    f'disconnected; attempting to reconnect after {self.timeout} '
                                    f'seconds: {error}')
                                await asyncio.sleep(self.timeout)

                                # exit inner loop
                                break

                except socket.gaierror as error:
                    self.logger.error(
                        f'failed with socket error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except ConnectionRefusedError as error:
                    self.logger.error(
                        f'connection refused; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except BaseException as error:
                    self.logger.error(
                        f'unknown connection error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue

        asyncio.ensure_future(do_subscribe())
Esempio n. 15
0
    def subscribe(self):
        network = self.scheduler.get_network()
        messages = MutableSignal()
        json_messages = Map(network, messages, lambda x: json.loads(x))

        class PositionUpdateScheduler(Event):
            # noinspection PyShadowingNames
            def __init__(self, sub: PhemexExchangePositionService,
                         json_messages: Signal):
                self.sub = sub
                self.json_messages = json_messages

            def on_activate(self) -> bool:
                if self.json_messages.is_valid():
                    msg = self.json_messages.get_value()
                    if 'positions' in msg:
                        for position in msg['positions']:
                            if position['accountID'] == self.sub.account:
                                qty = (position['crossSharedBalanceEv'] /
                                       100_000_000)
                                ccy_symbol = position['currency']
                                ccy = self.sub.instrument_cache.get_or_create_currency(
                                    ccy_symbol)
                                xp = ExchangePosition(self.sub.account, ccy,
                                                      qty)
                                self.sub.scheduler.schedule_update(
                                    self.sub.exchange_positions, xp)

                    return True
                else:
                    return False

        network.connect(json_messages,
                        PositionUpdateScheduler(self, json_messages))

        # noinspection PyShadowingNames,PyBroadException
        async def do_subscribe():
            while True:
                try:
                    async with websockets.connect(self.ws_uri) as sock:
                        self.logger.info(
                            'sending Account-Order-Position subscription request for positions'
                        )
                        auth_msg = self.auth.get_user_auth_message(2)
                        await sock.send(auth_msg)
                        error_msg = await sock.recv()
                        error_struct = json.loads(error_msg)
                        if error_struct['error'] is not None:
                            raise ConnectionError(
                                f'Unable to authenticate: {error_msg}')

                        aop_sub_msg = {
                            'id': 3,
                            'method': 'aop.subscribe',
                            'params': []
                        }
                        await sock.send(json.dumps(aop_sub_msg))
                        while True:
                            try:
                                self.scheduler.schedule_update(
                                    messages, await sock.recv())
                            except BaseException as error:
                                self.logger.error(
                                    f'disconnected; attempting to reconnect after {self.timeout} '
                                    f'seconds: {error}')
                                await asyncio.sleep(self.timeout)

                                # exit inner loop
                                break

                except socket.gaierror as error:
                    self.logger.error(
                        f'failed with socket error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except ConnectionRefusedError as error:
                    self.logger.error(
                        f'connection refused; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue
                except BaseException as error:
                    self.logger.error(
                        f'unknown connection error; attempting to reconnect after {self.timeout} '
                        f'seconds: {error}')
                    await asyncio.sleep(self.timeout)
                    continue

        asyncio.ensure_future(do_subscribe())
Esempio n. 16
0
    async def _subscribe_trades_and_quotes(self):
        network = self.scheduler.get_network()

        for instrument in self.get_instruments():
            symbol = instrument.get_exchange_instrument_code()
            if symbol == self.include_symbol or self.include_symbol == '*':
                self.instrument_trades[symbol] = MutableSignal()
                self.instrument_order_book_events[symbol] = MutableSignal()
                self.instrument_order_books[symbol] = OrderBookBuilder(
                    network, self.instrument_order_book_events[symbol])

                # magic: inject the bare Signal into the graph so we can
                # fire events on it without any downstream connections
                network.attach(self.instrument_trades[symbol])
                network.attach(self.instrument_order_book_events[symbol])
                network.attach(self.instrument_order_books[symbol])

                trade_subscribe_msg = {
                    'id': 1,
                    'method': 'trade.subscribe',
                    'params': [symbol]
                }

                trade_messages = MutableSignal()
                trade_json_messages = Map(network, trade_messages,
                                          lambda x: json.loads(x))
                trade_incr_messages = Filter(
                    network, trade_json_messages,
                    lambda x: x.get('type', None) == 'incremental')
                trade_lists = Map(network, trade_incr_messages,
                                  lambda x: self.__extract_trades(x))
                trades = FlatMap(self.scheduler, trade_lists)

                class TradeScheduler(Event):
                    # noinspection PyShadowingNames
                    def __init__(self, fh: PhemexFeedHandler, trades: Signal):
                        self.fh = fh
                        self.trades = trades

                    def on_activate(self) -> bool:
                        if self.trades.is_valid():
                            trade = self.trades.get_value()
                            trade_symbol = trade.get_instrument(
                            ).get_exchange_instrument_code()
                            trade_signal = self.fh.instrument_trades[
                                trade_symbol]
                            self.fh.scheduler.schedule_update(
                                trade_signal, trade)
                            return True
                        else:
                            return False

                network.connect(trades, TradeScheduler(self, trades))

                orderbook_subscribe_msg = {
                    'id': 2,
                    'method': 'orderbook.subscribe',
                    'params': [symbol]
                }

                obe_messages = MutableSignal()
                obe_json_messages = Map(network, obe_messages,
                                        lambda x: json.loads(x))
                obe_json_messages = Filter(
                    network, obe_json_messages, lambda x: x.get('type', None)
                    in ['incremental', 'snapshot'])
                order_book_events = Map(
                    network, obe_json_messages,
                    lambda x: self.__extract_order_book_event(x))

                class OrderBookEventScheduler(Event):
                    # noinspection PyShadowingNames
                    def __init__(self, fh: PhemexFeedHandler,
                                 order_book_events: Signal):
                        self.fh = fh
                        self.order_book_events = order_book_events

                    def on_activate(self) -> bool:
                        if self.order_book_events.is_valid():
                            obe = self.order_book_events.get_value()
                            obe_symbol = obe.get_instrument(
                            ).get_exchange_instrument_code()
                            obe_signal = self.fh.instrument_order_book_events[
                                obe_symbol]
                            self.fh.scheduler.schedule_update(obe_signal, obe)
                            return True
                        else:
                            return False

                network.connect(
                    order_book_events,
                    OrderBookEventScheduler(self, order_book_events))

                asyncio.ensure_future(
                    websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                                   self.logger,
                                                   trade_subscribe_msg,
                                                   self.scheduler,
                                                   trade_messages, symbol,
                                                   'trade'))
                asyncio.ensure_future(
                    websocket_subscribe_with_retry(self.ws_uri, self.timeout,
                                                   self.logger,
                                                   orderbook_subscribe_msg,
                                                   self.scheduler,
                                                   obe_messages, symbol,
                                                   'order book'))

        # we are now live
        self.scheduler.schedule_update(self.state, FeedHandlerState.LIVE)