Consolidate zero-denominator guards into helper.SafeDivide - #505
Open
cinar wants to merge 1 commit into
Open
Conversation
Replace 17 ad hoc "if denom == 0 { return fallback }" guards across
trend, momentum, volatility, and volume with a single generic
helper.SafeDivide[T Number](numerator, denominator, fallback T) T.
Behavior and fallback values (0, 50, or 0.5) are unchanged at every
call site; volume.Vwap (carries forward the last valid value) and
trend.McginleyDynamic (same shape) are left as-is since they don't
fit the constant-fallback pattern, and momentum.Rsi and
volatility.Chop are left as-is since their guards check a compound
or non-quotient condition rather than a plain zero denominator.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xv4stuAb6WuQ8rPZ4cupLp
Codecov Reportβ
All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #505 +/- ##
==========================================
+ Coverage 91.96% 91.99% +0.02%
==========================================
Files 233 234 +1
Lines 7719 7703 -16
==========================================
- Hits 7099 7086 -13
+ Misses 531 528 -3
Partials 89 89 β View full report in Codecov by Harness. π New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Earlier work independently added zero-denominator/NaN guards across ~17 indicators in
trend,momentum,volatility, andvolume, each shaped like:This PR adds a single generic helper,
helper.SafeDivide[T Number](numerator, denominator, fallback T) T(inhelper/safe_divide.go, modeled on the existing scalar helperhelper.RoundDigit), and refactors every matching call site to use it. Behavior is unchanged β same fallback values, same floating-point evaluation order β verified by the full existing test suite passing unmodified.Call sites refactored
Direct pattern (
SafeDivide(num, denom, fallback)replaces the guard 1:1):trend/kama.goβ Efficiency Ratio, fallback0trend/bop.goβ BOP, fallback0trend/vwma.goβ VWMA, fallback0momentum/ibs.goβ Internal Bar Strength, fallback0momentum/ultimate_oscillator.goβ per-period average, fallback0.5volatility/bollinger_band_width.goβ fallback0volatility/z_score.goβ fallback0volatility/acceleration_bands.goβ fallback0volatility/percent_b.goβ %B, fallback0.5volume/mfm.goβ Money Flow Multiplier, fallback0volume/cmf.goβ CMF, fallback0Scaled pattern (original code applied a
* 100or pre-multiplied the numerator by100around the guarded division; to keep the arithmetic byte-for-byte identical, the fallback is expressed in the same pre-scale units so the surrounding multiply reproduces the exact original float value):trend/kdj.goβ RSV:SafeDivide(num, denom, 0.5) * 100(0.5 Γ 100 = 50, exact)trend/slow_stochastic.goβ fastK: same transformtrend/stochastic.goβ %K: same transformmomentum/stochastic_oscillator.goβ fastK: same transformtrend/tsi.goβSafeDivide(pcd, apcd, 0) * 100(fallback 0, scaling is a no-op)trend/cfo.goβSafeDivide(num, price, 0) * 100(same)volatility/po.goβ original pre-multiplied the numerator (100 * (closing - pl) / denom), so here it'sSafeDivide(100*(closing-pl), denom, 50)β fallback needs no rescaling since the original guard already returned the unscaled50literalhelper.SafeDivideis constrained tohelper.Number(nothelper.Float) specifically so thattrend/tsi.go, whoseTsi[T helper.Number]is generic over integers too, can call it directly without widening or narrowing any existing type's constraint.Explicitly skipped (do not fit the pattern)
volume/vwap.goβ carries forward the last valid computed value instead of a constant fallback (per task instructions, left untouched).trend/mcginley_dynamic.goβ same shape as Vwap: on a zero previous value it reseeds state and takes a completely different code path (not a single guarded return of a quotient), so it isn't aSafeDividecandidate.momentum/rsi.goβ the guard condition isaverageGain == 0 && averageLoss == 0(compound, two variables), not a single denominator check, so it doesn't fitSafeDivide(numerator, denominator, fallback)'s shape.volatility/chop.goβ the guarded division feeds intomath.Log10(sum/diff)rather than being returned directly as a quotient; the guard and the formula shape don't reduce to a plainnumerator/denominator.Test plan
go build ./...go vet ./...gofmt -l .(no new files flagged; pre-existing unrelated formatting debt in unrelatedexamples//strategy//backtesttest files, untouched by this PR)go test ./... -timeout 300sβ all packages pass unmodified, confirming no behavior changeπ€ Generated with Claude Code
https://claude.ai/code/session_01Xv4stuAb6WuQ8rPZ4cupLp