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,
},
});