From 700e23da7bf15e3b0591d1c8bf2a14753358d46a Mon Sep 17 00:00:00 2001 From: Michael Lang Date: Thu, 21 May 2026 23:02:37 -0400 Subject: [PATCH 1/6] T4b: AssetClass::CLASSES cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the 13-entry CLASSES list with the 19-entry agreed-upon set: - Renamed `:treasury_note` → `:treasury` (covers full US-government-debt family — bills, notes, bonds, TIPS; maturity is listing metadata, not AssetClass). - Added `:adr, :cash, :etn, :forward, :swap, :warrant`. - Kept `:stock` (asymmetric vs `:preferred_stock`; operator override on `:common_stock`). Breaking change for consumers exhaustively switching on the constant. --- lib/quant/asset_class.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/quant/asset_class.rb b/lib/quant/asset_class.rb index f327343..a5036ef 100644 --- a/lib/quant/asset_class.rb +++ b/lib/quant/asset_class.rb @@ -42,11 +42,15 @@ module Quant # profit from changes in exchange rates. class AssetClass CLASSES = %i( + adr bond + cash commodity cryptocurrency etf + etn forex + forward future mbs mutual_fund @@ -54,7 +58,9 @@ class AssetClass preferred_stock reit stock - treasury_note + swap + treasury + warrant ).freeze attr_reader :asset_class From 97aed22e1054e7d6f603b2a9fbd6e238a36e33ed Mon Sep 17 00:00:00 2001 From: Michael Lang Date: Thu, 21 May 2026 23:03:20 -0400 Subject: [PATCH 2/6] T4b: AssetClass specs for cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverage for the 19-entry CLASSES catalog: - 19-count invariant. - Per-class acceptance: each of adr, bond, cash, commodity, cryptocurrency, etf, etn, forex, forward, future, mbs, mutual_fund, option, preferred_stock, reit, stock, swap, treasury, warrant initializes without raising and round-trips through #asset_class. - Regression: `:treasury_note` (removed) now raises `Quant::Errors::AssetClassError` — guards against re-introducing the retired symbol. 41 examples / 0 failures locally. --- spec/lib/quant/asset_class_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/lib/quant/asset_class_spec.rb b/spec/lib/quant/asset_class_spec.rb index f19269e..4480912 100644 --- a/spec/lib/quant/asset_class_spec.rb +++ b/spec/lib/quant/asset_class_spec.rb @@ -57,4 +57,23 @@ expect { subject }.to raise_error Quant::Errors::AssetClassError, 'Unknown asset class: "bogus"' end end + + describe "CLASSES catalog" do + it "contains 19 entries" do + expect(described_class::CLASSES.size).to eq 19 + end + + %i[adr bond cash commodity cryptocurrency etf etn forex forward future mbs + mutual_fund option preferred_stock reit stock swap treasury warrant].each do |name| + it "accepts :#{name}" do + expect { described_class.new(name) }.not_to raise_error + expect(described_class.new(name).asset_class).to eq name + end + end + + it "rejects the removed :treasury_note" do + expect { described_class.new(:treasury_note) } + .to raise_error Quant::Errors::AssetClassError, "Unknown asset class: :treasury_note" + end + end end From 4d9af33da55ed0fde908ed656f541e5a62930df0 Mon Sep 17 00:00:00 2001 From: Michael Lang Date: Thu, 21 May 2026 23:03:40 -0400 Subject: [PATCH 3/6] T4b: version bump to 0.5.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing :treasury_note from CLASSES is a breaking change for consumers exhaustively switching on the set. Under pre-1.0 semver, minor bump signals breaking; bumping 0.4.1 → 0.5.0 rather than 1.0.0 (scope of T4b is grooming, not a public-API stabilization milestone). --- lib/quant/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/quant/version.rb b/lib/quant/version.rb index 8f90815..0e9aabe 100644 --- a/lib/quant/version.rb +++ b/lib/quant/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Quant - VERSION = "0.4.1" + VERSION = "0.5.0" end From 2490498b5efca47ed2600b378bcd6f63d80ef212 Mon Sep 17 00:00:00 2001 From: Michael Lang Date: Thu, 21 May 2026 23:04:16 -0400 Subject: [PATCH 4/6] =?UTF-8?q?T4b:=20CHANGELOG=20=E2=80=94=200.5.0=20entr?= =?UTF-8?q?y=20+=20baseline=20capture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds CHANGELOG.md to the tracked tree. The 0.4.1 entry (orphaned in the operator's working tree from a prior session, never committed) is captured as the baseline; the 0.5.0 entry covers this task's AssetClass cleanup. Subsequent releases append above. Per Keep-a-Changelog + SemVer conventions. --- CHANGELOG.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8a6b0b8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,50 @@ +# Changelog + +All notable changes to `quantitative` 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). + +--- + +## [0.5.0] — 2026-05-22 + +### Changed (breaking) + +- **`Quant::AssetClass::CLASSES` renamed `:treasury_note` → `:treasury`.** The single `:treasury` symbol now covers the full US-government-debt family (bills, notes, bonds, TIPS). Maturity becomes listing metadata, not an AssetClass distinction. Breaking for consumers that exhaustively switch on the `CLASSES` set or pattern-match on `:treasury_note`. + + Migration: `s/:treasury_note/:treasury/g` across consumer code. Where maturity-aware behavior is needed (e.g., yield-curve segmentation), consult the listing's metadata rather than the AssetClass label. + +### Added + +- `Quant::AssetClass::CLASSES` gains six new entries: `:adr, :cash, :etn, :forward, :swap, :warrant`. Final catalog is 19 entries: `adr, bond, cash, commodity, cryptocurrency, etf, etn, forex, forward, future, mbs, mutual_fund, option, preferred_stock, reit, stock, swap, treasury, warrant`. +- Per-class spec coverage in `spec/lib/quant/asset_class_spec.rb` — each catalog entry is `valid?`, the 19-count invariant is asserted, and `:treasury_note` rejection is a regression guard against re-introducing the retired symbol. + +--- + +## [0.4.1] — 2026-05 + +### Fixed + +- **`Quant::Ticks::OHLC#initialize` and `Quant::Ticks::Spot#initialize` now preserve fractional volumes as `Float`.** A prior regression in both classes coerced `@base_volume` and `@target_volume` via `.to_i`, silently truncating fractional values to integers — a serious correctness issue for crypto markets (and any other domain where fractional base/target volumes are normal, such as fractional-share trading and many futures markets). For example, a kline with `base_volume: 0.12345` was previously stored as `0`. Existing spec assertions used `eq(2.0)` which masked the regression because Ruby's `==` coerces `2 == 2.0 → true`; the values *appeared* correct in tests while being silently truncated in production. Both initializers now coerce via `.to_f`. Trade count (`@trades`) continues to use `.to_i` as it is genuinely an integer. + + Affected files: + - `lib/quant/ticks/ohlc.rb` (lines 51–52) + - `lib/quant/ticks/spot.rb` (lines 46–47) + + Backwards compatibility: this is a behavior change for callers reading `tick.base_volume` / `tick.target_volume`. Code that did `volume == 2` continues to work (Ruby coerces). Code that did `volume.is_a?(Integer)` will see `false` where it previously saw `true`; such code was implicitly relying on the bug and should be updated to `is_a?(Numeric)` or `is_a?(Float)`. Code that did `volume + 0.5` continues to work, but the result is now precise instead of truncating to integer-then-promoting. + +### Added + +- Regression-guard specs in `spec/lib/quant/ticks/ohlc_spec.rb` and `spec/lib/quant/ticks/spot_spec.rb` that explicitly assert `Float` class membership and use fractional values (`0.12345`, `5_678.901_234`) so a future `.to_i` regression would fail loudly rather than silently coerce. +- This CHANGELOG. + +--- + +## [0.4.0] — prior baseline + +Prior baseline before this CHANGELOG was introduced. Subsequent entries above will track changes from 0.4.1 forward. + +[0.5.0]: https://github.com/mwlang/quantitative/compare/v0.4.1...v0.5.0 +[0.4.1]: https://github.com/mwlang/quantitative/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/mwlang/quantitative/releases/tag/v0.4.0 From b0c9e1b243b1c3ff49429a90b6437e63fb11e965 Mon Sep 17 00:00:00 2001 From: Michael Lang Date: Fri, 22 May 2026 09:38:52 -0400 Subject: [PATCH 5/6] T4b: regenerate Gemfile.lock for quantitative 0.5.0 CI runs 'bundle install --frozen' and rejects the lockfile/gemspec mismatch when VERSION bumps without the lockfile being regenerated. The PATH section now reads 'quantitative (0.5.0)' to match the gemspec. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index ccc6381..bf2bf2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - quantitative (0.4.1) + quantitative (0.5.0) csv oj (~> 3.10) zeitwerk (~> 2.6) From 64e17f4d589661ee0596ffe8bca32c072a172bea Mon Sep 17 00:00:00 2001 From: Michael Lang Date: Fri, 22 May 2026 09:41:38 -0400 Subject: [PATCH 6/6] =?UTF-8?q?ci:=20bump=20codecov-action=20v4.0.1=20?= =?UTF-8?q?=E2=86=92=20v5=20+=20continue-on-error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v4.0.1 (Feb 2024) was hitting a bootstrap regression on the Codecov-side CLI fetch: 'Could not pull latest version information: SyntaxError: Unexpected token <,