refactor: rename project from Predix to NexQuant

Rename all source files, scripts, tests, documentation, and configuration
from Predix/predix to NexQuant/nexquant across the entire codebase.
This commit is contained in:
TPTBusiness
2026-05-09 17:48:22 +02:00
parent 85b56b8179
commit cbe1c52e00
92 changed files with 3690 additions and 1015 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# Pre-commit hooks configuration for Predix # Pre-commit hooks configuration for NexQuant
# See https://pre-commit.com for more information # See https://pre-commit.com for more information
repos: repos:
+453 -453
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -52,7 +52,7 @@ an individual is officially representing the community in public spaces.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
nico@predix.io. nico@nexquant.io.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
## Attribution ## Attribution
+8 -8
View File
@@ -1,6 +1,6 @@
# Contributing to Predix # Contributing to NexQuant
We welcome contributions and suggestions to improve Predix. Whether it's solving an issue, addressing a bug, enhancing documentation, or even correcting a typo, every contribution is valuable and helps improve the project. We welcome contributions and suggestions to improve NexQuant. Whether it's solving an issue, addressing a bug, enhancing documentation, or even correcting a typo, every contribution is valuable and helps improve the project.
## Getting Started ## Getting Started
@@ -15,11 +15,11 @@ grep -r "TODO:"
```bash ```bash
# Fork the repository on GitHub, then clone your fork # Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR-USERNAME/Predix.git git clone https://github.com/YOUR-USERNAME/NexQuant.git
cd Predix cd NexQuant
# Add upstream remote # Add upstream remote
git remote add upstream https://github.com/TPTBusiness/Predix.git git remote add upstream https://github.com/TPTBusiness/NexQuant.git
``` ```
### 2. Create a Branch ### 2. Create a Branch
@@ -141,7 +141,7 @@ All PRs are reviewed by maintainers. Expect:
## Project Structure ## Project Structure
``` ```
Predix/ NexQuant/
├── rdagent/ # Core framework (open source) ├── rdagent/ # Core framework (open source)
│ ├── app/ # CLI and scenario apps │ ├── app/ # CLI and scenario apps
│ ├── components/ # Reusable agent components │ ├── components/ # Reusable agent components
@@ -157,8 +157,8 @@ Predix/
## Need Help? ## Need Help?
- **Issues**: [GitHub Issues](https://github.com/TPTBusiness/Predix/issues) - **Issues**: [GitHub Issues](https://github.com/TPTBusiness/NexQuant/issues)
- **Discussions**: [GitHub Discussions](https://github.com/TPTBusiness/Predix/discussions) - **Discussions**: [GitHub Discussions](https://github.com/TPTBusiness/NexQuant/discussions)
- **Documentation**: See `docs/` folder - **Documentation**: See `docs/` folder
## License ## License
+60 -60
View File
@@ -1,4 +1,4 @@
# Predix # NexQuant
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/Python-3.10%20|%203.11-blue?style=for-the-badge&logo=python" alt="Python"> <img src="https://img.shields.io/badge/Python-3.10%20|%203.11-blue?style=for-the-badge&logo=python" alt="Python">
@@ -27,17 +27,17 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/TPTBusiness/Predix/actions/workflows/ci.yml"> <a href="https://github.com/TPTBusiness/NexQuant/actions/workflows/ci.yml">
<img src="https://img.shields.io/github/actions/workflow/status/TPTBusiness/Predix/ci.yml?branch=master&label=CI&logo=github&style=flat-square" alt="CI Status"> <img src="https://img.shields.io/github/actions/workflow/status/TPTBusiness/NexQuant/ci.yml?branch=master&label=CI&logo=github&style=flat-square" alt="CI Status">
</a> </a>
<a href="https://github.com/TPTBusiness/Predix/actions/workflows/codacy.yml"> <a href="https://github.com/TPTBusiness/NexQuant/actions/workflows/codacy.yml">
<img src="https://img.shields.io/github/actions/workflow/status/TPTBusiness/Predix/codacy.yml?branch=master&label=Security&logo=shield&style=flat-square" alt="Security Scan"> <img src="https://img.shields.io/github/actions/workflow/status/TPTBusiness/NexQuant/codacy.yml?branch=master&label=Security&logo=shield&style=flat-square" alt="Security Scan">
</a> </a>
<a href="https://codecov.io/gh/TPTBusiness/Predix"> <a href="https://codecov.io/gh/TPTBusiness/NexQuant">
<img src="https://img.shields.io/codecov/c/github/TPTBusiness/Predix?style=flat-square&logo=codecov" alt="Coverage"> <img src="https://img.shields.io/codecov/c/github/TPTBusiness/NexQuant?style=flat-square&logo=codecov" alt="Coverage">
</a> </a>
<a href="https://github.com/TPTBusiness/Predix/blob/master/LICENSE"> <a href="https://github.com/TPTBusiness/NexQuant/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/TPTBusiness/Predix?style=flat-square" alt="License"> <img src="https://img.shields.io/github/license/TPTBusiness/NexQuant?style=flat-square" alt="License">
</a> </a>
<a href="https://www.conventionalcommits.org/"> <a href="https://www.conventionalcommits.org/">
<img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow?style=flat-square" alt="Conventional Commits"> <img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow?style=flat-square" alt="Conventional Commits">
@@ -45,17 +45,17 @@
<a href="https://github.com/astral-sh/ruff"> <a href="https://github.com/astral-sh/ruff">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json&style=flat-square" alt="Ruff"> <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json&style=flat-square" alt="Ruff">
</a> </a>
<a href="https://github.com/TPTBusiness/Predix/stargazers"> <a href="https://github.com/TPTBusiness/NexQuant/stargazers">
<img src="https://img.shields.io/github/stars/TPTBusiness/Predix?style=flat-square" alt="Stars"> <img src="https://img.shields.io/github/stars/TPTBusiness/NexQuant?style=flat-square" alt="Stars">
</a> </a>
<a href="https://github.com/TPTBusiness/Predix/forks"> <a href="https://github.com/TPTBusiness/NexQuant/forks">
<img src="https://img.shields.io/github/forks/TPTBusiness/Predix?style=flat-square" alt="Forks"> <img src="https://img.shields.io/github/forks/TPTBusiness/NexQuant?style=flat-square" alt="Forks">
</a> </a>
<a href="https://github.com/TPTBusiness/Predix/issues"> <a href="https://github.com/TPTBusiness/NexQuant/issues">
<img src="https://img.shields.io/github/issues/TPTBusiness/Predix?style=flat-square" alt="Issues"> <img src="https://img.shields.io/github/issues/TPTBusiness/NexQuant?style=flat-square" alt="Issues">
</a> </a>
<a href="https://github.com/TPTBusiness/Predix/commits/master"> <a href="https://github.com/TPTBusiness/NexQuant/commits/master">
<img src="https://img.shields.io/github/last-commit/TPTBusiness/Predix?style=flat-square" alt="Last Commit"> <img src="https://img.shields.io/github/last-commit/TPTBusiness/NexQuant?style=flat-square" alt="Last Commit">
</a> </a>
</p> </p>
@@ -64,18 +64,18 @@
## 🖥️ CLI Dashboard ## 🖥️ CLI Dashboard
```bash ```bash
rdagent predix rdagent nexquant
``` ```
![Predix CLI Welcome Screen](docs/cli-welcome-screen.png) ![NexQuant CLI Welcome Screen](docs/cli-welcome-screen.png)
*The Predix CLI shows system status, available commands, and quick start guide.* *The NexQuant CLI shows system status, available commands, and quick start guide.*
--- ---
## Overview ## Overview
**Predix** is an autonomous AI agent for quantitative trading strategies in the EUR/USD forex market. Built on a multi-agent framework, Predix automates the full research and development cycle: **NexQuant** is an autonomous AI agent for quantitative trading strategies in the EUR/USD forex market. Built on a multi-agent framework, NexQuant automates the full research and development cycle:
- 📊 **Factor Generation** — LLM proposes novel alpha factors; Kronos foundation model generates OHLCV-based predictions - 📊 **Factor Generation** — LLM proposes novel alpha factors; Kronos foundation model generates OHLCV-based predictions
- 💡 **Strategy Discovery** — Autopilot generates + backtests trading strategies 24/7 - 💡 **Strategy Discovery** — Autopilot generates + backtests trading strategies 24/7
@@ -83,7 +83,7 @@ rdagent predix
- 📈 **Backtesting** — Unified engine with 10 runtime invariants on 1-min EUR/USD data (20202026) - 📈 **Backtesting** — Unified engine with 10 runtime invariants on 1-min EUR/USD data (20202026)
- 🔄 **Auto-Restart** — All services run as daemons with automatic crash recovery - 🔄 **Auto-Restart** — All services run as daemons with automatic crash recovery
Predix is optimized for **1-minute EUR/USD FX data** (20202026) and supports both local LLMs (llama.cpp) and cloud backends (OpenRouter). NexQuant is optimized for **1-minute EUR/USD FX data** (20202026) and supports both local LLMs (llama.cpp) and cloud backends (OpenRouter).
> **Backtest Verification**: Every backtest result is automatically verified at runtime against mathematical invariants (MaxDD ∈ [-1,0], WinRate ∈ [0,1], Sharpe finite, sign consistency, etc.). 1125 collected tests with deep property-based, fuzzing, and hypothesis tests ensure metric correctness. See [Backtest Integrity](#backtest-integrity). > **Backtest Verification**: Every backtest result is automatically verified at runtime against mathematical invariants (MaxDD ∈ [-1,0], WinRate ∈ [0,1], Sharpe finite, sign consistency, etc.). 1125 collected tests with deep property-based, fuzzing, and hypothesis tests ensure metric correctness. See [Backtest Integrity](#backtest-integrity).
@@ -99,7 +99,7 @@ Special thanks to:
- **[ai-hedge-fund](https://github.com/virattt/ai-hedge-fund)** - Inspiration for macro analysis (Stanley Druckenmiller agent), risk management concepts, and market regime detection. - **[ai-hedge-fund](https://github.com/virattt/ai-hedge-fund)** - Inspiration for macro analysis (Stanley Druckenmiller agent), risk management concepts, and market regime detection.
All code in Predix is originally written and implemented independently. Predix extends these frameworks with EUR/USD forex-specific features, 1-minute backtesting capabilities, comprehensive risk management, and trading dashboards. All code in NexQuant is originally written and implemented independently. NexQuant extends these frameworks with EUR/USD forex-specific features, 1-minute backtesting capabilities, comprehensive risk management, and trading dashboards.
--- ---
@@ -129,12 +129,12 @@ All code in Predix is originally written and implemented independently. Predix e
```bash ```bash
# Clone repository # Clone repository
git clone https://github.com/TPTBusiness/Predix git clone https://github.com/TPTBusiness/NexQuant
cd Predix cd NexQuant
# Create and activate conda environment # Create and activate conda environment
conda create -n predix python=3.10 -y conda create -n nexquant python=3.10 -y
conda activate predix conda activate nexquant
# Install in editable mode # Install in editable mode
pip install -e . pip install -e .
@@ -143,14 +143,14 @@ pip install -e .
docker run --rm hello-world docker run --rm hello-world
``` ```
> **Important:** Predix requires a conda environment to manage dependencies properly. > **Important:** NexQuant requires a conda environment to manage dependencies properly.
> Using plain Python or other environment managers may cause conflicts. > Using plain Python or other environment managers may cause conflicts.
--- ---
## Data Setup ## Data Setup
Predix requires **1-minute EUR/USD OHLCV data** in HDF5 format. This is a hard prerequisite — the system cannot run without it. NexQuant requires **1-minute EUR/USD OHLCV data** in HDF5 format. This is a hard prerequisite — the system cannot run without it.
### Step 1: Get the data ### Step 1: Get the data
@@ -266,7 +266,7 @@ market_context:
## No GPU? Use OpenRouter ## No GPU? Use OpenRouter
If you don't have a CUDA-capable GPU, you can run Predix using [OpenRouter](https://openrouter.ai) for LLM inference — no local model download required. If you don't have a CUDA-capable GPU, you can run NexQuant using [OpenRouter](https://openrouter.ai) for LLM inference — no local model download required.
**1. Set up `.env` for OpenRouter:** **1. Set up `.env` for OpenRouter:**
@@ -293,7 +293,7 @@ rdagent fin_quant --model openrouter
**4. Parallel runs** (uses API concurrency instead of GPU slots): **4. Parallel runs** (uses API concurrency instead of GPU slots):
```bash ```bash
python predix_parallel.py --runs 5 --api-keys 1 -m openrouter python nexquant_parallel.py --runs 5 --api-keys 1 -m openrouter
``` ```
> Ollama is still required for embeddings even in the OpenRouter path. Install from [ollama.com](https://ollama.com) and run `ollama pull nomic-embed-text` once. > Ollama is still required for embeddings even in the OpenRouter path. Install from [ollama.com](https://ollama.com) and run `ollama pull nomic-embed-text` once.
@@ -318,7 +318,7 @@ curl http://localhost:8081/health
### 1. Run Trading Loop ### 1. Run Trading Loop
```bash ```bash
conda activate predix conda activate nexquant
rdagent fin_quant rdagent fin_quant
# or with explicit options: # or with explicit options:
rdagent fin_quant --loop-n 5 --step-n 2 rdagent fin_quant --loop-n 5 --step-n 2
@@ -332,7 +332,7 @@ rdagent server_ui --port 19899 --log-dir git_ignore_folder/RD-Agent_workspace/
# then open http://127.0.0.1:19899 # then open http://127.0.0.1:19899
# Best strategies so far # Best strategies so far
python predix.py best python nexquant.py best
``` ```
### 3. Run Continuously (Auto-Restart) ### 3. Run Continuously (Auto-Restart)
@@ -344,7 +344,7 @@ python predix.py best
nohup bash -c 'while true; do rdagent fin_quant --loop-n 10 --model local >> /tmp/fin_quant_daemon.log 2>&1; sleep 10; done' & nohup bash -c 'while true; do rdagent fin_quant --loop-n 10 --model local >> /tmp/fin_quant_daemon.log 2>&1; sleep 10; done' &
# Autopilot — 24/7 strategy generator (Kronos factors auto-selected) # Autopilot — 24/7 strategy generator (Kronos factors auto-selected)
nohup python scripts/predix_autopilot.py >> /tmp/autopilot_daemon.log 2>&1 & nohup python scripts/nexquant_autopilot.py >> /tmp/autopilot_daemon.log 2>&1 &
# Live Trader — FTMO FIX API (requires credentials) # Live Trader — FTMO FIX API (requires credentials)
nohup python git_ignore_folder/live_trading/ftmo_live_trader.py >> ftmo_live_trader.log 2>&1 & nohup python git_ignore_folder/live_trading/ftmo_live_trader.py >> ftmo_live_trader.log 2>&1 &
@@ -369,13 +369,13 @@ nohup python git_ignore_folder/live_trading/ftmo_live_trader.py >> ftmo_live_tra
| Command | Description | | Command | Description |
|---------|-------------| |---------|-------------|
| `python predix.py best` | Show top strategies by composite score | | `python nexquant.py best` | Show top strategies by composite score |
| `python predix.py best -n 20 -m sharpe` | Top 20 by Sharpe ratio | | `python nexquant.py best -n 20 -m sharpe` | Top 20 by Sharpe ratio |
| `python predix.py best --show NAME` | Full metadata for one strategy | | `python nexquant.py best --show NAME` | Full metadata for one strategy |
| `python predix_gen_strategies_real_bt.py 10` | Generate 10 strategies with LLM + real OHLCV backtest | | `python nexquant_gen_strategies_real_bt.py 10` | Generate 10 strategies with LLM + real OHLCV backtest |
| `python predix_gen_strategies_real_bt.py 20` | Generate 20 strategies (parallel workers) | | `python nexquant_gen_strategies_real_bt.py 20` | Generate 20 strategies (parallel workers) |
| `python scripts/predix_autopilot.py` | 24/7 Auto-Pilot: endless strategy generation | | `python scripts/nexquant_autopilot.py` | 24/7 Auto-Pilot: endless strategy generation |
| `python predix_continuous_strategies.py` | Continuous generation with ML training | `python nexquant_continuous_strategies.py` | Continuous generation with ML training
### Kronos Foundation Model ### Kronos Foundation Model
@@ -390,16 +390,16 @@ Kronos runs automatically — no separate command needed. Factors are regenerate
| Command | Description | | Command | Description |
|---------|-------------| |---------|-------------|
| `python predix.py evaluate --all` | Evaluate all generated factors | | `python nexquant.py evaluate --all` | Evaluate all generated factors |
| `python predix.py top -n 20` | Show top 20 factors by IC | | `python nexquant.py top -n 20` | Show top 20 factors by IC |
| `python predix.py portfolio-simple` | Simple portfolio optimization | | `python nexquant.py portfolio-simple` | Simple portfolio optimization |
### Parallel Execution ### Parallel Execution
| Command | Description | | Command | Description |
|---------|-------------| |---------|-------------|
| `python predix_parallel.py --runs 5 --api-keys 1 -m openrouter` | Run 5 parallel factor evolutions | | `python nexquant_parallel.py --runs 5 --api-keys 1 -m openrouter` | Run 5 parallel factor evolutions |
| `python predix_parallel.py --runs 20 --api-keys 2 -m openrouter` | Run 20 runs with 2 API keys | | `python nexquant_parallel.py --runs 20 --api-keys 2 -m openrouter` | Run 20 runs with 2 API keys |
### Monitoring & Debug ### Monitoring & Debug
@@ -407,8 +407,8 @@ Kronos runs automatically — no separate command needed. Factors are regenerate
|---------|-------------| |---------|-------------|
| `rdagent server_ui --port 19899 --log-dir <path>` | Start web dashboard | | `rdagent server_ui --port 19899 --log-dir <path>` | Start web dashboard |
| `rdagent health_check` | Validate environment setup | | `rdagent health_check` | Validate environment setup |
| `python predix_batch_backtest.py` | Batch backtest multiple factors | | `python nexquant_batch_backtest.py` | Batch backtest multiple factors |
| `python predix_rebacktest_strategies.py` | Re-backtest existing strategies | | `python nexquant_rebacktest_strategies.py` | Re-backtest existing strategies |
--- ---
@@ -416,7 +416,7 @@ Kronos runs automatically — no separate command needed. Factors are regenerate
### 🔄 Iterative Factor Evolution ### 🔄 Iterative Factor Evolution
Predix continuously proposes, implements, and validates new alpha factors: NexQuant continuously proposes, implements, and validates new alpha factors:
- Learns from backtest feedback - Learns from backtest feedback
- Avoids overfitting through walk-forward validation - Avoids overfitting through walk-forward validation
@@ -458,7 +458,7 @@ Real-time dashboard for monitoring:
### 🤖 Kronos Foundation Model Integration ### 🤖 Kronos Foundation Model Integration
Predix integrates Kronos — an OHLCV foundation model from the NeoQuasar team (AAAI 2026, **MIT License**) — for alpha factor generation: NexQuant integrates Kronos — an OHLCV foundation model from the NeoQuasar team (AAAI 2026, **MIT License**) — for alpha factor generation:
| Model | Params | p24 IC | Best For | | Model | Params | p24 IC | Best For |
|-------|--------|--------|----------| |-------|--------|--------|----------|
@@ -496,7 +496,7 @@ Automated quality assurance:
## Project Structure ## Project Structure
``` ```
predix/ nexquant/
├── rdagent/ # Core agent framework ├── rdagent/ # Core agent framework
│ ├── app/ # CLI and scenario apps │ ├── app/ # CLI and scenario apps
│ │ └── qlib_rd_loop/ # Quant R&D loop (factor + model generation) │ │ └── qlib_rd_loop/ # Quant R&D loop (factor + model generation)
@@ -518,12 +518,12 @@ predix/
│ ├── scenarios/ # Domain-specific scenarios (qlib, kaggle, rl) │ ├── scenarios/ # Domain-specific scenarios (qlib, kaggle, rl)
│ └── utils/ # Utilities │ └── utils/ # Utilities
├── scripts/ # Daily operation scripts ├── scripts/ # Daily operation scripts
│ ├── predix_autopilot.py # 24/7 auto strategy generator │ ├── nexquant_autopilot.py # 24/7 auto strategy generator
│ ├── predix_gen_strategies_real_bt.py # Parallel strategy generation │ ├── nexquant_gen_strategies_real_bt.py # Parallel strategy generation
│ ├── predix_parallel.py # Multi-instance parallel R&D │ ├── nexquant_parallel.py # Multi-instance parallel R&D
│ ├── predix_continuous_strategies.py # Continuous strategy generation │ ├── nexquant_continuous_strategies.py # Continuous strategy generation
│ ├── predix_fast_rebacktest.py # Fast strategy re-evaluation │ ├── nexquant_fast_rebacktest.py # Fast strategy re-evaluation
│ └── predix_rebacktest_parent.py # Parallel rebacktest orchestrator │ └── nexquant_rebacktest_parent.py # Parallel rebacktest orchestrator
├── test/ # Test suite (1,125+ collected) ├── test/ # Test suite (1,125+ collected)
│ ├── backtesting/ # Backtest engine deep tests │ ├── backtesting/ # Backtest engine deep tests
│ ├── qlib/ # Quant loop, factor, model tests │ ├── qlib/ # Quant loop, factor, model tests
@@ -581,7 +581,7 @@ For major changes, please open an issue first to discuss your approach.
## Citation ## Citation
If you use Predix in your research, please cite the underlying framework: If you use NexQuant in your research, please cite the underlying framework:
```bibtex ```bibtex
@misc{yang2025rdagentllmagentframeworkautonomous, @misc{yang2025rdagentllmagentframeworkautonomous,
@@ -598,7 +598,7 @@ If you use Predix in your research, please cite the underlying framework:
## Support ## Support
- **Issues**: [GitHub Issues](https://github.com/TPTBusiness/Predix/issues) - **Issues**: [GitHub Issues](https://github.com/TPTBusiness/NexQuant/issues)
--- ---
@@ -630,7 +630,7 @@ pytest test/backtesting/ -q # backtest engine deep tests
## Disclaimer ## Disclaimer
Predix is provided "as is" for **research and educational purposes only**. It is **not** intended for: NexQuant is provided "as is" for **research and educational purposes only**. It is **not** intended for:
- Live trading or financial advice - Live trading or financial advice
- Production use without thorough testing - Production use without thorough testing
+2 -2
View File
@@ -2,13 +2,13 @@
## Reporting a Vulnerability ## Reporting a Vulnerability
We take the security of Predix seriously. If you believe you have found a security vulnerability, please report it responsibly. We take the security of NexQuant seriously. If you believe you have found a security vulnerability, please report it responsibly.
**Please do not report security vulnerabilities through public GitHub issues.** **Please do not report security vulnerabilities through public GitHub issues.**
### How to Report ### How to Report
1. **Open a private security advisory** on GitHub: https://github.com/TPTBusiness/Predix/security/advisories 1. **Open a private security advisory** on GitHub: https://github.com/TPTBusiness/NexQuant/security/advisories
2. Provide a detailed description of the vulnerability 2. Provide a detailed description of the vulnerability
3. Include steps to reproduce if possible 3. Include steps to reproduce if possible
4. We will respond within 48 hours 4. We will respond within 48 hours
+3 -3
View File
@@ -6,12 +6,12 @@ This project uses GitHub Issues to track bugs and feature requests. Please searc
issues before filing new issues to avoid duplicates. For new issues, file your bug or issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue. feature request as a new Issue.
- **Issues**: [https://github.com/PredixAI/predix/issues](https://github.com/PredixAI/predix/issues) - **Issues**: [https://github.com/NexQuantAI/nexquant/issues](https://github.com/NexQuantAI/nexquant/issues)
For help and questions about using this project, please reach out via: For help and questions about using this project, please reach out via:
- **Email**: nico@predix.io - **Email**: nico@nexquant.io
- **GitHub Discussions**: [https://github.com/PredixAI/predix/discussions](https://github.com/PredixAI/predix/discussions) - **GitHub Discussions**: [https://github.com/NexQuantAI/nexquant/discussions](https://github.com/NexQuantAI/nexquant/discussions)
## Community Support ## Community Support
+8 -8
View File
@@ -1,4 +1,4 @@
# Predix v1.0.0 Release Notes # NexQuant v1.0.0 Release Notes
**Release Date:** 2026-04-02 **Release Date:** 2026-04-02
@@ -8,7 +8,7 @@
## 🎉 Overview ## 🎉 Overview
Initial release of Predix - an autonomous AI-powered quantitative trading agent for EUR/USD forex markets. Initial release of NexQuant - an autonomous AI-powered quantitative trading agent for EUR/USD forex markets.
--- ---
@@ -75,8 +75,8 @@ Initial release of Predix - an autonomous AI-powered quantitative trading agent
## 🔧 Changed ## 🔧 Changed
- Rebranded from RD-Agent to Predix for EUR/USD quantitative trading - Rebranded from RD-Agent to NexQuant for EUR/USD quantitative trading
- Updated project metadata for PredixAI organization - Updated project metadata for NexQuantAI organization
- All code comments translated to English - All code comments translated to English
- Removed 'Inspired by' comments, added comprehensive Acknowledgments - Removed 'Inspired by' comments, added comprehensive Acknowledgments
- Enhanced .gitignore for better file management - Enhanced .gitignore for better file management
@@ -137,7 +137,7 @@ This release builds upon and is inspired by:
- **TradingAgents** (Apache 2.0 License) - Multi-agent debate patterns - **TradingAgents** (Apache 2.0 License) - Multi-agent debate patterns
- **ai-hedge-fund** - Macro analysis and risk management concepts - **ai-hedge-fund** - Macro analysis and risk management concepts
**All code in Predix v1.0.0 is originally written and independently implemented.** **All code in NexQuant v1.0.0 is originally written and independently implemented.**
--- ---
@@ -149,7 +149,7 @@ This release builds upon and is inspired by:
If you use this code or concepts in your project, you **must**: If you use this code or concepts in your project, you **must**:
1. Include the MIT License text 1. Include the MIT License text
2. Keep the copyright notice: "Copyright (c) 2025 Predix Team" 2. Keep the copyright notice: "Copyright (c) 2025 NexQuant Team"
3. Provide attribution to the original project 3. Provide attribution to the original project
See [ATTRIBUTION.md](../ATTRIBUTION.md) for detailed guidelines. See [ATTRIBUTION.md](../ATTRIBUTION.md) for detailed guidelines.
@@ -158,7 +158,7 @@ See [ATTRIBUTION.md](../ATTRIBUTION.md) for detailed guidelines.
## 🔗 Links ## 🔗 Links
- **GitHub Release:** https://github.com/TPTBusiness/Predix/releases/tag/v1.0.0 - **GitHub Release:** https://github.com/TPTBusiness/NexQuant/releases/tag/v1.0.0
- **Main Changelog:** ../CHANGELOG.md - **Main Changelog:** ../CHANGELOG.md
- **Attribution Guidelines:** ../ATTRIBUTION.md - **Attribution Guidelines:** ../ATTRIBUTION.md
- **Installation Guide:** ../README.md#installation - **Installation Guide:** ../README.md#installation
@@ -168,7 +168,7 @@ See [ATTRIBUTION.md](../ATTRIBUTION.md) for detailed guidelines.
<div align="center"> <div align="center">
**Made with ❤️ by Predix Team** **Made with ❤️ by NexQuant Team**
For detailed usage guidelines, see [README.md](../README.md) For detailed usage guidelines, see [README.md](../README.md)
+6 -6
View File
@@ -1,4 +1,4 @@
# Predix v2.0.0 Release Notes # NexQuant v2.0.0 Release Notes
**Release Date:** 2026-04-10 **Release Date:** 2026-04-10
@@ -8,7 +8,7 @@
## 🎉 Overview ## 🎉 Overview
Major update adding AI-powered strategy generation, realistic backtesting, and comprehensive CLI tooling. Predix now autonomously generates, evaluates, and optimizes trading strategies using local LLMs. Major update adding AI-powered strategy generation, realistic backtesting, and comprehensive CLI tooling. NexQuant now autonomously generates, evaluates, and optimizes trading strategies using local LLMs.
--- ---
@@ -28,7 +28,7 @@ Major update adding AI-powered strategy generation, realistic backtesting, and c
- **Proper Annualization**: sqrt(252*1440) for 1-min data - **Proper Annualization**: sqrt(252*1440) for 1-min data
### CLI Commands ### CLI Commands
- `rdagent predix` - Show beautiful welcome screen (perfect for screenshots!) - `rdagent nexquant` - Show beautiful welcome screen (perfect for screenshots!)
- `rdagent start_llama` - Start llama.cpp server - `rdagent start_llama` - Start llama.cpp server
- `rdagent start_loop` - Start strategy generator loop with auto-restart - `rdagent start_loop` - Start strategy generator loop with auto-restart
- `rdagent generate_strategies` - Generate strategies from factors - `rdagent generate_strategies` - Generate strategies from factors
@@ -65,8 +65,8 @@ Major update adding AI-powered strategy generation, realistic backtesting, and c
## 📦 Installation ## 📦 Installation
```bash ```bash
git clone https://github.com/TPTBusiness/Predix git clone https://github.com/TPTBusiness/NexQuant
cd Predix cd NexQuant
pip install -e . pip install -e .
``` ```
@@ -74,7 +74,7 @@ pip install -e .
```bash ```bash
# Show welcome screen # Show welcome screen
rdagent predix rdagent nexquant
# Start LLM server # Start LLM server
rdagent start_llama rdagent start_llama
+1 -1
View File
@@ -1,7 +1,7 @@
# Bandit Security Scanner Configuration # Bandit Security Scanner Configuration
# Documentation: https://bandit.readthedocs.io/ # Documentation: https://bandit.readthedocs.io/
title: Bandit Security Scan for Predix title: Bandit Security Scan for NexQuant
# Tests to skip (known false positives or acceptable risks) # Tests to skip (known false positives or acceptable risks)
skips: skips:
+1 -1
View File
@@ -1,5 +1,5 @@
# ============================================================ # ============================================================
# Predix Data Configuration # NexQuant Data Configuration
# Change instrument, frequency, and time periods here # Change instrument, frequency, and time periods here
# All other components read from this file # All other components read from this file
# ============================================================ # ============================================================
+7 -7
View File
@@ -1,6 +1,6 @@
# Attribution Guidelines # Attribution Guidelines
## Using Predix in Your Project ## Using NexQuant in Your Project
If you use code, concepts, or ideas from this project, you **must**: If you use code, concepts, or ideas from this project, you **must**:
@@ -11,8 +11,8 @@ Include the full MIT License text in your project's LICENSE file or documentatio
### 2. Include Copyright Notice ### 2. Include Copyright Notice
``` ```
Copyright (c) 2025 Predix Team Copyright (c) 2025 NexQuant Team
Original Project: https://github.com/TPTBusiness/Predix Original Project: https://github.com/TPTBusiness/NexQuant
``` ```
### 3. Provide Attribution ### 3. Provide Attribution
@@ -22,7 +22,7 @@ Add a notice in your documentation or README:
```markdown ```markdown
## Acknowledgments ## Acknowledgments
This project uses code/concepts from [Predix](https://github.com/TPTBusiness/Predix), This project uses code/concepts from [NexQuant](https://github.com/TPTBusiness/NexQuant),
licensed under the [MIT License](https://opensource.org/licenses/MIT). licensed under the [MIT License](https://opensource.org/licenses/MIT).
``` ```
@@ -33,7 +33,7 @@ If you modified the code:
```markdown ```markdown
## Modifications ## Modifications
Based on Predix (original by Predix Team). Based on NexQuant (original by NexQuant Team).
Modified by [Your Name/Organization] on [Date]. Modified by [Your Name/Organization] on [Date].
Changes: [Brief description of changes] Changes: [Brief description of changes]
``` ```
@@ -63,13 +63,13 @@ Changes: [Brief description of changes]
```markdown ```markdown
# My Trading Project # My Trading Project
This project uses factor generation concepts from [Predix](https://github.com/TPTBusiness/Predix). This project uses factor generation concepts from [NexQuant](https://github.com/TPTBusiness/NexQuant).
## License ## License
MIT License - see LICENSE file for details. MIT License - see LICENSE file for details.
## Credits ## Credits
- Original Predix code by Predix Team (MIT License) - Original NexQuant code by NexQuant Team (MIT License)
- Modified by John Doe, 2025 - Modified by John Doe, 2025
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
# Changelog # Changelog
All notable changes to Predix will be documented in this file. All notable changes to NexQuant will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+4 -4
View File
@@ -10,9 +10,9 @@ import subprocess
latest_tag = subprocess.check_output(["git", "describe", "--tags", "--abbrev=0"], text=True).strip() latest_tag = subprocess.check_output(["git", "describe", "--tags", "--abbrev=0"], text=True).strip()
project = "Predix" project = "NexQuant"
copyright = "2025, Predix Team" copyright = "2025, NexQuant Team"
author = "Predix Team" author = "NexQuant Team"
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -66,7 +66,7 @@ html_static_path = ["_static"]
html_favicon = "_static/favicon.ico" html_favicon = "_static/favicon.ico"
html_theme_options = { html_theme_options = {
"source_repository": "https://github.com/PredixAI/predix", "source_repository": "https://github.com/NexQuantAI/nexquant",
"source_branch": "main", "source_branch": "main",
"source_directory": "docs/", "source_directory": "docs/",
} }
+11 -11
View File
@@ -1,4 +1,4 @@
# Predix Parallel Run System # NexQuant Parallel Run System
## Overview ## Overview
@@ -10,8 +10,8 @@ The Parallel Run System enables concurrent execution of 5+ factor generation exp
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `predix.py` | Extended with `--run-id` parameter for isolated single runs | | `nexquant.py` | Extended with `--run-id` parameter for isolated single runs |
| `predix_parallel.py` | Parallel runner manager with Rich live dashboard | | `nexquant_parallel.py` | Parallel runner manager with Rich live dashboard |
| `factor_runner.py` | Modified to use `PARALLEL_RUN_ID` for path isolation | | `factor_runner.py` | Modified to use `PARALLEL_RUN_ID` for path isolation |
| `CoSTEER/__init__.py` | Modified to use `PARALLEL_RUN_ID` for intermediate results | | `CoSTEER/__init__.py` | Modified to use `PARALLEL_RUN_ID` for intermediate results |
@@ -57,26 +57,26 @@ RD-Agent_workspace_run2/ # Parallel run #2
```bash ```bash
# Run with isolated results # Run with isolated results
predix quant --run-id 1 -m openrouter nexquant quant --run-id 1 -m openrouter
``` ```
### CLI - Parallel Runner (Direct) ### CLI - Parallel Runner (Direct)
```bash ```bash
# Run 5 experiments with 2 API keys # Run 5 experiments with 2 API keys
python predix_parallel.py --runs 5 --api-keys 2 python nexquant_parallel.py --runs 5 --api-keys 2
# Run 3 experiments with local model # Run 3 experiments with local model
python predix_parallel.py --runs 3 --model local python nexquant_parallel.py --runs 3 --model local
# Custom configuration # Custom configuration
python predix_parallel.py -n 10 -k 2 -m openrouter python nexquant_parallel.py -n 10 -k 2 -m openrouter
``` ```
### Programmatic Usage ### Programmatic Usage
```python ```python
from predix_parallel import main from nexquant_parallel import main
result = main(runs=5, api_keys=2, model="openrouter") result = main(runs=5, api_keys=2, model="openrouter")
print(f"Success: {result['success']}/{result['total']}") print(f"Success: {result['success']}/{result['total']}")
@@ -132,7 +132,7 @@ The parallel runner shows a Rich-based live dashboard:
``` ```
┌─────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐
│ 🔀 Predix Parallel Run Dashboard │ │ 🔀 NexQuant Parallel Run Dashboard │
├──────┬──────────┬──────────┬─────────┬──────────┬───────┤ ├──────┬──────────┬──────────┬─────────┬──────────┬───────┤
│ Run │ Status │ Elapsed │ API Key │ Model │ Exit │ │ Run │ Status │ Elapsed │ API Key │ Model │ Exit │
├──────┼──────────┼──────────┼─────────┼──────────┼───────┤ ├──────┼──────────┼──────────┼─────────┼──────────┼───────┤
@@ -222,10 +222,10 @@ if parallel_run_id != "0":
pytest test/integration/test_all_features.py -v pytest test/integration/test_all_features.py -v
# Test parallel runner imports # Test parallel runner imports
python -c "from predix_parallel import ParallelRunner, main; print('✅ OK')" python -c "from nexquant_parallel import ParallelRunner, main; print('✅ OK')"
# Test CLI options # Test CLI options
predix quant --help # Should show --run-id option nexquant quant --help # Should show --run-id option
``` ```
## Future Enhancements ## Future Enhancements
+1 -1
View File
@@ -1,4 +1,4 @@
# Security Runbook für Predix # Security Runbook für NexQuant
## Bandit Security Scanner ## Bandit Security Scanner
+2 -2
View File
@@ -127,8 +127,8 @@ jupyter notebook examples/notebooks/quickstart.ipynb
- **Dokumentation:** `docs/` oder [README.md](../README.md) - **Dokumentation:** `docs/` oder [README.md](../README.md)
- **CLI Hilfe:** `rdagent COMMAND --help` - **CLI Hilfe:** `rdagent COMMAND --help`
- **Issues:** [GitHub Issues](https://github.com/nico/Predix/issues) - **Issues:** [GitHub Issues](https://github.com/nico/NexQuant/issues)
- **Community:** [Discussions](https://github.com/nico/Predix/discussions) - **Community:** [Discussions](https://github.com/nico/NexQuant/discussions)
## ⚠️ Wichtige Hinweise ## ⚠️ Wichtige Hinweise
+2 -2
View File
@@ -1,6 +1,6 @@
# Predix Models # NexQuant Models
This directory contains all ML model definitions for Predix trading factors. This directory contains all ML model definitions for NexQuant trading factors.
--- ---
+99 -99
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Predix CLI - Wrapper for rdagent with LLM model selection. NexQuant CLI - Wrapper for rdagent with LLM model selection.
Usage: Usage:
predix quant # Local llama.cpp (default) nexquant quant # Local llama.cpp (default)
predix quant --model local # Explicit local nexquant quant --model local # Explicit local
predix quant --model openrouter # OpenRouter cloud model nexquant quant --model openrouter # OpenRouter cloud model
predix quant -d # With web dashboard nexquant quant -d # With web dashboard
""" """
import os import os
import sys import sys
@@ -26,7 +26,7 @@ except ImportError:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
app = typer.Typer(help="Predix - AI Quantitative Trading Agent") app = typer.Typer(help="NexQuant - AI Quantitative Trading Agent")
console = Console() console = Console()
@@ -178,13 +178,13 @@ def quant(
0 = single run mode (default: 0) 0 = single run mode (default: 0)
Examples: Examples:
$ predix quant # Local llama.cpp, single run $ nexquant quant # Local llama.cpp, single run
$ predix quant -m openrouter # OpenRouter cloud model $ nexquant quant -m openrouter # OpenRouter cloud model
$ predix quant -d # With web dashboard on :5000 $ nexquant quant -d # With web dashboard on :5000
$ predix quant -m openrouter -d # Cloud model + web dashboard $ nexquant quant -m openrouter -d # Cloud model + web dashboard
$ predix quant --run-id 1 # Parallel run #1 (isolated) $ nexquant quant --run-id 1 # Parallel run #1 (isolated)
$ predix quant --run-id 2 --loop-n 50 # Parallel run #2, 50 loops $ nexquant quant --run-id 2 --loop-n 50 # Parallel run #2, 50 loops
$ predix quant --log-file custom.log # Custom log file path $ nexquant quant --log-file custom.log # Custom log file path
Expected Output: Expected Output:
- Generated alpha factors saved to results/factors/ as JSON files - Generated alpha factors saved to results/factors/ as JSON files
@@ -197,9 +197,9 @@ def quant(
Local models are faster but may have lower quality than cloud models. Local models are faster but may have lower quality than cloud models.
See Also: See Also:
predix evaluate - Evaluate existing factors with full 1min data nexquant evaluate - Evaluate existing factors with full 1min data
predix top - Show top-performing factors by IC or Sharpe nexquant top - Show top-performing factors by IC or Sharpe
predix health - Check system health and configuration nexquant health - Check system health and configuration
""" """
import subprocess import subprocess
import sys import sys
@@ -323,7 +323,7 @@ def quant(
if cli_dashboard: if cli_dashboard:
def start_cli_dash(): def start_cli_dash():
from rdagent.log.ui.predix_dashboard import run_dashboard from rdagent.log.ui.nexquant_dashboard import run_dashboard
run_dashboard(log_path="fin_quant.log", refresh_interval=3) run_dashboard(log_path="fin_quant.log", refresh_interval=3)
threading.Thread(target=start_cli_dash, daemon=True).start() threading.Thread(target=start_cli_dash, daemon=True).start()
@@ -408,11 +408,11 @@ def evaluate(
when recalculating with updated methodology. (default: False) when recalculating with updated methodology. (default: False)
Examples: Examples:
$ predix evaluate # Evaluate 100 NEW factors $ nexquant evaluate # Evaluate 100 NEW factors
$ predix evaluate --top 500 # Evaluate 500 NEW factors $ nexquant evaluate --top 500 # Evaluate 500 NEW factors
$ predix evaluate --all # Evaluate all remaining factors $ nexquant evaluate --all # Evaluate all remaining factors
$ predix evaluate --force --top 50 # Re-evaluate 50 factors $ nexquant evaluate --force --top 50 # Re-evaluate 50 factors
$ predix evaluate -p 8 # Use 8 parallel workers $ nexquant evaluate -p 8 # Use 8 parallel workers
Expected Output: Expected Output:
- Updated JSON files in results/factors/ with IC, Sharpe, Max DD, Win Rate - Updated JSON files in results/factors/ with IC, Sharpe, Max DD, Win Rate
@@ -424,22 +424,22 @@ def evaluate(
With --parallel 4, expect ~30-60 seconds per factor wall-clock time. With --parallel 4, expect ~30-60 seconds per factor wall-clock time.
See Also: See Also:
predix top - Show top-performing factors by IC or Sharpe nexquant top - Show top-performing factors by IC or Sharpe
predix portfolio - Select a diversified portfolio of uncorrelated factors nexquant portfolio - Select a diversified portfolio of uncorrelated factors
predix quant - Generate new factors via LLM trading loop nexquant quant - Generate new factors via LLM trading loop
""" """
from rdagent.log.daily_log import session as _daily_session from rdagent.log.daily_log import session as _daily_session
from rich.panel import Panel from rich.panel import Panel
console.print(Panel( console.print(Panel(
"[bold cyan]📊 Predix Factor Evaluator[/bold cyan]\n" "[bold cyan]📊 NexQuant Factor Evaluator[/bold cyan]\n"
"Evaluating factors with FULL 1min data (2020-2026)\n" "Evaluating factors with FULL 1min data (2020-2026)\n"
"Skips already evaluated factors automatically", "Skips already evaluated factors automatically",
border_style="cyan", border_style="cyan",
)) ))
# Import and run the evaluator # Import and run the evaluator
from predix_full_eval import main as eval_main from nexquant_full_eval import main as eval_main
_eval_ctx = {"top": "all" if all_factors else top, "workers": parallel} _eval_ctx = {"top": "all" if all_factors else top, "workers": parallel}
if force: if force:
@@ -490,10 +490,10 @@ def top(
(default: "ic") (default: "ic")
Examples: Examples:
$ predix top # Top 20 factors by absolute IC $ nexquant top # Top 20 factors by absolute IC
$ predix top -n 50 # Top 50 factors by absolute IC $ nexquant top -n 50 # Top 50 factors by absolute IC
$ predix top -m sharpe # Top 20 factors by absolute Sharpe $ nexquant top -m sharpe # Top 20 factors by absolute Sharpe
$ predix top -n 100 -m sharpe # Top 100 factors by Sharpe $ nexquant top -n 100 -m sharpe # Top 100 factors by Sharpe
Expected Output: Expected Output:
- Formatted table showing Factor name, IC, Sharpe, Annualized Return, - Formatted table showing Factor name, IC, Sharpe, Annualized Return,
@@ -505,9 +505,9 @@ def top(
May take a few seconds with thousands of factor files. May take a few seconds with thousands of factor files.
See Also: See Also:
predix evaluate - Evaluate factors to generate performance metrics nexquant evaluate - Evaluate factors to generate performance metrics
predix portfolio - Select diversified portfolio from top factors nexquant portfolio - Select diversified portfolio from top factors
predix build-strategies - Combine factors into trading strategies nexquant build-strategies - Combine factors into trading strategies
""" """
import glob as glob_module import glob as glob_module
import json import json
@@ -639,10 +639,10 @@ def portfolio(
high-IC factors. Typical range: 0.2-0.5. (default: 0.3) high-IC factors. Typical range: 0.2-0.5. (default: 0.3)
Examples: Examples:
$ predix portfolio # Select top 10 from top 50 candidates $ nexquant portfolio # Select top 10 from top 50 candidates
$ predix portfolio -n 100 -t 20 # Select top 20 from top 100 $ nexquant portfolio -n 100 -t 20 # Select top 20 from top 100
$ predix portfolio -c 0.5 # Allow higher correlation (0.5) $ nexquant portfolio -c 0.5 # Allow higher correlation (0.5)
$ predix portfolio -n 200 -t 15 -c 0.2 # Strict diversification $ nexquant portfolio -n 200 -t 15 -c 0.2 # Strict diversification
Expected Output: Expected Output:
- Formatted table showing selected factors with IC, Sharpe, and max correlation - Formatted table showing selected factors with IC, Sharpe, and max correlation
@@ -654,9 +654,9 @@ def portfolio(
Each factor must be re-evaluated to compute time-series values for correlation. Each factor must be re-evaluated to compute time-series values for correlation.
See Also: See Also:
predix portfolio-simple - Faster category-based diversification nexquant portfolio-simple - Faster category-based diversification
predix top - View top factors before portfolio selection nexquant top - View top factors before portfolio selection
predix build-strategies - Build strategies from selected factors nexquant build-strategies - Build strategies from selected factors
""" """
import glob as glob_module import glob as glob_module
import json import json
@@ -939,9 +939,9 @@ def portfolio_simple(
the chance of finding factors in all categories. (default: 100) the chance of finding factors in all categories. (default: 100)
Examples: Examples:
$ predix portfolio-simple # Top factors from different categories $ nexquant portfolio-simple # Top factors from different categories
$ predix portfolio-simple -n 200 # Consider top 200 factors $ nexquant portfolio-simple -n 200 # Consider top 200 factors
$ predix portfolio-simple -n 50 # Quick selection from top 50 $ nexquant portfolio-simple -n 50 # Quick selection from top 50
Expected Output: Expected Output:
- Formatted table showing selected factors with their category, IC, and Sharpe - Formatted table showing selected factors with their category, IC, and Sharpe
@@ -954,9 +954,9 @@ def portfolio_simple(
Only loads existing JSON results and performs keyword matching. Only loads existing JSON results and performs keyword matching.
See Also: See Also:
predix portfolio - Correlation-based diversification (more accurate but slower) nexquant portfolio - Correlation-based diversification (more accurate but slower)
predix top - View top factors before portfolio selection nexquant top - View top factors before portfolio selection
predix build-strategies - Build strategies from selected factors nexquant build-strategies - Build strategies from selected factors
""" """
import glob as glob_module import glob as glob_module
import json import json
@@ -1115,10 +1115,10 @@ def build_strategies(
strategies. (default: False) strategies. (default: False)
Examples: Examples:
$ predix build-strategies # Build from top 50, pairs only $ nexquant build-strategies # Build from top 50, pairs only
$ predix build-strategies -n 100 -c 3 # Top 100, up to triplets $ nexquant build-strategies -n 100 -c 3 # Top 100, up to triplets
$ predix build-strategies -d # Diversified (cross-category) only $ nexquant build-strategies -d # Diversified (cross-category) only
$ predix build-strategies -n 30 -c 2 -d # Top 30, diversified pairs $ nexquant build-strategies -n 30 -c 2 -d # Top 30, diversified pairs
Expected Output: Expected Output:
- Formatted table of top strategies ranked by Sharpe ratio - Formatted table of top strategies ranked by Sharpe ratio
@@ -1130,9 +1130,9 @@ def build_strategies(
Scales with O(n^k) where n=factors, k=max_combo_size. Scales with O(n^k) where n=factors, k=max_combo_size.
See Also: See Also:
predix build-strategies-ai - AI-powered strategy generation via LLM nexquant build-strategies-ai - AI-powered strategy generation via LLM
predix portfolio - Select diversified factors before combining nexquant portfolio - Select diversified factors before combining
predix top - View top factors before building strategies nexquant top - View top factors before building strategies
""" """
import numpy as np import numpy as np
from rdagent.scenarios.qlib.developer.strategy_builder import StrategyBuilder from rdagent.scenarios.qlib.developer.strategy_builder import StrategyBuilder
@@ -1140,7 +1140,7 @@ def build_strategies(
from rich.table import Table from rich.table import Table
console.print(Panel( console.print(Panel(
"[bold cyan]🏗️ Predix Strategy Builder[/bold cyan]\n" "[bold cyan]🏗️ NexQuant Strategy Builder[/bold cyan]\n"
"Systematically combining factors into trading strategies", "Systematically combining factors into trading strategies",
border_style="cyan", border_style="cyan",
)) ))
@@ -1271,12 +1271,12 @@ def build_strategies_ai(
may require multiple improvement loops. (default: 1) may require multiple improvement loops. (default: 1)
Examples: Examples:
$ predix build-strategies-ai # Generate 1 strategy, 5 loops max $ nexquant build-strategies-ai # Generate 1 strategy, 5 loops max
$ predix build-strategies-ai -t 100 # Use top 100 factors as pool $ nexquant build-strategies-ai -t 100 # Use top 100 factors as pool
$ predix build-strategies-ai -l 10 # Allow 10 improvement loops $ nexquant build-strategies-ai -l 10 # Allow 10 improvement loops
$ predix build-strategies-ai --min-sharpe 2.0 # Stricter Sharpe requirement $ nexquant build-strategies-ai --min-sharpe 2.0 # Stricter Sharpe requirement
$ predix build-strategies-ai --max-dd -0.15 # Tighter drawdown limit $ nexquant build-strategies-ai --max-dd -0.15 # Tighter drawdown limit
$ predix build-strategies-ai -c 5 # Generate 5 accepted strategies $ nexquant build-strategies-ai -c 5 # Generate 5 accepted strategies
Expected Output: Expected Output:
- Formatted table of accepted strategies with Sharpe, return, drawdown, - Formatted table of accepted strategies with Sharpe, return, drawdown,
@@ -1289,9 +1289,9 @@ def build_strategies_ai(
Each loop requires a full backtest execution plus LLM API calls. Each loop requires a full backtest execution plus LLM API calls.
See Also: See Also:
predix build-strategies - Systematic (non-AI) strategy combination nexquant build-strategies - Systematic (non-AI) strategy combination
predix quant - Generate new alpha factors via LLM trading loop nexquant quant - Generate new alpha factors via LLM trading loop
predix evaluate - Evaluate factors before strategy building nexquant evaluate - Evaluate factors before strategy building
""" """
from pathlib import Path from pathlib import Path
@@ -1345,7 +1345,7 @@ def build_strategies_ai(
if not factors_dir.exists(): if not factors_dir.exists():
console.print("[bold red]❌ No factors directory found at results/factors/[/bold red]") console.print("[bold red]❌ No factors directory found at results/factors/[/bold red]")
console.print("[yellow]Run 'predix quant' to generate factors first.[/yellow]") console.print("[yellow]Run 'nexquant quant' to generate factors first.[/yellow]")
return return
# Load evaluated factors # Load evaluated factors
@@ -1365,7 +1365,7 @@ def build_strategies_ai(
if len(factors) < 10: if len(factors) < 10:
console.print(f"[bold red]❌ Only {len(factors)} evaluated factors found. Need at least 10.[/bold red]") console.print(f"[bold red]❌ Only {len(factors)} evaluated factors found. Need at least 10.[/bold red]")
console.print("[yellow]Run 'predix evaluate' or 'predix quant' to generate more factors.[/yellow]") console.print("[yellow]Run 'nexquant evaluate' or 'nexquant quant' to generate more factors.[/yellow]")
return return
# Sort by IC and take top factors # Sort by IC and take top factors
@@ -1493,17 +1493,17 @@ def generate_strategies(
MaxDD on equity curve, WinRate on trade P&L) with runtime verification. MaxDD on equity curve, WinRate on trade P&L) with runtime verification.
Examples: Examples:
$ predix generate-strategies # 10 strategies, Optuna, swing $ nexquant generate-strategies # 10 strategies, Optuna, swing
$ predix generate-strategies -n 20 -w 4 # 20 strategies, 4 workers $ nexquant generate-strategies -n 20 -w 4 # 20 strategies, 4 workers
$ predix generate-strategies --min-sharpe 3.0 # Stricter acceptance $ nexquant generate-strategies --min-sharpe 3.0 # Stricter acceptance
$ predix generate-strategies -s daytrading # Day trading style $ nexquant generate-strategies -s daytrading # Day trading style
$ predix generate-strategies --no-optuna # Skip optimization $ nexquant generate-strategies --no-optuna # Skip optimization
""" """
from rich.console import Console as RichConsole from rich.console import Console as RichConsole
from rich.table import Table as RichTable from rich.table import Table as RichTable
console.print(f"\n[bold cyan]{'='*60}[/bold cyan]") console.print(f"\n[bold cyan]{'='*60}[/bold cyan]")
console.print("[bold cyan] Predix Strategy Generator[/bold cyan]") console.print("[bold cyan] NexQuant Strategy Generator[/bold cyan]")
console.print(f"[bold cyan]{'='*60}[/bold cyan]") console.print(f"[bold cyan]{'='*60}[/bold cyan]")
console.print(f" Strategies: [cyan]{count}[/cyan] Workers: [cyan]{workers}[/cyan] Style: [cyan]{style}[/cyan]") console.print(f" Strategies: [cyan]{count}[/cyan] Workers: [cyan]{workers}[/cyan] Style: [cyan]{style}[/cyan]")
console.print(f" Optuna: {'[green]Yes[/green]' if optuna else '[yellow]No[/yellow]'} (trials={optuna_trials}) Factors: [cyan]{top_factors}[/cyan]") console.print(f" Optuna: {'[green]Yes[/green]' if optuna else '[yellow]No[/yellow]'} (trials={optuna_trials}) Factors: [cyan]{top_factors}[/cyan]")
@@ -1564,8 +1564,8 @@ def health():
helps identify setup issues before running computationally expensive operations. helps identify setup issues before running computationally expensive operations.
Examples: Examples:
$ predix health # Run full system health check $ nexquant health # Run full system health check
$ predix health --verbose # Detailed output (if supported) $ nexquant health --verbose # Detailed output (if supported)
Expected Output: Expected Output:
- Python version and dependency status - Python version and dependency status
@@ -1579,8 +1579,8 @@ def health():
~5-15 seconds depending on network and database checks. ~5-15 seconds depending on network and database checks.
See Also: See Also:
predix status - Show current trading loop status and statistics nexquant status - Show current trading loop status and statistics
predix quant - Main trading loop command nexquant quant - Main trading loop command
""" """
from rdagent.app.utils.health_check import health_check from rdagent.app.utils.health_check import health_check
health_check() health_check()
@@ -1597,8 +1597,8 @@ def status():
and verifying data persistence. and verifying data persistence.
Examples: Examples:
$ predix status # Show current trading loop status $ nexquant status # Show current trading loop status
$ predix status --json # JSON output (if supported) $ nexquant status --json # JSON output (if supported)
Expected Output: Expected Output:
- Trading loop process status: RUNNING or STOPPED - Trading loop process status: RUNNING or STOPPED
@@ -1610,9 +1610,9 @@ def status():
Nearly instantaneous (< 1 second). Nearly instantaneous (< 1 second).
See Also: See Also:
predix health - Check system health and configuration nexquant health - Check system health and configuration
predix quant - Start the quantitative trading loop nexquant quant - Start the quantitative trading loop
predix top - View top evaluated factors nexquant top - View top evaluated factors
""" """
import sqlite3 import sqlite3
@@ -1707,11 +1707,11 @@ def best(
"""Rank backtested strategies by performance — source code is never exposed. """Rank backtested strategies by performance — source code is never exposed.
Examples: Examples:
$ predix best # Top 10 by composite score $ nexquant best # Top 10 by composite score
$ predix best -n 20 -m sharpe # Top 20 by Sharpe $ nexquant best -n 20 -m sharpe # Top 20 by Sharpe
$ predix best --no-realistic # Include numerically suspicious runs $ nexquant best --no-realistic # Include numerically suspicious runs
$ predix best --show TrendMomentumHybrid $ nexquant best --show TrendMomentumHybrid
$ predix best -n 50 --export /tmp/top.json $ nexquant best -n 50 --export /tmp/top.json
""" """
import json import json
@@ -1779,7 +1779,7 @@ def best(
) )
console.print(table) console.print(table)
console.print(f"\n[dim]{len(pool)} strategies matched filters (of {len(items)} total). " console.print(f"\n[dim]{len(pool)} strategies matched filters (of {len(items)} total). "
f"Use [bold]predix best --show NAME[/bold] for details.[/dim]") f"Use [bold]nexquant best --show NAME[/bold] for details.[/dim]")
if export: if export:
payload = [{k: v for k, v in s.items() if k != "code"} for s in top] payload = [{k: v for k, v in s.items() if k != "code"} for s in top]
@@ -1800,7 +1800,7 @@ def kronos_factor(
"""Generate Kronos-mini predicted-return alpha factor (Option A). """Generate Kronos-mini predicted-return alpha factor (Option A).
Runs Kronos-mini (4.1M params OHLCV foundation model, AAAI 2026) on rolling Runs Kronos-mini (4.1M params OHLCV foundation model, AAAI 2026) on rolling
windows of EUR/USD 1-min data and saves a predicted-return factor in Predix's windows of EUR/USD 1-min data and saves a predicted-return factor in NexQuant's
standard MultiIndex (datetime, instrument) format. standard MultiIndex (datetime, instrument) format.
Strategy: every STRIDE bars, use the previous CONTEXT bars as input and Strategy: every STRIDE bars, use the previous CONTEXT bars as input and
@@ -1813,13 +1813,13 @@ def kronos_factor(
git_ignore_folder/factor_implementation_source_data/intraday_pv.h5 git_ignore_folder/factor_implementation_source_data/intraday_pv.h5
Examples: Examples:
$ predix kronos-factor # Default: daily stride, GPU $ nexquant kronos-factor # Default: daily stride, GPU
$ predix kronos-factor --pred 30 --device cpu # 30-bar horizon, CPU $ nexquant kronos-factor --pred 30 --device cpu # 30-bar horizon, CPU
$ predix kronos-factor --context 256 --pred 48 $ nexquant kronos-factor --context 256 --pred 48
See Also: See Also:
predix kronos-eval - Evaluate Kronos as model and compute IC vs LightGBM nexquant kronos-eval - Evaluate Kronos as model and compute IC vs LightGBM
predix top - Show top factors by IC nexquant top - Show top factors by IC
""" """
from rdagent.components.coder.kronos_adapter import _cuda_available from rdagent.components.coder.kronos_adapter import _cuda_available
_device = device or ("cuda" if _cuda_available() else "cpu") _device = device or ("cuda" if _cuda_available() else "cpu")
@@ -1871,7 +1871,7 @@ def kronos_factor(
console.print(f"\n[green]Factor saved:[/green] {out_path}") console.print(f"\n[green]Factor saved:[/green] {out_path}")
console.print(f" Shape: {factor_df.shape} | Non-NaN: {meta['n_non_nan']}") console.print(f" Shape: {factor_df.shape} | Non-NaN: {meta['n_non_nan']}")
console.print(f" Metadata: {meta_path}") console.print(f" Metadata: {meta_path}")
console.print("\n[dim]Use 'predix top' to compare with other factors.[/dim]") console.print("\n[dim]Use 'nexquant top' to compare with other factors.[/dim]")
@app.command("kronos-eval") @app.command("kronos-eval")
@@ -1897,13 +1897,13 @@ def kronos_eval(
git_ignore_folder/factor_implementation_source_data/intraday_pv.h5 git_ignore_folder/factor_implementation_source_data/intraday_pv.h5
Examples: Examples:
$ predix kronos-eval # Default: 30-bar horizon $ nexquant kronos-eval # Default: 30-bar horizon
$ predix kronos-eval --pred 96 --device cuda # Daily horizon, GPU $ nexquant kronos-eval --pred 96 --device cuda # Daily horizon, GPU
$ predix kronos-eval --context 256 --pred 15 # Shorter horizon $ nexquant kronos-eval --context 256 --pred 15 # Shorter horizon
See Also: See Also:
predix kronos-factor - Generate Kronos factor for the factor pipeline nexquant kronos-factor - Generate Kronos factor for the factor pipeline
predix best - Show top strategies nexquant best - Show top strategies
""" """
from rdagent.components.coder.kronos_adapter import _cuda_available from rdagent.components.coder.kronos_adapter import _cuda_available
_device = device or ("cuda" if _cuda_available() else "cpu") _device = device or ("cuda" if _cuda_available() else "cpu")
+2 -2
View File
@@ -1,6 +1,6 @@
# Predix Prompts Index # NexQuant Prompts Index
Centralized location for all LLM prompts used in the Predix trading system. Centralized location for all LLM prompts used in the NexQuant trading system.
## Structure ## Structure
+6 -6
View File
@@ -1,6 +1,6 @@
# Predix Prompts # NexQuant Prompts
This directory contains all LLM prompts for the Predix trading agent. This directory contains all LLM prompts for the NexQuant trading agent.
--- ---
@@ -174,13 +174,13 @@ prompt_v2 = load_yaml_file("prompts/local/factor_discovery_v2.yaml")
```bash ```bash
# Backup to private repo # Backup to private repo
cd ~/Predix cd ~/NexQuant
git archive --format=tar prompts/local/ | gzip > ~/backups/prompts_local_$(date +%Y%m%d).tar.gz git archive --format=tar prompts/local/ | gzip > ~/backups/prompts_local_$(date +%Y%m%d).tar.gz
# Or sync to private GitHub repo # Or sync to private GitHub repo
git clone git@github.com:TPTBusiness/predix-prompts-private.git git clone git@github.com:TPTBusiness/nexquant-prompts-private.git
cp -r prompts/local/* predix-prompts-private/ cp -r prompts/local/* nexquant-prompts-private/
cd predix-prompts-private && git push cd nexquant-prompts-private && git push
``` ```
--- ---
+5 -5
View File
@@ -7,7 +7,7 @@ requires = [
[project] [project]
authors = [ authors = [
{email = "nico@predix.io", name = "Predix Team"}, {email = "nico@nexquant.io", name = "NexQuant Team"},
] ]
classifiers = [ classifiers = [
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
@@ -16,7 +16,7 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
] ]
description = "Predix - AI-gestützter Quantitative Trading Agent für EUR/USD" description = "NexQuant - AI-gestützter Quantitative Trading Agent für EUR/USD"
dynamic = [ dynamic = [
"dependencies", "dependencies",
"optional-dependencies", "optional-dependencies",
@@ -29,7 +29,7 @@ keywords = [
"EUR/USD", "EUR/USD",
"Forex", "Forex",
] ]
name = "predix" name = "nexquant"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
@@ -37,8 +37,8 @@ requires-python = ">=3.10"
rdagent = "rdagent.app.cli:app" rdagent = "rdagent.app.cli:app"
[project.urls] [project.urls]
homepage = "https://github.com/PredixAI/predix/" homepage = "https://github.com/NexQuantAI/nexquant/"
issue = "https://github.com/PredixAI/predix/issues" issue = "https://github.com/NexQuantAI/nexquant/issues"
[tool.coverage.report] [tool.coverage.report]
fail_under = 80 fail_under = 80
+13 -13
View File
@@ -294,7 +294,7 @@ def fin_quant_cli(
# Start CLI Dashboard wenn gewünscht # Start CLI Dashboard wenn gewünscht
if with_cli_dashboard: if with_cli_dashboard:
def start_cli_dash(): def start_cli_dash():
from rdagent.log.ui.predix_dashboard import run_dashboard from rdagent.log.ui.nexquant_dashboard import run_dashboard
run_dashboard(log_path="fin_quant.log", refresh_interval=3) run_dashboard(log_path="fin_quant.log", refresh_interval=3)
cli_thread = threading.Thread(target=start_cli_dash, daemon=True) cli_thread = threading.Thread(target=start_cli_dash, daemon=True)
@@ -1262,9 +1262,9 @@ def start_loop_cli(
from datetime import datetime from datetime import datetime
script_dir = str(Path(__file__).parent.parent.parent) script_dir = str(Path(__file__).parent.parent.parent)
generator = [sys.executable, f"{script_dir}/scripts/predix_smart_strategy_gen.py"] generator = [sys.executable, f"{script_dir}/scripts/nexquant_smart_strategy_gen.py"]
logfile = f"{script_dir}/results/logs/generator_loop.log" logfile = f"{script_dir}/results/logs/generator_loop.log"
pidfile = "/tmp/predix_loop.pid" # nosec B108 — administrative PID file, single-process daemon pidfile = "/tmp/nexquant_loop.pid" # nosec B108 — administrative PID file, single-process daemon
os.makedirs(f"{script_dir}/results/logs", exist_ok=True) os.makedirs(f"{script_dir}/results/logs", exist_ok=True)
@@ -1418,7 +1418,7 @@ def parallel_cli(
from rdagent.log import daily_log as _dlog from rdagent.log import daily_log as _dlog
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
script = project_root / "scripts" / "predix_parallel.py" script = project_root / "scripts" / "nexquant_parallel.py"
if not script.exists(): if not script.exists():
typer.echo(f"❌ Script not found: {script}") typer.echo(f"❌ Script not found: {script}")
@@ -1469,7 +1469,7 @@ def eval_all_cli(
from rdagent.log import daily_log as _dlog from rdagent.log import daily_log as _dlog
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
script = project_root / "scripts" / "predix_full_eval.py" script = project_root / "scripts" / "nexquant_full_eval.py"
if not script.exists(): if not script.exists():
typer.echo(f"❌ Script not found: {script}") typer.echo(f"❌ Script not found: {script}")
@@ -1522,7 +1522,7 @@ def batch_backtest_cli(
from pathlib import Path from pathlib import Path
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
script = project_root / "scripts" / "predix_batch_backtest.py" script = project_root / "scripts" / "nexquant_batch_backtest.py"
if not script.exists(): if not script.exists():
typer.echo(f"❌ Script not found: {script}") typer.echo(f"❌ Script not found: {script}")
@@ -1574,7 +1574,7 @@ def simple_eval_cli(
from pathlib import Path from pathlib import Path
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
script = project_root / "scripts" / "predix_simple_eval.py" script = project_root / "scripts" / "nexquant_simple_eval.py"
if not script.exists(): if not script.exists():
typer.echo(f"❌ Script not found: {script}") typer.echo(f"❌ Script not found: {script}")
@@ -1620,7 +1620,7 @@ def rebacktest_cli(
from pathlib import Path from pathlib import Path
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
script = project_root / "scripts" / "predix_rebacktest_strategies.py" script = project_root / "scripts" / "nexquant_rebacktest_strategies.py"
if not script.exists(): if not script.exists():
typer.echo(f"❌ Script not found: {script}") typer.echo(f"❌ Script not found: {script}")
@@ -1673,7 +1673,7 @@ def report_cli(
from pathlib import Path from pathlib import Path
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
script = project_root / "scripts" / "predix_strategy_report.py" script = project_root / "scripts" / "nexquant_strategy_report.py"
if not script.exists(): if not script.exists():
typer.echo(f"❌ Script not found: {script}") typer.echo(f"❌ Script not found: {script}")
@@ -1697,10 +1697,10 @@ def report_cli(
@app.command(name="predix") @app.command(name="nexquant")
def predix_welcome(): def nexquant_welcome():
""" """
Show Predix welcome screen with system overview. Show NexQuant welcome screen with system overview.
This command displays a beautiful dashboard showing: This command displays a beautiful dashboard showing:
- System status (factors, strategies, security) - System status (factors, strategies, security)
@@ -1710,7 +1710,7 @@ def predix_welcome():
Perfect for GitHub README screenshots! Perfect for GitHub README screenshots!
Examples: Examples:
rdagent predix rdagent nexquant
""" """
from rdagent.app.cli_welcome import show_welcome from rdagent.app.cli_welcome import show_welcome
show_welcome() show_welcome()
+4 -4
View File
@@ -1,5 +1,5 @@
""" """
Predix CLI Welcome Screen - Beautiful dashboard for GitHub README screenshot. NexQuant CLI Welcome Screen - Beautiful dashboard for GitHub README screenshot.
""" """
import os import os
@@ -16,7 +16,7 @@ from datetime import datetime
console = Console() console = Console()
def show_welcome(): def show_welcome():
"""Show beautiful Predix welcome screen.""" """Show beautiful NexQuant welcome screen."""
# Header # Header
console.print() console.print()
@@ -89,7 +89,7 @@ def show_welcome():
console.print() console.print()
# Footer # Footer
footer = Text("📄 github.com/TPTBusiness/Predix • 🔒 MIT License • 📖 docs/", style="dim white") footer = Text("📄 github.com/TPTBusiness/NexQuant • 🔒 MIT License • 📖 docs/", style="dim white")
console.print(Align.center(footer)) console.print(Align.center(footer))
console.print() console.print()
@@ -98,5 +98,5 @@ if __name__ == "__main__":
def main(): def main():
"""Entry point for 'predix' CLI command.""" """Entry point for 'nexquant' CLI command."""
show_welcome() show_welcome()
+1 -1
View File
@@ -1,4 +1,4 @@
"""Predix Backtesting Package""" """NexQuant Backtesting Package"""
from .backtest_engine import BacktestMetrics, FactorBacktester from .backtest_engine import BacktestMetrics, FactorBacktester
from .results_db import ResultsDatabase from .results_db import ResultsDatabase
from .risk_management import CorrelationAnalyzer, PortfolioOptimizer, AdvancedRiskManager from .risk_management import CorrelationAnalyzer, PortfolioOptimizer, AdvancedRiskManager
@@ -1,5 +1,5 @@
""" """
Predix Backtesting Engine - IC, Sharpe, Drawdown NexQuant Backtesting Engine - IC, Sharpe, Drawdown
Thin wrapper around the unified ``vbt_backtest.backtest_signal`` engine. Thin wrapper around the unified ``vbt_backtest.backtest_signal`` engine.
All metric formulas live in ``vbt_backtest``; this module exists for All metric formulas live in ``vbt_backtest``; this module exists for
@@ -1,5 +1,5 @@
""" """
Trading Protection System for Predix. Trading Protection System for NexQuant.
Prevents excessive losses by automatically pausing trading Prevents excessive losses by automatically pausing trading
when risk thresholds are exceeded. when risk thresholds are exceeded.
@@ -3,7 +3,7 @@ Trading Protection System
Prevents excessive losses by automatically pausing trading when risk thresholds are exceeded. Prevents excessive losses by automatically pausing trading when risk thresholds are exceeded.
Inspired by common trading protection patterns, implemented from scratch for Predix. Inspired by common trading protection patterns, implemented from scratch for NexQuant.
""" """
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
+2 -2
View File
@@ -1,5 +1,5 @@
""" """
Predix Results Database - SQLite für Backtest-Ergebnisse NexQuant Results Database - SQLite für Backtest-Ergebnisse
Stores backtest metrics from Qlib/MLflow runs for querying and dashboard display. Stores backtest metrics from Qlib/MLflow runs for querying and dashboard display.
""" """
@@ -409,7 +409,7 @@ class ResultsDatabase:
worst_dd_str = self._fmt_float(best['worst_drawdown'], ".4f") worst_dd_str = self._fmt_float(best['worst_drawdown'], ".4f")
md_lines = [ md_lines = [
"# Predix Results Summary", "# NexQuant Results Summary",
"", "",
f"**Generated:** {summary['generated_at']}", f"**Generated:** {summary['generated_at']}",
f"**Database:** `{summary['database_path']}`", f"**Database:** `{summary['database_path']}`",
@@ -1,5 +1,5 @@
""" """
Predix Risk Management - Korrelation, Portfolio-Optimierung NexQuant Risk Management - Korrelation, Portfolio-Optimierung
""" """
import numpy as np import numpy as np
@@ -2,7 +2,7 @@
Unified, verifiable backtesting engine. Unified, verifiable backtesting engine.
Single entry point (`backtest_signal`) used by: Single entry point (`backtest_signal`) used by:
- scripts/predix_gen_strategies_real_bt.py - scripts/nexquant_gen_strategies_real_bt.py
- rdagent/scenarios/qlib/local/strategy_orchestrator.py - rdagent/scenarios/qlib/local/strategy_orchestrator.py
- rdagent/scenarios/qlib/local/optuna_optimizer.py - rdagent/scenarios/qlib/local/optuna_optimizer.py
- rdagent/components/backtesting/backtest_engine.py - rdagent/components/backtesting/backtest_engine.py
@@ -1,5 +1,5 @@
""" """
Predix Factor Auto-Fixer - Automatically patches common factor code issues. NexQuant Factor Auto-Fixer - Automatically patches common factor code issues.
This module intercepts LLM-generated factor code and automatically fixes known problems: This module intercepts LLM-generated factor code and automatically fixes known problems:
1. min_periods mismatch in rolling window calculations 1. min_periods mismatch in rolling window calculations
+5 -5
View File
@@ -1,5 +1,5 @@
""" """
Kronos Foundation Model Adapter for Predix. Kronos Foundation Model Adapter for NexQuant.
Wraps the Kronos-mini OHLCV foundation model (4.1M params, AAAI 2026, MIT) Wraps the Kronos-mini OHLCV foundation model (4.1M params, AAAI 2026, MIT)
for use as: for use as:
@@ -55,8 +55,8 @@ def _ensure_kronos() -> bool:
return _KRONOS_AVAILABLE return _KRONOS_AVAILABLE
def _ohlcv_from_predix(df: pd.DataFrame) -> pd.DataFrame: def _ohlcv_from_nexquant(df: pd.DataFrame) -> pd.DataFrame:
"""Convert Predix HDF5 format ($open/$close/...) to Kronos format (open/close/...).""" """Convert NexQuant HDF5 format ($open/$close/...) to Kronos format (open/close/...)."""
col_map = {"$open": "open", "$high": "high", "$low": "low", "$close": "close", "$volume": "volume"} col_map = {"$open": "open", "$high": "high", "$low": "low", "$close": "close", "$volume": "volume"}
renamed = df.rename(columns=col_map) renamed = df.rename(columns=col_map)
cols = [c for c in ["open", "high", "low", "close", "volume"] if c in renamed.columns] cols = [c for c in ["open", "high", "low", "close", "volume"] if c in renamed.columns]
@@ -253,7 +253,7 @@ def build_kronos_factor(
instrument = raw.index.get_level_values("instrument").unique()[0] instrument = raw.index.get_level_values("instrument").unique()[0]
df = raw.xs(instrument, level="instrument") df = raw.xs(instrument, level="instrument")
ohlcv = _ohlcv_from_predix(df) ohlcv = _ohlcv_from_nexquant(df)
adapter = KronosAdapter(device=device, max_context=min(context_bars, 512), model_size=model_size) adapter = KronosAdapter(device=device, max_context=min(context_bars, 512), model_size=model_size)
adapter.load() adapter.load()
@@ -328,7 +328,7 @@ def evaluate_kronos_model(
raw = pd.read_hdf(hdf5_path, key="data") raw = pd.read_hdf(hdf5_path, key="data")
instrument = raw.index.get_level_values("instrument").unique()[0] instrument = raw.index.get_level_values("instrument").unique()[0]
df = raw.xs(instrument, level="instrument") df = raw.xs(instrument, level="instrument")
ohlcv = _ohlcv_from_predix(df) ohlcv = _ohlcv_from_nexquant(df)
adapter = KronosAdapter(device=device, max_context=min(context_bars, 512), model_size=model_size) adapter = KronosAdapter(device=device, max_context=min(context_bars, 512), model_size=model_size)
adapter.load() adapter.load()
+1 -1
View File
@@ -1,4 +1,4 @@
"""RL Trading Agent components for Predix. """RL Trading Agent components for NexQuant.
This package provides reinforcement learning trading capabilities. This package provides reinforcement learning trading capabilities.
Works with or without stable-baselines3 (graceful fallback). Works with or without stable-baselines3 (graceful fallback).
+1 -1
View File
@@ -2,7 +2,7 @@
RL Trading Agent wrapper for Stable Baselines3. RL Trading Agent wrapper for Stable Baselines3.
Provides an easy-to-use interface for training, evaluating, and deploying Provides an easy-to-use interface for training, evaluating, and deploying
RL trading agents within the Predix framework. RL trading agents within the NexQuant framework.
Supported algorithms: Supported algorithms:
- PPO: Proximal Policy Optimization (most stable, recommended for production) - PPO: Proximal Policy Optimization (most stable, recommended for production)
+1 -1
View File
@@ -5,7 +5,7 @@ Gym-compatible environment for training RL trading agents.
Supports single-asset (EUR/USD) trading with technical indicators Supports single-asset (EUR/USD) trading with technical indicators
and portfolio state as observations. and portfolio state as observations.
Inspired by common RL trading environment patterns, implemented from scratch for Predix. Inspired by common RL trading environment patterns, implemented from scratch for NexQuant.
""" """
import gymnasium as gym import gymnasium as gym
+1 -1
View File
@@ -2,7 +2,7 @@
Fallback RL implementation for users without stable-baselines3. Fallback RL implementation for users without stable-baselines3.
Provides simple rule-based trading when RL library is not available. Provides simple rule-based trading when RL library is not available.
This ensures the Predix system works for all GitHub users, even This ensures the NexQuant system works for all GitHub users, even
without the optional stable-baselines3 dependency. without the optional stable-baselines3 dependency.
The fallback implements a momentum-based strategy as a placeholder The fallback implements a momentum-based strategy as a placeholder
+2 -2
View File
@@ -1,5 +1,5 @@
""" """
Predix Model Loader NexQuant Model Loader
Loads models from: Loads models from:
1. models/local/*.py (your improved models - not in Git) 1. models/local/*.py (your improved models - not in Git)
@@ -23,7 +23,7 @@ from typing import Optional, Any
# Base paths # Base paths
BASE_DIR = Path(__file__).parent.parent.parent # Predix/ BASE_DIR = Path(__file__).parent.parent.parent # NexQuant/
MODELS_DIR = BASE_DIR / "models" MODELS_DIR = BASE_DIR / "models"
LOCAL_MODELS_DIR = MODELS_DIR / "local" LOCAL_MODELS_DIR = MODELS_DIR / "local"
STANDARD_MODELS_DIR = MODELS_DIR / "standard" STANDARD_MODELS_DIR = MODELS_DIR / "standard"
+2 -2
View File
@@ -1,5 +1,5 @@
""" """
Predix Prompt Loader NexQuant Prompt Loader
Loads prompts from: Loads prompts from:
1. prompts/local/*.yaml (your improved prompts - not in Git) 1. prompts/local/*.yaml (your improved prompts - not in Git)
@@ -22,7 +22,7 @@ from typing import Optional, Dict, Any
# Base paths # Base paths
BASE_DIR = Path(__file__).parent.parent.parent # Predix/ BASE_DIR = Path(__file__).parent.parent.parent # NexQuant/
PROMPTS_DIR = BASE_DIR / "prompts" PROMPTS_DIR = BASE_DIR / "prompts"
LOCAL_PROMPTS_DIR = PROMPTS_DIR / "local" LOCAL_PROMPTS_DIR = PROMPTS_DIR / "local"
STANDARD_PROMPTS_FILE = PROMPTS_DIR / "standard_prompts.yaml" STANDARD_PROMPTS_FILE = PROMPTS_DIR / "standard_prompts.yaml"
@@ -969,7 +969,7 @@ class QlibFactorRunner(CachedRunner[QlibFactorExperiment]):
# Run factor code on full data in a temp workspace # Run factor code on full data in a temp workspace
import pandas as pd import pandas as pd
with tempfile.TemporaryDirectory(prefix="predix_fullval_") as tmp_dir: with tempfile.TemporaryDirectory(prefix="nexquant_fullval_") as tmp_dir:
tmp = Path(tmp_dir) tmp = Path(tmp_dir)
shutil.copy(str(factor_py), str(tmp / "factor.py")) shutil.copy(str(factor_py), str(tmp / "factor.py"))
shutil.copy(str(full_data), str(tmp / "intraday_pv.h5")) shutil.copy(str(full_data), str(tmp / "intraday_pv.h5"))
@@ -1,5 +1,5 @@
""" """
Predix Strategy Builder - Systematically combine factors into trading strategies. NexQuant Strategy Builder - Systematically combine factors into trading strategies.
This module: This module:
1. Loads evaluated factors with time-series values 1. Loads evaluated factors with time-series values
@@ -8,9 +8,9 @@ This module:
4. Ranks and saves best strategies 4. Ranks and saves best strategies
Usage: Usage:
predix build-strategies # Build strategies from top factors nexquant build-strategies # Build strategies from top factors
predix build-strategies --top 50 # Use top 50 factors nexquant build-strategies --top 50 # Use top 50 factors
predix build-strategies --max-combo 3 # Allow up to 3-factor combinations nexquant build-strategies --max-combo 3 # Allow up to 3-factor combinations
""" """
import json import json
@@ -56,7 +56,7 @@ Current Date: {current_date}
Live Macro Data: Live Macro Data:
{macro_data} {macro_data}
Factor Report from Predix RD-Agent: Factor Report from NexQuant RD-Agent:
{factor_report} {factor_report}
Analyze the macro environment and its impact on the proposed factor: Analyze the macro environment and its impact on the proposed factor:
@@ -47,7 +47,7 @@ Active Session: {session}
Expected Regime: {regime} Expected Regime: {regime}
Session Notes: {session_note} Session Notes: {session_note}
Factor Report from Predix RD-Agent: Factor Report from NexQuant RD-Agent:
{factor_report} {factor_report}
Analyze whether the proposed factor is suitable for the current session regime. Analyze whether the proposed factor is suitable for the current session regime.
@@ -17,7 +17,7 @@ def create_fx_trader(llm):
You have received reports from your team: You have received reports from your team:
FACTOR ANALYSIS (Predix RD-Agent): FACTOR ANALYSIS (NexQuant RD-Agent):
{factor_report} {factor_report}
SESSION ANALYSIS: SESSION ANALYSIS:
@@ -1,5 +1,5 @@
""" """
FX Validator Graph Multi-Agent Validierung für Predix Faktoren FX Validator Graph Multi-Agent Validierung für NexQuant Faktoren
Implementiert Multi-Agenten-System für Trading-Entscheidungen: Implementiert Multi-Agenten-System für Trading-Entscheidungen:
- Session Analyst: Analysiert aktuelle FX-Session - Session Analyst: Analysiert aktuelle FX-Session
@@ -88,10 +88,10 @@ def create_fx_validator(config: dict = None):
def validate_factor(factor_report: str, trade_date: str = None) -> dict: def validate_factor(factor_report: str, trade_date: str = None) -> dict:
""" """
Hauptfunktion validiert einen Predix-Faktor durch Multi-Agent Debatte Hauptfunktion validiert einen NexQuant-Faktor durch Multi-Agent Debatte
Args: Args:
factor_report: Der Faktor-Report von Predix RD-Agent factor_report: Der Faktor-Report von NexQuant RD-Agent
trade_date: Datum/Zeit in ISO Format (default: jetzt) trade_date: Datum/Zeit in ISO Format (default: jetzt)
Returns: Returns:
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Predix Quant Loop Factory - Selects appropriate workflow based on available components. NexQuant Quant Loop Factory - Selects appropriate workflow based on available components.
This module is the entry point for the quantitative trading loop. This module is the entry point for the quantitative trading loop.
It automatically selects between: It automatically selects between:
+1 -1
View File
@@ -3,7 +3,7 @@
# Install with: pip install -r requirements/rl.txt # Install with: pip install -r requirements/rl.txt
# #
# These dependencies are OPTIONAL. # These dependencies are OPTIONAL.
# The Predix RL trading system works without them using a simple momentum fallback. # The NexQuant RL trading system works without them using a simple momentum fallback.
# #
# Only install if you want to use full PPO/A2C/SAC training. # Only install if you want to use full PPO/A2C/SAC training.
+2 -2
View File
@@ -5,8 +5,8 @@ import numpy as np
import pandas as pd import pandas as pd
from pathlib import Path from pathlib import Path
OHLCV_PATH = Path('/home/nico/Predix/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5') OHLCV_PATH = Path('/home/nico/NexQuant/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5')
FACTORS_DIR = Path('/home/nico/Predix/results/factors') FACTORS_DIR = Path('/home/nico/NexQuant/results/factors')
VALUES_DIR = FACTORS_DIR / 'values' VALUES_DIR = FACTORS_DIR / 'values'
print("=" * 70) print("=" * 70)
+3 -3
View File
@@ -3,10 +3,10 @@
Option A: Generate Kronos predicted-return factor from EUR/USD 1-min data. Option A: Generate Kronos predicted-return factor from EUR/USD 1-min data.
Runs Kronos-mini inference in daily strides (96 bars/day) over all available Runs Kronos-mini inference in daily strides (96 bars/day) over all available
OHLCV data and saves the resulting factor for use in Predix's factor pipeline. OHLCV data and saves the resulting factor for use in NexQuant's factor pipeline.
Usage: Usage:
conda activate predix conda activate nexquant
python scripts/kronos_factor_gen.py python scripts/kronos_factor_gen.py
python scripts/kronos_factor_gen.py --context 512 --pred 96 --device cuda python scripts/kronos_factor_gen.py --context 512 --pred 96 --device cuda
python scripts/kronos_factor_gen.py --device cpu # slower but no GPU needed python scripts/kronos_factor_gen.py --device cpu # slower but no GPU needed
@@ -71,7 +71,7 @@ def main():
print(f"\nSample (first 5):") print(f"\nSample (first 5):")
print(factor_df.head()) print(factor_df.head())
# Save metadata for predix.py top / best integration # Save metadata for nexquant.py top / best integration
meta = { meta = {
"factor_name": f"KronosPredReturn_p{args.pred}", "factor_name": f"KronosPredReturn_p{args.pred}",
"description": f"Kronos-mini predicted return, {args.pred}-bar horizon", "description": f"Kronos-mini predicted return, {args.pred}-bar horizon",
+1 -1
View File
@@ -6,7 +6,7 @@ Computes IC (Information Coefficient) and hit rate for Kronos predictions
vs actual realized returns. Results are printed for comparison with LightGBM. vs actual realized returns. Results are printed for comparison with LightGBM.
Usage: Usage:
conda activate predix conda activate nexquant
python scripts/kronos_model_eval.py python scripts/kronos_model_eval.py
python scripts/kronos_model_eval.py --pred 30 --context 512 --device cuda python scripts/kronos_model_eval.py --pred 30 --context 512 --device cuda
""" """
@@ -10,8 +10,8 @@ For each accepted strategy, add:
- Generate Live Trading report - Generate Live Trading report
Usage: Usage:
python predix_add_risk_management.py python nexquant_add_risk_management.py
python predix_add_risk_management.py --live # Mark as live-ready python nexquant_add_risk_management.py --live # Mark as live-ready
""" """
import os, sys, json, time import os, sys, json, time
from pathlib import Path from pathlib import Path
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python
"""
NexQuant Auto-Pilot vollautomatischer Strategie-Generator.
Läuft unbegrenzt, kein menschlicher Eingriff nötig.
Jede Runde: Factors laden LLM Code Pre-Flight Backtest Optuna Ensemble
Bei Crash: auto-restart nach 30s.
Usage:
python scripts/nexquant_autopilot.py
"""
from __future__ import annotations
import json, logging, os, sys, time, traceback
from datetime import datetime
from pathlib import Path
import numpy as np, pandas as pd
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
# Load .env before any rdagent imports (required for pydantic-settings)
try:
from dotenv import load_dotenv
_env_path = Path(__file__).resolve().parent.parent / ".env"
load_dotenv(_env_path)
except ImportError:
pass
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("autopilot")
LOG_FILE = Path(__file__).resolve().parent.parent / "git_ignore_folder" / "logs" / f"autopilot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
fh = logging.FileHandler(str(LOG_FILE))
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
logger.addHandler(fh)
BATCH_SIZE = 2
OPTUNA_TRIALS = 10
COOLDOWN = 30
MAX_CONSECUTIVE_FAILS = 5
def main_round(style: str, round_num: int) -> int:
"""Run one round. Returns number of accepted strategies."""
from rdagent.scenarios.qlib.local.strategy_orchestrator import StrategyOrchestrator
accepted_count = 0
try:
orch = StrategyOrchestrator(
top_factors=20, trading_style=style,
min_sharpe=0.1, use_optuna=True, optuna_trials=OPTUNA_TRIALS,
)
except Exception as e:
logger.error(f"Orchestrator init failed: {e}")
return 0
try:
results = orch.generate_strategies(count=BATCH_SIZE, workers=1)
except Exception as e:
logger.error(f"generate_strategies failed: {e}")
return 0
for r in results:
status = r.get("status", "?")
if status == "accepted":
accepted_count += 1
logger.info(f"{r.get('strategy_name','?')[:40]:40s} S={r.get('sharpe_ratio',0):.1f} OOS={r.get('oos_sharpe',0):.1f}")
else:
reason = r.get("reason", "?")[:80]
logger.debug(f"{r.get('strategy_name','?')[:40]:40s} {reason}")
if accepted_count >= 2:
try:
ensemble = orch.build_ensemble(results)
if ensemble and ensemble.get("status") == "success":
logger.info(f" Ensemble: S={ensemble['sharpe_ratio']:.1f} OOS={ensemble['oos_sharpe']:.1f} ({len(ensemble['members'])} members)")
except Exception:
pass
return accepted_count
def main():
print(f"\n{'='*50}")
print(f" NexQuant Auto-Pilot")
print(f" Log: {LOG_FILE}")
print(f" Batch: {BATCH_SIZE} | Optuna: {OPTUNA_TRIALS} trials")
print(f"{'='*50}\n")
round_num = 0
total_accepted = 0
consecutive_fails = 0
start_time = datetime.now()
styles = ["swing", "daytrading"]
while True:
round_num += 1
style = styles[round_num % 2]
print(f"\n[Round {round_num}] {style} | {datetime.now().strftime('%H:%M:%S')}", flush=True)
try:
accepted = main_round(style, round_num)
total_accepted += accepted
if accepted == 0:
consecutive_fails += 1
else:
consecutive_fails = 0
elapsed = (datetime.now() - start_time).total_seconds()
rate = total_accepted / (elapsed / 3600) if elapsed > 0 else 0
print(f" Accepted: {accepted} | Total: {total_accepted} | Rate: {rate:.1f}/h | Fails: {consecutive_fails}", flush=True)
if consecutive_fails >= MAX_CONSECUTIVE_FAILS:
logger.warning(f"{consecutive_fails} consecutive failures — cooling down {COOLDOWN*2}s")
time.sleep(COOLDOWN * 2)
consecutive_fails = 0
except KeyboardInterrupt:
print(f"\n\nStopped after {round_num} rounds. Total accepted: {total_accepted}")
break
except Exception as e:
logger.error(f"Round {round_num} crashed: {e}\n{traceback.format_exc()[-500:]}")
consecutive_fails += 1
time.sleep(COOLDOWN)
time.sleep(COOLDOWN)
if __name__ == "__main__":
main()
@@ -1,14 +1,14 @@
""" """
Predix Batch Backtest Script - Extract and backtest existing factors. NexQuant Batch Backtest Script - Extract and backtest existing factors.
Scans generated factor code from workspaces, runs Qlib backtests directly Scans generated factor code from workspaces, runs Qlib backtests directly
(bypassing CoSTEER), and saves results to JSON + SQLite. (bypassing CoSTEER), and saves results to JSON + SQLite.
Usage: Usage:
python predix_batch_backtest.py --factors 100 # Backtest top 100 factors python nexquant_batch_backtest.py --factors 100 # Backtest top 100 factors
python predix_batch_backtest.py --all # Backtest all discovered factors python nexquant_batch_backtest.py --all # Backtest all discovered factors
python predix_batch_backtest.py --parallel 5 # 5 parallel backtests python nexquant_batch_backtest.py --parallel 5 # 5 parallel backtests
python predix_batch_backtest.py --scan-only # Only scan, don't run backtests python nexquant_batch_backtest.py --scan-only # Only scan, don't run backtests
""" """
import json import json
@@ -660,7 +660,7 @@ def _run_factor_directly(factor_info: FactorInfo) -> Optional[BacktestResult]:
import tempfile import tempfile
import subprocess import subprocess
with tempfile.TemporaryDirectory(prefix="predix_factor_") as tmp_dir: with tempfile.TemporaryDirectory(prefix="nexquant_factor_") as tmp_dir:
ws = Path(tmp_dir) ws = Path(tmp_dir)
# Write factor code # Write factor code
@@ -742,7 +742,7 @@ def _run_qlib_single(factor_info: FactorInfo) -> BacktestResult:
import tempfile import tempfile
# Create temp workspace # Create temp workspace
with tempfile.TemporaryDirectory(prefix="predix_bt_") as tmp_dir: with tempfile.TemporaryDirectory(prefix="nexquant_bt_") as tmp_dir:
ws = Path(tmp_dir) ws = Path(tmp_dir)
# Write factor code # Write factor code
@@ -1182,7 +1182,7 @@ def main(
Metric for ranking ('ic' or 'sharpe') Metric for ranking ('ic' or 'sharpe')
""" """
console.print(Panel( console.print(Panel(
"[bold cyan]Predix Batch Backtest Runner[/bold cyan]\n" "[bold cyan]NexQuant Batch Backtest Runner[/bold cyan]\n"
f"Scanning workspaces for generated factors...", f"Scanning workspaces for generated factors...",
border_style="cyan", border_style="cyan",
)) ))
@@ -1196,7 +1196,7 @@ def main(
if not all_factors_list: if not all_factors_list:
console.print("\n[red]No factors found in workspaces![/red]") console.print("\n[red]No factors found in workspaces![/red]")
console.print( console.print(
"[yellow]Ensure factors have been generated via `predix.py quant` first.[/yellow]" "[yellow]Ensure factors have been generated via `nexquant.py quant` first.[/yellow]"
) )
return return
@@ -1407,7 +1407,7 @@ if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Predix Batch Backtest - Extract and backtest existing factors" description="NexQuant Batch Backtest - Extract and backtest existing factors"
) )
parser.add_argument( parser.add_argument(
"--factors", "-n", "--factors", "-n",
@@ -12,9 +12,9 @@ Features:
- Daytrading AND swing style alternating - Daytrading AND swing style alternating
Usage: Usage:
python scripts/predix_continuous_strategies.py python scripts/nexquant_continuous_strategies.py
python scripts/predix_continuous_strategies.py --style daytrading --rounds 100 python scripts/nexquant_continuous_strategies.py --style daytrading --rounds 100
python scripts/predix_continuous_strategies.py --style both --workers 4 python scripts/nexquant_continuous_strategies.py --style both --workers 4
""" """
from __future__ import annotations from __future__ import annotations
@@ -105,7 +105,7 @@ def main():
args = parser.parse_args() args = parser.parse_args()
print(f"\n{'='*60}") print(f"\n{'='*60}")
print(f" Predix Continuous Strategy Generator") print(f" NexQuant Continuous Strategy Generator")
print(f" Style: {args.style} | Workers: {args.workers}") print(f" Style: {args.style} | Workers: {args.workers}")
print(f" Min Sharpe: {args.min_sharpe} | Batch: {args.batch_size}") print(f" Min Sharpe: {args.min_sharpe} | Batch: {args.batch_size}")
print(f" ML every {args.ml_rounds} rounds") print(f" ML every {args.ml_rounds} rounds")
+156
View File
@@ -0,0 +1,156 @@
#!/usr/bin/env python
"""Fast rebacktest: only strategies with factor parquets, skip already-done."""
import json, sys, pandas as pd, subprocess, tempfile, numpy as np
from pathlib import Path
from datetime import datetime
sys.path.insert(0, str(Path(__file__).resolve().parent))
from rdagent.components.backtesting.vbt_backtest import backtest_signal
OHLCV = Path("git_ignore_folder/factor_implementation_source_data/intraday_pv.h5")
FACTORS_DIR = Path("results/factors/values")
STRAT_DIR = Path("results/strategies_new")
# Pre-build factor name → path map
fmap = {p.stem: str(p) for p in FACTORS_DIR.glob("*.parquet")}
# Load close once
print("Loading OHLCV...")
ohlcv = pd.read_hdf(str(OHLCV), key="data")
close = ohlcv["$close"].dropna()
if isinstance(close.index, pd.MultiIndex):
close = close.droplevel(-1)
close = close.astype(float).sort_index()
print(f"{len(close):,} bars")
# Build work list
work = []
for f in sorted(STRAT_DIR.glob("*.json")):
try:
d = json.loads(f.read_text())
except Exception:
continue
if d.get("reevaluation_status") == "verified_v2":
continue
names = d.get("factor_names", [])
code = d.get("code", "")
if not names or not code:
continue
paths = []
for n in names:
p = fmap.get(n) or fmap.get(n.replace("/", "_")[:150])
if p:
paths.append((n, p))
if len(paths) >= 2:
work.append((f, d, paths))
print(f"{len(work)} strategies to process")
if not work:
print("All done!")
sys.exit(0)
ok = skip = fail = 0
start = datetime.now()
for i, (f, data, factor_paths) in enumerate(work):
name = data.get("strategy_name", f.stem)[:45]
code = data.get("code", "")
# Load factor series
series = {}
for fn, fp in factor_paths:
try:
s = pd.read_parquet(fp).iloc[:, 0]
series[fn] = s
except Exception:
pass
if len(series) < 2:
skip += 1
continue
df = pd.DataFrame(series).sort_index()
if isinstance(df.index, pd.MultiIndex):
df = df.droplevel(-1)
try:
df_1m = df.reindex(close.index).ffill()
except Exception:
skip += 1
continue
valid = df_1m.notna().any(axis=1)
if valid.sum() < 1000:
skip += 1
continue
ca = close.loc[valid]
fa = df_1m.loc[valid]
# Execute strategy code
try:
with tempfile.TemporaryDirectory() as td:
tdp = Path(td)
fa.to_parquet(str(tdp / "factors.parquet"))
ca.to_pickle(str(tdp / "close.pkl"))
exec_script = (
"import pandas as pd, numpy as np\n"
"factors = pd.read_parquet('factors.parquet')\n"
"close = pd.read_pickle('close.pkl')\n"
"df = factors\n"
+ code +
"\nif 'signal' not in dir():\n"
" raise SystemExit(1)\n"
"pd.Series(signal).fillna(0).to_pickle('signal.pkl')\n"
)
(tdp / "run.py").write_text(exec_script)
r = subprocess.run(
["python", "run.py"],
capture_output=True, text=True, timeout=60, cwd=str(tdp),
)
if r.returncode != 0:
fail += 1
continue
sig = pd.read_pickle(tdp / "signal.pkl")
except Exception:
fail += 1
continue
try:
sig = sig.reindex(ca.index).ffill().fillna(0)
result = backtest_signal(ca, sig, txn_cost_bps=2.14)
except Exception:
fail += 1
continue
# Write back
data["reevaluation_status"] = "verified_v2"
data["sharpe_ratio"] = result.get("sharpe")
data["max_drawdown"] = result.get("max_drawdown")
data["win_rate"] = result.get("win_rate")
data["total_return"] = result.get("total_return")
data["summary"] = {
**data.get("summary", {}),
"sharpe": result.get("sharpe"),
"max_drawdown": result.get("max_drawdown"),
"win_rate": result.get("win_rate"),
"monthly_return_pct": result.get("monthly_return_pct"),
"real_n_trades": result.get("n_trades"),
"total_return": result.get("total_return"),
"annualized_return": result.get("annualized_return"),
"engine": "verified_v2",
"txn_cost_bps": 2.14,
}
f.write_text(json.dumps(data, indent=2, ensure_ascii=False))
ok += 1
elapsed = (datetime.now() - start).total_seconds()
rate = ok / elapsed * 60 if elapsed > 0 else 0
print(f" [{ok:4d}/{len(work)}] {rate:5.0f}/min {name:45s} "
f"S={result['sharpe']:6.1f} DD={result['max_drawdown']:7.2%} "
f"WR={result['win_rate']:5.1%} T={result['n_trades']:4d}")
elapsed = (datetime.now() - start).total_seconds()
print(f"\nDONE: ok={ok} skip={skip} fail={fail} in {elapsed:.0f}s")
@@ -1,13 +1,13 @@
""" """
Predix Full Data Factor Evaluator - Evaluate factors with FULL 1min data. NexQuant Full Data Factor Evaluator - Evaluate factors with FULL 1min data.
Evaluates factors using the complete intraday_pv.h5 dataset (2022-2026, ~2.26M rows) Evaluates factors using the complete intraday_pv.h5 dataset (2022-2026, ~2.26M rows)
instead of the debug dataset (2024 only, ~371K rows). instead of the debug dataset (2024 only, ~371K rows).
Usage: Usage:
python predix_full_eval.py --top 100 # Evaluate top 100 factors with full data python nexquant_full_eval.py --top 100 # Evaluate top 100 factors with full data
python predix_full_eval.py --all # Evaluate all factors python nexquant_full_eval.py --all # Evaluate all factors
python predix_full_eval.py --parallel 4 # 4 parallel workers python nexquant_full_eval.py --parallel 4 # 4 parallel workers
""" """
import json import json
@@ -271,7 +271,7 @@ def evaluate_factor_full(factor: FactorInfo, full_data: pd.DataFrame,
import tempfile import tempfile
import subprocess import subprocess
with tempfile.TemporaryDirectory(prefix="predix_full_") as tmp_dir: with tempfile.TemporaryDirectory(prefix="nexquant_full_") as tmp_dir:
ws = Path(tmp_dir) ws = Path(tmp_dir)
try: try:
@@ -628,7 +628,7 @@ def main(
) -> None: ) -> None:
"""Main entry point.""" """Main entry point."""
console.print(Panel( console.print(Panel(
"[bold cyan]Predix Full Data Factor Evaluator[/bold cyan]\n" "[bold cyan]NexQuant Full Data Factor Evaluator[/bold cyan]\n"
f"Using FULL 1min data: {FULL_DATA_FILE}", f"Using FULL 1min data: {FULL_DATA_FILE}",
border_style="cyan", border_style="cyan",
)) ))
@@ -679,7 +679,7 @@ if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Predix Full Data Factor Evaluator" description="NexQuant Full Data Factor Evaluator"
) )
parser.add_argument( parser.add_argument(
"--top", "-n", "--top", "-n",
@@ -7,13 +7,13 @@ each with real backtesting on OHLCV data.
Usage: Usage:
# Swing trading (96-bar forward returns) # Swing trading (96-bar forward returns)
python predix_gen_strategies_real_bt.py 10 python nexquant_gen_strategies_real_bt.py 10
# Daytrading with FTMO constraints (12-bar forward returns) # Daytrading with FTMO constraints (12-bar forward returns)
TRADING_STYLE=daytrading python predix_gen_strategies_real_bt.py 5 TRADING_STYLE=daytrading python nexquant_gen_strategies_real_bt.py 5
# With parallel workers (default: CPU count) # With parallel workers (default: CPU count)
TRADING_STYLE=daytrading WORKERS=4 python predix_gen_strategies_real_bt.py 20 TRADING_STYLE=daytrading WORKERS=4 python nexquant_gen_strategies_real_bt.py 20
""" """
import os, sys, json, time, math, random, logging, warnings, subprocess import os, sys, json, time, math, random, logging, warnings, subprocess
from pathlib import Path from pathlib import Path
@@ -42,9 +42,9 @@ except Exception:
# ============================================================================ # ============================================================================
# Configuration # Configuration
# ============================================================================ # ============================================================================
OHLCV_PATH = Path('/home/nico/Predix/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5') OHLCV_PATH = Path('/home/nico/NexQuant/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5')
FACTORS_DIR = Path('/home/nico/Predix/results/factors') FACTORS_DIR = Path('/home/nico/NexQuant/results/factors')
STRATEGIES_DIR = Path('/home/nico/Predix/results/strategies_new') STRATEGIES_DIR = Path('/home/nico/NexQuant/results/strategies_new')
STRATEGIES_DIR.mkdir(parents=True, exist_ok=True) STRATEGIES_DIR.mkdir(parents=True, exist_ok=True)
# Trading style # Trading style
@@ -632,7 +632,7 @@ def main(target_count=10):
# Generate PDF report # Generate PDF report
try: try:
from predix_strategy_report import StrategyPerformanceReporter from nexquant_strategy_report import StrategyPerformanceReporter
reporter = StrategyPerformanceReporter(strategy) reporter = StrategyPerformanceReporter(strategy)
reporter.generate_report() reporter.generate_report()
except: except:
@@ -1,16 +1,16 @@
""" """
Predix Parallel Runner - Run multiple factor experiments concurrently. NexQuant Parallel Runner - Run multiple factor experiments concurrently.
Spawns N subprocesses, each running `predix.py quant` with isolated config: Spawns N subprocesses, each running `nexquant.py quant` with isolated config:
- Separate log files (fin_quant_run1.log, fin_quant_run2.log, etc.) - Separate log files (fin_quant_run1.log, fin_quant_run2.log, etc.)
- Separate result directories (results/runs/run1/, results/runs/run2/, etc.) - Separate result directories (results/runs/run1/, results/runs/run2/, etc.)
- Separate workspace directories - Separate workspace directories
- API key distribution across multiple keys (round-robin) - API key distribution across multiple keys (round-robin)
Usage: Usage:
python predix_parallel.py --runs 5 --api-keys 2 python nexquant_parallel.py --runs 5 --api-keys 2
python predix_parallel.py --runs 3 --model openrouter python nexquant_parallel.py --runs 3 --model openrouter
python predix_parallel.py --runs 5 --model local --api-keys 1 python nexquant_parallel.py --runs 5 --model local --api-keys 1
""" """
import os import os
import signal import signal
@@ -188,7 +188,7 @@ class ParallelRunner:
def _build_command(self, run_state: RunState) -> list[str]: def _build_command(self, run_state: RunState) -> list[str]:
""" """
Build the subprocess command to run predix quant. Build the subprocess command to run nexquant quant.
Parameters Parameters
---------- ----------
@@ -202,7 +202,7 @@ class ParallelRunner:
""" """
cmd = [ cmd = [
sys.executable, # Use same Python interpreter sys.executable, # Use same Python interpreter
str(self.project_root / "predix.py"), str(self.project_root / "nexquant.py"),
"quant", "quant",
"--model", run_state.model, "--model", run_state.model,
"--run-id", str(run_state.run_id), "--run-id", str(run_state.run_id),
@@ -327,7 +327,7 @@ class ParallelRunner:
# Build summary table # Build summary table
table = Table( table = Table(
title="🔀 Predix Parallel Run Dashboard", title="🔀 NexQuant Parallel Run Dashboard",
show_header=True, show_header=True,
header_style="bold cyan", header_style="bold cyan",
expand=True, expand=True,
@@ -399,7 +399,7 @@ class ParallelRunner:
signal.signal(signal.SIGTERM, self._signal_handler) signal.signal(signal.SIGTERM, self._signal_handler)
console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]") console.print(f"\n[bold cyan]{'=' * 60}[/bold cyan]")
console.print("[bold cyan]🔀 Predix Parallel Runner[/bold cyan]") console.print("[bold cyan]🔀 NexQuant Parallel Runner[/bold cyan]")
console.print(f"[bold cyan]{'=' * 60}[/bold cyan]") console.print(f"[bold cyan]{'=' * 60}[/bold cyan]")
console.print(f" Runs: {self.num_runs}") console.print(f" Runs: {self.num_runs}")
console.print(f" API Keys: {self.num_api_keys} ({len(self.api_keys)} available)") console.print(f" API Keys: {self.num_api_keys} ({len(self.api_keys)} available)")
@@ -503,7 +503,7 @@ if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Predix Parallel Runner - Run multiple factor experiments concurrently", description="NexQuant Parallel Runner - Run multiple factor experiments concurrently",
) )
parser.add_argument( parser.add_argument(
"--runs", "-n", "--runs", "-n",
+467
View File
@@ -0,0 +1,467 @@
#!/usr/bin/env python
"""
Quick Daytrading Strategy Generator with CORRECT factor alignment.
Uses forward-fill to align daily factors to 1-min frequency,
then runs fast backtests without LLM calls.
Usage:
python nexquant_quick_daytrading.py 5
python nexquant_quick_daytrading.py 10
"""
import json, time, subprocess, tempfile # nosec
from pathlib import Path
import numpy as np
import pandas as pd
from rich.console import Console
console = Console()
STRATEGIES_DIR = Path('results/strategies_new')
STRATEGIES_DIR.mkdir(parents=True, exist_ok=True)
FACTOR_FILES = Path('results/factors')
VALUE_FILES = FACTOR_FILES / 'values'
OHLCV_PATH = Path('git_ignore_folder/factor_implementation_source_data/intraday_pv.h5')
# Best daytrading strategies (12-min horizon, optimized for FTMO)
DAYTRADING_COMBOS = [
{
'name': 'MomentumDivergence12min',
'factors': ['daily_close_return_96', 'daily_session_momentum_divergence_1d'],
'code': '''mom = factors['daily_close_return_96']
div = factors['daily_session_momentum_divergence_1d']
w = 20
mom_z = (mom - mom.rolling(w).mean()) / (mom.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
composite = (mom_z - div_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.3] = 1
signal[composite < -0.3] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'LondonSessionScalp',
'factors': ['london_mom', 'daily_session_momentum_divergence_1d'],
'code': '''mom = factors['london_mom']
div = factors['daily_session_momentum_divergence_1d']
w = 15
mom_z = (mom - mom.rolling(w).mean()) / (mom.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
composite = (mom_z - div_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.25] = 1
signal[composite < -0.25] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'TrendReversionScalp',
'factors': ['daily_ols_slope_96', 'daily_session_momentum_divergence_1d', 'DailyTrendStrength_Raw'],
'code': '''slope = factors['daily_ols_slope_96']
div = factors['daily_session_momentum_divergence_1d']
trend = factors['DailyTrendStrength_Raw']
w = 20
slope_z = (slope - slope.rolling(w).mean()) / (slope.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
trend_z = (trend - trend.rolling(w).mean()) / (trend.rolling(w).std() + 1e-8)
composite = (0.5 * slope_z - 0.3 * div_z + 0.2 * trend_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.3] = 1
signal[composite < -0.3] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'VolAdjMomentum12',
'factors': ['daily_ret_vol_adj_1d', 'daily_session_momentum_divergence_1d', 'DCP'],
'code': '''vol = factors['daily_ret_vol_adj_1d']
div = factors['daily_session_momentum_divergence_1d']
dcp = factors['DCP']
w = 20
vol_z = (vol - vol.rolling(w).mean()) / (vol.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
dcp_z = (dcp - dcp.rolling(w).mean()) / (dcp.rolling(w).std() + 1e-8)
composite = (0.5 * vol_z - 0.3 * div_z + 0.2 * dcp_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.35] = 1
signal[composite < -0.35] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'SessionMeanReversion',
'factors': ['session_momentum_diff', 'daily_norm_body', 'daily_c2c_return'],
'code': '''session = factors['session_momentum_diff']
body = factors['daily_norm_body']
c2c = factors['daily_c2c_return']
w = 15
sess_z = (session - session.rolling(w).mean()) / (session.rolling(w).std() + 1e-8)
body_z = (body - body.rolling(w).mean()) / (body.rolling(w).std() + 1e-8)
c2c_z = (c2c - c2c.rolling(w).mean()) / (c2c.rolling(w).std() + 1e-8)
composite = (0.5 * sess_z + 0.3 * body_z + 0.2 * c2c_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.4] = 1
signal[composite < -0.4] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'MomentumContinuation',
'factors': ['daily_mom', 'daily_ret_1d', 'momentum_1d'],
'code': '''mom = factors['daily_mom']
ret = factors['daily_ret_1d']
mom2 = factors['momentum_1d']
w = 12
mom_z = (mom - mom.rolling(w).mean()) / (mom.rolling(w).std() + 1e-8)
ret_z = (ret - ret.rolling(w).mean()) / (ret.rolling(w).std() + 1e-8)
mom2_z = (mom2 - mom2.rolling(w).mean()) / (mom2.rolling(w).std() + 1e-8)
composite = (0.4 * mom_z + 0.3 * ret_z + 0.3 * mom2_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.2] = 1
signal[composite < -0.2] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'HighFreqScalper',
'factors': ['daily_close_return_96', 'DCP', 'london_mom'],
'code': '''close_ret = factors['daily_close_return_96']
dcp = factors['DCP']
london = factors['london_mom']
w = 10
cr_z = (close_ret - close_ret.rolling(w).mean()) / (close_ret.rolling(w).std() + 1e-8)
dcp_z = (dcp - dcp.rolling(w).mean()) / (dcp.rolling(w).std() + 1e-8)
lon_z = (london - london.rolling(w).mean()) / (london.rolling(w).std() + 1e-8)
composite = (0.4 * cr_z + 0.3 * dcp_z + 0.3 * lon_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.25] = 1
signal[composite < -0.25] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'AdaptiveMomentumMR',
'factors': ['daily_close_return_96', 'daily_session_momentum_divergence_1d', 'daily_ols_slope_96'],
'code': '''mom = factors['daily_close_return_96']
div = factors['daily_session_momentum_divergence_1d']
slope = factors['daily_ols_slope_96']
w = 20
mom_z = (mom - mom.rolling(w).mean()) / (mom.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
slope_z = (slope - slope.rolling(w).mean()) / (slope.rolling(w).std() + 1e-8)
# Regime detection: high momentum = trend, low = mean reversion
regime = (mom_z.abs() > 1.0).astype(float)
composite = (regime * mom_z + (1 - regime) * (-div_z) + 0.3 * slope_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.4] = 1
signal[composite < -0.4] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'TrendPullbackScalp',
'factors': ['daily_close_return_96', 'daily_session_momentum_divergence_1d', 'daily_norm_body'],
'code': '''mom = factors['daily_close_return_96']
div = factors['daily_session_momentum_divergence_1d']
body = factors['daily_norm_body']
w = 15
mom_z = (mom - mom.rolling(w).mean()) / (mom.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
body_z = (body - body.rolling(w).mean()) / (body.rolling(w).std() + 1e-8)
# Enter on pullbacks (divergence against trend)
composite = (mom_z - 0.5 * div_z * mom_z.sign() + 0.2 * body_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.35] = 1
signal[composite < -0.35] = -1
signal = signal.fillna(0).astype(int)''',
},
{
'name': 'IntradayMomentumBlend',
'factors': ['daily_close_return_96', 'london_mom', 'daily_session_momentum_divergence_1d', 'DCP'],
'code': '''mom = factors['daily_close_return_96']
lon = factors['london_mom']
div = factors['daily_session_momentum_divergence_1d']
dcp = factors['DCP']
w = 20
mom_z = (mom - mom.rolling(w).mean()) / (mom.rolling(w).std() + 1e-8)
lon_z = (lon - lon.rolling(w).mean()) / (lon.rolling(w).std() + 1e-8)
div_z = (div - div.rolling(w).mean()) / (div.rolling(w).std() + 1e-8)
dcp_z = (dcp - dcp.rolling(w).mean()) / (dcp.rolling(w).std() + 1e-8)
composite = (0.3 * mom_z + 0.3 * lon_z - 0.2 * div_z + 0.2 * dcp_z).fillna(0)
signal = pd.Series(0, index=close.index, name='signal')
signal[composite > 0.3] = 1
signal[composite < -0.3] = -1
signal = signal.fillna(0).astype(int)''',
},
]
def load_factor_series(name):
"""Load factor parquet and return as Series with correct index."""
safe = name.replace('/','_').replace('\\','_')[:150]
pf = VALUE_FILES / f"{safe}.parquet"
if not pf.exists():
return None
df = pd.read_parquet(str(pf))
# Extract EURUSD
if df.index.names == ['datetime', 'instrument']:
df_reset = df.reset_index()
if 'instrument' in df_reset.columns:
df_eur = df_reset[df_reset['instrument'] == 'EURUSD'].copy()
df_eur = df_eur.set_index('datetime')
series = df_eur.iloc[:, -1] # Last column is the factor value
series.name = name
return series
# If single index, just return first column
series = df.iloc[:, 0]
series.name = name
return series
def main(n_strategies=5):
console.print("[bold cyan]🎯 Daytrading Strategy Generator (Quick Mode)[/bold cyan]\n")
console.print(" Style: 12-minute forward returns")
console.print(" Target: FTMO compliant (IC>0.02, Sharpe>0.5, Trades>20, DD>-10%)\n")
# Load OHLCV data
if not OHLCV_PATH.exists():
console.print(f"[red]✗ OHLCV data not found: {OHLCV_PATH}[/red]")
return
ohlcv = pd.read_hdf(str(OHLCV_PATH), key='data')
# Extract close prices with datetime-only index (not MultiIndex)
if '$close' in ohlcv.columns:
close = ohlcv['$close'].dropna()
elif 'close' in ohlcv.columns:
close = ohlcv['close'].dropna()
else:
close = ohlcv.select_dtypes(include=[np.number]).iloc[:, 0].dropna()
# Extract datetime from MultiIndex if present
if isinstance(close.index, pd.MultiIndex):
close_dt_idx = close.index.get_level_values('datetime')
close_series = pd.Series(close.values, index=close_dt_idx, name='close')
else:
close_series = close
close_series = close_series.dropna()
console.print(f"[green]✓[/green] Loaded {len(close_series):,} OHLCV bars")
# Load all factor series and align to close index
all_factor_series = {}
for combo in DAYTRADING_COMBOS:
for factor_name in combo['factors']:
if factor_name in all_factor_series:
continue
series = load_factor_series(factor_name)
if series is not None:
# Forward fill to match close frequency
series_ff = series.reindex(close_series.index).ffill()
all_factor_series[factor_name] = series_ff
# Create factors DataFrame
df_factors = pd.DataFrame(all_factor_series)
df_factors = df_factors.dropna(how='all')
console.print(f"[green]✓[/green] Loaded {len(df_factors.columns)} factor series")
console.print(f"[green]✓[/green] Aligned to {len(df_factors):,} bars\n")
accepted = []
for i, combo in enumerate(DAYTRADING_COMBOS[:n_strategies]):
console.print(f"[{i+1}/{n_strategies}] Testing {combo['name']}...")
# Build factor dataframe
valid_factors = [f for f in combo['factors'] if f in df_factors.columns]
if len(valid_factors) < 2:
console.print(f" ✗ Not enough valid factors")
continue
strat_factors = df_factors[valid_factors].dropna()
if len(strat_factors) < 1000:
console.print(f" ✗ Not enough data: {len(strat_factors)} bars")
continue
# Build backtest script
forward_bars = 12
strategy_code = combo['code']
script = f"""
import pandas as pd
import numpy as np
import json
close = pd.read_pickle('close.pkl') # nosec
factors = pd.read_pickle('factors.pkl') # nosec
# Execute strategy
try:
{chr(10).join(' ' + l for l in strategy_code.split(chr(10)))}
except Exception as e:
print(f"ERROR: {{e}}")
exit(1)
if 'signal' not in dir():
print("ERROR: No signal generated")
exit(1)
signal = signal.fillna(0)
# Align
common_idx = close.index.intersection(signal.index)
close = close.loc[common_idx]
signal = signal.loc[common_idx]
# Forward returns (12-min horizon for daytrading)
FORWARD_BARS = {forward_bars}
returns_fwd = close.pct_change(FORWARD_BARS).shift(-FORWARD_BARS)
signal_aligned = signal.loc[returns_fwd.dropna().index]
fwd_returns = returns_fwd.loc[signal_aligned.index]
if len(signal_aligned) < 100 or len(fwd_returns) < 100:
print("ERROR: Not enough data")
exit(1)
# Metrics
ic = signal_aligned.corr(fwd_returns)
strategy_returns = signal_aligned * fwd_returns
sharpe = strategy_returns.mean() / strategy_returns.std() * np.sqrt(252 * 1440 / {forward_bars}) if strategy_returns.std() > 0 else 0
cum = (1 + strategy_returns).cumprod()
running_max = cum.expanding().max()
drawdown = (cum - running_max) / running_max.replace(0, np.nan)
max_dd = drawdown.min() if len(drawdown) > 0 else 0
win_rate = (strategy_returns > 0).sum() / len(strategy_returns) if len(strategy_returns) > 0 else 0
n_trades = int((signal_aligned != signal_aligned.shift(1)).sum())
total_return = cum.iloc[-1] - 1
n_bars = len(strategy_returns)
n_months = n_bars / (252 * 1440 / {forward_bars} / 12) if n_bars > 0 else 1
monthly_return = (1 + total_return) ** (1 / n_months) - 1 if n_months > 0 and (1 + total_return) > 0 else total_return
result = {{
"status": "success",
"sharpe": float(sharpe),
"max_drawdown": float(max_dd) if not np.isnan(max_dd) else -0.20,
"win_rate": float(win_rate),
"ic": float(ic) if not np.isnan(ic) else 0,
"n_trades": n_trades,
"total_return": float(total_return),
"monthly_return_pct": float(monthly_return * 100),
"n_bars": int(n_bars),
"n_months": float(n_months),
"signal_long": int((signal_aligned == 1).sum()),
"signal_short": int((signal_aligned == -1).sum()),
"signal_neutral": int((signal_aligned == 0).sum()),
}}
print(json.dumps(result))
"""
# Run backtest
import tempfile
with tempfile.TemporaryDirectory() as td:
tdp = Path(td)
strat_close = close_series.loc[strat_factors.index]
strat_close.to_pickle(str(tdp / 'close.pkl')) # nosec
strat_factors.to_pickle(str(tdp / 'factors.pkl')) # nosec
script_path = tdp / 'run.py'
script_path.write_text(script)
try:
result_proc = subprocess.run( # nosec B603
[sys.executable, str(script_path)],
capture_output=True, text=True, timeout=60,
cwd=str(tdp)
)
if result_proc.returncode != 0:
console.print(f" ✗ Failed: {result_proc.stderr[:200]}")
continue
result = None
for line in result_proc.stdout.strip().split('\n'):
try:
result = json.loads(line)
break
except:
continue
if not result or result.get('status') != 'success':
console.print(f" ✗ Invalid result")
continue
except subprocess.TimeoutExpired: # nosec
console.print(f" ✗ Timeout")
continue
except Exception as e:
console.print(f" ✗ Error: {e}")
continue
ic = result.get('ic', 0)
sharpe = result.get('sharpe', 0)
trades = result.get('n_trades', 0)
dd = result.get('max_drawdown', 0)
# FTMO criteria
if abs(ic) > 0.02 and sharpe > 0.5 and trades > 20 and dd > -0.10:
strategy = {
'strategy_name': combo['name'],
'factor_names': combo['factors'],
'description': f"Daytrading strategy combining {', '.join(combo['factors'])}",
'code': combo['code'],
'real_backtest': result,
'metrics': result,
'summary': {
'sharpe': sharpe,
'max_drawdown': dd,
'win_rate': result.get('win_rate', 0),
'monthly_return_pct': result.get('monthly_return_pct', 0),
'real_ic': ic,
'real_n_trades': trades,
'forward_bars': 12,
'trading_style': 'daytrading',
}
}
fname = f"{int(time.time())}_{combo['name']}.json"
with open(STRATEGIES_DIR / fname, 'w') as f:
json.dump(strategy, f, indent=2, ensure_ascii=False)
accepted.append(strategy)
console.print(f" ✓ [green]ACCEPT[/green]: IC={ic:.4f}, Sharpe={sharpe:.2f}, Trades={trades}, DD={dd:.1%}")
else:
console.print(f" ✗ [red]REJECT[/red]: IC={ic:.4f}, Sharpe={sharpe:.2f}, Trades={trades}, DD={dd:.1%}")
console.print(f"\n[bold green]✓ {len(accepted)}/{n_strategies} strategies accepted[/bold green]\n")
if accepted:
console.print("[bold]Results:[/bold]")
for s in accepted:
bt = s['real_backtest']
console.print(f"{s['strategy_name']:30s} IC={bt['ic']:.4f} Sharpe={bt['sharpe']:.2f} "
f"Monthly={bt['monthly_return_pct']:.2f}% Trades={bt['n_trades']}")
if __name__ == '__main__':
import sys
n = int(sys.argv[1]) if len(sys.argv) > 1 else 5
main(n)
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python
"""One strategy runner — standalone, called from parent script."""
import json, sys, pandas as pd, subprocess, tempfile, numpy as np
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from rdagent.components.backtesting.vbt_backtest import backtest_signal
if len(sys.argv) < 2:
print("Usage: python nexquant_rebacktest_one.py <strategy_json_path>")
sys.exit(1)
strat_path = Path(sys.argv[1])
data = json.loads(strat_path.read_text())
OHLCV = Path("git_ignore_folder/factor_implementation_source_data/intraday_pv.h5")
FACTORS_DIR = Path("results/factors/values")
fmap = {p.stem: str(p) for p in FACTORS_DIR.glob("*.parquet")}
names = data.get("factor_names", [])
code = data.get("code", "")
name = data.get("strategy_name", strat_path.stem)
if not names or not code:
print(json.dumps({"status": "skipped", "reason": "no factors/code"}))
sys.exit(0)
# Load close
ohlcv = pd.read_hdf(str(OHLCV), key="data")
close = ohlcv["$close"].dropna()
if isinstance(close.index, pd.MultiIndex):
close = close.droplevel(-1)
close = close.astype(float).sort_index()
# Load factors
series = {}
for fn in names:
fp = fmap.get(fn) or fmap.get(fn.replace("/", "_")[:150])
if fp:
try:
s = pd.read_parquet(fp).iloc[:, 0]
series[fn] = s
except Exception:
pass
if len(series) < 2:
print(json.dumps({"status": "skipped", "reason": f"only {len(series)} factors loaded"}))
sys.exit(0)
df = pd.DataFrame(series).sort_index()
if isinstance(df.index, pd.MultiIndex):
df = df.droplevel(-1)
df_1m = df.reindex(close.index).ffill()
valid = df_1m.notna().any(axis=1)
if valid.sum() < 1000:
print(json.dumps({"status": "skipped", "reason": f"only {valid.sum()} valid bars"}))
sys.exit(0)
ca = close.loc[valid]
fa = df_1m.loc[valid]
# Execute
try:
with tempfile.TemporaryDirectory() as td:
tdp = Path(td)
fa.to_parquet(str(tdp / "factors.parquet"))
ca.to_pickle(str(tdp / "close.pkl"))
exec_script = (
"import sys, os\n"
"sys.stdout = open(os.devnull, 'w')\n"
"sys.stderr = open(os.devnull, 'w')\n"
"import pandas as pd, numpy as np\n"
"factors = pd.read_parquet('factors.parquet')\n"
"close = pd.read_pickle('close.pkl')\n"
"df = factors\n"
+ code +
"\nif 'signal' not in dir():\n"
" raise SystemExit(1)\n"
"pd.Series(signal).fillna(0).to_pickle('signal.pkl')\n"
)
(tdp / "run.py").write_text(exec_script)
r = subprocess.run(
["python", "run.py"],
capture_output=True, text=True, timeout=60, cwd=str(tdp),
stdin=subprocess.DEVNULL,
)
if r.returncode != 0:
print(json.dumps({"status": "code_failed", "stderr": r.stderr[:500]}))
sys.exit(1)
sig = pd.read_pickle(tdp / "signal.pkl")
except Exception as e:
print(json.dumps({"status": "code_failed", "error": str(e)[:500]}))
sys.exit(1)
sig = sig.reindex(ca.index).ffill().fillna(0)
result = backtest_signal(ca, sig, txn_cost_bps=2.14)
# Return result as JSON
output = {
"status": "ok",
"sharpe": result.get("sharpe"),
"max_drawdown": result.get("max_drawdown"),
"win_rate": result.get("win_rate"),
"n_trades": result.get("n_trades"),
"total_return": result.get("total_return"),
"monthly_return_pct": result.get("monthly_return_pct"),
"annualized_return": result.get("annualized_return"),
}
print(json.dumps(output))
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env python
"""Parent orchestrator: calls nexquant_rebacktest_one.py for each strategy."""
import json, subprocess, sys
from pathlib import Path
from datetime import datetime
STRAT_DIR = Path("results/strategies_new")
# Build work list
work = []
for f in sorted(STRAT_DIR.glob("*.json")):
if "verified_v2" in f.read_text():
continue
try:
d = json.loads(f.read_text())
except Exception:
continue
if d.get("factor_names") and d.get("code"):
work.append(f)
print(f"{len(work)} strategies to re-backtest", flush=True)
ok = skip = fail = 0
start = datetime.now()
for i, f in enumerate(work):
name = f.stem[:45]
print(f"[{i+1}/{len(work)}] {name} ...", end=" ", flush=True)
try:
r = subprocess.run(
["timeout", "-s", "KILL", "90", "python", "scripts/nexquant_rebacktest_one.py", str(f)],
capture_output=True, text=True, timeout=120,
stdin=subprocess.DEVNULL,
)
result = json.loads(r.stdout.strip() or "{}")
except subprocess.TimeoutExpired:
print("TIMEOUT", flush=True)
fail += 1
continue
except Exception as e:
print(f"ERROR: {e}", flush=True)
fail += 1
continue
if result.get("status") == "ok":
data = json.loads(f.read_text())
data["reevaluation_status"] = "verified_v2"
data["sharpe_ratio"] = result.get("sharpe")
data["max_drawdown"] = result.get("max_drawdown")
data["win_rate"] = result.get("win_rate")
data["total_return"] = result.get("total_return")
data["summary"] = {
**data.get("summary", {}),
"sharpe": result.get("sharpe"),
"max_drawdown": result.get("max_drawdown"),
"win_rate": result.get("win_rate"),
"monthly_return_pct": result.get("monthly_return_pct"),
"real_n_trades": result.get("n_trades"),
"total_return": result.get("total_return"),
"annualized_return": result.get("annualized_return"),
"engine": "verified_v2",
"txn_cost_bps": 2.14,
}
f.write_text(json.dumps(data, indent=2, ensure_ascii=False))
ok += 1
print(f"S={result['sharpe']:.1f} DD={result['max_drawdown']:.2%} WR={result['win_rate']:.1%} T={result['n_trades']}", flush=True)
elif result.get("status") == "skipped":
skip += 1
print(f"SKIP: {result.get('reason', '?')}", flush=True)
else:
fail += 1
print(f"FAIL: {result.get('stderr', result.get('error', '?'))[:100]}", flush=True)
elapsed = (datetime.now() - start).total_seconds()
print(f"\nDONE: ok={ok} skip={skip} fail={fail} in {elapsed:.0f}s", flush=True)
@@ -116,8 +116,8 @@ except Exception as e:
"n_short":int((sig==-1).sum()), "n_neutral":int((sig==0).sum())} "n_short":int((sig==-1).sum()), "n_neutral":int((sig==0).sum())}
def main(count=None): def main(count=None):
sdir = Path('/home/nico/Predix/results/strategies') sdir = Path('/home/nico/NexQuant/results/strategies')
vdir = Path('/home/nico/Predix/results/factors/values') vdir = Path('/home/nico/NexQuant/results/factors/values')
files = [] files = []
for f in sorted(sdir.glob('*.json'), reverse=True): for f in sorted(sdir.glob('*.json'), reverse=True):
@@ -13,9 +13,9 @@ For every strategy JSON in results/strategies_new (or a user-supplied dir):
Does NOT mutate the strategy JSON files read-only comparison. Does NOT mutate the strategy JSON files read-only comparison.
Usage: Usage:
python scripts/predix_rebacktest_unified.py # all strategies python scripts/nexquant_rebacktest_unified.py # all strategies
python scripts/predix_rebacktest_unified.py 50 # first 50 python scripts/nexquant_rebacktest_unified.py 50 # first 50
python scripts/predix_rebacktest_unified.py 50 --csv report.csv python scripts/nexquant_rebacktest_unified.py 50 --csv report.csv
""" """
from __future__ import annotations from __future__ import annotations
@@ -38,9 +38,9 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeEl
sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from rdagent.components.backtesting.vbt_backtest import backtest_signal_ftmo # noqa: E402 from rdagent.components.backtesting.vbt_backtest import backtest_signal_ftmo # noqa: E402
OHLCV_PATH = Path("/home/nico/Predix/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5") OHLCV_PATH = Path("/home/nico/NexQuant/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5")
FACTORS_VALUES_DIR = Path("/home/nico/Predix/results/factors/values") FACTORS_VALUES_DIR = Path("/home/nico/NexQuant/results/factors/values")
STRATEGIES_DIR = Path("/home/nico/Predix/results/strategies_new") STRATEGIES_DIR = Path("/home/nico/NexQuant/results/strategies_new")
# ── Logging setup: everything printed goes to log file + stdout ─────────────── # ── Logging setup: everything printed goes to log file + stdout ───────────────
_LOG_DIR = Path(__file__).resolve().parent.parent / "git_ignore_folder" / "logs" _LOG_DIR = Path(__file__).resolve().parent.parent / "git_ignore_folder" / "logs"
@@ -1,13 +1,13 @@
""" """
Predix Simple Factor Evaluator - Direct IC/Sharpe computation. NexQuant Simple Factor Evaluator - Direct IC/Sharpe computation.
Evaluates existing factor results by computing IC and Sharpe directly Evaluates existing factor results by computing IC and Sharpe directly
from factor values and forward returns, without Qlib infrastructure. from factor values and forward returns, without Qlib infrastructure.
Usage: Usage:
python predix_simple_eval.py --top 100 # Evaluate top 100 factors python nexquant_simple_eval.py --top 100 # Evaluate top 100 factors
python predix_simple_eval.py --all # Evaluate all python nexquant_simple_eval.py --all # Evaluate all
python predix_simple_eval.py --parallel 4 # 4 parallel workers python nexquant_simple_eval.py --parallel 4 # 4 parallel workers
""" """
import json import json
@@ -421,7 +421,7 @@ def main(
) -> None: ) -> None:
"""Main entry point.""" """Main entry point."""
console.print(Panel( console.print(Panel(
"[bold cyan]Predix Simple Factor Evaluator[/bold cyan]\n" "[bold cyan]NexQuant Simple Factor Evaluator[/bold cyan]\n"
f"Scanning workspaces for generated factors...", f"Scanning workspaces for generated factors...",
border_style="cyan", border_style="cyan",
)) ))
@@ -467,7 +467,7 @@ if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Predix Simple Factor Evaluator - Direct IC/Sharpe computation" description="NexQuant Simple Factor Evaluator - Direct IC/Sharpe computation"
) )
parser.add_argument( parser.add_argument(
"--top", "-n", "--top", "-n",
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Strategy Performance Report Generator for Predix. Strategy Performance Report Generator for NexQuant.
Generates detailed PDF reports with charts for each accepted strategy. Generates detailed PDF reports with charts for each accepted strategy.
@@ -11,8 +11,8 @@ Features:
- Full metrics table and strategy code - Full metrics table and strategy code
Usage: Usage:
python predix_strategy_report.py # All strategies python nexquant_strategy_report.py # All strategies
python predix_strategy_report.py results/strategies_new/123.json # Single strategy python nexquant_strategy_report.py results/strategies_new/123.json # Single strategy
""" """
import os, sys, json, warnings import os, sys, json, warnings
from pathlib import Path from pathlib import Path
@@ -39,8 +39,8 @@ from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
warnings.filterwarnings('ignore') warnings.filterwarnings('ignore')
# Config # Config
OHLCV_PATH = Path('/home/nico/Predix/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5') OHLCV_PATH = Path('/home/nico/NexQuant/git_ignore_folder/factor_implementation_source_data/intraday_pv.h5')
REPORTS_DIR = Path('/home/nico/Predix/results/strategy_reports') REPORTS_DIR = Path('/home/nico/NexQuant/results/strategy_reports')
REPORTS_DIR.mkdir(parents=True, exist_ok=True) REPORTS_DIR.mkdir(parents=True, exist_ok=True)
# Colors # Colors
@@ -226,7 +226,7 @@ class StrategyPerformanceReporter:
def _gen_pdf_report(self, pdf_path): def _gen_pdf_report(self, pdf_path):
doc = SimpleDocTemplate(str(pdf_path), pagesize=A4, doc = SimpleDocTemplate(str(pdf_path), pagesize=A4,
title=f"Predix: {self.name}", author="Predix AI", title=f"NexQuant: {self.name}", author="NexQuant AI",
leftMargin=2*cm, rightMargin=2*cm, topMargin=2*cm, bottomMargin=2*cm) leftMargin=2*cm, rightMargin=2*cm, topMargin=2*cm, bottomMargin=2*cm)
styles = getSampleStyleSheet() styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='PTitle', fontName='Helvetica-Bold', fontSize=22, leading=26, alignment=TA_CENTER, textColor=colors.HexColor('#1A237E'))) styles.add(ParagraphStyle(name='PTitle', fontName='Helvetica-Bold', fontSize=22, leading=26, alignment=TA_CENTER, textColor=colors.HexColor('#1A237E')))
@@ -324,7 +324,7 @@ def generate_report_for_strategy(path: str) -> dict:
def generate_all_reports(): def generate_all_reports():
d = Path('/home/nico/Predix/results/strategies_new') d = Path('/home/nico/NexQuant/results/strategies_new')
if not d.exists(): print("No strategies."); return if not d.exists(): print("No strategies."); return
for jf in sorted(d.glob('*.json')): for jf in sorted(d.glob('*.json')):
try: try:
+1 -1
View File
@@ -14,7 +14,7 @@ FTMO 100k rules enforced:
Out-of-sample window: 2024-01-01 onwards (never seen during factor research). Out-of-sample window: 2024-01-01 onwards (never seen during factor research).
Usage: Usage:
conda activate predix conda activate nexquant
python scripts/realistic_backtest_all.py python scripts/realistic_backtest_all.py
python scripts/realistic_backtest_all.py --target-monthly 4.0 --min-trades 50 python scripts/realistic_backtest_all.py --target-monthly 4.0 --min-trades 50
python scripts/realistic_backtest_all.py --workers 8 python scripts/realistic_backtest_all.py --workers 8
+2 -2
View File
@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Run all Predix integration tests # Run all NexQuant integration tests
# Usage: # Usage:
# ./scripts/run_all_tests.sh # Full test suite # ./scripts/run_all_tests.sh # Full test suite
# ./scripts/run_all_tests.sh --quick # Skip slow tests # ./scripts/run_all_tests.sh --quick # Skip slow tests
@@ -12,7 +12,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
echo "=========================================" echo "========================================="
echo "Predix Integration Test Suite" echo "NexQuant Integration Test Suite"
echo "=========================================" echo "========================================="
echo "Project: $PROJECT_ROOT" echo "Project: $PROJECT_ROOT"
echo "Date: $(date '+%Y-%m-%d %H:%M:%S')" echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
+5 -5
View File
@@ -4,11 +4,11 @@
# Restarts automatically on crash, generates strategies continuously. # Restarts automatically on crash, generates strategies continuously.
# ============================================================================ # ============================================================================
SCRIPT_DIR="/home/nico/Predix" SCRIPT_DIR="/home/nico/NexQuant"
GENERATOR="python ${SCRIPT_DIR}/predix_smart_strategy_gen.py" GENERATOR="python ${SCRIPT_DIR}/nexquant_smart_strategy_gen.py"
TARGET_COUNT=3 TARGET_COUNT=3
LOGFILE="${SCRIPT_DIR}/results/logs/generator_loop.log" LOGFILE="${SCRIPT_DIR}/results/logs/generator_loop.log"
PIDFILE="/tmp/predix_loop.pid" PIDFILE="/tmp/nexquant_loop.pid"
echo $$ > "$PIDFILE" echo $$ > "$PIDFILE"
mkdir -p "${SCRIPT_DIR}/results/logs" mkdir -p "${SCRIPT_DIR}/results/logs"
@@ -19,7 +19,7 @@ log() {
cleanup() { cleanup() {
log "Received termination signal. Cleaning up..." log "Received termination signal. Cleaning up..."
pkill -f "predix_smart_strategy_gen.py" 2>/dev/null pkill -f "nexquant_smart_strategy_gen.py" 2>/dev/null
rm -f "$PIDFILE" rm -f "$PIDFILE"
log "Cleanup complete. Exiting." log "Cleanup complete. Exiting."
exit 0 exit 0
@@ -53,7 +53,7 @@ while true; do
log "📁 Existing strategies: ${STRAT_COUNT}" log "📁 Existing strategies: ${STRAT_COUNT}"
# Kill any stale processes # Kill any stale processes
pkill -9 -f "predix_smart_strategy_gen.py" 2>/dev/null pkill -9 -f "nexquant_smart_strategy_gen.py" 2>/dev/null
sleep 2 sleep 2
# Start generator # Start generator
+6 -6
View File
@@ -4,13 +4,13 @@
# Checks every 20min: is the generator running? If not, (re)start it. # Checks every 20min: is the generator running? If not, (re)start it.
# ============================================================================ # ============================================================================
SCRIPT_DIR="/home/nico/Predix" SCRIPT_DIR="/home/nico/NexQuant"
GENERATOR="python ${SCRIPT_DIR}/predix_smart_strategy_gen.py" GENERATOR="python ${SCRIPT_DIR}/nexquant_smart_strategy_gen.py"
TARGET_COUNT=3 TARGET_COUNT=3
LOGFILE="${SCRIPT_DIR}/results/logs/watchdog.log" LOGFILE="${SCRIPT_DIR}/results/logs/watchdog.log"
LOCKFILE="/tmp/predix_generator.lock" LOCKFILE="/tmp/nexquant_generator.lock"
MAX_ATTEMPTS=50 # Stop after this many attempts MAX_ATTEMPTS=50 # Stop after this many attempts
PIDFILE="/tmp/predix_generator_attempt.pid" PIDFILE="/tmp/nexquant_generator_attempt.pid"
mkdir -p "${SCRIPT_DIR}/results/logs" mkdir -p "${SCRIPT_DIR}/results/logs"
@@ -51,7 +51,7 @@ check_progress() {
# Kill any existing generator processes # Kill any existing generator processes
cleanup() { cleanup() {
pkill -9 -f "predix_smart_strategy_gen.py" 2>/dev/null pkill -9 -f "nexquant_smart_strategy_gen.py" 2>/dev/null
rm -f "$LOCKFILE" rm -f "$LOCKFILE"
log "Cleaned up old processes" log "Cleaned up old processes"
} }
@@ -63,7 +63,7 @@ if [ "$(get_attempt_count)" -ge "$MAX_ATTEMPTS" ]; then
fi fi
# Check if generator is running # Check if generator is running
if pgrep -f "predix_smart_strategy_gen.py" > /dev/null 2>&1; then if pgrep -f "nexquant_smart_strategy_gen.py" > /dev/null 2>&1; then
# Check if it's making progress # Check if it's making progress
if check_progress; then if check_progress; then
log "Generator is running and making progress. Exiting." log "Generator is running and making progress. Exiting."
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -26,7 +26,7 @@ Die Pakete sind in `requirements.txt` enthalten.
### Alle Tests ausführen ### Alle Tests ausführen
```bash ```bash
cd /home/nico/Predix cd /home/nico/NexQuant
pytest test/backtesting/ pytest test/backtesting/
``` ```
@@ -226,8 +226,8 @@ Für GitHub Actions oder andere CI/CD-Systeme:
```bash ```bash
# Stelle sicher dass du im Projekt-Verzeichnis bist # Stelle sicher dass du im Projekt-Verzeichnis bist
cd /home/nico/Predix cd /home/nico/NexQuant
export PYTHONPATH=/home/nico/Predix:$PYTHONPATH export PYTHONPATH=/home/nico/NexQuant:$PYTHONPATH
pytest test/backtesting/ pytest test/backtesting/
``` ```
+1 -1
View File
@@ -1 +1 @@
"""Predix Backtesting Test Package""" """NexQuant Backtesting Test Package"""
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Predix Backtesting Test Fixtures NexQuant Backtesting Test Fixtures
Wiederverwendbare Test-Daten und Fixtures für alle Backtesting-Tests Wiederverwendbare Test-Daten und Fixtures für alle Backtesting-Tests
""" """
import pytest import pytest
+29 -29
View File
@@ -25,8 +25,8 @@ def _make_ohlcv(n: int = 600, freq: str = "1min") -> pd.DataFrame:
}, index=idx) }, index=idx)
def _make_predix_hdf5(tmp_path: Path, n: int = 300) -> Path: def _make_nexquant_hdf5(tmp_path: Path, n: int = 300) -> Path:
"""Write a minimal Predix-format HDF5 file and return its path.""" """Write a minimal NexQuant-format HDF5 file and return its path."""
idx = pd.MultiIndex.from_arrays( idx = pd.MultiIndex.from_arrays(
[pd.date_range("2024-01-01", periods=n, freq="1min"), ["EURUSD"] * n], [pd.date_range("2024-01-01", periods=n, freq="1min"), ["EURUSD"] * n],
names=["datetime", "instrument"], names=["datetime", "instrument"],
@@ -63,12 +63,12 @@ def _make_mock_adapter():
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Unit tests: _ohlcv_from_predix # Unit tests: _ohlcv_from_nexquant
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class TestOhlcvConversion: class TestOhlcvConversion:
def test_renames_dollar_columns(self): def test_renames_dollar_columns(self):
from rdagent.components.coder.kronos_adapter import _ohlcv_from_predix from rdagent.components.coder.kronos_adapter import _ohlcv_from_nexquant
idx = pd.MultiIndex.from_arrays( idx = pd.MultiIndex.from_arrays(
[pd.date_range("2024-01-01", periods=3, freq="1min"), ["EURUSD"] * 3], [pd.date_range("2024-01-01", periods=3, freq="1min"), ["EURUSD"] * 3],
names=["datetime", "instrument"], names=["datetime", "instrument"],
@@ -78,17 +78,17 @@ class TestOhlcvConversion:
"$low": [1.05, 1.15, 1.25], "$close": [1.12, 1.22, 1.32], "$low": [1.05, 1.15, 1.25], "$close": [1.12, 1.22, 1.32],
"$volume": [100.0, 200.0, 300.0], "$volume": [100.0, 200.0, 300.0],
}, index=idx) }, index=idx)
result = _ohlcv_from_predix(df) result = _ohlcv_from_nexquant(df)
assert list(result.columns) == ["open", "high", "low", "close", "volume"] assert list(result.columns) == ["open", "high", "low", "close", "volume"]
def test_no_dollar_columns_passthrough(self): def test_no_dollar_columns_passthrough(self):
from rdagent.components.coder.kronos_adapter import _ohlcv_from_predix from rdagent.components.coder.kronos_adapter import _ohlcv_from_nexquant
df = pd.DataFrame({"open": [1.0], "close": [1.1], "high": [1.2], "low": [0.9], "volume": [100.0]}) df = pd.DataFrame({"open": [1.0], "close": [1.1], "high": [1.2], "low": [0.9], "volume": [100.0]})
result = _ohlcv_from_predix(df) result = _ohlcv_from_nexquant(df)
assert "close" in result.columns assert "close" in result.columns
def test_output_is_float64(self): def test_output_is_float64(self):
from rdagent.components.coder.kronos_adapter import _ohlcv_from_predix from rdagent.components.coder.kronos_adapter import _ohlcv_from_nexquant
df = pd.DataFrame({ df = pd.DataFrame({
"$open": np.array([1.1], dtype="float32"), "$open": np.array([1.1], dtype="float32"),
"$close": np.array([1.1], dtype="float32"), "$close": np.array([1.1], dtype="float32"),
@@ -96,7 +96,7 @@ class TestOhlcvConversion:
"$low": np.array([1.1], dtype="float32"), "$low": np.array([1.1], dtype="float32"),
"$volume": np.array([100.0], dtype="float32"), "$volume": np.array([100.0], dtype="float32"),
}) })
result = _ohlcv_from_predix(df) result = _ohlcv_from_nexquant(df)
assert result["close"].dtype == np.float64 assert result["close"].dtype == np.float64
@@ -134,7 +134,7 @@ class TestBuildKronosFactor:
def test_output_has_correct_multiindex(self, tmp_path, monkeypatch): def test_output_has_correct_multiindex(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path) h5 = _make_nexquant_hdf5(tmp_path)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert result.index.names == ["datetime", "instrument"] assert result.index.names == ["datetime", "instrument"]
assert result.index.nlevels == 2 assert result.index.nlevels == 2
@@ -142,14 +142,14 @@ class TestBuildKronosFactor:
def test_output_column_name(self, tmp_path, monkeypatch): def test_output_column_name(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path) h5 = _make_nexquant_hdf5(tmp_path)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert "KronosPredReturn" in result.columns assert "KronosPredReturn" in result.columns
def test_output_has_non_nan_values(self, tmp_path, monkeypatch): def test_output_has_non_nan_values(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path) h5 = _make_nexquant_hdf5(tmp_path)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert result["KronosPredReturn"].notna().sum() > 0 assert result["KronosPredReturn"].notna().sum() > 0
@@ -157,7 +157,7 @@ class TestBuildKronosFactor:
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
n = 300 n = 300
h5 = _make_predix_hdf5(tmp_path, n=n) h5 = _make_nexquant_hdf5(tmp_path, n=n)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert len(result) == n assert len(result) == n
@@ -165,7 +165,7 @@ class TestBuildKronosFactor:
"""Values within a predicted window should be forward-filled, not NaN.""" """Values within a predicted window should be forward-filled, not NaN."""
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=300) h5 = _make_nexquant_hdf5(tmp_path, n=300)
result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") result = mod.build_kronos_factor(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
non_nan_ratio = result["KronosPredReturn"].notna().mean() non_nan_ratio = result["KronosPredReturn"].notna().mean()
assert non_nan_ratio > 0.5, f"Expected >50% non-NaN, got {non_nan_ratio:.2%}" assert non_nan_ratio > 0.5, f"Expected >50% non-NaN, got {non_nan_ratio:.2%}"
@@ -185,7 +185,7 @@ class TestEvaluateKronosModel:
def test_returns_required_keys(self, tmp_path, monkeypatch): def test_returns_required_keys(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=400) h5 = _make_nexquant_hdf5(tmp_path, n=400)
metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
for key in ["IC_mean", "IC_std", "IC_IR", "hit_rate", "n_predictions"]: for key in ["IC_mean", "IC_std", "IC_IR", "hit_rate", "n_predictions"]:
assert key in metrics, f"Missing key: {key}" assert key in metrics, f"Missing key: {key}"
@@ -193,14 +193,14 @@ class TestEvaluateKronosModel:
def test_hit_rate_in_valid_range(self, tmp_path, monkeypatch): def test_hit_rate_in_valid_range(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=400) h5 = _make_nexquant_hdf5(tmp_path, n=400)
metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert 0.0 <= metrics["hit_rate"] <= 1.0 assert 0.0 <= metrics["hit_rate"] <= 1.0
def test_n_predictions_positive(self, tmp_path, monkeypatch): def test_n_predictions_positive(self, tmp_path, monkeypatch):
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
h5 = _make_predix_hdf5(tmp_path, n=400) h5 = _make_nexquant_hdf5(tmp_path, n=400)
metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu") metrics = mod.evaluate_kronos_model(h5, context_bars=100, pred_bars=20, stride_bars=20, device="cpu")
assert metrics["n_predictions"] > 0 assert metrics["n_predictions"] > 0
@@ -213,41 +213,41 @@ class TestCLICommands:
def test_kronos_factor_missing_data_exits(self, tmp_path, monkeypatch): def test_kronos_factor_missing_data_exits(self, tmp_path, monkeypatch):
"""kronos-factor exits with code 1 when HDF5 data is missing.""" """kronos-factor exits with code 1 when HDF5 data is missing."""
from typer.testing import CliRunner from typer.testing import CliRunner
import predix as predix_mod import nexquant as nexquant_mod
monkeypatch.chdir(tmp_path) monkeypatch.chdir(tmp_path)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix_mod.app, ["kronos-factor"]) result = runner.invoke(nexquant_mod.app, ["kronos-factor"])
assert result.exit_code == 1 assert result.exit_code == 1
def test_kronos_eval_missing_data_exits(self, tmp_path, monkeypatch): def test_kronos_eval_missing_data_exits(self, tmp_path, monkeypatch):
"""kronos-eval exits with code 1 when HDF5 data is missing.""" """kronos-eval exits with code 1 when HDF5 data is missing."""
from typer.testing import CliRunner from typer.testing import CliRunner
import predix as predix_mod import nexquant as nexquant_mod
monkeypatch.chdir(tmp_path) monkeypatch.chdir(tmp_path)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix_mod.app, ["kronos-eval"]) result = runner.invoke(nexquant_mod.app, ["kronos-eval"])
assert result.exit_code == 1 assert result.exit_code == 1
def test_kronos_factor_runs_with_mock(self, tmp_path, monkeypatch): def test_kronos_factor_runs_with_mock(self, tmp_path, monkeypatch):
"""kronos-factor completes and saves parquet + json when adapter is mocked.""" """kronos-factor completes and saves parquet + json when adapter is mocked."""
from typer.testing import CliRunner from typer.testing import CliRunner
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
import predix as predix_mod import nexquant as nexquant_mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
data_dir = tmp_path / "git_ignore_folder" / "factor_implementation_source_data" data_dir = tmp_path / "git_ignore_folder" / "factor_implementation_source_data"
data_dir.mkdir(parents=True) data_dir.mkdir(parents=True)
_make_predix_hdf5(data_dir.parent.parent, n=300) _make_nexquant_hdf5(data_dir.parent.parent, n=300)
h5_src = tmp_path / "intraday_pv.h5" h5_src = tmp_path / "intraday_pv.h5"
# Put HDF5 where the CLI expects it # Put HDF5 where the CLI expects it
import shutil import shutil
src = _make_predix_hdf5(tmp_path, n=300) src = _make_nexquant_hdf5(tmp_path, n=300)
shutil.copy(src, data_dir / "intraday_pv.h5") shutil.copy(src, data_dir / "intraday_pv.h5")
monkeypatch.chdir(tmp_path) monkeypatch.chdir(tmp_path)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix_mod.app, [ result = runner.invoke(nexquant_mod.app, [
"kronos-factor", "--context", "100", "--pred", "20", "--device", "cpu" "kronos-factor", "--context", "100", "--pred", "20", "--device", "cpu"
]) ])
assert result.exit_code == 0, result.output assert result.exit_code == 0, result.output
@@ -257,20 +257,20 @@ class TestCLICommands:
"""kronos-eval completes and prints IC metrics when adapter is mocked.""" """kronos-eval completes and prints IC metrics when adapter is mocked."""
from typer.testing import CliRunner from typer.testing import CliRunner
import rdagent.components.coder.kronos_adapter as mod import rdagent.components.coder.kronos_adapter as mod
import predix as predix_mod import nexquant as nexquant_mod
monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter()) monkeypatch.setattr(mod, "KronosAdapter", lambda **kw: _make_mock_adapter())
data_dir = tmp_path / "git_ignore_folder" / "factor_implementation_source_data" data_dir = tmp_path / "git_ignore_folder" / "factor_implementation_source_data"
data_dir.mkdir(parents=True) data_dir.mkdir(parents=True)
_make_predix_hdf5(data_dir.parent.parent, n=400) _make_nexquant_hdf5(data_dir.parent.parent, n=400)
src = _make_predix_hdf5(tmp_path, n=400) src = _make_nexquant_hdf5(tmp_path, n=400)
import shutil import shutil
shutil.copy(src, data_dir / "intraday_pv.h5") shutil.copy(src, data_dir / "intraday_pv.h5")
monkeypatch.chdir(tmp_path) monkeypatch.chdir(tmp_path)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix_mod.app, [ result = runner.invoke(nexquant_mod.app, [
"kronos-eval", "--context", "100", "--pred", "20", "--device", "cpu" "kronos-eval", "--context", "100", "--pred", "20", "--device", "cpu"
]) ])
assert result.exit_code == 0, result.output assert result.exit_code == 0, result.output
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Shared fixtures for Predix integration tests. Shared fixtures for NexQuant integration tests.
Provides common test data, mock objects, and utilities. Provides common test data, mock objects, and utilities.
""" """
import pytest import pytest
+42 -42
View File
@@ -1,5 +1,5 @@
""" """
Comprehensive Integration Test Suite for Predix Comprehensive Integration Test Suite for NexQuant
Tests all 13 implemented features to ensure they work correctly. Tests all 13 implemented features to ensure they work correctly.
Usage: Usage:
@@ -1457,22 +1457,22 @@ class TestFinQuantCriticalIntegrations:
# ============================================================================= # =============================================================================
# CLI Model Selection Tests (predix.py, cli.py) # CLI Model Selection Tests (nexquant.py, cli.py)
# ============================================================================= # =============================================================================
class TestCLIModelSelection: class TestCLIModelSelection:
"""Test CLI model selection (--model/-m flag) for local vs OpenRouter.""" """Test CLI model selection (--model/-m flag) for local vs OpenRouter."""
def test_predix_cli_imports(self): def test_nexquant_cli_imports(self):
"""Test that predix.py CLI module can be imported.""" """Test that nexquant.py CLI module can be imported."""
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
assert hasattr(predix, "app") assert hasattr(nexquant, "app")
assert hasattr(predix, "quant") assert hasattr(nexquant, "quant")
def test_fin_quant_cli_has_model_option(self): def test_fin_quant_cli_has_model_option(self):
"""Test that fin_quant CLI has --model option.""" """Test that fin_quant CLI has --model option."""
@@ -1488,36 +1488,36 @@ class TestCLIModelSelection:
# (Typer auto-generates help from function signatures) # (Typer auto-generates help from function signatures)
assert isinstance(result.output, str) assert isinstance(result.output, str)
def test_predix_quant_has_model_option(self): def test_nexquant_quant_has_model_option(self):
"""Test that predix quant CLI has --model option.""" """Test that nexquant quant CLI has --model option."""
from typer.testing import CliRunner from typer.testing import CliRunner
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix.app, ["quant", "--help"]) result = runner.invoke(nexquant.app, ["quant", "--help"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "--model" in result.output or "-m" in result.output assert "--model" in result.output or "-m" in result.output
def test_predix_quant_has_log_file_option(self): def test_nexquant_quant_has_log_file_option(self):
"""Test that predix quant CLI has --log-file option.""" """Test that nexquant quant CLI has --log-file option."""
from typer.testing import CliRunner from typer.testing import CliRunner
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix.app, ["quant", "--help"]) result = runner.invoke(nexquant.app, ["quant", "--help"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "--log-file" in result.output assert "--log-file" in result.output
@@ -1541,12 +1541,12 @@ class TestCLIModelSelection:
import inspect import inspect
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
source = inspect.getsource(predix.quant) source = inspect.getsource(nexquant.quant)
assert "OPENROUTER_API_KEY" in source assert "OPENROUTER_API_KEY" in source
assert "not set" in source or "not set in" in source assert "not set" in source or "not set in" in source
finally: finally:
@@ -1554,50 +1554,50 @@ class TestCLIModelSelection:
os.environ["OPENROUTER_API_KEY"] = original_key os.environ["OPENROUTER_API_KEY"] = original_key
def test_tee_writer_class_exists(self): def test_tee_writer_class_exists(self):
"""Test that TeeWriter class is defined in predix.py.""" """Test that TeeWriter class is defined in nexquant.py."""
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
# TeeWriter is defined inside the quant function # TeeWriter is defined inside the quant function
# Verify the function source contains TeeWriter # Verify the function source contains TeeWriter
import inspect import inspect
source = inspect.getsource(predix.quant) source = inspect.getsource(nexquant.quant)
assert "TeeWriter" in source assert "TeeWriter" in source
def test_predix_health_command(self): def test_nexquant_health_command(self):
"""Test that predix health command exists.""" """Test that nexquant health command exists."""
from typer.testing import CliRunner from typer.testing import CliRunner
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix.app, ["health", "--help"]) result = runner.invoke(nexquant.app, ["health", "--help"])
assert result.exit_code == 0 assert result.exit_code == 0
def test_predix_status_command(self): def test_nexquant_status_command(self):
"""Test that predix status command exists.""" """Test that nexquant status command exists."""
from typer.testing import CliRunner from typer.testing import CliRunner
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix", Path(__file__).parent.parent.parent / "predix.py" "nexquant", Path(__file__).parent.parent.parent / "nexquant.py"
) )
predix = importlib.util.module_from_spec(spec) nexquant = importlib.util.module_from_spec(spec)
spec.loader.exec_module(predix) spec.loader.exec_module(nexquant)
runner = CliRunner() runner = CliRunner()
result = runner.invoke(predix.app, ["status", "--help"]) result = runner.invoke(nexquant.app, ["status", "--help"])
assert result.exit_code == 0 assert result.exit_code == 0
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Integration Tests for Full Predix Pipeline (P6-P9) Integration Tests for Full NexQuant Pipeline (P6-P9)
Tests the complete end-to-end pipeline including: Tests the complete end-to-end pipeline including:
- Feedback Loop Integration (P6) - Feedback Loop Integration (P6)
+18 -18
View File
@@ -1,4 +1,4 @@
"""Deep tests for predix_autopilot.py — property-based, mocks, edge cases. """Deep tests for nexquant_autopilot.py — property-based, mocks, edge cases.
Tests the core logic of the 24/7 strategy generator by mocking Tests the core logic of the 24/7 strategy generator by mocking
the StrategyOrchestrator at the correct import path. the StrategyOrchestrator at the correct import path.
@@ -39,14 +39,14 @@ class TestMainRound:
"rdagent.scenarios.qlib.local.strategy_orchestrator.StrategyOrchestrator", "rdagent.scenarios.qlib.local.strategy_orchestrator.StrategyOrchestrator",
side_effect=RuntimeError("no data"), side_effect=RuntimeError("no data"),
): ):
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert result == 0 assert result == 0
def test_returns_zero_on_generate_failure(self, mock_orch): def test_returns_zero_on_generate_failure(self, mock_orch):
mock_cls, instance = mock_orch mock_cls, instance = mock_orch
instance.generate_strategies.side_effect = RuntimeError("crash") instance.generate_strategies.side_effect = RuntimeError("crash")
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert result == 0 assert result == 0
@@ -56,7 +56,7 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "s1", "sharpe_ratio": 0.5, "oos_sharpe": 0.3}, {"status": "accepted", "strategy_name": "s1", "sharpe_ratio": 0.5, "oos_sharpe": 0.3},
{"status": "rejected", "strategy_name": "s2", "reason": "low"}, {"status": "rejected", "strategy_name": "s2", "reason": "low"},
] ]
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert result == 1 assert result == 1
@@ -70,7 +70,7 @@ class TestMainRound:
"status": "success", "sharpe_ratio": 0.95, "oos_sharpe": 0.65, "status": "success", "sharpe_ratio": 0.95, "oos_sharpe": 0.65,
"members": ["a", "b"], "members": ["a", "b"],
} }
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
main_round("daytrading", 1) main_round("daytrading", 1)
instance.build_ensemble.assert_called_once() instance.build_ensemble.assert_called_once()
@@ -80,7 +80,7 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "a", "sharpe_ratio": 0.5, "oos_sharpe": 0.3}, {"status": "accepted", "strategy_name": "a", "sharpe_ratio": 0.5, "oos_sharpe": 0.3},
{"status": "rejected", "strategy_name": "b", "reason": "no"}, {"status": "rejected", "strategy_name": "b", "reason": "no"},
] ]
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
main_round("daytrading", 1) main_round("daytrading", 1)
instance.build_ensemble.assert_not_called() instance.build_ensemble.assert_not_called()
@@ -91,14 +91,14 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "b", "sharpe_ratio": 0.6, "oos_sharpe": 0.4}, {"status": "accepted", "strategy_name": "b", "sharpe_ratio": 0.6, "oos_sharpe": 0.4},
] ]
instance.build_ensemble.side_effect = RuntimeError("boom") instance.build_ensemble.side_effect = RuntimeError("boom")
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert result == 2 # Still counts accepted assert result == 2 # Still counts accepted
def test_empty_results_returns_zero(self, mock_orch): def test_empty_results_returns_zero(self, mock_orch):
mock_cls, instance = mock_orch mock_cls, instance = mock_orch
instance.generate_strategies.return_value = [] instance.generate_strategies.return_value = []
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert result == 0 assert result == 0
@@ -109,7 +109,7 @@ class TestMainRound:
{"status": "accepted", "strategy_name": "b", "sharpe_ratio": 0.6, "oos_sharpe": 0.4}, {"status": "accepted", "strategy_name": "b", "sharpe_ratio": 0.6, "oos_sharpe": 0.4},
] ]
instance.build_ensemble.return_value = None instance.build_ensemble.return_value = None
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert result == 2 assert result == 2
@@ -130,27 +130,27 @@ class TestMainRound:
"reason": "test"}, "reason": "test"},
] ]
mock_cls.return_value = instance mock_cls.return_value = instance
from scripts.predix_autopilot import main_round from scripts.nexquant_autopilot import main_round
result = main_round("daytrading", 1) result = main_round("daytrading", 1)
assert isinstance(result, int) and result >= 0 assert isinstance(result, int) and result >= 0
class TestConfig: class TestConfig:
def test_batch_size_positive(self): def test_batch_size_positive(self):
from scripts import predix_autopilot from scripts import nexquant_autopilot
assert predix_autopilot.BATCH_SIZE > 0 assert nexquant_autopilot.BATCH_SIZE > 0
def test_optuna_trials_positive(self): def test_optuna_trials_positive(self):
from scripts import predix_autopilot from scripts import nexquant_autopilot
assert predix_autopilot.OPTUNA_TRIALS > 0 assert nexquant_autopilot.OPTUNA_TRIALS > 0
def test_cooldown_positive(self): def test_cooldown_positive(self):
from scripts import predix_autopilot from scripts import nexquant_autopilot
assert predix_autopilot.COOLDOWN > 0 assert nexquant_autopilot.COOLDOWN > 0
def test_max_consecutive_fails_positive(self): def test_max_consecutive_fails_positive(self):
from scripts import predix_autopilot from scripts import nexquant_autopilot
assert predix_autopilot.MAX_CONSECUTIVE_FAILS > 0 assert nexquant_autopilot.MAX_CONSECUTIVE_FAILS > 0
class TestStyleCycling: class TestStyleCycling:
+33 -33
View File
@@ -2,9 +2,9 @@
Tests for background task infrastructure (parallel runner, CLI paths, env loading). Tests for background task infrastructure (parallel runner, CLI paths, env loading).
Verifies bugs that were previously present: Verifies bugs that were previously present:
- predix_parallel.py: project_root pointing to scripts/ instead of repo root - nexquant_parallel.py: project_root pointing to scripts/ instead of repo root
- predix_parallel.py: .env loaded from scripts/ instead of repo root - nexquant_parallel.py: .env loaded from scripts/ instead of repo root
- predix_parallel.py: API key round-robin overwritten by comma-separated list - nexquant_parallel.py: API key round-robin overwritten by comma-separated list
- cli.py: project_root depth wrong (4 .parent hops instead of 3) - cli.py: project_root depth wrong (4 .parent hops instead of 3)
- cli.py start_loop: hardcoded "python" instead of sys.executable - cli.py start_loop: hardcoded "python" instead of sys.executable
- cli.py parallel: hardcoded model=local - cli.py parallel: hardcoded model=local
@@ -18,7 +18,7 @@ from unittest.mock import Mock, patch
import pytest import pytest
# ── predix_parallel.py ────────────────────────────────────────────────── # ── nexquant_parallel.py ──────────────────────────────────────────────────
class TestParallelRunnerProjectRoot: class TestParallelRunnerProjectRoot:
@@ -26,36 +26,36 @@ class TestParallelRunnerProjectRoot:
def test_project_root_is_repo_root(self): def test_project_root_is_repo_root(self):
"""Bug: project_root was Path(__file__).parent (= scripts/).""" """Bug: project_root was Path(__file__).parent (= scripts/)."""
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
root = runner.project_root root = runner.project_root
# Must contain predix.py (repo root), NOT be the scripts/ dir # Must contain nexquant.py (repo root), NOT be the scripts/ dir
assert (root / "predix.py").exists(), ( assert (root / "nexquant.py").exists(), (
f"project_root={root} does not contain predix.py — " f"project_root={root} does not contain nexquant.py — "
f"likely still pointing to scripts/ instead of repo root" f"likely still pointing to scripts/ instead of repo root"
) )
assert root.name != "scripts", ( assert root.name != "scripts", (
f"project_root={root} ends with 'scripts/' — should be repo root" f"project_root={root} ends with 'scripts/' — should be repo root"
) )
def test_build_command_points_to_predix_py(self): def test_build_command_points_to_nexquant_py(self):
"""Bug: command pointed to scripts/predix.py which doesn't exist.""" """Bug: command pointed to scripts/nexquant.py which doesn't exist."""
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
run = RunState(run_id=1, api_key_idx=0, model="local") run = RunState(run_id=1, api_key_idx=0, model="local")
cmd = runner._build_command(run) cmd = runner._build_command(run)
predix_path = Path(cmd[1]) nexquant_path = Path(cmd[1])
assert predix_path.exists(), ( assert nexquant_path.exists(), (
f"Command references {predix_path} which does not exist — " f"Command references {nexquant_path} which does not exist — "
f"project_root likely still wrong" f"project_root likely still wrong"
) )
assert predix_path.name == "predix.py" assert nexquant_path.name == "nexquant.py"
assert predix_path.parent.name != "scripts", ( assert nexquant_path.parent.name != "scripts", (
"predix.py should be in repo root, not scripts/" "nexquant.py should be in repo root, not scripts/"
) )
def test_env_loading_from_repo_root(self): def test_env_loading_from_repo_root(self):
@@ -75,7 +75,7 @@ class TestParallelRunnerAPIKeys:
def test_single_api_key_no_overwrite(self): def test_single_api_key_no_overwrite(self):
"""Bug: with num_api_keys=1, individual key was set then overwritten.""" """Bug: with num_api_keys=1, individual key was set then overwritten."""
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, {}, clear=True): with patch.dict(os.environ, {}, clear=True):
os.environ["OPENROUTER_API_KEY"] = "sk-test-key-1" os.environ["OPENROUTER_API_KEY"] = "sk-test-key-1"
@@ -95,7 +95,7 @@ class TestParallelRunnerAPIKeys:
def test_multi_api_key_comma_separated(self): def test_multi_api_key_comma_separated(self):
"""With 2+ keys, all runs get comma-separated list for load balancing.""" """With 2+ keys, all runs get comma-separated list for load balancing."""
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, {}, clear=True): with patch.dict(os.environ, {}, clear=True):
os.environ["OPENROUTER_API_KEY"] = "sk-key-a" os.environ["OPENROUTER_API_KEY"] = "sk-key-a"
@@ -111,7 +111,7 @@ class TestParallelRunnerAPIKeys:
def test_round_robin_api_key_index(self): def test_round_robin_api_key_index(self):
"""Verify round-robin API key index assignment is computed correctly.""" """Verify round-robin API key index assignment is computed correctly."""
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
with patch.dict(os.environ, {}, clear=True): with patch.dict(os.environ, {}, clear=True):
os.environ["OPENROUTER_API_KEY"] = "a" os.environ["OPENROUTER_API_KEY"] = "a"
@@ -129,7 +129,7 @@ class TestParallelRunnerLogFileHandling:
def test_log_file_paths_in_repo_root(self): def test_log_file_paths_in_repo_root(self):
"""Bug: logs went to scripts/fin_quant_runN.log.""" """Bug: logs went to scripts/fin_quant_runN.log."""
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=2, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=2, num_api_keys=1, model="local")
@@ -170,7 +170,7 @@ class TestCLIProjectRoot:
"4 .parent hops should NOT yield repo root " "4 .parent hops should NOT yield repo root "
f"(got {buggy}, expected {self.REPO_ROOT.parent})" f"(got {buggy}, expected {self.REPO_ROOT.parent})"
) )
assert (buggy / "Predix").exists() or buggy == self.REPO_ROOT.parent, ( assert (buggy / "NexQuant").exists() or buggy == self.REPO_ROOT.parent, (
f"4 .parent hops overshoots repo root: {buggy}" f"4 .parent hops overshoots repo root: {buggy}"
) )
@@ -210,12 +210,12 @@ class TestCLIProjectRoot:
# All these commands use Path(__file__).parent.parent.parent as project_root # All these commands use Path(__file__).parent.parent.parent as project_root
commands = { commands = {
"eval_all": "scripts/predix_full_eval.py", "eval_all": "scripts/nexquant_full_eval.py",
"batch_backtest": "scripts/predix_batch_backtest.py", "batch_backtest": "scripts/nexquant_batch_backtest.py",
"simple_eval": "scripts/predix_simple_eval.py", "simple_eval": "scripts/nexquant_simple_eval.py",
"rebacktest": "scripts/predix_rebacktest_strategies.py", "rebacktest": "scripts/nexquant_rebacktest_strategies.py",
"report": "scripts/predix_strategy_report.py", "report": "scripts/nexquant_strategy_report.py",
"parallel": "scripts/predix_parallel.py", "parallel": "scripts/nexquant_parallel.py",
} }
for cmd_name, script_path in commands.items(): for cmd_name, script_path in commands.items():
@@ -231,12 +231,12 @@ class TestCLIProjectRoot:
import inspect import inspect
source = inspect.getsource(start_loop_cli) source = inspect.getsource(start_loop_cli)
# The generator should reference scripts/predix_smart_strategy_gen.py # The generator should reference scripts/nexquant_smart_strategy_gen.py
assert "predix_smart_strategy_gen.py" in source, ( assert "nexquant_smart_strategy_gen.py" in source, (
"start_loop_cli should reference predix_smart_strategy_gen.py" "start_loop_cli should reference nexquant_smart_strategy_gen.py"
) )
script = self.REPO_ROOT / "scripts" / "predix_smart_strategy_gen.py" script = self.REPO_ROOT / "scripts" / "nexquant_smart_strategy_gen.py"
assert script.exists(), ( assert script.exists(), (
f"Generator script not found at {script}" f"Generator script not found at {script}"
) )
@@ -266,7 +266,7 @@ class TestImportsDontCrash:
def test_import_parallel_runner(self): def test_import_parallel_runner(self):
"""ParallelRunner should import without errors.""" """ParallelRunner should import without errors."""
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
assert runner.num_runs == 1 assert runner.num_runs == 1
assert len(runner.runs) == 1 assert len(runner.runs) == 1
+8 -8
View File
@@ -15,8 +15,8 @@ Verifies:
- strategy_orchestrator.py exec() exception logged at ERROR level - strategy_orchestrator.py exec() exception logged at ERROR level
- strategy_orchestrator.py template validation warns on unreplaced {{...}} - strategy_orchestrator.py template validation warns on unreplaced {{...}}
- factor_runner.py IC_max guard against scalar (AttributeError) - factor_runner.py IC_max guard against scalar (AttributeError)
- predix_parallel.py handle leak on Popen failure - nexquant_parallel.py handle leak on Popen failure
- predix_rebacktest_strategies.py bare except replaced with except Exception - nexquant_rebacktest_strategies.py bare except replaced with except Exception
""" """
import ast import ast
@@ -228,32 +228,32 @@ class TestFactorRunnerICMaxGuard:
) )
# ── Fix 13: predix_parallel.py handle leak ─────────────────────────────── # ── Fix 13: nexquant_parallel.py handle leak ───────────────────────────────
class TestParallelRunnerHandleLeak: class TestParallelRunnerHandleLeak:
def test_log_f_close_on_popen_failure(self): def test_log_f_close_on_popen_failure(self):
"""Bug: open() file handle leaked if Popen failed.""" """Bug: open() file handle leaked if Popen failed."""
source = (REPO_ROOT / "scripts/predix_parallel.py").read_text() source = (REPO_ROOT / "scripts/nexquant_parallel.py").read_text()
# After fix, log_f.close() is called before re-raise # After fix, log_f.close() is called before re-raise
assert "log_f.close()" in source, ( assert "log_f.close()" in source, (
"predix_parallel.py should close log file handle on Popen failure" "nexquant_parallel.py should close log file handle on Popen failure"
) )
# ── Fix 14: predix_rebacktest_strategies.py bare except ────────────────── # ── Fix 14: nexquant_rebacktest_strategies.py bare except ──────────────────
class TestRebacktestBareExcept: class TestRebacktestBareExcept:
def test_not_bare_except(self): def test_not_bare_except(self):
"""Bug: bare except: pass swallowed all errors including SystemExit.""" """Bug: bare except: pass swallowed all errors including SystemExit."""
source = (REPO_ROOT / "scripts/predix_rebacktest_strategies.py").read_text() source = (REPO_ROOT / "scripts/nexquant_rebacktest_strategies.py").read_text()
# After fix, should use except Exception, not bare except # After fix, should use except Exception, not bare except
assert "except Exception:" in source assert "except Exception:" in source
assert "except:" not in source, ( assert "except:" not in source, (
"predix_rebacktest_strategies.py should not use bare except:" "nexquant_rebacktest_strategies.py should not use bare except:"
) )
+9 -9
View File
@@ -1,4 +1,4 @@
"""Deep tests for predix_continuous_strategies.py — ML model building, style cycling. """Deep tests for nexquant_continuous_strategies.py — ML model building, style cycling.
Tests the build_ml_model function and the round/style alternation logic Tests the build_ml_model function and the round/style alternation logic
without requiring real StrategyOrchestrator connections. without requiring real StrategyOrchestrator connections.
@@ -45,7 +45,7 @@ def close_data():
class TestBuildMLModel: class TestBuildMLModel:
def test_insufficient_data_returns_none(self, factor_data, close_data): def test_insufficient_data_returns_none(self, factor_data, close_data):
"""<5000 rows should return None.""" """<5000 rows should return None."""
from scripts.predix_continuous_strategies import build_ml_model from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(factor_data.iloc[:100], close_data.iloc[:100], "swing") result = build_ml_model(factor_data.iloc[:100], close_data.iloc[:100], "swing")
assert result is None assert result is None
@@ -55,7 +55,7 @@ class TestBuildMLModel:
"sharpe": 1.5, "max_drawdown": -0.1, "win_rate": 0.55, "sharpe": 1.5, "max_drawdown": -0.1, "win_rate": 0.55,
"n_trades": 200, "wf_oos_sharpe_mean": 0.8, "n_trades": 200, "wf_oos_sharpe_mean": 0.8,
} }
from scripts.predix_continuous_strategies import build_ml_model from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(factor_data, close_data, "daytrading") result = build_ml_model(factor_data, close_data, "daytrading")
assert result is not None assert result is not None
assert "strategy_name" in result assert "strategy_name" in result
@@ -69,7 +69,7 @@ class TestBuildMLModel:
"sharpe": 1.5, "max_drawdown": -0.1, "win_rate": 0.55, "sharpe": 1.5, "max_drawdown": -0.1, "win_rate": 0.55,
"n_trades": 200, "wf_oos_sharpe_mean": -0.3, "n_trades": 200, "wf_oos_sharpe_mean": -0.3,
} }
from scripts.predix_continuous_strategies import build_ml_model from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(factor_data, close_data, "swing") result = build_ml_model(factor_data, close_data, "swing")
assert result is None assert result is None
@@ -90,7 +90,7 @@ class TestBuildMLModel:
}, index=factor_data.index[:n]) }, index=factor_data.index[:n])
c = pd.Series(1.10 + rng.normal(0, 0.001, n).cumsum(), index=f.index) c = pd.Series(1.10 + rng.normal(0, 0.001, n).cumsum(), index=f.index)
try: try:
from scripts.predix_continuous_strategies import build_ml_model from scripts.nexquant_continuous_strategies import build_ml_model
result = build_ml_model(f, c, "swing") result = build_ml_model(f, c, "swing")
assert result is None or isinstance(result, dict) assert result is None or isinstance(result, dict)
except Exception as e: except Exception as e:
@@ -102,12 +102,12 @@ class TestBuildMLModel:
class TestConfig: class TestConfig:
def test_batch_size_is_positive(self): def test_batch_size_is_positive(self):
from scripts import predix_continuous_strategies from scripts import nexquant_continuous_strategies
assert predix_continuous_strategies.BATCH_SIZE > 0 assert nexquant_continuous_strategies.BATCH_SIZE > 0
def test_cooldown_is_positive(self): def test_cooldown_is_positive(self):
from scripts import predix_continuous_strategies from scripts import nexquant_continuous_strategies
assert predix_continuous_strategies.COOLDOWN_SECONDS > 0 assert nexquant_continuous_strategies.COOLDOWN_SECONDS > 0
class TestStyleCycling: class TestStyleCycling:
+4 -4
View File
@@ -1,4 +1,4 @@
"""Deep tests for predix_gen_strategies_real_bt.py — property-based, edge cases. """Deep tests for nexquant_gen_strategies_real_bt.py — property-based, edge cases.
Tests factor loading, threshold rescaling, backtest runner, acceptance Tests factor loading, threshold rescaling, backtest runner, acceptance
criteria, and the TeeFile logger without requiring real OHLCV data or LLM. criteria, and the TeeFile logger without requiring real OHLCV data or LLM.
@@ -27,7 +27,7 @@ from hypothesis import strategies as st
@pytest.fixture @pytest.fixture
def gen_module(): def gen_module():
import importlib import importlib
import scripts.predix_gen_strategies_real_bt as m import scripts.nexquant_gen_strategies_real_bt as m
return m return m
@@ -280,7 +280,7 @@ class TestConfiguration:
"""Daytrading config uses tighter risk limits.""" """Daytrading config uses tighter risk limits."""
os.environ["TRADING_STYLE"] = "daytrading" os.environ["TRADING_STYLE"] = "daytrading"
import importlib import importlib
import scripts.predix_gen_strategies_real_bt as m import scripts.nexquant_gen_strategies_real_bt as m
importlib.reload(m) importlib.reload(m)
assert m.MIN_IC == 0.02 assert m.MIN_IC == 0.02
assert m.MIN_SHARPE == 0.5 assert m.MIN_SHARPE == 0.5
@@ -289,7 +289,7 @@ class TestConfiguration:
def test_swing_defaults(self): def test_swing_defaults(self):
os.environ["TRADING_STYLE"] = "swing" os.environ["TRADING_STYLE"] = "swing"
import importlib import importlib
import scripts.predix_gen_strategies_real_bt as m import scripts.nexquant_gen_strategies_real_bt as m
importlib.reload(m) importlib.reload(m)
assert m.MIN_TRADES == 10 assert m.MIN_TRADES == 10
assert m.MAX_DRAWDOWN == -0.30 assert m.MAX_DRAWDOWN == -0.30
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Tests for MLTrainer - ML Training Pipeline for Predix quant trading system. Tests for MLTrainer - ML Training Pipeline for NexQuant quant trading system.
Tests cover: Tests cover:
- Feature matrix building - Feature matrix building
@@ -1,4 +1,4 @@
"""Deep tests for predix_parallel.py — property-based, state transitions, edge cases. """Deep tests for nexquant_parallel.py — property-based, state transitions, edge cases.
Tests RunState, ParallelRunner configuration, environment building, Tests RunState, ParallelRunner configuration, environment building,
command building, and API key loading logic. command building, and API key loading logic.
@@ -25,7 +25,7 @@ from hypothesis import strategies as st
@pytest.fixture @pytest.fixture
def runstate(): def runstate():
from scripts.predix_parallel import RunState from scripts.nexquant_parallel import RunState
return RunState(run_id=1, api_key_idx=0, model="local") return RunState(run_id=1, api_key_idx=0, model="local")
@@ -43,7 +43,7 @@ class TestRunState:
def test_elapsed_running(self, runstate): def test_elapsed_running(self, runstate):
runstate.start_time = datetime(2024, 1, 1, 12, 0, 0) runstate.start_time = datetime(2024, 1, 1, 12, 0, 0)
with patch("scripts.predix_parallel.datetime") as mock_dt: with patch("scripts.nexquant_parallel.datetime") as mock_dt:
mock_dt.now.return_value = datetime(2024, 1, 1, 13, 30, 45) mock_dt.now.return_value = datetime(2024, 1, 1, 13, 30, 45)
assert runstate.elapsed == "01:30:45" assert runstate.elapsed == "01:30:45"
@@ -94,7 +94,7 @@ class TestRunState:
class TestParallelRunnerConfig: class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_load_api_keys_openrouter(self): def test_load_api_keys_openrouter(self):
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
with patch.dict(os.environ, { with patch.dict(os.environ, {
"OPENROUTER_API_KEY": "sk-key1", "OPENROUTER_API_KEY": "sk-key1",
"OPENROUTER_API_KEY_2": "sk-key2", "OPENROUTER_API_KEY_2": "sk-key2",
@@ -106,13 +106,13 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_load_api_keys_local(self): def test_load_api_keys_local(self):
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
assert runner.api_keys == ["local"] assert runner.api_keys == ["local"]
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_load_api_keys_round_robin(self): def test_load_api_keys_round_robin(self):
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=5, num_api_keys=2, model="local") runner = ParallelRunner(num_runs=5, num_api_keys=2, model="local")
assert len(runner.runs) == 5 assert len(runner.runs) == 5
idxs = [r.api_key_idx for r in runner.runs] idxs = [r.api_key_idx for r in runner.runs]
@@ -120,7 +120,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_build_env_local_model(self): def test_build_env_local_model(self):
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, { with patch.dict(os.environ, {
"OPENAI_API_KEY": "local", "OPENAI_API_KEY": "local",
"OPENAI_API_BASE": "http://localhost:8081/v1", "OPENAI_API_BASE": "http://localhost:8081/v1",
@@ -135,7 +135,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_build_env_openrouter(self): def test_build_env_openrouter(self):
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
with patch.dict(os.environ, { with patch.dict(os.environ, {
"OPENROUTER_API_KEY": "sk-test", "OPENROUTER_API_KEY": "sk-test",
"OPENROUTER_API_KEY_2": "sk-test2", "OPENROUTER_API_KEY_2": "sk-test2",
@@ -147,7 +147,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_build_env_sets_workspace(self): def test_build_env_sets_workspace(self):
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
rs = RunState(run_id=42, api_key_idx=0, model="local") rs = RunState(run_id=42, api_key_idx=0, model="local")
env = runner._build_env(rs) env = runner._build_env(rs)
@@ -156,11 +156,11 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_build_command(self): def test_build_command(self):
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
rs = RunState(run_id=7, api_key_idx=0, model="local") rs = RunState(run_id=7, api_key_idx=0, model="local")
cmd = runner._build_command(rs) cmd = runner._build_command(rs)
assert "predix.py" in cmd[1] or "predix" in cmd[1] assert "nexquant.py" in cmd[1] or "nexquant" in cmd[1]
assert "quant" in cmd assert "quant" in cmd
assert "--model" in cmd assert "--model" in cmd
assert "local" in cmd assert "local" in cmd
@@ -168,7 +168,7 @@ class TestParallelRunnerConfig:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_parallel_runner_init_counts(self): def test_parallel_runner_init_counts(self):
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
for n in [1, 3, 10]: for n in [1, 3, 10]:
runner = ParallelRunner(num_runs=n, num_api_keys=2, model="local") runner = ParallelRunner(num_runs=n, num_api_keys=2, model="local")
assert len(runner.runs) == n assert len(runner.runs) == n
@@ -178,20 +178,20 @@ class TestParallelRunnerConfig:
class TestParallelRunnerEdgeCases: class TestParallelRunnerEdgeCases:
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_max_runs_limit(self): def test_max_runs_limit(self):
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=100, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=100, num_api_keys=1, model="local")
assert len(runner.runs) == 100 assert len(runner.runs) == 100
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_api_keys_empty_uses_local(self): def test_api_keys_empty_uses_local(self):
from scripts.predix_parallel import ParallelRunner from scripts.nexquant_parallel import ParallelRunner
runner = ParallelRunner(num_runs=1, num_api_keys=2, model="openrouter") runner = ParallelRunner(num_runs=1, num_api_keys=2, model="openrouter")
assert len(runner.api_keys) >= 1 assert len(runner.api_keys) >= 1
@patch.dict(os.environ, {}, clear=True) @patch.dict(os.environ, {}, clear=True)
def test_build_env_preserves_existing_env(self, monkeypatch): def test_build_env_preserves_existing_env(self, monkeypatch):
monkeypatch.setenv("MY_CUSTOM_VAR", "custom_value") monkeypatch.setenv("MY_CUSTOM_VAR", "custom_value")
from scripts.predix_parallel import ParallelRunner, RunState from scripts.nexquant_parallel import ParallelRunner, RunState
runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local") runner = ParallelRunner(num_runs=1, num_api_keys=1, model="local")
rs = RunState(run_id=1, api_key_idx=0, model="local") rs = RunState(run_id=1, api_key_idx=0, model="local")
env = runner._build_env(rs) env = runner._build_env(rs)
+2 -2
View File
@@ -643,14 +643,14 @@ class TestStrategySaver:
'passed': True, 'passed': True,
'checks': {}, 'checks': {},
}, },
metadata={'version': '1.0', 'author': 'Predix'}, metadata={'version': '1.0', 'author': 'NexQuant'},
) )
with open(filepath) as f: with open(filepath) as f:
data = json.load(f) data = json.load(f)
assert data['metadata']['version'] == '1.0' assert data['metadata']['version'] == '1.0'
assert data['metadata']['author'] == 'Predix' assert data['metadata']['author'] == 'NexQuant'
def test_save_strategy_with_llm_response(self, strategy_saver): def test_save_strategy_with_llm_response(self, strategy_saver):
"""Test saving strategy with LLM response preview.""" """Test saving strategy with LLM response preview."""
+10 -10
View File
@@ -123,9 +123,9 @@ class TestWebDashboard:
class TestScriptsImportable: class TestScriptsImportable:
def test_predix_full_eval(self): def test_nexquant_full_eval(self):
import importlib import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_full_eval.py") spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_full_eval.py")
assert spec is not None assert spec is not None
def test_extract_results(self): def test_extract_results(self):
@@ -153,24 +153,24 @@ class TestScriptsImportable:
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/kronos_model_eval.py") spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/kronos_model_eval.py")
assert spec is not None assert spec is not None
def test_predix_add_risk_management(self): def test_nexquant_add_risk_management(self):
import importlib import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_add_risk_management.py") spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_add_risk_management.py")
assert spec is not None assert spec is not None
def test_predix_gen_strategies(self): def test_nexquant_gen_strategies(self):
import importlib import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_gen_strategies_real_bt.py") spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_gen_strategies_real_bt.py")
assert spec is not None assert spec is not None
def test_predix_quick_daytrading(self): def test_nexquant_quick_daytrading(self):
import importlib import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_quick_daytrading.py") spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_quick_daytrading.py")
assert spec is not None assert spec is not None
def test_predix_rebacktest_unified(self): def test_nexquant_rebacktest_unified(self):
import importlib import importlib
spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/predix_rebacktest_unified.py") spec = importlib.util.spec_from_file_location("m", PROJECT_ROOT / "scripts/nexquant_rebacktest_unified.py")
assert spec is not None assert spec is not None
def test_realistic_backtest_all(self): def test_realistic_backtest_all(self):
+3 -3
View File
@@ -196,7 +196,7 @@ class TestInfNanHandlingInsertion:
# ============================================================================= # =============================================================================
# Bug 5: scan_factors reads factor_code twice (predix_full_eval.py:174 + 195) # Bug 5: scan_factors reads factor_code twice (nexquant_full_eval.py:174 + 195)
# ============================================================================= # =============================================================================
class TestScanFactorsDoubleRead: class TestScanFactorsDoubleRead:
@@ -205,9 +205,9 @@ class TestScanFactorsDoubleRead:
def test_factor_code_read_only_when_needed(self): def test_factor_code_read_only_when_needed(self):
"""Confirm the scan_factors double-read behavior (line 174+195).""" """Confirm the scan_factors double-read behavior (line 174+195)."""
import inspect import inspect
from scripts import predix_full_eval from scripts import nexquant_full_eval
source = inspect.getsource(predix_full_eval.scan_factors) source = inspect.getsource(nexquant_full_eval.scan_factors)
# Count occurrences of `.read_text()` # Count occurrences of `.read_text()`
count = source.count(".read_text()") count = source.count(".read_text()")
+3 -3
View File
@@ -18,8 +18,8 @@ class TestContinuousGenerator:
def test_module_imports(self): def test_module_imports(self):
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
"predix_autopilot", "nexquant_autopilot",
PROJECT_ROOT / "scripts/predix_autopilot.py", PROJECT_ROOT / "scripts/nexquant_autopilot.py",
) )
assert spec is not None assert spec is not None
@@ -107,6 +107,6 @@ class TestAutopilotIntegration:
def test_autopilot_pid_running(self): def test_autopilot_pid_running(self):
import os import os
result = os.system("pgrep -f predix_autopilot > /dev/null 2>&1") result = os.system("pgrep -f nexquant_autopilot > /dev/null 2>&1")
# 0 = running, 1 = not running — both are valid states # 0 = running, 1 = not running — both are valid states
assert result in (0, 1) assert result in (0, 1)
@@ -1,4 +1,4 @@
"""Tests for scripts/predix_full_eval.py pure functions and dataclasses.""" """Tests for scripts/nexquant_full_eval.py pure functions and dataclasses."""
from __future__ import annotations from __future__ import annotations
@@ -13,7 +13,7 @@ sys.path.insert(0, str(PROJECT_ROOT))
class TestFactorInfo: class TestFactorInfo:
def test_construction(self): def test_construction(self):
from scripts.predix_full_eval import FactorInfo from scripts.nexquant_full_eval import FactorInfo
fi = FactorInfo( fi = FactorInfo(
workspace_hash="abc123", workspace_hash="abc123",
factor_name="test_factor", factor_name="test_factor",
@@ -26,7 +26,7 @@ class TestFactorInfo:
class TestEvalResult: class TestEvalResult:
def test_defaults(self): def test_defaults(self):
from scripts.predix_full_eval import EvalResult from scripts.nexquant_full_eval import EvalResult
er = EvalResult(factor_name="f1", workspace_hash="h1") er = EvalResult(factor_name="f1", workspace_hash="h1")
assert er.status == "" assert er.status == ""
assert er.ic is None assert er.ic is None
@@ -34,7 +34,7 @@ class TestEvalResult:
assert er.non_null_count == 0 assert er.non_null_count == 0
def test_failed_result(self): def test_failed_result(self):
from scripts.predix_full_eval import EvalResult from scripts.nexquant_full_eval import EvalResult
er = EvalResult( er = EvalResult(
factor_name="f1", workspace_hash="h1", factor_name="f1", workspace_hash="h1",
status="failed", error_message="timeout", status="failed", error_message="timeout",
@@ -43,7 +43,7 @@ class TestEvalResult:
assert er.error_message == "timeout" assert er.error_message == "timeout"
def test_to_dict(self): def test_to_dict(self):
from scripts.predix_full_eval import EvalResult from scripts.nexquant_full_eval import EvalResult
er = EvalResult(factor_name="f1", workspace_hash="h1", status="success", ic=0.05) er = EvalResult(factor_name="f1", workspace_hash="h1", status="success", ic=0.05)
d = er.to_dict() d = er.to_dict()
assert d["factor_name"] == "f1" assert d["factor_name"] == "f1"
@@ -53,26 +53,26 @@ class TestEvalResult:
class TestExtractFactorDescription: class TestExtractFactorDescription:
def test_docstring_extracted(self): def test_docstring_extracted(self):
from scripts.predix_full_eval import _extract_factor_description from scripts.nexquant_full_eval import _extract_factor_description
code = '"""This is a test factor.\nComputes momentum."""\nx=1' code = '"""This is a test factor.\nComputes momentum."""\nx=1'
desc = _extract_factor_description(code) desc = _extract_factor_description(code)
assert "test factor" in desc assert "test factor" in desc
def test_comment_extraction(self): def test_comment_extraction(self):
from scripts.predix_full_eval import _extract_factor_description from scripts.nexquant_full_eval import _extract_factor_description
code = "# Momentum factor\n# Uses 20-bar window\nx=1" code = "# Momentum factor\n# Uses 20-bar window\nx=1"
desc = _extract_factor_description(code) desc = _extract_factor_description(code)
assert "Momentum factor" in desc assert "Momentum factor" in desc
assert "20-bar window" in desc assert "20-bar window" in desc
def test_no_docstring_or_comments(self): def test_no_docstring_or_comments(self):
from scripts.predix_full_eval import _extract_factor_description from scripts.nexquant_full_eval import _extract_factor_description
code = "x = 1\ny = 2\n" code = "x = 1\ny = 2\n"
desc = _extract_factor_description(code) desc = _extract_factor_description(code)
assert desc == "No description available" assert desc == "No description available"
def test_shebang_skipped(self): def test_shebang_skipped(self):
from scripts.predix_full_eval import _extract_factor_description from scripts.nexquant_full_eval import _extract_factor_description
code = "#!/usr/bin/env python\n# Real comment\nx=1" code = "#!/usr/bin/env python\n# Real comment\nx=1"
desc = _extract_factor_description(code) desc = _extract_factor_description(code)
assert "Real comment" in desc assert "Real comment" in desc
+2 -2
View File
@@ -16,10 +16,10 @@ PROJECT_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT)) sys.path.insert(0, str(PROJECT_ROOT))
class TestPredixCLI: class TestNexQuantCLI:
def test_cli_commands_available(self): def test_cli_commands_available(self):
import subprocess import subprocess
r = subprocess.run([sys.executable, "predix.py", "--help"], capture_output=True, text=True, timeout=10) r = subprocess.run([sys.executable, "nexquant.py", "--help"], capture_output=True, text=True, timeout=10)
assert r.returncode == 0 assert r.returncode == 0
for cmd in ["evaluate", "top", "best", "portfolio", "build-strategies", "generate-strategies", "health"]: for cmd in ["evaluate", "top", "best", "portfolio", "build-strategies", "generate-strategies", "health"]:
assert cmd in r.stdout.lower(), f"Missing command: {cmd}" assert cmd in r.stdout.lower(), f"Missing command: {cmd}"
+3 -3
View File
@@ -1,5 +1,5 @@
""" """
Predix Dashboard API NexQuant Dashboard API
Flask-Backend für das Web-Dashboard. Flask-Backend für das Web-Dashboard.
Zeigt COMPLETE Progress von EURUSD Trading-Agent. Zeigt COMPLETE Progress von EURUSD Trading-Agent.
@@ -290,7 +290,7 @@ def get_full_dashboard():
def index(): def index():
"""Root Endpoint - zeigt API-Info.""" """Root Endpoint - zeigt API-Info."""
return jsonify({ return jsonify({
"name": "Predix Dashboard API", "name": "NexQuant Dashboard API",
"version": "1.0.0", "version": "1.0.0",
"description": "COMPLETE Progress Visualisierung für EURUSD Trading-Agent", "description": "COMPLETE Progress Visualisierung für EURUSD Trading-Agent",
"endpoints": { "endpoints": {
@@ -307,7 +307,7 @@ def index():
if __name__ == '__main__': if __name__ == '__main__':
print("="*60) print("="*60)
print("Predix Dashboard API") print("NexQuant Dashboard API")
print("="*60) print("="*60)
print(f"Modules available: {MODULES_AVAILABLE}") print(f"Modules available: {MODULES_AVAILABLE}")
print(f"Starting server on http://localhost:5000") print(f"Starting server on http://localhost:5000")