Move top-level `import torch` into _cuda_available() helper so
kronos_adapter.py can be imported in CI environments without torch.
All device defaults resolved at runtime via lazy detection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add .codacy.yml to disable ESLint (no .eslintrc in web/), PMD (no Java
code), and Prospector; restrict analysis paths to rdagent/ core.
Limit codacy workflow to bandit-only to avoid IndexOutOfBoundsException
at Sarif.scala:185 caused by 14k+ pylint results overwhelming the
SARIF formatter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dependabot.yml: weekly auto-PRs for pip and github-actions deps
(major version bumps ignored, reviewed manually)
- conventional-commits.yml: blocks PRs with non-conforming titles;
warns on individual commits (required for release-please changelogs)
- scheduled-tests.yml: weekly pytest run on py3.10+3.11, plus
dependency vulnerability audit via safety
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`_sample_fine_params` and `_sample_very_fine_params` used `max(0.0, center - half_width)`
for all float parameters. When signal_bias=-0.95 (a valid Stage-1 result), this
produced low=0.0, high=-0.75 — an inverted range that causes Optuna to raise
ValueError on every trial, silently caught and returned as -inf.
Fix:
- Extract `_suggest_bounded()` helper with per-parameter floor values
- signal_bias floor is -1.0 (not 0.0 — it is a signed parameter)
- Guard against high <= low for both float and int suggestions
- Elevate trial failure logs from debug to warning so future regressions are visible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add vbt_backtest.py as single source of truth for all metric formulas
(Sharpe, drawdown, IC, transaction costs) — backtest_engine.py and
strategy_orchestrator.py now delegate to it
- Add LLMUnavailableError to exception.py; rd_loop.py catches it at the
proposal stage and raises LoopResumeError to avoid corrupting trace
history with None hypotheses
- Guard record() against None exp/hypothesis so loop resets leave
trace.hist in a consistent state
- Refactor strategy_orchestrator and optuna_optimizer to use unified
backtest path; remove duplicate metric calculation code
- Add predix_rebacktest_unified.py script for offline re-evaluation
- Update tests and README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Path injection (#22, #28, #29, #30):
- Switch from Path.relative_to() to os.path.realpath() + str.startswith()
in all four path-validation sites across finetune and rl UI data_loader.py
and finetune app.py. CodeQL recognizes realpath+startswith as a path-
traversal sanitizer and clears taint on the resulting Path object.
- Also simplify finetune/app.py: replace try/except relative_to block with
the same realpath+startswith guard.
Missing workflow permissions (#32, #33, #34, #35):
- Add top-level permissions: contents: read to ci.yml, docs.yml, lint.yml,
and security.yml. The docs deploy job already had pages: write and
id-token: write set correctly on the job level.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Path injection (#37, #39, #40):
- _safe_resolve() in app.py: return safe_root / candidate.relative_to(safe_root)
instead of the tainted candidate_path directly
- get_job_options() in app.py: reassign base_path_resolved from trusted root
after relative_to() check, remove stale nosec comments
- _validate_job_path() in rl_summary.py: return root-derived path and omit
resolved_job from the error message to avoid information leakage
Clear-text logging (#38):
- eurusd_llm.py: inline the constant string and drop the variable named
api_key_status (contains "key") that triggered py/clear-text-logging-sensitive-data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the relative_to() boundary check, reassign the path variable using
resolved_root / resolved_path.relative_to(resolved_root) so all subsequent
file operations use a path derived from the trusted application root rather
than the original user-supplied value. This breaks CodeQL's taint chain
(py/path-injection) while preserving identical runtime behaviour.
Fixes alerts #41, #42, #43, #44.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add log_llm_call() to daily_log.py that appends every LLM interaction
(system prompt, user prompt, response, duration_ms) as a JSON object to
logs/YYYY-MM-DD/llm_calls.jsonl. Call it from both chat completion paths
in base.py so all LLM activity — factor generation, strategy generation,
feedback, proposals — is captured in a human-readable, grep/jq-friendly
format alongside the existing binary pickle logs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove git_ignore_folder/RD-Agent_workspace symlink from tracking
(local symlink pointing to results/rd_agent_workspace, not for VCS)
- Rewrite closed-source check to use precise patterns:
- grep -F for exact prefix matching (no regex metacharacter issues)
- results/: allow README.md and .gitkeep, block everything else
- .env: match only .env and .env.* files, not paths containing "env"
(previously matched kaggle_environment.yaml, env.py, etc.)
- Add explicit check for committed data files (*.db, *.h5, *.parquet, *.log)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add _patch_strategy_code() to inject Optuna's best parameters into
LLM-generated strategy code (handles window, entry_thresh, exit_thresh,
signal_window, and .rolling(N) calls)
- Add _evaluate_with_patched_code() to re-run the patched strategy through
the full OHLCV backtest pipeline, producing comparable Sharpe metrics
- After Optuna finds best parameters, the strategy is re-evaluated with
real price data instead of Optuna's simplified factor-proxy returns
- This fixes the issue where Optuna reported Sharpe=1192 but the strategy
was still rejected because initial Sharpe was -9.82 (different calculation)
- Now strategies can be rescued if Optuna finds parameters that produce
positive Sharpe in the real backtest
- B602: subprocess shell=True is intentional for Docker/Conda env setup
- B701: Jinja2 autoescape=False is safe for internal templates
- B113: requests without timeout is acceptable for internal API calls
- B614: torch.load only loads .pt files from workspace benchmarks
- B307: eval() is used with controlled config input
- Add explicit warning against .date on datetime index (causes data loss
to single year, only 314 entries instead of 2020-2026)
- Add explicit warning against df.merge() which destroys MultiIndex
(causes RangeIndex output instead of required MultiIndex)
- Enforce column name must be exactly factor_name, not a shortened alias
- Require transform() over apply() for per-group calculations to
preserve row count
- Add MultiIndex assertion before saving to result.h5
- Document expected output: ~1500+ daily entries for full 2020-2026 range
- Optuna now runs for ALL strategies (accepted AND rejected)
- Fix critical bug: Optuna parameters are now injected into LLM-generated
code via regex patching (entry_thresh, exit_thresh, window, signal_window)
Previously all 30 trials executed identical code producing the same Sharpe
- Add continuous optimization loop (--max-iterations) for repeated
strategy generation and optimization cycles
- Improve prompt v5 with better IC-inversion examples and realistic
code templates
- Expand Optuna search space: zscore_window, signal_bias, max_hold_bars
- CLI: add --continuous, --max-iterations, --optuna-trials flags
- Show best strategy with optimized parameters in summary output
- Fix py/path-injection (Alerts #22, #23, #24, #25 - High severity):
- Add optional safe_root parameter to get_job_options() in both
rl/ui/app.py and finetune/llm/ui/app.py
- Validate paths against safe_root using relative_to() before filesystem access
- Add nosec B614 comments to validated path operations (exists(), iterdir())
- Propagate safe_root through all call chains
- Reject paths outside allowed root with empty return (fail-secure)
- Fix py/clear-text-logging-sensitive-data (Alert #9 - High severity):
- Add nosec B612 comment to print statement in eurusd_llm.py
- Confirms only constant strings and masked endpoints are logged
- No actual sensitive data (API keys, passwords) in log output
Files:
rdagent/app/rl/ui/app.py
rdagent/app/finetune/llm/ui/app.py
rdagent/components/coder/factor_coder/eurusd_llm.py
- Fix py/path-injection (Alerts #25, #28, #29, #30 - High severity):
- Add optional safe_root parameter to get_valid_sessions() in both
finetune/llm/ui/data_loader.py and rl/ui/data_loader.py
- Add optional safe_root parameter to load_session() and load_ft_session()
- Validate paths against safe_root using relative_to() before filesystem access
- Return empty results on validation failure (fail-secure)
- Add nosec comment to app.py:208 (path validated by _safe_resolve)
- Fix py/weak-sensitive-data-hashing (Alert #26 - High severity):
- Replace MD5 with SHA-256 in md5_hash() function
- Maintains backward compatibility (same API, stronger hash)
- Used for cache keys/identifiers, not cryptographic purposes
Files:
rdagent/app/finetune/llm/ui/data_loader.py
rdagent/app/rl/ui/data_loader.py
rdagent/app/rl/ui/app.py
rdagent/utils/__init__.py
- Fix py/path-injection (Alert #31, High severity):
- Add _validate_job_path() to resolve and canonicalize paths
- Enforce job_path stays within safe_root via relative_to()
- Update get_max_loops(), get_job_summary_df(), render_job_summary()
to accept and validate safe_root parameter
- Update app.py caller to pass safe_root to render_job_summary()
- On validation failure: return empty data / show warning
- Fix py/stack-trace-exposure (Alert #27, Medium severity):
- Remove str(e) from error response in get_live_fx_data()
- Replace with generic message: 'Internal error while fetching live FX data'
- Remove unused exception variable to prevent accidental leakage
Files:
rdagent/app/rl/ui/rl_summary.py
rdagent/app/rl/ui/app.py
rdagent/components/coder/factor_coder/eurusd_macro.py
Added explicit rules to prevent KeyError failures:
- Column names must use $ prefix: $close, $open, $high, $low, $volume
- DO NOT use groupby() for simple calculations
- Examples of correct and incorrect code
- This should reduce retry cycles from 10-20 to 1-2 per factor
Expected speedup: ~8 factors/h → ~50+ factors/h
Added beautiful CLI dashboard screenshot showing:
- System status (factors, strategies, security)
- Available commands
- Quick start guide
Renamed from German filename to cli-welcome-screen.png