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

# Basket residual

> How much of this market's move a basket's common moves don't explain, from PCA.

![Basket residual on a BTC chart](https://mintcdn.com/0xarchive-e895b8e7/iWF6OFnAgDYSS9hp/images/terminal/examples/basket-residual.webp?fit=max\&auto=format\&n=iWF6OFnAgDYSS9hp\&q=85\&s=34187fc3b3178cc0e50b7389e29d6f32)

BTC on 1-hour bars in the 0xArchive Terminal, 2026-10-25.

> **[Open on a chart](https://terminal.0xarchive.io/?symbol=BTC\&interval=1h\&scripts=example%3Abasket-residual)**
>
> Load it on BTC 1h in the terminal, then change its settings or open it in the script editor.

The part of this market's return that the basket's shared moves don't explain, in standard deviations. Near 0, it moved with the basket; far from it, it moved on its own. The chart's market is always in the basket.

## How it works

`input.markets` holds the basket, editable in the settings, and `markets.map((m) => bars({ market: m }))` opens a stream per market. On each closed bar `ml.pca` takes every market's log return and, over the last **Window** bars, finds the **Components** moves they share; the first is usually the whole market rising and falling together. `residualZ(0)` is what is left of the chart's own return, over its usual size.

> [!NOTE]
>
> PCA waits until every market in the basket has a full window of bars, so a newly listed market holds the whole basket back until it has the history. Take it out of the basket to start sooner.

## The script

```ts basket-residual.ts theme={"theme":"github-dark"}
// How much of this market's move a basket's common moves don't explain. PCA finds the moves the
// basket shares (the first is usually the whole market rising and falling together); what is left
// of this market's return is its own, in standard deviations. Far from 0, it moved on its own:
// the raw material of a stat-arb trade. The chart's market is always in the basket.
indicator('Basket residual', { shortName: 'Resid z', precision: 2 });

const markets = input.markets('Basket', ['BTC', 'ETH', 'SOL', 'HYPE', 'XRP', 'DOGE', 'BNB', 'AVAX', 'LINK', 'SUI']);
const window = input.int('Window', 500, { min: 50, max: 5000, tooltip: 'Bars the components are estimated over' });
const components = input.int('Components', 3, { min: 1, max: 10, tooltip: 'Common moves taken out' });

const chart = bars();
const legs = [chart, ...markets.filter((m) => m !== chart.market.symbol).map((m) => bars({ market: m }))];
const pca = ml.pca(Math.max(2, legs.length), { window: Math.max(window, legs.length + 1), components: Math.min(components, legs.length) });

const resid = plot.histogram('Residual z');
const common = plot.line('First component share', { hidden: true, precision: 2 });
plot.level('Own move up', 2, { style: 'dashed' });
plot.level('Own move down', -2, { style: 'dashed' });

chart.onClose(() => {
  const returns = legs.map((leg) => Math.log(leg.close / leg.ago(1).close));
  const p = pca.update(returns);
  const z = p.residualZ(0);
  resid.set(z, z >= 0 ? color.up : color.down);
  common.set(p.explained[0] * 100);
});
```

## Next step

See the next example, [Order-flow forecast](https://docs.0xarchive.io/terminal/examples/order-flow-forecast.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.