Skip to content

Add market metadata, chart quotes, and account graph APIs#6

Open
snacsnoc wants to merge 2 commits into
henryhuangh:mainfrom
snacsnoc:add-market-chart-apis
Open

Add market metadata, chart quotes, and account graph APIs#6
snacsnoc wants to merge 2 commits into
henryhuangh:mainfrom
snacsnoc:add-market-chart-apis

Conversation

@snacsnoc
Copy link
Copy Markdown

@snacsnoc snacsnoc commented May 6, 2026

This PR adds a few helpful of Wealthsimple calls I’ve been using in a side project for the last couple months, pulled from browser network traffic and wired into the main client. The helper methods were needed to make them usable from normal ticker input. The main value here is that it fills in a few obvious gaps on the read side without changing auth or order flow.

New functions:

  • get_markets_metadata()
    Exchange-level metadata like open/close times and MIC codes
  • get_market_buffer(country, is_option)
    Returns the market buffer multiplier Wealthsimple uses
  • get_security_status(security_id)
    Returns the current trading status for a security
  • get_intraday_chart_quotes(...)
    Returns Wealthsimple-native chart points with timestamps
  • get_account_current_financials(account_id)
    Returns current account-level liquidation value, deposits, withdrawals, and returns.
  • get_account_graph_data(...)
    Returns the account graph payload used for charting.
  • parse_ticker_market(), _is_us_exchange(), _is_canadian_exchange(), resolve_security_id()
    Small glue helpers for inputs like TSLA.US and WCP.TO
    Parsing the ticker market and market checking were specific to my needs, so not sure if they would be helpful to others.

Overview:

get_intraday_chart_quotes() exposes Wealthsimple's own chart data instead of forcing callers to infer timing from quote snapshots or rely entirely on external feeds.

get_account_graph_data() and get_account_current_financials() do the same thing on the account side. Before this, the client had balances and historical financials, but not the broker native graph payload or a clean current financials read for a single account.

get_security_status() gives an explicit status check instead of guessing from quote data.

The ticker helpers are boring but necessary. If the caller has TICKER.MARKET input, they should not have to reimplement symbol parsing and exchange filtering outside the client.

The return values are plain dict/list/scalar values. resolve_security_id() caches hits after the first lookup, and saves an API call (note: spamming the WealthSimple API endpoint with a request every 30 seconds will give you 429s...) I left __typename in the payloads, stripping it would just add cleanup code for no real gain.

README was updated with the new calls.

Example shapes

Since this PR is adding several new features, I've added below some example outputs from the functions just as a reference.

ws.get_markets_metadata()

[
  {
    "__typename": "MarketMetadata",
    "cacheDate": "2026-03-13",
    "close": "16:00:00",
    "date": "2026-03-13",
    "exchangeName": "NASDAQ",
    "lastOpenDate": "2026-03-12",
    "lastOpenTime": "09:30:00",
    "mic": "XNAS",
    "nextOpenDate": "2026-03-16",
    "nextOpenTime": "09:30:00",
    "open": "09:30:00"
  }
]

ws.get_market_buffer(country="CA")

1.006

ws.get_security_status("sec-s-6e73535b8e474d8689064c4c9fee326a")

{
  "__typename": "Security",
  "id": "sec-s-6e73535b8e474d8689064c4c9fee326a",
  "optionDetails": None,
  "status": "TRADING"
}

ws.get_intraday_chart_quotes(...)

[
  {
    "__typename": "ChartBarQuote",
    "currency": "CAD",
    "marketStatus": "OPEN",
    "price": "17.1000",
    "securityId": "sec-s-6e73535b8e474d8689064c4c9fee326a",
    "sessionPrice": "17.1000",
    "timestamp": "2026-03-13T13:30:00Z"
  }
]

ws.get_account_current_financials("my-wealthsimple-account-id")

{
  "__typename": "AccountCurrentFinancials",
  "id": "my-wealthsimple-account-id-CAD",
  "netLiquidationValueV2": {
    "__typename": "Money",
    "amount": "580.08505",
    "cents": 58009,
    "currency": "CAD"
  },
  "netDeposits": {
    "__typename": "Money",
    "amount": "954.22",
    "cents": 95422,
    "currency": "CAD"
  },
  "simpleReturns": {
    "__typename": "SimpleReturns",
    "amount": {
      "__typename": "Money",
      "amount": "0.00765",
      "cents": 1,
      "currency": "CAD"
    },
    "asOf": None,
    "rate": "0.000013",
    "referenceDate": "2026-03-09"
  }
}

ws.get_account_graph_data("my-wealthsimple-account-id")

{
  "previousClose": {
    "__typename": "HistoricalGraphDataPoint",
    "dateTime": "2026-03-09T20:00:00.000Z",
    "netLiquidationValue": {
      "__typename": "Money",
      "amount": "580.0774",
      "currency": "CAD"
    }
  },
  "data": [
    {
      "__typename": "HistoricalGraphDataPoint",
      "dateTime": "2026-03-10T13:30:00.000Z",
      "netDeposits": {
        "__typename": "Money",
        "amount": "954.22",
        "currency": "CAD"
      },
      "netLiquidationValue": {
        "__typename": "Money",
        "amount": "580.271",
        "currency": "CAD"
      }
    }
  ]
}

