Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ more ergonomic typed wrappers.
injection for tests and mock workflows.
- Typed domestic stock methods for quotation price, balance inquiry, and cash
order calls.
- Domain-scoped domestic futures/options inventory methods for 44
order/account, quotation, and realtime quotation endpoints.
- Inventory-backed `execute_inventory` support for the bundled official
endpoint inventory, including required input/header validation and TR ID
selection rules from the captured metadata.
Expand Down Expand Up @@ -107,6 +109,20 @@ The typed SDK currently exposes:
| `inquire_domestic_stock_balance` | `/uapi/domestic-stock/v1/trading/inquire-balance` | Domestic stock balance read. |
| `place_domestic_stock_cash_order` | `/uapi/domestic-stock/v1/trading/order-cash` | Mock cash orders are supported; real cash orders are locally blocked by `KisError::LiveTradingDisabled`. |

The domestic futures/options SDK surface exposes inventory-backed domain
methods for all 44 endpoints in these bundled official collections:

| Collection | Endpoint count | SDK entry point |
| --- | ---: | --- |
| Domestic futures/options trading/account | 15 | `execute_domestic_futures_options_trading_account` |
| Domestic futures/options quotations | 9 | `execute_domestic_futures_options_quotation` |
| Domestic futures/options realtime quotations | 20 | `execute_domestic_futures_options_realtime_quotation` |

The same endpoints are also available through the combined
`execute_domestic_futures_options` method. Operation ids are exposed through
`kis_sdk::apis::domestic_futures_options::{TRADING_ACCOUNT_OPERATION_IDS,
QUOTATION_OPERATION_IDS, REALTIME_QUOTATION_OPERATION_IDS}`.

The bundled inventory covers 338 official endpoints across 22 collections.
Endpoints outside the typed SDK surface do not yet have ergonomic typed Rust
request methods, but they can be addressed and called through the lower-level
Expand Down
18 changes: 18 additions & 0 deletions docs/contract-quality-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ The initial typed SDK surface intentionally exposes a narrow domestic stock slic

All other official endpoints are represented in the bundled contract and mock route inventory, but are not yet promoted to typed SDK request/response methods.

## Domestic Futures/Options SDK Coverage

The domestic futures/options child surface exposes scoped inventory-backed SDK
methods for all 44 endpoints in the requested official collections:

| Collection | Endpoint count | Mock support in inventory | SDK coverage |
| --- | ---: | ---: | --- |
| Domestic futures/options trading/account | 15 | 5 real+mock, 10 real-only | Covered by `execute_domestic_futures_options_trading_account` and `TRADING_ACCOUNT_OPERATION_IDS`. |
| Domestic futures/options quotations | 9 | 3 real+mock, 6 real-only | Covered by `execute_domestic_futures_options_quotation` and `QUOTATION_OPERATION_IDS`. |
| Domestic futures/options realtime | 20 | 1 real+mock, 19 real-only | Covered by `execute_domestic_futures_options_realtime_quotation` and `REALTIME_QUOTATION_OPERATION_IDS`. |

The combined `execute_domestic_futures_options` method accepts the same 44
operation ids and rejects operation ids outside the domestic futures/options
collections before network I/O. Order-changing endpoints retain local
`KisError::LiveTradingDisabled` protection in `Environment::Real`, and
side/session-specific TR ID metadata requires explicit
`InventoryRequest::tr_id_override(...)` selection.

## Mock Contract Evidence

The mock server loads the bundled contract through `ContractInventory::bundled()` and builds its route index from every `(method, path)` pair.
Expand Down
47 changes: 43 additions & 4 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ against the bundled mock server first and then wire real KIS credentials through
their own secret-management path.

The typed SDK surface currently covers OAuth token issuance, domestic stock
price inquiry, domestic stock balance inquiry, and domestic stock cash-order
requests. The shared inventory-backed execution API can also call every endpoint
captured in `contracts/kis_official_endpoint_inventory.compact.json` by stable
operation id while follow-on work adds more ergonomic typed wrappers.
price inquiry, domestic stock balance inquiry, domestic stock cash-order
requests, and domain-scoped domestic futures/options inventory calls. The
shared inventory-backed execution API can also call every endpoint captured in
`contracts/kis_official_endpoint_inventory.compact.json` by stable operation id
while follow-on work adds more ergonomic typed wrappers.

## Prerequisites

Expand Down Expand Up @@ -130,6 +131,44 @@ filled by the client. Endpoints with ambiguous TR IDs require
`Environment::Mock`, and real trading mutations remain blocked locally by
`KisError::LiveTradingDisabled`.

## Call A Domestic Futures/Options Endpoint

Domestic futures/options coverage is exposed as a scoped inventory API for 44
official endpoints: 15 trading/account endpoints, 9 quotation endpoints, and 20
realtime quotation endpoints. The operation id constants are available from
`kis_sdk::apis::domestic_futures_options`.

