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
32 changes: 32 additions & 0 deletions .github/workflows/develop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Develop CI

on:
push:
branches: [develop]
pull_request:
branches: [develop]

jobs:
fast-tests:
name: Lint & Fast Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

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

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pandas numpy scipy pyarrow PyYAML omegaconf antlr4-python3-runtime datasets pytest ruff

- name: Run linter
run: ruff check .

- name: Run fast-tier tests
run: PYTHONPATH=. pytest tests/
32 changes: 32 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Main CI

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

jobs:
full-tests:
name: Lint & Full Test Suite
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

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

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pandas numpy scipy pyarrow PyYAML omegaconf antlr4-python3-runtime datasets pytest ruff

- name: Run linter
run: ruff check .

- name: Run full test suite
run: PYTHONPATH=. pytest tests/
6 changes: 3 additions & 3 deletions config/training_vast_qlora_grpo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ model:
adapter: qlora

grpo:
script: factor_lab.training.train_grpo_qlora
script: src.training.train_grpo_qlora
objective: executable_factor_reward
reward_source: factor_lab.training.grpo_reward.make_grpo_reward_func
reward_source: src.training.grpo_reward.make_grpo_reward_func
reward_components:
- DSL validity
- daily cross-sectional RankIC
Expand All @@ -23,6 +23,6 @@ grpo:
loss_type: dapo

data:
local_crypto_panel: ClassProject/data/crypto_panel_clean.pkl
local_crypto_panel: data/crypto/crypto_panel_clean.pkl
wrds_crsp_target: daily_cross_sectional_rankic
taq_target: intraday_microstructure_direction
2 changes: 1 addition & 1 deletion config/verl_qwen25_7b_grpo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ model:
data:
train_parquet: factor_lab/outputs/verl/crypto_grpo_tasks.parquet
crypto_panel: data/crypto_panel_clean.pkl
tickers: BTC-USD,ETH-USD,XRP-USD
tickers: ADA-USD,BNB-USD,BTC-USD,DOGE-USD,ETH-USD,LINK-USD,XLM-USD,XRP-USD

reward:
function: factor_lab.verl.reward_function.reward_fn
Expand Down
4 changes: 3 additions & 1 deletion config/verl_qwen3_14b_fullft_a100.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ data:
train_parquet: outputs/verl/crypto_grpo_tasks.parquet
val_parquet: outputs/verl/crypto_grpo_val_tasks.parquet
crypto_panel: data/crypto/crypto_panel_clean.pkl
tickers: BTC-USD,ETH-USD,XRP-USD
tickers: ADA-USD,BNB-USD,BTC-USD,DOGE-USD,ETH-USD,LINK-USD,XLM-USD,XRP-USD
max_prompt_length: 1536
max_response_length: 256

