Skip to content

Add bimonthly index rebase to prevent structural decay#1

Merged
DavyMorgan merged 2 commits into
mainfrom
dev
Apr 12, 2026
Merged

Add bimonthly index rebase to prevent structural decay#1
DavyMorgan merged 2 commits into
mainfrom
dev

Conversation

@DavyMorgan

Copy link
Copy Markdown
Collaborator

Summary

The GP 300 index has dropped from 1000 to 192 in one month due to structural decay: prediction markets inherently converge toward certainty (entropy → 0), and the fixed divisor can't counteract this. Without intervention, the index would approach zero.

This PR adds a periodic full rebase plus schedule changes to keep the index in a meaningful range.

  • Bimonthly rebase: Every 8 weeks, reset divisor so index = 1000 (no continuity at rebase points — intentional)
  • Schedule changes: Weekly reconstitution (Sunday) + daily rebalance (every midnight) replace the previous biweekly reconstitution + weekly rebalance
  • Chart updates: New "Period" range button (default), rebase markers (orange arrows), last_rebase field in /api/current

Backward compatible with existing D1 data — the state JSON blob adds a new last_rebase field that defaults to None for existing state, which triggers a rebase on the next Sunday reconstitution.

Test plan

  • Existing engine tests pass (pytest test_engine.py)
  • Verified rebase fires at 8-week boundary in isolated test
  • Verified rebase does NOT fire before 8 weeks
  • Deploy to production — next Sunday reconstitution should reset index from ~192 to 1000
  • Verify chart shows rebase marker and defaults to "Period" view after first rebase

🤖 Generated with Claude Code

The index decays structurally because prediction markets converge toward
certainty (entropy → 0). This adds a full rebase every 8 weeks that
resets the index to 1000, plus schedule changes for fresher data:

- Engine: rebase logic resets divisor every 8 weekly reconstitutions
- Worker: new cron schedule (daily rebalance, weekly reconstitution)
- Chart: "Period" range button, rebase markers, default to current period
- API: last_rebase field in /api/current response

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 12, 2026 13:30
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gp300 66c3284 Commit Preview URL

Branch Preview URL
Apr 12 2026, 05:04 PM

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a periodic “rebase” mechanism and scheduling/UI updates to keep the GP 300 index in a meaningful range despite entropy-driven structural decay.

Changes:

  • Introduces an 8-week rebase (divisor reset so index returns to 1000) tracked via last_rebase and a rebased flag.
  • Updates cron schedule to weekly reconstitution (Sunday) plus daily rebalance (midnight), with 5-min updates skipping midnight.
  • Updates /api/current and the frontend chart to support a “Period” range and display rebase markers.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
wrangler.toml Updates cron triggers to daily rebalance + weekly reconstitution.
src/worker.py Persists/serves last_rebase, logs rebase adjustments, and updates scheduled cron routing.
src/engine.py Adds rebase configuration/state fields and implements rebase logic during reconstitution.
public/index.html Adds “Period” range selection and renders rebase markers on the chart.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/engine.py
Comment on lines +732 to +752
# Check if a bimonthly rebase is due (only at reconstitution)
last_rebase = current_state.last_rebase
should_rebase = reconstitute and (
last_rebase is None
or (now - last_rebase).days >= config.rebase_interval_weeks * 7
)

if should_rebase and we > 0:
new_divisor = we / config.base_value
value = config.base_value
return IndexState(
value=value,
divisor=new_divisor,
weighted_entropy=we,
num_constituents=len(selected),
timestamp=now,
constituents=selected,
pre_adjustment_value=pre_val,
last_rebase=now,
rebased=True,
)

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

The new rebase path doesn’t appear to have unit test coverage. Given the engine already has targeted edge-case tests, please add tests that (1) assert a rebase triggers exactly at/after the configured 8-week interval on a reconstitution run, (2) does not trigger before the interval, and (3) confirms last_rebase and rebased are set as expected when the rebase happens.

