# Tutorial: Getting Started with the Engine

The following set of commands enable setting up the engine to be ready to accept orders from users and to perform its core functions. They only need to be done once and must be done via the control gateway.

Reference input types:

int : 32 bits integer
long : 64 bits integer
string : a sequence of characters
double : 64 bits floating decimal value

# Matching the first Trade

A few setup steps must be performed before it is possible to place the first order and make a trade. These steps are one-time control-gateway operations (the first three are engine account allocations)

  1. Allocate an account as the Fees account
  2. Allocate an account as the Insurance fund
  3. Allocate an account as the Penny Jar
  4. Add at least two Assets
  5. Create at least one Fee Schedule
  6. Add at least one Instrument, referencing the Fee Schedule and the two Assets
  7. Make a deposit into at least one Trading Account
  8. Create an order on the Trading Account

# Allocating Engine Accounts

These allocations are control-gateway operations that create the engine’s internal ledger accounts used for fee collection, insurance coverage, and rounding/penny adjustments. Complete these before adding fee schedules or instruments. These operations must be done via the control gateway.

# Fees account

Purpose: dedicated account to collect fees. Referenced by fee schedules and instrument-level fee calculations. The engine credits fees to this account whenever trades incur fees.

Path: /Account/FeesAccount
Method: POST
Parameters:
account: long

# Insurance fund (Liquidation account)

Purpose: dedicated account to hold insurance fund balances used for covering losses during liquidations or other risk events. Referenced by liquidation flows and must be initialized before liquidation-related operations can proceed.

Path: /Account/LiquidationAccount
Method: POST
Parameters:
account: long

# Penny Jar

Purpose: dedicated account for rounding adjustments and small residual amounts. Instruments and settlement flows may reference this account for rounding behavior and tiny residuals. Once set, deposit a small amount in each spot and margin currency to ensure healthy operation.

Path: /Account/PennyJarAccount
Method: POST
Parameters:
account: long

# Creating Assets

The engine needs to be given the assets that can be used to maintain balances and calculate margin requirements and fees.

Add at least two assets so instruments can reference both a base/settlement asset and a quote asset. Each asset entry should include the asset identifier, and precision.

# Ledger Units:

Internally, the matching engine performs all accounting using a single smallest indivisible quantity called the ledger unit.

Every Asset, whether fiat or crypto, is expressed as a multiple of this unit.

  • The Minor Multiplier defines how many ledger units correspond to one minor unit of the asset (e.g., 1 US cent = 1,000 ledger units).

  • The Major Multiplier defines how many ledger units correspond to one major unit (e.g., 1 USD = 100 x 1,000 = 100,000

This ensures consistent integer-based accounting across all assets, regardless of their external decimal precision.

The precision defined here (via minor and major multipliers) establishes how balances are represented internally. When creating instruments later, you will separately define the smallest allowable contract size using the Inverse Step Size, which is based on the instrument’s minimum tradable quantity rather than ledger units.

# Concepts:

  • Major and minor asset names: denomination may vary by context (e.g., BTC quoted in BTC, settled in Satoshi).
  • Minor multiplier: finest-grained native unit stored internally (e.g., milliSatoshi = 1000 = 10^{3}).
  • Major multiplier: number of native units per major currency unit (e.g., 1 BTC = 100,000,000 satoshi = 10^{3+8} = 10^{11} = 100,000,000,000.

Example currencies:

Major Symbol Minor Symbol Minor Multiplier Major-Minor ratio Major Multiplier Proper Name
BTC BTc 1,000 100,000,000 100,000,000,000 Bitcoin
ETH Gwei 1 1,000,000,000 1,000,000,000 Ethereum
USD USd 1,000 100 100,000 United States Dollar
USDT USDt 1 1,000,000 1,000,000 Tether
GBP GBp 1,000 100 100,000 Pound Sterling
EUR EUr 1,000 100 100,000 euro
MEME MEMe 1,000 100,000 100,000,000 Memecoin
ADA ADa 100 1,000,000 100,000,000 Cardano
DOGE DOGe 1 100,000,000 100,000,000 Dogecoin
Path: /Asset
Method: POST
Parameters:
assetMajorName: string
majorMultiplier: long
assetMinorName: string
minorMultiplier: long
assetProperName: string

# Setting up Fees

Create at least one Fee Schedule before adding instruments that reference it. Fee schedules determine maker/taker rates, tiering, and how fees are applied to trades.

Fee type Scope
Maker fee applied to an order that matches while resting in the order book
Taker fee applied to an order that matches immediately
Hidden fee (maker or taker) applied to a hidden order
Iceberg hidden maker fee applied to the hidden portion of the iceberg
Iceberg visible maker fee applied to the visible portion of the iceberg
Iceberg final fee applied when an iceberg resting order becomes fully visible
Iceberg taker fee applied to the portion of an iceberg order that executes immediately
Liquidation maker fee applied to the insurance fund when it takes over a position and is trading out of it
Liquidation taker fee applied when an order is generated by the system to liquidate a position

# Fee Tier Example

Here's an example set of fee schedules. Generally the fees would decrease (or become rebates) as the tier increases. The intention is that higher tiered users are doing more volume and so are offered a more attractive fee schedule.

Fee Tier 0 Tier 1 Tier 2 Tier 3 Tier 4
Maker Fee 0.0002 0.0002 0.0001 0 -0.00005
Taker Fee 0.001 0.00075 0.0005 0.0005 0.0005
Hidden Maker Fee 0.001 0.00075 0.0005 0.0005 0.0005
Hidden Taker Fee 0.0015 0.0009 0.001 0.001 0.001
Iceberg Hidden Maker Fee 0.001 0.00075 0.0005 0.0005 0.0005
Iceberg Visible Maker Fee 0.0002 0.0002 0.0001 0.0001 0.0001
Iceberg Final Berg Fee 0.0002 0.0002 0.0001 0 -0.00005
Iceberg Taker Fee 0.0015 0.0009 0.001 0.001 0.001
Liquidation Maker Fee 0.0002 0.0002 0.0002 0.0002 0.0002
Liquidation Taker Fee 0.001 0.00075 0.0005 0.0005 0.0005
Path: /FeeSchedule
Method: POST
Parameters:
id: string (uuid) (used to link instruments to this fee schedule)
name: string (unused)

Body:

{
  "elements": [
    {
      "userTierId": 0,
      "makerFee": 0.01,
      "takerFee": 0.02,
      "hiddenMakerFee": 0.03,
      "hiddenTakerFee": 0.04,
      "icebergHiddenMakerFee": 0.05,
      "icebergVisibleMakerFee": 0.06,
      "icebergFinalBergFee": 0.06,
      "icebergTakerFee": 0.07,
      "liquidationMakerFee": 0.08,
      "liquidationTakerFee": 0.09
    },
    {
      "userTierId": 1
      // ...
    }
  ]
}

Whenever a fee is applied, it will be credited to the Fees account. Ensure the Fees account has been allocated before creating orders.

Path: /Account/FeesAccount
Method: POST
Parameters:
account: long

# Adding Instruments

The engine needs to be given the instruments that can be traded. Add at least one Instrument and ensure it references the Fee Schedule and the two assets you created.

An instrument must specify:

  • Settlement asset (one of the two assets)
  • Quote asset (the other asset)
  • Fee Schedule ID
  • Inverse Step Size

# Inverse Step Size

Instruments define financial contracts linking two assets. Each instrument specifies an InverseStepSize, which determines the smallest allowable contract size. This value represents the inverse of the minimum contract size, expressed in the natural major units of the base asset. In other words, if the smallest tradable quantity is x, the the InverseStepSize is 1/x.

The InverseStepSize allows the engine to represent contract quantities as integers internally while still supporting fractional contract sizes externally.

Typical patters:

  • Contract sized in major units If the contract size is 1 major unit, the: InverseStepSize = 1
  • Contract sized in minor units If the minimum contract size is 0.01 (e.g., cents, hundredths), then: InverseStepSize = 100
  • Contracts requiring finer granularity For minimum sizes suc as 0.001, 0.0001, or 0.00001, the InverseStepSize is the reciprocal of that increment.
# BTC/USD Example

Suppose as BTC/USD instrument allows a minimum contract size of 0.00001 BTC

  1. Minimum size = 0.00001 BTC
  2. Therefore: InverseStepSize = 1/(0.00001) = 100,000

This is the correct configuration value. Note that this calculation does not depend on ledger units, satoshis, or multipliers. Those define balance precision, whereas InverseStepSize defines contract granularity.

# Spot instrument example (required parameters):

Path: /Instrument/Spot
Method: POST
Parameters:
instrumentShortCode: BTC_USD
asset1ShortCode: BTC
asset2ShortCode: USD
lotSize: 100
feeScheduleId: the uuid of the fee schedule as mentioned in the fees section
tickSize: 0.01
minPrice: 0.01
maxPrice: 1000000
maxOrderQuantity: 1000000
inverseStepSize: 100000

When adding a spot instrument, include tick size, lot size, settlement currency, and inverse step size details.

# Futures

Futures instrument example (supports linear/inverse and term/perpetual; settlement currency can be either side or a different currency):

Path: /Instrument/Future
Method: POST
Parameters:
contractShortCode: BTCUSD
isInverse: true
isPerpetual: true
asset1ShortCode: BTC
asset2ShortCode: USD
settlementCurrency: BTc
lotSize: 100
feeScheduleId: the uuid of the fee schedule as mentioned in the fees section
tickSize: 0.5
minPrice: 0.01
maxPrice: 1000000
initialPrice: 53000.5
maxOrderQuantity: 1000000
isLastPriceMarked: false
initialMarginSpec: 0,0.01 20000000000,0.0135 40000000000,0.017
maintenanceMarginSpec: 0,0.0035 20000000000,0.007 40000000000,0.0105
inverseStepSize: 100000

Margin specs provide margin requirements at each risk level. Example interpretation: a client with a risk limit above 20bn BTc but below 40bn BTc would have an initial margin requirement of 1.35% and a maintenance margin requirement of 0.7%.

# Onboarding Customers

Provide trading accounts for users and ensure they can hold balances in the assets you added. Creating a user account on the engine is typically done by depositing funds into an account number.

# Depositing funds:

Path: /Deposits
Method: POST
Parameters:
accountIdentifier: long
assetShortName: string
amount: long

Notes:

  • The asset short name must match the minor name of an asset added via the /Asset request (e.g., Satoshi).
  • Account numbers can be any valid 64-bit long. For memory efficiency, concentrate active account numbers in a closely packed range.

# Withdrawing funds:

Path: /Withdrawals
Method: POST
Parameters:
accountIdentifier: long
assetShortName: string
amount: long

Withdrawals validate that the account contains sufficient free funds (excluding funds locked by open orders or margined positions).

Once you have deposited funds into at least one trading account (and created the instrument and fee schedule), you are ready to place orders and match.

# Creating Orders

Once a trading account has a deposit in the instrument’s settlement currency and the instrument references a valid fee schedule and assets, create an order on the trading account. Use the control‑gateway Swagger UI for exact parameter names and examples: https://$/docs/

Required at a glance

  • symbol — instrument short code or ID
  • account — trading account identifier
  • side — Buy or Sell
  • orderQty — signed integer quantity (follow gateway convention)
  • price — limit price (respect tick size)
  • ordType — e.g., Limit or Market
  • timeInForce — e.g., GoodTillCancel

Optional but useful

  • displayQty, stopPx, execInst, text
  • clOrdID — client order id to reuse when retrying the same logical order attempt

# Minimal curl example

  export CONTROL_GATEWAY="https://your-control-gateway.nekuti.com:8181"
  curl -i -X POST "${CONTROL_GATEWAY}/AccountAdmin/Order?account=1001&symbol=BTCUSD&side=Sell&orderQty=-1000000000&price=92820&ordType=Limit&timeInForce=GoodTillCancel&clOrdID=client-20260216-0001" -H "Authorization: Bearer <token>"

# Success check

Look for a non‑empty orderID and "ordStatus":"New" in the JSON response — that means the order was accepted and is resting on the book.

# Troubleshooting

  • Validation errors — check the Swagger AccountAdmin/Order schema for required fields and formats.
  • Insufficient funds — confirm the deposit and that funds are not locked by other orders.

# Further One-time setup

# Liquidation Strategy

When liquidation happens, the engine will try to liquidate positions on instruments one by one until the account’s margin requirement becomes less than its available funds.

The order in which the engine goes down the list of instrument to liquidate is specified by this request:

Path: /Instrument/LiquidationStrategy
Method: POST
Parameters:

Body:

[
  "BTCUSD",
  "ETHUSD",
  "BTCUSDT"
  …
]

# Rounding

The engine stores and calculates account balances and transaction amounts in whole integer units, so that arithmetic operations never unintentionally create or destroy balances. This means that in some cases, there will be a residual 1-unit discrepancy between the buy and sell sides of a trade.

A buyer wants to purchase 2000 units of an inverse futures product at a price of 3.0 and match with two sellers that sell 1000 each.

Buyer cost: \frac{2000}{3.0} = 667

Seller cost: 2 * \frac{1000}{3.0} = 2 * 333 = 666

In this example the buyer's cost is one unit higher than the sum of the seller's costs.

In such cases, the difference will be credited to (or taken away from) a special “penny jar” account, to ensure the net cost impact of the transaction on the overall exchange is always 0.

It is recommended to use fine-grained units in the definition of Assets (e.g. milliSatoshi) so that the size of rounding differences is minimised.

The application needs to be told the account number of the penny jar account using the following request:

Path: /Account/PennyJarAccount
Method: POST
Parameters:
account: long

Once the penny jar account has been set, a small amount of funds should be deposited there in each spot and margin currency in order to ensure the healthy operation of the exchange.

# Ongoing Operations

To continue operation of the exchange, a few operational processes must be maintained. These can be automated by external processes.

# Mark Prices

Futures instruments can be last-price marked or fair-price marked. For fair-price marking, the engine must receive frequent market data updates.

Path: /Instrument/MarkPrices
Method: POST
Parameters:
instrument: string
price: double

Body:

[
  {
    "instrument": BTCUSD,
    "price": 53000.5
  },
  {
    "instrument": ETHUSD,
    "price": 3765.2
  }
  // …
]

# Term Futures Expiry

Upon expiry, futures contracts automatically settle at the expiryPrice. Notify the engine of expiry:

Path: /Instrument/Expire
Method: PUT
Parameters:
instrument: string
expiryPrice: double

# Perpetual Futures Funding

Perpetual futures are subject to regular funding cycles. Notify the engine each funding cycle:

Path: /Instrument/FundingCharge
Method: POST

Body:

[
  {
    "instrument": "XBTUSD",
    "fundingRate": 0.0001,
    "referencePrice": 61335.61
  },
  {
    ...
  }
]

# Funding charge example:

For a position of 0.5 BTC in a BTCUSDT linear future contract, with reference price 30000 and rate 0.0001:

fundingCharge = { 0.5 * 30000 * 0.0001 }

# Optional Settings

# Linking subaccounts

Link an account to a parent account (owner):

Path: /Account/Owner
Method: POST
Parameters:
account: long
owner: long

# Setting the Fee Tier

Every account defaults to fee tier 0. To change an account's fee tier:

Path: /Account/FeeTier
Method: POST
Parameters:
feeTier: int (32 bits integer) must match the userTierId of one of the fee schedule above
Body: [1, 2, 3]: array of long

# Exchange Suspended on Startup

The exchange is initially suspended when it first starts. This is to prevent any orders being placed before basic setup tasks have been completed. To check which setup tasks remain, run the "Resume" endpoint /ExchangeWideControls/ResumeTrading on the control gateway. The exchange will either be resumed, or it will send a response indicating which setup tasks remain.

Reasons why the exchange would be suspended:

Message Reason Solution
UNABLE_TO_PERFORM_LIQUIDATION The liquidation account (insurance fund account) has not been set. Initialize the account using the POST /ExchangeWideControls/LiquidationAccount endpoint
UNABLE_TO_COLLECT_FEES The fees account has not been set Initialize the account using the POST /ExchangeWideControls/FeesAccount endpoint
PENNY_JAR_NOT_CONFIGURED The penny jar account has not been set Initialize the account using the POST /ExchangeWideControls/PennyJarAccount endpoint
MANUALLY_SUSPENDED The exchange was suspended manually using the /ExchangeWideControls/SuspendTrading endpoint Resume using the /ExchangeWideControls/ResumeTrading endpoint

# Worked Example - full setup and first order

This copy‑pasteable script performs the complete minimal setup (fees, liquidation, penny jar), registers two assets, creates a fee schedule and a linear perpetual instrument, deposits funds into a trading account, and submits a first order. Before running, set CONTROL_GATEWAY to your control‑gateway HTTPS endpoint and add any required authentication headers (for example -H "Authorization: Bearer ") if your gateway requires them. Run the commands in order to satisfy the engine’s one‑time setup dependencies.

# Set the HTTPS endpoint of the control gateway (edit to your gateway)
export CONTROL_GATEWAY="https://your-control-gateway.nekuti.com:8181"

## Worked example — full setup and first order (copy/paste)

# 1) Allocate Fees account
curl -i -X POST "${CONTROL_GATEWAY}/ExchangeWideControls/FeesAccount?account=1"

# 2) Allocate Liquidation (Insurance) account with Unrestricted permissions
curl -i -X POST "${CONTROL_GATEWAY}/ExchangeWideControls/LiquidationAccount?permissions=Unrestricted&account=2"

# 3) Allocate Penny Jar account
curl -i -X POST "${CONTROL_GATEWAY}/ExchangeWideControls/PennyJarAccount?account=3"

# 4) Add BTC asset (major=BTC, minor=BTc)
curl -i -X POST "${CONTROL_GATEWAY}/Asset?assetMajorName=BTC&majorMultiplier=1000000000&assetMinorName=BTc&minorMultiplier=1&supportsBalances=true&fullName=Bitcoin"

# 5) Add USD asset (major=USD, minor=USd)
curl -i -X POST "${CONTROL_GATEWAY}/Asset?assetMajorName=USD&majorMultiplier=1000000000&assetMinorName=USd&minorMultiplier=1&supportsBalances=true&fullName=United+States+Dollar"

# 6) Create Fee Schedule (Use provided ID)
curl -i -X POST \
  -H "Content-Type: application/json" \
  -d '{
  "elements": [
    {
      "userTierId": 0,
      "makerFee": 0,
      "takerFee": 0.0005,
      "hiddenMakerFee": 0,
      "hiddenTakerFee": 0.0005,
      "icebergHiddenMakerFee": 0,
      "icebergVisibleMakerFee": 0.0005,
      "icebergFinalBergFee": 0.0005,
      "icebergTakerFee": 0.0005,
      "liquidationMakerFee": 0,
      "liquidationTakerFee": 0.0005
    }
  ]
}' "${CONTROL_GATEWAY}/Instrument/FeeSchedule?id=a6acc3c2-5335-4aea-ab7c-afe7fd7d4259&name=BTCUSD"

# 7) Add a linear perpetual future instrument referencing the fee schedule and assets
curl -i -X POST ${CURL_AUTH:-} \
  -H "Content-Type: application/json" \
  -d '{
  "initialMarginLevels": [
    {
      "riskLevel": 2000000000,
      "marginRequirement": 0.01
    }
  ],
  "maintenanceMarginLevels": [
    {
      "riskLevel": 2000000000,
      "marginRequirement": 0.01
    }
  ]
}' "${CONTROL_GATEWAY}/Instrument/LinearFuture?contractShortCode=BTCUSD&isPerpetual=true&asset1MajorName=BTC&asset2MajorName=USD&lotSize=100000&feeScheduleId=a6acc3c2-5335-4aea-ab7c-afe7fd7d4259&tickSize=0.1&minPrice=0&maxPrice=1000000&initialPrice=100000&maxOrderQuantity=10000000000&isLastPriceMarked=false&inverseStepSize=1000000000&fundingScale=SimpleRate&fundingRoundingMethod=HalfToEven&openValueCap=500000000000000"

# 8) Deposit USD into trading account 1001 (amount in minor units)
curl -i -X POST "${CONTROL_GATEWAY}/Asset/Deposit?accountIdentifier=1001&assetShortName=USd&amount=1000000000000000"

# 9) Create an order from account 1001 (sell order)
curl -i -X POST "${CONTROL_GATEWAY}/AccountAdmin/Order?account=1001&symbol=BTCUSD&side=Sell&orderQty=-1000000000&price=92820"

# Expected Result

If the script runs successfully, the final AccountAdmin/Order call will return a JSON response confirming the order was accepted and is on the book in New state. An example successful response:

{
  "orderID": "83d7b47c-c80d-a7ee-6ce7-a04caa20e8d9",
  "clOrdID": "",
  "clOrdLinkID": "",
  "account": 1001,
  "symbol": "BTCUSD",
  "side": "Sell",
  "orderQty": -1000000000,
  "price": 92820.0,
  "displayQty": -1000000000,
  "stopPx": 0.0,
  "currency": "USD",
  "settlCurrency": "USD",
  "ordType": "Limit",
  "timeInForce": "GoodTillCancel",
  "execInst": "",
  "ordStatus": "New",
  "triggered": "Triggered",
  "workingIndicator": true,
  "leavesQty": -1000000000,
  "cumQty": 0,
  "text": "",
  "transactTime": "1970-01-01T00:00Z",
  "timestamp": "1970-01-01T00:00Z",
  "updateTime": 1771269193104
}

Look for "ordStatus":"New" (order accepted and resting on the book) and a non-empty "orderID" as confirmation that the order was placed. If you receive an error or a different status, check the preceding setup steps (fees, assets, fee schedule, instrument, deposit) and any gateway authentication or validation messages.