Packages
dp_exchange_coinbase
0.1.9
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.1.31
0.1.30
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
EXPERIMENTAL — Coinbase venue package for the DpExchange family. Market data, trading and streaming behind the shared DpExchange.Core.Venue facade.
Current section
Files
Jump to
Current section
Files
dp_exchange_coinbase
CHANGELOG.md
CHANGELOG.md
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Status: EXPERIMENTAL
Stated here rather than only per-release, because a reader arriving at a specific version
needs it as much as one reading the top.
This package has not run in production. While it is `0.x` the API may change without a
major version. Coverage is uneven by design: fakes and live public endpoints are well
covered, order placement and authenticated flows are not.
**Whenever an endpoint moves to `:proven`, the entry that does it states the evidence** —
what was run against the live venue, and when. "Marked proven" with no evidence is not an
acceptable changelog line.
## [Unreleased]
### Added
- **`convert/4` and `get_trade_volume/2` (Core 0.1.22) are declared unsupported, with the
reasons checked.** Advanced Trade's convert is the **two-step** form —
`POST /convert/quote`, `POST /convert/trade/{id}`, `GET /convert/trade/{id}` — which is
`quote_conversion/4` and friends, scheduled separately. The one-step `POST /conversions`
belongs to the **Exchange** API, a different product this package does not reach.
`/products/volume-summary` is market volume and lives there too; `get_trade_volume/2`
asks what *this account* traded, which Advanced Trade does not aggregate.
- **`preview_replace/4` and `close_position/3`.** Two documented endpoints this package had
no facade for.
`POST /orders/edit_preview` prices an amendment before it is made. It is not
`preview_order/3` with an order id: the venue prices the amendment against the resting
order's own state, including whatever of it has already filled, and its response carries
`average_filled_price` and `order_margin_total` — numbers a fresh order does not have.
It takes the same `:price` / `:quantity` change set `replace_order/4` does and refuses
anything else before the request.
`POST /orders/close_position` flattens a position by having the venue place the closing
order. **The returned `Order` carries no side.** The venue never states one, and it
worked the side out from a position this package did not read — filling in `:sell`
because closing is usually selling is wrong exactly where it matters, on a short. The
order type, time in force and size *are* read, from the `order_configuration` the venue
echoes back, and a configuration key this package does not recognise leaves them `nil`
rather than picking the nearest.
### Fixed
- **`cancel_all_orders/2` is declared unsupported, with the reason checked.**
`POST /orders/batch_cancel` takes an explicit `order_ids` list — it is the endpoint
`cancel_order/3` already uses, one id at a time. There is no "cancel everything" call
here, and assembling one from `get_orders/2` plus a batch would be N partial outcomes
with no way to reach an order that appeared between the listing and the cancel.
- **BREAKING: `get_historical_prices/4` returns `Core.Types.Candle`. It was returning
`Quote`s with `price: close`.**
The venue sends open, high, low and close for every bar. Three of them were discarded
here, at the boundary, where no caller could see it happen — and everything that came out
was a real number, so nothing looked wrong. A caller reading `price` was holding one
corner of a bar with no way to learn it.
**This is the same defect the coverage plan's 2.10 found in Schwab**, with the same
reasoning behind it, still live here after that one was fixed. The fake had it too: it
returned `get_price/2`'s `Quote`, so the suite agreed with the bug it existed to catch.
Bars now carry all four prices and `:opened_at` — the venue's own bucket start, used
as-is. A bar the venue did not date is refused with `:missing_venue_timestamp` rather
than stamped with the local clock, which would place it wrongly while looking right.
### Fixed
- **This package claimed the venue has no order preview and no atomic replace. It has
both.** `supports_order_preview` and `supports_order_replace` were declared `false` on
those claims, and neither was checked against the venue's reference. Coinbase publishes
`POST /orders/preview` and `POST /orders/edit`; both flags are now `true` and both
endpoints are implemented.
The replace claim was the worse of the two. Its moduledoc called
`supports_order_replace: false` "a claim about **risk** rather than convenience", because
cancel-then-replace opens a window in which no order is live. The risk was real and the
claim was wrong: **the package was describing a hazard it was creating by not implementing
the endpoint that avoids it.**
### Added
- **`preview_order/3`** builds the same `order_configuration` as `place_order/3`, so a
preview is a preview of the order that would actually be sent. **A `200` carrying a
populated `errs` is a refusal** — returning it as a successful preview would tell a caller
its order is fine when the venue has already said otherwise. A `warning` is passed through
and does *not* make it a refusal.
- **`replace_order/4`** edits price or size in place. **Any other change is refused rather
than dropped**: a caller trying to change the side is describing a different order, and
editing only the price would leave it holding one it did not ask for. The venue's edit
response carries no order body, so the order is **read back** rather than reconstructed
from the request — reporting what was asked for as though the venue had confirmed it is
the mistake this whole contract is written against.
### Added
- **`cancel_order/3`, `get_order/3`, `get_orders/2`.** The order lifecycle, where there was
none.
**Cancellation is a batch endpoint that refuses per order.** `POST /orders/batch_cancel`
answers with a `results` array carrying its own `success` and `failure_reason` per id, so
a `200` says nothing about whether anything was cancelled. A batch of one is still a
batch. An order already filled comes back as a **refusal**, not an `:ok` — "I cancelled
it" and "it was not there to cancel" are different facts, and a caller retrying on the
second is chasing nothing.
**`CANCEL_QUEUED` maps to `:open`, not `:cancelled`.** An order accepted for cancellation
is still live until the venue says otherwise; reporting it gone invites a second order for
the same exposure.
**A status, side, order type or time-in-force this package does not recognise is `nil`,
never the nearest atom.** A venue adding a word later produces an absent field rather than
a plausible wrong one.
`get_orders/2` filters at the venue rather than in this package — a client-side filter
over one page would silently drop matching orders sitting on the next. **It returns one
page and does not follow the cursor**, which is stated rather than left for a caller to
discover while reconciling.
- **`place_order/3`.** This venue could not place an order; it can now.
Coinbase names the order type and the time-in-force in a **single key** —
`limit_limit_gtc`, `market_market_ioc`, `stop_limit_stop_limit_gtd` — and the set of names
is sparse. There is no `limit_limit_ioc`, no `market_market_gtc`.
**A pair the venue does not name is refused before the request is sent.** Sending
`{:limit, :ioc}` as `limit_limit_fok` would place an order that fills-or-kills where the
caller asked for immediate-or-cancel, and every field in the request would look right.
Three further refusals rather than defaults: a limit without a price, a stop-limit without
a stop price, and a market order sized in neither base nor quote. `post_only` is omitted
when unset rather than sent as `false`, because silence is not a decision to take
liquidity.
A `200` carrying `success: false` is a **refusal**, not a placed order.
`client_order_id` is the venue's idempotency key: a caller's own is passed through, and a
v4 UUID is generated from the VM's CSPRNG when absent.
### Added
- `DeprecatedEndpointsTest` — fails the build if any code path constructs one of Coinbase's
six vendor-deprecated INTX endpoints. They are absent today; nothing kept them absent.
- `docs/reference/coinbase/endpoints-enumerated.tsv` and a rewritten inventory: the documented
surface is **712 REST operations and 46 socket channels**, enumerated endpoint by endpoint
from all 806 reference pages, replacing a page count. **Deribit alone was recorded as 37 and
is 115** — Coinbase renders it as twelve sibling trees with no `deribit` in their paths.
- Prime's custodial staking enumerated: **13 documentation pages, 9 endpoints**, four pairs
being duplicate pages for one path.
### Added
- Repo scaffold from the DpExchange standard; extraction pinned to the host's
`553fa787` with its working-tree state recorded, since the Coinbase subtree was dirty
at extraction time.