Add Alpaca dev adapter and option selection
This commit is contained in:
parent
a35ab0b778
commit
6dc279099f
8 changed files with 917 additions and 6 deletions
102
services/ingest-options/py/ibkr_stream.py
Normal file
102
services/ingest-options/py/ibkr_stream.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import argparse
|
||||
import json
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
from datetime import timezone
|
||||
|
||||
from ib_insync import IB, Option
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Stream IBKR option trades as JSON lines.")
|
||||
parser.add_argument("--host", required=True)
|
||||
parser.add_argument("--port", type=int, required=True)
|
||||
parser.add_argument("--client-id", type=int, required=True)
|
||||
parser.add_argument("--symbol", required=True)
|
||||
parser.add_argument("--expiry", required=True)
|
||||
parser.add_argument("--strike", type=float, required=True)
|
||||
parser.add_argument("--right", required=True)
|
||||
parser.add_argument("--exchange", required=True)
|
||||
parser.add_argument("--currency", required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
|
||||
ib = IB()
|
||||
|
||||
def shutdown(_signal: int, _frame) -> None:
|
||||
if ib.isConnected():
|
||||
ib.disconnect()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, shutdown)
|
||||
signal.signal(signal.SIGTERM, shutdown)
|
||||
|
||||
try:
|
||||
ib.connect(args.host, args.port, clientId=args.client_id)
|
||||
except Exception as exc:
|
||||
print(f"IBKR connection failed: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
contract = Option(
|
||||
args.symbol,
|
||||
args.expiry,
|
||||
args.strike,
|
||||
args.right,
|
||||
exchange=args.exchange,
|
||||
currency=args.currency,
|
||||
)
|
||||
|
||||
try:
|
||||
ib.qualifyContracts(contract)
|
||||
except Exception as exc:
|
||||
print(f"IBKR contract qualification failed: {exc}", file=sys.stderr)
|
||||
ib.disconnect()
|
||||
return 1
|
||||
|
||||
ticker = ib.reqMktData(contract, "", False, False)
|
||||
last_key = None
|
||||
|
||||
def on_update(updated_ticker) -> None:
|
||||
nonlocal last_key
|
||||
if updated_ticker.last is None or updated_ticker.lastSize is None:
|
||||
return
|
||||
|
||||
ts = updated_ticker.time
|
||||
if ts is None:
|
||||
ts_ms = int(time.time() * 1000)
|
||||
else:
|
||||
if ts.tzinfo is None:
|
||||
ts = ts.replace(tzinfo=timezone.utc)
|
||||
ts_ms = int(ts.timestamp() * 1000)
|
||||
|
||||
key = (ts_ms, updated_ticker.last, updated_ticker.lastSize)
|
||||
if key == last_key:
|
||||
return
|
||||
last_key = key
|
||||
|
||||
exchange = None
|
||||
if hasattr(updated_ticker, "lastExchange"):
|
||||
exchange = updated_ticker.lastExchange
|
||||
if not exchange:
|
||||
exchange = updated_ticker.exchange or "IBKR"
|
||||
|
||||
payload = {
|
||||
"ts": ts_ms,
|
||||
"price": float(updated_ticker.last),
|
||||
"size": int(updated_ticker.lastSize),
|
||||
"exchange": exchange,
|
||||
}
|
||||
|
||||
print(json.dumps(payload), flush=True)
|
||||
|
||||
ticker.updateEvent += on_update
|
||||
ib.run()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue