Implementing Historic Search

This page provides a guide to the changes you need to make to your integration adapters to implement FX Sales' Historic Search feature.

Requirements

To provide a backend implementation of the Historic Search, you require the following:

  • Front end: Caplin FX Sales 2.13 or greater

  • Back end: A trading integration adapter based on the FX Integration API 3.49 or greater.

Implementation checklist

To enable the use of Historic Search in your instance of Caplin FX Sales, perform each of the tasks below:

  • Create an implementation of a search blotter listener

  • Create an implementation of a configuration provider

  • Grant user permissions

  • Enable Historic Search

For a working example of an adapter with a Historic Search implementation, see the Novo Trading Adapter example in the FX Integration API Kit (v3.50 or greater).

Implementing a search blotter provider

To implement a search blotter provider, follow the steps below:

  1. Create an implementation of the SearchBlotterListener interface:

    com.caplin.motif.fx.searchSearchBlotterListenerinitialise(BlotterConfiguration, BlotterItemFactory)searchChannelOpened(SearchBlotterChannel, SearchExpression)searchChannelClosed(SearchBlotterChannel)MySearchBlotterListenerinitialise(BlotterConfiguration, BlotterItemFactory)searchChannelOpened(SearchBlotterChannel, SearchExpression)searchChannelClosed(SearchBlotterChannel)
  2. In your implementation’s searchChannelOpened method, perform the following tasks:

    1. Refering to the SearchExpression parameter, build a query to a source of historical data (for example, a database). For more information see Search expression syntax and Parsing a search expression.

    2. For each trade returned by your query, create a blotter item using the BlotterItemFactory instance passed to your implementation in the initialise method. Publish each blotter item with the SearchBlotterChannel.sendBlotterItem method.

  3. Register your implementation of SearchBlotterListener with an instance of FXTradeBlotterAdapter:

    FXTradeBlotterAdapter blotterAdapter = new FXTradeBlotterAdapter(dataSource);
    blotterAdapter.registerSalesExecutionSearchBlotterListener(
        new MySearchBlotterListener());

Search expression syntax

The Historic Search subject takes the following format:

/PRIVATE/FX/SALES/BLOTTER/SEARCH/EXECUTION/searchFilter=search_expression

The search_expression is a string with the following format:

image/svg+xml ( field == != >= <= > < value ) & |
Boolean operators (in order of precedence)
Operator Description

&

Logical AND

|

Logical OR

Comparators
Comparator Description

==

Equals

!=

Not Equals

>=

Greater Than or Equal To

<=

Less Than or Equal To

>

Greater Than

<

Less Than

Example search subjects:

  • /PRIVATE/FX/SALES/BLOTTER/SEARCH/EXECUTION/searchFilter=Status==Completed

  • /PRIVATE/FX/SALES/BLOTTER/SEARCH/EXECUTION/searchFilter=Status==Completed&(Amount>1000&Amount<=2000)

Parsing a search expression

The FX Integration API parses the search expression and passes it to the SearchBlotterListener.searchChannelOpened method as a parse tree of SearchExpression objects.

com.caplin.motif.fx.searchSearchExpressionSearchExpression.Type getType()String getText()SearchOperator getOperator()SearchComparator getComparator()SearchExpression getLeft()SearchExpression getRight()SearchOperatorANDORSearchComparatorEQGTGTELTLTENESearchExpression.TypeCOMPARATOROPERATORTEXT
SearchExpression class and associated enumerators

Each node in the tree is one of three types:

  • SearchExpression.Type.OPERATOR: a boolean operator (AND, OR)

  • SearchExpression.Type.COMPARATOR: a comparator (==, !=, <, >, <=, >=)

  • SearchExpression.Type.TEXT: a string operand (a field name or a field value)

For example, the parse tree for the search expression Field1=ABCDE&Field2>=1000 would be as follows:

«SearchExpression»node1type = OPERATORoperator = AND«SearchExpression»node2type = COMPARATORcomparator = EQ«SearchExpression»node3type = COMPARATORcomparator = GTE«SearchExpression»node4type = TEXTtext = "Field1"«SearchExpression»node5type = TEXTtext = "ABCDE"«SearchExpression»node6type = TEXTtext = "Field2"«SearchExpression»node7type = TEXTtext = "1000"leftrightleftrightleftright
Parse tree for the search expression "Field1==ABCDE&Field2>=1000"

You can use the SearchExpression parse tree to build a query to a source of historical data. The example below shows a recursive method that builds a WHERE clause for a SQL query. To keep the example simple, the method makes the following assumptions:

  • Trading history is stored in one database table

  • Database column names are identical to FX Sales field names

  • All columns are string types (TEXT, VARCHAR, CHAR, …​)

