# Self-Match Detection

# Purpose

Self-match detection in the Nekuti Matching Engine serves as a compliance and integrity safeguard. Its primary function is to prevent orders from accounts with the same beneficial owner from executing against each other in a way that could be interpreted as wash trading or used to simulate market activity.

# Key Concepts

# Pre-Match Evaluation

Self-match detection is performed during the matching process, prior to trade publication. If two orders are eligible to match and originate from accounts with shared beneficial ownership, the engine diverts the match into a distinct workflow. This evaluation considers:

  • Beneficial ownership metadata attached to each account
  • Order parameters and match eligibility
  • Whether the execution should be marked as publishable to public market data

# Transfer-Trade Workflow

When a self-match is detected:

  • The engine executes a transfer-trade instead of a regular trade
  • The transfer-trade is reported to the participant via standard execution report APIs
  • The execution report includes metadata indicating the trade is non-publishable
  • Consuming systems should filter these trades from public market data feeds based on the indicator

This workflow ensures that self-matches are handled transparently without contributing to public trade volume or price formation when consuming systems properly respect the non-publishable flag.

# Market Integrity

Transfer-trades are designed to:

  • Prevent the appearance of wash trading
  • Avoid artificial volume inflation or tape painting
  • Maintain complete audit trails for participants while excluding trades from public volume and price statistics

# Integration Requirements

  • Participants must configure and maintain accurate beneficial ownership metadata
  • Consuming systems must detect and suppress transfer-trades based on execution report flags
  • No additional configuration is required to enable this feature; it is active by default
  • The following sections will provide execution report examples, metadata specifications, and implementation guidelines for consuming systems.

# Setting Beneficial Ownership

The Control Gateway provides the /AccountAdmin/Owner endpoint to associate accounts with their beneficial owners. The owner is represented as a 64-bit signed integer identifier.

Note: Orders from the same account always implicitly share the same beneficial owner, even if no explicit owner is set via the API. Setting the owner field is only required when multiple accounts need to be associated with the same beneficial owner.

# API Endpoint

POST http://[adminServer]:8181/AccountAdmin/Owner?account={accountNumber}&owner={ownerIdentifier}

# Example: Configuring Accounts with Shared Ownership

Consider a scenario where a trading firm manages multiple accounts for the same client. Account 1001 and Account 1002 both belong to beneficial owner 500:

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

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

Once configured, any orders from accounts 1001 and 1002 that would match against each other will be routed through the transfer-trade workflow.

# Transfer-Trade Detection Example

# Scenario

  • Account 1001 places a buy order for 100 BTCUSD @ 60000
  • Account 1002 places a sell order for 100 BTCUSD @ 60000
  • Both accounts share beneficial owner 500
  • The orders are eligible to match

# Normal Trade vs. Transfer-Trade

Without self-match detection, these orders would execute as a normal trade:

  • Both execution reports would have selfTradeLastQty: 0
  • The trade would appear on public market data feeds
  • The trade would contribute to volume statistics

With self-match detection enabled, the engine diverts to transfer-trade workflow:

  • Both execution reports have a non-zero selfTradeLastQty value equal to the matched quantity
  • The trade is excluded from public market data feeds
  • Account positions and balances are updated normally
  • The trade is visible only to the participant via private execution APIs

# Execution Report Structure

When monitoring executions via the WebSocket feed (ws://[adminServer]/realtime?executions), transfer-trades are identified by the selfTradeLastQty field:

{
  "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "clOrdId": "client-order-001",
  "account": 1001,
  "symbol": "BTCUSD",
  "side": "Buy",
  "execType": "Trade",
  "execId": "exec-12345",
  "lastQty": 100,
  "lastPx": 60000.0,
  "transactTime": "2025-10-28T10:15:30.123Z",
  "selfTradeLastQty": 100,
  ...
}

A non-zero value in selfTradeLastQty signals that:

  1. This execution resulted from a self-match
  2. The trade should not be published to public feeds
  3. Consuming systems should exclude this from public volume calculations
  4. The trade remains valid for internal position tracking and settlement
  5. The selfTradeLastQty value indicates the quantity that matched against the same beneficial owner

# Implementation Guidance

When consuming execution reports:

// Example: Filtering transfer-trades from public market data
function shouldPublishTrade(execution) {
  // Transfer-trades have non-zero selfTradeLastQty
  return !execution.selfTradeLastQty || execution.selfTradeLastQty === 0;
}

// Example: Processing executions with proper categorization
function processExecution(execution) {
  if (execution.execType === "Trade") {
    if (shouldPublishTrade(execution)) {
      publishToMarketData(execution);
      updateVolumeMetrics(execution);
    } else {
      // This is a transfer-trade (self-match)
      logTransferTrade(execution, execution.selfTradeLastQty);
    }
    
    // Always update internal position tracking
    updateAccountPosition(execution.account, execution);
  }
}

# Operational Considerations

  • Auditability: Transfer-trades are fully auditable through private execution APIs
  • Position Management: Transfer-trades affect positions identically to regular trades
  • Fee Treatment: Commission and fee rules apply to transfer-trades as configured
  • Reporting: Internal risk and compliance reporting should include transfer-trades
  • Market Data: Public market data feeds should exclude transfer-trades to prevent artificial volume inflation
  • Liquidation Price Feeds: Transfer-trades do not feed into the internal price feed used for liquidation checks on instruments whose mark price source is set to last-price

# Cancel Maker Option

In addition to the transfer-trade workflow, the engine supports the option to cancel the maker order to prevent the match entirely.

When the cancel maker option is enabled, the engine will:

  • Cancel the maker order only if the taker will actually match.
  • Report the cancellation to the participant via standard execution websocket stream.
  • The cancellation message will include the reason for cancellation.

Important Notes:

  • If the taker is filled before hitting the maker from the same account, the maker won't be canceled.
  • For FillOrKill orders, if the taker can only be filled by matching the maker, then only the taker is canceled.

This option is configurable on each order. Only the option set on the taker order is considered to avoid ambiguity. The selfTradePrevention flag on the order has to be set to CancelMaker. Private Execution, (Transfer-trades) is the default behaviour