ObjectiveModel¶
Defined in fynance.models.objective
- class ObjectiveModel(net=None, *, n_assets=None, layers=(16, 8), loss=None, optimizer=torch.optim.Adam, lr=1e-3, epochs=80, batch_size=None, shuffle=True, position_fn=torch.tanh, cost=0.0, seed=0)[source]
Bases:
objectTrain a net to maximize a differentiable financial objective.
- Parameters:
- nettorch.nn.Module, optional
Architecture mapping a feature matrix
(T, F)to a position book(T, N)(N= number of assets;(T, 1)for the single-asset case). Defaults to an MLP built lazily on the firstfit(so it learnsFandN). Pass anynn.Module(e.g. a TCN/LSTM) to use a custom architecture; a custom net receivesXas the 2-D matrix(T, F)(a 3-D panel(T, N, M)is flattened to(T, N*M)first).- n_assetsint, optional
Number of assets
Nin the position book.None(default) infers it atfit: from the 2nd dimension of a 2-Dy(T, N), or from the 2nd dimension of a 3-DX(T, N, M), falling back to1for a 1-Dy(the single-asset case).- layerstuple of int
Hidden sizes of the default MLP (ignored when
netis given).- lossBaseLoss, optional
Differentiable financial loss applied to the strategy returns
positions * returns. Defaults toSharpeLoss.- optimizertype[torch.optim.Optimizer]
Optimizer class (default
Adam).- lrfloat
Learning rate.
- epochsint
Passes over the data per
fit. With full-batch (batch_sizeNone) this is the number of optimizer steps; with mini-batches it isepochs * ceil(T / batch_size)steps — far more updates, which the objective usually needs to converge on long series.- batch_sizeint, optional
Train on contiguous mini-batches of this many bars (order preserved so the turnover penalty stays meaningful).
None(default) = full batch. Mini-batching is the practical way to actually train on long (e.g. minute) series — full-batch gives onlyepochsgradient steps total.- shufflebool
When mini-batching, shuffle the order of the contiguous chunks each epoch (rows within a chunk stay ordered). Improves SGD; default True.
- position_fncallable
Maps the net output to a position; default
tanh(positions in[-1, 1]).- costfloat
Per-bar proportional turnover cost penalized during training (e.g.
0.0026for 26 bps). When non-zero the objective is computed on the net-of-cost returnpositions * returns - cost * |Δpositions|, so the net learns to hold positions instead of churning — the anti-churn brick for high-cost / high-frequency settings. Use the same value as the backtest’sProportionalCost. Default0(no penalty, original behaviour).- seedint
Seed for reproducible initialization/training.
Notes
The net is warm-started across successive
fitcalls (so a walk-forward refit adapts online). Build a fresh model for an independent run.- clone()[source]
A fresh model with the same (deep-copied) weights, trained apart.
The net is deep-copied so the clone starts from identical weights yet shares no parameter storage with the original; its optimizer is built lazily on first training, so fitting the clone leaves the original’s predictions bit-identical. This is the per-asset branch of the pretrain/finetune workflow:
pretrain_pooleda shared net, thenclone().finetune(...)once per asset.- Returns:
- ObjectiveModel
The independent copy.
Examples
>>> import numpy as np >>> from fynance.models import ObjectiveModel >>> rng = np.random.default_rng(0) >>> X = rng.standard_normal((16, 2)).astype("float32") >>> y = (X[:, 0] * 0.01).astype("float32") >>> m = ObjectiveModel(layers=(4,), epochs=3, seed=0).fit(X, y) >>> c = m.clone() >>> bool(np.allclose(m.predict(X), c.predict(X), atol=1e-7)) True >>> before = m.predict(X) >>> _ = c.fit(X, y) # train the clone ... >>> bool(np.array_equal(before, m.predict(X))) # ... original untouched True
- finetune(X, y, *, freeze_trunk=True, lr=None, epochs=None, **fit_kw)[source]
Continue training from the current weights (no reinitialization).
Unlike
fit(which warm-starts but reuses the model’s persistent optimizer and full parameter set),finetunebuilds a fresh optimizer over the currently trainable parameters, so a differentlror a frozen trunk take effect immediately. It is the adaptation step afterpretrain_pooled: pretrain one net on a pool of assets, thenfinetunea per-asset copy (seeclone) on that asset’s own data.- Parameters:
- X, yarray-like
One contiguous
(X, y)series, same shapes asfit.- freeze_trunkbool, optional
If
True(default), freeze every parameter except the head — the last parameterized leaf module (see_head_module); for the default MLP only the finalLinearhead is trained. IfFalse, all parameters (trunk included) keep training.- lrfloat, optional
Learning rate for the finetuning optimizer.
None(default) reuses the model’slr.- epochsint, optional
Passes over the data.
None(default) reusesepochs.- **fit_kw
Per-run overrides of the training hyper-parameters
batch_size,shuffle,costandseed(restored afterwards).
- Returns:
- ObjectiveModel
self.
- Raises:
- RuntimeError
If called before the model has been fitted (no weights to continue from).
Examples
>>> import numpy as np >>> from fynance.models import ObjectiveModel >>> rng = np.random.default_rng(0) >>> X = rng.standard_normal((16, 2)).astype("float32") >>> y = (X[:, 0] * 0.01).astype("float32") >>> m = ObjectiveModel(layers=(4,), epochs=3, seed=0).fit(X, y) >>> _ = m.finetune(X, y, epochs=2) # continue from current weights >>> m.predict(X).shape (16, 1)
- fit(X, y)[source]
Train the net to maximize the objective of the net-of-cost return.
- Parameters:
- Xarray-like, shape (T, F) or (T, N, M)
Feature matrix. A 3-D panel
(T, N, M)(Nassets,Mfeatures each) is flattened to(T, N*M)for the default dense net.- yarray-like, shape (T,) or (T, N)
Realized per-bar returns aligned with
X(not a supervised label). A 2-Dycarries the per-asset returns of the position book; a 1-Dyis the single-asset case (N == 1).
- Returns:
- ObjectiveModel
self.
- classmethod load(path)[source]
Reconstruct a model saved by
save.Rebuilds the model from the persisted init config (net, loss, optimizer class, position function and scalar hyper-parameters) and reloads the weights, yielding predictions identical to the saved model. A fresh optimizer is created lazily on the next
fit/finetune, so the reloaded model is immediately trainable again.- Parameters:
- pathstr or os.PathLike
File written by
save.
- Returns:
- ObjectiveModel
The reconstructed model.
See also
savethe round-trip example lives there.
- predict(X)[source]
Return the position book for
X, shape(T, N).Accepts a 2-D
X(T, F)or a 3-D panel(T, N, M)(flattened to(T, N*M)for the default net). The output is a position book with one column per asset ((T, 1)in the single-asset case). With the defaultposition_fn(tanh) positions are bounded in[-1, 1]; a customposition_fnmay produce unbounded values.
- save(path)[source]
Serialize the weights and the full init config to
path.Writes (via
torch.save) the netstate_dicttogether with everything needed to reconstruct the model: the net module, the loss, the optimizer class, the position function and the scalar hyper-parameters. Reload withload; the pair round-trips to bit-identical predictions and the reloaded model stays trainable.- Parameters:
- pathstr or os.PathLike
Destination file.
Examples
>>> import os, tempfile >>> import numpy as np >>> from fynance.models import ObjectiveModel >>> rng = np.random.default_rng(0) >>> X = rng.standard_normal((16, 2)).astype("float32") >>> y = (X[:, 0] * 0.01).astype("float32") >>> m = ObjectiveModel(layers=(4,), epochs=3, seed=0).fit(X, y) >>> path = os.path.join(tempfile.mkdtemp(), "objective.pt") >>> m.save(path) >>> restored = ObjectiveModel.load(path) >>> bool(np.allclose(m.predict(X), restored.predict(X), atol=1e-7)) True