Obscura

Forex Positions

Retail forex positioning by pair (share of traders long versus short), collected from several broker sentiment feeds.

forex_positions — the dataset name to pass to the Obscura API.

Category Markets Columns 13 Refresh Every 30 minutes Entity key pair Point-in-time available_date

What one row means

One retail-forex positioning snapshot for one pair from one broker/vendor source at one point in time: percent long/short by account count, account counts and USD-notional volumes on each side, and (where exposed) a reference price. Unified across sources (Dukascopy SWFX, AMarkets, OANDA position book, Myfxbook, IG); history accretes forward only (no source offers a deep archive).

One row per (source, pair, event_time).

Point-in-time availability

Every Obscura dataset carries available_date: the calendar day the publisher made the row available, day-of, with no session rounding. It is the one column a backtest filters on, and it means the same thing on every dataset in the catalog.

For forex_positions: snapshot: available_date = captured_date, a STORED GENERATED column (migration 000205). WHY THAT IS THE DAY IT BECAME AVAILABLE: not one of the five positioning sources offers an archive endpoint, so a past book cannot be fetched and this history exists only because a 30-minute poll captured it — the day it was captured is the day it was obtainable, and there is no vendor dissemination event for it to lag. captured_date replaced a generated `(event_time AT TIME ZONE 'UTC')::date`: substantively identical (event_time is the clamped observation instant) but a poll instant cannot NAME itself the capture day. Existing rows were backfilled from exactly that expression, so no stored value moved.

Refresh cadence

Obscura refreshes forex_positions every 30 minutes — the most frequent scheduled job that re-collects or re-exports it. This is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when a row became public is recorded per row in available_date.

Schema — 13 columns

The full public column list for forex_positions, with the meaning of every field. The same schema is served unauthenticated at https://api.obscura.trade/v1/catalog/forex_positions.

ColumnTypeDescription
sourcetext · not nullWhich collector/vendor produced this snapshot: dukascopy | amarkets | oanda | myfxbook | ig. Part of the primary key.
pairtext · not nullNormalized instrument symbol (EURUSD, XAUUSD, BTCUSD, ...); separators stripped, upper-cased. Not a resolved equity ticker. Part of the primary key.
event_timetimestamp with time zone · not nullSnapshot instant: source-reported timestamp where available (dukascopy epoch-ms, oanda RFC3339, amarkets widget Timestamp), else Utc::now() at scrape (myfxbook, ig). CLAMPED TO THE OBSERVATION INSTANT: the amarkets widget stamps its broker-local wall clock and labels it UTC, running ~2h ahead, so an unclamped evening snapshot took tomorrow's available_date; a source timestamp later than the moment we read the response is impossible and is pulled back to it. Part of the primary key; available_date derives from it.
captured_datedate · not nullThe UTC calendar day the collector POLLED this source's positioning book — the date-part of the clamped observation instant on a live run, the bronze object's capture day on a replay. available_date is generated directly from it.
available_datedatePUBLIC-availability date = captured_date. DB-generated (STORED), read-only; the point-in-time column to filter/join on. No source offers a positioning archive, so the day we polled is the only day this book was obtainable.
long_pctdouble precisionPercent (0-100) of the source's retail book positioned long, by account count; complement of short_pct.
short_pctdouble precisionPercent (0-100) positioned short, by account count; for amarkets computed as 100 - long_pct.
long_volumedouble precisionNotional volume on the long side in the source's own units (amarkets: USD). Not comparable across sources.
short_volumedouble precisionNotional volume on the short side in the source's own units (amarkets: USD).
long_positionsbigintCount of open long positions/accounts at this source and snapshot (amarkets: Buyers).
short_positionsbigintCount of open short positions/accounts at this source and snapshot (amarkets: Sellers).
pricedouble precisionReference/mid price at snapshot where the source exposes one (e.g. oanda order-book bucket price); NULL for sources like amarkets that report positioning only.
rawjsonbSource's raw per-pair payload verbatim, for fields not mapped onto typed columns (e.g. amarkets SentimentByVolumes).

Access forex_positions

Two delivery paths, one identifier. Both require an Obscura account and an active subscription; the catalog entry and the schema above are public.

import obscura

client = obscura.Client("obs_live_…")

df = client.query(
    dataset="forex_positions",
    symbols=["NVDA", "AAPL"],
    start="2024-01-01",
)

Create a free account Browse all 95 datasets

Frequently asked questions

What is in the forex_positions dataset?

Retail forex positioning by pair (share of traders long versus short), collected from several broker sentiment feeds. One retail-forex positioning snapshot for one pair from one broker/vendor source at one point in time: percent long/short by account count, account counts and USD-notional volumes on each side, and (where exposed) a reference price. Unified across sources (Dukascopy SWFX, AMarkets, OANDA position book, Myfxbook, IG); history accretes forward only (no source offers a deep archive).

How do I avoid look-ahead bias with forex_positions?

Filter on forex_positions.available_date, the day the publisher made the row public. For this dataset that date is derived as follows — snapshot: available_date = captured_date, a STORED GENERATED column (migration 000205). WHY THAT IS THE DAY IT BECAME AVAILABLE: not one of the five positioning sources offers an archive endpoint, so a past book cannot be fetched and this history exists only because a 30-minute poll captured it — the day it was captured is the day it was obtainable, and there is no vendor dissemination event for it to lag. captured_date replaced a generated `(event_time AT TIME ZONE 'UTC')::date`: substantively identical (event_time is the clamped observation instant) but a poll instant cannot NAME itself the capture day. Existing rows were backfilled from exactly that expression, so no stored value moved. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.

In what formats can I get forex_positions?

As a Parquet bulk export (POST https://api.obscura.trade/v1/download) or as JSON from the typed query API (POST https://api.obscura.trade/v1/query), both with dataset="forex_positions". The column schema is public at https://api.obscura.trade/v1/catalog/forex_positions.

Can I filter forex_positions by company or symbol?

Yes. forex_positions carries pair, the column the API's symbols filter resolves against.

How often is forex_positions updated?

Obscura refreshes forex_positions on a every 30 minutes schedule — that is the most frequent scheduled job that re-collects or re-exports the table. It is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when the publisher makes a row available is described by the availability rule above, and is recorded per row in available_date.

Related datasets

Cboe IndicesDaily history of Cboe's volatility indices, including VIX, sourced directly from Cboe. Crypto MarketsPer-coin market data from CoinGecko: price, market cap and volume across thousands of cryptocurrencies. DeFiLlama ProtocolsTotal value locked in each DeFi protocol, tracked by DeFiLlama. DeFiLlama StablecoinsCirculating supply of each stablecoin over time, from DeFiLlama. DeFiLlama TVLTotal value locked on each blockchain, from DeFiLlama. Flow MetricsTrade-flow metrics per symbol in fixed time windows: taker (aggressor) buy versus sell volume and trade counts, broken o… Forex OrderbookOANDA's order book and position book by currency pair: where retail traders' open orders and positions sit relative to p… Forex Positions AggregateA consensus long/short reading per currency pair, blending the individual broker sentiment feeds.