Drift
Both SDKs accept a native date/time object for the start / end bounds of fetchOHLCV and stringify it before putting it on the wire, but they emit different formats:
- TypeScript calls
Date.prototype.toISOString(), which always normalizes to UTC and appends a Z designator.
- Python calls
datetime.isoformat(), which emits no timezone designator at all for a naive datetime (the common case), and a +HH:MM offset for an aware one.
The same wall-clock value therefore reaches the sidecar as two different instants depending on which SDK the caller used.
TypeScript SDK
sdks/typescript/pmxt/client.ts:1991-2003:
const paramsDict: any = { resolution: params.resolution };
if (params.start) {
paramsDict.start = params.start.toISOString(); // <-- line 1993
}
if (params.end) {
paramsDict.end = params.end.toISOString(); // <-- line 1996
}
if (params.limit) {
paramsDict.limit = params.limit;
}
const args = [resolvedOutcomeId, paramsDict];
const query = { outcomeId: resolvedOutcomeId, ...paramsDict };
new Date(2025, 0, 1).toISOString() → "2025-01-01T00:00:00.000Z" (always UTC, always Z, always milliseconds).
Python SDK
sdks/python/pmxt/client.py:2371-2387:
params_dict = {}
if resolution:
params_dict["resolution"] = resolution
if start:
params_dict["start"] = start.isoformat() # <-- line 2375
if end:
params_dict["end"] = end.isoformat() # <-- line 2377
if limit:
params_dict["limit"] = limit
...
args = [outcome_id, params_dict]
query = {"outcomeId": outcome_id, **params_dict}
datetime(2025, 1, 1).isoformat() → "2025-01-01T00:00:00" — no Z, no offset, no milliseconds.
datetime(2025, 1, 1, tzinfo=timezone.utc).isoformat() → "2025-01-01T00:00:00+00:00" — offset form, still not Z.
The start/end parameter is documented as start: Start datetime for historical data (client.py:2352), and every example in the repo passes a naive datetime, so the no-designator form is the realistic path.
Expected
TypeScript is right: the wire value should be an unambiguous, UTC-normalized ISO-8601 timestamp. Python should convert to UTC and emit a Z-suffixed string, e.g.
start.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
(with a documented assumption — or an explicit timezone.utc attachment — for naive inputs), so both SDKs put the same instant on the wire for the same wall-clock input.
Impact
A caller in a non-UTC timezone gets a different candle range from each SDK for identical code. exchange.fetch_ohlcv(outcome, resolution="1h", start=datetime(2025, 1, 1)) in Python sends 2025-01-01T00:00:00, which the sidecar must guess at — typically interpreting it as UTC — whereas the TypeScript caller's new Date(2025, 0, 1) is converted from local time first and lands on a genuinely different instant (up to ±14 hours away). Neither side errors; the results just silently disagree, which is the worst failure mode for a historical-data API where users cross-check the two SDKs against each other. The aware-datetime case is milder but still emits a format (+00:00) that TypeScript never produces, so any server-side or downstream string comparison on the timestamp diverges too.
Found by automated SDK cross-language drift audit
Drift
Both SDKs accept a native date/time object for the
start/endbounds offetchOHLCVand stringify it before putting it on the wire, but they emit different formats:Date.prototype.toISOString(), which always normalizes to UTC and appends aZdesignator.datetime.isoformat(), which emits no timezone designator at all for a naivedatetime(the common case), and a+HH:MMoffset for an aware one.The same wall-clock value therefore reaches the sidecar as two different instants depending on which SDK the caller used.
TypeScript SDK
sdks/typescript/pmxt/client.ts:1991-2003:new Date(2025, 0, 1).toISOString()→"2025-01-01T00:00:00.000Z"(always UTC, alwaysZ, always milliseconds).Python SDK
sdks/python/pmxt/client.py:2371-2387:datetime(2025, 1, 1).isoformat()→"2025-01-01T00:00:00"— noZ, no offset, no milliseconds.datetime(2025, 1, 1, tzinfo=timezone.utc).isoformat()→"2025-01-01T00:00:00+00:00"— offset form, still notZ.The
start/endparameter is documented asstart: Start datetime for historical data(client.py:2352), and every example in the repo passes a naivedatetime, so the no-designator form is the realistic path.Expected
TypeScript is right: the wire value should be an unambiguous, UTC-normalized ISO-8601 timestamp. Python should convert to UTC and emit a
Z-suffixed string, e.g.(with a documented assumption — or an explicit
timezone.utcattachment — for naive inputs), so both SDKs put the same instant on the wire for the same wall-clock input.Impact
A caller in a non-UTC timezone gets a different candle range from each SDK for identical code.
exchange.fetch_ohlcv(outcome, resolution="1h", start=datetime(2025, 1, 1))in Python sends2025-01-01T00:00:00, which the sidecar must guess at — typically interpreting it as UTC — whereas the TypeScript caller'snew Date(2025, 0, 1)is converted from local time first and lands on a genuinely different instant (up to ±14 hours away). Neither side errors; the results just silently disagree, which is the worst failure mode for a historical-data API where users cross-check the two SDKs against each other. The aware-datetime case is milder but still emits a format (+00:00) that TypeScript never produces, so any server-side or downstream string comparison on the timestamp diverges too.Found by automated SDK cross-language drift audit