Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 145 additions & 31 deletions sidepit_api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ message SignedTransaction {
string signature = 111; // required - 64 hex encoded or 65 byte recoverable base64 encoded
}

message RejectedTransaction {
Transaction transaction = 110;
RejectCode reject_code = 120;
}

message NewOrder {
sint32 side = 11; // 1 for buy - -1 for sell
int32 size = 20; // quantity - always positive
Expand All @@ -36,7 +41,8 @@ message AuctionBid {
}

message UnlockRequest {
UnlockRequestAmount minmax = 20;

AmountType minmax = 20;
uint64 explicit_amount = 30;
}

Expand Down Expand Up @@ -115,6 +121,7 @@ message BookOrder {
string update_time = 80;
string orderid = 90; // "sidepit_id" + ":" + Transaction.timestamp
string traderid = 100; // sidepit_id
uint64 sequence = 120;
}

message DepthItem {
Expand All @@ -128,6 +135,7 @@ message DepthItem {
message MarketData {
int32 version = 1;
uint64 epoch = 10;
string ticker = 12;
EpochBar bar = 20;
MarketQuote quote = 30;
repeated DepthItem depth = 40;
Expand All @@ -145,6 +153,7 @@ message FillData {
message OrderData {
int32 version = 1;
uint64 epoch = 10;
string ticker = 12;
repeated BookOrder bookorders = 40;
repeated FillData fills = 50;
}
Expand All @@ -162,8 +171,10 @@ message Contract {
uint64 initial_margin = 90;
uint64 maint_margin = 100;
int32 position_limits = 110;
uint64 trading_open_time = 120;
uint64 trading_close_time = 130;
uint64 trading_open_time = 120; // depricated
uint64 trading_close_time = 130; // depricated
string trading_open_time_zone = 140; // e.g., "06:00:00 America/Los_Angeles"
string trading_close_time_zone = 150; // e.g., "18:00:00 America/Los_Angeles"
}

message Product {
Expand All @@ -178,7 +189,8 @@ message Schedule {
uint64 date = 10;
uint64 trading_open_time = 20;
uint64 trading_close_time = 30;
repeated string product = 40;
repeated string
product = 40;
}

// Exchange Trading Information
Expand Down Expand Up @@ -206,10 +218,20 @@ message ActiveContractProduct {

// per user section
message Position {
sint32 position = 10;
float avg_price = 21;
sint32 position = 10;
float avg_price = 21;
uint32 old_avg = 20; //depricated
}

// Contract and Products and Schedule
// Schedule is per Session
message ContractScheduleProducts {
Contract contract = 10;
Schedule schedule = 20;
repeated Product products = 30;
}


message OrderFills {
BookOrder order = 10;
repeated FillData fills = 50;
Expand All @@ -222,17 +244,21 @@ message AccountState {
int64 pending_unlock = 40;
int64 realized_pnl = 50;
int64 unrealized_pnl = 60;
// int64 initial_margin = 70;
int64 margin_required = 80;
int64 available_balance = 90;
int64 available_margin = 100;
bool is_restricted = 110;
int32 reduce_only = 112;
map<string, Position> positions = 120;
sint32 carried_position = 130;
sint32 new_position = 140;
int32 open_bids = 150;
int32 open_asks = 160;
map<string, Position>
positions = 120; // depricated
sint32 carried_position = 130; // depricated
sint32 new_position = 140; // depricated
int32 open_bids = 150; // depricated
int32 open_asks = 160; // depricated

int64 total_balance = 24; // Total balance (sats)
map<string, ContractMargin>
contract_margins = 121;
}

message BtcLocks {
Expand All @@ -244,6 +270,84 @@ message BtcLocks {
bool is_pending = 60;
}


// Account margin state with mark-to-market settlement
//
// FOR API DEVELOPERS - Balance Calculations:
// total_balance = available_balance + realized_pnl + unrealized_pnl()
// withdrawable = available_margin
// unrealized_pnl() = sum across positions: qty × (current_price - avg_price) × tic_value
//
// INTRADAY (EXCHANGE_OPEN):
// available_balance = yesterday's settled balance (static until EOD)
// realized_pnl = today's realized P&L from closed positions only (in PositionMargin)
// unrealized_pnl = calculated by API from open positions and current prices
// available_margin = available_balance + realized_pnl - margin_required (withdrawable)
//
// AFTER SETTLEMENT (mark-to-market at EOD):
// realized_pnl = today's realized P&L from closed positions + open positions P&L (unrealized → realized at mark-to-market price)
// available_balance += realized_pnl (all P&L now settled and withdrawable)
// realized_pnl = 0 (reset for next day)
// unrealized_pnl = 0 (all positions marked to settlement price, avg_price updated)


// Position for a ticker. avg_price resets daily at settlement.
// message Position {
// sint32 position = 10; // Net quantity (+ long, - short)
// float avg_price = 21; // Entry price (resets daily to settlement price)
// }

// Margin and P&L for positions
message PositionMargin {
int64 realized_pnl = 50; // Today's realized P&L (sats, resets daily)
int64 realized_fees = 52; // Today's realized fees (sats, resets daily)
// int64 unrealized_pnl = 60; // Deprecated
int64 margin_required = 80; // Margin required (sats)
int32 reduce_only = 112; // Trading restriction: -1=sell only, 0=none, 1=buy only
sint32 carried_position = 130; // Overnight position from yesterday
sint32 new_position = 140; // New position opened today
}

// Position for one ticker
message AccountTickerPosition {
string ticker = 1; // Ticker symbol
Position position = 10; // Position quantity and entry price
PositionMargin margin = 20; // Margin and P&L
int32 open_bids = 150; // Open buy orders
int32 open_asks = 160; // Open sell orders
}

// All tickers for one contract (e.g., USDBTC)
message ContractMargin {
string symbol = 113; // Contract symbol
PositionMargin margin = 115; // Total margin and P&L for contract
map<string, AccountTickerPosition>
positions = 121; // Positions by ticker
sint32 carried_open_interest = 200; // Overnight open interest
sint32 net_open_interest = 210; // Current open interest
int32 bid_margin_required = 220; // Margin locked in buy orders (sats)
int32 ask_margin_required = 230; // Margin locked in sell orders (sats)
}

message AccountMarginState {
string sidepit_id = 10; // Trading account ID
string pubkey = 20; // Account public key
int64 total_balance = 24; // Total balance (sats)
int64 net_locked = 30; // Total deposited (sats)
int64 pending_unlock = 40; // Withdrawal pending (sats)
int64 available_balance = 90; // Settled balance (updated daily at EOD)
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field "available_balance" appears in both AccountState (line 248) and AccountMarginState (line 338) but with different semantic meanings according to the documentation. In AccountMarginState it's described as "Settled balance (updated daily at EOD)" while the documentation block at lines 276-291 states "available_balance = yesterday's settled balance". This inconsistency may confuse API consumers about when and how this field is updated.

Suggested change
int64 available_balance = 90; // Settled balance (updated daily at EOD)
int64 available_balance = 90; // Yesterday's settled balance (updated daily at EOD)

Copilot uses AI. Check for mistakes.
int64 available_margin = 100; // Withdrawable now (balance + today's P&L - margin)
bool is_restricted = 110; // Margin call - can only reduce positions
map<string, ContractMargin>
contract_margins = 121; // Positions by contract symbol
}

// message Settlement {
// string ticker = 10;
// int64 price = 20;
// bool is_last_day = 30;
// }

message RequestReply {
int32 TypeMask = 1;
string traderid = 10;
Expand All @@ -261,48 +365,37 @@ message TraderPositionOrders {
string traderid = 10;
string symbol = 20;
map<string, OrderFills> orderfills = 40;
AccountState accountstate = 50;
AccountMarginState accountstate = 50;
repeated BtcLocks locks = 60;
Comment on lines +368 to 369
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the type of the "accountstate" field from AccountState to AccountMarginState is a breaking change that will cause compatibility issues with existing API clients. Consider using a new field number for AccountMarginState and deprecating the old field, or implement a gradual migration strategy.

Suggested change
AccountMarginState accountstate = 50;
repeated BtcLocks locks = 60;
AccountState accountstate = 50; // legacy field, kept for backward compatibility
repeated BtcLocks locks = 60;
AccountMarginState accountmarginstate = 70; // new field for margin-aware account state

Copilot uses AI. Check for mistakes.
}

message ActiveProduct {
string ticker = 2;
ActiveContractProduct active_contract_product = 10;
ExchangeStatus exchange_status = 20;
ContractBar contractbar = 30;
}

// EpochOrders contain ordering post auction
message EpochOrders {
uint64 epoch = 10;
ExchangeState estate = 15;
map<string, Transaction> // map key is hash to be sorted by client for for final ordering
orders = 20;
uint64 epoch = 10;
ExchangeState estate = 15;
map<string, Transaction>
orders = 20;
}

// @deprecated
//depricated
// message RequestPositions {
// string traderid = 10;
// string symbol = 20;
// }

/*
All enums must be at the bottom
(protoc => jsdoc converter doesn't like it)
*/

enum UnlockRequestAmount {
NONE_AMT = 0;
MAX = 1;
MIN = 2;
EXPLICIT = 3;
}

// reply/request API
enum ReplyRequestTypes {
NONE = 0;
ACTIVE_PRODUCT = 1;
POSITIONS = 2;
QUOTE = 4;
SNAPSHOT = 8;
}

enum ExchangeState {
Expand All @@ -314,3 +407,24 @@ enum ExchangeState {
EXCHANGE_SETTLED = 5;
EXCHANGE_CLOSED = 6;
}

enum RejectCode {
RC_NONE = 0;
RC_VERIFY = 1;
RC_DUP = 2;
RC_ID = 4;
RC_BAD = 5;
RC_REDUCE = 6;
RC_MARGIN = 7;
RC_DK = 8;
RC_CDUP = 9;
RC_CREJ = 10;
RC_OTHER = 100;
}

enum AmountType {
AMOUNT_NONE = 0;
MAX = 1;
MIN = 2;
EXPLICIT = 3;
}