Skip to content

Latest commit

 

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

spire-core

The arithmetic a clearing layer is made of.

Windows, novation, netting, margin, collateral and the default waterfall as pure functions. No transport, no keys, no network: feed it data you fetched yourself and check the result against your own.

License Node Dependencies Tests


Every amount is BigInt and every rate is an integer. A margin rate written as a float is a rounding error waiting for a stress event, and the one place you find out is the one place nobody wants to.

Install

npm install @spireproto/core

The six primitives

Module What it settles
window Which settlement window a fill belongs to, and what phase that window is in
obligation Discharging a bilateral trade into the two obligations that replace it
netting Collapsing a window per member and per asset, across venues
margin What a position costs to carry, against the net and at the tier of the asset
collateral Posted value turned into a limit, and a breach turned into a code
waterfall Absorbing a default in an order fixed before anyone defaults

Windows

Windows are derived from the clock, not announced by an operator. Given a unix second, anyone can say which window it belongs to without asking us.

const { window } = require('@spireproto/core');

window.windowOf(1800000042);        // 6000000
window.phaseOf(6000000, 1800000310); // 'finalising'
window.assign(1800000310, 6000000);
// { windowId: 6000001, deferred: true, reason: 'SPIRE-4001' }

Intake is contiguous: window N finalises and settles while N+1 is already accepting fills. That is why there are 288 windows in a day and not 192, and it is why a fill sent to a window that just closed is carried rather than refused.

Netting

const { netting } = require('@spireproto/core');

// buy 400, sell 250, buy 350, one window, two venues
netting.positions(obligations);
// [{ member, asset, windowId, gross: 1000n, net: 500n, compression: 5000, ... }]

Netting is per member, per asset, per window. Not per venue: after novation two fills from two venues are the same instrument, so they net, and neither venue learns that the other side exists.

Margin follows the net

const { margin } = require('@spireproto/core');

margin.marginOnPosition({ gross: 1000n, net: 250n }, price, 1);
// grossMargin 80  netMargin 20  saved 60

A thousand of turnover in one window that nets to 250 carries 20 of margin at tier 1. Under prefunding the same turnover requires a thousand of assets sitting still. That difference is the product, not an optimisation.

Collateral is a limit, not a balance

const { collateral } = require('@spireproto/core');

collateral.limitOf(1_000_000_000_000n, 1);   // 12,500,000 USDC of notional
collateral.checkNovation({ acct, tier: 1, addNotional });
// { ok: false, code: 'SPIRE-3001', limit, attempted }

Checks run at novation, before anything is owed, and they return a code rather than throwing. A limit breach is a business condition, not an outage. free is signed: negative is a live margin call with an absolute deadline, and that deadline survives a window boundary.

The waterfall

const { waterfall } = require('@spireproto/core');

waterfall.absorb(shortfall, { margin, ownFund, insurance, members });
// { fromMargin, fromOwnFund, fromInsurance, fromMutual, toAuction, layers, events }

Five layers, in order: the defaulter's margin, the defaulter's fund contribution, protocol insurance, the mutualised fund of the other members, then auction. The four sums are returned separately so the order of absorption is observable after the fact rather than asserted. No member is assessed past twice its contribution in one event.

Run it

git clone https://github.com/spireproto/spire-core
cd spire-core
node --test test/*.test.js
node examples/netting.js
node examples/default.js

55 tests, zero dependencies. Every worked example from the parameter table is one of them, so if the published numbers and this package ever drift apart, the suite fails here first.

Parameters

All of them live in src/params.js and mirror spireproto.xyz/docs/parameters: window 300s plus 30s finalisation plus 120s to deliver, margin 8/12/20% by tier, haircuts 0/2/15/35%, default fund 15% of margin with a 50,000 USDC floor, a 2,500,000 USDC insurance layer, assessments capped at 2x, concentration 25%, cure 600s, clearing fee 0.35 bps.

What this is not

It is not a client and it does not talk to a chain. It holds no keys and signs nothing: see spire-sdk for the wire format and spire-contracts for the on-chain surface. Nothing here custodies an asset.

License

MIT

About

Clearing primitives for tokenised assets: settlement windows, novation, netting in time, margin, collateral limits and the default waterfall. BigInt throughout, zero dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages