> ## Documentation Index
> Fetch the complete documentation index at: https://takeprofit.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Use your own OHLCV candle series in an Indie indicator. CSV column requirements, the required time_frame argument, alignment with the chart and lookahead.

# External candles from CSV

A CSV file with OHLCV candles can be used as an additional data source in an indicator, the same way an additional
instrument is used with [`Context.calc_on()`](/docs/indie/Request-additional-instruments): a `@sec_context` callback runs
over the external candles, and its results are merged into the chart timescale.

## CSV columns

The file must have a header row with these columns (any order, header names are case-insensitive, unknown extra
columns are ignored):

| Column                         | Required | Notes                                                                                                 |
| ------------------------------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `time`                         | yes      | Candle open time. The column name can be changed with the `time_column` argument of `sources.Csv()`.  |
| `open`, `high`, `low`, `close` | yes      | Prices. `high` must not be below `low`, `open` or `close`; `low` must not be above `open` or `close`. |
| `volume`                       | no       | Missing column or empty cell means volume 0. Negative values are rejected.                            |

```txt candles.csv theme={null}
time,open,high,low,close,volume
2024-01-02T00:00:00Z,187.15,188.44,183.89,185.64,82488700
2024-01-03T00:00:00Z,184.22,185.88,183.43,184.25,58414500
2024-01-04T00:00:00Z,182.15,183.09,180.88,181.91,71983600
```

Timestamps must be strictly increasing. Accepted time formats, cell value rules and file size limits are described in
[CSV format and limits](/docs/indie/External-data/CSV-format-and-limits). Note that candle timestamps cannot have a
sub-second part.

## Declaring the source

```py External candle close theme={null}
# indie:lang_version = 5
from indie import indicator, sec_context, MainContext, TimeFrame
from indie.data import sources

@sec_context
def ExternalCandles(self):
    return self.close[0]

@indicator('External candle close')
class Main(MainContext):
    def __init__(self):
        self._ext_close = self.calc_on(
            ExternalCandles,
            time_frame=TimeFrame.from_str('1D'),
            source=sources.Csv('https://example.com/candles.csv'))

    def calc(self):
        return self._ext_close[0]
```

Two parts do the work here. The `@sec_context` callback runs over the external candles exactly as it would over a
secondary instrument, and `source=sources.Csv(...)` tells the platform where those candles come from.

The `source` argument is mutually exclusive with `exchange` and `ticker` — an external source has no exchange or
ticker of its own. Like any `Context.calc_on()` call, the source must be declared in `__init__`.

## The time frame is required

For a candle CSV the `time_frame` argument of `Context.calc_on()` is required. The time frame is a property of your
file — the platform does not guess it from the data:

* `time_frame` declares the cadence of the rows. If rows arrive more frequently than the declared time frame, the
  file is rejected with the `external_timeframe_mismatch` error. The reverse mismatch — rows sparser than the
  declared time frame — is not detected.
* Tick time frames are not supported for external sources.
* Omitting `time_frame` is a compilation error.

## How external candles align with the chart

An external candle CSV behaves like a secondary instrument on its own time frame. On each chart bar, the merged
series returns the value of the latest external candle that has **closed** by the close of that bar:

```txt 5-minute CSV on a 1-minute chart theme={null}
CSV candles (open time): 00:00 close=10 | 00:05 close=20 | 00:10 close=30

chart bar    00:00 00:01 00:02 00:03 00:04 00:05 00:06 00:07 00:08 00:09 00:10
ext close       10    10    10    10    10    10    10    10    10    20    20
```

* The candle that opens at 00:05 closes at 00:10, so its value 20 first appears on the chart bar that also closes
  at 00:10 — the 00:09 bar. On a daily CSV and a 1-hour chart this means Monday's candle first appears on the bar
  covering Monday's final hour.
* The last candle (close 30) has not closed within the loaded history, so history bars never see it. When the chart
  switches to realtime, that candle is the freshest data available and the series starts returning 30.
* Chart bars that come before the first loaded candle read that candle's values instead of being empty.
* These rules assume the CSV time frame is equal to or larger than the chart's. With a finer CSV, each chart bar
  shows the last external candle that opened at or before the bar's open.

With `lookahead=True` a candle is visible already from the chart bar where it **opens**:

```txt Same file with lookahead=True theme={null}
chart bar    00:00 00:01 00:02 00:03 00:04 00:05 00:06 00:07 00:08 00:09 00:10
ext close       10    10    10    10    10    20    20    20    20    20    30
```

`lookahead=True` always requires an explicit `time_frame`.

<Warning>
  `lookahead=True` lets a bar see values that were not yet final at that moment in time. In a backtest this shows up
  as results that are too good to repeat live (lookahead bias).
</Warning>

## Where external candles differ from market instruments

* Timestamps are interpreted as UTC and the external instrument runs on a 24/7 session — there are no
  trading-session gaps or exchange timezone.
* There are no realtime updates: the series is history-only.

For file requirements, size limits and the full list of error codes, see
[CSV format and limits](/docs/indie/External-data/CSV-format-and-limits).
