Skip to content

ddsntc1/ETF_Backtesting

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

61 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ETF Backtesting API

πŸ”§ κ°€μƒν™˜κ²½ μ„ΈνŒ… 및 μ˜μ‘΄μ„± μ„€μΉ˜

1. Python κ°€μƒν™˜κ²½ 생성

# .bat싀행에 λ¬Έμ œμ—†λ„λ‘ 'backtest'λͺ…μ˜ κ°€μƒν™˜κ²½μ„ λ³Έ ν”„λ‘œμ νŠΈμ™€μ™€ 동일 디렉토리에 μ„€μΉ˜ λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.
# .
# β”œβ”€β”€ Backtest  -> κ°€μƒν™˜κ²½
# β”œβ”€β”€ Project /
# β”‚   β”œβ”€β”€ api_test
# β”‚   β”œβ”€β”€ data
# β”‚   β”œβ”€β”€ import_price_data
# β”‚   └── ...

python -m venv backtest

2. κ°€μƒν™˜κ²½ ν™œμ„±ν™”

  • Windows

    backtest\Scripts\activate
  • Linux

    source backtest/bin/activate

3. νŒ¨ν‚€μ§€ μ„€μΉ˜

# ν”„λ‘œμ νŠΈ 폴더/
pip install -r requirements.txt

πŸ—ƒοΈ Database μ„€μ •

PostgreSQLμ—μ„œ backtest DB와 μœ μ €λ₯Ό μ•„λž˜μ™€ 같이 μƒμ„±ν•©λ‹ˆλ‹€:

CREATE DATABASE backtest;
CREATE USER backtest WITH PASSWORD 'backtest';
GRANT ALL PRIVILEGES ON DATABASE backtest TO backtest;

Linuxν™˜κ²½μ—μ„œ μ‹€ν–‰μ‹œ backtest둜 λ‘œκ·ΈμΈν•˜κΈ° μœ„ν•΄ /etc/postgresql/{버전}/main/pg_hba.conf μ—μ„œ 인증방식을 λ³€κ²½ν•΄μ•Ό ν•  수 μžˆμŠ΅λ‹ˆλ‹€. Peer -> md5

DB μ—°κ²° μ •λ³΄λŠ” .env λ˜λŠ” DATABASE_URL ν™˜κ²½λ³€μˆ˜λ‘œ κ΄€λ¦¬ν•©λ‹ˆλ‹€.

#env λ‚΄μš©μž…λ‹ˆλ‹€.
# μœ„μΉ˜ : μ΅œμƒμœ„ 경둜
DATABASE_URL = postgresql://backtest:[email protected]:5432/backtest

πŸ“¦ import_price_data (초기 가격 데이터)

과제 λ‚΄μš© 쀑 μ£Όμ–΄μ§„ 가격데이터λ₯Ό λ°μ΄ν„°λ² μ΄μŠ€μ— λ„£λŠ” κ³Όμ • μž…λ‹ˆλ‹€.

# ν”„λ‘œμ νŠΈ 폴더/
python import_price_data.py

πŸƒ ν”„λ‘œμ νŠΈ μ‹€ν–‰

1. FastAPI μ„œλ²„ μ‹€ν–‰

uvicorn main:app --reload

2. Swagger λ¬Έμ„œ 및 API λ‚΄μš©

API ν™•μΈν•˜κΈ°

곡톡 정보

ν•­λͺ© λ‚΄μš©
Base URL /backtest
Response Format JSON
Auth μ—†μŒ

κΈ°λŠ₯ 1. λ°±ν…ŒμŠ€νŠΈ μ‹€ν–‰ 및 μ €μž₯

  • Method: POST
  • URL: /backtest/run
  • Description: μž…λ ₯값을 λ°”νƒ•μœΌλ‘œ λ°±ν…ŒμŠ€νŠΈ 계산 ν›„ DB에 μ €μž₯ν•˜κ³  κ²°κ³Ό λ°˜ν™˜
βœ… Request Body
{
  "start_year": 2020,
  "start_month": 1,
  "trade_day": 10,
  "initial_balance": 1000,
  "fee_rate": 0.001,
  "weight_months": 6
}
βœ… Response
{
  "data_id": 1,
  "output": {
    "total_return": 0.66,
    "cagr": 0.1043,
    "vol": 0.121,
    "sharpe": 0.86,
    "mdd": -0.1947
  },
  "last_rebalance_weight": [
    ["SPY", 0.5],
    ["QQQ", 0.5],
    ["BIL", 0.0]
  ]
}

κΈ°λŠ₯ 2. λ°±ν…ŒμŠ€νŠΈ 전체 λͺ©λ‘ 쑰회

  • Method: GET
  • URL: /backtest/
  • Description: μ €μž₯된 λͺ¨λ“  λ°±ν…ŒμŠ€νŠΈ data_id와 λ§ˆμ§€λ§‰ λ¦¬λ°ΈλŸ°μ‹± 비쀑 λ°˜ν™˜
βœ… Response
[
  {
    "data_id": 1,
    "last_rebalance_weight": [["SPY", 0.5], ["QQQ", 0.5], ["BIL", 0.0]]
  },
  {
    "data_id": 2,
    "last_rebalance_weight": [["GLD", 0.5], ["QQQ", 0.5], ["BIL", 0.0]]
  }
]

κΈ°λŠ₯ 3. νŠΉμ • λ°±ν…ŒμŠ€νŠΈ κ²°κ³Ό 쑰회

  • Method: GET
  • URL: /backtest/{data_id}
  • Description: νŠΉμ • data_id의 μž…λ ₯κ°’ + 톡계 + λ§ˆμ§€λ§‰ λ¦¬λ°ΈλŸ°μ‹± 비쀑 λ°˜ν™˜
βœ… Response
{
  "input": {
    "start_year": 2020,
    "start_month": 1,
    "invest": 1000,
    "trade_date": 10,
    "cost": 0.001,
    "caculate_month": 6
  },
  "output": {
    "data_id": 1,
    "total_return": 0.66,
    "cagr": 0.1043,
    "vol": 0.121,
    "sharpe": 0.86,
    "mdd": -0.1947
  },
  "last_rebalance_weight": [
    ["SPY", 0.5],
    ["QQQ", 0.5],
    ["BIL", 0.0]
  ]
}

κΈ°λŠ₯ 4. λ°±ν…ŒμŠ€νŠΈ μ‚­μ œ

  • Method: DELETE
  • URL: /backtest/{data_id}
  • Description: νŠΉμ • data_id의 λ°±ν…ŒμŠ€νŠΈ κ²°κ³Ό μ‚­μ œ
βœ… Response
{
  "data_id": 2
}

ν…ŒμŠ€νŠΈμš© API

Endpoint μ„€λͺ…
/backtest/test λ§€λ§€ date 및 ETF 가격 DataFrame을 JSON으둜 λ°˜ν™˜

πŸ“¦ 배치 ν”„λ‘œκ·Έλž¨ (μ¦μ‹œ 마감 ν›„ 가격 μ—…λ°μ΄νŠΈ)

ETF 가격을 μ •κΈ°μ μœΌλ‘œ μ—…λ°μ΄νŠΈν•˜κΈ° μœ„ν•œ 배치 μŠ€ν¬λ¦½νŠΈμž…λ‹ˆλ‹€.

β–Ά Windows – .bat 파일

update_prices.bat μ˜ˆμ‹œ

@echo off
cd /d "%~dp0"  
call "..\{κ°€μƒν™˜κ²½ 이름름}\Scripts\activate.bat"
python update_prices.py
  • μž‘μ—… μŠ€μΌ€μ€„λŸ¬(Task Scheduler)μ—μ„œ .bat νŒŒμΌμ„ μ›ν•˜λŠ” μ‹œκ°„λŒ€μ— μ‹€ν–‰ 등둝

  • 크둀링 μ‹œ EST μ‹œκ°„μœΌλ‘œ μ§„ν–‰ν•˜κΈ° λ•Œλ¬Έμ— ν˜„μž¬ μ‹œκ°„μΈ KST(UTC+9) κΈ°μ€€ μ˜€μ „ 8μ‹œμ— μ‹€ν–‰μ‹œν‚€λ„λ‘ λ“±λ‘ν•˜λ©΄ λ©λ‹ˆλ‹€.

β–Ά Linux – cronjob 등둝

1. νŽΈμ§‘κΈ° μ—΄κΈ°

crontab -e

2. μ•„λž˜ λ‚΄μš© μΆ”κ°€ (KST κΈ°μ€€ 맀일 μ˜€μ „ 8μ‹œ μ‹€ν–‰)

0 8 * * * /home/username/κ°€μƒν™˜κ²½ 경둜/bin/python /home/username/ν”„λ‘œμ νŠΈ 경둜/update_prices.py

venv κ²½λ‘œμ™€ update_prices.py κ²½λ‘œλŠ” μ‹€μ œ ν™˜κ²½μ— 맞게 μˆ˜μ •ν•΄μ£Όμ„Έμš”.


πŸ““ Prices ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”

배치 ν”„λ‘œκ·Έλž¨ ν…ŒμŠ€νŠΈ μ§„ν–‰ 쀑 bat νŒŒμΌμ„ μ‹€ν–‰μ‹œν‚€λ©΄ 데이터가 μž…λ ₯ 될 것이라 μƒκ°ν•˜μ—¬ νŽΈλ¦¬ν•œ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄ μ•„λž˜ SQL문을 λ„£μ—ˆμŠ΅λ‹ˆλ‹€.

# psqlλ‚΄ μ‹€ν–‰ -> κΈ°μ‘΄ 데이터 μ‚­μ œ 및 인덱슀 μ΄ˆκΈ°ν™”
TRUNCATE TABLE prices RESTART IDENTITY;

DBμ΄ˆκΈ°ν™” 이후 import_price_dataλ₯Ό λ‹€μ‹œν•œλ²ˆ μ‹€ν–‰μ‹œν‚€λ©΄ λ¬Έμ œμ—†μ΄ μ§„ν–‰ 될 것이라 μƒκ°ν•©λ‹ˆλ‹€.

python import_price_data.py

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰 μ˜ˆμ‹œ


.
β”œβ”€β”€ api_test/
β”‚   β”œβ”€β”€ main.py
β”‚   β”œβ”€β”€ database.py
β”‚   β”œβ”€β”€ schemas.bat
β”‚   β”œβ”€β”€ routes/
β”‚   β”œβ”€β”€ services/
β”‚   └── migrations/
β”œβ”€β”€ data/
β”‚   └── price_data.csv
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ update_prices.bat
β”œβ”€β”€ update_prices.log
└── update_prices.py

πŸ“Œ 기타 μ°Έκ³  사항

  • 주말(EST κΈ°μ€€)μ—λŠ” 가격이 μ—…λ°μ΄νŠΈλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.