#
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
#
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: 0or 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 (
/TradeREST 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
#
Best Practices
- Set ownership at account creation: Configure beneficial owner immediately when creating sub-accounts
- Monitor execution feeds: Always check
selfTradeLastQtybefore publishing to market data - Maintain audit trails: Log all transfer-trades for compliance and reconciliation
- Update ownership changes: Reconfigure owner associations when beneficial ownership changes
- Test detection: Verify self-match detection is working correctly in staging environments before going live