An in-process Robinhood, for a consumer's tier-1 tests and for the conformance suite.
It is not a mock. A real implementation of DpExchange.Core.Venue answering from
memory, running the same conformance suite as the real adapter.
What it models that is specific to this venue
- Credentials are required for market data, because the real venue signs every call
and has no anonymous endpoint. Without them:
{:refused, :missing_credentials}. - Coverage is
:internal_poll, not:stream— the one place a consumer can see that this venue has no socket, and it shows up as what is arriving, never as how. - No candles, no order book, no volume. The venue serves none, so neither does this.
Failure injection and anonymous mode
Every function below that has a real success path (not an unconditional
Venue.not_supported()) checks DpExchange.Core.FakeInjection.next_outcome/1 or /2
first — a queued or always-set outcome from FakeInjection.queue_failures/2,3 or
fail_always/2,3 short-circuits the fake's normal logic and is returned as-is.
authenticated/1 also checks FakeInjection.credentials_bypassed?/1 before its normal
{:refused, :missing_credentials} path. Neither changes anything for a test that never
calls FakeInjection — see that module for the full contract.
subscribe/2, unsubscribe/2 and update_symbols/2 are NOT wired: each takes a list
of symbols in one call, and "this one symbol in the batch fails, the rest succeed" is a
case whole-call injection cannot express — see FakeInjection's own moduledoc.
Summary
Functions
coverage/1, split by kind. Same single-key shape as the real venue, and for the
same reason: get_top_of_book/2 above is this fake's only source of subscription
data and it produces exclusively Types.TopOfBook, so :top_of_book is the only
kind there is to report — see DpExchange.Robinhood.coverage_by_kind/1 for why the
family requires this callback even where a venue has nothing to split.
Functions
@spec coverage_by_kind(keyword()) :: %{ required(DpExchange.Core.Capabilities.data_kind()) => %{ required(DpExchange.Core.Venue.symbol()) => DpExchange.Core.Venue.route() } }
coverage/1, split by kind. Same single-key shape as the real venue, and for the
same reason: get_top_of_book/2 above is this fake's only source of subscription
data and it produces exclusively Types.TopOfBook, so :top_of_book is the only
kind there is to report — see DpExchange.Robinhood.coverage_by_kind/1 for why the
family requires this callback even where a venue has nothing to split.