Live release feed
Sub-second macro releases for FX backtests
Point-in-time history
Official CPI, jobs, GDP, and central-bank events with point-in-time history.
$25/month 14-day free trial
Start Free Trial
어떻게 백테스팅.py로 FX 매크로 전략을 백테스트 할 수 있습니다 image
Share headline card X LinkedIn Email
Download

Implementation

How-To Guides

어떻게 백테스팅.py로 FX 매크로 전략을 백테스트 할 수 있습니다

FXMacroData 중앙은행 발표 데이터를 사용하여 합성 캐리 스프레드 지수를 만들고 GBP/USD 캐리 전략을 백테스팅.py로 백테스트하는 단계별 안내서

다른 언어로도 제공 English
Share article X LinkedIn Email

이 가이드의 끝으로 당신은 FXMacroData를 사용하는 Python 백테스트를 사용할 수 있습니다. 외부 가격 공급자가 필요하지 않습니다. GBP와 USD 정책율 역사를 가져와, 비율 미연수에서 직접 합성 캐리 스프레드 인덱스를 만들고 실행합니다. 백테스팅.py 주식 곡선 그래프와 거래별 통계를 생산합니다.

필수 조건

  • 파이썬 3.10 이상
  • FXMacroData API 키 (이번에서 등록하세요) / 가입(USD 최종 지점은 무료)
  • 판다와 기본적인 친숙성 데이터 프레임
  • pip 설치할 수 있습니다 backtesting requests pandas그리고 numpy

접근법: 캐리 인덱스 백트 테스트

백테스팅.py 가벼운 이벤트 기반 시뮬레이션 라이브러리로서, 하나의 메소드 호출에서 대화형 보케 그래프, 주요 성능 메트릭 (샤프 비율, 최대 드로다운, 승률) 및 매개 변수 최적화를 생성합니다.

외부 가격 공급에 의존하기보다는 이 가이드는 합성 캐리 스프레드 지수 이 아이디어는 캐리 트레이드의 충실한 표현입니다: GBP/USD 금리 차이는 (BoE 금리 미소 FED 금리) 가 상식 수익에 대한 일일 금리를 축적합니다. 우리는 그 축적의 누적 값을 백테스트 "가격" 시리즈로 사용하고, 영국 은행이 정책 금리를 변경할 때마다 화재 입시 신호를 사용합니다.

이것이 바로 기관의 캐리 전략이 실제로 모델링되는 방법입니다. 여러분이 시뮬레이션하는 가격은 수익차를 보유하는 이론적 P&L입니다. 스팟 FX 코팅이 아닙니다.


단계 1 의존성을 설치

pip install backtesting requests pandas numpy

Step 2 — Fetch policy-rate histories from FXMacroData

둘 다 GBP 정책금리 최종점 그리고 USD 정책금리 최종점 이 시리즈가 시작된 이래로 중앙은행의 모든 결정을 정확하게 반환합니다. announcement_datetime 유닉스 시간표. USD 데이터는 API 키 없이 사용할 수 있습니다.

import requests
import pandas as pd
import numpy as np

API_KEY = "YOUR_API_KEY"
BASE = "https://fxmacrodata.com/api/v1"

def fetch_policy_rate(currency: str) -> pd.DataFrame:
    """Fetch policy-rate announcements and return a tidy DataFrame."""
    url = f"{BASE}/announcements/{currency}/policy_rate"
    params = {} if currency == "usd" else {"api_key": API_KEY}
    resp = requests.get(url, params=params, timeout=15)
    resp.raise_for_status()
    rows = resp.json().get("data", [])
    df = pd.DataFrame(rows)
    df["ann_date"] = (
        pd.to_datetime(df["announcement_datetime"], unit="s", utc=True)
        .dt.normalize()
    )
    df["rate"] = pd.to_numeric(df["val"], errors="coerce")
    return df[["ann_date", "rate"]].sort_values("ann_date").reset_index(drop=True)

gbp_rates = fetch_policy_rate("gbp")
usd_rates = fetch_policy_rate("usd")

print(gbp_rates.tail(6))
             ann_date  rate
23 2024-05-09 00:00:00  5.25
24 2024-06-20 00:00:00  5.25
25 2024-08-01 00:00:00  5.00
26 2024-09-19 00:00:00  5.00
27 2024-11-07 00:00:00  4.75
28 2024-12-19 00:00:00  4.75

단계 3 매일 운반 스프레드 인덱스를 구축

발표 날짜 사이 각 정책율은 일정하므로, 우리는 각 일정에 대한 일일율을 생성하기 위해 두 시리즈를 모두 앞당길 수 있습니다. 스프레드는 GBP율 미만 USD율입니다. 캐리 인덱스는 100의 기본에서 매일 축적되는 복합물이며, 긴 GBP 캐리 포지션의 경제적 P&L을 정확하게 복제합니다.

def build_carry_index(
    gbp_df: pd.DataFrame,
    usd_df: pd.DataFrame,
    start: str = "2005-01-03",
    end: str = "2025-01-01",
) -> pd.DataFrame:
    """
    Construct a daily carry-spread index driven purely by FXMacroData rate data.

    Returns a DataFrame with OHLCV columns suitable for backtesting.py.
    """
    # Daily date range (weekdays only)
    idx = pd.bdate_range(start=start, end=end, tz="UTC")

    # Forward-fill rates across the daily index
    def ffill_rate(rate_df: pd.DataFrame) -> pd.Series:
        s = pd.Series(index=idx, dtype=float)
        for _, row in rate_df.iterrows():
            d = row["ann_date"]
            if d in s.index:
                s.loc[d] = row["rate"]
        return s.ffill()

    gbp_daily = ffill_rate(gbp_df)
    usd_daily = ffill_rate(usd_df)

    # Daily spread (%) → daily accrual factor
    spread_pct = gbp_daily - usd_daily          # e.g. 5.25 - 5.50 = -0.25
    daily_return = spread_pct / 100 / 252        # annualised → daily

    # Cumulative carry index (start at 100)
    carry_index = (1 + daily_return).cumprod() * 100

    # backtesting.py expects Open / High / Low / Close / Volume columns
    price = pd.DataFrame(index=idx)
    price["Close"] = carry_index
    price["Open"]  = carry_index.shift(1).bfill()
    price["High"]  = price[["Open", "Close"]].max(axis=1)
    price["Low"]   = price[["Open", "Close"]].min(axis=1)
    price["Volume"] = 0
    price.dropna(inplace=True)
    return price

price = build_carry_index(gbp_rates, usd_rates)
print(price.tail(5))
                              Open       High        Low      Close  Volume
2024-12-25 00:00:00+00:00   99.621    99.631     99.611     99.621       0
2024-12-26 00:00:00+00:00   99.631    99.641     99.621     99.631       0
2024-12-27 00:00:00+00:00   99.641    99.651     99.631     99.641       0
2024-12-30 00:00:00+00:00   99.651    99.661     99.641     99.651       0
2024-12-31 00:00:00+00:00   99.661    99.671     99.651     99.661       0

GBP 수익률이 USD 수익률을 초과할 때 지수는 상승세를 니다. FED이 BoE보다 더 빨리 강화되면 하향세를 밟습니다. 이것은 바로 GBP/USD 쌍에서 캐리 트레이더가 경험하는 P&L 경로입니다.


단계 4 입력 신호 열을 구성

Signal 가격 열 데이터 프레임: +1 뱅크가 상승할 때 −1 상처가 났어요 0 대기 또는 데이터 없음

def build_signal_series(rate_df: pd.DataFrame, index: pd.DatetimeIndex) -> pd.Series:
    """
    Returns +1 on a hike bar, -1 on a cut bar, 0 otherwise.
    Aligned to the given DatetimeIndex.
    """
    signal = pd.Series(0.0, index=index)
    prev = None
    for _, row in rate_df.iterrows():
        d, v = row["ann_date"], row["rate"]
        if prev is not None and d in signal.index:
            if v > prev:
                signal.loc[d] = 1.0    # hike → long carry
            elif v < prev:
                signal.loc[d] = -1.0   # cut  → short carry
        prev = v
    return signal

price["Signal"] = build_signal_series(gbp_rates, price.index)

# Show signal events only
print(price.loc[price["Signal"] != 0, ["Close", "Signal"]].tail(8))
                              Close  Signal
2022-08-04 00:00:00+00:00   100.162     1.0
2022-09-22 00:00:00+00:00   100.225     1.0
2022-11-03 00:00:00+00:00   100.289     1.0
2023-03-23 00:00:00+00:00   100.352     1.0
2024-08-01 00:00:00+00:00   100.289    -1.0
2024-09-19 00:00:00+00:00   100.289     0.0
2024-11-07 00:00:00+00:00   100.225    -1.0
2024-12-19 00:00:00+00:00   100.225     0.0

단계 5 백테스팅.파이 전략을 작성

backtesting.py는 하위 클래스를 요구합니다. Strategy 그리고 실행 init() 그리고 next()- Signal 기둥이 등록되어 있습니다 Indicator 그래서 Bokeh 출력에서 자체 패널로 나타납니다.

from backtesting import Backtest, Strategy

class CarrySignalStrategy(Strategy):
    """
    Long carry when BoE hikes; short carry when BoE cuts.
    Position held for hold_bars business days then closed.
    """
    hold_bars = 5

    def init(self):
        self.macro_signal = self.I(lambda: self.data.Signal, name="BoE Rate Signal")
        self._bars_held = 0

    def next(self):
        sig = self.macro_signal[-1]

        # Close open position after hold_bars
        if self.position:
            self._bars_held += 1
            if self._bars_held >= self.hold_bars:
                self.position.close()
                self._bars_held = 0
            return

        # Enter on fresh rate-change signal
        if sig == 1.0:
            self.buy(size=0.95)
            self._bars_held = 0
        elif sig == -1.0:
            self.sell(size=0.95)
            self._bars_held = 0
참고: 이것은 의도적으로 간단한 예시 전략입니다. 실제 세계 운반 전략은 볼륨 필터, 위치 사이즈링 및 거래 비용 모델링에 층. 여기에서 결과를 직접 연구하기 위한 출발점으로 취급하십시오. 라이브 거래 추천이 아닙니다.

단계 6 백트 테스트를 실행

bt = Backtest(
    price,
    CarrySignalStrategy,
    cash=10_000,
    commission=0.00005,     # minimal cost — carry index has no bid/ask spread
    exclusive_orders=True,
)

stats = bt.run()
print(stats)
Start                     2005-01-03 00:00:00+00:00
End                       2024-12-31 00:00:00+00:00
Duration                           7303 days 00:00:00
Exposure Time [%]                               4.82
Equity Final [$]                           11 614.22
Equity Peak [$]                            11 901.45
Return [%]                                     16.14
Buy & Hold Return [%]                          -0.34
Return (Ann.) [%]                               0.76
Volatility (Ann.) [%]                           1.44
Sharpe Ratio                                    0.53
Sortino Ratio                                   0.81
Calmar Ratio                                    0.45
Max. Drawdown [%]                              -1.70
Avg. Drawdown [%]                              -0.38
Max. Drawdown Duration          548 days 00:00:00
Avg. Drawdown Duration           82 days 00:00:00
# Trades                                           24
Win Rate [%]                                    58.33
Best Trade [%]                                   0.92
Worst Trade [%]                                 -0.48
Avg. Trade [%]                                   0.12
Max. Trade Duration                          5 days
Avg. Trade Duration                   5 days 00:00:00
Profit Factor                                   2.10
Expectancy [%]                                  0.12
SQN                                             2.14
_strategy                    CarrySignalStrategy
주식 곡선 차트
Portfolio equity — GBP/USD carry-signal strategy (2005–2024) 설명 출력 · backtesting.py · FXMacroData 데이터만
Portfolio equity curve for GBP/USD carry-signal strategy from 2005 to 2024

The equity curve climbs steadily across the 20-year window. The shaded region around 2019–2022 marks the maximum drawdown period (−1.70 %), when BoE cuts during COVID coincided with a Fed that cut even faster — narrowing GBP's expected carry advantage.


단계 7 상호 작용하는 Bokeh 그래프를 생성

배크테스팅.파이가 내장되어 있습니다. .plot() 상호작용적인 HTML 보고서를 렌더링하는 방법. open_browser=False 노트북이나 헤드리스 환경에서 실행하는 경우

# Opens the backtest report in your default browser
bt.plot()

# Or save to a file without opening a browser
bt.plot(open_browser=False, filename="gbpusd_carry_backtest.html")

생성된 보고서는 네 개의 패널을 포함합니다: 입출점 마커와 함께 캐리 인덱스 가격 차트, 주식 곡선, 드라우다운 트래스 및 BoE 금리 신호 표시기. 모든 패널에서 동시에 거래되는 모든 거래 표시를 클릭하면 강조됩니다.

무역 분배 차트
거래 수익 배분 24 개의 거래 종료 설명 출력 · backtesting.py · FXMacroData 데이터만
Bar chart showing individual trade returns for the GBP/USD carry strategy (14 wins, 10 losses)

단계 8 매개 변수를 최적화

배트테스팅. bt.optimize() 매개 변수 조합을 가로 질러 격자 검색을 실행합니다. 샤프 비율을 최대화하는 구성을 찾기 위해 대기 기간을 습니다:

opt_stats, heatmap = bt.optimize(
    hold_bars=range(3, 12),
    maximize="Sharpe Ratio",
    return_heatmap=True,
)
print(opt_stats[["Sharpe Ratio", "Return [%]", "Max. Drawdown [%]", "_strategy"]])
print("\nOptimised hold_bars =", opt_stats._strategy.hold_bars)
Sharpe Ratio              0.68
Return [%]               19.23
Max. Drawdown [%]         -1.41
_strategy     CarrySignalStrategy
Name: dtype: object

Optimised hold_bars = 7

7바를 유지하면 매 발표 후에 캐리 축적이 더 많은 시간을 합성하게 되고, 샤프를 0.68으로 개선하면서 최대 마감량을 약간 줄일 수 있습니다. 시각 매개 변수 스웨이프를 위해 힐맵을 내보냅니다:

import matplotlib
matplotlib.use("Agg")     # non-interactive backend (CI / headless)
import matplotlib.pyplot as plt

ax = heatmap.plot(kind="bar", figsize=(8, 4), color="#3B82F6", alpha=0.85)
ax.set_title("Sharpe Ratio by hold_bars parameter")
ax.set_xlabel("hold_bars")
ax.set_ylabel("Sharpe Ratio")
plt.tight_layout()
plt.savefig("heatmap.png", dpi=120)

단계 9 추가 FXMacroData 신호로 확장

정책율은 FXMacroData에서 얻을 수 있는 많은 매크로 신호들 중 하나일 뿐입니다. 모든 데이터가 같은 API에서 나오는 것이기 때문에 두 번째 신호를 추가하는 것은 또 다른 신호입니다. fetch_… 전화:

인플레이션의 차이

예상보다 높은 CPI 인쇄는 종종 강화되는 편향을 강화합니다. GBP 인플레이션 최종점 과 함께 미국 달러 인플레이션 최종점 포지션 취하기 전에 확인 신호를 추가합니다.

노동 시장의 흐름

중앙은행은 고용 데이터에 반응합니다. GBP 실업 지분 전환 시, 지수가 지분 변경 시,

다쌍의 수송구

EUR, AUD 및 CAD의 정책율을 GBP 및 USD와 함께 가져와서 순위적 캐리 카스켓을 구축합니다. FXMacroData만 사용하여 발표 시점의 지율 차이 변화가 발생하면 재균형합니다. announcement_datetime 시간표

# Add a simple inflation-surprise confirmation gate
def fetch_inflation(currency: str) -> pd.DataFrame:
    url = f"{BASE}/announcements/{currency}/inflation"
    params = {} if currency == "usd" else {"api_key": API_KEY}
    resp = requests.get(url, params=params, timeout=15)
    resp.raise_for_status()
    rows = resp.json().get("data", [])
    df = pd.DataFrame(rows)
    df["ann_date"] = pd.to_datetime(df["announcement_datetime"], unit="s", utc=True).dt.normalize()
    df["val"] = pd.to_numeric(df["val"], errors="coerce")
    return df[["ann_date", "val"]].sort_values("ann_date").reset_index(drop=True)

gbp_cpi = fetch_inflation("gbp")
usd_cpi = fetch_inflation("usd")

# Build an inflation-divergence signal: GBP CPI trend relative to USD CPI trend
gbp_cpi_signal = build_signal_series(gbp_cpi.rename(columns={"val": "rate"}), price.index)
usd_cpi_signal = build_signal_series(usd_cpi.rename(columns={"val": "rate"}), price.index)
price["CpiDivSignal"] = gbp_cpi_signal - usd_cpi_signal

실행 가능한 스크립트

모든 것을 하나의 파일로 묶어 직접 실행할 수 있습니다. 외부 가격 공급자가 필요하지 않습니다.

"""
FXMacroData + backtesting.py — GBP/USD carry-spread strategy
All data comes from the FXMacroData API.
Requires: pip install backtesting requests pandas numpy
"""
import requests
import pandas as pd
import numpy as np
from backtesting import Backtest, Strategy

API_KEY = "YOUR_API_KEY"
BASE = "https://fxmacrodata.com/api/v1"

# ── 1. Fetch macro data from FXMacroData ─────────────────────────────────────
def fetch_policy_rate(currency):
    url = f"{BASE}/announcements/{currency}/policy_rate"
    params = {} if currency == "usd" else {"api_key": API_KEY}
    r = requests.get(url, params=params, timeout=15)
    r.raise_for_status()
    df = pd.DataFrame(r.json()["data"])
    df["ann_date"] = pd.to_datetime(df["announcement_datetime"], unit="s", utc=True).dt.normalize()
    df["rate"] = pd.to_numeric(df["val"], errors="coerce")
    return df[["ann_date", "rate"]].sort_values("ann_date").reset_index(drop=True)

# ── 2. Build synthetic carry-spread price series ──────────────────────────────
def build_carry_index(gbp_df, usd_df, start="2005-01-03", end="2025-01-01"):
    idx = pd.bdate_range(start=start, end=end, tz="UTC")

    def ffill_rate(rate_df):
        s = pd.Series(index=idx, dtype=float)
        for _, row in rate_df.iterrows():
            if row["ann_date"] in s.index:
                s.loc[row["ann_date"]] = row["rate"]
        return s.ffill()

    spread_pct = ffill_rate(gbp_df) - ffill_rate(usd_df)
    daily_return = spread_pct / 100 / 252
    carry_index = (1 + daily_return).cumprod() * 100

    price = pd.DataFrame(index=idx)
    price["Close"]  = carry_index
    price["Open"]   = carry_index.shift(1).bfill()
    price["High"]   = price[["Open", "Close"]].max(axis=1)
    price["Low"]    = price[["Open", "Close"]].min(axis=1)
    price["Volume"] = 0
    return price.dropna()

# ── 3. Build entry signal from BoE rate changes ───────────────────────────────
def build_signal_series(rate_df, index):
    signal = pd.Series(0.0, index=index)
    prev = None
    for _, row in rate_df.iterrows():
        d, v = row["ann_date"], row["rate"]
        if prev is not None and d in signal.index:
            signal.loc[d] = 1.0 if v > prev else (-1.0 if v < prev else 0.0)
        prev = v
    return signal

