live

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package live provides a signed, read-only Kalshi account client for live mode. Stage 1 of the execution adapter: portfolio reads only — no order writes are reachable through this package.

Credential rules (docs/THREAT_MODEL.md):

  • the private key is loaded once from disk at construction;
  • key material never appears in MCP arguments, results, or errors;
  • every failure is a typed error; nothing is retried here.

Index

Constants

View Source
const (
	ErrUpstream      = "upstream_error"
	ErrRateLimited   = "rate_limited"
	ErrUnauthed      = "not_authorized"
	ErrBadInput      = "invalid_input"
	ErrUnreachable   = "upstream_unreachable"
	ErrBadPayload    = "upstream_payload_invalid"
	ErrBadKey        = "credential_invalid"
	ErrNotFound      = "order_not_found"
	ErrNotResting    = "order_not_resting"
	ErrIndeterminate = "outcome_indeterminate"
)

Typed error codes surfaced through mcptools.Response.Error.Code.

View Source
const (
	ErrDisarmed    = "live_trading_not_armed"
	ErrLimitExceed = "risk_limit_exceeded"
)

Typed error codes for the stage-3 interlocks.

Variables

View Source
var DefaultRiskLimits = RiskLimits{
	MaxOrderNotionalDollars: "25.00",
	MaxDailyNotionalDollars: "100.00",
	MaxDailyOrders:          200,
}

DefaultRiskLimits: "don't like risk" defaults. Small enough that a fat-fingered agent call cannot do real damage; raise explicitly via env.

Functions

func Code

func Code(err error) string

Types

type AmendResult

type AmendResult struct {
	OrderID       string `json:"order_id"`
	ClientOrderID string `json:"client_order_id,omitempty"`
	RemainingFP   string `json:"remaining_count_fp,omitempty"`
	FillCountFP   string `json:"fill_count_fp,omitempty"`
	TsMs          int64  `json:"ts_ms"`
}

AmendResult is the exchange's V2 amend acknowledgement.

type ArmState

type ArmState struct {
	// contains filtered or unexported fields
}

ArmState tracks whether live writes are permitted for this process. Config alone (mode=live + credentials) is NEVER sufficient authority to trade; an explicit arm step is also required, per issue #16.

func (*ArmState) Arm

func (a *ArmState) Arm(acknowledgement string) error

Arm marks this process as authorized to place/amend live orders. The acknowledgement phrase must match exactly; it is the same literal the startup config requires, re-provided here as a deliberate act.

func (*ArmState) Armed

func (a *ArmState) Armed() bool

Armed reports whether writes are currently permitted.

func (*ArmState) Disarm

func (a *ArmState) Disarm()

Disarm revokes write authority for this process immediately.

type Balance

type Balance struct {
	BalanceDollars   string `json:"balance_dollars"`
	PortfolioDollars string `json:"portfolio_value_dollars"`
	UpdatedTS        int64  `json:"updated_ts"`
}

Balance is the live account balance snapshot. Fixed-point strings are passed through byte-for-byte; cents ints stay ints.

type CancelResult

type CancelResult struct {
	OrderID       string `json:"order_id"`
	ClientOrderID string `json:"client_order_id,omitempty"`
	ReducedByFP   string `json:"reduced_by_fp"`
	TsMs          int64  `json:"ts_ms"`
}

CancelResult reports what the exchange acknowledged about a cancellation.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client performs authenticated GET requests against Kalshi portfolio endpoints using RSA-PSS request signatures.

func New

func New(apiKeyID, keyPath string) (*Client, error)

New loads the private key from keyPath and binds a client to the production Kalshi host. The key must be PKCS#1 or PKCS#8 PEM.

func NewWithBaseURL

func NewWithBaseURL(apiKeyID string, key *rsa.PrivateKey, baseURL string, httpClient *http.Client) *Client

NewWithBaseURL is used by tests to point the client at a fixture server.

func (*Client) AmendOrder

func (c *Client) AmendOrder(ctx context.Context, orderID string, req PlaceRequest, arm *ArmState, tracker *RiskTracker) (*AmendResult, error)

AmendOrder updates the price and/or total max-fillable count of one resting event-market order (POST .../amend). Same interlock sequence as place: arm -> state gate -> risk check -> submit -> reconcile.

func (*Client) CancelOrder

func (c *Client) CancelOrder(ctx context.Context, orderID, marketTicker string) (*CancelResult, error)

CancelOrder cancels the remaining quantity of one resting event-market order (DELETE /portfolio/events/orders/{order_id}, V2 response shape).

Indeterminate-outcome contract (docs/THREAT_MODEL.md): a network timeout is NEVER reported as a clean failure. On timeout or transport failure we immediately re-query the order and report either its true state or an explicit outcome_indeterminate error — never a blind retry of the delete.

func (*Client) GetOrder

func (c *Client) GetOrder(ctx context.Context, orderID string) (*LiveOrder, error)

GetOrder fetches the authoritative order state from the exchange. This is the reconciliation source of truth after any write attempt.

func (*Client) GetPortfolio

func (c *Client) GetPortfolio(ctx context.Context) (*Portfolio, error)

GetPortfolio fetches balance, positions, resting/executed orders, and recent fills from the authenticated event-contract account.

func (*Client) PlaceOrder

func (c *Client) PlaceOrder(ctx context.Context, req PlaceRequest, arm *ArmState, limits *RiskLimits, tracker *RiskTracker) (*PlaceResult, error)

PlaceOrder submits one event-market order (POST /portfolio/events/orders).

Interlock sequence, in order:

  1. armed? (process-level authority)
  2. risk caps (per-order + daily aggregate, kernel-side)
  3. fresh market state + price sanity band vs the live touch
  4. submit with stable client_order_id (idempotency)
  5. timeout -> reconcile via GetOrder, never blind retry

type Fill

type Fill struct {
	FillID      string `json:"fill_id"`
	OrderID     string `json:"order_id"`
	Ticker      string `json:"ticker"`
	OutcomeSide string `json:"outcome_side,omitempty"`
	BookSide    string `json:"book_side,omitempty"`
	CountFP     string `json:"count_fp,omitempty"`
	YesPrice    string `json:"yes_price_dollars,omitempty"`
	IsTaker     *bool  `json:"is_taker,omitempty"`
	FeeCost     string `json:"fee_cost_dollars,omitempty"`
	CreatedTime string `json:"created_time,omitempty"`
}

Fill is one live execution print.

type LiveOrder

type LiveOrder struct {
	OrderID        string `json:"order_id"`
	ClientOrderID  string `json:"client_order_id,omitempty"`
	Ticker         string `json:"ticker"`
	OutcomeSide    string `json:"outcome_side,omitempty"`
	BookSide       string `json:"book_side,omitempty"`
	Status         string `json:"status"`
	Type           string `json:"type,omitempty"`
	YesPrice       string `json:"yes_price_dollars,omitempty"`
	FillCountFP    string `json:"fill_count_fp,omitempty"`
	RemainingFP    string `json:"remaining_count_fp,omitempty"`
	InitialCountFP string `json:"initial_count_fp,omitempty"`
}

LiveOrder is the authoritative exchange-side view of one order.

type PlaceRequest

type PlaceRequest struct {
	Ticker                  string
	ClientOrderID           string
	Side                    string // bid | ask
	CountFP                 string
	PriceDollars            string
	TimeInForce             string
	ExpirationTimeSec       int64
	PostOnly                bool
	ReduceOnly              bool
	CancelOrderOnPause      bool
	SelfTradePreventionType string
}

PlaceRequest carries a validated intent to place one event-market order.

type PlaceResult

type PlaceResult struct {
	OrderID       string `json:"order_id"`
	ClientOrderID string `json:"client_order_id,omitempty"`
	FillCountFP   string `json:"fill_count_fp,omitempty"`
	RemainingFP   string `json:"remaining_count_fp,omitempty"`
	AvgFillPrice  string `json:"average_fill_price,omitempty"`
	AvgFeePaid    string `json:"average_fee_paid,omitempty"`
	StatusEcho    string `json:"reconciled_status,omitempty"`
	TsMs          int64  `json:"ts_ms"`
}

PlaceResult is the exchange's V2 placement acknowledgement.

type Portfolio

type Portfolio struct {
	Balance   Balance        `json:"balance"`
	Positions []Position     `json:"positions"`
	Orders    []RestingOrder `json:"orders"`
	Fills     []Fill         `json:"fills"`
}

Portfolio is the aggregated live portfolio read.

type Position

type Position struct {
	Ticker             string `json:"ticker"`
	PositionFP         string `json:"position_fp"`
	MarketExposure     string `json:"market_exposure_dollars"`
	RealizedPnlDollars string `json:"realized_pnl_dollars"`
	FeesPaidDollars    string `json:"fees_paid_dollars"`
	LastUpdated        string `json:"last_updated_ts,omitempty"`
}

Position is one live market position.

type RestingOrder

type RestingOrder struct {
	OrderID       string `json:"order_id"`
	ClientOrderID string `json:"client_order_id,omitempty"`
	Ticker        string `json:"ticker"`
	Side          string `json:"side"`
	Action        string `json:"action"`
	CountFP       string `json:"count_fp,omitempty"`
	PriceDollars  string `json:"yes_price_dollars,omitempty"`
	Status        string `json:"status"`
	FilledCountFP string `json:"filled_count_fp,omitempty"`
	RemainingFP   string `json:"remaining_count_fp,omitempty"`
	CreatedTime   string `json:"created_time,omitempty"`
}

RestingOrder is one live order (any status).

type RiskLimits

type RiskLimits struct {
	MaxOrderNotionalDollars string // per-order |count * price| cap
	MaxDailyNotionalDollars string // rolling-UTC-day aggregate notional cap
	MaxDailyOrders          int    // rolling-UTC-day order count cap
}

RiskLimits are kernel-side caps enforced BEFORE any order reaches the exchange. Defaults are deliberately conservative; every value can be tightened or raised via environment at startup only.

type RiskTracker

type RiskTracker struct {
	// contains filtered or unexported fields
}

RiskTracker enforces the daily aggregate caps in-process.

func NewRiskTracker

func NewRiskTracker(limits RiskLimits) (*RiskTracker, error)

func (*RiskTracker) Snapshot

func (r *RiskTracker) Snapshot() (orders int, notional string)

Snapshot returns today's usage for kernel_status transparency.

type TypedError

type TypedError struct {
	Code    string
	Message string
}

Code maps an error to its typed code for mcptools.Response.Error. TypedError is an exported typed error for cross-package construction.

func (*TypedError) Error

func (e *TypedError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL