土耳其股票 API 技術接入指南:BIST 即時行情與歷史數據深度解析

  1. iTick
  2. 教程
土耳其股票 API 技術接入指南:BIST 即時行情與歷史數據深度解析 - iTick
土耳其股票 API 技術接入指南:BIST 即時行情與歷史數據深度解析

引言:為什麼土耳其市場值得技術開發者關注?

土耳其作為橫跨歐亞大陸的關鍵新興市場,其資本市場近年吸引越來越多國際投資者的目光。伊斯坦堡證券交易所(Borsa Istanbul,簡稱 BIST)是該國唯一的交易所,擁有超過 400 家上市公司,總市值超過 2000 億美元,涵蓋金融、工業、消費品、能源等多樣化產業。

對技術開發者及量化團隊而言,土耳其股票市場具有獨特的技術挑戰與機遇:

1. 新興市場的數據接入難題:與歐美成熟市場不同,土耳其股票數據的標準化程度較低,免費公共介面普遍存在 15 分鐘以上的延遲,無法滿足即時交易需求;歷史數據往往只有 1-2 年,不足以支撐長期策略回測;文件品質參差不齊,股票代碼格式混亂導致接入困難。

2. 歐亞樞紐的策略地位:土耳其位處歐亞交界,其資本市場與歐洲、中東、中亞市場存在較強的聯動性,為跨區域套利策略提供獨特機會。同時,土耳其里拉(TRY)的匯率波動也為量化策略增添多元化的 alpha 來源。

3. 優質藍籌股的投資價值:從銀行巨頭 İş Bankası(ISCTR)、Garanti BBVA(GARAN),到工業龍頭 Koç Holding(KCHOL),從鋼鐵巨頭 Ereğli Demir ve Çelik(EREGL),到全球航線網絡最廣的土耳其航空(THYAO),BIST 市場為投資者提供豐富的行業選擇。

4. 高波動性的交易機會:土耳其里拉匯率波動及宏觀經濟環境變化,令 BIST 市場呈現較高波動性,為短線交易者及量化策略創造更多機會。

本文將從技術開發者的角度,深度解析如何使用 iTick API 構建穩定、高效的土耳其股票數據接入模組。iTick 官方文件顯示,平台已全面支援土耳其市場(Turkey),開發者可透過統一 API 介面獲取 BIST 交易所全部股票的即時行情、歷史 K 線及盤口數據。

一、iTick 土耳其市場數據能力一覽

根據 iTick 官方文件,平台針對土耳其市場提供以下核心數據服務:

數據類型REST 介面WebSocket 推送典型應用場景
即時報價/stock/quote支援即時行情展示、價格監控
歷史 K 線/stock/kline支援策略回測、技術分析
盤口深度/stock/depth支援流動性分析、訂單簿監控
逐筆成交/stock/tick支援高頻交易、市場微觀結構分析
公司資訊/stock/info不支援基本面分析、選股策略

技術特點

  • 低延遲:WebSocket 推送延遲<50ms,滿足高頻交易需求
  • 多週期 K 線:支援分鐘線(1、5、15、30、60 分鐘)、日線、週線、月線
  • 多檔盤口:支援買賣雙方多檔掛單數據
  • 統一認證:透過 API Token 在 headers 中鑑權
  • 免費套餐:基礎行情無限呼叫,個人開發者友好

二、土耳其市場核心數據速查

主流土耳其股票代碼參考

公司名稱股票代碼所屬板塊業務簡介
Koç Holding (科奇控股)KCHOL工業集團土耳其最大工業集團,業務涵蓋能源、汽車、消費品等領域
Garanti BBVAGARAN金融西班牙 BBVA 控股的土耳其領先銀行
Ereğli Demir ve Çelik (埃雷利鋼鐵)EREGL鋼鐵土耳其最大鋼鐵生產商
Turkish Airlines (土耳其航空)THYAO航空全球航線網絡最廣的航空公司之一
Turkcell (土耳其電信)TCELL電信土耳其領先移動通訊營運商
Arçelik (阿奇立克)ARCLK家電歐洲知名家電品牌
Koza Altın (科扎黃金)KOZAL礦業土耳其領先黃金礦業公司

市場技術參數

項目說明
市場代碼region=TR(REST)或 $TR(WebSocket)
交易所Borsa Istanbul (BIST)
指數代碼XU100(BIST 100 指數)
交易時間伊斯坦堡時間 10:00-18:00(夏令時)/ 11:00-19:00(冬令時)
對應北京時間15:00-23:00(夏令時)/ 16:00-00:00(冬令時)
貨幣單位土耳其里拉(TRY)

三、REST API 實戰:歷史數據獲取與回測支援

3.1 基礎配置與鑑權

