Skip to content

Time-of-Day Return Profile ​

Mean bar return in each intraday time bucket. The local day is split into buckets equal slices and each bar's simple return is averaged into the bucket of its time-of-day.

Quick reference ​

ItemValue
FamilySeasonality & Session
Input typeCandle (uses close, timestamp)
Output typeTimeOfDayReturnProfileOutput { bins } (length buckets)
Output rangeunbounded per bin (typically small)
Default parametersbuckets = 24, utc_offset_minutes = 0
Warmup period2
InterpretationIntraday return seasonality curve

Formula ​

bucket = floor(minute_of_day * buckets / 1440)   (clamped to buckets-1)
ret_t  = close_t / close_{t-1} - 1
bins[b] = mean of ret over bars whose bucket == b   (0.0 if empty)

See crates/wickra-core/src/indicators/time_of_day_return_profile.rs.

Parameters ​

NameTypeDefaultConstraintSourceDescription
bucketsusize24> 0time_of_day_return_profile.rs:43Number of intraday slices (24 = hourly).
utc_offset_minutesi320nonetime_of_day_return_profile.rs:44Shifts the instant before bucketing.

TimeOfDayReturnProfile::new returns Error::PeriodZero for buckets == 0.

Inputs / Outputs ​

rust
const _: fn(&mut wickra::TimeOfDayReturnProfile, wickra::Candle) -> Option<wickra::TimeOfDayReturnProfileOutput> =
    <wickra::TimeOfDayReturnProfile as wickra::Indicator>::update;
  • Python. update((o,h,l,c,v,ts)) → Matrix of length buckets (or None); batch(...) → (n, buckets) array, warmup rows NaN.
  • Node. update(...) → number[] (or null); batch(...) → flat number[] length n*buckets.
  • WASM. update(...) → Float64Array (or null).

Warmup ​

warmup_period() == 2. The first bar produces no output (no return yet); from the second bar onward the full profile is reported each bar.

Edge cases ​

  • Buckets by hour and means the returns (test buckets_by_hour_and_means_returns).
  • Last bucket clamped for an end-of-day (23:59) bar (test last_bucket_clamped_for_end_of_day).
  • Rejects buckets == 0 (test rejects_zero_buckets).
  • Zero prior close uses a 0.0 return (test zero_prev_close_uses_zero_return).
  • Reset clears all buckets (test reset_clears_state).

Examples ​

Rust ​

rust
use wickra::{Candle, Indicator, TimeOfDayReturnProfile};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let hour = 3_600_000;
    let mut prof = TimeOfDayReturnProfile::new(24, 0)?;
    prof.update(Candle::new(100.0, 100.0, 100.0, 100.0, 1.0, 0)?);            // 00:00, no return
    let out = prof.update(Candle::new(101.0, 101.0, 101.0, 101.0, 1.0, hour)?).unwrap();
    println!("{}", out.bins[1]); // 0.01 (01:00 bucket)
    Ok(())
}

Python ​

python
import wickra as ta

HOUR = 3_600_000
prof = ta.TimeOfDayReturnProfile(24, 0)
prof.update((100.0, 100.0, 100.0, 100.0, 1.0, 0))
out = prof.update((101.0, 101.0, 101.0, 101.0, 1.0, HOUR))
print(out[1])  # 0.01

Node ​

javascript
const wickra = require('wickra');
const HOUR = 3_600_000;
const prof = new wickra.TimeOfDayReturnProfile(24, 0);
prof.update(100, 100, 100, 100, 1, 0);
console.log(prof.update(101, 101, 101, 101, 1, HOUR)[1]); // 0.01

Streaming ​

python
prof = ta.TimeOfDayReturnProfile(48, -300)  # half-hourly, US Eastern
for o, h, l, c, v, ts in candle_feed:
    curve = prof.update((o, h, l, c, v, ts))
    # curve[b] is the running mean return for intraday slice b

Interpretation ​

  1. Intraday seasonality map. Reveals systematically strong/weak times of day (e.g. open and close drift) — the basis for time-of-day entry filters.
  2. Execution timing. Avoid placing passive orders in buckets with adverse mean drift.

Common pitfalls ​

  • Bar spacing vs buckets. Choose buckets consistent with your bar interval; 24 buckets on 5-minute bars groups twelve bars per bucket.
  • Survivorship. Means are unweighted across days; a few extreme days can dominate a thinly-populated bucket early on.

See also ​