Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,25 @@ jobs:
- name: Run Tests
run: cargo test --all-features --verbose

# 6. Build Documentation
# 6. Build Documentation (Stable)
- name: Verify Documentation Build
run: cargo doc --no-deps --document-private-items
env:
RUSTDOCFLAGS: "-D warnings" # Fail if docs have broken links
RUSTDOCFLAGS: "-D warnings"

# 7. Check Examples (Compilation Only)
# 7 Verify docs.rs Compatibility (Nightly - SOFT FAIL)
# docs.rs uses the nightly compiler. Upstream crates frequently break here.
# We use continue-on-error so a nightly upstream break doesn't block our PRs.
- name: Check Nightly Docs Compatibility
continue-on-error: true
run: |
rustup toolchain install nightly --profile minimal
cargo +nightly doc --no-deps
env:
# We don't use -D warnings here because nightly often introduces
# new experimental lints that we don't want to enforce yet.
RUSTDOCFLAGS: ""

# 8. Check Examples (Compilation Only)
- name: Check Examples Compile
run: cargo build --examples --verbose
42 changes: 21 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "chapaty"
version = "1.1.0"
version = "1.1.1"
edition = "2024"
authors = ["Len Williamson <lencewa13@gmail.com>"]
description = "High-performance backtesting and financial simulation framework for trading strategies and reinforcement learning agents. Async-first, Gym-like API in Rust."
description = "An event-driven Rust engine for building and evaluating quantitative trading agents. Features a Gym-style API for algorithmic backtesting and reinforcement learning."
license = "Apache-2.0"
readme = "README.md"
# homepage = "https://www.chapaty.com"
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Chapaty

[![Discord](https://img.shields.io/discord/1495690333911257108.svg?label=Discord&logo=discord&color=7289da&logoColor=white)](https://discord.gg/k7GWpDQC)
[![Discord](https://img.shields.io/discord/1495690333911257108.svg?label=Discord&logo=discord&color=7289da&logoColor=white)][discord]
[![Crates.io](https://img.shields.io/crates/v/chapaty.svg)](https://crates.io/crates/chapaty)
[![Docs.rs](https://img.shields.io/docsrs/chapaty)](https://docs.rs/chapaty)
[![CI (Main)](https://github.com/LenWilliamson/chapaty/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/LenWilliamson/chapaty/actions/workflows/ci.yml)
[![CI (Develop)](https://github.com/LenWilliamson/chapaty/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/LenWilliamson/chapaty/actions/workflows/ci.yml)

**Chapaty** is an Rust engine for **building and evaluating quantitative trading agents**. Designed with a familiar, [**Gym-style API**][gymnasiumLink], Chapaty brings the rigor of standardized simulation interfaces to **event-driven financial backtesting**.
**Chapaty** is a Rust engine for **building and evaluating quantitative trading agents**. Designed with a familiar [**Gym-style API**][gymnasiumLink], Chapaty brings the rigor of standardized simulation interfaces to **event-driven financial backtesting**.

## Getting Started

Expand Down Expand Up @@ -111,7 +111,7 @@ For practical, _ready-to-run agents_, check out the `examples/` directory to get

## Community

If you are excited about the project, don't hesitate to join our [Discord](https://discord.gg/k7GWpDQC)! It is the perfect place to ask questions, file data requests, discuss new features, and share what you have built with the community.
If you are excited about the project, don't hesitate to join our [Discord][discord]! It is the perfect place to ask questions, file data requests, discuss new features, and share what you have built with the community.

## Contributing

Expand All @@ -133,5 +133,6 @@ This software is provided **“AS IS”**, without warranties or conditions of a

By using Chapaty, you acknowledge that **you are solely responsible for any trading decisions, strategies, or outcomes**.

[discord]: https://discord.gg/MmMAB6NCuK
[gymnasiumLink]: https://github.com/Farama-Foundation/Gymnasium
[deepmindLink]: https://github.com/deepmind/dm_control
48 changes: 33 additions & 15 deletions bin/pre-push.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ echo -e "${BLUE}>>> Starting Local CI Pipeline for Chapaty...${NC}"
# JOB 1: Compliance & Security
# ==============================================================================

echo -e "\n${YELLOW}[1/10] Checking Security (Secrets)...${NC}"
echo -e "\n${YELLOW}[1/11] Checking Security (Secrets)...${NC}"
# Check if .cargo/config.toml is tracked by git
if git ls-files --error-unmatch .cargo/config.toml > /dev/null 2>&1; then
echo -e "${RED}[FAIL] CRITICAL: .cargo/config.toml is being tracked by git! Remove it immediately.${NC}"
exit 1
fi
echo -e "${GREEN}[OK] No leaked secrets in git index.${NC}"

echo -e "\n${YELLOW}[2/10] Checking Formatting...${NC}"
echo -e "\n${YELLOW}[2/11] Checking Formatting...${NC}"
# Fails if code is not formatted. Remove '--check' to auto-format instead.
cargo fmt -- --check || { echo -e "${RED}[FAIL] Formatting invalid. Run 'cargo fmt' to fix.${NC}"; exit 1; }
echo -e "${GREEN}[OK] Formatting is correct.${NC}"

echo -e "\n${YELLOW}[3/10] Checking Architecture Guardrails...${NC}"
echo -e "\n${YELLOW}[3/11] Checking Architecture Guardrails...${NC}"
# Prevent circular dependencies via prelude imports within the library
if grep -r "use crate::prelude::" src/; then
echo -e "${RED}[FAIL] Architecture violation: Internal imports from 'crate::prelude' found.${NC}"
Expand All @@ -40,7 +40,7 @@ echo -e "${GREEN}[OK] Architecture compliant.${NC}"
# JOB 2: Build, Test & Verify
# ==============================================================================

echo -e "\n${YELLOW}[4/10] Security Audit (Dependencies)...${NC}"
echo -e "\n${YELLOW}[4/11] Security Audit (Dependencies)...${NC}"
# Check if cargo-audit is installed
if ! command -v cargo-audit &> /dev/null; then
echo -e "${RED}[FAIL] 'cargo-audit' is not installed.${NC}"
Expand All @@ -50,52 +50,70 @@ fi
cargo audit
echo -e "${GREEN}[OK] Dependencies audited.${NC}"

echo -e "\n${YELLOW}[5/10] Linting (Clippy)...${NC}"
echo -e "\n${YELLOW}[5/11] Linting (Clippy)...${NC}"
# Deny warnings to match CI strictness
cargo clippy --all-targets --all-features -- -D warnings
echo -e "${GREEN}[OK] Code is clean.${NC}"

echo -e "\n${YELLOW}[6/10] Building Workspace...${NC}"
echo -e "\n${YELLOW}[6/11] Building Workspace...${NC}"
cargo build --all-features
echo -e "${GREEN}[OK] Workspace compiled successfully.${NC}"

echo -e "\n${YELLOW}[7/10] Running Unit Tests...${NC}"
echo -e "\n${YELLOW}[7/11] Running Unit Tests...${NC}"
cargo test --all-features
echo -e "${GREEN}[OK] All tests passed.${NC}"

echo -e "\n${YELLOW}[8/10] Verifying Documentation...${NC}"
echo -e "\n${YELLOW}[8/11] Verifying Documentation...${NC}"
# Ensure documentation builds without warnings (broken links, etc.)
export RUSTDOCFLAGS="-D warnings"
cargo doc --no-deps --document-private-items
echo -e "${GREEN}[OK] Documentation builds successfully.${NC}"

echo -e "\n${YELLOW}[9/11] Verifying Docs.rs Compatibility (Nightly)...${NC}"
# docs.rs strictly uses the nightly compiler. We run a soft-fail check here.
if rustup toolchain list | grep -q nightly; then
# We suppress stdout to keep it clean, but let stderr show if it fails.
# We inline RUSTDOCFLAGS="" to override the strict warnings exported in Step 8.
if env RUSTDOCFLAGS="" cargo +nightly doc --no-deps > /dev/null 2>&1; then
echo -e "${GREEN}[OK] Nightly docs build successfully.${NC}"
else
echo -e "${YELLOW}[WARN] Nightly docs build failed!${NC}"
echo -e "${YELLOW} Your code compiles on Stable, but your docs.rs page will likely fail.${NC}"
echo -e "${YELLOW} This is usually an upstream dependency breaking on Nightly.${NC}"
echo -e "${YELLOW} -> Pipeline continuing because Stable is intact.${NC}"
fi
else
echo -e "${BLUE}[SKIP] Nightly toolchain not installed.${NC}"
echo -e "${BLUE} Run 'rustup toolchain install nightly' to enable docs.rs dry-runs.${NC}"
fi

# ==============================================================================
# NEW STEP: Build All Examples, Then Run (excluding grids)
# ==============================================================================

echo -e "\n${YELLOW}[9/10] Compiling All Examples...${NC}"
echo -e "\n${YELLOW}[10/11] Compiling All Examples...${NC}"
# This mirrors CI Step 7: Ensures even grid.rs examples compile properly
cargo build --examples
echo -e "${GREEN}[OK] All examples compiled.${NC}"

echo -e "\n${YELLOW}[10/10] Running Examples (skipping *grid.rs)...${NC}"
echo -e "\n${YELLOW}[11/11] Running Examples (skipping *grid.rs except noop_grid)...${NC}"

# Iterate over all .rs files in the examples directory
for file in examples/*.rs; do
# 1. Extract filename (e.g., "news_breakout_grid.rs")
filename=$(basename "$file")

# 2. Extract example name (remove .rs extension)
example_name="${filename%.*}"

# 3. Filter: Check if filename contains "grid.rs"
if [[ "$filename" == *"grid.rs"* ]]; then
# 3. Filter: Check if filename contains "grid.rs", but explicitly ALLOW noop_grid
if [[ "$filename" == *"grid.rs"* && "$filename" != "noop_grid.rs" ]]; then
echo -e "${BLUE}[SKIP] Long-running example: $example_name (Compiled, but not run)${NC}"
continue
fi

echo -ne " Running example: $example_name ... "

# 4. Run the example
# Redirect stdout to /dev/null to keep terminal clean, but keep stderr for errors.
if cargo run --example "$example_name" > /dev/null; then
Expand All @@ -107,4 +125,4 @@ for file in examples/*.rs; do
fi
done

echo -e "\n${GREEN}>>> SUCCESS! All checks passed. Ready to push.${NC}"
echo -e "\n${GREEN}>>> SUCCESS! All checks passed. Ready to push.${NC}"
69 changes: 69 additions & 0 deletions examples/noop_grid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use anyhow::{Context, Result};
use chapaty::prelude::*;
use rayon::iter::ParallelBridge;
use serde::Serialize;
use std::path::Path;
use std::sync::Arc;

#[derive(Clone, Serialize)]
struct NoOpAgent;

impl Agent for NoOpAgent {
fn identifier(&self) -> AgentIdentifier {
AgentIdentifier::Named(Arc::new("NoOpAgent".to_string()))
}

fn reset(&mut self) {}

fn act(&mut self, _obs: Observation) -> ChapatyResult<Actions> {
// Return no actions, guaranteeing 0 trades
Ok(Actions::no_op())
}
}

#[tokio::main]
async fn main() -> Result<()> {
let mut env = environment().await?;
let num_agents = 5;
let agents: Vec<(usize, NoOpAgent)> = (0..num_agents).map(|uid| (uid, NoOpAgent)).collect();

let leaderboard = env.evaluate_agents(
agents.into_iter().par_bridge(),
10, // top_k
num_agents as u64,
)?;

println!(
"Evaluation complete. Leaderboard size: {}",
leaderboard.as_df().height()
);

let export_dir = Path::new("examples/reports/noop_grid");
let file_cfg = FileConfig::default().with_dir(export_dir);
leaderboard.to_file_sync(&file_cfg)?;

println!("Saved leaderboard to {}", export_dir.display());

Ok(())
}

async fn environment() -> Result<Environment> {
let preset = EnvPreset::BinanceBtcUsdt1d;
let file_stem = preset.to_string();
let loc = StorageLocation::HuggingFace { version: None };
let cfg = IoConfig::new(loc).with_file_stem(&file_stem);

chapaty::load(preset, &cfg)
.await
.context("Failed to load trading environment")
}

#[allow(dead_code)] // Provided for completeness
fn ohlcv_id() -> OhlcvId {
OhlcvId {
broker: DataBroker::Binance,
exchange: Exchange::Binance,
symbol: Symbol::Spot(SpotPair::BtcUsdt),
period: Period::Day(1),
}
}
Loading
Loading