Files
PythagorasGoal/scripts/research/alt/runs/SEA07.py
T
Adriano Dal Pastro 5ac4e16af8 research(alt): sweep 104 strategie alternative su Deribit (153 agenti) + marginal scorer
Ondata di ricerca onesta a largo spettro su BTC/ETH+DVOL certificati: 104 ipotesi
distinte (11 famiglie), un agente-finder per ipotesi, verifica avversariale a 3
scettici sui promettenti, sintesi (153 agenti totali). Esito: NIENTE di nuovo regge
-> conferma del soffitto strutturale ~1.3 BTC/ETH-direzionale; lo stack
TP01+XS01+VRP01 resta imbattuto.

- altlib.py: harness condiviso vettoriale leak-free (eval_weights/study_weights,
  fee-sweep, both-asset + hold-out 2025+). Riproduce i numeri canonici di TP01.
- MARGINAL SCORER (study_marginal/marginal_vs_tp01): Sharpe INCREMENTALE vs baseline
  TP01 (corr, blend uplift OOS, alpha residua) + jackknife OOS (clean-year +
  drop-best-month). earns_slot = abs!=FAIL & ADDS & robust_oos. Smaschera gli overlay
  su TSMOM con PASS assoluti fasulli (CMB04, VOL11, ...) e il falso positivo KAMA
  (ADDS ma muore al jackknife).
- runs/*.py (104) script riproducibili per ipotesi; wf_altstrat.js workflow.
- Verdetto: 0 candidati deployabili; 2 LEAD fragili (VOL08, STA05_LS) da forward-monitor.
- test_marginal_scorer.py blocca baseline + invarianti. Suite: 32 verde.

Diario: docs/diary/2026-06-20-alt-strategies-100agent-sweep.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:50:39 +00:00

172 lines
7.3 KiB
Python

"""SEA07 — Monday Effect (expanding Monday expectancy).
IDEA: On 1d bars, use the expanding-window mean Monday return as a directional signal.
- Compute an expanding (causal) mean of Monday returns seen so far.
- If the expanding Monday mean > 0 (continuation): go long (+1) on Mondays, flat otherwise.
- If the expanding Monday mean < 0 (reversal): go short (-1) on Mondays, flat otherwise.
- Also try "Friday signal": what happened last Friday may predict the Monday direction.
We track expanding Friday return mean and use its sign to predict the following Monday.
Signal styles tested (4 configs, 1 TF = 1d, 2 assets = <=8 cells total):
1. Monday continuation: long on Mondays when expanding E[Monday ret] > 0, else flat
2. Monday always long: always long on Mondays regardless of prior expectancy (baseline)
3. Friday-to-Monday: on Monday, go in the direction of last Friday's expanding mean
4. Monday vol-adjusted: same as #1 but NOT vol-targeted (raw position, to isolate the signal)
All signals are on 1d only (as required).
Causal guarantee:
- Expanding Monday mean at bar i uses only Monday returns j < i (causal).
- Friday-to-Monday: expanding Friday mean uses only Friday returns j < i (causal).
- lib shifts position by 1 bar automatically (decided at close[i], held during bar i+1).
WAIT: Monday bar i means we hold on Monday. close[i] of a Monday is ALREADY the end of Monday.
So to hold DURING Monday, we must decide at close[i-1] (Sunday or prior day).
Implementation: set target[i] = 0 always; set target[i-1] = signal for Monday i.
But altlib shifts target[i] -> held at bar i+1. So to be in position DURING bar i:
we need target[i-1] != 0, which becomes pos[i] = target[i-1].
Correct approach: for each Monday bar at index i, we set target[i-1] = signal.
This means at close of Sunday (i-1), we enter; held during bar i (Monday).
Since 1d bars, Sunday doesn't exist: previous bar is Friday at i-1.
So: at close of Friday (i-1), we set the position to be held on Monday (i).
This is the natural way: target[i-1] = signal, lib shifts to pos[i] = target[i-1].
Expanding stats use only data BEFORE the current Monday being evaluated:
- When setting target[i-1] for Monday i: we have seen all Monday returns up to i-1 (none of
which are Mondays in typical weeks; so effectively all Mondays before this one).
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al
import numpy as np
import pandas as pd
def sea07_monday_continuation(df: pd.DataFrame, min_samples: int = 10,
use_friday: bool = False,
vol_tgt: bool = True) -> np.ndarray:
"""
Monday-effect signal on daily bars.
Parameters
----------
min_samples : int
Minimum Monday (or Friday) samples needed before trusting the expectancy.
use_friday : bool
If True, use the expanding mean of Friday returns to predict Monday direction.
If False, use the expanding mean of Monday returns (continuation/reversal).
vol_tgt : bool
Whether to apply vol-targeting to the direction signal.
"""
dt = pd.to_datetime(df["datetime"])
c = df["close"].values.astype(float)
r = al.simple_returns(c)
n = len(df)
# Day of week: 0=Monday, 1=Tuesday, ..., 4=Friday, 5=Saturday, 6=Sunday
dow = dt.dt.dayofweek.values # 0=Mon, 4=Fri
# Expanding stats for Monday and Friday returns
mon_sum = 0.0
mon_cnt = 0
fri_sum = 0.0
fri_cnt = 0
# target[i]: position decided at close[i], held during bar i+1
# To be in position DURING Monday bar i, we set target[i-1].
# target is indexed by bar where decision is made.
target = np.zeros(n, dtype=float)
for i in range(1, n):
# Update stats with bar i-1 (the bar we just closed)
prev_dow = dow[i - 1]
prev_r = r[i - 1]
if prev_dow == 0: # previous bar was a Monday
# We accumulate Monday return AFTER using it for the next decision
# (this bar i is Tuesday or later; the Monday return r[i-1] is now known)
pass # will update after computing signal for i
# Current bar i: what day is it?
curr_dow = dow[i]
if curr_dow == 0:
# Bar i is a Monday. We want to be in position during this bar.
# Decision must be made at close[i-1] (Friday or whatever preceded it).
# So we set target[i-1] based on stats available BEFORE bar i.
if use_friday:
# Use expanding Friday expectancy to decide Monday direction
if fri_cnt >= min_samples and fri_sum != 0:
fri_mean = fri_sum / fri_cnt
direction = 1.0 if fri_mean > 0 else -1.0
else:
direction = 0.0
else:
# Use expanding Monday expectancy: continuation or reversal
if mon_cnt >= min_samples and mon_sum != 0:
mon_mean = mon_sum / mon_cnt
direction = 1.0 if mon_mean > 0 else -1.0
else:
direction = 0.0
target[i - 1] = direction
# Now update the expanding stats with bar i-1's return (after using stats for bar i)
# This ensures we never use r[i-1] to decide signal for bar i
if prev_dow == 0:
mon_sum += prev_r
mon_cnt += 1
elif prev_dow == 4:
fri_sum += prev_r
fri_cnt += 1
if vol_tgt:
return al.vol_target(target, df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0)
else:
return target
if __name__ == "__main__":
results = []
# Grid: 4 configs on 1d only
grid = [
# (name_suffix, min_samples, use_friday, vol_tgt)
("mon-cont-ms10-vt", 10, False, True), # Monday continuation, vol-targeted
("mon-cont-ms20-vt", 20, False, True), # Monday continuation, more samples
("fri2mon-ms10-vt", 10, True, True), # Friday->Monday, vol-targeted
("fri2mon-ms20-vt", 20, True, True), # Friday->Monday, more samples
]
# Use study_weights (continuous position style is appropriate for "hold on Mondays")
for suffix, min_s, use_fri, vt in grid:
name = f"SEA07-{suffix}"
rep = al.study_weights(
name,
lambda df, ms=min_s, uf=use_fri, v=vt: sea07_monday_continuation(
df, min_samples=ms, use_friday=uf, vol_tgt=v
),
tfs=("1d",),
)
print(al.fmt(rep))
print("JSON:", al.as_json(rep))
print()
best_cell = max(rep["cells"], key=lambda c: c.get("min_asset_holdout_sharpe", -9))
results.append((
rep["verdict"].get("best_holdout_sharpe",
best_cell.get("min_asset_holdout_sharpe", -9)),
rep["verdict"].get("best_full_sharpe",
best_cell.get("min_asset_full_sharpe", -9)),
name,
rep,
))
# Pick best config by hold-out Sharpe (tie-break: full Sharpe)
results.sort(key=lambda x: (x[0], x[1]), reverse=True)
best_hold, best_full, best_name, best_rep = results[0]
print("\n=== BEST CONFIG ===", best_name)
print(al.fmt(best_rep))
print("JSON:", al.as_json(best_rep))