Copilot uses AI. Check for mistakes.
Comment thread src/worker.py Outdated
adj_data = {
"divisor_before": prev_state.divisor,
"divisor_after": state.divisor,
"index_before": round(prev_state.value, 2),

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

For the rebase adjustment log, index_before is taken from prev_state.value, which may be stale relative to the reconstitution timestamp (other adjustment events use state.pre_adjustment_value when available). If you want the audit trail to reflect the true pre-rebase level at current prices, consider logging state.pre_adjustment_value (or computing an equivalent) as index_before.

Suggested change
"index_before": round(prev_state.value, 2),
"index_before": round(state.pre_adjustment_value, 2)
if state.pre_adjustment_value is not None
else round(prev_state.value, 2),

Copilot uses AI. Check for mistakes.
Comment thread public/index.html Outdated
Comment on lines +771 to +777
// Default to current rebase period if available, else 1M
if (currentData && currentData.last_rebase) {
activeRange = 'PERIOD';
document.querySelectorAll('.period-btn').forEach(b => b.classList.remove('active'));
const periodBtn = document.querySelector('.period-btn[data-range="PERIOD"]');
if (periodBtn) periodBtn.classList.add('active');
}

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

The comment says “Default to current rebase period if available, else 1M”, but activeRange is initialized to '1D' and is only changed when last_rebase is present. Either update the comment to match the actual default, or set activeRange to '1M' in the else branch if that’s the intended behavior.

Copilot uses AI. Check for mistakes.
Comment thread public/index.html Outdated
Comment on lines +576 to +582
// Detect rebase points: where index jumps back to ~1000 from a much lower value
const rebases = [];
for (let i = 1; i < historyData.length; i++) {
const prev = historyData[i - 1].value;
const curr = historyData[i].value;
// Rebase: value jumps by >50% upward and lands near 1000
if (curr > prev * 1.5 && curr > 800) {

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

findRebaseTimestamps() uses a heuristic (curr > prev * 1.5 && curr > 800) that can misidentify large organic moves as a rebase. Since a rebase sets the index to the configured base (1000) deterministically, consider tightening detection to check that the post-jump value is near the base (e.g., within an epsilon of 1000) and that the prior value was materially below it, to avoid false positives in marker rendering.

Suggested change
// Detect rebase points: where index jumps back to ~1000 from a much lower value
const rebases = [];
for (let i = 1; i < historyData.length; i++) {
const prev = historyData[i - 1].value;
const curr = historyData[i].value;
// Rebase: value jumps by >50% upward and lands near 1000
if (curr > prev * 1.5 && curr > 800) {
// Detect rebase points: where index resets to the configured base from a materially lower value
const REBASE_BASE = 1000;
const REBASE_EPSILON = 25;
const MIN_PRE_REBASE_GAP = 100;
const rebases = [];
for (let i = 1; i < historyData.length; i++) {
const prev = historyData[i - 1].value;
const curr = historyData[i].value;
const landsNearBase = Math.abs(curr - REBASE_BASE) <= REBASE_EPSILON;
const prevMateriallyBelowBase = prev <= REBASE_BASE - MIN_PRE_REBASE_GAP;
// Rebase: value moves upward and lands back near the configured base
if (curr > prev && landsNearBase && prevMateriallyBelowBase) {

Copilot uses AI. Check for mistakes.
- worker: use state.pre_adjustment_value for rebase index_before (consistent with other adjustment events)
- index.html: tighten rebase detection with explicit bounds (proximity to base + pre-rebase gap) to avoid false positives on organic moves
- index.html: clarify default range comment
- test: add coverage for rebase logic (triggers at 8 weeks, not before, skips on rebalance-only, sets fields correctly)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@DavyMorgan

Copy link
Copy Markdown
Collaborator Author

@copilot to review again.

Copilot AI commented Apr 12, 2026

Copy link
Copy Markdown

@copilot to review again.

All four review items have been addressed in commit 66c3284:

  1. Rebase unit tests — Added test_rebase_on_first_run, test_rebase_does_not_trigger_before_interval, test_rebase_triggers_at_interval, and test_rebase_skips_on_rebalance_only. All 11 tests pass.
  2. index_before audit log — Now uses state.pre_adjustment_value (falling back to prev_state.value), consistent with other adjustment events.
  3. findRebaseTimestamps() heuristic — Tightened to check landsNearBase (within ±25 of 1000) and prevMateriallyBelowBase (≥100 below base), eliminating false positives on large organic moves.
  4. Comment accuracy — Updated to "else keep the HTML default (1D)" to match actual behavior.

@DavyMorgan DavyMorgan merged commit b00e0c7 into main Apr 12, 2026
2 of 3 checks passed
Copilot stopped work on behalf of DavyMorgan due to an error April 12, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants