fix bug
This commit is contained in:
@@ -57,7 +57,7 @@ class BaseHandler(RequestHandler):
|
||||
self.finish(json.dumps({
|
||||
"error": self._reason,
|
||||
"status_code": status_code
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
def ctx(self):
|
||||
return self.application.ContextInfo
|
||||
@@ -67,55 +67,23 @@ class BaseHandler(RequestHandler):
|
||||
|
||||
|
||||
# ============= 1. ContextInfo properties =============
|
||||
# ContextInfo.period - Get the current period
|
||||
class ContextPeriodHandler(BaseHandler):
|
||||
class ContextInfoHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write(json.dumps({"period": self.ctx().period}, 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}, 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}, ensure_ascii=False))
|
||||
|
||||
# ContextInfo.stockcode - Get the current chart symbol
|
||||
class ContextStockCodeHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write(json.dumps({"stockcode": self.ctx().stockcode}, 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}, ensure_ascii=False))
|
||||
|
||||
# ContextInfo.market - Get the current chart market
|
||||
class ContextMarketHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write(json.dumps({"market": self.ctx().market}, 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}, ensure_ascii=False))
|
||||
|
||||
# ContextInfo.benchmark - Get the backtest benchmark
|
||||
class ContextBenchmarkHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write(json.dumps({"benchmark": self.ctx().benchmark}, ensure_ascii=False))
|
||||
|
||||
# ContextInfo.capital - Get the initial backtest capital
|
||||
class ContextCapitalHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write(json.dumps({"capital": self.ctx().capital}, 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()}, ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 2. Data queries (ContextInfo get_*) =============
|
||||
@@ -125,7 +93,7 @@ class StockNameHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "name": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_open_date() - Get the listing date by symbol
|
||||
class OpenDateHandler(BaseHandler):
|
||||
@@ -133,7 +101,7 @@ class OpenDateHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
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):
|
||||
@@ -143,7 +111,7 @@ class LastVolumeHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "last_volume": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_bar_timetag() - Get the bar timestamp
|
||||
class BarTimetagHandler(BaseHandler):
|
||||
@@ -151,13 +119,13 @@ class BarTimetagHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
index = int(data.get('index', -1))
|
||||
ret = safe_call(self.ctx().get_bar_timetag, index)
|
||||
self.write(json.dumps({"index": index, "timetag": ret}, 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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"timetag": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_sector() - Get index constituents
|
||||
class SectorHandler(BaseHandler):
|
||||
@@ -168,7 +136,7 @@ class SectorHandler(BaseHandler):
|
||||
if not sector:
|
||||
raise HTTPError(400, "need args sector")
|
||||
ret = safe_call(self.ctx().get_sector, sector, int(realtime) if realtime != '0' else 0)
|
||||
self.write(json.dumps({"sector": sector, "stocks": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"sector": sector, "stocks": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_industry() - Get industry constituents
|
||||
class IndustryHandler(BaseHandler):
|
||||
@@ -179,7 +147,7 @@ class IndustryHandler(BaseHandler):
|
||||
raise HTTPError(400, "need args industry")
|
||||
print(industry)
|
||||
ret = safe_call(self.ctx().get_industry, industry)
|
||||
self.write(json.dumps({"industry": industry, "stocks": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"industry": industry, "stocks": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_stock_list_in_sector() - Get sector constituents
|
||||
class StockListInSectorHandler(BaseHandler):
|
||||
@@ -189,7 +157,7 @@ class StockListInSectorHandler(BaseHandler):
|
||||
if not sectorname:
|
||||
raise HTTPError(400, "need args sectorname")
|
||||
ret = safe_call(self.ctx().get_stock_list_in_sector, sectorname)
|
||||
self.write(json.dumps({"sectorname": sectorname, "stocks": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"sectorname": sectorname, "stocks": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_weight_in_index() - Get weights in an index
|
||||
class WeightInIndexHandler(BaseHandler):
|
||||
@@ -198,7 +166,7 @@ class WeightInIndexHandler(BaseHandler):
|
||||
indexcode = data.get('indexcode', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_weight_in_index, indexcode, stockcode)
|
||||
self.write(json.dumps({"indexcode": indexcode, "stockcode": stockcode, "weight": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"indexcode": indexcode, "stockcode": stockcode, "weight": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_contract_multiplier() - Get the contract multiplier
|
||||
class ContractMultiplierHandler(BaseHandler):
|
||||
@@ -206,7 +174,7 @@ class ContractMultiplierHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
contractcode = data.get('contractcode', '')
|
||||
ret = safe_call(self.ctx().get_contract_multiplier, contractcode)
|
||||
self.write(json.dumps({"contractcode": contractcode, "multiplier": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"contractcode": contractcode, "multiplier": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_risk_free_rate() - Get the risk-free rate
|
||||
class RiskFreeRateHandler(BaseHandler):
|
||||
@@ -214,7 +182,7 @@ class RiskFreeRateHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
index = int(data.get('index', '-1'))
|
||||
ret = safe_call(self.ctx().get_risk_free_rate, index)
|
||||
self.write(json.dumps({"index": index, "risk_free_rate": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"index": index, "risk_free_rate": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_date_location() - Get the bar index for a date
|
||||
class DateLocationHandler(BaseHandler):
|
||||
@@ -222,7 +190,7 @@ class DateLocationHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
strdate = data.get('strdate', '')
|
||||
ret = safe_call(self.ctx().get_date_location, strdate)
|
||||
self.write(json.dumps({"strdate": strdate, "location": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"strdate": strdate, "location": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_history_data() - Get historical market data for multiple symbols
|
||||
class HistoryDataHandler(BaseHandler):
|
||||
@@ -234,7 +202,7 @@ class HistoryDataHandler(BaseHandler):
|
||||
dividend_type = int(data.get('dividend_type', '0'))
|
||||
skip_paused = data.get('skip_paused', 'true').lower() == 'true'
|
||||
ret = safe_call(self.ctx().get_history_data, length, period, field, dividend_type, skip_paused)
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get historical data"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get historical data"}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_market_data() - Get market data as a DataFrame
|
||||
class MarketDataHandler(BaseHandler):
|
||||
@@ -254,7 +222,7 @@ class MarketDataHandler(BaseHandler):
|
||||
raise HTTPError(500, "Failed to get market data")
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_market_data_ex() - Get extended Level 2 market data
|
||||
class MarketDataExHandler(BaseHandler):
|
||||
@@ -278,7 +246,7 @@ class MarketDataExHandler(BaseHandler):
|
||||
result[k] = v.to_dict()
|
||||
else:
|
||||
result[k] = str(v)
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_full_tick() - Get full tick data
|
||||
class FullTickHandler(BaseHandler):
|
||||
@@ -290,7 +258,7 @@ class FullTickHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_full_tick, stocks)
|
||||
if not ret:
|
||||
raise HTTPError(500, "Failed to get tick data")
|
||||
self.write(json.dumps(ret, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps(ret, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_divid_factors() - Get dividend and adjustment factors
|
||||
class DividFactorsHandler(BaseHandler):
|
||||
@@ -298,7 +266,7 @@ class DividFactorsHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_divid_factors, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "factors": ret or {}}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "factors": ret or {}}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_main_contract() - Get the main futures contract
|
||||
class MainContractHandler(BaseHandler):
|
||||
@@ -306,7 +274,7 @@ class MainContractHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
codemarket = data.get('codemarket', '')
|
||||
ret = safe_call(self.ctx().get_main_contract, codemarket)
|
||||
self.write(json.dumps({"codemarket": codemarket, "main_contract": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"codemarket": codemarket, "main_contract": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# timetag_to_datetime() - Convert a millisecond timestamp to datetime
|
||||
class TimetagToDatetimeHandler(BaseHandler):
|
||||
@@ -315,7 +283,7 @@ class TimetagToDatetimeHandler(BaseHandler):
|
||||
timetag = int(data.get('timetag', '0'))
|
||||
fmt = data.get('format', '%Y-%m-%d %H:%M:%S')
|
||||
ret = safe_call(timetag_to_datetime, timetag, fmt)
|
||||
self.write(json.dumps({"timetag": timetag, "datetime": ret}, 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):
|
||||
@@ -323,7 +291,7 @@ class TotalShareHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
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):
|
||||
@@ -336,7 +304,7 @@ class TradingDatesHandler(BaseHandler):
|
||||
period = data.get('period', '1d')
|
||||
count_int = int(count) if count else -1
|
||||
ret = safe_call(self.ctx().get_trading_dates, stockcode, start_date, end_date, count_int, period)
|
||||
self.write(json.dumps({"dates": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"dates": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_svol() - Get sell-side volume
|
||||
class SvolHandler(BaseHandler):
|
||||
@@ -344,7 +312,7 @@ class SvolHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "svol": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_bvol() - Get buy-side volume
|
||||
class BvolHandler(BaseHandler):
|
||||
@@ -352,7 +320,7 @@ class BvolHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "bvol": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_longhubang() - Get Dragon-Tiger List data
|
||||
class LonghubangHandler(BaseHandler):
|
||||
@@ -365,7 +333,7 @@ class LonghubangHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_longhubang, slist, startTime, endTime)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get Dragon-Tiger List data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get Dragon-Tiger List data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_top10_share_holder() - Get top-ten shareholder data
|
||||
class Top10ShareHolderHandler(BaseHandler):
|
||||
@@ -379,7 +347,7 @@ class Top10ShareHolderHandler(BaseHandler):
|
||||
ret = safe_call(get_top10_share_holder, slist, data_name, start_time, end_time)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get top-ten shareholder data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get top-ten shareholder data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_option_detail_data() - Get option details
|
||||
class OptionDetailHandler(BaseHandler):
|
||||
@@ -387,7 +355,7 @@ class OptionDetailHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
optioncode = data.get('optioncode', '')
|
||||
ret = safe_call(self.ctx().get_option_detail_data, optioncode)
|
||||
self.write(json.dumps({"optioncode": optioncode, "detail": ret or {}}, ensure_ascii=False))
|
||||
self.write(json.dumps({"optioncode": optioncode, "detail": ret or {}}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_turnover_rate() - Get turnover rate
|
||||
class TurnoverRateHandler(BaseHandler):
|
||||
@@ -400,7 +368,7 @@ class TurnoverRateHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_turnover_rate, slist, startTime, endTime)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get turnover rate"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get turnover rate"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_etf_info() - Get ETF creation/redemption and constituent data
|
||||
class EtfInfoHandler(BaseHandler):
|
||||
@@ -408,7 +376,7 @@ class EtfInfoHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_etf_info, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "info": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"stockcode": stockcode, "info": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_etf_iopv() - Get the ETF indicative optimized portfolio value
|
||||
class EtfIopvHandler(BaseHandler):
|
||||
@@ -416,7 +384,7 @@ class EtfIopvHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_etf_iopv, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "iopv": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "iopv": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_instrumentdetail() - Get instrument details
|
||||
class InstrumentDetailHandler(BaseHandler):
|
||||
@@ -424,7 +392,7 @@ class InstrumentDetailHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_instrumentdetail, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "detail": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"stockcode": stockcode, "detail": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_contract_expire_date() - Get the futures contract expiration date
|
||||
class ContractExpireDateHandler(BaseHandler):
|
||||
@@ -432,7 +400,7 @@ class ContractExpireDateHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
codemarket = data.get('codemarket', '')
|
||||
ret = safe_call(self.ctx().get_contract_expire_date, codemarket)
|
||||
self.write(json.dumps({"codemarket": codemarket, "expire_date": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"codemarket": codemarket, "expire_date": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_option_undl_data() - Get option products for an underlying asset
|
||||
class OptionUndlDataHandler(BaseHandler):
|
||||
@@ -440,7 +408,7 @@ class OptionUndlDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
undl_code_ref = data.get('undl_code_ref', '')
|
||||
ret = safe_call(self.ctx().get_option_undl_data, undl_code_ref)
|
||||
self.write(json.dumps({"data": ret or []}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret or []}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_financial_data() - Get financial data
|
||||
class FinancialDataHandler(BaseHandler):
|
||||
@@ -465,7 +433,7 @@ class FinancialDataHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_financial_data, fields, stocks, start_date, end_date, rtype)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get financial data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get financial data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_factor_data() - Get multi-factor data
|
||||
class FactorDataHandler(BaseHandler):
|
||||
@@ -484,7 +452,7 @@ class FactorDataHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_factor_data, fields, stocks, start_date, end_date)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get factor data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get factor data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_his_st_data() - Get historical ST data
|
||||
class HisStDataHandler(BaseHandler):
|
||||
@@ -492,7 +460,7 @@ class HisStDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockCode = data.get('stockCode', '')
|
||||
ret = safe_call(self.ctx().get_his_st_data, stockCode)
|
||||
self.write(json.dumps({"stockCode": stockCode, "data": ret or {}}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockCode": stockCode, "data": ret or {}}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_his_index_data() - Get historical index data
|
||||
class HisIndexDataHandler(BaseHandler):
|
||||
@@ -500,13 +468,13 @@ class HisIndexDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
index = data.get('index', '')
|
||||
ret = safe_call(self.ctx().get_his_index_data, index)
|
||||
self.write(json.dumps({"index": index, "data": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"index": index, "data": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_all_subscription() - Get all current market-data subscriptions
|
||||
class AllSubscriptionHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().get_all_subscription)
|
||||
self.write(json.dumps({"subscriptions": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"subscriptions": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_option_list() - Get a specified option list
|
||||
class OptionListHandler(BaseHandler):
|
||||
@@ -517,7 +485,7 @@ class OptionListHandler(BaseHandler):
|
||||
opttype = data.get('opttype', '')
|
||||
isavailable = data.get('isavailable', 'true').lower() == 'true'
|
||||
ret = safe_call(self.ctx().get_option_list, undl_code, dedate, opttype, isavailable)
|
||||
self.write(json.dumps({"option_list": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"option_list": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_his_contract_list() - Get expired contracts
|
||||
class HisContractListHandler(BaseHandler):
|
||||
@@ -525,7 +493,7 @@ class HisContractListHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
market = data.get('market', '')
|
||||
ret = safe_call(self.ctx().get_his_contract_list, market)
|
||||
self.write(json.dumps({"market": market, "contracts": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"market": market, "contracts": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_option_iv() - Get real-time option implied volatility
|
||||
class OptionIvHandler(BaseHandler):
|
||||
@@ -533,7 +501,7 @@ class OptionIvHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
optioncode = data.get('optioncode', '')
|
||||
ret = safe_call(self.ctx().get_option_iv, optioncode)
|
||||
self.write(json.dumps({"optioncode": optioncode, "iv": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"optioncode": optioncode, "iv": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.bsm_price() - Calculate a European option price with the Black-Scholes model
|
||||
class BsmPriceHandler(BaseHandler):
|
||||
@@ -551,7 +519,7 @@ class BsmPriceHandler(BaseHandler):
|
||||
except ValueError:
|
||||
op = [float(x) for x in objectPrices.split(',')]
|
||||
ret = safe_call(self.ctx().bsm_price, optionType, op, strikePrice, riskFree, sigma, days, dividend)
|
||||
self.write(json.dumps({"price": ret}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"price": ret}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.bsm_iv() - Calculate European option implied volatility
|
||||
class BsmIvHandler(BaseHandler):
|
||||
@@ -565,7 +533,7 @@ class BsmIvHandler(BaseHandler):
|
||||
days = int(data.get('days', '0'))
|
||||
dividend = float(data.get('dividend', '0'))
|
||||
ret = safe_call(self.ctx().bsm_iv, optionType, objectPrices, strikePrice, optionPrice, riskFree, days, dividend)
|
||||
self.write(json.dumps({"iv": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"iv": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_local_data() - Get market data from local storage
|
||||
class LocalDataHandler(BaseHandler):
|
||||
@@ -580,7 +548,7 @@ class LocalDataHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_local_data, stock_code, start_time, end_time, period, divid_type, count)
|
||||
if ret is None:
|
||||
raise HTTPError(500, "Failed to get local market data")
|
||||
self.write(json.dumps({"data": ret}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.subscribe_quote() - Subscribe to market data
|
||||
class SubscribeQuoteHandler(BaseHandler):
|
||||
@@ -590,7 +558,7 @@ class SubscribeQuoteHandler(BaseHandler):
|
||||
period = data.get('period', 'follow')
|
||||
dividend_type = data.get('dividend_type', 'follow')
|
||||
ret = safe_call(self.ctx().subscribe_quote, stock_code, period, dividend_type)
|
||||
self.write(json.dumps({"status": "success" if ret is not None else "failed", "sub_id": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret is not None else "failed", "sub_id": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.unsubscribe_quote() - Unsubscribe from market data
|
||||
class UnsubscribeQuoteHandler(BaseHandler):
|
||||
@@ -598,7 +566,7 @@ class UnsubscribeQuoteHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sub_id = int(data.get('sub_id', '0'))
|
||||
safe_call(self.ctx().unsubscribe_quote, sub_id)
|
||||
self.write(json.dumps({"status": "success", "sub_id": sub_id}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "sub_id": sub_id}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 3. Predicate functions (is_*) =============
|
||||
@@ -606,13 +574,13 @@ class UnsubscribeQuoteHandler(BaseHandler):
|
||||
class IsLastBarHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().is_last_bar)
|
||||
self.write(json.dumps({"is_last_bar": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"is_last_bar": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.is_new_bar() - Check whether this is a new bar
|
||||
class IsNewBarHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().is_new_bar)
|
||||
self.write(json.dumps({"is_new_bar": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"is_new_bar": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.is_suspended_stock() - Check whether a stock is suspended
|
||||
class IsSuspendedStockHandler(BaseHandler):
|
||||
@@ -620,7 +588,7 @@ class IsSuspendedStockHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().is_suspended_stock, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "is_suspended": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "is_suspended": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# is_sector_stock() - Check whether a stock belongs to a sector
|
||||
class IsSectorStockHandler(BaseHandler):
|
||||
@@ -630,7 +598,7 @@ class IsSectorStockHandler(BaseHandler):
|
||||
market = data.get('market', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(is_sector_stock, sectorname, market, stockcode)
|
||||
self.write(json.dumps({"sectorname": sectorname, "stockcode": stockcode, "is_in_sector": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"sectorname": sectorname, "stockcode": stockcode, "is_in_sector": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# is_typed_stock() - Check whether a stock belongs to a category
|
||||
class IsTypedStockHandler(BaseHandler):
|
||||
@@ -640,7 +608,7 @@ class IsTypedStockHandler(BaseHandler):
|
||||
market = data.get('market', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(is_typed_stock, stocktypenum, market, stockcode)
|
||||
self.write(json.dumps({"stocktypenum": stocktypenum, "stockcode": stockcode, "result": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stocktypenum": stocktypenum, "stockcode": stockcode, "result": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_industry_name_of_stock() - Get a stock's industry classification
|
||||
class GetIndustryNameOfStockHandler(BaseHandler):
|
||||
@@ -649,7 +617,7 @@ class GetIndustryNameOfStockHandler(BaseHandler):
|
||||
industryType = data.get('industryType', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_industry_name_of_stock, industryType, stockcode)
|
||||
self.write(json.dumps({"industryType": industryType, "stockcode": stockcode, "industry_name": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"industryType": industryType, "stockcode": stockcode, "industry_name": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 4. Trading functions =============
|
||||
@@ -691,7 +659,7 @@ class PassorderHandler(BaseHandler):
|
||||
"strategy_name": strategy_name,
|
||||
"local_order_id": order_id,
|
||||
"order_ref": str(order_ref)
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# algo_passorder() - Submit an algorithmic order
|
||||
class AlgoPassorderHandler(BaseHandler):
|
||||
@@ -706,7 +674,7 @@ class AlgoPassorderHandler(BaseHandler):
|
||||
data.get('userOrderId', ''), data.get('userOrderParam', {}),
|
||||
self.ctx()
|
||||
)
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("algo_passorder failed")
|
||||
raise HTTPError(400, f"Algorithmic order submission failed: {str(e)}")
|
||||
@@ -725,7 +693,7 @@ class SmartAlgoPassorderHandler(BaseHandler):
|
||||
data.get('startTime', ''), data.get('endTime', ''),
|
||||
self.ctx()
|
||||
)
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("smart_algo_passorder failed")
|
||||
raise HTTPError(400, f"Smart algorithmic order submission failed: {str(e)}")
|
||||
@@ -737,7 +705,7 @@ class OrderLotsHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_lots(data['stock'], int(data['lots']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_lots", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_lots", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_lots failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -749,7 +717,7 @@ class OrderValueHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_value(data['stock'], float(data['value']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_value", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_value", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_value failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -761,7 +729,7 @@ class OrderPercentHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_percent(data['stock'], float(data['percent']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_percent", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_percent", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_percent failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -773,7 +741,7 @@ class OrderTargetValueHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_target_value(data['stock'], float(data['tar_value']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_value", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_value", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_target_value failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -785,7 +753,7 @@ class OrderTargetPercentHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_target_percent(data['stock'], float(data['tar_percent']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_percent", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_percent", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_target_percent failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -797,7 +765,7 @@ class OrderSharesHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_shares(data['stock'], int(data['shares']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_shares", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_shares", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_shares failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -811,7 +779,7 @@ class FuturesBuyOpenHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
buy_open(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_open", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_open", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("buy_open failed")
|
||||
raise HTTPError(400, f"Failed to open a long futures position: {str(e)}")
|
||||
@@ -823,7 +791,7 @@ class FuturesBuyCloseTdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
buy_close_tdayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_tdayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_tdayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("buy_close_tdayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a short futures position (today first): {str(e)}")
|
||||
@@ -835,7 +803,7 @@ class FuturesBuyCloseYdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
buy_close_ydayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_ydayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_ydayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("buy_close_ydayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a short futures position (yesterday first): {str(e)}")
|
||||
@@ -847,7 +815,7 @@ class FuturesSellOpenHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sell_open(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_open", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_open", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("sell_open failed")
|
||||
raise HTTPError(400, f"Failed to open a short futures position: {str(e)}")
|
||||
@@ -859,7 +827,7 @@ class FuturesSellCloseTdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sell_close_tdayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_tdayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_tdayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("sell_close_tdayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a long futures position (today first): {str(e)}")
|
||||
@@ -871,7 +839,7 @@ class FuturesSellCloseYdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sell_close_ydayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_ydayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_ydayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("sell_close_ydayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a long futures position (yesterday first): {str(e)}")
|
||||
@@ -886,7 +854,7 @@ class CancelTaskHandler(BaseHandler):
|
||||
taskId = data['taskId']
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = cancel_task(taskId, self.acc(), accountType, self.ctx())
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("cancel_task failed")
|
||||
raise HTTPError(400, f"Failed to cancel task: {str(e)}")
|
||||
@@ -899,7 +867,7 @@ class PauseTaskHandler(BaseHandler):
|
||||
taskId = data['taskId']
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = pause_task(taskId, self.acc(), accountType, self.ctx())
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("pause_task failed")
|
||||
raise HTTPError(400, f"Failed to pause task: {str(e)}")
|
||||
@@ -912,7 +880,7 @@ class ResumeTaskHandler(BaseHandler):
|
||||
taskId = data['taskId']
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = resume_task(taskId, self.acc(), accountType, self.ctx())
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("resume_task failed")
|
||||
raise HTTPError(400, f"Failed to resume task: {str(e)}")
|
||||
@@ -922,7 +890,7 @@ class DoOrderHandler(BaseHandler):
|
||||
def post(self):
|
||||
try:
|
||||
do_order(self.ctx())
|
||||
self.write(json.dumps({"status": "success", "message": "Signal triggered"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "message": "Signal triggered"}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("do_order failed")
|
||||
raise HTTPError(400, f"Failed to trigger signal: {str(e)}")
|
||||
@@ -966,8 +934,7 @@ class TradeDetailDataHandler(BaseHandler):
|
||||
ret = safe_call(get_trade_detail_data, self.acc(), account, datatype)
|
||||
if ret is None:
|
||||
ret = []
|
||||
result = [fixed_fields(obj) for obj in ret]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_value_by_order_id() - Get order or trade details by order ID
|
||||
class ValueByOrderIdHandler(BaseHandler):
|
||||
@@ -978,7 +945,7 @@ class ValueByOrderIdHandler(BaseHandler):
|
||||
datatype = data.get('datatype', 'ORDER')
|
||||
ret = safe_call(get_value_by_order_id, orderId, self.acc(), accountType, datatype)
|
||||
attrs = fixed_fields(ret) if ret else {}
|
||||
self.write(json.dumps({"orderId": orderId, "data": attrs}, ensure_ascii=False))
|
||||
self.write(json.dumps({"orderId": orderId, "data": attrs}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_last_order_id() - Get the latest order ID
|
||||
class LastOrderIdHandler(BaseHandler):
|
||||
@@ -987,7 +954,7 @@ class LastOrderIdHandler(BaseHandler):
|
||||
account = data.get('account', 'stock')
|
||||
datatype = data.get('datatype', 'ORDER')
|
||||
ret = safe_call(get_last_order_id, self.acc(), account, datatype)
|
||||
self.write(json.dumps({"last_order_id": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"last_order_id": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# can_cancel_order() - Check whether an order can be canceled
|
||||
class CanCancelOrderHandler(BaseHandler):
|
||||
@@ -996,7 +963,7 @@ class CanCancelOrderHandler(BaseHandler):
|
||||
orderId = data.get('orderId', '')
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = safe_call(can_cancel_order, orderId, self.acc(), accountType)
|
||||
self.write(json.dumps({"orderId": orderId, "can_cancel": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"orderId": orderId, "can_cancel": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
class CancelByIdHandler(BaseHandler):
|
||||
@@ -1012,13 +979,13 @@ class CancelByIdHandler(BaseHandler):
|
||||
self.write(json.dumps({
|
||||
"status": "failed", "order_id": order_id,
|
||||
"message": "Order does not exist or cannot currently be canceled"
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
return
|
||||
result = safe_call(cancel, order_id, self.acc(), account_type, self.ctx())
|
||||
self.write(json.dumps({
|
||||
"status": "success" if result is not False else "failed",
|
||||
"order_id": order_id,
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_debt_contract() - Get margin financing and securities lending debt contracts
|
||||
class DebtContractHandler(BaseHandler):
|
||||
@@ -1027,7 +994,7 @@ class DebtContractHandler(BaseHandler):
|
||||
accId = data.get('accId', self.acc())
|
||||
ret = safe_call(get_debt_contract, accId)
|
||||
result = [fixed_fields(obj) for obj in (ret or [])]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_assure_contract() - Get margin collateral instruments
|
||||
class AssureContractHandler(BaseHandler):
|
||||
@@ -1036,7 +1003,7 @@ class AssureContractHandler(BaseHandler):
|
||||
accId = data.get('accId', self.acc())
|
||||
ret = safe_call(get_assure_contract, accId)
|
||||
result = [fixed_fields(obj) for obj in (ret or [])]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_enable_short_contract() - Get securities available for short selling
|
||||
class EnableShortContractHandler(BaseHandler):
|
||||
@@ -1045,7 +1012,7 @@ class EnableShortContractHandler(BaseHandler):
|
||||
accId = data.get('accId', self.acc())
|
||||
ret = safe_call(get_enable_short_contract, accId)
|
||||
result = [fixed_fields(obj) for obj in (ret or [])]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_ipo_data() - Get today's new stock and bond offerings
|
||||
class IpoDataHandler(BaseHandler):
|
||||
@@ -1053,7 +1020,7 @@ class IpoDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
typ = data.get('type', '')
|
||||
ret = safe_call(get_ipo_data, typ)
|
||||
self.write(json.dumps({"data": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_new_purchase_limit() - Get the new-share subscription quota
|
||||
class NewPurchaseLimitHandler(BaseHandler):
|
||||
@@ -1061,7 +1028,7 @@ class NewPurchaseLimitHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
accid = data.get('accid', self.acc())
|
||||
ret = safe_call(get_new_purchase_limit, accid)
|
||||
self.write(json.dumps({"data": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
|
||||
# ============= 8. Reference functions (ext_data) =============
|
||||
@@ -1073,7 +1040,7 @@ class ExtDataHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(ext_data, extdataname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "value": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "value": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ext_data_rank() - Get an extended-data ranking
|
||||
class ExtDataRankHandler(BaseHandler):
|
||||
@@ -1083,7 +1050,7 @@ class ExtDataRankHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(ext_data_rank, extdataname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "rank": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "rank": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_factor_value() - Get a factor value
|
||||
class GetFactorValueHandler(BaseHandler):
|
||||
@@ -1093,7 +1060,7 @@ class GetFactorValueHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(get_factor_value, factorname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "value": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "value": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_factor_rank() - Get a factor ranking
|
||||
class GetFactorRankHandler(BaseHandler):
|
||||
@@ -1103,7 +1070,7 @@ class GetFactorRankHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(get_factor_rank, factorname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "rank": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "rank": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 9. Legacy handlers (compatibility) =============
|
||||
@@ -1113,28 +1080,7 @@ class HoldingHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
account = data.get('account', 'stock')
|
||||
positions = safe_call(get_trade_detail_data, self.acc(), account, 'position') or []
|
||||
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(holding, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": positions}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_trade_detail_data('account') - Query account assets
|
||||
class AssetsHandler(BaseHandler):
|
||||
@@ -1145,7 +1091,7 @@ class AssetsHandler(BaseHandler):
|
||||
info = _data[0] if _data else None
|
||||
if not info:
|
||||
raise HTTPError(500, "Failed to get account data")
|
||||
self.write(json.dumps({"total": round(info.m_dBalance, 2),"available": round(info.m_dAvailable, 2)}, 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
|
||||
@@ -1157,7 +1103,7 @@ class TotalMoneyHandler(BaseHandler):
|
||||
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)}, ensure_ascii=False))
|
||||
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):
|
||||
@@ -1168,7 +1114,7 @@ class AvailableMoneyHandler(BaseHandler):
|
||||
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)}, ensure_ascii=False))
|
||||
self.write(json.dumps({"available_money": round(info.m_dAvailable, 2)}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# passorder(23) - Simplified buy order wrapper
|
||||
class BuyHandler(BaseHandler):
|
||||
@@ -1184,7 +1130,7 @@ class BuyHandler(BaseHandler):
|
||||
self.write(json.dumps({
|
||||
"status": "success", "action": "buy", "stock": stock,
|
||||
"order_ref": str(order_ref) if order_ref else "unknown"
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Buy order submission failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -1203,7 +1149,7 @@ class SellHandler(BaseHandler):
|
||||
self.write(json.dumps({
|
||||
"status": "success", "action": "sell", "stock": stock,
|
||||
"order_ref": str(order_ref) if order_ref else "unknown"
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Sell order submission failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -1223,7 +1169,7 @@ class OrderStatusHandler(BaseHandler):
|
||||
"volume_left": order.m_nVolumeTotal,
|
||||
"volume_traded": order.m_nVolumeTraded,
|
||||
})
|
||||
self.write(json.dumps({"orders": rets}, ensure_ascii=False))
|
||||
self.write(json.dumps({"orders": rets}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# cancel() - Cancel all orders
|
||||
class CancelAllHandler(BaseHandler):
|
||||
@@ -1246,7 +1192,7 @@ class CancelAllHandler(BaseHandler):
|
||||
"status": "success",
|
||||
"message": f"Submitted {len(canceled_list)} cancellation requests",
|
||||
"canceled_orders": canceled_list
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Cancel-all operation failed")
|
||||
raise HTTPError(500, f"Order cancellation failed: {str(e)}")
|
||||
@@ -1269,7 +1215,7 @@ class CancelByRuleHandler(BaseHandler):
|
||||
if order.m_nVolumeTotal + order.m_nVolumeTraded == cancel_volume and order_code == stock and can_cancel_order(order.m_strOrderSysID, self.acc(), account):
|
||||
target_orders.append(order)
|
||||
if not target_orders:
|
||||
self.write(json.dumps({"status": "failed", "message": "No matching active orders found"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "failed", "message": "No matching active orders found"}, separators=(',', ':'), ensure_ascii=False))
|
||||
return
|
||||
canceled_ids = []
|
||||
for t_order in target_orders:
|
||||
@@ -1279,7 +1225,7 @@ class CancelByRuleHandler(BaseHandler):
|
||||
"status": "success",
|
||||
"message": f"Matched {len(target_orders)} orders and submitted cancellation requests",
|
||||
"canceled_sys_ids": canceled_ids
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Rule-based cancellation failed")
|
||||
raise HTTPError(500, f"Order cancellation failed: {str(e)}")
|
||||
@@ -1299,13 +1245,13 @@ class PythonVersionHandler(BaseHandler):
|
||||
"serial": sys.version_info.serial,
|
||||
}
|
||||
}
|
||||
self.write(json.dumps(version_info, ensure_ascii=False))
|
||||
self.write(json.dumps(version_info, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# sys: Shut down the HTTP service
|
||||
class ShutdownHandler(BaseHandler):
|
||||
def post(self):
|
||||
logger.info("Shutdown request received; the server is stopping...")
|
||||
self.write(json.dumps({"status": "success", "message": "Server is shutting down..."}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "message": "Server is shutting down..."}, separators=(',', ':'), ensure_ascii=False))
|
||||
self.finish()
|
||||
IOLoop.current().add_callback(IOLoop.current().stop)
|
||||
|
||||
@@ -1317,7 +1263,7 @@ class DealHandler(BaseHandler):
|
||||
sn = data.get('sn','')
|
||||
deals = safe_call(get_trade_detail_data, self.acc(), account, 'deal', sn) or []
|
||||
rets = [fixed_fields(deal) for deal in deals]
|
||||
self.write(json.dumps({"deals": rets}, ensure_ascii=False))
|
||||
self.write(json.dumps({"deals": rets}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= Route registration =============
|
||||
@@ -1340,16 +1286,7 @@ def make_app():
|
||||
(r"/api/order/deal", DealHandler),
|
||||
|
||||
# ContextInfo properties
|
||||
(r"/api/context/period", ContextPeriodHandler),
|
||||
(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),
|
||||
(r"/api/context/info", ContextInfoHandler),
|
||||
|
||||
# Data queries
|
||||
(r"/api/data/stock_name", StockNameHandler),
|
||||
@@ -57,7 +57,7 @@ class BaseHandler(RequestHandler):
|
||||
self.finish(json.dumps({
|
||||
"error": self._reason,
|
||||
"status_code": status_code
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
def ctx(self):
|
||||
return self.application.ContextInfo
|
||||
@@ -70,52 +70,52 @@ class BaseHandler(RequestHandler):
|
||||
# ContextInfo.period - Get the current period
|
||||
class ContextPeriodHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write(json.dumps({"period": self.ctx().period}, ensure_ascii=False))
|
||||
self.write(json.dumps({"period": self.ctx().period}, 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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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}, ensure_ascii=False))
|
||||
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()}, ensure_ascii=False))
|
||||
self.write(json.dumps({"universe": self.ctx().get_universe()}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 2. Data queries (ContextInfo get_*) =============
|
||||
@@ -125,7 +125,7 @@ class StockNameHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "name": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_open_date() - Get the listing date by symbol
|
||||
class OpenDateHandler(BaseHandler):
|
||||
@@ -133,7 +133,7 @@ class OpenDateHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
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):
|
||||
@@ -143,7 +143,7 @@ class LastVolumeHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "last_volume": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_bar_timetag() - Get the bar timestamp
|
||||
class BarTimetagHandler(BaseHandler):
|
||||
@@ -151,13 +151,13 @@ class BarTimetagHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
index = int(data.get('index', -1))
|
||||
ret = safe_call(self.ctx().get_bar_timetag, index)
|
||||
self.write(json.dumps({"index": index, "timetag": ret}, 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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"timetag": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_sector() - Get index constituents
|
||||
class SectorHandler(BaseHandler):
|
||||
@@ -168,7 +168,7 @@ class SectorHandler(BaseHandler):
|
||||
if not sector:
|
||||
raise HTTPError(400, "need args sector")
|
||||
ret = safe_call(self.ctx().get_sector, sector, int(realtime) if realtime != '0' else 0)
|
||||
self.write(json.dumps({"sector": sector, "stocks": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"sector": sector, "stocks": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_industry() - Get industry constituents
|
||||
class IndustryHandler(BaseHandler):
|
||||
@@ -179,7 +179,7 @@ class IndustryHandler(BaseHandler):
|
||||
raise HTTPError(400, "need args industry")
|
||||
print(industry)
|
||||
ret = safe_call(self.ctx().get_industry, industry)
|
||||
self.write(json.dumps({"industry": industry, "stocks": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"industry": industry, "stocks": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_stock_list_in_sector() - Get sector constituents
|
||||
class StockListInSectorHandler(BaseHandler):
|
||||
@@ -189,7 +189,7 @@ class StockListInSectorHandler(BaseHandler):
|
||||
if not sectorname:
|
||||
raise HTTPError(400, "need args sectorname")
|
||||
ret = safe_call(self.ctx().get_stock_list_in_sector, sectorname)
|
||||
self.write(json.dumps({"sectorname": sectorname, "stocks": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"sectorname": sectorname, "stocks": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_weight_in_index() - Get weights in an index
|
||||
class WeightInIndexHandler(BaseHandler):
|
||||
@@ -198,7 +198,7 @@ class WeightInIndexHandler(BaseHandler):
|
||||
indexcode = data.get('indexcode', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_weight_in_index, indexcode, stockcode)
|
||||
self.write(json.dumps({"indexcode": indexcode, "stockcode": stockcode, "weight": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"indexcode": indexcode, "stockcode": stockcode, "weight": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_contract_multiplier() - Get the contract multiplier
|
||||
class ContractMultiplierHandler(BaseHandler):
|
||||
@@ -206,7 +206,7 @@ class ContractMultiplierHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
contractcode = data.get('contractcode', '')
|
||||
ret = safe_call(self.ctx().get_contract_multiplier, contractcode)
|
||||
self.write(json.dumps({"contractcode": contractcode, "multiplier": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"contractcode": contractcode, "multiplier": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_risk_free_rate() - Get the risk-free rate
|
||||
class RiskFreeRateHandler(BaseHandler):
|
||||
@@ -214,7 +214,7 @@ class RiskFreeRateHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
index = int(data.get('index', '-1'))
|
||||
ret = safe_call(self.ctx().get_risk_free_rate, index)
|
||||
self.write(json.dumps({"index": index, "risk_free_rate": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"index": index, "risk_free_rate": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_date_location() - Get the bar index for a date
|
||||
class DateLocationHandler(BaseHandler):
|
||||
@@ -222,7 +222,7 @@ class DateLocationHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
strdate = data.get('strdate', '')
|
||||
ret = safe_call(self.ctx().get_date_location, strdate)
|
||||
self.write(json.dumps({"strdate": strdate, "location": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"strdate": strdate, "location": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_history_data() - Get historical market data for multiple symbols
|
||||
class HistoryDataHandler(BaseHandler):
|
||||
@@ -234,7 +234,7 @@ class HistoryDataHandler(BaseHandler):
|
||||
dividend_type = int(data.get('dividend_type', '0'))
|
||||
skip_paused = data.get('skip_paused', 'true').lower() == 'true'
|
||||
ret = safe_call(self.ctx().get_history_data, length, period, field, dividend_type, skip_paused)
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get historical data"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get historical data"}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_market_data() - Get market data as a DataFrame
|
||||
class MarketDataHandler(BaseHandler):
|
||||
@@ -254,7 +254,7 @@ class MarketDataHandler(BaseHandler):
|
||||
raise HTTPError(500, "Failed to get market data")
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_market_data_ex() - Get extended Level 2 market data
|
||||
class MarketDataExHandler(BaseHandler):
|
||||
@@ -278,7 +278,7 @@ class MarketDataExHandler(BaseHandler):
|
||||
result[k] = v.to_dict()
|
||||
else:
|
||||
result[k] = str(v)
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_full_tick() - Get full tick data
|
||||
class FullTickHandler(BaseHandler):
|
||||
@@ -290,7 +290,7 @@ class FullTickHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_full_tick, stocks)
|
||||
if not ret:
|
||||
raise HTTPError(500, "Failed to get tick data")
|
||||
self.write(json.dumps(ret, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps(ret, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_divid_factors() - Get dividend and adjustment factors
|
||||
class DividFactorsHandler(BaseHandler):
|
||||
@@ -298,7 +298,7 @@ class DividFactorsHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_divid_factors, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "factors": ret or {}}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "factors": ret or {}}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_main_contract() - Get the main futures contract
|
||||
class MainContractHandler(BaseHandler):
|
||||
@@ -306,7 +306,7 @@ class MainContractHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
codemarket = data.get('codemarket', '')
|
||||
ret = safe_call(self.ctx().get_main_contract, codemarket)
|
||||
self.write(json.dumps({"codemarket": codemarket, "main_contract": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"codemarket": codemarket, "main_contract": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# timetag_to_datetime() - Convert a millisecond timestamp to datetime
|
||||
class TimetagToDatetimeHandler(BaseHandler):
|
||||
@@ -315,7 +315,7 @@ class TimetagToDatetimeHandler(BaseHandler):
|
||||
timetag = int(data.get('timetag', '0'))
|
||||
fmt = data.get('format', '%Y-%m-%d %H:%M:%S')
|
||||
ret = safe_call(timetag_to_datetime, timetag, fmt)
|
||||
self.write(json.dumps({"timetag": timetag, "datetime": ret}, 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):
|
||||
@@ -323,7 +323,7 @@ class TotalShareHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
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):
|
||||
@@ -336,7 +336,7 @@ class TradingDatesHandler(BaseHandler):
|
||||
period = data.get('period', '1d')
|
||||
count_int = int(count) if count else -1
|
||||
ret = safe_call(self.ctx().get_trading_dates, stockcode, start_date, end_date, count_int, period)
|
||||
self.write(json.dumps({"dates": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"dates": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_svol() - Get sell-side volume
|
||||
class SvolHandler(BaseHandler):
|
||||
@@ -344,7 +344,7 @@ class SvolHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "svol": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_bvol() - Get buy-side volume
|
||||
class BvolHandler(BaseHandler):
|
||||
@@ -352,7 +352,7 @@ class BvolHandler(BaseHandler):
|
||||
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}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "bvol": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_longhubang() - Get Dragon-Tiger List data
|
||||
class LonghubangHandler(BaseHandler):
|
||||
@@ -365,7 +365,7 @@ class LonghubangHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_longhubang, slist, startTime, endTime)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get Dragon-Tiger List data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get Dragon-Tiger List data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_top10_share_holder() - Get top-ten shareholder data
|
||||
class Top10ShareHolderHandler(BaseHandler):
|
||||
@@ -379,7 +379,7 @@ class Top10ShareHolderHandler(BaseHandler):
|
||||
ret = safe_call(get_top10_share_holder, slist, data_name, start_time, end_time)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get top-ten shareholder data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get top-ten shareholder data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_option_detail_data() - Get option details
|
||||
class OptionDetailHandler(BaseHandler):
|
||||
@@ -387,7 +387,7 @@ class OptionDetailHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
optioncode = data.get('optioncode', '')
|
||||
ret = safe_call(self.ctx().get_option_detail_data, optioncode)
|
||||
self.write(json.dumps({"optioncode": optioncode, "detail": ret or {}}, ensure_ascii=False))
|
||||
self.write(json.dumps({"optioncode": optioncode, "detail": ret or {}}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_turnover_rate() - Get turnover rate
|
||||
class TurnoverRateHandler(BaseHandler):
|
||||
@@ -400,7 +400,7 @@ class TurnoverRateHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_turnover_rate, slist, startTime, endTime)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get turnover rate"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret else {"error": "Failed to get turnover rate"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_etf_info() - Get ETF creation/redemption and constituent data
|
||||
class EtfInfoHandler(BaseHandler):
|
||||
@@ -408,7 +408,7 @@ class EtfInfoHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_etf_info, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "info": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"stockcode": stockcode, "info": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_etf_iopv() - Get the ETF indicative optimized portfolio value
|
||||
class EtfIopvHandler(BaseHandler):
|
||||
@@ -416,7 +416,7 @@ class EtfIopvHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_etf_iopv, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "iopv": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "iopv": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_instrumentdetail() - Get instrument details
|
||||
class InstrumentDetailHandler(BaseHandler):
|
||||
@@ -424,7 +424,7 @@ class InstrumentDetailHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().get_instrumentdetail, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "detail": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"stockcode": stockcode, "detail": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_contract_expire_date() - Get the futures contract expiration date
|
||||
class ContractExpireDateHandler(BaseHandler):
|
||||
@@ -432,7 +432,7 @@ class ContractExpireDateHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
codemarket = data.get('codemarket', '')
|
||||
ret = safe_call(self.ctx().get_contract_expire_date, codemarket)
|
||||
self.write(json.dumps({"codemarket": codemarket, "expire_date": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"codemarket": codemarket, "expire_date": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_option_undl_data() - Get option products for an underlying asset
|
||||
class OptionUndlDataHandler(BaseHandler):
|
||||
@@ -440,7 +440,7 @@ class OptionUndlDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
undl_code_ref = data.get('undl_code_ref', '')
|
||||
ret = safe_call(self.ctx().get_option_undl_data, undl_code_ref)
|
||||
self.write(json.dumps({"data": ret or []}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret or []}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_financial_data() - Get financial data
|
||||
class FinancialDataHandler(BaseHandler):
|
||||
@@ -465,7 +465,7 @@ class FinancialDataHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_financial_data, fields, stocks, start_date, end_date, rtype)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get financial data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get financial data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_factor_data() - Get multi-factor data
|
||||
class FactorDataHandler(BaseHandler):
|
||||
@@ -484,7 +484,7 @@ class FactorDataHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_factor_data, fields, stocks, start_date, end_date)
|
||||
if hasattr(ret, 'to_dict'):
|
||||
ret = ret.to_dict()
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get factor data"}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret} if ret is not None else {"error": "Failed to get factor data"}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_his_st_data() - Get historical ST data
|
||||
class HisStDataHandler(BaseHandler):
|
||||
@@ -492,7 +492,7 @@ class HisStDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockCode = data.get('stockCode', '')
|
||||
ret = safe_call(self.ctx().get_his_st_data, stockCode)
|
||||
self.write(json.dumps({"stockCode": stockCode, "data": ret or {}}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockCode": stockCode, "data": ret or {}}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_his_index_data() - Get historical index data
|
||||
class HisIndexDataHandler(BaseHandler):
|
||||
@@ -500,13 +500,13 @@ class HisIndexDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
index = data.get('index', '')
|
||||
ret = safe_call(self.ctx().get_his_index_data, index)
|
||||
self.write(json.dumps({"index": index, "data": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"index": index, "data": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_all_subscription() - Get all current market-data subscriptions
|
||||
class AllSubscriptionHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().get_all_subscription)
|
||||
self.write(json.dumps({"subscriptions": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"subscriptions": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.get_option_list() - Get a specified option list
|
||||
class OptionListHandler(BaseHandler):
|
||||
@@ -517,7 +517,7 @@ class OptionListHandler(BaseHandler):
|
||||
opttype = data.get('opttype', '')
|
||||
isavailable = data.get('isavailable', 'true').lower() == 'true'
|
||||
ret = safe_call(self.ctx().get_option_list, undl_code, dedate, opttype, isavailable)
|
||||
self.write(json.dumps({"option_list": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"option_list": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_his_contract_list() - Get expired contracts
|
||||
class HisContractListHandler(BaseHandler):
|
||||
@@ -525,7 +525,7 @@ class HisContractListHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
market = data.get('market', '')
|
||||
ret = safe_call(self.ctx().get_his_contract_list, market)
|
||||
self.write(json.dumps({"market": market, "contracts": ret or []}, ensure_ascii=False))
|
||||
self.write(json.dumps({"market": market, "contracts": ret or []}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_option_iv() - Get real-time option implied volatility
|
||||
class OptionIvHandler(BaseHandler):
|
||||
@@ -533,7 +533,7 @@ class OptionIvHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
optioncode = data.get('optioncode', '')
|
||||
ret = safe_call(self.ctx().get_option_iv, optioncode)
|
||||
self.write(json.dumps({"optioncode": optioncode, "iv": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"optioncode": optioncode, "iv": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.bsm_price() - Calculate a European option price with the Black-Scholes model
|
||||
class BsmPriceHandler(BaseHandler):
|
||||
@@ -551,7 +551,7 @@ class BsmPriceHandler(BaseHandler):
|
||||
except ValueError:
|
||||
op = [float(x) for x in objectPrices.split(',')]
|
||||
ret = safe_call(self.ctx().bsm_price, optionType, op, strikePrice, riskFree, sigma, days, dividend)
|
||||
self.write(json.dumps({"price": ret}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"price": ret}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.bsm_iv() - Calculate European option implied volatility
|
||||
class BsmIvHandler(BaseHandler):
|
||||
@@ -565,7 +565,7 @@ class BsmIvHandler(BaseHandler):
|
||||
days = int(data.get('days', '0'))
|
||||
dividend = float(data.get('dividend', '0'))
|
||||
ret = safe_call(self.ctx().bsm_iv, optionType, objectPrices, strikePrice, optionPrice, riskFree, days, dividend)
|
||||
self.write(json.dumps({"iv": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"iv": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.get_local_data() - Get market data from local storage
|
||||
class LocalDataHandler(BaseHandler):
|
||||
@@ -580,7 +580,7 @@ class LocalDataHandler(BaseHandler):
|
||||
ret = safe_call(self.ctx().get_local_data, stock_code, start_time, end_time, period, divid_type, count)
|
||||
if ret is None:
|
||||
raise HTTPError(500, "Failed to get local market data")
|
||||
self.write(json.dumps({"data": ret}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# ContextInfo.subscribe_quote() - Subscribe to market data
|
||||
class SubscribeQuoteHandler(BaseHandler):
|
||||
@@ -590,7 +590,7 @@ class SubscribeQuoteHandler(BaseHandler):
|
||||
period = data.get('period', 'follow')
|
||||
dividend_type = data.get('dividend_type', 'follow')
|
||||
ret = safe_call(self.ctx().subscribe_quote, stock_code, period, dividend_type)
|
||||
self.write(json.dumps({"status": "success" if ret is not None else "failed", "sub_id": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret is not None else "failed", "sub_id": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.unsubscribe_quote() - Unsubscribe from market data
|
||||
class UnsubscribeQuoteHandler(BaseHandler):
|
||||
@@ -598,7 +598,7 @@ class UnsubscribeQuoteHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sub_id = int(data.get('sub_id', '0'))
|
||||
safe_call(self.ctx().unsubscribe_quote, sub_id)
|
||||
self.write(json.dumps({"status": "success", "sub_id": sub_id}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "sub_id": sub_id}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 3. Predicate functions (is_*) =============
|
||||
@@ -606,13 +606,13 @@ class UnsubscribeQuoteHandler(BaseHandler):
|
||||
class IsLastBarHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().is_last_bar)
|
||||
self.write(json.dumps({"is_last_bar": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"is_last_bar": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.is_new_bar() - Check whether this is a new bar
|
||||
class IsNewBarHandler(BaseHandler):
|
||||
def get(self):
|
||||
ret = safe_call(self.ctx().is_new_bar)
|
||||
self.write(json.dumps({"is_new_bar": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"is_new_bar": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ContextInfo.is_suspended_stock() - Check whether a stock is suspended
|
||||
class IsSuspendedStockHandler(BaseHandler):
|
||||
@@ -620,7 +620,7 @@ class IsSuspendedStockHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(self.ctx().is_suspended_stock, stockcode)
|
||||
self.write(json.dumps({"stockcode": stockcode, "is_suspended": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stockcode": stockcode, "is_suspended": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# is_sector_stock() - Check whether a stock belongs to a sector
|
||||
class IsSectorStockHandler(BaseHandler):
|
||||
@@ -630,7 +630,7 @@ class IsSectorStockHandler(BaseHandler):
|
||||
market = data.get('market', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(is_sector_stock, sectorname, market, stockcode)
|
||||
self.write(json.dumps({"sectorname": sectorname, "stockcode": stockcode, "is_in_sector": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"sectorname": sectorname, "stockcode": stockcode, "is_in_sector": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# is_typed_stock() - Check whether a stock belongs to a category
|
||||
class IsTypedStockHandler(BaseHandler):
|
||||
@@ -640,7 +640,7 @@ class IsTypedStockHandler(BaseHandler):
|
||||
market = data.get('market', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(is_typed_stock, stocktypenum, market, stockcode)
|
||||
self.write(json.dumps({"stocktypenum": stocktypenum, "stockcode": stockcode, "result": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"stocktypenum": stocktypenum, "stockcode": stockcode, "result": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_industry_name_of_stock() - Get a stock's industry classification
|
||||
class GetIndustryNameOfStockHandler(BaseHandler):
|
||||
@@ -649,7 +649,7 @@ class GetIndustryNameOfStockHandler(BaseHandler):
|
||||
industryType = data.get('industryType', '')
|
||||
stockcode = data.get('stockcode', '')
|
||||
ret = safe_call(get_industry_name_of_stock, industryType, stockcode)
|
||||
self.write(json.dumps({"industryType": industryType, "stockcode": stockcode, "industry_name": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"industryType": industryType, "stockcode": stockcode, "industry_name": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 4. Trading functions =============
|
||||
@@ -691,7 +691,7 @@ class PassorderHandler(BaseHandler):
|
||||
"strategy_name": strategy_name,
|
||||
"local_order_id": order_id,
|
||||
"order_ref": str(order_ref)
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# algo_passorder() - Submit an algorithmic order
|
||||
class AlgoPassorderHandler(BaseHandler):
|
||||
@@ -706,7 +706,7 @@ class AlgoPassorderHandler(BaseHandler):
|
||||
data.get('userOrderId', ''), data.get('userOrderParam', {}),
|
||||
self.ctx()
|
||||
)
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("algo_passorder failed")
|
||||
raise HTTPError(400, f"Algorithmic order submission failed: {str(e)}")
|
||||
@@ -725,7 +725,7 @@ class SmartAlgoPassorderHandler(BaseHandler):
|
||||
data.get('startTime', ''), data.get('endTime', ''),
|
||||
self.ctx()
|
||||
)
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "order_ref": str(order_ref) if order_ref else "unknown"}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("smart_algo_passorder failed")
|
||||
raise HTTPError(400, f"Smart algorithmic order submission failed: {str(e)}")
|
||||
@@ -737,7 +737,7 @@ class OrderLotsHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_lots(data['stock'], int(data['lots']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_lots", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_lots", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_lots failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -749,7 +749,7 @@ class OrderValueHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_value(data['stock'], float(data['value']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_value", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_value", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_value failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -761,7 +761,7 @@ class OrderPercentHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_percent(data['stock'], float(data['percent']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_percent", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_percent", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_percent failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -773,7 +773,7 @@ class OrderTargetValueHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_target_value(data['stock'], float(data['tar_value']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_value", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_value", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_target_value failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -785,7 +785,7 @@ class OrderTargetPercentHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_target_percent(data['stock'], float(data['tar_percent']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_percent", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_target_percent", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_target_percent failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -797,7 +797,7 @@ class OrderSharesHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
order_shares(data['stock'], int(data['shares']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "order_shares", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "order_shares", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("order_shares failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -811,7 +811,7 @@ class FuturesBuyOpenHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
buy_open(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_open", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_open", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("buy_open failed")
|
||||
raise HTTPError(400, f"Failed to open a long futures position: {str(e)}")
|
||||
@@ -823,7 +823,7 @@ class FuturesBuyCloseTdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
buy_close_tdayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_tdayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_tdayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("buy_close_tdayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a short futures position (today first): {str(e)}")
|
||||
@@ -835,7 +835,7 @@ class FuturesBuyCloseYdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
buy_close_ydayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_ydayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "buy_close_ydayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("buy_close_ydayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a short futures position (yesterday first): {str(e)}")
|
||||
@@ -847,7 +847,7 @@ class FuturesSellOpenHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sell_open(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_open", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_open", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("sell_open failed")
|
||||
raise HTTPError(400, f"Failed to open a short futures position: {str(e)}")
|
||||
@@ -859,7 +859,7 @@ class FuturesSellCloseTdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sell_close_tdayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_tdayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_tdayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("sell_close_tdayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a long futures position (today first): {str(e)}")
|
||||
@@ -871,7 +871,7 @@ class FuturesSellCloseYdayFirstHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
sell_close_ydayfirst(data['stock'], int(data['amount']), data.get('style', 'LATEST'),
|
||||
float(data.get('price', 0)), self.ctx(), data.get('accId', self.acc()))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_ydayfirst", "stock": data['stock']}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "action": "sell_close_ydayfirst", "stock": data['stock']}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("sell_close_ydayfirst failed")
|
||||
raise HTTPError(400, f"Failed to close a long futures position (yesterday first): {str(e)}")
|
||||
@@ -886,7 +886,7 @@ class CancelTaskHandler(BaseHandler):
|
||||
taskId = data['taskId']
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = cancel_task(taskId, self.acc(), accountType, self.ctx())
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("cancel_task failed")
|
||||
raise HTTPError(400, f"Failed to cancel task: {str(e)}")
|
||||
@@ -899,7 +899,7 @@ class PauseTaskHandler(BaseHandler):
|
||||
taskId = data['taskId']
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = pause_task(taskId, self.acc(), accountType, self.ctx())
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("pause_task failed")
|
||||
raise HTTPError(400, f"Failed to pause task: {str(e)}")
|
||||
@@ -912,7 +912,7 @@ class ResumeTaskHandler(BaseHandler):
|
||||
taskId = data['taskId']
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = resume_task(taskId, self.acc(), accountType, self.ctx())
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success" if ret else "failed", "taskId": taskId}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("resume_task failed")
|
||||
raise HTTPError(400, f"Failed to resume task: {str(e)}")
|
||||
@@ -922,7 +922,7 @@ class DoOrderHandler(BaseHandler):
|
||||
def post(self):
|
||||
try:
|
||||
do_order(self.ctx())
|
||||
self.write(json.dumps({"status": "success", "message": "Signal triggered"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "message": "Signal triggered"}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("do_order failed")
|
||||
raise HTTPError(400, f"Failed to trigger signal: {str(e)}")
|
||||
@@ -966,7 +966,7 @@ class TradeDetailDataHandler(BaseHandler):
|
||||
ret = safe_call(get_trade_detail_data, self.acc(), account, datatype)
|
||||
if ret is None:
|
||||
ret = []
|
||||
self.write(json.dumps({"data": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_value_by_order_id() - Get order or trade details by order ID
|
||||
class ValueByOrderIdHandler(BaseHandler):
|
||||
@@ -977,7 +977,7 @@ class ValueByOrderIdHandler(BaseHandler):
|
||||
datatype = data.get('datatype', 'ORDER')
|
||||
ret = safe_call(get_value_by_order_id, orderId, self.acc(), accountType, datatype)
|
||||
attrs = fixed_fields(ret) if ret else {}
|
||||
self.write(json.dumps({"orderId": orderId, "data": attrs}, ensure_ascii=False))
|
||||
self.write(json.dumps({"orderId": orderId, "data": attrs}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_last_order_id() - Get the latest order ID
|
||||
class LastOrderIdHandler(BaseHandler):
|
||||
@@ -986,7 +986,7 @@ class LastOrderIdHandler(BaseHandler):
|
||||
account = data.get('account', 'stock')
|
||||
datatype = data.get('datatype', 'ORDER')
|
||||
ret = safe_call(get_last_order_id, self.acc(), account, datatype)
|
||||
self.write(json.dumps({"last_order_id": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"last_order_id": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# can_cancel_order() - Check whether an order can be canceled
|
||||
class CanCancelOrderHandler(BaseHandler):
|
||||
@@ -995,7 +995,7 @@ class CanCancelOrderHandler(BaseHandler):
|
||||
orderId = data.get('orderId', '')
|
||||
accountType = data.get('accountType', 'stock')
|
||||
ret = safe_call(can_cancel_order, orderId, self.acc(), accountType)
|
||||
self.write(json.dumps({"orderId": orderId, "can_cancel": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"orderId": orderId, "can_cancel": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
class CancelByIdHandler(BaseHandler):
|
||||
@@ -1011,13 +1011,13 @@ class CancelByIdHandler(BaseHandler):
|
||||
self.write(json.dumps({
|
||||
"status": "failed", "order_id": order_id,
|
||||
"message": "Order does not exist or cannot currently be canceled"
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
return
|
||||
result = safe_call(cancel, order_id, self.acc(), account_type, self.ctx())
|
||||
self.write(json.dumps({
|
||||
"status": "success" if result is not False else "failed",
|
||||
"order_id": order_id,
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_debt_contract() - Get margin financing and securities lending debt contracts
|
||||
class DebtContractHandler(BaseHandler):
|
||||
@@ -1026,7 +1026,7 @@ class DebtContractHandler(BaseHandler):
|
||||
accId = data.get('accId', self.acc())
|
||||
ret = safe_call(get_debt_contract, accId)
|
||||
result = [fixed_fields(obj) for obj in (ret or [])]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_assure_contract() - Get margin collateral instruments
|
||||
class AssureContractHandler(BaseHandler):
|
||||
@@ -1035,7 +1035,7 @@ class AssureContractHandler(BaseHandler):
|
||||
accId = data.get('accId', self.acc())
|
||||
ret = safe_call(get_assure_contract, accId)
|
||||
result = [fixed_fields(obj) for obj in (ret or [])]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_enable_short_contract() - Get securities available for short selling
|
||||
class EnableShortContractHandler(BaseHandler):
|
||||
@@ -1044,7 +1044,7 @@ class EnableShortContractHandler(BaseHandler):
|
||||
accId = data.get('accId', self.acc())
|
||||
ret = safe_call(get_enable_short_contract, accId)
|
||||
result = [fixed_fields(obj) for obj in (ret or [])]
|
||||
self.write(json.dumps({"data": result}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": result}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_ipo_data() - Get today's new stock and bond offerings
|
||||
class IpoDataHandler(BaseHandler):
|
||||
@@ -1052,7 +1052,7 @@ class IpoDataHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
typ = data.get('type', '')
|
||||
ret = safe_call(get_ipo_data, typ)
|
||||
self.write(json.dumps({"data": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
# get_new_purchase_limit() - Get the new-share subscription quota
|
||||
class NewPurchaseLimitHandler(BaseHandler):
|
||||
@@ -1060,7 +1060,7 @@ class NewPurchaseLimitHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
accid = data.get('accid', self.acc())
|
||||
ret = safe_call(get_new_purchase_limit, accid)
|
||||
self.write(json.dumps({"data": ret or {}}, ensure_ascii=False, default=str))
|
||||
self.write(json.dumps({"data": ret or {}}, separators=(',', ':'), ensure_ascii=False, default=str))
|
||||
|
||||
|
||||
# ============= 8. Reference functions (ext_data) =============
|
||||
@@ -1072,7 +1072,7 @@ class ExtDataHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(ext_data, extdataname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "value": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "value": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# ext_data_rank() - Get an extended-data ranking
|
||||
class ExtDataRankHandler(BaseHandler):
|
||||
@@ -1082,7 +1082,7 @@ class ExtDataRankHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(ext_data_rank, extdataname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "rank": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"extdataname": extdataname, "stockcode": stockcode, "rank": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_factor_value() - Get a factor value
|
||||
class GetFactorValueHandler(BaseHandler):
|
||||
@@ -1092,7 +1092,7 @@ class GetFactorValueHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(get_factor_value, factorname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "value": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "value": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_factor_rank() - Get a factor ranking
|
||||
class GetFactorRankHandler(BaseHandler):
|
||||
@@ -1102,7 +1102,7 @@ class GetFactorRankHandler(BaseHandler):
|
||||
stockcode = data.get('stockcode', '')
|
||||
deviation = int(data.get('deviation', '0'))
|
||||
ret = safe_call(get_factor_rank, factorname, stockcode, deviation, self.ctx())
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "rank": ret}, ensure_ascii=False))
|
||||
self.write(json.dumps({"factorname": factorname, "stockcode": stockcode, "rank": ret}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= 9. Legacy handlers (compatibility) =============
|
||||
@@ -1112,7 +1112,7 @@ class HoldingHandler(BaseHandler):
|
||||
data = json.loads(self.request.body)
|
||||
account = data.get('account', 'stock')
|
||||
positions = safe_call(get_trade_detail_data, self.acc(), account, 'position') or []
|
||||
self.write(json.dumps({"data": positions}, ensure_ascii=False))
|
||||
self.write(json.dumps({"data": positions}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# get_trade_detail_data('account') - Query account assets
|
||||
class AssetsHandler(BaseHandler):
|
||||
@@ -1123,7 +1123,7 @@ class AssetsHandler(BaseHandler):
|
||||
info = _data[0] if _data else None
|
||||
if not info:
|
||||
raise HTTPError(500, "Failed to get account data")
|
||||
self.write(json.dumps({"total": round(info.m_dBalance, 2),"available": round(info.m_dAvailable, 2)}, 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
|
||||
@@ -1135,7 +1135,7 @@ class TotalMoneyHandler(BaseHandler):
|
||||
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)}, ensure_ascii=False))
|
||||
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):
|
||||
@@ -1146,7 +1146,7 @@ class AvailableMoneyHandler(BaseHandler):
|
||||
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)}, ensure_ascii=False))
|
||||
self.write(json.dumps({"available_money": round(info.m_dAvailable, 2)}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# passorder(23) - Simplified buy order wrapper
|
||||
class BuyHandler(BaseHandler):
|
||||
@@ -1162,7 +1162,7 @@ class BuyHandler(BaseHandler):
|
||||
self.write(json.dumps({
|
||||
"status": "success", "action": "buy", "stock": stock,
|
||||
"order_ref": str(order_ref) if order_ref else "unknown"
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Buy order submission failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -1181,7 +1181,7 @@ class SellHandler(BaseHandler):
|
||||
self.write(json.dumps({
|
||||
"status": "success", "action": "sell", "stock": stock,
|
||||
"order_ref": str(order_ref) if order_ref else "unknown"
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Sell order submission failed")
|
||||
raise HTTPError(400, f"Order submission failed: {str(e)}")
|
||||
@@ -1201,7 +1201,7 @@ class OrderStatusHandler(BaseHandler):
|
||||
"volume_left": order.m_nVolumeTotal,
|
||||
"volume_traded": order.m_nVolumeTraded,
|
||||
})
|
||||
self.write(json.dumps({"orders": rets}, ensure_ascii=False))
|
||||
self.write(json.dumps({"orders": rets}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# cancel() - Cancel all orders
|
||||
class CancelAllHandler(BaseHandler):
|
||||
@@ -1224,7 +1224,7 @@ class CancelAllHandler(BaseHandler):
|
||||
"status": "success",
|
||||
"message": f"Submitted {len(canceled_list)} cancellation requests",
|
||||
"canceled_orders": canceled_list
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Cancel-all operation failed")
|
||||
raise HTTPError(500, f"Order cancellation failed: {str(e)}")
|
||||
@@ -1247,7 +1247,7 @@ class CancelByRuleHandler(BaseHandler):
|
||||
if order.m_nVolumeTotal + order.m_nVolumeTraded == cancel_volume and order_code == stock and can_cancel_order(order.m_strOrderSysID, self.acc(), account):
|
||||
target_orders.append(order)
|
||||
if not target_orders:
|
||||
self.write(json.dumps({"status": "failed", "message": "No matching active orders found"}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "failed", "message": "No matching active orders found"}, separators=(',', ':'), ensure_ascii=False))
|
||||
return
|
||||
canceled_ids = []
|
||||
for t_order in target_orders:
|
||||
@@ -1257,7 +1257,7 @@ class CancelByRuleHandler(BaseHandler):
|
||||
"status": "success",
|
||||
"message": f"Matched {len(target_orders)} orders and submitted cancellation requests",
|
||||
"canceled_sys_ids": canceled_ids
|
||||
}, ensure_ascii=False))
|
||||
}, separators=(',', ':'), ensure_ascii=False))
|
||||
except Exception as e:
|
||||
logger.exception("Rule-based cancellation failed")
|
||||
raise HTTPError(500, f"Order cancellation failed: {str(e)}")
|
||||
@@ -1277,13 +1277,13 @@ class PythonVersionHandler(BaseHandler):
|
||||
"serial": sys.version_info.serial,
|
||||
}
|
||||
}
|
||||
self.write(json.dumps(version_info, ensure_ascii=False))
|
||||
self.write(json.dumps(version_info, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
# sys: Shut down the HTTP service
|
||||
class ShutdownHandler(BaseHandler):
|
||||
def post(self):
|
||||
logger.info("Shutdown request received; the server is stopping...")
|
||||
self.write(json.dumps({"status": "success", "message": "Server is shutting down..."}, ensure_ascii=False))
|
||||
self.write(json.dumps({"status": "success", "message": "Server is shutting down..."}, separators=(',', ':'), ensure_ascii=False))
|
||||
self.finish()
|
||||
IOLoop.current().add_callback(IOLoop.current().stop)
|
||||
|
||||
@@ -1295,7 +1295,7 @@ class DealHandler(BaseHandler):
|
||||
sn = data.get('sn','')
|
||||
deals = safe_call(get_trade_detail_data, self.acc(), account, 'deal', sn) or []
|
||||
rets = [fixed_fields(deal) for deal in deals]
|
||||
self.write(json.dumps({"deals": rets}, ensure_ascii=False))
|
||||
self.write(json.dumps({"deals": rets}, separators=(',', ':'), ensure_ascii=False))
|
||||
|
||||
|
||||
# ============= Route registration =============
|
||||
9
buf.gen.yaml
Normal file
9
buf.gen.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: v2
|
||||
clean: true
|
||||
inputs:
|
||||
- directory: proto
|
||||
plugins:
|
||||
- remote: buf.build/protocolbuffers/python:v3.14.0
|
||||
out: api/gen
|
||||
- remote: buf.build/grpc/python:v1.62.1
|
||||
out: api/gen # 必须与上面保持同一输出目录
|
||||
61
grpc/qmt_grpc_new.py
Normal file
61
grpc/qmt_grpc_new.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# -*- coding: gbk -*-
|
||||
import grpc
|
||||
import qmt_service_pb2
|
||||
import qmt_service_pb2_grpc
|
||||
import time
|
||||
|
||||
class QmtServiceServicer(qmt_service_pb2_grpc.QmtServiceServicer):
|
||||
"""实现QMT服务(单线程版本)"""
|
||||
|
||||
def GetAsset(self, request, context):
|
||||
"""实现GetAsset方法"""
|
||||
print(f"收到查询请求,账户: {request.account_id}")
|
||||
|
||||
# 这里是你调用大QMT API获取数据的逻辑
|
||||
# 实际使用时,请替换为真实的xt_trader查询代码
|
||||
# 参考: asset = xt_trader.query_stock_asset(acc)
|
||||
|
||||
# 模拟数据
|
||||
total = 1000000.0
|
||||
cash = 500000.0
|
||||
market_val = 500000.0
|
||||
|
||||
# 模拟一些耗时操作(如查询数据库)
|
||||
# time.sleep(0.1) # 如果需要可以取消注释
|
||||
|
||||
# 返回响应
|
||||
return qmt_service_pb2.AssetResponse(
|
||||
total_asset=total,
|
||||
cash=cash,
|
||||
market_value=market_val
|
||||
)
|
||||
|
||||
def serve():
|
||||
"""启动gRPC服务(单线程)"""
|
||||
# 使用单线程服务器,通过设置maximum_concurrent_rpcs参数限制并发
|
||||
# 或者使用同步服务器,直接处理请求
|
||||
server = grpc.server()
|
||||
|
||||
# 注册服务
|
||||
qmt_service_pb2_grpc.add_QmtServiceServicer_to_server(
|
||||
QmtServiceServicer(),
|
||||
server
|
||||
)
|
||||
|
||||
# 监听端口
|
||||
server.add_insecure_port('[::]:58051')
|
||||
|
||||
# 启动服务器
|
||||
server.start()
|
||||
print("QMT gRPC 服务已启动(单线程模式),监听端口 58051...")
|
||||
print("所有请求将串行处理,不会并发执行")
|
||||
|
||||
# 保持服务运行
|
||||
try:
|
||||
server.wait_for_termination()
|
||||
except KeyboardInterrupt:
|
||||
print("\n服务已停止")
|
||||
server.stop(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
serve()
|
||||
19
grpc/qmt_service.proto
Normal file
19
grpc/qmt_service.proto
Normal file
@@ -0,0 +1,19 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// 定义服务
|
||||
service QmtService {
|
||||
// 查询账户资产
|
||||
rpc GetAsset (AssetRequest) returns (AssetResponse) {}
|
||||
}
|
||||
|
||||
// 请求消息
|
||||
message AssetRequest {
|
||||
string account_id = 1; // 账户ID
|
||||
}
|
||||
|
||||
// 响应消息
|
||||
message AssetResponse {
|
||||
double total_asset = 1; // 总资产
|
||||
double cash = 2; // 可用资金
|
||||
double market_value = 3; // 持仓市值
|
||||
}
|
||||
Reference in New Issue
Block a user