feat: dealer flow, mirror portfolio (M21), options convexity, FINRA short interest, alert producers, vendor gate
CI / Test & Type-Check (push) Canceled after 0s
CI / Test & Type-Check (push) Canceled after 0s
Snapshot of in-progress module work across multiple slices: - Dealer Flow: dealerExposureEngine, dealerMapService, dealerMapExplain, dealerMapIntegrity, dealerMapReplay, dealerStudyEngine, hanStyleLevels - Mirror Portfolio (M21): fundRepository, captureIngest, mirrorAlertProducers, fund holdings strip, live book, position capture ingest - Options: BSM, NormalizedOptionSurface types, OptionsChainRouter, ConvexityGate, option legs panel - Alert producers: vixLevel, rotation, thesis, unlock, portfolioRisk, mirror (fund_capture, fund_13f, mirror_diff) - FINRA short interest adapter + queue integration - SEC company tickers adapter + ingest (symbol search index seed) - Vendor gate (rate-limit-first data plane, ADR-0009) - CUSIP registry, reverse 13F refresh, stock float service - LRU cache, portfolio backtest engine - Frontend: dealer-flow, funds, journal, lab, monitor, plan, portfolio, reports, screener, strategies, theses, guided-start, exits, more pages - Volume profile, workspace profile, visibility-aware poll - ADRs 0010 (mirror math not advice), 0011 (symbol search index) - VENDOR_INTEGRATIONS.md, END_USER_TEST.md - .gitignore: exclude DBs, .DS_Store, local config, agent scratch
This commit is contained in:
@@ -13,14 +13,15 @@ export type DeriskingStrategyId =
|
||||
| 'thesis_based_partial'
|
||||
| 'option_protected_collar'
|
||||
| 'regime_cut'
|
||||
| 'correlation_driven';
|
||||
| 'correlation_driven'
|
||||
| 'dividend_at_risk';
|
||||
|
||||
export interface DeriskingStrategy {
|
||||
id: DeriskingStrategyId;
|
||||
name: string;
|
||||
description: string;
|
||||
alfredFraming: string;
|
||||
requiresOptionsSleeve: boolean;
|
||||
requiresOptionsSleeve?: boolean; // true if this strategy requires options unlock
|
||||
}
|
||||
|
||||
export const DERISKING_STRATEGIES: DeriskingStrategy[] = [
|
||||
@@ -29,21 +30,18 @@ export const DERISKING_STRATEGIES: DeriskingStrategy[] = [
|
||||
name: 'Scale Out at Targets',
|
||||
description: 'Reduce partial position size as price reaches predefined profit targets. This adds flexibility — some position remains if the thesis continues to play out.',
|
||||
alfredFraming: 'Winners have flexibility. Scaling out at targets locks in some gains while leaving room for further upside.',
|
||||
requiresOptionsSleeve: false,
|
||||
},
|
||||
{
|
||||
id: 'stop_trail_ema21_50',
|
||||
name: 'Stop-Trail EMA 21/50',
|
||||
description: 'Trail a stop above the 21-day or 50-day EMA. This adapts the stop to volatility, allowing the position to breathe while protecting gains.',
|
||||
alfredFraming: 'Trailing stops adapt to changing conditions. A static stop ignores new information.',
|
||||
requiresOptionsSleeve: false,
|
||||
},
|
||||
{
|
||||
id: 'thesis_based_partial',
|
||||
name: 'Thesis-Based Partial Reduction',
|
||||
description: 'If the thesis is weakening but not fully invalidated, consider reducing position size rather than fully exiting. This acknowledges uncertainty without abandoning the thesis entirely.',
|
||||
alfredFraming: 'When the thesis weakens but is not broken, flexibility means adjusting size — not all or nothing.',
|
||||
requiresOptionsSleeve: false,
|
||||
},
|
||||
{
|
||||
id: 'option_protected_collar',
|
||||
@@ -57,14 +55,18 @@ export const DERISKING_STRATEGIES: DeriskingStrategy[] = [
|
||||
name: 'Regime-Cut Reduction',
|
||||
description: 'When the market regime shifts to trending-down, consider reducing exposure across the portfolio. This is a macro-level derisking response to changed conditions.',
|
||||
alfredFraming: 'When the tide turns, reduce exposure. Fighting a regime shift is a common way winners become losers.',
|
||||
requiresOptionsSleeve: false,
|
||||
},
|
||||
{
|
||||
id: 'correlation_driven',
|
||||
name: 'Correlation-Driven Reduction',
|
||||
description: 'When multiple positions become highly correlated, consider reducing the most correlated ones. Concentrated correlation creates hidden portfolio risk.',
|
||||
alfredFraming: 'When everything moves together, you do not have diversification — you have one bet wearing many masks.',
|
||||
requiresOptionsSleeve: false,
|
||||
},
|
||||
{
|
||||
id: 'dividend_at_risk',
|
||||
name: 'Dividend at Risk',
|
||||
description: 'When a dividend-paying stock shows signs of a potential cut — high payout ratio, declining yield, or skipped ex-date — consider reducing exposure before the cut is announced.',
|
||||
alfredFraming: 'Dividend cuts usually arrive after the warning signs are visible. The market often prices a cut before it is announced.',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -80,8 +82,10 @@ export interface DeriskingInput {
|
||||
thesisStatus?: 'intact' | 'weakening' | 'broken';
|
||||
currentRegime?: 'trending-up' | 'trending-down' | 'range-bound';
|
||||
portfolioCorrelation?: number; // 0-1, how correlated this position is with others
|
||||
optionsUnlockState?: number; // 0-4, from ConvexityGate
|
||||
profitTargets?: number[]; // price levels
|
||||
dividendYield?: number;
|
||||
payoutRatio?: number;
|
||||
optionsUnlockState?: number; // 0 = locked, 3+ = unlocked for option strategies
|
||||
}
|
||||
|
||||
export interface DeriskingSuggestion {
|
||||
@@ -152,13 +156,14 @@ function evaluateStrategy(
|
||||
}
|
||||
|
||||
case 'option_protected_collar': {
|
||||
const hasOptionsSleeve = (input.optionsUnlockState ?? 0) >= 3;
|
||||
// Gated by options unlock state (0 = locked, 3+ = unlocked)
|
||||
const hasOptionsUnlock = (input.optionsUnlockState ?? 0) >= 3;
|
||||
return {
|
||||
strategy: strat,
|
||||
applicable: hasOptionsSleeve && inProfit,
|
||||
reason: hasOptionsSleeve
|
||||
? 'Options insurance sleeve is unlocked. A collar may define downside risk while partially financing protection.'
|
||||
: 'Options insurance sleeve is not unlocked. Collar strategy requires the insurance sleeve tier.',
|
||||
applicable: inProfit && hasOptionsUnlock,
|
||||
reason: !hasOptionsUnlock
|
||||
? 'Option collar requires options unlock level 3 or higher.'
|
||||
: 'A collar may define downside risk while partially financing protection.',
|
||||
details: strat.alfredFraming,
|
||||
};
|
||||
}
|
||||
@@ -186,5 +191,21 @@ function evaluateStrategy(
|
||||
details: strat.alfredFraming,
|
||||
};
|
||||
}
|
||||
|
||||
case 'dividend_at_risk': {
|
||||
const dy = input.dividendYield;
|
||||
const pr = input.payoutRatio;
|
||||
const atRisk = (dy !== undefined && dy < 0.005) || (pr !== undefined && pr > 0.95);
|
||||
return {
|
||||
strategy: strat,
|
||||
applicable: atRisk,
|
||||
reason: atRisk
|
||||
? (dy !== undefined && dy < 0.005
|
||||
? `Dividend yield is very low (${(dy * 100).toFixed(2)}%). This may indicate a recent cut or unsustainable payout.`
|
||||
: `Payout ratio is very high (${((pr ?? 0) * 100).toFixed(0)}%). Earnings may not cover the dividend.`)
|
||||
: 'Dividend fundamentals appear healthy.',
|
||||
details: strat.alfredFraming,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user