ws.resolve_security_id("WCP.TO")

"sec-s-ab88ccb65fe24dcc991215e07eea2e41"

ws.parse_ticker_market("TSLA.US")

("TSLA", "US")

ws._is_us_exchange("NASDAQ")

True

ws._is_canadian_exchange("TSX")

True

@henryhuangh
Copy link
Copy Markdown
Owner

Hi @snacsnoc , thank you for your detailed PR! I went through the changes, everything looks good. However, ws.get_markets_metadata() returned UNPROCESSABLE_ENTITY, could you let me know how you were able to get the network traffic for that request?

@snacsnoc
Copy link
Copy Markdown
Author

snacsnoc commented May 8, 2026

Hi @snacsnoc , thank you for your detailed PR! I went through the changes, everything looks good. However, ws.get_markets_metadata() returned UNPROCESSABLE_ENTITY, could you let me know how you were able to get the network traffic for that request?

No problem.

I got marketsMetadata from Wealthsimple's browser GraphQL traffic. The query in the PR matches what I captured
from devtools:

query FetchMarketsMetadata {
  marketsMetadata {
    cacheDate
    close
    date
    exchangeName
    lastOpenDate
    lastOpenTime
    mic
    nextOpenDate
    nextOpenTime
    open
    __typename
  }
}

One possible explanation for the UNPROCESSABLE_ENTITY is request context. In the original environment I was using, this call went out with more browser style headers (Origin, Referer, X-WS-Session-ID, X-WS-Device-ID, etc). In the PR I kept the query but dropped that browser/session/device plumbing. I was hitting the Wealthsimple API too hard and got 429s, figured it would be "safe" to add the browser-like headers. I ported my changes from a fork of wealthsimple_v2.py I wrote with the addition of MCP capabilities, back into the above PR to keep it inline with the original.

So if I had to guess the issue is not the query text itself, but that marketsMetadata may depend on a more browser like request shapes than the other calls. I can re-check that one specifically later this weekend and either update it or remove it if it is not reliable in the cleaned up client.

@snacsnoc
Copy link
Copy Markdown
Author

snacsnoc commented May 9, 2026

I dug into it more and found the likely replacement.

The old marketsMetadata shape I captured in March was flattened market hours metadata, but I
can no longer reproduce theFetchMarketsMetadata query nor even find it being called anymore.

I poked around and what does exist right now is this query:

query FetchNearestMarketOpen {
  nearestMarketOpen {
    currentTradeDay {
      date
      overnight { start end __typename }
      preMarket { start end __typename }
      regular { start end __typename }
      postMarket { start end __typename }
      __typename
    }
    exchangeName
    isTodayEarlyClose
    mic
    nextTradeDay {
      date
      overnight { start end __typename }
      preMarket { start end __typename }
      regular { start end __typename }
      postMarket { start end __typename }
      __typename
    }
    previousTradeDay {
      date
      overnight { start end __typename }
      preMarket { start end __typename }
      regular { start end __typename }
      postMarket { start end __typename }
      __typename
    }
    __typename
  }
}

Output:

{
  "data": {
    "nearestMarketOpen": {
      "currentTradeDay": null,
      "exchangeName": "NASDAQ - ALL MARKETS",
      "isTodayEarlyClose": false,
      "mic": "XNAS",
      "nextTradeDay": {
        "date": "2026-05-11",
        "overnight": [
          {
            "start": "2026-05-11T00:00:00.000Z",
            "end": "2026-05-11T08:00:00.000Z",
            "__typename": "TradingSessionBoundaries"
          }
        ],
        "preMarket": [
          {
            "start": "2026-05-11T08:00:00.000Z",
            "end": "2026-05-11T13:30:00.000Z",
            "__typename": "TradingSessionBoundaries"
          <snipped>

which was very very similar to the response I had before with FetchMarketsMetadata:

{
   "data":{
      "marketsMetadata":[
         {
            "cacheDate":"2026-03-13",
            "close":"16:00:00",
            "date":"2026-03-13",
            "exchangeName":"NASDAQ",
            "lastOpenDate":"2026-03-12",
            "lastOpenTime":"09:30:00",
            "mic":"XNAS",
            "nextOpenDate":"2026-03-16",
            "nextOpenTime":"09:30:00",
            "open":"09:30:00",
            "__typename":"MarketMetadata"
         },
         {
            "cacheDate":"2026-03-13",
            "close":"16:00:00",
            "date":"2026-03-13",
            "exchangeName":"NYSE",
            "lastOpenDate":"2026-03-12",
            "lastOpenTime":"09:30:00",
            "mic":"XNYS",
            "nextOpenDate":"2026-03-16",
            "nextOpenTime":"09:30:00",
            "open":"09:30:00",
            "\"__typ"
<snipped>

This one works today and returns the same underlying pieces I was using before: exchange name, MIC, previous trade day, and next trade day with session date + time. So I think what happened is the old MarketMetadata shape did exist at the time (last response I have saved is from March 13) and the current live path is probably nearestMarketOpen based on extracting the GraphQL calls in their JS source.

I’m going to push a follow-up commit to this PR that fixes this issue, thanks again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants