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
- Variables
- func Code(err error) string
- type AmendResult
- type ArmState
- type Balance
- type CancelResult
- type Client
- func (c *Client) AmendOrder(ctx context.Context, orderID string, req PlaceRequest, arm *ArmState, ...) (*AmendResult, error)
- func (c *Client) CancelOrder(ctx context.Context, orderID, marketTicker string) (*CancelResult, error)
- func (c *Client) GetOrder(ctx context.Context, orderID string) (*LiveOrder, error)
- func (c *Client) GetPortfolio(ctx context.Context) (*Portfolio, error)
- func (c *Client) PlaceOrder(ctx context.Context, req PlaceRequest, arm *ArmState, limits *RiskLimits, ...) (*PlaceResult, error)
- type Fill
- type LiveOrder
- type PlaceRequest
- type PlaceResult
- type Portfolio
- type Position
- type RestingOrder
- type RiskLimits
- type RiskTracker
- type TypedError
Constants ¶
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.
const ( ErrDisarmed = "live_trading_not_armed" ErrLimitExceed = "risk_limit_exceeded" )
Typed error codes for the stage-3 interlocks.
Variables ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
GetOrder fetches the authoritative order state from the exchange. This is the reconciliation source of truth after any write attempt.
func (*Client) GetPortfolio ¶
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:
- armed? (process-level authority)
- risk caps (per-order + daily aggregate, kernel-side)
- fresh market state + price sanity band vs the live touch
- submit with stable client_order_id (idempotency)
- 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 ¶
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