Commit Graph

10 Commits

Author SHA1 Message Date
agentson
d1698dee33 feat: 국내주식 지정가 전환 및 미체결 처리 (#232)
- KISBroker에 get_domestic_pending_orders (TTTC0084R, 실전전용)
  및 cancel_domestic_order (실전 TTTC0013U / 모의 VTTC0013U) 추가
- main.py 국내 주문 price=0 → 지정가 전환 (2곳):
  · BUY +0.2% / SELL -0.2%, kr_round_down으로 KRX 틱 반올림 적용
- handle_domestic_pending_orders 함수 추가:
  · BUY 미체결 → 취소 + buy_cooldown 설정
  · SELL 미체결 → 취소 후 -0.4% 재주문 (최대 1회)
- daily/realtime 두 모드 market 루프 내 domestic pending 호출 추가
  (sell_resubmit_counts는 해외용과 공유, key prefix "KR:" vs 거래소코드)
- 테스트 14개 추가:
  · test_broker.py: TestGetDomesticPendingOrders 3개 + TestCancelDomesticOrder 5개
  · test_main.py: TestHandleDomesticPendingOrders 4개 + TestDomesticLimitOrderPrice 2개

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 22:02:09 +09:00
agentson
d6a389e0b7 feat: 실전 투자 전환 — TR_ID 분기, URL, 신뢰도 임계값, 텔레그램 알림 수정 (#201~#205, #208, #214)
Some checks failed
CI / test (pull_request) Has been cancelled
- #201: 국내/해외 TR_ID 실전/모의 자동 분기
  - get_balance: TTTC8434R(실전) / VTTC8434R(모의)
  - send_order: TTTC0012U/0011U(실전) / VTTC0012U/0011U(모의) [현금주문]
  - get_overseas_balance: TTTS3012R(실전) / VTTS3012R(모의)
  - send_overseas_order: TTTT1002U/1006U(실전) / VTTT1002U/1001U(모의)
- #202: KIS_BASE_URL 기본값 VTS 포트 9443→29443 수정
- #203: PAPER_OVERSEAS_CASH fallback 실전(MODE=live)에서 비활성화, 중복 코드 제거
- #205: BULLISH 시장 BUY confidence 임계값 75→80(기본값) 수정 (CLAUDE.md 비협상 규칙)
- #208: Daily 모드 CircuitBreakerTripped 시 텔레그램 알림 추가
- #214: 시스템 종료 시 notify_system_shutdown() 호출 추가

테스트 22개 추가 (TR_ID 분기 12개, confidence 임계값 1개 수정)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 10:28:24 +09:00
agentson
7834b89f10 fix: domestic current price fetching and KRX tick unit rounding (#157)
Some checks failed
CI / test (pull_request) Has been cancelled
**Problem 1 — Current price always 0**
get_orderbook() used inquire-asking-price-exp-ccn which has no stck_prpr
in output1 (only askp/bidp data). This caused every domestic BUY to be
skipped with "no affordable quantity (cash=..., price=0.00)".

**Problem 2 — KRX tick unit error on limit orders**
Limit order prices were passed unrounded, triggering 호가단위 오류 in VTS.
Also ORD_DVSN was wrongly set to "01" (시장가) for limit orders.

**Fix**
- Add kr_tick_unit(price) and kr_round_down(price) module-level helpers
  implementing KRX 7-tier price tick rules (1/5/10/50/100/500/1000원).
- Add get_current_price(stock_code) → (price, change_pct, foreigner_net)
  using FHKST01010100 / inquire-price API (works in VTS, returns correct
  stck_prpr, prdy_ctrt, frgn_ntby_qty).
- Fix send_order() ORD_DVSN: "00"=지정가, "01"=시장가 (was "01"/"06").
- Apply kr_round_down() to limit order price inside send_order().
- Replace both get_orderbook() calls in main.py with get_current_price().
- Update all 4 test_main.py mock sites to use get_current_price AsyncMock.

**Tests added** (25 new tests, all 646 pass)
- TestKrTickUnit: 13 parametrized boundary cases + 7 round-down cases
- TestGetCurrentPrice: correct fields, correct API path/TR_ID, HTTP error
- TestSendOrderTickRounding: tick rounding, ORD_DVSN 00/01

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 12:40:55 +09:00
agentson
2e550f8b58 fix: correct TR_ID, path, and params for fetch_market_rankings (#155)
Some checks failed
CI / test (pull_request) Has been cancelled
Three bugs found by comparing against KIS official GitHub examples:

1. FID_COND_SCR_DIV_CODE: "20001" → "20171" (volume-rank screen code)
2. FID_TRGT_EXLS_CLS_CODE: "000000" (6-digit) → "0000000000" (10-digit)
3. fluctuation ranking:
   - TR_ID: "FHPST01710100" (invalid) → "FHPST01700000"
   - path: /quotations/volume-rank → /ranking/fluctuation
   - params: volume-rank params → lowercase fluctuation-specific params
     (fid_rank_sort_cls_code, fid_input_cnt_1, fid_prc_cls_code,
      fid_rsfl_rate1, fid_rsfl_rate2, etc.)

Note: VTS (paper trading) does not return data from ranking APIs regardless
of parameter correctness — this is a KIS policy restriction, not a code bug.
These fixes ensure correct behavior when switching to a live account.

Tests: TestFetchMarketRankings (3 tests) added to test_broker.py

Closes #155

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 10:25:38 +09:00
agentson
b484f0daff fix: align cooldown test with wait-and-retry behavior + boost overseas coverage
Some checks failed
CI / test (pull_request) Has been cancelled
- test_token_refresh_cooldown: updated to match the wait-then-retry
  behavior introduced in aeed881 (was expecting fail-fast ConnectionError)
- Added 22 tests for OverseasBroker: get_overseas_price, get_overseas_balance,
  send_overseas_order, _get_currency_code, _extract_ranking_rows
- src/broker/overseas.py coverage: 52% → 100%
- All 594 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 01:12:09 +09:00
agentson
71ac59794e fix: implement comprehensive KIS API rate limiting solution
Some checks failed
CI / test (push) Has been cancelled
Root cause analysis revealed 3 critical issues causing EGW00201 errors:

1. **Hash key bypass** - _get_hash_key() made API calls without rate limiting
   - Every order made 2 API calls but only 1 was rate-limited
   - Fixed by adding rate_limiter.acquire() to _get_hash_key()

2. **Scanner concurrent burst** - scan_market() launched all stocks via asyncio.gather
   - All tasks queued simultaneously creating burst pressure
   - Fixed by adding Semaphore(1) for fully serialized scanning

3. **RPS too aggressive** - 5.0 RPS exceeded KIS API's real ~2 RPS limit
   - Lowered to 2.0 RPS (500ms interval) for maximum safety

Changes:
- src/broker/kis_api.py: Add rate limiter to _get_hash_key()
- src/analysis/scanner.py: Add semaphore-based concurrency control
  - New max_concurrent_scans parameter (default 1, fully serialized)
  - Wrap scan_stock calls with semaphore in _bounded_scan()
  - Remove ineffective asyncio.sleep(0.2) from scan_stock()
- src/config.py: Lower RATE_LIMIT_RPS from 5.0 to 2.0
- tests/test_broker.py: Add 2 tests for hash key rate limiting
- tests/test_volatility.py: Add test for scanner concurrency limit

Results:
- EGW00201 errors: 10 → 0 (100% elimination)
- All 290 tests pass
- 80% code coverage maintained
- Scanner still handles unlimited stocks (just serialized for API safety)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 01:09:34 +09:00
agentson
a56adcd342 fix: add token refresh cooldown to prevent EGW00133 cascading failures (issue #54)
Some checks failed
CI / test (pull_request) Has been cancelled
Prevents rapid retry attempts when token refresh hits KIS API's
1-per-minute rate limit (EGW00133: 접근토큰 발급 잠시 후 다시 시도하세요).

Changes:
- src/broker/kis_api.py:58-61 - Add cooldown tracking variables
- src/broker/kis_api.py:102-111 - Enforce 60s cooldown between refresh attempts
- tests/test_broker.py - Add cooldown behavior tests

Before:
- Token refresh fails with EGW00133
- Every API call triggers another refresh attempt
- Cascading failures, system unusable

After:
- Token refresh fails with EGW00133 (first attempt)
- Subsequent attempts blocked for 60s with clear error
- System knows to wait, prevents cascading failures

Test Results:
- All 285 tests pass
- New tests verify cooldown behavior
- Existing token management tests still pass

Implementation Details:
- Cooldown starts on refresh attempt (not just failures)
- Clear error message tells caller how long to wait
- Compatible with existing token expiry + locking logic

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 00:37:20 +09:00
agentson
95f540e5df fix: add token refresh lock to prevent concurrent API calls (issue #42)
Some checks failed
CI / test (pull_request) Has been cancelled
Add asyncio.Lock to prevent multiple coroutines from simultaneously
refreshing the KIS access token, which hits the 1-per-minute rate
limit (EGW00133: "접근토큰 발급 잠시 후 다시 시도하세요").

Changes:
- Add self._token_lock in KISBroker.__init__
- Wrap token refresh in async with self._token_lock
- Re-check token validity after acquiring lock (double-check pattern)
- Add concurrent token refresh test (5 parallel requests → 1 API call)

The lock ensures that when multiple coroutines detect an expired token,
only the first one refreshes while others wait and reuse the result.

Fixes: #42

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 00:08:56 +09:00
agentson
b26ff0c1b8 feat: implement timezone-based global market auto-selection
Some checks failed
CI / test (pull_request) Has been cancelled
Implement comprehensive multi-market trading system with automatic
market selection based on timezone and trading hours.

## New Features
- Market schedule module with 10 global markets (KR, US, JP, HK, CN, VN)
- Overseas broker for KIS API international stock trading
- Automatic market detection based on current time and timezone
- Next market open waiting logic when all markets closed
- ConnectionError retry with exponential backoff (max 3 attempts)

## Architecture Changes
- Market-aware trading cycle with domestic/overseas broker routing
- Market context in AI prompts for better decision making
- Database schema extended with market and exchange_code columns
- Config setting ENABLED_MARKETS for market selection

## Testing
- 19 new tests for market schedule (timezone, DST, lunch breaks)
- All 54 tests passing
- Lint fixes with ruff

## Files Added
- src/markets/schedule.py - Market schedule and timezone logic
- src/broker/overseas.py - KIS overseas stock API client
- tests/test_market_schedule.py - Market schedule test suite

## Files Modified
- src/main.py - Multi-market main loop with retry logic
- src/config.py - ENABLED_MARKETS setting
- src/db.py - market/exchange_code columns with migration
- src/brain/gemini_client.py - Dynamic market context in prompts

Resolves #5

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 09:29:25 +09:00
d1750af80f Add complete Ouroboros trading system with TDD test suite
Some checks failed
CI / test (push) Has been cancelled
Implement the full autonomous trading agent architecture:
- KIS broker with async API, token refresh, leaky bucket rate limiter, and hash key signing
- Gemini-powered decision engine with JSON parsing and confidence threshold enforcement
- Risk manager with circuit breaker (-3% P&L) and fat finger protection (30% cap)
- Evolution engine for self-improving strategy generation via failure analysis
- 35 passing tests written TDD-first covering risk, broker, and brain modules
- CI/CD pipeline, Docker multi-stage build, and AI agent context docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 02:08:48 +09:00