Skip to content

Commit b2e9b35

Browse files
authored
Merge pull request #26 from pbeets/feat/batch1-api-updates
feat: batch 1 API updates — new endpoints, WS channel, _fp fields
2 parents d1c5cca + 1001d2d commit b2e9b35

File tree

19 files changed

+381
-38
lines changed

19 files changed

+381
-38
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- New `trigger_order_group()` endpoint to manually trigger an order group's
13+
auto-cancel (PUT `/portfolio/order_groups/{id}/trigger`).
14+
- New `update_order_group_limit()` endpoint to change an order group's contracts
15+
limit (PUT `/portfolio/order_groups/{id}/limit`).
16+
- New `get_api_limits()` endpoint to retrieve API tier and rate limits
17+
(GET `/account/limits`).
18+
- `contracts_limit_fp` fixed-point string field on `CreateOrderGroupRequest`,
19+
`UpdateOrderGroupLimitRequest`, `GetOrderGroupResponse`, and `OrderGroupSummary`.
20+
The integer `contracts_limit` field is now optional (provide one or both).
21+
- New `OrderGroupUpdates` WebSocket channel for order group lifecycle events
22+
(`OrderGroupUpdateData`, `OrderGroupEventType`).
23+
- Fixed-point `_fp` fields added to WebSocket message types: `delta_fp` on
24+
`OrderbookDeltaData`, `volume_fp`/`open_interest_fp` on `TickerData`,
25+
`count_fp` on `TradeData`, `count_fp`/`post_position_fp` on `FillData`,
26+
and `position_fp`/`volume_fp` on `MarketPositionData`.
27+
1028
### Fixed
1129

1230
- `OrderbookAggregator` now drops delta messages that arrive before a snapshot

examples/order_groups.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use kalshi_trade_rs::{
1313
Action, CreateOrderGroupRequest, CreateOrderRequest, GetMarketsParams, GetOrderGroupsParams,
1414
KalshiClient, KalshiConfig, MarketFilterStatus, OrderType, Side, TimeInForce,
15+
UpdateOrderGroupLimitRequest,
1516
};
1617

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

111-
// 6. Reset Order Group
112+
// 6. Update Order Group Limit
113+
println!("=== Update Order Group Limit ===");
114+
println!("Updating contracts limit to 20...\n");
115+
let update_request = UpdateOrderGroupLimitRequest::new(20);
116+
client
117+
.update_order_group_limit(order_group_id, update_request)
118+
.await?;
119+
println!("Limit updated to 20.");
120+
println!();
121+
122+
// 7. Reset Order Group
112123
println!("=== Reset Order Group ===");
113124
println!("Resetting matched contracts counter...\n");
114125
client.reset_order_group(order_group_id).await?;
115126
println!("Reset complete.");
116127
println!();
117128

118-
// 7. Delete Order Group
129+
// 8. Trigger Order Group
130+
println!("=== Trigger Order Group ===");
131+
println!("Triggering order group (cancels all orders)...\n");
132+
client.trigger_order_group(order_group_id).await?;
133+
println!("Order group triggered.");
134+
println!();
135+
136+
// 9. Delete Order Group
119137
println!("=== Delete Order Group ===");
120138
println!("Deleting the order group (this cancels all orders)...\n");
121139
client.delete_order_group(order_group_id).await?;
@@ -146,7 +164,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
146164
println!(" get_order_group(id) - Get order group details");
147165
println!(" list_order_groups() - List all order groups");
148166
println!(" list_order_groups_with_params() - List with pagination");
167+
println!(" update_order_group_limit(id, r) - Update contracts limit");
149168
println!(" reset_order_group(id) - Reset matched contracts counter");
169+
println!(" trigger_order_group(id) - Trigger auto-cancel manually");
150170
println!(" delete_order_group(id) - Delete group and cancel orders");
151171
println!();
152172

src/api.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! These modules contain endpoint-specific logic. The public API is exposed
44
//! through flat methods on [`KalshiClient`](crate::KalshiClient).
55
6+
pub(crate) mod account;
67
pub(crate) mod api_keys;
78
pub(crate) mod communications;
89
pub(crate) mod events;

src/api/README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Complete reference for all Kalshi REST API endpoints supported by this library.
2121
|----------|-----------|----------|
2222
| Exchange | 5 | 100% |
2323
| Orders | 10 | 100% |
24-
| Order Groups | 5 | 100% |
24+
| Order Groups | 7 | 100% |
25+
| Account | 1 | 100% |
2526
| Portfolio | 5 | 100% |
2627
| Subaccounts | 5 | 100% |
2728
| Markets | 6 | 100% |
@@ -36,7 +37,7 @@ Complete reference for all Kalshi REST API endpoints supported by this library.
3637
| Structured Targets | 2 | 100% |
3738
| Incentive Programs | 1 | 100% |
3839
| FCM | 2 | 100% |
39-
| **Total** | **75** | **100%** |
40+
| **Total** | **78** | **100%** |
4041

