Pourquoi les données macro alimentent le Bitcoin
Bitcoin n'est pas une entreprise. Il ne paie aucun dividende, ne rapporte aucun bénéfice et n'a pas de multiples de revenus pour ancrer une valorisation. Au lieu de cela, BTC se négocie comme l'actif le plus sensible au risque du marché un pari à effet de levier sur les conditions de liquidité mondiales, la force du dollar et les attentes en termes de taux d'intérêt réels. Cela signifie que la même boîte à outils macro utilisée par les traders FX pour positionner en EUR/USD ou AUD/JPY s'applique directement à Bitcoin.
Lorsque la Fed réduit les taux, la liquidité du dollar s'élargit et les actifs à risque rallient BTC mène généralement la charge. Lorsque l'IPC surprend à la hausse, les récits de couverture de l'inflation réapparaissent. Lorsque les rendements réels s'effondrent, les narrations sur la dévaluation monétaire poussent le capital vers des alternatives d'actifs dur. Chacun de ces signaux est observable à l'avance via les points d'extrémité des indicateurs de FXMacroData, timestampés à la seconde et disponibles via une API REST propre.
Ce guide vous guide dans la construction d'un robot de trading algorithmique basé sur des signaux macro pour BTC/USDT sur Binance.
- Extrait des signaux macro en temps réel de FXMacroData (taux directeur, inflation, taux de rentabilité, or)
- Les combiner dans un score macro composite
- Planifier l'exécution autour des sorties à fort impact via le calendrier de sortie
- Envoie des ordres de marché et de limite sur Binance via le SDK Python officiel
Thèse de base
Les changements de régime macro cycles de réduction des taux, pivots d'inflation, inversions de tendance du dollar créent des vents de revanche directionnels de plusieurs semaines pour Bitcoin.
Pré-requis
Avant de commencer, assurez- vous d'avoir les choses suivantes prêtes:
- Python 3.9+ tous les extraits utilisent une syntaxe de saisie standard
- Clé de l'API FXMacroData inscrivez-vous à / souscrivez et prends ta clé du tableau de bord
- Compte Binance avec un solde USDT financé créer des clés API dans le tableau de bord Binance avec Activation des opérations au comptant et sur marge vérifié
- Paquets PythonJe suis désolé .
requestsJe suis désolé .python-binanceJe suis désolé .pandasJe suis désolé .schedule
pip install requests python-binance pandas schedule
Stockez vos clés API sous forme de variables d'environnement plutôt que de les coder:
export FXMACRO_API_KEY="YOUR_FXMACRODATA_KEY"
export BINANCE_API_KEY="YOUR_BINANCE_KEY"
export BINANCE_SECRET_KEY="YOUR_BINANCE_SECRET"
Étape 1: Récupérer les signaux macro à partir de FXMacroData
Quatre séries macro sont au cœur d' un modèle de régime BTC: Taux directeur en USDJe suis désolé . Inflation par IPC- Je suis désolé . Taux d'inflation de rupture de rentabilité à 10 ans, et prix au comptant de l'orEnsemble, ils décrivent l'environnement de liquidité, le régime d'inflation et le sentiment de fuite vers les actifs dures.
import os
import requests
BASE_URL = "https://fxmacrodata.com/api/v1"
FXMACRO_KEY = os.environ["FXMACRO_API_KEY"]
def get_series(path: str, start: str = "2024-01-01") -> list[dict]:
"""Fetch a time-series from FXMacroData."""
resp = requests.get(
f"{BASE_URL}{path}",
params={"api_key": FXMACRO_KEY, "start": start},
timeout=10,
)
resp.raise_for_status()
return resp.json()["data"]
# Macro inputs
policy_rate = get_series("/announcements/usd/policy_rate")
cpi = get_series("/announcements/usd/inflation")
breakeven_10y = get_series("/announcements/usd/breakeven_inflation_rate")
gold = get_series("/commodities/gold")
# Each item: {"date": "2025-04-08", "val": 5.25, "announcement_datetime": "..."}
print(f"Policy rate (latest): {policy_rate[0]['val']}%")
print(f"CPI (latest): {cpi[0]['val']}%")
print(f"10Y breakeven: {breakeven_10y[0]['val']}%")
print(f"Gold spot: ${gold[0]['val']:.2f}")
Chaque point final renvoie une liste classée de la plus récente à la plus ancienne data[0] La clé pour les produits de base est val; pour les indicateurs macroéconomiques, val Il tient le titre et ... announcement_datetime porte l'horodatage de sortie de deuxième niveau utile pour la planification (couvert à l'étape 4).
Les entrées de macrosignal Régime actuel
Illustrative values based on 2024–2025 data. As the Fed cut rates and breakeven inflation rose, BTC formed a multi-month bull trend.
Étape 2: Construire un score macro composite
Au lieu de réagir à un seul indicateur, un score composite synthétise les quatre signaux en un nombre directionnel entre -1 (complètement sans risque) et +1 (completelement sans risque).
def macro_score(
policy_rate_pct: float,
cpi_pct: float,
breakeven_pct: float,
gold_usd: float,
*,
gold_baseline: float = 1900.0,
) -> float:
"""
Returns a composite macro score in [-1, +1].
Positive = risk-on / BTC bullish environment.
Negative = risk-off / BTC bearish environment.
"""
score = 0.0
# ── Policy rate regime (weight 0.35) ──────────────────────────
# Sub-4.5% = accommodative → bullish; above 5.5% = restrictive → bearish
if policy_rate_pct < 4.5:
score += 0.35
elif policy_rate_pct <= 5.5:
score += 0.35 * (5.5 - policy_rate_pct) / 1.0
else:
score -= 0.20
# ── Inflation regime (weight 0.25) ────────────────────────────
# CPI 2–4%: moderate inflation → neutral/slightly bullish
# CPI > 6%: high inflation → monetary debasement narrative → bullish
# CPI < 1.5%: deflationary risk → bearish
if cpi_pct > 6.0:
score += 0.25
elif cpi_pct >= 2.0:
score += 0.10
else:
score -= 0.15
# ── Breakeven inflation (weight 0.20) ─────────────────────────
# Rising breakevens signal inflation expectations are re-anchoring → bullish
if breakeven_pct >= 2.5:
score += 0.20
elif breakeven_pct >= 2.0:
score += 0.10
else:
score -= 0.10
# ── Gold trend (weight 0.20) ──────────────────────────────────
# Gold above baseline confirms hard-asset demand → bullish
gold_ratio = (gold_usd - gold_baseline) / gold_baseline
score += 0.20 * max(-1.0, min(1.0, gold_ratio * 5))
return round(max(-1.0, min(1.0, score)), 4)
score = macro_score(
policy_rate_pct=policy_rate[0]["val"],
cpi_pct=cpi[0]["val"],
breakeven_pct=breakeven_10y[0]["val"],
gold_usd=gold[0]["val"],
)
print(f"Composite macro score: {score:+.4f}")
# e.g. → +0.6500 (bullish regime)
Guide d'interprétation du score
| Plage de notation | Régime macro | Signal suggéré |
|---|---|---|
| +0,5 à +1,0 | Risque sur taux accommodants, inflation modérée à élevée | Accumuler / détenir des BTC longs |
| +0,1 à +0,5 | Légèrement favorable signaux mixtes, régime de transition | Position réduite, en attente de confirmation. |
| -0,1 à +0,1 | Neutre pas de signal de régime fort | Plate / hors de marché |
| -1,0 à -0,1 | Risque de déduction taux élevés, environnement déflationniste ou stagnflationnistes | Sortie / Réduction de la longue exposition |
Étape 3: Connectez-vous à Binance et obtenez le prix BTC
Avec le signal macro prêt, connectez-vous à Binance en utilisant le service officiel. python-binance Il est toujours nécessaire de rechercher le prix au comptant actuel avant de passer une commande afin d'éviter que les valeurs de référence ne soient obsolètes.
import os
from binance.client import Client
BINANCE_KEY = os.environ["BINANCE_API_KEY"]
BINANCE_SECRET = os.environ["BINANCE_SECRET_KEY"]
client = Client(BINANCE_KEY, BINANCE_SECRET)
# Verify connectivity
status = client.get_system_status()
print(f"Binance status: {status['msg']}") # → "normal"
# Latest BTC/USDT price
ticker = client.get_symbol_ticker(symbol="BTCUSDT")
btc_price = float(ticker["price"])
print(f"BTC/USDT spot: ${btc_price:,.2f}")
# Current USDT balance
account = client.get_account()
usdt_balance = next(
(float(b["free"]) for b in account["balances"] if b["asset"] == "USDT"),
0.0,
)
print(f"Available USDT: ${usdt_balance:,.2f}")
Le commerce du papier d'abord
Binance fournit un environnement de testnet à testnet.binance.visionUtilisez Client(key, secret, testnet=True) Valider la logique du signal et le dimensionnement pendant au moins deux semaines avant de passer à la vie.
Étape 4: Planifier autour des événements de sortie de macro
L'une des fonctionnalités les plus puissantes de FXMacroData pour le trading algorithmique est la point final du calendrier de sortieAu lieu de polling indicateurs sur un chronomètre fixe, vous pouvez interroger l'heure de sortie prévue exacte pour tout indicateur et le feu de votre logique précisément lorsque de nouvelles données atterrissent.
import datetime
import schedule
import time
def get_next_release(currency: str, indicator: str) -> datetime.datetime | None:
"""
Returns the next scheduled release datetime for an indicator.
The calendar endpoint returns upcoming release dates ordered ascending.
"""
resp = requests.get(
f"{BASE_URL}/calendar/{currency}",
params={"api_key": FXMACRO_KEY},
timeout=10,
)
resp.raise_for_status()
events = resp.json().get("data", [])
now_utc = datetime.datetime.now(tz=datetime.timezone.utc)
for event in events:
if event.get("indicator") != indicator:
continue
release_str = event.get("release_datetime") or event.get("date")
if not release_str:
continue
release_dt = datetime.datetime.fromisoformat(release_str.replace("Z", "+00:00"))
if release_dt > now_utc:
return release_dt
return None
# Find when the next FOMC policy rate decision is scheduled
next_fomc = get_next_release("usd", "policy_rate")
if next_fomc:
delta = next_fomc - datetime.datetime.now(tz=datetime.timezone.utc)
print(f"Next FOMC release: {next_fomc.isoformat()} ({delta.days}d {delta.seconds // 3600}h away)")
else:
print("No upcoming policy_rate event found in calendar.")
# Find when the next CPI release is scheduled
next_cpi = get_next_release("usd", "inflation")
if next_cpi:
print(f"Next CPI release: {next_cpi.isoformat()}")
Armé de l'horodatage exact de la sortie, vous pouvez planifier une mise à jour du signal post-sortie permettant au marché d'absorber l'impression pendant quelques minutes avant de réévaluer et de rééchanger:
def on_macro_release():
"""Called shortly after a scheduled macro release."""
print("Macro release fired — refreshing signals...")
run_strategy()
def schedule_next_release(currency: str, indicator: str, delay_seconds: int = 90):
"""
Schedules the strategy to run 'delay_seconds' after the next release.
A 90-second delay lets the initial market reaction settle slightly.
"""
release_dt = get_next_release(currency, indicator)
if not release_dt:
return
fire_at = release_dt + datetime.timedelta(seconds=delay_seconds)
fire_str = fire_at.strftime("%H:%M:%S") # schedule library uses HH:MM:SS
schedule.every().day.at(fire_str).do(on_macro_release).tag(f"{currency}_{indicator}")
print(f"Scheduled signal refresh at {fire_str} UTC after {currency.upper()} {indicator}")
schedule_next_release("usd", "policy_rate", delay_seconds=90)
schedule_next_release("usd", "inflation", delay_seconds=60)
Étape 5: Taille des positions et soumission des commandes
La taille de position est l'endroit où la plupart des stratégies algorithmiques perdent de l'argent pas dans la logique du signal. La fonction ci-dessous mesure le commerce BTC comme une fraction du capital disponible, en s'échelonnant avec la grandeur absolue du score macro. Un environnement macro à plus forte conviction (score plus loin de zéro) justifie une allocation plus grande, mais jamais plus qu'un maximum configurable.
from binance.enums import SIDE_BUY, SIDE_SELL, ORDER_TYPE_MARKET, ORDER_TYPE_LIMIT
from binance.exceptions import BinanceAPIException
import math
def compute_quantity(
score: float,
usdt_balance: float,
btc_price: float,
max_position_pct: float = 0.20,
) -> float:
"""
Scale position size between 0 and max_position_pct of USDT balance.
Only trade when |score| > 0.30 to avoid noise-driven entries.
Returns BTC quantity rounded to Binance's minimum lot size (0.00001 BTC).
"""
if abs(score) < 0.30:
return 0.0
conviction = (abs(score) - 0.30) / 0.70 # 0.0 → 1.0
usdt_to_trade = usdt_balance * max_position_pct * conviction
btc_qty = usdt_to_trade / btc_price
return math.floor(btc_qty * 100_000) / 100_000 # 5 decimal places
def place_order(side: str, quantity: float, btc_price: float) -> dict | None:
"""
Submit a market order. For limit orders, pass a price to get_order_book
and sit 0.1% inside the spread.
"""
if quantity <= 0.0:
print("Quantity zero — no order placed.")
return None
try:
order = client.order_market(
symbol="BTCUSDT",
side=side,
quantity=quantity,
)
print(f"Order placed: {side} {quantity:.5f} BTC @ ~${btc_price:,.2f}")
return order
except BinanceAPIException as exc:
print(f"Binance order error: {exc.message}")
return None
Étape 6: assembler la boucle de stratégie complète
Maintenant , combinez chaque pièce en une seule . run_strategy() fonction qui récupère de nouvelles données macro, calcule le score, vérifie un changement de régime et ouvre ou ferme une position BTC en conséquence.
import json
import pathlib
STATE_FILE = pathlib.Path("strategy_state.json")
def load_state() -> dict:
if STATE_FILE.exists():
return json.loads(STATE_FILE.read_text())
return {"position": 0.0, "last_score": 0.0}
def save_state(state: dict) -> None:
STATE_FILE.write_text(json.dumps(state, indent=2))
def run_strategy() -> None:
state = load_state()
# ── 1. Fetch fresh macro data ──────────────────────────────────
policy_rate_val = get_series("/announcements/usd/policy_rate")[0]["val"]
cpi_val = get_series("/announcements/usd/inflation")[0]["val"]
breakeven_val = get_series("/announcements/usd/breakeven_inflation_rate")[0]["val"]
gold_val = get_series("/commodities/gold")[0]["val"]
# ── 2. Compute macro score ─────────────────────────────────────
score = macro_score(policy_rate_val, cpi_val, breakeven_val, gold_val)
print(f"Macro score: {score:+.4f} (prev: {state['last_score']:+.4f})")
# ── 3. Fetch current Binance position and price ───────────────
account = client.get_account()
btc_held = float(next(b["free"] for b in account["balances"] if b["asset"] == "BTC"))
usdt_held = float(next(b["free"] for b in account["balances"] if b["asset"] == "USDT"))
btc_price = float(client.get_symbol_ticker(symbol="BTCUSDT")["price"])
# ── 4. Regime change logic ─────────────────────────────────────
prev_score = state["last_score"]
was_long = prev_score >= 0.30
is_long_now = score >= 0.30
was_flat = abs(prev_score) < 0.30
is_flat_now = abs(score) < 0.30
if is_long_now and (was_flat or prev_score < 0):
# Enter or increase long
qty = compute_quantity(score, usdt_held, btc_price)
place_order(SIDE_BUY, qty, btc_price)
elif is_flat_now and was_long and btc_held > 0.0001:
# Exit long — macro regime has turned neutral
exit_qty = math.floor(btc_held * 100_000) / 100_000
place_order(SIDE_SELL, exit_qty, btc_price)
elif score < -0.30 and btc_held > 0.0001:
# Hard exit on bearish signal
exit_qty = math.floor(btc_held * 100_000) / 100_000
place_order(SIDE_SELL, exit_qty, btc_price)
print("BEARISH regime — full exit.")
# ── 5. Persist state ───────────────────────────────────────────
state["last_score"] = score
save_state(state)
# ── Run once immediately, then on each scheduled release ──────────────
run_strategy()
# Keep scheduler alive
while True:
schedule.run_pending()
time.sleep(10)
BTC/USDT par rapport au Macro Score Simulé 2024
Le score macro a dépassé +0,3 au début de 2024, coïncidant avec le début du passage de BTC de ~ 40k $ à 70k $. Le score a reculé à mesure que les attentes de réduction des taux ont été évaluées et que le récit des actifs dures s'est refroidi.
Étape 7: Gestion des risques et considérations opérationnelles
A macro signal strategy has a much lower trading frequency than a pure technical system — entries and exits typically occur around 6–8 major indicator releases per year. That low frequency is a feature, not a bug: you are positioning for multi-week regime changes, not intraday noise. However, it also means each position carries more risk per trade, making disciplined risk management non-negotiable.
✓ Faire
- Run on a 0.5–2% stop-loss per trade based on ATR
- Limiter la position maximale à 20% du compte par transaction
- Tests antérieurs d'au moins 2 cycles de taux de la Fed avant la mise en service
- Enregistrez chaque transaction et score dans un fichier pour vérification
- Utilisez le testnet Binance pour les tests à sec d'abord
Évitez
- Poursuite des mouvements intradiens de BTC avec des signaux macro
- Entering immediately on data release (wait 60–90 seconds)
- Suradaptation des poids de score à un seul cycle
- Fonctionnement sans interrupteur de décharge quotidienne
- Codes d'API à codage dur dans les fichiers source
One additional consideration: BTC trades 24/7 but macro events are scheduled. The FOMC, CPI, and NFP releases all occur during US market hours. Your scheduler should be timezone-aware — use UTC throughout and convert only when displaying to end users. FXMacroData's announcement_datetime Le champ est toujours UTC, ce qui rend cela simple.
Élargissement de la stratégie
Ce cadre est délibérément modulaire. Voici quelques extensions naturelles à explorer ensuite:
- Ajouter les données de positionnement du COT Le point de fin CFTC COT de FXMacroData fournit un positionnement spéculatif hebdomadaire sur les contrats à terme en USD.
/cot/usdet ajouter un terme de positionnement net au score macro. - Score multi-monnaies — incorporate EUR, JPY, and GBP macro signals to construct a global liquidity score. When multiple G10 central banks are in easing mode simultaneously, BTC risk-on conditions are strongest.
- Vélèbre de tendance de rupture de rentabilité au lieu du niveau de rentabilité, utiliser le taux de variation de 4 semaines.
- Intégration du calendrier de sortie demande à calendrier de sortie pour tous les événements USD d'un mois, les regrouper par poids d'impact et identifier les fenêtres où plusieurs émissions à fort impact se regroupent dans les 48 heures ce sont les périodes pour lesquelles il convient de se positionner.
Résumé
Vous avez maintenant un robot de trading Bitcoin fonctionnel axé sur des signaux macro qui relie les données de l'indicateur FXMacroData directement à l'exécution de Binance.
Le prochain article de cette série couvre le backtesting de ce cadre par rapport aux données historiques et l'étalonnage des pondérations de score en utilisant des séries temporelles réelles FXMacroData remontant à 2015.