mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
fix(stockbit): bid/offer while trading; drop stale foreign flow
Observed in session on 2026-11-03:
- The regular board trades as STATE_NAME_SESSION_2, not the assumed
STATE_NAME_MARKET_OPEN, so session quotes hid bid/offer all day. Any
numbered STATE_NAME_SESSION_<n> now counts as trading; the lunch break
is STATE_NAME_BREAK.
- In session the order book's fbuy/fsell/fnet were the previous session's
(equal to IPOT's 2026-11-02 net for BBCA, BBRI and TLKM) and
has_foreign_bs was false. Foreign figures are kept only when that flag
is true.
Fixtures for the break, the trading state and an in-session book; the
table names the session ("market open (session 2)").
Claude-Session: https://claude.ai/code/session_01RqPPVeVDrnLoP7NUZmLRxY
This commit is contained in:
parent
ca4d3d09c2
commit
32f476bae7
14 changed files with 113 additions and 39 deletions
|
|
@ -96,7 +96,7 @@ idx ownership changes --from 2026-07-31 --to 2026-08-31
|
||||||
- Stockbit, opt-in: without a login, `stocks intraday` (today's price by the
|
- Stockbit, opt-in: without a login, `stocks intraday` (today's price by the
|
||||||
minute) and `--provider stockbit` quotes with the best bid/offer while the
|
minute) and `--provider stockbit` quotes with the best bid/offer while the
|
||||||
market is open, delayed at least 10 minutes. With your own account
|
market is open, delayed at least 10 minutes. With your own account
|
||||||
(`idx stockbit login`): `stocks orderbook` depth and foreign flow, live
|
(`idx stockbit login`): `stocks orderbook` depth, live
|
||||||
quotes and Stockbit daily history. Personal use under Stockbit's terms; see
|
quotes and Stockbit daily history. Personal use under Stockbit's terms; see
|
||||||
the [Stockbit page](https://idx.rasyidanaf.com/cli/stockbit/)
|
the [Stockbit page](https://idx.rasyidanaf.com/cli/stockbit/)
|
||||||
- Local file cache with TTL, and `--offline` mode with stale-cache fallback
|
- Local file cache with TTL, and `--offline` mode with stale-cache fallback
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,9 @@ history lives in git.
|
||||||
- Order book depth without a login: no free source exists (IDX sells delayed
|
- Order book depth without a login: no free source exists (IDX sells delayed
|
||||||
depth to vendors); `stocks orderbook` needs the user's Stockbit login.
|
depth to vendors); `stocks orderbook` needs the user's Stockbit login.
|
||||||
- Stockbit login follow-ups: intraday OHLCV from the session's `1d` chart;
|
- Stockbit login follow-ups: intraday OHLCV from the session's `1d` chart;
|
||||||
observing the session API's open-market state name (assumed
|
confirming session 1 is `STATE_NAME_SESSION_1` (session 2, `BREAK` and
|
||||||
`STATE_NAME_MARKET_OPEN`; `STATE_NAME_MARKET_CLOSED` seen 2026-10-29); an
|
`MARKET_CLOSED` were recorded on 2026-11-03), and when `has_foreign_bs`
|
||||||
|
turns on with the day's foreign figures; an
|
||||||
opt-in daily refresh so a session outlives the refresh token's week, once
|
opt-in daily refresh so a session outlives the refresh token's week, once
|
||||||
it is known whether Stockbit caps a session's total life; and the sign-in
|
it is known whether Stockbit caps a session's total life; and the sign-in
|
||||||
response (`/login/v6/*`, read over DevTools' `Fetch`) as a second signal
|
response (`/login/v6/*`, read over DevTools' `Fetch`) as a second signal
|
||||||
|
|
|
||||||
|
|
@ -272,10 +272,11 @@ pub fn print_intraday(day: &Intraday, no_color: bool) -> Result<(), IdxError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stockbit's market state for people: `STATE_NAME_MARKET_CLOSED` reads
|
/// Stockbit's market state for people: `STATE_NAME_MARKET_CLOSED` reads
|
||||||
/// `closed`.
|
/// `closed`, `STATE_NAME_BREAK` `break`, `STATE_NAME_SESSION_2` `session 2`.
|
||||||
fn market_words(state: &str) -> String {
|
fn market_words(state: &str) -> String {
|
||||||
state
|
state
|
||||||
.strip_prefix("STATE_NAME_MARKET_")
|
.strip_prefix("STATE_NAME_MARKET_")
|
||||||
|
.or_else(|| state.strip_prefix("STATE_NAME_"))
|
||||||
.unwrap_or(state)
|
.unwrap_or(state)
|
||||||
.replace('_', " ")
|
.replace('_', " ")
|
||||||
.to_lowercase()
|
.to_lowercase()
|
||||||
|
|
@ -326,7 +327,9 @@ pub fn print_orderbook(book: &OrderBook, no_color: bool) -> Result<(), IdxError>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
match book.market_status.as_deref() {
|
match book.market_status.as_deref() {
|
||||||
Some(idx_core::api::stockbit::exodus::MARKET_OPEN) => println!("market open"),
|
Some(state) if idx_core::api::stockbit::exodus::is_trading(state) => {
|
||||||
|
println!("market open ({})", terminal_safe(&market_words(state)))
|
||||||
|
}
|
||||||
Some(state) => println!(
|
Some(state) => println!(
|
||||||
"market {}: the book as the market left it",
|
"market {}: the book as the market left it",
|
||||||
terminal_safe(&market_words(state))
|
terminal_safe(&market_words(state))
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,13 @@
|
||||||
//! user's session (see `auth`). Opt-in: used only when a session is stored.
|
//! user's session (see `auth`). Opt-in: used only when a session is stored.
|
||||||
//!
|
//!
|
||||||
//! - `GET /company-price-feed/v2/orderbook/companies/{CODE}`: last price,
|
//! - `GET /company-price-feed/v2/orderbook/companies/{CODE}`: last price,
|
||||||
//! OHLC, volume (shares), value, frequency, foreign buy/sell/net (IDR) and
|
//! OHLC, volume (shares), value, frequency, foreign buy/sell/net (IDR, used
|
||||||
//! the depth ladder `bid[]`/`offer[]` of `{price, que_num, volume}` (shares).
|
//! only when `has_foreign_bs` is true) and the depth ladder
|
||||||
|
//! `bid[]`/`offer[]` of `{price, que_num, volume}` (shares).
|
||||||
//! - `GET /company-price-feed/market-time/session`: the market's state,
|
//! - `GET /company-price-feed/market-time/session`: the market's state,
|
||||||
//! `data.detail.regular.state_name` (e.g. `STATE_NAME_MARKET_CLOSED`).
|
//! `data.detail.regular.state_name`: `STATE_NAME_SESSION_2` while trading,
|
||||||
|
//! `STATE_NAME_BREAK`, `STATE_NAME_MARKET_CLOSED` (recorded). The `fca`
|
||||||
|
//! (full call auction) board beside it keeps its own state and is not read.
|
||||||
//! - `GET /charts/{CODE}/daily?timeframe=1m|3m|1y&chart_type=PRICE_CHART_TYPE_CANDLE`:
|
//! - `GET /charts/{CODE}/daily?timeframe=1m|3m|1y&chart_type=PRICE_CHART_TYPE_CANDLE`:
|
||||||
//! daily candles, `formatted_date` `YYYY-MM-DD`, string OHLC, `value` the
|
//! daily candles, `formatted_date` `YYYY-MM-DD`, string OHLC, `value` the
|
||||||
//! close and `volume` in lots.
|
//! close and `volume` in lots.
|
||||||
|
|
@ -29,10 +32,19 @@ use crate::error::IdxError;
|
||||||
|
|
||||||
const BASE_URL: &str = "https://exodus.stockbit.com";
|
const BASE_URL: &str = "https://exodus.stockbit.com";
|
||||||
const REFRESH_PATH: &str = "/login/refresh";
|
const REFRESH_PATH: &str = "/login/refresh";
|
||||||
/// The state in which the book is live. Assumed from Stockbit's naming
|
/// The prefix of the states in which the regular market trades:
|
||||||
/// (only `STATE_NAME_MARKET_CLOSED` has been recorded); any other state hides
|
/// `STATE_NAME_SESSION_2` was recorded on 2026-11-03 from 13:30; session 1 is
|
||||||
/// bid/offer in quotes, as on the public page.
|
/// assumed to follow the same naming. Every other state (`STATE_NAME_BREAK`,
|
||||||
pub const MARKET_OPEN: &str = "STATE_NAME_MARKET_OPEN";
|
/// `STATE_NAME_MARKET_CLOSED`, auctions) hides bid/offer in quotes, as on the
|
||||||
|
/// public page.
|
||||||
|
const TRADING_PREFIX: &str = "STATE_NAME_SESSION_";
|
||||||
|
|
||||||
|
/// Whether Stockbit's market state is a trading session.
|
||||||
|
pub fn is_trading(state: &str) -> bool {
|
||||||
|
state
|
||||||
|
.strip_prefix(TRADING_PREFIX)
|
||||||
|
.is_some_and(|n| !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit()))
|
||||||
|
}
|
||||||
/// The market's state changes a few times a day; one read serves this long.
|
/// The market's state changes a few times a day; one read serves this long.
|
||||||
const MARKET_STATE_TTL: Duration = Duration::from_secs(30);
|
const MARKET_STATE_TTL: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
|
|
@ -293,6 +305,12 @@ pub fn parse_orderbook(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
// Stockbit flags whether `fbuy`/`fsell`/`fnet` are current. While the
|
||||||
|
// flag was off (2026-11-03, in session) they were the previous session's
|
||||||
|
// figures (equal to IPOT's for 2026-11-02 for BBCA, BBRI and TLKM), so
|
||||||
|
// they are dropped rather than shown beside today's prices.
|
||||||
|
let foreign_current = d.get("has_foreign_bs").and_then(Value::as_bool) == Some(true);
|
||||||
|
let foreign = |key: &str| foreign_current.then(|| whole(d.get(key))).flatten();
|
||||||
Ok(OrderBook {
|
Ok(OrderBook {
|
||||||
symbol: resolved.to_string(),
|
symbol: resolved.to_string(),
|
||||||
name: d.get("name").and_then(Value::as_str).map(str::to_string),
|
name: d.get("name").and_then(Value::as_str).map(str::to_string),
|
||||||
|
|
@ -306,9 +324,9 @@ pub fn parse_orderbook(
|
||||||
volume,
|
volume,
|
||||||
value: count(d.get("value")),
|
value: count(d.get("value")),
|
||||||
frequency: count(d.get("frequency")),
|
frequency: count(d.get("frequency")),
|
||||||
foreign_buy: whole(d.get("fbuy")),
|
foreign_buy: foreign("fbuy"),
|
||||||
foreign_sell: whole(d.get("fsell")),
|
foreign_sell: foreign("fsell"),
|
||||||
foreign_net: whole(d.get("fnet")),
|
foreign_net: foreign("fnet"),
|
||||||
bids: levels("bid"),
|
bids: levels("bid"),
|
||||||
offers: levels("offer"),
|
offers: levels("offer"),
|
||||||
market_status,
|
market_status,
|
||||||
|
|
@ -318,7 +336,7 @@ pub fn parse_orderbook(
|
||||||
|
|
||||||
/// A quote from the order book; bid/offer only while the market is open.
|
/// A quote from the order book; bid/offer only while the market is open.
|
||||||
pub fn quote_from_book(book: &OrderBook) -> Quote {
|
pub fn quote_from_book(book: &OrderBook) -> Quote {
|
||||||
let open = book.market_status.as_deref() == Some(MARKET_OPEN);
|
let open = book.market_status.as_deref().is_some_and(is_trading);
|
||||||
let best = |levels: &[BookLevel]| open.then(|| levels.first().map(|l| l.price)).flatten();
|
let best = |levels: &[BookLevel]| open.then(|| levels.first().map(|l| l.price)).flatten();
|
||||||
Quote {
|
Quote {
|
||||||
symbol: book.symbol.clone(),
|
symbol: book.symbol.clone(),
|
||||||
|
|
@ -473,21 +491,59 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quote_shows_the_best_levels_only_while_the_market_is_open() {
|
fn quote_shows_the_best_levels_only_while_the_market_is_open() {
|
||||||
let mut book = parse_orderbook("BBCA.JK", &fixture("orderbook_bbca.json"), None).unwrap();
|
let trading = parse_market_status(&fixture("market_session_trading.json"));
|
||||||
let closed = quote_from_book(&book);
|
let book = |state: Option<String>| {
|
||||||
assert_eq!((closed.bid, closed.offer), (None, None));
|
parse_orderbook("BBCA.JK", &fixture("orderbook_bbca_in_session.json"), state).unwrap()
|
||||||
book.market_status = Some(MARKET_OPEN.to_string());
|
};
|
||||||
let open = quote_from_book(&book);
|
let open = quote_from_book(&book(trading));
|
||||||
assert_eq!((open.bid, open.offer), (None, Some(5175)));
|
assert_eq!((open.bid, open.offer), (Some(4850), Some(4860)));
|
||||||
assert_eq!(open.price, 5175);
|
assert_eq!(open.price, 4860);
|
||||||
|
for state in ["market_session_break.json", "market_session.json"] {
|
||||||
|
let shut = quote_from_book(&book(parse_market_status(&fixture(state))));
|
||||||
|
assert_eq!((shut.bid, shut.offer), (None, None), "{state}");
|
||||||
|
}
|
||||||
|
assert_eq!(quote_from_book(&book(None)).bid, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn market_status_is_read_verbatim() {
|
fn market_status_is_read_verbatim() {
|
||||||
|
for (name, state) in [
|
||||||
|
("market_session.json", "STATE_NAME_MARKET_CLOSED"),
|
||||||
|
("market_session_break.json", "STATE_NAME_BREAK"),
|
||||||
|
("market_session_trading.json", "STATE_NAME_SESSION_2"),
|
||||||
|
] {
|
||||||
|
assert_eq!(parse_market_status(&fixture(name)).as_deref(), Some(state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn only_numbered_sessions_are_trading() {
|
||||||
|
assert!(is_trading("STATE_NAME_SESSION_1"));
|
||||||
|
assert!(is_trading("STATE_NAME_SESSION_2"));
|
||||||
|
for state in [
|
||||||
|
"STATE_NAME_BREAK",
|
||||||
|
"STATE_NAME_MARKET_CLOSED",
|
||||||
|
"STATE_NAME_MARKET_OPEN",
|
||||||
|
"STATE_NAME_SESSION_",
|
||||||
|
"STATE_NAME_SESSION_PRE_OPENING",
|
||||||
|
"",
|
||||||
|
] {
|
||||||
|
assert!(!is_trading(state), "{state}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn foreign_flow_only_when_stockbit_marks_it_current() {
|
||||||
|
// In session (has_foreign_bs false) the figures were 2026-11-02's.
|
||||||
|
let live =
|
||||||
|
parse_orderbook("BBCA.JK", &fixture("orderbook_bbca_in_session.json"), None).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_market_status(&fixture("market_session.json")).as_deref(),
|
(live.foreign_buy, live.foreign_sell, live.foreign_net),
|
||||||
Some("STATE_NAME_MARKET_CLOSED")
|
(None, None, None)
|
||||||
);
|
);
|
||||||
|
assert_eq!(live.volume, Some(134_744_100));
|
||||||
|
let done = parse_orderbook("BBCA.JK", &fixture("orderbook_bbca.json"), None).unwrap();
|
||||||
|
assert_eq!(done.foreign_buy, Some(1_616_461_977_500));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,8 @@ pub struct OrderBook {
|
||||||
pub value: Option<u64>,
|
pub value: Option<u64>,
|
||||||
/// Trades in the session.
|
/// Trades in the session.
|
||||||
pub frequency: Option<u64>,
|
pub frequency: Option<u64>,
|
||||||
/// Foreign buy, sell and net value over the session, IDR.
|
/// Foreign buy, sell and net value over the session, IDR; `None` while
|
||||||
|
/// Stockbit marks them not current (in session they lag a day).
|
||||||
pub foreign_buy: Option<i64>,
|
pub foreign_buy: Option<i64>,
|
||||||
pub foreign_sell: Option<i64>,
|
pub foreign_sell: Option<i64>,
|
||||||
pub foreign_net: Option<i64>,
|
pub foreign_net: Option<i64>,
|
||||||
|
|
@ -134,7 +135,8 @@ pub struct OrderBook {
|
||||||
pub bids: Vec<BookLevel>,
|
pub bids: Vec<BookLevel>,
|
||||||
/// Best price first.
|
/// Best price first.
|
||||||
pub offers: Vec<BookLevel>,
|
pub offers: Vec<BookLevel>,
|
||||||
/// Stockbit's session state, verbatim (e.g. `STATE_NAME_MARKET_CLOSED`).
|
/// Stockbit's session state, verbatim (e.g. `STATE_NAME_SESSION_2`,
|
||||||
|
/// `STATE_NAME_BREAK`, `STATE_NAME_MARKET_CLOSED`).
|
||||||
pub market_status: Option<String>,
|
pub market_status: Option<String>,
|
||||||
/// Stockbit's listing status when it is not active (e.g. suspended).
|
/// Stockbit's listing status when it is not active (e.g. suspended).
|
||||||
pub session_note: Option<String>,
|
pub session_note: Option<String>,
|
||||||
|
|
|
||||||
1
crates/idx-core/tests/fixtures/stockbit_exodus_market_session_break.json
vendored
Normal file
1
crates/idx-core/tests/fixtures/stockbit_exodus_market_session_break.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"message":"Successfully get session info data","data":{"datetime":"2026-11-03 12:35:59","detail":{"fca":{"session":3,"state_name":"STATE_NAME_BREAK","is_last_session":false,"is_end_of_day":false,"state_start_time":"12:00:00","state_end_time":"13:59:59","time_left":{"raw":5040,"formatted":"1 jam 24 menit 0 detik"},"suspend_info":""},"regular":{"session":1,"state_name":"STATE_NAME_BREAK","is_last_session":false,"is_end_of_day":false,"state_start_time":"12:00:00","state_end_time":"13:29:59","time_left":{"raw":3240,"formatted":"0 jam 54 menit 0 detik"},"suspend_info":""}}}}
|
||||||
1
crates/idx-core/tests/fixtures/stockbit_exodus_market_session_trading.json
vendored
Normal file
1
crates/idx-core/tests/fixtures/stockbit_exodus_market_session_trading.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"message":"Successfully get session info data","data":{"datetime":"2026-11-03 13:30:03","detail":{"fca":{"session":3,"state_name":"STATE_NAME_BREAK","is_last_session":false,"is_end_of_day":false,"state_start_time":"12:00:00","state_end_time":"13:59:59","time_left":{"raw":1796,"formatted":"0 jam 29 menit 56 detik"},"suspend_info":""},"regular":{"session":2,"state_name":"STATE_NAME_SESSION_2","is_last_session":true,"is_end_of_day":false,"state_start_time":"13:30:00","state_end_time":"15:49:59","time_left":{"raw":9896,"formatted":"2 jam 44 menit 56 detik"},"suspend_info":""}}}}
|
||||||
1
crates/idx-core/tests/fixtures/stockbit_exodus_orderbook_bbca_in_session.json
vendored
Normal file
1
crates/idx-core/tests/fixtures/stockbit_exodus_orderbook_bbca_in_session.json
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -165,9 +165,10 @@ per-minute volume. Empty `points` means the stock did not trade this session (se
|
||||||
#[tool(
|
#[tool(
|
||||||
description = "Order book of one IDX stock from the user's own Stockbit session: \
|
description = "Order book of one IDX stock from the user's own Stockbit session: \
|
||||||
`bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, \
|
`bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, \
|
||||||
volume (shares), value and frequency, and foreign buy/sell/net in IDR. `market_status` is \
|
volume (shares), value and frequency, and foreign buy/sell/net in IDR (null while Stockbit \
|
||||||
Stockbit's state, verbatim; outside STATE_NAME_MARKET_OPEN the book is as the market left \
|
marks them not current, as during trading; get_foreign_flow has them per session). \
|
||||||
it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL \
|
`market_status` is Stockbit's state, verbatim: STATE_NAME_SESSION_1 or _2 is trading; in any \
|
||||||
|
other state the book is as the market left it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL \
|
||||||
the user can open."
|
the user can open."
|
||||||
)]
|
)]
|
||||||
async fn get_orderbook(&self, params: Parameters<OrderBookParams>) -> ToolResult {
|
async fn get_orderbook(&self, params: Parameters<OrderBookParams>) -> ToolResult {
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@
|
||||||
"name": "get_investor_holdings"
|
"name": "get_investor_holdings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Order book of one IDX stock from the user's own Stockbit session: `bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, volume (shares), value and frequency, and foreign buy/sell/net in IDR. `market_status` is Stockbit's state, verbatim; outside STATE_NAME_MARKET_OPEN the book is as the market left it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL the user can open.",
|
"description": "Order book of one IDX stock from the user's own Stockbit session: `bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, volume (shares), value and frequency, and foreign buy/sell/net in IDR (null while Stockbit marks them not current, as during trading; get_foreign_flow has them per session). `market_status` is Stockbit's state, verbatim: STATE_NAME_SESSION_1 or _2 is trading; in any other state the book is as the market left it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL the user can open.",
|
||||||
"inputSchema": {
|
"inputSchema": {
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
|
|
@ -269,13 +269,17 @@ WIB as the fallback.
|
||||||
its recordings are `tests/fixtures/stockbit_exodus_*`):
|
its recordings are `tests/fixtures/stockbit_exodus_*`):
|
||||||
`/company-price-feed/v2/orderbook/companies/{CODE}` gives the depth ladder
|
`/company-price-feed/v2/orderbook/companies/{CODE}` gives the depth ladder
|
||||||
(`volume` in shares, `que_num` orders), OHLC, value, frequency and foreign
|
(`volume` in shares, `que_num` orders), OHLC, value, frequency and foreign
|
||||||
buy/sell/net; `/company-price-feed/market-time/session` the market state
|
buy/sell/net (kept only when `has_foreign_bs` is true: on 2026-11-03 in
|
||||||
(`STATE_NAME_MARKET_CLOSED`, cached 30 s per process); `/charts/{CODE}/daily`
|
session it was false and the figures were the previous session's);
|
||||||
|
`/company-price-feed/market-time/session` the market state of the `regular`
|
||||||
|
board (`STATE_NAME_SESSION_2` while trading, `STATE_NAME_BREAK`,
|
||||||
|
`STATE_NAME_MARKET_CLOSED`; cached 30 s per process; the `fca` board's own
|
||||||
|
state is not read); `/charts/{CODE}/daily`
|
||||||
daily candles for `1m`/`3m`/`1y` windows (volume in lots, returned in
|
daily candles for `1m`/`3m`/`1y` windows (volume in lots, returned in
|
||||||
shares). Requests share the public page's cross-process pacing.
|
shares). Requests share the public page's cross-process pacing.
|
||||||
- With a session, `--provider stockbit` quotes come from the order book (bid
|
- 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
|
and offer only in a `STATE_NAME_SESSION_<n>` state; session 2 was observed
|
||||||
in session), `stocks orderbook` works, and `auto` history under the stockbit
|
on 2026-11-03, session 1 is assumed to match), `stocks orderbook` works, and `auto` history under the stockbit
|
||||||
provider uses Stockbit's daily windows (`5d` to `1y`, trimmed from `1m`,
|
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
|
`3m` or `1y`), Yahoo otherwise. Without one, quotes use the public page and
|
||||||
the order book is `AUTHERROR`.
|
the order book is `AUTHERROR`.
|
||||||
|
|
|
||||||
|
|
@ -195,12 +195,15 @@ What the login adds:
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `--provider stockbit` quotes | public page, delayed | Stockbit's API, live |
|
| `--provider stockbit` quotes | public page, delayed | Stockbit's API, live |
|
||||||
| Bid and offer in quotes | best price, while open | best price, while open |
|
| Bid and offer in quotes | best price, while open | best price, while open |
|
||||||
| `stocks orderbook` | `AUTHERROR` | every level with lots and queued orders, OHLC, value, trades, foreign buy/sell/net |
|
| `stocks orderbook` | `AUTHERROR` | every level with lots and queued orders, OHLC, value, trades; foreign buy/sell/net once Stockbit marks them current |
|
||||||
| `stocks history` under `--provider stockbit` | Yahoo | Stockbit's daily candles for `5d` to `1y`; Yahoo for other windows |
|
| `stocks history` under `--provider stockbit` | Yahoo | Stockbit's daily candles for `5d` to `1y`; Yahoo for other windows |
|
||||||
| `stocks intraday` | public page | public page |
|
| `stocks intraday` | public page | public page |
|
||||||
|
|
||||||
The order book is what Stockbit's own app shows. Outside trading it is the
|
The order book is what Stockbit's own app shows. Outside trading it is the
|
||||||
book as the market left it, and the output says so. Sizes are lots in the
|
book as the market left it, and the output says so. Foreign buy, sell and net
|
||||||
|
appear only when Stockbit marks them current: during trading its figures are
|
||||||
|
the previous session's, so idx leaves them out (`stocks flow` has foreign
|
||||||
|
flow session by session). Sizes are lots in the
|
||||||
table and shares in JSON. `--history-provider stockbit` asks for Stockbit's
|
table and shares in JSON. `--history-provider stockbit` asks for Stockbit's
|
||||||
candles explicitly; windows it charts only weekly or hourly (`2y`, `5y`,
|
candles explicitly; windows it charts only weekly or hourly (`2y`, `5y`,
|
||||||
`1wk`) answer `UNSUPPORTED`.
|
`1wk`) answer `UNSUPPORTED`.
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ idx stocks orderbook BBRI --depth 5
|
||||||
|
|
||||||
With your own Stockbit session (`idx stockbit login`), `orderbook` shows
|
With your own Stockbit session (`idx stockbit login`), `orderbook` shows
|
||||||
every bid and offer level with lots and queued orders, the session's OHLC,
|
every bid and offer level with lots and queued orders, the session's OHLC,
|
||||||
value and trades, and foreign buy, sell and net. Without one it answers
|
value and trades, and foreign buy, sell and net once Stockbit marks them
|
||||||
|
current (not during trading, when they lag a day). Without one it answers
|
||||||
`AUTHERROR`. See [Stockbit](/cli/stockbit/#with-your-stockbit-login).
|
`AUTHERROR`. See [Stockbit](/cli/stockbit/#with-your-stockbit-login).
|
||||||
|
|
||||||
## Company data
|
## Company data
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ Every IDX holding (above 1%) of one investor, matched by name.
|
||||||
|
|
||||||
## `get_orderbook`
|
## `get_orderbook`
|
||||||
|
|
||||||
Order book of one IDX stock from the user's own Stockbit session: `bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, volume (shares), value and frequency, and foreign buy/sell/net in IDR. `market_status` is Stockbit's state, verbatim; outside STATE_NAME_MARKET_OPEN the book is as the market left it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL the user can open.
|
Order book of one IDX stock from the user's own Stockbit session: `bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, volume (shares), value and frequency, and foreign buy/sell/net in IDR (null while Stockbit marks them not current, as during trading; get_foreign_flow has them per session). `market_status` is Stockbit's state, verbatim: STATE_NAME_SESSION_1 or _2 is trading; in any other state the book is as the market left it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL the user can open.
|
||||||
|
|
||||||
| Input | Type | Required | Description |
|
| Input | Type | Required | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue