Common MLink Queries
| Example | Description |
|---|---|
| AAPL Firehose | Streams the NBBO as filtered for AAPL only. |
| MLinkStream Surfaces | Streams surface information from LiveImpliedQuoteAdjust to display best bid and ask. |
| Child Orders | Displays working child orders (if any exist.) |
| MLinkStream Symbol Risk | Streams risk information on symbols in their account (if any exist). |
| Parent Limit Order | Details a three stage process to send orders with an AUX limit. |
| Initiating a Block Auction | Details the process to initiate a Block Auction. |
| Initiating a Flash Auction | Details the process to initiate a Flash Auction. |
1. AAPL Firehose
import asyncio
import json
import time
import websockets
import nest_asyncio
import threading
import datetime
from IPython.display import display
import pandas as pd
from pandas import json_normalize
nest_asyncio.apply()
uriJson = "wss://mlink-live.nms.venus.spiderrockconnect.com/mlink/json"
authentication_key = ""
stream_df = pd.DataFrame(columns=["ticker", "bidPrice1", "askPrice1"]) # Global DataFrame for accumulating messages
async def recv_msg(websocket):
global stream_df
buffer = await websocket.recv()
result = json.loads(buffer)
if isinstance(result, dict):
if result.get("header", {}).get("mTyp") == "StockBookQuote":
msg = result.get("message", {})
pkey = msg.get("pkey", {}).get("ticker", {})
ticker = f"{pkey.get('tk')}-{pkey.get('ts')}-{pkey.get('at')}"
record = {
"ticker": ticker,
"bidPrice1": msg.get("bidPrice1"),
"askPrice1": msg.get("askPrice1")
}
# Only append if all values are present
if all(record.values()):
stream_df = pd.concat([stream_df, pd.DataFrame([record])], ignore_index=True)
#print(stream_df.tail(1)) # Show latest
display(stream_df.tail(1))
return True
async def query_mlink(authentication_key):
retry = True
while retry:
try:
async with websockets.connect(uriJson,
additional_headers={"Authorization": f"Bearer {authentication_key}"},
ping_timeout=None) as websocket:
msg = {
"header": {
"mTyp": "MLinkStream"
},
"message": {
"queryLabel": "ExampleStockNbbo",
"activeLatency": 1, #you can stream AAPL with minimum latency
"msgName": "StockBookQuote", #the message you wish to stream
"view":"ticker|bidprice1|askprice1",
"where":"ticker:eq:AAPL-NMS-EQT" #can also do ticker.tk:eq:AAPL & ticker.at:eq:EQT & ticker.ts:eq:NMS
}
}
t = time.time_ns()
tstr = '.'.join([time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1000000000)), "%06d" % ((t / 1000) % 1000000)])
msg['header']['sTim'] = tstr
msg['header']['encT'] = tstr
smsg = json.dumps(msg)
await websocket.send(smsg)
notDone = True
while notDone:
notDone = await recv_msg(websocket)
except asyncio.exceptions.TimeoutError:
print("timeout occurred, retrying...")
if __name__ == "__main__":
asyncio.run(query_mlink(authentication_key))
2. MLinkStream Surfaces
import asyncio
import json
import time
import websockets
import nest_asyncio
import pandas as pd
from pandas import json_normalize
from IPython.display import display
nest_asyncio.apply()
uriJson = "wss://mlink-live.nms.venus.spiderrockconnect.com/mlink/json"
authentication_key = ""
async def recv_msg(websocket):
buffer = await websocket.recv()
result = json.loads(buffer)
print(result)
return True
stream_df = pd.DataFrame(columns=["ticker","Call/Put" ,"strike","opt_exp","delta" ,"uprc", "obid", "oask","obiv","oaiv","svol","timestamp"])
async def recv_msg(websocket):
global stream_df
buffer = await websocket.recv()
result = json.loads(buffer)
if isinstance(result, dict):
if result.get("header", {}).get("mTyp") == "LiveImpliedQuoteAdj":
msg = result.get("message", {})
# Build ticker from message.ticker
#t = msg.get("ticker", {})
#ticker = f"{t.get('tk')}-{t.get('ts')}-{t.get('at')}"
ticker = msg.get("pkey", {}).get("okey", {}).get("tk")
# Extract strike from pkey.okey.xx
strike = msg.get("pkey", {}).get("okey", {}).get("xx")
# Extract Call/Put from pkey.okey.xx
callput = msg.get("pkey", {}).get("okey", {}).get("cp")
# Extract opt_exp from pkey.okey.xx
opt_exp = msg.get("pkey", {}).get("okey", {}).get("dt")
record = {
"ticker": ticker,
"strike": strike,
"Call/Put": callput,
"opt_exp": opt_exp,
"uprc": msg.get("uprc"),
"obid": msg.get("obid"),
"oask": msg.get("oask"),
"obiv": msg.get("obiv"),
"oaiv": msg.get("oaiv"),
"svol": msg.get("svol"),
"delta": msg.get("de"),
"timestamp": msg.get("timestamp")
}
#if all(record.values()):
#stream_df = pd.concat([stream_df, pd.DataFrame([record])], ignore_index=True)
#print(stream_df.tail(5))
display(pd.DataFrame([record]))
return True
async def send_signal(websocket):
while True:
await asyncio.sleep(20)
signal = {
"header": {
"mTyp": "MLinkSignalReady"
},
"message": {
# "sesionID": "",
# "signalID": "",
"readyScan": "FullScan"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
signal['header']['sTim'] = tstr
signal['header']['encT'] = tstr
smsg = json.dumps(signal)
await websocket.send(smsg)
await asyncio.sleep(0)
async def query_mlink(authentication_key):
retry = True
while retry:
try:
async with websockets.connect(
uriJson,
additional_headers={"Authorization": f"Bearer {authentication_key}"},
ping_timeout=None
) as websocket:
msg = {
"header": {
"mTyp": "MLinkStream"
},
"message": {
"queryLabel": "ExampleStockNbbo",
"activeLatency": 5000,
"msgName": "LiveImpliedQuoteAdj",
#"view":"ticker|xx|uprc|obid|oask|svol|de|ga|th|ve",
#"where": "ticker:eq:SPY-NMS-EQT-2025-05-16%26de:cb:0.40$0.60%26de:cb:-0.40$-0.60"
#"where": "okey:eq:SPY-NMS-EQT-2025-05-16 & ((de:ge:-0.60 & de:le:-0.40) | (de:ge:0.40 & de:le:0.60))"
#line above does the same thing as this line below
#"where": "okey:eq:SPY-NMS-EQT-2025-05-21 & ((de:cb:-0.60$-0.40) | (de:cb:0.40$0.60))"
"where": "ticker:eq:SPY-NMS-EQT & ((de:cb:-0.60$-0.40) | (de:cb:0.40$0.60)) & years:lt:.006"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
msg['header']['sTim'] = tstr
msg['header']['encT'] = tstr
await websocket.send(json.dumps(msg))
asyncio.create_task(send_signal(websocket))
notDone = True
while notDone:
notDone = await recv_msg(websocket)
except asyncio.exceptions.TimeoutError:
print("Timeout occurred, retrying...")
if __name__ == "__main__":
asyncio.run(query_mlink(authentication_key))
3. Child Orders
import asyncio
import json
import time
import websockets
import nest_asyncio
import pandas as pd
from IPython.display import display
nest_asyncio.apply()
uriJson = "wss://mlink-live.nms.venus.spiderrockconnect.com/mlink/json"
authentication_key = ""
# Initialize DataFrame
#account,username ,ticker, dt, xx, cp, orderside,limitPrice, nbbobid, nbboask, ordersize, leavesQuantity
stream_df = pd.DataFrame(columns=["accnt","userName","ticker","exp","strike","call/put","orderSide","child_px","child_sz","child_ex"])
async def recv_msg(websocket):
global stream_df
buffer = await websocket.recv()
result = json.loads(buffer)
if isinstance(result, dict):
if result.get("header", {}).get("mTyp") == "SpdrParentBrkrState":
msg = result.get("message", {})
account = msg.get("pkey", {}).get("accnt")
user_name = msg.get("userName")
ticker = msg.get("pkey", {}).get("secKey", {}).get("tk")
exp_date = msg.get("pkey", {}).get("secKey", {}).get("dt")
strike = msg.get("pkey", {}).get("secKey", {}).get("xx")
call_put = msg.get("pkey", {}).get("secKey", {}).get("cp")
order_side = msg.get("pkey", {}).get("orderSide")
child_price = msg.get("cpx1")
child_size = msg.get("csz1")
child_ex = msg.get("cex1")
#orderSize = msg.get("orderSize")
#leavesQuantity = msg.get("leavesQuantity")
record = {
"accnt": account,
"userName": user_name,
"ticker": ticker,
"exp": exp_date,
"strike": strike,
"call/put": call_put,
"orderSide": order_side,
"child_px": child_price,
"child_sz": child_size,
"child_ex": child_ex
#"ordersize": orderSize,
#"leavesQuantity": leavesQuantity
}
#if all(record.values()):
# stream_df = pd.concat([stream_df, pd.DataFrame([record])], ignore_index=True)
# print(stream_df.tail(5))
show_df = pd.DataFrame([record])
#print(pd.DataFrame([record]))
display(show_df)
return True
async def send_signal(websocket):
while True:
await asyncio.sleep(20)
signal = {
"header": {
"mTyp": "MLinkSignalReady"
},
"message": {
"readyScan": "FullScan"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
signal['header']['sTim'] = tstr
signal['header']['encT'] = tstr
await websocket.send(json.dumps(signal))
await asyncio.sleep(0)
async def query_mlink(authentication_key):
retry = True
while retry:
try:
async with websockets.connect(
uriJson,
additional_headers={"Authorization": f"Bearer {authentication_key}"},
ping_timeout=None
) as websocket:
msg = {
"header": {
"mTyp": "MLinkStream"
},
"message": {
"queryLabel": "ExampleSymbolRiskSummary",
"activeLatency": 900,
"msgName": "SpdrParentBrkrState",
#"view": "userName|cpx1|cex1|csz1",
"where": "accnt:eq:T.MTL.VEN & spdrBrokerStatus:eq:ACTIVE"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
msg['header']['sTim'] = tstr
msg['header']['encT'] = tstr
await websocket.send(json.dumps(msg))
asyncio.create_task(send_signal(websocket))
while True:
await recv_msg(websocket)
except asyncio.exceptions.TimeoutError:
print("Timeout occurred, retrying...")
if __name__ == "__main__":
asyncio.run(query_mlink(authentication_key))
4. MLinkStream Symbol Risk
import asyncio
import json
import time
import websockets
import nest_asyncio
import pandas as pd
from IPython.display import display
nest_asyncio.apply()
uriJson = "wss://mlink-live.nms.venus.spiderrockconnect.com/mlink/json"
authentication_key = ""
# Initialize DataFrame
stream_df = pd.DataFrame(columns=["ticker", "tradeDate", "VaRsu50", "VaRsd50"])
async def recv_msg(websocket):
global stream_df
buffer = await websocket.recv()
result = json.loads(buffer)
if isinstance(result, dict):
if result.get("header", {}).get("mTyp") == "SymbolRiskSummaryV5":
msg = result.get("message", {})
#ticker = msg.get("ticker")
ticker = msg.get("pkey", {}).get("ticker", {}).get("tk")
#trade_date = msg.get("tradeDate")
trade_date = msg.get("pkey", {}).get("tradeDate")
var_su50 = msg.get("VaRsu50")
var_sd50 = msg.get("VaRsd50")
record = {
"ticker": ticker,
"tradeDate": trade_date,
"VaRsu50": var_su50,
"VaRsd50": var_sd50
}
#if all(record.values()):
# stream_df = pd.concat([stream_df, pd.DataFrame([record])], ignore_index=True)
# print(stream_df.tail(5))
#print(pd.DataFrame([record]))
display(pd.DataFrame([record]))
return True
async def send_signal(websocket):
while True:
await asyncio.sleep(20)
signal = {
"header": {
"mTyp": "MLinkSignalReady"
},
"message": {
"readyScan": "FullScan"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
signal['header']['sTim'] = tstr
signal['header']['encT'] = tstr
await websocket.send(json.dumps(signal))
await asyncio.sleep(0)
async def query_mlink(authentication_key):
retry = True
while retry:
try:
async with websockets.connect(
uriJson,
additional_headers={"Authorization": f"Bearer {authentication_key}"},
ping_timeout=None
) as websocket:
msg = {
"header": {
"mTyp": "MLinkStream"
},
"message": {
"queryLabel": "ExampleSymbolRiskSummary",
"activeLatency": 2500,
"msgName": "SymbolRiskSummaryV5",
#"view": "ticker|accnt|tradeDate|VaRsu50|VaRsd50",
#"where": "ticker:eq:AAPL-NMS-EQT & risksession:eq:regular & tradeDate:eq:2025-05-20"
"where": "accnt:eq:T.TJ.VEN3 & tradeDate:eq:2025-06-05"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
msg['header']['sTim'] = tstr
msg['header']['encT'] = tstr
await websocket.send(json.dumps(msg))
asyncio.create_task(send_signal(websocket))
while True:
await recv_msg(websocket)
except asyncio.exceptions.TimeoutError:
print("Timeout occurred, retrying...")
if __name__ == "__main__":
asyncio.run(query_mlink(authentication_key))
5. Parent Limit Order
Step One: Send order to parentOrder table.
Initially, this will do nothing as limitType is equal to AUX.
import requests
import json
import time
import copy
# REST endpoint and auth token
rest_url = "https://mlink-live.nms.venus.spiderrockconnect.com/rest/json"
authentication_key = ""
# Base order payload template
base_order_payload = {
"header": {
"mTyp": "SpdrParentOrder"
},
"message": {
"secKey": {
"at": "EQT",
"ts": "NMS",
"tk": "AAPL",
"dt": "2025-07-18",
"xx": 200,
"cp": "Put"
},
"positionType": "Auto",
"parentShape": "Single",
"secType": "Option",
"accnt": "T.MTL.VEN", # <-- insert your account here
"orderSide": "Sell",
"clientFirm": "SRCORE",
"spdrActionType": "Add",
"startType": 0,
"orderSize": 50,
"orderActiveSize": 7,
"marketSession": "RegMkt",
"parentOrderHandling": "PostOnly",
"parentBalanceHandling": "PostLimit",
"orderLimitType": "Aux",
"takeLimitClass": "SurfProb",
"makeLimitClass": "SurfProb",
"takeProbability": 0.1,
"makeProbability": 0.3,
"takeAlphaType": "Static",
"makeAlphaType": "Static",
"takeReachRule": "None",
"publicSize": "FullSizeR",
"numMakeExchanges": 1,
"autohedge": "None",
"userName": "matt.leli",
"maxGrpDayDDeltaLn": 100000.0,
"maxGrpDayDDeltaSh": 100000.0,
"maxGrpDayVegaLn": 100000.0,
"maxGrpDayVegaSh": 100000.0,
"orderDuration": -1,
"maxExposureSize": -1,
"spdrStageType": "None",
"progressExposeTime": 0,
"userData1": "Default",
"userData2": "EXAMPLE_STRATEGY",
"orderVolLimit": 0.24
}
}
def generate_distinct_ids():
"""Generate 10 distinct grouping codes and risk group IDs"""
grouping_codes = []
risk_group_ids = []
for i in range(10):
# Generate distinct grouping codes (incrementing the last segment)
grouping_code = f"2137-1312-3867-{5309 + i}"
grouping_codes.append(grouping_code)
# Generate distinct risk group IDs (incrementing the last segment)
risk_group_id = f"4124-3534-5867-{5309 + i}"
risk_group_ids.append(risk_group_id)
return grouping_codes, risk_group_ids
def create_order_payload(grouping_code, risk_group_id):
"""Create an order payload with specific grouping code and risk group ID"""
order_payload = base_order_payload.copy()
order_payload["message"] = base_order_payload["message"].copy()
order_payload["message"]["groupingCode"] = grouping_code
order_payload["message"]["riskGroupId"] = risk_group_id
return order_payload
def submit_order(order_payload, order_number):
"""Submit a single order"""
try:
headers = {
"Authorization": f"Bearer {authentication_key}",
"Content-Type": "application/json"
}
# Use postmsgs command with required parameters only
params = {
"cmd": "postmsgs",
"postaction": "I" # (I)nsert - required parameter
}
response = requests.post(rest_url, headers=headers, params=params, json=[order_payload])
response.raise_for_status()
print(f"Order {order_number} - Gateway response:", response.json())
return True
except requests.RequestException as e:
print(f"Order {order_number} - Submission failed:", e)
if hasattr(e, 'response') and e.response is not None:
print(f"Order {order_number} - Response status:", e.response.status_code)
print(f"Order {order_number} - Response text:", e.response.text)
return False
def submit_multiple_orders():
"""Submit 10 orders with distinct grouping codes and risk group IDs"""
grouping_codes, risk_group_ids = generate_distinct_ids()
successful_orders = 0
failed_orders = 0
orders_in_current_second = 0
second_start_time = time.time()
for i in range(10):
# Rate limiting: max 4 orders per second
current_time = time.time()
time_elapsed = current_time - second_start_time
if time_elapsed >= 3.0:
# Reset counter for new second
orders_in_current_second = 0
second_start_time = current_time
elif orders_in_current_second >= 2:
# Wait for next second if we've hit the limit
sleep_time = 1.0 - time_elapsed
print(f"Rate limit reached, waiting {sleep_time:.2f} seconds...")
time.sleep(sleep_time)
orders_in_current_second = 0
second_start_time = time.time()
print(f"\nSubmitting order {i + 1}/10...")
print(f"Grouping Code: {grouping_codes[i]}")
print(f"Risk Group ID: {risk_group_ids[i]}")
order_payload = create_order_payload(grouping_codes[i], risk_group_ids[i])
if submit_order(order_payload, i + 1):
successful_orders += 1
else:
failed_orders += 1
orders_in_current_second += 1
print(f"\n=== Summary ===")
print(f"Successful orders: {successful_orders}")
print(f"Failed orders: {failed_orders}")
if __name__ == "__main__":
submit_multiple_orders()
Step Two: Add order to parentLimit table for further updates.
Note:
When first sending to spdrParentLimit, the orderLimitType and orderActiveSize is changed.
# Base order payload for Replace
base_order_payload = {
"header": {
"mTyp": "SpdrParentLimit"
},
"message": {
"secKey": {
"at": "EQT",
"ts": "NMS",
"tk": "AAPL",
"dt": "2025-07-18",
"xx": 200,
"cp": "Put"
},
"positionType": "Auto",
"parentShape": "Single",
"secType": "Option",
"accnt": "T.MTL.VEN",
"orderSide": "Sell",
"groupingCode": "placeholder", # will be replaced
"clientFirm": "SRCORE",
"spdrActionType": "Replace",
"orderLimitType": "Vol",
"takeLimitClass": "SurfProb",
"makeLimitClass": "SurfProb",
"orderActiveSize": 27,
"orderVolLimit": 0.275,
"takeAlphaType": "Static",
"makeAlphaType": "Static",
"takeProbability": 0.1,
"makeProbability": 0.3,
"takeSurfVolOffset": 0.1,
"makeSurfVolOffset": 0.1,
"maxGrpDayDDeltaLn": 100000.0,
"maxGrpDayDDeltaSh": 100000.0,
"maxGrpDayVegaLn": 100000.0,
"maxGrpDayVegaSh": 100000.0,
"addCumFillQuantity": "No",
"spdrSource": "MLink"
}
}
def get_matching_grouping_codes():
"""Return groupingCodes used in the original insert orders"""
return [f"2137-1312-3867-{5309 + i}" for i in range(10)]
def create_order_payload(grouping_code):
"""Create a payload with updated grouping code"""
payload = copy.deepcopy(base_order_payload)
payload["message"]["groupingCode"] = grouping_code
return payload
def submit_order(payload, order_number):
"""Submit a single replace order"""
try:
headers = {
"Authorization": f"Bearer {authentication_key}",
"Content-Type": "application/json"
}
params = {
"cmd": "postmsgs",
"postaction": "I" # Insert action still used for Replace
}
response = requests.post(rest_url, headers=headers, params=params, json=[payload])
response.raise_for_status()
print(f"Order {order_number} - Gateway response:", response.json())
return True
except requests.RequestException as e:
print(f"Order {order_number} - Submission failed:", e)
if hasattr(e, 'response') and e.response is not None:
print("Response status:", e.response.status_code)
print("Response text:", e.response.text)
return False
def submit_replacement_orders():
"""Submit 10 SpdrParentLimit Replace orders"""
grouping_codes = get_matching_grouping_codes()
success_count = 0
failure_count = 0
for i, code in enumerate(grouping_codes, start=1):
print(f"\nSubmitting replacement order {i} with groupingCode: {code}")
payload = create_order_payload(code)
if submit_order(payload, i):
success_count += 1
else:
failure_count += 1
print("\n=== Summary ===")
print(f"Successful replacements: {success_count}")
print(f"Failed replacements: {failure_count}")
if __name__ == "__main__":
submit_replacement_orders()
Step Three: Update order params.
# Base order payload for Replace
base_order_payload = {
"header": {
"mTyp": "SpdrParentLimit"
},
"message": {
"secKey": {
"at": "EQT",
"ts": "NMS",
"tk": "AAPL",
"dt": "2025-07-18",
"xx": 200,
"cp": "Put"
},
"positionType": "Auto",
"parentShape": "Single",
"secType": "Option",
"accnt": "T.MTL.VEN",
"orderSide": "Sell",
"groupingCode": "placeholder", # will be replaced
"clientFirm": "SRCORE",
"spdrActionType": "Replace",
"orderLimitType": "Vol",
"takeLimitClass": "SurfProb",
"makeLimitClass": "SurfProb",
"orderActiveSize": 44,
"orderVolLimit": 0.25,
"takeAlphaType": "Static",
"makeAlphaType": "Static",
"takeProbability": 0.1,
"makeProbability": 0.3,
"takeSurfVolOffset": 0.1,
"makeSurfVolOffset": 0.1,
"maxGrpDayDDeltaLn": 100000.0,
"maxGrpDayDDeltaSh": 100000.0,
"maxGrpDayVegaLn": 100000.0,
"maxGrpDayVegaSh": 100000.0,
"addCumFillQuantity": "No",
"spdrSource": "MLink"
}
}
def get_matching_grouping_codes():
"""Return groupingCodes used in the original insert orders"""
return [f"2137-1312-3867-{5309 + i}" for i in range(10)]
def create_order_payload(grouping_code):
"""Create a payload with updated grouping code"""
payload = copy.deepcopy(base_order_payload)
payload["message"]["groupingCode"] = grouping_code
return payload
def submit_order(payload, order_number):
"""Submit a single replace order"""
try:
headers = {
"Authorization": f"Bearer {authentication_key}",
"Content-Type": "application/json"
}
params = {
"cmd": "postmsgs",
"postaction": "U" # Insert action still used for Replace
}
response = requests.post(rest_url, headers=headers, params=params, json=[payload])
response.raise_for_status()
print(f"Order {order_number} - Gateway response:", response.json())
return True
except requests.RequestException as e:
print(f"Order {order_number} - Submission failed:", e)
if hasattr(e, 'response') and e.response is not None:
print("Response status:", e.response.status_code)
print("Response text:", e.response.text)
return False
def submit_replacement_orders():
"""Submit 10 SpdrParentLimit Replace orders"""
grouping_codes = get_matching_grouping_codes()
success_count = 0
failure_count = 0
for i, code in enumerate(grouping_codes, start=1):
print(f"\nSubmitting replacement order {i} with groupingCode: {code}")
payload = create_order_payload(code)
if submit_order(payload, i):
success_count += 1
else:
failure_count += 1
print("\n=== Summary ===")
print(f"Successful replacements: {success_count}")
print(f"Failed replacements: {failure_count}")
if __name__ == "__main__":
submit_replacement_orders()
6. Initiating a Block Auction
This example demonstrates how to initiate a Block Auction using the MLink API.
Step 1: Configure and submit a SpdrParentOrder
Set up the order payload and submit a SpdrParentOrder to initiate a Block Auction. Please review the SpdrParentOrder table schema for more information.
import requests
import pandas as pd
import json
from datetime import datetime
from IPython.display import display
# REST endpoint and auth token
rest_url = "https://mlink-live.nms.saturn.spiderrockconnect.com/rest/json"
authentication_key = ""
# Global variable to store parentNumber across cells
parentNumber = None
order_payload = {
"header": {
"mTyp": "SpdrParentOrder"
},
"message": {
"spdrActionType": "AddReplace",
"parentShape": "Single",
"secKey": {
"at": "EQT",
"ts": "NMS",
"tk": "CAKE",
"dt": "2026-07-17",
"xx": 62.5,
"cp": "Call"
},
"secType": "Option",
"accnt": "T.MTL.VEN", # <--Insert your account here
"clientFirm": "SRCORE",
"spdrSource": "SpdrTicket",
"groupingCode": "4968-D169-C69A-8150",
"orderSide": "Sell",
"orderSize": 100,
"orderActiveSize": -1,
"progressRule": "AllowImmediate",
"atsVisibility": "SidePrice",
"cxlUPrcRange": "Yes",
"minUBid": -0.02,
"maxUAsk": 0.02,
"minMaxType": "Pct",
"marketSession": "RegMkt",
"activeDuration": 900,
"parentOrderHandling": "BlockAuction",
"orderLimitType": "Prc",
"takeReachRule": "AllOrNone",
"orderPrcLimit": 4.75,
"hedgeInstrument": "Default",
"hedgeSession": "RegMkt",
"positionType": "Closing",
"includeSRNetwork": "Include"
}
}
def submit_order():
global parentNumber # Declare we're using the global variable
try:
headers = {
"Authorization": f"Bearer {authentication_key}",
"Content-Type": "application/json"
}
params = {
"cmd": "postmsgs",
"postaction": "I",
"postmerge": "Y"
}
response = requests.post(rest_url, headers=headers, params=params, json=[order_payload])
response.raise_for_status()
gateway_response = response.json()
print("Gateway response:", gateway_response)
# Extract parentNumber (pkey) from the PostAck message
parentNumber = None
for msg in gateway_response:
if msg.get('header', {}).get('mTyp') == 'PostAck':
parentNumber = msg.get('message', {}).get('pkey')
break
if parentNumber:
print(f"Parent Number extracted: {parentNumber}")
else:
print("Warning: Could not find pkey in response")
return parentNumber
except requests.RequestException as e:
print("Submission failed:", e)
if hasattr(e, 'response') and e.response is not None:
print("Response status:", e.response.status_code)
print("Response text:", e.response.text)
return None
# Run the order submission
parentNumber = submit_order()
if parentNumber:
print(f"\nStored parentNumber: {parentNumber}")
print("You can now run the next cell to stream AuctionState data")
else:
print("\nNo parentNumber retrieved")
Gateway response:
[{'header': {'mTyp': 'PostAck'}, 'message': {'msgType': 4095, 'sendTs': 1770232662448841100, 'pkey': '02BD-B117-8025-5342', 'result': 'OK'}}, {'header': {'mTyp': 'QueryResult'}, 'message': {'numBytesSent': 130, 'numMessagesSent': 1, 'result': 'Ok', 'receiveTimestamp': '2026-02-04 19:17:42.439368', 'sendTimestamp': '2026-02-04 19:17:42.439368'}}]
Parent Number extracted: 02BD-B117-8025-5342
Stored parentNumber: 02BD-B117-8025-5342
You can now run the next cell to stream AuctionState data.
Step 2: Retrieve notice number and stream auction updates
Use the parentNumber to query the database for the noticeNumber, then subscribe to AuctionState messages to stream the auction and listen for responses.
import asyncio
import json
import time
import websockets
import nest_asyncio
import pandas as pd
from pandas import json_normalize
from IPython.display import display
import mysql.connector
nest_asyncio.apply()
uriJson = "wss://mlink-live.nms.saturn.spiderrockconnect.com/mlink/json"
authentication_key = ""
# === Database Parameters ===
ORDER_SQL = """
select noticeNumber from srtrade.msgsrparentbrkrstate where parentNumber = '{parentNumber}';
"""
def get_notice_numbers():
"""Retrieve notice numbers from database"""
global parentNumber # Access the global parentNumber
if parentNumber is None:
print("Error: parentNumber is not set. Please run the order submission cell first.")
return None
ORDER_SQL = f"""
select noticeNumber from srtrade.msgsrparentbrkrstate where parentNumber = '{parentNumber}';
"""
try:
cnx = mysql.connector.connect(
host="srse.saturn.srplatform.net",
port=,
user="",
password=""
)
cur = cnx.cursor()
cur.execute(ORDER_SQL)
results = cur.fetchall()
notice_numbers = [row[0] for row in results]
print(f"Retrieved {len(notice_numbers)} notice number(s) for parentNumber {parentNumber}: {notice_numbers}")
cur.close()
cnx.close()
return notice_numbers
except mysql.connector.Error as err:
print(f"Database error: {err}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
# Create DataFrame for streaming data
stream_df = pd.DataFrame(columns=[
"noticeNumber", "ticker","bidSol_numResponders", "bidSol_matchPrice", "bidSol_matchSize",
"askSol_numResponders", "askSol_matchPrice", "askSol_matchSize", "tradeDate", "isTestAuction", "auctionType",
"atsAuctionCounter", "atsAuctionStatus", "custSide", "custQty", "custPrc",
"hasCustPrc", "uBid", "uAsk", "nbboBid", "nbboAsk", "nbboBidSz", "nbboAskSz",
"surfacePrc", "surfaceVol", "timestamp"
])
async def recv_msg(websocket):
buffer = await websocket.recv()
result = json.loads(buffer)
if isinstance(result, dict):
if result.get("header", {}).get("mTyp") == "AuctionState":
msg = result.get("message", {})
# Extract ticker information
ticker_obj = msg.get("ticker", {})
ticker = ticker_obj.get("tk", "")
# Extract BidSolution data (first element if exists)
bid_solution = msg.get("BidSolution", [])
bid_sol_data = bid_solution[0] if bid_solution else {}
# Extract AskSolution data (first element if exists)
ask_solution = msg.get("AskSolution", [])
ask_sol_data = ask_solution[0] if ask_solution else {}
# Build record from AuctionState message
record = {
"noticeNumber": msg.get("pkey", {}).get("noticeNumber"),
"ticker": ticker,
"bidSol_numResponders": bid_sol_data.get("numResponders"),
"bidSol_matchPrice": bid_sol_data.get("matchPrice"),
"bidSol_matchSize": bid_sol_data.get("matchSize"),
"tradeDate": msg.get("tradeDate"),
"isTestAuction": msg.get("isTestAuction"),
"auctionType": msg.get("auctionType"),
"atsAuctionCounter": msg.get("atsAuctionCounter"),
"atsAuctionStatus": msg.get("atsAuctionStatus"),
"custSide": msg.get("custSide"),
"custQty": msg.get("custQty"),
"custPrc": msg.get("custPrc"),
"hasCustPrc": msg.get("hasCustPrc"),
"uBid": msg.get("uBid"),
"uAsk": msg.get("uAsk"),
"nbboBid": msg.get("nbboBid"),
"nbboAsk": msg.get("nbboAsk"),
#"nbboBidSz": msg.get("nbboBidSz"),
#"nbboAskSz": msg.get("nbboAskSz"),
"surfacePrc": msg.get("surfacePrc"),
"surfaceVol": msg.get("surfaceVol"),
"timestamp": msg.get("timestamp"),
# BidSolution fields
# AskSolution fields
"askSol_numResponders": ask_sol_data.get("numResponders"),
"askSol_matchPrice": ask_sol_data.get("matchPrice"),
"askSol_matchSize": ask_sol_data.get("matchSize")
}
# Clear previous output and display only the latest record
from IPython.display import clear_output
clear_output(wait=True)
print(f"Latest AuctionState Update - {record['timestamp']}")
display(pd.DataFrame([record]))
return True
async def send_signal(websocket):
while True:
await asyncio.sleep(20)
signal = {
"header": {
"mTyp": "MLinkSignalReady"
},
"message": {
"readyScan": "FullScan"
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
signal['header']['sTim'] = tstr
signal['header']['encT'] = tstr
smsg = json.dumps(signal)
await websocket.send(smsg)
await asyncio.sleep(0)
async def query_mlink(authentication_key, notice_numbers):
retry = True
while retry:
try:
async with websockets.connect(
uriJson,
additional_headers={"Authorization": f"Bearer {authentication_key}"},
ping_timeout=None
) as websocket:
# Build Subscribe list for each notice number
subscribe_list = [
{"msgName": "AuctionState", "msgPKey": notice_num}
for notice_num in notice_numbers
]
msg = {
"header": {
"mTyp": "MLinkSubscribe"
},
"message": {
"activeLatency": 1,
"doReset": "No",
"View": [{"msgName": "AuctionState", "view": "noticeNumber|BidSolution|tradeDate|auctionType|atsAuctionStatus|custSide|custQty|custPrc|uBid|uAsk|nbboBid|nbboAsk|surfacePrc|surfaceVol"}],
"Subscribe": subscribe_list
}
}
t = time.time_ns()
tstr = '.'.join([
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t / 1_000_000_000)),
"%06d" % ((t / 1_000) % 1_000_000)
])
msg['header']['sTim'] = tstr
msg['header']['encT'] = tstr
print(f"Subscribing to notice numbers: {notice_numbers}")
await websocket.send(json.dumps(msg))
asyncio.create_task(send_signal(websocket))
notDone = True
while notDone:
notDone = await recv_msg(websocket)
except asyncio.exceptions.TimeoutError:
print("Timeout occurred, retrying...")
if __name__ == "__main__":
# Step 1: Get notice numbers from database
notice_numbers = get_notice_numbers()
if notice_numbers:
# Step 2: Stream AuctionState messages for those notice numbers
print(f"Starting WebSocket subscription for notice numbers: {notice_numbers}")
asyncio.run(query_mlink(authentication_key, notice_numbers))
else:
print("No notice numbers retrieved. Exiting.")
7. Initiating a Flash Auction
This example demonstrates how to initiate a Flash Auction using the MLink API.
Send a SpdrParentOrder with parentOrderHandling: "FlashAuction" to initiate a Flash Auction. The MLink server processes the order and returns a parent number, which serves as the primary identifier for the parent order. Flash Auctions complete their trial match within 100ms of initiation and AuctionState messages are sent at the conclusion of the auction (unlike Block Auctions which send updates once per second).
Please review the SpdrParentOrder table schema for more information.
import requests
import pandas as pd
import json
from datetime import datetime
from IPython.display import display
# REST endpoint and auth token
rest_url = "https://mlink-live.nms.saturn.spiderrockconnect.com/rest/json"
authentication_key = ""
# Global variable to store parentNumber across cells
parentNumber = None
order_payload = {
"header": {
"mTyp": "SpdrParentOrder"
},
"message": {
"sysEnvironment": "Saturn",
"runStatus": "Prod",
"spdrActionType": "AddReplace",
"parentShape": "Single",
"secKey": {
"at": "EQT",
"ts": "NMS",
"tk": "CAKE",
"dt": "2026-07-17",
"xx": 65,
"cp": "Call"
},
"secType": "Option",
"accnt": "T.MTLV8", #<-- Insert your account here
"clientFirm": "SRCORE",
"groupingCode": "1235-E91C-5A3A-B08F",
"userName": "matt.leli",
"orderSide": "Sell",
"orderSize": 100,
"orderActiveSize": -1,
"progressRule": "AutoComplete",
"atsVisibility": "SidePrice",
"cxlUPrcRange": "Yes",
"minUBid": -0.02,
"maxUAsk": 0.02,
"minMaxType": "Pct",
"marketSession": "RegMkt",
"parentOrderHandling": "FlashAuction",
"orderLimitType": "Prc",
"takeReachRule": "UpToQty",
"orderPrcLimit": 4.25,
"hedgeInstrument": "Default",
"hedgeSession": "RegMkt",
"positionType": "Closing",
"includeSRNetwork": "Include"
}
}
def submit_order():
global parentNumber # Declare we're using the global variable
try:
headers = {
"Authorization": f"Bearer {authentication_key}",
"Content-Type": "application/json"
}
params = {
"cmd": "postmsgs",
"postaction": "I",
"postmerge": "Y"
}
response = requests.post(rest_url, headers=headers, params=params, json=[order_payload])
response.raise_for_status()
gateway_response = response.json()
print("Gateway response:", gateway_response)
# Extract parentNumber (pkey) from the PostAck message
parentNumber = None
for msg in gateway_response:
if msg.get('header', {}).get('mTyp') == 'PostAck':
parentNumber = msg.get('message', {}).get('pkey')
break
if parentNumber:
print(f"Parent Number extracted: {parentNumber}")
else:
print("Warning: Could not find pkey in response")
return parentNumber
except requests.RequestException as e:
print("Submission failed:", e)
if hasattr(e, 'response') and e.response is not None:
print("Response status:", e.response.status_code)
print("Response text:", e.response.text)
return None
# Run the order submission
parentNumber = submit_order()
if parentNumber:
print(f"\nStored parentNumber: {parentNumber}")
print("You can now run the next cell to stream AuctionState data")
else:
print("\nNo parentNumber retrieved")
Gateway response:
[{'header': {'mTyp': 'PostAck'}, 'message': {'msgType': 4095, 'sendTs': 1770324908966033700, 'pkey': '0A2B-691A-35EC-A56B', 'result': 'OK'}}, {'header': {'mTyp': 'QueryResult'}, 'message': {'numBytesSent': 130, 'numMessagesSent': 1, 'result': 'Ok', 'receiveTimestamp': '2026-02-05 20:55:08.965837', 'sendTimestamp': '2026-02-05 20:55:08.965837'}}]
Parent Number extracted: 0A2B-691A-35EC-A56B
Stored parentNumber: 0A2B-691A-35EC-A56B