Closes #023-warn - Implement PEAD fallback transparency warning badge

This commit is contained in:
Antigravity Agent
2026-06-14 16:05:06 +02:00
parent 8ffe8de06a
commit 776a41f159
5 changed files with 88 additions and 12 deletions

View File

@@ -136,3 +136,33 @@ Indicators in the cockpit cards use HSL-tailored status lights (Emerald-Green, A
}
}
```
### PEAD Drift Radar API Response Schema (`/api/scanner`)
```json
{
"results": [
{
"ticker": "NVDA",
"name": "NVIDIA Corporation",
"peadSector": "Technology",
"announcementDate": "2026-05-20",
"daysElapsed": 25,
"epsActual": 6.12,
"epsConsensus": 5.58,
"surprisePercent": 9.68,
"driftStatus": "Active Drift",
"isLiveApi": true
}
],
"isShieldActive": false
}
```
---
## 4. Fallback Protection & Operational Transparency Levels
The workstation enforces zero silent caching or historical data ingestion:
* **PEAD Drift Radar**: Uses a dynamic state flag `isLivePeadApi` to toggle status rendering.
* *Green Badge (`🟢 LIVE EPS FEED`)*: FMP API endpoint responded with real-world quarterly reports (`isLiveApi: true`).
* *Amber Badge (`⚠️ ARCHIV-DATEN (API OFFLINE)`)*: Live API timeout, failure, or developer shield fallback active (`isLiveApi: false`).

View File

@@ -288,6 +288,18 @@ This document tracks all modifications, npm packages, active compilation states,
* **Active Bugs**: None.
* **Type Checker Status**: Verified 100% clean type verification (`npx tsc --noEmit` returns exit code 0).
---
## [2026-06-14] - PEAD Fallback Transparency Warning Graphic (#ISSUE-023-WARN)
### Added
* **Transparency Flag Ingestion**: Refactored `fetchFmpEarningsSurprise` and `getSimulatedPEAD` in [/api/scanner/route.ts](file:///c:/Users/jannr/.gemini/antigravity/scratch/investment-sandbox/app/api/scanner/route.ts) to return `isLiveApi: true` upon successful live API queries and `isLiveApi: false` on failure or simulated fallbacks.
* **Dynamic Warning Badges**: Declared a state hook `isLivePeadApi` in [ScannerDemo.tsx](file:///c:/Users/jannr/.gemini/antigravity/scratch/investment-sandbox/components/modules/scanner/ScannerDemo.tsx) and bound it to scan responses in `handleMarketScan`. Renders green `"🟢 LIVE EPS FEED"` and amber `"⚠️ ARCHIV-DATEN (API OFFLINE)"` status badges adjacent to the PEAD Drift Radar header title depending on the scan api status.
### Active Bugs / Compile Status
* **Active Bugs**: None.
* **Type Checker Status**: Verified 100% clean type verification (`npx tsc --noEmit` returns exit code 0).

View File

@@ -38,6 +38,9 @@ This document serves as the permanent, centralized system architecture design an
* **Phase 7.0: Live Time-Lock Resolution, Ensemble Map Fix & Multi-Asset Logging**
* *Features*: Disabled developer mock test clocks inside verification loop hooks. Configured strict real-time calendar locks (T+1 resolves after 24h, T+5 after 5d, T+10 after 10d). Corrected the ensemble mapping anomaly inside `getPredictionProb` by passing the active asset's ticker context parameter. Embedded a dynamic asset selector filter tab-bar above the Active Learning Feedback Loop table component, enabling isolated tracking and filtering of multi-asset predictions (BTC, ETH, SOL).
* *Status*: **Fully Operational (Production Lock)**.
* **Phase 8.0: Isolated PEAD Screener & Fallback Warning Graphic**
* *Features*: Integrated zero-coupling Post-Earnings Announcement Drift (PEAD) Screener parsing reported vs. consensus EPS surprise vectors. Implemented an `isLiveApi` flag to detect and handle offline API or rate-limit simulated fallbacks. Mounted glassmorphic warning status badges (`🟢 LIVE EPS FEED` vs `⚠️ ARCHIV-DATEN (API OFFLINE)`) in the frontend header to prevent trading on historical cached data without operational awareness.
* *Status*: **Fully Operational (Production Lock)**.
---

View File

@@ -27,6 +27,7 @@ interface TickerDetails {
epsConsensus?: number;
surprisePercent?: number;
driftStatus?: string;
isLiveApi?: boolean;
}
// 14-day Welles Wilder RSI solver
@@ -412,6 +413,7 @@ function getSimulatedPEAD(ticker: string): {
epsConsensus: number;
surprisePercent: number;
driftStatus: 'Active Drift' | 'Consolidating';
isLiveApi: boolean;
} {
const getSector = (t: string) => {
const tech = ['AAPL', 'MSFT', 'NVDA', 'AMD', 'SMCI', 'ADBE', 'CRM', 'AVGO', 'QCOM', 'TXN', 'INTC', 'MU', 'AMAT', 'LRCX', 'PLTR'];
@@ -438,7 +440,8 @@ function getSimulatedPEAD(ticker: string): {
epsActual: 0,
epsConsensus: 0,
surprisePercent: 0,
driftStatus: 'Consolidating'
driftStatus: 'Consolidating',
isLiveApi: false
};
}
@@ -464,7 +467,8 @@ function getSimulatedPEAD(ticker: string): {
epsActual,
epsConsensus,
surprisePercent,
driftStatus
driftStatus,
isLiveApi: false
};
}
@@ -476,6 +480,7 @@ async function fetchFmpEarningsSurprise(ticker: string, apiKey: string): Promise
epsConsensus: number;
surprisePercent: number;
driftStatus: 'Active Drift' | 'Consolidating';
isLiveApi: boolean;
}> {
const getSector = (t: string) => {
const tech = ['AAPL', 'MSFT', 'NVDA', 'AMD', 'SMCI', 'ADBE', 'CRM', 'AVGO', 'QCOM', 'TXN', 'INTC', 'MU', 'AMAT', 'LRCX', 'PLTR'];
@@ -502,7 +507,8 @@ async function fetchFmpEarningsSurprise(ticker: string, apiKey: string): Promise
epsActual: 0,
epsConsensus: 0,
surprisePercent: 0,
driftStatus: 'Consolidating'
driftStatus: 'Consolidating',
isLiveApi: false
};
}
@@ -536,7 +542,8 @@ async function fetchFmpEarningsSurprise(ticker: string, apiKey: string): Promise
epsActual,
epsConsensus,
surprisePercent,
driftStatus
driftStatus,
isLiveApi: true
};
}
}
@@ -545,7 +552,10 @@ async function fetchFmpEarningsSurprise(ticker: string, apiKey: string): Promise
}
// Fallback to deterministic simulation
return getSimulatedPEAD(ticker);
return {
...getSimulatedPEAD(ticker),
isLiveApi: false
};
}
export async function GET(request: Request) {

View File

@@ -40,6 +40,7 @@ interface PEADData {
epsConsensus: number;
surprisePercent: number;
driftStatus: 'Active Drift' | 'Consolidating';
isLiveApi?: boolean;
}
export default function ScannerDemo() {
@@ -71,6 +72,7 @@ export default function ScannerDemo() {
{ ticker: 'TSLA', name: 'Tesla Inc.', peadSector: 'Consumer Goods', announcementDate: '2026-04-23', daysElapsed: 52, epsActual: 0.45, epsConsensus: 0.51, surprisePercent: -11.76, driftStatus: 'Consolidating' },
{ ticker: 'JPM', name: 'JPMorgan Chase & Co.', peadSector: 'Financial Services', announcementDate: '2026-04-12', daysElapsed: 63, epsActual: 4.44, epsConsensus: 4.15, surprisePercent: 6.99, driftStatus: 'Consolidating' }
]);
const [isLivePeadApi, setIsLivePeadApi] = useState(false);
// Cache for metadata and prices retrieved dynamically
const [alertsMetadata, setAlertsMetadata] = useState<Record<string, { name: string; whyDropped: string; sentiment: 'GREEN' | 'YELLOW' | 'RED' }>>({});
@@ -193,12 +195,16 @@ export default function ScannerDemo() {
epsActual: r.epsActual || 0,
epsConsensus: r.epsConsensus || 0,
surprisePercent: r.surprisePercent || 0,
driftStatus: r.driftStatus || 'Consolidating'
driftStatus: r.driftStatus || 'Consolidating',
isLiveApi: r.isLiveApi
});
});
peadList.sort((a, b) => Math.abs(b.surprisePercent) - Math.abs(a.surprisePercent));
setPeadData(peadList);
const hasLivePead = peadList.some(item => item.isLiveApi === true);
setIsLivePeadApi(hasLivePead);
// Update global store alerts for Sandbox module use
updateScannerAlerts(newAlerts);
@@ -766,12 +772,27 @@ export default function ScannerDemo() {
>
<Sparkles className="text-amber-400 w-4 h-4 animate-pulse" /> 3-Tier Screener Capacity Grid
</button>
<button
onClick={() => setLeftPanelTab('pead')}
className={`text-sm font-bold flex items-center gap-2 pb-1 transition-all cursor-pointer ${leftPanelTab === 'pead' ? 'text-white border-b-2 border-amber-500' : 'text-slate-400 hover:text-slate-200'}`}
>
<TrendingUp className="text-amber-400 w-4 h-4" /> 🚀 PEAD Drift Radar
</button>
<div className="flex items-center gap-2">
<button
onClick={() => setLeftPanelTab('pead')}
className={`text-sm font-bold flex items-center gap-2 pb-1 transition-all cursor-pointer ${leftPanelTab === 'pead' ? 'text-white border-b-2 border-amber-500' : 'text-slate-400 hover:text-slate-200'}`}
>
<TrendingUp className="text-amber-400 w-4 h-4" /> 🚀 PEAD Drift Radar
</button>
{leftPanelTab === 'pead' && (
isLivePeadApi ? (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[9px] font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 shadow-[0_0_10px_rgba(16,185,129,0.15)]">
<span className="w-1 h-1 rounded-full bg-emerald-500 animate-ping" />
🟢 LIVE EPS FEED
</span>
) : (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[9px] font-bold bg-amber-500/10 text-amber-400 border border-amber-500/20 shadow-[0_0_10px_rgba(245,158,11,0.15)]">
<span className="w-1 h-1 rounded-full bg-amber-500 animate-pulse" />
ARCHIV-DATEN (API OFFLINE)
</span>
)
)}
</div>
</div>
<span className="text-[10px] text-slate-400 font-mono">Modus: {scanMode.toUpperCase()} | Region: {marketRegion.toUpperCase()}</span>
</div>