187 lines
4.8 KiB
Python
187 lines
4.8 KiB
Python
import asyncio
|
|
import logging
|
|
from os import getenv
|
|
|
|
import orjson
|
|
import websockets
|
|
|
|
import db
|
|
|
|
# Logger setup
|
|
logging.basicConfig(level=logging.INFO)
|
|
log = logging.getLogger("RTS")
|
|
|
|
# Environment variables
|
|
MONOLITH_RTS_MEXC_API_ACCESS_KEY = getenv("MONOLITH_RTS_MEXC_API_ACCESS_KEY", None)
|
|
MONOLITH_RTS_MEXC_API_SECRET_KEY = getenv("MONOLITH_RTS_MEXC_API_SECRET_KEY", None)
|
|
|
|
# WebSocket endpoint
|
|
MEXC_WS_URL = "wss://wbs.mexc.com/ws"
|
|
|
|
{
|
|
"d": {
|
|
"e": "spot@public.kline.v3.api",
|
|
"k": {
|
|
"t": 1737901140, # TS
|
|
"o": "684.4", # Open
|
|
"c": "684.5", # Close
|
|
"h": "684.5", # High
|
|
"l": "684.4", # Low
|
|
"v": "0.173", # Volume of the base
|
|
"a": "118.41", # Volume of the quote (Quantity)
|
|
"T": 1737901200, # ?
|
|
"i": "Min1", # ?
|
|
},
|
|
},
|
|
"c": "spot@public.kline.v3.api@BNBUSDT@Min1", # Channel
|
|
"t": 1737901159239,
|
|
"s": "BNBUSDT", # Symbol
|
|
}
|
|
|
|
# Scan DB for last endtime (T)
|
|
# Request Kline data from last endtime (T) to now
|
|
|
|
|
|
# Check Server Time
|
|
|
|
# Response
|
|
|
|
# {
|
|
# "serverTime" : 1645539742000
|
|
# }
|
|
|
|
# GET /api/v3/time
|
|
|
|
# Weight(IP): 1
|
|
|
|
# Parameter:
|
|
|
|
# NONE
|
|
|
|
# Kline/Candlestick Data
|
|
|
|
# Response
|
|
|
|
# [
|
|
# [
|
|
# 1640804880000,
|
|
# "47482.36",
|
|
# "47482.36",
|
|
# "47416.57",
|
|
# "47436.1",
|
|
# "3.550717",
|
|
# 1640804940000,
|
|
# "168387.3"
|
|
# ]
|
|
# ]
|
|
|
|
# GET /api/v3/klines
|
|
|
|
# Weight(IP): 1
|
|
|
|
# Kline/candlestick bars for a symbol. Klines are uniquely identified by their open time.
|
|
|
|
# Parameters:
|
|
# Name Type Mandatory Description
|
|
# symbol string YES
|
|
# interval ENUM YES ENUM: Kline Interval
|
|
# startTime long NO
|
|
# endTime long NO
|
|
# limit integer NO Default 500; max 1000.
|
|
|
|
# Scrub function:
|
|
# For each record, ensure there are no time gaps
|
|
# When the 1m window goes over, the next t is always the last T.
|
|
# Check for gaps, and request all klines between those gaps to ensure a full DB, even with restarts.
|
|
|
|
|
|
# Idle jitter function - compare our time with server time.
|
|
# Compare ts with our time and print jitter. Add jitter warning to log and OHLC.
|
|
# High jitter may prevent us from getting the correct data for trading.
|
|
async def mex_handle(data):
|
|
message = orjson.loads(data)
|
|
# print(orjson.dumps(message, option=orjson.OPT_INDENT_2).decode("utf-8"))
|
|
if "code" in message:
|
|
if message["code"] == 0:
|
|
log.info("Control message received")
|
|
return
|
|
|
|
symbol = message["s"]
|
|
open = message["d"]["k"]["o"]
|
|
close = message["d"]["k"]["c"]
|
|
high = message["d"]["k"]["h"]
|
|
low = message["d"]["k"]["l"]
|
|
volume_base = message["d"]["k"]["v"] # ERROR IN API DOCS
|
|
volume_quote = message["d"]["k"]["a"] # > a bigDecimal volume
|
|
|
|
interval = message["d"]["k"]["i"]
|
|
|
|
start_time = message["d"]["k"]["t"] # > t long stratTime
|
|
end_time = message["d"]["k"]["T"] # > T long endTime
|
|
event_time = message["t"] # t long eventTime
|
|
|
|
index = f"mex_ohlc_{symbol.lower()}"
|
|
|
|
reformatted = {
|
|
"s": symbol,
|
|
"o": float(open),
|
|
"c": float(close),
|
|
"h": float(high),
|
|
"l": float(low),
|
|
"v": float(volume_base),
|
|
"a": float(volume_quote),
|
|
"i": interval,
|
|
"t": int(start_time),
|
|
"t2": int(end_time),
|
|
"ts": int(event_time),
|
|
}
|
|
|
|
await db.rts_store_message(index, reformatted)
|
|
print(index)
|
|
print(orjson.dumps(reformatted, option=orjson.OPT_INDENT_2).decode("utf-8"))
|
|
print()
|
|
|
|
|
|
# Kline WebSocket handler
|
|
async def mex_main():
|
|
await db.init_mysql_pool()
|
|
async with websockets.connect(MEXC_WS_URL) as websocket:
|
|
log.info("WebSocket connected")
|
|
|
|
# Define symbols and intervals
|
|
symbols = ["BTCUSDT"] # Add more symbols as needed
|
|
interval = "Min1" # Kline interval
|
|
|
|
# Prepare subscription requests for Kline streams
|
|
# Request: spot@public.kline.v3.api@<symbol>@<interval>
|
|
subscriptions = [
|
|
f"spot@public.kline.v3.api@{symbol}@{interval}" for symbol in symbols
|
|
]
|
|
|
|
# Send subscription requests
|
|
subscribe_request = {
|
|
"method": "SUBSCRIPTION",
|
|
"params": subscriptions,
|
|
# "id": 1,
|
|
}
|
|
await websocket.send(orjson.dumps(subscribe_request).decode("utf-8"))
|
|
|
|
log.info(f"Subscribed to: {subscriptions}")
|
|
|
|
# Listen for messages
|
|
while True:
|
|
try:
|
|
message = await websocket.recv()
|
|
await mex_handle(message)
|
|
except websockets.exceptions.ConnectionClosed as e:
|
|
log.error(f"WebSocket connection closed: {e}")
|
|
break
|
|
|
|
|
|
# Entry point
|
|
if __name__ == "__main__":
|
|
try:
|
|
asyncio.run(mex_main())
|
|
except KeyboardInterrupt:
|
|
log.info("RTS process terminated.")
|