Skip to main content
Time Price Opportunity (TPO) groups the prices traded during fixed time blocks into a market profile. Indie can request these profiles as a typed Series[TpoProfile] and use their Point of Control, Initial Balance, price rows and block metadata in an indicator. TPO is platform-provided data for the instrument of the current chart. It needs no URL or user-defined schema and does not make the indicator an external-data indicator.

Plot the daily Point of Control

Request the profiles once in __init__, then read the most recent profile with [0] in calc:
Daily TPO Point of Control
sources.Tpo() includes the main session, pre-market and post-market by default and merges them into one profile for each requested period. The time_frame belongs to request_series, not to the source: TPO supports day, week and month units such as 1D, 1W and 1M. The series is aligned by profile end time. On each chart bar, [0] is the latest TPO profile available at that time; larger offsets access earlier profiles.
TPO always uses the instrument of the chart where the indicator is running. It cannot be used to select another exchange or ticker.

Compare Point of Control with the Initial Balance

This example plots the distance between the Point of Control and the midpoint of the Initial Balance. Positive values place the Point of Control above that midpoint; negative values place it below.
TPO Point of Control vs Initial Balance
Here block_size='1h' makes each TPO block one hour long, while row_size_ticks=1 fixes every profile row to one instrument tick. Set row_size_ticks=0 (the default) to let the service choose the row size automatically. Supported block sizes are 30m, 1h, 2h and 4h. Use tpo_merge_sessions.MERGE_ALL to combine the selected sessions into one profile, or tpo_merge_sessions.GROUP_BY_TYPE to keep the session types in separate profiles.

Value area

A profile does not carry its value area: TpoProfile exposes the Point of Control and the Initial Balance, but no VAH, VAL or value-area ratio. The value area you see on the TPO chart type is not part of the data either — the chart works it out itself, from the same rows and Point of Control you get here plus its own Value area percentage, 70% by default. An indicator cannot read that chart setting, so whatever you build has to name the percentage it wants. You can derive it in the indicator instead, the same way the chart does. Every price row lists the blocks that traded at it, so len(row.block_indexes) is the TPO count of that row. Start at the Point of Control and widen the range one row at a time, always taking the richer of the two neighbours, until the accumulated count covers the share of the profile you treat as value:
TPO value area high and low
Two rules in that loop are what keep the result equal to the chart’s:
  • The value area stays one continuous price range. You cannot simply take the busiest rows in the profile — they have to be added outwards from the Point of Control, neighbour by neighbour.
  • Ties are broken by distance. When the row above and the row below hold the same number of blocks, the chart takes the one closer to the Point of Control, and the upper row when both are equally far.
The empty-row check is not optional either: a profile arrives with no rows on the bars before the first one is complete, and indexing into it there stops the indicator with a runtime error. Two more details are worth knowing before you compare the result with the chart. price is the lower bound of a row, so the top of the value area is rows[high_index].price + profile.row_size if you want the row’s upper edge. And the chart’s own value area is built with its Value area setting, which defaults to 70% — keep value_area_ratio in step with it if the two are meant to agree. The same rows also carry volume_buy and volume_sell, so the identical walk over volume_buy + volume_sell instead of the block count gives you a volume-weighted value area rather than a time-based one. For the complete source signature and all read-only profile fields, see sources.Tpo and TpoProfile in the Library Reference.