ALPHIODeveloper console

Docs

Full API reference for the Alphio open platform.

Chat integration guide →

Getting started

Quick start
  1. 1.Create an API key in API Key. Copy it once — we hash it before storage.
  2. 2.Onboard your end users: UpsertVirtualUser → CreateChatSession → /openchat-steam/completions
  3. 3.Wire your SSE reader to handle submitted → delta* → done events.
curl https://api.alphio.ai/v1/openapi/users \
  -H "Authorization: Bearer pk_live_••••••••••••••••••••••••••••" \
  -H "Content-Type: application/json" \
  -d '{"partner_user_ref": "u_998877"}'
Base URL & envelope
Base URL
https://api.alphio.ai
Response envelope
{ ret, msg, data }
HTTP status
Always 200 (for non-SSE). Inspect ret for the business result.
SSE
Chat completions stream as text/event-stream.

Authentication

Every request carries a Bearer API key. End-user-scoped endpoints additionally need an X-Partner-User header.

HeaderValueWhen
AuthorizationBearer pk_live_…Every request
Content-Typeapplication/jsonEvery request
X-Partner-Useryour_user_idOnly for end-user-scoped endpoints: /openchat-steam/completions + whales/following/ranking
Accepttext/event-streamSSE chat only

The value of X-Partner-User is your own user ID. The platform maps it to a stable internal_user_id like op_{partner_code}_{nanoid}. The user must first be registered via UpsertVirtualUser or chat returns ret=10404.

Errors & limits

Error codes
HTTP is always 200; check ret
retMeaningTypical cause
0OKSuccess
10400Bad requestMissing required field / type mismatch / empty message
10401UnauthenticatedMissing / wrong / expired / revoked Bearer
10403ForbiddenUser disabled / cross-user access
10404Not foundUser not registered (call UpsertVirtualUser) / bad session_id
10429Rate limitedRPM quota exceeded
10500Server errorContact support with occurred_at / chat_id
10501Insufficient balanceTop up to resume chat
Rate limits & billing

Default ceilings: 600 partner RPM · 60 per-user RPM · 8 concurrent chat streams. Negotiated on a per-plan basis.

Billing: one chat round (submit → done) deducts one billable unit. Interrupted / disconnected sessions still bill (anti-spam). failed events do not bill. Analysis, Whales, and Data endpoints are free of charge.

Virtual users

Manage end-user identities under your partner account. Virtual users are created lazily; the same partner_user_ref always maps to the same internal_user_id.

POST/v1/openapi/users
UpsertVirtualUser

Register or fetch a virtual user. Idempotent — calling repeatedly with the same partner_user_ref is safe.

Request parameters
FieldTypeRequiredDescription
partner_user_refstringyesYour own user ID for this end user.
display_namestringnoOptional, for audit/debugging.
metadatastringnoOpaque JSON string, not parsed by the platform.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataVirtualUserResponse payload.
└internal_user_idstringPlatform-assigned ID. Format: "op_{partner_code}_{nanoid12}".
└partner_user_refstringEchoed back.
└display_namestringEchoed back.
└metadatastringEchoed back (JSON string).
└statusstring"active" / "disabled".
└is_newbooltrue only on first creation.
└created_atstringISO8601 UTC.
└last_active_atstringISO8601 UTC. Empty if never used.
Example request
curl -X POST https://api.alphio.ai/v1/openapi/users \
  -H "Authorization: Bearer pk_live_••••••••••••••••••••••••••••" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_user_ref": "u_998877",
    "display_name": "Alice"
  }'
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "internal_user_id": "op_intellectia_qUk8CyDetjsK",
    "partner_user_ref": "u_998877",
    "display_name": "Alice",
    "metadata": "",
    "status": "active",
    "is_new": true,
    "created_at": "2026-06-09T08:02:14Z",
    "last_active_at": ""
  }
}
POST/v1/openapi/users/get
GetVirtualUser

Look up a virtual user by partner_user_ref. Response shape matches UpsertVirtualUser except is_new is always false.

Request parameters
FieldTypeRequiredDescription
partner_user_refstringyes—
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataVirtualUserResponse payload.
└internal_user_idstringPlatform-assigned ID. Format: "op_{partner_code}_{nanoid12}".
└partner_user_refstringEchoed back.
└display_namestringEchoed back.
└metadatastringEchoed back (JSON string).
└statusstring"active" / "disabled".
└is_newbooltrue only on first creation.
└created_atstringISO8601 UTC.
└last_active_atstringISO8601 UTC. Empty if never used.
POST/v1/openapi/users/disable
DisableVirtualUser

Disable a virtual user. Their chat requests will return ret=10403 until re-enabled via UpsertVirtualUser.

Request parameters
FieldTypeRequiredDescription
partner_user_refstringyes—
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDisableVirtualUserDataResponse payload.
└statusstringAlways "disabled".
POST/v1/openapi/users/list
ListVirtualUsers

Paginated list of your virtual users.

Request parameters
FieldTypeRequiredDescription
filterVirtualUserListFilternoOptional filters.
└statusstringno"active" / "disabled" / "" (all).
└created_afterstringnoISO8601 UTC.
└created_beforestringnoISO8601 UTC.
pageint32no1-indexed. Default 1.
sizeint32noDefault 20, max 200.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataListVirtualUsersDataResponse payload.
└itemsVirtualUser[]—
└internal_user_idstringPlatform-assigned ID. Format: "op_{partner_code}_{nanoid12}".
└partner_user_refstringEchoed back.
└display_namestringEchoed back.
└metadatastringEchoed back (JSON string).
└statusstring"active" / "disabled".
└is_newbooltrue only on first creation.
└created_atstringISO8601 UTC.
└last_active_atstringISO8601 UTC. Empty if never used.
└paginationPagination—
└pageint321-indexed.
└sizeint32Items per page.
└totalint64Total rows matching the filter.
└has_moreboolWhether more pages exist.

Chat (SSE streaming)

Multi-turn AI chat with built-in skills (technical analysis, market data, news). Response is text/event-stream — every event is emitted as `event: NAME\ndata: JSON\n\n`. Unlike all other endpoints, the SSE stream is NOT wrapped in `{ret, msg, data}`.

POST/openchat-steam/completionsX-Partner-User required
ChatCompletions

Start a chat turn. Returns an SSE stream until done or failed.

Each delta.content is incremental — append to a client buffer. The final done event carries the full content as a sanity check.
Failed events do not bill. Submitted → done is one billable round.
Request parameters
FieldTypeRequiredDescription
chat_idstringyesYour generated unique ID for this turn, e.g. chat-{uuid}. Used for idempotency and tracing.
messagestringyesThe user's question.
metadataChatMetadatayes—
└session_idstringyesFrom CreateChatSession. Wrong/missing → ret=10404.
└timezonestringnoIANA timezone, e.g. "Asia/Shanghai". Affects "today/yesterday" interpretation.
└languagestringno"zh" / "en" / etc. Default: adaptive to user language.
Response fields
FieldTypeDescription
event: submittedSSEAcknowledgement that the request was accepted.
└chat_idstringEchoed back.
└statusstring"RUNNING".
└user_idstringinternal_user_id resolved from X-Partner-User.
└session_idstringEchoed back.
└occurred_atstringISO8601 UTC.
event: statusSSEIntermediate progress.
└stagestringagent_round_start / tool_call_start / model_call_start / etc.
└messagestringHuman-readable status.
└progressfloat0.0 – 1.0.
event: deltaSSEStreaming answer chunk.
└contentstringIncremental fragment — append to a client buffer.
└statusstring"RUNNING".
event: doneSSEStream complete.
└contentstringFull final answer (sanity check against the joined deltas).
└statusstring"SUCCESS".
└progressfloat1.0.
└token_usageobjectToken counters for billing visibility.
event: failedSSETerminal failure (insufficient balance, backend error, etc).
└messagestringError description.
└statusstring"FAILED".
Example request
curl -N -X POST https://api.alphio.ai/openchat-steam/completions \
  -H "Authorization: Bearer pk_live_••••••••••••••••••••••••••••" \
  -H "X-Partner-User: u_998877" \
  -H "Accept: text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "chat-998877-1719283400",
    "message": "What is AAPL'\''s current RSI?",
    "metadata": {
      "session_id": "sess_ifm92XaV7gqx",
      "timezone": "America/New_York",
      "language": "en"
    }
  }'
Example response
event: submitted
data: {"chat_id":"chat-998877-1719283400","status":"RUNNING",
       "user_id":"op_intellectia_qUk8CyDetjsK","session_id":"sess_ifm92XaV7gqx",
       "occurred_at":"2026-06-09T08:18:24.77Z"}

event: status
data: {"chat_id":"...","stage":"agent_round_start",
       "status":"PROCESSING","progress":0.15,"message":"Understanding question"}

event: status
data: {"chat_id":"...","stage":"tool_call_start",
       "status":"PROCESSING","progress":0.50,"message":"Fetching technical indicators"}

event: delta
data: {"chat_id":"...","status":"RUNNING","content":"AAPL's current RSI(14) is 68,"}

event: delta
data: {"chat_id":"...","status":"RUNNING","content":" approaching the overbought zone (70)..."}

event: done
data: {"chat_id":"...","status":"SUCCESS","progress":1.0,
       "stage":"task_completed","content":"AAPL's current RSI(14) is 68, approaching..."}

Chat sessions & messages

A session groups related chat turns. History within a session is automatically injected as context.

POST/v1/openapi/chat/sessions/create
CreateChatSession
Request parameters
FieldTypeRequiredDescription
internal_user_idstringyesFrom UpsertVirtualUser.
titlestringnoDisplay title for your UI.
metadatastringnoOpaque JSON string.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataChatSessionResponse payload.
└session_idstringFormat: "sess_{nanoid}".
└internal_user_idstringOwner.
└titlestring—
└metadatastringJSON string.
└statusstring"active" / "deleted".
└last_message_atstringISO8601 UTC. Empty for a freshly created session.
└created_atstringISO8601 UTC.
└updated_atstringISO8601 UTC.
POST/v1/openapi/chat/sessions/list
ListChatSessions
Request parameters
FieldTypeRequiredDescription
internal_user_idstringnoEmpty = list across all your virtual users.
statusstringno"active" / "deleted" / "" (all). Default "active".
pageint32noDefault 1.
sizeint32noDefault 20, max 200.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataListChatSessionsDataResponse payload.
└itemsChatSession[]—
└session_idstringFormat: "sess_{nanoid}".
└internal_user_idstringOwner.
└titlestring—
└metadatastringJSON string.
└statusstring"active" / "deleted".
└last_message_atstringISO8601 UTC. Empty for a freshly created session.
└created_atstringISO8601 UTC.
└updated_atstringISO8601 UTC.
└pageint321-indexed.
└sizeint32Items per page.
└totalint64Total rows matching the filter.
└has_moreboolWhether more pages exist.
POST/v1/openapi/chat/sessions/get
GetChatSession
Request parameters
FieldTypeRequiredDescription
session_idstringyes—
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataChatSessionResponse payload.
└session_idstringFormat: "sess_{nanoid}".
└internal_user_idstringOwner.
└titlestring—
└metadatastringJSON string.
└statusstring"active" / "deleted".
└last_message_atstringISO8601 UTC. Empty for a freshly created session.
└created_atstringISO8601 UTC.
└updated_atstringISO8601 UTC.
POST/v1/openapi/chat/sessions/update
UpdateChatSession

Edit the title or metadata of an existing session.

Request parameters
FieldTypeRequiredDescription
session_idstringyes—
titlestringnoEmpty = unchanged.
metadatastringnoEmpty = unchanged.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataChatSessionResponse payload.
└session_idstringFormat: "sess_{nanoid}".
└internal_user_idstringOwner.
└titlestring—
└metadatastringJSON string.
└statusstring"active" / "deleted".
└last_message_atstringISO8601 UTC. Empty for a freshly created session.
└created_atstringISO8601 UTC.
└updated_atstringISO8601 UTC.
POST/v1/openapi/chat/sessions/delete
DeleteChatSession

Soft-delete by default. Message history remains queryable.

Request parameters
FieldTypeRequiredDescription
session_idstringyes—
soft_deleteboolnoDefault true. Set false for permanent deletion.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDeleteChatSessionDataResponse payload.
└statusstring"deleted" / "hard_deleted".
POST/v1/openapi/chat/sessions/messages
ListSessionMessages

Cursor-paginated history. Order is by created_at DESC.

Request parameters
FieldTypeRequiredDescription
session_idstringyes—
before_message_idstringnoCursor — pass the message_id of your current oldest message to fetch older ones.
sizeint32noDefault 50, max 200.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataListSessionMessagesDataResponse payload.
└itemsChatMessage[]—
└message_idstringFormat: "msg_{nanoid}".
└chat_idstringMatches the SSE chat_id. One chat_id usually produces 2 messages (user + assistant).
└session_idstringMay be empty for session-less chat.
└internal_user_idstring—
└rolestring"user" / "assistant".
└contentstringRaw Markdown stream. May contain ```json card fences.
└attachmentsChatAttachment[]—
└urlstringPre-signed URL. May expire.
└mimestring—
└namestring—
└tool_callsChatToolCall[]—
└step_idstring—
└kindstring"round" / "tool_call".
└roundint32—
└tool_namestring—
└args_jsonstringStringified tool arguments.
└result_summarystring—
└statusstring"ok" / "error" / "running".
└started_at_msint64Unix milliseconds.
└ended_at_msint64Unix milliseconds.
└sourcesChatSource[]—
└titlestring—
└urlstring—
└snippetstring—
└error_codestringNon-empty on terminal failure.
└error_messagestring—
└doc_idstringChatbot's assistant doc_id.
└created_atstringISO8601 UTC.
└has_morebool—
POST/v1/openapi/chat/messages/get
GetChatMessage
Request parameters
FieldTypeRequiredDescription
message_idstringyes—
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataChatMessageResponse payload.
└message_idstringFormat: "msg_{nanoid}".
└chat_idstringMatches the SSE chat_id. One chat_id usually produces 2 messages (user + assistant).
└session_idstringMay be empty for session-less chat.
└internal_user_idstring—
└rolestring"user" / "assistant".
└contentstringRaw Markdown stream. May contain ```json card fences.
└attachmentsChatAttachment[]—
└urlstringPre-signed URL. May expire.
└mimestring—
└namestring—
└tool_callsChatToolCall[]—
└step_idstring—
└kindstring"round" / "tool_call".
└roundint32—
└tool_namestring—
└args_jsonstringStringified tool arguments.
└result_summarystring—
└statusstring"ok" / "error" / "running".
└started_at_msint64Unix milliseconds.
└ended_at_msint64Unix milliseconds.
└sourcesChatSource[]—
└titlestring—
└urlstring—
└snippetstring—
└error_codestringNon-empty on terminal failure.
└error_messagestring—
└doc_idstringChatbot's assistant doc_id.
└created_atstringISO8601 UTC.
POST/v1/openapi/chat/messages/rate
RateChatMessage

Up/down vote an assistant reply. Only valid for role=assistant messages.

Request parameters
FieldTypeRequiredDescription
message_idstringyes—
ratingstringyes"up" or "down".
notestringnoOptional user note.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataRateChatMessageDataResponse payload.
└message_idstringEchoed back.
└ratingstringEchoed back.
└created_atstringISO8601 UTC.
POST/v1/openapi/chat/recommend-questions
ChatRecommendQuestions

Suggested starter questions for your chat entry screen, regenerated daily from a digest of recent market news. Served from cache — this endpoint never invokes an LLM and is not billed. Each call returns a fresh random subset, so repeated calls give your UI variety.

Not billed and no X-Partner-User required.
An empty questions array with ret=0 means the daily batch is not available yet (or expired) — hide the suggestions UI rather than treating it as an error.
Request parameters
FieldTypeRequiredDescription
langstringnoLanguage tag: "en" / "en-us", "zh" / "zh-cn" / "zh-hans", "zh-tw" / "zh-hant" / "zh-hk". Case-insensitive. Empty or unrecognized falls back to "en".
countint32noMax questions to return. Default 10, max 50.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataChatRecommendQuestionsDataResponse payload.
└questionsstring[]Randomly ordered subset of the day's batch.
└langstringLanguage actually served after normalization/fallback: "en" / "zh-cn" / "zh-tw".
└generated_atint64Unix seconds when the batch was generated.
Example request
curl -X POST https://api.alphio.ai/v1/openapi/chat/recommend-questions \
  -H "Authorization: Bearer pk_live_••••••••••••••••••••••••••••" \
  -H "Content-Type: application/json" \
  -d '{
    "lang": "en",
    "count": 3
  }'
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "questions": [
      "How could the latest Fed commentary affect rate-cut expectations this summer?",
      "Is NVDA's pullback after earnings a buying opportunity?",
      "What is driving the rally in gold prices this week?"
    ],
    "lang": "en",
    "generated_at": 1781136000
  }
}

Pivot point

Pivot point / support-resistance signals.

POST/v1/openapi/analysis/pivot-point/info
PivotPointInfo

Returns every 神奇九转 + RSI(6) pivot signal (DIP / ALERT) the algorithm fired within the look-back window. The K-line period is selectable with multiplier / timespan (1–720 minute, hour, day, week, month, quarter, year); both omitted = daily bars over the trailing 1 year, the behaviour that predates the fields. Computed server-side from datafabric kline (US / HK / crypto all supported); cached 5 minutes, 1 minute for intraday periods. Empty `data.list` with `ret=0` means no signal in the window — distinct from a real error. A window longer than the period's max span comes back one page at a time — pass `data.next_cursor` back as `cursor` to walk further back.

