Skip to content
All hypotheses

Hypotheses

je-suis-tm 'Oil Money' faithful port: 50-day OLS of XOP on USO, R2>0.7 validity gate, LONG on close above fitted+2 sigma / SHORT below fitted-2 sigma, 10-bar counter time exit, symmetric 3.7% stop (XOP.USEQ traded, USO.USEQ regressor, 1-DAY, long-short, frozen defaults)

This is a faithful translation of 'Oil Money Trading backtest' by je-suis-tm (quant-trading repo, Apache-2.0, commit 611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5). All credit for the strategy goes to the author. The code…

Hypothesis

This is a faithful translation of 'Oil Money Trading backtest' by je-suis-tm (quant-trading repo, Apache-2.0, commit 611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5). All credit for the strategy goes to the author. The code is a tradeable strategy (signal_generation produces long/short signals). The remaining functions (oil_money, plot, profit, and the portfolio mark-to-market) are scaffolding and visualization. INSTRUMENT MAPPING, the one unavoidable substitution: the original regresses NOKJPY (y, traded) on Brent……Show moreShow less

This is a faithful translation of 'Oil Money Trading backtest' by je-suis-tm (quant-trading repo, Apache-2.0, commit 611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5). All credit for the strategy goes to the author. The code is a tradeable strategy (signal_generation produces long/short signals). The remaining functions (oil_money, plot, profit, and the portfolio mark-to-market) are scaffolding and visualization. INSTRUMENT MAPPING, the one unavoidable substitution: the original regresses NOKJPY (y, traded) on Brent crude (x, regressor). The platform has no FX or Brent feed. The closest tradeable analogue with the same economic structure (an oil-linked asset regressed on the oil price, trading the oil-linked asset) and decades of history is y = XOP.USEQ (S&P Oil & Gas E&P ETF, traded, primary) and x = USO.USEQ (crude oil fund, regressor/signal only, never traded). Both have daily bars from mid-2006, about 5,100 overlapping sessions. Venue: USEQ, Reg-T margin, shorts allowed, zero commission with roughly 0.02-0.05% round-trip spread and impact modeled. Timeframe: 1-DAY bars, as in the original daily data. ALGORITHM, line for line from signal_generation (defaults holding_threshold=10, stop=0.5, rsquared_threshold=0.7, train_len=50). State: holding in {-1,0,1}, trained=False, counter=0, frozen coefficients (a,b), sigma, entry_price. The loop starts at bar index train_len, so the first 50 aligned bars are warm-up. On each aligned daily bar i (XOP and USO closes for the same session both present): (A) If holding != 0: (A1) if counter > holding_threshold, exit (signal = -holding), set holding=0, trained=False, counter=0, and do NOTHING else this bar (continue, no refit and no entry). (A2) Otherwise, if |close_y[i] - entry_price| >= stop, exit the same way and continue. (A3) Otherwise counter += 1. Note that the counter starts at 0 on the entry bar and is incremented after the checks, so the time exit fires on the 12th bar after the entry bar. (B) If holding == 0: (B1) if not trained, fit OLS with a constant, y ~ a + b*x, on the PREVIOUS 50 bars [i-50, i), excluding bar i. If R-squared > rsquared_threshold (strict), set trained=True, freeze a and b, and set sigma = population std (ddof=0) of the in-sample residuals. If R-squared fails, stay untrained and retry the fit on the next bar with the window rolled forward. (B2) If trained (this includes the bar where the model was just fitted), forecast_i = a + b*close_x[i] using the CURRENT bar's USO close and the FROZEN coefficients. If close_y[i] > forecast_i + 2*sigma, go LONG (holding=1, entry_price=close_y[i]). Else if close_y[i] < forecast_i - 2*sigma, go SHORT (holding=-1, entry_price=close_y[i]). Note the polarity: the source goes LONG when the traded asset is ABOVE the upper band and SHORT when BELOW the lower band. This is a residual-breakout / follow direction, not a fade. Translate it exactly and do not 'correct' it to mean reversion. While trained and flat, the model is never refit. The same frozen a, b and sigma are applied to each new x until a trade happens and then exits (exit sets trained=False and forces a fresh fit on the next bar). The 1-sigma bands and the band-collapsing after entry only affect the plot and do not change trading logic, so omit them. PARAMETER TRANSLATION: stop=0.5 is in absolute NOKJPY points. NOKJPY traded around 12-14 over the author's sample, so 0.5 points is about 3.7% of price. The port expresses it scale-free as stop_fraction=0.037 of entry_price (|close/entry_price - 1| >= 0.037), because 0.5 dollars on a ~$40-150 ETF would be a different rule. All other defaults are kept verbatim. SIZING: the original buys a fixed share count capital0//max(close over the WHOLE series), which is look-ahead and is replaced. The port sizes each entry at position_fraction=0.95 of current account equity in whole XOP shares (1x, about the original's all-in notional), for longs and shorts alike. EXECUTION: the source fills at the signal bar's close. On the platform, signals are evaluated on the daily close and filled by market order at the next session open. The stop and time checks reference entry_price = the signal-bar close, as in the source (df[y][signals!=0].iloc[-1]). PRE-STUDY (research lead's pandas replay of the exact loop on catalog closes, 2006-2026, close-to-close fills, no costs; unverified): 144 trades, mean +0.38%/trade, win rate 56%, profit factor 1.16. Robustness check with XLE as y: 137 trades, +0.22%/trade, PF 1.10. The edge is thin, so the platform backtest decides.

Analysis

Oil Money Trading backtest

Backtest and paper results are hypothetical. Trading involves risk of loss.