Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ jobs:
run: bundle exec rspec

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: mwlang/quantitative
continue-on-error: true
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
quantitative (0.4.1)
quantitative (0.5.0)
csv
oj (~> 3.10)
zeitwerk (~> 2.6)
Expand Down
8 changes: 7 additions & 1 deletion lib/quant/asset_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,25 @@ 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
option
preferred_stock
reit
stock
treasury_note
swap
treasury
warrant
).freeze

attr_reader :asset_class
Expand Down
2 changes: 1 addition & 1 deletion lib/quant/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Quant
VERSION = "0.4.1"
VERSION = "0.5.0"
end
19 changes: 19 additions & 0 deletions spec/lib/quant/asset_class_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading