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:

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 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 and RFSTrade no longer extend com.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 and com.caplin.generated.motif.fx.trading.rfs.RFSTrade.

    Create event objects using the createEventName() and sendEventName() methods on ESPTrade and RFSTrade. For example, use RFSTrade.createExecuteAckTradeEvent() to create an ExecuteAckTradeEvent, and use RFSTrade.sendExecuteAckTradeEvent(ExecuteAckTradeEvent event) to send the event.

  • 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.

<field> attributes
Attribute Mandatory Default Value Description

name

Yes

The name of the field.

required

No

true

Fields are required by default. Set to false to define the field as optional.

description

Yes

A description of the field.

isDeprecated

No

false

Set to true to mark the field as deprecated.

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);