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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kis-sdk"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
description = "Rust SDK core and mock contract harness for Korea Investment & Securities Open API"
license = "MIT OR Apache-2.0"
Expand Down
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ergonomic typed wrappers without changing the current coverage boundary.
## Current Status

- Package name: `kis-sdk`.
- Current package version: `0.2.1`.
- Crates.io publishing: package metadata is publishable, but the crate has not
been published yet. Actual upload still requires an authorized `v*.*.*` tag,
the `crates-io` environment gate, and `CARGO_REGISTRY_TOKEN`.
Expand All @@ -25,7 +26,8 @@ ergonomic typed wrappers without changing the current coverage boundary.

- `KisClient` builder with explicit environment selection and shared `reqwest`
client reuse.
- Redacted `AppCredentials`, `Account`, and `SecretString` helpers.
- Redacted `AppCredentials`, `Account`, `AccountProductCode`, and
`SecretString` helpers.
- OAuth token issuance, token revoke, and WebSocket approval-key issuance, with
in-memory token reuse and static bearer token injection for tests.
- Typed domestic stock methods for quotation price, balance inquiry, and cash
Expand All @@ -34,9 +36,11 @@ ergonomic typed wrappers without changing the current coverage boundary.
trading/account, quotation, market-analysis, and realtime-quotation
collections.
- Domain-scoped domestic futures/options inventory methods for 44
order/account, quotation, and realtime quotation endpoints.
order/account, quotation, and realtime quotation endpoints, with typed
operation-id newtypes for safer call sites.
- Domain-scoped inventory helpers for 29 domestic stock realtime tryitout
endpoints and 18 listed bond endpoints.
endpoints and 18 listed bond endpoints, with typed operation-id newtypes
available alongside the legacy string constants.
- Collection-specific overseas futures/options inventory wrapper covering all
35 order/account, quotation, and realtime endpoints from the bundled
official inventory.
Expand Down Expand Up @@ -71,7 +75,7 @@ kis-sdk = "0.2"

```rust
use kis_sdk::{
apis::domestic_stock::InquirePriceRequest,
apis::domestic_stock::{DomesticStockMarketDivision, InquirePriceRequest},
config::Environment,
credentials::AppCredentials,
KisClient,
Expand All @@ -87,7 +91,10 @@ async fn main() -> Result<(), kis_sdk::KisError> {
.build()?;

let quote = client
.inquire_domestic_stock_price(&InquirePriceRequest::new("005930"))
.inquire_domestic_stock_price(&InquirePriceRequest::with_market(
DomesticStockMarketDivision::Stock,
"005930",
))
.await?;

assert!(quote.is_success());
Expand Down Expand Up @@ -121,6 +128,22 @@ The typed SDK currently exposes:
| `execute_bond_realtime_tryitout` | `/tryitout/*` | Domain-scoped inventory execution for 3 listed bond realtime tryitout endpoints. This is not a live WebSocket subscription API. |
| `execute_overseas_futures_options` | 35 overseas futures/options inventory endpoints | Collection-specific wrapper keyed by `OverseasFuturesOptionsEndpoint`; all bundled endpoints are real-only, required fields are validated from inventory, and real trading mutations are locally blocked. |

For new call sites, prefer the typed variants where they exist:
`Account::domestic_stock`, `InquirePriceRequest::with_market`,
`CashOrderRequest::with_order_division`,
`execute_domestic_stock_realtime_tryitout_operation`,
`execute_bond_*_operation`, and
`execute_domestic_futures_options_operation`. The older `String` fields,
string constants, and `&str` operation-id methods remain available for
compatibility.

Typed helpers still serialize to the exact KIS wire values. For example,
`AccountProductCode::DomesticStock` serializes to `01`,
`DomesticStockMarketDivision::Stock` serializes to `J`, and
`CashOrderDivision::Limit` serializes to `00`. Operation newtypes expose their
stable inventory operation id through `operation_id()` and reject out-of-scope
strings through `FromStr`.

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

Expand Down
2 changes: 1 addition & 1 deletion docs/release/crates-publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Expected successful guard output includes the package name and version, for exam

```text
Publishable crates.io package(s):
- kis-sdk 0.2.0
- kis-sdk 0.2.1
```

If a release workflow succeeds but crates.io does not show the crate, check the publish job log for the guard output, the `Publish crate` step, and the action's crates.io propagation check. A missing guard output or an empty action package list indicates the publish path did not prove that a workspace package was publishable.
46 changes: 34 additions & 12 deletions docs/usage-ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,14 @@ WebSocket session, subscription, reconnect, message decoding을 관리하지
## 국내주식 현재가 조회

```rust
use kis_sdk::apis::domestic_stock::InquirePriceRequest;
use kis_sdk::apis::domestic_stock::{DomesticStockMarketDivision, InquirePriceRequest};

async fn inquire_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let response = client
.inquire_domestic_stock_price(&InquirePriceRequest::new("005930"))
.inquire_domestic_stock_price(&InquirePriceRequest::with_market(
DomesticStockMarketDivision::Stock,
"005930",
))
.await?;

if response.is_success() {
Expand All @@ -131,6 +134,22 @@ async fn inquire_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisEr
현재 응답 `output`은 provider field를 `serde_json::Value`로 보존합니다. 넓은
범위의 typed response struct는 후속 작업에서 점진적으로 추가될 수 있습니다.

## Typed KIS 코드 Helper 우선 사용

SDK가 typed helper를 제공하는 곳에서는 고정 KIS 문자열을 직접 입력하지 않는
경로를 우선 사용하세요. typed 값은 provider가 요구하는 wire value를 그대로
직렬화합니다.

- `Account::domestic_stock(...)`는 계좌 상품 코드 `01`을 채웁니다.
- `DomesticStockMarketDivision::Stock`은 `J`로 직렬화됩니다.
- `CashOrderDivision::Limit`은 `00`으로 직렬화됩니다.
- 채권, 국내주식 realtime, 국내선물옵션 operation newtype은
`operation_id()`로 stable inventory id를 노출하고, `FromStr`에서 scope 밖
문자열을 거부합니다.

기존 문자열 상수와 `&str` 기반 메서드는 호환성을 위해 유지됩니다. 새 호출부는
typed 생성자와 `*_operation` 메서드를 우선 사용하세요.

## Inventory Endpoint 호출

공식 inventory에 포함된 endpoint는 stable operation id와
Expand Down Expand Up @@ -230,17 +249,20 @@ trading mutation은 network I/O 전에 `KisError::LiveTradingDisabled`를

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

async fn domestic_futures_options_quote(
client: &kis_sdk::KisClient,
) -> Result<(), kis_sdk::KisError> {
let operation = DomesticFuturesOptionsOperation::from_static(QUOTATION_OPERATION_IDS[0])?;
let response = client
.execute_domestic_futures_options_quotation::<serde_json::Value>(
QUOTATION_OPERATION_IDS[0],
.execute_domestic_futures_options_operation::<serde_json::Value>(
operation,
InventoryRequest::new().query(json!({
"FID_COND_MRKT_DIV_CODE": "F",
"FID_INPUT_ISCD": "101W09"
Expand All @@ -264,15 +286,15 @@ API이며 live WebSocket subscription API가 아닙니다.

```rust
use kis_sdk::{
apis::domestic_stock_realtime,
apis::domestic_stock_realtime::DomesticStockRealtimeOperation,
endpoint::InventoryRequest,
};
use serde_json::json;

async fn realtime_tryitout(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let response = client
.execute_domestic_stock_realtime_tryitout::<serde_json::Value>(
domestic_stock_realtime::REALTIME_TRADE_KRX,
.execute_domestic_stock_realtime_tryitout_operation::<serde_json::Value>(
DomesticStockRealtimeOperation::REALTIME_TRADE_KRX,
InventoryRequest::new()
.header("approval_key", "test_approval_key")
.header("tr_type", "1")
Expand All @@ -295,15 +317,15 @@ scope가 나뉩니다. 실환경 trading mutation은 network I/O 전에 차단

```rust
use kis_sdk::{
apis::bond,
apis::bond::BondQuotationOperation,
endpoint::InventoryRequest,
};
use serde_json::json;

async fn bond_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let response = client
.execute_bond_quotation::<serde_json::Value>(
bond::INQUIRE_PRICE,
.execute_bond_quotation_operation::<serde_json::Value>(
BondQuotationOperation::INQUIRE_PRICE,
InventoryRequest::new().query(json!({
"FID_COND_MRKT_DIV_CODE": "B",
"FID_INPUT_ISCD": "KR103502GA34"
Expand All @@ -328,7 +350,7 @@ use kis_sdk::{
};

async fn inquire_balance(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let account = Account::new("12345678", "01");
let account = Account::domestic_stock("12345678");
let response = client
.inquire_domestic_stock_balance(&InquireBalanceRequest::new(&account))
.await?;
Expand Down
45 changes: 33 additions & 12 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,14 @@ message decoding.
## Inquire A Domestic Stock Price

```rust
use kis_sdk::apis::domestic_stock::InquirePriceRequest;
use kis_sdk::apis::domestic_stock::{DomesticStockMarketDivision, InquirePriceRequest};

async fn inquire_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let response = client
.inquire_domestic_stock_price(&InquirePriceRequest::new("005930"))
.inquire_domestic_stock_price(&InquirePriceRequest::with_market(
DomesticStockMarketDivision::Stock,
"005930",
))
.await?;

if response.is_success() {
Expand All @@ -129,6 +132,21 @@ async fn inquire_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisEr
The current output type preserves provider fields as `serde_json::Value` so the
SDK can expose the endpoint before broad typed response structs are finalized.

## Prefer Typed KIS Code Helpers

Prefer typed helpers where the SDK exposes them. They keep call sites away from
mistyped fixed KIS strings while preserving the provider wire values:

- `Account::domestic_stock(...)` fills account product code `01`.
- `DomesticStockMarketDivision::Stock` serializes to `J`.
- `CashOrderDivision::Limit` serializes to `00`.
- Bond, domestic stock realtime, and domestic futures/options operation
newtypes expose valid inventory ids through `operation_id()` and reject
out-of-scope strings through `FromStr`.

Existing string constants and `&str` methods remain available for compatibility,
but new code should prefer typed constructors and `*_operation` methods.

## Call An Inventory Endpoint

Use `InventoryCatalog` to inspect generated operation ids and
Expand Down Expand Up @@ -233,17 +251,20 @@ realtime quotation endpoints. The operation id constants are available from

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

async fn domestic_futures_options_quote(
client: &kis_sdk::KisClient,
) -> Result<(), kis_sdk::KisError> {
let operation = DomesticFuturesOptionsOperation::from_static(QUOTATION_OPERATION_IDS[0])?;
let response = client
.execute_domestic_futures_options_quotation::<serde_json::Value>(
QUOTATION_OPERATION_IDS[0],
.execute_domestic_futures_options_operation::<serde_json::Value>(
operation,
InventoryRequest::new().query(json!({
"FID_COND_MRKT_DIV_CODE": "F",
"FID_INPUT_ISCD": "101W09"
Expand All @@ -270,15 +291,15 @@ tryitout endpoints, but they are not live WebSocket subscription APIs.

```rust
use kis_sdk::{
apis::domestic_stock_realtime,
apis::domestic_stock_realtime::DomesticStockRealtimeOperation,
endpoint::InventoryRequest,
};
use serde_json::json;

async fn realtime_tryitout(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let response = client
.execute_domestic_stock_realtime_tryitout::<serde_json::Value>(
domestic_stock_realtime::REALTIME_TRADE_KRX,
.execute_domestic_stock_realtime_tryitout_operation::<serde_json::Value>(
DomesticStockRealtimeOperation::REALTIME_TRADE_KRX,
InventoryRequest::new()
.header("approval_key", "test_approval_key")
.header("tr_type", "1")
Expand All @@ -304,15 +325,15 @@ I/O.

```rust
use kis_sdk::{
apis::bond,
apis::bond::BondQuotationOperation,
endpoint::InventoryRequest,
};
use serde_json::json;

async fn bond_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let response = client
.execute_bond_quotation::<serde_json::Value>(
bond::INQUIRE_PRICE,
.execute_bond_quotation_operation::<serde_json::Value>(
BondQuotationOperation::INQUIRE_PRICE,
InventoryRequest::new().query(json!({
"FID_COND_MRKT_DIV_CODE": "B",
"FID_INPUT_ISCD": "KR103502GA34"
Expand All @@ -334,7 +355,7 @@ use kis_sdk::{
};

async fn inquire_balance(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> {
let account = Account::new("12345678", "01");
let account = Account::domestic_stock("12345678");
let response = client
.inquire_domestic_stock_balance(&InquireBalanceRequest::new(&account))
.await?;
Expand Down
Loading
Loading