The BidFX .NET API is a price API that connects to the BidFX trading platform to subscribe to realtime pricing. The API supports the following features:
- FX streaming executable prices (RFS)
- FX request for quote (RFQ)
- FX and Futures trading from .NET is available via the REST.
Most users of the API will trade against the received prices. Trading is achieved by making RESTful requests to post orders via the BidFX REST API. The REST API is easily accessed using .NET.
BidFX is the market-leading provider of electronic trading solutions for the global foreign exchange marketplace. BidFX has addressed the challenges of the FX market by introducing a complete suite of negotiation protocols – which include: auto-routing, streaming, request-for-quote, voice, algo-trading and best execution – via a cloud-based SaaS trading platform. BidFX offer clients access to a cutting edge, broker-neutral, Execution Management System (EMS) backed by a hub to all major bank's algo suites. You can read about all BidFX products on the BidFX Website.
BidFX clients generally access the trading platform via a dedicated User Interface (UI) either on their desktop PC, web browser or mobile device. APIs provide a secondary means of accessing the trading platform that can either supplement the UI or replace it entirely for some use cases, including systematic trading, OMS integration and market intelligence. BidFX place significant emphasis on API support and therefore provide a suite of APIs for different high-level programming languages and data exchange protocols.
You can read about the complete BidFX API range, and their different capabilities, at BidFX API Overview.
This document describes the BidFX Public API for .NET. The .NET API is written in C#. All of the code examples below are also presented in C#. To maximise compatability across the potential community of users, we have built the API targetting .NET Framework v4.7.1. We use the tag Public to indicated that, this API is designed and maintained for public use by BidFX clients. Being public implies a degree of support, API stability and future compatibility appropriate to client usage.
Liquidity Providers (LPs) mostly publish tradeable price/quotes into the BidFX platform using the FIX protocol. The quotes from LPs are firm prices with an associated price ID that needs to be attached to any order made against the quote. The BidFX platform consumes billions for FIX messages per day. We provision high-bandwidth, cross-connect circuits in the main global data centres for this purpose. BidFX cross-connect where most banks host their price engines, in particular in:
- London (LD4),
- New York (NY4)
- Singapore (SG1) and
- Tokyo (TY3).
FX quotes are short-lived and LPs reserve the right of last look. A quote usually is good for no more than a few hundred milliseconds. Network latency between the client application and the LP is therefore a significant consideration if we want to avoid rejections. If clients intend to trade directly against price IDs, then it is recommended that they run their application very close to the source of liquidity and cross-connect within the same data centre if possible. Alternatively, clients may route their orders to the BidFX Strategy Server to minimise both rejections and slippage.
The BidFX .NET API implements a highly optimised, bespoke binary protocol to deliver realtime quotes from LPs directly to into a client's application with minimal latency. The binary delivery mechanism is approximately 80 times more efficient than the FIX protocol. Furthermore, using the publish and subscribe paradigm, BidFX servers publish only those quotes that are subscribed to.
As part of the BidFX Open Source initiative, we have published the source code for the BidFX C# API on the BidFX Github Page. You can clone the API with the following command.
git clone https://github.com/bidfx/bidfx-api-dotnet.git
For C# development we recommend an integrated development environment (IDE) designed for programming in C#. We use the Rider IDE from JetBrains. You can configure Rider as follows.
The Price API makes use of a publish-subscribe paradigm in which clients register for price updates by subscribing on subjects. Subjects identify individual products or instruments for which realtime pricing may be obtained. Liquidity providers 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.
The namespace BidFX.Public.API.Price
contains all of the classes, methods and event handlers that are necessary to
subscribe to pricing from several pricing services.
The common usage patten for the API involves:
- Creating a connection to BidFX's externally accessible pricing service.
- Subscribe to subjects representing instruments from multiple price providers.
- Receive status and realtime price updates on each subscription.
- Unsubscribe from price subjects to stop receiving updates.
When using the API the main class you create is called a Client
although you will not normally use this class
directly.
Since most applications use only a single 'Client', they usually get access to it via the
singleton BidFX.Public.API.DefaultClient
.
var client = BidFX.Public.API.DefaultClient.Client
client.Host = "api.ld.bidfx.biz";
client.Username = "myusername";
client.Password = "mysecretpassword!";
client.ProductSerial = "f2d332674ddfe0b02833979c";
It doesn't matter what order you configure your details, but do ensure you set all those fields listed in the following table:
Name | Description | Type |
---|---|---|
Host | The hostname of the BidFX POP. The host determines the environment. | std::string |
Username | The username to authenticate with. Must be a valid API user for the environment. | std::string |
Password | The password of the selected user. | std::string |
ProductSerialNumber | The serial number assigned by BidFX for the product. Used for product licensing purposes. | std::string |
Once you have created you Client
with the correct properties, you can then get the price session and the price
subscriber like so:
var priceSession = client.PriceSession;
var priceSubscriber = client.PriceSubscriber;
This will initiate the connection to the price services and await subscriptions from you. If you encounter an error at this point then this is most likely a result of a mis-configuration of one of more of the above properties.
Real-time price updates are forwarded to the client application via the standard .NET event pattern. To register for price updates, you create a callback method (event delegate) to handle the published events. The method signature should be as follows:
void OnPriceUpdate(object source, PriceUpdateEvent priceEvent)
You then associate your callback with the event mechanism by adding it to the session's field-like event
handler PriceUpdateEventHandler
. For example:
// Define an event delegate for handling price update events.
private void OnPriceUpdate(object source, PriceUpdateEvent priceEvent)
{
IPriceMap price = priceEvent.AllPriceFields;
decimal bid = price.DecimalField(FieldName.Bid) ?? 0.0m;
decimal ask = price.DecimalField(FieldName.Ask) ?? 0.0m;
Console.WriteLine(priceEvent.Subject + " bid=" + bid + " ask=" + ask);
}
// Register the callback with the session's price update event mechanism.
priceSession.PriceUpdateEventHandler += OnPriceUpdate;
// If required, later de-register the callback from the session's price update event mechanism.
priceSession.PriceUpdateEventHandler -= OnPriceUpdate;
If the price service cannot provide a price for a subject for some reason, it will publish a subscription status event against the problem subject. Again the standard .NET event pattern is employed to manage these events. To register for status updates, you create a callback method (event delegate) to handle the event. The method signature should be as follows:
void OnSubscriptionStatus(object source, SubscriptionStatusEvent statusEvent)
You then associate your callback with the event mechanism by adding it to the session's field-like event handler SubscriptionStatusEventHandler. We recommend that all applications register for subscription status events. You can also unsubscribe from status updates if you are so inclined. For example:
// Define an event delegate for handling status updates.
private void OnSubscriptionStatus(object source, SubscriptionStatusEvent statusEvent)
{
Console.WriteLine(subscriptionStatusEvent.Subject + " " + subscriptionStatusEvent.Status + " - " + statusEvent.Reason);
}
// Register for status updates.
priceSession.SubscriptionStatusEventHandler += OnSubscriptionStatus;
// If required, later de-register from status updates.
priceSession.SubscriptionStatusEventHandler -= OnSubscriptionStatus;
To check if the session is ready for use, use the Ready property of the ISession.
if (priceSession.Ready) {
// the session is ready
}
else
{
// the session is not ready
}
It is not necessary to wait until the pricing connections are ready before making subscriptions but some applications
need this capability.
Should you wish to wait until the pricing connections are ready, then you can call the method WaitUntilReady
on the
price session.
This method takes a System.TimeSpan
as a timeout interval.
If the pricing connections become ready within the given time interval then the method returns true.
If the pricing connections are still not ready by the end of the time interval then it returns false.
In the latter case you might then take the opportunity to output the status of each connection - accessible
via ProviderProperties
- to find out why that connection failed to ready itself.
For example:
if (priceSession.WaitUntilReady(TimeSpan.FromSeconds(30)))
{
// pricing session is ready for subscriptions
}
else
{
if (priceSession.ProviderProperties().Any(pp => ProviderStatus.Unauthorized == pp.ProviderStatus)
{
// invalid credentials
}
priceSession.stop();
}
A BidFX.Public.API.Subject.Subject
is immutable and therefore cannot be edited once created - we have therefore
provided a builder class (BidFX.Public.API.Subject.SubjecBuilder
) to allow for easy subject creation.
We have provided static helper methods in BidFX.Public.API.Subject.CommonSubjects
to help create some likely subjects.
Common component values are provided as string constants in BidFX.Public.API.Price.Subject.CommonComponents
to help
reduce typing errors.
Below are some examples on how to create subjects in different ways:
var spotStreamingSubject = CommonSubjects.CreateLevelOneSpotStreamingSubject("My_Account", "EURUSD", "UBSFX", "EUR", "1000000.00");
var ndfQuoteSubject = CommonSubjects.CreateLevelOneNdfQuoteSubject("My_Account", "USDKRW", "UBSFX", "USD", "1000000.00", "", "20170707");
var spotDepthSubject = CommonSubjects.CreateLevelTwoSpotStreamingSubject("My_Account", "EURUSD", "USD", "1000000.00");
var indicativeSubject = CommonSubjects.CreateIndicativePriceSubject("EURUSD");
// To create your own from scratch yourself you can do so like this:
new SubjectBuilder()
.SetComponent(SubjectComponentName.AssetClass, CommonComponents.Fx)
.SetComponent(SubjectComponentName.Level, "1")
.SetComponent(SubjectComponentName.LiquidityProvider, "Indi")
.SetComponent(SubjectComponentName.CcyPair, "EURUSD")
.CreateSubject();
When creating a Premium FX stream the following fields can be used:
Name | Type | Description | Required | Example Value |
---|---|---|---|---|
CcyPair | string | The currency pair | Y | "EURUSD" |
Tiered | Boolean | Whether to return multiple levels tiered on Quantity | Y | False |
CrossCurrencyPairs | Boolean | Whether to cross the currency pairs for non USD currency pairs | Y | False |
When creating an Indicative price stream the following fields can be used:
Name | Type | Description | Required | Example Value |
---|---|---|---|---|
CcyPair | string | The currency pair | Y | "EURUSD" |
FieldName | string | The field to be displayed from the price subscription | Y | "Bid" |
Field | Value | Notes |
---|---|---|
BuySideAccount |
Either a BuySideAccount (your account with BidFX to attach to the order) or the name of an AllocationTemplate must be specified |
|
AllocationTemplate |
||
AssetClass |
Fx |
|
Currency |
||
Quantity |
Also AllocQuantity for each BuySideAllocAccount specified | |
RequestFor |
Stream or Quote |
|
LiquidityProvider |
If Level 2 then FXTS . For Indicative pricing, use Indi and for Premium FX pricing, use PremiumFX |
|
DealType |
Spot , Swap , Outright , NDS , or NDF |
|
CcyPair |
||
Tenor |
Spot , 1W , 2W , 1M , 2M , 1Y , 2Y , etc. |
User can send Tenor or SettlementDate to same effect. If sending both, the SettlementDate will take precedence |
SettlementDate |
YYYYMMDD |
|
Level |
1 or 2 | |
NumAllocs |
If any BuySideAllocAccount specified |
Field | Value | Notes |
---|---|---|
FarTenor |
1W , 2W , 1M , 2M , 1Y , 2Y , etc. |
User can sent FarTenor or FarSettlementDate |
FarSettlementDate |
YYYYMMDD |
|
FarCurrency |
Should be the same as Currency |
|
FarQuantity |
Also FarAllocQuantity for each BuySideAllocAccount specified |
Field | Value | Notes |
---|---|---|
FarTenor |
1W , 2W , 1M , 2M , 1Y , 2Y , etc. |
User can sent FarTenor or FarSettlementDate |
FarSettlementDate |
YYYYMMDD |
Tenor |
---|
TOD |
TOM |
SPOT |
SPOT_NEXT |
1W |
2W |
3W |
1M |
2M |
3M |
4M |
5M |
6M |
7M |
8M |
9M |
10M |
11M |
1Y |
2Y |
3Y |
BD |
IMMH |
IMMM |
IMMU |
IMMZ |
To subscribe or unsubscribe from prices you can call the Subscribe
or Unsubscribe
methods of the price subscriber.
These both accept a BidFX.Public.API.Subject.Subject
to operate on.
var subject = new BidFX.Public.API.Subject.Subject("Level=1,LiquidityProvider=CITIFX,Symbol=EURUSD,Currency=EUR,DealType=Spot");
// Subscribe to the subject.
priceSubscriber.Subscribe(subject);
// Unsubscribe to the subject.
priceSubscriber.Unsubscribe(subject);
Subscriptions are made asynchronously in the background. The Subscribe
method will provide no immediate feedback on
the success of otherwise of its action. The client will however quickly receive feedback on the subscription via one of
the event delegate callback methods. If there is a problem then you will receive a SubscriptionStatus
event. If a
price was obtained then you will receive a PriceUpdate
event. Price updates have an implied status
of SubscriptionStatus.OK
. Note that it is not uncommon for some price feeds to initially
publish SubscriptionStatus.PENDING
, to notify the receipt of a subscription, soon followed by a price update once the
data becomes available from its own remote feed.
Subscribe has an optional argument, autoRefresh
. Setting this to True
will cause the subscription to automatically
resubscribe if the subscription is closed without Unsubscribe
being called on the subject, such as a Quote
subscription expiring.
Upon receiving a price update in the form of PriceUpdateEvent
, you'll want to access the data inside of it. There are
three properties:
- A
Subject
calledSubject
equal to the subject passed toSubscribe
, indicating the instrument you have received an update for. - An
IPriceMap
calledAllPriceFields
containing all current price fields for the instrument. - An
IPriceMap
calledChangedPriceFields
containing only those fields which changed in this most recent update.
The ChangedPriceFields
are always a subset of AllPriceFields
and in some cases, such as the initial snapshot, they
are identical.
Often GUI applications make use of the ChangedPriceFields
to flash the fields that have just updated.
The interface BidFX.Public.API.Price.IPriceMap
, as the name suggests, provides a map of price fields stored as
name-value pairs.
With an IPriceMap
you can enumerate over either all the field names in the map or all of the name-value pairs.
You can also directly access individual fields by their name.
Field names are just strings with short camel-case word list.
All of the likely published field names are defined as string constants in the class
called BidFX.Public.API.Price.FieldName
.
To avoid simple typing errors you should use these constants for accessing specific fields from the IPriceMap
.
For example:
// Your event delegate for price updates
private void OnPriceUpdate(object source, PriceUpdateEvent priceEvent)
{
// You can get either AllPriceFields or ChangedPriceFields - for the purpose of this example I will get AllPriceFields
IPriceMap price = priceEvent.AllPriceFields;
// To enumerate over all the field names as strings you can call:
IEnumerable<string> allFieldNames = price.FieldNames;
// To enumerate over all key-pairs you can call:
IEnumerable<KeyValuePair<string, IPriceField>> allPriceFields = price.PriceFields;
// To access a specific field you can call:
IPriceField bid = price.Field(FieldName.Bid); // the result may be null if the field is absent
// We also include convenience methods for accessing fields of a known type, for example
decimal? ask = price.DecimalField(FieldName.Ask);
long? askSize = price.LongField(FieldName.AskSize);
int? numAsks = price.IntField(FieldName.NumAsks);
string name = price.StringField(FieldName.Name);
DateTime? bidTime = price.DateTimeField(FieldName.BidTime);
}
The table below defines some of the more common field names that can be returned from a subscription.
These are defined as string constants in the class called BidFX.Public.API.Price.FieldName
for convenience.
FieldName | Stream & Quote | Depth | Indicative | PremiumFX |
---|---|---|---|---|
Bid |
X | X | X | |
Bid1 |
X | X | ||
Bid2 |
X | X | ||
Bidn |
X | X | ||
Ask |
X | X | X | |
Ask1 |
X | X | ||
Ask2 |
X | X | ||
Askn |
X | X | ||
BidFirm1 |
X | |||
BidFirm2 |
X | |||
BidFirmn |
X | |||
AskFirm1 |
X | |||
AskFirm2 |
X | |||
AskFirmn |
X | |||
BidTime1 |
X | |||
BidTime2 |
X | |||
BidTimen |
X | |||
AskTime1 |
X | |||
AskTime2 |
X | |||
AskTimen |
X | |||
BidLevels |
X | |||
AskLevels |
X | |||
OriginTime |
X | |||
PriceID |
X | |||
BidSpot |
X | |||
AskSpot |
X | |||
HopLatency1 |
X | |||
HopLatency2 |
X | |||
SystemTime |
X | X | X | |
High |
X | |||
Low |
X | |||
Broker |
X | |||
PercentChange |
X | |||
LastTick |
X | |||
Open |
X | |||
Close |
X | |||
Provider |
X | |||
NetChange |
X | |||
Status |
X | X | ||
Category1 |
X | |||
Category2 |
X | |||
Categoryn |
X |
Time fields returned from a depth subscription are Decimal
fields.
Their value is an integer representing the number of milliseconds since the Unix epoch (Java Time).
We have provided a helper method, BidFX.Public.API.Price.Tools.TimeFieldTools.ToDateTime(decimal timeValue)
to convert
this value into a DateTime object.
Upon receiving a status update in the form of a SubscriptionStatusEvent, you'll want to access the data inside of it. There are three properties:
- A
Subject
calledSubject
gives the subject of the subscription the status update refers to. - A
SubscriptionStatus
containing an enum value that defines the status of the subscription. - A string containing a more detailed reason of the change in status.
The data can be accessed as follows:
// Your event delegate for subscription status events
private void OnStatusUpdate(object source, SubscriptionStatusEvent statusEvent)
{
Subject subject = statusEvent.Subject;
SubscriptionStatus status = statusEvent.SubscriptionStatus;
string reason = statusEvent.StatusReason;
}
Most subscription status' result from temporary issues that can be resolved by the price service. For example, if a
connection goes down and fails over then the API will publish a recoverable status code such
as SubscriptionStatus.STALE
and then quickly reconnect and resume publishing prices again.
Other status codes are more serious and tend to be semi-permanent in nature; examples are, bad symbols, invalid price
source, user authentication or authorisation error. These are normally unrecoverable without some admin user
intervention followed by re-subscribing. That said, in a long running application we need every type of error the be
potentially recoverable in time without the need to restart the application. So if a subscription comes back with a
server status code, such as the liquidity provider's remote feed not running, the API will automatically resubscribe
periodically in an attempt to recover. This re-subscription interval is, by default, a relatively long 5 minutes to
prevent overly stressing the server with bad subscriptions. The time interval can however be configured if required by
setting the session property SubscriptionRefreshInterval
.
When the price session is started it will create and start connections to each of it's providing services. Each service
connection will be initially in a down state with the status ProviderStatus.TemporarilyDown
. As connections come
online or go offline they send status updates in the form of ProviderStatusEvents
. Clients can register callbacks to
receive notification of these events.
// define a callback for handling ProviderStatusEvents
private static void OnProviderStatus(object sender, ProviderStatusEvent providerStatusEvent)
{
Log.Info("provider status: " +
providerStatusEvent.Name + " changed status from " +
providerStatusEvent.PreviousProviderStatus
+ " to " + providerStatusEvent.ProviderStatus
+ " because: " + providerStatusEvent.StatusReason);
}
// register callback with the Pisa session
priceSession.ProviderStatusEventHandler += OnProviderStatus;
It is possible to access the current properties of each provider by calling ProviderProperties
on the price session.
This gives the current status of each provider. For example:
ICollection<IProviderProperties> providerProperties = priceSession.ProviderProperties();
foreach (var providerProperties in providerProperties)
{
Log.Info(providerProperties.Name + " is " + providerProperties.ProviderStatus);
}
When you are finished with the pricing session you should stop the session to free up its resources by calling Stop
.
For
example:
// Stop the session.
BidFX.Public.API.DefaultClient.Client.PriceSession.Stop();
Once you have created your Client
with the correct properties, you can get the trade manager which is able to submit
and query orders.
var tradeSession = client.TradeSession;
The Trade manager has the following workflow:
- User registers an event handler to
TradeSession.OrderSubmitEventHandler
. - User creates an
FxOrder
object, using anFxOrderBuilder
. - User submits the order using
TradeSession.SubmitOrder()
. This returns a locally-unique MessageId. The order submission to the BidFX system is done asynchonously, so you can have multiple messages in-flight at once. - The TradeSession receives a response from BidFX for the order and notifies the event handlers.
- The user can determine which message the callback invocation is for by checking the
MessageId
of theOrderResponse
passed to it.
These are all of the order fields that can be set in FxOrderBuilder. Each of these fields can be set via their corresponding Set method. For example
FxOrder fxOrder = new FxOrderBuilder()
.SetAccount("FX_ACCT")
.SetCurrencyPair("EURGBP")
.SetCurrency("GBP")
.SetDealType("Spot")
.SetHandlingType("stream")
.SetPriceType("Limit")
.SetPrice("1.180000")
.SetQuantity("2000000")
.SetSide("Sell")
.SetSettlementDate("2018-02-07")
.SetReferenceOne(".NET API Example")
.Build();
Field Name | Field Type | Example Data | FX Order Type | Notes |
---|---|---|---|---|
Account |
String | "FX_ACCT" | Any | The account to book the order to. |
AllocationTemplate |
String | Any | The name of a pre-trade allocation template to assign to the order. | |
Currency |
String | "GBP" | Any | The dealt currency, must match either the base or term from the given currency pair. |
CurrencyPair |
String | "EURGBP" | Any | The currency pair being traded. |
DealType |
String | "Spot" | Any | The deal type to be traded. |
FarCurrency |
String | "GBP" | Swap and NDS | The dealt currency of the far leg of a swap order. |
FarFixingDate |
String | "2020-01-01" | NDS | The fixing date of the far leg of a swap order in the format YYYY-MM-DD . |
FarQuantity |
String | "10000000" | Swap and NDS | The dealt quantity of the far leg of a swap. |
FarSettlementDate |
String | "2020-01-01" | Swap and NDS | The settlement date of the far leg of a swap in the format YYYY-MM-DD . |
FarTenor |
String | "2M" | Swap and NDS | The tenor of the far leg of a swap order. |
FixingDate |
String | "2020-01-01" | NDF and NDS | The fixing date in the format YYYY-MM-DD . |
HandlingType |
String | "Stream" | Any | The handling type of the order, see table below for supported types. |
Price |
String | "1.34586" | Any | The price of the order. For an algo this could be the limit or stop price. For a market order the price can be omitted. |
Quantity |
String | "10000000" | Any | The dealt quantity to be traded. Specific in terms of the dealt_ccy. |
ReferenceOne |
String | Any | STP reference. Valid values vary and if in doubt, please ask your account manager. | |
ReferenceTwo |
String | Any | STP reference. Valid values vary and if in doubt, please ask your account manager. | |
SettlementDate |
String | "2018-01-30" | Any | The settlement date in the format "YYYY-MM-DD". |
Side |
String | "Buy" | Any | The side of the order. |
Tenor |
String | "1M" | Any | The tenor of the order. |
- An order must contain either a tenor or settlement date.
- Orders resulting from "request for stream/quote" workflow need to return the last price received from the LP that the order is being routed to.
Deal Type | Description |
---|---|
Spot |
For a spot order |
Forward |
For a forward outright order |
Outright |
For a forward outright order |
Swap |
For a swap order |
NDF |
For a non-deliverable forward outright |
NDS |
For a non-deliverable swap order |
Handling Type | Description |
---|---|
Stream |
Used in request for stream workflow |
Quote |
Used in request for quote workflow |
Automatic |
Used for sending an order to bank algos |
Price Type |
---|
Limit |
Market |
Quoted |
New order submissions and order queries are sent to the server asynchronously, and the response is delivered as a callback to the OrderSubmitEventHandler and OrderQueryEventHandler event handlers. These operate in the standard .NET event pattern.
A callback method (event delegate) should have the following method signature, for both OrderSubmit events and OrderQuery events:
void MethodName(object source, OrderResponse orderResponse)
You then associate your callback with the event mechanism by adding it to the session's field-like event handler PriceUpdateEventHandler. For example:
// Define event delegates for handling order response events.
private void OnOrderSubmitResponse(object source, OrderResponse orderResponse)
{
long messageId = orderResponse.GetMessageId();
string tsOrderId = orderResponse.GetOrderId();
string state = orderResponse.GetState();
//Do something...
}
private void OnOrderQueryResponse(object source, OrderResponse orderResponse)
{
long messageId = orderResponse.GetMessageId();
string tsOrderId = orderResponse.GetOrderId();
string state = orderResponse.GetState();
//Do something...
}
// Register the callbacks with the session's event mechanism.
tradeSession.OrderSubmitEventHandler += OnOrderSubmitResponse;
tradeSession.OrderQueryEventHandler += OnOrderQueryResponse;
// If required, later de-register the callbacks from the session's event mechanism.
tradeSession.OrderSubmitEventHandler -= OnOrderSubmitResponse;
tradeSession.OrderQueryEventHandler -= OnOrderQueryResponse;
The API makes use of the logging framework Serilog for logging important activity and errors. As
a guide we log high volume messages, such as price ticks, at debug level so they are easily disabled via
configuration. Server connection attempts are logged at info level, as are subscriptions. Connections failures are
logged at warn level. All log messages from the API have the SourceContext
property set to their namespace &
class.
We suggest setting the log level to info in a production system to keep the level of logging manageable. If you choose to enable debug logging in production then there will be a performance impact when prices update quickly.
Serilog requires the use of sink packages in order to have somewhere to log to. For example, by using
the Serilog.Sinks.Console
package, we can log to the console with the following configuration:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u4}][{SourceContext}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();