Skip to content

Commit a1fc65a

Browse files
aaron-jangclaude
andcommitted
upstream 병합: V24.10 ~ V24.16 (8개 커밋 이식)
아키텍처 대수술: - V24.14: VolatilityEngine 래퍼 클래스 신설, TelegramController DI 수정 - V24.15: strategy.py → 중앙 라우터 + strategy_v14.py 플러그인 분리 - V24.16: V_VWAP 영구 소각, V14+V-REV 2대 코어 체제 확립 버그 수정: - V24.10: API 결측치(None) Safe Casting 전면 이식 - V24.11: 텔레그램 타임아웃 방어 + 커넥션 풀 512 - V24.12: 타임존 통제 + 프리장 거짓 잭팟 차단 - V24.13: 외화 RP 이중 차감 버그 수정 파일 변경: - 신규: trading_bot/strategy/v14.py (V14 플러그인) - 삭제: trading_bot/strategy/vwap.py (V_VWAP 소각) - 삭제: tests/test_vwap_strategy.py 포크 수정 재적용: - background.png assets/ 경로 - broker output2 빈 리스트 방어 - EST 통일 스케줄, cron 방식, misfire 120초, 텔레그램 자동완성 테스트: 197개 통과 (V_VWAP/P-Trade 관련 20개 제거) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0244719 commit a1fc65a

17 files changed

Lines changed: 1275 additions & 1895 deletions

‎tests/conftest.py‎

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Phase 0: 특성 테스트(Characterization Test) 공통 Fixture
2+
특성 테스트(Characterization Test) 공통 Fixture
33
44
기존 코드의 동작을 캡처하기 위해 임시 data/ 디렉토리를 생성하고
55
ConfigManager가 그 디렉토리를 사용하도록 패치합니다.
@@ -11,7 +11,6 @@
1111
import tempfile
1212
import pytest
1313

14-
# 프로젝트 루트를 sys.path에 추가
1514
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
1615
if PROJECT_ROOT not in sys.path:
1716
sys.path.insert(0, PROJECT_ROOT)
@@ -31,7 +30,6 @@ def tmp_data_dir(tmp_path):
3130
def cfg(tmp_data_dir, monkeypatch):
3231
"""
3332
ConfigManager를 임시 디렉토리에서 동작하도록 패치.
34-
실제 파일시스템 I/O를 하되, 테스트 격리를 보장.
3533
모든 파일 경로를 절대 경로로 변환하여 CWD 오염을 방지.
3634
"""
3735
work_dir = str(tmp_data_dir.parent)
@@ -40,33 +38,10 @@ def cfg(tmp_data_dir, monkeypatch):
4038
from trading_bot.config import ConfigManager
4139
config = ConfigManager()
4240

43-
# FILES 딕셔너리의 모든 경로를 절대 경로로 변환하여 완전 격리
41+
# FILES 딕셔너리의 모든 경로를 절대 경로로 변환
4442
for key in config.FILES:
4543
config.FILES[key] = os.path.join(work_dir, config.FILES[key])
4644

47-
# 내부 위임 객체도 동일한 절대 경로 기반으로 재설정
48-
data_dir = str(tmp_data_dir)
49-
config._ledger._ledger_path = config.FILES["LEDGER"]
50-
config._ledger._history_path = config.FILES["HISTORY"]
51-
config._ledger._split_history_path = config.FILES["SPLIT_HISTORY"]
52-
config._locks._path = config.FILES["LOCKS"]
53-
# TradingConfig 경로를 ConfigManager.FILES 기반 절대 경로로 재매핑
54-
files = config.FILES
55-
config._config._paths = {
56-
"SEED": files["SEED_CFG"],
57-
"SPLIT": files["SPLIT"],
58-
"PROFIT": files["PROFIT_CFG"],
59-
"COMPOUND": files["COMPOUND_CFG"],
60-
"VERSION": files["VERSION_CFG"],
61-
"SNIPER_MULT": files["SNIPER_MULTIPLIER_CFG"],
62-
"REVERSE": files["REVERSE_CFG"],
63-
"UPWARD_SNIPER": files["UPWARD_SNIPER"],
64-
"TICKER": files["TICKER"],
65-
"SECRET": files["SECRET_MODE"],
66-
"CHAT_ID": files["CHAT_ID"],
67-
"P_TRADE": files["P_TRADE_DATA"],
68-
}
69-
7045
return config
7146

7247

