Skip to content
Merged
Show file tree
Hide file tree
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- New `trigger_order_group()` endpoint to manually trigger an order group's
auto-cancel (PUT `/portfolio/order_groups/{id}/trigger`).
- New `update_order_group_limit()` endpoint to change an order group's contracts
limit (PUT `/portfolio/order_groups/{id}/limit`).
- New `get_api_limits()` endpoint to retrieve API tier and rate limits
(GET `/account/limits`).
- `contracts_limit_fp` fixed-point string field on `CreateOrderGroupRequest`,
`UpdateOrderGroupLimitRequest`, `GetOrderGroupResponse`, and `OrderGroupSummary`.
The integer `contracts_limit` field is now optional (provide one or both).
- New `OrderGroupUpdates` WebSocket channel for order group lifecycle events
(`OrderGroupUpdateData`, `OrderGroupEventType`).
- Fixed-point `_fp` fields added to WebSocket message types: `delta_fp` on
`OrderbookDeltaData`, `volume_fp`/`open_interest_fp` on `TickerData`,
`count_fp` on `TradeData`, `count_fp`/`post_position_fp` on `FillData`,
and `position_fp`/`volume_fp` on `MarketPositionData`.

### Fixed

- `OrderbookAggregator` now drops delta messages that arrive before a snapshot
Expand Down
24 changes: 22 additions & 2 deletions examples/order_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use kalshi_trade_rs::{
Action, CreateOrderGroupRequest, CreateOrderRequest, GetMarketsParams, GetOrderGroupsParams,
KalshiClient, KalshiConfig, MarketFilterStatus, OrderType, Side, TimeInForce,
UpdateOrderGroupLimitRequest,
};

#[tokio::main]
Expand Down Expand Up @@ -108,14 +109,31 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Page 1: {} groups", page1.order_groups.len());
println!();

// 6. Reset Order Group
// 6. Update Order Group Limit
println!("=== Update Order Group Limit ===");
println!("Updating contracts limit to 20...\n");
let update_request = UpdateOrderGroupLimitRequest::new(20);
client
.update_order_group_limit(order_group_id, update_request)
.await?;
println!("Limit updated to 20.");
println!();

// 7. Reset Order Group
println!("=== Reset Order Group ===");
println!("Resetting matched contracts counter...\n");
client.reset_order_group(order_group_id).await?;
println!("Reset complete.");
println!();

// 7. Delete Order Group
// 8. Trigger Order Group
println!("=== Trigger Order Group ===");
println!("Triggering order group (cancels all orders)...\n");
client.trigger_order_group(order_group_id).await?;
println!("Order group triggered.");
println!();

// 9. Delete Order Group
println!("=== Delete Order Group ===");
println!("Deleting the order group (this cancels all orders)...\n");
client.delete_order_group(order_group_id).await?;
Expand Down Expand Up @@ -146,7 +164,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" get_order_group(id) - Get order group details");
println!(" list_order_groups() - List all order groups");
println!(" list_order_groups_with_params() - List with pagination");
println!(" update_order_group_limit(id, r) - Update contracts limit");
println!(" reset_order_group(id) - Reset matched contracts counter");
println!(" trigger_order_group(id) - Trigger auto-cancel manually");
println!(" delete_order_group(id) - Delete group and cancel orders");
println!();

Expand Down
1 change: 1 addition & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! These modules contain endpoint-specific logic. The public API is exposed
//! through flat methods on [`KalshiClient`](crate::KalshiClient).

pub(crate) mod account;
pub(crate) mod api_keys;
pub(crate) mod communications;
pub(crate) mod events;
Expand Down
17 changes: 15 additions & 2 deletions src/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Complete reference for all Kalshi REST API endpoints supported by this library.
|----------|-----------|----------|
| Exchange | 5 | 100% |
| Orders | 10 | 100% |
| Order Groups | 5 | 100% |
| Order Groups | 7 | 100% |
| Account | 1 | 100% |
| Portfolio | 5 | 100% |
| Subaccounts | 5 | 100% |
| Markets | 6 | 100% |
Expand All @@ -36,7 +37,7 @@ Complete reference for all Kalshi REST API endpoints supported by this library.
| Structured Targets | 2 | 100% |
| Incentive Programs | 1 | 100% |
| FCM | 2 | 100% |
| **Total** | **75** | **100%** |
| **Total** | **78** | **100%** |

---

