Step 5: Update exchange price endpoint to use new pricing config

- Update ExchangeConfigResponse schema with direction-specific fields
- Remove premium_percentage from PriceResponse (now in config)
- Update price endpoint to load pricing config from database
- Update frontend to use direction-specific min/max and calculate premium
- Update tests to seed pricing config
- Add logic to clamp amount when direction changes
This commit is contained in:
counterweight 2025-12-26 20:20:23 +01:00
parent d838d1be96
commit d317939ad0
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
5 changed files with 145 additions and 44 deletions

View file

@ -13,6 +13,7 @@ interface PriceDisplayProps {
lastUpdate: Date | null;
direction: "buy" | "sell";
agreedPrice: number;
premiumPercent: number;
}
/**
@ -94,10 +95,10 @@ export function PriceDisplay({
lastUpdate,
direction,
agreedPrice,
premiumPercent,
}: PriceDisplayProps) {
const t = useTranslation("exchange");
const marketPrice = priceData?.price?.market_price ?? 0;
const premiumPercent = priceData?.price?.premium_percentage ?? 5;
const isPriceStale = priceData?.price?.is_stale ?? false;
return (

View file

@ -116,24 +116,42 @@ export default function ExchangePage() {
// Config from API
const config = priceData?.config;
const eurMin = config?.eur_min ?? 100;
const eurMax = config?.eur_max ?? 3000;
const eurMinBuy = config?.eur_min_buy ?? 10000;
const eurMaxBuy = config?.eur_max_buy ?? 300000;
const eurMinSell = config?.eur_min_sell ?? 10000;
const eurMaxSell = config?.eur_max_sell ?? 300000;
const eurIncrement = config?.eur_increment ?? 20;
// Get direction-specific min/max
const eurMin = direction === "buy" ? eurMinBuy : eurMinSell;
const eurMax = direction === "buy" ? eurMaxBuy : eurMaxSell;
// Compute trade details
const price = priceData?.price;
const marketPrice = price?.market_price ?? 0;
const premiumPercent = price?.premium_percentage ?? 5;
const premiumBuy = config?.premium_buy ?? 5;
const premiumSell = config?.premium_sell ?? 5;
const smallTradeThreshold = config?.small_trade_threshold_eur ?? 0;
const smallTradeExtraPremium = config?.small_trade_extra_premium ?? 0;
// Calculate agreed price based on direction
// Calculate total premium: base premium for direction + extra if small trade
const totalPremiumPercent = useMemo(() => {
const basePremium = direction === "buy" ? premiumBuy : premiumSell;
if (eurAmount <= smallTradeThreshold) {
return basePremium + smallTradeExtraPremium;
}
return basePremium;
}, [direction, premiumBuy, premiumSell, eurAmount, smallTradeThreshold, smallTradeExtraPremium]);
// Calculate agreed price based on direction and total premium
const agreedPrice = useMemo(() => {
if (!marketPrice) return 0;
if (direction === "buy") {
return marketPrice * (1 + premiumPercent / 100);
return marketPrice * (1 + totalPremiumPercent / 100);
} else {
return marketPrice * (1 - premiumPercent / 100);
return marketPrice * (1 - totalPremiumPercent / 100);
}
}, [marketPrice, premiumPercent, direction]);
}, [marketPrice, totalPremiumPercent, direction]);
// Calculate sats amount
const satsAmount = useMemo(() => {
@ -155,6 +173,15 @@ export default function ExchangePage() {
}
}, [isLightningDisabled, bitcoinTransferMethod]);
// Clamp amount when direction changes (min/max may differ per direction)
useEffect(() => {
if (eurAmount < eurMin) {
setEurAmount(eurMin);
} else if (eurAmount > eurMax) {
setEurAmount(eurMax);
}
}, [direction, eurMin, eurMax]); // eslint-disable-line react-hooks/exhaustive-deps
// Fetch slots when date is selected
useEffect(() => {
if (selectedDate && user && isAuthorized) {
@ -307,6 +334,7 @@ export default function ExchangePage() {
lastUpdate={lastPriceUpdate}
direction={direction}
agreedPrice={agreedPrice}
premiumPercent={totalPremiumPercent}
/>
{/* Step Indicator */}
@ -322,8 +350,8 @@ export default function ExchangePage() {
eurAmount={eurAmount}
onEurAmountChange={setEurAmount}
satsAmount={satsAmount}
eurMin={eurMin}
eurMax={eurMax}
eurMin={eurMin / 100}
eurMax={eurMax / 100}
eurIncrement={eurIncrement}
isPriceStale={isPriceStale}
hasPrice={!!priceData?.price}