TradingView session research

Overnight drift: separate the return from the fill assumption

Overnight drift describes a return pattern outside the regular trading session. To study it on an equity chart, pair the previous regular close with the next regular open, then pair that open with the same session close. Their gross return factors multiply to the close-to-close factor. A positive historical overnight return does not establish that a trader could capture it after auction rules, execution costs and idle-cash assumptions.

See the decision guide

The research term has two different windows

Boyarchenko, Larsen and Whelan study an overnight drift around the opening of European markets using US equity futures. That narrow window is different from holding a stock from its regular close to its next regular open. Their July 2026 New York Fed update reports that the earlier 02:00 to 03:00 Eastern pattern was approximately flat in the January 2021 to December 2025 sample. This page measures the broader equity close-to-open interval; it does not replicate that futures study or claim a persistent premium.

  • Freeze the instrument, data vendor, sample dates, exchange session and adjustment convention before comparing periods. A cash ETF, cash index and futures contract are different exposures.
  • Daily bars can split an equity day into two return legs. They cannot locate a return within the overnight interval or reproduce a one-hour futures study. That requires intraday data and contract-roll accounting.

Multiply paired factors; do not add compounded returns

Let C(t-1) be the previous regular-session close, O(t) the next regular open and C(t) that session close, all on one consistent basis. Define rON(t) = O(t) / C(t-1) - 1 and rID(t) = C(t) / O(t) - 1. For every valid pair, (1 + rON) × (1 + rID) = C(t) / C(t-1). Across the identical accepted session set S, WON = product(1 + rON), WID = product(1 + rID), and WCC = WON × WID. All three wealth indices start at 1.

  • WON = (102 / 100) × (100 / 101) = 1.00990099. WID = (101 / 102) × (103 / 100) = 1.01990196. Their product is 1.03, equal to 103 / 100. These are invented prices for arithmetic, not market results.
  • Bad attribution: adding the two compounded returns gives 2.980295%, not 3%. The missing 0.019705 percentage points are their interaction. Log returns can be added exactly when every factor is positive; simple returns cannot.
  • Bad sample: retaining only session A for overnight gives 1.02 × 1.01990196 = 1.0403, falsely suggesting a 4.03% full-period gain. Reject or retain each observation for all three series together.
  • If sessions are excluded, the identity still holds over the accepted pairs, but WCC is no longer uninterrupted buy-and-hold. Report exclusions and do not label the result as a continuous portfolio return.
Hypothetical sessionPrevious close → open → closeOvernightIntradayClose-to-close
A100 → 102 → 101+2.0000%-0.9804%+1.0000%
B101 → 100 → 103-0.9901%+3.0000%+1.9802%
Both sessions compoundedSame two pairs+0.9901%+1.9902%+3.0000%

Keep adjustments and distributions on one ledger

The script below requests split-adjusted prices without dividend adjustment. Its output is price-return attribution. TradingView describes dividend-adjusted charts as a total-return view; mixing their close with an unadjusted open changes the meaning of the overnight leg. Use a separately documented total-return study if distributions are part of the research question.

  • Historical adjusted prices are analytical units, not historical cash fills. Corporate-action factors can revise earlier data; preserve the export date and settings used for a reproducible study.
  • A cash ledger must handle entitlement, share changes, withholding, receivables and payment timing. Do not treat an unpaid dividend as cash available for the next purchase. This price-only script does not model those items.
BoundaryCorrect treatmentBad counterexample
Two-for-one splitRestate the earlier 100 close as 50 when the next open is 50; the price return is 0%. Reconcile share counts for actual trades.Raw 100 → 50 produces a fictitious -50% overnight loss.
Ex-dividend sessionWith a 100 close, 99 open and 1 cash dividend, the price leg is -1%; an eligible holder has zero gross economic change before costs under these assumptions.Calling the -1% price change a total loss ignores the distribution. Adding another dividend to an already total-return-adjusted series double counts it.
Adjusted close mixed with raw openUse the same provider, session and adjustment convention for O, C and prior C.Adjusted prior close 99 with raw open 100 creates +1.0101% even if raw close and open were both 100.

A trading session is not a calendar day

For an ordinary NYSE core session, the published hours are 09:30 to 16:00 Eastern, with exchange holidays and early closes. Use the instrument calendar and actual session endpoints. Friday close to Tuesday open after a Monday holiday is one close-to-open observation, with more elapsed exposure than a normal weekday night.

  • Preserve the exchange timezone and daylight-saving rules. A fixed UTC offset can shift the local opening boundary. The chart display timezone alone does not redefine the data session.
  • The regular close-to-open interval includes after-hours and premarket price discovery. Replacing it with the last extended-hours print to the first premarket print leaves other intervals out and answers a different question.
  • On many TradingView futures symbols, session.regular means the longer electronic session. This equity example must not be applied to futures by assuming that regular always means the US cash session.
  • Early closes change the time at risk. Holidays create no synthetic zero-return bars. A data outage or halt may also produce a long gap; elapsed hours cannot distinguish it from a scheduled closure. Reconcile returned dates against the exchange calendar.
  • An official auction price, daily vendor open and first intraday trade can differ. Inspect the chosen feed definition before claiming an auction-to-auction measurement.

