Skip to content

Commit 1c12177

Browse files
committed
latest protocol version.
* exposed bulkSubscriptionFilter. * added clientVersion on logon message.
1 parent cd1c2a3 commit 1c12177

File tree

8 files changed

+345
-168
lines changed

8 files changed

+345
-168
lines changed

openfeed/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,4 @@
77
# FIXME
88

99
from .openfeed_client import OpenfeedClient
10-
11-
VERSION = '1.2.0'
10+
from .version import VERSION

openfeed/generated/openfeed_api_pb2.py

Lines changed: 103 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openfeed/generated/openfeed_instrument_pb2.py

Lines changed: 76 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openfeed/generated/openfeed_pb2.py

Lines changed: 101 additions & 94 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openfeed/openfeed_client.py

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
from generated import openfeed_api_pb2
33
from generated import openfeed_pb2
44
from generated import openfeed_instrument_pb2
5+
from struct import unpack
6+
from io import BytesIO
7+
import platform
58
import time
69
import traceback
710
import sys
811
import inspect
912
import collections
1013
import websocket
1114
import random
15+
from .version import VERSION
1216
try:
1317
import thread
1418
except ImportError:
@@ -109,7 +113,7 @@ def add_symbol_subscription(self, symbol: Union[str, list], callback, service="R
109113

110114
return self
111115

112-
def add_exchange_subscription(self, exchange: Union[str, list], callback, service="REAL_TIME", subscription_type=["QUOTE"], instrument_type=[], snapshot_interval_seconds=60):
116+
def add_exchange_subscription(self, exchange: Union[str, list], callback, service="REAL_TIME", subscription_type=["QUOTE"], instrument_type=[], snapshot_interval_seconds=60, bulk_subscription_filters=[]):
113117
"""Subscribe to [Market Data] by Barchart Exchange code(s).
114118
115119
Complete list of [SubscriptionTypes]. List of [Service] types.
@@ -121,18 +125,21 @@ def add_exchange_subscription(self, exchange: Union[str, list], callback, servic
121125
exchange: str, list
122126
Barchart Exchange code(s)
123127
service: str, optional
124-
Default is `REAL_TIME` for delayed market data, set it to `DELAYED`, or for snapshots set it as one of `REAL_TIME_SNAPSHOT` or `DELAYED_SNAPSHOT'
128+
[ServiceType]. Default is `REAL_TIME` for delayed market data, set it to `DELAYED`, or for snapshots set it as one of `REAL_TIME_SNAPSHOT` or `DELAYED_SNAPSHOT'
125129
callback: Callable
126130
Your callback function for Market Data messages
127131
subscription_type: list, optional
128-
Default is ['QUOTE']. Can contain any of: 'ALL', 'QUOTE', 'QUOTE_PARTICIPANT', 'DEPTH_PRICE', 'DEPTH_ORDER', 'TRADES', 'OHLC'
132+
[SubscriptionTypes]. Default is ['QUOTE']. Can contain any of: 'ALL', 'QUOTE', 'QUOTE_PARTICIPANT', 'DEPTH_PRICE', 'DEPTH_ORDER', 'TRADES', 'OHLC'
129133
instrument_type: list, optional
130-
Spreads and Options must be explicitly requested. Can contain any of: 'SPREAD', 'OPTION', 'FUTURE', 'FOREX', 'EQUITY', 'INDEX', 'MUTUAL_FUND', 'MONEY_MARKET', 'MONEY_MARKET_FUND'
134+
[InstrumentTypes]. Spreads and Options must be explicitly requested. Can contain any of: 'SPREAD', 'OPTION', 'FUTURE', 'FOREX', 'EQUITY', 'INDEX', 'MUTUAL_FUND', 'MONEY_MARKET', 'MONEY_MARKET_FUND'
135+
bulk_subscription_filters: list of BulkSubscriptionFilter, optional
136+
List of [BulkSubscriptionFilter]
131137
132138
[Market Data]: https://docs.barchart.com/openfeed/#/proto?id=marketupdate
133139
[SubscriptionTypes]: https://docs.barchart.com/openfeed/#/proto?id=subscriptiontype
134-
[Service]: https://docs.barchart.com/openfeed/#/proto?id=service
140+
[ServiceType]: https://docs.barchart.com/openfeed/#/proto?id=service
135141
[InstrumentTypes]: https://docs.barchart.com/openfeed/#/proto?id=instrumentdefinitioninstrumenttype
142+
[BulkSubscriptionFilter]: https://docs.barchart.com/openfeed/#/proto?id=bulksubscriptionfilter
136143
"""
137144
exchanges = []
138145

@@ -146,11 +153,11 @@ def add_exchange_subscription(self, exchange: Union[str, list], callback, servic
146153
self.exchange_handlers[exch] = []
147154

148155
self.exchange_handlers[exch].append(Listener(
149-
exchange=exch, callback=callback, service=service, subscription_type=subscription_type, instrument_type=instrument_type, snapshot_interval_seconds=snapshot_interval_seconds))
156+
exchange=exch, callback=callback, service=service, subscription_type=subscription_type, instrument_type=instrument_type, snapshot_interval_seconds=snapshot_interval_seconds, bulk_subscription_filters=bulk_subscription_filters))
150157

151158
if self.token is not None:
152-
self._send_message(
153-
self.__create_subscription_request(exchanges=exchanges, service=service, subscription_type=subscription_type, instrument_type=instrument_type, snapshot_interval_seconds=snapshot_interval_seconds))
159+
self._send_message(
160+
self.__create_subscription_request(exchanges=exchanges, service=service, subscription_type=subscription_type, instrument_type=instrument_type, snapshot_interval_seconds=snapshot_interval_seconds, bulk_subscription_filters=bulk_subscription_filters))
154161

155162
return self
156163

@@ -371,22 +378,34 @@ def handleOHLC(msg):
371378
}
372379

373380
def on_message(ws: websocket.WebSocketApp, message):
381+
byte_buffer = BytesIO(message)
382+
total = byte_buffer.getbuffer().nbytes
374383

375-
msg = openfeed_api_pb2.OpenfeedGatewayMessage()
376-
msg.ParseFromString(message)
384+
msg_count = 0
377385

378-
msg_type = msg.WhichOneof("data")
386+
while byte_buffer.tell() != total:
387+
msg_len = int.from_bytes(byte_buffer.read(
388+
2), byteorder='big', signed=True)
379389

380-
handler = handlers.get(
381-
msg_type, lambda x: print("Unhandled Message:", x))
390+
msg = openfeed_api_pb2.OpenfeedGatewayMessage()
391+
msg.ParseFromString(byte_buffer.read(msg_len))
382392

383-
try:
384-
handler(msg)
385-
except Exception as e:
386393
if self.debug:
387-
print("Failed handling incoming message:", msg_type, e)
388-
traceback.print_exc()
389-
self.__callback(self.on_error, e)
394+
msg_count = msg_count+1
395+
print("msg len:", msg_len, "number of messages:", msg_count)
396+
397+
msg_type = msg.WhichOneof("data")
398+
399+
handler = handlers.get(
400+
msg_type, lambda x: print("Unhandled Message:", x))
401+
402+
try:
403+
handler(msg)
404+
except Exception as e:
405+
if self.debug:
406+
print("Failed handling incoming message:", msg_type, e)
407+
traceback.print_exc()
408+
self.__callback(self.on_error, e)
390409

391410
def on_error(ws, error):
392411
if self.debug:
@@ -473,7 +492,7 @@ def __send_existing_interest(self):
473492
listeners_by_service = interest[l.service]
474493
if l.key() not in listeners_by_service:
475494
listeners_by_service[l.key()] = Listener(
476-
symbol=l.symbol, exchange=l.exchange, service=l.service, subscription_type=l.subscription_type, instrument_type=l.instrument_type, snapshot_interval_seconds=l.snapshot_interval_seconds)
495+
symbol=l.symbol, exchange=l.exchange, service=l.service, subscription_type=l.subscription_type, instrument_type=l.instrument_type, snapshot_interval_seconds=l.snapshot_interval_seconds, bulk_subscription_filters=l.bulk_subscription_filters)
477496
else:
478497
existing = listeners_by_service[l.key()]
479498
existing.subscription_type = list(set(
@@ -483,13 +502,19 @@ def __send_existing_interest(self):
483502
for service in interest.keys():
484503
for i in interest[service].values():
485504
self._send_message(
486-
self.__create_subscription_request(exchanges=i.exchanges(), symbols=i.symbols(), service=service, subscription_type=i.subscription_type, instrument_type=i.get_instrument_types(), snapshot_interval_seconds=i.snapshot_interval_seconds))
505+
self.__create_subscription_request(exchanges=i.exchanges(),
506+
symbols=i.symbols(),
507+
service=service,
508+
subscription_type=i.subscription_type,
509+
instrument_type=i.get_instrument_types(),
510+
snapshot_interval_seconds=i.snapshot_interval_seconds,
511+
bulk_subscription_filters=i.bulk_subscription_filters))
487512

488513
# send other rpc requests
489514
for req in self.request_id_handlers.values():
490515
req.send(self)
491516

492-
def __create_subscription_request(self, exchanges=[], symbols=[], service="REAL_TIME", subscription_type=["QUOTE"], instrument_type=[], snapshot_interval_seconds=60):
517+
def __create_subscription_request(self, exchanges=[], symbols=[], service="REAL_TIME", subscription_type=["QUOTE"], instrument_type=[], snapshot_interval_seconds=60, bulk_subscription_filters=[]):
493518
requests = []
494519

495520
if len(exchanges) > 0:
@@ -500,7 +525,9 @@ def __create_subscription_request(self, exchanges=[], symbols=[], service="REAL_
500525
t) for t in subscription_type],
501526
snapshotIntervalSeconds=snapshot_interval_seconds,
502527
instrumentType=[openfeed_instrument_pb2.InstrumentDefinition.InstrumentType.Value(
503-
t) for t in instrument_type]
528+
t) for t in instrument_type],
529+
bulkSubscriptionFilter=[openfeed_api_pb2.BulkSubscriptionFilter(
530+
symbolType=f.symbolType, symbolPattern=f.symbolPattern) for f in bulk_subscription_filters]
504531
))
505532

506533
if len(symbols) > 0:
@@ -568,8 +595,11 @@ def __create_instrument(self, symbol):
568595
)
569596

570597
def __create_login_request(self):
598+
client_version = "sdk_python_version={}:python_version={}:sys_version:{}".format(
599+
VERSION, platform.python_version(), sys.version)
571600
return openfeed_api_pb2.OpenfeedGatewayRequest(
572601
loginRequest=openfeed_api_pb2.LoginRequest(
602+
protocolVersion=1, clientVersion=client_version,
573603
username=self.username, password=self.password))
574604

575605
def __callback(self, callback, *args):
@@ -581,14 +611,15 @@ def __callback(self, callback, *args):
581611

582612

583613
class Listener(object):
584-
def __init__(self, symbol="", exchange="", callback=None, service="REAL_TIME", subscription_type=["QUOTE"], instrument_type=[], snapshot_interval_seconds=60):
614+
def __init__(self, symbol="", exchange="", callback=None, service="REAL_TIME", subscription_type=["QUOTE"], instrument_type=[], snapshot_interval_seconds=60, bulk_subscription_filters=[]):
585615
self.symbol = symbol
586616
self.exchange = exchange
587617
self.callback = callback
588618
self.service = service
589619
self.subscription_type = subscription_type
590620
self.instrument_type = instrument_type
591621
self.snapshot_interval_seconds = snapshot_interval_seconds
622+
self.bulk_subscription_filters = bulk_subscription_filters
592623

593624
def key(self):
594625
if len(self.exchange) > 0:
@@ -638,6 +669,12 @@ def is_type(self, request_type):
638669
return request_type == self.request.WhichOneof("data")
639670

640671

672+
class BulkSubscriptionFilter(object):
673+
def __init__(self, symbolType, symbolPattern):
674+
self.symbolType = symbolType
675+
self.symbolPattern = symbolPattern
676+
677+
641678
if __name__ == "__main__":
642679

643680
def handle_message(msg):

openfeed/version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERSION = '1.3.0'

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "openfeed"
3-
version = "1.2.0"
3+
version = "1.3.0"
44
description = "Python SDK for Openfeed"
55
authors = ["Barchart <[email protected]>"]
66
license = "MIT"

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
setup(
1919
name='openfeed',
20-
version='1.2.0',
20+
version='1.3.0',
2121
author='Barchart',
2222
author_email='[email protected]',
2323
license='MIT',

0 commit comments

Comments
 (0)