Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b7b346d
feat: add planner passthrough support for agents and enhance planning…
vcfgv Nov 5, 2025
d2b0ad9
feat: implement StrategyAgent streaming and decision cycle with enhan…
vcfgv Nov 5, 2025
cff38d6
feat: enhance StrategyAgent streaming with portfolio updates and refi…
vcfgv Nov 5, 2025
087d432
feature: add ccxt as dependency
vcfgv Nov 5, 2025
75b6e63
feat: refactor StrategyAgent and DecisionCoordinator for async suppor…
vcfgv Nov 5, 2025
18aa0ee
feat: enhance feature computation with technical indicators using pan…
vcfgv Nov 5, 2025
328622a
feat: update PortfolioService to expose apply_trades method for state…
vcfgv Nov 5, 2025
f7dce68
feat: refactor Composer interface and implement LlmComposer
vcfgv Nov 6, 2025
5a57996
feat: enhance logging in LlmComposer for better traceability and debu…
vcfgv Nov 6, 2025
9eedbbe
feat: enhance InMemoryPortfolioService to compute derived metrics and…
vcfgv Nov 6, 2025
bf2923a
make format
vcfgv Nov 6, 2025
784ca85
feat: add aggressive and insane trading strategy templates
vcfgv Nov 6, 2025
5293a0a
feat: enhance trading execution and portfolio management with constra…
vcfgv Nov 6, 2025
9d56f34
feat: implement sub-step handling for trade instructions to enforce s…
vcfgv Nov 6, 2025
901d68a
feat: update PortfolioView to use buying_power instead of available_c…
vcfgv Nov 6, 2025
7a91b14
fix: correct logging format in StrategyAgent and simplify target quan…
vcfgv Nov 6, 2025
16fb542
feat: enhance decision coordination with authoritative unrealized PnL…
vcfgv Nov 6, 2025
38a93ff
feat: add conservative slippage handling in LlmComposer to improve un…
vcfgv Nov 6, 2025
c14b848
feat: enhance trade history pairing by detecting closes and annotatin…
vcfgv Nov 6, 2025
5833287
feat: make cap_factor configurable in LlmComposer and add to TradingC…
vcfgv Nov 6, 2025
60c9682
refactor: streamline trade application by removing redundant method a…
vcfgv Nov 6, 2025
e7cf3cd
refactor: simplify DefaultDecisionCoordinator constructor by removing…
vcfgv Nov 6, 2025
d12e965
feat: enhance trade note handling by preserving LLM rationale and app…
vcfgv Nov 6, 2025
a4d2e8f
feat: enhance trade history entry creation by implementing full close…
vcfgv Nov 6, 2025
0ed4054
feat: add fee_cost to TradeHistoryEntry and adjust cash flow calculat…
vcfgv Nov 6, 2025
69735b3
refactor: move components out of runtime.py
vcfgv Nov 6, 2025
0c28a15
fix: update template_id in test_agent to use aggressive strategy
vcfgv Nov 7, 2025
963227c
feat: enhance logging and error handling in StrategyAgent and market …
vcfgv Nov 7, 2025
f395bbf
feat: implement trade history persistence and portfolio view handling
vcfgv Nov 7, 2025
3b50c6c
feat: enhance strategy persistence with improved trade history and su…
vcfgv Nov 7, 2025
1104ffe
feat: add comprehensive strategy template
vcfgv Nov 7, 2025
f62690b
feature: add strategy agent launch script
vcfgv Nov 7, 2025
2e017ba
feat: enhance strategy agent with improved runtime checks and logging
vcfgv Nov 7, 2025
a246de7
refactor: remove unused base_prices and counters from SimpleMarketDat…
vcfgv Nov 7, 2025
2809c70
make format
vcfgv Nov 7, 2025
70ebea9
fix tests
vcfgv Nov 7, 2025
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
29 changes: 29 additions & 0 deletions python/configs/agent_cards/strategy_agent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "StrategyAgent",
"display_name": "Strategy Agent",
"description": "LLM-driven strategy composer that turns market features into normalized trade instructions. Includes a simple runtime for demo and testing.",
"capabilities": {
"streaming": true,
"push_notifications": true
},
"skills": [
{
"id": "strategy_run",
"name": "Run Strategy",
"description": "Start a strategy using the provided model, exchange and trading configuration.",
"examples": [
"Run strategy with DeepSeek model on BTC-USD and ETH-USD",
"Start a virtual backtest with $10,000 initial capital for BTC"
],
"tags": ["strategy", "run", "trading", "compose"]
}
],
"enabled": true,
"metadata": {
"planner_passthrough": true,
"version": "0.1.0",
"author": "ValueCell Team",
"tags": ["strategy", "trading", "llm", "demo"],
"notes": "This card is a lightweight example; replace model api_key and tune parameters for production use."
}
}
1 change: 1 addition & 0 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies = [
"loguru>=0.7.3",
"aiofiles>=24.1.0",
"crawl4ai>=0.7.4",
"ccxt>=4.5.15",
]

[project.optional-dependencies]
Expand Down
11 changes: 10 additions & 1 deletion python/scripts/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@
RESEARCH_AGENT_NAME = "ResearchAgent"
AUTO_TRADING_AGENT_NAME = "AutoTradingAgent"
NEWS_AGENT_NAME = "NewsAgent"
STRATEGY_AGENT_NAME = "StrategyAgent"
# AGENTS = list(MAP_NAME_ANALYST.keys()) + [
# TRADING_AGENTS_NAME,
# RESEARCH_AGENT_NAME,
# AUTO_TRADING_AGENT_NAME,
# ]
AGENTS = [RESEARCH_AGENT_NAME, AUTO_TRADING_AGENT_NAME, NEWS_AGENT_NAME]
AGENTS = [
RESEARCH_AGENT_NAME,
AUTO_TRADING_AGENT_NAME,
NEWS_AGENT_NAME,
STRATEGY_AGENT_NAME,
]

PROJECT_DIR = Path(__file__).resolve().parent.parent.parent
PYTHON_DIR = PROJECT_DIR / "python"
Expand Down Expand Up @@ -78,6 +84,9 @@
MAP_NAME_COMMAND[NEWS_AGENT_NAME] = (
f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.news_agent"
)
MAP_NAME_COMMAND[STRATEGY_AGENT_NAME] = (
f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.strategy_agent"
)
BACKEND_COMMAND = (
f"cd {PYTHON_DIR_STR} && uv run --env-file {ENV_PATH_STR} -m valuecell.server.main"
)
Expand Down
107 changes: 105 additions & 2 deletions python/uv.lock

Large diffs are not rendered by default.

119 changes: 106 additions & 13 deletions python/valuecell/agents/strategy_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
from __future__ import annotations

import asyncio
from datetime import datetime
from typing import AsyncGenerator, Dict, Optional

from loguru import logger

from valuecell.core.agent.responses import streaming
from valuecell.core.types import BaseAgent, StreamResponse
from valuecell.server.services import strategy_persistence

from .models import (
ComponentType,
StrategyStatus,
StrategyStatusContent,
UserRequest,
)
from .runtime import create_strategy_runtime

class StrategyAgent(BaseAgent):
"""Minimal StrategyAgent entry for system integration.

This is a placeholder agent that streams a short greeting and completes.
It can be extended to wire the Strategy Agent decision loop
(data -> features -> composer -> execution -> history/digest).
"""

def __init__(self, **kwargs):
super().__init__(**kwargs)
class StrategyAgent(BaseAgent):
"""Top-level Strategy Agent integrating the decision coordinator."""

async def stream(
self,
Expand All @@ -24,8 +29,96 @@ async def stream(
task_id: str,
dependencies: Optional[Dict] = None,
) -> AsyncGenerator[StreamResponse, None]:
# Minimal streaming lifecycle: one message and done
yield streaming.message_chunk(
"StrategyAgent is online. Decision pipeline will be wired here."
try:
request = UserRequest.model_validate_json(query)
except ValueError as exc:
logger.exception("StrategyAgent received invalid payload")
yield streaming.message_chunk(str(exc))
yield streaming.done()
return

runtime = create_strategy_runtime(request)
strategy_id = runtime.strategy_id
logger.info(
"Created runtime for strategy_id={} conversation={} task={}",
strategy_id,
conversation_id,
task_id,
)
initial_payload = StrategyStatusContent(
strategy_id=strategy_id,
status=StrategyStatus.RUNNING,
)
yield streaming.component_generator(
content=initial_payload.model_dump_json(),
component_type=ComponentType.STATUS.value,
)
yield streaming.done()

# Wait until strategy is marked as running in persistence layer
since = datetime.now()
while not strategy_persistence.strategy_running(strategy_id):
if (datetime.now() - since).total_seconds() > 300:
logger.error(
"Timeout waiting for strategy_id={} to be marked as running",
strategy_id,
)
break

await asyncio.sleep(1)
logger.info(
"Waiting for strategy_id={} to be marked as running", strategy_id
)

try:
logger.info("Starting decision loop for strategy_id={}", strategy_id)
while True:
if not strategy_persistence.strategy_running(strategy_id):
logger.info(
"Strategy_id={} is no longer running, exiting decision loop",
strategy_id,
)
break

result = await runtime.run_cycle()
logger.info(
"Run cycle completed for strategy={} trades_count={}",
strategy_id,
len(result.trades),
)
# Persist and stream trades
for trade in result.trades:
item = strategy_persistence.persist_trade_history(
strategy_id, trade
)
if item:
logger.info(
"Persisted trade {} for strategy={}",
getattr(trade, "trade_id", None),
strategy_id,
)

# Persist portfolio snapshot (positions)
ok = strategy_persistence.persist_portfolio_view(result.portfolio_view)
if ok:
logger.info(
"Persisted portfolio view for strategy={}",
strategy_id,
)

# Persist strategy summary
ok = strategy_persistence.persist_strategy_summary(
result.strategy_summary
)
if ok:
logger.info(
"Persisted strategy summary for strategy={}",
strategy_id,
)

except asyncio.CancelledError:
raise
except Exception as err: # noqa: BLE001
logger.exception("StrategyAgent stream failed: {}", err)
yield streaming.message_chunk(f"StrategyAgent error: {err}")
finally:
yield streaming.done()
1 change: 1 addition & 0 deletions python/valuecell/agents/strategy_agent/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
DEFAULT_MAX_POSITIONS = 5
DEFAULT_MAX_SYMBOLS = 5
DEFAULT_MAX_LEVERAGE = 10.0
DEFAULT_CAP_FACTOR = 1.5
Loading