所有 iTick REST API 請求都需要在 headers 中攜帶 API Token,基址為https://api.itick.org

      import requests

API_TOKEN = "your_token_here"  # 從iTick官網獲取
BASE_URL = "https://api.itick.org"

def get_headers():
    return {
        "accept": "application/json",
        "token": API_TOKEN
    }

    

3.2 即時行情獲取

獲取土耳其航空(THYAO)即時報價:

      def get_turkey_quote(symbol):
    """獲取土耳其股票即時報價"""
    url = f"{BASE_URL}/stock/quote"
    params = {"region": "TR", "code": symbol}

    resp = requests.get(url, params=params, headers=get_headers(), timeout=5)
    data = resp.json()

    if data.get("code") == 0:
        quote = data["data"]
        print(f"📊 {quote.get('n')} ({quote.get('s')})")
        print(f"最新價: {quote.get('ld')} TRY")
        print(f"漲跌幅: {quote.get('chp')}%")
        print(f"成交量: {quote.get('v')}")
        return quote
    else:
        print(f"API錯誤: {data.get('msg')}")

# 測試
get_turkey_quote("THYAO")

    

回應欄位說明

  • ld:最新價
  • chp:漲跌幅(百分比)
  • v:成交量
  • o:開盤價
  • h:最高價
  • l:最低價
  • t:時間戳(毫秒)

3.3 歷史 K 線數據

歷史數據是量化回測的基礎。iTick 提供超 30 年的 K 線數據,支援多種週期:

      def get_turkey_kline(symbol, ktype=8, limit=100):
    """
    獲取土耳其股票歷史K線數據
    ktype參數說明:
        1: 1分鐘線
        2: 5分鐘線
        3: 15分鐘線
        4: 30分鐘線
        5: 60分鐘線
        8: 日線
        9: 週線
        10: 月線
    """
    url = f"{BASE_URL}/stock/kline"
    params = {
        "region": "TR",
        "code": symbol,
        "kType": ktype,
        "limit": str(limit)  # 注意:limit需要傳字串
    }

    resp = requests.get(url, params=params, headers=get_headers())
    data = resp.json()

    if data.get("code") == 0:
        klines = data.get("data", [])
        period_map = {1:"1分鐘",2:"5分鐘",3:"15分鐘",4:"30分鐘",5:"60分鐘",8:"日線",9:"週線",10:"月線"}
        print(f"✅ 獲取到 {len(klines)}{period_map.get(ktype, '')}數據")

        # 轉換為OHLCV格式
        for k in klines[-5:]:  # 顯示最近5條
            print(f"時間:{k['t']} 開:{k['o']} 高:{k['h']} 低:{k['l']} 收:{k['c']} 量:{k['v']}")
        return klines
    else:
        print(f"錯誤: {data.get('msg')}")

# 測試:獲取KCHOL日線數據
get_turkey_kline("KCHOL", ktype=8, limit=30)

# 測試:獲取EREGL週線數據
get_turkey_kline("EREGL", ktype=9, limit=20)

# 測試:獲取GARAN60分鐘線
get_turkey_kline("GARAN", ktype=5, limit=50)

    

3.4 盤口深度數據

盤口數據對於分析市場流動性、辨識支撐阻力位至關重要:

      def get_turkey_depth(symbol):
    """獲取土耳其股票即時盤口數據"""
    url = f"{BASE_URL}/stock/depth"
    params = {"region": "TR", "code": symbol}

    resp = requests.get(url, params=params, headers=get_headers())
    data = resp.json()

    if data.get("code") == 0:
        depth = data.get("data", {})
        print(f"📊 {symbol} 盤口深度")
        print(f"--- 賣盤 (Ask) ---")
        for ask in depth.get('a', [])[:5]:  # 顯示前5檔
            print(f"檔位{ask.get('po')}: {ask.get('p')} TRY | 數量: {ask.get('v')}")
        print(f"--- 買盤 (Bid) ---")
        for bid in depth.get('b', [])[:5]:
            print(f"檔位{bid.get('po')}: {bid.get('p')} TRY | 數量: {bid.get('v')}")
        return depth
    else:
        print(f"錯誤: {data.get('msg')}")

# 測試
get_turkey_depth("KCHOL")

    

3.5 公司基本面數據

      def get_turkey_company_info(symbol):
    """獲取土耳其公司基本資訊"""
    url = f"{BASE_URL}/stock/info"
    params = {"region": "TR", "code": symbol}

    resp = requests.get(url, params=params, headers=get_headers())
    data = resp.json()

    if data.get("code") == 0:
        company = data.get("data", {})
        print(f"🏢 {company.get('n')} ({symbol})")
        print(f"行業: {company.get('i')}")
        print(f"板塊: {company.get('s')}")
        print(f"簡介: {company.get('bd')[:100]}...")
        print(f"總市值: {company.get('mcb')}")
        print(f"市盈率: {company.get('pet')}")
        return company
    else:
        print(f"錯誤: {data.get('msg')}")

get_turkey_company_info("KCHOL")

    

四、WebSocket 即時推送:構建低延遲數據流

對於量化交易系統,WebSocket 是實現即時行情監控的首選方案。iTick WebSocket 支援quote(報價)、tick(逐筆成交)、depth(盤口深度)三種數據類型。

4.1 生產級 WebSocket 客戶端

以下實現包含自動重連、心跳保活、多標的訂閱等生產級特性:

      import websocket
import json
import threading
import time
from typing import List, Callable

class TurkeyWebSocketClient:
    """土耳其股票WebSocket即時行情客戶端(支援自動重連)"""

    def __init__(self, token: str, symbols: List[str], callback: Callable, types: str = "quote"):
        self.token = token
        self.symbols = [f"{s}$TR" for s in symbols]  # 格式:代碼$TR
        self.callback = callback
        self.types = types
        self.ws = None
        self.running = False
        self.reconnect_interval = 5
        self.ws_url = "wss://api.itick.org/stock"

    def start(self):
        self.running = True
        self._connect()

    def _connect(self):
        headers = {"token": self.token}
        self.ws = websocket.WebSocketApp(
            self.ws_url,
            header=headers,
            on_open=self._on_open,
            on_message=self._on_message,
            on_error=self._on_error,
            on_close=self._on_close
        )

        # 在獨立執行緒運行
        wst = threading.Thread(target=self.ws.run_forever, daemon=True)
        wst.start()

    def _on_open(self, ws):
        print("✅ WebSocket連接成功,開始訂閱...")
        subscribe_msg = {
            "ac": "subscribe",
            "params": ",".join(self.symbols),
            "types": self.types  # quote/tick/depth
        }
        ws.send(json.dumps(subscribe_msg))
        print(f"📤 訂閱請求: {subscribe_msg['params']}")

        # 啟動心跳
        self._start_heartbeat()

    def _start_heartbeat(self):
        def heartbeat():
            while self.running:
                time.sleep(30)
                if self.ws and self.ws.sock and self.ws.sock.connected:
                    ping = {"ac": "ping", "params": str(int(time.time()*1000))}
                    self.ws.send(json.dumps(ping))
                    print("💓 心跳已發送")
        threading.Thread(target=heartbeat, daemon=True).start()

    def _on_message(self, ws, message):
        try:
            data = json.loads(message)
            if "data" in data:
                self.callback(data["data"])
        except Exception as e:
            print(f"訊息解析錯誤: {e}")

    def _on_error(self, ws, error):
        print(f"❌ WebSocket錯誤: {error}")

    def _on_close(self, ws, close_status, close_msg):
        print(f"🔌 連接關閉: {close_msg}")
        if self.running:
            print(f"⏱️ {self.reconnect_interval}秒後重連...")
            time.sleep(self.reconnect_interval)
            self._connect()

    def stop(self):
        self.running = False
        if self.ws:
            self.ws.close()

# 使用範例
def handle_market_data(data):
    """自訂數據處理函數"""
    data_type = data.get("type")
    symbol = data.get("s")

    if data_type == "quote":
        print(f"[{symbol}] 最新:{data.get('ld')} TRY | 漲跌:{data.get('chp')}% | 量:{data.get('v')}")
    elif data_type == "tick":
        print(f"[{symbol}] 成交:{data.get('ld')} TRY | 時間:{data.get('t')}")
    elif data_type == "depth":
        bids = data.get("b", [])[:3]
        asks = data.get("a", [])[:3]
        print(f"[{symbol}] 買盤:{bids} | 賣盤:{asks}")

# 訂閱土耳其主流股票
client = TurkeyWebSocketClient(
    token="your_token_here",
    symbols=["KCHOL", "EREGL", "THYAO", "GARAN"],
    types="quote,tick,depth",
    callback=handle_market_data
)
client.start()

# 保持主執行緒運行
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    client.stop()

    

4.2 WebSocket 回應範例

報價(quote)回應

      {
  "code": 1,
  "data": {
    "s": "KCHOL",
    "r": "TR",
    "ld": 164.5,
    "chp": 1.23,
    "v": 12456700,
    "o": 162.8,
    "h": 165.2,
    "l": 162.5,
    "t": 1741680000000,
    "type": "quote"
  }
}

    

