feat: add max position limits to TradePanel and page components for enhanced trading controls
Build Images and Deploy / Update-PROD-Stack (push) Failing after 31s

This commit is contained in:
2026-03-19 22:13:30 -04:00
parent 005b4543f6
commit c5e5f9eaf6
2 changed files with 22 additions and 4 deletions
+15 -3
View File
@@ -11,11 +11,13 @@ interface Props {
shortPosition: { shares: number; avgBuyPrice: number } | null
fundId?: string
fundName?: string
maxPositionShares: number
maxPositionValue: number
}
type Tab = 'BUY_LONG' | 'SELL_LONG' | 'BUY_SHORT' | 'SELL_SHORT'
export function TradePanel({ hashtag, balance, longPosition, shortPosition, fundId, fundName }: Props) {
export function TradePanel({ hashtag, balance, longPosition, shortPosition, fundId, fundName, maxPositionShares, maxPositionValue }: Props) {
const router = useRouter()
const [tab, setTab] = useState<Tab>('BUY_LONG')
const [shares, setShares] = useState('')
@@ -25,12 +27,19 @@ export function TradePanel({ hashtag, balance, longPosition, shortPosition, fund
const sharesNum = parseFloat(shares) || 0
const cost = sharesNum * hashtag.currentPrice
const maxBuyShares = hashtag.currentPrice > 0 ? Math.floor((balance / hashtag.currentPrice) * 100) / 100 : 0
// For buys: max is the lowest of (balance cap, shares cap, value cap) minus existing position
const existingBuyShares = tab === 'BUY_LONG' ? (longPosition?.shares ?? 0) : (shortPosition?.shares ?? 0)
const remainingShareCap = Math.max(0, maxPositionShares - existingBuyShares)
const remainingValueCap = Math.max(0, maxPositionValue - existingBuyShares * hashtag.currentPrice)
const sharesFromValueCap = hashtag.currentPrice > 0 ? remainingValueCap / hashtag.currentPrice : 0
const sharesFromBalance = hashtag.currentPrice > 0 ? balance / hashtag.currentPrice : 0
const maxBuyShares = Math.floor(Math.min(remainingShareCap, sharesFromValueCap, sharesFromBalance) * 100) / 100
const maxSellShares =
tab === 'SELL_LONG' ? longPosition?.shares ?? 0 : shortPosition?.shares ?? 0
const canAfford =
tab === 'BUY_LONG' || tab === 'BUY_SHORT' ? cost <= balance : sharesNum <= (maxSellShares ?? 0)
tab === 'BUY_LONG' || tab === 'BUY_SHORT' ? cost <= balance && sharesNum <= maxBuyShares : sharesNum <= (maxSellShares ?? 0)
async function handleTrade() {
if (sharesNum <= 0) return
@@ -129,6 +138,9 @@ export function TradePanel({ hashtag, balance, longPosition, shortPosition, fund
)}
>
Max
{(tab === 'BUY_LONG' || tab === 'BUY_SHORT') && maxBuyShares === 0
? ' (limit reached)'
: null}
</button>
</div>
<input
+7 -1
View File
@@ -13,6 +13,10 @@ import { AutoRefresh } from '@/components/AutoRefresh'
const ZOMBIE_ZERO_COUNT = parseInt(process.env.ZOMBIE_ZERO_COUNT ?? '1000', 10)
const PRICE_UPDATE_INTERVAL_MINUTES = parseInt(process.env.PRICE_UPDATE_INTERVAL_MINUTES ?? '60', 10)
const MAX_POSITION_SHARES = parseInt(process.env.MAX_POSITION_SHARES ?? '100', 10)
const MAX_POSITION_VALUE = parseInt(process.env.MAX_POSITION_VALUE ?? '1000', 10)
const FUND_MAX_POSITION_SHARES = parseInt(process.env.FUND_MAX_POSITION_SHARES ?? '1000', 10)
const FUND_MAX_POSITION_VALUE = parseInt(process.env.FUND_MAX_POSITION_VALUE ?? '10000', 10)
export const dynamic = 'force-dynamic'
@@ -128,7 +132,7 @@ export default async function HashtagPage({ params, searchParams }: Props) {
return (
<div className="space-y-8">
<AutoRefresh intervalMs={30_000} />
{/* Header */}}
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4">
<div>
<h1 className="text-3xl font-bold">#{hashtag.displayTag}</h1>
@@ -193,6 +197,8 @@ export default async function HashtagPage({ params, searchParams }: Props) {
shortPosition={activeShort ? { shares: activeShort.shares, avgBuyPrice: activeShort.avgBuyPrice } : null}
fundId={fundContext?.id}
fundName={fundContext?.name}
maxPositionShares={fundContext ? FUND_MAX_POSITION_SHARES : MAX_POSITION_SHARES}
maxPositionValue={fundContext ? FUND_MAX_POSITION_VALUE : MAX_POSITION_VALUE}
/>
) : (
<div className="bg-surface-card border border-surface-border rounded-xl p-6 text-center">