```rust
use kis_sdk::{
apis::domestic_futures_options::QUOTATION_OPERATION_IDS,
endpoint::InventoryRequest,
};
use serde_json::json;

async fn domestic_futures_options_quote(
client: &kis_sdk::KisClient,
) -> Result<(), kis_sdk::KisError> {
let response = client
.execute_domestic_futures_options_quotation::<serde_json::Value>(
QUOTATION_OPERATION_IDS[0],
InventoryRequest::new().query(json!({
"FID_COND_MRKT_DIV_CODE": "F",
"FID_INPUT_ISCD": "101W09"
})),
)
.await?;

assert!(response.is_success());
Ok(())
}
```

Order-changing domestic futures/options endpoints keep the same SDK safety
rules as other trading mutations. Inventory metadata with side/session-specific
TR ID text requires `InventoryRequest::tr_id_override(...)`, and real
environment trading mutations are blocked locally by
`KisError::LiveTradingDisabled`.

## Inquire A Domestic Stock Balance

```rust
Expand Down
1 change: 1 addition & 0 deletions src/apis.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod domestic_futures_options;
pub mod domestic_stock;
153 changes: 153 additions & 0 deletions src/apis/domestic_futures_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use serde::de::DeserializeOwned;

use crate::{
client::{KisClient, KisEnvelope},
endpoint::InventoryRequest,
error::KisError,
};

pub const TRADING_ACCOUNT_OPERATION_IDS: [&str; 15] = [
"domestic_futures_options_trading_account.post_domestic_futureoption_trading_order",
"domestic_futures_options_trading_account.post_domestic_futureoption_trading_order_rvsecncl",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_ccnl",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_balance",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_psbl_order",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_ngt_ccnl",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_ngt_balance",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_psbl_ngt_order",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_ngt_margin_detail",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_balance_settlement_pl",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_deposit",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_balance_valuation_pl",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_ccnl_bstime",
"domestic_futures_options_trading_account.get_domestic_futureoption_trading_inquire_daily_amount_fee",
"domestic_futures_options_trading_account.get_domestic_futureoption_quotations_margin_rate",
];

pub const QUOTATION_OPERATION_IDS: [&str; 9] = [
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_inquire_price",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_inquire_asking_price",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_inquire_daily_fuopchartprice",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_inquire_time_fuopchartprice",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_display_board_option_list",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_display_board_top",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_display_board_callput",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_display_board_futures",
"domestic_futures_options_quotation.get_domestic_futureoption_quotations_exp_price_trend",
];

pub const REALTIME_QUOTATION_OPERATION_IDS: [&str; 20] = [
"domestic_futures_options_realtime_quotation.post_tryitout_h0ifasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0ifcnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0ioasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0iocnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0ifcni0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0cfasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0cfcnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0zfasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0zfcnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0zfanc0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0zoasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0zocnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0zoanc0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0euasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0eucnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0euanc0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0eucni0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0mfasp0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0mfcnt0",
"domestic_futures_options_realtime_quotation.post_tryitout_h0mfcni0",
];

pub fn operation_ids() -> impl Iterator<Item = &'static str> {
TRADING_ACCOUNT_OPERATION_IDS
.into_iter()
.chain(QUOTATION_OPERATION_IDS)
.chain(REALTIME_QUOTATION_OPERATION_IDS)
}

impl KisClient {
pub async fn execute_domestic_futures_options<T>(
&self,
operation_id: &str,
request: InventoryRequest,
) -> Result<KisEnvelope<T>, KisError>
where
T: DeserializeOwned,
{
validate_domestic_futures_options_operation(operation_id)?;
self.execute_inventory(operation_id, request).await
}

pub async fn execute_domestic_futures_options_trading_account<T>(
&self,
operation_id: &str,
request: InventoryRequest,
) -> Result<KisEnvelope<T>, KisError>
where
T: DeserializeOwned,
{
validate_collection_operation(
"domestic futures/options trading/account",
operation_id,
&TRADING_ACCOUNT_OPERATION_IDS,
)?;
self.execute_inventory(operation_id, request).await
}

pub async fn execute_domestic_futures_options_quotation<T>(
&self,
operation_id: &str,
request: InventoryRequest,
) -> Result<KisEnvelope<T>, KisError>
where
T: DeserializeOwned,
{
validate_collection_operation(
"domestic futures/options quotation",
operation_id,
&QUOTATION_OPERATION_IDS,
)?;
self.execute_inventory(operation_id, request).await
}

pub async fn execute_domestic_futures_options_realtime_quotation<T>(
&self,
operation_id: &str,
request: InventoryRequest,
) -> Result<KisEnvelope<T>, KisError>
where
T: DeserializeOwned,
{
validate_collection_operation(
"domestic futures/options realtime quotation",
operation_id,
&REALTIME_QUOTATION_OPERATION_IDS,
)?;
self.execute_inventory(operation_id, request).await
}
}

fn validate_domestic_futures_options_operation(operation_id: &str) -> Result<(), KisError> {
if operation_ids().any(|candidate| candidate == operation_id) {
Ok(())
} else {
Err(KisError::Validation(format!(
"operation id {operation_id} is not a domestic futures/options endpoint"
)))
}
}

fn validate_collection_operation(
collection: &str,
operation_id: &str,
allowed: &[&str],
) -> Result<(), KisError> {
if allowed.contains(&operation_id) {
Ok(())
} else {
Err(KisError::Validation(format!(
"operation id {operation_id} is not a {collection} endpoint"
)))
}
}
Loading
Loading