Expand Down Expand Up @@ -58,4 +58,6 @@ verl:
nnodes: 1
n_gpus_per_node: 8
save_freq: 20
test_freq: 20
val_before_train: true
total_epochs: 1
17 changes: 9 additions & 8 deletions examples/baseline_rollout.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from examples.build_seed_bank import crypto_frames_from_panel
from examples.dsl_smoke_test import DEFAULT_CRYPTO_PANEL, load_dotenv
from src.data import verify_crypto_panel
from src.rft import (
DatabaseSelectionConfig,
MinerConfig,
Expand All @@ -33,19 +34,19 @@

def run_crypto_baseline_rollout(
*,
oracle_model: str,
miner_model: str,
oracle_model: str = "deepseek/deepseek-chat-v3.1",
miner_model: str = "qwen/qwen3-235b-a22b-2507",
panel_path: Path = DEFAULT_CRYPTO_PANEL,
tickers: tuple[str, ...] | None = None,
oracle_count: int = 12,
oracle_count: int = 24,
miner_count: int = 4,
top_k_seeds: int = 3,
output_dir: Path = DEFAULT_OUTPUT_DIR,
) -> dict:
"""Run oracle seed generation plus miner baseline rollouts on crypto data."""

load_dotenv()
panel = pd.read_pickle(panel_path)
panel = verify_crypto_panel(panel_path, tickers=tickers)
frames = crypto_frames_from_panel(panel, tickers=tickers)
close = panel["close"]
scenario = FactorScenario.from_benchmark(
Expand All @@ -70,7 +71,7 @@ def run_crypto_baseline_rollout(
windows=windows,
raw_candidates=raw_candidates,
min_history=30,
min_assets=max(3, min(5, len(frames))),
min_assets=8,
pool_config=SeedPoolConfig(top_k=top_k_seeds, quality_threshold=-1.0),
)

Expand All @@ -84,7 +85,7 @@ def run_crypto_baseline_rollout(
price_col="close",
count=miner_count,
min_history=30,
min_assets=max(3, min(5, len(frames))),
min_assets=8,
miner_config=MinerConfig(model=miner_model),
archive=archive,
selection_config=DatabaseSelectionConfig(min_score=-1.0, min_reward=-1.0),
Expand Down Expand Up @@ -143,8 +144,8 @@ def main() -> int:
parser.add_argument("--oracle-model", default="deepseek/deepseek-chat-v3.1")
parser.add_argument("--miner-model", default="qwen/qwen3-235b-a22b-2507")
parser.add_argument("--crypto-panel", type=Path, default=DEFAULT_CRYPTO_PANEL)
parser.add_argument("--tickers", default="BTC-USD,ETH-USD,XRP-USD")
parser.add_argument("--oracle-count", type=int, default=12)
parser.add_argument("--tickers", default="ADA-USD,BNB-USD,BTC-USD,DOGE-USD,ETH-USD,LINK-USD,XLM-USD,XRP-USD")
parser.add_argument("--oracle-count", type=int, default=24)
parser.add_argument("--miner-count", type=int, default=4)
parser.add_argument("--top-k-seeds", type=int, default=3)
parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT_DIR)
Expand Down
13 changes: 5 additions & 8 deletions examples/build_seed_bank.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pandas as pd

from src.data import adapt_crypto_ohlcv, adapt_crsp_dsf_v2
from src.data import adapt_crsp_dsf_v2, adapt_crypto_ohlcv, verify_crypto_panel
from examples.dsl_smoke_test import (
DEFAULT_CRYPTO_PANEL,
fetch_wrds_crsp_dsf_v2_sample,
Expand All @@ -25,11 +25,8 @@
def crypto_frames_from_panel(panel: dict, tickers: tuple[str, ...] | None = None) -> dict[str, pd.DataFrame]:
"""Convert saved crypto panel dict into per-asset DSL-ready frames."""

verify_crypto_panel(panel, tickers=tickers)
required = ["open", "high", "low", "close", "volume"]
missing = [key for key in required if key not in panel]
if missing:
raise ValueError(f"crypto panel missing keys: {missing}")

available = tuple(str(col) for col in panel["close"].columns)
selected = tickers or available
frames = {}
Expand All @@ -51,7 +48,7 @@ def build_crypto_cross_sectional_seed_bank(
) -> dict:
"""Build a cross-sectional crypto seed bank from the saved project panel."""

panel = pd.read_pickle(panel_path)
panel = verify_crypto_panel(panel_path, tickers=tickers)
frames = crypto_frames_from_panel(panel, tickers=tickers)
close = panel["close"]
windows = [
Expand All @@ -71,7 +68,7 @@ def build_crypto_cross_sectional_seed_bank(
windows=windows,
raw_candidates=raw_candidates,
min_history=30,
min_assets=max(3, min(5, len(frames))),
min_assets=8,
pool_config=SeedPoolConfig(top_k=top_k, quality_threshold=quality_threshold),
)
return {"result": result, "asset_count": len(frames)}
Expand Down Expand Up @@ -128,7 +125,7 @@ def main() -> int:
parser.add_argument("--candidate-source", choices=("template", "openrouter", "file"), default="template")
parser.add_argument("--oracle-output-file", type=Path)
parser.add_argument("--oracle-count", type=int, default=24)
parser.add_argument("--openrouter-model", default="deepseek/deepseek-chat")
parser.add_argument("--openrouter-model", default="deepseek/deepseek-chat-v3.1")
parser.add_argument("--wrds-crsp", action="store_true", help="also build a tiny live WRDS CRSP seed bank")
parser.add_argument("--wrds-permno", type=int, default=14593)
parser.add_argument("--wrds-start", default="2023-01-01")
Expand Down
2 changes: 1 addition & 1 deletion examples/check_openrouter_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def main() -> int:
parser.add_argument("--namespace")
parser.add_argument("--count", type=int, default=12)
parser.add_argument("--seed-expr", default="div(ts_mean(crypto.volume(10)), ts_std(crypto.returns(30)))")
parser.add_argument("--seed-score", type=float, default=0.655671862964597)
parser.add_argument("--seed-score", type=float, default=0.38385972330719526)
parser.add_argument("--max-tokens", type=int, default=1)
args = parser.parse_args()

Expand Down
9 changes: 3 additions & 6 deletions examples/dsl_smoke_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import pandas as pd

from src.data import adapt_crypto_ohlcv, adapt_crsp_dsf_v2
from src.data import adapt_crsp_dsf_v2, adapt_crypto_ohlcv, verify_crypto_panel
from src.dsl import PointInTimeContext, evaluate_expr


Expand Down Expand Up @@ -71,11 +71,8 @@ def load_dotenv(path: Path | None = None) -> None:
def crypto_asset_frame_from_panel(panel: dict, ticker: str) -> pd.DataFrame:
"""Build one asset OHLCV frame from the saved crypto panel dictionary."""

verify_crypto_panel(panel, tickers=(ticker,))
required = ["open", "high", "low", "close", "volume"]
missing = [key for key in required if key not in panel]
if missing:
raise ValueError(f"crypto panel missing keys: {missing}")

frame = pd.DataFrame({key: panel[key][ticker] for key in required})
if "returns" in panel:
frame["returns"] = panel["returns"][ticker]
Expand Down Expand Up @@ -106,7 +103,7 @@ def evaluate_last_available(
def run_crypto_smoke(panel_path: Path = DEFAULT_CRYPTO_PANEL, ticker: str = "BTC-USD") -> dict[str, float]:
"""Run the crypto namespace smoke test on the saved project panel."""

panel = pd.read_pickle(panel_path)
panel = verify_crypto_panel(panel_path, tickers=(ticker,))
frame = crypto_asset_frame_from_panel(panel, ticker)
adapted = adapt_crypto_ohlcv(frame)
value = evaluate_last_available("crypto", adapted, CRYPTO_EXPR, min_history=25)
Expand Down
11 changes: 6 additions & 5 deletions examples/evaluate_rollout_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@

import pandas as pd

from src.evaluation import PostSelectionConfig, evaluate_factor_library
from src.evaluation.post_selection import write_evaluation_report
from examples.baseline_rollout import DEFAULT_OUTPUT_DIR
from examples.build_seed_bank import crypto_frames_from_panel
from examples.dsl_smoke_test import DEFAULT_CRYPTO_PANEL
from src.data import verify_crypto_panel
from src.evaluation import PostSelectionConfig, evaluate_factor_library
from src.evaluation.post_selection import write_evaluation_report


def load_valid_rollout_exprs(path: Path) -> list[str]:
Expand Down Expand Up @@ -46,7 +47,7 @@ def run_crypto_rollout_library_evaluation(
"""Run validation-guided decorrelated selection and equal-weight fusion."""

exprs = load_valid_rollout_exprs(rollout_json)
panel = pd.read_pickle(panel_path)
panel = verify_crypto_panel(panel_path, tickers=tickers)
frames = crypto_frames_from_panel(panel, tickers=tickers)
if validation_start is None or validation_end is None or test_start is None or test_end is None:
dates = pd.DatetimeIndex(panel["close"].index).sort_values()
Expand All @@ -69,7 +70,7 @@ def run_crypto_rollout_library_evaluation(
correlation_threshold=correlation_threshold,
top_k=top_k,
min_history=30,
min_assets=max(3, min(5, len(frames))),
min_assets=8,
horizon=1,
),
)
Expand All @@ -82,7 +83,7 @@ def main() -> int:
parser.add_argument("--rollout-json", type=Path, default=DEFAULT_OUTPUT_DIR / "baseline_rollout.json")
parser.add_argument("--crypto-panel", type=Path, default=DEFAULT_CRYPTO_PANEL)
parser.add_argument("--output-path", type=Path, default=DEFAULT_OUTPUT_DIR / "paper_style_evaluation.json")
parser.add_argument("--tickers", default="BTC-USD,ETH-USD,XRP-USD")
parser.add_argument("--tickers", default="ADA-USD,BNB-USD,BTC-USD,DOGE-USD,ETH-USD,LINK-USD,XLM-USD,XRP-USD")
parser.add_argument("--validation-start")
parser.add_argument("--validation-end")
parser.add_argument("--test-start")
Expand Down
6 changes: 3 additions & 3 deletions examples/launch_verl_qwen3_14b_a100.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ set -euo pipefail
# with a fallback default, so build_dataset below and the eventual Verl
# reward_fn agree on the same crypto data/tickers/output locations).
export FACTOR_LAB_CRYPTO_PANEL="${FACTOR_LAB_CRYPTO_PANEL:-data/crypto/crypto_panel_clean.pkl}"
export FACTOR_LAB_TICKERS="${FACTOR_LAB_TICKERS:-BTC-USD,ETH-USD,XRP-USD}"
export FACTOR_LAB_TICKERS="${FACTOR_LAB_TICKERS:-ADA-USD,BNB-USD,BTC-USD,DOGE-USD,ETH-USD,LINK-USD,XLM-USD,XRP-USD}"
export FACTOR_LAB_ARCHIVE_JSONL="${FACTOR_LAB_ARCHIVE_JSONL:-outputs/verl/qwen3_14b_fullft/mined_factors.jsonl}"
export FACTOR_LAB_REWARD_LOG_JSONL="${FACTOR_LAB_REWARD_LOG_JSONL:-outputs/verl/qwen3_14b_fullft/reward_rollouts.jsonl}"

Expand All @@ -19,7 +19,7 @@ export FACTOR_LAB_REWARD_LOG_JSONL="${FACTOR_LAB_REWARD_LOG_JSONL:-outputs/verl/
python -m src.verl_integration.build_dataset \
--output outputs/verl/crypto_grpo_tasks.parquet \
--seed-expr "div(ts_mean(crypto.volume(10)), ts_std(crypto.returns(30)))" \
--seed-score 0.655671862964597 \
--seed-score 0.38385972330719526 \
--crypto-panel "${FACTOR_LAB_CRYPTO_PANEL}" \
--tickers "${FACTOR_LAB_TICKERS}" \
--repeats 400
Expand All @@ -29,7 +29,7 @@ python -m src.verl_integration.build_dataset \
python -m src.verl_integration.build_dataset \
--output outputs/verl/crypto_grpo_val_tasks.parquet \
--seed-expr "div(ts_mean(crypto.volume(10)), ts_std(crypto.returns(30)))" \
--seed-score 0.655671862964597 \
--seed-score 0.38385972330719526 \
--crypto-panel "${FACTOR_LAB_CRYPTO_PANEL}" \
--tickers "${FACTOR_LAB_TICKERS}" \
--repeats 32
Expand Down
2 changes: 1 addition & 1 deletion examples/run_compared_approaches.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def main() -> int:
parser = argparse.ArgumentParser(description="Run compared alpha-discovery benchmark approaches.")
parser.add_argument("--model", default="qwen/qwen3-235b-a22b-2507")
parser.add_argument("--crypto-panel", type=Path, default=DEFAULT_CRYPTO_PANEL)
parser.add_argument("--tickers", default="BTC-USD,ETH-USD,XRP-USD")
parser.add_argument("--tickers", default="ADA-USD,BNB-USD,BTC-USD,DOGE-USD,ETH-USD,LINK-USD,XLM-USD,XRP-USD")
parser.add_argument(
"--approaches",
default="alphabench,quantaalpha,rd_agent,alpha_jungle,factor_lab",
Expand Down
Loading
Loading