Building a WHERE clause from a SearchExpression parse tree
private String buildWhereClause(SearchExpression se, String tableName)
{
    StringBuilder sb = new StringBuilder();
    switch (se.getType()) {
        case OPERATOR:
            switch (se.getOperator()) {
                case AND:
                    sb.append("(");
                    sb.append(buildWhereClause(se.getLeft(), tableName));
                    sb.append(" AND ");
                    sb.append(buildWhereClause(se.getRight(), tableName));
                    sb.append(")");
                    break;
                case OR:
                    sb.append("(");
                    sb.append(buildWhereClause(se.getLeft(), tableName));
                    sb.append(" OR ");
                    sb.append(buildWhereClause(se.getRight(), tableName));
                    sb.append(")");
                    break;
            }
            break;
        case COMPARATOR:
            String fieldName = se.getLeft().getText();
            String fieldValue = se.getRight().getText();
            sb.append(tableName).append(".").append(fieldName);
            switch (se.getComparator()) {
                case EQ:
                    sb.append("==");
                    break;
                case NE:
                    sb.append("!=");
                    break;
                case GTE:
                    sb.append(">=");
                    break;
                case LTE:
                    sb.append("<=");
                    break;
                case GT:
                    sb.append(">");
                    break;
                case LT:
                    sb.append("<");
                    break;
            }
            sb.append("'").append(fieldValue).append("'");
            break;
        case TEXT:
            break;
    }
    return sb.toString();
}

Implementing a search configuration provider

FX Sales automatically generates the search-criteria panel based on search-field metadata received from the server:

historic search overview
Historic Search feature’s user interface

Search-criteria metadata is provided by the FX Integration API’s Configuration Service. The Configuration Service is implemented using a CachedObjectProvider that provides a Config object:

com.caplin.motif.datasourcecom.caplin.motif.fx.configCachedObjectProviderinitialise()onRequest()onDiscard()ConfigSearchConfig getExecutionSearchConfig()provides

To provide FX Sales with search-criteria metadata, follow the steps below:

  1. Add the following object mapping to your adapter’s rttpd.conf file:

    object-map "/PRIVATE/FX/CONFIG" "/PRIVATE/%u/FX/CONFIG"
  2. Add the following include pattern to the add-data-service block in your adapter’s rttpd.conf file

    include-pattern "^/PRIVATE/[^/]+/FX/CONFIG"
  3. Create an implementation of the Config interface. In the getExecutionSearchConfig() method, instantiate and return a SearchConfig object. The SearchConfig.getSearchFields() method returns a List of SearchField objects, each describing a search criteria field.

    com.caplin.motif.fx.configcom.caplin.motif.fx.searchConfigSearchConfig getExecutionSearchConfig()SearchConfigList<SearchField> getSearchFields()MyConfigSearchConfig getExecutionSearchConfig()
  4. Create an implementation of CachedObjectProvider (MyConfigProvider in the diagram below). In the onRequest method, instantiate and publish an instance of MyConfig.

    com.caplin.motif.datasourceCachedObjectProviderinitialise()onRequest()onDiscard()MyConfigProviderinitialise()onRequest()onDiscard()MyConfigSearchConfig getExecutionSearchConfig()provides
  5. In the initialisation code for your integration adapter, instantiate a MyConfigProvider and register it as the configuration provider for a FXTradeAdapter instance:

    fxTradeAdapter.registerConfigProvider(new MyConfigProvider())
    com.caplin.motif.fx.tradingFXTradeAdapterregisterConfigProvider()MyConfigProviderinitialise()onRequest()onDiscard()registers

For a working example of an adapter with a Historic Search implementation, see the Novo Trading Adapter example in the FX Integration API Kit (v3.50 or greater).

Granting permissions

To allow a user to request Historic Search subjects, grant the user (or a group the user belongs to) the following permissions in the global namespace:

Historic Search permission
Field Description

Action

VIEW

Product

^/PRIVATE/%u/FX/SALES/BLOTTER/SEARCH/EXECUTION/.*

Namespace

Authorisation

ALLOW

Filtered Historic Search permission
Field Description

Action

VIEW

Product

^/FILTER/PRIVATE/%u/FX/SALES/BLOTTER/SEARCH/EXECUTION/.*

Namespace

Authorisation

ALLOW

Configuration service permission
Field Description

Action

VIEW

Product

^/PRIVATE/%u/FX/CONFIG

Namespace

Authorisation

ALLOW

Caplin FX Sales uses Caplin’s Permissioning Service to authenticate and authorise users. For more information on the Caplin Permissioning Service and the terms used in the tables above, see Caplin Platform Architecture > Permissioning.

By default, the Historic Search feature is disabled. To enable this feature in FX Sales, set the the configuration option HISTORIC_SEARCH.ENABLED to the boolean value true.


See also: