Skip to content
Back to App

History & Bars

The History endpoint lets you retrieve historical OHLCV (Open, High, Low, Close, Volume) bar data for any available instrument. Use this to pre-load data for indicator calculations, analyze historical price patterns, or build lookback buffers before entering the replay loop.


POST History/retrieveBars

Retrieve historical bars for a contract. You can request data by specifying the number of bars to look back, or by defining a time window with start/end timestamps.

URL: POST /api/topstep-sim/History/retrieveBars

Authentication: Bearer token required.

Request body

{
"contractId": "CON.F.US.ENQ.H25",
"unit": 2,
"unitNumber": 5,
"barsBack": 200,
"startTime": null,
"endTime": null
}
FieldTypeRequiredDescription
contractIdstringYesThe contract ID to retrieve data for
unitnumberYesBar aggregation unit. See AggregateBarUnit below.
unitNumbernumberYesNumber of units per bar (e.g., 5 with unit=2 gives 5-minute bars)
barsBacknumber or nullNoNumber of bars to retrieve, counting back from the current time or endTime. Cannot be used together with startTime.
startTimestring or nullNoISO 8601 start timestamp. Retrieve bars from this time forward. Cannot be used together with barsBack.
endTimestring or nullNoISO 8601 end timestamp. Defaults to the current session time if not specified.

AggregateBarUnit

ValueUnit
1Second
2Minute
3Hour
4Day

Common timeframe configurations

TimeframeunitunitNumber
1-second11
1-minute21
5-minute25
15-minute215
30-minute230
1-hour31
4-hour34
Daily41

Response

{
"success": true,
"errorCode": 0,
"errorMessage": null,
"bars": [
{
"t": "2025-01-15T14:00:00Z",
"o": 21480.50,
"h": 21495.25,
"l": 21478.00,
"c": 21492.75,
"v": 3420
},
{
"t": "2025-01-15T14:05:00Z",
"o": 21492.75,
"h": 21505.50,
"l": 21490.00,
"c": 21503.25,
"v": 2815
}
]
}
FieldTypeDescription
barsarrayList of bar objects in chronological order (oldest first)
bars[].tstringISO 8601 timestamp for the bar’s open time
bars[].onumberOpen price
bars[].hnumberHigh price
bars[].lnumberLow price
bars[].cnumberClose price
bars[].vnumberVolume (number of contracts traded)

Example: Look back N bars

# Get the last 200 5-minute bars
result = api("History/retrieveBars", {
"contractId": CONTRACT_ID,
"unit": 2, # Minute
"unitNumber": 5, # 5-minute bars
"barsBack": 200
})
if result["success"]:
bars = result.get("bars", [])
print(f"Retrieved {len(bars)} bars")
# Calculate a simple moving average from historical data
closes = [b["c"] for b in bars]
sma_20 = sum(closes[-20:]) / 20
print(f"SMA(20) = {sma_20:.2f}")

Example: Time window

# Get all 1-minute bars between 14:00 and 15:00 UTC
result = api("History/retrieveBars", {
"contractId": CONTRACT_ID,
"unit": 2, # Minute
"unitNumber": 1, # 1-minute bars
"startTime": "2025-01-15T14:00:00Z",
"endTime": "2025-01-15T15:00:00Z"
})
bars = result.get("bars", [])
print(f"Retrieved {len(bars)} bars in the 14:00-15:00 window")

Building a lookback buffer

Many strategies require historical data to calculate indicators (e.g., moving averages, RSI, Bollinger Bands) before entering the replay loop. Use History/retrieveBars to pre-load this data:

# Pre-load 50 bars for SMA calculation
history = api("History/retrieveBars", {
"contractId": CONTRACT_ID,
"unit": 2,
"unitNumber": 5,
"barsBack": 50
})
closes = [b["c"] for b in history.get("bars", [])]
# Now enter the replay loop with a warm indicator
for i in range(TOTAL_BARS):
bar = get_next_bar()
if bar is None:
break
closes.append(bar["c"])
# Need at least 20 bars for SMA(20)
if len(closes) >= 20:
sma = sum(closes[-20:]) / 20
if bar["c"] > sma:
# Price above SMA — bullish signal
pass

Multi-timeframe analysis

You can retrieve bars at different timeframes for the same instrument. This is useful for strategies that use a higher timeframe for trend direction and a lower timeframe for entries.

# Get daily bars for trend context
daily = api("History/retrieveBars", {
"contractId": CONTRACT_ID,
"unit": 4, # Day
"unitNumber": 1, # Daily
"barsBack": 20
})
daily_bars = daily.get("bars", [])
daily_closes = [b["c"] for b in daily_bars]
daily_sma = sum(daily_closes[-10:]) / 10
daily_trend = "bullish" if daily_closes[-1] > daily_sma else "bearish"
# Get hourly bars for recent structure
hourly = api("History/retrieveBars", {
"contractId": CONTRACT_ID,
"unit": 3, # Hour
"unitNumber": 1, # 1-hour bars
"barsBack": 24
})
hourly_bars = hourly.get("bars", [])
recent_high = max(b["h"] for b in hourly_bars[-4:])
recent_low = min(b["l"] for b in hourly_bars[-4:])
print(f"Daily trend: {daily_trend}")
print(f"4-hour range: {recent_low}{recent_high}")

Usage notes

  • Bars are returned in chronological order (oldest first).
  • Use either barsBack or startTime to define the data range — not both.
  • If endTime is omitted, data is returned up to the current point in the replay session.
  • The maximum number of bars per request depends on the instrument and timeframe. For high-frequency data (1-second bars), consider limiting barsBack to avoid large responses.
  • Historical bar data uses the same format as bars returned by Replay/step. See Bar Data Format for full field documentation.