@@ -82,7 +57,6 @@ def cfg_with_ledger(cfg, tmp_data_dir):
8257
@pytest.fixture
8358
def cfg_with_history(cfg, tmp_data_dir):
8459
"""샘플 히스토리가 로드된 ConfigManager"""
85-
# 장부도 함께 로드
8660
shutil.copy(
8761
os.path.join(FIXTURES_DIR, "sample_ledger.json"),
8862
tmp_data_dir / "manual_ledger.json"
@@ -96,31 +70,14 @@ def cfg_with_history(cfg, tmp_data_dir):
9670

9771
@pytest.fixture
9872
def sample_ledger():
99-
"""fixture JSON을 파이썬 객체로 반환"""
10073
with open(os.path.join(FIXTURES_DIR, "sample_ledger.json"), "r") as f:
10174
return json.load(f)
10275

10376

10477
@pytest.fixture
10578
def sample_execution_history():
106-
"""KIS API 체결 내역 응답 모킹 데이터"""
10779
return [
108-
{
109-
"sll_buy_dvsn_cd": "02", # BUY
110-
"ft_ccld_qty": "10",
111-
"ft_ccld_unpr3": "48.50",
112-
"ord_tmd": "170500"
113-
},
114-
{
115-
"sll_buy_dvsn_cd": "02", # BUY
116-
"ft_ccld_qty": "5",
117-
"ft_ccld_unpr3": "48.00",
118-
"ord_tmd": "170300"
119-
},
120-
{
121-
"sll_buy_dvsn_cd": "01", # SELL
122-
"ft_ccld_qty": "8",
123-
"ft_ccld_unpr3": "52.00",
124-
"ord_tmd": "170400"
125-
}
80+
{"sll_buy_dvsn_cd": "02", "ft_ccld_qty": "10", "ft_ccld_unpr3": "48.50", "ord_tmd": "170500"},
81+
{"sll_buy_dvsn_cd": "02", "ft_ccld_qty": "5", "ft_ccld_unpr3": "48.00", "ord_tmd": "170300"},
82+
{"sll_buy_dvsn_cd": "01", "ft_ccld_qty": "8", "ft_ccld_unpr3": "52.00", "ord_tmd": "170400"},
12683
]

‎tests/test_config.py‎

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -183,38 +183,17 @@ class TestLocksAndEscrow:
183183
def test_escrow_default_zero(self, cfg):
184184
assert cfg.get_escrow_cash("TQQQ") == 0.0
185185

186-
def test_set_and_get_escrow(self, cfg):
187-
cfg.set_escrow_cash("TQQQ", 500.0)
188-
assert cfg.get_escrow_cash("TQQQ") == 500.0
189-
190-
def test_add_escrow(self, cfg):
191-
cfg.set_escrow_cash("TQQQ", 100.0)
192-
cfg.add_escrow_cash("TQQQ", 200.0)
193-
assert cfg.get_escrow_cash("TQQQ") == 300.0
194-
195-
def test_clear_escrow(self, cfg):
196-
cfg.set_escrow_cash("TQQQ", 500.0)
197-
cfg.clear_escrow_cash("TQQQ")
198-
assert cfg.get_escrow_cash("TQQQ") == 0.0
199-
200-
def test_total_locked_cash(self, cfg):
201-
cfg.set_escrow_cash("TQQQ", 300.0)
202-
cfg.set_escrow_cash("SOXL", 200.0)
203-
total = cfg.get_total_locked_cash()
204-
assert total == 500.0
186+
def test_escrow_is_dynamic_from_ledger(self, cfg):
187+
"""V24.10: 에스크로는 장부 기반 동적 산출 (set은 no-op)"""
188+
cfg.set_escrow_cash("TQQQ", 500.0) # no-op
189+
assert cfg.get_escrow_cash("TQQQ") == 0.0 # 장부 비어있으면 0
205190

206-
def test_total_locked_cash_exclude(self, cfg):
207-
cfg.set_escrow_cash("TQQQ", 300.0)
208-
cfg.set_escrow_cash("SOXL", 200.0)
209-
total = cfg.get_total_locked_cash(exclude_ticker="TQQQ")
210-
assert total == 200.0
191+
def test_total_locked_cash_empty(self, cfg):
192+
assert cfg.get_total_locked_cash() == 0.0
211193

212-
def test_reset_locks_preserves_escrow(self, cfg):
213-
cfg.set_escrow_cash("TQQQ", 500.0)
194+
def test_reset_locks(self, cfg):
214195
cfg.set_lock("TQQQ", "REG")
215196
cfg.reset_locks()
216-
# 에스크로는 보존
217-
assert cfg.get_escrow_cash("TQQQ") == 500.0
218197

219198

220199
# ==============================================================
@@ -315,19 +294,4 @@ def test_calibrate_ledger_prices(self, cfg_with_ledger, sample_execution_history
315294
assert isinstance(changed, int)
316295

317296

318-
# ==============================================================
319-
# 8. P매매 데이터 테스트
320-
# ==============================================================
321-
class TestPTradeData:
322-
def test_p_trade_default_empty(self, cfg):
323-
assert cfg.get_p_trade_data() == {}
324-
325-
def test_set_and_get_p_trade_data(self, cfg):
326-
data = {"ticker": "TQQQ", "targets": [{"side": "BUY", "price": 45.0, "qty": 10}]}
327-
cfg.set_p_trade_data(data)
328-
assert cfg.get_p_trade_data() == data
329-
330-
def test_clear_p_trade_data(self, cfg):
331-
cfg.set_p_trade_data({"some": "data"})
332-
cfg.clear_p_trade_data()
333-
assert cfg.get_p_trade_data() == {}
297+
# P매매 테스트 삭제 (V24.00에서 P매매 소각됨)

‎tests/test_strategy.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,9 @@ def test_plan_return_structure(self, strategy):
308308
)
309309
required_keys = [
310310
"orders", "core_orders", "bonus_orders",
311-
"smart_core_orders", "smart_bonus_orders",
312311
"t_val", "one_portion", "process_status",
313312
"is_reverse", "star_price", "star_ratio",
314-
"real_cash_used", "tracking_info"
313+
"real_cash_used"
315314
]
316315
for key in required_keys:
317316
assert key in plan, f"누락된 키: {key}"

‎tests/test_vwap_strategy.py‎

Lines changed: 0 additions & 211 deletions
This file was deleted.

0 commit comments

Comments
 (0)