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

๋ฐ˜์‘ํ˜•

๐Ÿงญ ํ€€ํŠธ ์ž๋™๋งค๋งค ์„œ๋ฒ„ ๊ตฌ์ถ• – Flask + Scheduler + DB๋กœ ์™„์ „ ์ž๋™ํ™” ์‹œ์Šคํ…œ ๋งŒ๋“ค๊ธฐ

์ง€๋‚œ ๊ธ€์—์„œ๋Š” ํ‚ค์›€ OpenAPI๋ฅผ ์ด์šฉํ•œ ์‹ค์‹œ๊ฐ„ ์ž๋™ ๋งค๋งค ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด์ œ๋Š” ์ด ๋กœ์ง์„ ๊ฐœ์ธ ๋…ธํŠธ๋ถ์ด ์•„๋‹ˆ๋ผ, ํ•ญ์ƒ ์ผœ์ ธ ์žˆ๋Š” ์„œ๋ฒ„์—์„œ ์•ˆ์ •์ ์œผ๋กœ ๋Œ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋Š˜์€ Python์˜ Flask ์›น ์„œ๋ฒ„, ์Šค์ผ€์ค„๋Ÿฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ฒฐํ•ฉํ•ด
์‹ค์ œ ํŽ€๋“œ ์‹œ์Šคํ…œ์ฒ˜๋Ÿผ “์ž๋™์œผ๋กœ ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ → ์ฃผ๋ฌธ → ๋กœ๊ทธ ๊ธฐ๋ก → ๋Œ€์‹œ๋ณด๋“œ ํ‘œ์‹œ”๊ฐ€ ๋Œ์•„๊ฐ€๋Š”
๋ฐฑ์—”๋“œํ˜• ํ€€ํŠธ ๋งค๋งค ์—”์ง„์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


1๏ธโƒฃ ์‹œ์Šคํ…œ ๊ฐœ์š”

[ Flask ์„œ๋ฒ„ ]  
     โ”œโ”€โ”€ /trade (์ฃผ๋ฌธ ์‹คํ–‰ API)  
     โ”œโ”€โ”€ /report (์„ฑ๊ณผ ์กฐํšŒ API)  
     โ”œโ”€โ”€ /rebalance (ํŒฉํ„ฐ ๊ธฐ๋ฐ˜ ์ข…๋ชฉ ๊ฐฑ์‹ )  
     โ””โ”€โ”€ Scheduler (๋งค์ผ 09:00 ์ž๋™ ์‹คํ–‰)
          ↓
       [ DB (SQLite or PostgreSQL) ]
          ↓
       [ Log + Report ์ž๋™ ์—…๋ฐ์ดํŠธ ]

์ด ๊ตฌ์กฐ์˜ ํ•ต์‹ฌ์€,
๐Ÿ‘‰ API ์„œ๋ฒ„๋กœ ๋งŒ๋“ค์–ด๋‘๋ฉด ์›น/๋ชจ๋ฐ”์ผ/๋Œ€์‹œ๋ณด๋“œ ์–ด๋””์„œ๋“  ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.


2๏ธโƒฃ ๊ธฐ๋ณธ ์„ธํŒ…

๐Ÿ“ฆ ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

pip install flask apscheduler pandas sqlalchemy yfinance
  • flask: ์›น API ์„œ๋ฒ„
  • apscheduler: ์ฃผ๊ธฐ์  ์ž‘์—… ์ž๋™ ์‹คํ–‰
  • sqlalchemy: DB ORM
  • yfinance: ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

3๏ธโƒฃ Flask ์„œ๋ฒ„ ๊ธฐ๋ณธ ๊ตฌ์กฐ

from flask import Flask, jsonify
from apscheduler.schedulers.background import BackgroundScheduler
from sqlalchemy import create_engine
import pandas as pd, yfinance as yf, datetime

app = Flask(__name__)
engine = create_engine("sqlite:///quant.db", echo=False)

# ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ๋ฐ ์ €์žฅ ํ•จ์ˆ˜
def collect_factor_data():
    tickers = ["005930.KS", "000660.KS", "035420.KS"]
    rows = []
    for t in tickers:
        df = yf.download(t, period="1y")
        ret = (df["Close"].iloc[-1] / df["Close"].iloc[-120]) - 1
        vol = df["Close"].pct_change().std()
        rows.append({"ticker": t, "return": ret, "vol": vol, "updated": datetime.datetime.now()})
    pd.DataFrame(rows).to_sql("factors", engine, if_exists="replace", index=False)
    print("โœ… ํŒฉํ„ฐ ๋ฐ์ดํ„ฐ ๊ฐฑ์‹  ์™„๋ฃŒ")

# ๋งค๋งค ํ•จ์ˆ˜ (๋‹จ์ˆœ ์˜ˆ์‹œ)
def execute_trade():
    df = pd.read_sql("SELECT * FROM factors", engine)
    top = df.sort_values("return", ascending=False).iloc[0]
    print(f"๐Ÿš€ {top['ticker']} ๋งค์ˆ˜ ์‹คํ–‰ (return={top['return']:.2%})")

# ์Šค์ผ€์ค„๋Ÿฌ ๋“ฑ๋ก
scheduler = BackgroundScheduler()
scheduler.add_job(collect_factor_data, "cron", hour=8, minute=55)  # ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ 
scheduler.add_job(execute_trade, "cron", hour=9, minute=1)         # ๋งค์ˆ˜ ์‹คํ–‰
scheduler.start()

@app.route("/report", methods=["GET"])
def report():
    df = pd.read_sql("SELECT * FROM factors", engine)
    return jsonify(df.to_dict(orient="records"))

if __name__ == "__main__":
    collect_factor_data()
    app.run(host="0.0.0.0", port=5000)

4๏ธโƒฃ ์ฝ”๋“œ ์„ค๋ช…

๋ฐ˜์‘ํ˜•

๊ธฐ๋Šฅ ์„ค๋ช…

collect_factor_data() yfinance๋กœ ์ข…๋ชฉ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ํ›„ DB ์ €์žฅ
execute_trade() ์ƒ์œ„ ํŒฉํ„ฐ ์ ์ˆ˜ ์ข…๋ชฉ ์ž๋™ ๋งค์ˆ˜ (API ์—ฐ๋™ ๊ฐ€๋Šฅ)
apscheduler ์ •ํ•ด์ง„ ์‹œ๊ฐ์— ํ•จ์ˆ˜ ์ž๋™ ์‹คํ–‰
/report ํ˜„์žฌ ํฌํŠธํด๋ฆฌ์˜ค ๋ฐ ํŒฉํ„ฐ ์ ์ˆ˜ REST API๋กœ ์ œ๊ณต

๐Ÿ’ก ์ด ์„œ๋ฒ„๋ฅผ AWS EC2, Google Cloud, ๋˜๋Š” NAS ์„œ๋ฒ„์— ์˜ฌ๋ ค๋‘๋ฉด,
๋งค์ผ ์•„์นจ ์ž๋™์œผ๋กœ “๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ → ์ข…๋ชฉ ์„ ํƒ → ์ฃผ๋ฌธ ์‹คํ–‰”๊นŒ์ง€ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.


5๏ธโƒฃ DB ๊ตฌ์กฐ (SQLite ์˜ˆ์‹œ)

์ปฌ๋Ÿผ ์„ค๋ช…

ticker ์ข…๋ชฉ ์ฝ”๋“œ
return ์ตœ๊ทผ 6๊ฐœ์›” ์ˆ˜์ต๋ฅ 
vol ๋ณ€๋™์„ฑ
updated ์—…๋ฐ์ดํŠธ ์‹œ๊ฐ

์ด DB๋Š” ๋ฆฌํฌํŠธ ์ƒ์„ฑ๊ธฐ(PDF)์™€ ์—ฐ๊ฒฐ๋˜์–ด
๋งค์›” ์ž๋™ ํˆฌ์ž ์„ฑ๊ณผ๋ฅผ ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


6๏ธโƒฃ ๋Œ€์‹œ๋ณด๋“œ ์—ฐ๋™ (์˜ต์…˜)

Streamlit ์—ฐ๋™ ์˜ˆ์‹œ

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

engine = create_engine("sqlite:///quant.db")
df = pd.read_sql("SELECT * FROM factors", engine)

st.title("๐Ÿ“Š Quant Trading Dashboard")
st.dataframe(df)
st.bar_chart(df.set_index("ticker")["return"])

๐Ÿ‘‰ streamlit run dashboard.py
→ ์›น ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŒฉํ„ฐ ์ ์ˆ˜์™€ ์ข…๋ชฉ ํ˜„ํ™ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


7๏ธโƒฃ ์šด์˜ ์ž๋™ํ™” ํŒ

ํ•ญ๋ชฉ ์„ค๋ช…

๐Ÿ•“ Scheduler APScheduler๋กœ ์ •๊ธฐ ์‹คํ–‰ (cron ํ˜•์‹)
๐Ÿงพ ๋กœ๊ทธ ๊ด€๋ฆฌ logging ๋ชจ๋“ˆ๋กœ ๋ชจ๋“  ์ฃผ๋ฌธ ๊ธฐ๋ก ๋‚จ๊ธฐ๊ธฐ
โ˜๏ธ ๋ฐฐํฌ AWS EC2 / Railway / Docker๋กœ ๋ฐฐํฌ
๐Ÿ”’ ๋ณด์•ˆ ํ‚ค์›€ API ํ‚ค๋Š” .env์— ์ €์žฅ ํ›„ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๋กœ๋“œ
๐Ÿง  ์žฅ์•  ๋ณต๊ตฌ ์‹คํŒจ ์‹œ Slack/Email ์•Œ๋ฆผ ์ถ”๊ฐ€ ์ถ”์ฒœ

๐Ÿ“Œ ์ •๋ฆฌ

๋‹จ๊ณ„ ๋‚ด์šฉ

1 Flask ์„œ๋ฒ„๋กœ ์ž๋™๋งค๋งค API ๊ตฌ์„ฑ
2 APScheduler๋กœ ์ž๋™ ์‹คํ–‰ ๋“ฑ๋ก
3 SQLite๋กœ ๋ฐ์ดํ„ฐ ์˜๊ตฌ ์ €์žฅ
4 Streamlit์œผ๋กœ ์‹œ๊ฐํ™” ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ
5 ํด๋ผ์šฐ๋“œ ๋ฐฐํฌ๋กœ ์™„์ „ ์ž๋™ํ™” ์™„์„ฑ

์ด์ œ ๋…ธํŠธ๋ถ์„ ์ผœ๋‘์ง€ ์•Š์•„๋„,
์„œ๋ฒ„๊ฐ€ ์Šค์Šค๋กœ ํˆฌ์žํ•˜๊ณ  ๊ธฐ๋กํ•˜๊ณ  ๋ณด๊ณ ํ•˜๋Š” ์‹œ์Šคํ…œ์ด ์™„์„ฑ๋์Šต๋‹ˆ๋‹ค.


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

๋‹ค์Œ ํŽธ์—์„œ๋Š” **“์‹ค์ „ ์šด์˜ ๋ชจ๋“œ – ์˜ค๋ฅ˜ ๊ฐ์ง€, ์Šฌ๋ž™ ์•Œ๋ฆผ, ๋ฐฑ์—… ์ „๋žต”**์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.
์ฆ‰, ์‹ค์ œ ์šด์šฉ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ •์ ์œผ๋กœ ๋Œ์•„๊ฐ€๊ฒŒ ๋งŒ๋“œ๋Š” ์šด์˜ ์ž๋™ํ™”(Ops) ๊ตฌ์กฐ ์„ค๊ณ„๋ฅผ ๋ณด์—ฌ๋“œ๋ฆด๊ฒŒ์š”.


 

ํ€€ํŠธ์„œ๋ฒ„,Flask์ž๋™๋งค๋งค,ํŒŒ์ด์ฌ์Šค์ผ€์ค„๋Ÿฌ,apscheduler,quantAPI,๋ฐ์ดํ„ฐํˆฌ์ž,์ž๋™๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ,ํ€€ํŠธ๋Œ€์‹œ๋ณด๋“œ,streamlit,์ฃผ์‹์ž๋™ํ™”


 

โ€ป ์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
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
๊ธ€ ๋ณด๊ด€ํ•จ
๋ฐ˜์‘ํ˜•