Request parameters
FieldTypeRequiredDescription
codestringyesTicker, e.g. "AAPL", "00700", "BTC". Case-insensitive; uppercased server-side.
asset_typeint32yes0=stock, 1=etf, 2=crypto.
localestringnoMarket the ticker belongs to: "US", "HK", "FOREX", "METAL". Case-insensitive; uppercased server-side. Empty defaults to "US". Crypto (asset_type=2) keeps the field for shape consistency but it's inert.
multiplierint32noPeriod count, paired with timespan. Allowed values depend on timespan: minute 1–720, hour 1–12, month 1 / 3 (= quarter) / 12 (= year); day, week, quarter and year accept 1 only. Any other combination is a parameter error rather than a silently mis-aggregated series. Default 1.
timespanint32noK-line period unit: 2=minute, 3=hour, 4=day, 5=week, 6=month, 7=quarter, 8=year (1=second is not supported). Omitted together with multiplier = daily. Bar stamping — day and above carry the period's FIRST TRADING DAY (NVDA's August 2026 monthly bar is 2026-08-03, its Q1 and 2026 yearly bars are both 2026-01-02); minute and hour bars are right-edge stamped and anchored to each trading session's open, truncated at the close (HK 60m → 10:30 / 11:30 / 12:00 / 14:00 / 15:00 / 16:00, HK 90m → 11:00 / 12:00 / 14:30 / 16:00, and a 3m series stamps the 09:30 bar 09:33). This anchoring is done by THIS endpoint, which folds hourly widths from 5-minute bars for the purpose; /v1/openapi/data/kline passes timespan straight through to the vendor and returns its CLOCK-aligned hourly stamps (10:00, 11:00) instead — so a 60m bar read from the two endpoints carries different timestamps by design. Crypto and perp serve only the exchange's native widths: 1 / 3 / 5 / 15 / 30 / 60 / 120 / 240 / 360 / 480 / 720 minutes, plus day / week / month. Note the upstream vendor stamps week and above on the CALENDAR period start (August 2026 arrives as 08-01); this endpoint moves them onto the first day the market actually traded, so those stamps can differ from the same bar read through /v1/openapi/data/kline.
fromstringnoWindow start, YYYY-MM-DD (UTC, inclusive). Must be set together with to. The algorithm runs on exactly the [from, to] bars with no warmup preload — the signal math is window-sensitive (RSI warmup, TD-9 counts, FILTER suppression), so to reproduce the App chart's markers pass exactly the bar range the chart has loaded. Both empty = a default look-back sized for the period: 1 year for daily bars, days for a 1-minute series, decades for yearly.
tostringnoWindow end, YYYY-MM-DD (UTC, inclusive). Must be set together with from. Max span 5 years for minute / hour / day / week, 50 years for month / quarter / year. A LONGER window is not rejected — it is clamped to the ceiling, keeping `to` and moving `from` forward, and `msg` says which window actually ran (e.g. "ok (requested from 2020-12-07 exceeds the max span for this period; window clamped to 2021-08-27..2026-08-31)"). Windows shorter than 30 bars return "insufficient kline history" — which also means yearly signals only exist for tickers with 30+ years of history. Ignored when `cursor` is set — the cursor decides where the page ends.
cursorint64noBackward paging cursor, unix seconds. One request only computes the bars inside the max span for the period, so a longer history comes back one page at a time: take `data.next_cursor` from the response and pass it here to get the page immediately BEFORE it — the bars with ts strictly less than cursor, one max-span window wide, never earlier than `from`. `to` is ignored when cursor is set; `from` still bounds how far back paging can go. Pages are computed independently yet agree bar-for-bar with one big window: the server preloads ~100 extra bars before each page start to warm RSI / TD-9 / COUNT up and returns only the signals inside the page, so concatenating pages neither drops nor duplicates a signal at a boundary. Omit or 0 = first page.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisPivotPointInfoDataResponse payload.
└listAnalysisPivotAlertInfo[]Filtered signals in CHRONOLOGICAL ASCENDING order. Empty when no signal fired in the look-back window. For the latest signal, take list[list.length-1].
└datestringBar label in the market's own timezone (UTC for crypto / perp): "YYYY-MM-DD HH:MM" for minute and hour periods, "YYYY-MM-DD" for day and above.
└codestringEchoed ticker, uppercased.
└signstring"DIP" (bullish reversal) or "ALERT" (bearish reversal).
└markstring"Buy Signal" / "Sell Signal" — human-readable label.
└pricestringDisplay price with 4-decimal precision. DIP = low × 0.98 (paint below the bar); ALERT = high × 1.03 (paint above).
└tsint64Bar timestamp, unix seconds — the same instant date renders.
└next_cursorint64Pass it back as `cursor` to get the page before this one. Unix seconds — the ts of the OLDEST bar on this page. 0 means there is no earlier page: paging has reached `from`, fewer than 30 bars remain before it, or this first page already covered the whole requested window.
Example request
# Daily (period omitted — the pre-existing behaviour)
POST https://api.alphio.ai/v1/openapi/analysis/pivot-point/info
{ "code": "AAPL", "asset_type": 0, "locale": "US", "from": "2026-01-01", "to": "2026-07-28" }

# 60-minute bars
POST https://api.alphio.ai/v1/openapi/analysis/pivot-point/info
{ "code": "AAPL", "asset_type": 0, "locale": "US", "multiplier": 60, "timespan": 2 }

# Older signals — feed the previous response's next_cursor back as cursor
POST https://api.alphio.ai/v1/openapi/analysis/pivot-point/info
{ "code": "AAPL", "asset_type": 0, "locale": "US", "multiplier": 1, "timespan": 2, "cursor": 1789394400 }
Example response
// Daily
{
  "ret": 0, "msg": "ok",
  "data": {
    "list": [
      {
        "date": "2026-02-09", "code": "AAPL",
        "sign": "ALERT", "mark": "Sell Signal",
        "price": "286.5460", "ts": 1770613200
      },
      {
        "date": "2026-06-09", "code": "AAPL",
        "sign": "DIP", "mark": "Buy Signal",
        "price": "291.7340", "ts": 1781007137
      }
    ]
  }
}

// 60-minute — note date carries the time of day
{
  "ret": 0, "msg": "ok",
  "data": {
    "list": [
      {
        "date": "2026-08-24 11:30", "code": "AAPL",
        "sign": "ALERT", "mark": "Sell Signal",
        "price": "286.5460", "ts": 1787585400
      },
      {
        "date": "2026-08-27 14:30", "code": "AAPL",
        "sign": "DIP", "mark": "Buy Signal",
        "price": "291.7340", "ts": 1787855400
      }
    ]
  }
}

// 1-minute with cursor — next_cursor is the oldest bar on this page; 0 = no more pages
{
  "ret": 0, "msg": "ok",
  "data": {
    "list": [
      {
        "date": "2026-09-11 10:31", "code": "AAPL",
        "sign": "DIP", "mark": "Buy Signal",
        "price": "289.4100", "ts": 1789137060
      }
    ],
    "next_cursor": 1789051500
  }
}
POST/v1/openapi/analysis/pivot-point/signals
PivotPointSignals

Bulk scan / signal screener.

Request parameters
FieldTypeRequiredDescription
signal_typeint32yes0=all, 1=bottom (dip / bottom-reversal), 2=alert (top warning).
pageint32no1-indexed.
page_sizeint32noItems per page.
asset_typestringnoNumeric string: "0"=stock, "1"=etf, "2"=crypto. Empty = all.
periodint32no0=all, 1=15min, 2=30min, 3=1h, 4=2h, 5=4h. This is the screener's own enum — NOT the timespan / multiplier pair PivotPointInfo takes, and it has no daily or above option.
sort_fieldint32no0=timestamp, 2=volume (1=market_cap is deprecated).
orderstringno"asc" (default) / "desc".
first_idstringnoForward cursor.
last_idstringnoBackward cursor.
refreshboolnoForce-bust cache.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisPivotPointSignalsDataResponse payload.
└signalsAnalysisDayTradingSignal[]—
└symbolstring—
└codestring—
└namestringCompany name.
└logostring—
└pricestring—
└change_ratiostring—
└timestampint64Unix seconds.
└signal_typesint32[]Active signal labels.
└periodint32—
└klinesAnalysisKlinePoint[]OHLCV + technicals.
└opendouble—
└highdouble—
└lowdouble—
└closedouble—
└volumedouble—
└timestampint64Unix seconds.
└ma5double—
└ma20double—
└rsi6double—
└macddouble—
└macd_signaldouble—
└macd_histdouble—
└asset_typeint32—
└volumestring—
└idstringStable row ID for cursor pagination.
└prev_closestring—
└totalint64Total rows matching the filter.
└new_addint64Rows newly added since last query.

Swingmax (US)

Swing-trade signals for US equities.

POST/v1/openapi/analysis/swingmax/signals
SwingmaxSignals

Query open swingmax signals for the given tickers. Returns the SAME rich item shape as /swingmax/stocks (AnalysisSwingmaxStock — with kline, company name/logo, the r1/r2/r3 target ladder, stop loss). Ladder field names match the App swing-max/list payload one-to-one.

Lookup by id since 2026-09-15: pass ids (max 20) instead of tickers to fetch specific signals, including ones that have closed since — the tickers path stays open-only. Every item now carries id; it is assigned at entry and never changes, so store it from any response and pass it back later.
The status field is localized. A "lang" BODY PARAM takes priority when set; otherwise the "lang" REQUEST HEADER is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
Request parameters
FieldTypeRequiredDescription
tickersAnalysisAsset[]noTickers to query. Send either tickers or ids — one of them is required; both together is InvalidArgument.
└asset_typeint32no0=stock, 1=etf, 2=crypto.
└symbolstringnoLegacy field.
└tickerstringnoPreferred field.
└localestringno"US" / "HK".
idsint64[]noSignal ids to look up — the id field of a returned item. At most 20; more is InvalidArgument, NOT truncated, so a silently missing 21st id cannot happen. Mutually exclusive with tickers. Unlike tickers, which only sees the open pool, ids also returns positions that have closed since (exit_time_ts > 0), so an id taken from an earlier response can be followed until the trade ends. Unknown ids are skipped, not errored. Added 2026-09-15.
langstringnoLocalizes the status text. When set it takes PRIORITY over the "lang" request header: "zh" / "zh-cn" = Simplified Chinese, "zh-tw" = Traditional Chinese. Omit/empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxSignalsDataResponse payload.
└signalsAnalysisSwingmaxStock[]Same item shape as /swingmax/stocks — one entry per matched open ticker.
└idint64Signal id. Assigned when the position opens and unchanged for its whole life, including after it closes. Store it and pass it back as ids on /swingmax/signals to follow one trade. Added 2026-09-15. Filled on /swingmax/signals and on /swingmax/stocks for every stype: since 2026-09-16 the closed list (stype≠1) reads the same v2 store as the open pool, so an id taken from any /swingmax/stocks row round-trips through ids on /swingmax/signals. Before that date closed rows carried 0.
└symbolstring—
└codestring—
└company_namestring—
└logostring—
└returndoubleRealized return since entry, percent. Weighted over the ladder: 40% sold at Target 2 + 30% sold at Target 3 + the remaining 30% at final exit (unsold parts marked to the latest close).
└entry_pricedouble—
└exit_pricedoubleFinal-exit fill price for the remaining position. 0 while the position is open.
└entry_timestringEntry date string from upstream, e.g. "2026-06-02".
└exit_timestringExit date string; empty while the position is open.
└change_ratiodoubleNot populated since 2026-09-16 (the closed list moved to the v2 store, which has no such column); always 0. Use return instead.
└klineAnalysisKlinePointV2[]Snapshot bars with full OHLC + volume — enough to draw candlesticks.
└closedouble—
└timestampint64Unix seconds.
└opendouble—
└highdouble—
└lowdouble—
└volumedouble—
└entry_time_tsint64Unix seconds.
└exit_time_tsint64Unix seconds; 0 while open.
└target_pricestringLegacy alias of r2_price (Target 2), kept for backward compatibility — prefer the r1/r2/r3_price ladder.
└stop_lossstringStop-loss price. The whole remaining position exits if it is hit.
└intervalint32Signal timeframe: 0=1d, 1=15min, 2=30min, 3=1h, 4=2h, 5=4h.
└sideint320=long, 1=short.
└market_capstring—
└current_pricestring—
└r1_pricestringTarget 1 price — the first (lowest) pivot level above entry. A price milestone only: no partial sell is executed at R1.
└r1_signal_tsint64Reserved for shape parity with the App swing-max/list; always 0 on this stock feed (only the crypto pipeline fills R1 triggers).
└r2_pricestringTarget 2 price. When first reached, the strategy sells 40% of the position. Same value as the legacy target_price.
└r2_signal_tsint64Unix seconds when the price first reached Target 2 (40% partial exit executed); 0 if not reached yet.
└r2_sell_pricestringFill price of the 40% partial exit at Target 2; empty if not reached yet.
└r3_pricestringTarget 3 price — the highest ladder level. When first reached, the strategy sells another 30% of the position.
└r3_signal_tsint64Unix seconds when the price first reached Target 3 (30% partial exit executed); 0 if not reached yet.
└r3_sell_pricestringFill price of the 30% partial exit at Target 3; empty if not reached yet.
└statusstringServer-rendered progress narrative (entry → 40% partial exit at Target 2 → 30% at Target 3 → final close) — same text the App shows on swing-max/list. Localized by the "lang" body param (priority) or "lang" request header: omit both for English, "zh" / "zh-cn" = Simplified Chinese, "zh-tw" = Traditional Chinese. Empty on the closed list (stype≠1).
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax/signals
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{
  "tickers":[
    {"asset_type":0,"ticker":"AAPL","locale":"US"},
    {"asset_type":0,"ticker":"NVDA","locale":"US"}
  ],
  "lang":"zh-tw"
}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "signals":[
      {"symbol":"AAPL","code":"AAPL.O","company_name":"Apple Inc","logo":"https://...",
       "return":3.21,"entry_price":187.2,"exit_price":0,"entry_time":"2026-06-02","exit_time":"",
       "change_ratio":3.21,
       "kline":[{"close":190.5,"timestamp":1717286400,"open":188.9,"high":191.2,"low":188.1,"volume":51234567}],
       "entry_time_ts":1717286400,"exit_time_ts":0,"target_price":"210.5","stop_loss":"177.84",
       "interval":0,"side":0,"market_cap":"2.9T","current_price":"193.1",
       "r1_price":"198.6","r1_signal_ts":0,
       "r2_price":"210.5","r2_signal_ts":1719878400,"r2_sell_price":"211.02",
       "r3_price":"224.9","r3_signal_ts":0,"r3_sell_price":"",
       "status":"Booked partial profits @211.02 on 2026-07-01 09:35 ET and riding runners"}
    ]
  }
}
POST/v1/openapi/analysis/swingmax/stocks
SwingmaxStocks

Browse the supported universe. NOTE: only stype=1 returns the OPEN positions pool; any other value returns the closed list. Since 2026-09-16 both come from the same v2 store: closed rows carry id, the r1/r2/r3 ladder fields and status like open rows, and return is the realized ladder return. Two fields differ from the pre-2026-09-16 closed list: change_ratio is no longer populated (0), and side=1 (short) returns an empty list because the v2 store is long-only. Before 2026-09-16 the closed list came from an older store that had stopped updating in 2025-08 and carried no id.

data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
The status field is localized. A "lang" BODY PARAM takes priority when set; otherwise the "lang" REQUEST HEADER is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
Request parameters
FieldTypeRequiredDescription
stypeint32yesGetSwingMaxStocksType. 1=open pool, 2=removed(closed), 3=today, 4=today-open, 5=today-update. Only stype=1 = open pool; 2-5 all return the same closed list (v2 store, rows carry id).
sort_typeint32no1-4 match the platform-wide meaning shared with every other list endpoint: 1=latest, 2=oldest, 3=highest return, 4=lowest return. 5-11 are extra orderings only this endpoint supports: 5=latest exit, 6=oldest exit, 7=latest entry, 8=oldest entry, 9=highest market cap, 10=lowest market cap, 11=highest potential.
pageint64no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, max 20 — larger values are clamped, not rejected. The same ceiling as every other paged list on this API; only the two swingmax /open endpoints are uncapped. Before 2026-09-15 the default was 100 with no clamp, so omitting size failed with a parameter error from upstream.
keywordstringnoCase-insensitive substring match on ticker, code and company name. Omit to disable filtering. Max 64 characters.
sideint32no0=long, 1=short.
trade_statusint32no0=not traded, 1=traded, 2=all.
langstringnoLocalizes the status text. When set it takes PRIORITY over the "lang" request header: "zh" / "zh-cn" = Simplified Chinese, "zh-tw" = Traditional Chinese. Omit/empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxStocksDataResponse payload.
└stocksAnalysisSwingmaxStock[]—
└idint64Signal id. Assigned when the position opens and unchanged for its whole life, including after it closes. Store it and pass it back as ids on /swingmax/signals to follow one trade. Added 2026-09-15. Filled on /swingmax/signals and on /swingmax/stocks for every stype: since 2026-09-16 the closed list (stype≠1) reads the same v2 store as the open pool, so an id taken from any /swingmax/stocks row round-trips through ids on /swingmax/signals. Before that date closed rows carried 0.
└symbolstring—
└codestring—
└company_namestring—
└logostring—
└returndoubleRealized return since entry, percent. Weighted over the ladder: 40% sold at Target 2 + 30% sold at Target 3 + the remaining 30% at final exit (unsold parts marked to the latest close).
└entry_pricedouble—
└exit_pricedoubleFinal-exit fill price for the remaining position. 0 while the position is open.
└entry_timestringEntry date string from upstream, e.g. "2026-06-02".
└exit_timestringExit date string; empty while the position is open.
└change_ratiodoubleNot populated since 2026-09-16 (the closed list moved to the v2 store, which has no such column); always 0. Use return instead.
└klineAnalysisKlinePointV2[]Snapshot bars with full OHLC + volume — enough to draw candlesticks.
└closedouble—
└timestampint64Unix seconds.
└opendouble—
└highdouble—
└lowdouble—
└volumedouble—
└entry_time_tsint64Unix seconds.
└exit_time_tsint64Unix seconds; 0 while open.
└target_pricestringLegacy alias of r2_price (Target 2), kept for backward compatibility — prefer the r1/r2/r3_price ladder.
└stop_lossstringStop-loss price. The whole remaining position exits if it is hit.
└intervalint32Signal timeframe: 0=1d, 1=15min, 2=30min, 3=1h, 4=2h, 5=4h.
└sideint320=long, 1=short.
└market_capstring—
└current_pricestring—
└r1_pricestringTarget 1 price — the first (lowest) pivot level above entry. A price milestone only: no partial sell is executed at R1.
└r1_signal_tsint64Reserved for shape parity with the App swing-max/list; always 0 on this stock feed (only the crypto pipeline fills R1 triggers).
└r2_pricestringTarget 2 price. When first reached, the strategy sells 40% of the position. Same value as the legacy target_price.
└r2_signal_tsint64Unix seconds when the price first reached Target 2 (40% partial exit executed); 0 if not reached yet.
└r2_sell_pricestringFill price of the 40% partial exit at Target 2; empty if not reached yet.
└r3_pricestringTarget 3 price — the highest ladder level. When first reached, the strategy sells another 30% of the position.
└r3_signal_tsint64Unix seconds when the price first reached Target 3 (30% partial exit executed); 0 if not reached yet.
└r3_sell_pricestringFill price of the 30% partial exit at Target 3; empty if not reached yet.
└statusstringServer-rendered progress narrative (entry → 40% partial exit at Target 2 → 30% at Target 3 → final close) — same text the App shows on swing-max/list. Localized by the "lang" body param (priority) or "lang" request header: omit both for English, "zh" / "zh-cn" = Simplified Chinese, "zh-tw" = Traditional Chinese. Empty on the closed list (stype≠1).
└totalint32—
POST/v1/openapi/analysis/swingmax/open
SwingmaxOpen

Returns every currently-open position in a single call by default. Static signals only — no kline, no realtime return, no company name/logo. Cheap, cached ~1 minute.

data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Omit to return every open position in one call — that is the historical behaviour of this endpoint and it is unchanged. Set it only if you want pages.
sort_typeint32no0 = endpoint default, 1 = latest first, 2 = oldest first, 3/4 = by potential_gain (target-based upside, NOT realised return), high/low first. The same number means the same thing on the US and HK endpoints.
keywordstringnoCase-insensitive substring match on ticker and code — this endpoint carries no company name. Omit to disable filtering. Max 64 characters.
langstringnoLocalizes server-rendered status text. Takes priority over the lang header.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxOpenDataResponse payload.
└positionsAnalysisSwingmaxOpenPosition[]Every currently-open position.
└idint64Signal id — the same number as id on /swingmax/signals and /swingmax/stocks. Take it from this list and pass it back as ids on /swingmax/signals to follow one position until it closes. Added 2026-09-15.
└symbolstring—
└codestring—
└buy_pricestringEntry / Buy Target.
└stop_lossstring—
└target1stringSell Target 1 (r2).
└target2stringSell Target 2 (r3).
└potential_gaindoubleStatic: (r3/buy - 1) * 100, percent.
└signal_timeint64Entry time, unix seconds.
└last_exit_tsint64Exit time, unix seconds; 0 when open.
└totalint32—
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax/open
{}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "positions":[
      {"symbol":"GME","code":"GME.N","buy_price":"21.715","stop_loss":"20.629",
       "target1":"24.252","target2":"28.152","potential_gain":29.6,
       "signal_time":1647388800,"last_exit_ts":0}
    ],
    "total": 1
  }
}

Swingmax (HK)

Swing-trade signals for Hong Kong equities. Same engine and same r2/r3 target ladder as the US endpoints, but a separate item shape: HK carries board_lot and trade_currency, which are meaningless for US equities. Amounts are in trade_currency, not always HKD.

POST/v1/openapi/analysis/swingmax/hk/signals
SwingmaxHKSignals

Current open swingmax positions for the given HK tickers. Mirrors /swingmax/signals on the US side, including the fact that it returns OPEN positions only. Until 2026-09-08 this endpoint also returned closed positions — the status filter was missing, so querying two tickers could return a decade of history alongside the live ones. If you were relying on that to read past trades, switch to /swingmax/hk/stocks with stype=2 (closed); that is the endpoint built for it.

Lookup by id since 2026-09-15: pass ids (max 20) instead of tickers to fetch specific signals, including ones that have closed since — the tickers path stays open-only. Every item now carries id; it is assigned at entry and never changes, so store it from any response and pass it back later.
The lang BODY PARAM did nothing on the three HK swingmax endpoints before 2026-09-15 — only the header was honored, so a request with lang in the body and no header came back in English. Fixed; both now behave as documented on all three. Sending the header still works.
tickers is an array of objects since 2026-09-09, matching US /swingmax/signals field for field. The old plain string array ["00700"] still works — the gateway normalizes it to [{"ticker":"00700"}] — but it is a transitional shim for integrations completed before the change; new code should send objects.
The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
Field names match US swingmax as of 2026-09-09: name is now company_name, buy_date/buy_price are entry_time/entry_price, sell_date/sell_price are exit_time/exit_price, and the r2_triggered/r3_triggered booleans are gone — use r2_signal_ts/r3_signal_ts > 0 instead. The booleans said that a target was hit but never when, so no marker could be placed on a chart. entry_time_ts and exit_time_ts are new. Also on 2026-09-09: profit_rate was renamed to return, and entry_price / exit_price / return became numbers instead of strings — all three now match US swingmax exactly. HK-specific fields (trade_currency, board_lot, realized, exit_reason, hold_days) are unchanged.
Open positions only — a ticker with no live position returns nothing here even if it traded many times before. Use /swingmax/hk/stocks (date range + stype) to browse history.
Bars arrive on this endpoint. They were empty from launch until 2026-09-07 — the field was in the response shape but nothing ever filled it, so callers got an empty array with no error and reasonably concluded HK had no bars. If you cached that conclusion, re-check.
Universe: ordinary shares and REITs only. Warrants, CBBCs, ETFs and LIPs are excluded — HK lists roughly 30k derivative warrants against 3k ordinary shares, so an unfiltered pool would be about 90% derivatives.
Additional filters: average daily turnover at least HKD 20M, close at least HKD 2, market cap at least HKD 3B. The price floor exists because the HK main board has no delisting rule equivalent to the US $1 threshold.
Amounts are in trade_currency, NOT always HKD. Check the field before aggregating across symbols.
Request parameters
FieldTypeRequiredDescription
tickersAnalysisAsset[]noHK codes to query, 5 digits each — an array of OBJECTS, the same shape as US /swingmax/signals: [{"ticker":"00700"}]. asset_type and locale are accepted but inert here: the path already fixes the market. Send either tickers or ids — one of them is required; both together is InvalidArgument.
└asset_typeint32no0=stock, 1=etf, 2=crypto.
└symbolstringnoLegacy field.
└tickerstringnoPreferred field.
└localestringno"US" / "HK".
idsint64[]noSignal ids to look up — the id field of a returned item. At most 20; more is InvalidArgument, NOT truncated, so a silently missing 21st id cannot happen. Mutually exclusive with tickers. Unlike tickers, which only sees the open pool, ids also returns positions that have closed since (exit_time_ts > 0), so an id taken from an earlier response can be followed until the trade ends. Unknown ids are skipped, not errored. Added 2026-09-15.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxHKSignalsDataResponse payload.
└signalsAnalysisSwingmaxHKItem[]One entry per matched ticker.
└idint64Signal id — same meaning as US swingmax id: assigned at entry, stable through close. Pass it back as ids on /swingmax/hk/signals to follow one trade. Added 2026-09-15. Also present on /swingmax/hk/open.
└symbolstringHK code — always 5 digits, e.g. 00700.
└codestringSymbol with the .HK suffix.
└company_namestringCompany name, in the language of the listing.
└trade_currencystringHKD / CNY / USD. HK has three trading counters; the same company can list under more than one symbol, so amounts are NOT comparable across symbols without checking this. HK-only field.
└board_lotint32Shares per lot. HK trades in lots, not shares — 00700 is 100 shares/lot (~HKD 40k). 0 means the lot size could not be resolved. HK-only field.
└entry_timestringSignal date, a Hong Kong trading day (YYYY-MM-DD).
└entry_time_tsint64Entry time, unix seconds — midnight HKT on entry_time. Always > 0. Use this to place the entry marker; it saves parsing the date string.
└entry_pricedoubleBuy target (breakout level), in trade_currency. The entry marker goes here. A number, not a string — matching US swingmax.
└stop_lossstringSupport level. Entry minus 15%; moves up to the entry price once r2 triggers.
└r2_pricestringSell Target 1 (first resistance).
└r2_sell_pricestringActual r2 fill; empty when not triggered. This is the price to mark, not r2_price.
└r2_signal_tsint64r2 trigger time, unix seconds. 0 means not triggered — draw the first target marker only when this is > 0. Same rule as US swingmax.
└r3_pricestringSell Target 2 (second resistance).
└r3_sell_pricestringActual r3 fill; empty when not triggered.
└r3_signal_tsint64r3 trigger time, unix seconds. 0 means not triggered — draw the second target marker only when this is > 0.
└exit_timestringExit date; empty while the position is still open.
└exit_time_tsint64Exit time, unix seconds. 0 while the position is open — this one field carries both when it exited and whether it has exited, exactly as in US swingmax. There is no exit_signal_ts field in either market.
└exit_pricedoubleFinal exit price. The exit marker goes here. 0 while the position is open — check exit_time_ts, not this, to tell open from closed.
└returndoubleNet return of the WHOLE position, decimal fraction. Same field, same meaning and same type as US swingmax return. It was called profit_rate until 2026-09-09.
└realizedstringReturn already banked by the scale-out, decimal fraction. This — not return — is the win indicator: realized > 0 means the trade counted as a win, matching how US swingmax reports its win rate. The two numbers differ materially, so do not mix them. HK-only field.
└exit_reasonstringALERT = stopped at the original -15% stop (the only losing bucket). BREAKEVEN = r2 was hit, the stop moved up to the entry price, and it later stopped out at cost. R3 = took profit and gave back via the trailing stop. MAX_HOLD = held the full 60 trading days. HK-only field.
└hold_daysint32Trading days held. HK-only field.
└statusstringopen / closed.
└klineAnalysisKlinePointV2[]Daily bars with full OHLC + volume — enough to draw candlesticks. Roughly the last 90 calendar days, ending today. Same source and format as the US endpoints. NOT returned by /swingmax/hk/open, which is built for pulling the whole list at once and would inflate several times over with bars.
└closedouble—
└timestampint64Unix seconds.
└opendouble—
└highdouble—
└lowdouble—
└volumedouble—
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax/hk/signals
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{
  "tickers":[
    {"asset_type":0,"ticker":"00700","locale":"HK"},
    {"asset_type":0,"ticker":"09988","locale":"HK"}
  ],
  "lang":"zh-tw"
}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "signals":[
      {"symbol":"00700","code":"00700.HK","company_name":"腾讯控股","trade_currency":"HKD","board_lot":100,
       "entry_time":"2026-03-12","entry_time_ts":1773244800,"entry_price":402.4,"stop_loss":"402.4000000",
       "r2_price":"441.8000000","r2_sell_price":"441.8000000","r2_signal_ts":1775750400,
       "r3_price":"482.8800000","r3_sell_price":"","r3_signal_ts":0,
       "exit_time":"","exit_time_ts":0,"exit_price":0,"return":0,"realized":"0.0391000",
       "exit_reason":"","hold_days":31,"status":"open"}
    ]
  }
}
POST/v1/openapi/analysis/swingmax/hk/stocks
SwingmaxHKStocks

Browse HK swingmax signals over a date range. Mirrors /swingmax/stocks on the US side.

The lang BODY PARAM did nothing on the three HK swingmax endpoints before 2026-09-15 — only the header was honored, so a request with lang in the body and no header came back in English. Fixed; both now behave as documented on all three. Sending the header still works.
Field names match US swingmax as of 2026-09-09: name is now company_name, buy_date/buy_price are entry_time/entry_price, sell_date/sell_price are exit_time/exit_price, and the r2_triggered/r3_triggered booleans are gone — use r2_signal_ts/r3_signal_ts > 0 instead. The booleans said that a target was hit but never when, so no marker could be placed on a chart. entry_time_ts and exit_time_ts are new. Also on 2026-09-09: profit_rate was renamed to return, and entry_price / exit_price / return became numbers instead of strings — all three now match US swingmax exactly. HK-specific fields (trade_currency, board_lot, realized, exit_reason, hold_days) are unchanged.
Bars arrive on this endpoint. They were empty from launch until 2026-09-07 — the field was in the response shape but nothing ever filled it, so callers got an empty array with no error and reasonably concluded HK had no bars. If you cached that conclusion, re-check.
data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Universe: ordinary shares and REITs only. Warrants, CBBCs, ETFs and LIPs are excluded — HK lists roughly 30k derivative warrants against 3k ordinary shares, so an unfiltered pool would be about 90% derivatives.
Additional filters: average daily turnover at least HKD 20M, close at least HKD 2, market cap at least HKD 3B. The price floor exists because the HK main board has no delisting rule equivalent to the US $1 threshold.
Amounts are in trade_currency, NOT always HKD. Check the field before aggregating across symbols.
Request parameters
FieldTypeRequiredDescription
stypeint32noGetSwingMaxStocksType. 1=open pool, 2=closed, 3=today's new signals, 4=today's still-open. 5 (today-updated) returns InvalidArgument on HK: the HK signal table records only entry_time, with no last-updated column to distinguish it.
sideint32no0=long. HK swingmax is long-only, so side=1 (short) returns InvalidArgument rather than an empty list — an empty list reads as "no signals today" and would leave you waiting for data that never comes.
trade_statusint32no0 or 2 (all). HK has no paper-trade execution tracking, so trade_status=1 (traded) returns InvalidArgument.
pageint64no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, capped at 20 — the same ceiling as every other paged list on this API; only the two swingmax /open endpoints are uncapped. Was default 100 / cap 500 before 2026-09-15.
sort_typeint32no0 = endpoint default, 1 = latest first, 2 = oldest first, 3/4 = by return (whole-trade net P&L), high/low first. Note this is NOT realized, which is the banked scale-out portion used for win-rate — the two differ materially. The same number means the same thing on the US and HK endpoints.
keywordstringnoCase-insensitive substring match on ticker, code and company name. Omit to disable filtering. Max 64 characters.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxHKStocksDataResponse payload.
└stocksAnalysisSwingmaxHKItem[]One entry per signal. This array was called items until 2026-09-09; it is stocks now, matching US /swingmax/stocks. Reading the old key gets you nothing and no error — it looks like HK returned no data.
└idint64Signal id — same meaning as US swingmax id: assigned at entry, stable through close. Pass it back as ids on /swingmax/hk/signals to follow one trade. Added 2026-09-15. Also present on /swingmax/hk/open.
└symbolstringHK code — always 5 digits, e.g. 00700.
└codestringSymbol with the .HK suffix.
└company_namestringCompany name, in the language of the listing.
└trade_currencystringHKD / CNY / USD. HK has three trading counters; the same company can list under more than one symbol, so amounts are NOT comparable across symbols without checking this. HK-only field.
└board_lotint32Shares per lot. HK trades in lots, not shares — 00700 is 100 shares/lot (~HKD 40k). 0 means the lot size could not be resolved. HK-only field.
└entry_timestringSignal date, a Hong Kong trading day (YYYY-MM-DD).
└entry_time_tsint64Entry time, unix seconds — midnight HKT on entry_time. Always > 0. Use this to place the entry marker; it saves parsing the date string.
└entry_pricedoubleBuy target (breakout level), in trade_currency. The entry marker goes here. A number, not a string — matching US swingmax.
└stop_lossstringSupport level. Entry minus 15%; moves up to the entry price once r2 triggers.
└r2_pricestringSell Target 1 (first resistance).
└r2_sell_pricestringActual r2 fill; empty when not triggered. This is the price to mark, not r2_price.
└r2_signal_tsint64r2 trigger time, unix seconds. 0 means not triggered — draw the first target marker only when this is > 0. Same rule as US swingmax.
└r3_pricestringSell Target 2 (second resistance).
└r3_sell_pricestringActual r3 fill; empty when not triggered.
└r3_signal_tsint64r3 trigger time, unix seconds. 0 means not triggered — draw the second target marker only when this is > 0.
└exit_timestringExit date; empty while the position is still open.
└exit_time_tsint64Exit time, unix seconds. 0 while the position is open — this one field carries both when it exited and whether it has exited, exactly as in US swingmax. There is no exit_signal_ts field in either market.
└exit_pricedoubleFinal exit price. The exit marker goes here. 0 while the position is open — check exit_time_ts, not this, to tell open from closed.
└returndoubleNet return of the WHOLE position, decimal fraction. Same field, same meaning and same type as US swingmax return. It was called profit_rate until 2026-09-09.
└realizedstringReturn already banked by the scale-out, decimal fraction. This — not return — is the win indicator: realized > 0 means the trade counted as a win, matching how US swingmax reports its win rate. The two numbers differ materially, so do not mix them. HK-only field.
└exit_reasonstringALERT = stopped at the original -15% stop (the only losing bucket). BREAKEVEN = r2 was hit, the stop moved up to the entry price, and it later stopped out at cost. R3 = took profit and gave back via the trailing stop. MAX_HOLD = held the full 60 trading days. HK-only field.
└hold_daysint32Trading days held. HK-only field.
└statusstringopen / closed.
└klineAnalysisKlinePointV2[]Daily bars with full OHLC + volume — enough to draw candlesticks. Roughly the last 90 calendar days, ending today. Same source and format as the US endpoints. NOT returned by /swingmax/hk/open, which is built for pulling the whole list at once and would inflate several times over with bars.
└closedouble—
└timestampint64Unix seconds.
└opendouble—
└highdouble—
└lowdouble—
└volumedouble—
└totalint32—
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax/hk/stocks
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{"stype":2,"page":1,"size":50,"sort_type":1}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "stocks":[
      {"symbol":"00700","code":"00700.HK","company_name":"腾讯控股","trade_currency":"HKD","board_lot":100,
       "entry_time":"2026-03-12","entry_time_ts":1773244800,"entry_price":402.4,"stop_loss":"402.4000000",
       "r2_price":"441.8000000","r2_sell_price":"441.8000000","r2_signal_ts":1775750400,
       "r3_price":"482.8800000","r3_sell_price":"","r3_signal_ts":0,
       "exit_time":"2026-04-28","exit_time_ts":1777305600,"exit_price":402.4,
       "return":0.0391,"realized":"0.0391000",
       "exit_reason":"BREAKEVEN","hold_days":31,"status":"closed"}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/swingmax/hk/open
SwingmaxHKOpen

Returns every currently-open HK position in a single call by default. Mirrors /swingmax/open on the US side, including the shared paging parameters.

The lang BODY PARAM did nothing on the three HK swingmax endpoints before 2026-09-15 — only the header was honored, so a request with lang in the body and no header came back in English. Fixed; both now behave as documented on all three. Sending the header still works.
This endpoint changed shape on 2026-09-09 to match US /swingmax/open field for field. It no longer returns the same item as /swingmax/hk/signals and /swingmax/hk/stocks. The same numbers now carry different names depending on which endpoint you call: buy_price is entry_price, target1 is r2_price, target2 is r3_price, signal_time is entry_time_ts, last_exit_ts is exit_time_ts. That split exists in US swingmax and HK now mirrors it, so the two markets' /open responses are interchangeable. Fields with no counterpart here — company_name, return, realized, exit_reason, hold_days, kline — are on /swingmax/hk/signals and /swingmax/hk/stocks.
data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Universe: ordinary shares and REITs only. Warrants, CBBCs, ETFs and LIPs are excluded — HK lists roughly 30k derivative warrants against 3k ordinary shares, so an unfiltered pool would be about 90% derivatives.
Additional filters: average daily turnover at least HKD 20M, close at least HKD 2, market cap at least HKD 3B. The price floor exists because the HK main board has no delisting rule equivalent to the US $1 threshold.
Amounts are in trade_currency, NOT always HKD. Check the field before aggregating across symbols.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Omit to return every open position in one call — that is the historical behaviour of this endpoint and it is unchanged. Set it only if you want pages.
sort_typeint32no0 = endpoint default, 1 = latest first, 2 = oldest first, 3/4 = by return (whole-trade net P&L, not the banked realized portion), high/low first. The same number means the same thing on the US and HK endpoints.
keywordstringnoCase-insensitive substring match on ticker, code and company name. Omit to disable filtering. Max 64 characters.
langstringnoLocalizes server-rendered status text. Takes priority over the lang header.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxHKOpenDataResponse payload.
└positionsAnalysisSwingmaxHKOpenPosition[]Every currently-open HK position. Field for field the same shape as US /swingmax/open, including HK-only trade_currency and board_lot at the end.
└idint64Signal id — the same number as id on /swingmax/hk/signals and /swingmax/hk/stocks. Take it from this list and pass it back as ids on /swingmax/hk/signals to follow one position until it closes. Added 2026-09-15.
└symbolstringHK code — always 5 digits, e.g. 00700.
└codestringSymbol with the .HK suffix.
└buy_pricestringEntry / Buy Target, in trade_currency. Same number that /swingmax/hk/signals calls entry_price — a string here, a double there, because this endpoint mirrors US /swingmax/open.
└stop_lossstringSupport level. Entry minus 15%; moves up to the entry price once target1 triggers.
└target1stringSell Target 1 (r2). Same number /swingmax/hk/signals calls r2_price.
└target2stringSell Target 2 (r3). Same number /swingmax/hk/signals calls r3_price.
└potential_gaindoubleStatic: (target2/buy_price - 1) * 100, percent, rounded to 2 decimals. Identical computation to US swingmax, so the two markets' values are directly comparable. Note it measures the distance to target2, the FURTHER target — a position that already ran past target1 still reports the full distance to target2. This is what sort_type 3/4 sorts by on this endpoint.
└signal_timeint64Entry time, unix seconds. Same number /swingmax/hk/signals calls entry_time_ts.
└last_exit_tsint64Exit time, unix seconds; always 0 here since this endpoint returns open positions only.
└trade_currencystringHKD / CNY / USD. buy_price and both targets are in this currency, so they are NOT comparable across symbols without checking it. HK-only field.
└board_lotint32Shares per lot. HK trades in lots, not shares — 00700 is 100 shares/lot. 0 means the lot size could not be resolved. HK-only field.
└totalint32Rows after filtering, before paging.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax/hk/open
{}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "positions":[
      {"symbol":"00700","code":"00700.HK","buy_price":"402.4","stop_loss":"342.04",
       "target1":"441.8","target2":"482.88","potential_gain":20,
       "signal_time":1787155200,"last_exit_ts":0,
       "trade_currency":"HKD","board_lot":100}
    ],
    "total": 1
  }
}

DayTrading (US)

Intraday signals for US equities. A different animal from Swingmax: signals fire on 15m/30m bars, a dozen or so a day, and most resolve within hours — Swingmax is a daily-bar swing strategy whose positions live for weeks. Poll accordingly.

POST/v1/openapi/analysis/daytrading/open
DaytradingOpen

Positions from one US trading session, open and closed. Which session: on a trading day before the US open (09:30 ET), and on any non-trading day, it is the previous trading session; from the open onward it is the current one, including after the close. Closed positions carry sell_price, sell_ts and a realized return_rate; open ones carry current_price and a live return_rate. NOTE: between the open and the first signal of the day (intraday signals only start at 10:00 ET), and on days when no signal fires at all, the current session is empty — that is expected, not an outage.

The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
The response array is data.list, as on all three daytrading endpoints. It was called positions on /open and signals on /signals until 2026-09-10. Reading the old key yields nothing and no error — it looks like the endpoint returned no data.
There is deliberately no time-range parameter. The signal set is small (about 2k rows for US in total), so page + total is enough to reach any record — and a hidden default window is a trap: callers who pass nothing get an empty list and conclude the endpoint is broken, which is exactly what happened when the HK history endpoint launched. Sort with sort_type, then walk the pages using total.
data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Intraday signals fire only in two windows — 10:00-11:00 and 13:00-14:00 ET — and everything still open is force-closed at 15:50 ET. Outside US market hours this endpoint will usually return an empty list, which is correct, not an outage.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, capped at 20 — the same ceiling as every other paged list on this API. Unlike the two swingmax /open endpoints this one is NOT uncapped: every row carries minute bars, so a full-book response would be several times larger. Was 10 before 2026-09-15.
sort_typeint32no0 = endpoint default (by return_rate, high to low — realized for closed positions, live unrealized for open ones), 1 = latest first, 2 = oldest first, 3 = highest return first, 4 = lowest return first. The same number means the same thing on the US and HK endpoints.
keywordstringnoCase-insensitive substring match on ticker, code and company name. Omit to disable filtering. Max 64 characters.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisDaytradingOpenDataResponse payload.
└listAnalysisDaytradingPosition[]Every position of the session this endpoint is showing — open and closed. Field for field the same shape as /daytrading/signals and /daytrading/history.
└idint64Signal id — the same number on /open, /signals and /history for the same trade. Store it from any of the three and pass it back as ids on /signals to follow this position.
└symbolstringBare code. US: NVDA. HK: five digits, e.g. 00700.
└codestringSuffixed form. US: NVDA.O. HK: 00700.HK.
└namestringCompany name.
└logostringLogo URL; empty when unavailable.
└intervalstringBar interval behind the entry, 15m or 30m.
└signal_datestringSignal day on that market's trading calendar, YYYY-MM-DD. Derived from buy_ts in the market's own timezone — an HK 09:30 signal is still the previous day in New York.
└signal_timestringHuman-readable trigger time, YYYY-MM-DD HH:MM:SS in the market's own timezone. Same format as on /daytrading/signals and /daytrading/history.
└buy_tsint64Entry time, unix seconds. On 30m signals it is the right-edge timestamp of the bar that triggered the entry, so it matches one klines entry exactly.
└buy_pricestringEntry price.
└sell_tsint64Exit time, unix seconds; 0 while the position is still open. This endpoint returns every position of the session it shows, closed ones included, so closed rows carry their exit time. On 30m signals a kline-triggered exit (take-profit or stop) carries that bar's right-edge timestamp; the 15:50 force-close carries wall-clock time.
└sell_pricestringExit price; empty while the position is still open.
└stop_loss_pricestringStrategy stop, entry × 0.97 — both markets stop at -3%. Without it you cannot reproduce the strategy.
└current_pricestringLatest price.
└return_ratestringCurrent return, percent.
└statusstringHuman-readable progress text. Same field as on /daytrading/signals and /daytrading/history.
└klinesAnalysisKlinePointV2[]Minute bars, same point shape as everywhere else in this API. Sorted by timestamp ascending (oldest first) — the same order in both markets, on all three daytrading endpoints, and as swingmax. Bar stamping since 2026-09-17: on 30m signals the bars are right-edge stamped and anchored to the session open, the same convention as /pivot-point/info — the first US bar of the day is 10:00, the first HK afternoon bar 13:30, and buy_ts is one of these timestamps. 15m signals keep the upstream left-edge stamps with the 09:30 bar removed, so that series starts at 09:45.
└totalint32Rows after filtering, before paging.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/daytrading/open
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{"page":1,"size":10,"sort_type":1}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list":[
      {"id":90211,"symbol":"NVDA","code":"NVDA.O","name":"NVIDIA Corp","logo":"https://.../nvda.png",
       "interval":"15m","signal_date":"2025-08-19","signal_time":"2025-08-19 10:00:00",
       "buy_ts":1755612000,"buy_price":"118.42","sell_ts":0,"sell_price":"",
       "stop_loss_price":"114.87","current_price":"119.80",
       "return_rate":"1.17","status":"Entered @118.42 and staying patient",
       "klines":[]}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/daytrading/signals
DaytradingSignals

Signal records for the given tickers — what fired and how it resolved, as opposed to /daytrading/open which describes the current book. Same request shape as /swingmax/signals: pass tickers, get every signal for those codes in one response.

Lookup by id since 2026-09-15: pass ids (max 20) instead of tickers. Items have carried id all along; that is the value to pass back. Unknown ids are skipped rather than errored, so an empty list for a single id means that id does not exist.
The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
The response array is data.list, as on all three daytrading endpoints. It was called positions on /open and signals on /signals until 2026-09-10. Reading the old key yields nothing and no error — it looks like the endpoint returned no data.
Field names now match /daytrading/open and /daytrading/history — this endpoint used to expose dip_price / alert_price, which were the signal table's own column names. Only the names changed; the values map one-to-one. All three daytrading endpoints now return the same 17 fields; what differs is the rows, not the shape — this one carries in-flight signals, /history holds settled round-trips only.
Removed on 2026-09-14: page, size, sort_type and keyword. A single code has at most a few dozen signals, so paging had no use here, and sending any of them now fails at the gateway as an unknown field. There is deliberately no time-range parameter either: the response is every signal for the codes you asked for, so a hidden default window cannot hand you an empty list that reads like an outage. To walk the whole table use /daytrading/history (paged); for today's book use /daytrading/open.
Historical win-rate figures are NOT exposed here on purpose: the internal historical analyzer currently evaluates past signals against today's stock universe, which is look-ahead biased. Do not derive strategy performance from a long backfill of this endpoint.
Request parameters
FieldTypeRequiredDescription
tickersAnalysisAsset[]noAn array of OBJECTS, not of strings: [{"ticker":"NVDA"}]. Either tickers or ids is required; both together is InvalidArgument. Matches both the bare code and the suffixed one, case-insensitively. Every signal for those codes comes back in one response, newest buy_ts first.
└asset_typeint32no0=stock, 1=etf, 2=crypto.
└symbolstringnoLegacy field.
└tickerstringnoPreferred field.
└localestringno"US" / "HK".
idsint64[]noSignal ids to look up — the id field of an item returned by /open, /signals or /history (all three carry the same signal id). At most 20; more is InvalidArgument, NOT truncated. Mutually exclusive with tickers. Selects the same rows the tickers path would (in-flight and resolved signals alike), just by id. Negative ids — HK backtest rows from /history — are resolved too, returning that round-trip in the same item shape (status starts with "Backtest:"). 0 is InvalidArgument; unknown ids are skipped, not errored. Added 2026-09-15.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisDaytradingSignalsDataResponse payload.
└listAnalysisDaytradingSignalItem[]One entry per signal.
└idint64Signal id — the value to pass back as ids on this endpoint; the same number as id on /open and /history for the same trade.
└symbolstringBare code. US: NVDA. HK: five digits, e.g. 00700.
└codestringSuffixed form. Empty on US — that pipeline has no ticker lookup on this path.
└namestringCompany name. Populated on HK (real name from the HK code table). Empty on US.
└logostringLogo URL. Empty on both: HK has no logo source, and the US pipeline is left untouched here.
└intervalstringBar interval that produced the entry, 15m or 30m.
└signal_datestringSignal day on that market's trading calendar, YYYY-MM-DD.
└signal_timestringHuman-readable trigger time. HK only.
└buy_tsint64Entry time, unix seconds. On 30m signals it is the right-edge timestamp of the bar that triggered the entry, so it matches one klines entry exactly.
└buy_pricestringEntry signal price.
└sell_tsint64Exit time, unix seconds. 0 while still open. On 30m signals a kline-triggered exit (take-profit or stop) carries that bar's right-edge timestamp; the 15:50 force-close carries wall-clock time.
└sell_pricestringExit signal price; empty while still open.
└stop_loss_pricestringStrategy stop (entry × 0.97 — both markets stop at -3%).
└current_pricestringLatest price; empty for settled signals.
└return_ratestringPercent, not a fraction. Same name and meaning as on /open and /history.
└statusstringHuman-readable progress/exit text — the same field as status on /open and /history.
└klinesAnalysisKlinePointV2[]Minute bars centred on the entry. Sorted by timestamp ascending (oldest first) — the same order in both markets, on all three daytrading endpoints, and as swingmax. Bar stamping since 2026-09-17: on 30m signals the bars are right-edge stamped and anchored to the session open, the same convention as /pivot-point/info — the first US bar of the day is 10:00, the first HK afternoon bar 13:30, and buy_ts is one of these timestamps. 15m signals keep the upstream left-edge stamps with the 09:30 bar removed, so that series starts at 09:45.
└totalint32Always equal to the length of list — this endpoint is not paged.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/daytrading/signals
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{"tickers":[{"asset_type":0,"ticker":"NVDA","locale":"US"}]}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list":[
      {"id":90211,"symbol":"NVDA","code":"","name":"","logo":"","interval":"15m","signal_date":"2026-08-19","signal_time":"",
       "buy_ts":1755612000,"buy_price":"118.42","sell_ts":1755620100,"sell_price":"121.97","stop_loss_price":"114.87","current_price":"",
       "return_rate":"3.00","status":"Booked partial profits @121.97",
       "klines":[{"close":118.9,"timestamp":1755612000,"open":118.4,"high":119.1,"low":118.2,"volume":1234567}]}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/daytrading/history
DaytradingHistory

Closed round-trips — one row per completed entry-and-exit, with both prices, both timestamps and the realised return. Deliberately carries no K-line data. Call /v1/openapi/data/kline if you need bars — inlining them here would grow the payload by an order of magnitude for callers who only want the numbers.

The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
There is deliberately no time-range parameter. The signal set is small (about 2k rows for US in total), so page + total is enough to reach any record — and a hidden default window is a trap: callers who pass nothing get an empty list and conclude the endpoint is broken, which is exactly what happened when the HK history endpoint launched. Sort with sort_type, then walk the pages using total.
data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Easy to confuse with /daytrading/signals: signals is the event log (what fired, what state it is in now), history is the P&L ledger (did this trade make money). Signals answers "what happened", history answers "how did it end".
When you pass a symbol, the most recent still-open position for that symbol is prepended as a synthetic row with exit_time, exit_price and return_rate all 0 — and it is inserted after paging, so that page can return size + 1 rows and total will not count it. The HK endpoint does not do this.
Filter on exit_time > 0 before computing any statistic. It is the only rule that holds for both markets, and it keeps those return_rate 0 rows from being counted as break-even trades in a win rate.
Request parameters
FieldTypeRequiredDescription
symbolstringnoOptional ticker, e.g. NVDA. Omit for all symbols. Passing one enables the synthetic open-position row described in the notes.
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, capped at 20 — the same ceiling as every other paged list on this API; only the two swingmax /open endpoints are uncapped. Was 10 before 2026-09-15.
sort_typeint32no0 = endpoint default, 1 = latest first, 2 = oldest first. 3 and 4 (by return) return InvalidArgument here: this endpoint pages upstream, so sorting would only reorder the current page — sort client-side after fetching all pages instead. The same number means the same thing on the US and HK endpoints.
keywordstringnoExact ticker match, applied upstream before paging (the trades table has no name column). If symbol is also set, symbol wins — an exact filter should not be overridden by a fuzzy one. Omit to disable filtering. Max 64 characters.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisDaytradingHistoryDataResponse payload.
└listAnalysisDaytradingHistoryItem[]One entry per round-trip, newest entry first.
└idint64Signal id — the same number as id on /open and /signals for this trade, so an id stored from any of the three endpoints can be passed back as ids on /signals. Before 2026-09-15 this field was the round-trip table's own key, a different keyspace. Negative means a backtest row (HK only — exit_reason starts with "Backtest:"): every backtest row carries its own distinct negative id, so it works as a bookmark key, and passing it back as ids on /daytrading/hk/signals returns that round-trip in the same item shape as a live signal. 0 means a legacy row with no linked signal; 0 is the one value ids rejects.
└symbolstringBare code. US: NVDA. HK: five digits, e.g. 00700.
└codestringSuffixed form. US: NVDA.O. HK: 00700.HK.
└namestringCompany name.
└logostringLogo URL; empty when unavailable.
└intervalstringBar interval behind the entry, 15m or 30m.
└signal_datestringSignal day on that market's trading calendar, YYYY-MM-DD.
└signal_timestringHuman-readable trigger time.
└buy_tsint64Entry time, unix seconds. On 30m signals it is the right-edge timestamp of the bar that triggered the entry, so it matches one klines entry exactly.
└buy_pricestringEntry price.
└sell_tsint64Exit time, unix seconds. 0 = not settled yet. On 30m signals a kline-triggered exit (take-profit or stop) carries that bar's right-edge timestamp; the 15:50 force-close carries wall-clock time.
└sell_pricestringExit price; empty when not settled.
└stop_loss_pricestringStrategy stop price (HK daytrading stops at -3%). This was missing before — without it you cannot reproduce or check the strategy.
└current_pricestringLatest price.
└return_ratestringPercent, not a fraction: "2.35" means +2.35%. Same name and meaning as on /daytrading/open.
└statusstringHuman-readable exit text. Same field as on /daytrading/open.
└klinesAnalysisKlinePointV2[]Minute bars around the trade — the same series the product cards draw candlesticks from. Previously suppressed by this layer even though upstream always returned them. Sorted by timestamp ascending (oldest first) — the same order in both markets, on all three daytrading endpoints, and as swingmax. Bar stamping since 2026-09-17: on 30m signals the bars are right-edge stamped and anchored to the session open, the same convention as /pivot-point/info — the first US bar of the day is 10:00, the first HK afternoon bar 13:30, and buy_ts is one of these timestamps. 15m signals keep the upstream left-edge stamps with the 09:30 bar removed, so that series starts at 09:45.
└totalint32Total rows matching the range, for paging. Excludes the synthetic open-position row.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/daytrading/history
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
Content-Type: application/json

{ "symbol": "NVDA", "page": 1, "size": 20 }
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list": [
      {
        "id": 3521,
        "symbol": "NVDA",
        "code": "NVDA",
        "trade_date": "2026-08-19",
        "entry_time": 1755612000,
        "exit_time": 1755620100,
        "entry_price": "118.4200",
        "exit_price": "121.9700",
        "return_rate": "3.00",
        "interval": "15m",
        "exit_reason": "Booked 3.00% profits @121.97 at 11:35 AM"
      }
    ],
    "total": 1
  }
}

DayTrading (HK)

Intraday signals for HK equities. Same shape as the US feed but a SEPARATE pipeline end to end — its own process, its own signal table, its own switch. One market going down does not affect the other, which is also why the paths differ instead of taking a locale parameter. The strategy parameters are NOT the US ones: take profit at 0.5%, book on a 0.2% pull-back from the high, stop at -3%, and entries only fire while the market (equal-weighted intraday move across the pool) is below -0.4% (it was -0.8% until 2026-09-29; relaxed so more sessions produce entries). Those numbers were calibrated on HK data — the US set (3% / 1%) scored a 46.5% win rate with negative expectancy when replayed on HK bars. Sessions are 09:30-12:00 and 13:00-16:00 HKT; signal windows are 10:00-11:00 and 13:00-14:00, and anything still open is force-closed at 15:50.

POST/v1/openapi/analysis/daytrading/hk/open
DaytradingHKOpen

Positions from one Hong Kong trading session, open and closed. Which session: on a trading day before the HK open (09:30 HKT), and on any non-trading day, it is the previous trading session; from the open onward it is the current one, including after the close. `code` carries the .HK suffix (00700.HK); `symbol` is the bare five-digit number. Closed positions carry sell_price, sell_ts and a realized return_rate; open ones carry current_price and a live return_rate. NOTE: between the open and the first signal of the day (intraday signals only start at 10:00 HKT), and on days when the market-breadth gate never opens, the current session is empty — that is expected, not an outage.

The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
The response array is data.list, as on all three daytrading endpoints. It was called positions on /open and signals on /signals until 2026-09-10. Reading the old key yields nothing and no error — it looks like the endpoint returned no data.
There is deliberately no time-range parameter. The signal set is small (about 2k rows for US in total), so page + total is enough to reach any record — and a hidden default window is a trap: callers who pass nothing get an empty list and conclude the endpoint is broken, which is exactly what happened when the HK history endpoint launched. Sort with sort_type, then walk the pages using total.
data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Intraday signals fire only in two windows — 10:00-11:00 and 13:00-14:00 HKT — and everything still open is force-closed at 15:50 HKT. Note the lunch break: the HK session runs 09:30-12:00 and 13:00-16:00, so the second window opens right at the afternoon bell. Outside HK market hours this endpoint will usually return an empty list, which is correct, not an outage.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, capped at 20 — the same ceiling as every other paged list on this API. Unlike the two swingmax /open endpoints this one is NOT uncapped: every row carries minute bars, so a full-book response would be several times larger. Was 10 before 2026-09-15.
sort_typeint32no0 = endpoint default (by return_rate, high to low — realized for closed positions, live unrealized for open ones), 1 = latest first, 2 = oldest first, 3 = highest return first, 4 = lowest return first. The same number means the same thing on the US and HK endpoints.
keywordstringnoCase-insensitive substring match on ticker, code and company name. Omit to disable filtering. Max 64 characters.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisDaytradingOpenDataResponse payload.
└listAnalysisDaytradingPosition[]Every position of the session this endpoint is showing — open and closed. Field for field the same shape as /daytrading/signals and /daytrading/history.
└idint64Signal id — the same number on /open, /signals and /history for the same trade. Store it from any of the three and pass it back as ids on /signals to follow this position.
└symbolstringBare code. US: NVDA. HK: five digits, e.g. 00700.
└codestringSuffixed form. US: NVDA.O. HK: 00700.HK.
└namestringCompany name.
└logostringLogo URL; empty when unavailable.
└intervalstringBar interval behind the entry, 15m or 30m.
└signal_datestringSignal day on that market's trading calendar, YYYY-MM-DD. Derived from buy_ts in the market's own timezone — an HK 09:30 signal is still the previous day in New York.
└signal_timestringHuman-readable trigger time, YYYY-MM-DD HH:MM:SS in the market's own timezone. Same format as on /daytrading/signals and /daytrading/history.
└buy_tsint64Entry time, unix seconds. On 30m signals it is the right-edge timestamp of the bar that triggered the entry, so it matches one klines entry exactly.
└buy_pricestringEntry price.
└sell_tsint64Exit time, unix seconds; 0 while the position is still open. This endpoint returns every position of the session it shows, closed ones included, so closed rows carry their exit time. On 30m signals a kline-triggered exit (take-profit or stop) carries that bar's right-edge timestamp; the 15:50 force-close carries wall-clock time.
└sell_pricestringExit price; empty while the position is still open.
└stop_loss_pricestringStrategy stop, entry × 0.97 — both markets stop at -3%. Without it you cannot reproduce the strategy.
└current_pricestringLatest price.
└return_ratestringCurrent return, percent.
└statusstringHuman-readable progress text. Same field as on /daytrading/signals and /daytrading/history.
└klinesAnalysisKlinePointV2[]Minute bars, same point shape as everywhere else in this API. Sorted by timestamp ascending (oldest first) — the same order in both markets, on all three daytrading endpoints, and as swingmax. Bar stamping since 2026-09-17: on 30m signals the bars are right-edge stamped and anchored to the session open, the same convention as /pivot-point/info — the first US bar of the day is 10:00, the first HK afternoon bar 13:30, and buy_ts is one of these timestamps. 15m signals keep the upstream left-edge stamps with the 09:30 bar removed, so that series starts at 09:45.
└totalint32Rows after filtering, before paging.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/daytrading/hk/open
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{"page":1,"size":10,"sort_type":1}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list":[
      {"id":13,"symbol":"00522","code":"00522.HK","name":"ASMPT Ltd","logo":"https://.../00522.png",
       "interval":"30m","signal_date":"2026-09-10","signal_time":"2026-09-10 13:41:49",
       "buy_ts":1789018909,"buy_price":"164.40","sell_ts":0,"sell_price":"",
       "stop_loss_price":"159.47","current_price":"165.60",
       "return_rate":"0.73","status":"Entered @164.40 and staying patient",
       "klines":[]}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/daytrading/hk/signals
DaytradingHKSignals

Signal records for the given tickers — what fired and how it resolved, as opposed to /daytrading/hk/open which describes the current book. Same request shape as /swingmax/hk/signals: pass tickers, get every signal for those codes in one response.

Lookup by id since 2026-09-15: pass ids (max 20) instead of tickers. Items have carried id all along; that is the value to pass back. Unknown ids are skipped rather than errored, so an empty list for a single id means that id does not exist.
The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
The response array is data.list, as on all three daytrading endpoints. It was called positions on /open and signals on /signals until 2026-09-10. Reading the old key yields nothing and no error — it looks like the endpoint returned no data.
Field names now match /daytrading/open and /daytrading/history — this endpoint used to expose dip_price / alert_price, which were the signal table's own column names. Only the names changed; the values map one-to-one. All three daytrading endpoints now return the same 17 fields; what differs is the rows, not the shape — this one carries in-flight signals, /history holds settled round-trips only.
Removed on 2026-09-14: page, size, sort_type and keyword. A single code has at most a few dozen signals, so paging had no use here, and sending any of them now fails at the gateway as an unknown field. There is deliberately no time-range parameter either: the response is every signal for the codes you asked for, so a hidden default window cannot hand you an empty list that reads like an outage. To walk the whole table use /daytrading/hk/history (paged); for today's book use /daytrading/hk/open.
Historical win-rate figures are NOT exposed here on purpose: the internal historical analyzer currently evaluates past signals against today's stock universe, which is look-ahead biased. Do not derive strategy performance from a long backfill of this endpoint.
Request parameters
FieldTypeRequiredDescription
tickersAnalysisAsset[]noAn array of OBJECTS, not of strings: [{"ticker":"00700"}]. Either tickers or ids is required; both together is InvalidArgument. Matches both the bare code and the suffixed one, case-insensitively. Every signal for those codes comes back in one response, newest buy_ts first.
└asset_typeint32no0=stock, 1=etf, 2=crypto.
└symbolstringnoLegacy field.
└tickerstringnoPreferred field.
└localestringno"US" / "HK".
idsint64[]noSignal ids to look up — the id field of an item returned by /open, /signals or /history (all three carry the same signal id). At most 20; more is InvalidArgument, NOT truncated. Mutually exclusive with tickers. Selects the same rows the tickers path would (in-flight and resolved signals alike), just by id. Negative ids — HK backtest rows from /history — are resolved too, returning that round-trip in the same item shape (status starts with "Backtest:"). 0 is InvalidArgument; unknown ids are skipped, not errored. Added 2026-09-15.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisDaytradingSignalsDataResponse payload.
└listAnalysisDaytradingSignalItem[]One entry per signal.
└idint64Signal id — the value to pass back as ids on this endpoint; the same number as id on /open and /history for the same trade.
└symbolstringBare code. US: NVDA. HK: five digits, e.g. 00700.
└codestringSuffixed form. Empty on US — that pipeline has no ticker lookup on this path.
└namestringCompany name. Populated on HK (real name from the HK code table). Empty on US.
└logostringLogo URL. Empty on both: HK has no logo source, and the US pipeline is left untouched here.
└intervalstringBar interval that produced the entry, 15m or 30m.
└signal_datestringSignal day on that market's trading calendar, YYYY-MM-DD.
└signal_timestringHuman-readable trigger time. HK only.
└buy_tsint64Entry time, unix seconds. On 30m signals it is the right-edge timestamp of the bar that triggered the entry, so it matches one klines entry exactly.
└buy_pricestringEntry signal price.
└sell_tsint64Exit time, unix seconds. 0 while still open. On 30m signals a kline-triggered exit (take-profit or stop) carries that bar's right-edge timestamp; the 15:50 force-close carries wall-clock time.
└sell_pricestringExit signal price; empty while still open.
└stop_loss_pricestringStrategy stop (entry × 0.97 — both markets stop at -3%).
└current_pricestringLatest price; empty for settled signals.
└return_ratestringPercent, not a fraction. Same name and meaning as on /open and /history.
└statusstringHuman-readable progress/exit text — the same field as status on /open and /history.
└klinesAnalysisKlinePointV2[]Minute bars centred on the entry. Sorted by timestamp ascending (oldest first) — the same order in both markets, on all three daytrading endpoints, and as swingmax. Bar stamping since 2026-09-17: on 30m signals the bars are right-edge stamped and anchored to the session open, the same convention as /pivot-point/info — the first US bar of the day is 10:00, the first HK afternoon bar 13:30, and buy_ts is one of these timestamps. 15m signals keep the upstream left-edge stamps with the 09:30 bar removed, so that series starts at 09:45.
└totalint32Always equal to the length of list — this endpoint is not paged.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/daytrading/hk/signals
Authorization: Bearer pk_live_••••••••••••••••••••••••••••
{"tickers":[{"asset_type":0,"ticker":"09988","locale":"HK"}],"lang":"zh-tw"}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list":[
      {"id":41207,"symbol":"09988","code":"09988.HK","name":"阿里巴巴-W","logo":"","interval":"15m","signal_date":"2026-08-19","signal_time":"2026-08-19 10:15:00",
       "buy_ts":1755569700,"buy_price":"118.40","sell_ts":1755571500,"sell_price":"119.00","stop_loss_price":"114.85","current_price":"",
       "return_rate":"0.51","status":"Booked partial profits @119.00",
       "klines":[{"close":118.6,"timestamp":1755569700,"open":118.4,"high":118.9,"low":118.3,"volume":2310000}]}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/daytrading/hk/history
DaytradingHKHistory

Closed round-trips — one row per completed entry-and-exit, with both prices, both timestamps and the realised return. Deliberately carries no K-line data. Call /v1/openapi/data/kline if you need bars — inlining them here would grow the payload by an order of magnitude for callers who only want the numbers.

The status field is localized. The "lang" body param takes priority when set; otherwise the "lang" request header is used: omit both for English, "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Every other field (prices, timestamps) is language-independent.
There is deliberately no time-range parameter. The signal set is small (about 2k rows for US in total), so page + total is enough to reach any record — and a hidden default window is a trap: callers who pass nothing get an empty list and conclude the endpoint is broken, which is exactly what happened when the HK history endpoint launched. Sort with sort_type, then walk the pages using total.
data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
This endpoint is currently seeded with BACKTEST rows, not live fills. They are identifiable two ways: dip_id is negative (live signal ids are positive and auto-increment), and exit_reason starts with "Backtest:". Live rows begin appending from the 2026-09-08 session onward, into the same list.
What the backtest is: the production signal generator replayed over 5-minute bars for 414 HK names across 2026-06-10 to 2026-09-02 (63 sessions), with the live parameters — 0.5% take-profit, 0.2% trailing drawdown, -3% stop, entries only while the equal-weighted pool is down more than 0.8% on the day. 394 round-trips, 72.1% of them positive.
Backtest caveats that do not apply to live rows: fills are taken at bar prices rather than at the tick the live engine would have traded, and no transaction cost is deducted. HK round-trip cost is about 0.22%, which takes mean return per trade from +0.30% down to +0.081%. Do not present these figures as realised performance.
Easy to confuse with /daytrading/hk/signals: signals is the event log (what fired, what state it is in now), history is the P&L ledger (did this trade make money). Signals answers "what happened", history answers "how did it end".
exit_time = 0 means the round-trip is not settled yet — either still held, or the exit price was missing. Those rows carry return_rate 0 as well; treat 0/0 as "unknown", not as "broke even".
return_rate is gross in both kinds of row. The live writer derives it from alert_price / dip_price with no cost deducted, and the backtest rows follow the same convention on purpose — mixing gross and net in one column would make the two indistinguishable.
Request parameters
FieldTypeRequiredDescription
symbolstringnoOptional. Bare five-digit HK code (00700). Omit for all symbols.
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, capped at 20 — the same ceiling as every other paged list on this API; only the two swingmax /open endpoints are uncapped. Was 10 before 2026-09-15.
sort_typeint32no0 = endpoint default, 1 = latest first, 2 = oldest first. 3 and 4 (by return) return InvalidArgument here: this endpoint pages upstream, so sorting would only reorder the current page — sort client-side after fetching all pages instead. The same number means the same thing on the US and HK endpoints.
keywordstringnoExact ticker match, applied upstream before paging (the trades table has no name column). If symbol is also set, symbol wins — an exact filter should not be overridden by a fuzzy one. Omit to disable filtering. Max 64 characters.
langstringnoLocalizes the status text. When set it takes priority over the "lang" request header: "zh" / "zh-cn" for Simplified Chinese, "zh-tw" for Traditional Chinese. Omit or leave empty to fall back to the header (English by default).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisDaytradingHistoryDataResponse payload.
└listAnalysisDaytradingHistoryItem[]One entry per completed round-trip, newest entry first.
└idint64Signal id — the same number as id on /open and /signals for this trade, so an id stored from any of the three endpoints can be passed back as ids on /signals. Before 2026-09-15 this field was the round-trip table's own key, a different keyspace. Negative means a backtest row (HK only — exit_reason starts with "Backtest:"): every backtest row carries its own distinct negative id, so it works as a bookmark key, and passing it back as ids on /daytrading/hk/signals returns that round-trip in the same item shape as a live signal. 0 means a legacy row with no linked signal; 0 is the one value ids rejects.
└symbolstringBare code. US: NVDA. HK: five digits, e.g. 00700.
└codestringSuffixed form. US: NVDA.O. HK: 00700.HK.
└namestringCompany name.
└logostringLogo URL; empty when unavailable.
└intervalstringBar interval behind the entry, 15m or 30m.
└signal_datestringSignal day on that market's trading calendar, YYYY-MM-DD.
└signal_timestringHuman-readable trigger time.
└buy_tsint64Entry time, unix seconds. On 30m signals it is the right-edge timestamp of the bar that triggered the entry, so it matches one klines entry exactly.
└buy_pricestringEntry price.
└sell_tsint64Exit time, unix seconds. 0 = not settled yet. On 30m signals a kline-triggered exit (take-profit or stop) carries that bar's right-edge timestamp; the 15:50 force-close carries wall-clock time.
└sell_pricestringExit price; empty when not settled.
└stop_loss_pricestringStrategy stop price (HK daytrading stops at -3%). This was missing before — without it you cannot reproduce or check the strategy.
└current_pricestringLatest price.
└return_ratestringPercent, not a fraction: "2.35" means +2.35%. Same name and meaning as on /daytrading/open.
└statusstringHuman-readable exit text. Same field as on /daytrading/open.
└klinesAnalysisKlinePointV2[]Minute bars around the trade — the same series the product cards draw candlesticks from. Previously suppressed by this layer even though upstream always returned them. Sorted by timestamp ascending (oldest first) — the same order in both markets, on all three daytrading endpoints, and as swingmax. Bar stamping since 2026-09-17: on 30m signals the bars are right-edge stamped and anchored to the session open, the same convention as /pivot-point/info — the first US bar of the day is 10:00, the first HK afternoon bar 13:30, and buy_ts is one of these timestamps. 15m signals keep the upstream left-edge stamps with the 09:30 bar removed, so that series starts at 09:45.
└totalint32Total rows matching the range, for paging.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/daytrading/hk/history
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "symbol": "00700", "page": 1, "size": 20 }
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list": [
      {
        "id": 12,
        "symbol": "00700",
        "code": "00700.HK",
        "trade_date": "2026-09-08",
        "entry_time": 1788838200,
        "exit_time": 1788845400,
        "entry_price": "445.4000",
        "exit_price": "448.2000",
        "return_rate": "0.63",
        "interval": "15m",
        "exit_reason": "Booked 0.63% profits @448.20 at 12:30 PM"
      }
    ],
    "total": 1
  }
}

AI Stock Picker

A model portfolio, not a signal feed. AI Stock Picker holds a basket of roughly ten US stocks by weight, rebalances weekly and stops names out intraday. Unlike /swingmax and /daytrading, where every row is an independent trade, these rows add up to one book and performance is measured on the book. Three endpoints mirror the product page: current holdings, closed positions, performance. Scope: analysis:stock-picker.

POST/v1/openapi/analysis/stock-picker/current
StockPickerCurrent

Current holdings of the AI Stock Picker book, with entry, stop, latest price, unrealized return, weight and daily bars for each name. Returns every holding in one call by default; the paging, sorting and keyword parameters are there for callers that want them.

data.total is the count AFTER keyword filtering and BEFORE paging — divide it by size to get the page count. Paging past the last page returns an empty list with total unchanged, which is the normal way to detect the end, not an error.
Returns and weights are percent numbers everywhere in the two portfolio products (2.35 = +2.35%, 10 = 10%), and prices are always double — even where the upstream service carries them as strings or fractions.
There is no lang parameter: narrative and rationale exist in English only.
Empty positions is a valid state right after a full stop-out or on the data pipeline's rebuild window, not an outage. as_of still tells you which snapshot you are looking at.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Omit to return every holding in one call — the book holds about ten names. Set it only if you want pages.
sort_typeint32no0 = endpoint default (upstream order: weight, high to low), 1 = newest join_date first, 2 = oldest join_date first, 3 = highest return first, 4 = lowest return first.
keywordstringnoCase-insensitive substring match on symbol and code. Omit to disable filtering. Max 64 characters.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisStockPickerCurrentDataResponse payload.
└positionsAnalysisStockPickerPosition[]One row per holding.
└symbolstringBare ticker, e.g. NVDA.
└codestringSame as symbol for this product today — the upstream feed carries no exchange suffix here (e.g. MSFT, not MSFT.O).
└localestringAlways US.
└asset_typeint320 = stock.
└narrativestringTheme the name was picked under, e.g. "AI infrastructure". The portfolio is diversified across themes, so this is the grouping key. English only.
└rationalestringWhy this name was picked, as written by the model at entry. English only.
└join_datestringEntry date, YYYY-MM-DD (US Eastern trading day). Compare with as_of to tell a fresh pick from a name that has been held for weeks.
└entry_pricedoubleCost basis.
└stop_lossdoubleStop price. When it triggers the row moves to status=stopped and drops out of this list at the next snapshot; the exit then shows up on /stock-picker/history with exit_reason=stop_loss.
└current_pricedoubleLatest price. 0 means the quote could not be fetched — in that case return is also 0, which is NOT "flat".
└returndouble(current_price / entry_price - 1) * 100, in percent, 2 decimals. Unrealized.
└weightdoublePortfolio weight in percent (10 = 10% of the book).
└statusstringactive, or stopped for a name whose stop triggered intraday and is waiting for the next snapshot to leave the book.
└klineAnalysisKlinePointV2[]Daily bars from join_date to today, oldest first. OHLC only — volume is always 0 on this endpoint.
└totalint32Rows after keyword filtering, before paging.
└as_ofstringSnapshot date of the book, YYYY-MM-DD US Eastern. The snapshot is refreshed every trading day around 08:00 ET, but rebalancing only happens weekly — a new as_of does not mean the holdings changed; look at join_date for that.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/stock-picker/current
{}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "positions": [
      {"symbol":"ULTA","code":"ULTA","locale":"US","asset_type":0,
       "narrative":"Beauty retail resilience","rationale":"...",
       "join_date":"2026-10-06","entry_price":412.5,"stop_loss":371.25,
       "current_price":430.1,"return":4.27,"weight":10,"status":"active",
       "kline":[{"close":415.2,"timestamp":1759723200,"open":411.9,"high":417.3,"low":410.8,"volume":0}]}
    ],
    "total": 1,
    "as_of": "2026-10-15"
  }
}
POST/v1/openapi/analysis/stock-picker/history
StockPickerHistory

Closed positions of the AI Stock Picker book, newest exit first, paged upstream. Each row is one complete holding: entry and exit dates and prices, realized return, weight while held, and why it left the book.

There is deliberately no sort_type and no keyword on this endpoint. Rows are paged upstream, so this layer never holds the whole table; sorting the current page by return would look right and be wrong. Walk the pages and sort client-side, or use /stock-picker/performance for the aggregate numbers.
Rows start in early 2026 when the current strategy went live; earlier picks from the previous strategy generation are not included, so the history and /stock-picker/performance describe the same set of trades.
A position whose stop triggers today appears here the same day with exit_reason=stop_loss; rebalance exits land on the rebalance day. Cached for about a minute when the requested window includes today, longer for windows entirely in the past.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, maximum 20; larger values are clamped, not rejected.
start_datestringnoEarliest exit_date to include, YYYY-MM-DD, inclusive. Omit for no lower bound.
end_datestringnoLatest exit_date to include, YYYY-MM-DD, inclusive. Omit for no upper bound. Must not be before start_date — a reversed range is InvalidArgument, not an empty list.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisStockPickerHistoryDataResponse payload.
└listAnalysisStockPickerClosedPosition[]Closed positions, newest exit_date first.
└symbolstringBare ticker, e.g. NVDA.
└codestringSame as symbol for this product today — the upstream feed carries no exchange suffix here (e.g. MSFT, not MSFT.O).
└localestringAlways US.
└asset_typeint320 = stock.
└narrativestringTheme the name was picked under, e.g. "AI infrastructure". The portfolio is diversified across themes, so this is the grouping key. English only.
└rationalestringWhy this name was picked, as written by the model at entry. English only.
└join_datestringEntry date, YYYY-MM-DD.
└exit_datestringExit date, YYYY-MM-DD. This is what start_date / end_date filter on.
└entry_pricedoubleCost basis.
└exit_pricedoubleFill price at exit. 0 on the rare early rows where the pipeline did not record it.
└returndoubleRealized return of the position, (exit_price / entry_price - 1) * 100, in percent.
└weightdoublePortfolio weight while held, in percent.
└exit_reasonstringrebalance = swapped out at the weekly rebalance; stop_loss = the stop triggered intraday.
└totalint32Rows matching the date filter, before paging.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/stock-picker/history
{"page": 1, "size": 20, "start_date": "2026-09-01", "end_date": "2026-09-30"}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list": [
      {"symbol":"NVDA","code":"NVDA","locale":"US","asset_type":0,
       "narrative":"AI infrastructure","rationale":"...",
       "join_date":"2026-09-01","exit_date":"2026-09-29",
       "entry_price":100,"exit_price":112.35,"return":12.35,"weight":8,
       "exit_reason":"rebalance"}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/stock-picker/performance
StockPickerPerformance

Track record of the AI Stock Picker book since inception: headline statistics plus the cumulative-return curve against the S&P 500 on the same time axis. Recomputed upstream once a day after the US close; there is no period parameter.

Every ratio except sharpe_ratio is a percent number here, converted from the fractions the upstream table stores — 12.3 means +12.3%.
If the nightly recompute has not produced a row yet (new deployment, pipeline outage) the endpoint returns ret=ERR_RECORD_NOT_EXIST instead of a payload full of zeros, so a missing track record cannot be mistaken for a flat one.
Statistics here and rows on /stock-picker/history come from the same set of trades (the current strategy generation, early 2026 onward).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisStockPickerPerformanceDataResponse payload.
└strategy_namestringProduct name, "AI Stock Picker".
└descriptionstringOne-paragraph strategy description. English only.
└total_returndoubleCumulative return since inception, percent.
└annualized_returndoubleAnnualized return, percent.
└win_ratedoubleShare of closed positions that were profitable, percent.
└max_drawdowndoubleLargest peak-to-trough decline of the book, percent, negative.
└sharpe_ratiodoubleSharpe ratio. Not a percent.
└day1_changedoubleChange of the book over the latest trading day, percent.
└biggest_lose_returndoubleWorst single closed position, percent, negative.
└strategy_curveAnalysisPerformancePoint[]Cumulative return of the book, one point per trading day, oldest first.
└datestringTrading day, YYYY-MM-DD in US Eastern time.
└tsint64Unix seconds at 00:00 US Eastern on that trading day. Both curves carry date and ts so one charting routine works for every portfolio product.
└cumulative_returndoubleCumulative return since the first point of the curve, in percent (2.35 = +2.35%).
└sp500_curveAnalysisPerformancePoint[]S&P 500 over the same days, same time axis, so the two can be drawn on one chart.
└datestringTrading day, YYYY-MM-DD in US Eastern time.
└tsint64Unix seconds at 00:00 US Eastern on that trading day. Both curves carry date and ts so one charting routine works for every portfolio product.
└cumulative_returndoubleCumulative return since the first point of the curve, in percent (2.35 = +2.35%).
Example request
POST https://api.alphio.ai/v1/openapi/analysis/stock-picker/performance
{}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "strategy_name": "AI Stock Picker",
    "description": "...",
    "total_return": 23.45, "annualized_return": 30.1, "win_rate": 61.23,
    "max_drawdown": -10.2, "sharpe_ratio": 1.23, "day1_change": 0.42,
    "biggest_lose_return": -14.8,
    "strategy_curve": [{"date":"2026-01-02","ts":1767330000,"cumulative_return":0}, {"date":"2026-01-05","ts":1767589200,"cumulative_return":0.8}],
    "sp500_curve":    [{"date":"2026-01-02","ts":1767330000,"cumulative_return":0}, {"date":"2026-01-05","ts":1767589200,"cumulative_return":0.3}]
  }
}

SwingMax Portfolio

SwingMax Portfolio is the book built from SwingMax signals: up to ten US names held by weight, with the same R2/R3 ladder exits as the single-name signals, plus a cash sleeve. It is a separate product from /swingmax/* — those endpoints give you every signal, this one gives you the managed portfolio — and a separate scope: analysis:swingmax-portfolio. Three endpoints: current book, closed trades, performance.

POST/v1/openapi/analysis/swingmax-portfolio/current
SwingmaxPortfolioCurrent

The book as it stands: current positions with weight and unrealized return, the industry breakdown, and the fills of the most recent trading session. One call, no paging — the book holds at most ten names.

Which session latest_trades covers: on weekends and US holidays, the previous trading day; on a trading day before 08:00 ET, also the previous trading day; from 08:00 ET onward, today. Pull before 08:00 ET and you get yesterday's fills.
price is the latest daily close, so return moves once a day. For intraday marks, quote the symbols yourself.
The path is /swingmax-portfolio/, not /swingmax/portfolio: scopes match by path prefix, and this product is granted separately from analysis:swingmax. A key with only analysis:swingmax gets 10403 here.
Returns and weights are percent numbers everywhere in the two portfolio products (2.35 = +2.35%, 10 = 10%), and prices are always double — even where the upstream service carries them as strings or fractions.
Request parameters
FieldTypeRequiredDescription
langstringnoLocalizes description. "zh" / "zh-cn" = Simplified Chinese, "zh-tw" = Traditional Chinese; anything else, or empty, is English. Takes priority over the lang request header.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxPortfolioCurrentDataResponse payload.
└descriptionstringOne-paragraph product description, localized by lang.
└positionsAnalysisSwingmaxPortfolioPosition[]Current holdings, weight high to low, at most ten.
└symbolstringBare ticker, e.g. NVDA.
└namestringCompany name.
└localestringAlways US.
└asset_typeint320 = stock.
└pricedoubleLatest daily close — not a live quote.
└costdoubleEntry price.
└returndouble(price / cost - 1) * 100, percent, 2 decimals. Unrealized.
└weightdoublePortfolio weight in percent (10 = 10% of the book).
└entry_tsint64Entry time, unix seconds.
└industriesAnalysisSwingmaxPortfolioIndustry[]Industry breakdown of the book, Cash first then by weight.
└industrystringTRBC industry group name. The first row is always "Cash" — the part of the book not invested.
└weightdoublePercent of the book. All rows add up to 100.
└latest_tradesAnalysisSwingmaxPortfolioTrade[]Fills of the most recent trading session, newest first. Empty on a session with no trades.
└symbolstringBare ticker, e.g. NVDA.
└namestringCompany name.
└localestringAlways US.
└asset_typeint320 = stock.
└signal_tsint64Fill time, unix seconds.
└filled_pricedoubleFill price.
└weight_beforedoubleWeight of this name in the book before the fill, percent.
└weight_afterdoubleWeight after the fill, percent. 0 on a full exit.
└eventstringBUY, R2, R3 or SELL — the same ladder names as the /swingmax signals: R2 and R3 are partial profit-taking at the first and second targets, SELL is the final exit (stop or max hold).
└sideint321 = buy, 2 = sell. R2, R3 and SELL are all sells.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax-portfolio/current
{"lang": ""}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "description": "SwingMax Portfolio is a curated AI portfolio built from the best SwingMax signals. ...",
    "positions": [
      {"symbol":"AAPL","name":"Apple Inc","locale":"US","asset_type":0,
       "price":230.5,"cost":220,"return":4.77,"weight":9.5,"entry_ts":1759723200}
    ],
    "industries": [
      {"industry":"Cash","weight":12.5},
      {"industry":"Software & IT Services","weight":87.5}
    ],
    "latest_trades": [
      {"symbol":"NVDA","name":"NVIDIA Corp","locale":"US","asset_type":0,
       "signal_ts":1760000000,"filled_price":180.25,
       "weight_before":9.8,"weight_after":4.9,"event":"R2","side":2}
    ]
  }
}
POST/v1/openapi/analysis/swingmax-portfolio/history
SwingmaxPortfolioHistory

Closed trades of the SwingMax Portfolio book, one row per completed round trip: entry and exit time and price, weight while held, realized return. Paged and sorted upstream, so all four sort orders are available.

No keyword on this endpoint: the upstream query has no symbol filter. To find one name, walk the pages.
A name can appear several times — each round trip is its own row, and the book re-enters names it has traded before.
Request parameters
FieldTypeRequiredDescription
pageint32no1-based. 0 or negative is treated as unset and means page 1.
sizeint32noRows per page. Default 20, maximum 20; larger values are clamped, not rejected.
sort_typeint32no0 or 1 = newest entry first, 2 = oldest entry first, 3 = highest realized_return first, 4 = lowest first. The default is newest first, not the upstream table's natural order.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxPortfolioHistoryDataResponse payload.
└listAnalysisSwingmaxPortfolioClosedTrade[]Closed trades in the requested order.
└symbolstringBare ticker, e.g. NVDA.
└namestringCompany name.
└localestringAlways US.
└asset_typeint320 = stock.
└entry_tsint64Entry time, unix seconds.
└entry_pricedoubleEntry price.
└exit_tsint64Exit time, unix seconds.
└exit_pricedoubleExit price.
└weightdoublePortfolio weight while held, in percent.
└realized_returndoubleRealized return of the trade, percent, 2 decimals.
└totalint32All closed trades, before paging.
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax-portfolio/history
{"page": 1, "size": 20, "sort_type": 3}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "list": [
      {"symbol":"TSLA","name":"Tesla Inc","locale":"US","asset_type":0,
       "entry_ts":1757376000,"entry_price":100,"exit_ts":1759104000,"exit_price":110,
       "weight":9.9,"realized_return":10}
    ],
    "total": 1
  }
}
POST/v1/openapi/analysis/swingmax-portfolio/performance
SwingmaxPortfolioPerformance

Performance of the SwingMax Portfolio book over a chosen window: the book's return and the S&P 500's over the same window, both cumulative-return curves on one time axis, and the since-inception statistics.

Long windows are downsampled so the curves stay drawable: up to ~180 trading days daily, up to ~520 weekly, beyond that monthly, and period 5 is always weekly. The last point is always kept, so the curve's end matches portfolio_return.
stats is the same since-inception block regardless of period; only portfolio_return, sp500_return and the two curves follow the window.
Returns and weights are percent numbers everywhere in the two portfolio products (2.35 = +2.35%, 10 = 10%), and prices are always double — even where the upstream service carries them as strings or fractions.
Request parameters
FieldTypeRequiredDescription
periodint32noWindow for the curves and the two window returns: 0 or 5 = everything since 2024-01-01, 1 = last 1 month, 2 = last 3 months, 3 = last 6 months, 4 = last 1 year. Any other value is InvalidArgument.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataAnalysisSwingmaxPortfolioPerformanceDataResponse payload.
└periodint32The window actually applied — 0 is echoed back as 5.
└portfolio_returndoubleReturn of the book over the window, percent: last capital in the window against the first.
└sp500_returndoubleS&P 500 (SPY close) over the same window, percent.
└statsAnalysisSwingmaxPortfolioStatsSince-inception statistics. Independent of period.
└total_returndoubleCumulative return since the book's inception, percent.
└annualized_returndoubleAnnualized return, percent.
└day1_changedoubleChange of the book over the latest trading day, percent.
└sharpe_ratiodoubleSharpe ratio. Not a percent.
└portfolio_curveAnalysisPerformancePoint[]Cumulative return of the book inside the window, rebased to 0 at the first point. Aligned to SPY's trading days; days the book has no record for carry the previous value forward.
└datestringTrading day, YYYY-MM-DD in US Eastern time.
└tsint64Unix seconds at 00:00 US Eastern on that trading day. Both curves carry date and ts so one charting routine works for every portfolio product.
└cumulative_returndoubleCumulative return since the first point of the curve, in percent (2.35 = +2.35%).
└sp500_curveAnalysisPerformancePoint[]S&P 500 cumulative return over the same days, same time axis.
└datestringTrading day, YYYY-MM-DD in US Eastern time.
└tsint64Unix seconds at 00:00 US Eastern on that trading day. Both curves carry date and ts so one charting routine works for every portfolio product.
└cumulative_returndoubleCumulative return since the first point of the curve, in percent (2.35 = +2.35%).
Example request
POST https://api.alphio.ai/v1/openapi/analysis/swingmax-portfolio/performance
{"period": 2}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "period": 2,
    "portfolio_return": 6.4,
    "sp500_return": 3.1,
    "stats": {"total_return": 40.1, "annualized_return": 20.2, "day1_change": -0.3, "sharpe_ratio": 1.4},
    "portfolio_curve": [{"date":"2026-07-15","ts":1784088000,"cumulative_return":0}, {"date":"2026-07-16","ts":1784174400,"cumulative_return":0.5}],
    "sp500_curve":     [{"date":"2026-07-15","ts":1784088000,"cumulative_return":0}, {"date":"2026-07-16","ts":1784174400,"cumulative_return":0.2}]
  }
}

Whales tracker

Endpoints tracking institutional (13F) and Congressional holdings. entity_type: 1=institution, 2=congress member. seo_name is the stable partner-facing identifier. All return/chart values are decimal fractions (0.012 = +1.2%). Rankings are cached ~10s server-side.

POST/v1/openapi/whales/top-returns
TopReturns

Institutions ranked by the chosen period's return, descending. Institutions only — for congress members use Ranking with entity_type=2.

Request parameters
FieldTypeRequiredDescription
periodstringyes"1d" / "1w" / "1m" / "3m" / "1y".
limitint32yes1 – 50.
includestringnoComma-separated: "chart" (equity curve) / "holdings" (top_holdings ≤2 tickers + holding_count) / "performance" (sharpe_ratio + mdd) / "returns" (return_1d / return_1w / return_1m / return_1y). Empty = base fields only.
keywordstringnoFuzzy entity-name filter (matches names containing the keyword). Empty = no filtering.
sortstringno"<field>:<asc|desc>", one field at a time. Fields: total_return (the requested period's return), sharpe_ratio, return_1d, return_1w, return_1m, return_1y, mdd. Empty = period return, descending. Sorting works regardless of include — include only controls which fields are returned.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesTopReturnsDataResponse payload.
└entitiesWhalesEntity[]Ranked by return_value descending by default; sort overrides the order.
└namestringEntity display name.
└actionstringCurrently always "Holding" on ranking endpoints; empty on FollowingRanking.
└return_valuedoubleReturn for the requested period, decimal fraction (0.618 = +61.8%). TopReturnsByAsset always uses the 3m return.
└top_holdingsWhalesHolding[]Largest positions, tickers only. Only populated when include contains holdings (capped at 2-3 tickers).
└tickerstringTicker symbol.
└holding_countint32Number of positions in the latest filing. 0 unless include contains holdings.
└chartWhalesChartPoint[]Equity-curve points. Only populated when include contains chart.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└avatarstringCDN avatar URL.
└entity_typeint321=institution, 2=congress.
└seo_namestringStable slug — the partner-facing identifier, feed it to DetailsBySeoName.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio. Only populated when include contains performance.
└mdddoubleMaximum drawdown, computed from the equity curve. Positive decimal fraction (0.2 = 20% drawdown). Only populated when include contains performance.
└return_1ddouble1-day return, decimal fraction (0.012 = +1.2%). Only populated when include contains returns.
└return_1wdouble1-week return, decimal fraction. Only populated when include contains returns.
└return_1mdouble1-month return, decimal fraction. Only populated when include contains returns.
└return_1ydouble1-year return, decimal fraction. Only populated when include contains returns.
POST/v1/openapi/whales/ranking
Ranking

Ranking filtered by entity_type. Query one period (period → data.entities) or up to 4 at once (periods → data.period_groups).

top_holdings is always empty on this endpoint for partner API calls.
Sending both period and periods returns an InvalidArgument error.
Request parameters
FieldTypeRequiredDescription
periodstringnoSingle-period query: "1d" / "1w" / "1m" / "3m" / "1y". Exactly one of period / periods must be set.
periodsstring[]noMulti-period query: up to 4 unique values from the same set. Response returns one period_groups entry per requested period, in request order.
limitint32yes1 – 50 (applies to each period's list).
includestringnoComma-separated: "chart" (mini equity curve per entity) / "performance" (sharpe_ratio + mdd) / "returns" (return_1d / return_1w / return_1m / return_1y).
entity_typeWhalesEntityTypeyesINSTITUTION (1) / CONGRESS (2).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesRankingDataResponse payload.
└entitiesWhalesEntity[]Populated for single-period requests.
└namestringEntity display name.
└actionstringCurrently always "Holding" on ranking endpoints; empty on FollowingRanking.
└return_valuedoubleReturn for the requested period, decimal fraction (0.618 = +61.8%). TopReturnsByAsset always uses the 3m return.
└top_holdingsWhalesHolding[]Largest positions, tickers only. Only populated when include contains holdings (capped at 2-3 tickers).
└tickerstringTicker symbol.
└holding_countint32Number of positions in the latest filing. 0 unless include contains holdings.
└chartWhalesChartPoint[]Equity-curve points. Only populated when include contains chart.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└avatarstringCDN avatar URL.
└entity_typeint321=institution, 2=congress.
└seo_namestringStable slug — the partner-facing identifier, feed it to DetailsBySeoName.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio. Only populated when include contains performance.
└mdddoubleMaximum drawdown, computed from the equity curve. Positive decimal fraction (0.2 = 20% drawdown). Only populated when include contains performance.
└return_1ddouble1-day return, decimal fraction (0.012 = +1.2%). Only populated when include contains returns.
└return_1wdouble1-week return, decimal fraction. Only populated when include contains returns.
└return_1mdouble1-month return, decimal fraction. Only populated when include contains returns.
└return_1ydouble1-year return, decimal fraction. Only populated when include contains returns.
└period_groupsWhalesRankingPeriodGroup[]Populated for multi-period requests, in the same order as req.periods.
└periodstringThe requested period this group belongs to.
└entitiesWhalesEntity[]Ranking list for this period.
└namestringEntity display name.
└actionstringCurrently always "Holding" on ranking endpoints; empty on FollowingRanking.
└return_valuedoubleReturn for the requested period, decimal fraction (0.618 = +61.8%). TopReturnsByAsset always uses the 3m return.
└top_holdingsWhalesHolding[]Largest positions, tickers only. Only populated when include contains holdings (capped at 2-3 tickers).
└tickerstringTicker symbol.
└holding_countint32Number of positions in the latest filing. 0 unless include contains holdings.
└chartWhalesChartPoint[]Equity-curve points. Only populated when include contains chart.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└avatarstringCDN avatar URL.
└entity_typeint321=institution, 2=congress.
└seo_namestringStable slug — the partner-facing identifier, feed it to DetailsBySeoName.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio. Only populated when include contains performance.
└mdddoubleMaximum drawdown, computed from the equity curve. Positive decimal fraction (0.2 = 20% drawdown). Only populated when include contains performance.
└return_1ddouble1-day return, decimal fraction (0.012 = +1.2%). Only populated when include contains returns.
└return_1wdouble1-week return, decimal fraction. Only populated when include contains returns.
└return_1mdouble1-month return, decimal fraction. Only populated when include contains returns.
└return_1ydouble1-year return, decimal fraction. Only populated when include contains returns.
POST/v1/openapi/whales/top-returns-by-asset
TopReturnsByAsset

Reverse-lookup: institutions currently holding a specific ticker, ranked by their 3-month return (descending). return_value is always the 3m return.

Request parameters
FieldTypeRequiredDescription
assetWhalesAssetyesThe ticker to look up.
└asset_typeint32no0=stock, 1=etf.
└tickerstringyesTicker symbol, e.g. "AAPL".
└localestringno"US" (default) / "HK".
includestringnoComma-separated: "chart" / "holdings" / "performance" / "returns". With holdings, the queried ticker is guaranteed to appear in top_holdings.
limitint32noDefaults to 6 when omitted.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesTopReturnsDataResponse payload.
└entitiesWhalesEntity[]Ranked by 3m return, descending.
└namestringEntity display name.
└actionstringCurrently always "Holding" on ranking endpoints; empty on FollowingRanking.
└return_valuedoubleReturn for the requested period, decimal fraction (0.618 = +61.8%). TopReturnsByAsset always uses the 3m return.
└top_holdingsWhalesHolding[]Largest positions, tickers only. Only populated when include contains holdings (capped at 2-3 tickers).
└tickerstringTicker symbol.
└holding_countint32Number of positions in the latest filing. 0 unless include contains holdings.
└chartWhalesChartPoint[]Equity-curve points. Only populated when include contains chart.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└avatarstringCDN avatar URL.
└entity_typeint321=institution, 2=congress.
└seo_namestringStable slug — the partner-facing identifier, feed it to DetailsBySeoName.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio. Only populated when include contains performance.
└mdddoubleMaximum drawdown, computed from the equity curve. Positive decimal fraction (0.2 = 20% drawdown). Only populated when include contains performance.
└return_1ddouble1-day return, decimal fraction (0.012 = +1.2%). Only populated when include contains returns.
└return_1wdouble1-week return, decimal fraction. Only populated when include contains returns.
└return_1mdouble1-month return, decimal fraction. Only populated when include contains returns.
└return_1ydouble1-year return, decimal fraction. Only populated when include contains returns.
POST/v1/openapi/whales/following/rankingX-Partner-User required
FollowingRanking

Ranks the entities the calling user follows (their watchlist) by the chosen period's return, descending. Requires X-Partner-User, like the watchlist endpoints.

A user's first-ever call auto-seeds their watchlist with the featured entities from Home.
Request parameters
FieldTypeRequiredDescription
periodstringyes"1d" / "1w" / "1m" / "3m" / "1y".
limitint32yes1 – 50. Truncates the sorted list.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesFollowingRankingDataResponse payload.
└entitiesWhalesEntity[]Only name, return_value, avatar, entity_type and seo_name are populated here — no action / top_holdings / holding_count / chart / performance metrics.
└namestringEntity display name.
└actionstringCurrently always "Holding" on ranking endpoints; empty on FollowingRanking.
└return_valuedoubleReturn for the requested period, decimal fraction (0.618 = +61.8%). TopReturnsByAsset always uses the 3m return.
└top_holdingsWhalesHolding[]Largest positions, tickers only. Only populated when include contains holdings (capped at 2-3 tickers).
└tickerstringTicker symbol.
└holding_countint32Number of positions in the latest filing. 0 unless include contains holdings.
└chartWhalesChartPoint[]Equity-curve points. Only populated when include contains chart.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└avatarstringCDN avatar URL.
└entity_typeint321=institution, 2=congress.
└seo_namestringStable slug — the partner-facing identifier, feed it to DetailsBySeoName.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio. Only populated when include contains performance.
└mdddoubleMaximum drawdown, computed from the equity curve. Positive decimal fraction (0.2 = 20% drawdown). Only populated when include contains performance.
└return_1ddouble1-day return, decimal fraction (0.012 = +1.2%). Only populated when include contains returns.
└return_1wdouble1-week return, decimal fraction. Only populated when include contains returns.
└return_1mdouble1-month return, decimal fraction. Only populated when include contains returns.
└return_1ydouble1-year return, decimal fraction. Only populated when include contains returns.
POST/v1/openapi/whales/details
Details

Full entity detail by id (from any ranking response).

Request parameters
FieldTypeRequiredDescription
idint32yes≥ 1.
includestringnoComma-separated: "chart" (equity curves), "trade_update" (recent trades + last_updated), "current_holdings" (sector distribution). Base fields and positions are always returned.
entity_typeWhalesEntityTypeyesINSTITUTION (1) / CONGRESS (2).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesEntityDetailsResponse payload.
└namestringEntity display name.
└is_watchedboolWhether the requesting user watches this entity. Always false on partner API calls — no user context is forwarded on this endpoint.
└top20_holding_mvint64Aggregate market value (USD) of the entity's 20 largest positions, per the latest filing.
└last_updatedstringDate (YYYY-MM-DD) of the most recent reported portfolio change. Only populated when include contains trade_update.
└holding_countint32Number of positions currently held.
└descriptionstringShort text description of the entity.
└performanceWhalesPerformanceTracked-portfolio performance stats. Returns are decimal fractions (0.012 = +1.2%).
└return_1ddoubleLatest 1-day return, decimal fraction.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio.
└total_returndoubleCumulative return since tracking began, decimal fraction.
└annualized_returndoubleAnnualized return, decimal fraction.
└chartsWhalesChartsEquity curves. Only populated when include contains chart.
└strategyWhalesChartPoint[]Entity portfolio equity curve.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└sp500WhalesChartPoint[]S&P 500 benchmark over the same window.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└trade_updatesWhalesTradeUpdate[]Portfolio changes from the most recent rebalance/filing. Only populated when include contains trade_update.
└datestringRebalance date, YYYY-MM-DD.
└actionstring"buy" / "sell" / "hold" — derived from the weight change (rounded to 2 decimals).
└tickerstringTicker symbol, e.g. "NVDA".
└codestringExchange-qualified code, e.g. "NVDA.O".
└namestringSecurity display name.
└asset_typeint320=stock, 1=etf.
└localestring"US" / "HK".
└old_percentdoublePortfolio weight (%) before the change.
└new_percentdoublePortfolio weight (%) after the change.
└fill_pricedoublePrice at the rebalance date.
└current_holdingsWhalesCurrentHoldingsCurrent portfolio. positions is always returned; sector_distribution requires include=current_holdings.
└sector_distributionWhalesSectorWeight[]Sector weights of the current portfolio. Only populated when include contains current_holdings.
└sectorstringSector name; "other" when unclassified.
└weightdoubleWeight of the sector, in %.
└positionsWhalesPosition[]All current positions.
└tickerstringTicker symbol, e.g. "NVDA".
└codestringExchange-qualified code, e.g. "NVDA.O".
└namestringSecurity display name.
└asset_typeint320=stock, 1=etf.
└localestring"US" / "HK".
└cost_pricedoubleAverage cost per share.
└current_percentdoubleCurrent portfolio weight (%).
└total_returndoubleCumulative return of the position since entry.
└sector_distribution_descstringServer-generated text summary of the sector mix.
└entity_typeint321=institution, 2=congress.
└institution_profileWhalesInstitutionProfileOnly set for institutions (entity_type=1).
└person_namestringFund manager / key person name.
└avatarstringCDN avatar URL.
└profilestringManager / institution bio.
└investing_philosophystringInvesting-philosophy blurb.
└congress_member_infoWhalesCongressMemberInfoOnly set for congress members (entity_type=2).
└avatarstringAvatar URL.
└partystringPolitical party, e.g. "Republican" / "Democrat".
└organizationstringChamber, e.g. "Senate" / "House".
└faqWhalesFAQInfo[]Server-generated FAQ entries derived from the stats above (holding count, returns, AUM).
└questionstring—
└answerstring—
POST/v1/openapi/whales/details-by-seo-name
DetailsBySeoName

Same as Details but keyed by stable seo_name (from any ranking / search / home response). Preferred when you cached an entity earlier.

Request parameters
FieldTypeRequiredDescription
seo_namestringyesStable entity slug, min_len 1.
includestringnoComma-separated: "chart" (equity curves), "trade_update" (recent trades + last_updated), "current_holdings" (sector distribution). Base fields and positions are always returned.
entity_typeWhalesEntityTypeyesINSTITUTION (1) / CONGRESS (2).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesEntityDetailsResponse payload.
└namestringEntity display name.
└is_watchedboolWhether the requesting user watches this entity. Always false on partner API calls — no user context is forwarded on this endpoint.
└top20_holding_mvint64Aggregate market value (USD) of the entity's 20 largest positions, per the latest filing.
└last_updatedstringDate (YYYY-MM-DD) of the most recent reported portfolio change. Only populated when include contains trade_update.
└holding_countint32Number of positions currently held.
└descriptionstringShort text description of the entity.
└performanceWhalesPerformanceTracked-portfolio performance stats. Returns are decimal fractions (0.012 = +1.2%).
└return_1ddoubleLatest 1-day return, decimal fraction.
└sharpe_ratiodoubleSharpe ratio of the tracked portfolio.
└total_returndoubleCumulative return since tracking began, decimal fraction.
└annualized_returndoubleAnnualized return, decimal fraction.
└chartsWhalesChartsEquity curves. Only populated when include contains chart.
└strategyWhalesChartPoint[]Entity portfolio equity curve.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└sp500WhalesChartPoint[]S&P 500 benchmark over the same window.
└datestringYYYY-MM-DD.
└valuedoubleCumulative return since tracking began, decimal fraction (0.618 = +61.8%).
└trade_updatesWhalesTradeUpdate[]Portfolio changes from the most recent rebalance/filing. Only populated when include contains trade_update.
└datestringRebalance date, YYYY-MM-DD.
└actionstring"buy" / "sell" / "hold" — derived from the weight change (rounded to 2 decimals).
└tickerstringTicker symbol, e.g. "NVDA".
└codestringExchange-qualified code, e.g. "NVDA.O".
└namestringSecurity display name.
└asset_typeint320=stock, 1=etf.
└localestring"US" / "HK".
└old_percentdoublePortfolio weight (%) before the change.
└new_percentdoublePortfolio weight (%) after the change.
└fill_pricedoublePrice at the rebalance date.
└current_holdingsWhalesCurrentHoldingsCurrent portfolio. positions is always returned; sector_distribution requires include=current_holdings.
└sector_distributionWhalesSectorWeight[]Sector weights of the current portfolio. Only populated when include contains current_holdings.
└sectorstringSector name; "other" when unclassified.
└weightdoubleWeight of the sector, in %.
└positionsWhalesPosition[]All current positions.
└tickerstringTicker symbol, e.g. "NVDA".
└codestringExchange-qualified code, e.g. "NVDA.O".
└namestringSecurity display name.
└asset_typeint320=stock, 1=etf.
└localestring"US" / "HK".
└cost_pricedoubleAverage cost per share.
└current_percentdoubleCurrent portfolio weight (%).
└total_returndoubleCumulative return of the position since entry.
└sector_distribution_descstringServer-generated text summary of the sector mix.
└entity_typeint321=institution, 2=congress.
└institution_profileWhalesInstitutionProfileOnly set for institutions (entity_type=1).
└person_namestringFund manager / key person name.
└avatarstringCDN avatar URL.
└profilestringManager / institution bio.
└investing_philosophystringInvesting-philosophy blurb.
└congress_member_infoWhalesCongressMemberInfoOnly set for congress members (entity_type=2).
└avatarstringAvatar URL.
└partystringPolitical party, e.g. "Republican" / "Democrat".
└organizationstringChamber, e.g. "Senate" / "House".
└faqWhalesFAQInfo[]Server-generated FAQ entries derived from the stats above (holding count, returns, AUM).
└questionstring—
└answerstring—
POST/v1/openapi/whales/home
Home

Static editorial list of featured entities (ARK, Nancy Pelosi, Berkshire Hathaway, Soros, Renaissance, Pershing Square). Empty body.

Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesHomeDataResponse payload.
└itemsWhalesHomeInfo[]Curated, fixed order — not a ranking.
└namestringEntity display name.
└coverstringCDN cover-image URL for the home card.
└labelstringEditorial tag; currently always "Featured".
└entity_typeint321=institution, 2=congress.
└seo_namestringFeed to DetailsBySeoName.
POST/v1/openapi/whales/faq
FAQ

Static product-level FAQ about the Whales tracker, localized via Accept-Language (English default, Chinese supported). Not entity-specific — per-entity FAQ comes in Details.faq. Empty body.

Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesFAQDataResponse payload.
└itemsWhalesFAQInfo[]Fixed question/answer list.
└questionstring—
└answerstring—
POST/v1/openapi/whales/watchlist/listX-Partner-User required
WatchlistList

List entities the user follows, with their returns across all periods. Requires X-Partner-User.

A user's first-ever call auto-seeds their watchlist with the featured entities from Home. If they later unfollow everything, the list stays empty.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataWhalesWatchlistListDataResponse payload.
└itemsWhalesWatchlistItem[]One row per followed entity.
└namestringEntity display name.
└entity_typeint321=institution, 2=congress.
└return_1ddouble1-day return, decimal fraction (0.012 = +1.2%).
└return_7ddouble7-day return, decimal fraction.
└return_1mdouble1-month return, decimal fraction.
└return_3mdouble3-month return, decimal fraction.
└return_1ydouble1-year return, decimal fraction.
└avatarstringCDN avatar URL.
└seo_namestringStable slug for DetailsBySeoName.
POST/v1/openapi/whales/watchlist/createX-Partner-User required
WatchlistCreate

Add an entity to the calling user's watchlist. Requires X-Partner-User.

Request parameters
FieldTypeRequiredDescription
idint32yesInternal numeric entity id, ≥ 1.
entity_typeWhalesEntityTypeyesINSTITUTION (1) / CONGRESS (2).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success.
msgstringStatus / error message.
POST/v1/openapi/whales/watchlist/deleteX-Partner-User required
WatchlistDelete

Remove an entity from the calling user's watchlist. Requires X-Partner-User.

Request parameters
FieldTypeRequiredDescription
idint32yesInternal numeric entity id, ≥ 1.
entity_typeWhalesEntityTypeyesINSTITUTION (1) / CONGRESS (2).
Response fields
FieldTypeDescription
retint32Business return code. 0 = success.
msgstringStatus / error message.

Market data

Raw market data endpoints powering Chat card details. Keyed by symbol; partner UIs typically fetch these on-demand when expanding a card.

POST/v1/openapi/data/valuation
DataValuation

Historical valuation time-series.

Request parameters
FieldTypeRequiredDescription
tickerstringyese.g. "AAPL".
valuation_typeint32yesEValuationType. 0=PE / 1=PB / 2=PS / 3=Price-to-FCF / 4=EV-EBITDA / 5=Price-to-OCF / 6=FCF-Yield / 7=EV-EBIT.
is_forwardboolnotrue=forward, false=trailing (default).
fromstringnoYYYY-MM-DD. Default last 1Y.
tostringnoYYYY-MM-DD. Default today.
localestringno"US" / "HK".
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDataValuationDataResponse payload.
└valuationDataValuationItem[]Time series.
└tstringDate (YYYY-MM-DD).
└vstringValuation value.
└variance_infoDataVarianceInfoFive reference bands.
└meanstring—
└variancestring—
└overvaluedstringmean + 1σ.
└undervaluedstringmean − 1σ.
└strongly_overvaluedstringmean + 2σ.
└strongly_undervaluedstringmean − 2σ.
Example request
POST https://api.alphio.ai/v1/openapi/data/valuation
{ "ticker":"AAPL", "valuation_type":0, "from":"2025-06-01", "to":"2026-06-01", "locale":"US" }
Example response
{
  "ret": 0, "msg": "ok",
  "data": {
    "valuation":[{"t":"2025-06-01","v":"28.5"},{"t":"2025-06-02","v":"28.7"}, ...],
    "variance_info":{
      "mean":"30.2","variance":"3.1",
      "overvalued":"33.3","undervalued":"27.1",
      "strongly_overvalued":"36.4","strongly_undervalued":"24.0"
    }
  }
}
POST/v1/openapi/data/rating
DataRating

Analyst ratings.

Request parameters
FieldTypeRequiredDescription
tickerstringyese.g. "AAPL".
fromstringnoYYYY-MM-DD.
tostringnoYYYY-MM-DD.
pageint32no1-indexed.
sizeint32no1 – 100, default 20.
localestringno"US" / "HK".
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDataRatingDataResponse payload.
└resultsDataStockAnalysis—
└tickerstring—
└codestring—
└analysesDataAnalysisItem[]—
└rating_analyststring—
└rating_datestring—
└rating_actionstring"upgrade" / "downgrade" / "maintain" / "initiate".
└rating_analyst_starsdouble—
└rating_analyst_firmstring—
└rating_price_targetstring—
└ratingstringe.g. "buy" / "hold".
└rating_upsidedouble—
└analysis_summaryDataAnalysisSummaryAggregate snapshot.
└codestring—
└symbolstring—
└average_price_targetstring—
└buyint32Count of buy ratings.
└contentstring[]Highlight bullets.
└highest_price_targetstring—
└holdint32Count of hold ratings.
└lowest_price_targetstring—
└paginationDataPagination—
└current_pageint32—
└total_pagesint32—
└total_itemsint32—
POST/v1/openapi/data/snapshot
DataSnapshot

Financial-metric snapshot (per-quarter or annual).

Request parameters
FieldTypeRequiredDescription
codestringyes—
typeint32yesEFinancialIndicatorsType. 1=EPS / 2=Revenue / 3=ROE / 4=Gross Margin / 5=Net Margin / 6=Current Ratio / 7=Debt to Equity / 8=Operating Margin.
report_typeint32yesFinanceReportType. 1=annual / 2=quarterly.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDataSnapshotDataResponse payload.
└columnsDataSnapshotColumn[]Each column = one metric series.
└namestring—
└valuesDataSnapshotValue[]One cell per period.
└titlestringe.g. "Q1 2026".
└valuedouble—
└cur_typestringCurrency / unit.
└validboolfalse → missing data; render as "—".
POST/v1/openapi/data/kline
DataKline

OHLCV bars.

Request parameters
FieldTypeRequiredDescription
tickersWhalesAsset[]yesMax 20 per request.
└asset_typeint32noSymbolType: 0=stock, 1=etf, 2=crypto, 3=commodity, 4=forex, 5=perp.
└tickerstringnoe.g. "TSLA".
└localestringno"US" / "HK".
fromstringnoYYYY-MM-DD (US-Eastern) or unix seconds.
tostringnoYYYY-MM-DD (US-Eastern) or unix seconds.
multiplierint32noe.g. 1, 5, 15.
timespanint32noTimespanOpt. 2=minute, 3=hour, 4=day, 5=week, 6=month, 7=quarter, 8=year. Values outside 2–8 are rejected.
limitint32noMax 1000. Default 500.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDataKlineDataResponse payload.
└seriesDataKlineSeries[]One series per requested ticker.
└tickerstring—
└asset_typeint32—
└pointsDataKlinePoint[]OHLCV bars. Numbers are strings to avoid precision loss.
└openstring—
└highstring—
└lowstring—
└closestring—
└volumestringTraded value (turnover) for the bar — per proto this is 成交额, not share count.
└timestampint64Unix seconds.
Example request
POST https://api.alphio.ai/v1/openapi/data/kline
{
  "tickers":[ { "asset_type":0, "ticker":"TSLA", "locale":"US" } ],
  "from":"2026-06-09", "to":"2026-07-09",
  "multiplier":1, "timespan":4, "limit":30
}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "series": [
      {
        "ticker": "TSLA",
        "asset_type": 0,
        "points": [
          {"open":"391.09","high":"396.14","low":"388.42","close":"394.26",
           "volume":"58231290","timestamp":"1783486800"}
        ]
      }
    ]
  }
}
POST/v1/openapi/data/price-prediction
DataPricePrediction

Historical OHLC bars backing the price-prediction card — no forecast fields in the payload itself. Request is a discriminated union by ticker_type; response has matching stock_data / crypto_data.

Request parameters
FieldTypeRequiredDescription
ticker_typestringyes"stock" / "etf" / "" → stock branch; "crypto" → crypto branch.
stockDataPricePredictionStockReqnoRequired when ticker_type is stock / etf / empty.
└codestringnoTicker, e.g. "AAPL".
└multiplierint32noTime multiplier.
└timespanint32noTimespanOpt. 2=minute, 3=hour, 4=day, 5=week, 6=month, 7=quarter, 8=year.
└timestampint64noAnchor end-time (unix seconds). Empty = now.
└countint32noBars to return. Max 5000, recommended 800.
└localestringno"US" / "HK".
cryptoDataPricePredictionCryptoReqnoRequired when ticker_type = "crypto".
└typeint32noQueryType. 1=crypto spot, 2=etf, 3=crypto derivative (HL perps).
└symbolstringno—
└multiplierint32no—
└timespanint32noTimespanOpt. 2=minute, 3=hour, 4=day, 5=week, 6=month, 7=quarter, 8=year.
└limitint32no—
└start_timeint64noUnix seconds.
└end_timeint64noUnix seconds.
└symbol_liststring[]noBatch query — per-symbol series come back in crypto_data.infos.
└localestringnoSource platform for multi-source assets, e.g. "HYPERLIQUID". Empty = default source.
Response fields
FieldTypeDescription
retint32Business return code. 0 = success, non-zero = error code (see Errors).
msgstringHuman-readable status / error message. "ok" on success.
dataDataPricePredictionDataResponse payload.
└ticker_typestringEchoed back.
└stock_dataDataPricePredictionStockDataPresent on the stock branch.
└pointsDataPricePredictionStockPoint[]Historical bars, oldest first.
└codestring—
└closedouble—
└highdouble—
└lowdouble—
└transactionsint64Share volume (per proto: 交易量).
└opendouble—
└timestampint64Unix seconds.
└volumedoubleTraded value / turnover (per proto: 成交额), not share count.
└vwapdoubleVolume-weighted average price.
└otcbool—
└pre_closedoublePrevious-period values (pre_* mirror the base fields).
└pre_highdouble—
└pre_lowdouble—
└pre_transactionsint64—
└pre_opendouble—
└pre_timestampint64—
└pre_volumedouble—
└pre_vwapdouble—
└symbolstring—
└last_timeint64Unix seconds.
└crypto_dataDataPricePredictionCryptoDataPresent on the crypto branch.
└pointsDataPricePredictionCryptoPoint[]Bars for the primary symbol.
└highdouble—
└opendouble—
└lowdouble—
└closedouble—
└timestampint64Unix seconds.
└volumeint64Legacy integer volume — kept for old clients only.
└volume_decimaldoublePreferred: float volume (needed for fractional assets like HL perps).
└infosmap<string, DataPricePredictionCryptoSeries>Per-symbol series when symbol_list was provided.
Example request
POST https://api.alphio.ai/v1/openapi/data/price-prediction
{
  "ticker_type":"stock",
  "stock":{ "code":"AAPL", "multiplier":1, "timespan":4, "count":800, "locale":"US" }
}
Example response
{
  "ret": 0,
  "msg": "ok",
  "data": {
    "ticker_type": "stock",
    "stock_data": {
      "points": [
        {"code":"AAPL","close":195.5,"open":194.2,"high":196.1,"low":193.8,
         "timestamp":1717286400,"volume":48230000.0,"vwap":195.1,"otc":false}
      ],
      "last_time": 1717286400
    }
  }
}
POST/v1/openapi/data/screener/stock
DataScreenerStock

Stock screener. Request / response follow the upstream datafabric schema (chatfinbot.datafabric.v1.SelectStocksReq / SelectStocksRsp). Contact us for the full schema, or reuse a screenId returned by a Chat card.

POST/v1/openapi/data/screener/etf
DataScreenerETF

ETF screener. Uses chatfinbot.datafabric.v1.SelectEtfsReq / SelectEtfsRsp upstream.

POST/v1/openapi/data/screener/crypto
DataScreenerCrypto

Crypto screener. Uses chatfinbot.datafabric.v1.SelectCryptosReq / SelectCryptosRsp upstream.