chore: 미사용 파일/의존성 정리
- HtsHeader.tsx/css 삭제 (레거시, 미사용) - realtime/examples.py 삭제 (예제 파일, 프로덕션 미사용) - backtest_results/ 삭제 + .gitignore 추가 - .env.bak 삭제 - requirements.txt: celery 제거 (미사용), httpx 중복 제거 - package.json: recharts 제거 (미사용) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,8 @@
|
||||
"Bash(for f:*)",
|
||||
"Bash(do python:*)",
|
||||
"Bash(echo \"$f OK\")",
|
||||
"Bash(done)"
|
||||
"Bash(done)",
|
||||
"Bash(find /Users/commylife/myproject/java/workspace/auto-stock -type f \\\\\\(-name *.tmp -o -name *.bak -o -name *.swap \\\\\\))"
|
||||
]
|
||||
},
|
||||
"enableAllProjectMcpServers": true,
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -76,6 +76,8 @@ summary_output.json
|
||||
*.bak
|
||||
.env.bak
|
||||
|
||||
# Backtest results (runtime generated)
|
||||
backend/backtest_results/
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
.venv/
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
"""
|
||||
Realtime Module Usage Examples
|
||||
|
||||
Realtime 모듈 사용 예시
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
|
||||
from app.realtime import KISWebSocketClient, event_handler, EventType, market_scheduler
|
||||
from app.realtime.websocket_client import create_websocket_client
|
||||
from app.strategy import strategy_manager
|
||||
from app.trading import order_manager, market_data_provider
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ── 예시 1: 기본 실시간 시세 수신 ────────────────────────
|
||||
|
||||
async def example_basic_price_stream():
|
||||
"""기본 실시간 시세 수신 예시"""
|
||||
|
||||
# 핸들러 등록
|
||||
async def on_price_update(event_data: Dict[str, Any]):
|
||||
stock_code = event_data['stock_code']
|
||||
price = event_data['current_price']
|
||||
print(f"💰 {stock_code}: {price}원")
|
||||
|
||||
event_handler.register(EventType.PRICE_UPDATE, on_price_update)
|
||||
|
||||
# WebSocket 연결
|
||||
ws_client = create_websocket_client()
|
||||
await ws_client.connect()
|
||||
|
||||
# 종목 구독
|
||||
await ws_client.subscribe_price("005930") # 삼성전자
|
||||
await ws_client.subscribe_price("000660") # SK하이닉스
|
||||
|
||||
# 메시지 수신 (무한 루프)
|
||||
await ws_client.listen()
|
||||
|
||||
|
||||
# ── 예시 2: 실시간 전략 실행 ───────────────────────────
|
||||
|
||||
async def example_realtime_strategy():
|
||||
"""실시간 시세로 전략 실행"""
|
||||
|
||||
from app.strategy.strategies import DayTradingStrategy
|
||||
|
||||
# 전략 등록
|
||||
strategy = DayTradingStrategy()
|
||||
strategy_manager.register_strategy(strategy)
|
||||
|
||||
# 실시간 시세 수신 시 전략 실행
|
||||
async def on_price_update(event_data: Dict[str, Any]):
|
||||
stock_code = event_data['stock_code']
|
||||
current_price = event_data['current_price']
|
||||
|
||||
# 시장 데이터 구성
|
||||
market_data = {
|
||||
"current_price": current_price,
|
||||
"minute_chart": await market_data_provider.get_minute_chart(stock_code),
|
||||
}
|
||||
|
||||
# 전략 실행
|
||||
signal = await strategy_manager.run_strategy(
|
||||
"DayTrading",
|
||||
stock_code=stock_code,
|
||||
market_data=market_data
|
||||
)
|
||||
|
||||
# 신호 발생 시 주문
|
||||
if signal:
|
||||
logger.info(f"📡 신호 발생: {signal}")
|
||||
|
||||
result = await order_manager.place_order(
|
||||
stock_code=signal.stock_code,
|
||||
order_type=signal.signal_type.value,
|
||||
quantity=10,
|
||||
price=signal.price
|
||||
)
|
||||
|
||||
logger.info(f"📤 주문 결과: {result}")
|
||||
|
||||
event_handler.register(EventType.PRICE_UPDATE, on_price_update)
|
||||
|
||||
# WebSocket 실행
|
||||
ws_client = create_websocket_client()
|
||||
await ws_client.run() # 자동 재연결 포함
|
||||
|
||||
|
||||
# ── 예시 3: 장 스케줄러 활용 ───────────────────────────
|
||||
|
||||
async def example_market_scheduler():
|
||||
"""장 스케줄러 활용 예시"""
|
||||
|
||||
ws_client = None
|
||||
|
||||
# 장 시작 콜백
|
||||
async def on_market_open():
|
||||
nonlocal ws_client
|
||||
logger.info("🔔 장 시작 - WebSocket 연결 및 구독")
|
||||
|
||||
ws_client = create_websocket_client()
|
||||
await ws_client.connect()
|
||||
|
||||
# 관심 종목 구독
|
||||
watch_list = ["005930", "000660", "035420"]
|
||||
for stock_code in watch_list:
|
||||
await ws_client.subscribe_price(stock_code)
|
||||
|
||||
# 수신 시작
|
||||
asyncio.create_task(ws_client.listen())
|
||||
|
||||
# 장 종료 콜백
|
||||
async def on_market_close():
|
||||
logger.info("🔕 장 종료 - WebSocket 연결 해제")
|
||||
|
||||
if ws_client:
|
||||
await ws_client.disconnect()
|
||||
|
||||
# 콜백 등록
|
||||
market_scheduler.register_callbacks(
|
||||
on_open=on_market_open,
|
||||
on_close=on_market_close
|
||||
)
|
||||
|
||||
# 스케줄러 실행
|
||||
await market_scheduler.run()
|
||||
|
||||
|
||||
# ── 예시 4: 복합 예시 (전체 시스템) ──────────────────────
|
||||
|
||||
async def example_full_system():
|
||||
"""전체 자동매매 시스템 예시"""
|
||||
|
||||
from app.strategy.strategies import DayTradingStrategy
|
||||
|
||||
# 1. 전략 등록
|
||||
strategy = DayTradingStrategy(
|
||||
config={
|
||||
"max_positions": 3,
|
||||
"stop_loss_pct": 0.025
|
||||
}
|
||||
)
|
||||
strategy_manager.register_strategy(strategy, priority=10)
|
||||
|
||||
# 2. 이벤트 핸들러 등록
|
||||
async def on_price_update(event_data: Dict[str, Any]):
|
||||
"""실시간 시세 → 전략 실행 → 주문"""
|
||||
stock_code = event_data['stock_code']
|
||||
current_price = event_data['current_price']
|
||||
|
||||
# 시장 데이터 수집
|
||||
market_data = {
|
||||
"current_price": current_price,
|
||||
"minute_chart": await market_data_provider.get_minute_chart(stock_code),
|
||||
}
|
||||
|
||||
# 포트폴리오 데이터
|
||||
balance = await order_manager.get_balance()
|
||||
# ... 보유 종목 파싱
|
||||
|
||||
# 전략 실행
|
||||
signal = await strategy_manager.run_strategy(
|
||||
"DayTrading",
|
||||
stock_code=stock_code,
|
||||
market_data=market_data
|
||||
)
|
||||
|
||||
if signal:
|
||||
# 주문 실행
|
||||
from app.strategy.signal import SignalType
|
||||
|
||||
if signal.signal_type == SignalType.BUY:
|
||||
await order_manager.place_order(
|
||||
stock_code=signal.stock_code,
|
||||
order_type="BUY",
|
||||
quantity=10,
|
||||
price=signal.price
|
||||
)
|
||||
|
||||
elif signal.signal_type in [SignalType.SELL, SignalType.STOP_LOSS, SignalType.TAKE_PROFIT]:
|
||||
await order_manager.place_order(
|
||||
stock_code=signal.stock_code,
|
||||
order_type="SELL",
|
||||
quantity=signal.quantity,
|
||||
price=signal.price
|
||||
)
|
||||
|
||||
event_handler.register(EventType.PRICE_UPDATE, on_price_update)
|
||||
|
||||
# 3. 장 스케줄러 설정
|
||||
ws_client = None
|
||||
|
||||
async def on_market_open():
|
||||
nonlocal ws_client
|
||||
logger.info("🔔 장 시작")
|
||||
|
||||
# WebSocket 연결
|
||||
ws_client = create_websocket_client()
|
||||
await ws_client.connect()
|
||||
|
||||
# 관심 종목 구독
|
||||
watch_list = ["005930", "000660", "035420", "005380"]
|
||||
for code in watch_list:
|
||||
await ws_client.subscribe_price(code)
|
||||
|
||||
# 수신 시작
|
||||
asyncio.create_task(ws_client.listen())
|
||||
|
||||
async def on_market_close():
|
||||
logger.info("🔕 장 종료")
|
||||
|
||||
# WebSocket 연결 해제
|
||||
if ws_client:
|
||||
await ws_client.disconnect()
|
||||
|
||||
market_scheduler.register_callbacks(
|
||||
on_open=on_market_open,
|
||||
on_close=on_market_close
|
||||
)
|
||||
|
||||
# 4. 시스템 실행
|
||||
logger.info("🚀 자동매매 시스템 시작")
|
||||
await market_scheduler.run()
|
||||
|
||||
|
||||
# ── 메인 함수 ─────────────────────────────────────────
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# 예시 선택 실행
|
||||
# asyncio.run(example_basic_price_stream())
|
||||
# asyncio.run(example_realtime_strategy())
|
||||
# asyncio.run(example_market_scheduler())
|
||||
asyncio.run(example_full_system())
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -22,13 +22,11 @@ ta==0.11.0
|
||||
python-dotenv==1.0.0
|
||||
pytz>=2024.1
|
||||
holidays>=0.45
|
||||
celery==5.3.4
|
||||
pytest==7.4.3
|
||||
pytest-asyncio==0.21.1
|
||||
feedparser==6.0.11
|
||||
joblib>=1.3.0
|
||||
beautifulsoup4>=4.12.0
|
||||
httpx>=0.27.1
|
||||
finance-datareader>=0.9.50
|
||||
# ML Ensemble
|
||||
torch>=2.1.0
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
"react-chartjs-2": "^5.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.20.0",
|
||||
"recharts": "^2.10.3",
|
||||
"tailwind-merge": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
.hts-header {
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
padding: 12px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #00ff88;
|
||||
box-shadow: 0 2px 10px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.header-left h1 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #00ff88;
|
||||
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
|
||||
}
|
||||
|
||||
.header-center {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hts-nav {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 5px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 8px 20px;
|
||||
color: #8892b0;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
color: #00ff88;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: #00ff88;
|
||||
color: #0a192f;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.live-indicator {
|
||||
font-size: 11px;
|
||||
padding: 3px 8px;
|
||||
background: rgba(244, 67, 54, 0.2);
|
||||
border: 1px solid #f44336;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.header-right .time {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.mcp-status-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.status-indicator.connected {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #00ff88;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #00ff88;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.status-indicator.disconnected {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #f44336;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #f44336;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.mcp-indicator {
|
||||
font-size: 11px;
|
||||
padding: 3px 8px;
|
||||
background: rgba(0, 255, 136, 0.15);
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 3px;
|
||||
margin-left: 8px;
|
||||
color: #00ff88;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mcp-indicator.offline {
|
||||
background: rgba(244, 67, 54, 0.15);
|
||||
border: 1px solid #f44336;
|
||||
color: #f44336;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* @deprecated 이 컴포넌트는 더 이상 사용되지 않습니다.
|
||||
* Layout.tsx가 메인 헤더 역할을 담당하며, App.tsx에서도 import하지 않습니다.
|
||||
* 삭제 여부는 팀 확인 후 결정하세요.
|
||||
*
|
||||
* HtsHeader.tsx — HTS 스타일 상단 헤더 컴포넌트 (레거시)
|
||||
*
|
||||
* 역할:
|
||||
* - 네비게이션 메뉴, MCP 연결 상태, 현재 시각 표시
|
||||
* - 리스크 체크 버튼을 통한 MCP 포트폴리오 리스크 평가 요청
|
||||
*/
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import api from '../lib/api'
|
||||
import './HtsHeader.css'
|
||||
|
||||
/** 네비게이션 메뉴 항목 정의 */
|
||||
const navItems = [
|
||||
{ path: '/', label: '대시보드' },
|
||||
{ path: '/rules', label: '트레이딩 규칙' },
|
||||
{ path: '/trades', label: '거래 내역' },
|
||||
{ path: '/mcp', label: 'MCP 상태' },
|
||||
]
|
||||
|
||||
/**
|
||||
* HtsHeader — HTS(홈 트레이딩 시스템) 스타일 헤더
|
||||
*
|
||||
* 상태(State):
|
||||
* - currentTime: 1초마다 갱신되는 현재 시각 (setInterval)
|
||||
*
|
||||
* 쿼리:
|
||||
* - mcp-status: MCP 서버 연결 상태 (5초 폴링)
|
||||
*/
|
||||
const HtsHeader = () => {
|
||||
const location = useLocation()
|
||||
// 현재 시각 — 1초 간격 갱신
|
||||
const [currentTime, setCurrentTime] = useState(() => new Date())
|
||||
|
||||
// 1초마다 현재 시각 업데이트
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setCurrentTime(new Date())
|
||||
}, 1000)
|
||||
|
||||
return () => clearInterval(timer)
|
||||
}, [])
|
||||
|
||||
// MCP 서버 연결 상태 가져오기 (5초 폴링)
|
||||
const { data: mcpStatus } = useQuery({
|
||||
queryKey: ['mcp-status'],
|
||||
queryFn: async () => (await api.get('/mcp/status')).data,
|
||||
refetchInterval: 5000,
|
||||
})
|
||||
|
||||
/**
|
||||
* 리스크 체크 핸들러
|
||||
* MCP 서버에 RISK_ASSESSMENT 분석을 요청하고 결과를 alert로 표시
|
||||
*/
|
||||
const handleRiskCheck = async () => {
|
||||
try {
|
||||
const response = await api.post('/mcp/analyze', {
|
||||
analysis_type: 'RISK_ASSESSMENT',
|
||||
data: { message: '현재 포트폴리오 리스크를 분석해주세요' }
|
||||
});
|
||||
const result = response.data;
|
||||
alert(`포트폴리오 리스크 평가:\n\n${result.analysis || result.result}`);
|
||||
} catch (error) {
|
||||
console.error('리스크 평가 실패:', error);
|
||||
alert('리스크 평가에 실패했습니다. MCP 서버를 확인해주세요.');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="hts-header">
|
||||
<div className="header-left">
|
||||
<h1>📊 Auto Stock Trading Dashboard</h1>
|
||||
</div>
|
||||
<div className="header-center">
|
||||
<nav className="hts-nav">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={`nav-item ${location.pathname === item.path ? 'active' : ''}`}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
<div className="header-right">
|
||||
{/* MCP 상태 표시 */}
|
||||
<span className={`mcp-indicator ${mcpStatus?.status === 'connected' ? 'live' : 'offline'}`}>
|
||||
{mcpStatus?.status === 'connected' ? '🟢 MCP LIVE' : '⚫ MCP'}
|
||||
</span>
|
||||
<span className="live-indicator" style={{ marginLeft: '8px' }}>🔴 LIVE</span>
|
||||
<button
|
||||
onClick={handleRiskCheck}
|
||||
style={{
|
||||
marginLeft: '16px',
|
||||
padding: '6px 12px',
|
||||
background: '#f59e0b',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '12px',
|
||||
fontWeight: '600'
|
||||
}}
|
||||
onMouseOver={(e) => e.currentTarget.style.background = '#d97706'}
|
||||
onMouseOut={(e) => e.currentTarget.style.background = '#f59e0b'}
|
||||
>
|
||||
⚠️ 리스크 체크
|
||||
</button>
|
||||
<span className="time" style={{ marginLeft: '16px' }}>{currentTime.toLocaleTimeString('ko-KR')}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HtsHeader
|
||||
Reference in New Issue
Block a user