diff --git a/Cargo.lock b/Cargo.lock index 791ac34..9bd1241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -450,7 +450,7 @@ dependencies = [ [[package]] name = "kis-sdk" -version = "0.1.0" +version = "0.2.0" dependencies = [ "axum", "http", diff --git a/Cargo.toml b/Cargo.toml index 43813e1..996c4cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kis-sdk" -version = "0.1.0" +version = "0.2.0" edition = "2021" description = "Rust SDK core and mock contract harness for Korea Investment & Securities Open API" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 6b2ab26..92c1dca 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,11 @@ Rust SDK core for Korea Investment & Securities Open API. -`kis-sdk` is an early Rust client and local mock contract harness for KIS Open -API integrations. The narrow typed SDK surface focuses on OAuth token issuance -and a small domestic stock slice, while inventory-backed SDK APIs account for -all 338 endpoints in the bundled official inventory by stable operation id. -Follow-on work can add more ergonomic typed wrappers without changing the -current coverage boundary. +`kis-sdk` is an early Rust client for KIS Open API integrations. The narrow +typed SDK surface focuses on OAuth token issuance and a small domestic stock +slice, while inventory-backed SDK APIs account for all 338 endpoints in the +bundled official inventory by stable operation id. Follow-on work can add more +ergonomic typed wrappers without changing the current coverage boundary. ## Current Status @@ -24,12 +23,11 @@ current coverage boundary. ## Features -- `KisClient` builder with explicit real/mock environment selection and shared - `reqwest` client reuse. +- `KisClient` builder with explicit environment selection and shared `reqwest` + client reuse. - Redacted `AppCredentials`, `Account`, 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 and mock - workflows. + in-memory token reuse and static bearer token injection for tests. - Typed domestic stock methods for quotation price, balance inquiry, and cash order calls. - Inventory-backed overseas stock API surface for 51 endpoints across @@ -48,14 +46,9 @@ current coverage boundary. - Domestic stock REST `execute_domestic_stock_rest` support for the 158 listed endpoints across the domestic stock trading/account, quotation, ELW, sector/misc, product info, market analysis, and ranking analysis collections. -- Local mock server generated from the bundled official endpoint inventory. - Explicit `RetryPolicy` and `FallbackPolicy` options. Retry is disabled by default. `RetryPolicy::conservative_reads()` retries retryable GET/read failures only and does not retry trading POST mutations. -- Real-to-mock fallback is opt-in, read-only, and recorded in response execution - metadata. Fallback requests require separate fallback credentials and a - fallback bearer token, so primary real credentials are not reused across the - fallback trust boundary. ## Installation @@ -63,7 +56,7 @@ If the crate is not available on crates.io yet, use the repository directly: ```toml [dependencies] -kis-sdk = { git = "https://github.com/bogyie/kis-sdk", branch = "bog-220-kis-sdk" } +kis-sdk = { git = "https://github.com/bogyie/kis-sdk", branch = "main" } ``` After the first authorized crates.io publish completes, consumers should be able @@ -71,21 +64,10 @@ to switch to a versioned dependency: ```toml [dependencies] -kis-sdk = "0.1" +kis-sdk = "0.2" ``` -## Quick Start With The Mock Server - -Start the local mock server: - -```sh -cargo run --bin kis-mock-server -- 127.0.0.1:0 -``` - -The server prints the bound URL, for example -`kis mock server listening on http://127.0.0.1:49152`. - -Use that URL with static local-only credentials and a dummy bearer token: +## Quick Start ```rust use kis_sdk::{ @@ -97,10 +79,11 @@ use kis_sdk::{ #[tokio::main] async fn main() -> Result<(), kis_sdk::KisError> { - let client = KisClient::builder(Environment::Mock) - .base_url("http://127.0.0.1:49152") - .app_credentials(AppCredentials::new("test_app_key", "test_app_secret")) - .static_bearer_token("test_access_token") + let client = KisClient::builder(Environment::Real) + .app_credentials(AppCredentials::new( + std::env::var("KIS_APP_KEY").expect("KIS_APP_KEY is required"), + std::env::var("KIS_APP_SECRET").expect("KIS_APP_SECRET is required"), + )) .build()?; let quote = client @@ -131,11 +114,11 @@ The typed SDK currently exposes: | `issue_realtime_approval_key` | `/oauth2/Approval` | Issues a WebSocket access approval key only; live WebSocket subscription management is outside the current typed API. | | `inquire_domestic_stock_price` | `/uapi/domestic-stock/v1/quotations/inquire-price` | Domestic stock quote read. | | `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`. | -| `execute_domestic_stock_realtime_tryitout` | `/tryitout/*` | Domain-scoped inventory execution for 29 domestic stock realtime tryitout/mock-contract endpoints. This is not a live WebSocket subscription API. | +| `place_domestic_stock_cash_order` | `/uapi/domestic-stock/v1/trading/order-cash` | Real cash orders are locally blocked by `KisError::LiveTradingDisabled`. | +| `execute_domestic_stock_realtime_tryitout` | `/tryitout/*` | Domain-scoped inventory execution for 29 domestic stock realtime tryitout endpoints. This is not a live WebSocket subscription API. | | `execute_bond_trading_account` | `/uapi/domestic-bond/v1/trading/*` | Domain-scoped inventory execution for 7 listed bond trading/account endpoints. Real trading mutations remain locally blocked. | | `execute_bond_quotation` | `/uapi/domestic-bond/v1/quotations/*` | Domain-scoped inventory execution for 8 listed bond quotation endpoints. | -| `execute_bond_realtime_tryitout` | `/tryitout/*` | Domain-scoped inventory execution for 3 listed bond realtime tryitout/mock-contract endpoints. This is not a live WebSocket subscription API. | +| `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. | The domestic futures/options SDK surface exposes inventory-backed domain @@ -250,9 +233,9 @@ let response = client ``` The realtime helpers intentionally execute the REST-style inventory tryitout -shape used by the bundled mock contract. Future live WebSocket subscription -support should use a separate API so callers do not confuse mock/tryitout -coverage with streaming behavior. +shape captured in the bundled official inventory. Future live WebSocket +subscription support should use a separate API so callers do not confuse +tryitout coverage with streaming behavior. ## Credentials And Safety @@ -266,8 +249,6 @@ coverage with streaming behavior. - `SecretString` redacts debug output, but callers must still avoid logging raw values before constructing SDK types. - Real trading mutations are blocked locally in the current implementation. - Mock cash-order examples are for integration testing only and do not execute - live orders. ## Testing And Verification @@ -285,6 +266,9 @@ Contract evidence is recorded in [`docs/contract-quality-report.md`](docs/contract-quality-report.md). The mock-server test suite requests every bundled endpoint and verifies expected mock support or explicit `KIS_MOCK_UNSUPPORTED_ENVIRONMENT` rejection. +The developer-only mock server guide is available at +[`docs/mock-server/README.md`](docs/mock-server/README.md) for contract and +test-harness validation. ## Architecture @@ -299,10 +283,6 @@ mock support or explicit `KIS_MOCK_UNSUPPORTED_ENVIRONMENT` rejection. - [Crates.io publish workflow](docs/release/crates-publish.md) -## Mock Server - -- [KIS Mock Server](docs/mock-server/README.md) - ## Package Readiness This repository is prepared for an authorized crates.io publish, but publishing @@ -310,9 +290,10 @@ has not been performed from this branch. - `Cargo.toml` includes package name, version, edition, license, description, repository, README, and keywords. -- README and usage documentation avoid secrets and use local/mock placeholders. -- The mock server and contract-quality report provide package validation - evidence without live KIS credentials. +- README and usage documentation avoid secrets and use placeholder-only + examples. +- The developer mock-server harness and contract-quality report provide package + validation evidence without live KIS credentials. - The publish workflow runs `scripts/verify-crates-publishable.py` before the third-party publish action so `publish = false` or registry restrictions cannot produce a false-positive empty publish. diff --git a/docs/release/crates-publish.md b/docs/release/crates-publish.md index de14188..ea84029 100644 --- a/docs/release/crates-publish.md +++ b/docs/release/crates-publish.md @@ -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.1.0 +- kis-sdk 0.2.0 ``` 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. diff --git a/docs/usage-ko.md b/docs/usage-ko.md index c5444dc..84d3e19 100644 --- a/docs/usage-ko.md +++ b/docs/usage-ko.md @@ -1,9 +1,8 @@ # KIS SDK 한국어 사용 가이드 이 문서는 현재 `kis-sdk` Rust 구현을 사용하는 애플리케이션 개발자를 -위한 한국어 가이드입니다. 먼저 로컬 mock server로 요청 형식과 안전 -경계를 검증한 뒤, 승인된 비밀 관리 경로를 통해 실거래 환경의 읽기 -요청을 연결하는 흐름을 기준으로 설명합니다. +위한 한국어 가이드입니다. 승인된 비밀 관리 경로를 통해 실거래 환경의 +읽기 요청을 연결하는 흐름을 기준으로 설명합니다. 현재 SDK는 OAuth 토큰 발급/폐기, WebSocket approval key 발급, 일부 국내주식 typed 메서드, 그리고 공식 inventory 기반 실행 API를 제공합니다. @@ -17,7 +16,6 @@ account됩니다. 모든 endpoint가 개별 Rust request/response struct로 승 - Rust 2021 toolchain - `tokio` 기반 async runtime -- 로컬 개발과 테스트용 `kis-mock-server` - 실환경 읽기 호출에 사용할 KIS app key/app secret 실제 app key, app secret, access token, approval key, 계좌번호, 고객 데이터는 @@ -25,12 +23,12 @@ account됩니다. 모든 endpoint가 개별 Rust request/response struct로 승 ## 의존성 추가 -현재 crate는 crates.io에 publish되지 않았고 `Cargo.toml`에 `publish = false`가 -유지되어 있습니다. 통합 브랜치를 직접 참조하세요. +현재 crate가 아직 crates.io에 publish되지 않았다면 repository를 직접 +참조하세요. ```toml [dependencies] -kis-sdk = { git = "https://github.com/bogyie/kis-sdk", branch = "bog-220-kis-sdk" } +kis-sdk = { git = "https://github.com/bogyie/kis-sdk", branch = "main" } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } serde_json = "1" ``` @@ -40,30 +38,14 @@ serde_json = "1" ```toml [dependencies] -kis-sdk = "0.1" +kis-sdk = "0.2" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } serde_json = "1" ``` -## 로컬 Mock Server 실행 - -```sh -cargo run --bin kis-mock-server -- 127.0.0.1:0 -``` - -서버는 선택된 로컬 주소를 출력합니다. - -```text -kis mock server listening on http://127.0.0.1:49152 -``` - -출력된 URL을 client `base_url`로 사용합니다. 포트 `0`은 OS가 빈 포트를 -선택하게 하므로 병렬 테스트에 적합합니다. - -## Mock Client 생성 +## 실환경 읽기 Client 구성 -Mock 요청도 KIS 요청과 같은 header 형태를 사용하므로 placeholder app -credential과 dummy bearer token을 넣습니다. +실환경 읽기 호출은 credential을 repository 밖에서 로드하세요. ```rust use kis_sdk::{ @@ -72,17 +54,20 @@ use kis_sdk::{ KisClient, }; -fn mock_client(base_url: &str) -> Result { - KisClient::builder(Environment::Mock) - .base_url(base_url) - .app_credentials(AppCredentials::new("test_app_key", "test_app_secret")) - .static_bearer_token("test_access_token") - .build() +fn real_read_client_from_env() -> Result> { + let app_key = std::env::var("KIS_APP_KEY")?; + let app_secret = std::env::var("KIS_APP_SECRET")?; + + let client = KisClient::builder(Environment::Real) + .app_credentials(AppCredentials::new(app_key, app_secret)) + .build()?; + + Ok(client) } ``` -위 값은 로컬 개발용 placeholder입니다. 운영 설정이나 공유 테스트 환경에 -실제 credential을 넣지 마세요. +로드한 값을 출력하거나 저장하지 마세요. 공유 개발 머신 또는 public CI에서 +실제 credential을 사용하는 테스트를 실행하지 마세요. ## OAuth와 Approval Key @@ -200,7 +185,8 @@ Inventory layer는 network I/O 전에 필수 query/body/non-standard header를 검증합니다. `appkey`, `appsecret`, `authorization`, `custtype`, `content-type`, 명확한 `tr_id` 같은 표준 KIS header는 client가 채웁니다. TR ID가 여러 후보로 표현된 endpoint는 `InventoryRequest::tr_id_override(...)` -를 통해 caller가 명시적으로 선택해야 합니다. +를 통해 caller가 명시적으로 선택해야 하며, 실환경 trading mutation은 +`KisError::LiveTradingDisabled`로 차단됩니다. ## 해외주식 Endpoint 호출 @@ -272,9 +258,9 @@ async fn domestic_futures_options_quote( ## 실시간 Tryitout Endpoint 호출 -국내주식 실시간과 채권 실시간 helper는 공식 inventory와 mock contract에 -보존된 REST-style `/tryitout/*` 형태를 실행합니다. 이는 mock contract와 요청 -검증을 위한 API이며 live WebSocket subscription API가 아닙니다. +국내주식 실시간과 채권 실시간 helper는 공식 inventory에 보존된 REST-style +`/tryitout/*` 형태를 실행합니다. 이는 inventory 기반 요청 형식 검증을 위한 +API이며 live WebSocket subscription API가 아닙니다. ```rust use kis_sdk::{ @@ -305,8 +291,7 @@ async fn realtime_tryitout(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::K ## 채권 Endpoint 호출 채권 helper는 주문/계좌 7개, 시세 8개, realtime tryitout 3개 endpoint로 -scope가 나뉩니다. bundled inventory 기준 대부분은 `real_only`이므로 mock -mode에서는 `KisError::UnsupportedEnvironment`가 반환될 수 있습니다. +scope가 나뉩니다. 실환경 trading mutation은 network I/O 전에 차단됩니다. ```rust use kis_sdk::{ @@ -331,7 +316,7 @@ async fn bond_price(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError } ``` -## 잔고 조회와 Mock 주문 +## 잔고 조회 잔고 조회에는 placeholder 계좌 값을 사용할 수 있습니다. 실제 계좌 식별자는 민감 정보이며 승인된 secret path에서만 주입해야 합니다. @@ -353,72 +338,21 @@ async fn inquire_balance(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::Kis } ``` -Mock cash order는 SDK 요청 구성과 mock contract 처리를 검증하기 위한 예제이며 -실제 주문을 내지 않습니다. - -```rust -use kis_sdk::{ - apis::domestic_stock::{CashOrderRequest, CashOrderSide}, - credentials::Account, -}; - -async fn submit_mock_order(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> { - let account = Account::new("12345678", "01"); - let request = CashOrderRequest::limit(&account, "005930", 1, 70_000); - - let response = client - .place_domestic_stock_cash_order(CashOrderSide::Buy, &request) - .await?; - - assert!(response.is_success()); - Ok(()) -} -``` - 현재 구현은 실환경 cash order를 network I/O 전에 `KisError::LiveTradingDisabled`로 차단합니다. -## 실환경 읽기 Client 구성 - -실환경 읽기 호출은 credential을 repository 밖에서 로드하세요. - -```rust -use kis_sdk::{ - config::Environment, - credentials::AppCredentials, - KisClient, -}; - -fn real_read_client_from_env() -> Result> { - let app_key = std::env::var("KIS_APP_KEY")?; - let app_secret = std::env::var("KIS_APP_SECRET")?; - - let client = KisClient::builder(Environment::Real) - .app_credentials(AppCredentials::new(app_key, app_secret)) - .build()?; - - Ok(client) -} -``` - -로드한 값을 출력하거나 저장하지 마세요. 공유 개발 머신 또는 public CI에서 -실제 credential을 사용하는 테스트를 실행하지 마세요. - ## Live Trading 안전 경계 - 예제는 placeholder credential과 placeholder 계좌번호만 사용합니다. -- 테스트와 mock server는 live credential, production account data, live API - call, live order execution을 요구하지 않습니다. +- 문서 예제는 live credential, production account data, live order execution을 + 소스 코드에 넣지 않습니다. - `Environment::Real`의 trading mutation은 현재 local guard로 차단되며 `KisError::LiveTradingDisabled`를 반환합니다. -- Realtime helper의 `/tryitout/*` 호출은 mock/inventory REST shape 검증용이며 +- Realtime helper의 `/tryitout/*` 호출은 inventory REST shape 검증용이며 live WebSocket subscription이 아닙니다. - Retry는 기본적으로 꺼져 있습니다. `RetryPolicy::conservative_reads()`는 retry 가능한 GET/read 실패만 재시도하며 trading POST mutation은 재시도하지 않습니다. -- Real-to-mock fallback은 기본적으로 꺼져 있고 opt-in/read-only입니다. 별도 - fallback credential과 fallback bearer token을 요구하므로 primary real - credential이 mock fallback target으로 재사용되지 않습니다. ## Endpoint Coverage 상태 @@ -443,7 +377,7 @@ request/response struct로 제공된다는 의미도 아닙니다. 현재 보장 endpoint가 typed method, scoped inventory API, 또는 lower-level `execute_inventory` 경로로 SDK-callable하게 account된다는 점입니다. -자세한 collection split, mock-contract evidence, known limitation은 +자세한 collection split, contract evidence, known limitation은 [`contract-quality-report.md`](contract-quality-report.md)를 참고하세요. ## 검증 명령 @@ -466,6 +400,5 @@ git diff --check - [Repository README](../README.md) - [English usage guide](usage.md) -- [Mock server guide](mock-server/README.md) - [Contract quality report](contract-quality-report.md) - [Runtime architecture ADR/RFC](adr/0001-kis-sdk-runtime-architecture.md) diff --git a/docs/usage.md b/docs/usage.md index bc930a3..76e2700 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,9 +1,8 @@ # KIS SDK Usage Guide This guide shows the supported `kis-sdk` workflows for the current early Rust -implementation. It is written for application developers who want to test -against the bundled mock server first and then wire real KIS credentials through -their own secret-management path. +implementation. It is written for application developers who want to wire real +KIS credentials through their own secret-management path. The typed SDK surface currently covers OAuth token issuance and revoke, WebSocket approval-key issuance, domestic stock price inquiry, domestic stock @@ -20,7 +19,6 @@ while follow-on work adds more ergonomic typed wrappers. - Rust 2021 toolchain. - Async runtime using `tokio`. - KIS app credentials for real API calls. Do not store these in source control. -- A local mock server for deterministic development and tests. ## Add The Dependency @@ -29,7 +27,7 @@ project is in integration: ```toml [dependencies] -kis-sdk = { git = "https://github.com/bogyie/kis-sdk", branch = "bog-220-kis-sdk" } +kis-sdk = { git = "https://github.com/bogyie/kis-sdk", branch = "main" } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` @@ -37,29 +35,13 @@ After the first authorized crates.io release, switch to a versioned dependency: ```toml [dependencies] -kis-sdk = "0.1" +kis-sdk = "0.2" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` -## Run The Local Mock Server +## Create A Real Read Client -```sh -cargo run --bin kis-mock-server -- 127.0.0.1:0 -``` - -The server prints the selected local address: - -```text -kis mock server listening on http://127.0.0.1:49152 -``` - -Use the printed URL as the client `base_url`. Port `0` lets the operating system -choose a free port, which keeps parallel tests isolated. - -## Create A Mock Client - -Mock requests still require the same header shape as KIS requests, so provide -placeholder app credentials and a dummy bearer token: +Load credentials outside the repository and pass them to the SDK at runtime: ```rust use kis_sdk::{ @@ -68,17 +50,20 @@ use kis_sdk::{ KisClient, }; -fn mock_client(base_url: &str) -> Result { - KisClient::builder(Environment::Mock) - .base_url(base_url) - .app_credentials(AppCredentials::new("test_app_key", "test_app_secret")) - .static_bearer_token("test_access_token") - .build() +fn real_read_client_from_env() -> Result> { + let app_key = std::env::var("KIS_APP_KEY")?; + let app_secret = std::env::var("KIS_APP_SECRET")?; + + let client = KisClient::builder(Environment::Real) + .app_credentials(AppCredentials::new(app_key, app_secret)) + .build()?; + + Ok(client) } ``` -These placeholder values are for local development only. They are not real KIS -credentials and must not be copied into production configuration. +Do not print or persist the loaded values. Do not use production credentials in +tests that can run on shared developer machines or public CI. ## Manage OAuth Tokens And WebSocket Approval Keys @@ -199,9 +184,8 @@ The inventory layer validates required query, body, and non-standard header fields before network I/O. Standard KIS headers such as `appkey`, `appsecret`, `authorization`, `custtype`, `content-type`, and unambiguous `tr_id` values are filled by the client. Endpoints with ambiguous TR IDs require -`InventoryRequest::tr_id_override(...)`. Real-only endpoints are rejected in -`Environment::Mock`, and real trading mutations remain blocked locally by -`KisError::LiveTradingDisabled`. +`InventoryRequest::tr_id_override(...)`, and real trading mutations remain +blocked locally by `KisError::LiveTradingDisabled`. ## Call An Overseas Stock Endpoint @@ -281,9 +265,8 @@ environment trading mutations are blocked locally by ## Call A Realtime Tryitout Endpoint Realtime domain helpers use the REST-style `/tryitout/*` shape preserved in the -official inventory and local mock contract. They are useful for mock-contract -coverage and request validation, but they are not live WebSocket subscription -APIs. +official inventory. They validate the request shape for inventory-backed +tryitout endpoints, but they are not live WebSocket subscription APIs. ```rust use kis_sdk::{ @@ -315,9 +298,9 @@ async fn realtime_tryitout(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::K Listed bond helpers scope inventory execution to bond trading/account, quotation, or realtime tryitout operation id constants. Most listed bond -endpoints in the bundled inventory are `real_only`, so mock-mode calls return -`KisError::UnsupportedEnvironment`; real trading mutations are still blocked -before network I/O. +endpoints in the bundled inventory are read-only quotation calls or guarded +trading/account calls; real trading mutations are still blocked before network +I/O. ```rust use kis_sdk::{ @@ -364,56 +347,8 @@ async fn inquire_balance(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::Kis Use placeholders in examples and tests. Real account identifiers are sensitive and should be supplied only by an approved runtime secret path. -## Submit A Mock Cash Order - -```rust -use kis_sdk::{ - apis::domestic_stock::{CashOrderRequest, CashOrderSide}, - credentials::Account, -}; - -async fn submit_mock_order(client: &kis_sdk::KisClient) -> Result<(), kis_sdk::KisError> { - let account = Account::new("12345678", "01"); - let request = CashOrderRequest::limit(&account, "005930", 1, 70_000); - - let response = client - .place_domestic_stock_cash_order(CashOrderSide::Buy, &request) - .await?; - - assert!(response.is_success()); - Ok(()) -} -``` - The current implementation blocks real-environment cash orders before network -I/O with `KisError::LiveTradingDisabled`. Mock cash-order examples validate SDK -request construction and mock contract handling; they do not place live orders. - -## Use Real Credentials For Read Calls - -For real read calls, load credentials outside the repository: - -```rust -use kis_sdk::{ - config::Environment, - credentials::AppCredentials, - KisClient, -}; - -fn real_read_client_from_env() -> Result> { - let app_key = std::env::var("KIS_APP_KEY")?; - let app_secret = std::env::var("KIS_APP_SECRET")?; - - let client = KisClient::builder(Environment::Real) - .app_credentials(AppCredentials::new(app_key, app_secret)) - .build()?; - - Ok(client) -} -``` - -Do not print or persist the loaded values. Do not use production credentials in -tests that can run on shared developer machines or public CI. +I/O with `KisError::LiveTradingDisabled`. ## Configure Retry @@ -422,7 +357,7 @@ Retry is disabled by default: ```rust use kis_sdk::{config::Environment, KisClient}; -let client = KisClient::builder(Environment::Mock).build()?; +let client = KisClient::builder(Environment::Real).build()?; ``` Enable conservative read retries explicitly: @@ -438,47 +373,6 @@ let client = KisClient::builder(Environment::Real) `RetryPolicy::conservative_reads()` retries retryable GET/read failures. It does not retry trading POST mutations, which avoids hidden duplicate-write behavior. -## Configure Real-To-Mock Fallback - -Fallback is disabled by default. Real-to-mock fallback is opt-in, read-only, and -requires separate fallback credentials and a fallback bearer token: - -```rust -use kis_sdk::{ - config::Environment, - credentials::AppCredentials, - fallback::FallbackPolicy, - KisClient, -}; - -let client = KisClient::builder(Environment::Real) - .app_credentials(AppCredentials::new("", "")) - .fallback_policy(FallbackPolicy::real_to_mock_reads()) - .fallback_base_url("http://127.0.0.1:49152") - .fallback_credentials(AppCredentials::new("", "")) - .fallback_static_bearer_token("mock_access_token") - .build()?; -``` - -When fallback is used, the response `execution.fallback` metadata records the -source and target environments and base URLs. Primary real credentials are not -sent to the fallback target. - -## Mock Fixture Scenarios - -The mock server supports deterministic scenario headers for error-path testing: - -| Header | Result | -| --- | --- | -| `x-kis-mock-scenario: unauthorized` | Unauthorized provider envelope. | -| `x-kis-mock-scenario: rate-limit` | HTTP 429 with `retry-after: 1`. | -| `x-kis-mock-scenario: retryable-500` | HTTP 503. | -| `x-kis-mock-scenario: provider-error` | HTTP 200 with provider error envelope. | - -Routes marked `real_only` in the bundled official inventory return -`KIS_MOCK_UNSUPPORTED_ENVIRONMENT` instead of simulating unsupported mock -behavior. - ## Package Readiness Checklist Current package evidence: @@ -489,7 +383,7 @@ Current package evidence: controlled by the release workflow tag, environment, and secret gates. - README and this guide use placeholders only and do not contain app keys, access tokens, account numbers, customer data, or live order instructions. -- Contract and mock evidence is documented in +- Contract and test evidence is documented in [`contract-quality-report.md`](contract-quality-report.md). - The expected local verification suite is: @@ -509,6 +403,5 @@ and rerun the full verification suite. ## Related Documents - [Repository README](../README.md) -- [Mock server guide](mock-server/README.md) - [Contract quality report](contract-quality-report.md) - [Runtime architecture ADR/RFC](adr/0001-kis-sdk-runtime-architecture.md)