ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

๐Ÿ’ผ ์‹ค์ „ ํ€€ํŠธ ํŽ€๋“œ ์šด์˜ ์‹œ์Šคํ…œ – ๋ฐฑํ…Œ์ŠคํŠธ์™€ ์‹ค์‹œ๊ฐ„ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ๊ตฌ์ถ•

— AI๊ฐ€ ์˜ˆ์ธกํ•˜๊ณ , ์‹ค์‹œ๊ฐ„ ์‹œ์žฅ๊ณผ ์Šค์Šค๋กœ ๋™๊ธฐํ™”ํ•˜๋Š” ์™„์ „ํ•œ ํŠธ๋ ˆ์ด๋”ฉ ์—”์ง„ ๋งŒ๋“ค๊ธฐ

์ง€๋‚œ ๊ธ€์—์„œ ์šฐ๋ฆฌ๋Š” Airflow + MLflow ๊ธฐ๋ฐ˜์˜ ์™„์ „ ์ž๋™ํ™”๋œ AI ํ€€ํŠธ ํŒŒ์ดํ”„๋ผ์ธ์„ ์™„์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด์ œ ๊ทธ ๋‹ค์Œ ๋‹จ๊ณ„, ์ฆ‰ **“์‹ค์ œ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ์™€ ๋™๊ธฐํ™”๋˜๋Š” ์‹ค์ „ ํŽ€๋“œ ์šด์šฉ ์‹œ์Šคํ…œ”**์„ ๋งŒ๋“ค ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

์˜ค๋Š˜์€ ๋ฐฑํ…Œ์ŠคํŠธ ์‹œ์Šคํ…œ๊ณผ ์‹ค์‹œ๊ฐ„ ๊ฑฐ๋ž˜ ๋ชจ๋“ˆ์„ ๊ฒฐํ•ฉํ•ด,
AI ๋ชจ๋ธ์˜ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฅผ ์‹œ์žฅ์—์„œ ์ฆ‰์‹œ ๊ฒ€์ฆํ•˜๊ณ  ๋ฐ˜์˜ํ•˜๋Š” ๊ตฌ์กฐ
๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.


๐ŸŽฏ ์ด๋ฒˆ ๊ธ€์˜ ๋ชฉํ‘œ

1๏ธโƒฃ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋กœ ๋ฐฑํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰
2๏ธโƒฃ ์‹ค์‹œ๊ฐ„ ์‹œ์„ธ(REST/WebSocket)์™€ ์ž๋™ ๋™๊ธฐํ™”
3๏ธโƒฃ ๋ฐฑํ…Œ์ŠคํŠธ ↔ ์‹ค๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ฉ ์ €์žฅ
4๏ธโƒฃ ์„ฑ๊ณผ ๋ฆฌํฌํŠธ ์ž๋™ ์—…๋ฐ์ดํŠธ


โš™๏ธ 1๏ธโƒฃ ์‹œ์Šคํ…œ ๊ตฌ์„ฑ๋„

[ ๋ฐ์ดํ„ฐ ๊ณ„์ธต ]
   โ”œโ”€โ”€ PostgreSQL (factor, trade_log, live_quotes)
   โ”œโ”€โ”€ Redis (์‹ค์‹œ๊ฐ„ ์บ์‹œ)
   โ””โ”€โ”€ S3 (๋ฐฑํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋ฐฑ์—…)

[ ์ฒ˜๋ฆฌ ๊ณ„์ธต ]
   โ”œโ”€โ”€ Transformer + RL Model (์˜ˆ์ธก)
   โ”œโ”€โ”€ Backtest Engine (๋ฐฑํ…Œ์ŠคํŠธ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ)
   โ”œโ”€โ”€ Trade Executor (์‹ค๊ฑฐ๋ž˜ API)
   โ””โ”€โ”€ Sync Engine (์‹œ์žฅ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”)

[ ๋Œ€์‹œ๋ณด๋“œ ]
   โ”œโ”€โ”€ Streamlit (์„ฑ๊ณผ ๋ฆฌํฌํŠธ)
   โ””โ”€โ”€ Grafana (์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง)

๐Ÿ“Š 2๏ธโƒฃ ๋ฐฑํ…Œ์ŠคํŠธ ์—”์ง„ ๊ตฌ์ถ•

๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํฌํŠธํด๋ฆฌ์˜ค ์ „๋žต์˜ ์„ฑ๊ณผ๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ฉ๋‹ˆ๋‹ค.

import pandas as pd
import numpy as np

class Backtester:
    def __init__(self, price_df, signals_df, initial_cash=1_000_000):
        self.price_df = price_df
        self.signals_df = signals_df
        self.cash = initial_cash
        self.holdings = {ticker: 0 for ticker in price_df.columns}
        self.value_log = []

    def run(self):
        for date in self.price_df.index:
            prices = self.price_df.loc[date]
            signals = self.signals_df.loc[date]

            # ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ: ๋น„์ค‘ ์กฐ์ •
            total_value = self.cash + sum(self.holdings[t]*prices[t] for t in prices.index)
            target_value = {t: total_value * signals[t] for t in prices.index}

            # ๋งค๋งค ์‹คํ–‰
            for t in prices.index:
                diff = target_value[t] - self.holdings[t]*prices[t]
                shares = diff / prices[t]
                self.holdings[t] += shares
                self.cash -= shares * prices[t]

            portfolio_value = self.cash + sum(self.holdings[t]*prices[t] for t in prices.index)
            self.value_log.append(portfolio_value)

        return pd.Series(self.value_log, index=self.price_df.index)

๐Ÿ‘‰ signals_df ๋Š” Transformer+RL ๋ชจ๋ธ์˜ ์˜ˆ์ธก ๋น„์ค‘ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.


๐Ÿง  3๏ธโƒฃ ์‹ค์‹œ๊ฐ„ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”

๋ฐ˜์‘ํ˜•

์‹ค์‹œ๊ฐ„ ์‹œ์žฅ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
(๊ตญ๋‚ด ํ™˜๊ฒฝ์—์„œ๋Š” ํ‚ค์›€ OpenAPI, Upbit API, Alpha Vantage WebSocket ๋“ฑ์„ ์‚ฌ์šฉ)

import websocket, json, threading, time, redis

r = redis.Redis(host="localhost", port=6379)

def on_message(ws, message):
    data = json.loads(message)
    symbol = data["symbol"]
    price = data["price"]
    timestamp = data["timestamp"]
    r.hset("live_quotes", symbol, price)
    print(f"[{symbol}] {price} ({timestamp})")

def start_stream():
    ws = websocket.WebSocketApp(
        "wss://stream.binance.com/ws/btcusdt@trade",
        on_message=on_message
    )
    ws.run_forever()

threading.Thread(target=start_stream, daemon=True).start()
time.sleep(2)
print("โœ… ์‹ค์‹œ๊ฐ„ ์‹œ์„ธ ์ŠคํŠธ๋ฆผ ์‹œ์ž‘ ์™„๋ฃŒ")

๐Ÿ’ก ์ด ๋ฐ์ดํ„ฐ๋Š” Redis์— ์ €์žฅ๋˜์–ด Flask API๋‚˜ Streamlit ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์ฆ‰์‹œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ” 4๏ธโƒฃ ๋ฐฑํ…Œ์ŠคํŠธ ↔ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํ†ตํ•ฉ

import pandas as pd

def sync_backtest_live():
    backtest = pd.read_sql("SELECT * FROM backtest_results", con=engine)
    live_quotes = {k.decode(): float(v) for k, v in r.hgetall("live_quotes").items()}
    merged = backtest.copy()
    merged["current_price"] = merged["ticker"].map(live_quotes)
    merged["current_pnl"] = (merged["current_price"] - merged["entry_price"]) * merged["quantity"]
    merged.to_sql("portfolio_snapshot", engine, if_exists="replace", index=False)

์ด ํ•จ์ˆ˜๋Š”
๋ฐฑํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ + ์‹ค์‹œ๊ฐ„ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ
ํ˜„์žฌ ์†์ต(PnL), ํ‰๊ฐ€๊ธˆ์•ก, ์ˆ˜์ต๋ฅ  ๋“ฑ์„ ์ž๋™ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“ˆ 5๏ธโƒฃ Streamlit ์‹ค์‹œ๊ฐ„ ์„ฑ๊ณผ ๋ฆฌํฌํŠธ

import streamlit as st
import pandas as pd
from sqlalchemy import create_engine

engine = create_engine("postgresql://quant_user:quant_pass@localhost:5432/quantdb")

st.title("๐Ÿ“ˆ Quant AI Live Performance Dashboard")

df = pd.read_sql("SELECT * FROM portfolio_snapshot", engine)

col1, col2 = st.columns(2)
col1.metric("์ด ํˆฌ์ž๊ธˆ", f"{df['current_price'].sum():,.0f} ์›")
col2.metric("ํ‰๊ท  ์ˆ˜์ต๋ฅ ", f"{(df['current_pnl'].sum()/df['entry_price'].sum()):.2%}")

st.line_chart(df.set_index("date")["current_pnl"])
st.dataframe(df)

๐Ÿ“Š ์ด Streamlit ๋Œ€์‹œ๋ณด๋“œ๋Š” ๋งค์ดˆ ์—…๋ฐ์ดํŠธ๋˜๋Š” ์‹ค์‹œ๊ฐ„ PnL, ๋ˆ„์  ์ˆ˜์ต๋ฅ , ํฌํŠธ ๋น„์ค‘ ๋ณ€ํ™”๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.


๐Ÿงฎ 6๏ธโƒฃ ์„ฑ๊ณผ ๋น„๊ต ์ž๋™ ๋ฆฌํฌํŠธ

๋ฆฌํฌํŠธ๋Š” ๋งค์ผ ์ž๋™ ์ƒ์„ฑ๋˜์–ด MLflow์— ์—…๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
import datetime

def generate_daily_report():
    doc = SimpleDocTemplate(f"reports/quant_{datetime.date.today()}.pdf")
    styles = getSampleStyleSheet()
    elements = [Paragraph("๐Ÿ“˜ Daily Quant Report", styles["Title"])]
    df = pd.read_sql("SELECT * FROM portfolio_snapshot", engine)
    total_return = df["current_pnl"].sum()/df["entry_price"].sum()
    elements.append(Paragraph(f"์ด ์ˆ˜์ต๋ฅ : {total_return:.2%}", styles["Normal"]))
    doc.build(elements)
    print("โœ… ์ผ์ผ ๋ฆฌํฌํŠธ ์ƒ์„ฑ ์™„๋ฃŒ")

๐Ÿงฐ 7๏ธโƒฃ Airflow DAG๊ณผ ์—ฐ๊ฒฐ

def sync_task():
    sync_backtest_live()
    generate_daily_report()
    requests.post(os.getenv("SLACK_WEBHOOK_URL"), json={"text": "๐Ÿ“Š ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ๋ฐ ๋ฆฌํฌํŠธ ์™„๋ฃŒ"})

sync_operator = PythonOperator(
    task_id="sync_and_report",
    python_callable=sync_task,
)

์ด์ œ Airflow์—์„œ ๋งค์ผ ์˜ค์ „ 9์‹œ๋งˆ๋‹ค

  • ์‹œ์žฅ ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ 
  • PnL ๋™๊ธฐํ™”
  • ๋ฆฌํฌํŠธ ์ƒ์„ฑ
  • Slack ์•Œ๋ฆผ
    ์ด ์ž๋™์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

โš™๏ธ 8๏ธโƒฃ ์„ฑ๋Šฅ ๊ฒ€์ฆ (์ƒ˜ํ”Œ ๊ฒฐ๊ณผ)

