Begin implementing RTS
This commit is contained in:
193
rts.py
193
rts.py
@@ -1,31 +1,186 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from os import getenv
|
||||
|
||||
import uvloop
|
||||
import orjson
|
||||
import websockets
|
||||
|
||||
import util
|
||||
import db
|
||||
|
||||
# Use UVLoop
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
# Logger setup
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
log = logging.getLogger("RTS")
|
||||
|
||||
log = util.get_logger("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)
|
||||
|
||||
modules_enabled = getenv("MODULES_ENABLED", False)
|
||||
if "rts" not in modules_enabled:
|
||||
log.info("RTS disabled.")
|
||||
exit(0)
|
||||
# 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
|
||||
|
||||
|
||||
async def main(loop):
|
||||
log.info("RTS started.")
|
||||
# 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.
|
||||
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.create_task(main(loop))
|
||||
# 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
|
||||
|
||||
try:
|
||||
loop.run_forever()
|
||||
except KeyboardInterrupt:
|
||||
log.info("RTS process terminating")
|
||||
finally:
|
||||
loop.close()
|
||||
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.")
|
||||
|
||||
Reference in New Issue
Block a user