Expand Down Expand Up @@ -82,11 +83,23 @@ Complete reference for all Kalshi REST API endpoints supported by this library.
| ✅ | GET | `/portfolio/order_groups` | `list_order_groups()` | |
| ✅ | DELETE | `/portfolio/order_groups/{id}` | `delete_order_group()` | Cancels all orders in group |
| ✅ | PUT | `/portfolio/order_groups/{id}/reset` | `reset_order_group()` | Resets contracts counter |
| ✅ | PUT | `/portfolio/order_groups/{id}/trigger` | `trigger_order_group()` | Triggers auto-cancel |
| ✅ | PUT | `/portfolio/order_groups/{id}/limit` | `update_order_group_limit()` | Updates contracts limit |

**Source**: `src/api/order_groups.rs`

---

## Account API

| Status | Method | Endpoint | Rust Function | Notes |
|--------|--------|----------|---------------|-------|
| ✅ | GET | `/account/limits` | `get_api_limits()` | API tier and rate limits |

**Source**: `src/api/account.rs`

---

## Portfolio API

| Status | Method | Endpoint | Rust Function | Notes |
Expand Down
8 changes: 8 additions & 0 deletions src/api/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Account API endpoints.

use crate::{client::HttpClient, error::Result, models::ApiTierLimitsResponse};

/// Returns the user's API tier and rate limits.
pub async fn get_api_limits(http: &HttpClient) -> Result<ApiTierLimitsResponse> {
http.get("/account/limits").await
}
24 changes: 23 additions & 1 deletion src/api/order_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
error::Result,
models::{
CreateOrderGroupRequest, CreateOrderGroupResponse, GetOrderGroupResponse,
GetOrderGroupsParams, OrderGroupsResponse,
GetOrderGroupsParams, OrderGroupsResponse, UpdateOrderGroupLimitRequest,
},
};

Expand Down Expand Up @@ -55,3 +55,25 @@ pub async fn reset_order_group(http: &HttpClient, order_group_id: &str) -> Resul
);
http.put_empty_json(&path).await
}

/// Triggers an order group, cancelling all orders within it.
pub async fn trigger_order_group(http: &HttpClient, order_group_id: &str) -> Result<()> {
let path = format!(
"/portfolio/order_groups/{}/trigger",
encode_id(order_group_id)
);
http.put_empty_json(&path).await
}

/// Updates the contracts limit for an order group.
pub async fn update_order_group_limit(
http: &HttpClient,
order_group_id: &str,
request: UpdateOrderGroupLimitRequest,
) -> Result<()> {
let path = format!(
"/portfolio/order_groups/{}/limit",
encode_id(order_group_id)
);
http.put_no_response(&path, &request).await
}
101 changes: 83 additions & 18 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ pub use websocket::WebSocketClient;

