Skip to content

Options expirations dates are one day before in SDK vs raw API #8

@MarketDataApp

Description

@MarketDataApp

Summary

client.options.expirations(symbol) returns expiration dates that are one day earlier than the raw API response. The API returns calendar dates like 2026-02-20; the SDK shows them as 2026-02-19 19:00:00-05:00 (previous calendar day in US/Eastern).

Steps to reproduce

  1. Raw API (curl):

    curl -s -H "Authorization: Bearer $MARKETDATA_TOKEN" \
      -H "Accept: application/json" \
      "https://api.marketdata.app/v1/options/expirations/AAPL/"
  2. SDK:

    from marketdata.client import MarketDataClient
    client = MarketDataClient()
    df = client.options.expirations("AAPL")
    print(df)

Expected vs actual

Raw API response

{
  "s": "ok",
  "expirations": [
    "2026-01-23",
    "2026-01-30",
    "2026-02-06",
    "2026-02-13",
    "2026-02-20",
    "2026-02-27",
    ...
  ],
  "updated": 1769193350
}

SDK output (DataFrame)

                                            updated
expirations
2026-01-22 19:00:00-05:00 2026-01-23 13:35:51-05:00
2026-01-29 19:00:00-05:00 2026-01-23 13:35:51-05:00
2026-02-05 19:00:00-05:00 2026-01-23 13:35:51-05:00
2026-02-12 19:00:00-05:00 2026-01-23 13:35:51-05:00
2026-02-19 19:00:00-05:00 2026-01-23 13:35:51-05:00   ← API has "2026-02-20"
...

Comparison (first 5)

# API (raw) SDK (date part)
1 2026-01-23 2026-01-22
2 2026-01-30 2026-01-29
3 2026-02-06 2026-02-05
4 2026-02-13 2026-02-12
5 2026-02-20 2026-02-19

Expected: SDK expiration dates should match the API's calendar dates (e.g. 2026-02-20).

Actual: SDK shows the previous calendar day for every expiration (e.g. 2026-02-19 19:00:00-05:00).

Root cause

The API returns date-only strings (YYYY-MM-DD) in expirations. The SDK's PandasOutputHandler._convert_timestamp_columns treats them as timestamps and does:

pd.to_datetime(df[col], utc=True).dt.tz_convert(default_tz)  # default_tz = US/Eastern

For "2026-02-20":

  1. pd.to_datetime("2026-02-20", utc=True)2026-02-20 00:00:00+00:00 (midnight UTC).
  2. .dt.tz_convert("US/Eastern")2026-02-19 19:00:00-05:00 (previous calendar day in Eastern).

So date-only values are interpreted as midnight UTC and then converted to US/Eastern, which shifts the calendar date backward by one day in US time zones.

Suggested fix

For date-only strings (e.g. YYYY-MM-DD with no time), avoid "parse as UTC midnight then convert to local":

  • Option A: Parse as a date and keep the calendar day only (no time / timezone), or
  • Option B: Parse as midnight in the target timezone (e.g. US/Eastern) so the calendar date matches the API.

Relevant code:

  • src/marketdata/output_handlers/pandas.py: _convert_timestamp_columns (and equivalent in polars if used for this response).
  • src/marketdata/output_types/options_expirations.py: format_timestamp is used for OutputFormat.INTERNAL; datetime.fromisoformat("2026-02-20") returns a naive datetime at midnight. If that is later localized with UTC and converted to Eastern, the same off-by-one can occur.

Detection: when the string matches YYYY-MM-DD (and has no time part), use date-only or "midnight in target zone" logic instead of utc=True + tz_convert.

Environment

  • SDK: marketdata-sdk-py 1.1.0 (from https://github.com/MarketDataApp/sdk-py.git)
  • Python: 3.13
  • pandas: 3.0.0
  • OS: macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions