This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Plot all three curves | |
| plt.figure(figsize=(12, 5)) | |
| plt.plot(eq_agent_oos.index, eq_agent_oos.values, | |
| label='Agent-only (LLM policy)') | |
| plt.plot(eq_guard_oos.index, eq_guard_oos.values, | |
| label='Agent + Guardrails') | |
| plt.plot(eq_bh_oos.index, eq_bh_oos.values, | |
| label='Buy & Hold AAPL', linestyle='--', color='gray') | |
| plt.title(f'{SYMBOL}: OOS equity curves (from {OOS_START})') | |
| plt.xlabel('Date'); plt.ylabel('Equity (rebased to 1.0)') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| mt = metrics_table({ | |
| 'Agent-only': eq_agent_oos, | |
| 'Agent + Guardrails': eq_guard_oos, | |
| 'Buy & Hold AAPL': eq_bh_oos, | |
| }) | |
| # Expected output (approximate: depends on your LLM's policy calls): | |
| # | |
| # Sharpe CAGR Ann.Vol MaxDD | |
| # Agent-only 1.066 19.83% 18.60% -23.58% |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # One continuous agent position series across the whole OOS window | |
| agent_parts = [] | |
| params_by_day = {} | |
| for idx, policy, guard in month_blocks: | |
| seg = feat_all.loc[idx] | |
| agent_parts.append(build_agent_positions( | |
| seg, policy, | |
| state_lag = state_lag_full.loc[idx], # cross-month continuity | |
| vol_lag = vol_lag_full.loc[idx], | |
| )) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def run_walk_forward_oos(feat_all): | |
| oos_start = pd.to_datetime(OOS_START) | |
| months = month_starts(feat_all.index, oos_start) | |
| cache = load_cache(POLICY_CACHE_FILE) | |
| # Compute lags over FULL history so month boundaries never force flat. | |
| # state_lag[t] = state at close of t-1, available before t opens. | |
| state_lag_full = feat_all['state'].shift(1) | |
| vol_lag_full = feat_all['vol'].shift(1) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # A4: re-optimize guardrails every month (OPT_FREQ_MONTHS=1) | |
| if best_guard is None or ((m.month - 1) % OPT_FREQ_MONTHS == 0): | |
| best_guard = optimize_guardrails(train, policy) | |
| print('Re-opt guardrails @', m.date(), '->', best_guard) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| flat_days = 0 # counts consecutive days at zero exposure | |
| for t in df_seg.index: | |
| dd = equity / peak - 1.0 | |
| target = float(proposed_pos.loc[t]) | |
| if cooldown > 0: | |
| target = 0.0 | |
| cooldown -= 1 | |
| else: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def build_agent_positions(df_seg, policy, state_lag=None, vol_lag=None, mom_lag=None): | |
| if state_lag is None: state_lag = df_seg['state'].shift(1) | |
| if vol_lag is None: vol_lag = df_seg['vol'].shift(1) | |
| pos = pd.Series(0.0, index=df_seg.index) | |
| for t in df_seg.index: | |
| s = state_lag.loc[t] | |
| if pd.isna(s): continue | |
| rec = policy.get(s, {'action': 'LONG', 'size': 0.5}) | |
| base = action_to_base_exposure(rec['action'], rec['size']) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def mom_tilt(base, m): | |
| if pd.isna(m): return base | |
| tilt = 1.15 if float(m) >= 0 else 0.85 | |
| return base * tilt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def vol_scale(base_exposure, realized_vol): | |
| # Scale toward TARGET_VOL=0.25 using yesterday's realized vol. | |
| # Yesterday's vol avoids lookahead: we only know today's vol at close. | |
| if not USE_VOL_TARGET or base_exposure == 0.0: | |
| return base_exposure | |
| if pd.isna(realized_vol) or realized_vol <= 0: | |
| return base_exposure | |
| return base_exposure * min(TARGET_VOL / realized_vol, MAX_LEVERAGE) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def action_to_base_exposure(action, size): | |
| # Maps LLM output to a base exposure in [0.5, 1.0]. No shorts. | |
| if action == 'LONG': | |
| return LONG_FULL_EXP if size >= 1.0 else LONG_HALF_EXP # 1.0 or 0.8 | |
| return FLAT_EXP # 0.5: never fully exit |
NewerOlder