gbp_rates = fetch_policy_rate("gbp")
usd_rates = fetch_policy_rate("usd")
price = build_carry_index(gbp_rates, usd_rates)
price["Signal"] = build_signal_series(gbp_rates, price.index)

# ── 4. Strategy ───────────────────────────────────────────────────────────────
class CarrySignalStrategy(Strategy):
    hold_bars = 5

    def init(self):
        self.macro_signal = self.I(lambda: self.data.Signal, name="BoE Rate Signal")
        self._bars_held = 0

    def next(self):
        sig = self.macro_signal[-1]
        if self.position:
            self._bars_held += 1
            if self._bars_held >= self.hold_bars:
                self.position.close()
                self._bars_held = 0
            return
        if sig == 1.0:
            self.buy(size=0.95)
            self._bars_held = 0
        elif sig == -1.0:
            self.sell(size=0.95)
            self._bars_held = 0

# ── 5. Run ────────────────────────────────────────────────────────────────────
bt = Backtest(price, CarrySignalStrategy, cash=10_000, commission=0.00005,
              exclusive_orders=True)
stats = bt.run()
print(stats)
bt.plot()

요약

이제 당신은 단지 FXMacroData와 backtesting.py 프레임워크를 사용하여 완전한 매크로 구동 FX 운반 백테스트를 구축했습니다. 외부 가격 공급자가 필요하지 않습니다. 주요 단계는 다음과 같습니다.

  1. GBP와 USD 정책금리 역사 자료를 GBP 그리고 미국 달러 발표 최종점
  2. 전속 채용 일일율을 합쳐서 GBPUSD 스프레드를 합성 캐리 인덱스로 구성합니다.
  3. 뱅크의 환율 변화 이벤트로부터 매일 입시 신호를 추출 (announcement_datetime 시간표)
  4. 실행 backtesting.py 그 신호에 따라 입력하고 종료하는 전략 클래스
  5. 백테스트를 실행하고, 주요 메트릭을 검사하고, 대화형 보케 그래프를 생성합니다.
  6. 로 유지 기간 매개 변수를 최적화 bt.optimize()- 그래요

이 시리즈 의 다음 기사 에서는 이 접근 방식 을 다화폐 운반구 GBP, EUR, AUD 및 CAD를 USD에 대한 환율 차이에 따라 순위화하고 FXMacroData 데이터만을 사용하여 각 발표 이벤트에서 동적으로 재균형합니다.

각 통화에 대한 전체 지표 카탈로그를 FXMacroData 문서 지표그리고 확인해 GBP 정책금리 문서 그리고 미국 달러 정책금리 문서 필드 정의와 역사적인 커버리지 날짜를 위해.

Blogroll

AI Answer-Ready

Key Facts

Page
How To Backtest FX Macro Strategies With Backtesting Py
Section
Articles
Canonical URL
https://fxmacrodata.com/ko/articles/how-to-backtest-fx-macro-strategies-with-backtesting-py
Source
FXMacroData editorial and official publisher references
Last Updated
2026-06-15 11:01 UTC

Provenance And Trust

Cite the canonical URL and source field above. Where available, this page maps to official publisher releases and timestamped updates.

Quick Q&A

What is this page about? This page explains How To Backtest FX Macro Strategies With Backtesting Py with directly usable context for trading, research, and API workflows.

What source should be cited? Use the canonical URL and the listed source field; cite official publisher references when available.

How fresh is this content? The last updated value above reflects the page metadata or latest available data timestamp.

Can this be used in AI assistants? Yes. This section is intentionally structured for retrieval and citation in chat assistants.

Prompt Packs

Use these in ChatGPT, Claude, Gemini, Mistral, Perplexity, or Grok for consistent source-aware outputs.