Packages
dp_exchange_schwab
0.2.21
0.2.25
0.2.24
0.2.23
0.2.22
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.41
0.1.40
0.1.39
0.1.38
0.1.37
0.1.36
0.1.35
0.1.34
0.1.33
0.1.32
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 — Schwab 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_schwab
mix.exs
mix.exs
defmodule DpExchangeSchwab.MixProject do
use Mix.Project
# SEED, not a release. CI increments the last segment of whatever it finds here, so
# `0.1.0` first publishes as `0.1.1`. Hand-editing this to `0.2.0` is how a breaking
# change is signalled. The bump script matches the attribute assignment below by its
# exact literal form — do not reformat it, and do not repeat that form anywhere else
# in this file, comments included, or the script will rewrite the wrong line.
@version "0.2.21"
@source_url "https://github.com/DistortionPoint/dp-exchange-schwab"
def project do
[
app: :dp_exchange_schwab,
version: @version,
elixir: "~> 1.18",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
deps: deps(),
aliases: aliases(),
dialyzer: dialyzer(),
preferred_cli_env: preferred_cli_env(),
test_coverage: test_coverage(),
# Hex.pm
name: "DpExchangeSchwab",
description:
"EXPERIMENTAL — Schwab venue package for the DpExchange family. Market data, " <>
"trading and streaming behind the shared DpExchange.Core.Venue facade.",
package: package(),
source_url: @source_url,
docs: docs(),
usage_rules: usage_rules()
]
end
# No `mod:` — a library does not start itself. A consumer supervises this venue
# through `child_spec/1` and decides restart strategy, shutdown order and naming. A
# consumer that has not asked for Schwab must not find a socket open.
def application do
[extra_applications: [:logger]]
end
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_env), do: ["lib"]
defp deps do
[
# The contract. Three-part pin: while Core is 0.x a minor bump may break us, and
# that is the signal it is meant to send.
# Needs `Timeframe.nameable/0` — this venue serves 1w and 1M, which Core can name
# but deliberately cannot bucket — and `max_leverage: :per_account`, because Reg-T
# has no single ceiling. Both landed in Core for this package.
#
# Bumped to 0.1.50 for `PollingFeed.start_link/1`'s new `:on_notice` option — see
# `start_poller/1` in `Feed` and its moduledoc for why the fallback poll needs it
# (DpCryptoManagement's issue #21).
# `0.2.6` is the floor now, and unlike the history below it is a HARD one: `Feed`
# calls `Core.Fanout.max_queue_len!/2` in `init/1` and `Core.Fanout.deliver/4` on
# every payload, and neither existed before 0.2.6. Against a lower Core this package
# does not merely misbehave, it fails to compile — which is the good outcome, and the
# reason the floor is stated rather than left to `script/check_dependency_floor.sh` to
# discover. The older floor history is kept above because its lesson is the one that
# keeps applying: a floor is only correct once it has been RESOLVED and compiled
# against, never once it has been reasoned about.
#
# `0.3.1` is a MINOR bump, and the signal is deliberate: Core 0.3.0 deleted
# `Core.DataProvider` and `Core.FeedBehaviour`, two contracts with zero implementers.
# Nothing in this package referenced either, so there is no code change here — the
# floor moves because a pin of `~> 0.2.8` would not resolve 0.3.x, which is the pin
# doing its job rather than a problem to route around. Resolved and compiled against
# before this line was written, per the lesson recorded below: a floor is only correct
# once it has been RESOLVED, never once it has been reasoned about.
#
#
# `0.3.7` is the floor because this package's contract test passes `endpoint_opts:` to
# `Core.AdapterContract`, and that option does not exist before it. An older Core does
# not fail — it ignores the option, which is worse: the fake-driven assertions go back
# to calling this venue's account-scoped endpoints with no account, getting refused
# before they reach the behaviour under test, and skipping. Green, and proving nothing.
# That is the exact false pass the option was added to remove, so the floor is what
# keeps it removed. Resolved and compiled against before this line was written.
{:dp_exchange_core, "~> 0.3.7"},
# Core ships no venue-specific dependency: a venue that speaks WebSocket ships what
# it needs to speak it.
#
# **This package now speaks the venue's Streamer.** The comment here used to read
# "this venue has no streaming API at all", which was false — Schwab publishes a
# WebSocket Streamer with 15 services, including three book services and
# `ACCT_ACTIVITY`. It is described in the prose documentation this repository already
# held (`docs/reference/schwab/documentation/market-data-production.txt`) and is
# absent from both OpenAPI documents, which is exactly how the error was made: the
# specifications were read and the prose beside them was not.
#
# The comment then said "add `websockex` when it is implemented, and not before".
# It is, so it is here.
{:websockex, "~> 0.4"},
{:jason, "~> 1.4"},
{:decimal, "~> 2.0"},
# Dev/Test
{:usage_rules, "~> 1.2", only: :dev},
{:ex_doc, "~> 0.34", only: :dev, runtime: false},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:sobelow, "~> 0.13", only: [:dev, :test], runtime: false},
# Exercises the REST pipeline through Req's test seam, so tier-1 tests reach no
# network. Never ships.
{:plug, "~> 1.16", only: :test}
]
end
defp test_coverage, do: [threshold: 90, ignore_modules: []]
defp aliases do
[quality: ["format --check-formatted", "credo --strict", "dialyzer", "sobelow --config"]]
end
defp dialyzer do
[plt_add_apps: [:mix, :ex_unit], plt_file: {:no_warn, "priv/plts/dialyzer.plt"}]
end
defp preferred_cli_env, do: [quality: :test]
defp package do
[
licenses: ["MIT"],
links: %{"GitHub" => @source_url},
maintainers: ["bcatherall"],
# `files:` belongs HERE, not at the project level. Hex reads `package[:files]`; a
# `files:` in `project/0` is silently ignored and Hex ships its own defaults —
# which puts `priv/plts/dialyzer.plt` in the tarball and leaves out anything you
# meant to add. Nothing warns. Inspect `mix hex.build` before every publish.
#
# `priv/` is absent because the only thing in it is the dialyzer PLT.
# `config/` is absent: it governs this package's own dev and test, never a
# consumer's.
files: [
"lib",
"mix.exs",
".formatter.exs",
"README.md",
"LICENSE",
"CHANGELOG.md",
"AGENTS.md",
"usage-rules.md",
"docs/reference"
]
]
end
defp docs do
[
main: "DpExchange.Schwab",
extras: ["README.md", "CHANGELOG.md", "usage-rules.md"],
source_ref: "v#{@version}"
]
end
defp usage_rules, do: [file: "AGENTS.md", usage_rules: [:usage_rules]]
end