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
Build Images and Deploy / Update-PROD-Stack (push) Failing after 31s
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user