first vibe coding
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
db/models.py - SQLite 스키마 생성
|
||||
기획서 v2.1 기준 4개 테이블
|
||||
"""
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
DB_PATH = os.getenv("DB_PATH", "data/stockbot.db")
|
||||
|
||||
def init_db():
|
||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
c = conn.cursor()
|
||||
|
||||
# 체결 내역
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS trades (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT NOT NULL,
|
||||
ticker TEXT NOT NULL,
|
||||
name TEXT,
|
||||
entry_time TEXT NOT NULL,
|
||||
exit_time TEXT,
|
||||
entry_price REAL NOT NULL,
|
||||
exit_price REAL,
|
||||
quantity INTEGER NOT NULL,
|
||||
side TEXT NOT NULL,
|
||||
exit_reason TEXT,
|
||||
pnl REAL,
|
||||
fee REAL,
|
||||
slippage REAL,
|
||||
strategy TEXT DEFAULT 'VB',
|
||||
ai_boosted INTEGER DEFAULT 0
|
||||
)""")
|
||||
|
||||
# 일일 요약
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS daily_summary (
|
||||
date TEXT PRIMARY KEY,
|
||||
total_trades INTEGER DEFAULT 0,
|
||||
win_trades INTEGER DEFAULT 0,
|
||||
lose_trades INTEGER DEFAULT 0,
|
||||
gross_pnl REAL DEFAULT 0,
|
||||
total_fee REAL DEFAULT 0,
|
||||
net_pnl REAL DEFAULT 0,
|
||||
max_drawdown REAL DEFAULT 0,
|
||||
trading_stopped INTEGER DEFAULT 0
|
||||
)""")
|
||||
|
||||
# 포지션 (장중 현황)
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS positions (
|
||||
ticker TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
entry_time TEXT,
|
||||
entry_price REAL,
|
||||
quantity INTEGER,
|
||||
tp1_done INTEGER DEFAULT 0,
|
||||
target_price REAL,
|
||||
stop_price REAL,
|
||||
ai_boosted INTEGER DEFAULT 0
|
||||
)""")
|
||||
|
||||
# AI 판단 이력
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS ai_context_log (
|
||||
date TEXT PRIMARY KEY,
|
||||
generated_at TEXT,
|
||||
trade_allowed INTEGER,
|
||||
market_sentiment TEXT,
|
||||
sentiment_score INTEGER,
|
||||
risk_level TEXT,
|
||||
hot_sectors TEXT,
|
||||
avoid_sectors TEXT,
|
||||
boosted_tickers TEXT,
|
||||
blacklist_tickers TEXT,
|
||||
position_size_mult REAL,
|
||||
reason TEXT,
|
||||
claude_tokens_used INTEGER,
|
||||
api_call_success INTEGER DEFAULT 1
|
||||
)""")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"DB 초기화 완료: {DB_PATH}")
|
||||
|
||||
def get_conn():
|
||||
return sqlite3.connect(DB_PATH)
|
||||
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
db/repository.py - DB 접근 레이어
|
||||
"""
|
||||
import json
|
||||
from datetime import datetime
|
||||
from app.db.models import get_conn
|
||||
|
||||
|
||||
def save_trade(ticker, name, entry_time, entry_price,
|
||||
quantity, side, ai_boosted=False):
|
||||
with get_conn() as conn:
|
||||
conn.execute("""
|
||||
INSERT INTO trades
|
||||
(date, ticker, name, entry_time, entry_price, quantity, side, ai_boosted)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (datetime.now().strftime("%Y-%m-%d"), ticker, name,
|
||||
entry_time, entry_price, quantity, side,
|
||||
1 if ai_boosted else 0))
|
||||
|
||||
|
||||
def update_trade_exit(ticker, exit_time, exit_price, exit_reason, pnl, fee):
|
||||
with get_conn() as conn:
|
||||
conn.execute("""
|
||||
UPDATE trades SET exit_time=?, exit_price=?,
|
||||
exit_reason=?, pnl=?, fee=?
|
||||
WHERE ticker=? AND exit_time IS NULL
|
||||
ORDER BY id DESC LIMIT 1
|
||||
""", (exit_time, exit_price, exit_reason, pnl, fee, ticker))
|
||||
|
||||
|
||||
def save_daily_summary(date, total, wins, losses,
|
||||
gross_pnl, fee, net_pnl, mdd, stopped):
|
||||
with get_conn() as conn:
|
||||
conn.execute("""
|
||||
INSERT OR REPLACE INTO daily_summary
|
||||
VALUES (?,?,?,?,?,?,?,?,?)
|
||||
""", (date, total, wins, losses, gross_pnl, fee, net_pnl, mdd, stopped))
|
||||
|
||||
|
||||
def save_ai_context(ctx: dict, tokens_used: int = 0, success: bool = True):
|
||||
with get_conn() as conn:
|
||||
conn.execute("""
|
||||
INSERT OR REPLACE INTO ai_context_log
|
||||
(date, generated_at, trade_allowed, market_sentiment,
|
||||
sentiment_score, risk_level, hot_sectors, avoid_sectors,
|
||||
boosted_tickers, blacklist_tickers, position_size_mult,
|
||||
reason, claude_tokens_used, api_call_success)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
""", (
|
||||
ctx.get("date"), ctx.get("generated_at"),
|
||||
1 if ctx.get("trade_allowed") else 0,
|
||||
ctx.get("market_sentiment"), ctx.get("sentiment_score"),
|
||||
ctx.get("risk_level"),
|
||||
json.dumps(ctx.get("hot_sectors", []), ensure_ascii=False),
|
||||
json.dumps(ctx.get("avoid_sectors", []), ensure_ascii=False),
|
||||
json.dumps(ctx.get("boosted_tickers", []), ensure_ascii=False),
|
||||
json.dumps(ctx.get("blacklist_tickers", []), ensure_ascii=False),
|
||||
ctx.get("position_size_multiplier", 1.0),
|
||||
ctx.get("reason", ""),
|
||||
tokens_used, 1 if success else 0,
|
||||
))
|
||||
|
||||
|
||||
def get_today_trades(date: str = None):
|
||||
date = date or datetime.now().strftime("%Y-%m-%d")
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM trades WHERE date=?", (date,)
|
||||
).fetchall()
|
||||
return rows
|
||||
|
||||
|
||||
def get_open_positions():
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM positions"
|
||||
).fetchall()
|
||||
return rows
|
||||
Reference in New Issue
Block a user