盤口(depth)回應

      {
  "code": 1,
  "data": {
    "s": "KCHOL",
    "r": "TR",
    "a": [
      { "po": 1, "p": 164.6, "v": 15000 },
      { "po": 2, "p": 164.7, "v": 23000 }
    ],
    "b": [
      { "po": 1, "p": 164.4, "v": 12000 },
      { "po": 2, "p": 164.3, "v": 18000 }
    ],
    "type": "depth"
  }
}

    

五、高級應用:量化策略整合

5.1 將 K 線數據轉換為 DataFrame

      import pandas as pd

def kline_to_dataframe(klines):
    """將K線數據轉換為Pandas DataFrame"""
    df = pd.DataFrame(klines)
    df['timestamp'] = pd.to_datetime(df['t'], unit='ms')
    df.set_index('timestamp', inplace=True)
    df.rename(columns={'o': 'open', 'h': 'high', 'l': 'low', 'c': 'close', 'v': 'volume'}, inplace=True)

    # 轉換為數值類型
    for col in ['open', 'high', 'low', 'close', 'volume']:
        df[col] = pd.to_numeric(df[col])

    return df

# 獲取數據並轉換為DataFrame
klines = get_turkey_kline("KCHOL", ktype=8, limit=100)
if klines:
    df = kline_to_dataframe(klines)
    print(df.head())

    

5.2 計算技術指標

      def calculate_technical_indicators(df):
    """計算常用技術指標"""
    # 移動平均線
    df['MA20'] = df['close'].rolling(window=20).mean()
    df['MA50'] = df['close'].rolling(window=50).mean()

    # RSI
    delta = df['close'].diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
    rs = gain / loss
    df['RSI'] = 100 - (100 / (1 + rs))

    # MACD
    exp12 = df['close'].ewm(span=12, adjust=False).mean()
    exp26 = df['close'].ewm(span=26, adjust=False).mean()
    df['MACD'] = exp12 - exp26
    df['Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()

    return df

# 計算指標
df_with_indicators = calculate_technical_indicators(df)
print(df_with_indicators[['close', 'MA20', 'RSI', 'MACD']].tail())

    

六、生產環境最佳實務

6.1 限流與重試策略

為避免觸發 API 限制,建議在程式碼中加入限流和重試邏輯:

      import time
from functools import wraps

def rate_limit(max_calls_per_second=2):
    """限流裝飾器"""
    def decorator(func):
        last_called = [0.0]

        @wraps(func)
        def wrapper(*args, **kwargs):
            elapsed = time.time() - last_called[0]
            left_to_wait = 1.0/max_calls_per_second - elapsed
            if left_to_wait > 0:
                time.sleep(left_to_wait)
            ret = func(*args, **kwargs)
            last_called[0] = time.time()
            return ret
        return wrapper
    return decorator

@rate_limit(max_calls_per_second=2)
def rate_limited_api_call(symbol):
    return get_turkey_quote(symbol)

    

6.2 錯誤碼處理

錯誤碼含義解決方案
401無效 API Key檢查 Token 是否正確,重新生成
404數據不存在確認 region=TR,股票代碼正確
429請求過頻降低請求頻率,或升級套餐

6.3 數據對齊與快取策略

      class TurkeyDataCache:
    """簡單的本地快取,減少API呼叫"""

    def __init__(self, ttl_seconds=60):
        self.cache = {}
        self.ttl = ttl_seconds

    def get(self, key):
        if key in self.cache:
            data, timestamp = self.cache[key]
            if time.time() - timestamp < self.ttl:
                return data
        return None

    def set(self, key, data):
        self.cache[key] = (data, time.time())

# 使用範例
cache = TurkeyDataCache(ttl_seconds=30)

def get_cached_quote(symbol):
    cached = cache.get(f"quote_{symbol}")
    if cached:
        print("使用快取數據")
        return cached

    print("呼叫API獲取")
    data = get_turkey_quote(symbol)
    if data:
        cache.set(f"quote_{symbol}", data)
    return data

    

七、總結:構建專業級土耳其股票數據模組

透過本文的深度解析,您已經掌握了使用 iTick API 構建土耳其股票數據模組的核心技術:

  1. REST API:適用於歷史數據獲取、批量查詢、基本面分析,支援多種 K 線週期
  2. WebSocket:實現低延遲即時行情推送,支援自動重連和心跳保活
  3. 技術分析整合:將 K 線數據轉化為量化策略可用的技術指標
  4. 生產級優化:限流、重試、快取、錯誤處理等最佳實務

iTick 為土耳其市場提供完整的數據覆蓋,免費套餐即可滿足個人開發者需求,付費套餐適合專業量化團隊。平台支援透過 Telegram(iticksupport)和 WhatsApp 聯絡客服獲取技術支援。

立即前往 iTick 官網 註冊,獲取您的 API Key,開啟土耳其股市量化之旅!


延伸閱讀: