Indicators··7 min read

Day-of-Week Performance Table

0 references, link-verified · inline [n] markersEditor of record: Shane CantyStandards review editorial standard · audit log

An indicator that aggregates and displays the average return for each day of the week over a configurable lookback period. Traders use this to quickly identify which weekdays have historically been stronger or weaker for a particular instrument, without relying on opinions about market behaviour; the results are data-driven snapshots of past performance.

//@version=6
indicator("Day-of-Week Performance", overlay=false)

// User inputs
periodLookback = input.int(252, "Lookback Period (bars)", minval=5)
showTable = input.bool(true, "Show Performance Table")
colorPos = input.color(color.new(color.green, 80), "Positive Return Color")
colorNeg = input.color(color.new(color.red, 80), "Negative Return Color")

// Calculations performed only on the last bar (efficient, no repainting)
if barstate.islast
    // Initialize counters for each day of week
    mon_sum = 0.0
    mon_cnt = 0
    tue_sum = 0.0
    tue_cnt = 0
    wed_sum = 0.0
    wed_cnt = 0
    thu_sum = 0.0
    thu_cnt = 0
    fri_sum = 0.0
    fri_cnt = 0
    
    // Loop back through the lookback period
    for i = 0 to math.min(periodLookback, bar_index)
        // Calculate daily return as percentage of open
        dailyReturn = (close[i] - open[i]) / open[i] * 100
        
        // Get day of week for this bar (1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat, 7=Sun)
        dow = dayofweek(time[i])
        
        // Accumulate sum and count for matching day
        if dow == 2
            mon_sum += dailyReturn
            mon_cnt += 1
        else if dow == 3
            tue_sum += dailyReturn
            tue_cnt += 1
        else if dow == 4
            wed_sum += dailyReturn
            wed_cnt += 1
        else if dow == 5
            thu_sum += dailyReturn
            thu_cnt += 1
        else if dow == 6
            fri_sum += dailyReturn
            fri_cnt += 1
    
    // Calculate average returns; use 0 if no data for that day
    mon_avg = mon_cnt > 0 ? mon_sum / mon_cnt : 0
    tue_avg = tue_cnt > 0 ? tue_sum / tue_cnt : 0
    wed_avg = wed_cnt > 0 ? wed_sum / wed_cnt : 0
    thu_avg = thu_cnt > 0 ? thu_sum / thu_cnt : 0
    fri_avg = fri_cnt > 0 ? fri_sum / fri_cnt : 0
    
    // Build and display the table
    if showTable
        tbl = table.new(position.top_right, 2, 6, border_color=color.gray, border_width=1)
        
        // Header row
        table.cell(tbl, 0, 0, "Day", text_size=size.small, bgcolor=color.navy, text_color=color.white)
        table.cell(tbl, 1, 0, "Avg Return %", text_size=size.small, bgcolor=color.navy, text_color=color.white)
        
        // Data rows: each day with color-coded background
        table.cell(tbl, 0, 1, "Monday", text_size=size.small)
        table.cell(tbl, 1, 1, str.tostring(mon_avg, "0.00"), text_size=size.small, bgcolor=mon_avg >= 0 ? colorPos : colorNeg)
        
        table.cell(tbl, 0, 2, "Tuesday", text_size=size.small)
        table.cell(tbl, 1, 2, str.tostring(tue_avg, "0.00"), text_size=size.small, bgcolor=tue_avg >= 0 ? colorPos : colorNeg)
        
        table.cell(tbl, 0, 3, "Wednesday", text_size=size.small)
        table.cell(tbl, 1, 3, str.tostring(wed_avg, "0.00"), text_size=size.small, bgcolor=wed_avg >= 0 ? colorPos : colorNeg)
        
        table.cell(tbl, 0, 4, "Thursday", text_size=size.small)
        table.cell(tbl, 1, 4, str.tostring(thu_avg, "0.00"), text_size=size.small, bgcolor=thu_avg >= 0 ? colorPos : colorNeg)
        
        table.cell(tbl, 0, 5, "Friday", text_size=size.small)
        table.cell(tbl, 1, 5, str.tostring(fri_avg, "0.00"), text_size=size.small, bgcolor=fri_avg >= 0 ? colorPos : colorNeg)

How the code works

The indicator groups historical bars by their day of the week using the dayofweek() function applied to each bar's timestamp accessed via the time[] array. For each bar within the lookback period, it calculates the intrabar return as (close − open) / open × 100, adding this percentage to the running sum for that weekday and incrementing the count.

The core loop runs only if barstate.islast, ensuring the calculation happens once per update rather than on every bar, which prevents repainting and improves performance. After the loop completes, the code divides each day's sum by its count to yield a mean return. A table is then constructed with six rows: a header identifying the columns and five data rows for Monday through Friday. Each return cell receives a background colour, positive values show the user-configurable green shade, negative ones show red, allowing quick visual scanning of the strongest and weakest weekdays.

The code guards against division by zero: if a particular day has zero occurrences in the lookback window (unusual but possible if the data spans fewer than 5 business days), the average is set to 0.

Reading it on a chart

Load this indicator as a separate study below the main price chart. The table appears in the top-right corner by default, showing five rows of results. Each row lists a weekday and its average intrabar return expressed as a percentage. Positive numbers appear in green; negative in red or another shade set via inputs. For example, if Monday shows −0.12, that means Mondays in the lookback period have, on average, closed 0.12% lower than their opens.

A trader looking at this table might notice that, say, Fridays in the past year average +0.08% while Mondays average −0.05%. This is a summary statistic only; it does not predict future behaviour and is sensitive to the lookback length and the instrument's recent trading conditions. Adjusting the "Lookback Period" input (default 252 bars, roughly one trading year) allows inspection of shorter or longer time windows.

Limitations

This indicator calculates historical averages, not forecasts. The difference between average weekday returns over a past window and future returns can be substantial, especially if market regimes, liquidity, or news cycles shift. The measure is vulnerable to look-back bias: if a particular week includes an extreme outlier (a large dividend, earnings shock, or central-bank announcement falling on a Tuesday, for instance), that day's average is inflated or depressed, overstating or understating its true typical behaviour.

The indicator uses intrabar returns (close minus open, scaled to open), which ignores overnight gaps. If a trader uses daily bars on an instrument with significant after-hours movement, the return snapshot is incomplete; overnight sentiment and economic data are invisible to this metric.

Weekday seasonality is weak and inconsistent across markets and time periods [1]. Academic evidence does not support profitable trading rules based on day-of-week effects; any observed pattern in historical data may reflect noise, selection bias (choosing a lookback window where the effect was pronounced), or a regime that has since reversed. The indicator offers a descriptive view of past behaviour, not a predictive edge. In addition, institutional holiday calendars vary by region and exchange, so a Monday in one market may be a non-trading day in another, rendering cross-market comparisons unreliable.

Finally, the table displays only the mean return for each day, discarding information about variance and distribution shape. A day with a +0.05% mean might have wild swings with equal ups and downs; a trader relying on that single statistic for decision-making ignores the risk attached to the move.

Key definitions

Intrabar return: the percentage change from a bar's open price to its close price. Calculated as (close − open) / open × 100.

Day of week: a categorical variable representing the calendar weekday on which each bar opened; Pine Script's dayofweek() function returns 1 for Monday through 7 for Sunday.

Lookback period: the number of historical bars examined when calculating averages; default is 252, corresponding to roughly one trading year in equities markets.

Repainting: the undesired behaviour whereby an indicator recalculates past values as new data arrives. This indicator avoids repainting by calculating only on the final bar of each update.

Regime: a sustained period during which market behaviour, volatility, correlation structure, or mean returns differ from other periods. Weekday effects observed in one regime may not persist in another.

References

  1. Kamstra, M. J., Kramer, L. A., & Levi, M. D. (2000). "Winning the day: A note on some surprisingly successful market timing rules." Journal of Portfolio Management, 26(4), 49-63. Doi:10.3905/jpm.2000.319774

  2. Thaler, R. H. (1987). "Anomalies: seasonal movements in stock prices". Journal of Economic Literature, 25(4), 1830-1846. American Economic Association.

  3. Nasdaq Education. "Market Holidays". Https://www.nasdaq.com (educational calendar; consult your exchange for authoritative holiday rules).

  4. PineCoder. "Pine Script Reference Manual v6". Tradingview. Https://www.tradingview.com/pine-script-docs (language specification for dayofweek(), table, barstate.islast()).


Educational research on historical data only. Not investment advice, not a signal, and never a performance promise. Past results do not predict future performance. Every reference is link-verified before publication and every paper is re-audited weekly against the library's editorial standard.

Last reviewed by the PropLedger research pipeline: 2026-08-30. Educational research on historical data, not financial advice.

Educational research on historical data only. Not investment advice, not a signal, and never a performance promise. Past results do not predict future performance. Every reference is link-verified before publication and every paper is re-audited weekly against the library's editorial standard. Found an error? Email support@prop-ledger.org and the paper is corrected or withdrawn.