Merge branch 'dev' into main — 최소주문금액·종목당 비중 조정
This commit is contained in:
@@ -35,7 +35,8 @@
|
||||
|
||||
## 5. 리스크 규칙
|
||||
### 5.1 사이징·필터
|
||||
- 종목당 최대 **6%** (`MAX_POSITION_SIZE=0.06`), 최대 **20종목**(`MAX_HOLDINGS_COUNT`), 레버리지/인버스 ETF 최대 **20%**(`LEVERAGE_ETF_MAX_RATIO`).
|
||||
- 종목당 최대 **4%** (`MAX_POSITION_SIZE=0.04`), 최대 **20종목**(`MAX_HOLDINGS_COUNT`), 레버리지/인버스 ETF 최대 **20%**(`LEVERAGE_ETF_MAX_RATIO`).
|
||||
- 최소 주문금액 **15만원**(`MIN_BUY_AMOUNT_KRW`). 이 값이 종목당 예산에 근접하면 신뢰도·현금 보정으로 깎인 주문이 전부 탈락한다(2026-09-23 하루 141건 차단).
|
||||
- 가격 필터: 국내 >5,000원, 미국 >$4 (ETF·리츠 예외) (`MIN_PRICE_KRW`, `MIN_PRICE_USD`).
|
||||
- 상관관계: 섹터당 최대 3종목, 기존 포트폴리오와 상관계수 >0.7 진입 거부 (`correlation_filter`).
|
||||
- 연속 손실: 동일 종목 **3연패 → 42캘린더일(≈30거래일) 진입 금지** (`loss_tracker`). `record_trade_result()` 후 반드시 `save_to_db()`(db_worker upsert_stock_ban 경유). 재시작 시 `load_from_db()` 복원.
|
||||
@@ -91,7 +92,8 @@
|
||||
|
||||
| 설정 | 값 | 비고 |
|
||||
|------|-----|------|
|
||||
| `MAX_POSITION_SIZE` | 0.06 | 0.04→0.06 |
|
||||
| `MAX_POSITION_SIZE` | 0.04 | 0.06→0.04 (2026-09-23, 종목 수 확대) |
|
||||
| `MIN_BUY_AMOUNT_KRW` | 150,000 | 500,000→150,000 (거래 차단 해소) |
|
||||
| `MAX_HOLDINGS_COUNT` | 20 | |
|
||||
| `LEVERAGE_ETF_MAX_RATIO` | 0.20 | |
|
||||
| `ATR_STOP_MULTIPLIER` | 2.5 | 스윙 ATR SL 배수 |
|
||||
@@ -122,4 +124,4 @@
|
||||
| Defensive | 38 | 4% | -6% | ATR백분위>70 or 5일수익<-3% |
|
||||
| Crisis | 25 | 1.5% | -3% | ATR백분위>90 or 5일수익<-5% |
|
||||
|
||||
*마지막 업데이트: 2026-09-22 (스윙:DT 50:50, DT 시간대 제한 완화, ORB 감시목록 선채움)*
|
||||
*마지막 업데이트: 2026-09-23 (종목당 4%·최소주문 15만원, 시간대 KST 통일, 감사 수정 14건)*
|
||||
|
||||
@@ -693,7 +693,7 @@ def _suggest_buy_amount(
|
||||
budget = total_assets * position_cap
|
||||
# 신뢰도 보정 (기준 0.7)
|
||||
if confidence > 0:
|
||||
budget *= min(1.2, max(0.5, confidence / 0.7))
|
||||
budget *= min(1.2, max(0.8, confidence / 0.7)) # 하한 0.8 (과도 축소 방지)
|
||||
# 현금 여력
|
||||
reserve = total_assets * (1 - float(getattr(_cfg, "MAX_TOTAL_CAPITAL_USAGE", 0.95)))
|
||||
budget = min(budget, max(0.0, available_cash - reserve))
|
||||
|
||||
@@ -138,7 +138,7 @@ class Settings(BaseSettings):
|
||||
# ═════════════════════════════════════════════════════════════════════════════
|
||||
# 공통 리스크 관리 (섹션 5)
|
||||
# ═════════════════════════════════════════════════════════════════════════════
|
||||
MAX_POSITION_SIZE: float = 0.06 # 백테스트 최적: 종목당 6% (기존 4%, 적응형 모드에서 오버라이드)
|
||||
MAX_POSITION_SIZE: float = 0.04 # 종목당 4% (6%→4%, 2026-09-23: 종목 수를 늘려 표본 확보)
|
||||
MAX_HOLDINGS_COUNT: int = 20 # 동시 보유 최대 20종목 (섹션 5.1)
|
||||
LEVERAGE_ETF_MAX_RATIO: float = 0.20 # 레버리지/인버스 ETF 최대 20% (섹션 5.1)
|
||||
|
||||
@@ -169,7 +169,9 @@ class Settings(BaseSettings):
|
||||
# 최소 매수 금액 가드 (수수료 대비 비효율 방지, trade_executor._execute_buy)
|
||||
# 큰 계좌는 이 값이 그대로 하한으로 사용되고, 소액 계좌는 자산 대비로 완화된다
|
||||
# (trade_executor._MIN_BUY_AMOUNT 산출 로직 참고).
|
||||
MIN_BUY_AMOUNT_KRW: float = 500_000 # 기존 하드코딩 _MIN_BUY_AMOUNT와 동일한 기본값
|
||||
MIN_BUY_AMOUNT_KRW: float = 150_000 # 최소 주문금액 (500,000→150,000, 2026-09-23)
|
||||
# 50만원은 종목당 예산(총자산×6%≈59만)과 거의 같아, 신뢰도·현금 보정으로 조금만
|
||||
# 깎여도 전부 탈락했다(2026-09-23 하루 141건 차단). 계좌 대비 1.5~3% 주문을 허용한다.
|
||||
|
||||
# 연속 손실 관리 (섹션 5.5)
|
||||
CONSECUTIVE_LOSS_BAN_COUNT: int = 3 # 3회 연속 손실
|
||||
|
||||
27
backend/tests/test_entry_throughput_config.py
Normal file
27
backend/tests/test_entry_throughput_config.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""진입이 차단되지 않도록 하는 설정 관계 검증.
|
||||
|
||||
2026-09-23: 최소 주문금액(50만)이 종목당 예산(총자산×6%≈59만)과 거의 같아,
|
||||
신뢰도·현금 보정으로 조금만 깎여도 전부 탈락했다(하루 141건 min_amount_guard).
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
def test_min_buy_amount_is_well_below_position_budget():
|
||||
"""1,000만원 계좌 기준 종목당 예산의 절반 이하여야 보정 후에도 주문이 살아남는다."""
|
||||
budget = 10_000_000 * settings.MAX_POSITION_SIZE
|
||||
assert settings.MIN_BUY_AMOUNT_KRW <= budget * 0.5, (
|
||||
f"최소주문 {settings.MIN_BUY_AMOUNT_KRW:,.0f} vs 종목당 예산 {budget:,.0f}"
|
||||
)
|
||||
|
||||
|
||||
def test_position_size_allows_enough_concurrent_holdings():
|
||||
"""스윙 자본 상한 안에서 최소 10종목은 동시 보유할 수 있어야 표본이 쌓인다."""
|
||||
swing_cap = getattr(settings, "SWING_MAX_CAPITAL_RATIO", 1.0)
|
||||
assert swing_cap / settings.MAX_POSITION_SIZE >= 10
|
||||
|
||||
|
||||
def test_values_are_intended():
|
||||
assert settings.MAX_POSITION_SIZE == 0.04
|
||||
assert settings.MIN_BUY_AMOUNT_KRW == 150_000
|
||||
Reference in New Issue
Block a user