feat(sandbox): deploy Phase 1 and Phase 2 of Portfolio Sandbox including Swamy-Arora GLS solver and stress-test visualization
This commit is contained in:
140
lib/store.ts
140
lib/store.ts
@@ -116,6 +116,7 @@ export interface InsiderTrade {
|
||||
shares: number;
|
||||
value: number;
|
||||
date: string;
|
||||
insight?: string;
|
||||
}
|
||||
|
||||
export interface CongressTrade {
|
||||
@@ -128,6 +129,7 @@ export interface CongressTrade {
|
||||
transactionDate: string;
|
||||
filingDate: string;
|
||||
lagDays: number;
|
||||
insight?: string;
|
||||
}
|
||||
|
||||
export interface WhaleTrade {
|
||||
@@ -139,6 +141,7 @@ export interface WhaleTrade {
|
||||
sharesHeld: number;
|
||||
filingDate: string;
|
||||
estimatedValue: number;
|
||||
insight?: string;
|
||||
}
|
||||
|
||||
// --- Interfaces for Overreaction Scanner ---
|
||||
@@ -151,6 +154,12 @@ export interface ScannerAlert {
|
||||
status: 'UNDEREVALUATED' | 'FAIR' | 'OVERVALUATED';
|
||||
}
|
||||
|
||||
export interface PortfolioAsset {
|
||||
ticker: string;
|
||||
shares: number;
|
||||
entryPrice: number;
|
||||
}
|
||||
|
||||
export interface WatchlistItem {
|
||||
id: string;
|
||||
ticker: string;
|
||||
@@ -170,6 +179,7 @@ interface SandboxState {
|
||||
portfolios: Portfolio[];
|
||||
activePortfolioId: string;
|
||||
ewmaLambda: number;
|
||||
portfolio: PortfolioAsset[];
|
||||
|
||||
// 2. Overreaction Scanner State
|
||||
scanThreshold: number;
|
||||
@@ -196,6 +206,7 @@ interface SandboxState {
|
||||
name: string;
|
||||
date: string;
|
||||
scores: Record<string, number>; // asset -> score
|
||||
isSuggestion?: Record<string, boolean>;
|
||||
}[];
|
||||
calendarProposals: {
|
||||
id: string;
|
||||
@@ -207,10 +218,50 @@ interface SandboxState {
|
||||
lmmObservations: {
|
||||
asset: string;
|
||||
eventType: string;
|
||||
eventName?: string;
|
||||
score?: number;
|
||||
vix: number;
|
||||
trend: number;
|
||||
returnVal: number;
|
||||
}[];
|
||||
assetsList: {
|
||||
name: string;
|
||||
symbol: string;
|
||||
}[];
|
||||
lmmResults?: {
|
||||
fixedEffects: {
|
||||
name: string;
|
||||
estimate: number;
|
||||
se: number;
|
||||
pVal: number;
|
||||
sig: string;
|
||||
ciLower: number;
|
||||
ciUpper: number;
|
||||
}[];
|
||||
randomEffects: {
|
||||
asset: string;
|
||||
intercept: number;
|
||||
}[];
|
||||
randomEffectsVariance: {
|
||||
interceptVar: number;
|
||||
vixSlopeVar: number;
|
||||
eventMemoryVar: number;
|
||||
residualVar: number;
|
||||
};
|
||||
aic: number;
|
||||
bic: number;
|
||||
rSquared: number;
|
||||
roc?: {
|
||||
points: { fpr: number; tpr: number; threshold: number }[];
|
||||
auc: number;
|
||||
maxYouden: number;
|
||||
optimalThreshold: number;
|
||||
};
|
||||
survival?: {
|
||||
points: { time: number; highConvRate: number; lowConvRate: number }[];
|
||||
observationCount: number;
|
||||
};
|
||||
};
|
||||
|
||||
// Actions
|
||||
createPortfolio: (name: string, startingBalance: number) => void;
|
||||
@@ -242,6 +293,8 @@ interface SandboxState {
|
||||
updateBayesPrior: (prior: number) => void;
|
||||
updateBayesLikelihood: (likelihood: number) => void;
|
||||
setSelectedModel: (model: 'ROC' | 'SURVIVAL' | 'LMM') => void;
|
||||
updatePortfolioAsset: (ticker: string, shares: number, entryPrice: number) => void;
|
||||
removePortfolioAsset: (ticker: string) => void;
|
||||
}
|
||||
|
||||
// --- Helper: Generate Initial Historical Data ---
|
||||
@@ -309,50 +362,21 @@ export const useSandboxStore = create<SandboxState>((set, get) => ({
|
||||
],
|
||||
activePortfolioId: 'p1',
|
||||
ewmaLambda: 0.94,
|
||||
portfolio: [
|
||||
{ ticker: 'AAPL', shares: 150, entryPrice: 172.5 },
|
||||
{ ticker: 'MSFT', shares: 80, entryPrice: 388.0 },
|
||||
{ ticker: 'BTC-USD', shares: 1.5, entryPrice: 62000.0 }
|
||||
],
|
||||
|
||||
// 2. Overreaction Scanner Defaults
|
||||
scanThreshold: -0.05,
|
||||
scannerAlerts: [
|
||||
{ id: '1', ticker: 'NVDA', priceChange: -0.082, gjrGarchVol: 0.034, overreactionScore: 82, status: 'UNDEREVALUATED' },
|
||||
{ id: '2', ticker: 'AMD', priceChange: -0.061, gjrGarchVol: 0.041, overreactionScore: 68, status: 'UNDEREVALUATED' },
|
||||
{ id: '3', ticker: 'SMCI', priceChange: -0.124, gjrGarchVol: 0.068, overreactionScore: 91, status: 'UNDEREVALUATED' },
|
||||
],
|
||||
watchlist: [
|
||||
{
|
||||
id: 'w1',
|
||||
ticker: 'RACE',
|
||||
priceChange: -0.065,
|
||||
sentiment: 'GREEN',
|
||||
whyDropped: 'Emotionaler Abverkauf nach viralem Video von Cristiano Ronaldo, der sich über Autoprobleme beschwert. Keine fundamentalen Schäden.',
|
||||
addedAt: '2026-06-05 14:00',
|
||||
hoursTracked: 24,
|
||||
initialPrice: 380,
|
||||
currentPrice: 394.5,
|
||||
reboundPerformance: 3.81
|
||||
}
|
||||
],
|
||||
scannerAlerts: [],
|
||||
watchlist: [],
|
||||
|
||||
// 3. Insider / Whale Defaults
|
||||
insiderTrades: [
|
||||
{ id: '1', ticker: 'AMZN', insiderName: 'Bezos Jeff', relation: 'Director', type: 'SELL', shares: 50000, value: 9200000, date: '2026-06-05' },
|
||||
{ id: '2', ticker: 'META', insiderName: 'Zuckerberg Mark', relation: 'CEO', type: 'SELL', shares: 12000, value: 5760000, date: '2026-06-04' },
|
||||
{ id: '3', ticker: 'PLTR', insiderName: 'Karp Alexander', relation: 'CEO', type: 'BUY', shares: 150000, value: 3300000, date: '2026-06-03' },
|
||||
{ id: '4', ticker: 'PLTR', insiderName: 'Thiel Peter', relation: 'Director', type: 'BUY', shares: 100000, value: 2200000, date: '2026-06-02' },
|
||||
{ id: '5', ticker: 'PLTR', insiderName: 'Cohen Stephen', relation: 'President', type: 'BUY', shares: 80000, value: 1760000, date: '2026-06-01' },
|
||||
{ id: '6', ticker: 'RACE', insiderName: 'Vigna Benedetto', relation: 'CEO', type: 'BUY', shares: 8000, value: 3040000, date: '2026-06-04' },
|
||||
{ id: '7', ticker: 'RACE', insiderName: 'Elkann John', relation: 'Director', type: 'BUY', shares: 12000, value: 4560000, date: '2026-06-03' },
|
||||
{ id: '8', ticker: 'RACE', insiderName: 'Ferrari Piero', relation: 'Vice Chairman', type: 'BUY', shares: 10000, value: 3800000, date: '2026-06-02' }
|
||||
],
|
||||
congressTrades: [
|
||||
{ id: 'c1', ticker: 'MSFT', representative: 'Nancy Pelosi', chamber: 'HOUSE', type: 'BUY', valueRange: '$1,000,001 - $5,000,000', transactionDate: '2026-04-20', filingDate: '2026-06-01', lagDays: 42 },
|
||||
{ id: 'c2', ticker: 'NVDA', representative: 'Tommy Tuberville', chamber: 'SENATE', type: 'BUY', valueRange: '$100,001 - $250,000', transactionDate: '2026-04-25', filingDate: '2026-06-03', lagDays: 39 },
|
||||
{ id: 'c3', ticker: 'AAPL', representative: 'Nancy Pelosi', chamber: 'HOUSE', type: 'SELL', valueRange: '$500,001 - $1,000,000', transactionDate: '2026-04-15', filingDate: '2026-05-28', lagDays: 43 }
|
||||
],
|
||||
whaleTrades: [
|
||||
{ id: 'w1', ticker: 'AAPL', institution: 'Berkshire Hathaway', type: 'SELL', sharesTraded: 10000000, sharesHeld: 789000000, filingDate: '2026-05-15', estimatedValue: 1820000000 },
|
||||
{ id: 'w2', ticker: 'PLTR', institution: 'Renaissance Technologies', type: 'BUY', sharesTraded: 5400000, sharesHeld: 12500000, filingDate: '2026-05-15', estimatedValue: 118800000 },
|
||||
{ id: 'w3', ticker: 'NVDA', institution: 'BlackRock Inc.', type: 'BUY', sharesTraded: 15400000, sharesHeld: 182400000, filingDate: '2026-05-15', estimatedValue: 14553000000 }
|
||||
],
|
||||
insiderTrades: [],
|
||||
congressTrades: [],
|
||||
whaleTrades: [],
|
||||
insiderVolumes: {
|
||||
'PLTR': [30000, 25000, 45000, 18000, 22000, 31000, 27000, 36000, 29000, 40000, 33000, 150000], // 12-month rolling (scaled down representation for monthly)
|
||||
'RACE': [8000, 6000, 7500, 9000, 5200, 7100, 6800, 9500, 8100, 10200, 9300, 30000],
|
||||
@@ -375,19 +399,26 @@ export const useSandboxStore = create<SandboxState>((set, get) => ({
|
||||
{ id: 'ev2', name: 'US Wahlen (Präsidentschaft)', date: '2026-11-03', scores: { Apple: 2, NASDAQ: 1, Gold: 3, Bitcoin: 2 } },
|
||||
{ id: 'ev3', name: 'SpaceX IPO (Gerüchte)', date: '2026-06-25', scores: { Apple: 0, NASDAQ: 2, Gold: -1, Bitcoin: 1 } },
|
||||
],
|
||||
assetsList: [
|
||||
{ name: 'Apple', symbol: 'AAPL' },
|
||||
{ name: 'NASDAQ', symbol: '^IXIC' },
|
||||
{ name: 'Gold', symbol: 'GLD' },
|
||||
{ name: 'Bitcoin', symbol: 'BTC-USD' }
|
||||
],
|
||||
calendarProposals: [
|
||||
{ id: 'cp1', name: 'CPI Inflationsdaten', date: '2026-06-12', archetype: 'Macro Announcement', defaultScores: { Apple: 1, NASDAQ: 2, Gold: -2, Bitcoin: 1 } },
|
||||
{ id: 'cp2', name: 'US Non-Farm Payrolls', date: '2026-06-15', archetype: 'Employment Report', defaultScores: { Apple: 0, NASDAQ: 1, Gold: -1, Bitcoin: 0 } },
|
||||
{ id: 'cp3', name: 'EZB Pressekonferenz', date: '2026-06-18', archetype: 'Central Bank Policy', defaultScores: { Apple: -1, NASDAQ: -1, Gold: 2, Bitcoin: 1 } },
|
||||
],
|
||||
lmmObservations: [
|
||||
{ asset: 'Apple', eventType: 'BULLISH', vix: 14.2, trend: 0.02, returnVal: 0.018 },
|
||||
{ asset: 'NASDAQ', eventType: 'BULLISH', vix: 15.5, trend: 0.015, returnVal: 0.022 },
|
||||
{ asset: 'Gold', eventType: 'BEARISH', vix: 22.1, trend: -0.01, returnVal: -0.005 },
|
||||
{ asset: 'Bitcoin', eventType: 'BULLISH', vix: 18.4, trend: 0.03, returnVal: 0.035 },
|
||||
{ asset: 'Apple', eventType: 'BEARISH', vix: 16.8, trend: -0.005, returnVal: -0.012 },
|
||||
{ asset: 'NASDAQ', eventType: 'BEARISH', vix: 20.2, trend: -0.01, returnVal: -0.018 },
|
||||
{ asset: 'Apple', eventType: 'BULLISH', eventName: 'Fed-Zinsentscheid (FOMC)', score: 1, vix: 14.2, trend: 0.02, returnVal: 0.018 },
|
||||
{ asset: 'NASDAQ', eventType: 'BULLISH', eventName: 'Fed-Zinsentscheid (FOMC)', score: 2, vix: 15.5, trend: 0.015, returnVal: 0.022 },
|
||||
{ asset: 'Gold', eventType: 'BEARISH', eventName: 'Fed-Zinsentscheid (FOMC)', score: -1, vix: 22.1, trend: -0.01, returnVal: -0.005 },
|
||||
{ asset: 'Bitcoin', eventType: 'BULLISH', eventName: 'Fed-Zinsentscheid (FOMC)', score: 2, vix: 18.4, trend: 0.03, returnVal: 0.035 },
|
||||
{ asset: 'Apple', eventType: 'BEARISH', eventName: 'US-Inflationsdaten (CPI)', score: -1, vix: 16.8, trend: -0.005, returnVal: -0.012 },
|
||||
{ asset: 'NASDAQ', eventType: 'BEARISH', eventName: 'US-Inflationsdaten (CPI)', score: -2, vix: 20.2, trend: -0.01, returnVal: -0.018 },
|
||||
],
|
||||
lmmResults: undefined,
|
||||
|
||||
// --- Actions ---
|
||||
createPortfolio: (name, startingBalance) => set((state) => {
|
||||
@@ -523,6 +554,25 @@ export const useSandboxStore = create<SandboxState>((set, get) => ({
|
||||
|
||||
updateScannerAlerts: (scannerAlerts) => set({ scannerAlerts }),
|
||||
|
||||
updatePortfolioAsset: (ticker, shares, entryPrice) => set((state) => {
|
||||
const existingIndex = state.portfolio.findIndex(p => p.ticker === ticker);
|
||||
let newPortfolio = [...state.portfolio];
|
||||
if (existingIndex !== -1) {
|
||||
if (shares <= 0) {
|
||||
newPortfolio.splice(existingIndex, 1);
|
||||
} else {
|
||||
newPortfolio[existingIndex] = { ticker, shares, entryPrice };
|
||||
}
|
||||
} else if (shares > 0) {
|
||||
newPortfolio.push({ ticker, shares, entryPrice });
|
||||
}
|
||||
return { portfolio: newPortfolio };
|
||||
}),
|
||||
|
||||
removePortfolioAsset: (ticker) => set((state) => ({
|
||||
portfolio: state.portfolio.filter(p => p.ticker !== ticker)
|
||||
})),
|
||||
|
||||
addToWatchlist: (item) => set((state) => {
|
||||
const newItem: WatchlistItem = {
|
||||
...item,
|
||||
|
||||
Reference in New Issue
Block a user