TradingView backtesting research

Cointegration trading: test the relationship you will actually hold

Cointegration trading uses evidence that nonstationary price series share a stationary linear combination to research relative-value trades. Estimate that combination on an earlier training interval, use a cointegration test with the correct residual inference, then hold the coefficients fixed on later observations. A low training p-value cannot establish future convergence or profit after the costs of both legs. High price correlation alone does not establish cointegration.

See the decision guide

Correlation does not remove a stochastic trend

Correlation describes co-movement in the supplied series and window. Cointegration concerns whether a fixed linear combination removes the nonstationary component. In the usual two-series Engle-Granger setup, both series are assumed I(1): their levels are nonstationary and first differences are stationary. Check whether that description is plausible before treating a residual test as cointegration evidence.

  • Consider B(t) = W(t) and A(t) = 2W(t) + V(t), where W and V are independent random walks with nonzero innovation variances. If V has much smaller innovations, returns can be highly correlated. Yet A - beta × B = (2 - beta)W + V retains a random walk for every fixed beta. This construction has no stationary linear combination with a nonzero A weight.
  • If instead A(t) = 5 + 2B(t) + u(t), with B a random walk and u a stationary disturbance, A - 5 - 2B = u is stationary. The distinction is the residual process, not how closely the two price lines resemble each other.
  • Unit-root non-rejection does not prove I(1), particularly in short samples. Deterministic trends, I(0) inputs, I(2) inputs and changing regimes need different treatment. Document level and difference diagnostics and the economic reason for pairing the instruments.

Freeze the equation and the information boundary

For synchronized prices in the same currency, fit A(t) = alpha + beta × B(t) + e(t) using only the training rows. With an intercept, beta_hat = sum[(B - mean(B)) × (A - mean(A))] / sum[(B - mean(B))²], and alpha_hat = mean(A) - beta_hat × mean(B). Reject a constant B series or negligible residual scale before forming a z-score.

  • OLS is directional in finite samples: fitting A on B and fitting B on A generally do not give reciprocal slopes. Select the economic direction in advance. Testing both and keeping the smaller p-value is another selection step.
  • For a price-level residual, long one unit of the residual means long one share of A and short beta shares of B. For beta > 0, shorting the residual reverses those positions. A negative beta implies same-direction holdings. The intercept is not an extra stock position.
  • In a log-price equation, beta is a log exposure coefficient rather than a share count. Locally, qB ≈ beta × qA × A/B; keeping that exposure may require rebalancing. Futures multipliers, currency conversion and lot rounding must enter the execution model.
DecisionRecord before the test interval
DataSymbols, currency, timestamps, sessions, adjustment policy and missing-bar treatment. Never silently fill stale closes across different sessions.
ModelLevels or logs, dependent variable, intercept or trend, training start/end, and the candidate universe searched.
InferenceEngle-Granger specification, lag rule, significance level, diagnostic checks and all rejected pairs.
Held modelFrozen alpha, beta, residual mean and scale, entry/exit rules, position sizing, failure exits and refit schedule.
EvaluationLater dates that did not choose any of those settings; orders use executable prices after the signal becomes observable.

Use the residual test distribution that matches estimation

The Engle-Granger null is no cointegration. The first stage estimates the relationship; the second stage tests its residuals for a unit root. Because those residuals were fitted on the same sample, ordinary ADF p-values do not give valid Engle-Granger inference. Use a cointegration procedure with the chosen first-stage deterministic terms and number of series. Statsmodels coint returns the residual test statistic, MacKinnon approximate p-value and cointegration critical values.

  • Record the lag-selection method and inspect residual serial correlation. A small p-value under one lag choice is not a reason to suppress other prespecified diagnostics. Near-perfect collinearity can produce a numerical negative-infinity statistic and zero p-value; inspect duplicate feeds and residual variance.
  • A training rejection is conditional evidence, not a probability that the pair will converge. Searching hundreds of pairs, directions or endpoints increases false discoveries. Retain the search history and assess the selection procedure on untouched later data.
  • The related ADF guide covers lag, trend and residual critical-value choices in detail. Here the next question is whether the estimated coefficients remain useful when they can no longer be fitted to the observations being judged.

Eight training rows make the hedge arithmetic visible

These fictional USD equity prices illustrate estimation and accounting only. Eight constructed observations cannot establish market cointegration. Let training B = [10, 11, 12, 13, 14, 15, 16, 17] and A = [26, 26, 28, 32, 34, 34, 36, 40]. Then mean(B) = 13.5, mean(A) = 32, the centered cross-product is 84 and the centered B square sum is 42. The fitted equation is A = 5 + 2B + e.

  • The residuals are [1, -1, -1, 1, 1, -1, -1, 1], with mean 0 and squared sum 8. Use the descriptive sample standard deviation s = sqrt(8/7) = 1.069045 for this example. That is not the regression error estimate sqrt(8/6); the denominator convention is part of the frozen signal.
  • Hold alpha = 5, beta = 2, mean = 0 and s fixed for the following three observations. z = (A - 5 - 2B) / s. No test observation changes the equation or normalization.
Later observationB priceA priceFrozen residualFrozen z
1$18$4432.806243
2$19$4521.870829
3$20$4500

Translate convergence into both legs and all four fills

For accounting, assume one A share is sold at $44 and two B shares are bought at $18, followed by exits at A = $45 and B = $20. These are assumed fills for the example, not evidence that a close-based signal can fill at the same close. A live or historical execution model must determine fills after the information is available.

  • The allowance is a deduction from reference-price P&L; do not deduct it again if actual fills already include it. The example assumes no dividend payments, debit financing or interest on short proceeds. Broker borrow rates and collateral conventions vary; the stated rate is not a current quote.
  • Entry gross exposure is $44 + $36 = $80, while net dollar exposure is $36 - $44 = -$8. A regression hedge is not automatically dollar neutral. Account equity and margin determine a separate return denominator.
  • Model partial fills, unavailable borrow and the cost of unwinding an unmatched leg. A stationary residual cannot guarantee simultaneous executable prices or enough convergence to cover costs.
ComponentCalculationUSD result
Short A1 × (44 - 45)-1.0000
Long B2 × (20 - 18)+4.0000
Gross P&L3 - 0, the decline in the fixed residual+3.0000
Commission assumption: 5 basis points on every fill0.0005 × (44 + 36 + 45 + 40)-0.0825
Spread/slippage allowance: $0.01 per share per fill0.01 × (1 + 2 + 1 + 2)-0.0600
Borrow assumption: $44 fee base, 10% annual, 2/360 years44 × 0.10 × 2 / 360-0.0244
Net under these assumptions3 - 0.0825 - 0.06 - 0.024444...+2.8331

A slope break defeats the frozen hedge despite perfect co-movement

Keep the same eight training rows. In an alternative test path, the relation changes to A = 5 + 2.5B, with B = [18, 19, 20] and A = [50, 52.5, 55]. The old beta of 2 produces residuals [9, 9.5, 10]. The two price series have correlation 1 across those three rows, but the residual held by the trader moves farther from its training mean.

  • A short residual entered at the first reference prices loses $1 gross by the third row: 1 × (50 - 55) + 2 × (20 - 18) = -1. Costs add to that loss. The entry z is about 8.42, which a predeclared failure boundary could reject rather than treat as a stronger opportunity.
  • Refitting only those test rows gives alpha = 5 and beta = 2.5 with zero residuals. That retrospective fit neither recovers the lost dollar nor validates a trade using the old holdings. Refitting the entire chart also lets later prices influence earlier decisions.
  • An intercept shift can leave the residual fluctuating around a new mean; a slope shift introduces exposure to B into the old residual. Gregory and Hansen study cointegration tests that allow a regime shift. Such tests address a different model and do not provide permission to assume future stability.
  • Predeclare suspension rules for persistent mean shifts, excessive scale, stale prices, borrow failure or an adverse loss. Refit only on information available at the scheduled review. Changing beta while a position is open creates trades and costs that belong in the ledger.

Reproduce a training rejection and a later level shift

This separate synthetic experiment uses 400 observations and NumPy seed 816. B is a random walk; u is an AR(1) disturbance with coefficient 0.6 initialized from its stationary distribution; A = 5 + 2B + u. Fit the first 300 rows and preserve the next 100 for diagnostics. A second test path adds $8 to A only after the split. Both paths share exactly the same training evidence.

  • The shifted path has the same test-period residual dispersion but a different center. Recentring it using all 100 test rows would conceal the failure of the original reference mean. The exceedance fraction is a descriptive diagnostic, not a calibrated break-test p-value or a trading return.
  • The training p-value is unchanged because both paths are identical before the split. This counterfactual construction shows why a training test cannot rule out a later break. One seeded sample does not measure how often the method works in markets.
OutputResult
Training alpha / beta5.850873 / 1.984945
Training residual mean / sample scaleApproximately 0 / 1.202225
Training EG statistic / p-value / 5% critical value-7.761306 / 1.194071 × 10^-10 / -3.356641
Unchanged test: residual mean / scale / fraction |z| > 2-0.594110 / 1.133843 / 5%
Level-shift test: residual mean / scale / fraction |z| > 27.405890 / 1.133843 / 100%
Executed with NumPy 2.5.4 and Statsmodels 0.14.6. The code estimates and diagnoses a spread; it does not simulate trades or download market data.python
import numpy as np
from statsmodels.tsa.stattools import coint

rng = np.random.default_rng(816)
b = 50 + np.cumsum(rng.normal(size=400))
u = np.empty(400)
u[0] = rng.normal() / np.sqrt(1 - 0.6**2)
for t in range(1, 400):
    u[t] = 0.6 * u[t-1] + rng.normal()
a = 5 + 2 * b + u
split = 300
X = np.column_stack([np.ones(split), b[:split]])
alpha, beta = np.linalg.lstsq(X, a[:split], rcond=None)[0]
e = a[:split] - alpha - beta * b[:split]
mu, scale = e.mean(), e.std(ddof=1)
assert np.isfinite(scale) and scale > 0
stat, p, critical = coint(
    a[:split], b[:split], trend="c", maxlag=2, autolag=None)
print("fit", alpha, beta, mu, scale)
print("training EG", stat, p, critical[1])
for label, prices_a in [("unchanged", a[split:]),
                        ("level shift", a[split:] + 8)]:
    residual = prices_a - alpha - beta * b[split:]
    z = (residual - mu) / scale
    print(label, residual.mean(), residual.std(ddof=1),
          np.mean(np.abs(z) > 2))

Reconcile TradingView records before interpreting a report

TradingView exports each strategy-report tab separately. Its List of Trades is a trade ledger, not both synchronized price histories. Pineify Backtest Deep Report parses trade timestamps, entry and exit prices, quantity and P&L, and calculates supported metrics such as net profit and profit factor. The cointegration estimates and break diagnostics above are separate research calculations, not claimed built-in report fields.

  • Preserve both price histories, timestamp alignment, the frozen model and the per-leg execution ledger alongside the original export. Reconcile strategy version, symbol, timeframe, sample dates and costs before comparing report totals.
  • A Pine strategy simulates fills from its chart data. Requesting a second symbol supplies observations; it does not create an independently filled second position. Use a portfolio simulator or reconciled ledger for two-leg equity, financing and unmatched-fill risk.
  • Pine Script AI Coding Agent can help draft a monitor using explicit symbols, frozen coefficients and observation timing. Compare generated values with the research dataset. The existing pairs-trading guide includes a monitor; this page focuses on estimating and challenging the model behind it.
Where Pineify fits

Backtest Deep Report

Review supported metrics from TradingView trade records after reconciling the export. Keep cointegration inference and the two-leg execution ledger as separate research evidence.

Also useful: Pine Script AI Coding Agent. Draft a spread monitor from a documented model, then compare its observations with the external calculation.

Review Backtest Deep Report

Frequently asked questions

Educational research, not investment advice. All examples are constructed; no pair recommendation or observed trading performance is presented. Statistical relationships can change, short positions can incur large losses, and historical rejection does not guarantee future convergence.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.