Split out of #198, now closed as superseded.
The gap
trading_pairs carries the per-pair rules — min_order_amount, asset_increment, quote_increment, max_order_size — and nothing reads them before submission. RobinhoodTransport.get_trading_pairs is one of only two transport methods the adapter never invokes, and says so at transport.py:405:
They exist because they are the inputs the obvious next feature needs: asset_increment, quote_increment, and max_order_size are what would let this package round a size to the venue's tick LOCALLY instead of discovering the violation as a rejection.
Three constraints the implementation must respect
1. The rows are not uniform. From transport.py:416:
Every row carries symbol, asset_code, quote_code, asset_increment, quote_increment, max_order_size, status and is_api_tradable. min_order_amount is carried by 63 of the 89 pairs — BTC-USD (0.1) and ETH-USD among them — and absent from the other 26.
So min_order_amount is optional per pair. Code that assumes the endpoint is uniform is the exact bug #230 fixed: #217 F3 inspected results[0] only, results[0] is BILL-USD (one of the 26 without it), and #218 then removed the field from the fixture on that false negative. Any probe or check here reads the row for the pair being traded, never results[0].
min_order_size does not exist at all — do not look for it.
2. It must not raise on the exit path. Carried over from #198's original wording, and it is the constraint that makes this non-trivial. A pre-flight check that refuses an undersized order is correct for an entry and dangerous for an exit: refusing to sell a holding because it rounds below the venue minimum strands a position keel has decided to close. The exit path needs a different disposition — round, or proceed and let the venue answer — not a hard refusal.
3. It costs a request. get_trading_pairs against a 100 req/min limit with no backoff (see the backoff issue). Cache per symbol for the process lifetime, the way RobinhoodTransport._account() caches the account number.
Scope
- Read the pair row for the symbol being ordered; validate size against
min_order_amount (when present), asset_increment, and max_order_size.
- Entry: refuse with a clear reason. Exit: never refuse — round to increment and proceed.
- Cache the pair rules; do not re-fetch per order.
- Tests against a canned transport covering: pair with
min_order_amount, pair without it, off-increment size, over max_order_size, and the exit path for each.
Split out of #198, now closed as superseded.
The gap
trading_pairscarries the per-pair rules —min_order_amount,asset_increment,quote_increment,max_order_size— and nothing reads them before submission.RobinhoodTransport.get_trading_pairsis one of only two transport methods the adapter never invokes, and says so attransport.py:405:Three constraints the implementation must respect
1. The rows are not uniform. From
transport.py:416:So
min_order_amountis optional per pair. Code that assumes the endpoint is uniform is the exact bug #230 fixed: #217 F3 inspectedresults[0]only,results[0]isBILL-USD(one of the 26 without it), and #218 then removed the field from the fixture on that false negative. Any probe or check here reads the row for the pair being traded, neverresults[0].min_order_sizedoes not exist at all — do not look for it.2. It must not raise on the exit path. Carried over from #198's original wording, and it is the constraint that makes this non-trivial. A pre-flight check that refuses an undersized order is correct for an entry and dangerous for an exit: refusing to sell a holding because it rounds below the venue minimum strands a position keel has decided to close. The exit path needs a different disposition — round, or proceed and let the venue answer — not a hard refusal.
3. It costs a request.
get_trading_pairsagainst a 100 req/min limit with no backoff (see the backoff issue). Cache per symbol for the process lifetime, the wayRobinhoodTransport._account()caches the account number.Scope
min_order_amount(when present),asset_increment, andmax_order_size.min_order_amount, pair without it, off-increment size, overmax_order_size, and the exit path for each.