diff --git a/scripts/ui-drift-audit.sh b/scripts/ui-drift-audit.sh
index 35fa680..beb87a2 100755
--- a/scripts/ui-drift-audit.sh
+++ b/scripts/ui-drift-audit.sh
@@ -23,7 +23,7 @@ excluded_parts = {"test", "assets"}
patterns = [
("raw interactive controls outside approved primitives", "Raw Interactive Controls", re.compile(r"<(button|input|textarea|select)(\s|>)"), True),
- ("legacy global surface classes", "Legacy Global Surface Classes", re.compile(r"\b(surface-card|surface-muted|badge|input-shell)\b"), False),
+ ("legacy global surface classes", "Legacy Global Surface Classes", re.compile(r"(?;
+import './SymbolCard.css';
+import { PriceChart } from './PriceChart';
+import { Badge, ProductStatusBadge } from './ui/Primitives';
+
+interface SymbolCardProps {
+ symbol: string;
+ data: {
+ price: number;
+ change24h: number;
+ changeToday: number;
+ session: string;
+ volatility: string;
+ signal: string;
+ signalTime?: number;
+ tradingMode?: 'Paper' | 'Live' | 'Alerts';
+ activePosition?: {
+ side: 'BUY' | 'SELL';
+ entryPrice: number;
+ size: number;
+ stopLoss: number;
+ takeProfit: number;
+ unrealizedPnl?: number;
+ unrealizedPnlPercent?: number;
+ marketValue?: number;
+ } | null;
+ priceHistory: Array<{ timestamp: number; price: number }>;
rules: {
[ruleName: string]: {
passed: boolean;
@@ -57,61 +58,58 @@ interface SymbolCardProps {
ema20_1h?: number;
ema20_15m?: number;
ema50_4h?: number;
- ema200_4h?: number;
- rsi_1h?: number;
- rsi_15m?: number;
- };
- };
-}
-
-const ruleDisplayNames: { [key: string]: string } = {
- 'TrendBiasRule': 'Trend',
- 'SessionRule': 'Session',
- 'ZoneRule': 'Zone',
- 'MomentumRule': 'Momentum',
- 'EntryTriggerRule': 'Entry',
- 'AIAnalysisRule': 'AI',
- 'RiskManagementRule': 'Risk'
-};
-
+ ema200_4h?: number;
+ rsi_1h?: number;
+ rsi_15m?: number;
+ };
+ };
+}
+
+const ruleDisplayNames: { [key: string]: string } = {
+ 'TrendBiasRule': 'Trend',
+ 'SessionRule': 'Session',
+ 'ZoneRule': 'Zone',
+ 'MomentumRule': 'Momentum',
+ 'EntryTriggerRule': 'Entry',
+ 'AIAnalysisRule': 'AI',
+ 'RiskManagementRule': 'Risk'
+};
+
export const SymbolCard = ({ symbol, data }: SymbolCardProps) => {
- const signalColor = data.signal === 'BUY' ? '#00ff88' : data.signal === 'SELL' ? '#ff3366' : '#888';
const profileSignalEntries = Object.entries(data.profileSignals || {});
-
- return (
-
-
-
-
{symbol}
-
-
- {data.tradingMode}
-
- {data.session}
-
- {data.volatility} Vol
-
-
-
-
-
${data.price.toLocaleString()}
-
-
= 0 ? 'up' : 'down'}`}>
- Today: {data.changeToday >= 0 ? '+' : ''}{data.changeToday.toFixed(2)}%
-
-
= 0 ? 'up' : 'down'}`}>
- 24h: {data.change24h >= 0 ? '+' : ''}{data.change24h.toFixed(2)}%
-
-
-
-
-
-
- {data.signal || 'NONE'}
+
+ return (
+
+
+
+
{symbol}
+
+
{data.tradingMode}
+
{data.session}
+
+ {data.volatility} Vol
+
+
+
+
+
${data.price.toLocaleString()}
+
+
= 0 ? 'up' : 'down'}`}>
+ Today: {data.changeToday >= 0 ? '+' : ''}{data.changeToday.toFixed(2)}%
+
+
= 0 ? 'up' : 'down'}`}>
+ 24h: {data.change24h >= 0 ? '+' : ''}{data.change24h.toFixed(2)}%
+
+
+
+
+
+
+
{data.signal || 'NONE'}
{data.signalTime && (
{Math.floor((Date.now() - data.signalTime) / 60000)}m ago
-
+
)}
@@ -120,13 +118,6 @@ export const SymbolCard = ({ symbol, data }: SymbolCardProps) => {
Profile Signals
{profileSignalEntries.map(([profileId, profileSignal]) => {
- const badgeClass = profileSignal.signal === 'BUY'
- ? 'passed'
- : profileSignal.signal === 'SELL'
- ? 'failed'
- : profileSignal.signal === 'MIXED'
- ? 'pending'
- : 'skipped';
const executionState = profileSignal.execution?.status;
const executionClass = executionState === 'EXECUTED'
? 'executed'
@@ -135,12 +126,16 @@ export const SymbolCard = ({ symbol, data }: SymbolCardProps) => {
: 'skipped';
return (
-
+
{(profileSignal.profileName || profileId).slice(0, 18)}
{profileSignal.signal}
-
+
{profileSignal.execution && (
@@ -156,113 +151,114 @@ export const SymbolCard = ({ symbol, data }: SymbolCardProps) => {
)}
-
- {data.activePosition && (
-
-
- ACTIVE {data.activePosition.side}
- {data.activePosition.size.toFixed(4)} Units
-
-
-
- Entry
- ${data.activePosition.entryPrice.toLocaleString()}
-
-
= 0 ? 'up' : 'down'}`}>
- P/L ($)
-
- {data.activePosition.unrealizedPnl! >= 0 ? '+' : ''}
- {data.activePosition.unrealizedPnl!.toFixed(2)}
-
-
-
= 0 ? 'up' : 'down'}`}>
- Return
-
- {data.activePosition.unrealizedPnlPercent! >= 0 ? '+' : ''}
- {data.activePosition.unrealizedPnlPercent!.toFixed(2)}%
-
-
-
- Value
- ${data.activePosition.marketValue?.toLocaleString()}
-
-
- SL
- ${data.activePosition.stopLoss.toLocaleString()}
-
-
- TP
- ${data.activePosition.takeProfit.toLocaleString()}
-
-
-
- )}
-
-
-
-
-
Rules
-
- {Object.entries(data.rules).map(([ruleName, ruleData]) => {
- const isAI = ruleName === 'AIAnalysisRule';
- const aiData = isAI ? ruleData.metadata : null;
-
- return (
-
-
-
- {ruleData.isSkipped ? '➖' : (ruleData.isPending ? '⏳' : (ruleData.passed ? '✓' : '✗'))}
-
- {ruleDisplayNames[ruleName] || ruleName}
-
- {isAI && aiData && (
-
- {aiData.confidence !== undefined && (
-
Confidence: {aiData.confidence}%
- )}
- {aiData.reasoning && (
-
{aiData.reasoning}
- )}
-
- )}
-
- );
- })}
-
-
-
-
-
Indicators
-
- {data.indicators.rsi_15m !== undefined && (
-
- RSI 15m:
- {data.indicators.rsi_15m.toFixed(1)}
-
- )}
- {data.indicators.rsi_1h !== undefined && (
-
- RSI 1h:
- {data.indicators.rsi_1h.toFixed(1)}
-
- )}
- {data.indicators.ema20_15m !== undefined && (
-
- EMA20 15m:
- ${data.indicators.ema20_15m.toLocaleString()}
-
- )}
- {data.indicators.ema20_1h !== undefined && (
-
- EMA20 1h:
- ${data.indicators.ema20_1h.toLocaleString()}
-
- )}
-
-
-
- );
-};
+
+ {data.activePosition && (
+
+
+ ACTIVE {data.activePosition.side}
+ {data.activePosition.size.toFixed(4)} Units
+
+
+
+ Entry
+ ${data.activePosition.entryPrice.toLocaleString()}
+
+
= 0 ? 'up' : 'down'}`}>
+ P/L ($)
+
+ {data.activePosition.unrealizedPnl! >= 0 ? '+' : ''}
+ {data.activePosition.unrealizedPnl!.toFixed(2)}
+
+
+
= 0 ? 'up' : 'down'}`}>
+ Return
+
+ {data.activePosition.unrealizedPnlPercent! >= 0 ? '+' : ''}
+ {data.activePosition.unrealizedPnlPercent!.toFixed(2)}%
+
+
+
+ Value
+ ${data.activePosition.marketValue?.toLocaleString()}
+
+
+ SL
+ ${data.activePosition.stopLoss.toLocaleString()}
+
+
+ TP
+ ${data.activePosition.takeProfit.toLocaleString()}
+
+
+
+ )}
+
+
+
+
+
Rules
+
+ {Object.entries(data.rules).map(([ruleName, ruleData]) => {
+ const isAI = ruleName === 'AIAnalysisRule';
+ const aiData = isAI ? ruleData.metadata : null;
+
+ return (
+
+
+
+ {ruleData.isSkipped ? '➖' : (ruleData.isPending ? '⏳' : (ruleData.passed ? '✓' : '✗'))}
+
+ {ruleDisplayNames[ruleName] || ruleName}
+
+ {isAI && aiData && (
+
+ {aiData.confidence !== undefined && (
+
Confidence: {aiData.confidence}%
+ )}
+ {aiData.reasoning && (
+
{aiData.reasoning}
+ )}
+
+ )}
+
+ );
+ })}
+
+
+
+
+
Indicators
+
+ {data.indicators.rsi_15m !== undefined && (
+
+ RSI 15m:
+ {data.indicators.rsi_15m.toFixed(1)}
+
+ )}
+ {data.indicators.rsi_1h !== undefined && (
+
+ RSI 1h:
+ {data.indicators.rsi_1h.toFixed(1)}
+
+ )}
+ {data.indicators.ema20_15m !== undefined && (
+
+ EMA20 15m:
+ ${data.indicators.ema20_15m.toLocaleString()}
+
+ )}
+ {data.indicators.ema20_1h !== undefined && (
+
+ EMA20 1h:
+ ${data.indicators.ema20_1h.toLocaleString()}
+
+ )}
+
+
+
+ );
+};