Skip to content

[feat] 멀티 세션 실거래 분산 운용 (여러 전략 × 여러 계좌 × 여러 심볼 병렬) - #51

Merged
msaltnet merged 24 commits into
masterfrom
multi-session
Jul 7, 2026
Merged

[feat] 멀티 세션 실거래 분산 운용 (여러 전략 × 여러 계좌 × 여러 심볼 병렬)#51
msaltnet merged 24 commits into
masterfrom
multi-session

Conversation

@msaltnet

@msaltnet msaltnet commented Jul 7, 2026

Copy link
Copy Markdown
Owner

개요

여러 전략 × 여러 계좌 × 여러 심볼을 독립 세션으로 병렬 운영하고, LLM 오케스트레이터(SystemOperator)가 대화로 전체를 지휘합니다.

  • 설계 스펙: docs/superpowers/specs/2026-07-06-multi-session-trading-design.md
  • 구현 계획: docs/superpowers/plans/2026-07-06-multi-session-trading.md

주요 변경

컴포넌트 내용
AccountStore 계좌 자격증명 레지스트리 — 환경변수 이름만 저장(SMTM_KEY_1 방식), 키 원문은 파일/로그/대화 어디에도 비노출. 동일 키 쌍 별칭 중복·키 값 형태 env 이름 거부
SessionManager + TradingSession 병렬 세션 관리 핵심 — 생성 검증(이름/계좌/env/(계좌,심볼) 충돌/소프트 예산 합계 ≤ 실잔고), 실패 시 무부작용, replace 원복 보장
AccountGuard + CompositeSafetyGuard 계좌 수준 안전장치(일일 총 횟수·할당 총액, Lock 보호) — 같은 계좌의 모든 세션이 공유, 세션 재생성으로 리셋 불가
Trader 자격증명 주입 Upbit/Bithumb 생성자 env 오버라이드 + TraderFactory(account=...), 레거시 env 하위 호환
에이전트 Tool 계좌 3종 + 세션 6종(create/start/stop/remove/list/compare) 신규, 읽기 4종 세션 인식 리팩터
레거시 호환 기존 CLI/Tool은 default 세션으로 완전 위임 — --strategy/--profile 부팅, start/stop/select/switch_profile 동작 불변
컨트롤러 AccountStore 주입 + shutdown() 배선, Telegram 부팅 자동 시작 제거

안전 불변식

  • 매매 경로는 세션 내부 Strategy → Trader 단일 경로 (에이전트에 execute_trade 없음)
  • 알고리즘 전략 세션의 틱은 LLM 호출 0회
  • API 키 값은 환경변수에만 — E2E로 대화 이력/tool_call_log 비노출 검증
  • 부분 프로파일 적용 시 가상→실거래 무언 전환 방지 (config 오버레이 상속)

테스트

  • python -m pytest tests/unit_tests/ tests/e2e_tests/ -q492 passed (실 네트워크 0회)
  • E2E: 병렬 가상 세션 2개 + 성과 비교 / 레거시 default 플로우 / 키 비노출
  • 부팅 스모크: python -m smtm --mode 0 --strategy BNH --virtual 정상

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added multi-session trading support, including session creation, start/stop, listing, and performance comparison.
    • Added account registration and management for trading profiles, with safer handling of stored credentials.
    • Updated market data, portfolio, trade history, and status views to work per session.
  • Bug Fixes

    • Improved safety controls with account-level trade limits and allocation checks.
    • Fixed logging and monitoring so trading activity is tagged and filtered by session.
    • Updated trading flows to better handle errors and preserve session state during profile changes.

msaltnet added 24 commits July 6, 2026 19:39
…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.
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.
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

Multi-session trading and account management