use crate::{
api::{
api_keys, communications, events, exchange, fcm, incentive_programs, live_data, markets,
milestones, multivariate, order_groups, orders, portfolio, search, series,
account, api_keys, communications, events, exchange, fcm, incentive_programs, live_data,
markets, milestones, multivariate, order_groups, orders, portfolio, search, series,
structured_targets, subaccounts,
},
auth::KalshiConfig,
error::Result,
models::{
AcceptQuoteRequest, AmendOrderRequest, AmendOrderResponse, ApiKeysResponse,
BalanceResponse, BatchCancelOrdersRequest, BatchCancelOrdersResponse,
BatchCandlesticksResponse, BatchCreateOrdersRequest, BatchCreateOrdersResponse,
BatchLiveDataResponse, CancelOrderResponse, CandlesticksResponse, CommunicationsIdResponse,
CreateApiKeyRequest, CreateApiKeyResponse, CreateMarketInCollectionRequest,
CreateMarketInCollectionResponse, CreateOrderGroupRequest, CreateOrderGroupResponse,
CreateOrderRequest, CreateQuoteRequest, CreateRfqRequest, CreateSubaccountRequest,
CreateSubaccountResponse, DecreaseOrderRequest, DeleteApiKeyResponse,
EventCandlesticksResponse, EventForecastPercentileHistoryResponse, EventMetadataResponse,
EventResponse, EventsResponse, ExchangeAnnouncementsResponse, ExchangeScheduleResponse,
ExchangeStatusResponse, FeeChangesResponse, FillsResponse, FiltersBySportResponse,
GenerateApiKeyRequest, GenerateApiKeyResponse, GetBatchCandlesticksParams,
GetBatchLiveDataParams, GetCandlesticksParams, GetEventCandlesticksParams,
GetEventForecastPercentileHistoryParams, GetEventParams, GetEventsParams,
GetFcmOrdersParams, GetFcmPositionsParams, GetFeeChangesParams, GetFillsParams,
GetLookupHistoryParams, GetMarketsParams, GetMilestonesParams,
ApiTierLimitsResponse, BalanceResponse, BatchCancelOrdersRequest,
BatchCancelOrdersResponse, BatchCandlesticksResponse, BatchCreateOrdersRequest,
BatchCreateOrdersResponse, BatchLiveDataResponse, CancelOrderResponse,
CandlesticksResponse, CommunicationsIdResponse, CreateApiKeyRequest, CreateApiKeyResponse,
CreateMarketInCollectionRequest, CreateMarketInCollectionResponse, CreateOrderGroupRequest,
CreateOrderGroupResponse, CreateOrderRequest, CreateQuoteRequest, CreateRfqRequest,
CreateSubaccountRequest, CreateSubaccountResponse, DecreaseOrderRequest,
DeleteApiKeyResponse, EventCandlesticksResponse, EventForecastPercentileHistoryResponse,
EventMetadataResponse, EventResponse, EventsResponse, ExchangeAnnouncementsResponse,
ExchangeScheduleResponse, ExchangeStatusResponse, FeeChangesResponse, FillsResponse,
FiltersBySportResponse, GenerateApiKeyRequest, GenerateApiKeyResponse,
GetBatchCandlesticksParams, GetBatchLiveDataParams, GetCandlesticksParams,
GetEventCandlesticksParams, GetEventForecastPercentileHistoryParams, GetEventParams,
GetEventsParams, GetFcmOrdersParams, GetFcmPositionsParams, GetFeeChangesParams,
GetFillsParams, GetLookupHistoryParams, GetMarketsParams, GetMilestonesParams,
GetMultivariateCollectionsParams, GetMultivariateEventsParams, GetOrderGroupResponse,
GetOrderGroupsParams, GetOrderbookParams, GetOrdersParams, GetPositionsParams,
GetQueuePositionsParams, GetQuoteResponse, GetRfqResponse, GetSettlementsParams,
Expand All @@ -43,7 +43,7 @@ use crate::{
SettlementsResponse, StructuredTargetResponse, StructuredTargetsResponse,
SubaccountBalancesResponse, SubaccountTransfersResponse, TagsByCategoriesResponse,
TradesResponse, TransferBetweenSubaccountsRequest, TransferResponse,
UserDataTimestampResponse,
UpdateOrderGroupLimitRequest, UserDataTimestampResponse,
},
};

Expand Down Expand Up @@ -1085,6 +1085,71 @@ impl KalshiClient {
order_groups::reset_order_group(&self.http, order_group_id).await
}

/// Trigger an order group.
///
/// Triggers the order group, cancelling all orders within it as if the
/// contracts limit had been hit.
///
/// # Arguments
///
/// * `order_group_id` - The ID of the order group to trigger
///
/// # Example
///
/// ```ignore
/// client.trigger_order_group("og_123").await?;
/// println!("Triggered order group");
/// ```
pub async fn trigger_order_group(&self, order_group_id: &str) -> Result<()> {
order_groups::trigger_order_group(&self.http, order_group_id).await
}

/// Update the contracts limit for an order group.
///
/// Changes the maximum number of contracts that can be matched within
/// this group before auto-cancel is triggered.
///
/// # Arguments
///
/// * `order_group_id` - The ID of the order group to update
/// * `request` - The new contracts limit
///
/// # Example
///
/// ```ignore
/// use kalshi_trade_rs::UpdateOrderGroupLimitRequest;
///
/// let request = UpdateOrderGroupLimitRequest::new(200);
/// client.update_order_group_limit("og_123", request).await?;
/// println!("Updated order group limit");
/// ```
pub async fn update_order_group_limit(
&self,
order_group_id: &str,
request: UpdateOrderGroupLimitRequest,
) -> Result<()> {
order_groups::update_order_group_limit(&self.http, order_group_id, request).await
}

// =========================================================================
// Account API
// =========================================================================

/// Get API tier and rate limits.
///
/// Returns the user's API tier and associated rate limits.
///
/// # Example
///
/// ```ignore
/// let limits = client.get_api_limits().await?;
/// println!("Tier: {}", limits.usage_tier);
/// println!("Read limit: {}", limits.read_limit);
/// ```
pub async fn get_api_limits(&self) -> Result<ApiTierLimitsResponse> {
account::get_api_limits(&self.http).await
}

// =========================================================================
// Candlesticks API
// =========================================================================
Expand Down
11 changes: 11 additions & 0 deletions src/client/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,17 @@ impl HttpClient {
response.json::<T>().await.map_err(Error::Http)
}

/// Make a PUT request with a JSON body, expecting no response body.
///
/// # Arguments
/// * `path` - The API path
/// * `body` - The request body to serialize as JSON
pub async fn put_no_response<B: Serialize>(&self, path: &str, body: &B) -> Result<()> {
let request = self.build_request(Method::PUT, path)?.json(body);
self.execute(request).await?;
Ok(())
}

/// Make a PUT request with an empty body and deserialize the response.
///
/// # Arguments
Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ pub use client::{Environment, HttpClient, KalshiClient};
pub use error::{DisconnectReason, Error, MAX_BATCH_SIZE, Result};
pub use models::{
AcceptQuoteRequest, Action, AmendOrderRequest, AmendOrderResponse, Announcement,
AnnouncementStatus, AnnouncementType, ApiKey, ApiKeysResponse, BalanceResponse,
BatchCancelOrderResult, BatchCancelOrdersRequest, BatchCancelOrdersResponse,
AnnouncementStatus, AnnouncementType, ApiKey, ApiKeysResponse, ApiTierLimitsResponse,
BalanceResponse, BatchCancelOrderResult, BatchCancelOrdersRequest, BatchCancelOrdersResponse,
BatchCandlesticksResponse, BatchCreateOrdersRequest, BatchCreateOrdersResponse,
BatchLiveDataResponse, BatchOrderError, BatchOrderResult, CancelOrderResponse, Candlestick,
CandlestickPeriod, CandlesticksResponse, CommunicationsIdResponse, CompetitionFilter,
Expand All @@ -81,8 +81,8 @@ pub use models::{
Series, SeriesFeeChange, SeriesListResponse, SeriesResponse, Settlement, SettlementStatus,
SettlementsResponse, Side, SportFilter, StandardHoursPeriod, StrikeType, StructuredTarget,
StructuredTargetResponse, StructuredTargetsResponse, TagsByCategoriesResponse, TakerSide,
TimeInForce, Trade, TradesResponse, TradingSession, UserDataTimestampResponse,
cents_to_dollars,
TimeInForce, Trade, TradesResponse, TradingSession, UpdateOrderGroupLimitRequest,
UserDataTimestampResponse, cents_to_dollars,
};

// Re-export WebSocket types for convenience
Expand Down
4 changes: 3 additions & 1 deletion src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! All monetary values are in cents unless noted otherwise.
//! Fields ending in `_dollars` are fixed-point dollar strings.

mod account;
mod api_key;
mod balance;
mod common;
Expand All @@ -27,6 +28,7 @@ mod structured_target;
mod subaccount;

// Re-export all public types
pub use account::ApiTierLimitsResponse;
pub use api_key::{
ApiKey, ApiKeysResponse, CreateApiKeyRequest, CreateApiKeyResponse, DeleteApiKeyResponse,
GenerateApiKeyRequest, GenerateApiKeyResponse,
Expand Down Expand Up @@ -81,7 +83,7 @@ pub use order::{
};
pub use order_group::{
CreateOrderGroupRequest, CreateOrderGroupResponse, GetOrderGroupResponse, GetOrderGroupsParams,
OrderGroupSummary, OrderGroupsResponse,
OrderGroupSummary, OrderGroupsResponse, UpdateOrderGroupLimitRequest,
};
pub use position::{EventPosition, GetPositionsParams, MarketPosition, PositionsResponse};
pub use search::{
Expand Down
16 changes: 16 additions & 0 deletions src/models/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! Account models and response types.

use serde::{Deserialize, Serialize};

/// Response from GET /account/limits.
///
/// Contains information about the user's API tier and rate limits.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiTierLimitsResponse {
/// The user's API usage tier (e.g., "standard", "premier").
pub usage_tier: String,
/// Maximum read requests per second.
pub read_limit: i64,
/// Maximum write requests per second.
pub write_limit: i64,
}
Loading