Skip to content

Learn

Stock Portfolio Research

Diagnose infeasible stock portfolio constraints

A portfolio allocation can be unavailable for several reasons: the constraints contradict each other, inputs are incomplete, the solver cannot run, or the chosen search grid misses feasible continuous weights. Give those outcomes different statuses. An equal-weight fallback that violates a sector cap is not a successful optimization simply because it contains numbers summing to one.

This guide builds a constraint ledger, a small conflict certificate and independently validated candidate outputs. Its standard-library example searches an invented three-stock, long-only allocation grid. It does not run SciPy or verify any Vibe or CoTrading constraints; optimization and diagnostic orchestration remain external methods. Keep market and currency separate.

Translate the request into mathematical requirements

List stock identities, sector mapping, minimum and maximum weights, cash policy, return or risk gates and the objective. Mark each requirement as mandatory or exploratory. A phrase such as diversified should not silently become an undocumented number inside the optimizer.

Our basket has A and B in Tech and C in Health. Stock weights are nonnegative, no stock may exceed 0.6, and the account must be fully invested in stocks with cash zero. An illustrative minimum expected return is 0.055. These are synthetic policy inputs, not investment forecasts or regulatory limits.

Record units and source versions. A 0.6 weight means sixty percent of the same portfolio budget; sector limits aggregate that same denominator. If cash is allowed, state its minimum and maximum explicitly rather than normalizing the stock portion to one and concealing an unallocated balance.

Validate inputs before solving

Check finite weights, caps, expected returns and risk inputs before comparisons or summation. The independent valid() checker also validates its own sector names, finite nonnegative sector caps, finite nonnegative required budget and asset cap, and finite return gate before testing weights. It does not rely on solve() to sanitize its inputs. A test such as abs(sum(weights)-1) > tolerance does not reject every NaN path because comparisons with NaN can be false. The Python isfinite documentation defines an explicit check for infinities and NaN.

The fixture uses a fixed complete sector map and rejects unknown constrained sector names. A real map with unclassified holdings needs an explicit unresolved policy; an unknown sector cannot be assumed to have zero exposure. Likewise data insufficiency is not evidence that a mathematical model has no solution.

Separate schema validation from economic assumptions. Positive diagonal variances and complete sample arrays may make the toy objective calculable, but do not establish credible forecasts. Preserve how risk estimates were constructed and which observations were available before the allocation date.

Define the finite search accurately

Candidate weights are multiples of 0.1 between zero and one. Evaluate every triple, retaining only those satisfying budget, stock caps, sector caps and the return gate. Minimize the diagonal variance expression 0.04*wA² + 0.01*wB² + 0.0025*wC² within that finite set.

Expected-return assumptions are 0.08, 0.06 and 0.03 for A, B and C. With Tech cap 0.7 and Health cap 0.6, the selected grid point is [0.2,0.5,0.3]. It has expected return 0.055 and variance 0.004325 under the invented diagonal model.

This is a grid optimum, not proof of the continuous global optimum or a complete covariance-aware portfolio solution. The availability flag in the example is injected to test outcome handling; it is not a probe claiming that an external solver was installed or missing on the user’s machine.

Recheck the actual returned vector

Do not trust only a solver success flag. Apply the same constraints to its actual output, including any fallback. The essential order is:

if any(not isfinite(w) or w < 0 for w in weights):
    return False
if abs(sum(weights) - 1) > tolerance:
    return False
# Then check each stock cap, sector total and return gate.

Preserve field-level residuals so a rejection explains whether the issue was budget, one security, a sector or the return target. In this fixture, budget and cap comparisons use explicit small tolerances. They do not authorize relaxing a materially violated limit.

Check vector length and security order as well. Swapping two entries can preserve the total while assigning a limit to the wrong stock. Keep the identity map with the numerical output so the consumer can reproduce the same interpretation.

If a production solver reports numerical difficulty or an iteration limit, retain that state even if a tentative vector happens to pass feasibility. Feasible output and proven optimization completion are separate claims; one cannot be substituted for the other.

Produce an analytic conflict certificate

Change both sector caps to 0.4 while requiring stock investment of one. Tech and Health cover the full basket, so their maximum combined stock allocation is 0.8. Cash is prohibited. Therefore no continuous allocation can meet all three requirements, independently of the grid.

The certificate contains full investment, Tech cap and Health cap. As a three-constraint subsystem with basic nonnegative stock bounds, it is subset-minimal: removing either sector cap admits a constructed allocation, and removing the investment requirement admits zero stock weight. The return gate is outside this certificate.

Do not describe this as a search for the globally smallest conflict among every model condition. Other conflicting subsets may exist. The practical artifact is a concise, inspectable explanation of one proven contradiction, with its exact scope stated.

Keep a grid miss separate from mathematical infeasibility

Set Tech cap 0.65 and Health cap 0.35. Full investment forces Health weight exactly 0.35, which the 0.1 grid cannot represent. The search returns no_grid_candidate, yet [0.4,0.25,0.35] passes the continuous constraint checker.

This counterexample shows why no sampled candidate is weaker than a mathematical infeasibility certificate. Refine the search or use an appropriate solver before concluding that the requirements conflict. Do not relax the client’s limits merely to make a coarse grid return a row.

Retain search resolution and candidate count with the result. A denser grid changes the computational experiment and may change its selected point. Keep the earlier empty-grid status instead of rewriting it as if the finer search had been used originally.

Handle solver absence and fallback explicitly

Under the feasible 0.7/0.6 sector caps, the injected solver-unavailable branch proposes equal weights and validates them. The status is solver_unavailable_fallback, with optimized=False. Passing feasibility does not transform that fallback into the minimum-variance result.

With Tech cap 0.6, equal weights put two thirds in Tech, so the fallback is rejected. That does not prove the portfolio problem infeasible; another vector may satisfy the limits. The example returns no admitted fallback rather than emitting a warning beside invalid weights and proceeding as if everything succeeded.

The SciPy linprog documentation distinguishes success, infeasibility, iteration limits and numerical problems. We use that distinction as a reference for explicit status design, without executing SciPy or copying its codes into an unverified product interface.

Review policy changes as new decisions

Allowing at least 0.2 cash could remove the simple sector-cap contradiction, but that changes the full-investment requirement. Raising a cap or reducing the return gate also changes the mandate. Record such alternatives with approval and a new context rather than silently applying them as technical fixes.

For handoff, include constraint names, units, source, computed residuals, status, admitted output and the conflict certificate. Keep rejected fallback weights for diagnosis while making clear they are not executable instructions. The artifact should explain both what failed and what remains unknown.

Download the complete example. Use Python 3.12 or later and run python R628.py. Assertions verify the grid result and exact metrics, analytic cap contradiction, grid-miss counterexample, missing-data and solver-unavailable states, fallback revalidation, nonfinite-vector rejection, and direct invalid-constraint calls including NaN caps, budget and asset cap plus an unknown sector.

Accept only the scoped claims supported by those checks. Rebuild after universe, cash policy, limits, estimates or search method changes. The example verifies synthetic constraint reasoning, not historical profitability or automatic enforcement across arbitrary Vibe or CoTrading strategies.

FAQ

Does no grid candidate prove no feasible allocation exists?

No. A continuous feasible point may fall between grid values.

Can a fallback be called optimized if it passes limits?

No. Feasibility and optimization completion need distinct labels.