Files
Auto-Trader/agent/scripts/bench_performance.py
T
shadowinlife 8234058562 refactor: centralize environment variable management with Pydantic schema + CI gate
- Add agent/src/config/env_schema.py: Pydantic models for ~50 env vars across 6 sub-models (LLMConfig, DataConfig, APIConfig, SwarmConfig, AgentTuningConfig, PathConfig)
- Add agent/src/config/accessor.py: thread-safe lazy singleton get_env_config() + unified _parse_bool() + reset_env_config() for test isolation
- Fix TIMEOUT_SECONDS CLI display default (2400 -> 120, matching runtime)
- Fix MAX_RETRIES CLI display default ('(not set)' -> 2, matching runtime)
- Fix VIBE_TRADING_API_KEY server-side alias (security.py now resolves it via get_env_config().api.api_auth_key)
- Fix VIBE_TRADING_CHANNELS_AUTO_START=on silently ignored (unified _parse_bool accepts 'on')
- Migrate 207 scattered os.getenv/os.environ.get calls across 50+ files to centralized accessor
- Convert 12 module-level import-time fixations to lazy accessor pattern (loop.py, swarm_tool.py, _backend.py, worker.py, security.py)
- Add reset_env_config() calls after settings_routes.py env mutations (13 mutation points)
- Replace print('[WARN]...') with logger.warning() in tushare/okx/yfinance loaders
- Add tools/ci_env_var_gate.py: AST-based CI gate preventing new os.getenv calls outside src/config/
- Add tools/test_ci_env_var_gate.py: 18 test cases for the CI gate
- Update tools/ci_grep_gates.sh to call the new env var gate
- Add agent/tests/test_env_schema.py: 81 test cases covering defaults, type coercion, singleton, thread safety, bool parser
- Update agent/.env.example with schema cross-references
- Update agent/tests/conftest.py with reset_env_config autouse fixture

Closes HKUDS/Vibe-Trading#438

Co-Authored-By: OpenCode <noreply@opencode.ai>
AI-Model: alibaba-cn/glm-5.2

Signed-off-by: shadowinlife <shadowinlife@gmail.com>
Co-Authored-By: opencode <noreply@ai-tool.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
AI-Contributed/Feature: 1383/1383
AI-Contributed/UT: 735/735
2026-07-08 19:36:24 +08:00

134 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Performance benchmark: compare old vs new operator/equity paths.
Development-only script — not included in the package.
Run: python agent/scripts/bench_performance.py
"""
from __future__ import annotations
import os
import sys
import time
import numpy as np
import pandas as pd
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
def bench_operators():
"""Benchmark factor operators: old pandas vs new fast paths."""
from src.factors.base import decay_linear, ts_argmax, ts_argmin, ts_rank
np.random.seed(42)
df = pd.DataFrame(np.random.randn(5000, 100))
n = 20
print("=== Operator Benchmarks (5000 rows × 100 cols, window=20) ===\n")
ops = [
("ts_rank", lambda: ts_rank(df, n)),
("ts_argmax", lambda: ts_argmax(df, n)),
("ts_argmin", lambda: ts_argmin(df, n)),
("decay_linear", lambda: decay_linear(df, n)),
]
for name, fn in ops:
_ = fn()
t0 = time.perf_counter()
for _ in range(3):
fn()
elapsed = (time.perf_counter() - t0) / 3
print(f" {name:20s}: {elapsed:.3f}s")
print()
def bench_equity():
"""Benchmark _calc_equity: vectorized vs loop."""
from backtest.engines.base import BaseEngine
from backtest.models import Position
class _Stub(BaseEngine):
def can_execute(self, *a):
return True
def round_size(self, s, p):
return s
def calc_commission(self, *a):
return 0.0
def apply_slippage(self, p, d):
return p
np.random.seed(42)
n_symbols = 50
n_days = 1000
symbols = [f"SYM{i:03d}" for i in range(n_symbols)]
dates = pd.date_range("2020-01-01", periods=n_days, freq="B")
close_df = pd.DataFrame(
np.cumsum(np.random.randn(n_days, n_symbols), axis=0) + 100,
index=dates,
columns=symbols,
)
engine = _Stub({"initial_cash": 10_000_000})
engine.capital = 5_000_000
for i, sym in enumerate(symbols):
engine.positions[sym] = Position(
symbol=sym,
direction=1 if i % 2 == 0 else -1,
size=100.0 + i * 10,
entry_price=95.0 + i,
leverage=1.0,
entry_time=dates[0],
)
ts = dates[500]
_ = engine._calc_equity(close_df, ts)
t0 = time.perf_counter()
for _ in range(1000):
engine._calc_equity(close_df, ts)
vec_time = (time.perf_counter() - t0) / 1000
# Force loop path by monkey-patching
original_pnl = type(engine)._calc_pnl
def _loop_pnl(self, *a):
return original_pnl(self, *a)
type(engine)._calc_pnl = _loop_pnl
_ = engine._calc_equity(close_df, ts)
t0 = time.perf_counter()
for _ in range(1000):
engine._calc_equity(close_df, ts)
loop_time = (time.perf_counter() - t0) / 1000
type(engine)._calc_pnl = original_pnl
speedup = loop_time / vec_time if vec_time > 0 else float("inf")
print("=== Equity Calculation (50 positions, 1000 iterations) ===\n")
print(f" Vectorized: {vec_time * 1e6:.1f} µs/call")
print(f" Loop: {loop_time * 1e6:.1f} µs/call")
print(f" Speedup: {speedup:.1f}x")
print()
if __name__ == "__main__":
print("Vibe-Trading Performance Benchmark")
print("=" * 50)
print()
from src.factors._backend import HAS_BOTTLENECK
print(f"Bottleneck available: {HAS_BOTTLENECK}")
from src.config.accessor import get_env_config
print(f"VIBE_TRADING_DISABLE_BOTTLENECK: {get_env_config().agent_tuning.vibe_trading_disable_bottleneck}")
print()
bench_operators()
bench_equity()