mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
feat: Stockbit login for the order book, live quotes and daily history
Opt-in, with the user's own Stockbit session from a pasted credentialStorage cookie (or IDX_STOCKBIT_REFRESH_TOKEN): - idx stockbit login/status/logout; session file 0600, tokens never shown - rotating refresh under a cross-process lock; one refresh+retry on 401 - stocks orderbook: depth with lots and orders, OHLC, value, foreign flow - --provider stockbit quotes from the session API when logged in - --history-provider stockbit, and auto under the stockbit provider for 5d-1y daily windows - MCP get_orderbook and stockbit_status (17 tools) Endpoints, units and recorded responses from fzn0x/idx (MIT).
This commit is contained in:
parent
570439369e
commit
b5ae9b98de
61 changed files with 2882 additions and 129 deletions
|
|
@ -164,8 +164,8 @@ WIB as the fallback.
|
|||
`chart.prices[]` holds one price per minute in WIB with no offset, after a
|
||||
synthetic first row at the previous close; per-minute OHLC and volume are
|
||||
empty. An unknown code is HTTP 404 with the 404 page.
|
||||
- Stockbit's JSON API (`exodus.stockbit.com`: chart, order book) answers 401
|
||||
without a user's bearer token; it is not used.
|
||||
- Stockbit's JSON API (`exodus.stockbit.com`) answers 401 without a user's
|
||||
bearer token; it is used only with the user's own session (below).
|
||||
- Bid and offer are kept only while `market_hour.status` is `open`, so a
|
||||
closing book never reads as a live one. Observed 2026-10-23: `open` during
|
||||
trading (bid/offer set), `break` from 11:30 on a Friday, `close` after
|
||||
|
|
@ -189,6 +189,35 @@ WIB as the fallback.
|
|||
personal use; frontends say so. Fixtures (`tests/fixtures/stockbit_*`) are
|
||||
pages recorded 2026-10-22 after the close, cut to the fields read.
|
||||
|
||||
### Stockbit session (opt-in login)
|
||||
- `idx stockbit login` stores the user's session from a pasted
|
||||
`credentialStorage` cookie (or a bare refresh token) in
|
||||
`<config dir>/stockbit-session.json`, written atomically with mode 0600;
|
||||
`IDX_STOCKBIT_REFRESH_TOKEN` / `IDX_STOCKBIT_ACCESS_TOKEN` fill fields the
|
||||
file lacks. `api/stockbit/auth.rs` reads expiries from the JWTs (`exp`,
|
||||
unverified) and refreshes an access token within an hour of expiry with
|
||||
`POST /login/refresh` (bearer = refresh token), which rotates both tokens.
|
||||
Because a refresh token is spent once, refreshes run under an exclusive
|
||||
lock on `stockbit-session.lock` and re-read the file first, so concurrent
|
||||
idx processes never spend the same token. A data request answered 401 (or
|
||||
a JSON 403) refreshes once and retries once; a second refusal is
|
||||
`AUTHERROR`. Tokens never reach output, errors, logs or cache keys
|
||||
(`Session`'s `Debug` redacts them).
|
||||
- `api/stockbit/exodus.rs` (endpoints and units mapped by fzn0x/idx, MIT;
|
||||
its recordings are `tests/fixtures/stockbit_exodus_*`):
|
||||
`/company-price-feed/v2/orderbook/companies/{CODE}` gives the depth ladder
|
||||
(`volume` in shares, `que_num` orders), OHLC, value, frequency and foreign
|
||||
buy/sell/net; `/company-price-feed/market-time/session` the market state
|
||||
(`STATE_NAME_MARKET_CLOSED`, cached 30 s per process); `/charts/{CODE}/daily`
|
||||
daily candles for `1m`/`3m`/`1y` windows (volume in lots, returned in
|
||||
shares). Requests share the public page's cross-process pacing.
|
||||
- With a session, `--provider stockbit` quotes come from the order book (bid
|
||||
and offer only in `STATE_NAME_MARKET_OPEN`, a name assumed until observed
|
||||
in session), `stocks orderbook` works, and `auto` history under the stockbit
|
||||
provider uses Stockbit's daily windows (`5d` to `1y`, trimmed from `1m`,
|
||||
`3m` or `1y`), Yahoo otherwise. Without one, quotes use the public page and
|
||||
the order book is `AUTHERROR`.
|
||||
|
||||
### Capability Gating
|
||||
```
|
||||
MarketDataProvider = QuoteProvider + FundamentalsProvider
|
||||
|
|
@ -196,15 +225,17 @@ HistoryProvider = separate trait, not all providers implement
|
|||
|
||||
Factory functions:
|
||||
default_provider(kind) → Box<dyn MarketDataProvider>
|
||||
history_provider(kind, mode, verbose) → Result<(ProviderKind, Box<dyn HistoryProvider>)>
|
||||
history_provider(source, verbose) → Box<dyn HistoryProvider> (source from history_source)
|
||||
broker_provider() → Box<dyn BrokerSummaryProvider>
|
||||
trading_summary_provider() → Box<dyn TradingSummaryProvider>
|
||||
intraday_provider() → Box<dyn IntradayProvider>
|
||||
orderbook_provider() → Box<dyn OrderBookProvider>
|
||||
```
|
||||
|
||||
`ProviderKind::Stockbit` implements quotes only: fundamentals are
|
||||
`UNSUPPORTED`, the MSN-only capabilities say "requires --provider msn", and
|
||||
`auto` history falls back to Yahoo.
|
||||
`ProviderKind::Stockbit` serves quotes (and, with a session, daily history):
|
||||
fundamentals are `UNSUPPORTED`, the MSN-only capabilities say "requires
|
||||
--provider msn", and `auto` history uses Yahoo unless a session covers the
|
||||
window.
|
||||
|
||||
## Ownership Module (SQLite-backed)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue