1"""Simple neural baselines on the 1-minute branch only: MLP on the origin features and a small TCN over the last 3902one-minute bars, both with the same Student-t head and loss as MRPT (matched information set, matched tuning budget).3PatchTST-style 1m-only = MRPT(branches=['A']) (see configs/ablations.yaml); iTransformer/Chronos/TimesFM: see4docs/MODEL_CARD.md §baselines-not-run for the licensing/compatibility audit."""5from __future__ import annotations67import torch8import torch.nn as nn9import torch.nn.functional as F1011from ..data.windows import F_BAR, F_GLOBAL12from ..models.student_t import StudentTHead131415class MLPBaseline(nn.Module):16 """Flattened last 60 one-minute bars + global features -> MLP -> per-minute Student-t."""1718 def __init__(self, horizon: int = 60, n_last: int = 60, d_hidden: int = 256, dropout: float = 0.1):19 super().__init__()20 self.n_last, self.H = n_last, horizon21 self.net = nn.Sequential(nn.Linear(n_last * F_BAR + F_GLOBAL + 4, d_hidden), nn.GELU(), nn.Dropout(dropout),22 nn.Linear(d_hidden, d_hidden), nn.GELU(), nn.Dropout(dropout))23 self.fut = nn.Linear(4, d_hidden)24 self.head = StudentTHead(d_hidden)25 self.cfg = type("C", (), {"head": "student_t", "quantiles": [], "__dict__": {}})()2627 def n_params(self):28 return sum(p.numel() for p in self.parameters())2930 def forward(self, b):31 x = b["xA"][:, -self.n_last:].reshape(b["xA"].shape[0], -1)32 g = torch.cat([x, b["glob"], b["fut_cal"][:, 0]], -1)33 h = self.net(g).unsqueeze(1) + self.fut(b["fut_cal"].float())34 out = self.head(F.gelu(h), b["sigma"]); out["hidden"] = h35 return out363738class TCNBaseline(nn.Module):39 """Dilated causal 1-D convolutions over the 390 one-minute bars; last state + future calendar -> Student-t."""4041 def __init__(self, horizon: int = 60, channels: int = 96, n_blocks: int = 6, kernel: int = 3, dropout: float = 0.1):42 super().__init__()43 self.inp = nn.Conv1d(F_BAR + 1, channels, 1)44 self.blocks = nn.ModuleList()45 for i in range(n_blocks):46 d = 2 ** i47 self.blocks.append(nn.Sequential(nn.Conv1d(channels, channels, kernel, dilation=d, padding=(kernel - 1) * d),48 nn.GELU(), nn.Dropout(dropout), nn.Conv1d(channels, channels, 1)))49 self.pad = [(kernel - 1) * 2 ** i for i in range(n_blocks)]50 self.glob = nn.Linear(F_GLOBAL, channels)51 self.fut = nn.Linear(4, channels)52 self.pos = nn.Embedding(horizon, channels)53 self.head = StudentTHead(channels)54 self.cfg = type("C", (), {"head": "student_t", "quantiles": [], "__dict__": {}})()5556 def n_params(self):57 return sum(p.numel() for p in self.parameters())5859 def forward(self, b):60 x = torch.cat([b["xA"], b["mA"].unsqueeze(-1).float()], -1).transpose(1, 2)61 h = self.inp(x)62 for blk, p in zip(self.blocks, self.pad):63 y = blk(h)[:, :, :-p] if p > 0 else blk(h)64 h = h + y65 last = h[:, :, -1] + self.glob(b["glob"].float())66 H = b["fut_cal"].shape[1]67 q = last.unsqueeze(1) + self.fut(b["fut_cal"].float()) + self.pos.weight[:H].unsqueeze(0)68 out = self.head(F.gelu(q), b["sigma"]); out["hidden"] = q69 return out70