feat QMT_API.py
This commit is contained in:
@@ -26,10 +26,9 @@ def safe_call(func, *args, **kwargs):
|
|||||||
except HTTPError:
|
except HTTPError:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("%s call failed", func.__name__)
|
|
||||||
raise HTTPError(
|
raise HTTPError(
|
||||||
502,
|
500,
|
||||||
reason="QMT upstream call failed: %s" % func.__name__,
|
reason="QMT: %s call failed." % func.__name__,
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
@@ -48,16 +47,13 @@ class BaseHandler(RequestHandler):
|
|||||||
if self.__class__ not in AUTH_EXEMPT:
|
if self.__class__ not in AUTH_EXEMPT:
|
||||||
token = self.request.headers.get('X-Token')
|
token = self.request.headers.get('X-Token')
|
||||||
if token != 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):
|
def set_default_headers(self):
|
||||||
self.set_header("Content-Type", "application/json; charset=utf-8")
|
self.set_header("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
def write_error(self, status_code, **kwargs):
|
def write_error(self, **kwargs):
|
||||||
self.finish(json.dumps({
|
self.finish(self._reason)
|
||||||
"error": self._reason,
|
|
||||||
"status_code": status_code
|
|
||||||
}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
def ctx(self):
|
def ctx(self):
|
||||||
return self.application.ContextInfo
|
return self.application.ContextInfo
|
||||||
@@ -67,6 +63,7 @@ class BaseHandler(RequestHandler):
|
|||||||
|
|
||||||
|
|
||||||
# ============= 1. ContextInfo properties =============
|
# ============= 1. ContextInfo properties =============
|
||||||
|
# "/api/v2/context/info"
|
||||||
class ContextInfoHandler(BaseHandler):
|
class ContextInfoHandler(BaseHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
ctx = self.ctx()
|
ctx = self.ctx()
|
||||||
@@ -80,38 +77,47 @@ class ContextInfoHandler(BaseHandler):
|
|||||||
"do_back_test": ctx.do_back_test,
|
"do_back_test": ctx.do_back_test,
|
||||||
"benchmark": ctx.benchmark,
|
"benchmark": ctx.benchmark,
|
||||||
"capital": ctx.capital,
|
"capital": ctx.capital,
|
||||||
|
"timetag":ctx.timetag,
|
||||||
"universe": ctx.get_universe(),
|
"universe": ctx.get_universe(),
|
||||||
}
|
}
|
||||||
self.write(data, separators=(',', ':'), ensure_ascii=False)
|
self.write(data, separators=(',', ':'), ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ============= 2. Data queries (ContextInfo get_*) =============
|
# ============= 2. Data queries (ContextInfo get_*) =============
|
||||||
# ContextInfo.get_stock_name() - Get a stock name by symbol
|
STOCK_HANDLER = {
|
||||||
class StockNameHandler(BaseHandler):
|
# stock_name|open_date|last_volume|total_share|svol|bvol
|
||||||
def post(self):
|
"stock_name": ("get_stock_name", "stock_code", True),
|
||||||
data = json.loads(self.request.body)
|
"open_date": ("get_open_date", "stock_code", True),
|
||||||
stockcode = data.get('stockcode', '')
|
"last_volume": ("get_last_volume", "stock_code", True),
|
||||||
ret = safe_call(self.ctx().get_stock_name, stockcode)
|
"total_share": ("get_total_share", "stock_code", True),
|
||||||
self.write(json.dumps({"stockcode": stockcode, "name": ret}, separators=(',', ':'), ensure_ascii=False))
|
"svol": ("get_svol", "stock_code", True),
|
||||||
|
"bvol": ("get_bvol", "stock_code", True),
|
||||||
# get_open_date() - Get the listing date by symbol
|
}
|
||||||
class OpenDateHandler(BaseHandler):
|
# "/api/v2/get/*" ContextInfo.* Get a stock name by symbol
|
||||||
def post(self):
|
class StockGetHandler(BaseHandler):
|
||||||
data = json.loads(self.request.body)
|
def get(self,handler_type):
|
||||||
stockcode = data.get('stockcode', '')
|
# 快速路径:配置查找
|
||||||
ret = safe_call(get_open_date, stockcode)
|
cfg = STOCK_HANDLER.get(handler_type)
|
||||||
self.write(json.dumps({"stockcode": stockcode, "open_date": ret}, separators=(',', ':'), ensure_ascii=False))
|
if not cfg:
|
||||||
|
self.write_error(500, reason="Unknown API")
|
||||||
# ContextInfo.get_last_volume() - Get the latest outstanding shares
|
return
|
||||||
class LastVolumeHandler(BaseHandler):
|
|
||||||
def post(self):
|
# 参数验证
|
||||||
data = json.loads(self.request.body)
|
|
||||||
stockcode = data.get('stockcode', '')
|
query_vals = self.get_query_argument(cfg.index[1], "").strip()
|
||||||
ret = safe_call(self.ctx().get_last_volume, stockcode)
|
if not query_vals:
|
||||||
if ret is None:
|
self.write_error(500, reason=f"{cfg.index[1]} required")
|
||||||
raise HTTPError(500, "Failed to get outstanding shares")
|
return
|
||||||
self.write(json.dumps({"stockcode": stockcode, "last_volume": ret}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
# 方法调用
|
||||||
|
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
|
# ContextInfo.get_bar_timetag() - Get the bar timestamp
|
||||||
class BarTimetagHandler(BaseHandler):
|
class BarTimetagHandler(BaseHandler):
|
||||||
@@ -121,12 +127,6 @@ class BarTimetagHandler(BaseHandler):
|
|||||||
ret = safe_call(self.ctx().get_bar_timetag, index)
|
ret = safe_call(self.ctx().get_bar_timetag, index)
|
||||||
self.write(json.dumps({"index": index, "timetag": ret}, separators=(',', ':'), ensure_ascii=False))
|
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
|
# ContextInfo.get_sector() - Get index constituents
|
||||||
class SectorHandler(BaseHandler):
|
class SectorHandler(BaseHandler):
|
||||||
def post(self):
|
def post(self):
|
||||||
@@ -285,14 +285,6 @@ class TimetagToDatetimeHandler(BaseHandler):
|
|||||||
ret = safe_call(timetag_to_datetime, timetag, fmt)
|
ret = safe_call(timetag_to_datetime, timetag, fmt)
|
||||||
self.write(json.dumps({"timetag": timetag, "datetime": ret}, separators=(',', ':'), ensure_ascii=False))
|
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
|
# ContextInfo.get_trading_dates() - Get the trading-day list
|
||||||
class TradingDatesHandler(BaseHandler):
|
class TradingDatesHandler(BaseHandler):
|
||||||
def post(self):
|
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)
|
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))
|
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
|
# ContextInfo.get_longhubang() - Get Dragon-Tiger List data
|
||||||
class LonghubangHandler(BaseHandler):
|
class LonghubangHandler(BaseHandler):
|
||||||
def post(self):
|
def post(self):
|
||||||
@@ -1272,6 +1248,9 @@ def make_app():
|
|||||||
# V2
|
# V2
|
||||||
(r"/api/v2/positions", HoldingHandler),
|
(r"/api/v2/positions", HoldingHandler),
|
||||||
(r"/api/v2/assets", AssetsHandler),
|
(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
|
# Legacy compatibility routes
|
||||||
(r"/api/holding", HoldingHandler),
|
(r"/api/holding", HoldingHandler),
|
||||||
@@ -1283,15 +1262,8 @@ def make_app():
|
|||||||
(r"/api/order/cancel_by_id", CancelByIdHandler),
|
(r"/api/order/cancel_by_id", CancelByIdHandler),
|
||||||
(r"/api/order/deal", DealHandler),
|
(r"/api/order/deal", DealHandler),
|
||||||
|
|
||||||
# ContextInfo properties
|
|
||||||
(r"/api/context/info", ContextInfoHandler),
|
|
||||||
|
|
||||||
# Data queries
|
# 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/bar_timetag", BarTimetagHandler),
|
||||||
(r"/api/data/tick_timetag", TickTimetagHandler),
|
|
||||||
(r"/api/data/sector", SectorHandler),
|
(r"/api/data/sector", SectorHandler),
|
||||||
(r"/api/data/industry", IndustryHandler),
|
(r"/api/data/industry", IndustryHandler),
|
||||||
(r"/api/data/stock_list_in_sector", StockListInSectorHandler),
|
(r"/api/data/stock_list_in_sector", StockListInSectorHandler),
|
||||||
@@ -1306,10 +1278,7 @@ def make_app():
|
|||||||
(r"/api/data/divid_factors", DividFactorsHandler),
|
(r"/api/data/divid_factors", DividFactorsHandler),
|
||||||
(r"/api/data/main_contract", MainContractHandler),
|
(r"/api/data/main_contract", MainContractHandler),
|
||||||
(r"/api/data/timetag_to_datetime", TimetagToDatetimeHandler),
|
(r"/api/data/timetag_to_datetime", TimetagToDatetimeHandler),
|
||||||
(r"/api/data/total_share", TotalShareHandler),
|
|
||||||
(r"/api/data/trading_dates", TradingDatesHandler),
|
(r"/api/data/trading_dates", TradingDatesHandler),
|
||||||
(r"/api/data/svol", SvolHandler),
|
|
||||||
(r"/api/data/bvol", BvolHandler),
|
|
||||||
(r"/api/data/longhubang", LonghubangHandler),
|
(r"/api/data/longhubang", LonghubangHandler),
|
||||||
(r"/api/data/top10_share_holder", Top10ShareHolderHandler),
|
(r"/api/data/top10_share_holder", Top10ShareHolderHandler),
|
||||||
(r"/api/data/option_detail", OptionDetailHandler),
|
(r"/api/data/option_detail", OptionDetailHandler),
|
||||||
|
|||||||
@@ -67,55 +67,23 @@ class BaseHandler(RequestHandler):
|
|||||||
|
|
||||||
|
|
||||||
# ============= 1. ContextInfo properties =============
|
# ============= 1. ContextInfo properties =============
|
||||||
# ContextInfo.period - Get the current period
|
class ContextInfoHandler(BaseHandler):
|
||||||
class ContextPeriodHandler(BaseHandler):
|
|
||||||
def get(self):
|
def get(self):
|
||||||
self.write(json.dumps({"period": self.ctx().period}, separators=(',', ':'), ensure_ascii=False))
|
ctx = self.ctx()
|
||||||
|
data = {
|
||||||
|
"period": ctx.period,
|
||||||
|
"barpos": ctx.barpos,
|
||||||
|
"time_tick_size": ctx.time_tick_size,
|
||||||
|
"stockcode": ctx.stockcode,
|
||||||
|
"dividend_type": ctx.dividend_type,
|
||||||
|
"market": ctx.market,
|
||||||
|
"do_back_test": ctx.do_back_test,
|
||||||
|
"benchmark": ctx.benchmark,
|
||||||
|
"capital": ctx.capital,
|
||||||
|
"universe": ctx.get_universe(),
|
||||||
|
}
|
||||||
|
self.write(data, separators=(',', ':'), ensure_ascii=False)
|
||||||
|
|
||||||
# ContextInfo.barpos - Get the current bar index
|
|
||||||
class ContextBarposHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"barpos": self.ctx().barpos}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.time_tick_size - Get the current bar count
|
|
||||||
class ContextTimeTickSizeHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"time_tick_size": self.ctx().time_tick_size}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.stockcode - Get the current chart symbol
|
|
||||||
class ContextStockCodeHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"stockcode": self.ctx().stockcode}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.dividend_type - Get the current adjustment mode
|
|
||||||
class ContextDividendTypeHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"dividend_type": self.ctx().dividend_type}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.market - Get the current chart market
|
|
||||||
class ContextMarketHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"market": self.ctx().market}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.do_back_test - Check whether backtesting is enabled
|
|
||||||
class ContextDoBackTestHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"do_back_test": self.ctx().do_back_test}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.benchmark - Get the backtest benchmark
|
|
||||||
class ContextBenchmarkHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"benchmark": self.ctx().benchmark}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.capital - Get the initial backtest capital
|
|
||||||
class ContextCapitalHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"capital": self.ctx().capital}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# ContextInfo.get_universe() - Get symbols in the universe
|
|
||||||
class ContextUniverseHandler(BaseHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write(json.dumps({"universe": self.ctx().get_universe()}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
|
|
||||||
# ============= 2. Data queries (ContextInfo get_*) =============
|
# ============= 2. Data queries (ContextInfo get_*) =============
|
||||||
@@ -966,7 +934,8 @@ class TradeDetailDataHandler(BaseHandler):
|
|||||||
ret = safe_call(get_trade_detail_data, self.acc(), account, datatype)
|
ret = safe_call(get_trade_detail_data, self.acc(), account, datatype)
|
||||||
if ret is None:
|
if ret is None:
|
||||||
ret = []
|
ret = []
|
||||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False))
|
result = [fixed_fields(obj) for obj in ret]
|
||||||
|
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||||
|
|
||||||
# get_value_by_order_id() - Get order or trade details by order ID
|
# get_value_by_order_id() - Get order or trade details by order ID
|
||||||
class ValueByOrderIdHandler(BaseHandler):
|
class ValueByOrderIdHandler(BaseHandler):
|
||||||
@@ -1112,7 +1081,28 @@ class HoldingHandler(BaseHandler):
|
|||||||
data = json.loads(self.request.body)
|
data = json.loads(self.request.body)
|
||||||
account = data.get('account', 'stock')
|
account = data.get('account', 'stock')
|
||||||
positions = safe_call(get_trade_detail_data, self.acc(), account, 'position') or []
|
positions = safe_call(get_trade_detail_data, self.acc(), account, 'position') or []
|
||||||
self.write(json.dumps({"data": positions}, separators=(',', ':'), ensure_ascii=False))
|
holding = {}
|
||||||
|
for position in positions:
|
||||||
|
stock = position.m_strInstrumentID + '.' + position.m_strExchangeID
|
||||||
|
holding[stock] = {
|
||||||
|
'StockCode': stock,
|
||||||
|
'StockName': position.m_strInstrumentName,
|
||||||
|
'Direction': position.m_nDirection,
|
||||||
|
'Volume': position.m_nVolume,
|
||||||
|
'OpenPrice': position.m_dOpenPrice,
|
||||||
|
'FloatProfit': position.m_dFloatProfit,
|
||||||
|
'MarketValue': position.m_dMarketValue,
|
||||||
|
'StockHolder': position.m_strStockHolder,
|
||||||
|
'FrozenVolume': position.m_nFrozenVolume,
|
||||||
|
'CanUseVolume': position.m_nCanUseVolume,
|
||||||
|
'OnRoadVolume': position.m_nOnRoadVolume,
|
||||||
|
'YesterdayVolume': position.m_nYesterdayVolume,
|
||||||
|
'LastPrice': position.m_dLastPrice,
|
||||||
|
'ProfitRate': position.m_dProfitRate,
|
||||||
|
'FutureTradeType': position.m_eFutureTradeType,
|
||||||
|
'ExpireDate': position.m_strExpireDate
|
||||||
|
}
|
||||||
|
self.write(json.dumps({"data": holding}, separators=(',', ':'), ensure_ascii=False))
|
||||||
|
|
||||||
# get_trade_detail_data('account') - Query account assets
|
# get_trade_detail_data('account') - Query account assets
|
||||||
class AssetsHandler(BaseHandler):
|
class AssetsHandler(BaseHandler):
|
||||||
@@ -1126,28 +1116,6 @@ class AssetsHandler(BaseHandler):
|
|||||||
self.write(json.dumps({"total": round(info.m_dBalance, 2),"available": round(info.m_dAvailable, 2)}, separators=(',', ':'), ensure_ascii=False))
|
self.write(json.dumps({"total": round(info.m_dBalance, 2),"available": round(info.m_dAvailable, 2)}, separators=(',', ':'), ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
# get_trade_detail_data('account') - Query total assets
|
|
||||||
class TotalMoneyHandler(BaseHandler):
|
|
||||||
def post(self):
|
|
||||||
data = json.loads(self.request.body)
|
|
||||||
account = data.get('account', 'stock')
|
|
||||||
_data = safe_call(get_trade_detail_data, self.acc(), account, 'account')
|
|
||||||
info = _data[0] if _data else None
|
|
||||||
if not info:
|
|
||||||
raise HTTPError(500, "Failed to get account data")
|
|
||||||
self.write(json.dumps({"total_money": round(info.m_dBalance, 2)}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# get_trade_detail_data('account') - Query available cash
|
|
||||||
class AvailableMoneyHandler(BaseHandler):
|
|
||||||
def post(self):
|
|
||||||
data = json.loads(self.request.body)
|
|
||||||
account = data.get('account', 'stock')
|
|
||||||
_data = safe_call(get_trade_detail_data, self.acc(), account, 'account')
|
|
||||||
info = _data[0] if _data else None
|
|
||||||
if not info:
|
|
||||||
raise HTTPError(500, "Failed to get account data")
|
|
||||||
self.write(json.dumps({"available_money": round(info.m_dAvailable, 2)}, separators=(',', ':'), ensure_ascii=False))
|
|
||||||
|
|
||||||
# passorder(23) - Simplified buy order wrapper
|
# passorder(23) - Simplified buy order wrapper
|
||||||
class BuyHandler(BaseHandler):
|
class BuyHandler(BaseHandler):
|
||||||
def post(self):
|
def post(self):
|
||||||
@@ -1307,8 +1275,6 @@ def make_app():
|
|||||||
|
|
||||||
# Legacy compatibility routes
|
# Legacy compatibility routes
|
||||||
(r"/api/holding", HoldingHandler),
|
(r"/api/holding", HoldingHandler),
|
||||||
(r"/api/money/total", TotalMoneyHandler),
|
|
||||||
(r"/api/money/available", AvailableMoneyHandler),
|
|
||||||
(r"/api/order/buy", BuyHandler),
|
(r"/api/order/buy", BuyHandler),
|
||||||
(r"/api/order/sell", SellHandler),
|
(r"/api/order/sell", SellHandler),
|
||||||
(r"/api/order/status", OrderStatusHandler),
|
(r"/api/order/status", OrderStatusHandler),
|
||||||
@@ -1318,16 +1284,7 @@ def make_app():
|
|||||||
(r"/api/order/deal", DealHandler),
|
(r"/api/order/deal", DealHandler),
|
||||||
|
|
||||||
# ContextInfo properties
|
# ContextInfo properties
|
||||||
(r"/api/context/period", ContextPeriodHandler),
|
(r"/api/context/info", ContextInfoHandler),
|
||||||
(r"/api/context/barpos", ContextBarposHandler),
|
|
||||||
(r"/api/context/time_tick_size", ContextTimeTickSizeHandler),
|
|
||||||
(r"/api/context/stockcode", ContextStockCodeHandler),
|
|
||||||
(r"/api/context/dividend_type", ContextDividendTypeHandler),
|
|
||||||
(r"/api/context/market", ContextMarketHandler),
|
|
||||||
(r"/api/context/do_back_test", ContextDoBackTestHandler),
|
|
||||||
(r"/api/context/benchmark", ContextBenchmarkHandler),
|
|
||||||
(r"/api/context/capital", ContextCapitalHandler),
|
|
||||||
(r"/api/context/universe", ContextUniverseHandler),
|
|
||||||
|
|
||||||
# Data queries
|
# Data queries
|
||||||
(r"/api/data/stock_name", StockNameHandler),
|
(r"/api/data/stock_name", StockNameHandler),
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,8 @@
|
|||||||
from .models import *
|
from .models import *
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
OP_BUY, OP_SELL = 23, 24
|
OP_BUY = 23
|
||||||
|
OP_SELL = 24
|
||||||
ORDER_TYPE_VOLUME, PR_TYPE_LATEST, QUICK_TRADE_NOW = 1101, 5, 2
|
ORDER_TYPE_VOLUME, PR_TYPE_LATEST, QUICK_TRADE_NOW = 1101, 5, 2
|
||||||
ORDER_SIDE_BY_OFFSET = {"23": "BUY", "24": "SELL", "48": "BUY", "49": "SELL"}
|
ORDER_SIDE_BY_OFFSET = {"23": "BUY", "24": "SELL", "48": "BUY", "49": "SELL"}
|
||||||
|
|
||||||
@@ -16,11 +17,11 @@ class TradeMixin:
|
|||||||
return self._post("/api/trade/passorder", body)
|
return self._post("/api/trade/passorder", body)
|
||||||
|
|
||||||
def passorder_latest(self, side, stock, volume): return self.passorder_latest_tagged(side, stock, volume, "", "")
|
def passorder_latest(self, side, stock, volume): return self.passorder_latest_tagged(side, stock, volume, "", "")
|
||||||
def passorder_latest_tagged(self, side, stock, volume, strategy_name, order_id):
|
def passorder_latest_tagged(self, side, stock_code, volume, strategy_name, order_id):
|
||||||
body = {
|
body = {
|
||||||
"opType": side,
|
"opType": side,
|
||||||
"orderType": ORDER_TYPE_VOLUME,
|
"orderType": ORDER_TYPE_VOLUME,
|
||||||
"stock": stock,
|
"stockCode": stock_code,
|
||||||
"prType": PR_TYPE_LATEST,
|
"prType": PR_TYPE_LATEST,
|
||||||
"price": -1,
|
"price": -1,
|
||||||
"volume": volume,
|
"volume": volume,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -108,7 +108,6 @@ def StartTrend() -> None:
|
|||||||
def RunOnce(run: Runtime, signals:list[SignalItem]) -> None:
|
def RunOnce(run: Runtime, signals:list[SignalItem]) -> None:
|
||||||
"""按固定步骤执行一轮趋势策略, ``RunOnce``。"""
|
"""按固定步骤执行一轮趋势策略, ``RunOnce``。"""
|
||||||
if not trading_time(datetime.now()):
|
if not trading_time(datetime.now()):
|
||||||
log.info("[运行] 非交易时间,跳过本轮")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
print("=" * 40 + f" Ticker {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} " +"=" * 40)
|
print("=" * 40 + f" Ticker {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} " +"=" * 40)
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ def do_open(run:Runtime,code:str,volume:int,signal_key:str)->None:
|
|||||||
run.client,
|
run.client,
|
||||||
OP_BUY,
|
OP_BUY,
|
||||||
code,
|
code,
|
||||||
|
-1,
|
||||||
volume,
|
volume,
|
||||||
order_id,
|
order_id,
|
||||||
signal_key,
|
signal_key,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class PlaceOrderRequest:
|
|||||||
client: Any
|
client: Any
|
||||||
op: int
|
op: int
|
||||||
code: str
|
code: str
|
||||||
|
price: float
|
||||||
volume: int
|
volume: int
|
||||||
order_id: str
|
order_id: str
|
||||||
strategy_name: str
|
strategy_name: str
|
||||||
|
|||||||
Reference in New Issue
Block a user