The covariance matrix Σ is symmetric positive-definite by construction (it's a Gram matrix on real returns), so CG is provably optimal — it converges in at most n iterations with no preconditioning, and typically far fewer when eigenvalues cluster.
-
Ensure neural-trader is available:
npm ls neural-trader 2>/dev/null || npm install --ignore-scripts neural-trader
-
Read the current covariance matrix Σ and expected-return vector μ from neural-trader's portfolio API:
npx neural-trader --portfolio current --json
npx neural-trader --portfolio current
mcp__ruflo__memory_search({ query: "covariance matrix current", namespace: "trading-risk", limit: 1 })
The skill expects the response to include covariance: number[][] (n × n) and expectedReturns: number[] (length n).
-
Solve Σ · x = μ via CG (preferred path) when RUFLO_NEURAL_TRADER_DISABLE_CG is unset and the MCP tool is available:
mcp__ruflo-sublinear__solve({
matrix: COVARIANCE,
rhs: EXPECTED_RETURNS,
algorithm: "cg",
tolerance: 1e-6,
maxIterations: 200
})
The tool's expected output shape:
{ solution: number[], iterations: number, residual: number }
The skill's adapter (plugins/ruflo-neural-trader/src/sublinear-adapter.ts) detects MCP availability and falls back transparently to the embedded CG kernel (~50 LOC) when mcp__ruflo-sublinear__solve is not registered in the running runtime. Either way the math is identical — CG, dense form, n × n SPD covariance.
-
Fallback (legacy Neumann) — if step 3 reports degraded: true (non-SPD input, non-square matrix, MCP error) OR if RUFLO_NEURAL_TRADER_DISABLE_CG=1:
npx neural-trader --portfolio optimize
Capture the weights output and tag the artifact metadata with method: 'neumann-fallback' and a reason field.
-
Store the optimal weights to trading-risk namespace with full provenance metadata:
mcp__ruflo__memory_store({
key: "portfolio-weights-PORTFOLIO_ID-TIMESTAMP",
namespace: "trading-risk",
value: JSON.stringify({
weights: SOLUTION, // number[] from step 3 or 4
method: 'cg-sublinear' | 'cg-local' | 'neumann-fallback',
solver: 'ruflo-sublinear@0.1.0', // or 'neural-trader-cli' on fallback
iterations: ITERATIONS,
residual: RESIDUAL,
latencyMs: LATENCY_MS,
capturedAt: NEW_DATE_ISO,
reason: FALLBACK_REASON || null
})
})
The trading-risk namespace is canonical (ADR-126 Phase 1; the five-namespace alignment). Long-lived — no TTL — because portfolio weights are the audit trail Phase 4 will Ed25519-sign.
-
Cross-check against historical patterns (optional but recommended):
mcp__ruflo__agentdb_pattern-search({
query: "portfolio weights Sharpe regime:CURRENT_REGIME",
namespace: "trading-risk"
})
If the new weights differ by more than 30% in any single asset from the historical median, flag for human review before applying. This is a guard-rail, not a hard block.