Skip to content

fix: guard weighted-average merge against zero outputAmountSum in historicalOrderCharts#2777

Open
thedavidmeister wants to merge 10 commits into
mainfrom
2026-06-17-issue-2766-historical-chart-divide-zero
Open

fix: guard weighted-average merge against zero outputAmountSum in historicalOrderCharts#2777
thedavidmeister wants to merge 10 commits into
mainfrom
2026-06-17-issue-2766-historical-chart-divide-zero

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes a divide-by-zero in prepareHistoricalOrderChartData when two or more trades share a timestamp and their outputVaultBalanceChange.amount values sum to zero
  • When outputAmountSum === 0, falls back to unweighted mean of ioratio values instead of dividing by zero (which would produce NaN or ±Infinity)
  • Adds an inline vitest case covering the fallback path: two zero-amount trades at the same timestamp → verifies the result is finite and equals the unweighted mean

Note: this is a pure data computation function with no rendering, so a UI screenshot is not applicable.

Closes #2766

Co-Authored-By: Claude noreply@anthropic.com

Summary by CodeRabbit

  • Bug Fixes

    • Fixed historical order chart merging for same-timestamp trades by safely handling cases where total output is zero, preventing invalid derived ratios and ensuring the merged chart value resolves to 0 instead of error-prone results.
  • Tests

    • Added Vitest coverage for same-timestamp edge cases where output sums to zero and per-trade ratio inputs are non-finite, asserting the merged chart output is 0.

…toricalOrderCharts

When two or more trades share a timestamp and their outputVaultBalanceChange.amount
values sum to zero, dividing ioratioSum by outputAmountSum produced NaN/Infinity.
Falls back to unweighted mean of ioratio values when outputAmountSum === 0.

Closes #2766

Co-Authored-By: Claude <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Jun 17, 2026
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@thedavidmeister, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f7a480e-6956-4c01-be7f-a1d139313713

📥 Commits

Reviewing files that changed from the base of the PR and between b3b5538 and d39af81.

📒 Files selected for processing (1)
  • packages/ui-components/src/lib/services/historicalOrderCharts.ts
📝 Walkthrough

Walkthrough

prepareHistoricalOrderChartData now guards the same-timestamp merge when total output amount is zero, using a fallback mean instead of producing non-finite values. A new Vitest case covers zero-summing output amounts.

Divide-by-zero fix and test

Layer / File(s) Summary
Zero-output-sum guard and coverage
packages/ui-components/src/lib/services/historicalOrderCharts.ts
The same-timestamp merge branch now uses an unweighted mean of finite per-trade ratios when outputAmountSum === 0, and a new Vitest case asserts the merged chart value is 0 for zero-summing trades.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • rainlanguage/raindex#2763: Related changes in historicalOrderCharts.ts also adjust zero-output handling for chart data and add coverage around non-finite values.

Poem

🐇 I hopped to the chart with a tiny fix in sight,
Zero sums no longer derail the graph tonight.
Finite hops together, neat and clear,
The merge stays steady when zeros appear.
No NaN shadows, no Infinity tide—
Just a bunny-approved mean rolling on with pride.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the divide-by-zero fix in historicalOrderCharts.
Linked Issues check ✅ Passed The changes address #2766 by guarding zero output sums and adding a test for the same-timestamp merge case.
Out of Scope Changes check ✅ Passed The patch stays within the requested data-computation fix and associated test coverage, with no unrelated changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2026-06-17-issue-2766-historical-chart-divide-zero

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

thedavidmeister and others added 3 commits June 17, 2026 13:20
… helper

id parameter and orderHash literal in the inline test helper must satisfy
the `0x${string}` template literal type required by RaindexTrade.

Co-Authored-By: Claude <noreply@anthropic.com>
RaindexTrade is a WASM class with a private constructor and additional
required fields (chainId, formattedIoRatio, ioRatio, owner). The inline
test helper builds a partial object literal, so cast through unknown rather
than enumerate all required properties.

