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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.DS_Store
.claude/
.trees/
.wrangler/
__pycache__/
*.egg-info/
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ The index tracks the **top 300 geopolitics-related contracts** from Polymarket,

- **5-minute updates**: Fetch latest prices and recompute index with frozen weights
- **Daily rebalance**: Recalculate weights based on current volume, apply 5% cap
- **Weekly reconstitution**: Re-rank eligible contracts, add/remove constituents using buffer rule
- **Weekly reconstitution + rebalance**: Re-rank eligible contracts, add/remove constituents using buffer rule, and refresh weights

## Architecture

Expand Down Expand Up @@ -144,10 +144,10 @@ cd gp300
pip install -e .

# Run the CLI locally
python -m cli
python -m cli # use python3 if your shell does not provide `python`

# Run tests
python test_engine.py
python -m pytest -q # use python3 if your shell does not provide `python`
```

### Database
Expand Down
8 changes: 8 additions & 0 deletions db.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def save_state(conn, state: IndexState):
"weighted_entropy": state.weighted_entropy,
"num_constituents": state.num_constituents,
"timestamp": state.timestamp.isoformat(),
"last_rebase": state.last_rebase.isoformat() if state.last_rebase else None,
"constituents": [
{
"id": c.id,
Expand Down Expand Up @@ -155,6 +156,12 @@ def load_state(conn) -> IndexState | None:
constituents.append(c)

ts = datetime.fromisoformat(data["timestamp"])
last_rebase = None
if data.get("last_rebase"):
try:
last_rebase = datetime.fromisoformat(data["last_rebase"])
except (ValueError, TypeError):
pass

return IndexState(
value=data["value"],
Expand All @@ -163,6 +170,7 @@ def load_state(conn) -> IndexState | None:
num_constituents=data["num_constituents"],
timestamp=ts,
constituents=constituents,
last_rebase=last_rebase,
)


Expand Down
12 changes: 6 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

Usage:
python main.py # First run: initialize index
python main.py --update # Regular 30-min update (prices only)
python main.py --rebalance # Weekly: recompute weights
python main.py --reconstitute # Biweekly: re-select constituents + weights
python main.py --update # Scheduled 5-minute update (prices only)
python main.py --rebalance # Scheduled daily rebalance
python main.py --reconstitute # Scheduled weekly reconstitution + rebalance
python main.py --dry-run # Compute but don't write files
"""

Expand Down Expand Up @@ -41,15 +41,15 @@ def make_constituent_snapshot(constituents) -> list[dict]:
def main():
parser = argparse.ArgumentParser(description="G&P 300 Index Engine")
parser.add_argument(
"--update", action="store_true", help="Regular price update (30-min cycle)"
"--update", action="store_true", help="Scheduled 5-minute price update"
)
parser.add_argument(
"--rebalance", action="store_true", help="Recompute weights (weekly cycle)"
"--rebalance", action="store_true", help="Scheduled daily rebalance"
)
parser.add_argument(
"--reconstitute",
action="store_true",
help="Re-select constituents (biweekly cycle)",
help="Scheduled weekly reconstitution + rebalance",
)
Comment on lines 41 to 53

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.

src/engine.run_full_pipeline docstring still describes reconstitute as biweekly and rebalance as weekly, which now conflicts with the updated CLI/help text (5-minute/daily/weekly). Consider updating the engine docstring too so schedule documentation is consistent across the codebase.

Copilot uses AI. Check for mistakes.
parser.add_argument(
"--dry-run", action="store_true", help="Compute but don't write files"
Expand Down
4 changes: 2 additions & 2 deletions src/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,8 +625,8 @@ def run_full_pipeline(
current_state: Previous index state (None = first run)
now: Current timestamp
config: Index configuration
reconstitute: If True, re-select constituents (biweekly)
rebalance: If True, recompute weights (weekly)
reconstitute: If True, run the scheduled weekly reconstitution flow
rebalance: If True, run the scheduled daily rebalance flow

Returns:
New IndexState
Expand Down
30 changes: 30 additions & 0 deletions test_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from datetime import datetime, timezone

import db
from src.engine import IndexState


def test_save_and_load_state_preserves_last_rebase(tmp_path, monkeypatch):
monkeypatch.setattr(db, "DB_PATH", tmp_path / "gp300.db")

conn = db.connect_local()
try:
ts = datetime(2026, 4, 12, 18, 30, tzinfo=timezone.utc)
last_rebase = datetime(2026, 4, 6, 0, 0, tzinfo=timezone.utc)
state = IndexState(
value=1000.0,
divisor=0.0005,
weighted_entropy=0.5,
num_constituents=0,
timestamp=ts,
constituents=[],
last_rebase=last_rebase,
)

db.save_state(conn, state)
loaded = db.load_state(conn)

assert loaded is not None
assert loaded.last_rebase == last_rebase
finally:
conn.close()