
Pandas, NumPy, Matplotlib으로 하는 데이터 분석
퀀트처럼 금융 데이터를 정제하고 변환하고 시각화하기
NumPy: 내부를 움직이는 엔진
Python에서 이루어지는 모든 진지한 금융 계산은 결국 NumPy를 거칩니다. Python 리스트는 유연하지만 느립니다—각 원소가 타입 검사 오버헤드를 가진 완전한 Python 객체이기 때문입니다. NumPy 배열은 원시 숫자를 연속된 메모리 블록에 저장하여, 순수 Python 루프보다 100배 빠른 연산을 가능하게 합니다.
실제로 어떤 차이가 있는지 살펴보겠습니다:
import numpy as np
# Create an array of 1 million random daily returns
returns = np.random.normal(0.0005, 0.02, 1_000_000)
# Vectorized operations — no loops needed
cumulative = np.cumprod(1 + returns) # Growth of $1
sharpe = returns.mean() / returns.std() * np.sqrt(252)
max_drawdown = np.min(cumulative / np.maximum.accumulate(cumulative) - 1)
print(f"Sharpe Ratio: {sharpe:.3f}")
print(f"Max Drawdown: {max_drawdown:.2%}")
금융에서 알아야 할 핵심 NumPy 개념:
- 벡터화 연산(Vectorized operations) — 원소 단위로 반복하는 대신 배열 전체에 한 번에 연산을 적용합니다.
- 브로드캐스팅(Broadcasting) — 형태가 다른 배열들을 산술 연산을 위해 자동으로 정렬합니다(예: 모든 원소에서 스칼라 평균을 빼는 경우).
- 선형대수(Linear algebra) —
np.linalg는 포트폴리오 최적화에 쓰이는 행렬 곱셈, 고유값 분해, 솔버를 제공합니다. - 무작위 샘플링(Random sampling) — 몬테카를로 시뮬레이션, 부트스트랩 샘플링, 확률적 모델링은 모두
np.random에 의존합니다.
성능 팁: Python for 루프보다 항상 벡터화된 NumPy 연산을 우선하세요. 500개 자산에 대한 포트폴리오 최적화가 루프로는 30초 걸리지만, 벡터화된 NumPy로는 50밀리초 이내에 끝날 수 있습니다 — 600배의 속도 향상입니다.
Pandas: 금융 데이터를 위한 DataFrame
NumPy가 엔진이라면 pandas는 대시보드입니다. NumPy 배열을 레이블이 붙은 인덱스형 구조로 감싸, 금융 데이터를 직관적이고 표현력 있게 다룰 수 있게 해줍니다.
핵심 구조는 두 가지입니다:
- Series — 인덱스가 있는 하나의 데이터 열입니다(예: 종가의 시계열).
- DataFrame — 각각이 Series인 열들의 테이블로, 공통 인덱스를 공유합니다(예: OHLCV 캔들 데이터).
데이터를 불러오고 살펴보는 방법은 간단합니다:
import pandas as pd
# Read from CSV
df = pd.read_csv("btc_daily.csv", parse_dates=["date"], index_col="date")
# Or from JSON (common in API responses)
df = pd.read_json("https://api.example.com/candles?symbol=ETH")
# Quick inspection
print(df.shape) # (365, 5)
print(df.dtypes) # Column types
print(df.describe()) # Statistical summary
print(df.tail()) # Last 5 rows
pandas가 금융 업계에 필수적인 이유는 바로 DatetimeIndex입니다. 인덱스가 datetime 타입이 되는 순간, 강력한 시계열 연산들을 사용할 수 있게 됩니다:
# Slice by date range
q1_data = df["2025-01":"2025-03"]
# Resample daily data to weekly OHLC
weekly = df["close"].resample("W").ohlc()
# Forward-fill missing data (weekends, holidays)
df = df.asfreq("D").ffill()
이러한 연산들은 금융 데이터의 지저분한 현실—공백, 시간대 차이, 불규칙한 타임스탬프—을 처리해 주므로, 데이터 배관 작업이 아니라 분석 자체에 집중할 수 있습니다.
롤링 계산: 이동평균과 변동성
금융 분석은 본질적으로 구간(window) 기반입니다. 단일 데이터 포인트 하나만 따로 신경 쓰는 경우는 드뭅니다—맥락은 그것이 최근 이력과 어떻게 관련되는지에서 나옵니다. pandas의 .rolling() 메서드가 여기서 핵심 도구입니다.
import pandas as pd
import numpy as np
# Assume df has a "close" column with DatetimeIndex
df["sma_20"] = df["close"].rolling(20).mean()
df["sma_50"] = df["close"].rolling(50).mean()
df["ema_12"] = df["close"].ewm(span=12).mean()
# Daily returns and rolling volatility
df["returns"] = df["close"].pct_change()
df["vol_30d"] = df["returns"].rolling(30).std() * np.sqrt(365)
# Bollinger Bands
df["bb_upper"] = df["sma_20"] + 2 * df["close"].rolling(20).std()
df["bb_lower"] = df["sma_20"] - 2 * df["close"].rolling(20).std()
# Rolling correlation between two assets
df["corr_btc_eth"] = df["btc_returns"].rolling(60).corr(df["eth_returns"])
모든 트레이더가 알아야 할 핵심 롤링 계산:
- 단순이동평균(SMA) — N개 구간에 걸친 동일 가중 평균입니다. 후행하지만 안정적입니다.
- 지수이동평균(EMA) —
.ewm()을 통해 최근 데이터에 더 큰 가중치를 부여합니다. 가격 변화에 더 민감하게 반응합니다. - 롤링 변동성(Rolling Volatility) — 구간 내 수익률의 표준편차를 연율화한 값입니다. 포지션 사이징과 리스크 예산 산정에 필수적입니다.
- 롤링 상관관계(Rolling Correlation) — 두 자산이 시간에 따라 얼마나 함께 움직이는지를 측정합니다. 포트폴리오 분산투자에 핵심적입니다.
GaiaEx와 같은 플랫폼에서는 거래 API를 통해 과거 캔들 데이터를 가져와 pandas DataFrame에 로드하고, 몇 초 만에 이러한 지표들을 계산할 수 있습니다—그러는 동안에도 자산은 여러분의 MPC 지갑 안에서 안전하게 보관됩니다.
포트폴리오 분석을 위한 GroupBy와 집계
여러 자산을 관리하고 있다면 .groupby()가 필수적입니다. 데이터를 범주별로 나누고, 계산을 적용하고, 결과를 결합하는—즉 분할-적용-결합(split-apply-combine) 패턴을 사용할 수 있게 해줍니다.
# DataFrame with multiple assets
trades = pd.DataFrame({
"symbol": ["BTC", "ETH", "BTC", "SOL", "ETH", "BTC"],
"side": ["buy", "buy", "sell", "buy", "sell", "buy"],
"pnl": [120.5, -45.2, 89.0, 210.3, 55.8, -30.1],
"volume": [5000, 3200, 4800, 1500, 2900, 5100],
})
# Performance by asset
summary = trades.groupby("symbol").agg(
total_pnl=("pnl", "sum"),
avg_pnl=("pnl", "mean"),
trade_count=("pnl", "count"),
total_volume=("volume", "sum"),
win_rate=("pnl", lambda x: (x > 0).mean()),
)
print(summary.sort_values("total_pnl", ascending=False))
포트폴리오 작업을 위한 고급 집계 패턴:
- 다단계 groupby — 심볼과 방향(side)으로 그룹화해 자산별 롱 대 숏 성과를 확인합니다.
- 사용자 정의 집계 함수 — 자산 그룹별로 샤프 비율, 소르티노 비율, 최대 드로다운을 계산합니다.
- 피벗 테이블 —
pd.pivot_table()을 사용해 데이터를 재구성하여, 자산별 월간 수익률을 히트맵에 적합한 형태로 표시합니다. - groupby를 활용한 리샘플링 — 시간 기반 리샘플링과 범주형 그룹화를 결합해 다중 자산 시계열 분석을 수행합니다.
Matplotlib: 가격 움직임과 신호 시각화
시각화 없는 데이터는 그저 숫자일 뿐입니다. Matplotlib은 원본 데이터에서는 보이지 않는 패턴을 드러내는 차트로 분석 결과를 변환해 줍니다.
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
fig, axes = plt.subplots(3, 1, figsize=(14, 10),
sharex=True, gridspec_kw={"height_ratios": [3, 1, 1]})
# Price + Moving Averages
axes[0].plot(df.index, df["close"], label="Close", linewidth=1.2)
axes[0].plot(df.index, df["sma_20"], label="SMA 20", linestyle="--")
axes[0].fill_between(df.index, df["bb_upper"], df["bb_lower"],
alpha=0.1, color="blue", label="Bollinger Bands")
axes[0].set_ylabel("Price (USD)")
axes[0].legend(loc="upper left")
# Volume bars
colors = ["green" if c > o else "red"
for c, o in zip(df["close"], df["open"])]
axes[1].bar(df.index, df["volume"], color=colors, alpha=0.7)
axes[1].set_ylabel("Volume")
# Rolling Volatility
axes[2].plot(df.index, df["vol_30d"], color="purple")
axes[2].set_ylabel("30d Volatility")
axes[2].axhline(y=0.8, color="red", linestyle=":", alpha=0.5)
axes[2].xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
plt.tight_layout()
plt.savefig("analysis_dashboard.png", dpi=150)
plt.show()
이렇게 하면 전문적인 3단 패널 대시보드가 만들어집니다: 위쪽에는 볼린저 밴드가 표시된 가격 움직임, 중간에는 거래량, 하단에는 변동성입니다. 이 레이아웃은 전문 트레이딩 터미널에서 볼 수 있는 화면을 그대로 재현합니다.
통계적 시각화를 위해서는 seaborn이 Matplotlib 위에서 더 높은 수준의 함수들을 제공합니다:
import seaborn as sns
# Return distribution
sns.histplot(df["returns"].dropna(), bins=100, kde=True)
# Correlation heatmap across assets
corr_matrix = portfolio_returns.corr()
sns.heatmap(corr_matrix, annot=True, cmap="RdYlGn", center=0)모두 합치기: 완전한 분석 파이프라인
진짜 힘은 이 도구들을 반복 가능한 파이프라인으로 엮을 때 나타납니다. 전문 퀀트들이 매일 사용하는 워크플로우는 다음과 같습니다:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def analyze_asset(symbol: str, df: pd.DataFrame) -> dict:
"""Full analysis pipeline for a single asset."""
df["returns"] = df["close"].pct_change()
df["log_returns"] = np.log(df["close"] / df["close"].shift(1))
return {
"symbol": symbol,
"total_return": (df["close"].iloc[-1] / df["close"].iloc[0]) - 1,
"annual_vol": df["returns"].std() * np.sqrt(365),
"sharpe": df["returns"].mean() / df["returns"].std() * np.sqrt(365),
"max_drawdown": (df["close"] / df["close"].cummax() - 1).min(),
"skewness": df["returns"].skew(),
"kurtosis": df["returns"].kurtosis(),
}
# Analyze multiple assets
results = [analyze_asset(sym, data) for sym, data in assets.items()]
summary = pd.DataFrame(results).set_index("symbol")
print(summary.round(4))
이 파이프라인은 원시 가격 데이터를 입력받아 각 자산의 포괄적인 리스크-수익 프로필을 만들어냅니다. 여기서부터 이 지표들을 포트폴리오 최적화기에 입력하거나, 배분 추천을 생성하거나, 리밸런싱 신호를 발생시킬 수 있습니다.
pandas-NumPy-Matplotlib 스택은 Python 금융의 다른 모든 것이 그 위에 쌓아 올려지는 기반입니다. 이 세 라이브러리를 숙달하면 어떤 시장이든 분석하고, 어떤 지표든 구축하고, 어떤 전략이든 시각화할 수 있는 역량을 갖추게 됩니다—NYSE에서 주식을 거래하든, GaiaEx에서 크립토를 거래하든, CME에서 파생상품을 거래하든 마찬가지입니다.

