Optimization answers a search question: which configuration of your strategies would have maximized
(or minimized) a chosen metric on the backtest, subject to constraints. Two typical asks: "how many
contracts of A and B for the highest MAR?" and "pick the subset of strategies that stays under $10,000
of margin and maximizes Sortino." The engine explores the space of integer contracts per strategy — a
count of 0 drops a strategy, so subset selection falls out of the same search — and returns the
configuration that scored best, with how well it holds up out-of-sample.
An optimized configuration is an in-sample result
This is the single most overfitting-prone thing in VEEMAN: maximizing a metric on past data also fits
noise. The result is framed as "the configuration that would have maximized X on the backtest" — never
advice, never a prediction. Always read the out-of-sample hold and the resampling band (below)
before trusting the headline number. The decision to trade, and how, is always yours.
Two ways in
The optimizer is one engine with two front-doors that produce the identical result:
This page (manual)
Give each strategy a role, pick the objective and the drawdown you tolerate, then press Optimize. You get the full result: winner, robustness cards, the candidate cloud, the best combinations and the objective surface.
Veeman AI (chat)
Ask in plain language. Veeman AI runs the same engine and answers with a compact proposal card you can Apply, Save as a variant, or Open here pre-filled.
Allocation mode
Contracts can be sized two very different ways: by maximizing a past metric, or by balancing tail risk.
Maximizing an in-sample metric is the most overfitting-prone thing you can do (it selects noise as readily as
edge), so for serious contract allocation the risk-based modes are usually the more defensible answer. Pick a
mode at the top of the form:
Classic metric
The search described below: find the mix of contracts that would have maximized MAR/Sortino/Sharpe on the backtest, under the constraints and the capital-adequacy guard. Powerful, but fragile — read the robustness cards before trusting it.
Risk parity
Sizes contracts so every strategy contributes the same tail risk to the portfolio, ignoring past returns entirely. Robust by construction. See Risk-based allocation.
Hybrid
The risk-parity backbone plus a bounded tilt toward your chosen metric, applied only if it holds up out-of-sample — balanced risk, with an edge grafted on only where the data supports it.
Inputs
A role per strategy
Every strategy gets one of three roles, and the difference matters. Vary puts it in play: it is searched over integer contract counts from a Min to a Max at a Step (set Min to 0 to let it be dropped, or use the 0/1 preset for a pure in/out selection, which pairs naturally with the margin and max-active-strategies constraints). Pin leaves it at exactly the contracts it has now and optimizes every other strategy around it — the way to say "this one is settled, fit the rest to it". Drop forces it to 0 contracts in every combination tried. When you first set a strategy to Vary, its range is anchored around its current contracts, so the search explores near what you already run rather than an arbitrary 0–10.
Objective
A risk-adjusted metric to maximize: MAR/Calmar, Sortino, or Sharpe — the exact metric shown elsewhere in the app. Each genuinely needs the engine: it finds the best mix per unit of risk, regardless of scale. Deliberately not objectives, because each has a degenerate optimum: drawdown / volatility (minimizing risk alone rewards the smallest position — at the limit, not trading), CAGR (maximizing growth alone rewards maximum leverage — cranking every contract to the ceiling), and win rate / profit factor (maximizable without a real edge). For growth you want Calmar (return per unit of drawdown); to cap risk, use a constraint (max drawdown / max margin).
Max drawdown you tolerate
The one input that decides how hard the engine may push, so it is shown first, next to your portfolio's current drawdown and the ceiling that would be applied if you leave it blank. Leaving it blank is a choice, not an absence: the capital-adequacy guard below then anchors a ceiling to your current risk. A candidate that breaks it is discarded from the search.
Other constraints
Optional caps: max margin ($) and max number of active strategies (cardinality). Same rule — a candidate that breaks a constraint never wins.
Robustness
On by default: adds the out-of-sample split, the walk-forward windows, the selection tests, the candidate cloud, the Monte Carlo band and the sensitivity check to the result.
Capital-adequacy guard
Contracts are a linear multiplier on P/L, so with no risk cost a return objective would push leverage to the
ceiling — configurations that, on their own backtest, would have blown up the account. To prevent this (not
merely warn about it), every optimization runs by default under a capital-adequacy guard:
Drawdown anchored to your current risk
The winner's backtest drawdown cannot exceed your current configuration's drawdown by more than a small multiple (with a floor so a tiny current drawdown still allows rebalancing, and an absolute survivability ceiling that always applies). The optimizer rebalances the mix within your current risk envelope; to scale risk up you raise the cap explicitly.
Margin ≤ capital
Where your files carry margin data (e.g. Option Omega), the winner's peak portfolio margin cannot exceed your capital — you cannot be proposed more contracts than the account can hold.
Ruin-aware verdict
If the Monte-Carlo probability of ruin is high, the verdict is Capital at risk regardless of how real the edge is — a strong edge sized to ruin is still a losing proposition.
Mix is not size — and drawdown is not tail risk
The optimizer finds the best mix of your strategies — what matters in the result is the relative weighting,
not the absolute contract counts. Those counts are chosen to maximize the chosen metric on the backtest, with no
notion of per-trade margin or risk of ruin, so a contract count here is not how much to trade. Actual position
sizing, with per-trade margin and ruin math, is per-strategy sizing in the workspace. And when a strategy carries no margin data, the guard can only
cap historical drawdown, which for options understates tail risk: a single gap can lose many multiples of
the premium. The result says so, and points you toward per-trade sizing. These guards are defaults you can widen or
remove in the constraints — impersonal survivability lines, not a risk profile.
Global optimum vs best found
The engine picks the method by the size of the search space, and always tells you which guarantee you got:
Global optimum (small spaces)
The whole space is enumerated — the winner is provably the best. The result shows "Global optimum" and the number of candidates evaluated.
Best found (large spaces)
Beyond a size cap the engine switches to a seeded heuristic search: a multi-start local search that combines single-axis moves, swaps for subset problems, and pairwise (2-opt) moves that optimise two weights together to escape diagonal ridges when strategies interact. It keeps restarting — half random, half guided by the current leaderboard — until it stops improving or spends its evaluation budget. The result shows "Best found" and the number of evaluations. It is deterministic (same inputs give the same answer) but not guaranteed to be the global optimum.
Restarts agree (confidence)
Next to "Best found" the result reports how many independent restarts, from different starting points, ended at this configuration — e.g. "14/16 restarts agree". Many means the optimum sits in a wide basin and the search found it repeatedly; few means it is an isolated maximum that one lucky restart stumbled on, which is both less certain to be the best and more fragile if it is. This is measured on your data at run time, not quoted from a benchmark.
Sortino ignores degenerate spikes
When the objective is Sortino, the optimizer will not hand you a configuration that scored a sky-high ratio just because its downside deviation was near zero on the backtest — whether from almost no losing days, or from many losing days that were all tiny. A near-zero denominator makes the ratio a division artifact, not a robust edge. Such candidates are excluded from selection (their displayed Sortino elsewhere in the app is unchanged), and if one had the highest nominal Sortino the result carries a note saying so. The winner is the best configuration with a real loss profile.
Robustness
This is what separates a serious answer from curve-fitting. Every result (with robustness on) carries an
overall verdict — robust, weak, likely overfit, or insufficient sample — backed by these checks. The
verdict is deliberately punitive: "robust" requires the selection to beat luck, to generalize, and to hold
out-of-sample, all at once.
Out-of-sample vs baseline
The engine optimizes on the first part of the history, then measures that winning configuration on the later part it never saw — and compares it to your current configuration on the same unseen data. The question that matters is not "the winner scores X out-of-sample" but "does it beat the config you already have, on data it was not chosen on".
Walk-forward (k windows)
The same test repeated across several anchored windows: for each, the engine re-optimizes on the past and checks whether the winner beats the baseline on the next unseen window. The result reports the share of windows won — a single split can be lucky, several rarely are.
Max test — is it luck?
Searching many configurations and keeping the best one finds noise even when there is no skill: the maximum of many tries is high by chance. The max test measures this directly by resampling — the probability that chance, over this exact search space and with the real correlation between candidates, produces a winner as good as yours. It is an empirical test (a bootstrap Reality Check), not an asymptotic formula: no normality, no independence assumed.
PBO — does it generalize?
The probability of backtest overfitting (via combinatorially symmetric cross-validation): across all balanced train/test splits, how often the in-sample winner ends up below the out-of-sample median. Around 0.5 means the selection is a coin flip; low means it generalizes. Distribution-free — exact counts, no distribution assumed.
Monte Carlo band
The winner's daily P/L is resampled (block bootstrap) to show a distribution of outcomes — the p05, p50 and p95 return and the probability of ending at a loss — not a single point.
Sensitivity
Perturbing the winner by one step per variable shows whether the optimum is a broad plateau (robust) or a sharp peak (fragile).
Every result carries an assumptions ledger: what is an exact count (candidates evaluated, cross-validation
splits, walk-forward tallies) and what is resampling (block bootstrap, with its block length, number of resamples
and seed printed). The one residual assumption is the validity of the block bootstrap — the same one the Monte
Carlo uses. What these checks cannot see is the search done upstream, outside VEEMAN, when the strategy was
built: how many parameter sets were tried and discarded before you loaded it. That multiplicity is unobservable
from the final series and is not corrected here — the verdict is bounded to "survives the selection this
optimization performed". The only assumption-free test against upstream overfitting is performance on new data
never optimized on — the Equity Control module.
The winner among the others
A winning number on its own tells you nothing about how much to trust it. Two views put it back in context.
Best combinations. The configurations right behind the winner, with how far back each one is, and your
current configuration on the same row scale for comparison. If the top ten sit within a percentage point of
each other, the winner is as arbitrary as its neighbours and you should pick the one that costs you least risk,
not the one that came first. If it stands clear of the field, that is worth knowing too.
The candidate cloud. One dot per configuration: horizontally how it scores on the first part of the
history, vertically how the same configuration scores on the second part — the same split the
out-of-sample card uses. The diagonal is "holds exactly". Read the shape:
Dots along the diagonal
Being good on the first half predicts being good on the second: the ranking carries information, and a winner from it is worth something.
A shapeless ball
The ordering does not survive. On this data the backtest ranking is noise, and no winner drawn from it is credible — however high its number.
Ranking hold
The same thing as one number: the rank correlation (Spearman) between the two halves, over the admissible candidates. It is computed on ranks, not values, so a single outlier cannot flatter it.
Three markers are called out: the winner, the winner of the first part alone, and your current
configuration. Dimmed dots are candidates the constraints had already excluded — they are drawn only to give
the cloud its shape.
The winner's own dot is optimistic — the cloud is not
The winner was selected on the whole history, second part included, so its position in this chart is
flattering by construction and is not an out-of-sample estimate. The honest out-of-sample figures are in the
Out-of-sample and walk-forward cards. What this chart adds is something they cannot show: whether the ranking
the winner came first in means anything.
The objective surface
The result always includes a heatmap of the objective — a cross-section through the winner. Dimmed cells
break the constraints, and the cell with the highlighted border is the winner itself. Two layers:
Value
Colour is the objective value. Useful, but read it knowing that the brightest cell is also the most overfitted to the backtest — this layer teaches the eye to chase peaks.
Plateau
Colour is local hold: how much the objective falls if you move a single contract towards the worst neighbouring cell. Bright means a broad plateau that survives being slightly wrong about the future; dark means a cliff. A lonely bright spike in the Value layer that goes dark here is exactly the configuration not to trade.
A heatmap has at most two axes, so when you vary one or two strategies it shows the whole grid directly.
With three or more, it plots the two most informative strategies (the ones the objective moves most along)
and holds the others fixed at their winning contracts — the panel names which ones and at what value. Every
cell is a real evaluation by the same engine, never interpolated; if an axis has too many values to draw it is
evenly sampled for display (the winner's own value is always kept on the grid). Because it is a slice
through the optimum it stays honest about fragility: a spike still shows up as a spike.
Risk-based allocation
The risk-parity and hybrid modes answer a different question from the metric optimizer: not "what maximized a
metric" but "how do I split contracts so no strategy dominates the risk". They ignore (risk parity) or only
cautiously use (hybrid) past returns, which is exactly what makes them hold up out-of-sample.
Tail risk, not volatility
Risk is measured on the tail, not the variance. Short-premium strategies show low day-to-day volatility right up to the day they blow up, so a volatility-based risk parity would overweight the very legs hiding the most risk. The engine uses the empirical Component CVaR: it takes the worst few percent of portfolio days and decomposes that loss per strategy (an Euler decomposition, so the contributions sum to the CVaR).
Hedges recognized, not cut
A leg that gains on the portfolio's worst days (a long-put hedge, say) has a negative tail contribution. The allocator sees this: it balances the risk-adding strategies to equal tail contribution, then sizes each hedge to cover a share of the book's tail loss — bounded, never a dominant position — rather than forcing it to an equal contribution, which would be ill-posed.
Feasible on your real day-by-day capital
Contracts are rounded to whole numbers that fit your capital, and the capital used is the real account equity of each day — the Funds at Close column from an Option Omega CSV, which grows over time — not one static number. A day's margin is checked against the capital you actually had that day, not your starting balance. With no equity column it falls back to the static initial capital.
A range, not false precision
The allocation is bootstrapped: alongside each strategy's contracts you get a range for its weight, because the data does not support single-contract precision.
Hybrid tilts only when it survives out-of-sample
Hybrid starts from the risk-parity backbone and searches a bounded neighborhood of it for an allocation that
scores higher on your metric, scored by cross-validation across time windows rather than in-sample. The tilt
is kept only if it beats the backbone out-of-sample by a margin; otherwise the backbone stands and the result
says so. The deviation from the backbone is capped, so a hybrid answer is never far from balanced risk.
The capital-adequacy guard does not apply to these modes: no metric is being pushed to the leverage ceiling,
and feasibility is the exact per-day "margin ≤ that day's equity" test instead of a single "peak margin ≤
starting capital" line. The margin, drawdown and cardinality constraints are likewise ignored — the tail-risk
balance and your real capital govern the allocation.
Acting on the result
Apply to session
Writes the winning weights (or, for risk-based modes, the allocated contracts) into the live session. It is recorded in the Timeline, so it is undoable.
Save as variant
Freezes the configuration as a variant and pins it to the comparison, without touching the live session — so you can overlay it against your current setup before committing. The variant inherits every portfolio-level setting of the session it came from — groups and their scale, compounding, daily risk budget, margin cap — so its numbers match the leaderboard that produced those weights.
How it stays accurate
Same engine as the charts and tables
The optimizer scores every candidate with the same maths that powers the metrics and equity curve, and
re-checks the winner through the canonical path before returning it — so the numbers it reports match what
the rest of the app would show for that configuration. Nothing is estimated.
Limits to be aware of
Margin needs margin data
The max-margin constraint only means something for strategies whose file carries a margin column (Option Omega). If some active strategies lack it, the margin is undercounted and the constraint is reported as unverifiable rather than a false pass.
Weights, not per-trade sizing
Here a "contract" is an integer weight multiplier on the strategy's P/L. Per-trade sizing under a margin/premium budget lives in dynamic sizing.
Runs in the background
The search executes on the server as a job the page polls, so it survives switching tabs or a reload. Small searches finish in under a second; large heuristic searches take a few seconds.
In-sample by construction
See the warning at the top. Optimization selects on the past; the robustness checks exist precisely because the headline number is optimistic.