feat: show current position on company detail trends tab
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
Displays an 'Open Position' card above the trend charts when we hold a position in that ticker. Shows shares, avg entry price, current price, market value, and unrealized P&L with green/red coloring. Card is hidden when no position exists for the ticker.
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
useTrendHistory,
|
||||
useMarketPrices,
|
||||
useDocument,
|
||||
usePositions,
|
||||
} from '../api/hooks';
|
||||
import { StatusBadge, ConfidenceBar, LoadingSpinner, Card } from '../components/ui';
|
||||
import { DataTable, type Column } from '../components/DataTable';
|
||||
@@ -45,6 +46,7 @@ export function CompanyDetailPage() {
|
||||
const { data: trends } = useTrends({ ticker: company?.ticker, limit: 200 });
|
||||
const { data: trendHistory } = useTrendHistory({ ticker: company?.ticker, limit: 500 });
|
||||
const { data: marketPrices } = useMarketPrices(company?.ticker, 200);
|
||||
const { data: positions } = usePositions(company?.ticker);
|
||||
const [tab, setTab] = useState<'trends' | 'sources' | 'aliases' | 'macro' | 'competitors' | 'patterns' | 'signals' | 'decisions'>('trends');
|
||||
|
||||
if (isLoading || !company) return <LoadingSpinner />;
|
||||
@@ -83,7 +85,10 @@ export function CompanyDetailPage() {
|
||||
</div>
|
||||
|
||||
{tab === 'trends' && (
|
||||
<TrendHistoryChart trends={trendHistory ?? []} latestTrends={trends ?? []} ticker={company.ticker} marketPrices={marketPrices ?? []} />
|
||||
<div className="space-y-4">
|
||||
<PositionCard positions={positions ?? []} ticker={company.ticker} />
|
||||
<TrendHistoryChart trends={trendHistory ?? []} latestTrends={trends ?? []} ticker={company.ticker} marketPrices={marketPrices ?? []} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === 'sources' && (
|
||||
@@ -615,6 +620,48 @@ function TrendTooltip({ active, payload, label }: Record<string, unknown>) {
|
||||
);
|
||||
}
|
||||
|
||||
function PositionCard({ positions, ticker }: { positions: import('../api/hooks').Position[]; ticker: string }) {
|
||||
const pos = positions.find((p) => p.ticker === ticker && p.quantity > 0);
|
||||
if (!pos) return null;
|
||||
|
||||
const marketValue = pos.current_price ? pos.quantity * pos.current_price : null;
|
||||
const pnlColor = (pos.unrealized_pnl ?? 0) >= 0 ? 'text-green-400' : 'text-red-400';
|
||||
const pnlSign = (pos.unrealized_pnl ?? 0) >= 0 ? '+' : '';
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-sm font-medium text-gray-400">Open Position</h2>
|
||||
<StatusBadge status="active" />
|
||||
</div>
|
||||
<dl className="mt-2 grid grid-cols-2 gap-x-8 gap-y-2 text-sm sm:grid-cols-5">
|
||||
<div>
|
||||
<dt className="text-gray-500">Shares</dt>
|
||||
<dd className="font-mono text-gray-200">{pos.quantity}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500">Avg Entry</dt>
|
||||
<dd className="font-mono text-gray-200">${pos.avg_entry_price.toFixed(2)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500">Current Price</dt>
|
||||
<dd className="font-mono text-gray-200">{pos.current_price ? `$${pos.current_price.toFixed(2)}` : '—'}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500">Market Value</dt>
|
||||
<dd className="font-mono text-gray-200">{marketValue ? `$${marketValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '—'}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-gray-500">Unrealized P&L</dt>
|
||||
<dd className={`font-mono font-semibold ${pnlColor}`}>
|
||||
{pos.unrealized_pnl != null ? `${pnlSign}$${Math.abs(pos.unrealized_pnl).toFixed(2)}` : '—'}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices }: { trends: TrendSummary[]; latestTrends: TrendSummary[]; ticker: string; marketPrices: MarketPrice[] }) {
|
||||
const [selectedWindow, setSelectedWindow] = useState('7d');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user