fix(assets): price the spot Market Sell instead of sending a zero - #406
Merged
Conversation
`TDoSellOrderCommand` carries no market-order flag — unlike `ClosePosition`, whose `market_sell` bool is real — so `price=0` was never a market order. The core substituted the market's lot step for the zero and divided by it: a 0.005 BTC holding reached Binance as `quantity=499.99999`, rejected five times by NOTIONAL, and the button failed silently (2026-09-03 BinKEAUSDC log). Moonbot's own Market Sell fills like a market order by pricing a LIMIT order through the book, so reproduce that: `last * 0.8`, measured from the core's own sale (ask 78528.68 -> order 62822.94, filled at 78528.67). A venue with a tighter band corrects it itself — Bitget answered `25205 trading price cannot be below 2%` and the core refilled at its own bound. The size stays the coin quantity, which is what this command has always taken. Two measured attempts prove it: sending a balance-currency notional sold 79.67 of 198.01 AERO, and offered $0.37 of a $103 MANTRA holding, which the venue rejected for its 1 USDT minimum. The price is read live at confirmation rather than captured at render: a wallet-derived row (Bitget and others expose spot holdings only through `transfer_assets`) carries a USDT valuation, not a quote price, and an open dialog goes stale against the book it must cross. `latest_price` could not serve that: its fallback to the snapshot's `p_last` sat unreachable under an early return demanding history readers, which moonproto only builds for markets inside the client's `TradeStorageScope`. A refusal now says so — the confirmation showed nothing at all when the command was dropped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Market Sell on an Assets spot row failed silently.
TDoSellOrderCommandcarries nomarket-order flag — unlike
ClosePosition, whosemarket_sellbool is real — so theprice=0this path sent was never "at market". The core substituted the market's lot stepfor the zero and divided the size by it: a 0.005 BTC holding reached Binance as
quantity=499.99999, was rejected five times byNOTIONAL, and nothing surfaced in the UI.Moonbot's own Market Sell fills like a market order by pricing a LIMIT order through the
book, so this reproduces that:
last * 0.8, measured from the core's own sale (ask78528.68 → order 62822.94 → filled at 78528.67). A venue with a tighter band corrects it
itself — Bitget answered
25205 trading price cannot be below 2%and the core refilled atits own bound.
Notable decisions
NewOrderParams::size("in the core account's balance currency"), but that documents an opening order — how
much balance to spend — while this command says how much coin to sell. Two measured
attempts settled it: a balance-currency notional sold 79.67 of a 198.01 AERO holding, and
offered $0.37 of a $103 MANTRA holding, which the venue rejected for its 1 USDT minimum.
(Bitget and others expose spot holdings only through
transfer_assets) carries a USDTvaluation rather than a quote price, and a dialog left open goes stale against the book it
has to cross.
latest_pricehad to be fixed to serve that. Its fallback to the snapshot'sp_lastsat unreachable under an early return demanding history readers, which moonproto only
builds for markets inside the client's
TradeStorageScope. History is an optimisationhere, not a requirement; the now-unreachable
LatestPriceError::NoHistoryReadersis gone.command was sent.
Known limitations
measured from core logs; the corrected build has not yet placed a real sale. The one check
that closes it: one Market Sell on a real holding, with
Sell: <whole balance>andSELL order DONE ! Quantity:equal to it in the core log.the core's own correction, as Bitget did above.
rejected order is visible only in the core log. Out of scope here.
How to verify
everything else green (7 ignored, pre-existing)
touched files on this branch against 18 on
origin/main, none inside this diff's hunks(
trade.rs163–233,read.rs203–232)🤖 Generated with Claude Code