Pine Script: an observable paired attribution study

Use standard 1D candles for an equity with verified regular-session data. The indicator requests one explicit split-adjusted regular-session context and processes completed bars only. It shows gross wealth indices, two cost sensitivity curves, per-session returns, elapsed closure hours and the identity residual in the Data Window. It sends no orders and reports no Strategy Tester profit.

  • The date filter uses the requested session opening timestamp: start inclusive, end exclusive. Load at least one earlier session for the first previous close. Every accepted pair updates all three gross series; positive prices are required.
  • The current unfinished daily session is excluded. Results become available after its close, even though its opening price was known earlier. Do not use this completed-bar calculation to claim an earlier trade.
  • Invalid observed pairs are counted. Entirely absent dates and missing session timestamps cannot be counted by this script; compare the exported dates with the calendar. A zero rejection count is not proof of complete data.
  • The residual should be near zero within floating-point precision. It verifies multiplication on shared inputs, not data correctness: consistently wrong session boundaries can still satisfy the identity. Zero accepted pairs means no estimate.
  • The cost curves assume one fully funded long round trip per accepted session, fractional sizing, immediate reuse of proceeds, no leverage, 0% idle-cash return and no dividends. These assumptions exclude settlement restrictions and funding constraints; use a ledger before interpreting the curves as a feasible portfolio.
Pine v6 syntax checked with the Pineify MCP tool. Syntax success does not validate data coverage, session semantics or executable fills.pine
//@version=6
indicator("Overnight vs intraday: paired price attribution", overlay = false)

int startAt = input.time(timestamp("01 Jan 2020 00:00 +0000"), "First session (inclusive)")
int endAt = input.time(timestamp("01 Jan 2026 00:00 +0000"), "Last boundary (exclusive)")
float sideBps = input.float(5.0, "Illustrative cost per side (bps)", minval = 0, maxval = 100)
if not timeframe.isdaily or timeframe.multiplier != 1
    runtime.error("Use standard 1D equity candles and audit the regular session.")
if startAt >= endAt
    runtime.error("Start must precede end.")

string regularTicker = ticker.new(syminfo.prefix, syminfo.ticker, session.regular, adjustment.splits)
[o, c, previousClose, sessionTime, previousEnd] = request.security(regularTicker, "1D", [open, close, close[1], time, time_close[1]], gaps = barmerge.gaps_on, lookahead = barmerge.lookahead_off)
bool inRange = not na(sessionTime) and sessionTime >= startAt and sessionTime < endAt
bool valid = not na(o) and not na(c) and not na(previousClose) and o > 0 and c > 0 and previousClose > 0
var float overnightWealth = 1.0
var float intradayWealth = 1.0
var float fullWealth = 1.0
var float overnightCostWealth = 1.0
var float intradayCostWealth = 1.0
var int pairs = 0
var int rejected = 0
var int lastSession = na
float overnightReturn = na
float intradayReturn = na
float closureHours = na
float costFactor = (1 - sideBps / 10000) / (1 + sideBps / 10000)

if barstate.isconfirmed and inRange and (na(lastSession) or sessionTime != lastSession)
    lastSession := sessionTime
    if valid
        float overnightFactor = o / previousClose
        float intradayFactor = c / o
        overnightReturn := 100 * (overnightFactor - 1)
        intradayReturn := 100 * (intradayFactor - 1)
        closureHours := (sessionTime - previousEnd) / 3600000.0
        overnightWealth *= overnightFactor
        intradayWealth *= intradayFactor
        fullWealth *= c / previousClose
        overnightCostWealth *= overnightFactor * costFactor
        intradayCostWealth *= intradayFactor * costFactor
        pairs += 1
    else
        rejected += 1

plot(pairs > 0 ? overnightWealth : na, "Close-to-open gross wealth", color = color.blue)
plot(pairs > 0 ? intradayWealth : na, "Open-to-close gross wealth", color = color.orange)
plot(pairs > 0 ? fullWealth : na, "Close-to-close gross wealth", color = color.gray)
plot(pairs > 0 ? overnightCostWealth : na, "Close-to-open cost scenario", color = color.aqua)
plot(pairs > 0 ? intradayCostWealth : na, "Open-to-close cost scenario", color = color.red)
plot(overnightReturn, "Paired overnight return (%)", display = display.data_window)
plot(intradayReturn, "Paired intraday return (%)", display = display.data_window)
plot(closureHours, "Hours between sessions", display = display.data_window)
plot(overnightWealth * intradayWealth - fullWealth, "Gross identity residual", display = display.data_window)
plot(pairs, "Accepted pairs", display = display.data_window)
plot(rejected, "Invalid observed pairs", display = display.data_window)

var table audit = table.new(position.top_right, 2, 4)
if barstate.islast
    table.cell(audit, 0, 0, "Accepted pairs")
    table.cell(audit, 1, 0, str.tostring(pairs))
    table.cell(audit, 0, 1, "Invalid observed pairs")
    table.cell(audit, 1, 1, str.tostring(rejected))
    table.cell(audit, 0, 2, "Gross residual")
    table.cell(audit, 1, 2, str.tostring(overnightWealth * intradayWealth - fullWealth, "#.########"))
    table.cell(audit, 0, 3, "Basis / idle cash")
    table.cell(audit, 1, 3, "Split-adjusted price / 0%")

Cost sensitivity is not an executable backtest

For a long round trip from reference entry Pentry to exit Pexit, let k be an assumed proportional cost on each side. Investing one unit including entry cost gives a net factor (Pexit / Pentry) × (1 - k) / (1 + k). The input bundles modeled spread, slippage and proportional fees; fixed minimum commissions need a separate cash ledger. If using actual fills that already include spread and slippage, do not deduct them twice.

  • The two repeated-trade curves each incur two round trips, so their gross factor is multiplied by [(1 - 0.0005) / (1 + 0.0005)]². The continuous buy-and-hold comparison incurs one round trip. Net curves therefore do not share the gross attribution identity.
  • At a zero gross return, the same 5 bps per-side assumption loses about 0.09995% in one round trip. Raising costs can reverse a small apparent advantage; zero costs reproduce the gross curves exactly.
  • Overnight-only exposure holds cash during regular hours; intraday-only exposure holds cash overnight, including weekends and holidays. The example credits neither with interest. If adding interest, use actual eligible cash, elapsed accrual time, broker terms and the same convention for the benchmark.
  • Do not add the two alternative wealth indices as though they were two fully funded portfolios sharing one unit of capital. A portfolio split between them requires stated weights and rebalancing.
Same two-session exampleGross wealthAfter 5 bps per side, each round trip
Overnight only1.009900991.00788321 (+0.7883%)
Intraday only1.019901961.01786420 (+1.7864%)
Buy and hold across both sessions1.030000001.02897051 (+2.8971%), one entry and one exit

Turn the measurement into a separate execution experiment

The completed close is an observation, not advance permission to trade at that close. A rule based on the final closing return cannot both wait for that return and presume an order filled in the same closing auction. TradingView strategies normally create an order at bar close and fill it on the next available tick. Changing an emulator setting does not establish real auction eligibility.

  • Keep the raw attribution study separate from the resulting strategy. Report observation count, executed-trade count, rejected orders, gross and net returns, worst gap, drawdown, market exposure hours and capital tied up.
  • A one-chart equity study does not reproduce an index constituent portfolio, a futures roll, a multi-asset hedge or options legs. Those require separate position, cash and execution records.
Execution decisionEvidence required
Enter near the closeFreeze the signal before the broker and venue cutoff, submit a supported order, and record the fill. A signal using the final close needs a later entry and a different measured interval.
Exit at the next openSpecify an order submitted before the applicable opening cutoff. Include adverse execution, fees, halts, rejected orders and partial fills. The printed daily open is a benchmark, not a guaranteed fill.
Use a stop overnightDefine where the order can trade and what happens during closures. A gap through the stop can produce a worse exit; an untraded stop price is not a fill.
Test a thresholdFreeze the information cutoff and candidate thresholds on development data; evaluate the selected rule on later untouched sessions. Do not choose sample dates to maximize the overnight curve.

Use Pineify after fixing the session contract

Pine Script AI Coding Agent can help draft or revise the observation and execution rules. Give it the selected instrument, session, price basis, signal cutoff, order timing and cost model, then inspect the generated source. The indicator on this page is an audit artifact; it is not a strategy-input optimization target.

  • Once an executable strategy has been implemented and its trades reconciled, Strategy Optimizer can test configured input combinations and compare recorded backtest results. That does not validate the underlying market-on-close or market-on-open assumptions.
  • Keep session definitions, corporate-action policy and data-cleaning rules fixed across candidate inputs. Compare only the predeclared strategy parameters and retain a later evaluation period outside the search.
Where Pineify fits

Pine Script AI Coding Agent

Draft session-aware Pine research code from explicit observation times, adjustment rules and execution assumptions, then inspect the output.

Also useful: Strategy Optimizer. Compare input combinations for a separately validated executable strategy. Attribution curves alone do not establish tradable strategy results.

Draft session research code

Frequently asked questions

Educational research, not investment advice. Prices and cost examples are hypothetical. No backtest or live performance is claimed. Trading can lose money; session attribution and syntax validation do not prove executable returns.

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.