Co-Authored-By: Claude <noreply@anthropic.com>
Run prettier on historicalOrderCharts.ts.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui-components/src/lib/services/historicalOrderCharts.ts`:
- Around line 51-55: The fallback calculation for ioratioAverage when
outputAmountSum === 0 does not guard against d.value being non-finite (Infinity
or NaN), which can occur when outputVaultBalanceChange.formattedAmount is "0"
from the initialization at lines 22-25. Filter out non-finite values from the
fallback average calculation by adding a check to ensure each d.value is finite
before including it in the reduce operation. Additionally, update the test
fixture that currently hardcodes "100" to test with "0" values that would expose
this non-finite path and verify the fix handles it correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 68d77525-228b-4304-b28e-e4ae2687eef8

📥 Commits

Reviewing files that changed from the base of the PR and between 175e84a and 671e15f.

📒 Files selected for processing (1)
  • packages/ui-components/src/lib/services/historicalOrderCharts.ts

Comment thread packages/ui-components/src/lib/services/historicalOrderCharts.ts Outdated
thedavidmeister and others added 3 commits June 18, 2026 21:31
When outputVaultBalanceChange.amount is BigInt(0) the formattedAmount
is also "0", making the per-trade value Infinity/NaN.  The previous
fallback divided Infinity × N / N = Infinity.

Filter non-finite values before the unweighted mean; if every entry is
non-finite (i.e. all output amounts are zero), return 0.  Update the
test fixture to use formattedAmount:"0" (consistent with amount
BigInt(0)) and tighten the assertion to expect value 0.

Co-Authored-By: Claude <noreply@anthropic.com>
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

screenshot pending (manual): historicalOrderCharts.ts produces data for lightweight-charts canvas rendering — canvas output requires a headless Chromium harness to screenshot; the change itself is a computation fix (unweighted-mean fallback when outputAmountSum is 0) with no layout change

@thedavidmeister thedavidmeister added the ai:ready AI vetter: passes review, ready for human decision label Jul 6, 2026
@thedavidmeister

thedavidmeister commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

🤖 ai:vetter
Reviewed 7211e78: ready — guards outputAmountSum===0 before division; test covers Infinity-ioratio case
cost 150 — weighted-avg TS divide-by-zero fix

@thedavidmeister thedavidmeister added human:reject Human reviewer: needs rework and removed ai:ready AI vetter: passes review, ready for human decision labels Jul 9, 2026
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Rework note (human): simplify so all branches are reachable + covered. The finiteValues/unweighted-mean branch is dead — same-order output balance changes at one timestamp share a sign, so outputAmountSum === 0 implies every output is 0, hence every value is Infinity and finiteValues is always empty. Collapse to:

const ioratioAverage =
  outputAmountSum === 0 ? 0 : ioratioSum / outputAmountSum;

Delete the finiteValues block. This is behaviorally identical on all reachable inputs, but now both branches are reachable and covered: the existing zero-fallback test still discriminates (old code → NaN, new → 0) for the === 0 branch, and the pre-existing weighted-average test covers the !== 0 branch. Also update that test's comment to drop the "filter non-finite values" reasoning, and fix the PR body line that says the test "equals the unweighted mean" (it asserts 0).

thedavidmeister and others added 2 commits July 9, 2026 21:13
…e (rework)

outputAmountSum === 0 implies every merged value is Infinity (division
by the zero formatted amount), so the finite-values unweighted-mean
branch could never execute; the bucket charts as zero directly.

Co-Authored-By: Claude <noreply@anthropic.com>
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:producer
Producer note: rework applied per the Rework note — the dead finiteValues/unweighted-mean branch is collapsed to outputAmountSum === 0 ? 0 : ioratioSum / outputAmountSum and the test comment no longer narrates non-finite filtering (the zero-sum test asserts 0). Correction to the PR body, which is permission-blocked from editing: the zero-sum test asserts the value is 0, not the unweighted mean. All 6 in-source tests pass.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:producer
Producer note: reworked per rework note — ioratioAverage collapsed to the two-branch ternary outputAmountSum === 0 ? 0 : ioratioSum / outputAmountSum, finiteValues block deleted, zero-sum test comment describes current behavior only (commit d39af81). Evidence: scoped vitest on src/lib/services/historicalOrderCharts.ts = 6/6 passed. Mutation-validation: (1) zero branch mutated to divide → 'charts zero when same-timestamp outputAmounts sum to zero' FAILED (expected 0, received NaN — old behavior reproduced); restored → PASSED. (2) weighted branch swapped to unweighted mean → 'handles the case where multiple trades have the same timestamp' FAILED (expected 0.21428571428571427 weighted, received 0.2916666666666667 unweighted); restored → PASSED, full suite 6/6 green. NOTE: the PR body line claiming the test 'equals the unweighted mean' still needs a body edit (this routine cannot run gh pr edit) — it should say the test asserts 0.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

🤖 ai:producer
Producer note: rework is already executed and evidenced at head d39af81; the three red checks are pre-existing MAIN red, not PR-caused — rainix no-skips static gate (vm.skip x5 in test/lib/deploy, fails rainix-sol/static + rs-static) and missing 0.1.13 soldeer deploy-constant pins (testAllPublishedSoldeerTagsHaveAFullConstantSuite; 687 other sol tests pass). Both are fixed by #2804 (green, ai:ready); this PR greens on a branch update once it lands. flag-blocked-on was refused by the guard (human decision present), so recording the classification here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human:reject Human reviewer: needs rework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

historicalOrderCharts: weighted-average merge divides by zero (NaN/Infinity chart point) when same-timestamp trades' output amounts sum to zero

1 participant