import React, { useState } from 'react'; import { View, Text, ScrollView, StyleSheet } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Colors, Fonts, FontSize, BorderRadius, Spacing } from '@/constants/theme'; import { formatPrice, formatPercent, formatCurrency } from '@/utils/format'; import SegmentedControl from '@/components/SegmentedControl'; import AnimatedCard from '@/components/AnimatedCard'; import PillBadge from '@/components/PillBadge'; import { useTradingData } from '@/providers/TradingDataProvider'; import { buildHistoryMetrics } from '@/lib/tradingViewModels'; const FILTERS = ['All', 'Today', 'This Week', 'This Month']; export default function HistoryScreen() { const insets = useSafeAreaInsets(); const { botState } = useTradingData(); const tradeHistory = botState?.history || []; const profileOptions = ['All', ...Array.from(new Set(tradeHistory.map((trade) => trade.profileName || 'Trading Profile')))]; const [profileIndex, setProfileIndex] = useState(0); const [filterIndex, setFilterIndex] = useState(0); const historyMetrics = buildHistoryMetrics(tradeHistory); const filteredTrades = profileIndex === 0 ? tradeHistory : tradeHistory.filter((trade) => (trade.profileName || 'Trading Profile') === profileOptions[profileIndex]); const timeFilteredTrades = filteredTrades.filter((trade) => { if (filterIndex === 0) return true; const ageMs = Date.now() - Number(trade.timestamp || 0); if (filterIndex === 1) return ageMs <= 24 * 60 * 60 * 1000; if (filterIndex === 2) return ageMs <= 7 * 24 * 60 * 60 * 1000; return ageMs <= 30 * 24 * 60 * 60 * 1000; }); return ( COMPLETE TRADE LEDGER Audit Logs = 0 ? '+' : '-'}$${Math.abs(historyMetrics.netPnl).toFixed(2)}`} color={historyMetrics.netPnl >= 0 ? Colors.accent.green : Colors.accent.red} mono /> {FILTERS.map((f, i) => ( setFilterIndex(i)} /> ))} {timeFilteredTrades.length === 0 ? ( No trades found Closed trades will appear here once the bot exits a position. ) : timeFilteredTrades.map((trade, index) => ( ))} ); } function MetricCard({ label, value, color, mono }: { label: string; value: string; color?: string; mono?: boolean }) { return ( {label} {value} ); } function PressableFilter({ label, active, onPress }: { label: string; active: boolean; onPress: () => void }) { return ( {label} ); } function TradeRow({ trade, index }: { trade: { symbol: string; side: string; entryPrice: number; exitPrice: number; size: number; pnl: number; pnlPercent: number; reason: string; source?: 'BOT' | 'MANUAL'; timestamp: number; }; index: number }) { const isLoss = trade.pnl < 0; const pnlColor = isLoss ? Colors.accent.red : Colors.accent.green; const reasonColors: Record = { 'Take Profit': { bg: 'rgba(0,255,136,0.1)', color: Colors.accent.green }, 'Stop Loss': { bg: 'rgba(255,51,102,0.1)', color: Colors.accent.red }, 'Signal Exit': { bg: 'rgba(52,152,219,0.1)', color: Colors.accent.blue }, 'Manual': { bg: 'rgba(255,255,255,0.05)', color: Colors.text.secondary }, }; const rc = reasonColors[trade.reason] || reasonColors['Manual']; return ( {isLoss && } {trade.symbol} {formatCurrency(trade.pnl)} ({formatPercent(trade.pnlPercent)}) {formatPrice(trade.entryPrice)} → {formatPrice(trade.exitPrice)} {trade.size} {trade.symbol.split('/')[0]} {new Date(trade.timestamp).toLocaleString()} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: Colors.background.primary, }, headerSection: { padding: Spacing.screenPadding, paddingBottom: 0, }, subtitle: { fontFamily: Fonts.inter.black, fontSize: FontSize.micro, color: Colors.accent.green, letterSpacing: 4, marginBottom: 8, }, pageTitle: { fontFamily: Fonts.inter.black, fontSize: FontSize.hero, color: Colors.text.primary, letterSpacing: -0.5, marginBottom: 16, }, scroll: { flex: 1, }, content: { padding: Spacing.screenPadding, gap: 16, paddingBottom: 120, }, metricsRow: { gap: 12, }, metricCard: { backgroundColor: Colors.background.subtle, borderRadius: BorderRadius.large, borderWidth: 1, borderColor: Colors.border.default, padding: 20, minWidth: 140, }, metricLabel: { fontFamily: Fonts.inter.black, fontSize: FontSize.micro, color: Colors.text.secondary, letterSpacing: 3, marginBottom: 8, }, metricValue: { fontFamily: Fonts.inter.black, fontSize: FontSize.hero, color: Colors.text.primary, }, filterRow: { flexDirection: 'row', gap: 8, }, filterPill: { paddingHorizontal: 14, paddingVertical: 7, borderRadius: BorderRadius.xs, borderWidth: 1, borderColor: Colors.border.medium, backgroundColor: 'rgba(255,255,255,0.03)', }, filterActive: { borderColor: 'rgba(0,255,136,0.45)', backgroundColor: 'rgba(0,255,136,0.08)', }, filterText: { fontFamily: Fonts.inter.semiBold, fontSize: FontSize.badge, color: '#a1a1aa', }, filterTextActive: { color: Colors.accent.green, }, tradeCard: { backgroundColor: Colors.background.card, borderRadius: BorderRadius.medium, borderWidth: 1, borderColor: Colors.border.default, overflow: 'hidden', flexDirection: 'row', }, tradeCardLoss: { backgroundColor: 'rgba(255,51,102,0.04)', }, lossLeftBorder: { width: 2, backgroundColor: Colors.accent.red, }, tradeContent: { flex: 1, padding: 16, gap: 8, }, tradeHeader: { flexDirection: 'row', alignItems: 'center', gap: 8, }, tradeSymbol: { fontFamily: Fonts.inter.extraBold, fontSize: FontSize.bodyLarge, color: Colors.text.primary, }, tradePnl: { fontFamily: Fonts.mono.extraBold, fontSize: FontSize.body, marginLeft: 'auto', }, tradeDetails: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, tradeArrow: { fontFamily: Fonts.mono.medium, fontSize: FontSize.body, color: Colors.text.primary, }, tradeSize: { fontFamily: Fonts.mono.regular, fontSize: FontSize.bodySmall, color: Colors.text.secondary, }, tradeFooter: { flexDirection: 'row', alignItems: 'center', gap: 8, marginTop: 4, }, tradeTime: { fontFamily: Fonts.inter.medium, fontSize: FontSize.micro, color: Colors.text.secondary, marginLeft: 'auto', }, emptyState: { alignItems: 'center' as const, justifyContent: 'center' as const, paddingVertical: 60, gap: 10, }, emptyText: { fontFamily: Fonts.inter.bold, fontSize: FontSize.subheading, color: Colors.text.secondary, }, emptyHint: { fontFamily: Fonts.inter.medium, fontSize: FontSize.body, color: Colors.text.muted, textAlign: 'center' as const, maxWidth: 260, lineHeight: 20, }, });