Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
859c076
feat: add prediction market sentiment engine
Barac9492 Mar 16, 2026
958867a
fix: Vercel build + redesign PredictionView UI/UX
Barac9492 Mar 16, 2026
2f26804
fix: add SPA rewrite rule for Vercel client-side routing
Barac9492 Mar 16, 2026
15b2177
feat: add Anthropic Claude support alongside Ollama/OpenAI
Barac9492 Mar 16, 2026
9e6e6c1
fix: Polymarket JSON string parsing + Anthropic support
Barac9492 Mar 16, 2026
40e5db8
Add PRD for Polymarket monetization engine
claude Mar 16, 2026
94d0ea5
Merge pull request #1 from Barac9492/claude/implement-todo-item-GZHYu
Barac9492 Mar 16, 2026
4747fd6
fix: refactor OasisProfileGenerator + SimulationConfigGenerator to us…
Barac9492 Mar 16, 2026
528389d
feat: add backtest script + reduce default rounds to 2
Barac9492 Mar 17, 2026
5513542
fix: HOLD signal when 0 posts analyzed, prevent false BUY signals
Barac9492 Mar 17, 2026
5debd9f
feat: split LLM strategy — Claude for analysis, Ollama for simulation
Barac9492 Mar 17, 2026
c4525c9
feat: replace OASIS simulation with direct LLM debate
Barac9492 Mar 17, 2026
b00c3bb
feat: tune prompts — evidence-weighted debate + blended probability
Barac9492 Mar 17, 2026
b47e484
feat: add signal calibration — market regression, edge penalty, date …
Barac9492 Mar 17, 2026
b9bc1f7
refactor: dead code cleanup, comment translation, calibration config …
Barac9492 Mar 18, 2026
59dc5ad
feat: add SQLite storage layer and backtest/position data models
Barac9492 Mar 18, 2026
c13d848
feat: add backtesting engine, calibration service, and paper trader
Barac9492 Mar 18, 2026
5e461fe
feat: add backtest API endpoints
Barac9492 Mar 18, 2026
1727353
test: add 62-test suite for backtesting system
Barac9492 Mar 18, 2026
7c2eab3
feat: add BacktestView frontend + PAPER badge
Barac9492 Mar 18, 2026
aa0dbc5
docs: add DESIGN.md, TODOS.md, update task tracker
Barac9492 Mar 18, 2026
10fa428
Merge remote-tracking branch 'origin/main' into feat/phase1-backtesting
Barac9492 Mar 18, 2026
1a7a003
docs: update project documentation for Phase 1
Barac9492 Mar 18, 2026
5bd459f
Merge pull request #1 from Barac9492/feat/phase1-backtesting
Barac9492 Mar 18, 2026
006b872
chore: add project config, design doc, and market data
Barac9492 Mar 18, 2026
f1f57df
Merge branch 'feat/phase1-backtesting'
Barac9492 Mar 18, 2026
22b6114
feat: add category/tier schema and market classification storage
Barac9492 Mar 18, 2026
157d7ea
feat: add MarketClassifier service with LLM classification and caching
Barac9492 Mar 18, 2026
c02e5c6
feat: per-category metrics and calibration offsets in backtester
Barac9492 Mar 18, 2026
7d24d26
feat: apply category calibration offsets in live predictions
Barac9492 Mar 18, 2026
c971a86
feat: category breakdown and confidence tier UI panels
Barac9492 Mar 18, 2026
35981af
test: comprehensive Phase 2 test suite (34 new tests)
Barac9492 Mar 18, 2026
7ead31e
chore: update DESIGN.md tokens, TODOS.md, and todo tracker
Barac9492 Mar 18, 2026
8360ea2
fix: add disk-full error handling to all SQLite write operations
Barac9492 Mar 18, 2026
3b7d8d7
chore: add CI/CD pipeline via GitHub Actions
Barac9492 Mar 18, 2026
993f238
feat: migrate prediction runs from JSON files to SQLite
Barac9492 Mar 18, 2026
725d7f6
chore: mark all TODOs complete
Barac9492 Mar 18, 2026
c8b6dd7
fix(qa): ISSUE-002 — roiClass referenced .summary.roi instead of .met…
Barac9492 Mar 19, 2026
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
55 changes: 55 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
backend-tests:
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Install dependencies
run: uv sync --dev

- name: Run tests
run: uv run python -m pytest tests/ -v --tb=short
env:
LLM_API_KEY: "test-key"

frontend-build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build
15 changes: 9 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.DS_Store
Thumbs.db

# 环境变量(保护敏感信息)
# Environment variables
.env
.env.local
.env.*.local
Expand Down Expand Up @@ -36,7 +36,7 @@ yarn-error.log*
*.swp
*.swo

# 测试
# Test
.pytest_cache/
.coverage
htmlcov/
Expand All @@ -45,17 +45,20 @@ htmlcov/
.cursor/
.claude/

# 文档与测试程序
# Documentation and test programs
mydoc/
mytest/

# 日志文件
# Log files
backend/logs/
*.log

# 上传文件
# Uploads
backend/uploads/

# Docker 数据
# SQLite data
backend/data/

# Docker
data/backend/venv311/
backend/venv311/
118 changes: 118 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# MiroFish Design Tokens

Extracted from `PredictionView.vue`. All new views and components must follow these tokens.

---

## Typography

| Role | Family | Variable |
|-------------|---------------------------------------------------|------------|
| Monospace | `'JetBrains Mono', 'SF Mono', monospace` | `--mono` |
| Sans-serif | `'Space Grotesk', 'Noto Sans SC', system-ui, sans-serif` | `--sans` |

### Sizes & Weights

| Element | Size | Weight | Letter-spacing |
|-------------------|---------|--------|----------------|
| Page title | 28px | 700 | -0.02em |
| Section heading | 16px | 600 | -0.01em |
| Body text | 14px | 400 | normal |
| Small / caption | 12px | 500 | 0.02em |
| Badge label | 11px | 600 | 0.05em |
| Mono data | 13-14px | 500 | normal |

---

## Colors

| Token | Hex | Usage |
|--------------------|-----------|--------------------------------|
| `--text-primary` | `#000` | Nav, headings, primary text |
| `--orange` | `#FF4500` | Accent, links, active states |
| `--green` | `#10B981` | Success, BUY signal, positive |
| `--red` | `#dc2626` | Error, SELL signal, negative |
| `--amber` | `#F59E0B` | Warning, MEDIUM tier |
| `--amber-bg` | `#FFFBEB` | Amber background fill |
| `--border` | `#EAEAEA` | Panel borders, dividers |
| `--bg-subtle` | `#FAFAFA` | Subtle background fills |
| `--text-secondary` | `#666` | Secondary labels |
| `--text-muted` | `#999` | Muted / tertiary text |

---

## Spacing

| Token | Value | Usage |
|-------------|---------|----------------------------|
| Max-width | 1400px | Page container |
| Padding | 40px | Container horizontal pad |
| Grid gap | 30px | Between panel columns |

---

## Components

### Panels
- Border: `1px solid var(--border)`
- Border-radius: **0** (no rounded corners)
- Background: `#fff`
- No box-shadow

### Badges
- Uppercase text, `11px` font, `600` weight, `0.05em` letter-spacing
- Padding: `4px 10px`
- Border: `1px solid` (color matches text)
- No border-radius

### Skeleton Loaders
- Background: `var(--bg-subtle)`
- Shimmer animation (left-to-right sweep)
- Match the dimensions of the content they replace

### Empty States
- Centered text, muted color (`var(--text-muted)`)
- Optional icon above text

### Progress Bars
- Track: `var(--bg-subtle)`
- Fill: `var(--orange)` or signal color
- Height: 4-6px
- No border-radius

---

## Anti-patterns

Do **not** use:
- Rounded corners (`border-radius`)
- Box shadows (`box-shadow`)
- Gradient fills (`linear-gradient`, `radial-gradient`)

---

## CSS Variables Reference

```css
:root {
--mono: 'JetBrains Mono', 'SF Mono', monospace;
--sans: 'Space Grotesk', 'Noto Sans SC', system-ui, sans-serif;
--orange: #FF4500;
--green: #10B981;
--red: #dc2626;
--border: #EAEAEA;
--bg-subtle: #FAFAFA;
--text-primary: #000;
--text-secondary: #666;
--text-muted: #999;
}
```

---

## Responsive Breakpoints

| Breakpoint | Target | Notes |
|------------|---------|-----------------------------------|
| 1024px | Tablet | Stack grid to single column |
| 768px | Mobile | Reduce padding, smaller type |
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ The [original MiroFish](https://github.com/666ghj/MiroFish) was built for the Ch
3. **Simulation** — Agents interact on simulated social platforms: posting, replying, arguing, shifting opinions. The system tracks sentiment evolution, topic propagation, and influence dynamics in real time.
4. **Report** — A ReportAgent analyzes the post-simulation environment, interviews a focus group of agents, searches the knowledge graph for evidence, and generates a structured analysis.
5. **Interaction** — Chat with any agent from the simulated world. Ask them why they posted what they posted. Full memory and personality persists.
6. **Prediction Markets** — Browse live Polymarket markets, run a multi-agent debate simulation, and generate calibrated trading signals (BUY_YES / BUY_NO / HOLD) with edge and confidence scores.
7. **Backtesting** — Validate signal quality against resolved markets. Computes accuracy, Brier score, ROI, Sharpe ratio, max drawdown, and calibration RMSE. Paper trading mode simulates execution with slippage.

## Screenshot

Expand Down Expand Up @@ -137,12 +139,15 @@ This fork introduces a clean abstraction layer between the application and the g
┌─────────────────────────────────────────┐
│ Flask API │
│ graph.py simulation.py report.py │
│ prediction.py backtest.py │
└──────────────┬──────────────────────────┘
│ app.extensions['neo4j_storage']
┌──────────────▼──────────────────────────┐
│ Service Layer │
│ EntityReader GraphToolsService │
│ GraphMemoryUpdater ReportAgent │
│ PredictionManager Backtester │
│ Calibrator PaperTrader │
└──────────────┬──────────────────────────┘
│ storage: GraphStorage
┌──────────────▼──────────────────────────┐
Expand Down Expand Up @@ -171,6 +176,8 @@ This fork introduces a clean abstraction layer between the application and the g
- Hybrid search: 0.7 × vector similarity + 0.3 × BM25 keyword search
- Synchronous NER/RE extraction via local LLM (replaces Zep's async episodes)
- All original dataclasses and LLM tools (InsightForge, Panorama, Agent Interviews) preserved
- Prediction pipeline: market → scenario → LLM debate → calibrated signal (60-90s per market)
- SQLite (WAL mode) for backtest results, paper trading positions, calibration models

## Hardware Requirements

Expand Down Expand Up @@ -203,3 +210,5 @@ This is a modified fork of [MiroFish](https://github.com/666ghj/MiroFish) by [66
- Entire frontend translated from Chinese to English (20 files, 1,000+ strings)
- All Zep references replaced with Neo4j across the UI
- Rebranded to MiroFish Offline
- Prediction market signal engine (Polymarket integration, LLM debate simulation)
- Backtesting + paper trading system with SQLite storage and 62-test suite
6 changes: 3 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# MiroFish-Offline Roadmap

## Current State (v0.2.0)
## Current State (v0.2.0+)

Fully local fork running on Neo4j CE + Ollama. All Zep Cloud dependencies removed. Core pipeline works: upload text → build knowledge graph → entity extraction → simulation → report generation.
Fully local fork running on Neo4j CE + Ollama. All Zep Cloud dependencies removed. Core pipeline works: upload text → build knowledge graph → entity extraction → simulation → report generation. Prediction market signal engine with backtesting, paper trading, and SQLite storage. 62-test suite.

---

Expand Down Expand Up @@ -52,7 +52,7 @@ Fully local fork running on Neo4j CE + Ollama. All Zep Cloud dependencies remove
- [ ] Authentication & multi-user support
- [ ] Graph versioning: snapshot and restore graph states
- [ ] Plugin system for custom NER extractors, search strategies, and report templates
- [ ] Comprehensive test suite (unit + integration + E2E)
- [x] Comprehensive test suite — 62 tests (unit + integration) for prediction/backtest system
- [ ] Performance benchmarks: document throughput (texts/min) and latency per hardware tier
- [ ] Helm chart for Kubernetes deployment

Expand Down
12 changes: 12 additions & 0 deletions TODOS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# MiroFish TODOs

## Backlog

(empty)

## Completed

- [x] ~~**P2** JSON to SQLite migration for historical prediction runs~~ — prediction_runs table + SQLitePredictionStore + migration script
- [x] ~~**P2** CI/CD pipeline via GitHub Actions~~ — .github/workflows/tests.yml (backend pytest + frontend build)
- [x] ~~**P2** SQLite disk-full error handling~~ — StorageError + _safe_write wrapper on all write methods
- [x] ~~**P2** Extract shared CSS components before Phase 2~~ — Phase 2 shipped without this; CSS components added inline per view
37 changes: 23 additions & 14 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""
MiroFish Backend - Flask Application Factory
MiroFish Backend Flask application factory
"""

import os
import warnings

# Suppress multiprocessing resource_tracker warnings (from third-party libraries like transformers)
# Must be set before all other imports
# Suppress multiprocessing resource_tracker warnings from third-party libs
warnings.filterwarnings("ignore", message=".*resource_tracker.*")

from flask import Flask, request
Expand All @@ -17,19 +16,17 @@


def create_app(config_class=Config):
"""Flask application factory function"""
"""Flask application factory"""
app = Flask(__name__)
app.config.from_object(config_class)

# Configure JSON encoding: ensure Chinese displays directly (not as \uXXXX)
# Flask >= 2.3 uses app.json.ensure_ascii, older versions use JSON_AS_ASCII config
# JSON encoding: display CJK characters directly (not \uXXXX)
if hasattr(app, 'json') and hasattr(app.json, 'ensure_ascii'):
app.json.ensure_ascii = False

# Setup logging
logger = setup_logger('mirofish')

# Only print startup info in reloader subprocess (avoid printing twice in debug mode)
# Only log startup info in the reloader child process (avoid duplicate logs in debug mode)
is_reloader_process = os.environ.get('WERKZEUG_RUN_MAIN') == 'true'
debug_mode = app.config.get('DEBUG', False)
should_log_startup = not debug_mode or is_reloader_process
Expand All @@ -54,19 +51,30 @@ def create_app(config_class=Config):
# Store None so endpoints can return 503 gracefully
app.extensions['neo4j_storage'] = None

# Register simulation process cleanup function (ensure all simulation processes terminate on server shutdown)
# --- Initialize SQLite storage ---
from .storage.sqlite_store import SQLiteStore
try:
sqlite_store = SQLiteStore(Config.SQLITE_DB_PATH)
app.extensions['sqlite'] = sqlite_store
if should_log_startup:
logger.info("SQLiteStore initialized (%s)", Config.SQLITE_DB_PATH)
except Exception as e:
logger.error("SQLiteStore initialization failed: %s", e)
app.extensions['sqlite'] = None

# Register simulation process cleanup
from .services.simulation_runner import SimulationRunner
SimulationRunner.register_cleanup()
if should_log_startup:
logger.info("Simulation process cleanup function registered")
logger.info("Simulation process cleanup registered")

# Request logging middleware
@app.before_request
def log_request():
logger = get_logger('mirofish.request')
logger.debug(f"Request: {request.method} {request.path}")
if request.content_type and 'json' in request.content_type:
logger.debug(f"Request body: {request.get_json(silent=True)}")
logger.debug(f"Body: {request.get_json(silent=True)}")

@app.after_request
def log_response(response):
Expand All @@ -75,18 +83,19 @@ def log_response(response):
return response

# Register blueprints
from .api import graph_bp, simulation_bp, report_bp
from .api import graph_bp, simulation_bp, report_bp, prediction_bp, backtest_bp
app.register_blueprint(graph_bp, url_prefix='/api/graph')
app.register_blueprint(simulation_bp, url_prefix='/api/simulation')
app.register_blueprint(report_bp, url_prefix='/api/report')
app.register_blueprint(prediction_bp, url_prefix='/api/prediction')
app.register_blueprint(backtest_bp, url_prefix='/api/backtest')

# Health check
@app.route('/health')
def health():
return {'status': 'ok', 'service': 'MiroFish-Offline Backend'}

if should_log_startup:
logger.info("MiroFish-Offline Backend startup complete")
logger.info("MiroFish-Offline Backend started")

return app

Loading