[feat] 멀티 세션 실거래 분산 운용 (여러 전략 × 여러 계좌 × 여러 심볼 병렬) - #51
Conversation
…allback note apply_profile now overlays the requested profile on top of the current effective config instead of passing it through as-is, so a partial profile without a "virtual" key no longer silently flips a virtual session to live trading. default_strategy_used is recomputed after a successful apply so the BNH fallback note isn't shown stale. Also tightens switch_profile's description and fixes stop_trading's tool wrapper to surface failures instead of always reporting success.
…emove telegram autostart
Switch Fake data-provider injection from post-setup attribute
replacement (operator.data_provider/.trading_operator, removed by the
SystemOperator overhaul) to patching DataProviderFactory.create, the
same pattern used across the unit tests. Route trader/operator
references through session_manager.get_session("default") and add
MultiSessionE2ETest covering two parallel virtual sessions with
compare_performance, the legacy start_trading/stop_trading path, and
verification that registered account keys never leak into
conversation history or the tool call log.
Add a Multi-Session Parallel Trading section (EN/KR) describing account registration by env-var name, profile-based session creation, and the create_session/start_session/compare_performance chat flow. Update the architecture overview to mention SessionManager and per-session TradingOperator instances, and correct the now-stale get_market_data schema requirement (session-based, not a currency enum).
TraderFactory.create() and budget parsing ran outside any try/except in create_session, so an unsupported-currency error from UpbitTrader (a UserWarning) propagated straight out of replace_session's rollback path, losing the existing session entirely. Wrap both in try/except returning a Korean error dict, and wrap replace_session's call to create_session itself so rollback is guaranteed no matter what escapes. Also stop leaking real-trade Trader worker threads: remove_session and the successful counter-carryover branch of replace_session now discard the outgoing trader's worker via _discard_trader.
- replace_session survives a TraderFactory.create exception (e.g. unsupported currency) and restores the prior session/allocation - remove_session stops the outgoing trader's worker - two real-trade sessions on the same account share the account-level daily trade limit via CompositeSafetyGuard
- save() now rejects registering a second account alias with the same (access_key_env, secret_key_env) pair, which previously let two aliases run independent AccountGuards/balance checks against the same real balance, allowing up to 2x over-allocation. Re-saving the same alias (update) is still allowed. - validate() now requires access_key_env/secret_key_env to look like env-var names (ENV_NAME_PATTERN), closing a path where a raw key value pasted into those fields would be persisted to disk and echoed back through tool results.
operator.setup() try/except only caught ValueError, unlike Controller's equivalent boot path which already catches Exception. Any other exception type (e.g. TypeError from a bad safety key, or a data-provider UserWarning) would crash startup instead of printing a clean error and exiting.
Each session polls the exchange independently, so document that the design targets a handful of concurrent sessions to respect exchange API rate limits.
📝 WalkthroughWalkthroughThis PR introduces multi-session account and trading orchestration: a new AccountStore for credential-name registration, AccountGuard/CompositeSafetyGuard for account-level trade/allocation limits, session-tagged monitoring, a SessionManager for parallel TradingSession lifecycle, and a SystemOperator rework delegating to sessions. LLM tools, trader factories, controllers, and docs are updated accordingly. ChangesMulti-session trading and account management
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant SystemOperator
participant SessionManager
participant AccountStore
participant TradingSession
User->>SystemOperator: register_account / create_session
SystemOperator->>AccountStore: save(account) / missing_env_vars(account)
AccountStore-->>SystemOperator: env_ready status
SystemOperator->>SessionManager: create_session(profile)
SessionManager->>AccountStore: load(account)
SessionManager->>SessionManager: validate budget, env vars, duplicates
SessionManager->>TradingSession: assemble operator/trader/guard
SessionManager-->>SystemOperator: session created
User->>SystemOperator: start_session(name)
SystemOperator->>SessionManager: start_session(name)
SessionManager->>TradingSession: operator.start()
TradingSession-->>SessionManager: state=running
SessionManager-->>SystemOperator: success
Estimated code review effortEstimated code review effort: 4 (Complex) | ~75 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces multi-session parallel trading capabilities to the smtm system, coordinated by a new SessionManager. It adds an AccountStore to securely manage account credentials via environment variable names, and implements AccountGuard and CompositeSafetyGuard to enforce account-level limits across concurrent sessions. New tools for managing accounts and sessions have been registered, and existing tools have been updated to support session-specific queries. The review feedback highlights important robustness improvements, specifically recommending that RegisterAccountTool and DeleteAccountTool catch general exceptions (such as OSError) during file system operations to prevent potential application crashes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def execute(self, arguments: dict) -> ToolResult: | ||
| try: | ||
| account = self.store.save(dict(arguments)) | ||
| except ValueError as err: | ||
| return ToolResult(success=False, error=str(err)) |
There was a problem hiding this comment.
계좌 저장 시 ValueError 외에도 파일 시스템 쓰기 권한 부족이나 디스크 풀 등으로 인해 OSError 등 예기치 않은 예외가 발생할 수 있습니다. 이 경우 예외가 잡히지 않고 상위로 전파되어 도구 실행 루프나 에이전트 전체가 크래시될 위험이 있습니다. Exception을 추가로 캐치하여 안전하게 ToolResult로 반환하도록 개선하는 것이 좋습니다.
| def execute(self, arguments: dict) -> ToolResult: | |
| try: | |
| account = self.store.save(dict(arguments)) | |
| except ValueError as err: | |
| return ToolResult(success=False, error=str(err)) | |
| def execute(self, arguments: dict) -> ToolResult: | |
| try: | |
| account = self.store.save(dict(arguments)) | |
| except ValueError as err: | |
| return ToolResult(success=False, error=str(err)) | |
| except Exception as err: | |
| return ToolResult(success=False, error=f"계좌 저장 중 오류가 발생했습니다: {err}") |
| def execute(self, arguments: dict) -> ToolResult: | ||
| name = arguments.get("name") | ||
| in_use = [s.name for s in self.session_manager.sessions.values() | ||
| if s.account == name] | ||
| if in_use: | ||
| return ToolResult( | ||
| success=False, | ||
| error=f"계좌 '{name}'은 세션에서 사용 중입니다: {', '.join(in_use)}") | ||
| if self.store.delete(name): | ||
| return ToolResult(success=True, data={"deleted": name}) | ||
| return ToolResult(success=False, error=f"계좌를 찾을 수 없습니다: {name}") |
There was a problem hiding this comment.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
smtm/llm/system_operator.py (1)
141-212: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
SessionManager.DEFAULT_SESSIONhere too
default_session,select_strategy,start_trading,stop_trading, andapply_profilestill hardcode"default". ReusingSessionManager.DEFAULT_SESSIONkeeps the session name centralized and avoids divergence if the constant changes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@smtm/llm/system_operator.py` around lines 141 - 212, The session name is still hardcoded as "default" in SystemOperator methods, which should be centralized. Update default_session, select_strategy, start_trading, stop_trading, and apply_profile to use SessionManager.DEFAULT_SESSION instead of the literal so all session operations stay aligned if the constant changes.smtm/llm/tools/market_data_tool.py (1)
39-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate session-resolution boilerplate across read tools.
The
get_session(...) or "default"+ValueError→ToolResultpattern in Lines 44-50 is repeated almost verbatim inPerformanceTool.executeandPortfolioTool.execute. Consider extracting a small shared helper (e.g., aresolve_session(arguments)method on a common base class) to avoid drift between these three tools' error-handling.♻️ Sketch of a shared helper
class SessionScopedTool(Tool): def __init__(self, session_manager): self.logger = LogManager.get_logger(self.__class__.__name__) self.session_manager = session_manager def resolve_session(self, arguments: dict): return self.session_manager.get_session(arguments.get("session") or "default")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@smtm/llm/tools/market_data_tool.py` around lines 39 - 50, Extract the repeated session lookup and ValueError-to-ToolResult handling in MarketDataTool.execute into a shared helper so it can be reused by the other read tools. Add a common method such as resolve_session(arguments) on a shared base like SessionScopedTool, then update MarketDataTool.execute to call it and keep only the data-fetching logic; mirror the same helper usage in PerformanceTool.execute and PortfolioTool.execute to prevent drift in their session resolution behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README-ko-kr.md`:
- Around line 257-275: The session/profile wording uses `심볼`, but the runtime
paths in `SessionManager.create_session` and the allocation checks rely on
`profile["currency"]`. Update the affected bullets in the README section to use
`currency` consistently wherever the configurable field is described, especially
around profile creation and per-session allocation. Keep the rest of the
multi-session explanation unchanged.
In `@README.md`:
- Around line 259-277: The README wording around session/profile creation is
inconsistent with the runtime contract in SessionManager.create_session, which
uses profile["currency"] and duplicate checks on currency rather than symbol.
Update the multi-session description and the “Create profiles” bullet to use
currency everywhere, and ensure the examples/terminology match the fields
consumed by SessionManager and TradingSession so users configure the correct
value.
In `@smtm/controller/telegram/telegram_controller.py`:
- Around line 49-57: The setup failure handling in TelegramController.__init__
currently prints only the exception message, which discards the traceback.
Update the exception path around self.operator.setup() to use
self.logger.exception(...) instead of print(str(err)), keeping the startup
context and preserving the full stack trace for diagnosability.
In `@smtm/llm/tools/session_tools.py`:
- Around line 52-57: `StartSessionTool` currently passes through to
`SessionManager.start_session` without any runtime confirmation, so add an
explicit confirm flag requirement for non-virtual/live sessions in the tool’s
argument schema and validate it before dispatching; then update
`SessionManager.start_session` to reject real-trading starts unless confirmation
is present, while keeping virtual sessions unchanged. Use the existing
`StartSessionTool.execute` and `SessionManager.start_session` entry points to
enforce the gate in both layers.
---
Nitpick comments:
In `@smtm/llm/system_operator.py`:
- Around line 141-212: The session name is still hardcoded as "default" in
SystemOperator methods, which should be centralized. Update default_session,
select_strategy, start_trading, stop_trading, and apply_profile to use
SessionManager.DEFAULT_SESSION instead of the literal so all session operations
stay aligned if the constant changes.
In `@smtm/llm/tools/market_data_tool.py`:
- Around line 39-50: Extract the repeated session lookup and
ValueError-to-ToolResult handling in MarketDataTool.execute into a shared helper
so it can be reused by the other read tools. Add a common method such as
resolve_session(arguments) on a shared base like SessionScopedTool, then update
MarketDataTool.execute to call it and keep only the data-fetching logic; mirror
the same helper usage in PerformanceTool.execute and PortfolioTool.execute to
prevent drift in their session resolution behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eea6f8bf-27ae-4def-926f-f513a4662c6c
📒 Files selected for processing (43)
README-ko-kr.mdREADME.mddocs/public/requirements.mdsmtm/__init__.pysmtm/account_store.pysmtm/analyzer.pysmtm/controller/controller.pysmtm/controller/jpt_controller.pysmtm/controller/telegram/telegram_controller.pysmtm/llm/__init__.pysmtm/llm/account_guard.pysmtm/llm/system_monitor.pysmtm/llm/system_operator.pysmtm/llm/tools/account_tools.pysmtm/llm/tools/market_data_tool.pysmtm/llm/tools/orchestration_tools.pysmtm/llm/tools/performance_tool.pysmtm/llm/tools/portfolio_tool.pysmtm/llm/tools/profile_tools.pysmtm/llm/tools/session_tools.pysmtm/llm/tools/trade_history_tool.pysmtm/profile_store.pysmtm/session_manager.pysmtm/trader/bithumb_trader.pysmtm/trader/trader_factory.pysmtm/trader/upbit_trader.pytests/e2e_tests/e2e_chat_trading_test.pytests/unit_tests/account_guard_test.pytests/unit_tests/account_store_test.pytests/unit_tests/account_tools_test.pytests/unit_tests/analyzer_test.pytests/unit_tests/market_data_tool_test.pytests/unit_tests/orchestration_tools_test.pytests/unit_tests/performance_tool_test.pytests/unit_tests/portfolio_tool_test.pytests/unit_tests/profile_store_test.pytests/unit_tests/profile_tools_test.pytests/unit_tests/session_manager_test.pytests/unit_tests/session_tools_test.pytests/unit_tests/system_monitor_test.pytests/unit_tests/system_operator_test.pytests/unit_tests/trade_history_tool_test.pytests/unit_tests/trader_factory_account_test.py
| 시스템은 2계층으로 나뉘며, SessionManager가 하나 이상의 세션을 병렬로 조율합니다: | ||
|
|
||
| - **SystemOperator** — 채팅 기반 LLM 에이전트. Tool을 통해 전략 선택, 시작/중지, 계좌 프로파일을 오케스트레이션 (직접 매매하지 않음) | ||
| - **TradingOperator** — 고정 주기 루프: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | ||
| - **SystemOperator** — 채팅 기반 LLM 에이전트. Tool을 통해 계좌 등록, 프로파일, 세션 수명 주기를 오케스트레이션 (직접 매매하지 않음) | ||
| - **SessionManager** — 모든 `TradingSession`(default 세션 + 채팅으로 생성한 세션)을 소유. 예산을 실제 계좌 잔고와 대조 검증하고 (계좌, 심볼) 중복 할당을 방지 | ||
| - **TradingOperator** — 세션당 1개; 고정 주기 루프: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | ||
| - **Strategy** — 교체 가능: 알고리즘 전략(Buy & Hold, RSI, SMA) 또는 매 틱 LLM 판단 1회(`LLM`) | ||
| - **SystemMonitor** — 모든 활동(시장 데이터, 요청, 결과, 안전 이벤트, LLM 사용량)을 독립적으로 기록 | ||
| - **SystemMonitor** — 모든 활동(시장 데이터, 요청, 결과, 안전 이벤트, LLM 사용량)을 세션별로 태깅하여 독립적으로 기록 | ||
|
|
||
| ### 멀티 세션 병렬 매매 | ||
|
|
||
| 여러 전략을 여러 계좌·심볼에 걸쳐 동시에 실행할 수 있습니다 — 모두 | ||
| 에이전트와의 채팅으로 제어합니다: | ||
|
|
||
| - 계좌는 환경변수 *이름*으로만 등록합니다 (`SMTM_KEY_1` 등), 키 원문은 저장하지 않습니다 | ||
| - 프로파일 생성 (전략 × 거래소 × 심볼 × 예산 × 계좌) | ||
| - 채팅으로 `create_session` / `start_session` / `compare_performance` 호출 | ||
| - 세션별 예산은 실제 계좌 잔고와 대조 검증되며, 계좌 단위 가드가 | ||
| 세션 전체에 걸친 일일 거래 한도를 관리합니다 | ||
| - 세션마다 거래소를 독립 폴링하므로, API 호출 한도를 고려해 소수의 세션 운영을 전제로 합니다 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Use currency consistently instead of 심볼.
SessionManager.create_session reads profile["currency"] and enforces allocation checks on that field, so the current wording can send users to configure a field the runtime never consumes.
Proposed wording fix
- - **SessionManager** — 모든 `TradingSession`(default 세션 + 채팅으로 생성한 세션)을 소유. 예산을 실제 계좌 잔고와 대조 검증하고 (계좌, 심볼) 중복 할당을 방지
+ - **SessionManager** — 모든 `TradingSession`(default 세션 + 채팅으로 생성한 세션)을 소유. 예산을 실제 계좌 잔고와 대조 검증하고 (계좌, currency) 중복 할당을 방지
...
- - 프로파일 생성 (전략 × 거래소 × 심볼 × 예산 × 계좌)
+ - 프로파일 생성 (전략 × 거래소 × currency × 예산 × 계좌)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 시스템은 2계층으로 나뉘며, SessionManager가 하나 이상의 세션을 병렬로 조율합니다: | |
| - **SystemOperator** — 채팅 기반 LLM 에이전트. Tool을 통해 전략 선택, 시작/중지, 계좌 프로파일을 오케스트레이션 (직접 매매하지 않음) | |
| - **TradingOperator** — 고정 주기 루프: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | |
| - **SystemOperator** — 채팅 기반 LLM 에이전트. Tool을 통해 계좌 등록, 프로파일, 세션 수명 주기를 오케스트레이션 (직접 매매하지 않음) | |
| - **SessionManager** — 모든 `TradingSession`(default 세션 + 채팅으로 생성한 세션)을 소유. 예산을 실제 계좌 잔고와 대조 검증하고 (계좌, 심볼) 중복 할당을 방지 | |
| - **TradingOperator** — 세션당 1개; 고정 주기 루프: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | |
| - **Strategy** — 교체 가능: 알고리즘 전략(Buy & Hold, RSI, SMA) 또는 매 틱 LLM 판단 1회(`LLM`) | |
| - **SystemMonitor** — 모든 활동(시장 데이터, 요청, 결과, 안전 이벤트, LLM 사용량)을 독립적으로 기록 | |
| - **SystemMonitor** — 모든 활동(시장 데이터, 요청, 결과, 안전 이벤트, LLM 사용량)을 세션별로 태깅하여 독립적으로 기록 | |
| ### 멀티 세션 병렬 매매 | |
| 여러 전략을 여러 계좌·심볼에 걸쳐 동시에 실행할 수 있습니다 — 모두 | |
| 에이전트와의 채팅으로 제어합니다: | |
| - 계좌는 환경변수 *이름*으로만 등록합니다 (`SMTM_KEY_1` 등), 키 원문은 저장하지 않습니다 | |
| - 프로파일 생성 (전략 × 거래소 × 심볼 × 예산 × 계좌) | |
| - 채팅으로 `create_session` / `start_session` / `compare_performance` 호출 | |
| - 세션별 예산은 실제 계좌 잔고와 대조 검증되며, 계좌 단위 가드가 | |
| 세션 전체에 걸친 일일 거래 한도를 관리합니다 | |
| - 세션마다 거래소를 독립 폴링하므로, API 호출 한도를 고려해 소수의 세션 운영을 전제로 합니다 | |
| 시스템은 2계층으로 나뉘며, SessionManager가 하나 이상의 세션을 병렬로 조율합니다: | |
| - **SystemOperator** — 채팅 기반 LLM 에이전트. Tool을 통해 계좌 등록, 프로파일, 세션 수명 주기를 오케스트레이션 (직접 매매하지 않음) | |
| - **SessionManager** — 모든 `TradingSession`(default 세션 + 채팅으로 생성한 세션)을 소유. 예산을 실제 계좌 잔고와 대조 검증하고 (계좌, currency) 중복 할당을 방지 | |
| - **TradingOperator** — 세션당 1개; 고정 주기 루프: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | |
| - **Strategy** — 교체 가능: 알고리즘 전략(Buy & Hold, RSI, SMA) 또는 매 틱 LLM 판단 1회(`LLM`) | |
| - **SystemMonitor** — 모든 활동(시장 데이터, 요청, 결과, 안전 이벤트, LLM 사용량)을 세션별로 태깅하여 독립적으로 기록 | |
| ### 멀티 세션 병렬 매매 | |
| 여러 전략을 여러 계좌·심볼에 걸쳐 동시에 실행할 수 있습니다 — 모두 | |
| 에이전트와의 채팅으로 제어합니다: | |
| - 계좌는 환경변수 *이름*으로만 등록합니다 (`SMTM_KEY_1` 등), 키 원문은 저장하지 않습니다 | |
| - 프로파일 생성 (전략 × 거래소 × currency × 예산 × 계좌) | |
| - 채팅으로 `create_session` / `start_session` / `compare_performance` 호출 | |
| - 세션별 예산은 실제 계좌 잔고와 대조 검증되며, 계좌 단위 가드가 | |
| 세션 전체에 걸친 일일 거래 한도를 관리합니다 | |
| - 세션마다 거래소를 독립 폴링하므로, API 호출 한도를 고려해 소수의 세션 운영을 전제로 합니다 |
🧰 Tools
🪛 LanguageTool
[grammar] ~257-~257: Ensure spelling is correct
Context: ...층으로 나뉘며, SessionManager가 하나 이상의 세션을 병렬로 조율합니다: - SystemOperator — 채팅 기반 LLM 에이전트. Tool을...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README-ko-kr.md` around lines 257 - 275, The session/profile wording uses
`심볼`, but the runtime paths in `SessionManager.create_session` and the
allocation checks rely on `profile["currency"]`. Update the affected bullets in
the README section to use `currency` consistently wherever the configurable
field is described, especially around profile creation and per-session
allocation. Keep the rest of the multi-session explanation unchanged.
| The system is split into two layers, coordinated by a SessionManager that runs one or more sessions in parallel: | ||
|
|
||
| - **SystemOperator** — chat-based LLM agent; orchestrates strategy selection, start/stop, and account profiles via Tools (does not trade directly) | ||
| - **TradingOperator** — fixed-interval loop: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | ||
| - **SystemOperator** — chat-based LLM agent; orchestrates account registration, profiles, and session lifecycle via Tools (does not trade directly) | ||
| - **SessionManager** — owns all `TradingSession`s (default session plus any created via chat); validates budgets against real account balances and prevents duplicate (account, symbol) allocations | ||
| - **TradingOperator** — one per session; fixed-interval loop: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | ||
| - **Strategy** — pluggable: algorithmic (Buy & Hold, RSI, SMA) or a single LLM judgment per tick (`LLM`) | ||
| - **SystemMonitor** — independently logs all activity (market data, requests, results, safety events, LLM usage) | ||
| - **SystemMonitor** — independently logs all activity (market data, requests, results, safety events, LLM usage), tagged by session | ||
|
|
||
| ### Multi-Session Parallel Trading | ||
|
|
||
| Run multiple strategies across accounts and symbols in parallel — all | ||
| controlled by chatting with the agent: | ||
|
|
||
| - Register accounts by env-var *names* (`SMTM_KEY_1`...), never raw keys | ||
| - Create profiles (strategy × exchange × symbol × budget × account) | ||
| - `create_session` / `start_session` / `compare_performance` via chat | ||
| - Per-session budgets are validated against the real account balance, | ||
| and an account-level guard caps daily trades across sessions | ||
| - Designed for a handful of concurrent sessions — each session polls the exchange independently, so keep session count modest to respect API rate limits |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Align the session/profile naming with the runtime contract.
SessionManager.create_session consumes profile["currency"] and checks duplicate allocations on currency, not symbol; this section should use the same term so users do not configure a field the system ignores.
Proposed wording fix
- - **SessionManager** — owns all `TradingSession`s (default session plus any created via chat); validates budgets against real account balances and prevents duplicate (account, symbol) allocations
+ - **SessionManager** — owns all `TradingSession`s (default session plus any created via chat); validates budgets against real account balances and prevents duplicate (account, currency) allocations
...
- - Create profiles (strategy × exchange × symbol × budget × account)
+ - Create profiles (strategy × exchange × currency × budget × account)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| The system is split into two layers, coordinated by a SessionManager that runs one or more sessions in parallel: | |
| - **SystemOperator** — chat-based LLM agent; orchestrates strategy selection, start/stop, and account profiles via Tools (does not trade directly) | |
| - **TradingOperator** — fixed-interval loop: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | |
| - **SystemOperator** — chat-based LLM agent; orchestrates account registration, profiles, and session lifecycle via Tools (does not trade directly) | |
| - **SessionManager** — owns all `TradingSession`s (default session plus any created via chat); validates budgets against real account balances and prevents duplicate (account, symbol) allocations | |
| - **TradingOperator** — one per session; fixed-interval loop: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | |
| - **Strategy** — pluggable: algorithmic (Buy & Hold, RSI, SMA) or a single LLM judgment per tick (`LLM`) | |
| - **SystemMonitor** — independently logs all activity (market data, requests, results, safety events, LLM usage) | |
| - **SystemMonitor** — independently logs all activity (market data, requests, results, safety events, LLM usage), tagged by session | |
| ### Multi-Session Parallel Trading | |
| Run multiple strategies across accounts and symbols in parallel — all | |
| controlled by chatting with the agent: | |
| - Register accounts by env-var *names* (`SMTM_KEY_1`...), never raw keys | |
| - Create profiles (strategy × exchange × symbol × budget × account) | |
| - `create_session` / `start_session` / `compare_performance` via chat | |
| - Per-session budgets are validated against the real account balance, | |
| and an account-level guard caps daily trades across sessions | |
| - Designed for a handful of concurrent sessions — each session polls the exchange independently, so keep session count modest to respect API rate limits | |
| The system is split into two layers, coordinated by a SessionManager that runs one or more sessions in parallel: | |
| - **SystemOperator** — chat-based LLM agent; orchestrates account registration, profiles, and session lifecycle via Tools (does not trade directly) | |
| - **SessionManager** — owns all `TradingSession`s (default session plus any created via chat); validates budgets against real account balances and prevents duplicate (account, currency) allocations | |
| - **TradingOperator** — one per session; fixed-interval loop: **DataProvider** -> **Strategy** -> **SafetyGuard** -> **Trader** -> **Analyzer** | |
| - **Strategy** — pluggable: algorithmic (Buy & Hold, RSI, SMA) or a single LLM judgment per tick (`LLM`) | |
| - **SystemMonitor** — independently logs all activity (market data, requests, results, safety events, LLM usage), tagged by session | |
| ### Multi-Session Parallel Trading | |
| Run multiple strategies across accounts and symbols in parallel — all | |
| controlled by chatting with the agent: | |
| - Register accounts by env-var *names* (`SMTM_KEY_1`...), never raw keys | |
| - Create profiles (strategy × exchange × currency × budget × account) | |
| - `create_session` / `start_session` / `compare_performance` via chat | |
| - Per-session budgets are validated against the real account balance, | |
| and an account-level guard caps daily trades across sessions | |
| - Designed for a handful of concurrent sessions — each session polls the exchange independently, so keep session count modest to respect API rate limits |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 259 - 277, The README wording around session/profile
creation is inconsistent with the runtime contract in
SessionManager.create_session, which uses profile["currency"] and duplicate
checks on currency rather than symbol. Update the multi-session description and
the “Create profiles” bullet to use currency everywhere, and ensure the
examples/terminology match the fields consumed by SessionManager and
TradingSession so users configure the correct value.
| self.operator = SystemOperator(llm_client, config, | ||
| account_store=AccountStore()) | ||
| try: | ||
| self.operator.setup() | ||
| except ValueError as err: | ||
| except Exception as err: | ||
| print(str(err)) | ||
| return | ||
|
|
||
| self.operator.start_trading() | ||
| print("'start'를 입력하면 default 세션 매매가 시작됩니다") |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the relevant file and nearby symbols.
ast-grep outline smtm/controller/telegram/telegram_controller.py --view expanded
# Read the controller with line numbers.
wc -l smtm/controller/telegram/telegram_controller.py
cat -n smtm/controller/telegram/telegram_controller.py | sed -n '1,220p'
# Locate the operator implementation and its setup/shutdown behavior.
fd -a 'system_operator.py' .
fd -a 'operator.py' .
rg -n "class SystemOperator|def setup|def shutdown|def start_trading|def stop_trading|def _terminate|AccountStore" smtm -S
# Read the operator file(s) if found.
for f in $(fd -a 'system_operator.py|operator.py' smtm); do
echo "===== $f ====="
wc -l "$f"
cat -n "$f" | sed -n '1,260p'
doneRepository: msaltnet/smtm
Length of output: 24258
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -a 'session_manager.py' smtm
fd -a 'trading_session.py' smtm
fd -a 'trader.py' smtm
rg -n "def create_session|def start_session|def stop_session|def stop_all|class SessionManager|class TradingSession|class Trader|Worker|thread|Timer|start\(" smtm/session_manager.py smtm/trading_session.py smtm/trader.py -S
for f in smtm/session_manager.py smtm/trading_session.py smtm/trader.py; do
if [ -f "$f" ]; then
echo "===== $f ====="
wc -l "$f"
cat -n "$f" | sed -n '1,260p'
fi
doneRepository: msaltnet/smtm
Length of output: 1007
🏁 Script executed:
#!/bin/bash
set -euo pipefail
wc -l /home/jailuser/git/smtm/session_manager.py
cat -n /home/jailuser/git/smtm/session_manager.py | sed -n '1,340p'Repository: msaltnet/smtm
Length of output: 16461
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect worker/trader startup behavior.
for f in smtm/worker.py smtm/trader/trader.py smtm/trader/base_exchange_trader.py smtm/trader/simulation_trader.py smtm/trader/upbit_trader.py smtm/trader/bithumb_trader.py smtm/trader/trader_factory.py; do
if [ -f "$f" ]; then
echo "===== $f ====="
wc -l "$f"
cat -n "$f" | sed -n '1,260p'
fi
doneRepository: msaltnet/smtm
Length of output: 43969
Preserve the startup traceback
print(str(err)) drops the stack trace; log the exception with self.logger.exception(...) so setup failures are diagnosable.
🧰 Tools
🪛 Ruff (0.15.20)
[warning] 53-53: Do not catch blind exception: Exception
(BLE001)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@smtm/controller/telegram/telegram_controller.py` around lines 49 - 57, The
setup failure handling in TelegramController.__init__ currently prints only the
exception message, which discards the traceback. Update the exception path
around self.operator.setup() to use self.logger.exception(...) instead of
print(str(err)), keeping the startup context and preserving the full stack trace
for diagnosability.
Source: Linters/SAST tools
| class StartSessionTool(_SessionActionTool): | ||
| name = "start_session" | ||
| description = "세션의 자동 매매를 시작합니다 (실거래 세션은 시작 전 사용자 확인 필수)" | ||
|
|
||
| def execute(self, arguments: dict) -> ToolResult: | ||
| return self._run(self.session_manager.start_session, arguments) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== File list =="
git ls-files | rg '(^|/)smtm/llm/tools/session_tools\.py$|(^|/)session_manager\.py$|(^|/)session_tools\.py$|(^|/)session.*manager.*\.py$'
echo
echo "== Outline session_tools.py =="
ast-grep outline smtm/llm/tools/session_tools.py --view expanded || true
echo
echo "== Outline likely session manager files =="
for f in $(git ls-files | rg 'session.*manager.*\.py$'); do
echo "--- $f ---"
ast-grep outline "$f" --view expanded || true
done
echo
echo "== Search for start_session and confirmation-related terms =="
rg -n --hidden --glob '!**/.git/**' 'start_session|confirm|confirmation|virtual|live|paper|real-trading|실거래|자동 매매' smtm || trueRepository: msaltnet/smtm
Length of output: 9505
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== session_tools.py relevant lines =="
sed -n '1,220p' smtm/llm/tools/session_tools.py | nl -ba | sed -n '1,220p'
echo
echo "== session manager file(s) around start_session =="
for f in $(git ls-files | rg 'session.*manager.*\.py$'); do
echo "--- $f ---"
rg -n -C 3 'def start_session|class .*SessionManager|confirm|virtual|live|paper|real' "$f" || true
doneRepository: msaltnet/smtm
Length of output: 229
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('smtm/llm/tools/session_tools.py')
text = p.read_text()
for i, line in enumerate(text.splitlines(), 1):
if 1 <= i <= 220:
print(f"{i:4d}: {line}")
PYRepository: msaltnet/smtm
Length of output: 4155
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== session_manager.py around start_session =="
python3 - <<'PY'
from pathlib import Path
p = Path('smtm/session_manager.py')
for i, line in enumerate(p.read_text().splitlines(), 1):
if 220 <= i <= 255:
print(f"{i:4d}: {line}")
PY
echo
echo "== system_operator.py confirmation-related context =="
python3 - <<'PY'
from pathlib import Path
p = Path('smtm/llm/system_operator.py')
for i, line in enumerate(p.read_text().splitlines(), 1):
if 240 <= i <= 285:
print(f"{i:4d}: {line}")
PY
echo
echo "== search for any explicit confirm arg/check in tools or session manager =="
rg -n 'confirm\b|confirmation\b|requires user confirmation|사용자 확인|start_session\(' smtm/llm smtm/session_manager.py smtm || trueRepository: msaltnet/smtm
Length of output: 4880
Add a real confirmation gate for live-trading starts. StartSessionTool still forwards session straight to SessionManager.start_session, and that method has no confirmation check either. The only safeguard is the system prompt, so prompt injection or model error can still start a real session. Add an explicit confirm requirement for non-virtual sessions in the tool schema and enforce it in SessionManager.start_session.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@smtm/llm/tools/session_tools.py` around lines 52 - 57, `StartSessionTool`
currently passes through to `SessionManager.start_session` without any runtime
confirmation, so add an explicit confirm flag requirement for non-virtual/live
sessions in the tool’s argument schema and validate it before dispatching; then
update `SessionManager.start_session` to reject real-trading starts unless
confirmation is present, while keeping virtual sessions unchanged. Use the
existing `StartSessionTool.execute` and `SessionManager.start_session` entry
points to enforce the gate in both layers.
개요
여러 전략 × 여러 계좌 × 여러 심볼을 독립 세션으로 병렬 운영하고, LLM 오케스트레이터(SystemOperator)가 대화로 전체를 지휘합니다.
docs/superpowers/specs/2026-07-06-multi-session-trading-design.mddocs/superpowers/plans/2026-07-06-multi-session-trading.md주요 변경
AccountStoreSMTM_KEY_1방식), 키 원문은 파일/로그/대화 어디에도 비노출. 동일 키 쌍 별칭 중복·키 값 형태 env 이름 거부SessionManager+TradingSessionAccountGuard+CompositeSafetyGuardTraderFactory(account=...), 레거시 env 하위 호환default세션으로 완전 위임 —--strategy/--profile부팅, start/stop/select/switch_profile 동작 불변shutdown()배선, Telegram 부팅 자동 시작 제거안전 불변식
Strategy → Trader단일 경로 (에이전트에 execute_trade 없음)테스트
python -m pytest tests/unit_tests/ tests/e2e_tests/ -q→ 492 passed (실 네트워크 0회)python -m smtm --mode 0 --strategy BNH --virtual정상🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes