From 96951687f6b8a5a3e71987d7329ddb7702535d8a Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:39:58 +0800 Subject: [PATCH 1/5] refactor: move get_current_timestamp_ms to utils and update references --- .../agents/common/trading/_internal/coordinator.py | 2 +- .../agents/common/trading/_internal/stream_controller.py | 2 +- .../agents/common/trading/decision/prompt_based/composer.py | 4 +--- .../agents/common/trading/features/market_snapshot.py | 2 +- python/valuecell/agents/common/trading/models.py | 6 +++++- python/valuecell/agents/common/trading/utils.py | 6 ------ python/valuecell/utils/ts.py | 6 ++++++ 7 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 python/valuecell/utils/ts.py diff --git a/python/valuecell/agents/common/trading/_internal/coordinator.py b/python/valuecell/agents/common/trading/_internal/coordinator.py index fba7aeef2..08d43f4ea 100644 --- a/python/valuecell/agents/common/trading/_internal/coordinator.py +++ b/python/valuecell/agents/common/trading/_internal/coordinator.py @@ -5,6 +5,7 @@ from loguru import logger +from valuecell.utils.ts import get_current_timestamp_ms from valuecell.utils.uuid import generate_uuid from ..decision import BaseComposer @@ -37,7 +38,6 @@ from ..utils import ( extract_market_snapshot_features, fetch_free_cash_from_gateway, - get_current_timestamp_ms, ) # Core interfaces for orchestration and portfolio service. diff --git a/python/valuecell/agents/common/trading/_internal/stream_controller.py b/python/valuecell/agents/common/trading/_internal/stream_controller.py index 0fed8b5ef..a2a33f359 100644 --- a/python/valuecell/agents/common/trading/_internal/stream_controller.py +++ b/python/valuecell/agents/common/trading/_internal/stream_controller.py @@ -15,8 +15,8 @@ from loguru import logger from valuecell.agents.common.trading import models as agent_models -from valuecell.agents.common.trading.utils import get_current_timestamp_ms from valuecell.server.services import strategy_persistence +from valuecell.utils.ts import get_current_timestamp_ms if TYPE_CHECKING: from valuecell.agents.common.trading._internal.coordinator import ( diff --git a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py index a7698c6a5..62487fd84 100644 --- a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py +++ b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py @@ -1,7 +1,6 @@ from __future__ import annotations import json -from datetime import datetime, timezone from typing import Dict from agno.agent import Agent as AgnoAgent @@ -210,9 +209,8 @@ async def _call_llm(self, prompt: str) -> TradePlanProposal: logger.error("LLM output failed validation: {}", content) return TradePlanProposal( - ts=int(datetime.now(timezone.utc).timestamp() * 1000), items=[], - rationale="LLM output failed validation", + rationale=f"LLM output failed validation: {content}", ) async def _send_plan_to_discord(self, plan: TradePlanProposal) -> None: diff --git a/python/valuecell/agents/common/trading/features/market_snapshot.py b/python/valuecell/agents/common/trading/features/market_snapshot.py index 12fbc9e95..203ca3576 100644 --- a/python/valuecell/agents/common/trading/features/market_snapshot.py +++ b/python/valuecell/agents/common/trading/features/market_snapshot.py @@ -11,7 +11,7 @@ InstrumentRef, MarketSnapShotType, ) -from valuecell.agents.common.trading.utils import get_current_timestamp_ms +from valuecell.utils.ts import get_current_timestamp_ms class MarketSnapshotFeatureComputer: diff --git a/python/valuecell/agents/common/trading/models.py b/python/valuecell/agents/common/trading/models.py index 163a8c283..2876edf77 100644 --- a/python/valuecell/agents/common/trading/models.py +++ b/python/valuecell/agents/common/trading/models.py @@ -12,6 +12,7 @@ DEFAULT_MAX_POSITIONS, DEFAULT_MODEL_PROVIDER, ) +from valuecell.utils.ts import get_current_timestamp_ms class TradingMode(str, Enum): @@ -568,7 +569,10 @@ def _normalize_instrument(cls, data): class TradePlanProposal(BaseModel): """Structured output before rule normalization.""" - ts: int + ts: Optional[int] = Field( + default_factory=get_current_timestamp_ms, + description="Proposal timestamp in ms (if available)", + ) items: List[TradeDecisionItem] = Field(default_factory=list) rationale: Optional[str] = Field( default=None, description="Optional natural language rationale" diff --git a/python/valuecell/agents/common/trading/utils.py b/python/valuecell/agents/common/trading/utils.py index 82e56b4f2..6742cead2 100644 --- a/python/valuecell/agents/common/trading/utils.py +++ b/python/valuecell/agents/common/trading/utils.py @@ -1,5 +1,4 @@ import os -from datetime import datetime, timezone from typing import Dict, List, Optional, Tuple import ccxt.pro as ccxtpro @@ -13,11 +12,6 @@ from valuecell.agents.common.trading.models import FeatureVector -def get_current_timestamp_ms() -> int: - """Get current timestamp in milliseconds.""" - return int(datetime.now(timezone.utc).timestamp() * 1000) - - async def fetch_free_cash_from_gateway( execution_gateway, symbols: list[str] ) -> Tuple[float, float]: diff --git a/python/valuecell/utils/ts.py b/python/valuecell/utils/ts.py new file mode 100644 index 000000000..aeda31373 --- /dev/null +++ b/python/valuecell/utils/ts.py @@ -0,0 +1,6 @@ +from datetime import datetime, timezone + + +def get_current_timestamp_ms() -> int: + """Get current timestamp in milliseconds.""" + return int(datetime.now(timezone.utc).timestamp() * 1000) From afa58ae51eeba99abd24b5063350a4a17638352c Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:39:58 +0800 Subject: [PATCH 2/5] fix(composer): enhance LLM output validation error message for clarity --- .../common/trading/decision/prompt_based/composer.py | 7 ++++++- python/valuecell/utils/model.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py index 62487fd84..b3e3536e9 100644 --- a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py +++ b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py @@ -210,7 +210,12 @@ async def _call_llm(self, prompt: str) -> TradePlanProposal: logger.error("LLM output failed validation: {}", content) return TradePlanProposal( items=[], - rationale=f"LLM output failed validation: {content}", + rationale=( + "LLM output failed validation. The model you chose " + f"`{model_utils.describe_model(model)}` " + "may be incompatible or returned unexpected output. " + f"Raw output: {content}", + ), ) async def _send_plan_to_discord(self, plan: TradePlanProposal) -> None: diff --git a/python/valuecell/utils/model.py b/python/valuecell/utils/model.py index 54105be36..be452c30d 100644 --- a/python/valuecell/utils/model.py +++ b/python/valuecell/utils/model.py @@ -9,7 +9,6 @@ - Backward compatible: Environment variables still work for model_id override """ -import logging import os from typing import Optional @@ -19,6 +18,7 @@ from agno.models.openai import OpenAILike as AgnoOpenAILikeModel from agno.models.openrouter import OpenRouter as AgnoOpenRouterModel from agno.models.siliconflow import Siliconflow as AgnoSiliconflowModel +from loguru import logger from valuecell.adapters.models.factory import ( create_embedder, @@ -27,7 +27,14 @@ create_model_for_agent, ) -logger = logging.getLogger(__name__) + +def describe_model(model: AgnoModel) -> str: + try: + model_description = f"{model.id} (via {model.provider})" + except Exception: + model_description = "unknown model/provider" + + return model_description def model_should_use_json_mode(model: AgnoModel) -> bool: From a6b8a9b0fcac9c42b47444c05066ad6e1e5c68f0 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:39:58 +0800 Subject: [PATCH 3/5] fix typo --- .../agents/common/trading/decision/prompt_based/composer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py index b3e3536e9..e78df3d5b 100644 --- a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py +++ b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py @@ -214,7 +214,7 @@ async def _call_llm(self, prompt: str) -> TradePlanProposal: "LLM output failed validation. The model you chose " f"`{model_utils.describe_model(model)}` " "may be incompatible or returned unexpected output. " - f"Raw output: {content}", + f"Raw output: {content}" ), ) From 3363ea172a4b9e212f839be36091c4ebd86a7a5d Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:39:58 +0800 Subject: [PATCH 4/5] make format --- .../agents/common/trading/decision/prompt_based/composer.py | 4 +++- python/valuecell/agents/common/trading/models.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py index e78df3d5b..bb087b12e 100644 --- a/python/valuecell/agents/common/trading/decision/prompt_based/composer.py +++ b/python/valuecell/agents/common/trading/decision/prompt_based/composer.py @@ -86,7 +86,9 @@ async def compose(self, context: ComposeContext) -> ComposeResult: return ComposeResult(instructions=[], rationale=plan.rationale) except Exception as exc: # noqa: BLE001 logger.error("LLM invocation failed: {}", exc) - return ComposeResult(instructions=[], rationale="LLM invocation failed") + return ComposeResult( + instructions=[], rationale=f"LLM invocation failed: {exc}" + ) # Optionally forward non-NOOP plan rationale to Discord webhook (env-driven) try: diff --git a/python/valuecell/agents/common/trading/models.py b/python/valuecell/agents/common/trading/models.py index 2876edf77..29d447559 100644 --- a/python/valuecell/agents/common/trading/models.py +++ b/python/valuecell/agents/common/trading/models.py @@ -4,6 +4,8 @@ from pydantic import BaseModel, Field, field_validator, model_validator +from valuecell.utils.ts import get_current_timestamp_ms + from .constants import ( DEFAULT_AGENT_MODEL, DEFAULT_CAP_FACTOR, @@ -12,7 +14,6 @@ DEFAULT_MAX_POSITIONS, DEFAULT_MODEL_PROVIDER, ) -from valuecell.utils.ts import get_current_timestamp_ms class TradingMode(str, Enum): From f144f74c44a89296c7a60aa20386721a0939b177 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:39:58 +0800 Subject: [PATCH 5/5] fix(provider): update default model to qwen3-max in configuration files --- docs/CONFIGURATION_GUIDE.md | 2 +- python/configs/providers/openrouter.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CONFIGURATION_GUIDE.md b/docs/CONFIGURATION_GUIDE.md index b00593fd0..321d943d8 100644 --- a/docs/CONFIGURATION_GUIDE.md +++ b/docs/CONFIGURATION_GUIDE.md @@ -269,7 +269,7 @@ models: # Fallback models for different providers provider_models: - siliconflow: "qwen/qwen-max" + siliconflow: "qwen/qwen3-max" google: "gemini-2.5-flash" # Model-specific parameters (override provider defaults) diff --git a/python/configs/providers/openrouter.yaml b/python/configs/providers/openrouter.yaml index 30a3188af..2f89555ec 100644 --- a/python/configs/providers/openrouter.yaml +++ b/python/configs/providers/openrouter.yaml @@ -12,7 +12,7 @@ connection: api_key_env: "OPENROUTER_API_KEY" # Default model if none specified -default_model: "qwen/qwen-max" +default_model: "qwen/qwen3-max" # Model Parameters Defaults defaults: @@ -29,8 +29,8 @@ models: name: "Claude Haiku 4.5" - id: "x-ai/grok-4" name: "Grok 4" - - id: "qwen/qwen-max" - name: "Qwen Max" + - id: "qwen/qwen3-max" + name: "Qwen3 Max" - id: "openai/gpt-5" name: "GPT-5" - id: "google/gemini-2.5-flash"