A high-level Python wrapper for the libmdf C SDK, providing access to the Millistream Data Feed (MDF) for real-time financial data streaming.
Project description
millistream-mdf
Table of Contents
- Overview
- Installation
- Quick Start
- API Reference
- Usage Examples
- Available Data Types
- Error Handling
- Documentation
- License
- Support
Overview
A high-level Python wrapper for the libmdf C SDK, providing access to the Millistream Data Feed (MDF) for real-time financial data streaming.
Installation
1. Install Package
Install with uv:
uv add millistream-mdf
Or with pip:
pip install millistream-mdf
2. Install Prerequisites
Ubuntu/Debian
With uv:
uv run python -m millistream_mdf --install-deps
Or:
python -m millistream_mdf --install-deps
For manual installation, refer to the official documentation.
macOS
It is recommended to use the latest libmdf installer to install the necessary dependencies for macOS.
Note: Will most likely be named
libmdf-x.x.x.pkg.
Windows
It is recommended to use the latest libmdf installer to install the necessary dependencies for Windows.
Note: Will most likely be named
libmdf-x.x.x.exe.
Quick Start
from millistream_mdf import MDF, RequestClass
with MDF(
url='sandbox.millistream.com',
port=9100,
username='sandbox',
password='sandbox'
) as session:
for message in session.subscribe(
request_classes=[RequestClass.QUOTE], # Subscrive to 'quote' data
instruments=[1146], # Volvo B
timeout=1
):
print('raw:', message.fields) # unformatted fields
print('parsed:', message.parse_fields(remap_keys=True)) # convert types and/or format keys
print('---')
or using the asyncio API:
from millistream_mdf import AsyncMDF, RequestClass
import asyncio
async def main():
async with AsyncMDF(
url='sandbox.millistream.com',
port=9100,
username='sandbox',
password='sandbox'
) as session:
async for message in session.subscribe(
request_classes=[RequestClass.QUOTE], # Subscrive to 'quote' data
instruments=[1146], # Volvo B
timeout=1
):
print('raw:', message.fields) # unformatted fields
print('parsed:', message.parse_fields(remap_keys=True)) # convert types and/or format keys
print('---')
asyncio.run(main())
Tip: You can use
sandbox.millistream.com:9100for free to test the MDF with username:sandboxand password:sandbox. The data will be delayed and might not have access to the full offering.
Tip: If you only want to convert the types you can use
parse_fields(remap_keys=False, convert_types=[...])
Example Output:
raw: {5: '272.60', 6: '272.80', 19: '1559', 20: '1988', 7: '272.80', 10: '2139312', 11: '584322621.58', 37: '7116', 8: '275.10', 9: '271.80', 39: '275', 123: '273.07232827', 367: '105139', 368: '28856646.78', 369: None, 370: None, 3: '2025-10-11', 4: '15:29:40'}
parsed: {'bidprice': 272.6, 'askprice': 272.8, 'bidquantity': 1559.0, 'askquantity': 1988.0, 'lastprice': 272.8, 'quantity': 2139312.0, 'turnover': 584322621.58, 'numtrades': 7116, 'dayhighprice': 275.1, 'daylowprice': 271.8, 'openprice': 275.0, 'vwap': 273.07232827, 'offbookquantity': '105139', 'offbookturnover': '28856646.78', 'darkquantity': None, 'darkturnover': None, 'date': datetime.date(2025, 10, 11), 'time': datetime.time(15, 29, 40)}
---
raw: {20: '31', 4: '15:29:45'}
parsed: {'askquantity': 31.0, 'time': datetime.time(15, 29, 45)}
---
raw: {19: '4796', 4: '15:29:57'}
parsed: {'bidquantity': 4796.0, 'time': datetime.time(15, 29, 57)}
---
raw: {19: '3173', 20: '1432', 4: '15:30:02'}
parsed: {'bidquantity': 3173.0, 'askquantity': 1432.0, 'time': datetime.time(15, 30, 2)}
Note: Only the differences are broadcasted for efficacy. In the example above a full image is broadcasted at the beginning since
subscription_modedefaults tofull(image+stream).
API Reference
MDF Class
The main client class for connecting to MDF servers.
Constructor Parameters
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
Server URL | |
port |
int |
Server port | 9100 |
username |
str |
Username for authentication | |
password |
str |
Password for authentication | |
heartbeat_interval |
int, float |
Heartbeat interval in seconds | 30 |
connect_timeout |
int, float |
Connection timeout in seconds | 10 |
tcp_nodelay |
bool |
Disable TCP Nagle algorithm | True |
no_encryption |
bool |
Disable encryption | False |
Attributes
Properties
| Name | Type | Description | Default |
|---|---|---|---|
is_connected |
bool |
Whether the client is connected to the server | False |
is_authenticated |
bool |
Whether the client is authenticated to the server | False |
Methods
connect()
Connect to the MDF server and authenticate.
Raises:
MDFConnectionError: If connection failsMDFAuthenticationError: If authentication fails
disconnect()
Disconnect from the MDF server.
subscribe(request_classes, instruments='*', subscription_mode='full', timeout=1)
Subscribe to data streams and yield messages.
Parameters:
request_classes: List of request classes to subscribe to (e.g.,[RequestClass.QUOTE, RequestClass.TRADE, RequestClass.BASICDATA]). Can be string names or integer MREF codesinstruments: Instrument references to subscribe to. Can be'*'for all, or numeric IDs (e.g.,[1146, 1147])subscription_mode: Subscription mode ('image','stream', or'full'). SeeSubscription Modesfor more information.timeout: Timeout in seconds for consume operations
Returns: Generator yielding Message objects
unsubscribe(request_classes='*', instruments='*')
Unsubscribe from data streams to stop receiving realtime data.
Parameters:
request_classes: List of request classes to unsubscribe from (e.g.,[RequestClass.QUOTE, RequestClass.TRADE]), or'*'for allinstruments: Instrument references to unsubscribe from. Can be'*'for all, or numeric IDs (e.g.,[1146, 1147])
Raises:
MDFError: If not connected or authenticatedMDFMessageError: If unsubscription request fails
Note: You can unsubscribe from a subset of your active subscriptions - the lists don't have to match previous subscription requests exactly.
Example:
# Unsubscribe from specific instruments
client.unsubscribe(
request_classes=[RequestClass.QUOTE],
instruments=[1146, 1147]
)
# Unsubscribe from all quotes
client.unsubscribe(request_classes=[RequestClass.QUOTE], instruments='*')
# Unsubscribe from everything
client.unsubscribe()
stream(timeout=1)
Stream messages from the server.
Parameters:
timeout: Timeout in seconds for consume operations
Returns: Generator yielding Message objects
send(mref, instrument, fields, delay=0)
Send a single message to the server with specified fields.
Parameters:
mref: Message reference (e.g.,MessageReference.QUOTE,MessageReference.TRADE)instrument: Instrument referencefields: Dictionary mapping field names to valuesdelay: Optional delay parameter (default:0)
Returns: True if the message was sent successfully
Raises:
MDFError: If not connected or authenticatedMDFMessageError: If message construction or sending fails
Example:
client.send(
mref=MessageReference.QUOTE,
instrument=12345,
fields={
Field.BIDPRICE: 100.50,
Field.ASKPRICE: 100.55,
Field.BIDQUANTITY: 1000,
Field.ASKQUANTITY: 500
}
)
send_batch(messages)
Send multiple messages in a single batch for better efficiency.
Parameters:
messages: List of message dictionaries with'mref','instrument','fields', and optionally'delay'
Returns: True if all messages were sent successfully
Example:
client.send_batch([
{
'mref': MessageReference.QUOTE,
'instrument': 12345,
'fields': {Field.BIDPRICE: 100.50, Field.ASKPRICE: 100.55},
},
{
'mref': MessageReference.TRADE,
'instrument': 12345,
'fields': {Field.TRADEPRICE: 100.52, Field.TRADEQUANTITY: 1000},
}
])
create_message_builder()
Create a new MessageBuilder for advanced message construction.
Returns: A new MessageBuilder instance (must be used as context manager)
Example:
with client.create_message_builder() as builder:
builder.add_message(mref=MessageReference.QUOTE, instrument=12345)
builder.add_field(Field.BIDPRICE, 100.50)
builder.add_field(Field.ASKPRICE, 100.55)
builder.send(client._handle)
Message Class
Represents a message received from the MDF server.
Attributes
| Name | Type | Description | Default | Example |
|---|---|---|---|---|
ref |
int |
What type of message it is (e.g. MessageReference.NEWSHEADLINE) |
MessageReference.QUOTE |
|
instrument |
` |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file millistream_mdf-0.1.10.tar.gz.
File metadata
- Download URL: millistream_mdf-0.1.10.tar.gz
- Upload date:
- Size: 35.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c80b086e76862ac9b798e3b6169f1b09958d0299a988e2edfa44e4943239d99
|
|
| MD5 |
8a4147470153dfd4b92c1dca0bf41be9
|
|
| BLAKE2b-256 |
dc3e0bd0d56338dd467037388fef56173b87033ad805ed8e6d448221ff44f15d
|
Provenance
The following attestation bundles were made for millistream_mdf-0.1.10.tar.gz:
Publisher:
publish.yml on mint273/millistream-mdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millistream_mdf-0.1.10.tar.gz -
Subject digest:
6c80b086e76862ac9b798e3b6169f1b09958d0299a988e2edfa44e4943239d99 - Sigstore transparency entry: 612186436
- Sigstore integration time:
-
Permalink:
mint273/millistream-mdf@dedf0a2577bd64c85590a9dc79a93b80521c11ea -
Branch / Tag:
refs/tags/v0.1.10 - Owner: https://github.com/mint273
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dedf0a2577bd64c85590a9dc79a93b80521c11ea -
Trigger Event:
release
-
Statement type:
File details
Details for the file millistream_mdf-0.1.10-py3-none-any.whl.
File metadata
- Download URL: millistream_mdf-0.1.10-py3-none-any.whl
- Upload date:
- Size: 36.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59ae2ed73d222c3a2337ace777baac3cf67da96cc7dd0e3c94658768240c9151
|
|
| MD5 |
95429a9e62345259b78dd0c1291d4c86
|
|
| BLAKE2b-256 |
0fd37b350306c7e902090d4767f3f3a79c802cc373857e2503abfea351edb686
|
Provenance
The following attestation bundles were made for millistream_mdf-0.1.10-py3-none-any.whl:
Publisher:
publish.yml on mint273/millistream-mdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millistream_mdf-0.1.10-py3-none-any.whl -
Subject digest:
59ae2ed73d222c3a2337ace777baac3cf67da96cc7dd0e3c94658768240c9151 - Sigstore transparency entry: 612186441
- Sigstore integration time:
-
Permalink:
mint273/millistream-mdf@dedf0a2577bd64c85590a9dc79a93b80521c11ea -
Branch / Tag:
refs/tags/v0.1.10 - Owner: https://github.com/mint273
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dedf0a2577bd64c85590a9dc79a93b80521c11ea -
Trigger Event:
release
-
Statement type: