docs: add detailed README.md with project documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
184
README.md
Normal file
184
README.md
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
# stock-common
|
||||||
|
|
||||||
|
한국 주식 분석 마이크로서비스 플랫폼의 **공유 라이브러리**. 모든 백엔드 서비스(`stock-dart-collector`, `stock-kis-collector`, `stock-screener` 등)가 이 패키지에 의존합니다.
|
||||||
|
|
||||||
|
## 주요 기능
|
||||||
|
|
||||||
|
- **데이터 모델**: Pydantic v2 기반 도메인 모델 (Stock, DailyPrice, Valuation, Financial, Screening 등)
|
||||||
|
- **데이터베이스**: PostgreSQL (asyncpg) + MongoDB (motor) 커넥션 풀 관리
|
||||||
|
- **Redis Streams**: 서비스 간 비동기 메시지 큐 (publish/consume/ack)
|
||||||
|
- **API 팩토리**: FastAPI 앱 생성 (`/health`, `/streams` 엔드포인트 자동 포함)
|
||||||
|
- **Collector 베이스 클래스**: Rate limiting, 자동 재시도, HTTP 세션 관리
|
||||||
|
- **설정 관리**: pydantic-settings 기반 환경변수 로딩
|
||||||
|
|
||||||
|
## 설치
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
다른 서비스에서 의존성으로 사용:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# pyproject.toml
|
||||||
|
[project]
|
||||||
|
dependencies = ["stock-common"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker 빌드 시:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
COPY stock-common/ /tmp/stock-common/
|
||||||
|
RUN pip install --no-cache-dir /tmp/stock-common/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 모듈 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
src/stock_common/
|
||||||
|
├── __init__.py
|
||||||
|
├── config.py # Settings (pydantic-settings) - 환경변수 관리
|
||||||
|
├── logging_config.py # structlog 기반 로깅 설정
|
||||||
|
├── api_base.py # FastAPI 앱 팩토리 (create_app)
|
||||||
|
├── collector_base.py # BaseCollector ABC (rate limit, retry)
|
||||||
|
├── database/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── postgres.py # asyncpg 커넥션 풀 (get_pg_pool)
|
||||||
|
│ └── mongodb.py # motor 클라이언트 (get_mongo_database)
|
||||||
|
├── queue/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── redis_client.py # Redis 싱글턴 (get_redis)
|
||||||
|
│ └── streams.py # publish / consume / ack
|
||||||
|
└── models/
|
||||||
|
├── __init__.py
|
||||||
|
├── stock.py # Stock, Market
|
||||||
|
├── price.py # DailyPrice
|
||||||
|
├── valuation.py # Valuation (PER, PBR, ROE...)
|
||||||
|
├── financial.py # Financial, PeriodType
|
||||||
|
├── screening.py # ScreeningResult, ScreeningStrategy
|
||||||
|
├── analysis.py # LLMAnalysis, Recommendation
|
||||||
|
├── disclosure.py # Disclosure, Sentiment
|
||||||
|
└── news.py # News
|
||||||
|
```
|
||||||
|
|
||||||
|
## 핵심 컴포넌트
|
||||||
|
|
||||||
|
### Settings (config.py)
|
||||||
|
|
||||||
|
pydantic-settings로 환경변수를 자동 로딩합니다. `.env` 파일도 지원합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from stock_common.config import settings
|
||||||
|
|
||||||
|
# 데이터베이스
|
||||||
|
settings.postgres_dsn # postgresql://stock:stock@localhost:5432/stockdb
|
||||||
|
settings.mongodb_uri # mongodb://localhost:27017
|
||||||
|
settings.redis_url # redis://localhost:6379/0
|
||||||
|
|
||||||
|
# API 키
|
||||||
|
settings.dart_api_key
|
||||||
|
settings.kis_app_key
|
||||||
|
settings.anthropic_api_key
|
||||||
|
|
||||||
|
# Rate Limits
|
||||||
|
settings.dart_rate_limit # 10.0 req/s
|
||||||
|
settings.kis_rate_limit # 20.0 req/s
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 팩토리 (api_base.py)
|
||||||
|
|
||||||
|
모든 서비스는 `create_app()`으로 FastAPI 인스턴스를 생성합니다. 자동으로 포함되는 엔드포인트:
|
||||||
|
|
||||||
|
| 엔드포인트 | 설명 |
|
||||||
|
|-----------|------|
|
||||||
|
| `GET /health` | 서비스 상태, Redis 연결, uptime |
|
||||||
|
| `GET /streams` | 관련 Redis Stream 큐 길이 |
|
||||||
|
|
||||||
|
```python
|
||||||
|
from stock_common.api_base import create_app
|
||||||
|
|
||||||
|
app = create_app(
|
||||||
|
title="stock-dart-collector",
|
||||||
|
streams=["queue:trigger-dart", "queue:raw-financials"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# 서비스별 엔드포인트 추가
|
||||||
|
@app.post("/collect/financials")
|
||||||
|
async def collect_financials(...):
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### BaseCollector (collector_base.py)
|
||||||
|
|
||||||
|
HTTP API 수집기의 공통 패턴:
|
||||||
|
|
||||||
|
- **Rate Limiting**: `aiolimiter.AsyncLimiter`로 초당 요청 수 제한
|
||||||
|
- **자동 재시도**: `tenacity`로 최대 3회, 지수 백오프 (1~30초)
|
||||||
|
- **429 처리**: `Retry-After` 헤더 존중
|
||||||
|
- **세션 관리**: `async with` 컨텍스트 매니저
|
||||||
|
|
||||||
|
```python
|
||||||
|
from stock_common.collector_base import BaseCollector
|
||||||
|
|
||||||
|
class DARTCollector(BaseCollector):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(rate_limit=10.0, timeout=30)
|
||||||
|
|
||||||
|
async def collect(self, **kwargs):
|
||||||
|
data = await self._request("GET", url, params=params)
|
||||||
|
binary = await self._download_binary(url)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redis Streams (queue/streams.py)
|
||||||
|
|
||||||
|
서비스 간 비동기 메시지 전달:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from stock_common.queue import publish, consume, ack
|
||||||
|
|
||||||
|
# 메시지 발행
|
||||||
|
msg_id = await publish("queue:trigger-dart", {"type": "financials", "stock_codes": ["005930"]})
|
||||||
|
|
||||||
|
# 메시지 소비 (consumer group)
|
||||||
|
messages = await consume("queue:trigger-dart", "dart-collectors", "worker-1", count=10, block=5000)
|
||||||
|
for msg in messages:
|
||||||
|
process(msg.data)
|
||||||
|
await ack("queue:trigger-dart", "dart-collectors", msg.message_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
```python
|
||||||
|
from stock_common.database import get_pg_pool, get_mongo_database
|
||||||
|
|
||||||
|
# PostgreSQL (asyncpg)
|
||||||
|
pool = await get_pg_pool()
|
||||||
|
async with pool.acquire() as conn:
|
||||||
|
rows = await conn.fetch("SELECT * FROM stock WHERE is_active = true")
|
||||||
|
|
||||||
|
# MongoDB (motor)
|
||||||
|
db = get_mongo_database()
|
||||||
|
cursor = db.llm_analysis.find({"stock_code": "005930"})
|
||||||
|
docs = await cursor.to_list(10)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 의존성
|
||||||
|
|
||||||
|
| 패키지 | 용도 |
|
||||||
|
|--------|------|
|
||||||
|
| `pydantic` >= 2.0 | 데이터 모델 |
|
||||||
|
| `pydantic-settings` >= 2.0 | 환경변수 설정 |
|
||||||
|
| `asyncpg` >= 0.29 | PostgreSQL 비동기 드라이버 |
|
||||||
|
| `motor` >= 3.3 | MongoDB 비동기 드라이버 |
|
||||||
|
| `redis` >= 5.0 | Redis 클라이언트 |
|
||||||
|
| `fastapi` >= 0.115 | REST API 프레임워크 |
|
||||||
|
| `uvicorn` >= 0.34 | ASGI 서버 |
|
||||||
|
| `aiohttp` >= 3.9 | 비동기 HTTP 클라이언트 |
|
||||||
|
| `aiolimiter` >= 1.1 | Rate limiting |
|
||||||
|
| `tenacity` >= 8.2 | 재시도 로직 |
|
||||||
|
| `structlog` >= 24.0 | 구조화된 로깅 |
|
||||||
|
|
||||||
|
## 요구사항
|
||||||
|
|
||||||
|
- Python >= 3.12
|
||||||
|
- 빌드: hatchling
|
||||||
Reference in New Issue
Block a user