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: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
# rspec failure tracking
.rspec_status
*.gem

# Serena AI assistant workspace
.serena/
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.0)
quantitative (0.4.1)
csv
oj (~> 3.10)
zeitwerk (~> 2.6)
Expand Down
7 changes: 5 additions & 2 deletions lib/quant/ticks/ohlc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ def initialize(
@low_price = low_price.to_f
@close_price = close_price.to_f

@base_volume = (volume || base_volume).to_i
@target_volume = (target_volume || @base_volume).to_i
# Volumes are floats, not ints — crypto markets and many futures markets express
# fractional base/target volumes routinely (e.g., 0.12345 BTC). Existing specs already
# asserted Float values (`eq(2.0)`); a prior `.to_i` regression silently truncated.
@base_volume = (volume || base_volume).to_f
@target_volume = (target_volume || @base_volume).to_f
@trades = trades.to_i

@green = green.nil? ? compute_green : green
Expand Down
8 changes: 6 additions & 2 deletions lib/quant/ticks/spot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ def initialize(
@close_timestamp = extract_time(timestamp || close_timestamp || Quant.current_time)
@open_timestamp = @close_timestamp

@base_volume = (volume || base_volume).to_i
@target_volume = (target_volume || @base_volume).to_i
# Volumes are floats, not ints — crypto markets and many futures markets express
# fractional base/target volumes routinely (e.g., 0.12345 BTC). Existing specs already
# asserted Float values (`eq(2.0)`); a prior `.to_i` regression silently truncated.
# See OHLC for the same fix.
@base_volume = (volume || base_volume).to_f
@target_volume = (target_volume || @base_volume).to_f

@trades = trades.to_i
super()
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.0"
VERSION = "0.4.1"
end
2 changes: 1 addition & 1 deletion quantitative.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Gem::Specification.new do |spec|

spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["changelog_uri"] = spec.homepage
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
Expand Down
19 changes: 18 additions & 1 deletion spec/lib/quant/ticks/ohlc_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
)
end

it { expect(tick.inspect).to eq("#<Quant::Ticks::OHLC ct=2024-01-15T08:30:05Z o=1.0 h=2.0 l=3.0 c=4.0 v=88>") }
it { expect(tick.inspect).to eq("#<Quant::Ticks::OHLC ct=2024-01-15T08:30:05Z o=1.0 h=2.0 l=3.0 c=4.0 v=88.0>") }
end

describe "equality" do
Expand Down Expand Up @@ -71,6 +71,23 @@
end
end

# Regression guard: volumes must be Float, not Integer-truncated. Crypto markets
# express fractional volumes routinely (e.g., 0.12345 BTC); a prior `.to_i` coercion
# in #initialize silently truncated them and passed loose `eq(2.0)` assertions
# because Ruby's `==` coerces `2 == 2.0`. This block asserts both the value AND the
# class so future regressions fail loudly.
context "with fractional crypto-style volumes" do
let(:json) do
{ "ot" => open_time.to_i, "ct" => close_time, "o" => 1.0,
"bv" => 0.12345, "tv" => 5_678.901_234 }
end

it { expect(subject.base_volume).to eq(0.12345) }
it { expect(subject.target_volume).to eq(5_678.901_234) }
it { expect(subject.base_volume).to be_a(Float) }
it { expect(subject.target_volume).to be_a(Float) }
end

context "without volume" do
let(:json) { { "ot" => open_time.to_i, "ct" => close_time, "o" => 1.0 } }

Expand Down
20 changes: 19 additions & 1 deletion spec/lib/quant/ticks/spot_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@
expect(subject.target_volume).to eq(0.0)
end
end

# Regression guard: volumes must be Float, not Integer-truncated. Crypto markets
# (and individual trade prints) express fractional volumes routinely (e.g.,
# 0.12345 BTC). A prior `.to_i` coercion in #initialize silently truncated and
# passed loose `eq(2.0)` assertions because Ruby's `==` coerces `2 == 2.0`.
# This block asserts both the value AND the class so future regressions fail loudly.
# See OHLC for the parallel fix.
context "with fractional crypto-style volumes" do
let(:hash) do
{ "ct" => close_time.to_i, "cp" => 1.0,
"bv" => 0.12345, "tv" => 5_678.901_234 }
end

it { expect(subject.base_volume).to eq(0.12345) }
it { expect(subject.target_volume).to eq(5_678.901_234) }
it { expect(subject.base_volume).to be_a(Float) }
it { expect(subject.target_volume).to be_a(Float) }
end
end

describe ".from_json" do
Expand Down Expand Up @@ -60,7 +78,7 @@
)
end

it { expect(tick.inspect).to eq("#<Quant::Ticks::Spot ct=2024-01-15 08:30:05 UTC c=1.25 v=88>") }
it { expect(tick.inspect).to eq("#<Quant::Ticks::Spot ct=2024-01-15 08:30:05 UTC c=1.25 v=88.0>") }
end

describe "#corresponding?" do
Expand Down
Loading