This release is a pre-release and may not be stable for production use.
fugle-marketdata
Fugle market data REST and WebSocket client for Python, powered by a Rust core through PyO3.
Installation
pip install --pre fugle-marketdata # 3.x pre-release
Wheels are published for CPython 3.8+ (abi3) on Linux glibc (x86_64, aarch64), macOS (x86_64, arm64) and Windows x64.
Development Build
# Create virtual environment
python -m venv .venv
source .venv/bin/activate
# Install maturin
pip install maturin
# Build and install in development mode
cd py
maturin develop
Release Build From Source
maturin build --release
pip install target/wheels/fugle_marketdata-*.whl
Quick Start
REST API
from fugle_marketdata import RestClient, MarketDataError
# Create client with API key
client = RestClient(api_key="your-api-key")
# Get stock quote
quote = client.stock.intraday.quote("2330")
print(f"TSMC Price: {quote['closePrice']}")
print(f"Change: {quote['change']}")
print(f"Volume: {quote['total']['tradeVolume']}")
# Get stock ticker info
ticker = client.stock.intraday.ticker("2330")
print(f"Name: {ticker['name']}")
# Get intraday candles (5-minute)
candles = client.stock.intraday.candles("2330", timeframe="5")
for candle in candles['data'][:3]:
print(f" {candle['time']}: O={candle['open']} H={candle['high']} L={candle['low']} C={candle['close']}")
# Get recent trades
trades = client.stock.intraday.trades("2330")
for trade in trades['data'][:5]:
print(f" Price: {trade['price']}, Size: {trade['size']}")
# FutOpt (futures/options) data
futopt_quote = client.futopt.intraday.quote("TXFC4")
print(f"Futures Price: {futopt_quote['closePrice']}")
WebSocket Streaming
from fugle_marketdata import WebSocketClient
import time
# Create WebSocket client
ws = WebSocketClient(api_key="your-api-key")
# --- Callback Mode ---
def on_message(msg):
"""Handle incoming messages"""
if msg.get('event') == 'data':
channel = msg.get('channel')
symbol = msg.get('symbol')
data = msg.get('data', {})
print(f"[{channel}] {symbol}: {data}")
def on_connect():
print("Connected!")
def on_disconnect(code, reason):
print(f"Disconnected: {code} - {reason}")
def on_error(message, code):
print(f"Error [{code}]: {message}")
# Register callbacks
stock = ws.stock
stock.on("message", on_message)
stock.on("connect", on_connect)
stock.on("disconnect", on_disconnect)
stock.on("error", on_error)
# Connect and subscribe
stock.connect()
stock.subscribe("trades", "2330")
stock.subscribe("books", "2330")
# Keep running for 10 seconds
time.sleep(10)
# Disconnect
stock.disconnect()
# --- Iterator Mode ---
ws2 = WebSocketClient(api_key="your-api-key")
stock2 = ws2.stock
stock2.connect()
stock2.subscribe("trades", "2330")
# Iterate over messages
for msg in stock2.messages():
print(msg)
# Add break condition as needed
Authentication
Three authentication methods are supported:
from fugle_marketdata import RestClient
# 1. API Key (most common)
client = RestClient(api_key="your-api-key")
# 2. Bearer Token
client = RestClient(bearer_token="your-bearer-token")
# 3. SDK Token
client = RestClient(sdk_token="your-sdk-token")
Configuration
Reconnection Config
Control WebSocket automatic reconnection behavior:
from fugle_marketdata import WebSocketClient, ReconnectConfig
# Create custom reconnect configuration
reconnect = ReconnectConfig(
enabled=True,
max_attempts=10,
initial_delay_ms=2000,
max_delay_ms=120000
)
ws = WebSocketClient(api_key="your-key", reconnect=reconnect)
ReconnectConfig Options:
enabled(bool): Whether auto-reconnect is enabled (default: True)max_attempts(int): Maximum reconnection attempts (default: 5, min: 1)initial_delay_ms(int): Initial delay for exponential backoff (default: 1000ms, min: 100ms)max_delay_ms(int): Maximum delay cap (default: 60000ms)
Health Check Config
Control WebSocket health check (ping-pong) behavior:
from fugle_marketdata import WebSocketClient, HealthCheckConfig
# Create custom health check configuration
health_check = HealthCheckConfig(
enabled=True,
interval_ms=15000,
max_missed_pongs=3
)
ws = WebSocketClient(api_key="your-key", health_check=health_check)
HealthCheckConfig Options:
enabled(bool): Whether health check is enabled (default: False)interval_ms(int): Ping interval in milliseconds (default: 30000ms, min: 5000ms)max_missed_pongs(int): Maximum missed pongs before considering connection stale (default: 2, min: 1)
Combined Configuration
from fugle_marketdata import WebSocketClient, ReconnectConfig, HealthCheckConfig
reconnect = ReconnectConfig(max_attempts=10, initial_delay_ms=2000)
health_check = HealthCheckConfig(enabled=True, interval_ms=15000)
ws = WebSocketClient(
api_key="your-key",
reconnect=reconnect,
health_check=health_check
)
API Reference
RestClient
Stock Intraday Methods
client.stock.intraday.quote(symbol) # Real-time quote
client.stock.intraday.ticker(symbol) # Symbol information
client.stock.intraday.candles(symbol, timeframe="1") # OHLCV candles
client.stock.intraday.trades(symbol) # Trade history
client.stock.intraday.volumes(symbol) # Volume by price
FutOpt Intraday Methods
client.futopt.intraday.quote(symbol) # Real-time quote
client.futopt.intraday.ticker(symbol) # Contract information
client.futopt.intraday.candles(symbol, timeframe="1") # OHLCV candles
client.futopt.intraday.trades(symbol) # Trade history
client.futopt.intraday.volumes(symbol) # Volume by price
client.futopt.intraday.products(type) # Product listing ("F" or "O")
WebSocketClient
Properties
ws.stock # Access StockWebSocketClient
ws.futopt # Access FutOptWebSocketClient
StockWebSocketClient / FutOptWebSocketClient Methods
client.connect() # Connect to server
client.disconnect() # Disconnect from server
client.is_connected() # Check connection status
client.is_closed() # Check if client is closed
client.subscribe(channel, symbol) # Subscribe to channel
client.unsubscribe(subscription_id) # Unsubscribe by ID
client.subscriptions() # List active subscriptions
client.on(event, callback) # Register event callback
client.off(event) # Unregister callback
client.messages() # Get message iterator
Event Types
| Event | Callback Signature | Description |
|---|---|---|
message |
fn(msg: dict) |
Incoming data message |
connect |
fn() |
Connection established |
disconnect |
fn(code: int, reason: str) |
Connection closed |
error |
fn(message: str, code: int) |
Error occurred |
Channels
| Channel | Description |
|---|---|
trades |
Real-time trade executions |
candles |
Candlestick updates |
books |
Order book (5 levels) |
aggregates |
Aggregated market data |
indices |
Index values (stock only) |
MessageIterator
# Get iterator from connected client
messages = stock.messages()
# Iterate (blocking)
for msg in messages:
print(msg)
# Manual iteration
msg = next(messages) # Blocking
msg = messages.try_recv() # Non-blocking, returns None if no message
msg = messages.recv_timeout(5.0) # Timeout in seconds
Error Handling
All API errors raise MarketDataError:
from fugle_marketdata import RestClient, MarketDataError
client = RestClient(api_key="invalid-key")
try:
quote = client.stock.intraday.quote("2330")
except MarketDataError as e:
message = e.args[0]
error_code = e.args[1]
print(f"Error [{error_code}]: {message}")
Error Codes
| Code | Error Type | Description |
|---|---|---|
| 1001 | InvalidSymbol | Invalid or unsupported symbol |
| 1002 | DeserializationError | JSON parsing failed |
| 1003 | RuntimeError | Internal runtime error |
| 1004 | ConfigError | Configuration error |
| 2001 | ConnectionError | Network connection failed |
| 2002 | AuthError | Authentication failed |
| 2003 | ApiError | API returned error response |
| 2010 | ClientClosed | Client has been closed |
| 3001 | TimeoutError | Operation timed out |
| 3002 | WebSocketError | WebSocket protocol error |
| 9999 | Other | Unexpected error |
Examples
Full REST Example
from fugle_marketdata import RestClient, MarketDataError
import os
def main():
api_key = os.environ.get("FUGLE_API_KEY")
if not api_key:
print("Set FUGLE_API_KEY environment variable")
return
client = RestClient(api_key=api_key)
try:
# Stock data
print("=== Stock Market Data ===")
quote = client.stock.intraday.quote("2330")
print(f"TSMC Quote: {quote['closePrice']}")
ticker = client.stock.intraday.ticker("2330")
print(f"Ticker: {ticker['name']}")
candles = client.stock.intraday.candles("2330", timeframe="5")
print(f"Candles: {len(candles['data'])} entries")
# FutOpt data
print("\n=== FutOpt Market Data ===")
products = client.futopt.intraday.products("F")
print(f"Futures products: {len(products['data'])}")
except MarketDataError as e:
print(f"Error [{e.args[1]}]: {e.args[0]}")
if __name__ == "__main__":
main()
Full WebSocket Example
from fugle_marketdata import WebSocketClient, MarketDataError
import os
import time
def main():
api_key = os.environ.get("FUGLE_API_KEY")
if not api_key:
print("Set FUGLE_API_KEY environment variable")
return
ws = WebSocketClient(api_key=api_key)
stock = ws.stock
message_count = 0
def on_message(msg):
nonlocal message_count
message_count += 1
if msg.get('event') == 'data':
print(f"[{message_count}] {msg.get('channel')}: {msg.get('symbol')}")
def on_connect():
print("Connected!")
def on_error(message, code):
print(f"Error [{code}]: {message}")
stock.on("message", on_message)
stock.on("connect", on_connect)
stock.on("error", on_error)
try:
stock.connect()
stock.subscribe("trades", "2330")
stock.subscribe("books", "2330")
print("Listening for 10 seconds...")
time.sleep(10)
print(f"\nReceived {message_count} messages")
print(f"Subscriptions: {stock.subscriptions()}")
except MarketDataError as e:
print(f"Error [{e.args[1]}]: {e.args[0]}")
finally:
if stock.is_connected():
stock.disconnect()
print("Done")
if __name__ == "__main__":
main()
Requirements
- Python 3.8+
- Rust toolchain (for building from source)
- maturin (for development builds)
License
MIT
Metadata
Release files for fugle-marketdata 3.0.0rc2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
Total release size: 15.0 MB
Release files / fugle_marketdata-3.0.0rc2-cp38-abi3-win_amd64.whl
| Download URL | fugle_marketdata-3.0.0rc2-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
a77ff5d05cf2ccc6e91bb5933911302a74c024c519ffb1addd5dd04b01532e0f
|
|
BLAKE2b-256 checksum How to use checksums |
86015547337afc160ec96cd081379c1de9c467780ea281cbce882482bc50b83c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / fugle_marketdata-3.0.0rc2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | fugle_marketdata-3.0.0rc2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 3.1 MB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
ba309e09d512af1da759e07e4adf0aafcf2a158d50d5354b826a6b3d88dc5402
|
|
BLAKE2b-256 checksum How to use checksums |
27f66d2fb6711ba10ab5091e15a69b06421e781b805958bbe27de020b2197212
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / fugle_marketdata-3.0.0rc2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | fugle_marketdata-3.0.0rc2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 3.4 MB |
| Tags | CPython 3.8 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
b878c9af50bf63c743de689a77d87379f53038e9ff3854121efa094ea5a9362b
|
|
BLAKE2b-256 checksum How to use checksums |
819a67f450176f589ba17b71d7f2ce075cc9c4d305753a245670e10999d4499e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / fugle_marketdata-3.0.0rc2-cp38-abi3-macosx_11_0_arm64.whl
| Download URL | fugle_marketdata-3.0.0rc2-cp38-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.9 MB |
| Tags | CPython 3.8 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
246cbe96cb3fc1d89d999206fa380dbad0e67d09adc1a5fa78424d437552b024
|
|
BLAKE2b-256 checksum How to use checksums |
e1e3e0c005177ff031d715004a28ccb429c32c5e2a142e5c9574283d493eb539
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / fugle_marketdata-3.0.0rc2-cp38-abi3-macosx_10_12_x86_64.whl
| Download URL | fugle_marketdata-3.0.0rc2-cp38-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.9 MB |
| Tags | CPython 3.8 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
498767c13d9064605cedae16ce059986b2fa634dad511b65f4996f2fb70c35f5
|
|
BLAKE2b-256 checksum How to use checksums |
9896c6a899374a641462bd5090f145fff1d855c4b7a66f6e9621aa1cd29df4cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logSecurity for fugle-marketdata 3.0.0rc2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 5 files in this release have been uploaded with provenance metadata.
Attestation for fugle_marketdata-3.0.0rc2-cp38-abi3-win_amd64.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.