Package: fenix-pipeline-sdk
Fenix Pipeline Python SDKThe Fenix Pipeline Python SDK facilitates connection to and interaction with theFenix Pipeline APIfrom any Python application.Prerequisites and NotesThe following should be considered when using the SDK:The Fenix Pipeline API requires an authentication token. Tokens are currently only available to select beta customers.The Python SDK is written usingasynciofor performance. There are not currently specific plans to add other async libraries (such asgevent) but this may be considered in future releases. If you need such support, contact Fenix to discuss.Sample UsageThe following program demonstrates the basic use of the SDK:importasyncioimportosfromfenix_pipelineimportConnectionClosedfromfenix_pipelineimportRawDataSocketfromfenix_pipelineimportSubscriptionTypesfromfenix_pipelineimportTradeasyncdefsimple_sample(event_loop):# read the API key from a local environment variable called `FENIX_API_KEY`socket=RawDataSocket(os.environ.get('FENIX_API_KEY'))# using a context managerasyncwithawaitsocket.connect(message_handler=print_messages)assubscriber:# subscribe to the `btc-usdt` streamawaitsubscriber.subscribe(SubscriptionTypes.TRADES_BY_MARKET,'btc-usdt')# just receive messages for the next 10 secondsawaitsubscriber.monitor(10)# unsubscribe from the `btc-usdt` streamawaitsubscriber.unsubscribe(SubscriptionTypes.TRADES_BY_MARKET,'btc-usdt')# doneasyncdefprint_messages(item):ifisinstance(item,Trade):log.info('received:%r',item)else:log.info('other message:%s',item)event_loop=asyncio.get_event_loop()event_loop.run_until_complete(simple_sample(event_loop))Detailed DocumentationThe SDK uses a context manager to manage connection state and an async coroutine to provide data to the user.RawDataSocket Context ManagerTheRawDataSocketclass provides the context manager and manages all async activity. Create a RawDataSocket by passing an API key, and then callconnect(message_handler)on the object to establish the connection:socket=RawDataSocket(my_api_key)# alternate, condensed versionwithsocket.connect(my_message_handler)assubscriber:# ... interact with server# now sleep this coroutineawaitsubscriber.monitor(10)Note:all following sections assume you are using the namesubscriberas shown above.Once you have a subscriber, you can check it's current connection state with the.connectedproperty:ifnotsubscriber.connected:# the connection has been closedpassYou can let the subscriber receive events for a prescribed amount of time or indefinitely using the.monitor()method:try:# monitor for 10 secondsawaitsubscriber.monitor(timeout=10)# monitor indefinitelyawaitsubscriber.monitor()exceptConnectionClosed:# the connection to the API was closed unexpectedlypassThe subscriber will continue to receive data after the call to.monitor()returns; this is simply a convenience method to sleep the context manager while data is being received.Subscribing to ChannelsWithin the context manager, thesubscriberobject gives you methods for requesting the channels you receive.Trade DataYou can get trade data in three basic subscription types. Each of these types is selectable from theSubscriptionTypesenum:SubscriptionTypes.TRADES_BY_EXCHANGE: all trades on any market within a certain exchangeSubscriptionTypes.TRADES_BY_MARKET: all trades from any exchange associated with a specific market (such asBTC-USDT)SubscriptionTypes.ALL_TRADES: every received trade regardless of market and regardless of exchangefromfenix_pipelineimportSubscriptionTypesubscription_type=SubscriptionTypes.ALL_TRADESAll TradesTo subscribe to all trades:awaitsubscriber.subscribe(SubscriptionTypes.ALL_TRADES)Note:theALL_TRADESsubscription will contain a very large number of trades; ensure your code is performant and your network connection is sufficient to avoid losing data in the stream.Trades by MarketTo subscribe to all trades within a given market (in this example, BTC-USDT):awaitsubscriber.subscribe(SubscriptionTypes.TRADES_BY_MARKET,'btc-usdt')Trades by ExchangeTo subscribe to all trades coming from a given exchange (in this example, Binance):awaitsubscriber.subscribe(SubscriptionTypes.TRADES_BY_EXCHANGE,'binance')Subscription ResponseAfter sending a subscription request, a message will be sent back confirming the channel has been successfully subscribed:awaitsubscriber.subscribe(SubscriptionTypes.TRADES_BY_EXCHANGE,'binance')... will result in the following message being passed into themessage_handlercoroutine:{'type':'subscribed','message':'trades/exchange/binance'}You should then begin receiving trade data from that exchange being passed into themessage_handlercoroutine.If you supply an invalid channel reference:awaitsubscriber.subscribe(SubscriptionTypes.TRADES_BY_MARKET,'btc-%')... will result in the following message being passed into themessage_handlercoroutine:{'type':'error','message':'trades/market/btc-% invalid'}Duplicate DataNote that subscriptions may include overlapping data; for instance, if you subscribe to all trades on the BTC-USDT market and also all trades on the Binance exchange, trades that take place on Binance related to the BTC-USDT market belong to both subscriptions.Data coming from the API may contain duplicate entities as documented in theAPI reference documentation. This behavior applies equally to all subscription types.Unsubscribing from ChannelsUnsubscribing from channels is exactly like subscribing but calling theunsubscribe()method ofsubscriber:awaitsubscriber.unsubscribe(SubscriptionTypes.TRADES_BY_EXCHANGE,'binance')... will result in the following message being passed into themessage_handlercoroutine:{'type':'unsubscribed','message':'trades/exchange/binance'}If you supply a reference to a channel you are not subscribed to:awaitsubscriber.unsubscribe(SubscriptionTypes.TRADES_BY_MARKET,'btc-eudt')... will result in the following message being passed into themessage_handlercoroutine:{'type':'error','message':'trades/market/btc-eudt not subscribed'}Data ModelApart fromsubscribe,unsubscribe, anderrormessages described above, the messages passed into themessage_handlercoroutine will depend on the type of object being transferred. Currently onlyTradeobejcts are supported; other types will be added in the future.Trade DataTrade data will be received as an object of typefenix_pipeline.Trade:>>>repr(trade)Trade(id=binance:btc-usdt:189128621,timestamp=1570839641.248,exchange=binance,pair=btc-usdt,euid=189128621,price=8272.17,quantity=0.012112,direction=buy)>>>trade.timestamp1570839641.248Trades consist of the following fields:exchange: the exchange where the trade was effectedmarket: the market the trade encompasseseuid: the unique ID as reported by the exchange for that tradethe combination ofexchange,market, andeuidis globally unique amongst all trades in the APIdirection:buyorsellprice: a Pythonfloatrepresentation of the price per unit of the tradequantity: a Pythonfloatrepresentation of the quantity tradedtimestamp: a timestamp in seconds since the epoch with millisecond resolutionTrades also have a method_key()that gives a globally-unique key to this trade:>>>trade._key()'binance:btc-usdt:189128621'Note:all floats will be represented by IEEE-754 double-precision values through the PI (with 53 bits of precision, or resolution to 16 significant digits); to ensure you do not lose precision, run only on 64-bit Python implementations.Note:all timestamps can be converted to adatetimeusingdatetime.datetime.utcfromtimestamp(t).astimezone(pytz.utc); the value above resolves todatetime.datetime(2019, 10, 12, 10, 20, 41, 248000, tzinfo=<UTC>)Closing the ConnectionExiting the context manager automatically closes the connection and cleans up all resources.Error ConditionsThe context manager will exit and clean up any resources it is using in any of the following cases:an unhandled exception in user code the context of the context managerthe socket is closed for any reason (by the server, the client, or the transport layer)The socket will close and clean up resources it is using but the context manager will still remain open in the following case:themessage_handlercoroutine raises an unhandled exceptionIt is the responsibility of the user to ensure any exceptions in thiermessage_handlercoroutine are properly handled. This behavior will be simplified in a future version of the SDK.