major update
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { TradeSetup } from '../../lib/types';
|
||||
import { formatPrice, formatDateTime } from '../../lib/format';
|
||||
import { formatPrice, formatPercent, formatDateTime } from '../../lib/format';
|
||||
|
||||
export type SortColumn = 'symbol' | 'direction' | 'entry_price' | 'stop_loss' | 'target' | 'rr_ratio' | 'composite_score' | 'detected_at';
|
||||
export type SortColumn = 'symbol' | 'direction' | 'entry_price' | 'stop_loss' | 'target' | 'risk_amount' | 'reward_amount' | 'rr_ratio' | 'stop_pct' | 'target_pct' | 'composite_score' | 'detected_at';
|
||||
export type SortDirection = 'asc' | 'desc';
|
||||
|
||||
interface TradeTableProps {
|
||||
@@ -18,11 +18,36 @@ const columns: { key: SortColumn; label: string }[] = [
|
||||
{ key: 'entry_price', label: 'Entry' },
|
||||
{ key: 'stop_loss', label: 'Stop Loss' },
|
||||
{ key: 'target', label: 'Target' },
|
||||
{ key: 'risk_amount', label: 'Risk $' },
|
||||
{ key: 'reward_amount', label: 'Reward $' },
|
||||
{ key: 'rr_ratio', label: 'R:R' },
|
||||
{ key: 'stop_pct', label: '% to Stop' },
|
||||
{ key: 'target_pct', label: '% to Target' },
|
||||
{ key: 'composite_score', label: 'Score' },
|
||||
{ key: 'detected_at', label: 'Detected' },
|
||||
];
|
||||
|
||||
export interface TradeAnalysis {
|
||||
risk_amount: number;
|
||||
reward_amount: number;
|
||||
stop_pct: number;
|
||||
target_pct: number;
|
||||
}
|
||||
|
||||
export function computeTradeAnalysis(trade: TradeSetup): TradeAnalysis {
|
||||
const risk_amount = Math.abs(trade.entry_price - trade.stop_loss);
|
||||
const reward_amount = Math.abs(trade.target - trade.entry_price);
|
||||
const stop_pct = (risk_amount / trade.entry_price) * 100;
|
||||
const target_pct = (reward_amount / trade.entry_price) * 100;
|
||||
return { risk_amount, reward_amount, stop_pct, target_pct };
|
||||
}
|
||||
|
||||
function rrColorClass(rr: number): string {
|
||||
if (rr >= 3.0) return 'text-green-400';
|
||||
if (rr >= 2.0) return 'text-amber-400';
|
||||
return 'text-red-400';
|
||||
}
|
||||
|
||||
function sortIndicator(column: SortColumn, active: SortColumn, dir: SortDirection) {
|
||||
if (column !== active) return '';
|
||||
return dir === 'asc' ? ' ▲' : ' ▼';
|
||||
@@ -50,30 +75,37 @@ export function TradeTable({ trades, sortColumn, sortDirection, onSort }: TradeT
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{trades.map((trade) => (
|
||||
<tr key={trade.id} className="border-b border-white/[0.04] transition-all duration-200 hover:bg-white/[0.03]">
|
||||
<td className="px-4 py-3.5">
|
||||
<Link to={`/ticker/${trade.symbol}`} className="font-medium text-blue-400 hover:text-blue-300 transition-colors duration-150">
|
||||
{trade.symbol}
|
||||
</Link>
|
||||
</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<span className={trade.direction === 'long' ? 'font-medium text-emerald-400' : 'font-medium text-red-400'}>
|
||||
{trade.direction}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(trade.entry_price)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(trade.stop_loss)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(trade.target)}</td>
|
||||
<td className="px-4 py-3.5 font-mono font-semibold text-gray-200">{trade.rr_ratio.toFixed(2)}</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<span className={`font-semibold ${trade.composite_score > 70 ? 'text-emerald-400' : trade.composite_score >= 40 ? 'text-amber-400' : 'text-red-400'}`}>
|
||||
{Math.round(trade.composite_score)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3.5 text-gray-400">{formatDateTime(trade.detected_at)}</td>
|
||||
</tr>
|
||||
))}
|
||||
{trades.map((trade) => {
|
||||
const analysis = computeTradeAnalysis(trade);
|
||||
return (
|
||||
<tr key={trade.id} className="border-b border-white/[0.04] transition-all duration-200 hover:bg-white/[0.03]">
|
||||
<td className="px-4 py-3.5">
|
||||
<Link to={`/ticker/${trade.symbol}`} className="font-medium text-blue-400 hover:text-blue-300 transition-colors duration-150">
|
||||
{trade.symbol}
|
||||
</Link>
|
||||
</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<span className={trade.direction === 'long' ? 'font-medium text-emerald-400' : 'font-medium text-red-400'}>
|
||||
{trade.direction}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(trade.entry_price)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(trade.stop_loss)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(trade.target)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(analysis.risk_amount)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPrice(analysis.reward_amount)}</td>
|
||||
<td className={`px-4 py-3.5 font-mono font-semibold ${rrColorClass(trade.rr_ratio)}`}>{trade.rr_ratio.toFixed(2)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPercent(analysis.stop_pct)}</td>
|
||||
<td className="px-4 py-3.5 font-mono text-gray-200">{formatPercent(analysis.target_pct)}</td>
|
||||
<td className="px-4 py-3.5">
|
||||
<span className={`font-semibold ${trade.composite_score > 70 ? 'text-emerald-400' : trade.composite_score >= 40 ? 'text-amber-400' : 'text-red-400'}`}>
|
||||
{Math.round(trade.composite_score)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3.5 text-gray-400">{formatDateTime(trade.detected_at)}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user