๊ตฌ๋ถ„ ์—ฐํ‰๊ท  ์ˆ˜์ต๋ฅ  MDD ์ƒคํ”„์ง€์ˆ˜ ์ž๋™ํ™” ๋ ˆ๋ฒจ

๋‹จ์ˆœ ๋ฐฑํ…Œ์ŠคํŠธ 12.8% -37% 0.95 ์ˆ˜๋™
AI ์˜ˆ์ธก + ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ 15.9% -26% 1.22 ๋ฐ˜์ž๋™
AI + ์‹ค์‹œ๊ฐ„ ๋™๊ธฐํ™” ์‹œ์Šคํ…œ 18.7% -19% 1.47 ์™„์ „ ์ž๋™

์‹ค์‹œ๊ฐ„ ์‹œ์žฅ ๋ฐ˜์‘ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ•ํ™”ํ•™์Šต ๋ชจ๋ธ์ด ์Šค์Šค๋กœ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ์กฐ์ •ํ•˜๋ฉด์„œ
๋ฆฌ์Šคํฌ๋Š” ์ค„๊ณ  ๋ˆ„์  ์ˆ˜์ต๋ฅ ์€ ๋”์šฑ ์•ˆ์ •์ ์œผ๋กœ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“Œ ์ •๋ฆฌ

๊ตฌ์„ฑ ์š”์†Œ ์—ญํ• 

Backtester ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
Live Sync ์‹ค์‹œ๊ฐ„ ๊ฑฐ๋ž˜์†Œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
PostgreSQL + Redis ์ €์žฅ ๋ฐ ์บ์‹ฑ
Streamlit ์‹ค์‹œ๊ฐ„ ์‹œ๊ฐํ™”
Airflow ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ํ™”
MLflow ๋ชจ๋ธ ๊ด€๋ฆฌ ๋ฐ ๋ฆฌํฌํŠธ ๋ฒ„์ „ ๊ด€๋ฆฌ

๊ฒฐ๊ณผ์ ์œผ๋กœ,

์ด์ œ AI ๋ชจ๋ธ์ด ๊ณผ๊ฑฐ๋ฅผ ํ•™์Šตํ•˜๊ณ ,
ํ˜„์žฌ ์‹œ์žฅ๊ณผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•˜๋ฉฐ,
๊ฒฐ๊ณผ๋ฅผ ์Šค์Šค๋กœ ๊ธฐ๋กํ•˜๊ณ  ํ‰๊ฐ€ํ•˜๋Š” ์‹œ์Šคํ…œ์ด ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“˜ ๋‹ค์Œ ๊ธ€ ์˜ˆ๊ณ 

๋‹ค์Œ ํŽธ์—์„œ๋Š” **“๋ฆฌ์Šคํฌ ๊ด€๋ฆฌ ์ž๋™ํ™” – VaR, CVaR, ํšŒ๋ณต ๊ธฐ๊ฐ„ ๊ธฐ๋ฐ˜์˜ ๋™์  ๋ฆฌ์Šคํฌ ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„”**์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.
์ฆ‰, ๋ชจ๋ธ์ด ์˜ˆ์ธกํ•œ ์‹œ์žฅ ์œ„ํ—˜์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ํฌํŠธํด๋ฆฌ์˜ค ๋น„์ค‘์„ ์ค„์ด๊ฑฐ๋‚˜ ํ™•๋Œ€ํ•˜๋Š”
“AI ๋ฆฌ์Šคํฌ ๋งค๋‹ˆ์ €”๋ฅผ ๋งŒ๋“ค์–ด๋ด…๋‹ˆ๋‹ค.

 

ํ€€ํŠธํŽ€๋“œ,AIํŠธ๋ ˆ์ด๋”ฉ,๋ฐฑํ…Œ์ŠคํŠธ,์‹ค์‹œ๊ฐ„๋ฐ์ดํ„ฐ,ํŒŒ์ด์ฌํŠธ๋ ˆ์ด๋”ฉ,Streamlit,Airflow,Redis,PostgreSQL,ํ€€ํŠธ์ž๋™ํ™”


 

โ€ป ์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2026/02   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•