Table of contents
Define the clocks first
For daily open and close , decompose the close-to-close log return into an overnight and intraday component:
The sum is exactly the close-to-close return. That identity is simple; producing trustworthy inputs is not. Corporate actions, exchange calendars, timestamp conventions, and the definition of “open” can all move the result.
A minimal calculation
frame["overnight"] = np.log(frame["open"] / frame["close"].shift())
frame["intraday"] = np.log(frame["close"] / frame["open"])
frame["close_to_close"] = np.log(
frame["close"] / frame["close"].shift()
)
check = frame["overnight"] + frame["intraday"]
np.testing.assert_allclose(check.dropna(), frame["close_to_close"].dropna())
The assertion is more than housekeeping. It protects the economic interpretation by ensuring the two return windows tile the full day without overlap or omission.
Aggregate facts are not strategies
If one component has a larger historical mean, that does not establish a tradeable anomaly. Execution prices, financing, survivorship, changing market structure, and the choice of sample all intervene.
The decomposition is still useful. It tells us when a return was earned and creates a sharper starting point for questions about information arrival and risk transfer.