fix(coin): improve sell insufficient message

This commit is contained in:
2026-06-20 09:52:23 +07:00
parent 71f3336387
commit 6aeca59b98
2 changed files with 41 additions and 7 deletions
+17 -5
View File
@@ -147,19 +147,20 @@ func (s *state) handleSell(ctx context.Context, b *bot.Bot, update *models.Updat
return chathelper.Reply(ctx, b, update.Message, "USD amount is too small to convert to a tradeable coin quantity at the current price.")
}
defer s.locks.Acquire(strconv.FormatInt(userID, 10))()
var insufficientHeld *float64
var insufficientHeldQty float64
var insufficientHeld bool
p, err := UpdatePortfolio(ctx, s.kv, userID, s.now().UnixMilli(), func(p *Portfolio) error {
ok, held := p.DeductAsset(coin.Symbol, qty)
if !ok {
insufficientHeld = &held
insufficientHeldQty = held
insufficientHeld = true
return errInsufficientCoin
}
p.AddUSD(amount)
return nil
})
if errors.Is(err, errInsufficientCoin) && insufficientHeld != nil {
return chathelper.Reply(ctx, b, update.Message,
"Insufficient "+coin.Symbol+". You have: "+FormatCoinQty(*insufficientHeld))
if errors.Is(err, errInsufficientCoin) && insufficientHeld {
return chathelper.Reply(ctx, b, update.Message, formatInsufficientSellMessage(coin, amount, insufficientHeldQty, price.USD))
}
if err != nil {
log.Error("coin_save_portfolio", "user", userID, "err", err)
@@ -169,3 +170,14 @@ func (s *state) handleSell(ctx context.Context, b *bot.Bot, update *models.Updat
"Sold "+FormatCoinQty(qty)+" "+coin.Symbol+" @ "+FormatUSD(price.USD)+" ("+price.Source+")"+
"\nProceeds: "+FormatUSD(amount)+"\nRemaining: "+FormatUSD(p.USD))
}
func formatInsufficientSellMessage(coin CoinSymbol, requestedUSD, heldQty, priceUSD float64) string {
heldQty = normalizeAmount(heldQty)
if heldQty == 0 {
return "No " + coin.Symbol + " available to sell.\nTry /coin_buy " + coin.Symbol + " <usd_amount> first."
}
availableUSD := heldQty * priceUSD
return "Not enough " + coin.Symbol + " to sell " + FormatUSD(requestedUSD) + ".\n" +
"Available to sell: " + FormatUSD(availableUSD) + " (" + FormatCoinQty(heldQty) + " " + coin.Symbol + " @ " + FormatUSD(priceUSD) + ").\n" +
"Try " + FormatUSD(availableUSD) + " or less."
}
+24 -2
View File
@@ -149,7 +149,17 @@ func TestHandleSellInsufficientCoin(t *testing.T) {
if err := s.handleSell(context.Background(), rb.Bot, testutil.NewPrivateMessage(7, "/coin_sell 10 ETH")); err != nil {
t.Fatalf("handleSell: %v", err)
}
rb.AssertSentText(t, "Insufficient ETH")
text := rb.LastSent().Text()
for _, want := range []string{"No ETH available to sell.", "Try /coin_buy ETH <usd_amount> first."} {
if !strings.Contains(text, want) {
t.Fatalf("zero-holdings sell message missing %q in %q", want, text)
}
}
for _, unwanted := range []string{"$0.00", "@"} {
if strings.Contains(text, unwanted) {
t.Fatalf("zero-holdings sell message included %q in %q", unwanted, text)
}
}
}
func TestHandleSellRejectsInvalidUSDAmount(t *testing.T) {
@@ -181,7 +191,19 @@ func TestHandleSellInsufficientCoinWithHoldings(t *testing.T) {
if err := s.handleSell(ctx, rb.Bot, testutil.NewPrivateMessage(7, "/coin_sell 600 BTC")); err != nil {
t.Fatalf("handleSell: %v", err)
}
rb.AssertSentText(t, "Insufficient BTC")
text := rb.LastSent().Text()
for _, want := range []string{
"Not enough BTC to sell $600.00.",
"Available to sell: $500.00 (0.01 BTC @ $50,000.00).",
"Try $500.00 or less.",
} {
if !strings.Contains(text, want) {
t.Fatalf("insufficient sell message missing %q in %q", want, text)
}
}
if strings.Contains(text, "have") {
t.Fatalf("insufficient sell message used ambiguous have wording: %q", text)
}
p, _ := LoadPortfolio(ctx, s.kv, 7, 999)
if p.USD != 500 || p.Assets["BTC"] != 0.01 {