FX Integration API upgrade guide: v2 to v3
The document provides guidance on migrating from version 2 to version 3 of the FX Integration API.
Requirements
The FX Integration API v3 requires Java 8.
Breaking changes
Breaking changes have been introduced in the following components:
Blotter API
The FX Integration API now uses the DataSource Blotter API, com.caplin.datasource.blotter
. The old Blotter API, com.caplin.motif.fx.blotter
, has been removed.
The DataSource Blotter API offers developers the following advantages over the old Blotter API:
-
You can use generic records instead of type 1 records:
BlotterConfiguration.setUseGenericObjects(boolean)
-
You can disable the image flag on the first item sent to the blotter:
BlotterConfiguration.setChannelUsingImageFlag(boolean)
. Disable the image flag when the integration adapter contributes items to a blotter that aggregates items from more than one integration adapter. -
Empty blotter responses can be sent automatically:
BlotterConfiguration.setAutoSendEmptyContainer()
.
For a detailed guide to using the DataSource Blotter API, see Blotter Integration API.
Trading API
The ESP and RFS trading API is now generated directly from the ESP and RFS trade model XML. The API is now more streamlined, consistent, and easier to use.
The packages com.caplin.motif.fx.trading.esp
and com.caplin.motif.fx.trading.rfs
have been replaced by com.caplin.generated.motif.fx.trading.esp
and com.caplin.generated.motif.fx.trading.rfs
respectively.
The Novo Adapter examples provide an overview of how to use the new API.
Most of the generated classes retain the same name as the classes they replace, but there are some significant differences:
-
The generated classes have a different inheritance model.
The new classes favour composition over inheritance. As an example, classes like
ESPTrade
andRFSTrade
no longer extendcom.caplin.motif.fx.trading.FXTrade
. -
Responders are now handled internally, and to create and send events you use methods on
com.caplin.generated.motif.fx.trading.esp.ESPTrade
andcom.caplin.generated.motif.fx.trading.rfs.RFSTrade
. -
Client message validation is now handled internally. The client message validators ESPTradeValidator and RFSTradeValidator have been removed from the FX Integration API 3.
Internal validation of client messages in the FX Integration API 3 is deliberately less strict than validation of client messages in the FX Integration API 2. Validation is restricted to checking that required fields are present and have content. Field formatting is not validated.
Trade model XML
To enable the ESP and RFS trading API to be generated directly from trade model XML, the trade model XML specification has been expanded to include field definitions for transitions.
Field definitions are defined using the <field>
element, a child of the <transition>
element:
<tradeModels>
<tradeModel name="ESP" initialState="Initial">
<state name="Initial">
<transition target="Submitted" trigger="Submit" source="client" utilities="ESPTradeUtilities.vm">
<field name="CurrencyPair"
description="The currency pair for the trade..."/>
<field name="DealtCurrency"
description="The dealt currency for the trade (what the amount is expressed in)..."/>
⋮
</transition>
</state>
⋮
</tradeModel>
⋮
</tradeModels>
See the table below for the attributes of the new <field>
element.
Attribute | Mandatory | Default Value | Description |
---|---|---|---|
|
Yes |
The name of the field. |
|
|
No |
|
Fields are required by default. Set to |
|
Yes |
A description of the field. |
|
|
No |
|
Set to |
Rates
Almost all classes and interfaces in the package com.caplin.motif.fx.rates
have been replaced by classes in the new com.caplin.generated.motif.fx.rates
package.
Quotes, such as com.caplin.generated.motif.fx.rates.QuoteTypesDef.SwapQuote
, now implement the com.caplin.motif.fx.common.Message
interface. Messages supply a map of fields (key-value pairs). Pricing events, such as com.caplin.generated.motif.fx.trading.rfs.events.server.PriceUpdateTradeEvent
, accept a Message
object as a constructor argument.
The quote builders have been re-designed to simplify the building of complex quotes. There is now one builder for swap quotes, removing the need for conditional branching to handle spot-forward swaps and forward-forward swaps. All quotes are built from smaller parts, each one of which implements the Message
interface and supplies a subset of the final quote’s fields. Each quote part has its own builder, with explicit setter methods for each field. Unlike in version 2, there are no calculated quote fields in version 3 — you must explicitly set each field.
Custom fields are now set on the pricing event object, as opposed to the quote builder.
To browse the different quotes and quote parts available in the FX Integration API, see com.caplin.generated.motif.fx.rates
.
Code examples
The code examples in this section compare the old and the new API. For more detailed examples, see the Novo Adapter Examples in the FX Integration API Kit.
Building a swap quote
This section compares building a swap quote using version 2 and version 3 of the FX Integration API.
Building a swap quote (FX Integration API 2)
// The following two variables need to be passed into your method.
boolean firstQuote = null;
FXSwapType fxSwapType = null;
// Declare the quote, and instantiate in one of the following blocks based on type
FXQuote quote = null;
SwapFields swapFields = SwapFieldsBuilder.create()
.swapAskPips("askSwapPips")
.swapRawAskPoints("rawAskSwapPips")
.swapBidPips("bidPips")
.swapRawBidPoints("rawBidSwapPips")
.build();
if( FXSwapType.SPOTFWD == fxSwapType)
{
SwapSpotQuote nearSpotQuote = SwapSpotQuoteBuilder.createNearLegBuilder()
.bidRate("bidSpotRate")
.askRate("askSpotRate")
.build();
SwapFwdQuote farFwdQuote = SwapFwdQuoteBuilder.createFarLegBuilder()
.askRate("askSpotRate", "askAllInPrice", "farAskPips")
.bidRate("bidSpotRate", "bidAllInPrice", "farBidPips")
.build();
quote = new SwapQuote("bidQuoteId", "askQuoteId",
nearSpotQuote, farFwdQuote, swapFields);
}
else if(FXSwapType.FWDFWD == fxSwapType)
{
SwapFwdQuote nearFwdQuote = SwapFwdQuoteBuilder.createNearLegBuilder()
.askRate("askSpotRate", "askAllInRate", "nearAskPips")
.bidRate("bidSpotRate", "bidAllInRate", "nearBidPips")
.build();
SwapFwdQuote farFwdQuote = SwapFwdQuoteBuilder.createFarLegBuilder()
.askRate("askSpotRate", "askAllInRate", "nearAskPips")
.bidRate("bidSpotRate", "bidAllInRate", "nearBidPips")
.build();
quote = new SwapQuote("bidQuoteId", "askQuoteId",
nearFwdQuote, farFwdQuote, swapFields);
}
if (firstQuote)
{
firstQuote = false;
int timeout = 120;
rfsTrade.getPickedUpResponder().sendPriceUpdate(quote, timeout);
}
else
{
rfsTrade.getExecutableResponder().sendPriceUpdate(quote);
}
Building a swap quote (FX Integration API 3)
CommonFields commQuoteFields = CommonFields.newBuilder()
.setOverallTimeOut("overallTimeout")
.setBidQuoteID("bidQuoteID")
.setAskQuoteID("quoteID")
.setTimePriceReceived("timePriceRecieved")
.setSpotRateDPS("spotRateDPS")
.setSpotBidRate("bidSpotRate")
.setSpotAskRate("askSpotRate")
.setDigitsBeforePips("numberOfDigitsBeforePips")
.setNumberOfPips("numberOfPips")
.setSwapGFA("swapGFA")
.setBidPips("bidPips")
.setAskPips("askPips")
.build();
LegFields nearLegFields = LegFields.newBuilder()
.setTenor("nearTenor")
.setSettlementDate("nearSettlementDate")
.setAllInRateDPS("nearAllInRateDps")
.setAllInAskRate("nearAllInAskRate")
.setAllInBidRate("nearAllInBidRate")
.setFwdAskPoints("nearFwdAskPoints")
.setFwdBidPoints("nearFwdBidPoints")
.build();
LegFields farLegFields = LegFields.newBuilder()
.setTenor("farTenor")
.setSettlementDate("farSettlementDate")
.setAllInRateDPS("farAllInRateDps")
.setAllInAskRate("farAllInAskRate")
.setAllInBidRate("farAllInBidRate")
.setFwdAskPoints("farFwdAskPoints")
.setFwdBidPoints("farFwdBidPoints")
.build();
SwapQuoteFields swapFields = SwapQuoteFields.newBuilder()
.setSwapAskPoints("askSwapPoints")
.setSwapBidPoints("bidSwapPoints")
.build();
SwapQuote swapQuote = SwapQuote.newBuilder()
.setCommonFields(commQuoteFields)
.setNearLegFields(nearLegFields)
.setFarLegFields(farLegFields)
.setSwapFields(swapFields)
.build();
PriceUpdateTradeEvent priceUpdateTradeEvent =
new PriceUpdateTradeEvent(rfsTrade, swapQuote);
priceUpdateTradeEvent.addField("custom_field_name_a", "custom_field_value_a");
priceUpdateTradeEvent.addField("custom_field_name_b", "custom_field_value_b");
rfsTrade.sendPriceUpdateEvent(priceUpdateTradeEvent);
Building a sales swap-quote
This section compares building a sales swap-quote using version 2 and version 3 of the FX Integration API.
Building a sales swap quote (FX Integration API 2)
// The following two variables need to be passed into your method
Boolean firstQuote = null;
FXSwapType fxSwapType = null;
// Declare the quote, and instantiate in one of the following blocks based on type
FXQuote quote = null;
SwapFields swapFields = SwapFieldsBuilder.create()
.swapAskPips("askSwapPips")
.swapRawAskPoints("rawAskSwapPips")
.swapBidPips("bidPips")
.swapRawBidPoints("rawBidSwapPips")
.build();
SalesSwapFields salesSwapFields = SalesSwapFieldsBuilder.create()
.defaultSwapAskMargin("defaultSwapAskMargin")
.defaultSwapBidMargin("defaultSwapBidMargin")
.swapFields(swapFields)
.build();
if( FXSwapType.SPOTFWD == fxSwapType)
{
SwapSpotQuote nearSpotQuote = SwapSpotQuoteBuilder.createNearLegBuilder()
.bidRate("bidSpotRate")
.askRate("askSpotRate")
.build();
SalesSwapLegQuote nearSalesSwapSpotQuote = SalesSwapSpotQuoteBuilder.create()
.swapSpotQuote(nearSpotQuote)
.defaultSpotAskMargin("defaultSpotAskMargin")
.defaultSpotBidMargin("defaultSpotBidMargin")
.build();
SwapFwdQuote farFwdQuote = SwapFwdQuoteBuilder.createFarLegBuilder()
.askRate("askSpotRate", "askAllInPrice", "farAskPips")
.bidRate("bidSpotRate", "bidAllInPrice", "farBidPips")
.build();
SalesSwapLegQuote farSalesSalesSwapFwdQuote = SalesSwapFwdQuoteBuilder.create()
.swapFwdQuote(farFwdQuote)
.defaultSpotAskMargin("defaultSpotAskMargin")
.defaultSpotBidMargin("defaultSpotBidMargin")
.build();
quote = new SalesSwapQuote("bidQuoteId", "askQuoteId",
nearSalesSwapSpotQuote, farSalesSalesSwapFwdQuote, salesSwapFields);
}
else if(FXSwapType.FWDFWD == fxSwapType)
{
SwapFwdQuote nearFwdQuote = SwapFwdQuoteBuilder.createNearLegBuilder()
.askRate("askSpotRate", "askAllInRate", "nearAskPips")
.bidRate("bidSpotRate", "bidAllInRate", "nearBidPips")
.build();
SalesSwapLegQuote nearSalesSalesSwapFwdQuote = SalesSwapFwdQuoteBuilder.create()
.swapFwdQuote(nearFwdQuote)
.defaultSpotAskMargin("defaultSpotAskMargin")
.defaultSpotBidMargin("defaultSpotBidMargin")
.build();
SwapFwdQuote farFwdQuote = SwapFwdQuoteBuilder.createFarLegBuilder()
.askRate("askSpotRate", "askAllInRate", "nearAskPips")
.bidRate("bidSpotRate", "bidAllInRate", "nearBidPips")
.build();
SalesSwapLegQuote farSalesSalesSwapFwdQuote = SalesSwapFwdQuoteBuilder.create()
.swapFwdQuote(farFwdQuote)
.defaultSpotAskMargin("defaultSpotAskMargin")
.defaultSpotBidMargin("defaultSpotBidMargin")
.build();
quote = new SalesSwapQuote("bidQuoteId", "askQuoteId",
nearSalesSalesSwapFwdQuote, farSalesSalesSwapFwdQuote, salesSwapFields);
}
if (firstQuote)
{
firstQuote = false;
int timeout = 120;
rfsTrade.getPickedUpResponder().sendPriceUpdate(quote, timeout);
}
else
{
rfsTrade.getExecutableResponder().sendPriceUpdate(quote);
}
Building a sales swap-quote (FX Integration API 3)
The code sample below is dependent on the swapQuote
object generated by the code in the Building a swap quote (FX Integration API 3) section above.
SalesCommonFields salesCommonFields = SalesCommonFields.newBuilder()
.setDefaultSpotAskMargin("spotAskMargin")
.setDefaultSpotBidMargin("spotBidMargin")
.setTraderSpotAskRate("traderSpotAskRate")
.setTraderSpotBidRate("traderSpotBidRate")
.setProfitCurrency("profitCurrency")
.setProfitAskRate("profitAskRate")
.setProfitBidRate("profitBidRate")
.setProfitCurrencyDPS("profitCurrencyDPS")
.build();
SalesLegFields nearSalesLegFields = SalesLegFields.newBuilder()
.setDefaultFwdAskMargin("nearLegFwdAskMargin")
.setTraderAllInAskRate("nearLegTraderAllInAskRate")
.setTraderFwdAskPoints("nearLegTraderFwdAskPoints")
.setDefaultFwdBidMargin("nearLegFwdBidMargin")
.setTraderAllInBidRate("nearLegTraderAllInBidRate")
.setTraderFwdBidPoints("nearLegTraderFwdBidPoints")
.build();
SalesLegFields farSalesLegFields = SalesLegFields.newBuilder()
.setDefaultFwdAskMargin("farLegFwdAskMargin")
.setTraderAllInAskRate("farLegTraderAllInAskRate")
.setTraderFwdAskPoints("farLegTraderFwdAskPoints")
.setDefaultFwdBidMargin("farLegFwdBidMargin")
.setTraderAllInBidRate("farLegTraderAllInBidRate")
.setTraderFwdBidPoints("farLegTraderFwdBidPoints")
.build();
SalesSwapQuoteFields salesSwapFields = SalesSwapQuoteFields.newBuilder()
.setDefaultSwapAskMargin("swapAskMargin")
.setDefaultSwapBidMargin("swapBidMargin")
.setTraderSwapBidPoints("traderSwapBidPoints")
.setTraderSwapAskPoints("traderSwapAskPoints")
.build();
SalesSwapQuote salesSwapQuote = SalesSwapQuote.newBuilder()
.setSwapQuote(swapQuote)
.setSalesCommonFields(salesCommonFields)
.setNearSalesLegFields(nearSalesLegFields)
.setFarSalesLegFields(farSalesLegFields)
.setSalesSwapFields(salesSwapFields)
.build();
PriceUpdateTradeEvent priceUpdateTradeEvent =
new PriceUpdateTradeEvent(rfsTrade, salesSwapQuote);
priceUpdateTradeEvent.addField("custom_field_name_a", "custom_field_value_a");
priceUpdateTradeEvent.addField("custom_field_name_b", "custom_field_value_b");
rfsTrade.sendPriceUpdateEvent(priceUpdateTradeEvent);