feat QMT_API.py
This commit is contained in:
@@ -26,10 +26,9 @@ def safe_call(func, *args, **kwargs):
|
||||
except HTTPError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.exception("%s call failed", func.__name__)
|
||||
raise HTTPError(
|
||||
502,
|
||||
reason="QMT upstream call failed: %s" % func.__name__,
|
||||
500,
|
||||
reason="QMT: %s call failed." % func.__name__,
|
||||
) from e
|
||||
|
||||
|
||||
@@ -48,16 +47,13 @@ class BaseHandler(RequestHandler):
|
||||
if self.__class__ not in AUTH_EXEMPT:
|
||||
token = self.request.headers.get('X-Token')
|
||||
if token != TOKEN:
|
||||
raise HTTPError(401, "Authentication failed: invalid or missing token")
|
||||
raise HTTPError(500, "Authentication failed: invalid or missing token")
|
||||
|
||||
def set_default_headers(self):
|
||||
self.set_header("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
def write_error(self, status_code, **kwargs):
|
||||
self.finish(json.dumps({
|
||||
"error": self._reason,
|
||||
"status_code": status_code
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
def write_error(self, **kwargs):
|
||||
self.finish(self._reason)
|
||||
|
||||
def ctx(self):
|
||||
return self.application.ContextInfo
|
||||
@@ -67,6 +63,7 @@ class BaseHandler(RequestHandler):
|
||||
|
||||
|
||||
# ============= 1. ContextInfo properties =============
|
||||
# "/api/v2/context/info"
|
||||
class ContextInfoHandler(BaseHandler):
|
||||
def get(self):
|
||||
ctx = self.ctx()
|
||||
@@ -80,38 +77,47 @@ class ContextInfoHandler(BaseHandler):
|
||||
"do_back_test": ctx.do_back_test,
|
||||
"benchmark": ctx.benchmark,
|
||||
"capital": ctx.capital,
|
||||
"timetag":ctx.timetag,
|
||||
"universe": ctx.get_universe(),
|
||||
}
|
||||
self.write(data, separators=(',', ':'), ensure_ascii=False)
|
||||
|
||||
|
||||
|
||||
# ============= 2. Data queries (ContextInfo get_*) =============
|
||||
# ContextInfo.get_stock_name() - Get a stock name by symbol
|
||||
class StockNameHandler(BaseHandler):
|
||||
def post(self):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_stock_name, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "name": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_open_date() - Get the listing date by symbol
|
||||
class OpenDateHandler(BaseHandler):
|
||||
def post(self):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_open_date, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "open_date": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_last_volume() - Get the latest outstanding shares
|
||||
class LastVolumeHandler(BaseHandler):
|
||||
def post(self):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_last_volume, stockcode)
|
||||
if ret is None:
|
||||
raise HTTPError(500, "Failed to get outstanding shares")
|
||||
self.write(json.dumps({"stockcode": stockcode, "last_volume": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
STOCK_HANDLER = {
|
||||
# stock_name|open_date|last_volume|total_share|svol|bvol
|
||||
"stock_name": ("get_stock_name", "stock_code", True),
|
||||
"open_date": ("get_open_date", "stock_code", True),
|
||||
"last_volume": ("get_last_volume", "stock_code", True),
|
||||
"total_share": ("get_total_share", "stock_code", True),
|
||||
"svol": ("get_svol", "stock_code", True),
|
||||
"bvol": ("get_bvol", "stock_code", True),
|
||||
}
|
||||
# "/api/v2/get/*" ContextInfo.* Get a stock name by symbol
|
||||
class StockGetHandler(BaseHandler):
|
||||
def get(self,handler_type):
|
||||
# 快速路径:配置查找
|
||||
cfg = STOCK_HANDLER.get(handler_type)
|
||||
if not cfg:
|
||||
self.write_error(500, reason="Unknown API")
|
||||
return
|
||||
|
||||
# 参数验证
|
||||
|
||||
query_vals = self.get_query_argument(cfg.index[1], "").strip()
|
||||
if not query_vals:
|
||||
self.write_error(500, reason=f"{cfg.index[1]} required")
|
||||
return
|
||||
|
||||
# 方法调用
|
||||
method = getattr(self.ctx(), cfg.index[0])
|
||||
result = safe_call(method, query_vals)
|
||||
|
||||
|
||||
# 响应
|
||||
self.finish({
|
||||
"stock_code": query_vals,
|
||||
"ref": result
|
||||
})
|
||||
|
||||
# ContextInfo.get_bar_timetag() - Get the bar timestamp
|
||||
class BarTimetagHandler(BaseHandler):
|
||||
@@ -121,12 +127,6 @@ class BarTimetagHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_bar_timetag, index)
|
||||
self.write(json.dumps({"index": index, "timetag": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_tick_timetag() - Get the latest tick timestamp
|
||||
class TickTimetagHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().get_tick_timetag)
|
||||
self.write(json.dumps({"timetag": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_sector() - Get index constituents
|
||||
class SectorHandler(BaseHandler):
|
||||
def post(self):
|
||||
@@ -285,14 +285,6 @@ class TimetagToDatetimeHandler(BaseHandler):
|
||||
ret = safe_call(timetag_to_datetime, timetag, fmt)
|
||||
self.write(json.dumps({"timetag": timetag, "datetime": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_total_share() - Get total shares
|
||||
class TotalShareHandler(BaseHandler):
|
||||
def post(self):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_total_share, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "total_share": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_trading_dates() - Get the trading-day list
|
||||
class TradingDatesHandler(BaseHandler):
|
||||
def post(self):
|
||||
@@ -306,22 +298,6 @@ class TradingDatesHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_trading_dates, stockcode, start_date, end_date, count_int, period)
|
||||
self.write(json.dumps({"dates": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_svol() - Get sell-side volume
|
||||
class SvolHandler(BaseHandler):
|
||||
def post(self):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_svol, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "svol": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_bvol() - Get buy-side volume
|
||||
class BvolHandler(BaseHandler):
|
||||
def post(self):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_bvol, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "bvol": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_longhubang() - Get Dragon-Tiger List data
|
||||
class LonghubangHandler(BaseHandler):
|
||||
def post(self):
|
||||
@@ -1272,6 +1248,9 @@ def make_app():
|
||||
# V2
|
||||
(r"/api/v2/positions", HoldingHandler),
|
||||
(r"/api/v2/assets", AssetsHandler),
|
||||
# ContextInfo properties
|
||||
(r"/api/v2/context/info", ContextInfoHandler),
|
||||
(r"/api/v2/get/(stock_name|open_date|last_volume|total_share|svol|bvol)", StockGetHandler),
|
||||
|
||||
# Legacy compatibility routes
|
||||
(r"/api/holding", HoldingHandler),
|
||||
@@ -1283,15 +1262,8 @@ def make_app():
|
||||
(r"/api/order/cancel_by_id", CancelByIdHandler),
|
||||
(r"/api/order/deal", DealHandler),
|
||||
|
||||
# ContextInfo properties
|
||||
(r"/api/context/info", ContextInfoHandler),
|
||||
|
||||
# Data queries
|
||||
(r"/api/data/stock_name", StockNameHandler),
|
||||
(r"/api/data/open_date", OpenDateHandler),
|
||||
(r"/api/data/last_volume", LastVolumeHandler),
|
||||
(r"/api/data/bar_timetag", BarTimetagHandler),
|
||||
(r"/api/data/tick_timetag", TickTimetagHandler),
|
||||
(r"/api/data/sector", SectorHandler),
|
||||
(r"/api/data/industry", IndustryHandler),
|
||||
(r"/api/data/stock_list_in_sector", StockListInSectorHandler),
|
||||
@@ -1306,10 +1278,7 @@ def make_app():
|
||||
(r"/api/data/divid_factors", DividFactorsHandler),
|
||||
(r"/api/data/main_contract", MainContractHandler),
|
||||
(r"/api/data/timetag_to_datetime", TimetagToDatetimeHandler),
|
||||
(r"/api/data/total_share", TotalShareHandler),
|
||||
(r"/api/data/trading_dates", TradingDatesHandler),
|
||||
(r"/api/data/svol", SvolHandler),
|
||||
(r"/api/data/bvol", BvolHandler),
|
||||
(r"/api/data/longhubang", LonghubangHandler),
|
||||
(r"/api/data/top10_share_holder", Top10ShareHolderHandler),
|
||||
(r"/api/data/option_detail", OptionDetailHandler),
|
||||
|
||||
Reference in New Issue
Block a user