Layer / File(s) Summary
AccountStore credential registry
smtm/account_store.py, smtm/__init__.py, tests/unit_tests/account_store_test.py
New AccountStore validates, saves, loads, deletes, and lists account credential-env-name records as JSON files; package exports and unit tests added.
AccountGuard and CompositeSafetyGuard
smtm/llm/account_guard.py, smtm/llm/__init__.py, tests/unit_tests/account_guard_test.py
New AccountGuard enforces daily trade limits and total allocation caps; CompositeSafetyGuard merges session and account guard decisions.
Session-tagged monitoring and analysis
smtm/llm/system_monitor.py, smtm/analyzer.py, tests/unit_tests/system_monitor_test.py, tests/unit_tests/analyzer_test.py
SystemMonitor log/query methods and Analyzer logging calls accept/filter by an optional session name.
SessionManager parallel session lifecycle
smtm/session_manager.py, tests/unit_tests/session_manager_test.py
New TradingSession/SessionManager implement create/replace/start/stop/remove/list/status/performance for parallel virtual and real-trade sessions with validation and account-guard allocation.
SystemOperator session-based orchestration
smtm/llm/system_operator.py, tests/unit_tests/system_operator_test.py
SystemOperator builds a SessionManager, creates a default session, delegates control/status to sessions, and updates the LLM system prompt to report per-session state.
Account and session management tools
smtm/llm/tools/account_tools.py, smtm/llm/tools/session_tools.py, tests/unit_tests/account_tools_test.py, tests/unit_tests/session_tools_test.py
New tools register/list/delete accounts and create/start/stop/remove/list/compare sessions via AccountStore/SessionManager.
Session-aware query and status tools
smtm/llm/tools/market_data_tool.py, smtm/llm/tools/portfolio_tool.py, smtm/llm/tools/performance_tool.py, smtm/llm/tools/trade_history_tool.py, smtm/llm/tools/orchestration_tools.py, tests/unit_tests/*_tool_test.py
Market data, portfolio, performance, trade history, and status/stop-trading tools resolve/filter by session name and propagate errors.
Profile schema account field and switch-profile wording
smtm/llm/tools/profile_tools.py, smtm/profile_store.py, tests/unit_tests/profile_store_test.py, tests/unit_tests/profile_tools_test.py
Adds an account field to profile schema/ALLOWED_FIELDS and updates SwitchProfileTool description text.
Per-account trader credential wiring
smtm/trader/bithumb_trader.py, smtm/trader/upbit_trader.py, smtm/trader/trader_factory.py, tests/unit_tests/trader_factory_account_test.py
Trader constructors and TraderFactory.create accept custom access/secret env-var names per account.
Controller AccountStore wiring and shutdown lifecycle
smtm/controller/controller.py, smtm/controller/jpt_controller.py, smtm/controller/telegram/telegram_controller.py
Controllers construct SystemOperator with an AccountStore and call operator.shutdown() instead of stop_trading() on termination.
End-to-end multi-session chat trading tests
tests/e2e_tests/e2e_chat_trading_test.py
E2E tests route through session_manager/default session objects and add multi-session create/start/compare and credential-leak checks.
Documentation updates for multi-session architecture
README.md, README-ko-kr.md, docs/public/requirements.md
Docs describe SessionManager, multi-session account registration/lifecycle, and session-scoped market data tool input.

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
Loading

Estimated code review effort

Estimated code review effort: 4 (Complex) | ~75 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 멀티 세션 실거래의 병렬 운용과 여러 전략·계좌·심볼 지원을 정확히 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch multi-session

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +26 to +30
def execute(self, arguments: dict) -> ToolResult:
try:
account = self.store.save(dict(arguments))
except ValueError as err:
return ToolResult(success=False, error=str(err))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

계좌 저장 시 ValueError 외에도 파일 시스템 쓰기 권한 부족이나 디스크 풀 등으로 인해 OSError 등 예기치 않은 예외가 발생할 수 있습니다. 이 경우 예외가 잡히지 않고 상위로 전파되어 도구 실행 루프나 에이전트 전체가 크래시될 위험이 있습니다. Exception을 추가로 캐치하여 안전하게 ToolResult로 반환하도록 개선하는 것이 좋습니다.

Suggested change
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}")

Comment on lines +64 to +74
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}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

계좌 삭제 시 파일 시스템에서 실제 파일을 삭제하는 과정(self.store.delete(name))에서 PermissionErrorFileNotFoundErrorOSError가 발생할 수 있습니다. 예기치 않은 예외로 인해 도구 실행이 크래시되는 것을 방지하기 위해 전체 실행 로직을 try-except Exception 블록으로 감싸는 것이 안전합니다.

    def execute(self, arguments: dict) -> ToolResult:
        try:
            name = arguments.get(

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
smtm/llm/system_operator.py (1)

141-212: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use SessionManager.DEFAULT_SESSION here too
default_session, select_strategy, start_trading, stop_trading, and apply_profile still hardcode "default". Reusing SessionManager.DEFAULT_SESSION keeps 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 win

Duplicate session-resolution boilerplate across read tools.

The get_session(...) or "default" + ValueErrorToolResult pattern in Lines 44-50 is repeated almost verbatim in PerformanceTool.execute and PortfolioTool.execute. Consider extracting a small shared helper (e.g., a resolve_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

📥 Commits

Reviewing files that changed from the base of the PR and between fd956d9 and 4fd8d41.

📒 Files selected for processing (43)
  • README-ko-kr.md
  • README.md
  • docs/public/requirements.md
  • smtm/__init__.py
  • smtm/account_store.py
  • smtm/analyzer.py
  • smtm/controller/controller.py
  • smtm/controller/jpt_controller.py
  • smtm/controller/telegram/telegram_controller.py
  • smtm/llm/__init__.py
  • smtm/llm/account_guard.py
  • smtm/llm/system_monitor.py
  • smtm/llm/system_operator.py
  • smtm/llm/tools/account_tools.py
  • smtm/llm/tools/market_data_tool.py
  • smtm/llm/tools/orchestration_tools.py
  • smtm/llm/tools/performance_tool.py
  • smtm/llm/tools/portfolio_tool.py
  • smtm/llm/tools/profile_tools.py
  • smtm/llm/tools/session_tools.py
  • smtm/llm/tools/trade_history_tool.py
  • smtm/profile_store.py
  • smtm/session_manager.py
  • smtm/trader/bithumb_trader.py
  • smtm/trader/trader_factory.py
  • smtm/trader/upbit_trader.py
  • tests/e2e_tests/e2e_chat_trading_test.py
  • tests/unit_tests/account_guard_test.py
  • tests/unit_tests/account_store_test.py
  • tests/unit_tests/account_tools_test.py
  • tests/unit_tests/analyzer_test.py
  • tests/unit_tests/market_data_tool_test.py
  • tests/unit_tests/orchestration_tools_test.py
  • tests/unit_tests/performance_tool_test.py
  • tests/unit_tests/portfolio_tool_test.py
  • tests/unit_tests/profile_store_test.py
  • tests/unit_tests/profile_tools_test.py
  • tests/unit_tests/session_manager_test.py
  • tests/unit_tests/session_tools_test.py
  • tests/unit_tests/system_monitor_test.py
  • tests/unit_tests/system_operator_test.py
  • tests/unit_tests/trade_history_tool_test.py
  • tests/unit_tests/trader_factory_account_test.py

Comment thread README-ko-kr.md
Comment on lines +257 to +275
시스템은 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 호출 한도를 고려해 소수의 세션 운영을 전제로 합니다

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Suggested change
시스템은 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.

Comment thread README.md
Comment on lines +259 to +277
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Suggested change
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.

Comment on lines +49 to +57
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 세션 매매가 시작됩니다")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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'
done

Repository: 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
done

Repository: 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
done

Repository: 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

Comment on lines +52 to +57
class StartSessionTool(_SessionActionTool):
name = "start_session"
description = "세션의 자동 매매를 시작합니다 (실거래 세션은 시작 전 사용자 확인 필수)"

def execute(self, arguments: dict) -> ToolResult:
return self._run(self.session_manager.start_session, arguments)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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 || true

Repository: 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
done

Repository: 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}")
PY

Repository: 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 || true

Repository: 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.

@msaltnet
msaltnet merged commit 11ca557 into master Jul 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant