# Self-Match Detection API

This guide demonstrates how to configure beneficial ownership and identify transfer-trades via the API.

# Overview

The Nekuti matching engine automatically detects when orders from accounts with the same beneficial owner would match against each other. Instead of publishing these as regular trades, the engine routes them through a transfer-trade workflow that:

  • Executes the match and updates positions
  • Marks the execution as non-publishable
  • Excludes the trade from public market data

# Setting Beneficial Ownership

Important: Orders from the same account always implicitly share the same beneficial owner, even if no explicit owner is configured. The owner field is only required when associating multiple accounts under the same beneficial owner.

# Control Gateway Endpoint

POST http://[adminServer]:8181/AccountAdmin/Owner

# Parameters

Parameter Type Required Description
account int64 Yes The account number to configure
owner int64 Yes The beneficial owner identifier (64-bit signed integer)

# Example Request

curl -X POST "http://[adminServer]:8181/AccountAdmin/Owner?account=1001&owner=500"

# Response

{}

A successful response returns HTTP 200 with an empty success confirmation.

# Complete Workflow Example

# Step 1: Configure Account Ownership

Two trading accounts belong to the same beneficial owner:

# Configure Account 1001
curl -X POST "http://[adminServer]:8181/AccountAdmin/Owner?account=1001&owner=500"

# Configure Account 1002
curl -X POST "http://[adminServer]:8181/AccountAdmin/Owner?account=1002&owner=500"

# Step 2: Place Orders

Place opposing orders from both accounts:

# Account 1001 buys 100 BTCUSD @ 60000
curl -X POST "https://paper.nekuti.com:8080/Order" \
  -H "$(nekuti_header_for_account_1001)" \
  -d "symbol=BTCUSD&side=Buy&orderQty=100&price=60000&ordType=Limit&timeInForce=GoodTillCancel"

# Account 1002 sells 100 BTCUSD @ 60000
curl -X POST "https://paper.nekuti.com:8080/Order" \
  -H "$(nekuti_header_for_account_1002)" \
  -d "symbol=BTCUSD&side=Sell&orderQty=100&price=60000&ordType=Limit&timeInForce=GoodTillCancel"

# Step 3: Monitor Execution Reports

Subscribe to the executions WebSocket feed:

wscat -c "ws://[adminServer]/realtime?executions"

# Step 4: Identify Transfer-Trades

The execution reports will contain the selfTradeLastQty field, which will be non-zero for transfer-trades:

{
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "clOrdId": "",
  "account": 1001,
  "symbol": "BTCUSD",
  "side": "Buy",
  "orderQty": 100,
  "price": 60000.0,
  "ordType": "Limit",
  "timeInForce": "GoodTillCancel",
  "execType": "Trade",
  "execId": "exec-0001",
  "lastQty": 100,
  "lastPx": 60000.0,
  "cumQty": 100,
  "leavesQty": 0,
  "ordStatus": "Filled",
  "transactTime": "2025-10-28T10:15:30.123Z",
  "lastLiquidityInd": "RemovedLiquidity",
  "execCost": 166666,
  "execComm": 83,
  "commission": 0.0005,
  "selfTradeLastQty": 100,
  "text": ""
}
{
  "orderId": "660f9511-f3ac-52e5-b827-557766551111",
  "clOrdId": "",
  "account": 1002,
  "symbol": "BTCUSD",
  "side": "Sell",
  "orderQty": 100,
  "price": 60000.0,
  "ordType": "Limit",
  "timeInForce": "GoodTillCancel",
  "execType": "Trade",
  "execId": "exec-0002",
  "lastQty": 100,
  "lastPx": 60000.0,
  "cumQty": 100,
  "leavesQty": 0,
  "ordStatus": "Filled",
  "transactTime": "2025-10-28T10:15:30.123Z",
  "lastLiquidityInd": "AddedLiquidity",
  "execCost": -166666,
  "execComm": 41,
  "commission": 0.00025,
  "selfTradeLastQty": 100,
  "text": ""
}

# Key Observations

# Transfer-Trade Marker

The critical field for identifying transfer-trades is selfTradeLastQty:

"selfTradeLastQty": 100

This field indicates:

  • A non-zero value means this is a transfer-trade (self-match)
  • The value represents the quantity that matched against the same beneficial owner
  • Regular trades will have selfTradeLastQty: 0 or the field may be omitted

# Position and Balance Updates

Despite being marked as non-publishable:

  • Account 1001's position increases by +100 BTCUSD
  • Account 1002's position decreases by -100 BTCUSD
  • Balances are adjusted for cost and commission
  • The trades appear in account history via private APIs

# Public Market Data Exclusion

These executions will NOT appear in:

  • Public trade feeds (/Trade REST endpoint)
  • Public WebSocket trade streams
  • Volume calculations on market data feeds
  • Price formation for public order book data
  • Internal price feed for liquidation checks on last-price marked instruments

# Implementation Pattern

// WebSocket client processing executions
ws.on('message', function(data) {
  const execution = JSON.parse(data);
  
  if (execution.execType === "Trade") {
    // Always update internal position tracking
    updateAccountPosition(execution.account, execution);
    
    // Check if this is a transfer-trade (self-match)
    const isTransferTrade = execution.selfTradeLastQty && execution.selfTradeLastQty > 0;
    
    if (!isTransferTrade) {
      // Regular trade - publish to public feeds
      publishToPublicFeed(execution);
      incrementVolumeStatistics(execution);
      updatePublicPriceTicker(execution);
    } else {
      // Transfer-trade - log for audit but don't publish
      logTransferTrade(execution, execution.selfTradeLastQty);
    }
  }
});

# API Reference Summary

Endpoint Method Purpose
/AccountAdmin/Owner POST Set beneficial owner for an account
ws://paper.nekuti.com:8080/realtime?executions WebSocket Stream execution reports
/AccountAdmin/Trades GET Query historical trades for an account (includes transfer-trades)

# Best Practices

  1. Set ownership at account creation: Configure beneficial owner immediately when creating sub-accounts
  2. Monitor execution feeds: Always check selfTradeLastQty before publishing to market data
  3. Maintain audit trails: Log all transfer-trades for compliance and reconciliation
  4. Update ownership changes: Reconfigure owner associations when beneficial ownership changes
  5. Test detection: Verify self-match detection is working correctly in staging environments before going live