COT 포지셔닝 데이터는 거래가 비상업 포지시팅의 방향 편향과 일치하면 높은 확률의 세트업과 정보 기관 흐름에 반대하는 거래를 분리하는 의미있는 필터를 추가합니다.
이 가이드는 전체 프로세스를 통해 걸습니다: FXMacroData API에서 COT 데이터를 뽑아내어, 키 파생 메트릭을 계산하고, 위치 필터를 구축하고, 입력 워크플로에 적용합니다.
당신 이 건축 할 것
- A Python function that fetches weekly COT data for any of the eight supported currencies
- 순위 위치 표준화 메트릭 (공개 이해의 순수 %)
- 구성 가능한 임계값을 가진 다조건 위치 필터
- 방향 신호를 돌려주는 트레이드 엔트리 게이트:
longshort, 또는neutral - EUR/USD를 실무 예로 사용 한 실용적인 안내
필수 조건
- FXMacroData API 키 이 사이트에서 이용하실 수 있습니다. / 가입COT 최종점은 모든 유료 계획에 포함되어 있습니다.
- 파이썬 3.9+ 과 함께
requests설치된 라이브러리 (pip install requests) 의 내용입니다. - COT 보고서 용어 (비상업 장기, 단기, 오픈 인테스) 에 대한 기본적인 친숙성. FX 트레이더를 위한 COT 보고서 가이드 기본 요소를 다루고 있습니다.
- 선택적으로,
pandas데이터 조작 단계에 대해 (pip install pandas) 의 내용입니다.
Step 1 — Fetch COT Data from the API
FXMacroData COT 엔드포인트 (endpoint) 는 통화 선물에 대한 주간 비상업 및 상업적 포지셔닝을 반환합니다. 지원되는 통화는 AUD, CAD, CHF, EUR, GBP, JPY, NZD 및 USD입니다. 각 기록에는 비상공 및 상업 참여자의 긴, 짧은 및 순계 계약 수와 전체 오픈 이점이 포함되어 있습니다.
curl "https://fxmacrodata.com/api/v1/cot/eur?api_key=YOUR_API_KEY&start=2023-01-01"
JSON 응답은 다음과 같은 구조를 가지고 있습니다.
{
"currency": "eur",
"data": [
{
"date": "2025-03-25",
"noncommercial_long": 198432,
"noncommercial_short": 61840,
"noncommercial_net": 136592,
"commercial_long": 68230,
"commercial_short": 201860,
"open_interest": 591400
},
{
"date": "2025-03-18",
"noncommercial_long": 185710,
"noncommercial_short": 66320,
"noncommercial_net": 119390,
"commercial_long": 72140,
"commercial_short": 189430,
"open_interest": 578200
}
]
}
파이썬에서, 이 호출을 시간 순으로 정렬된 데이터 목록을 반환하는 보조에 포장합니다:
import requests
from datetime import date, timedelta
BASE_URL = "https://fxmacrodata.com/api/v1"
API_KEY = "YOUR_API_KEY"
def fetch_cot(currency: str, lookback_days: int = 365) -> list[dict]:
"""Return COT weekly records for *currency* over the last *lookback_days* days."""
start = (date.today() - timedelta(days=lookback_days)).isoformat()
resp = requests.get(
f"{BASE_URL}/cot/{currency.lower()}",
params={"api_key": API_KEY, "start": start},
timeout=15,
)
resp.raise_for_status()
payload = resp.json()
return sorted(payload["data"], key=lambda r: r["date"])
records = fetch_cot("eur")
print(f"Loaded {len(records)} COT records for EUR")
print("Latest:", records[-1])
왜 12 개월 의 역사?
The filter thresholds in Step 3 are expressed as percentile ranks over the trailing year. One year is long enough to capture a full positioning cycle for most major pairs without including regime changes that are too old to be relevant. You can widen the window to 2–3 years for currencies with slower-moving positioning cycles like JPY or CHF.
단계 2 파생 위치 측정값을 계산
원료 계약 수치는 통화와 시간 간 비교하기가 어렵습니다. 80,000 계약의 순수 길이는 EUR 선물 (큰 유동 시장) 에 비해 CHF (소규모 오픈 인테스트) 에 매우 다른 것을 의미합니다. 두 가지 파생 메트릭이 이것을 해결합니다.
2a. 오픈 이의 비율로 순위
비상업 순위 지분을 전체 오픈 이자로 나누면 -1과 +1 사이의 정상화 비율이 생성됩니다. 이것은 측정법을 통화와 시간에 따라 직접 비교 할 수 있습니다.
def net_pct_oi(records: list[dict]) -> list[dict]:
"""Add 'net_pct' field = noncommercial_net / open_interest to each record."""
enriched = []
for r in records:
oi = r.get("open_interest") or 1 # guard against zero
enriched.append({**r, "net_pct": r["noncommercial_net"] / oi})
return enriched
records = net_pct_oi(records)
latest = records[-1]
print(f"EUR net % OI: {latest['net_pct']:.3f} ({latest['date']})")
2b. 퍼센틸 순위 위치
현재 위치가 극단적인지 알기 위해서는 역사적 맥락이 필요합니다. net_pct 후기 창 안에 절대 숫자를 퍼센틸로 변환합니다 (0 = 가장 하락, 100 = 가장 상승). 75 이상의 퍼센티일은 밀집 된 긴 것을 나타냅니다. 25 이하는 밀집된 짧은 것을 나타낸다.
def percentile_rank(series: list[float], value: float) -> float:
"""Return the percentile rank of *value* within *series* (0–100)."""
below = sum(1 for x in series if x < value)
return below / len(series) * 100
net_series = [r["net_pct"] for r in records]
current_net = records[-1]["net_pct"]
pct_rank = percentile_rank(net_series, current_net)
print(f"EUR positioning percentile: {pct_rank:.1f}th")
퍼센틸 순위 해석
- 75~100 퍼센틸: 비상업적인 종목은 긴 종목으로 가득차 있습니다. 트렌드가 유지되는 동안 긴 종합을 선호합니다. 기본 변동이 발생할 경우 반전 위험을 추가합니다.
- 25~75 퍼센틸: 중립 구역. 강한 위치 바람 또는 역풍 다른 신호가 이끌 수 없습니다.
- 0~25 퍼센틸: 비상업가들은 코스가 많고, 추세가 유지되는 동안 코스를 선호하며, 상승한 놀라움에 대한 압박 위험을 추가합니다.
2c 위치 모멘텀
트렌드 방향은 현재 수준만큼이나 중요합니다. 성장하는 넷 롱은 평형화되거나 줄어들기 시작한 넷 론드와 다른 신호입니다. 모멘텀을 파악하기 위해 net_pct의 4 주 변화를 계산하십시오:
def positioning_momentum(records: list[dict], periods: int = 4) -> float:
"""Return the change in net_pct over the last *periods* weeks."""
if len(records) < periods + 1:
return 0.0
return records[-1]["net_pct"] - records[-(periods + 1)]["net_pct"]
momentum = positioning_momentum(records)
print(f"EUR 4-week positioning change: {momentum:+.3f}")
단계 3 입력 필터를 구축
세 가지 메트릭스를 가지고, 어떤 통화에 대한 방향 신호를 반환하는 필터 함수를 만들 수 있습니다. 논리는 현재 수준, 퍼센틸 컨텍스트, 그리고 모멘텀 방향을 하나의 게이트로 결합합니다.
def cot_entry_filter(
currency: str,
lookback_days: int = 365,
long_pct_threshold: float = 55.0,
short_pct_threshold: float = 45.0,
momentum_min: float = 0.005,
) -> dict:
"""
Return a COT positioning signal for *currency*.
Parameters
----------
currency : ISO currency code (AUD, CAD, CHF, EUR, GBP, JPY, NZD, USD)
lookback_days : history window for percentile calculation
long_pct_threshold : minimum percentile to confirm a long bias
short_pct_threshold : maximum percentile to confirm a short bias
momentum_min : minimum absolute 4-week change to confirm momentum
Returns
-------
dict with keys: currency, signal, net_pct, percentile, momentum, date
"""
records = fetch_cot(currency, lookback_days)
records = net_pct_oi(records)
latest = records[-1]
net_series = [r["net_pct"] for r in records]
pct_rank = percentile_rank(net_series, latest["net_pct"])
momentum = positioning_momentum(records)
if pct_rank >= long_pct_threshold and momentum >= momentum_min:
signal = "long"
elif pct_rank <= short_pct_threshold and momentum <= -momentum_min:
signal = "short"
else:
signal = "neutral"
return {
"currency" : currency.upper(),
"signal" : signal,
"net_pct" : round(latest["net_pct"], 4),
"percentile" : round(pct_rank, 1),
"momentum" : round(momentum, 4),
"date" : latest["date"],
}
result = cot_entry_filter("eur")
print(result)
EUR 비상업이 붐비는 경우 샘플 출력
{
"currency" : "EUR",
"signal" : "long",
"net_pct" : 0.231,
"percentile": 82.4,
"momentum" : 0.018,
"date" : "2025-03-25"
}
단계 4 거래 항목에 필터를 적용
필터 함수는 세 개의 신호 중 하나를 반환합니다. long short, 또는 neutral- 의도된 사용은 주요 입력 신호 앞의 게이트입니다: COT 필터가 long (또는 neutral 만약 당신이 더 공격적이면), 그리고 COT 필터가 short- 그래요
def should_enter_trade(
currency: str,
proposed_direction: str,
allow_neutral: bool = False,
) -> bool:
"""
Return True if COT positioning supports *proposed_direction* for *currency*.
Parameters
----------
currency : ISO currency code
proposed_direction : 'long' or 'short'
allow_neutral : if True, a 'neutral' COT signal does not block entry
"""
cot = cot_entry_filter(currency)
if cot["signal"] == proposed_direction:
return True
if allow_neutral and cot["signal"] == "neutral":
return True
return False
# Example: checking whether to enter a EUR/USD long
currency = "eur" # base currency of the pair
direction = "long"
if should_enter_trade(currency, direction):
print(f"COT confirms {direction} bias for {currency.upper()} — proceed to entry check")
else:
print(f"COT filter blocked {direction} entry for {currency.upper()}")
실행 참고: COT는 주간 신호입니다.
COT 데이터는 전날 화요일부터 포지션에 대해 매주 금요일 공개됩니다. 이는 주간 또는 일일 편향을 필터링하는 데 적합 한 낮은 주파수 신호가됩니다. 금요일 오후 3:30 ET 출시 후 일주일에 한 번 필터를 다시 실행하고 결과를 캐시하고 다음 주에 모든 항목에 대한 정적 편향 게이트로 사용합니다. COT 최종점 문서 발매 시기를 확인하기 위해서요.
단계 5 다화 대시보드로 확장
Running the filter across all eight supported currencies at once gives you a weekly positioning dashboard. This is useful for identifying which FX pairs have the clearest speculator-driven tailwinds and which to avoid because positioning is working against your direction.
CURRENCIES = ["aud", "cad", "chf", "eur", "gbp", "jpy", "nzd", "usd"]
def cot_dashboard() -> list[dict]:
"""Return COT positioning signals for all supported currencies."""
results = []
for ccy in CURRENCIES:
try:
result = cot_entry_filter(ccy)
results.append(result)
except Exception as exc:
print(f"Warning: could not load {ccy.upper()} COT data — {exc}")
return results
dashboard = cot_dashboard()
for row in dashboard:
bar = "▲" if row["signal"] == "long" else ("▼" if row["signal"] == "short" else "–")
print(f"{row['currency']:4s} {bar} {row['signal']:8s} pct={row['percentile']:5.1f} mom={row['momentum']:+.3f}")
샘플 주간 출력:
AUD ▲ long pct= 71.2 mom=+0.012
CAD – neutral pct= 53.8 mom=-0.004
CHF ▲ long pct= 68.4 mom=+0.008
EUR ▲ long pct= 82.4 mom=+0.018
GBP – neutral pct= 48.1 mom=-0.002
JPY ▼ short pct= 19.6 mom=-0.022
NZD ▲ long pct= 63.0 mom=+0.007
USD ▼ short pct= 22.1 mom=-0.015
이 스냅샷을 읽음: 비상업 거래는 EUR, AUD, CHF 및 NZD 선물; JPY 및 USD의 짧은 포지션이며 CAD 및 GBP에 중립적입니다. EUR/JPY 장기 거래를 고려하는 거래자는 두 다리를 투기자의 흐름에 의해 확인되는 것을 발견합니다. USD/CAD 장기 거래를 검토하는 거래자가 USD에 대한 COT 역풍과 CAD에 대한 중립적인 배경에 직면 할 것입니다. 포지셔닝 관점에서 약한 설정.
단계 6 COT를 매크로 확인 계층과 결합
가장 강력한 설정은 같은 방향성 요점을 뒷받침하는 적어도 하나의 거시적 기본과 COT 위치화를 결합합니다. 금리 차이는 자연스러운 보완입니다. 투기자들이 EUR를 길게하고 ECB-Fed 금리차가 EUR의 이익을 위해 넓어지면 위치와 기본 사례가 일치합니다.
을 사용하세요 정책금리 최종점 각 통화의 가장 최근의 환율을 뽑아내고 이차를 계산하기 위해:
def fetch_latest_rate(currency: str) -> float | None:
"""Return the most recent policy rate for *currency* as a float."""
resp = requests.get(
f"{BASE_URL}/announcements/{currency.lower()}/policy_rate",
params={"api_key": API_KEY, "limit": 1},
timeout=15,
)
if resp.status_code != 200:
return None
data = resp.json().get("data", [])
return data[0]["val"] if data else None
def rate_differential(base_ccy: str, quote_ccy: str) -> float | None:
"""Return base_rate − quote_rate, or None if either rate is unavailable."""
base_rate = fetch_latest_rate(base_ccy)
quote_rate = fetch_latest_rate(quote_ccy)
if base_rate is None or quote_rate is None:
return None
return base_rate - quote_rate
def combined_filter(base_ccy: str, quote_ccy: str, direction: str) -> dict:
"""
Return a combined COT + rate-differential signal for a currency pair.
direction : 'long' (buy base) or 'short' (sell base)
"""
cot_base = cot_entry_filter(base_ccy)
cot_quote = cot_entry_filter(quote_ccy)
diff = rate_differential(base_ccy, quote_ccy)
# For a long (buy base): we want long bias on base AND short/neutral on quote
if direction == "long":
cot_ok = cot_base["signal"] == "long" and cot_quote["signal"] != "long"
macro_ok = diff is not None and diff > 0
else:
cot_ok = cot_base["signal"] == "short" and cot_quote["signal"] != "short"
macro_ok = diff is not None and diff < 0
return {
"pair" : f"{base_ccy.upper()}/{quote_ccy.upper()}",
"direction" : direction,
"cot_ok" : cot_ok,
"macro_ok" : macro_ok,
"confirmed" : cot_ok and macro_ok,
"cot_base" : cot_base,
"cot_quote" : cot_quote,
"rate_diff" : round(diff, 4) if diff is not None else None,
}
signal = combined_filter("eur", "jpy", "long")
print("Confirmed:", signal["confirmed"])
print("COT base :", signal["cot_base"]["signal"], "| COT quote:", signal["cot_quote"]["signal"])
print("Rate diff :", signal["rate_diff"])
COT와 매크로가 동의하지 않을 때
포지셔닝이 한쪽에서 매우 밀집되어 있지만 거시적 기본이 반대 방향으로 변할 때 (예를 들어, JPY 선물의 큰 단축이지만 일본 은행은 강화되기 시작합니다), 체제는 전환이 될 수 있습니다. 이것들은 가장 빠르고 가장 큰 움직임을 일으키는 설정입니다. 종종 단축 또는 긴 청산을 강요하는 방향으로. 그러한 경우 COT 필터 만으로는 충분하지 않습니다. 중앙은행 정책금리 역사 위치 풀기를 유발할 수 있는 모든 이동을 위해 가까이
요약
이제 당신은 라이브 FXMacroData API 데이터에 구축 된 완전한 COT 기반 입력 필터를 가지고 있습니다. 작업 흐름은 여섯 단계로 구성됩니다:
- 주간 COT 기록을 가져오기 위해 통화 또는 통화 당신은 거래.
- 순위 지분은 외환에 따라 정상화되도록 개방된 금리의 비율로 계산합니다.
- 극한 또는 중립적인 조건을 식별하기 위해 후기 일 년 내에 현재 위치를 순위 지정하십시오.
- 4주간의 동력을 계산해 보시면, 포지셔닝이 여러분의 편에 있는지 확인해 보실 수 있습니다.
- 필터 신호에 대한 거래 항목을 게이트 COT 정렬이 방향과 일치 할 때만 진행하십시오.
- 선택적으로 두 가지 요인 확인을 위해 비율 차이 확인과 결합합니다.
전체 다화 대시보드는 주간 스펙레이터 돈의 위치를 파악할 수 있도록 해줍니다. 따라서 당신은 기관 흐름에 대한 거래가 아니라 기관 흐름과 함께 거래를 합니다. 다음 단계로, COT 필터를 인플레이션 아니면 고용 마크로 점수 모델을 구축하기 위한 최종 지점으로 세 가지 신호를 모두 함께 위치, 금리 차수, 성장/인플레이션 동력을 더 완전한 체제 그림을 위해 가중합니다.