User Guide¶
Download and installation¶
Installation¶
The library is setup to use the pip
package manager.
It can be installed running pip
as follows.
pip install bidfx-api
Python version¶
BidFX Python API works with Python 3.6 and greater. To check that your have the right version of Python installed use the command:
python --version
You should get some output like Python 3.7.5
.
If you do not have Python, please install the latest 3.x version from python.org or refer to the Installing Python section of the Hitchhiker’s Guide to Python.
Git repository¶
As part of the BidFX Open Source initiative, BidFX intend to published the source code for the Python API on the BidFX Github Page. This source will be released soon. When available, you will be able to clone the API with the following command.
git clone https://github.com/bidfx/bidfx-api-py.git
Session configuration¶
The bidfx
package contains all of the classes,
methods and event handlers that are necessary to subscribe to pricing from
multiple pricing services.
To work with the API, the first thing you need to do is create a Session
.
Session
is the core class that allows you to subscribe to prices and trade via BidFX.
To connect to the BidFX platform the Session must first be configured.
There are three ways to configure the Session:
- by using a default config file located in your home directory
- specifying a named config file from any location
- creating a config in code.
Details of how to configure the API can be found at API Configuration. In our examples we will just use the default method to create and configure a Session as follows.
from bidfx import Session
session = Session.create_from_ini_file()
Pricing API¶
The Pricing API interface is obtained as a property of the Session
.
Pricing makes use of a publish-subscribe paradigm in which clients
register for price updates by subscribing on subjects. A Subject
identifies
a view of an individual instruments for which realtime pricing may be obtained.
LPs publish streams of realtime prices against large numbers of subjects. The BidFX price service matches the client’s subscribed subjects against the total universe of published subjects and forwards on to each client only those price updates that match their subscriptions.
Pricing uses threads to manage asynchronous communication with the price servers. The threads need to be started explicitly. Realtime data is passed back to the user-code via event-handling callback functions. The normal pattern for using the pricing API is:
- configure the Session
- fetch the pricing API from the Session
- register the pricing callback functions
- start the pricing threads
- subscribe to Subjects
Minimal example¶
Here is a small but complete example of a price consuming application:
from bidfx import Session
def on_price_event(event):
print(f"Price update to {event}")
def main():
session = Session.create_from_ini_file()
pricing = session.pricing
pricing.callbacks.price_event_fn = on_price_event
pricing.subscribe(
pricing.build.fx.stream.spot.liquidity_provider("CSFX")
.currency_pair("EURUSD")
.currency("EUR")
.quantity(1000000)
.create_subject()
)
pricing.start()
if __name__ == "__main__":
main()
After subscribing to a Subject, you will start receiving related PriceEvent
notifications via the registered callback function: pricing.callbacks.price_event_fn
.
In addition, if required, whenever the status of a subscription changes a SubscriptionEvent
notification is published via the
registered subscription status callback pricing.callbacks.subscription_event_fn
.
FX streaming example¶
Example of streaming (RFS) firm spot rates direct from LPs
import logging
from bidfx import Session, Subject
def on_price_event(event):
if event.price:
print(
"{} {} {} {} {} -> {}".format(
event.subject[Subject.CURRENCY_PAIR],
event.subject[Subject.LIQUIDITY_PROVIDER],
event.subject[Subject.DEAL_TYPE],
event.subject[Subject.CURRENCY],
event.subject[Subject.QUANTITY],
event.price,
)
)
def on_subscription_event(event):
print(f"Subscription to {event}")
def on_provider_event(event):
print(f"Provider {event}")
def main():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-7s %(threadName)-12s %(message)s",
)
session = Session.create_from_ini_file()
pricing = session.pricing
pricing.callbacks.price_event_fn = on_price_event
pricing.callbacks.subscription_event_fn = on_subscription_event
pricing.callbacks.provider_event_fn = on_provider_event
pricing.start()
pricing.subscribe(
pricing.build.fx.stream.spot.liquidity_provider("DBFX")
.currency_pair("EURUSD")
.currency("EUR")
.quantity(1000000)
.create_subject()
)
pricing.subscribe(
pricing.build.fx.stream.spot.liquidity_provider("DBFX")
.currency_pair("USDJPY")
.currency("USD")
.quantity(5000000)
.create_subject()
)
if __name__ == "__main__":
main()
Callbacks¶
The Price API notifies user-code of various pricing events via a set of callback functions. Separate callback are provided for:
- Price update events (
price_event_fn
forPriceEvent
) - Subscription status events (
subscription_event_fn
forSubscriptionEvent
) - Provider status events (
provider_event_fn
forProviderEvent
)
Events are dispatched through in instance of the class Callbacks
which is a property of the PricingAPI
.
Price field names¶
Price Fields as just strings key-pairs. The field names are simple works or terms such as “Bid”, “Ask” or “AskSize”.
A list of the most common price field names is provided by the class Field
.
Building subjects¶
Because BidFX connects to many different liquidity providers our instrument symbology is necessarily complex.
Each instrument that can be subscribed on is defined by a unique Subject
.
A Subject is an immutable object that looks and behaves similar to a read-only dict
.
It contains many key-value string pairs called Subject components.
FX price Subjects can be particularly large,
especially when it comes to swaps and NDS which are described by many components.
Here are a few example Subjects parsed from strings (not recommended):
from bidfx import Subject
indi_spot = Subject.parse_string("AssetClass=Fx,Exchange=OTC,Level=1,Source=Indi,Symbol=USDCAD")
rfs_spot = Subject.parse_string("AssetClass=Fx,BuySideAccount=GIVE_UP_ACCT,Currency=EUR,DealType=Spot,Level=1,LiquidityProvider=CSFX,Quantity=5000000.00,RequestFor=Stream,Symbol=EURUSD,Tenor=Spot,User=smartcorp_api")
rfq_ndf = Subject.parse_string("AssetClass=Fx,BuySideAccount=GIVE_UP_ACCT,Currency=USD,DealType=NDF,Level=1,LiquidityProvider=DBFX,Quantity=1000000.00,RequestFor=Quote,Symbol=USDKRW,Tenor=1M,User=smartcorp_api")
Subjects are case sensitive. Their components are ordered alphabetically by key. It is important to get the Subject syntax and component spellings right, otherwise the subscription will fail. This is non-trivial for newcomers as Subject formats vary by both asset class and deal type.
To build Subjects correctly, its is best to use a Subject builder which provides
method-chaining to aid syntax discovery and validation to check the result.
The API provides a Subject builder as a property of the PricingAPI
interface.
This allows you to construct to the following types of Subject:
- Indicative FX
- FX Request for Stream (RFS/ESP) - Spot, Forward, NDF
- FX Request for Quote (RFQ) - Spot, Forward, NDF, Swap and NDS
- Future
- Equity
Below are some Subject building examples that produce the same Subjects as the parsed strings above.
from bidfx import Session
pricing = Session.create_from_ini_file().pricing
indi_spot = pricing.build.fx.indicative.spot.currency_pair("USDCAD").create_subject()
rfs_spot = pricing.build.fx.stream.spot.liquidity_provider("CSFX").currency_pair(
"EURUSD").currency("EUR").quantity(5000000).create_subject()
rfq_ndf = pricing.build.fx.stream.spot.liquidity_provider("DBFX").currency_pair(
"USDKRW").currency("USD").quantity(1000000).create_subject()
# To subscribing to pricing
pricing.subscribe(indicative_spot)
# To un-subscribing from pricing
pricing.unsubscribe(indicative_spot)