Cyclicus is a point-in-time market-sentiment research workbench. It asks whether financial sentiment known at a declared historical decision time adds out-of-sample information beyond market and technical features.
The project reports historical research evidence. It does not provide investment advice, live recommendations, or claims of investment performance.
This repository is a greenfield revival of a small 2024 prototype. The legacy source
files remain in src/ as historical evidence while the tested application lives
under src/cyclicus/.
The executable revival includes:
/cyclicus/ production builds; andThe checked-in flagship run is deliberately synthetic. Its purpose is to make the research and presentation paths reproducible without credentials or licensed data. Live and scheduled provider ingestion is intentionally outside the scope of the public demo.
The optional BERT path is intentionally not used to fabricate the checked-in
fixture. Install it with uv sync --extra nlp, then construct
FinBertSentimentScorer.from_pretrained(). Production research should pass a
pinned model revision and record that revision in its evidence manifest.
Prerequisites: Python 3.12 or 3.13 and uv.
uv sync --dev
uv run pytest
uv run cyclicus build-demo \
--output artifacts/demo/flagship.json \
--web-copy web/public/data/flagship.json
uv run cyclicus verify-demo
uv run cyclicus serve
The FastAPI evidence document is available at
http://127.0.0.1:8000/api/v1/showcase and interactive documentation at
http://127.0.0.1:8000/docs.
Run the dashboard in a second terminal:
cd web
corepack enable
pnpm install --frozen-lockfile
pnpm dev
The local dashboard is available at http://127.0.0.1:5173. It uses the checked-in
fixture by default. To exercise the FastAPI boundary instead, start the API and
run VITE_DATA_MODE=backend pnpm dev.
Both modes consume the same versioned evidence schema:
web/dist/ directory at a root domain
or a subpath such as /cyclicus/. It loads a precomputed evidence bundle and
needs no backend or credentials.cyclicus.example.com.Build either static shape with:
pnpm --dir web build
pnpm --dir web build:subpath
See docs/deployment.md for the hosting contract, environment variables, container workflow, and same-origin reverse-proxy layout.
make check
make reproducibility
make check runs the Python tests, lint, strict type checks, frontend tests, and
both production builds. make reproducibility builds the fixture evidence twice
and proves byte-for-byte determinism within the current pinned environment, then
validates the checked-in public bundle. Seeded scikit-learn outputs may differ
across operating systems, so the project does not claim cross-platform bit
identity.
The shared vocabulary and non-negotiable invariants live in CONTEXT.md. The preregistered fixture experiment is documented in docs/research-protocol.md, and public claims are tracked in docs/claims-ledger.md.
The load-bearing rule is:
max_source_available_at <= decision_time < label_end_at
No model or presentation adapter is allowed to bypass the dataset module that enforces it.
Cyclicus is a point-in-time market-sentiment research workbench. It asks whether financial sentiment known at a declared historical decision time adds out-of-sample information beyond market and technical features.
The project reports historical research evidence. It does not provide investment advice, live recommendations, or claims of investment performance.
This repository is a greenfield revival of a small 2024 prototype. The legacy source
files remain in src/ as historical evidence while the tested application lives
under src/cyclicus/.
The executable revival includes:
/cyclicus/ production builds; andThe checked-in flagship run is deliberately synthetic. Its purpose is to make the research and presentation paths reproducible without credentials or licensed data. Live and scheduled provider ingestion is intentionally outside the scope of the public demo.
The optional BERT path is intentionally not used to fabricate the checked-in
fixture. Install it with uv sync --extra nlp, then construct
FinBertSentimentScorer.from_pretrained(). Production research should pass a
pinned model revision and record that revision in its evidence manifest.
Prerequisites: Python 3.12 or 3.13 and uv.
uv sync --dev
uv run pytest
uv run cyclicus build-demo \
--output artifacts/demo/flagship.json \
--web-copy web/public/data/flagship.json
uv run cyclicus verify-demo
uv run cyclicus serve
The FastAPI evidence document is available at
http://127.0.0.1:8000/api/v1/showcase and interactive documentation at
http://127.0.0.1:8000/docs.
Run the dashboard in a second terminal:
cd web
corepack enable
pnpm install --frozen-lockfile
pnpm dev
The local dashboard is available at http://127.0.0.1:5173. It uses the checked-in
fixture by default. To exercise the FastAPI boundary instead, start the API and
run VITE_DATA_MODE=backend pnpm dev.
Both modes consume the same versioned evidence schema:
web/dist/ directory at a root domain
or a subpath such as /cyclicus/. It loads a precomputed evidence bundle and
needs no backend or credentials.cyclicus.example.com.Build either static shape with:
pnpm --dir web build
pnpm --dir web build:subpath
See docs/deployment.md for the hosting contract, environment variables, container workflow, and same-origin reverse-proxy layout.
make check
make reproducibility
make check runs the Python tests, lint, strict type checks, frontend tests, and
both production builds. make reproducibility builds the fixture evidence twice
and proves byte-for-byte determinism within the current pinned environment, then
validates the checked-in public bundle. Seeded scikit-learn outputs may differ
across operating systems, so the project does not claim cross-platform bit
identity.
The shared vocabulary and non-negotiable invariants live in CONTEXT.md. The preregistered fixture experiment is documented in docs/research-protocol.md, and public claims are tracked in docs/claims-ledger.md.
The load-bearing rule is:
max_source_available_at <= decision_time < label_end_at
No model or presentation adapter is allowed to bypass the dataset module that enforces it.