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,
|
useTrendHistory,
|
||||||
useMarketPrices,
|
useMarketPrices,
|
||||||
useDocument,
|
useDocument,
|
||||||
|
usePositions,
|
||||||
} from '../api/hooks';
|
} from '../api/hooks';
|
||||||
import { StatusBadge, ConfidenceBar, LoadingSpinner, Card } from '../components/ui';
|
import { StatusBadge, ConfidenceBar, LoadingSpinner, Card } from '../components/ui';
|
||||||
import { DataTable, type Column } from '../components/DataTable';
|
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: trends } = useTrends({ ticker: company?.ticker, limit: 200 });
|
||||||
const { data: trendHistory } = useTrendHistory({ ticker: company?.ticker, limit: 500 });
|
const { data: trendHistory } = useTrendHistory({ ticker: company?.ticker, limit: 500 });
|
||||||
const { data: marketPrices } = useMarketPrices(company?.ticker, 200);
|
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');
|
const [tab, setTab] = useState<'trends' | 'sources' | 'aliases' | 'macro' | 'competitors' | 'patterns' | 'signals' | 'decisions'>('trends');
|
||||||
|
|
||||||
if (isLoading || !company) return <LoadingSpinner />;
|
if (isLoading || !company) return <LoadingSpinner />;
|
||||||
@@ -83,7 +85,10 @@ export function CompanyDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{tab === 'trends' && (
|
{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' && (
|
{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[] }) {
|
function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices }: { trends: TrendSummary[]; latestTrends: TrendSummary[]; ticker: string; marketPrices: MarketPrice[] }) {
|
||||||
const [selectedWindow, setSelectedWindow] = useState('7d');
|
const [selectedWindow, setSelectedWindow] = useState('7d');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user