4142
---
4243

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

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

8891
---
8992

93+
## Account API
94+
95+
| Status | Method | Endpoint | Rust Function | Notes |
96+
|--------|--------|----------|---------------|-------|
97+
|| GET | `/account/limits` | `get_api_limits()` | API tier and rate limits |
98+
99+
**Source**: `src/api/account.rs`
100+
101+
---
102+
90103
## Portfolio API
91104

92105
| Status | Method | Endpoint | Rust Function | Notes |

src/api/account.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//! Account API endpoints.
2+
3+
use crate::{client::HttpClient, error::Result, models::ApiTierLimitsResponse};
4+
5+
/// Returns the user's API tier and rate limits.
6+
pub async fn get_api_limits(http: &HttpClient) -> Result<ApiTierLimitsResponse> {
7+
http.get("/account/limits").await
8+
}

src/api/order_groups.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
error::Result,
88
models::{
99
CreateOrderGroupRequest, CreateOrderGroupResponse, GetOrderGroupResponse,
10-
GetOrderGroupsParams, OrderGroupsResponse,
10+
GetOrderGroupsParams, OrderGroupsResponse, UpdateOrderGroupLimitRequest,
1111
},
1212
};
1313

@@ -55,3 +55,25 @@ pub async fn reset_order_group(http: &HttpClient, order_group_id: &str) -> Resul
5555
);
5656
http.put_empty_json(&path).await
5757
}
58+
59+
/// Triggers an order group, cancelling all orders within it.
60+
pub async fn trigger_order_group(http: &HttpClient, order_group_id: &str) -> Result<()> {
61+
let path = format!(
62+
"/portfolio/order_groups/{}/trigger",
63+
encode_id(order_group_id)
64+
);
65+
http.put_empty_json(&path).await
66+
}
67+
68+
/// Updates the contracts limit for an order group.
69+
pub async fn update_order_group_limit(
70+
http: &HttpClient,
71+
order_group_id: &str,
72+
request: UpdateOrderGroupLimitRequest,
73+
) -> Result<()> {
74+
let path = format!(
75+
"/portfolio/order_groups/{}/limit",
76+
encode_id(order_group_id)
77+
);
78+
http.put_no_response(&path, &request).await
79+
}

src/client.rs

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,29 @@ pub use websocket::WebSocketClient;
66

77
use crate::{
88
api::{
9-
api_keys, communications, events, exchange, fcm, incentive_programs, live_data, markets,
10-
milestones, multivariate, order_groups, orders, portfolio, search, series,
9+
account, api_keys, communications, events, exchange, fcm, incentive_programs, live_data,
10+
markets, milestones, multivariate, order_groups, orders, portfolio, search, series,
1111
structured_targets, subaccounts,
1212
},
1313
auth::KalshiConfig,
1414
error::Result,
1515
models::{
1616
AcceptQuoteRequest, AmendOrderRequest, AmendOrderResponse, ApiKeysResponse,
17-
BalanceResponse, BatchCancelOrdersRequest, BatchCancelOrdersResponse,
18-
BatchCandlesticksResponse, BatchCreateOrdersRequest, BatchCreateOrdersResponse,
19-
BatchLiveDataResponse, CancelOrderResponse, CandlesticksResponse, CommunicationsIdResponse,
20-
CreateApiKeyRequest, CreateApiKeyResponse, CreateMarketInCollectionRequest,
21-
CreateMarketInCollectionResponse, CreateOrderGroupRequest, CreateOrderGroupResponse,
22-
CreateOrderRequest, CreateQuoteRequest, CreateRfqRequest, CreateSubaccountRequest,
23-
CreateSubaccountResponse, DecreaseOrderRequest, DeleteApiKeyResponse,
24-
EventCandlesticksResponse, EventForecastPercentileHistoryResponse, EventMetadataResponse,
25-
EventResponse, EventsResponse, ExchangeAnnouncementsResponse, ExchangeScheduleResponse,
26-
ExchangeStatusResponse, FeeChangesResponse, FillsResponse, FiltersBySportResponse,
27-
GenerateApiKeyRequest, GenerateApiKeyResponse, GetBatchCandlesticksParams,
28-
GetBatchLiveDataParams, GetCandlesticksParams, GetEventCandlesticksParams,
29-
GetEventForecastPercentileHistoryParams, GetEventParams, GetEventsParams,
30-
GetFcmOrdersParams, GetFcmPositionsParams, GetFeeChangesParams, GetFillsParams,
31-
GetLookupHistoryParams, GetMarketsParams, GetMilestonesParams,
17+
ApiTierLimitsResponse, BalanceResponse, BatchCancelOrdersRequest,
18+
BatchCancelOrdersResponse, BatchCandlesticksResponse, BatchCreateOrdersRequest,
19+
BatchCreateOrdersResponse, BatchLiveDataResponse, CancelOrderResponse,
20+
CandlesticksResponse, CommunicationsIdResponse, CreateApiKeyRequest, CreateApiKeyResponse,
21+
CreateMarketInCollectionRequest, CreateMarketInCollectionResponse, CreateOrderGroupRequest,
22+
CreateOrderGroupResponse, CreateOrderRequest, CreateQuoteRequest, CreateRfqRequest,
23+
CreateSubaccountRequest, CreateSubaccountResponse, DecreaseOrderRequest,
24+
DeleteApiKeyResponse, EventCandlesticksResponse, EventForecastPercentileHistoryResponse,
25+
EventMetadataResponse, EventResponse, EventsResponse, ExchangeAnnouncementsResponse,
26+
ExchangeScheduleResponse, ExchangeStatusResponse, FeeChangesResponse, FillsResponse,
27+
FiltersBySportResponse, GenerateApiKeyRequest, GenerateApiKeyResponse,
28+
GetBatchCandlesticksParams, GetBatchLiveDataParams, GetCandlesticksParams,
29+
GetEventCandlesticksParams, GetEventForecastPercentileHistoryParams, GetEventParams,
30+
GetEventsParams, GetFcmOrdersParams, GetFcmPositionsParams, GetFeeChangesParams,
31+
GetFillsParams, GetLookupHistoryParams, GetMarketsParams, GetMilestonesParams,
3232
GetMultivariateCollectionsParams, GetMultivariateEventsParams, GetOrderGroupResponse,
3333
GetOrderGroupsParams, GetOrderbookParams, GetOrdersParams, GetPositionsParams,
3434
GetQueuePositionsParams, GetQuoteResponse, GetRfqResponse, GetSettlementsParams,
@@ -43,7 +43,7 @@ use crate::{
4343
SettlementsResponse, StructuredTargetResponse, StructuredTargetsResponse,
4444
SubaccountBalancesResponse, SubaccountTransfersResponse, TagsByCategoriesResponse,
4545
TradesResponse, TransferBetweenSubaccountsRequest, TransferResponse,
46-
UserDataTimestampResponse,
46+
UpdateOrderGroupLimitRequest, UserDataTimestampResponse,
4747
},
4848
};
4949

@@ -1085,6 +1085,71 @@ impl KalshiClient {
10851085
order_groups::reset_order_group(&self.http, order_group_id).await
10861086
}
10871087

1088+
/// Trigger an order group.
1089+
///
1090+
/// Triggers the order group, cancelling all orders within it as if the
1091+
/// contracts limit had been hit.
1092+
///
1093+
/// # Arguments
1094+
///
1095+
/// * `order_group_id` - The ID of the order group to trigger
1096+
///
1097+
/// # Example
1098+
///
1099+
/// ```ignore
1100+
/// client.trigger_order_group("og_123").await?;
1101+
/// println!("Triggered order group");
1102+
/// ```
1103+
pub async fn trigger_order_group(&self, order_group_id: &str) -> Result<()> {
1104+
order_groups::trigger_order_group(&self.http, order_group_id).await
1105+
}
1106+
1107+
/// Update the contracts limit for an order group.
1108+
///
1109+
/// Changes the maximum number of contracts that can be matched within
1110+
/// this group before auto-cancel is triggered.
1111+
///
1112+
/// # Arguments
1113+
///
1114+
/// * `order_group_id` - The ID of the order group to update
1115+
/// * `request` - The new contracts limit
1116+
///
1117+
/// # Example
1118+
///
1119+
/// ```ignore
1120+
/// use kalshi_trade_rs::UpdateOrderGroupLimitRequest;
1121+
///
1122+
/// let request = UpdateOrderGroupLimitRequest::new(200);
1123+
/// client.update_order_group_limit("og_123", request).await?;
1124+
/// println!("Updated order group limit");
1125+
/// ```
1126+
pub async fn update_order_group_limit(
1127+
&self,
1128+
order_group_id: &str,
1129+
request: UpdateOrderGroupLimitRequest,
1130+
) -> Result<()> {
1131+
order_groups::update_order_group_limit(&self.http, order_group_id, request).await
1132+
}
1133+
1134+
// =========================================================================
1135+
// Account API
1136+
// =========================================================================
1137+
1138+
/// Get API tier and rate limits.
1139+
///
1140+
/// Returns the user's API tier and associated rate limits.
1141+
///
1142+
/// # Example
1143+
///
1144+
/// ```ignore
1145+
/// let limits = client.get_api_limits().await?;
1146+
/// println!("Tier: {}", limits.usage_tier);
1147+
/// println!("Read limit: {}", limits.read_limit);
1148+
/// ```
1149+
pub async fn get_api_limits(&self) -> Result<ApiTierLimitsResponse> {
1150+
account::get_api_limits(&self.http).await
1151+
}
1152+
10881153
// =========================================================================
10891154
// Candlesticks API
10901155
// =========================================================================

src/client/http.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ impl HttpClient {
191191
response.json::<T>().await.map_err(Error::Http)
192192
}
193193

194+
/// Make a PUT request with a JSON body, expecting no response body.
195+
///
196+
/// # Arguments
197+
/// * `path` - The API path
198+
/// * `body` - The request body to serialize as JSON
199+
pub async fn put_no_response<B: Serialize>(&self, path: &str, body: &B) -> Result<()> {
200+
let request = self.build_request(Method::PUT, path)?.json(body);
201+
self.execute(request).await?;
202+
Ok(())
203+
}
204+
194205
/// Make a PUT request with an empty body and deserialize the response.
195206
///
196207
/// # Arguments

src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ pub use client::{Environment, HttpClient, KalshiClient};
5353
pub use error::{DisconnectReason, Error, MAX_BATCH_SIZE, Result};
5454
pub use models::{
5555
AcceptQuoteRequest, Action, AmendOrderRequest, AmendOrderResponse, Announcement,
56-
AnnouncementStatus, AnnouncementType, ApiKey, ApiKeysResponse, BalanceResponse,
57-
BatchCancelOrderResult, BatchCancelOrdersRequest, BatchCancelOrdersResponse,
56+
AnnouncementStatus, AnnouncementType, ApiKey, ApiKeysResponse, ApiTierLimitsResponse,
57+
BalanceResponse, BatchCancelOrderResult, BatchCancelOrdersRequest, BatchCancelOrdersResponse,
5858
BatchCandlesticksResponse, BatchCreateOrdersRequest, BatchCreateOrdersResponse,
5959
BatchLiveDataResponse, BatchOrderError, BatchOrderResult, CancelOrderResponse, Candlestick,
6060
CandlestickPeriod, CandlesticksResponse, CommunicationsIdResponse, CompetitionFilter,
@@ -81,8 +81,8 @@ pub use models::{
8181
Series, SeriesFeeChange, SeriesListResponse, SeriesResponse, Settlement, SettlementStatus,
8282
SettlementsResponse, Side, SportFilter, StandardHoursPeriod, StrikeType, StructuredTarget,
8383
StructuredTargetResponse, StructuredTargetsResponse, TagsByCategoriesResponse, TakerSide,
84-
TimeInForce, Trade, TradesResponse, TradingSession, UserDataTimestampResponse,
85-
cents_to_dollars,
84+
TimeInForce, Trade, TradesResponse, TradingSession, UpdateOrderGroupLimitRequest,
85+
UserDataTimestampResponse, cents_to_dollars,
8686
};
8787

8888
// Re-export WebSocket types for convenience

src/models.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! All monetary values are in cents unless noted otherwise.
44
//! Fields ending in `_dollars` are fixed-point dollar strings.
55
6+
mod account;
67
mod api_key;
78
mod balance;
89
mod common;
@@ -27,6 +28,7 @@ mod structured_target;
2728
mod subaccount;
2829

2930
// Re-export all public types
31+
pub use account::ApiTierLimitsResponse;
3032
pub use api_key::{
3133
ApiKey, ApiKeysResponse, CreateApiKeyRequest, CreateApiKeyResponse, DeleteApiKeyResponse,
3234
GenerateApiKeyRequest, GenerateApiKeyResponse,
@@ -81,7 +83,7 @@ pub use order::{
8183
};
8284
pub use order_group::{
8385
CreateOrderGroupRequest, CreateOrderGroupResponse, GetOrderGroupResponse, GetOrderGroupsParams,
84-
OrderGroupSummary, OrderGroupsResponse,
86+
OrderGroupSummary, OrderGroupsResponse, UpdateOrderGroupLimitRequest,
8587
};
8688
pub use position::{EventPosition, GetPositionsParams, MarketPosition, PositionsResponse};
8789
pub use search::{

0 commit comments

Comments
 (0)