Resample daily stock prices into weekly OHLCV bars
To convert daily stock data into weekly OHLCV, group actual sessions under an explicit weekly boundary, take the first open and last close, calculate the high and low extrema and sum compatible volume and turnover. Then compare observed sessions with the market calendar. A row with two daily observations can be a valid unfinished week or an incomplete historical week; its OHLCV values alone cannot distinguish those states.
This guide actually aggregates a synthetic single-stock dataset and creates a weekly CSV string. It separates complete, open and missing-data weeks. It does not run a CoTrading frequency-conversion service or imply that one market’s calendar applies worldwide. Input bars are assumed already validated and completed at the declared daily cutoff.
Freeze the aggregation contract
Record the security, market timezone, raw or adjusted basis, volume unit, amount currency and daily-session definition. All rows in one group must use the same contract. A split-adjusted close mixed with raw opens cannot become a coherent weekly bar merely through aggregation. Shares, board lots and currency turnover also require separate treatment.
Our fixture uses fictional USD stock observations, volume in shares and amount in USD. Weeks are labeled by their Monday date. The date label is a grouping convention; it is not necessarily the date of the first actual session. For a holiday Monday, the first open would come from the following observed eligible session, while the label could remain Monday.
The pandas resample reference documents frequency conversion and the importance of bin boundaries and labels. The runnable example below uses the standard library instead of pandas, making those choices explicit in its grouping key. A different library default must be checked against this declared convention before results are compared.
Supply the market calendar and observation cutoff
The first example week contains 23, 24, 25 and 27 November 2026. The NYSE calendar identifies Thanksgiving on 26 November and an early close on the following day. We do not invent a daily bar for the holiday or rescale the early-close volume into an imagined full session.
The supplied calendar continues with 30 November and 1–4 December. Our simulated observation cutoff is after the completed daily bar on 1 December. The second week therefore has two available daily rows but three future expected sessions. It is still open, not historically complete.
The script’s calendar is a small explicit fixture list, not an exchange-calendar generator. For A-shares, Hong Kong or other US markets, obtain the applicable schedule and timezone separately. A list of weekdays cannot replace exchange holidays, and a calendar alone cannot establish whether a particular stock was suspended or newly listed.
Define each field’s aggregation
| Weekly field | Operation on ordered accepted daily bars |
|---|---|
| Open | first session’s open |
| High | maximum daily high |
| Low | minimum daily low |
| Close | last session’s close |
| Volume | sum of daily volume in one unit |
| Amount | sum of daily turnover in one currency |
Do not average daily opens or closes to obtain weekly OHLC. Do not sum a price field. Amount is a separately supplied traded-value field; multiplying the weekly close by weekly volume generally answers a different question. Retain first and last contributing session dates so the chosen open and close can be traced directly.
The week key subtracts the date’s weekday offset. Python’s datetime documentation supports the date arithmetic used here. Real timestamps need conversion to the intended session date before this step; grouping unconverted UTC dates can put a bar into the wrong local week.
Execute the grouping and status calculation
values = sorted(values)
weekly_open = values[0][1]
weekly_high = max(row[2] for row in values)
weekly_low = min(row[3] for row in values)
weekly_close = values[-1][4]
weekly_volume = sum(row[5] for row in values)
weekly_amount = sum(row[6] for row in values)
The full fixture checks duplicate daily keys and sessions outside the declared calendar or cutoff. It compares observed dates with expected dates already due. Missing past sessions produce missing_data; with no past gap but future sessions remaining, the status is open_week; a fully observed ended week is complete.
Download the complete example. Use Python 3.12+, the standard library and synthetic inputs; run python R533.py. The authoring source is docs/growth/research/round-5/examples/R533.py.
Actual first-week output is open 10, high 15, low 9, close 14, volume 750 and amount 8750. Its four observed sessions match four expected sessions, so it is complete. The second week has open 14, high 17, low 13, close 16, volume 210 and amount 3050, with two of five expected sessions observed; it remains open.
The script writes both rows into an in-memory CSV and reads it back, asserting two output records. It also reverses the daily input order and verifies unchanged weekly results. Thus input sorting is part of the method, not an assumption about the provider’s response order.
Distinguish an unfinished week from a missing session
The different-mechanism counterexample removes 24 November from the first week. That week has ended by the simulated cutoff, but an expected past session is absent. The script marks missing_data and identifies the date. It does not simply mark the row complete because a Friday close exists.
The second week is different: its unobserved Wednesday through Friday have not occurred at the cutoff. Retain provisional weekly values for inspection, but do not let a strategy consume them as a finalized weekly signal. If a past gap and future sessions coexist, missing-data status takes precedence in this fixture, with the precise missing dates retained.
Holiday absence is neither case. Thanksgiving is outside the expected-session list, so it does not create a repair request. Filling that date with the prior close and zero volume would introduce a synthetic observation into the lineage even if some aggregate totals happened to remain unchanged.
Reconcile and publish complete versus provisional outputs
For each weekly row, retain the contributing dates, observed and expected counts, status, unit metadata and input snapshot. Have a reviewer locate the first open, last close and extrema in the original daily table, then independently add volume and amount. This checks field aggregation separately from session coverage.
Export complete weeks for consumers that require finalized data and retain open or missing-data weeks in separate outputs or clearly marked rows. The fixture prints a combined CSV with explicit status; the adopting consumer must filter according to its declared policy rather than discarding that column during import.
When a daily row is corrected, rebuild its entire week and compare every aggregated field. A corrected middle-day high can change weekly high without changing weekly close, so rerunning only the final daily row is insufficient. A new completed session extends the open week’s last date and totals until its calendar boundary is satisfied.
Keep the earlier weekly version for research runs that already referenced it. The result is an executable daily-to-weekly data handoff with a field reconciliation and a completion boundary, not a generic discussion of frequency alignment or a guarantee that the underlying stock history is complete.
Inspect lineage and completely absent weeks
Build the output week list from the supplied calendar, not only from dates found in the price file. Otherwise an entirely absent historical week can disappear without a missing-data row. The full fixture includes an extra assertion that removes every first-week observation: that week remains present with missing-data status and null price fields. It does not receive a fabricated zero close or zero volume.
Keep the grouping label separate from the last observed session and the completion decision. A label can say Monday while the final accepted observation is Friday, or Thursday in a different holiday pattern. A downstream consumer should use the completion metadata rather than guess availability from the label.
For a correction, compare old and new weekly field contributions. A daily-volume amendment changes the sum, while a changed daily high matters only if it alters the maximum. Retaining the contributing dates lets the reviewer locate that difference directly. This lineage is also useful when an accepted daily row is later withdrawn: rebuild the week, reopen its coverage state and keep the earlier published version tied to its original daily snapshot.
FAQ
Should a holiday receive a zero-volume daily bar?
Not in this method. Aggregate actual accepted sessions and retain the calendar evidence for the absence.
Is a Friday close enough to finalize a week?
No. Verify all expected sessions and the observation cutoff; an earlier session may still be missing.