Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 0 additions & 91 deletions .cursor/rules/feature-flag-guidelines.mdc

This file was deleted.

45 changes: 44 additions & 1 deletion app/components/UI/Perps/Views/PerpsHomeView/PerpsHomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import {
ButtonSize,
} from '@metamask/design-system-react-native';
import { useStyles } from '../../../../../component-library/hooks';
import { TextColor } from '../../../../../component-library/components/Texts/Text';
import { strings } from '../../../../../../locales/i18n';
import { formatPnl, formatPercentage } from '../../utils/formatUtils';
import Routes from '../../../../../constants/navigation/Routes';
import {
usePerpsHomeData,
Expand Down Expand Up @@ -93,6 +95,10 @@ const PerpsHomeView = () => {
const totalBalance = perpsAccount?.totalBalance || '0';
const isBalanceEmpty = BigNumber(totalBalance).isZero();

// Calculate P&L for positions subtitle
const unrealizedPnl = perpsAccount?.unrealizedPnl || '0';
const roe = parseFloat(perpsAccount?.returnOnEquity || '0');

// Fetch all home screen data
const {
positions,
Expand All @@ -106,6 +112,40 @@ const PerpsHomeView = () => {
isLoading,
} = usePerpsHomeData({});

// Calculate positions subtitle with P&L
const hasPositions = positions.length > 0;
const { positionsSubtitle, positionsSubtitleColor, positionsSubtitleSuffix } =
useMemo(() => {
const pnlNum = parseFloat(unrealizedPnl);
const isPnlZero = BigNumber(unrealizedPnl).isZero();

// Only show subtitle when there are positions and P&L is non-zero
if (!hasPositions || isPnlZero) {
return {
positionsSubtitle: undefined,
positionsSubtitleColor: undefined,
positionsSubtitleSuffix: undefined,
};
}

const color =
pnlNum > 0
? TextColor.Success
: pnlNum < 0
? TextColor.Error
: TextColor.Alternative;

// Format: "-$18.47 (2.1%)" colored + "Unrealized PnL" in default color
const subtitle = `${formatPnl(pnlNum)} (${formatPercentage(roe, 1)})`;
const suffix = strings('perps.unrealized_pnl');

return {
positionsSubtitle: subtitle,
positionsSubtitleColor: color,
positionsSubtitleSuffix: suffix,
};
}, [hasPositions, unrealizedPnl, roe]);

// Determine if any data is loading for initial load tracking
// Orders and activity load via WebSocket instantly, only track positions and markets
const isAnyLoading = isLoading.positions || isLoading.markets;
Expand Down Expand Up @@ -248,13 +288,16 @@ const PerpsHomeView = () => {
>
{/* Balance Actions Component */}
<PerpsMarketBalanceActions
positions={positions}
showActionButtons={HOME_SCREEN_CONFIG.SHOW_HEADER_ACTION_BUTTONS}
/>

{/* Positions Section */}
<PerpsHomeSection
title={strings('perps.home.positions')}
subtitle={positionsSubtitle}
subtitleColor={positionsSubtitleColor}
subtitleSuffix={positionsSubtitleSuffix}
subtitleTestID={PerpsHomeViewSelectorsIDs.POSITIONS_PNL_VALUE}
isLoading={isLoading.positions}
isEmpty={positions.length === 0}
showWhenEmpty={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,14 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
throttleMs: 1000,
});

// Get current price from the last candle's close price for chart synchronization
// This ensures the current price line matches the live candle close price exactly
const chartCurrentPrice = useMemo(() => {
if (!candleData?.candles?.length) return 0;
const lastCandle = candleData.candles.at(-1);
return lastCandle?.close ? Number.parseFloat(lastCandle.close) : 0;
}, [candleData]);

// Auto-zoom to latest candle when interval changes and new data arrives
// This ensures the chart shows the most recent data after interval change
useEffect(() => {
Expand Down Expand Up @@ -394,24 +402,24 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
}, [existingPosition, orderFills]);

// Compute TP/SL lines for the chart based on existing position
// Always include currentPrice to ensure chart price line matches header (TAT-2112)
// Use chartCurrentPrice (from candle close) to ensure price line syncs with live candle
const tpslLines = useMemo(() => {
const currentPriceStr =
currentPrice > 0 ? currentPrice.toString() : undefined;
const chartPriceStr =
chartCurrentPrice > 0 ? chartCurrentPrice.toString() : undefined;

if (existingPosition) {
return {
entryPrice: existingPosition.entryPrice,
takeProfitPrice: existingPosition.takeProfitPrice,
stopLossPrice: existingPosition.stopLossPrice,
liquidationPrice: existingPosition.liquidationPrice || undefined,
currentPrice: currentPriceStr,
currentPrice: chartPriceStr,
};
}

// Even without position, show current price line on chart
return currentPriceStr ? { currentPrice: currentPriceStr } : undefined;
}, [existingPosition, currentPrice]);
return chartPriceStr ? { currentPrice: chartPriceStr } : undefined;
}, [existingPosition, chartCurrentPrice]);

// Stop loss prompt banner logic
// Hook handles visibility orchestration including fade-out animation
Expand Down Expand Up @@ -906,6 +914,7 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
onFullscreenPress={handleFullscreenChartOpen}
isFavorite={isWatchlist}
testID={PerpsMarketDetailsViewSelectorsIDs.HEADER}
currentPrice={chartCurrentPrice}
/>
</View>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,11 @@ const PerpsOrderBookView: React.FC<PerpsOrderBookViewProps> = ({
return (
<SafeAreaView style={styles.container} testID={testID}>
{market ? (
<PerpsMarketHeader market={market} onBackPress={handleBack} />
<PerpsMarketHeader
market={market}
onBackPress={handleBack}
currentPrice={marketPrice ?? 0}
/>
) : (
<View style={styles.header}>
<ButtonIcon
Expand Down Expand Up @@ -370,7 +374,13 @@ const PerpsOrderBookView: React.FC<PerpsOrderBookViewProps> = ({
return (
<SafeAreaView style={styles.container} testID={testID}>
{/* Market Header */}
{market && <PerpsMarketHeader market={market} onBackPress={handleBack} />}
{market && (
<PerpsMarketHeader
market={market}
onBackPress={handleBack}
currentPrice={marketPrice ?? 0}
/>
)}

{/* Controls Row - Unit Toggle and Grouping */}
<View style={styles.controlsRow}>
Expand Down
Loading
Loading