diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..09dcc78 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.11 diff --git a/main.py b/main.py new file mode 100644 index 0000000..dc140ca --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from real-trader!") + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7f19a01 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "real-trader" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.10.11" +dependencies = [ + "flask>=3.1.0", + "flask-limiter>=3.12", + "requests>=2.32.3", + "schedule>=1.2.2", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 81047af..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -Flask -Flask-Limiter -pywin32 -requests -schedule -pytesseract \ No newline at end of file diff --git a/src/config.py b/src/config.py index 511aefc..777391b 100644 --- a/src/config.py +++ b/src/config.py @@ -7,9 +7,9 @@ class Config: DEBUG = False # Trading hours - MARKET_OPEN_TIME = "09:00" + MARKET_OPEN_TIME = "09:20" MARKET_ACTIVE_TIME = "09:15" - MARKET_CLOSE_TIME = "15:30" + MARKET_CLOSE_TIME = "15:10" # Logging LOG_DIR = "logs" @@ -21,3 +21,7 @@ class Config: # API Rate limiting RATE_LIMIT_REQUESTS = 100 RATE_LIMIT_PERIOD = 60 # seconds + + # XtQuant 相关配置 + XT_ACCOUNT = os.getenv('XT_ACCOUNT', '80391818') + XT_PATH = os.getenv('XT_PATH', r'C:\\江海证券QMT实盘_交易\\userdata_mini') diff --git a/src/real_trader.py b/src/real_trader.py deleted file mode 100644 index fd8603e..0000000 --- a/src/real_trader.py +++ /dev/null @@ -1,99 +0,0 @@ -import easytrader -import time -import os - -""" -需要32位python -""" - - -class RealTrader: - def __init__(self): - self._ACCOUNT = os.environ.get("ACCOUNT") - self._PASSWORD = os.environ.get("PASSWORD") - self._exe_path = r"C:\\ths\\xiadan.exe" - pass - - def login(self): - print("准备登录:", self._ACCOUNT, self._PASSWORD) - # self.trader = easytrader.use("universal_client") - self.trader = easytrader.use("ths5.19") - # 使用修改版交易客户端时, 不会超时 - try: - self.trader.enable_type_keys_for_editor() - self.trader.prepare( - user=self._ACCOUNT, password=self._PASSWORD, exe_path=self._exe_path - ) - except Exception as e: - time.sleep(1) - - def logout(self): - self.trader.exit() - - def get_balance(self): - return self.trader.balance - - def get_positions(self): - return self.trader.position - - # 查询当日成交 - def get_today_trades(self): - return self.trader.today_trades - - # 查询当日委托 - def get_today_entrust(self): - return self.trader.today_entrusts - - # 刷新数据 - def refresh(self): - self.trader.refresh() - - def buy(self, code, price, amount): - return self.trader.buy(code, price, amount) - - def sell(self, code, price, amount): - return self.trader.sell(code, price, amount) - - def cancel(self, entrust_no): - return self.trader.cancel_entrust(entrust_no) - - -if __name__ == "__main__": - # 创建RealTrader实例 - trader = RealTrader() - - try: - # 测试登录 - print("正在登录...") - trader.login() - print("登录成功!") - - # 获取账户余额测试 - balance = trader.get_balance() - print("账户余额信息:", balance) - - # 获取持仓信息 - positions = trader.get_positions() - print("持仓信息:", positions) - - # 获取当日成交 - today_trades = trader.get_today_trades() - print("当日成交:", today_trades) - - # 获取当日委托 - today_entrust = trader.get_today_entrust() - print("当日委托:", today_entrust) - - # 刷新数据 - trader.refresh() - print("数据已刷新") - - except Exception as e: - print("发生错误:", str(e)) - # finally: - # # 确保退出登录 - # try: - # trader.logout() - # print("已安全退出!") - # except: - # pass diff --git a/src/trade_server.py b/src/trade_server.py index 0473760..471d1f2 100644 --- a/src/trade_server.py +++ b/src/trade_server.py @@ -1,7 +1,7 @@ import schedule import threading import time -from real_trader import RealTrader +from xt_trader import XtTrader from flask import Flask, request, abort, jsonify from flask_limiter import Limiter from flask_limiter.util import get_remote_address @@ -69,7 +69,7 @@ def run_pending_tasks(): # Run the task scheduler in a new thread threading.Thread(target=run_pending_tasks).start() -trader = RealTrader() +trader = XtTrader() trader.login() @@ -234,21 +234,6 @@ def get_today_entrust(): abort(500, description="Internal server error") -@app.route("/yu/refresh", methods=["GET"]) -def refresh(): - """Refresh the account.""" - logger.info("Received refresh request") - try: - trader.refresh() - logger.info("Account data refreshed successfully") - - response = {"success": True, "data": "Account data refreshed successfully."} - return jsonify(response), 200 - except Exception as e: - logger.error(f"Error processing refresh request: {str(e)}") - abort(500, description="Internal server error") - - if __name__ == "__main__": logger.info(f"Server starting on {Config.HOST}:{Config.PORT}") app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT) diff --git a/src/xt_trader.py b/src/xt_trader.py new file mode 100644 index 0000000..4843a8b --- /dev/null +++ b/src/xt_trader.py @@ -0,0 +1,179 @@ +import os +import random +import logging +from logging.handlers import TimedRotatingFileHandler +from config import Config +from xtquant.xttrader import XtQuantTrader +from xtquant.xttype import StockAccount +from xtquant import xtconstant + +# 日志配置 +LOG_DIR = "log" +if not os.path.exists(LOG_DIR): + os.makedirs(LOG_DIR) +log_path = os.path.join(LOG_DIR, "%Y-%m-%d.log") +logger = logging.getLogger("xt_trader") +logger.setLevel(logging.INFO) +handler = TimedRotatingFileHandler( + os.path.join(LOG_DIR, "xt_trader.log"), when="midnight", interval=1, backupCount=7, encoding="utf-8" +) +handler.suffix = "%Y-%m-%d" +formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +handler.setFormatter(formatter) +if not logger.handlers: + logger.addHandler(handler) + +class MyXtQuantTraderCallback: + def on_connected(self): + logger.info("连接成功") + def on_disconnected(self): + logger.warning("连接断开") + def on_account_status(self, status): + logger.info(f"账号状态: {status.account_id} {status.status}") + def on_stock_asset(self, asset): + logger.info(f"资金变动: {asset.account_id} {asset.cash} {asset.total_asset}") + def on_stock_order(self, order): + logger.info(f"委托回报: {order.stock_code} {order.order_status} {order.order_sysid}") + def on_stock_trade(self, trade): + logger.info(f"成交变动: {trade.account_id} {trade.stock_code} {trade.order_id}") + def on_stock_position(self, position): + logger.info(f"持仓变动: {position.stock_code} {position.volume}") + def on_order_error(self, order_error): + logger.error(f"委托失败: {order_error.order_id} {order_error.error_id} {order_error.error_msg}") + def on_cancel_error(self, cancel_error): + logger.error(f"撤单失败: {cancel_error.order_id} {cancel_error.error_id} {cancel_error.error_msg}") + def on_order_stock_async_response(self, response): + logger.info(f"异步下单反馈: {response.order_id}") + def on_cancel_order_stock_async_response(self, response): + logger.info(f"异步撤单反馈: {response.order_id}") + def on_smt_appointment_async_response(self, response): + logger.info(f"约券异步反馈: {response.seq}") + +class XtTrader: + def __init__(self): + self._ACCOUNT = Config.XT_ACCOUNT + self._PATH = Config.XT_PATH + self._SESSION_ID = random.randint(100000, 99999999) + self._account_type = os.environ.get("XT_ACCOUNT_TYPE", "STOCK") + self._strategy_name = os.environ.get("XT_STRATEGY_NAME", "xt_strategy") + self._remark = os.environ.get("XT_REMARK", "remark") + self._callback = MyXtQuantTraderCallback() + self.xt_trader = XtQuantTrader(self._PATH, self._SESSION_ID) + self.account = StockAccount(self._ACCOUNT, self._account_type) + self.xt_trader.register_callback(self._callback) + self.started = False + self.connected = False + self.subscribed = False + + def login(self): + if not self.started: + self.xt_trader.start() + self.started = True + if not self.connected: + result = self.xt_trader.connect() + self.connected = (result == 0) + if not self.subscribed: + result = self.xt_trader.subscribe(self.account) + self.subscribed = (result == 0) + return self.connected and self.subscribed + + def logout(self): + if self.started: + self.xt_trader.stop() + self.started = False + self.connected = False + self.subscribed = False + + def get_balance(self): + asset = self.xt_trader.query_stock_asset(self.account) + if asset: + return { + "account_id": asset.account_id, + "cash": asset.cash, + "frozen_cash": asset.frozen_cash, + "market_value": asset.market_value, + "total_asset": asset.total_asset + } + return None + + def get_positions(self): + positions = self.xt_trader.query_stock_positions(self.account) + if positions: + return [ + { + "account_id": p.account_id, + "stock_code": p.stock_code, + "volume": p.volume, + "can_use_volume": p.can_use_volume, + "open_price": p.open_price, + "market_value": p.market_value, + "frozen_volume": p.frozen_volume, + "on_road_volume": p.on_road_volume, + "yesterday_volume": p.yesterday_volume, + "avg_price": p.avg_price + } for p in positions + ] + return [] + + def get_today_trades(self): + trades = self.xt_trader.query_stock_trades(self.account) + if trades: + return [ + { + "account_id": t.account_id, + "stock_code": t.stock_code, + "order_id": t.order_id, + "traded_id": t.traded_id, + "traded_time": t.traded_time, + "traded_price": t.traded_price, + "traded_volume": t.traded_volume, + "traded_amount": t.traded_amount + } for t in trades + ] + return [] + + def get_today_entrust(self): + orders = self.xt_trader.query_stock_orders(self.account) + if orders: + return [ + { + "account_id": o.account_id, + "stock_code": o.stock_code, + "order_id": o.order_id, + "order_time": o.order_time, + "order_type": o.order_type, + "order_volume": o.order_volume, + "price_type": o.price_type, + "price": o.price, + "traded_volume": o.traded_volume, + "traded_price": o.traded_price, + "order_status": o.order_status, + "status_msg": o.status_msg + } for o in orders + ] + return [] + + def buy(self, code, price, amount): + order_id = self.xt_trader.order_stock( + self.account, code, xtconstant.STOCK_BUY, amount, xtconstant.FIX_PRICE, price, self._strategy_name, self._remark + ) + return {"order_id": order_id} + + def sell(self, code, price, amount): + order_id = self.xt_trader.order_stock( + self.account, code, xtconstant.STOCK_SELL, amount, xtconstant.FIX_PRICE, price, self._strategy_name, self._remark + ) + return {"order_id": order_id} + + def cancel(self, entrust_no): + # 撤单接口需要订单编号 + result = self.xt_trader.cancel_order_stock(self.account, int(entrust_no)) + return {"cancel_result": result} + +if __name__ == "__main__": + trader = XtTrader() + trader.login() + logger.info(f"账户余额: {trader.get_balance()}") + logger.info(f"持仓: {trader.get_positions()}") + logger.info(f"当日成交: {trader.get_today_trades()}") + logger.info(f"当日委托: {trader.get_today_entrust()}") diff --git a/src/xtquant/__init__.py b/src/xtquant/__init__.py new file mode 100644 index 0000000..c8554d3 --- /dev/null +++ b/src/xtquant/__init__.py @@ -0,0 +1,27 @@ +#coding: utf-8 + +__version__ = "xtquant" + + +def check_for_update(package_name): + import requests + from pkg_resources import get_distribution + # 获取当前安装的版本 + current_version = get_distribution(package_name).version + # 查询PyPI的API获取最新版本信息 + response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout = 10) + if response.status_code == 200: + latest_version = response.json()['info']['version'] + if current_version != latest_version: + print(f"xtquant{latest_version}已经发布,前往 http://dict.thinktrader.net/nativeApi/download_xtquant.html 查看更新说明\n") + else: + print("xtquant文档地址:http://dict.thinktrader.net/nativeApi/start_now.html") + else: + pass + + +try: + check_for_update("xtquant") +except: + pass + diff --git a/src/xtquant/config/MarketTime.ini b/src/xtquant/config/MarketTime.ini new file mode 100644 index 0000000..ff518dd --- /dev/null +++ b/src/xtquant/config/MarketTime.ini @@ -0,0 +1,185 @@ +[IF] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[SH] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= +bondsRepuropentime=093000,130000 +bondsRepuroclosetime=113000,153000 + +[SZ] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= +bondsRepuropentime=093000,130000 +bondsRepuroclosetime=113000,153000 + +[SHO] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[SZO] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[HGT] +tradetime=093000,120000,130000,161000 +opentime=093000,130000 +closetime=120000,161000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[SGT] +tradetime=093000,120000,130000,161000 +opentime=093000,130000 +closetime=120000,161000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[NEEQ] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[SF] +tradetime=-30000,23000,90000,101500,103000,113000,133000,150000 +opentime=090000,103000,133000 +closetime=101500,113000,150000 +nightopentime=210000,000000 +nightclosetime=235959,023000 +indexopentime= +indexclosetime= + +[DF] +tradetime=-30000,-10000,90000,101500,103000,113000,133000,150000 +opentime=090000,103000,133000 +closetime=101500,113000,150000 +nightopentime=210000 +nightclosetime=230000 +indexopentime= +indexclosetime= + +[ZF] +tradetime=-30000,-10000,90000,101500,103000,113000,133000,150000 +opentime=090000,103000,133000 +closetime=101500,113000,150000 +nightopentime=210000 +nightclosetime=230000 +indexopentime= +indexclosetime= + +[YSWP] +tradetime=-76000,23000,90000,113000,130000,150000 +opentime=090000 +closetime=155500 +nightopentime=164000,000000 +nightclosetime=235959,020000 +indexopentime=093000,130000 +indexclosetime=113000,150000 + +[INE] +tradetime=-30000,23000,90000,101500,103000,113000,133000,150000 +opentime=090000,103000,133000 +closetime=101500,113000,150000 +nightopentime=210000,000000 +nightclosetime=235959,023000 +indexopentime= +indexclosetime= + +[SI] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[BKZS] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= +futureindexopentime=090000,103000,133000 +futureindexclosetime=101500,113000,150000 + +[HK] +tradetime=093000,120000,130000,160000 +opentime=093000,130000 +closetime=120000,160000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[] +tradetime=000000,240000 +opentime=000000 +closetime=235959 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[WP] +tradetime=060000,290000 +opentime=060000 +closetime=290000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[GF] +tradetime=090000,101500,103000,113000,133000,150000 +opentime=090000,103000,133000 +closetime=101500,113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= + +[BJ] +tradetime=093000,113000,130000,150000 +opentime=093000,130000 +closetime=113000,150000 +nightopentime= +nightclosetime= +indexopentime= +indexclosetime= \ No newline at end of file diff --git a/src/xtquant/config/StockInfo.lua b/src/xtquant/config/StockInfo.lua new file mode 100644 index 0000000..67c6d7c --- /dev/null +++ b/src/xtquant/config/StockInfo.lua @@ -0,0 +1,7 @@ +--[[-------------------------------------------------------------- + Ʊ/ڻĻϢ + cnname : Ʊ/ڻ +----------------------------------------------------------------]] +StockBasicInfo = { + {label = "if1209", cnname = "ָ1209", blocks = {"ij"},}, +} \ No newline at end of file diff --git a/src/xtquant/config/captial_structure_1.ini b/src/xtquant/config/captial_structure_1.ini new file mode 100644 index 0000000..79a6870 --- /dev/null +++ b/src/xtquant/config/captial_structure_1.ini @@ -0,0 +1,7 @@ +股本表|CAPITALSTRUCTURE +变动日期|m_timetag|changeDate +公告日期|m_anntime|declareDate +总股本|total_capital|totalCapital +已上市流通A股|circulating_capital|floatCapitalA +其他流通股份|restrict_circulating_capital|otherCapital +自由流通股份|freeFloatCapital|freeFloatCapital diff --git a/src/xtquant/config/cashflow_new_1.ini b/src/xtquant/config/cashflow_new_1.ini new file mode 100644 index 0000000..ee98a8a --- /dev/null +++ b/src/xtquant/config/cashflow_new_1.ini @@ -0,0 +1,109 @@ +现金流量表|ASHARECASHFLOW +披露日期|m_anntime|m_annTime +截止日期|m_timetag|m_endTime +收到原保险合同保费取得的现金|cash_received_ori_ins_contract_pre|m_cashReceivedOriInsContractPre +收到再保险业务现金净额|net_cash_received_rei_ope|m_netCashReceivedReiOpe +保户储金及投资款净增加额|net_increase_insured_funds|m_netIncreaseInsuredFunds +处置交易性金融资产净增加额|net_increase_in_disposal|m_netIncreaseInDisposal +收取利息、手续费及佣金的现金|cash_for_interest|m_cashForInterest +回购业务资金净增加额|net_increase_in_repurchase_funds|m_netIncreaseInRepurchaseFunds +支付原保险合同赔付款项的现金|cash_for_payment_original_insurance|m_cashForPaymentOriginalInsurance +支付保单红利的现金|cash_payment_policy_dividends|m_cashPaymentPolicyDividends +处置子公司及其他收到的现金|disposal_other_business_units|m_disposalOtherBusinessUnits +减少质押和定期存款所收到的现金|cash_received_from_pledges|m_cashReceivedFromPledges +投资所支付的现金|cash_paid_for_investments|m_cashPaidForInvestments +质押贷款净增加额|net_increase_in_pledged_loans|m_netIncreaseInPledgedLoans +取得子公司及其他营业单位支付的现金净额|cash_paid_by_subsidiaries|m_cashPaidBySubsidiaries +增加质押和定期存款所支付的现金|increase_in_cash_paid|m_increaseInCashPaid +其中子公司吸收现金|cass_received_sub_abs|m_cassReceivedSubAbs +其中:子公司支付给少数股东的股利、利润|cass_received_sub_investments|m_cassReceivedSubInvestments +少数股东损益|minority_shareholder_profit_loss|m_minorityShareholderProfitLoss +未确认的投资损失|unrecognized_investment_losses|m_unrecognizedInvestmentLosses +递延收益增加(减:减少)|ncrease_deferred_income|m_ncreaseDeferredIncome +预计负债|projected_liability|m_projectedLiability +经营性应付项目的增加|increase_operational_payables|m_increaseOperationalPayables +已完工尚未结算款的减少(减:增加)|reduction_outstanding_amounts_less|m_reductionOutstandingAmountsLess +已结算尚未完工款的增加(减:减少)|reduction_outstanding_amounts_more|m_reductionOutstandingAmountsMore +销售商品、提供劳务收到的现金|goods_sale_and_service_render_cash|m_goodsSaleAndServiceRenderCash +客户存款和同业存放款项净增加额|net_incr_dep_cob|m_netIncrDepCob +向中央银行借款净增加额(万元|net_incr_loans_central_bank|m_netIncrLoansCentralBank +向其他金融机构拆入资金净增加额|net_incr_fund_borr_ofi|m_netIncrFundBorrOfi +拆入资金净增加额|net_incr_fund_borr_ofi|m_netIncrFundBorrOfi +收到的税费与返还|tax_levy_refund|m_taxLevyRefund +投资支付的现金|cash_paid_invest|m_cashPaidInvest +收到的其他与经营活动有关的现金|other_cash_recp_ral_oper_act|m_otherCashRecpRalOperAct +经营活动现金流入小计|stot_cash_inflows_oper_act|m_stotCashInflowsOperAct +购买商品、接受劳务支付的现金|goods_and_services_cash_paid|m_goodsAndServicesCashPaid +客户贷款及垫款净增加额|net_incr_clients_loan_adv|m_netIncrClientsLoanAdv +存放中央银行和同业款项净增加额|net_incr_dep_cbob|m_netIncrDepCbob +支付利息、手续费及佣金的现金|handling_chrg_paid|m_handlingChrgPaid +支付给职工以及为职工支付的现金|cash_pay_beh_empl|m_cashPayBehEmpl +支付的各项税费|pay_all_typ_tax|m_payAllTypTax +支付其他与经营活动有关的现金|other_cash_pay_ral_oper_act|m_otherCashPayRalOperAct +经营活动现金流出小计|stot_cash_outflows_oper_act|m_stotCashOutflowsOperAct +经营活动产生的现金流量净额|net_cash_flows_oper_act|m_netCashFlowsOperAct +收回投资所收到的现金|cash_recp_disp_withdrwl_invest|m_cashRecpDispWithdrwlInvest +取得投资收益所收到的现金|cash_recp_return_invest|m_cashRecpReturnInvest +处置固定资产、无形资产和其他长期投资收到的现金|net_cash_recp_disp_fiolta|m_netCashecpDispFiolta +收到的其他与投资活动有关的现金|other_cash_recp_ral_inv_act|m_otherCashRecpRalInvAct +投资活动现金流入小计|stot_cash_inflows_inv_act|m_stotCashInflowsInvAct +购建固定资产、无形资产和其他长期投资支付的现金|cash_pay_acq_const_fiolta|m_cashPayAcqConstFiolta +投资活动现金流出小计|stot_cash_outflows_inv_act|m_stotCashOutflowsInvAct +投资活动产生的现金流量净额|net_cash_flows_inv_acm_netCashFlowsInvAct|m_netCashFlowsInvAct +吸收投资收到的现金|cash_recp_cap_contrib|m_cashRecpCapContrib +取得借款收到的现金|cash_recp_borrow|m_cashRecpBorrow +发行债券收到的现金|proc_issue_bonds|m_procIssueBonds +收到其他与筹资活动有关的现金|other_cash_recp_ral_fnc_act|m_otherCashRecpRalFncAct +筹资活动现金流入小计|stot_cash_inflows_fnc_act|m_stotCashInflowsFncAct +偿还债务支付现金|cash_prepay_amt_borr|m_cashPrepayAmtBorr +分配股利、利润或偿付利息支付的现金|cash_pay_dist_dpcp_int_exp|m_cashPayDistDpcpIntExp +支付其他与筹资的现金|other_cash_pay_ral_fnc_act|m_otherCashPayRalFncAct +筹资活动现金流出小计|stot_cash_outflows_fnc_act|m_stotCashOutflowsFncAct +筹资活动产生的现金流量净额|net_cash_flows_fnc_act|m_netCashFlowsFncAct +汇率变动对现金的影响|eff_fx_flu_cash|m_effFxFluCash +现金及现金等价物净增加额|net_incr_cash_cash_equ|m_netIncrCashCashEqu +期初现金及现金等价物余额|cash_cash_equ_beg_period|m_cashCashEquBegPeriod +期末现金及现金等价物余额|cash_cash_equ_end_period|m_cashCashEquEndPeriod +净利润|net_profit|m_netProfit +资产减值准备|plus_prov_depr_assets|m_plusProvDeprAssets +固定资产折旧、油气资产折耗、生产性物资折旧|depr_fa_coga_dpba|m_deprFaCogaDpba +无形资产摊销|amort_intang_assets|m_amortIntangAssets +长期待摊费用摊销|amort_lt_deferred_exp|m_amortLtDeferredExp +待摊费用的减少|decr_deferred_exp|m_decrDeferredExp +预提费用的增加|incr_acc_exp|m_incrAccExp +处置固定资产、无形资产和其他长期资产的损失|loss_disp_fiolta|m_lossDispFiolta +固定资产报废损失|loss_scr_fa|m_lossScrFa +公允价值变动损失|loss_fv_chg|m_lossFvChg +财务费用|fin_exp|m_finExp +投资损失|invest_loss|m_investLoss +递延所得税资产减少|decr_deferred_inc_tax_assets|m_decrDeferredIncTaxAssets +递延所得税负债增加|incr_deferred_inc_tax_liab|m_incrDeferredIncTaxLiab +存货的减少|decr_inventories|m_decrInventories +经营性应收项目的减少|decr_oper_payable|m_decrOperPayable +其他|others|m_others +经营活动产生现金流量净额|im_net_cash_flows_oper_act|m_imNetCashFlowsOperAct +债务转为资本|conv_debt_into_cap|m_convDebtIntoCap +一年内到期的可转换公司债券|conv_corp_bonds_due_within_1y|m_convCorpBondsDueWithin1y +融资租入固定资产|fa_fnc_leases|m_faFncLeases +现金的期末余额|end_bal_cash|m_endBalCash +现金的期初余额|less_beg_bal_cash|m_lessBegBalCash +现金等价物的期末余额|plus_end_bal_cash_equ|m_plusEndBalCashEqu +现金等价物的期初余额|less_beg_bal_cash_equ|m_lessBegBalCashEqu +现金及现金等价物的净增加额|im_net_incr_cash_cash_equ|m_imNetIncrCashCashEqu + +销售商品、提供劳务收到的现金|m_cashSellingProvidingServices +拆出资金净减少额|m_netDecreaseUnwindingFunds +买入返售款项净减少额|m_netReductionPurchaseRebates +存放中央银行和同业款项净增加额|m_netIncreaseDepositsBanks +支付再保业务现金净额|m_netCashReinsuranceBusiness +保户储金及投资款净减少额|m_netReductionDeposInveFunds +拆出资金净增加额|m_netIncreaseUnwindingFunds +拆入资金净减少额|m_netReductionAmountBorrowedFunds +卖出回购款项净减少额|m_netReductionSaleRepurchaseProceeds +投资支付的现金|m_investmentPaidInCash +支付其他与投资活动有关的现金|m_paymentOtherCashRelated +投资活动产生的现金流出小计|m_cashOutFlowsInvesactivities +吸收权益性投资收到的现金|m_absorbCashEquityInv +其他对现金的影响|m_otherImpactsOnCash +经营性应收项目的增加|m_addOperatingReceivableItems + diff --git a/src/xtquant/config/config.lua b/src/xtquant/config/config.lua new file mode 100644 index 0000000..3521e84 --- /dev/null +++ b/src/xtquant/config/config.lua @@ -0,0 +1,446 @@ +local __config_lua_path = debug.getinfo(1, "S").source:sub(2) +local __config_lua_dir = __config_lua_path:match("(.-)[\\/][^\\/]-$") .. "/" +local function testDofile(path) + local abs_path = __config_lua_dir .. path + local file = io.open(abs_path, "r") + if file ~= nil then + dofile(abs_path) + return true + else + return false + end +end + +g_is_address_from_daemon = 0 +g_is_server = true +g_is_report_logcenter = false +g_system_tag = "" +g_is_topology_logcenter = false + +g_ftCategory = { + ["2101"]="ag", + ["2102"]="al", + ["2103"]="au", + ["2104"]="bu", + ["2105"]="cu", + ["2106"]="fu", + ["2107"]="hc", + ["2108"]="pb", + ["2109"]="rb", + ["2110"]="ru", + ["2111"]="wr", + ["2112"]="zn", + ["2113"]="ni", + ["2114"]="sn", + + ["2151"]="IF", + ["2152"]="T", + ["2153"]="TF", + ["2154"]="IC", + ["2155"]="IH", + + ["2201"]="SP a&a", + ["2202"]="SP b&b", + ["2203"]="SP bb&bb", + ["2204"]="SP c&c", + ["2205"]="SP cs&cs", + ["2206"]="SP fb&fb", + ["2207"]="SP i&i", + ["2208"]="SP j&j", + ["2209"]="SP jd&jd", + ["2210"]="SP jm&jm", + ["2211"]="SP l&l", + ["2212"]="SP m&m", + ["2213"]="SP p&p", + ["2214"]="SP pp&pp", + ["2215"]="SP v&v", + ["2216"]="SP y&y", + ["2217"]="SPC a&m", + ["2218"]="SPC c&cs", + ["2219"]="SPC fb&bb", + ["2220"]="SPC i&j", + ["2221"]="SPC i&jm", + ["2222"]="SPC j&jm", + ["2223"]="SPC l&pp", + ["2224"]="SPC l&v", + ["2225"]="SPC v&pp", + ["2226"]="SPC y&p", + ["2227"]="a", + ["2228"]="b", + ["2229"]="bb", + ["2230"]="c", + ["2231"]="cs", + ["2232"]="fb", + ["2233"]="i", + ["2234"]="j", + ["2235"]="jd", + ["2236"]="jm", + ["2237"]="l", + ["2238"]="m", + ["2239"]="p", + ["2240"]="pp", + ["2241"]="v", + ["2242"]="y", + + ["2251"]="CF", + ["2252"]="FG", + ["2253"]="IPS SF&SM", + ["2254"]="JR", + ["2255"]="LR", + ["2256"]="MA", + ["2257"]="ME", + ["2258"]="OI", + ["2259"]="PM", + ["2260"]="RI", + ["2261"]="RM", + ["2262"]="RS", + ["2263"]="SF", + ["2264"]="SM", + ["2265"]="SPD CF&CF", + ["2266"]="SPD FG&FG", + ["2267"]="SPD JR&JR", + ["2268"]="SPD LR&LR", + ["2269"]="SPD MA&MA", + ["2270"]="SPD ME&ME", + ["2271"]="SPD OI&OI", + ["2272"]="SPD PM&PM", + ["2273"]="SPD RI&RI", + ["2274"]="SPD RM&RM", + ["2275"]="SPD RS&RS", + ["2276"]="SPD SF&SF", + ["2277"]="SPD SM&SM", + ["2278"]="SPD SR&SR", + ["2279"]="SPD TA&TA", + ["2280"]="SPD TC&TC", + ["2281"]="SPD WH&WH", + ["2282"]="SR", + ["2283"]="TA", + ["2284"]="TC", + ["2285"]="WH" +} + +testDofile("../config/platform.lua") +testDofile("../config/serverEnv.lua") +if testDofile("../config/clientEnv.lua") or testDofile("../config/itsmClientEnv.lua") then g_is_server = false end +testDofile("../config/env.lua") +testDofile("../config/xtdaemon.lua") +testDofile("../config/clientEnv.lua") +testDofile("../config/itsmClientEnv.lua") +testDofile("../config/serverEnv.lua") +testDofile("../config/fairplaytables.lua") +testDofile("../config/configHelper.lua") +testDofile("../config/xtstocktype.lua") + +function getFutureOrderLimits() + return table2json({content = g_future_order_limits}) +end + +function getFuturePlatforms() + return table2json({content = g_future_platforms}) +end + +function getStockPlatforms() + return table2json({content = g_stock_platforms}) +end + +function getCreditPlatforms() + return table2json({content = g_credit_platforms}) +end + +function getHGTPlatforms() + return table2json({content = g_hgt_platforms}) +end + +function getHGTQuotePlatforms() + return table2json({content = g_hgt_quote_platforms}) +end + +function getFutureQuotePlatforms() + return table2json({content = g_future_quote_platforms}) +end + +function getStockQuotePlatforms() + return table2json({content = g_stock_quote_platforms}) +end + +function getStockOptionPlatforms() + return table2json({content = g_stockoption_platforms}) +end + +function getStockOptionQuotePlatforms() + return table2json({content = g_stockoption_quote_platforms}) +end + +function getNew3BoardPlatforms() + return table2json({content = g_new3board_platforms}) +end + +function getNew3BoardQuotePlatforms() + return table2json({content = g_new3board_quote_platforms}) +end + +function getGoldPlatforms() + return table2json({content = g_gold_platforms}) +end + +function getGoldQuotePlatforms() + return table2json({content = g_gold_quote_platforms}) +end + +function getBanks() + return table2json({content = g_banks}) +end + +function getTTServiceGlobalConfig() + return table2json(g_ttservice_global_config) +end + +function getMysqlConfig() + return table2json(g_mysql_config) +end + +function getMysqlConfigWhiteListFlowControl() + return table2json(g_mysql_config_white_list_flow_control) +end + +function getRabbitMqConfig() + return table2json(g_rabbitMq_config) +end + +function getBatchOrderConfig() + return table2json(g_batchOrder_config) +end + +function getPlatformInfo() + return getConfigByAppName("calcConfigEnv") +end + +function getSystemTag() + return g_system_tag +end + +-- ȡƱ +function getXtStockType() + return toIni(g_stocktype_info) +end + +function getBrokerAddressWithReInit(brokerType, platformId, brokerId, accountId, reInit) + if reInit then print("true") else print("false") end + local key = "xtbroker_" .. brokerType .. "_" .. platformId .. "_" ..brokerId + local address = g_defaultPorts[key] + if address == nil then + key = "xtbroker_" .. brokerType .. "_" .. platformId + address = g_defaultPorts[key] + if address == nil then + key = "xtbroker_" .. brokerType + address = g_defaultPorts[key] + if address == nil then + key = "xtbroker" + address = g_defaultPorts[key] + end + end + end + + if address == nil then + if reInit then + g_brokerPorts = genBrokerInfos() + mergeBrokerInfos(g_brokerPorts) + address = getBrokerAddressWithReInit(brokerType, platformId, brokerId, accountId, false) + end + end + if address == nil then address = "" end + return address +end + +function getBrokerAddress(brokerType, platformId, brokerId, accountId) + return getBrokerAddressWithReInit(brokerType, platformId, brokerId, accountId, true) +end + +-- tagplatformId +function getBrokerConfig(tag) + return getConfigByAppName("xtbroker", {tag}) +end + +function getSfitMdquoterConfig(tag) + return getConfigByAppName("sfitMdquoter", {tag}) +end + +function getXtQuoterConfig() + return getConfigByAppName("xtquoter") +end + +-- ȡTTService +function getXtServiceConfig() + return getConfigByAppName("xtservice") +end + +-- ȡ׷ +function getXtTraderServiceConfig() + return getConfigByAppName("xttraderservice") +end + +-- ȡط +function getXtRiskControlConfig() + return getConfigByAppName("xtriskcontrol") +end + +-- ȡMysqlService +function getXtMysqlServiceConfig() + return getConfigByAppName("xtmysqlservice") +end + +function getXtSourceConfig() + return getConfigByAppName("xtsource") +end + +function getXtTaskConfig(tag) + return getConfigByAppName("xttask", {tag}) +end + +function getXtMobileServiceConfig() + return getConfigByAppName("xtmobileservice") +end + +function getParam(param) + return table2json(_G[param]) +end + +function getXtClientConfig() + return getConfigByAppName("xtclient") +end + +function getXtMiniQmtConfig() + return getConfigByAppName("xtminiqmt") +end + +function getXtMiniQuoteConfig() + return getConfigByAppName("xtminiquote") +end + +function getXtQuantServiceConfig() + return getConfigByAppName("xtquantservice") +end + +function getXtItsmClientConfig() + return getConfigByAppName("xtitsmclient") +end + +function getXtItsmServiceConfig() + return getConfigByAppName("xtitsmservice") +end + +function getXtQueryBrokerConfig() + return getConfigByAppName("xtquerybroker") +end + +function getXtOtpConfig() + return getConfigByAppName("xtotpservice") +end + +function getXtLogCenterConfig() + return getConfigByAppName("xtlogcenter") +end + +function getCtpServiceConfig() + return getConfigByAppName("xtctpservice") +end + +function getXtApiServiceConfig() + return getConfigByAppName("xtapiservice") +end + +function getXtClearServiceConfig() + return getConfigByAppName("xtclearservice") +end + +function getDelegateServiceConfig() + return getConfigByAppName("xtdelegateservice") +end + +function getFtProduct() + return table2json(g_ftCategory) +end + +function getAlgoAdapterServiceConfig() + return getConfigByAppName("xtalgoadapterservice") +end + +function getXtFairPlayServiceConfig(tag) + return getConfigByAppName("xtfairplayservice", {tag} ) +end + +function getXtNonStandardServiceConfig() + return getConfigByAppName("xtnonstandardservice") +end + +function getModules() + modules = getModulesHelper() + return table2json(modules["modules"]) +end +--ȡͷϢ +function getCustomerServiceConfig() + return getConfigByAppName("customerservice") +end + +function getBrokerProxy() + return getConfigByAppName("xtbrokerproxy") +end + +function getHttpUrlConfig() + return getConfigByAppName("xthttpurlconfig") +end + +--require "std" +--require "io" +local function main() + if arg == nil then + return "" + end + + if arg[1] ~= nil then + local d = _G[ arg[1] ] + if d ~= nil then + if type(d) == "function" then + local newArg = {} + for i = 1, 100 do + if arg[1 + i] ~= nil then + table.insert(newArg, arg[1 + i]) + else + break + end + end + return d(unpack(newArg)) + elseif type(d) == "table" then + return table2json(d) + end + else + local newArg = {} + for i = 1, 100 do + if arg[1 + i] ~= nil then + table.insert(newArg, arg[1 + i]) + else + break + end + end + return getConfigByAppName(arg[1], newArg) + end + end +end + +print(main()) +--print(getXtClientConfig()) +--[[ +print(main()) +print(getPlatform("")) +print("=====================") +print(getXtServiceConfig()) +print("=====================") +print(getXTTraderServiceConfig()) +print("=====================") +print(getXtQuoterConfig()) +print("=====================") +print(getXtTaskConfig("xttask")) +print("=====================") +print(getBrokerConfig("1_21001_1001")) +--print(getConfigByAppName("sfit")) +]] diff --git a/src/xtquant/config/configHelper.lua b/src/xtquant/config/configHelper.lua new file mode 100644 index 0000000..f0f7db5 --- /dev/null +++ b/src/xtquant/config/configHelper.lua @@ -0,0 +1,545 @@ +local __config_helper_lua_path = debug.getinfo(1, "S").source:sub(2) +local __config_helper_lua_dir = __config_helper_lua_path:match("(.-)[\\/][^\\/]-$") .. "/" +dofile(__config_helper_lua_dir .. "table2json.lua") + +-- lua文件使用%作为分隔符 +-- ini文件使用&作为分隔符 +local function eval(str) + if type(str) == "string" then + if #str > 0 then + return loadstring("return " .. str)() + end + elseif type(str) == "number" then + return loadstring("return " .. tostring(str))() + else + error("is not a string") + end +end + +local function split(str,sep) + local ret={} + local n=1 + for w in str:gmatch("([^" .. sep .. "]*)") do + ret[n]=ret[n] or w -- only set once (so the blank after a string is ignored) + if w=="" then n=n+1 end -- step forwards on a blank but not a string + end + return ret +end + +function tableMerge(t1, t2) + for k,v in pairs(t2) do + if type(v) == "table" then + if type(t1[k] or false) == "table" then + tableMerge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end + +local function genMap(key, value) + local m = {} + local items = split(key, "%.") + local d = nil + for i = 0, (#items -1) do + local str = items[#items - i] + if i == 0 then + d = {} + d[str] = eval(value) + else + local raw = d + d = {} + d[str] = raw + end + end + for k, v in pairs(d) do + m[k] = v + end + return m +end + +function parse(param, localMap) + local ret = param + for w in string.gmatch(param, "{{([^}]*)}}") do + local v = localMap[w] + if v == nil then v = eval(w) end + if v ~= nil then + ret = string.gsub(ret, "{{" .. w .. "}}", v) + end + end + return ret +end + +local function getLocalFileMap(filePath) + local ret = {} + local file = io.open(__config_helper_lua_dir..filePath, "r") + if file ~= nil then + local content = file:read("*a") + local loadRet = loadstring(content) + if loadRet == nil then + loadRet = loadstring("return " .. content) + end + if loadRet ~= nil then + ret = loadRet() + end + end + return ret +end + +local function getLocalMap() + m1 = getLocalFileMap("../config_local/customer.lua") + m2 = getLocalFileMap("../config_local/machine.lua") + return tableMerge(m1, m2) +end + +function mergeLocal() + local m = getLocalMap() + local g_localMap = {} + local g_globalMap = {} + for k, v in pairs(m) do + local r1, r2 = k:find("g_") + if r1 == 1 then + g_globalMap[k] = v + else + g_localMap[k] = v + end + end + + _G = tableMerge(_G, g_globalMap) + _G["g_localMap"] = g_localMap + +end + +function toIni(map) + local ret = "" + for key, value in pairs(map) do + str = "[" .. key .. "]\n" + for itemKey, itemValue in pairs(value) do + if type(itemValue) == type("") then + if itemValue:len() > 0 then + str = str .. itemKey .. "=" .. itemValue .. "\n" + end + elseif type(itemValue) == type(true) then + local v = 1 + if itemValue then v = 1 else v = 0 end + str = str .. itemKey .. "=" .. v .. "\n" + else + str = str .. itemKey .. "=" .. itemValue .. "\n" + end + end + ret = ret .. str .. "\n" + end + return ret +end + +function getClientMap(tag) + local CLINET_CONFIG = { + tagTemplate = "", --标签模版, 可以做正则匹配 + address = "127.0.0.1:80", -- 地址 + isGetdAddressFromNameServer = g_is_address_from_daemon, -- 是否从NameServer取得地址 + isUsedAloneIO = 0, -- 是否单独使用一个ioservice + timeoutSecond = 600, -- 超时检测时间 + keepAliveCheckSecond = 5, -- 保活包 + reconnectSecond = 3, -- 断线重连时间间隔 + requestTimeoutSecond = 600, -- 请求超时时间 + isUseSSL = 0, --是否使用SSL + sslCaPath = "", -- SSL证书地址 + proxyType = "0", -- 代理类型, 0表示无, 1表示http, 2表示socket4, 3表示socket5 + proxyIp = "", -- 代理地址 + proxyPort = 80, -- 代理端口 + proxyNeedCheck = 0, -- 是否需要验证 + proxyUserName = "", -- 代理用户名 + proxyPassword = "", -- 代理密码 + packageDir = "", -- 存放网络包目录 + } + local tagKey = "client_" .. tag + local ret = { + [tagKey] = CLINET_CONFIG + } + local address = g_defaultPorts[tag] + if address == nil then address = "127.0.0.1:8000" end + local m = { + [tagKey] = { + tagTemplate = tag, + address = g_defaultPorts[tag] + } + } + ret = tableMerge(ret, m) + return ret +end + + +function getServerMap(tag) + local SERVER_CONFIG = { + tag = "", -- 标签模版, 可以做正则匹配 + address = "0.0.0.0:80", -- 地址 + isGetdAddressFromNameServer = 0,-- 是否从NameServer取得地址 + timeoutSecond = 600, -- 超时检测时间 + maxConnectionNum = 1000000, -- 最大连接数 + isAutoBind = g_is_address_from_daemon, -- 是否自动绑定(即端口无法监听, 监听下一端口) + isUseSSL = 0, -- 是否启用SSL + crtPath = "", + serverKeyPath = "", + tempDhPath = "", + sslPassword = "", + packageDir = "", + } + local port = 80 + local address = g_defaultPorts[tag] + if address ~= nil then + port = string.sub(address, string.find(address, ":") +1 ,#address) + end + + local ret = { + ["server_" .. tag] = SERVER_CONFIG + } + local m = { + ["server_" .. tag] = { + tag = tag, + address = "0.0.0.0:" .. port, + } + } + ret = tableMerge(ret, m) + ret["server"] = ret["server_" .. tag] + return ret +end + +function getLocalAppMap(tag) + local ret = { + app = { + appName = tag, + netThreadNum = 1, -- 线程数 + dispatcherThreadNum = 1, -- 处理线程数 + logPath = "", -- 日志文件路径 + reportSeconds = 60, -- 状态报告时间 + isReportLogCenter = 1 and g_is_report_logcenter or 0, -- 是否日志打印到logCenter + serverDeployType = g_server_deploy_type, -- 部署类型 + host_ip = g_host_ip, -- 主机IP + zkRunningDir = g_running_dir, -- 运行目录 + topology = 1 and g_is_topology_logcenter or 0, --是否发送拓扑数据 + topologyInterval = 20, --发送拓扑数据时间间隔 + }, + client_NameService = { + tagTemplate = "NameService", + address = g_defaultPorts["xtdaemon"], + reconnectSecond = 3, + }, + quoter_config = { + is_use_proxy_all_push = g_use_proxy_whole_quoter, + is_use_future_all_push = g_use_future_whole_quoter, + timeoutsec = 20, + }, + } + return ret +end + +function getAppMap(serverTag, clients) + local ret = getLocalAppMap(serverTag) + if serverTag ~= nil then + local serverMap = getServerMap(serverTag) + ret = tableMerge(ret, serverMap) + end + if clients ~= nil then + for i, v in pairs(clients) do + local map = getClientMap(v) + ret = tableMerge(ret, map) + end + end + return ret +end + +function getLog4cxx(tag) + local d = [[ +log4j.logger.TTStdFile=INFO,fa +log4j.logger.TTDbgFile=DEBUG,fa2 + +# 文件输出 +log4j.appender.fa=org.apache.log4j.DailyRollingFileAppender +log4j.appender.fa.MaxFileSize=500MB +log4j.appender.fa.datePattern='.'yyyy-MM-dd +log4j.appender.fa.File=../userdata/log/{{tag}}.log +log4j.appender.fa.Append=true +log4j.appender.fa.layout=org.apache.log4j.PatternLayout +log4j.appender.fa.layout.ConversionPattern=%d [%p] [%t] %m%n + +# 文件输出2 +log4j.appender.fa2=org.apache.log4j.FileAppender +log4j.appender.fa2.MaxFileSize=500MB +log4j.appender.fa2.MaxBackupIndex=10 +log4j.appender.fa2.File=../userdata/log/{{tag}}_debug.log +log4j.appender.fa2.Append=true +log4j.appender.fa2.layout=org.apache.log4j.PatternLayout +log4j.appender.fa2.layout.ConversionPattern=%d [%p] [%t] %m%n + +# 控制台输出 +log4j.appender.ca=org.apache.log4j.ConsoleAppender +log4j.appender.ca.layout=org.apache.log4j.PatternLayout +log4j.appender.ca.layout.ConversionPattern=%d [%p] [%t] %m%n +]] + d = parse(d, {["tag"] = tag}) + return d +end + +local function getTableDepth(t, depth) + if type(t) == "table" then + depth = depth + 1 + local maxDepth = depth + for k, v in pairs(t) do + if type(v) == "table" then + local d = getTableDepth(v, depth) + if d > maxDepth then maxDepth = d end + end + end + depth = maxDepth + end + return depth +end + +-- 根据App名称取配置 +local function getConfigTableByAppName(param, m) + if m == nil then m = {} end + if type(m) ~= "table" then + m = {m} + end + local paramObj = _G[param] + if paramObj == nil then + local file = io.open(__config_helper_lua_dir .. param .. ".lua", "r") + if file ~= nil then + local content = file:read("*a") + if content ~= nil then + local loadRet = loadstring(content) + if loadRet == nil then + loadRet = loadstring("return " .. content) + end + if loadRet ~= nil then + paramObj = loadRet() + _G[param] = paramObj + end + end + end + end + if paramObj == nil then paramObj = {} end + local t = {} + if type(paramObj) == "function" then + t = paramObj(unpack(m)) + elseif type(paramObj) == "table" then + t = paramObj + end + + -- 合并本地数据 + local localMap = {} + if g_localMap ~= nil then + localMap = g_localMap[param] + end + if localMap == nil then localMap = {} end + t = tableMerge(t, localMap) + return t +end + +-- 根据App名称取配置 +function getConfigByAppName(param, m) + local t = getConfigTableByAppName(param, m) + local depth = getTableDepth(t, 0) + if depth == 2 then + return toIni(t) + else + return table2json(t) + end +end + +function getModulesHelper() + return getLocalFileMap("../config_local/modules.lua") +end + +-- 取Broker模块配置 +function getBrokerModuleConfig(configTag, moduleTag) + local t = getConfigTableByAppName(configTag) + if moduleTag ~= nil then + local tag = moduleTag + local index = moduleTag:find("_") + if index ~= nil then + tag = moduleTag:sub(moduleTag:find("_", index+1) + 1) + end + tag = configTag .. "/" .. tag + local t1 = getConfigTableByAppName(tag) + t = tableMerge(t, t1) + end + local depth = getTableDepth(t, 0) + if depth == 2 then + return toIni(t) + else + return table2json(t) + end +end + +function getPlatform(tag) + for k, v in pairs(g_allPlatforms) do + local tTag = v["m_nType"] .. "_" .. v["m_nId"] .. "_" .. v["m_brokerId"] + local r1, r2 = tag:find(tTag) + if r1 == 1 then + return v + end + end + return nil +end + +local function genPlatformInfos() + local allTypes = {g_future_platforms, g_stock_platforms, g_credit_platforms, g_stockoption_platforms, g_new3board_platforms, g_hgt_platforms, g_gold_platforms} + for tk, tv in pairs(allTypes) do + for k, v in pairs(tv) do + table.insert(g_allPlatforms, v) + end + end +end + +function genBrokerInfos() + local key = "xtbroker_1_21001_9999" + local ret = {} + ret[key] = "127.0.0.1:" .. (58000 + 3) + + return ret +end + +-- 参数:待分割的字符串,分割字符 +-- 返回:子串表.(含有空串) +function lua_string_split(str, split_char) + local sub_str_tab = {}; + + while (true) do + local pos = string.find(str, split_char); + if (not pos) then + local size_t = table.getn(sub_str_tab) + table.insert(sub_str_tab,size_t+1,str); + break; + end + + local sub_str = string.sub(str, 1, pos - 1); + local size_t = table.getn(sub_str_tab); + table.insert(sub_str_tab,size_t+1,sub_str); + local t = string.len(str); + str = string.sub(str, pos + 1, t); + end + return sub_str_tab; +end + +function do_checkVersion(version, minVersion) + local tableVersion = lua_string_split(version, "%.") + local tempMinVersion = minVersion + local tableGMinVersion = lua_string_split(tempMinVersion , "%.") + local ret = false + local tablesize = 0 + local isVShort = false + if table.getn(tableVersion) < table.getn(tableGMinVersion) then + isVShort = true + tablesize = table.getn(tableVersion) + else + tablesize = table.getn(tableGMinVersion) + end + + for i = 1, tablesize , 1 do + tableVersion[i] = tonumber(tableVersion[i]) + tableGMinVersion[i] = tonumber(tableGMinVersion[i]) + if tableVersion[i] < tableGMinVersion[i] then + ret = true + return ret + elseif tableVersion[i] > tableGMinVersion[i] then + return ret + end + end + + if isVShort then + ret = true + end + + return ret +end + +function checkVersion(version) + local ret = do_checkVersion(version, g_minVersion) + local msg = "" + if ret then + msg = "您的客户端版本过低, 请联系" .. g_company .."获取最新版本!" + end + return msg +end + +function checkMobileVersion(version) + local ret = do_checkVersion(version, g_minMobileVersion) + local msg = "" + if ret then + msg = "您的移动客户端版本过低, 请联系" .. g_company .."获取最新版本!" + end + return msg +end + +-- 取地址Ip +function getIp(address) + local ip = address:sub(1, address:find(":") - 1) + return ip +end + +-- 取地址端口 +function getPort(address) + local port = address:sub(address:find(":") + 1, address:len()) + return port +end + +function genFairyPlayUnitInfos() + if g_fairplay_units == nil then return end + for k, v in pairs(g_fairplay_units) do + table.insert(g_fairPlayUnits, v) + end +end + +function getFairPlayUnit(tag) + for k, v in pairs(g_fairPlayUnits) do + local tTag = v["m_nType"] .. "_" .. v["m_nId"] + local r1, r2 = tag:find(tTag) + if r1 == 1 then + return v + end + end + return nil +end + +function mergeBrokerInfos(brokerPorts) + if g_allBrokers == nil then + g_allBrokers = {} + end + + for k, v in pairs(brokerPorts) do + if g_defaultPorts[k] == nil then + g_defaultPorts[k] = v + end + table.insert(g_allBrokers, k) + end +end + +-- 合并本地数据 +mergeLocal() + +if not g_is_address_from_daemon then + -- 产生平台信息 + genPlatformInfos() + + -- 产生券商信息 + if g_brokerPorts == nil then + g_brokerPorts = genBrokerInfos() + end + mergeBrokerInfos(g_brokerPorts) + + --产生公平交易单元信息 + genFairyPlayUnitInfos() +end + +function getExtraThreadPools(key) + local threadNum = g_extra_thread_pools[key] + if threadNum ~= nil then + return threadNum + end + return 0 +end diff --git a/src/xtquant/config/env.lua b/src/xtquant/config/env.lua new file mode 100644 index 0000000..287d1f7 --- /dev/null +++ b/src/xtquant/config/env.lua @@ -0,0 +1,159 @@ + +g_minVersion = "2.0.1.600" +g_minMobileVersion = "1.0.0.0" +g_company = "ڿ" +g_is_address_from_daemon = false +g_use_proxy_whole_quoter = 1 +g_use_future_whole_quoter = 0 +g_server_deploy_type = 0 + +g_defaultPorts = { + xtdaemon="127.0.0.1:55000", + xtservice="127.0.0.1:56000", + xtindex="127.0.0.1:56001", + xtmonitor="127.0.0.1:56002", + xtwebservice="127.0.0.1:56003", + xttraderservice="127.0.0.1:57000", + xtquoter="127.0.0.1:59000", + xtriskcontrol="127.0.0.1:60000", + proxy="210.14.136.66:55300", + proxy_backup="203.156.205.182:55300", + xtcounter="127.0.0.1:61100", + xtgateway="127.0.0.1:62100", + xtsource="127.0.0.1:63000", + xtitsmservice="127.0.0.1:63500", + xttask="127.0.0.1:61000", + xtquerybroker="127.0.0.1:65000", + xtotp="127.0.0.1:64200", + xtlogcenter="127.0.0.1:65100", + xtctpservice="127.0.0.1:65200", + xtapiservice="127.0.0.1:65300", + xtclearservice="127.0.0.1:64100", + xtdelegateservice="127.0.0.1:64300", + xtalgoadapterservice="127.0.0.1:64500", + xtmarket = "127.0.0.1:60100", + xtfairplayservice="127.0.0.1:64600", + xtnonstandardservice="127.0.0.1:64703", + xtantisharefinancingservice = "127.0.0.1:64800", + xtmysqlservice="127.0.0.1:64704", + xtmobileservice="127.0.0.1:65400", + xtmarketinfo="210.14.136.69:59500", +} + +g_allPlatforms = {} +g_allBrokers = {} +g_fairPlayUnits = {} + +g_ttservice_global_config = { + m_maxClientCount=1, + m_logCfg="ttservice.log4cxx", + m_listenIP="0.0.0.0", + m_nListenPort=56100, + m_proxyIP="210.14.136.66", + m_nProxyPort=55808, + m_nWorkFlowPort=63000, + m_workFlowIP="127.0.0.1", + m_redisHost="127.0.0.1", + m_redisPort=6379, + m_nPortalThread=5, + m_addrsPath="", + m_nProductMaxPortfilio=100, + m_debugAccounts="", + m_nUseMd5=0, +} + +g_future_quote_platforms = { + {m_nId=20001, m_strName="CTPʵ", m_strAbbrName="sqsp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20002, m_strName="ʵ", m_strAbbrName="hssp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21018, m_strName="v8tʵ", m_strAbbrName="sqsp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21001, m_strName="CTPģ", m_strAbbrName="gdmn", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21002, m_strName="ģ", m_strAbbrName="hsmn", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21003, m_strName="v8tģ", m_strAbbrName="gdmn", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20000, m_strName="ѸͶ߼", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21111, m_strName="ʹʵ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21112, m_strName="ʹģ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20013, m_strName="ʵ", m_strAbbrName="hshl", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21013, m_strName="ģ", m_strAbbrName="hshl", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21015, m_strName="Խ", m_strAbbrName="hsdy", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21014, m_strName="Ӣ", m_strAbbrName="hsyd", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21017, m_strName="", m_strAbbrName="hsjg", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=21019, m_strName="ԭ", m_strAbbrName="hszy", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20015, m_strName="Խʵ", m_strAbbrName="hsdysp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20014, m_strName="Ӣʵ", m_strAbbrName="hsydsp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20017, m_strName="ʵ", m_strAbbrName="hsjgsp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, + {m_nId=20019, m_strName="ԭʵ", m_strAbbrName="hszysp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=1,}, +} + +g_futureoption_quote_platforms = { + {m_nId=70001, m_strName="CTPʵ", m_strAbbrName="sqsp", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=5,}, + {m_nId=71001, m_strName="CTPģ", m_strAbbrName="gdmn", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=5,}, + {m_nId=71111, m_strName="ʹʵ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=5,}, + {m_nId=71112, m_strName="ʹģ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=5,}, +} + +g_stock_quote_platforms = { + {m_nId=10000, m_strName="ѸͶ߼", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=2,}, + {m_nId=1111, m_strName="ʹʵ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=2,}, + {m_nId=1112, m_strName="ʹģ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=2,}, +} + +g_credit_quote_platforms = { + {m_nId=10000, m_strName="ѸͶ߼", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=3,}, +} + +g_stockoption_quote_platforms = { + {m_nId=10001, m_strName="ѸͶ߼", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=6,}, + {m_nId=1211, m_strName="ʹʵ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=6,}, + {m_nId=1212, m_strName="ʹģ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=6,}, +} + +g_hgt_quote_platforms = { + {m_nId=10003, m_strName="ѸͶ߼", m_strAbbrName="hgtmn", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=7,}, + {m_nId=1411, m_strName="ʹʵ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=7,}, + {m_nId=1412, m_strName="ʹģ", m_strAbbrName="xtgj", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=7,}, +} + +g_new3board_quote_platforms = { + {m_nId=10002, m_strName="ѸͶ߼", m_strAbbrName="neeq", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=10,}, + {m_nId=1311, m_strName="ʹʵ", m_strAbbrName="neeq", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=10,}, + {m_nId=1312, m_strName="ʹģ", m_strAbbrName="neeq", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=10,}, +} + +g_gold_quote_platforms = { + {m_nId=31003, m_strName="ѸͶ߼", m_strAbbrName="zxjtgold", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=4,}, + {m_nId=31111, m_strName="ʹʵ", m_strAbbrName="zxjtgold", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=4,}, + {m_nId=31112, m_strName="ʹģ", m_strAbbrName="zxjtgold", m_strLogo="broker_logo_1", m_strBrokerTag="xtbroker", m_strQuoterTag="xtquoter", m_nType=4,}, +} + +g_future_order_limits = { + {m_strProductID="IF", m_nLimit=200}, + {m_strProductID="AU", m_nLimit=100}, +} + +g_banks = { + {m_strLogo="bank_logo_1", m_strId="1", m_strName="",}, + {m_strLogo="bank_logo_2", m_strId="2", m_strName="ũҵ",}, + {m_strLogo="bank_logo_3", m_strId="3", m_strName="й",}, + {m_strLogo="bank_logo_4", m_strId="4", m_strName="",}, + {m_strLogo="bank_logo_5", m_strId="5", m_strName="ͨ",}, + {m_strLogo="bank_logo_6", m_strId="6", m_strName="ڽ",}, + {m_strLogo="bank_logo_Z", m_strId="Z", m_strName="",} +} + +g_batchOrder_config = { + -- Ƿµ, 0 ʾ ʹ + is_batch_ordinaryOrder = 1, + -- µöٺ뷢һλ + buffer_clear_duration_milli_sec = 100, + buffer_clear_max_order_num = 100, + -- apiserver λʱڵ µ + api_order_upper_limit = 1000, + -- µޣöٺﵽ޲Ŵ + api_order_duration_milli_sec = 1000, + -- 㷨Сµ Ϊ 0.5s + api_min_algorithm_order_duration_milli_sec = 500, + -- ϵСµΪ 10s + api_min_group_order_duration_milli_sec = 10000, + max_order_duration_milli_sec = -1, + max_order_count = -1, +} diff --git a/src/xtquant/config/metaInfo.json b/src/xtquant/config/metaInfo.json new file mode 100644 index 0000000..8c152d6 --- /dev/null +++ b/src/xtquant/config/metaInfo.json @@ -0,0 +1,3032 @@ +{ + u"1008" : { + u"desc" : u"主买单", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"type" : u"double", + }, + u"1" : { + u"type" : u"long", + }, + u"2" : { + u"type" : u"double", + }, + u"3" : { + u"type" : u"int", + }, + }, + + }, + u"1009" : { + u"desc" : u"主卖单", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"type" : u"double", + }, + u"1" : { + u"type" : u"long", + }, + u"2" : { + u"type" : u"double", + }, + u"3" : { + u"type" : u"int", + }, + }, + }, + u"1010" : { + u"desc" : u"实时行情(L2)", + u"isLevel2" : 1, + u"periods" : [0, 86400000], + u"fields" : { + u"0" : { + u"desc" : u"当前价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"成交总量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"成交总额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"持仓量", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"昨结算", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"成交笔数", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"市盈率1", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"市盈率2", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"开盘价", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"最高价", + u"type" : u"double", + }, + u"a" : { + u"desc" : u"最低价", + u"type" : u"double", + }, + u"b" : { + u"desc" : u"今结算", + u"type" : u"double", + }, + u"c" : { + u"desc" : u"前收盘", + u"type" : u"double", + }, + u"d" : { + u"desc" : u"多档委买价", + u"type" : u"vdouble", + }, + u"e" : { + u"desc" : u"多档委买量", + u"type" : u"vint", + }, + u"f" : { + u"desc" : u"多档委卖价", + u"type" : u"vdouble", + }, + u"g" : { + u"desc" : u"多档委卖量", + u"type" : u"vint", + }, + u"h" : { + u"desc" : u"证券状态", + u"type" : u"int", + }, + u"v" : { + u"desc" : u"原始成交总量", + u"type" : u"long", + }, + }, + }, + u"1011" : { + u"desc" : u"实时行情补充(L2)", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"委买均价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"委买总量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"委卖均价", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"委卖总量", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"买入撤单总量", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"买入撤单总额", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"卖出撤单总量", + u"type" : u"long", + }, + u"7" : { + u"desc" : u"卖出撤单总额", + u"type" : u"double", + }, + }, + }, + u"1801" : { + u"desc" : u"逐笔成交(L2)", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"成交价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"成交量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"成交额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"买方订单号", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"卖方订单号", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"成交标志:0:未知;1:外盘;2:内盘; 3:撤单", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"成交类型:对应委托类型", + u"type" : u"int", + }, + u"7" : { + u"desc" : u"成交记录号", + u"type" : u"int", + }, + }, + }, + u"1802" : { + u"desc" : u"逐笔委托(L2)", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"委托价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"委托量", + u"type" : u"int", + }, + u"2" : { + u"desc" : u"委托订单号", + u"type" : u"long", + }, + u"3" : { + u"desc" : u"委托类型:参考深交所OrderKind字段", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"委托方向:1:买入;2:卖出;3:撤单", + u"type" : u"int", + }, + }, + }, + u"1803" : { + u"desc" : u"委买委卖千档盘口(L2)", + u"isLevel2" : 1, + u"periods" : [60000], + u"fields" : { + u"0" : { + u"desc" : u"委买价", + u"type" : u"vdouble", + }, + u"1" : { + u"desc" : u"委买量", + u"type" : u"vint", + }, + u"2" : { + u"desc" : u"委卖价", + u"type" : u"vdouble", + }, + u"3" : { + u"desc" : u"委卖量", + u"type" : u"vint", + }, + }, + }, + u"1804" : { + u"desc" : u"委买委卖一档委托队列", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"委买价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"委买明细", + u"type" : u"vint", + }, + u"2" : { + u"desc" : u"委卖价", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"委卖明细", + u"type" : u"vint", + }, + u"4" : { + u"desc" : u"委买总笔数", + u"type" : u"int", + }, + u"5" : { + u"desc" : u"委卖总笔数", + u"type" : u"int", + }, + }, + }, + u"1806" : { + u"desc" : u"逐笔成交统计", + u"isLevel2" : 1, + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"主买单总单数", + u"type" : u"int", + }, + u"1" : { + u"desc" : u"主买特大单成交量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"主买大单成交量", + u"type" : u"long", + }, + u"3" : { + u"desc" : u"主买中单成交量", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"主买小单成交量", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"主卖单总单数", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"主卖特大单成交量", + u"type" : u"long", + }, + u"7" : { + u"desc" : u"主卖大单成交量", + u"type" : u"long", + }, + u"8" : { + u"desc" : u"主卖中单成交量", + u"type" : u"long", + }, + u"9" : { + u"desc" : u"主卖小单成交量", + u"type" : u"long", + }, + u"a" : { + u"desc" : u"主买特大单成交额", + u"type" : u"double", + }, + u"b" : { + u"desc" : u"主买大单成交额", + u"type" : u"double", + }, + u"c" : { + u"desc" : u"主买中单成交额", + u"type" : u"double", + }, + u"d" : { + u"desc" : u"主买小单成交额", + u"type" : u"double", + }, + u"e" : { + u"desc" : u"主卖特大单成交额", + u"type" : u"double", + }, + u"f" : { + u"desc" : u"主卖大单成交额", + u"type" : u"double", + }, + u"g" : { + u"desc" : u"主卖中单成交额", + u"type" : u"double", + }, + u"h" : { + u"desc" : u"主卖小单成交额", + u"type" : u"double", + }, + u"i" : { + u"desc" : u"大单动向", + u"type" : u"double", + }, + u"j" : { + u"desc" : u"涨跌动因", + u"type" : u"double", + }, + u"k" : { + u"desc" : u"大单差分", + u"type" : u"double", + }, + u"l" : { + u"desc" : u"资金博弈 净流入", + u"type" : u"long", + }, + u"m" : { + u"desc" : u"资金博弈 超大单", + u"type" : u"long", + }, + u"n" : { + u"desc" : u"资金博弈 大单", + u"type" : u"long", + }, + u"o" : { + u"desc" : u"资金博弈 中单", + u"type" : u"long", + }, + u"p" : { + u"desc" : u"资金博弈 小单", + u"type" : u"long", + }, + u"q" : { + u"desc" : u"净挂", + u"type" : u"long", + }, + u"r" : { + u"desc" : u"净撤", + u"type" : u"long", + }, + u"s" : { + u"desc" : u"总撤买", + u"type" : u"long", + }, + u"t" : { + u"desc" : u"总撤卖", + u"type" : u"long", + }, + u"u" : { + u"desc" : u"被动买特大单成交量", + u"type" : u"long", + }, + u"v" : { + u"desc" : u"被动买大单成交量", + u"type" : u"long", + }, + u"w" : { + u"desc" : u"被动买中单成交量", + u"type" : u"long", + }, + u"x" : { + u"desc" : u"被动买小单成交量", + u"type" : u"long", + }, + u"y" : { + u"desc" : u"被动卖特大单成交量", + u"type" : u"long", + }, + u"z" : { + u"desc" : u"被动卖大单成交量", + u"type" : u"long", + }, + u"00" : { + u"desc" : u"被动卖中单成交量", + u"type" : u"long", + }, + u"01" : { + u"desc" : u"被动卖小单成交量", + u"type" : u"long", + }, + u"02" : { + u"desc" : u"被动买特大单成交额", + u"type" : u"double", + }, + u"03" : { + u"desc" : u"被动买大单成交额", + u"type" : u"double", + }, + u"04" : { + u"desc" : u"被动买中单成交额", + u"type" : u"double", + }, + u"05" : { + u"desc" : u"被动买小单成交额", + u"type" : u"double", + }, + u"06" : { + u"desc" : u"被动卖特大单成交额", + u"type" : u"double", + }, + u"07" : { + u"desc" : u"被动卖大单成交额", + u"type" : u"double", + }, + u"08" : { + u"desc" : u"被动卖中单成交额", + u"type" : u"double", + }, + u"09" : { + u"desc" : u"被动卖小单成交额", + u"type" : u"double", + }, + }, + }, + u"1830" : { + u"desc" : u"ETF统计数据L2", + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"申购笔数", + u"type" : u"int", + }, + u"1" : { + u"desc" : u"申购数量", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"申购金额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"赎回笔数", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"赎回数量", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"赎回金额", + u"type" : u"double", + }, + }, + }, + u"2" : { + u"desc" : u"市场交易日信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"交易日", + u"type" : u"long", + }, + }, + }, + u"2000" : { + u"desc" : u"股票列表", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"股票代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"股票名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"昨日收盘价", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"昨日结算价", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"昨日持仓量", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"涨停价", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"跌停价", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"总股本", + u"type" : u"long", + }, + u"8" : { + u"desc" : u"流通盘", + u"type" : u"long", + }, + u"9" : { + u"desc" : u"合约数量乘数", + u"type" : u"int", + }, + u"10" : { + u"desc" : u"产品代码", + u"type" : u"string", + }, + u"11" : { + u"desc" : u"产品类型", + u"type" : u"string", + }, + u"12" : { + u"desc" : u"交割年份", + u"type" : u"int", + }, + u"13" : { + u"desc" : u"交割月", + u"type" : u"int", + }, + u"14" : { + u"desc" : u"市价单最大下单量(买)", + u"type" : u"int", + }, + u"15" : { + u"desc" : u"市价单最小下单量(买)", + u"type" : u"int", + }, + u"16" : { + u"desc" : u"限价单最大下单量(买)", + u"type" : u"int", + }, + u"17" : { + u"desc" : u"限价单最小下单量(买)", + u"type" : u"int", + }, + u"18" : { + u"desc" : u"最小变动价位", + u"type" : u"double", + }, + u"19" : { + u"desc" : u"创建日", + u"type" : u"int", + }, + u"20" : { + u"desc" : u"上市日", + u"type" : u"int", + }, + u"21" : { + u"desc" : u"到期日", + u"type" : u"string", + }, + u"22" : { + u"desc" : u"开始交割日", + u"type" : u"int", + }, + u"23" : { + u"desc" : u"结束交割日", + u"type" : u"int", + }, + u"24" : { + u"desc" : u"合约生命周期状态", + u"type" : u"int", + }, + u"25" : { + u"desc" : u"当前是否交易", + u"type" : u"int", + }, + u"26" : { + u"desc" : u"持仓类型", + u"type" : u"int", + }, + u"27" : { + u"desc" : u"持仓日期类型", + u"type" : u"int", + }, + u"28" : { + u"desc" : u"多头保证金率", + u"type" : u"double", + }, + u"29" : { + u"desc" : u"空头保证金率", + u"type" : u"double", + }, + u"30" : { + u"desc" : u"是否主力合约", + u"type" : u"int", + }, + u"31" : { + u"desc" : u"是否近月合约", + u"type" : u"int", + }, + u"32" : { + u"desc" : u"合约在RZRK的代码", + u"type" : u"string", + }, + u"33" : { + u"desc" : u"合约在交易所的代码", + u"type" : u"string", + }, + u"34" : { + u"desc" : u"是否夜盘合约", + u"type" : u"int", + }, + u"35" : { + u"desc" : u"利息额", + u"type" : u"double", + }, + u"36" : { + u"desc" : u"除权除息", + u"type" : u"double", + }, + u"37" : { + u"desc" : u"停牌", + u"type" : u"int", + }, + u"38" : { + u"desc" : u"履行方式", + u"type" : u"int", + }, + u"39" : { + u"desc" : u"单位保证金", + u"type" : u"double", + }, + u"40" : { + u"desc" : u"行权价", + u"type" : u"double", + }, + u"41" : { + u"desc" : u"期权行权日", + u"type" : u"int", + }, + u"42" : { + u"desc" : u"期权到期日", + u"type" : u"int", + }, + u"43" : { + u"desc" : u"整手数", + u"type" : u"int", + }, + u"44" : { + u"desc" : u"币种", + u"type" : u"int", + }, + u"45" : { + u"desc" : u"市场代码", + u"type" : u"string", + }, + u"46" : { + u"desc" : u"前日成交量", + u"type" : u"long", + }, + u"47" : { + u"desc" : u"无风险收益率", + u"type" : u"double", + }, + u"48" : { + u"desc" : u"历史收益率", + u"type" : u"double", + }, + u"49" : { + u"desc" : u"标的合约", + u"type" : u"string", + }, + u"50" : { + u"desc" : u"投资者适当性管理分类", + u"type" : u"int", + }, + u"51" : { + u"desc" : u"标识港股是否为标的证券", + u"type" : u"int", + }, + u"52" : { + u"desc" : u"最小回购天数", + u"type" : u"int", + }, + u"53" : { + u"desc" : u"最大回购天数", + u"type" : u"int", + }, + u"54" : { + u"desc" : u"参考汇率买入价", + u"type" : u"double", + }, + u"55" : { + u"desc" : u"参考汇率卖出价", + u"type" : u"double", + }, + u"56" : { + u"desc" : u"每百元应计利息额", + u"type" : u"double", + }, + u"57" : { + u"desc" : u"证券类型 1.全国股转:标识分层信息", + u"type" : u"string", + }, + u"58" : { + u"desc" : u"持仓主力合约", + u"type" : u"int", + }, + u"59" : { + u"desc" : u"单票估值", + u"type" : u"double", + }, + u"60" : { + u"desc" : u"到期收益率", + u"type" : u"double", + }, + u"61" : { + u"desc" : u"注册资本", + u"type" : u"int", + }, + u"62" : { + u"desc" : u"最大有效申报范围", + u"type" : u"double", + }, + u"63" : { + u"desc" : u"最小有效申报范围", + u"type" : u"double", + }, + u"64" : { + u"desc" : u"同股同权比例", + u"type" : u"double", + }, + u"65" : { + u"desc" : u"证券种类", + u"type" : u"long", + }, + u"66" : { + u"desc" : u"转换比例", + u"type" : u"double", + }, + u"67" : { + u"desc" : u"原因", + u"type" : u"string", + }, + u"68" : { + u"desc" : u"要约收购信息", + u"type" : u"string", + }, + u"69" : { + u"desc" : u"证券种类和属性高位", + u"type" : u"int", + }, + u"70" : { + u"desc" : u"证券种类和属性低位", + u"type" : u"int", + }, + u"71" : { + u"desc" : u"市价单最大下单量(卖)", + u"type" : u"int", + }, + u"72" : { + u"desc" : u"市价单最小下单量(卖)", + u"type" : u"int", + }, + u"73" : { + u"desc" : u"限价单最大下单量(卖)", + u"type" : u"int", + }, + u"74" : { + u"desc" : u"限价单最小下单量(卖)", + u"type" : u"int", + }, + u"75" : { + u"desc" : u"收费方式", + u"type" : u"int", + }, + u"76" : { + u"desc" : u"分红方式", + u"type" : u"int", + }, + u"77" : { + u"desc" : u"投资期限", + u"type" : u"int", + }, + u"78" : { + u"desc" : u"投资品种", + u"type" : u"int", + }, + u"79" : { + u"desc" : u"风险等级", + u"type" : u"int", + }, + u"80" : { + u"desc" : u"起息日", + u"type" : u"int", + }, + u"81" : { + u"desc" : u"交易状态", + u"type" : u"string", + }, + u"82" : { + u"desc" : u"交易类型", + u"type" : u"int", + }, + u"83" : { + u"desc" : u"证券级别", + u"type" : u"string", + }, + u"84" : { + u"desc" : u"可购买额度", + u"type" : u"double", + }, + u"85" : { + u"desc" : u"净值", + u"type" : u"double", + }, + u"86" : { + u"desc" : u"预计年化收益率", + u"type" : u"double", + }, + u"87" : { + u"desc" : u"发行机构简称", + u"type" : u"string", + }, + u"88" : { + u"desc" : u"认购开始日期", + u"type" : u"int", + }, + u"89" : { + u"desc" : u"认购结束日期", + u"type" : u"int", + }, + u"90" : { + u"desc" : u"发行机构编号", + u"type" : u"string", + }, + u"91" : { + u"desc" : u"是否逐笔签约", + u"type" : u"int", + }, + u"92" : { + u"desc" : u"面值", + u"type" : u"double", + }, + u"95" : { + u"desc" : u"盘后定价委托数量上限(买)", + u"type" : u"int", + }, + u"96" : { + u"desc" : u"盘后定价委托数量下限(买)", + u"type" : u"int", + }, + u"97" : { + u"desc" : u"盘后定价委托数量上限(卖)", + u"type" : u"int", + }, + u"98" : { + u"desc" : u"盘后定价委托数量下限(卖)", + u"type" : u"int", + }, + }, + }, + u"2001" : { + u"desc" : u"指数板块信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"", + u"type" : u"string", + }, + }, + }, + u"2002" : { + u"desc" : u"行业板块信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"板块名称", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"成分股市场代码", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"成分股股票代码", + u"type" : u"string", + }, + u"5" : { + u"desc" : u"板块类别", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"板块代码", + u"type" : u"string", + }, + }, + }, + u"2004" : { + u"desc" : u"ETF申赎清单信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"基金代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"基金名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"现金差额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"最小申购、赎回单位净值", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"基金份额净值", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"预估现金差额", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"现金替代比例上限", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"是否需要公布IOPV", + u"type" : u"int", + }, + u"8" : { + u"desc" : u"最小申购、赎回单位", + u"type" : u"int", + }, + u"9" : { + u"desc" : u"申购的允许情况", + u"type" : u"int", + }, + u"10" : { + u"desc" : u"赎回的允许情况", + u"type" : u"int", + }, + u"11" : { + u"desc" : u"申购上限", + u"type" : u"double", + }, + u"12" : { + u"desc" : u"赎回上限", + u"type" : u"double", + }, + u"13" : { + u"desc" : u"成份股信息", + u"type" : u"string", + }, + u"14" : { + u"desc" : u"成份股代码", + u"type" : u"string", + }, + u"15" : { + u"desc" : u"成份股名称", + u"type" : u"string", + }, + u"16" : { + u"desc" : u"成份股数量", + u"type" : u"int", + }, + u"17" : { + u"desc" : u"现金替代标志", + u"type" : u"int", + }, + u"18" : { + u"desc" : u"申购现金替代溢价比率", + u"type" : u"double", + }, + u"19" : { + u"desc" : u"申购替代金额", + u"type" : u"double", + }, + u"20" : { + u"desc" : u"赎回现金替代折价比率", + u"type" : u"double", + }, + u"21" : { + u"desc" : u"赎回替代金额", + u"type" : u"double", + }, + u"22" : { + u"desc" : u"成份股所属市场", + u"type" : u"int", + }, + u"23" : { + u"desc" : u"映射代码", + u"type" : u"string", + }, + u"24" : { + u"desc" : u"是否实物对价申赎", + u"type" : u"int", + }, + u"25" : { + u"desc" : u"占净值比例", + u"type" : u"double", + }, + u"26" : { + u"desc" : u"持股数", + u"type" : u"int", + }, + u"27" : { + u"desc" : u"持仓市值", + u"type" : u"double", + }, + }, + }, + u"2005" : { + u"desc" : u"概念板块信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"板块代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"板块名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"板块成分股", + u"type" : u"string", + }, + }, + }, + u"2006" : { + u"desc" : u"债券信息", + u"periods" : [86400000], + u"fields" : { + }, + }, + u"2007" : { + u"desc" : u"期权组合策略信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"组合策略编码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"组合策略名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"组合自动解除日设置", + u"type" : u"int", + }, + u"3" : { + u"desc" : u"成份合约到期日要求", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"成份合约标的要求", + u"type" : u"int", + }, + u"5" : { + u"desc" : u"是否适用非标合约", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"适用标的清单", + u"type" : u"string", + }, + u"9" : { + u"desc" : u"成分合约个数", + u"type" : u"int", + }, + u"10" : { + u"desc" : u"成份合约信息", + u"type" : u"string", + }, + }, + }, + u"2008" : { + u"desc" : u"地域板块信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"板块代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"板块名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"板块成分股", + u"type" : u"string", + }, + }, + }, + u"2009" : { + u"desc" : u"板块信息(所有)", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"板块代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"板块名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"板块类别", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"分类等级", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"板块成分股", + u"type" : u"string", + }, + u"5" : { + u"desc" : u"成分板块", + u"type" : u"string", + }, + u"6" : { + u"desc" : u"板块原始代码", + u"type" : u"string", + }, + }, + }, + u"2010" : { + u"desc" : u"转融通证券出借业务", + u"periods" : [86400000], + u"fields" : { + u"S" : { + u"desc" : u"证券代码", + u"type" : u"string", + }, + u"0" : { + u"desc" : u"期限费率信息", + u"type" : u"string", + }, + }, + }, + u"2011" : { + u"desc" : u"ST变更历史", + u"periods" : [86400000], + u"fields" : { + }, + }, + u"2012" : { + u"desc" : u"板块成分股变动历史", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"调入成份股", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"调出成份股", + u"type" : u"string", + }, + }, + }, + u"2013" : { + u"desc" : u"港股通持股统计", + u"periods" : [86400000], + u"fields" : { + u"1" : { + u"desc" : u"持股量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"持股市值", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"持股数量占比", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"净买入", + u"type" : u"double", + }, + }, + }, + u"2014" : { + u"desc" : u"港股通持股明细", + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"持股机构名称", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"持股量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"持股市值", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"持股数量占比", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"净买入", + u"type" : u"double", + }, + }, + }, + u"2015" : { + u"desc" : u"新股申购信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"证券代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"代码简称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"所属市场", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"发行总量(股)", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"网上发行量(股)", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"申购代码", + u"type" : u"string", + }, + u"6" : { + u"desc" : u"申购上限(股)", + u"type" : u"long", + }, + u"7" : { + u"desc" : u"发行价格", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"产品类型: 1-股票 2-债券 3-基金 4-科创板", + u"type" : u"int", + }, + u"9" : { + u"desc" : u"申购开始日期", + u"type" : u"int", + }, + }, + }, + u"2016" : { + u"desc" : u"融资融券交易信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"融资买入额", + u"type" : u"long", + }, + u"1" : { + u"desc" : u"融资余额", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"融资偿还额", + u"type" : u"long", + }, + u"3" : { + u"desc" : u"融券卖出量", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"融券余量", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"融券偿还量", + u"type" : u"long", + }, + u"6" : { + u"desc" : u"融券余额", + u"type" : u"long", + }, + u"7" : { + u"desc" : u"融资融券余额", + u"type" : u"long", + }, + }, + }, + u"2017" : { + u"desc" : u"市场涨跌停相关数据统计", + u"periods" : [60000], + u"fields" : { + u"0" : { + u"desc" : u"昨日涨停股今日平均涨幅", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"涨停股数", + u"type" : u"int", + }, + u"2" : { + u"desc" : u"跌停股数", + u"type" : u"int", + }, + u"3" : { + u"desc" : u"非一字涨停股数", + u"type" : u"int", + }, + }, + }, + u"2018" : { + u"desc" : u"实时行情委买卖统计", + u"periods" : [60000], + u"fields" : { + u"0" : { + u"desc" : u"委买均价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"委买总量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"委卖均价", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"委卖总量", + u"type" : u"long", + }, + }, + }, + u"2025" : { + u"desc" : u"红利分配方案信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"公告日期", + }, + u"1" : { + u"desc" : u"分红方案", + }, + u"2" : { + u"desc" : u"方案内容", + }, + u"3" : { + u"desc" : u"股票代码", + }, + u"4" : { + u"desc" : u"股票市场", + }, + u"5" : { + u"desc" : u"每股股利", + }, + u"6" : { + u"desc" : u"每股红股", + }, + u"7" : { + u"desc" : u"每股转增股本", + }, + u"8" : { + u"desc" : u"每股配股数", + }, + u"9" : { + u"desc" : u"配股价格", + }, + u"a" : { + u"desc" : u"股权登记日", + }, + u"b" : { + u"desc" : u"除权除息日", + }, + u"c" : { + u"desc" : u"派息日", + }, + u"d" : { + u"desc" : u"红股/转增股上市日", + }, + u"e" : { + u"desc" : u"分红年度", + }, + }, + }, + u"2032" : { + u"desc" : u"国债收益率", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"10年期收益率", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"1年期收益率", + u"type" : u"double", + }, + }, + }, + u"2999" : { + u"desc" : u"实时行情(全市场L1)", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"当前价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"成交总量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"成交总额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"持仓量", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"昨结算", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"成交笔数", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"市盈率1", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"市盈率2", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"开盘价", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"最高价", + u"type" : u"double", + }, + u"a" : { + u"desc" : u"最低价", + u"type" : u"double", + }, + u"b" : { + u"desc" : u"今结算", + u"type" : u"double", + }, + u"c" : { + u"desc" : u"前收盘", + u"type" : u"double", + }, + u"d" : { + u"desc" : u"多档委买价", + u"type" : u"vdouble", + }, + u"e" : { + u"desc" : u"多档委买量", + u"type" : u"vint", + }, + u"f" : { + u"desc" : u"多档委卖价", + u"type" : u"vdouble", + }, + u"g" : { + u"desc" : u"多档委卖量", + u"type" : u"vint", + }, + u"h" : { + u"desc" : u"证券状态", + u"type" : u"int", + }, + u"v" : { + u"desc" : u"原始成交总量", + u"type" : u"long", + }, + }, + }, + u"3000" : { + u"desc" : u"实时行情(L1)", + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"当前价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"成交总量", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"成交总额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"持仓量", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"昨结算", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"成交笔数", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"市盈率1", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"市盈率2", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"开盘价", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"最高价", + u"type" : u"double", + }, + u"a" : { + u"desc" : u"最低价", + u"type" : u"double", + }, + u"b" : { + u"desc" : u"今结算", + u"type" : u"double", + }, + u"c" : { + u"desc" : u"前收盘", + u"type" : u"double", + }, + u"d" : { + u"desc" : u"多档委买价", + u"type" : u"vdouble", + }, + u"e" : { + u"desc" : u"多档委买量", + u"type" : u"vint", + }, + u"f" : { + u"desc" : u"多档委卖价", + u"type" : u"vdouble", + }, + u"g" : { + u"desc" : u"多档委卖量", + u"type" : u"vint", + }, + u"h" : { + u"desc" : u"证券状态", + u"type" : u"int", + }, + u"v" : { + u"desc" : u"原始成交总量", + u"type" : u"long", + }, + }, + }, + u"3001" : { + u"desc" : u"K线", + u"periods" : [60000, 300000, 3600000, 86400000], + u"fields" : { + u"0" : { + u"desc" : u"开盘价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"最高价", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"最低价", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"收盘价", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"前收盘价", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"成交量", + u"type" : u"long", + }, + u"6" : { + u"desc" : u"成交额", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"持仓量", + u"type" : u"int", + }, + u"8" : { + u"desc" : u"流通盘", + u"type" : u"long", + }, + u"9" : { + u"desc" : u"总股本", + u"type" : u"long", + }, + u"a" : { + u"desc" : u"当日除权系数", + u"type" : u"double", + }, + u"b" : { + u"desc" : u"总的除权系数", + u"type" : u"double", + }, + u"c" : { + u"desc" : u"停牌标志", + u"type" : u"int", + }, + u"d" : { + u"desc" : u"今结算", + u"type" : u"double", + }, + }, + }, + u"3002" : { + u"desc" : u"分时行情", + u"periods" : [60000], + u"fields" : { + u"0" : { + u"desc" : u"价格", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"成交量", + u"type" : u"int", + }, + }, + }, + u"3004" : { + u"desc" : u"快照指标", + u"periods" : [60000], + u"fields" : { + u"z" : { + u"desc" : u"量比", + u"type" : u"double", + }, + u"y" : { + u"desc" : u"1分钟涨速(%)", + u"type" : u"double", + }, + u"x" : { + u"desc" : u"5分钟涨速(%)", + u"type" : u"double", + }, + u"w" : { + u"desc" : u"3日涨幅(%)", + u"type" : u"double", + }, + u"v" : { + u"desc" : u"5日涨幅(%)", + u"type" : u"double", + }, + u"u" : { + u"desc" : u"10日涨幅(%)", + u"type" : u"double", + }, + u"t" : { + u"desc" : u"3日换手(%)", + u"type" : u"double", + }, + u"s" : { + u"desc" : u"5日换手(%)", + u"type" : u"double", + }, + u"r" : { + u"desc" : u"10日换手(%)", + u"type" : u"double", + }, + }, + }, + u"4" : { + u"desc" : u"节假日信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"日期", + u"type" : u"long", + }, + u"1" : { + u"desc" : u"节假日说明", + u"type" : u"string", + }, + }, + }, + u"3005" : { + u"desc" : u"港股盘前价格优化(集合竞价阶段价格限制)", + u"periods" : [0, 86400000], + u"fields" : { + u"0" : { + u"desc" : u"参考价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"买盘价格下限", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"买盘价格上限", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"卖盘价格下限", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"卖盘价格上限", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"市场状态", + u"type" : u"int", + }, + }, + }, + u"3006" : { + u"desc" : u"港股通资金流向", + u"periods" : [60000, 86400000], + u"fields" : { + u"0" : { + u"desc" : u"HGT北向买入资金", + u"type" : u"long", + }, + u"1" : { + u"desc" : u"HGT北向卖出资金", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"HGT南向买入资金", + u"type" : u"long", + }, + u"3" : { + u"desc" : u"HGT南向卖出资金", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"SGT北向买入资金", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"SGT北向卖出资金", + u"type" : u"long", + }, + u"6" : { + u"desc" : u"SGT南向买入资金", + u"type" : u"long", + }, + u"7" : { + u"desc" : u"SGT南向卖出资金", + u"type" : u"long", + }, + u"8" : { + u"desc" : u"HGT北向资金净流入", + u"type" : u"long", + }, + u"9" : { + u"desc" : u"HGT北向当日资金余额", + u"type" : u"long", + }, + u"a" : { + u"desc" : u"HGT南向资金净流入", + u"type" : u"long", + }, + u"b" : { + u"desc" : u"HGT南向当日资金余额", + u"type" : u"long", + }, + u"c" : { + u"desc" : u"SGT北向资金净流入", + u"type" : u"long", + }, + u"d" : { + u"desc" : u"SGT北向当日资金余额", + u"type" : u"long", + }, + u"e" : { + u"desc" : u"SGT南向资金净流入", + u"type" : u"long", + }, + u"f" : { + u"desc" : u"SGT南向当日资金余额", + u"type" : u"long", + }, + }, + }, + u"3030" : { + u"desc" : u"ETF统计数据", + u"periods" : [0], + u"fields" : { + u"0" : { + u"desc" : u"申购笔数", + u"type" : u"int", + }, + u"1" : { + u"desc" : u"申购数量", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"申购金额", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"赎回笔数", + u"type" : u"int", + }, + u"4" : { + u"desc" : u"赎回数量", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"赎回金额", + u"type" : u"double", + }, + }, + }, + u"4000" : { + u"desc" : u"除权除息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"每股股利(税前,元)", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"每股红股(股)", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"每股转增股本(股)", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"每股配股数(股)", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"配股价格(元)", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"是否股改", + u"type" : u"int", + }, + u"d" : { + u"desc" : u"除权系数 * 10^12", + u"type" : u"long", + }, + }, + }, + u"4002" : { + u"desc" : u"内外盘", + u"periods" : [60000, 300000, 3600000, 86400000], + u"fields" : { + u"0" : { + u"desc" : u"内盘", + u"type" : u"long", + }, + u"1" : { + u"desc" : u"外盘", + u"type" : u"long", + }, + u"2" : { + u"desc" : u"委买总量", + u"type" : u"long", + }, + u"3" : { + u"desc" : u"委买均价", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"委卖总量", + u"type" : u"long", + }, + u"5" : { + u"desc" : u"委卖均价", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"成交笔数", + u"type" : u"int", + }, + u"7" : { + u"desc" : u"委买量变化值", + u"type" : u"long", + }, + u"8" : { + u"desc" : u"委卖量变化值", + u"type" : u"long", + }, + }, + }, + u"4003" : { + u"periods" : [60000,86400000], + u"fields" : { + }, + }, + u"4004" : { + u"desc" : u"可转债基础信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"可转债代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"可转债简称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"正股代码", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"正股简称", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"发行年限", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"面值", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"发行价格", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"发行总额(元)", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"债券余额(元)", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"起息日期", + u"type" : u"int", + }, + u"10" : { + u"desc" : u"到期日期", + u"type" : u"int", + }, + u"11" : { + u"desc" : u"利率类型", + u"type" : u"string", + }, + u"12" : { + u"desc" : u"票面利率", + u"type" : u"double", + }, + u"13" : { + u"desc" : u"补偿利率", + u"type" : u"double", + }, + u"14" : { + u"desc" : u"年付息次数", + u"type" : u"int", + }, + u"15" : { + u"desc" : u"上市日期", + u"type" : u"int", + }, + u"16" : { + u"desc" : u"摘牌日", + u"type" : u"int", + }, + u"17" : { + u"desc" : u"上市地点", + u"type" : u"string", + }, + u"18" : { + u"desc" : u"转股起始日", + u"type" : u"int", + }, + u"19" : { + u"desc" : u"转股截止日", + u"type" : u"int", + }, + u"20" : { + u"desc" : u"初始转股价", + u"type" : u"double", + }, + u"21" : { + u"desc" : u"最新转股价", + u"type" : u"double", + }, + u"22" : { + u"desc" : u"利率说明", + u"type" : u"string", + }, + }, + }, + u"4005" : { + u"desc" : u"可转债转股价格历史变动", + u"periods" : [86400000], + u"fields" : { + u"S" : { + u"desc" : u"股票", + u"type" : u"string", + }, + u"C" : { + u"desc" : u"转股价历史变动信息", + u"type" : u"string", + }, + }, + }, + u"4011" : { + u"desc" : u"etfiopv", + u"periods" : [60000, 86400000], + u"fields" : { + u"0" : { + u"desc" : u"开盘价", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"最高价", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"最低价", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"收盘价", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"前收盘价", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"成交量", + u"type" : u"long", + }, + u"6" : { + u"desc" : u"成交额", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"持仓量", + u"type" : u"int", + }, + u"8" : { + u"desc" : u"流通盘", + u"type" : u"long", + }, + u"9" : { + u"desc" : u"总股本", + u"type" : u"long", + }, + u"a" : { + u"desc" : u"当日除权系数", + u"type" : u"double", + }, + u"b" : { + u"desc" : u"总的除权系数", + u"type" : u"double", + }, + u"c" : { + u"desc" : u"停牌标志", + u"type" : u"int", + }, + u"d" : { + u"desc" : u"今结算", + u"type" : u"double", + }, + u"e" : { + u"desc" : u"涨停价", + u"type" : u"double", + }, + u"f" : { + u"desc" : u"跌停价", + u"type" : u"double", + }, + }, + }, + u"4015" : { + u"desc" : u"期货仓单", + u"periods" : [86400000], + u"fields" : { + u"1" : { + u"desc" : u"仓库", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"仓单", + u"type" : u"string", + }, + }, + }, + u"4020" : { + u"desc" : u"退市可转债信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"可转债代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"可转债简称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"正股代码", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"正股名称", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"上市首日收盘价", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"最后交易价格", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"最低收盘价", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"最高收盘价", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"发行规模(亿)", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"回售规模(亿)", + u"type" : u"double", + }, + u"10" : { + u"desc" : u"剩余规模(亿)", + u"type" : u"double", + }, + u"11" : { + u"desc" : u"发行日期", + u"type" : u"int", + }, + u"12" : { + u"desc" : u"上市日期", + u"type" : u"int", + }, + u"13" : { + u"desc" : u"最后交易日", + u"type" : u"int", + }, + u"14" : { + u"desc" : u"到期日期", + u"type" : u"int", + }, + u"15" : { + u"desc" : u"存续年限(年)", + u"type" : u"double", + }, + u"16" : { + u"desc" : u"退市原因", + u"type" : u"string", + }, + }, + }, + u"4021" : { + u"desc" : u"代发可转债信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"正股代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"正股名称", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"可转债代码", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"可转债简称", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"方案进展", + u"type" : u"int", + }, + u"5" : { + u"desc" : u"进展公告日", + u"type" : u"int", + }, + u"6" : { + u"desc" : u"总发行规模(亿元)", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"评级", + u"type" : u"string", + }, + u"8" : { + u"desc" : u"股东配售率(%)", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"转股价", + u"type" : u"double", + }, + u"10" : { + u"desc" : u"正股每股净资产", + u"type" : u"double", + }, + u"11" : { + u"desc" : u"每股配售(元)", + u"type" : u"double", + }, + u"12" : { + u"desc" : u"股权登记日", + u"type" : u"int", + }, + u"13" : { + u"desc" : u"网上发行规模(亿元)", + u"type" : u"double", + }, + u"14" : { + u"desc" : u"中签率(%)", + u"type" : u"double", + }, + u"15" : { + u"desc" : u"单账户中签(顶格)", + u"type" : u"double", + }, + u"16" : { + u"desc" : u"申购户数(万户)", + u"type" : u"double", + }, + u"17" : { + u"desc" : u"网下顶格(亿元)", + u"type" : u"double", + }, + u"18" : { + u"desc" : u"顶格获配(万元)", + u"type" : u"double", + }, + u"19" : { + u"desc" : u"网下户数(户)", + u"type" : u"double", + }, + u"20" : { + u"desc" : u"包销比例(%)", + u"type" : u"double", + }, + u"21" : { + u"desc" : u"网上申购代码", + u"type" : u"string", + }, + }, + }, + u"4999" : { + u"desc" : u"除权除息(所有股票)", + u"periods" : [86400000], + u"fields" : { + }, + }, + u"5001" : { + u"periods" : [86400000], + u"fields" : { + }, + }, + u"5002" : { + u"desc" : u"龙虎榜", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"", + u"type" : u"string", + }, + u"5" : { + u"desc" : u"", + u"type" : u"string", + }, + u"6" : { + u"desc" : u"", + u"type" : u"string", + }, + u"7" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B10" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B11" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B12" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B13" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B14" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B15" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B20" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B21" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B22" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B23" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B24" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B25" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B30" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B31" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B32" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B33" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B34" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B35" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B40" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B41" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B42" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B43" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B44" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B45" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B50" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B51" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B52" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B53" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B54" : { + u"desc" : u"", + u"type" : u"string", + }, + u"B55" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S10" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S11" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S12" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S13" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S14" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S15" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S20" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S21" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S22" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S23" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S24" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S25" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S30" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S31" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S32" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S33" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S34" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S35" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S40" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S41" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S42" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S43" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S44" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S45" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S50" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S51" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S52" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S53" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S54" : { + u"desc" : u"", + u"type" : u"string", + }, + u"S55" : { + u"desc" : u"", + u"type" : u"string", + }, + }, + }, + u"5003" : { + u"desc" : u"分级基金", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"", + u"type" : u"double", + }, + u"1" : { + u"desc" : u"", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"", + u"type" : u"double", + }, + }, + }, + u"5004" : { + u"desc" : u"历史主力合约", + u"periods" : [86400000], + u"fields" : { + u"33" : { + u"desc" : u"合约在交易所的代码", + u"type" : u"string", + }, + u"150" : { + u"desc" : u"期货统一规则代码", + u"type" : u"string", + }, + u"151" : { + u"desc" : u"次主力合约代码", + u"type" : u"string", + }, + u"152" : { + u"desc" : u"次主力统一规则编码", + u"type" : u"string", + }, + u"S" : { + u"desc" : u"主力合约代码", + u"type" : u"string", + }, + u"G" : { + u"desc" : u"所属日期", + u"type" : u"long", + }, + }, + }, + u"5008" : { + u"desc" : u"期货日持仓成交排名", + u"periods" : [86400000], + u"fields" : { + u"1" : { + u"desc" : u"当日成交量排名", + }, + u"2" : { + u"desc" : u"持买单量排名", + }, + u"3" : { + u"desc" : u"持卖单量排名", + }, + u"4" : { + u"desc" : u"会员名称", + }, + u"5" : { + u"desc" : u"当日总量", + }, + u"6" : { + u"desc" : u"相比上日增减量", + }, + }, + }, + u"5100" : { + u"desc" : u"大宗交易", + u"periods" : [86400000], + u"fields" : { + }, + }, + u"6" : { + u"desc" : u"汇率", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"外国货币代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"本国货币代码", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"汇率", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"汇率日期", + u"type" : u"long", + }, + u"4" : { + u"desc" : u"汇率买入价", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"汇率卖出价", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"汇率买入浮动比例", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"汇率卖出浮动比例", + u"type" : u"double", + }, + }, + }, + u"6000" : { + u"desc" : u"板块指数信息", + u"periods" : [86400000], + u"fields" : { + u"turn" : { + u"desc" : u"", + u"type" : u"double", + }, + }, + }, + u"7" : { + u"desc" : u"个股交易日信息", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"交易日", + u"type" : u"long", + }, + u"1" : { + u"desc" : u"停牌状态", + u"type" : u"int", + }, + }, + }, + u"7000" : { + u"desc" : u"财务数据", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"股票代码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"股东人数", + u"type" : u"double", + }, + u"2" : { + u"desc" : u"净利润(元)", + u"type" : u"double", + }, + u"3" : { + u"desc" : u"经营现金流量净额", + u"type" : u"double", + }, + u"4" : { + u"desc" : u"投资现金流量净额", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"筹资现金流量净额", + u"type" : u"double", + }, + u"6" : { + u"desc" : u"资产总计", + u"type" : u"double", + }, + u"7" : { + u"desc" : u"负债总计", + u"type" : u"double", + }, + u"8" : { + u"desc" : u"资产负债比率", + u"type" : u"double", + }, + u"9" : { + u"desc" : u"净利润增长率", + u"type" : u"double", + }, + u"10" : { + u"desc" : u"主营业务利润", + u"type" : u"double", + }, + u"11" : { + u"desc" : u"主营增长率", + u"type" : u"double", + }, + u"12" : { + u"desc" : u"流通A/B股", + u"type" : u"double", + }, + u"13" : { + u"desc" : u"人均持股数", + u"type" : u"double", + }, + u"14" : { + u"desc" : u"上年度每股净收益", + u"type" : u"double", + }, + u"15" : { + u"desc" : u"全年预估每股净收益", + u"type" : u"double", + }, + u"16" : { + u"desc" : u"每股收益", + u"type" : u"double", + }, + u"17" : { + u"desc" : u"每股资本公积金", + u"type" : u"double", + }, + u"18" : { + u"desc" : u"每股未分配利润", + u"type" : u"double", + }, + u"19" : { + u"desc" : u"解禁数量", + u"type" : u"double", + }, + u"20" : { + u"desc" : u"解禁日期", + u"type" : u"long", + }, + u"21" : { + u"desc" : u"总质押股份数量", + u"type" : u"double", + }, + u"22" : { + u"desc" : u"质押股份占A股总股本比例", + u"type" : u"double", + }, + u"23" : { + u"desc" : u"发布日期", + u"type" : u"int", + }, + u"24" : { + u"desc" : u"报表年份", + u"type" : u"int", + }, + u"25" : { + u"desc" : u"报表季度", + u"type" : u"int", + }, + }, + }, + u"7011" : { + u"desc" : u"问答", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"问答编号", + }, + u"1" : { + u"desc" : u"问题时间", + }, + u"2" : { + u"desc" : u"问题内容", + }, + u"3" : { + u"desc" : u"回答时间", + }, + u"4" : { + u"desc" : u"回答内容", + }, + }, + }, + u"9000" : { + u"desc" : u"公告", + u"periods" : [0], + u"fields" : { + u"1" : { + u"desc" : u"证券", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"主题", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"摘要", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"格式", + u"type" : u"string", + }, + u"5" : { + u"desc" : u"内容", + u"type" : u"string", + }, + u"6" : { + u"desc" : u"级别", + u"type" : u"int", + }, + u"7" : { + u"desc" : u"类型 0-其他 1-财报类", + u"type" : u"int", + }, + }, + }, + u"9502" : { + u"desc" : u"期权数据", + u"periods" : [86400000], + u"fields" : { + u"0" : { + u"desc" : u"期权编码", + u"type" : u"string", + }, + u"1" : { + u"desc" : u"期权市场", + u"type" : u"string", + }, + u"2" : { + u"desc" : u"标的编码", + u"type" : u"string", + }, + u"3" : { + u"desc" : u"标的市场", + u"type" : u"string", + }, + u"4" : { + u"desc" : u"行权价", + u"type" : u"double", + }, + u"5" : { + u"desc" : u"方向", + u"type" : u"string", + }, + u"6" : { + u"desc" : u"到期月", + u"type" : u"int", + }, + u"7" : { + u"desc" : u"到期日", + u"type" : u"int", + }, + u"8" : { + u"desc" : u"合约类型", + u"type" : u"int", + }, + u"9" : { + u"desc" : u"上市日", + u"type" : u"int", + }, + u"10" : { + u"desc" : u"调整", + u"type" : u"int", + }, + u"11" : { + u"desc" : u"期权名称", + u"type" : u"string", + }, + u"12" : { + u"desc" : u"合约单位", + u"type" : u"int", + }, + }, + }, + u"9506" : { + u"desc" : u"日线涨跌停", + u"periods" : [86400000], + u"fields" : { + u"e" : { + u"desc" : u"涨停价", + u"type" : u"double", + }, + u"f" : { + u"desc" : u"跌停价", + u"type" : u"double", + }, + }, + }, +} \ No newline at end of file diff --git a/src/xtquant/config/pershare_new.ini b/src/xtquant/config/pershare_new.ini new file mode 100644 index 0000000..c7392af --- /dev/null +++ b/src/xtquant/config/pershare_new.ini @@ -0,0 +1,29 @@ +主要指标|PERSHAREINDEX +每股经营活动现金流量|s_fa_ocfps|m_sFaOcfps +每股净资产|s_fa_bps|m_sFaBps +基本每股收益|s_fa_eps_basic|m_sFaEpsBasic +稀释每股收益|s_fa_eps_diluted|m_sFaEpsDiluted +每股未分配利润|s_fa_undistributedps|m_sFaUndistributedps +每股资本公积金|s_fa_surpluscapitalps|m_sFaSurpluscapitalps +扣非每股收益|adjusted_earnings_per_share|m_adjustedEarningsPerShare +净资产收益率|du_return_on_equity|m_duReturnOnEquity +销售毛利率|sales_gross_profit|m_salesGrossProfit +主营收入同比增长|inc_revenue_rate|m_incRevenueRate +净利润同比增长|du_profit_rate|m_duProfitRate +归属于母公司所有者的净利润同比增长|inc_net_profit_rate|m_incNetProfitRate +扣非净利润同比增长|adjusted_net_profit_rate|m_adjustedNetProfitRate +营业总收入滚动环比增长|inc_total_revenue_annual|m_incTotalRevenueAnnual +归属净利润滚动环比增长|inc_net_profit_to_shareholders_annual|m_incNetProfitToShareholdersAnnual +扣非净利润滚动环比增长|adjusted_profit_to_profit_annual|m_adjustedProfitToProfitAnnual +加权净资产收益率|equity_roe|m_equityRoe +摊薄净资产收益率|net_roe|m_netRoe +摊薄总资产收益率|total_roe|m_totalRoe +毛利率|gross_profit|m_grossProfit +净利率|net_profit|m_netProfit +实际税率|actual_tax_rate|m_actualTaxRate +预收款/营业收入|pre_pay_operate_income|m_prePayOperateIncome +销售现金流/营业收入|sales_cash_flow|m_salesCashFlow +资产负债比率|gear_ratio|m_gearRatio +存货周转率|inventory_turnover|m_inventoryTurnover +公告日|m_anntime|m_anntime +报告截止日|m_timetag|m_endtime \ No newline at end of file diff --git a/src/xtquant/config/sharebalance_new_1.ini b/src/xtquant/config/sharebalance_new_1.ini new file mode 100644 index 0000000..13e4b8f --- /dev/null +++ b/src/xtquant/config/sharebalance_new_1.ini @@ -0,0 +1,143 @@ +资产负债表|ASHAREBALANCESHEET +披露日期|m_anntime|m_annTime +截止日期|m_timetag|m_endTime +内部应收款|internal_shoule_recv|m_internalShouldRecv +固定资产清理|fixed_capital_clearance|m_fixedCapitalClearance +应付分保账款|should_pay_money|m_shouldPayMony +结算备付金|settlement_payment|m_settlementPayment +应收保费|receivable_premium|m_receivablePremium +应收分保账款|accounts_receivable_reinsurance|m_accountsReceivableReinsurance +应收分保合同准备金|reinsurance_contract_reserve|m_reinsuranceContractReserve +应收股利|dividends_payable|m_dividendsPayable +应收出口退税|tax_rebate_for_export|m_taxRebateForExport +应收补贴款|subsidies_receivable|m_subsidiesReceivable +应收保证金|deposit_receivable|m_depositReceivable +待摊费用|apportioned_cost|m_apportionedCost +待处理流动资产损益|profit_and_current_assets_with_deal|m_profitAndCurrentAssetsWithDeal +一年内到期的非流动资产|current_assets_one_year|m_currentAssetsOneYear +长期应收款|long_term_receivables|m_longTermReceivables +其他长期投资|other_long_term_investments|m_otherLongTermInvestments +固定资产原值|original_value_of_fixed_assets|m_originalValueOfFixedAssets +固定资产净值|net_value_of_fixed_assets|m_netValueOfFixedAssets +固定资产减值准备|depreciation_reserves_of_fixed_assets|m_depreciationReservesOfFixedAssets +生产性生物资产|productive_biological_assets|m_productiveBiologicalAssets +公益性生物资产|public_welfare_biological_assets|m_publicWelfareBiologicalAssets +油气资产|oil_and_gas_assets|m_oilAndGasAssets +开发支出|development_expenditure|m_developmentExpenditure +股权分置流通权|right_of_split_share_distribution|m_rightSplitShareDistribution +其他非流动资产|other_non_mobile_assets|m_otherNonMobileEssets +应付手续费及佣金|handling_fee_and_commission|m_handlingFeeAndCommission +其他应交款|other_payables|m_otherPayables +应付保证金|margin_payable|m_marginPayable +内部应付款|internal_accounts_payable|m_internalAccountsPayable +预提费用|advance_cost|m_advanceCost +保险合同准备金|insurance_contract_reserve|m_insuranceContractReserve +代理买卖证券款|broker_buying_and_selling_securities|m_brokerBuyingSellingSecurities +代理承销证券款|acting_underwriting_securities|m_actingUnderwritingSecurities +国际票证结算|international_ticket_settlement|m_internationalTicketSettlement +国内票证结算|domestic_ticket_settlement|m_domesticTicketSettlement +递延收益|deferred_income|m_deferredIncome +应付短期债券|short_term_bonds_payable|m_shortTermBondsPayable +长期递延收益|long_term_deferred_income|m_longTermDeferredIncome +未确定的投资损失|undetermined_investment_losses|m_undeterminedInvestmentLosses +拟分配现金股利|quasi_distribution_of_cash_dividends|m_quasiDistributionCashDividends +预计负债|provisions_not|m_provisionsNot +吸收存款及同业存放|cust_bank_dep|m_custBankDep +预计流动负债|provisions|m_provisions +减:库存股|less_tsy_stk|m_lessTsyStk +货币资金|cash_equivalents|m_cashEquivalents +拆出资金|loans_to_oth_banks|m_loansToOthBanks +交易性金融资产|tradable_fin_assets|m_tradableFinAssets +衍生金融资产|derivative_fin_assets|m_derivativeFinAssets +应收票据|bill_receivable|m_billReceivable +应收账款|account_receivable|m_accountReceivable +预付款项|advance_payment|m_advancePayment +应收利息|int_rcv|m_intRcv +其他应收款|other_receivable|m_otherReceivable +买入返售金融资产|red_monetary_cap_for_sale|m_redMonetaryCapForSale +以公允价值计量且其变动计入当期损益的金融资产|agency_bus_assets|m_agencyBusAssets +存货|inventories|m_inventories +其他流动资产|other_current_assets|m_otherCurrentAssets +流动资产合计|total_current_assets|m_totalCurrentAssets +发放贷款及垫款|loans_and_adv_granted|m_loansAndAdvGranted +可供出售金融资产|fin_assets_avail_for_sale|m_finAssetsAvailForSale +持有至到期投资|held_to_mty_invest|m_heldToMtyInvest +长期股权投资|long_term_eqy_invest|m_longTermEqyInvest +投资性房地产|invest_real_estate|m_investRealEstate +累计折旧|accumulated_depreciation|m_accumulatedDepreciation +固定资产|fix_assets|m_fixAssets +在建工程|constru_in_process|m_construInProcess +工程物资|construction_materials|m_constructionMaterials +长期负债|long_term_liabilities|m_longTermLiabilities +无形资产|intang_assets|m_intangAssets +商誉|goodwill|m_goodwill +长期待摊费用|long_deferred_expense|m_longDeferredExpense +递延所得税资产|deferred_tax_assets|m_deferredTaxAssets +非流动资产合计|total_non_current_assets|m_totalNonCurrentAssets +资产总计|tot_assets|m_totAssets +短期借款|shortterm_loan|m_shorttermLoan +向中央银行借款|borrow_central_bank|m_borrowCentralBank +拆入资金|loans_oth_banks|m_loansOthBanks +交易性金融负债|tradable_fin_liab|m_tradableFinLiab +衍生金融负债|derivative_fin_liab|m_derivativeFinLiab +应付票据|notes_payable|m_notesPayable +应付账款|accounts_payable|m_accountsPayable +预收账款|advance_peceipts|m_advancePeceipts +卖出回购金融资产款|fund_sales_fin_assets_rp|m_fundSalesFinAssetsRp +应付职工薪酬|empl_ben_payable|m_emplBenPayable +应交税费|taxes_surcharges_payable|m_taxesSurchargesPayable +应付利息|int_payable|m_intPayable +应付股利|dividend_payable|m_dividendPayable +其他应付款|other_payable|m_otherPayable +一年内到期的非流动负债|non_current_liability_in_one_year|m_nonCurrentLiabilityInOneYear +其他流动负债|other_current_liability|m_otherCurrentLiability +流动负债合计|total_current_liability|m_totalCurrentLiability +长期借款|long_term_loans|m_longTermLoans +应付债券|bonds_payable|m_bondsPayable +长期应付款|longterm_account_payable|m_longtermAccountPayable +专项应付款|grants_received|m_grantsReceived +递延所得税负债|deferred_tax_liab|m_deferredTaxLiab +其他非流动负债|other_non_current_liabilities|m_otherNonCurrentLiabilities +非流动负债合计|non_current_liabilities|m_nonCurrentLiabilities +负债合计|tot_liab|m_totLiab +实收资本(或股本)|cap_stk|m_capStk +资本公积|cap_rsrv|m_capRsrv +专项储备|specific_reserves|m_specificReserves +盈余公积|surplus_rsrv|m_surplusRsrv +一般风险准备|prov_nom_risks|m_provNomRisks +未分配利润|undistributed_profit|m_undistributedProfit +外币报表折算差额|cnvd_diff_foreign_curr_stat|m_cnvdDiffForeignCurrStat +归属于母公司股东权益合计|tot_shrhldr_eqy_excl_min_int|m_totShrhldrEqyExclMinInt +少数股东权益|minority_int|m_minorityInt +所有者权益合计|total_equity|m_totalEquity +负债和股东权益总计|tot_liab_shrhldr_eqy|m_totLiabShrhldrEqy + +现金及存放中央银行款项:企业持有的现金、存放中央银行款项等总额|m_cashAdepositsCentralBank +贵金属:企业(金融)持有的黄金、白银等贵金属存货的成本|m_nobleMetal +存放同业和其它金融机构款项:企业(银行)存放于境内、境外银行和非银行金融机构的款项|m_depositsOtherFinancialInstitutions +短期投资|m_currentInvestment +买入返售金融资产|m_redemptoryMonetaryCapitalSale +应收代位追偿款净额|m_netAmountSubrogation +存出保证金|m_refundableDeposits +保户质押贷款净额|m_netAmountLoanPledged +定期存款|m_fixedTimeDeposit +长期债权投资净额|m_netLongtermDebtInvestments +长期投资|m_permanentInvestment +存出资本保证金|m_depositForcapitalRecognizance +在建工程净额|m_netBalConstructionProgress +独立账户资产|m_separateAccountAssets +代理业务资产|m_capitalInvicariousBussiness +其他资产|m_otherAssets +其中:同业及其他金融机构存放款项|m_depositsWithBanksOtherFinancialIns +应付赔付款|m_indemnityPayable +应付保单红利|m_policyDividendPayable +保户储金及投资款|m_guaranteeInvestmentFunds +预收保费|m_premiumsReceivedAdvance +保单负债|m_insuranceLiabilities +独立账户负债|m_liabilitiesIndependentAccounts +代理业务负债|m_liabilitiesVicariousBusiness +其他负债|m_otherLiablities +资本溢价|m_capitalPremium +保留溢利|m_petainedProfit +交易风险准备|m_provisionTransactionRisk +其他储备|m_otherReserves diff --git a/src/xtquant/config/shareholder_new_1.ini b/src/xtquant/config/shareholder_new_1.ini new file mode 100644 index 0000000..c97a46b --- /dev/null +++ b/src/xtquant/config/shareholder_new_1.ini @@ -0,0 +1,9 @@ +股东数|SHAREHOLDER +公告日期|declareDate|declareDate +截止日期|endDate|endDate +股东总数|shareholder|shareholder +A股东户数|shareholderA|shareholderA +B股东户数|shareholderB|shareholderB +H股东户数|shareholderH|shareholderH +已流通股东户数|shareholderFloat|shareholderFloat +未流通股东户数|shareholderOther|shareholderOther \ No newline at end of file diff --git a/src/xtquant/config/shareincome_new_1.ini b/src/xtquant/config/shareincome_new_1.ini new file mode 100644 index 0000000..481c695 --- /dev/null +++ b/src/xtquant/config/shareincome_new_1.ini @@ -0,0 +1,68 @@ +利润表|ASHAREINCOME +披露日期|m_anntime|m_annTime +截止日期|m_timetag|m_endTime +营业收入|revenue_inc|m_revenueInc +已赚保费|earned_premium|m_earnedPremium +房地产销售收入|real_estate_sales_income|m_realEstateSalesIncome +营业总成本|total_operating_cost|m_totalOperatingCost +房地产销售成本|real_estate_sales_cost|m_realEstateSalesCost +研发费用|research_expenses|m_researchExpenses +退保金|surrender_value|m_surrenderValue +赔付支出净额|net_payments|m_netPayments +提取保险合同准备金净额|net_withdrawal_ins_con_res|m_netWithdrawalInsConRes +保单红利支出|policy_dividend_expenses|m_policyDividendExpenses +分保费用|reinsurance_cost|m_reinsuranceCost +公允价值变动收益|change_income_fair_value|m_changeIncomeFairvalue +期货损益|futures_loss|m_futuresLoss +托管收益|trust_income|m_trustIncome +补贴收入|subsidize_revenue|m_subsidizeRevenue +其他业务利润|other_business_profits|m_otherBusinessProfits +被合并方在合并前实现净利润|net_profit_excl_merged_int_inc|m_netProfitExclMergedIntInc +利息收入|int_inc|m_intInc +手续费及佣金收入|handling_chrg_comm_inc|m_handlingChrgCommInc +手续费及佣金支出|less_handling_chrg_comm_exp|m_lessHandlingChrgCommExp +其他业务成本|other_bus_cost|m_otherBusCost +汇兑收益|plus_net_gain_fx_trans|m_plusNetGainFxTrans +非流动资产处置收益|il_net_loss_disp_noncur_asset|m_ilNetLossDispNoncurAsset +所得税费用|inc_tax|m_incTax +未确认投资损失|unconfirmed_invest_loss|m_unconfirmedInvestLoss +归属于母公司所有者的净利润|net_profit_excl_min_int_inc|m_netProfitExclMinIntInc +利息支出|less_int_exp|m_lessIntExp +其他业务收入|other_bus_inc|m_otherBusInc +营业总收入|revenue|m_revenue +营业成本|total_expense|m_totalExpense +营业税金及附加|less_taxes_surcharges_ops|m_lessTaxesSurchargesOps +销售费用|sale_expense|m_saleExpense +管理费用|less_gerl_admin_exp|m_lessGerlAdminExp +财务费用|financial_expense|m_financialExpense +资产减值损失|less_impair_loss_assets|m_lessImpairLossAssets +投资收益|plus_net_invest_inc|m_plusNetInvestInc +联营企业和合营企业的投资收益|incl_inc_invest_assoc_jv_entp|m_inclIncInvestAssocJvEntp +营业利润|oper_profit|m_operProfit +营业外收入|plus_non_oper_rev|m_plusNonOperRev +营业外支出|less_non_oper_exp|m_lessNonOperExp +利润总额|tot_profit|m_totProfit +净利润|net_profit_incl_min_int_inc|m_netProfitInclMinIntInc +净利润(扣除非经常性损益后)|net_profit_incl_min_int_inc_after|m_netProfitInclMinIntIncAfter +少数股东损益|minority_int_inc|m_minorityIntInc +基本每股收益|s_fa_eps_basic|m_sFaEpsBasic +稀释每股收益|s_fa_eps_diluted|m_sFaEpsDiluted +综合收益总额|total_income|m_totalIncome +归属于少数股东的综合收益总额|total_income_minority|m_totalIncomeMinority +其他收益|other_compreh_inc|m_otherComprehInc + +利息净收入|m_netinterestIncome +手续费及佣金净收入|m_netFeesCommissions +保险业务收入|m_insuranceBusiness +减:分出保费|m_separatePremium +减:提取未到期责任准备金|m_asideReservesUndueLiabilities +赔付支出|m_paymentsInsuranceClaims +减:摊回赔付支出|m_amortizedCompensationExpenses +提取保险责任准备金净额|m_netReserveInsuranceLiability +提取保险责任准备金|m_policyReserve +减:摊回保险责任准备金|m_amortizeInsuranceReserve +保险业务手续费及佣金支出|m_nsuranceFeesCommissionExpenses +业务及管理费|m_operationAdministrativeExpense +减:摊回分保费用|m_amortizedReinsuranceExpenditure +其中:非流动资产处置净损益|m_netProfitLossdisposalNonassets +影响净利润的其他项目|m_otherItemsAffectingNetProfit diff --git a/src/xtquant/config/table2json.lua b/src/xtquant/config/table2json.lua new file mode 100644 index 0000000..0e88d8b --- /dev/null +++ b/src/xtquant/config/table2json.lua @@ -0,0 +1,25 @@ + +-- 将lua中的table转换为json +function table2json(t) + local function serialize(tbl) + local tmp = {} + for k, v in pairs(tbl) do + local k_type = type(k) + local v_type = type(v) + local key = (k_type == "string" and "\"" .. k .. "\":") + or (k_type == "number" and "") + local value = (v_type == "table" and serialize(v)) + or (v_type == "boolean" and tostring(v)) + or (v_type == "string" and "\"" .. v .. "\"") + or (v_type == "number" and v) + tmp[#tmp + 1] = key and value and tostring(key) .. tostring(value) or nil + end + if table.maxn(tbl) == 0 then + return "{" .. table.concat(tmp, ",") .. "}" + else + return "[" .. table.concat(tmp, ",") .. "]" + end + end + assert(type(t) == "table") + return serialize(t) +end \ No newline at end of file diff --git a/src/xtquant/config/top10holder_new_1.ini b/src/xtquant/config/top10holder_new_1.ini new file mode 100644 index 0000000..544a384 --- /dev/null +++ b/src/xtquant/config/top10holder_new_1.ini @@ -0,0 +1,10 @@ +十大股东|十大流通股东|TOP10HOLDER|TOP10FLOWHOLDER +公告日期|declareDate|declareDate +截止日期|endDate|endDate +股东名称|name|name +股东类型|type|type +持股数量|quantity|quantity +变动原因|reason|reason +持股比例|ratio|ratio +股份性质|nature|nature +持股排名|rank|rank diff --git a/src/xtquant/config/tradeTime.txt b/src/xtquant/config/tradeTime.txt new file mode 100644 index 0000000..f783d01 --- /dev/null +++ b/src/xtquant/config/tradeTime.txt @@ -0,0 +1,370 @@ +// AG21:00--02:30 9:00--10:15 10:30-11:30 13:30--15:00 +// ZN21:00--01:00 9:00--11:30 13:30--15:00 +// SC21:00--02:30 9:00--10:15 10:30-11:30 13:30--15:00 + +// (AG),ƽ(AU) 21:00--02:30 +// п(ZN),ͭ(CU),Ǧ(PB),(AL),(NI),(SN),(SS),(AO) 21:00--01:00 +// Ƹ(RB),(HC),ʯ(BU,BUY) 21:00--23:00 +// Ȼ(RU),ȼ(FU),ֽ(SP) 21:00--23:00 + +// (P),(Y) 21:00--23:30 +// (M),(Y),̿(J),ƴһ(A,AX,AY) 21:00--23:00 +// ƴ󶹶(B),ú(JM),ʯ(I),ϩ(L),ϩ(V)21:00--23:00 +// ۱ϩ(PP),Ҷ(EG),(C),׵(CS) 21:00--23:00 +// ϩ(EB),Һʯ(PG),(RR) 21:00--23:00 + +// ֣ (SR,SRX,SRY),޻(CF),(RM),״(MA,ME),PTA(TA)21:00--23:00 +// ú(ZC),(OI),(FG),ɴ(CY) 21:00--23:00 +// (SA),(PF)ռ(SH)ױ(PX) 21:00--23:00 +// Դ ԭ(SC,SCW,SCX,SCY,SCZ) 21:00--02:30 +// ͭ(BC) 21:00--01:00 +// ȼ(LU),20Ž(NR) 21:00--23:00 + +// 1: ʱ 2: ҹ̿ʱ + + +1|SF|AG|555|1261-1440|1-150|541-615|631-690|811-900 +1|SF|AU|555|1261-1440|1-150|541-615|631-690|811-900 +1|SF|AUX|555|1261-1440|1-150|541-615|631-690|811-900 +1|SF|AUY|555|1261-1440|1-150|541-615|631-690|811-900 + +1|SF|ZN|465|1261-1440|1-60|541-615|631-690|811-900 +1|SF|CU|465|1261-1440|1-60|541-615|631-690|811-900 +1|SF|PB|465|1261-1440|1-60|541-615|631-690|811-900 +1|SF|AL|465|1261-1440|1-60|541-615|631-690|811-900 + +1|SF|SN|465|1261-1440|1-60|541-615|631-690|811-900 +1|SF|NI|465|1261-1440|1-60|541-615|631-690|811-900 +1|SF|SS|465|1261-1440|1-60|541-615|631-690|811-900 +1|SF|AO|465|1261-1440|1-60|541-615|631-690|811-900 + +1|SF|RB|345|1261-1380|541-615|631-690|811-900 +1|SF|HC|345|1261-1380|541-615|631-690|811-900 +1|SF|BU|345|1261-1380|541-615|631-690|811-900 +1|SF|BUY|345|1261-1380|541-615|631-690|811-900 +1|SF|RU|345|1261-1380|541-615|631-690|811-900 +1|SF|FU|345|1261-1380|541-615|631-690|811-900 +1|SF|SP|345|1261-1380|541-615|631-690|811-900 + +1|DF|J|345|1261-1380|541-615|631-690|811-900 +1|DF|P|345|1261-1380|541-615|631-690|811-900 + +1|DF|M|345|1261-1380|541-615|631-690|811-900 +1|DF|Y|345|1261-1380|541-615|631-690|811-900 +1|DF|A|345|1261-1380|541-615|631-690|811-900 +1|DF|AX|345|1261-1380|541-615|631-690|811-900 +1|DF|AY|345|1261-1380|541-615|631-690|811-900 +1|DF|B|345|1261-1380|541-615|631-690|811-900 +1|DF|JM|345|1261-1380|541-615|631-690|811-900 +1|DF|I|345|1261-1380|541-615|631-690|811-900 + +1|DF|L|345|1261-1380|541-615|631-690|811-900 +1|DF|V|345|1261-1380|541-615|631-690|811-900 +1|DF|PP|345|1261-1380|541-615|631-690|811-900 +1|DF|EG|345|1261-1380|541-615|631-690|811-900 +1|DF|C|345|1261-1380|541-615|631-690|811-900 +1|DF|CS|345|1261-1380|541-615|631-690|811-900 +1|DF|EB|345|1261-1380|541-615|631-690|811-900 +1|DF|PG|345|1261-1380|541-615|631-690|811-900 +1|DF|RR|345|1261-1380|541-615|631-690|811-900 + +1|ZF|SR|345|1261-1380|541-615|631-690|811-900 +1|ZF|SRX|345|1261-1380|541-615|631-690|811-900 +1|ZF|SRY|345|1261-1380|541-615|631-690|811-900 +1|ZF|CF|345|1261-1380|541-615|631-690|811-900 +1|ZF|RM|345|1261-1380|541-615|631-690|811-900 +1|ZF|MA|345|1261-1380|541-615|631-690|811-900 +1|ZF|ME|345|1261-1380|541-615|631-690|811-900 +1|ZF|TA|345|1261-1380|541-615|631-690|811-900 +1|ZF|ZC|345|1261-1380|541-615|631-690|811-900 +1|ZF|FG|345|1261-1380|541-615|631-690|811-900 +1|ZF|OI|345|1261-1380|541-615|631-690|811-900 +1|ZF|CY|345|1261-1380|541-615|631-690|811-900 +1|ZF|SA|345|1261-1380|541-615|631-690|811-900 +1|ZF|PF|345|1261-1380|541-615|631-690|811-900 +1|ZF|SH|345|1261-1380|541-615|631-690|811-900 +1|ZF|PX|345|1261-1380|541-615|631-690|811-900 + +1|YSWP|CN|976|540-955|1001-1440|1-120 +1|YSWP|ID|976|540-955|1001-1440|1-120 +1|YSWP|IN|976|540-955|1001-1440|1-120 +1|YSWP|SG|976|540-955|1001-1440|1-120 +1|YSWP|SPDCN|976|540-955|1001-1440|1-120 +1|YSWP|SPDID|976|540-955|1001-1440|1-120 +1|YSWP|SPDIN|976|540-955|1001-1440|1-120 +1|YSWP|SPDSG|976|540-955|1001-1440|1-120 + +1|INE|LU|345|1261-1380|541-615|631-690|811-900 +1|INE|NR|345|1261-1380|541-615|631-690|811-900 +1|INE|SC|555|1261-1440|1-150|541-615|631-690|811-900 +1|INE|SCW|555|1261-1440|1-150|541-615|631-690|811-900 +1|INE|SCX|555|1261-1440|1-150|541-615|631-690|811-900 +1|INE|SCY|555|1261-1440|1-150|541-615|631-690|811-900 +1|INE|SCZ|555|1261-1440|1-150|541-615|631-690|811-900 +1|INE|BC|465|1261-1440|1-60|541-615|631-690|811-900 + +2|SF|AG,AU,AUX,AUY|210000-235959|000000-023000 +2|SF|ZN,CU,PB,AL,SN,NI,SS,AO|210000-235959|000000-010000 +2|SF|RB,HC,BU,BUY,RU,FU,SP,BR|210000-230000 + +2|DF|A,AX,AY,B,M,Y,J,JM,I,L,V,PP,EG,C,CS,P,EB,PG,RR|210000-230000 + +2|ZF|SR,SRX,SRY,CF,RM,MA,ME,TA,ZC,FG,OI,CY,SA,PF,RI,SH,PX|210000-230000 + +2|YSWP|CN,ID,IN,SG,SPDCN,SPDID,SPDIN,SPDSG,CNINDEX|164000-235959|000000-020000 + +2|INE|LU,NR|210000-230000 +2|INE|SC,SCW,SCX,SCY,SCZ|210000-235959|000000-023000 +2|INE|BC|210000-235959|000000-010000 + +1|WP|NYMEX:NG|1381|360-1440|1-300 +1|WP|NYMEX:CL|1381|360-1440|1-300 +1|WP|NYMEX:BZ|1381|360-1440|1-300 +1|WP|NYMEX:QG|1381|360-1440|1-300 +1|WP|NYMEX:QM|1381|360-1440|1-300 +1|WP|NYMEX:HO|1381|360-1440|1-300 +1|WP|NYMEX:RB|1381|360-1440|1-300 +1|WP|NYMEX:MCL|1381|360-1440|1-300 +1|WP|NYMEX:PA|1381|360-1440|1-300 +1|WP|NYMEX:PL|1381|360-1440|1-300 +1|WP|CME:6L|1381|360-1440|1-300 +1|WP|CME:GE|1381|360-1440|1-300 +1|WP|CME:6A|1381|360-1440|1-300 +1|WP|CME:6B|1381|360-1440|1-300 +1|WP|CME:6C|1381|360-1440|1-300 +1|WP|CME:6E|1381|360-1440|1-300 +1|WP|CME:6J|1381|360-1440|1-300 +1|WP|CME:6S|1381|360-1440|1-300 +1|WP|CME:6Z|1381|360-1440|1-300 +1|WP|CME:SIR|1381|360-1440|1-300 +1|WP|CME:6M|1381|360-1440|1-300 +1|WP|CME:NKD|1381|360-1440|1-300 +1|WP|CME:ES|1381|360-1440|1-300 +1|WP|CME:MIR|1381|360-1440|1-300 +1|WP|CME:BTC|1381|360-1440|1-300 +1|WP|CME:ETH|1381|360-1440|1-300 +1|WP|CME:MBT|1381|360-1440|1-300 +1|WP|CME:MET|1381|360-1440|1-300 +1|WP|CME:NQ|1381|360-1440|1-300 +1|WP|CME:RP|1381|360-1440|1-300 +1|WP|CME:6N|1381|360-1440|1-300 +1|WP|CME:PJY|1381|360-1440|1-300 +1|WP|CME:RY|1381|360-1440|1-300 +1|WP|CME:EMD|1381|360-1440|1-300 +1|WP|CME:M2K|1381|360-1440|1-300 +1|WP|CME:MES|1381|360-1440|1-300 +1|WP|CME:MNQ|1381|360-1440|1-300 +1|WP|CME:RTY|1381|360-1440|1-300 +1|WP|CME:M6A|1381|360-1440|1-300 +1|WP|CME:M6B|1381|360-1440|1-300 +1|WP|CME:M6E|1381|360-1440|1-300 +1|WP|CME:M6J|1381|360-1440|1-300 +1|WP|CME:MCD|1381|360-1440|1-300 +1|WP|CME:MSF|1381|360-1440|1-300 +1|WP|CME:RU|1381|360-1440|1-300 +1|WP|CME:HE|276|1290-1440|1-125 +1|WP|CME:GF|276|1290-1440|1-125 +1|WP|CME:LE|276|1290-1440|1-125 +1|WP|HKFE:MCS|1066|1155-1440|1-180|511-1110 +1|WP|HKFE:CUS|1066|1155-1440|1-180|511-1110 +1|WP|HKFE:CEU|1066|1155-1440|1-180|511-1110 +1|WP|HKFE:CJP|1066|1155-1440|1-180|511-1110 +1|WP|HKFE:CAU|1066|1155-1440|1-180|511-1110 +1|WP|HKFE:UCN|1066|1155-1440|1-180|511-1110 +1|WP|HKFE:HHI|961|1035-1440|1-180|556-720|781-990 +1|WP|HKFE:MCH|961|1035-1440|1-180|556-720|781-990 +1|WP|HKFE:HSI|961|1035-1440|1-180|556-720|781-990 +1|WP|HKFE:MHI|961|1035-1440|1-180|556-720|781-990 +1|WP|HKFE:HTI|961|1035-1440|1-180|556-720|781-990 +1|WP|HKFE:CHI|1036|1035-1440|1-180|541-990 +1|WP|HKFE:MCA|1036|1035-1440|1-180|541-990 +1|WP|HKFE:MPS|1036|1035-1440|1-180|541-990 +1|WP|HKFE:MVI|1036|1035-1440|1-180|541-990 +1|WP|HKFE:MEI|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MIA|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MMA|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MDI|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MTD|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:CHN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:EMN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MPN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MVN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MSN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:EAN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MDN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MIN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MJU|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MMN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MTN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MXJ|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MAN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MCN|1036|1155-1440|1-180|541-1110 +1|WP|HKFE:MTW|1051|870-1440|1-180|526-825 +1|WP|HKFE:MWN|1051|1155-1440|1-180|526-1110 +1|WP|HKFE:GDU|1066|1035-1440|1-180|511-990 +1|WP|HKFE:FEM|1036|1035-1440|1-180|541-990 +1|WP|HKFE:LUA|1036|1035-1440|1-180|541-990 +1|WP|HKFE:LUC|1036|1035-1440|1-180|541-990 +1|WP|HKFE:LUN|1036|1035-1440|1-180|541-990 +1|WP|HKFE:LUP|1036|1035-1440|1-180|541-990 +1|WP|HKFE:LUS|1036|1035-1440|1-180|541-990 +1|WP|HKFE:LUZ|1036|1035-1440|1-180|541-990 +1|WP|COMEX:ALI|1381|360-1440|1-300 +1|WP|COMEX:HG|1381|360-1440|1-300 +1|WP|COMEX:MHG|1381|360-1440|1-300 +1|WP|COMEX:QC|1381|360-1440|1-300 +1|WP|COMEX:GC|1381|360-1440|1-300 +1|WP|COMEX:SI|1381|360-1440|1-300 +1|WP|COMEX:MGC|1381|360-1440|1-300 +1|WP|COMEX:QO|1381|360-1440|1-300 +1|WP|COMEX:QI|1381|360-1440|1-300 +1|WP|COMEX:SGU|1381|360-1440|1-300 +1|WP|COMEX:SIL|1381|360-1440|1-300 +1|WP|ICE.IFEU:T|1321|480-1440|1-360 +1|WP|ICE.IFEU:B|1321|480-1440|1-360 +1|WP|ICE.IFEU:G|1321|480-1440|1-360 +1|WP|CBOT:ZL|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:ZM|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:XK|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:ZS|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:XC|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:ZC|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:KE|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:XW|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:ZW|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:ZO|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:ZR|1056|481-1245|1290-1440|1-140 +1|WP|CBOT:MYM|1381|360-1440|1-300 +1|WP|CBOT:YM|1381|360-1440|1-300 +1|WP|CBOT:TN|1381|360-1440|1-300 +1|WP|CBOT:UB|1381|360-1440|1-300 +1|WP|CBOT:ZB|1381|360-1440|1-300 +1|WP|CBOT:ZF|1381|360-1440|1-300 +1|WP|CBOT:ZN|1381|360-1440|1-300 +1|WP|CBOT:ZT|1381|360-1440|1-300 +1|WP|CBOT:10Y|1381|360-1440|1-300 +1|WP|CBOT:2YY|1381|360-1440|1-300 +1|WP|CBOT:30Y|1381|360-1440|1-300 +1|WP|CBOT:5YY|1381|360-1440|1-300 +1|WP|SGX:FEF|1296|1215-1440|1-315|446-1200 +1|WP|SGX:NK|1276|895-1440|1-315|451-865 +1|WP|SGX:UC|1291|1095-1440|1-315|446-1075 +1|WP|SGX:IU|1291|1190-1440|1-315|446-1170 +1|WP|SGX:KU|1291|1190-1440|1-315|446-1170 +1|WP|SGX:TF|605|476-1080 +1|WP|SGX:US|1291|1190-1440|1-315|446-1170 +1|WP|SGX:FCH|1186|1020-1440|1-315|541-990 +1|WP|SGX:IN|1186|1120-1440|1-315|541-1090 +1|WP|SGX:CN|1186|1020-1440|1-315|541-990 +1|WP|SGX:SGP|1246|1070-1440|1-345|511-1040 +1|WP|ICE.IFUS:MME|1321|480-1440|1-360 +1|WP|ICE.IFUS:OJ|361|1200-1440|1-120 +1|WP|ICE.IFUS:CT|1041|540-1440|1-140 +1|WP|ICE.IFUS:KC|556|975-1440|1-90 +1|WP|ICE.IFUS:MFS|1321|480-1440|1-360 +1|WP|ICE.IFUS:SB|571|930-1440|1-60 +1|WP|ICE.IFUS:RS|1101|480-1440|1-140 +1|WP|ICE.IFUS:SF|241|1260-1440|1-60 +1|WP|ICE.IFUS:CC|526|1005-1440|1-90 +1|WP|ICE.IFUS:DX|1261|480-1440|1-300 +1|WP|ICE.IFUS:FNG|1321|480-1440|1-360 +1|WP|ICE.IFUS:QA|1261|480-1440|1-300 +1|WP|EUREX:DAX|1186|495-1440|1-240 +1|WP|EUREX:DXM|1186|495-1440|1-240 +1|WP|EUREX:ESX|1186|495-1440|1-240 +1|WP|EUREX:MHK|1186|495-1440|1-240 +1|WP|EUREX:MIN|1186|495-1440|1-240 +1|WP|EUREX:MTH|1186|495-1440|1-240 +1|WP|EUREX:SMI|1186|495-1440|1-240 +1|WP|EUREX:VS|1186|495-1440|1-240 +1|WP|EUREX:BON|1186|495-1440|1-240 +1|WP|EUREX:BTP|1186|495-1440|1-240 +1|WP|EUREX:CRQ|1186|495-1440|1-240 +1|WP|EUREX:ESB|1186|495-1440|1-240 +1|WP|EUREX:GBL|1186|495-1440|1-240 +1|WP|EUREX:GBM|1186|495-1440|1-240 +1|WP|EUREX:GBS|1186|495-1440|1-240 +1|WP|EUREX:GBX|1186|495-1440|1-240 +1|WP|EUREX:OAT|1186|495-1440|1-240 +1|WP|EUREX:STX|1186|495-1440|1-240 +1|WP|EUREX:TDX|1186|495-1440|1-240 +1|WP|ICE.IFLL:I|1201|480-1440|1-240 +1|WP|ICE.IFLL:Z|1201|480-1440|1-240 +1|WP|ICE.IFLL:R|601|900-1440|1-60 +1|WP|ICE.IFLX:C|445|991-1435 +1|WP|ICE.IFLX:RC|511|960-1440|1-30 +1|WP|ICE.IFLX:W|556|945-1440|1-60 +1|WP|BAN:BTC|1439|1-1439 +1|WP|BAN:ETH|1439|1-1439 +2|WP|NG,CL,BZ,QG,QM,HO,RB,MCL,PA,PL|060000-235959|000000-050000 +2|WP|6L,GE,6A,6B,6C,6E,6J,6S,6Z,SIR,6M,NKD,ES,MIR,BTC,ETH,MBT,MET,NQ,RP,6N,PJY,RY,EMD,M2K,MES,MNQ,RTY,M6A,M6B,M6E,M6J,MCD,MSF,RU|060000-235959|000000-050000 +2|WP|HE,GF,LE|213000-235959|000000-020500 +2|WP|MCS,CUS,CEU,CJP,CAU,UCN,MEI,MIA,MMA,MDI,MTD,CHN,EMN,MPN,MVN,MSN,EAN,MDN,MIN,MJU,MMN,MTN,MXJ,MAN,MCN,MWN|191500-235959|000000-030000 +2|WP|HHI,MCH,HSI,MHI,HTI,CHI,MCA,MPS,MVI,GDU,FEM,LUA,LUC,LUN,LUP,LUS,LUZ|171500-235959|000000-030000 +2|WP|MTW|143000-235959|000000-030000 +2|WP|ALI,HG,MHG,QC,GC,SI,MGC,QO,QI,SGU,SIL|060000-235959|000000-050000 +2|WP|T,B,G|080000-235959|000000-060000 +2|WP|ZL,ZM,XK,ZS,XC,ZC,KE,XW,ZW,ZO,ZR|080000-235959|000000-022000 +2|WP|MYM,YM,TN,UB,ZB,ZF,ZN,ZT,10Y,2YY,30Y,5YY|060000-235959|000000-050000 +2|WP|FEF|201500-235959|000000-051500 +2|WP|NK|145500-235959|000000-051500 +2|WP|UC|181500-235959|000000-051500 +2|WP|IU,KU,US|195000-235959|000000-051500 +2|WP|FCH,CN|170000-235959|000000-051500 +2|WP|IN|184000-235959|000000-051500 +2|WP|SGP|175000-235959|000000-054500 +2|WP|MME,MFS,FNG|080000-235959|000000-060000 +2|WP|OJ|200000-235959|000000-020000 +2|WP|CT|090000-235959|000000-022000 +2|WP|KC|161500-235959|000000-013000 +2|WP|SB|153000-235959|000000-010000 +2|WP|RS|080000-235959|000000-022000 +2|WP|SF|210000-235959|000000-010000 +2|WP|CC|164500-235959|000000-013000 +2|WP|DX,QA|080000-235959|000000-050000 +2|WP|DAX,DXM,ESX,MHK,MIN,MTH,SMI,VS,BON,BTP,CRQ,ESB,GBL,GBM,GBS,GBX,OAT,STX,TDX|081500-235959|000000-040000 +2|WP|I,Z|080000-235959|000000-040000 +2|WP|R|150000-235959|000000-010000 +2|WP|RC|160000-235959|000000-003000 +2|WP|W|154500-235959|000000-010000 + +1|BKZS|290000|555|1261-1440|1-150|541-615|631-690|811-900 +1|BKZS|290006|555|1261-1440|1-150|541-615|631-690|811-900 +1|BKZS|290020|555|1261-1440|1-150|541-615|631-690|811-900 +1|BKZS|290002|465|1261-1440|1-60|541-615|631-690|811-900 +1|BKZS|290001|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290003|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290004|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290005|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290007|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290008|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290009|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290010|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290012|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290013|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290014|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290015|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290016|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290017|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290018|345|1261-1380|541-615|631-690|811-900 +1|BKZS|290019|345|1261-1380|541-615|631-690|811-900 +2|BKZS|290000|210000-235959|000000-023000 +2|BKZS|290006|210000-235959|000000-023000 +2|BKZS|290020|210000-235959|000000-023000 +2|BKZS|290001|210000-230000 +2|BKZS|290003|210000-230000 +2|BKZS|290004|210000-230000 +2|BKZS|290005|210000-230000 +2|BKZS|290007|210000-230000 +2|BKZS|290008|210000-230000 +2|BKZS|290009|210000-230000 +2|BKZS|290010|210000-230000 +2|BKZS|290012|210000-230000 +2|BKZS|290013|210000-230000 +2|BKZS|290014|210000-230000 +2|BKZS|290015|210000-230000 +2|BKZS|290016|210000-230000 +2|BKZS|290017|210000-230000 +2|BKZS|290018|210000-230000 +2|BKZS|290019|210000-230000 +2|BKZS|290002|210000-235959|000000-010000 diff --git a/src/xtquant/config/user/root2/lua/ConstFunc.lua b/src/xtquant/config/user/root2/lua/ConstFunc.lua new file mode 100644 index 0000000..e69de29 diff --git a/src/xtquant/config/user/root2/lua/FunIndex.lua b/src/xtquant/config/user/root2/lua/FunIndex.lua new file mode 100644 index 0000000..e6661d9 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunIndex.lua @@ -0,0 +1,117 @@ +------------------------------------------------------------ +-- 指标函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author zhangjin +-- @since 2012-10-8 +----------------------------------------------------------- +function c_sar() + local cache = FormulaCacheContainer() + function sar(N, S, M, timetag, __formula) + return sar_c(cache, N, S, M, timetag, __formula) + end + return sar +end + +function c_sarturn() + local cache = FormulaCacheContainer() + function sarturn(N, S, M, timetag, __formula) + return sarturn_c(cache, N, S, M, timetag, __formula) + end + return sarturn +end + +function callstock2() + local container = FormulaCacheContainer() + function funcimpl(stockcode, metaID, fieldID, period, offset, timetag, formula) + return callstock2_c(container, stockcode, metaID, fieldID, period, offset, timetag, formula) + end + return funcimpl +end + +function getstocklist() + local container = FormulaCacheContainer() + function funcimpl(sector, timetag, formula) + return getstocklist_c(container, sector, timetag, formula) + end + return funcimpl +end + +function getinitgroup() + local container = FormulaCacheContainer() + function funcimpl(timetag, formula) + return getinitgroup_c(container, timetag, formula) + end + return funcimpl +end + +function c_getspotprodgroup() + local container = FormulaCacheContainer() + function funcimpl(productcode, timetag, formula) + return getspotprodgroup_c(container, productcode, timetag, formula) + end + return funcimpl +end + +function c_getspotprodinst() + local container = FormulaCacheContainer() + function funcimpl(productcode, stockindex, timetag, formula) + return getspotprodinst_c(container, productcode, stockindex, timetag, formula) + end + return funcimpl +end + +function c_getwarehousereceipt() + local container = FormulaCacheContainer() + function funcimpl(productcode, warehousecode, timetag, formula) + return getwarehousereceipt_c(container, productcode, warehousecode, timetag, formula) + end + return funcimpl +end + +function c_getwarehousename() + local container = FormulaCacheContainer() + function funcimpl(productcode, warehouseindex, timetag, formula) + return getwarehousename_c(container, productcode, warehouseindex, timetag, formula) + end + return funcimpl +end + +function c_getfutureseats() + local container = FormulaCacheContainer() + function funcimpl(stockcode, field, rank, timetag, formula) + return getfutureseats_c(container, stockcode, field, rank, timetag, formula) + end + return funcimpl +end + +function c_getfutureseatsname() + local container = FormulaCacheContainer() + function funcimpl(stockcode, field, rank, timetag, formula) + return getfutureseatsname_c(container, stockcode, field, rank, timetag, formula) + end + return funcimpl +end + +function c_findfutureseats() + local container = FormulaCacheContainer() + function funcimpl(stockcode, field, member, timetag, formula) + return findfutureseats_c(container, stockcode, field, member, timetag, formula) + end + return funcimpl +end + +function c_stocktype() + local container = FormulaCacheContainer() + function funcimpl(stockcode, timetag, formula) + return stocktype_c(container, stockcode, timetag, formula) + end + return funcimpl +end + +function c_convindex() + local container = FormulaCacheContainer() + function funcimpl(stockcode, type, timetag, formula) + return convindex_c(container, stockcode, type, timetag, formula) + end + return funcimpl +end diff --git a/src/xtquant/config/user/root2/lua/FunLogic.lua b/src/xtquant/config/user/root2/lua/FunLogic.lua new file mode 100644 index 0000000..e42234a --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunLogic.lua @@ -0,0 +1,238 @@ +------------------------------------------------------------ +-- 逻辑函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author zhangjin +-- @since 2012-9-18 +----------------------------------------------------------- +function c_any() + local count = 0 + local history = FormulaDataCacheBool(1) + local ret + function any(condition, N, timetag, type) + count, ret = all_c(not(condition), N, count, timetag, type, history) + return not(ret) + end + return any +end + +function c_exist() + return c_any() +end + +function c_all() + local count = 0 + local history = FormulaDataCacheBool(1) + local ret + function all(condition, N, timetag, type) + count, ret = all_c(condition, N, count, timetag, type, history) + return ret + end + return all +end + +--条件跟随函数 +function c_valuewhen() + local lastValue = 0 / 0 + local ret + local first = true + local lastTimetag = 0 + function valuewhen(condition, value, timetag) + if condition then + ret = value + else + ret = lastValue + end + if (lastTimetag ~= timetag) then + lastValue = ret + lastTimetag = timetag + end + return ret + end + return valuewhen +end + +function c_cross() + local lastV1 = 0 + local lastV2 = -1 + local lastTime = -1 + local t1 = 0 + local t2 = -1 + local count = 0 + function cross(v1, v2, timetag) + if timetag ~= lastTime then + lastTime = timetag + count = 0 + t1 = lastV1 + t2 = lastV2 + end + count = count + 1 + if count > 1 then + lastV1 = t1 + lastV2 = t2 + end + local ret = cross_c(v1, v2, lastV1, lastV2) + lastV1 = v1 + lastV2 = v2 + return ret + end + return cross +end + +function iff(condition, v1, v2) + --print(type(v1),type(v2)) + if condition then + return v1; + else + return v2; + end +end + +function ifelse(condition, v1, v2) + if condition then + return v1; + else + return v2; + end +end + +function ifn(X, A, B) + if X then + return B + else + return A + end +end + +function valid(value) + return isValid(value) +end + +--todo: 当A, B, C中出现无效值时,金字塔返回无效值,该函数返回false +--function between(A, B, C) + --if (A - B >= 1e-6 and A - C <= 1e-6) or (A - B <= 1e-6 and A - C >= 1e-6) then + --return true + --else + --return false + --end +--end + +--todo 这三个函数是隐藏的行情函数 +--function isdown(timetag, __formula) + --if close(timetag, __formula) - open(timetag, __formula) < 1e-6 then + --return true + --else + --return false + --end +--end +-- +--function isequal(timetag, __formula) + ----if close(timetag, __formula) == open(timetag, __formula) then + --if math.fabs(close(timetag, __formula) - open(timetag, __formula)) < 1e-6 then + --return true + --else + --return false + --end +--end +-- +--function isup(timetag, __formula) + --if close(timetag, __formula) - open(timetag, __formula) > 1e-6 then + --return true + --else + --return false + --end +--end + +function islastbar(timetag, __formula) + return timetag == __formula:getLastBar() +end + +function c_last() + local history = FormulaDataCacheBool(1) + local count = 0 + local ret + function last(X, A, B, timetag) + count, ret = last_c(X, A, B, timetag, count, history) + return ret + end + return last +end +--[[ +function c_longcross(A, B, N) + local historyA = FormulaDataCacheDouble(0) + local historyB = FormulaDataCacheDouble(0) + local lessCount = 0 + function longcross(A, B, N, type) + lessCount, ret = longcross_c(A, B, N, type, historyA, historyB, lessCount) + return ret + end + return longcross +end +]]-- + +function c_longcross(A, B, N) --jch + local lessCount = 0 + local tmplessCount = 0 + local lastTimetag = -1 + function longcross(A, B, N, timetag) + local ret = false + local condition = A < B + if lastTimetag ~= timetag then + tmplessCount = lessCount + lastTimetag = timetag + end + if condition then + lessCount = tmplessCount + 1 + else + if lessCount >= N then + ret = true + end + lessCount = 0 + end + return ret + end + return longcross +end + +function range(A, B, C) + if A - B > 1e-6 and A - C< 1e-6 then + return true + else + return false + end +end + +function c_orderdirection() + local cache = LastValueCache() + function orderdirection(timetag,formula) + return orderdirection_c(timetag, formula, cache) + end + return orderdirection +end + +function c_isbuyorder() + local cache = LastValueCache() + function orderdirection(timetag,formula) + local val = orderdirection_c(timetag, formula, cache) + if val == 1 then + return 1 + else + return 0 + end + return 0 + end + return orderdirection +end + +function c_issellorder() + local cache = LastValueCache() + function issellorder(timetag,formula) + local val = orderdirection_c(timetag, formula, cache) + if val == -1 then + return 1 + else + return 0 + end + return 0 + end + return issellorder +end \ No newline at end of file diff --git a/src/xtquant/config/user/root2/lua/FunMath.lua b/src/xtquant/config/user/root2/lua/FunMath.lua new file mode 100644 index 0000000..548d952 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunMath.lua @@ -0,0 +1,24 @@ +------------------------------------------------------------ +-- 数学函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author jiangchanghao +-- @since 2012-9-18 +----------------------------------------------------------- + + +function sgn(val) + if (type(val) == "boolean") + then if (val) + then return 1 + else return 0 + end + else if (val > 0) + then return 1 + else if (val == 0) + then return 0 + else return -1 + end + end + end + return -1 +end \ No newline at end of file diff --git a/src/xtquant/config/user/root2/lua/FunOther.lua b/src/xtquant/config/user/root2/lua/FunOther.lua new file mode 100644 index 0000000..25a7552 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunOther.lua @@ -0,0 +1,831 @@ +------------------------------------------------------------ +-- 指标函数--与通达信相关 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author +-- @since 2017-3-1 +----------------------------------------------------------- +function c_const() + local ret + function const(X) + if not(X) then + ret = 0 + else + ret = X + end + return ret; + end + return const +end + +function c_inblock() + local container = FormulaCacheContainer() + function inblock(sector, timetag, __formula) + return inblock_c(sector, container, timetag, __formula) + end + return inblock +end + +function c_inblock2() + local container = FormulaCacheContainer() + function inblock(sector, stockcode, timetag, __formula) + return inblock2_c(sector, stockcode, container, timetag, __formula) + end + return inblock +end + +function c_sellvol() + function sellvol(timetag, __formula) + return buysellvol_c(2, timetag, __formula) + end + return sellvol +end + +function c_buyvol() + function buyvol(timetag, __formula) + return buysellvol_c(1, timetag, __formula) + end + return buyvol +end + +function c_upnday() + local history = FormulaDataCacheDouble(0,0) + local his=FormulaDataCacheDouble(0,0) + local turn=1 + function upnday(X, M, timetag, style) + turn =1 + for i=0,M-1,1 do + if back(X,i, timetag,history,style) < back(X,i+1, timetag,his,style) then + turn = 0 + break + end + end + return turn + end + return upnday +end + +function c_downnday() + local history = FormulaDataCacheDouble(0, 0) + local his=FormulaDataCacheDouble(0, 0) + function downnday(X, M, timetag, style) + turn =1 + for i=0,M-1,1 do + if back(X,i, timetag,history,style) > back(X,i+1, timetag,his,style) then + turn = 0 + break + end + end + return turn + end + return downnday +end + +function c_nday() + local history = FormulaDataCacheDouble(0,0) + local turn=1 + function nday(X, M, timetag, style) + turn =1 + for i=0,M-1,1 do + if back(X,i, timetag,history,style)==0 then + turn = 0 + break + end + end + return turn + end + return nday +end + +function c_turn() + local cache = TurnDataCache(); + function turn(value, N, timetag, formula) + return turn_c(value,N,timetag, formula, cache) + end + return turn +end + +function c_transcationstatic() + local cache = TransactionCache(); + function transcationstatic(stockcode, type, timetag, formula) + return transcationstatic_c(stockcode, type, timetag, formula, cache) + end + return transcationstatic +end + +function c_transcationstaticl1() + local cache = TransactionCache(); + function transcationstaticl1(stockcode, type, timetag, formula) + return transcationstaticl1_c(stockcode, type, timetag, formula, cache) + end + return transcationstaticl1 +end + +function c_get_cb_convert_price() + local cache = CBConvertPriceCache(); + function getCbConvertPrice(bondCode,timetag, formula) + return get_cb_convert_price_c(bondCode,timetag, formula, cache) + end + return getCbConvertPrice +end + + +function c_external_data() + local cache = ExternalCache(); + function external_data(data_name,field,period,N,timetag, formula) + return external_data_c(data_name,field, period,N,timetag, formula, cache) + end + return external_data +end + +local __pydatacache = {} +function c_callpython() + local first = true + function callpython(script, period, stockcode, timetag, formula) + local _dckey = string.sub(script, 1, string.find(script, '%.'))..period..'.'..stockcode + local cache = __pydatacache[_dckey] + if not cache then + __pydatacache[_dckey] = DataCache() + cache = __pydatacache[_dckey] + else + first = false + end + local ret = callpython_c(script, period , stockcode, timetag, formula, first, cache) + return ret + end + return callpython +end + +function c_getfindata() + local cache = FinDataCache() + function getfindata(value1, value2, session,timetag, formula) + return getfindata_c(value1,value2,session,timetag, formula, cache) + end + return getfindata +end + +function c_getfindatabyperiod() + local container = FormulaCacheContainer() + function getfindatabyperiod(tablename, colname, year, period, announce, timetag, formula) + return getfindatabyperiod_c(container, tablename, colname, year, period, announce, timetag, formula) + end + return getfindatabyperiod +end + +function c_getfindatayear() + local cache = FormulaDataCacheDouble(0,0) + function getfindatayear(value1, value2, timetag, formula) + return getfindatayear_c(value1,value2,timetag, formula, cache) + end + return getfindatayear +end + +function c_get_longhubang() + local cache = LonghubangDataCache() + --value1:filed value2:direction value3: rank + function getlonghubang(value1, value2, value3, timetag,formula) + return get_longhubang_c(value1, value2, value3, timetag, formula, cache) + end + return getlonghubang +end + +function c_get_holderNumber() + local cache = HolderNumberCache() + --value:filed + function getholdernum(value,timetag,formula) + return get_holder_num_c(value,timetag, formula, cache) + end + return getholdernum +end + + +function c_get_top10shareholder() + local cache = Top10shareholderCache() + --value1: type ,value2 : filed value3 rank + function gettop10shareholder(value1, value2, value3, timetag,formula) + return get_top10shareholder_c(value1, value2, value3, timetag, formula, cache) + end + return gettop10shareholder +end + +function c_get_top10shareholderbyperiod() + local container = FormulaCacheContainer() + function gettop10shareholderbyperiod(tablename, colname, rank, year, period, announce, type, timetag,formula) + return get_top10shareholderbyperiod_c(container, tablename, colname, rank, year, period, announce, type, timetag, formula) + end + return gettop10shareholderbyperiod +end + +function c_gethismaincontract() + local cache = GetHisMainContractCache() + function gethismaincontract(value1, timetag, formula) + return get_his_main_contract_c(value1, timetag, formula, cache) + end + return gethismaincontract +end + + +function c_getrealcontract() + local cache = GetHisMainContractCache() + function getrealcontract(value1, timetag, formula) + return get_real_contract_c(value1, timetag, formula, cache) + end + return getrealcontract +end + +function c_maincontractchange() + local cache = GetHisMainContractCache() + function maincontractchange(value1,timetag,formula) + return main_contract_change_c(value1,timetag,formula,cache) + end + return maincontractchange +end + +function c_tickvoldistribution() + local volnum = 0 + local N = 0 + local cache = TickVolDataCache() + local ret = 0 + local stayTimetag = 0 + local ratio = 0 + local isFirst = true + function tickvoldistribution(seconds, ratio, direction, timetag, __formula, style) + if timetag==0 then + midret = findoptimumvol(0, ratio, seconds, stayTimetag,__formula, cache, isFirst) + isFirst = false + end + + if ratio<=0 or ratio >1 then + return -1 + end + if (direction==2 and close(timetag, __formula)==bidprice(timetag, __formula)) then + volnum = vol(timetag,__formula) + else + if(direction==1 and close(timetag, __formula)==askprice(timetag, __formula)) then + volnum = vol(timetag,__formula) + else + if(direction == 0) then + volnum = vol(timetag,__formula) + else + volnum = 0 + end + end + end + + --用C++找最优解,提高速度 + if volnum > 0 then + midret = findoptimumvol(volnum, ratio, seconds, stayTimetag,__formula, cache, isFirst) + end + + + if midret ~= -1 then + ret = midret + end + + if volnum > 0 then + stayTimetag = stayTimetag + 1 + end + return ret + end + return tickvoldistribution +end + +function c_finance() + local cache = FinDataCache() + function finance(value,timetag, formula) + return finance_c(value,timetag,formula,cache) + end + return finance +end + +function c_buysellvols() + local cache = QuoterDataCache() + function buysellvols(value,timetag,formula) + return buysellvols_c(value, timetag, formula, cache) + end + return buysellvols +end + +function c_iopv() + local cache = QuoterDataCache() + function iopv(timetag,formula) + return iopv_c(cache, timetag, formula) + end + return iopv +end + +function c_getopenamount() + local cache = QuoterDataCache() + function getopenamount(formula) + return getopenamount_c(cache,formula) + end + return getopenamount +end + +function c_getopenvol() + local cache = QuoterDataCache() + function getopenvol(formula) + return getopenvol_c(cache,formula) + end + return getopenvol +end + +function c_blkname() + local container = FormulaCacheContainer() + function blkname(formula) + return blkname_c(container, "申万一级行业板块", formula) + end + return blkname +end + +function c_findblock() + local container = FormulaCacheContainer() + function findblock(folder, formula) + return blkname_c(container, folder, formula) + end + return findblock +end + +function c_findindex() + local indexTable = {} + function findindex(sector, stockcode, timetag, formula) + key = sector..stockcode + for k, v in pairs(indexTable) do + if k == key then + return v + end + end + + index = findindex_c(sector, stockcode, timetag, formula) + indexTable[key] = index + return index + end + return findindex +end + +function c_switchindex() + local container = FormulaCacheContainer() + function funcimpl(stockcode, suffix, timetag, formula) + return switchindex_c(stockcode, suffix, container, timetag, formula) + end + return funcimpl +end + +function c_extdatablockrank() + local cache = ExtFormulaCache() + function extdatablockrank(name, stockcode, sector, timetag, formula) + return extdatablockrank_c(name, stockcode, sector, cache, timetag, formula) + end + return extdatablockrank +end + +function c_extdatablocksum() + local cache = ExtFormulaCache() + function extdatablocksum(name, sector, timetag, formula) + return extdatablocksum_c(name, sector, cache, timetag, formula) + end + return extdatablocksum +end + +function c_extdatablocksumrange() + local cache = ExtFormulaCache() + function funcimpl(name, sector, range, timetag, formula) + return extdatablocksumrange_c(name, sector, range, cache, timetag, formula) + end + return funcimpl +end + +function c_extblockranktocode() + local cache = ExtFormulaCache() + function funcimpl(name, sector, rate, timetag, formula) + return extblockranktocode_c(name, sector, rate, cache, timetag, formula) + end + return funcimpl +end + +function c_blocksize() + local container = FormulaCacheContainer() + function blocksize(sector, ...) + return blocksize_c(container, sector, ...) + end + return blocksize +end + +function c_stockbyblockrank() + local container = FormulaCacheContainer() + function stockbyblockrank(sector, fieldID, rate, timetag, formula) + return stockbyblockrank_c(sector, fieldID, rate, container, timetag, formula) + end + return stockbyblockrank +end + +function c_blocksum() + local container = FormulaCacheContainer() + function blocksum(sector, fieldID, timetag, formula) + return blocksum_c(sector, fieldID, container, timetag, formula) + end + return blocksum +end + +function c_blockrank() + local container = FormulaCacheContainer() + function wrapper(sector, stockcode, fieldID, timetag, formula) + return blockrank_c(sector, stockcode, fieldID, container, timetag, formula) + end + return wrapper +end + +function c_paramcombcalc() + local container = FormulaCacheContainer() + function paramcombcalc(...) + local a = -1 + local b = -1 + local c = bvector() + for k,v in ipairs({...}) do + if k == 1 then + a = v + elseif k == 2 then + b = v + else + c:push_back(v) + end + end + return paramcombcalc_c(a, b, c, container) + end + return paramcombcalc +end + +function c_getoptinfo() + local container = FormulaCacheContainer() + function wrapper(optcode, timetag, formula) + return getoptinfo_c(container, optcode, timetag, formula) + end + return wrapper +end + +function c_getoptcodebyundl() + local container = FormulaCacheContainer() + function wrapper(undlCode, index, timetag, formula) + return getoptcodebyundl_c(container, undlCode, index, timetag, formula) + end + return wrapper +end + +function c_getoptcode() + local container = FormulaCacheContainer() + function wrapper(optcode, side, price, contractType, timetag, formula) + return getoptcode_c(container, optcode, side, price, contractType, timetag, formula) + end + return wrapper +end + +function c_getoptundlcode() + local container = FormulaCacheContainer() + function wrapper(optcode, timetag, formula) + return getoptundlcode_c(container, optcode, timetag, formula) + end + return wrapper +end + +function c_getoptcodebyno() + local container = FormulaCacheContainer() + function wrapper(undlCode, side, contractType, no, day, contractType1, mode, period, timetag, formula) + local param = ivector() + param:push_back(contractType) + param:push_back(no) + param:push_back(day) + param:push_back(contractType1) + param:push_back(mode) + param:push_back(period) + param:push_back(1) + return getoptcodebyno_c(container, undlCode, side, param, timetag, formula) + end + return wrapper +end + +function c_getoptcodebyno2() + local container = FormulaCacheContainer() + function wrapper(undlCode, side, contractType, no, day, contractType1, mode, period, timetag, formula) + local param = ivector() + param:push_back(contractType) + param:push_back(no) + param:push_back(day) + param:push_back(contractType1) + param:push_back(mode) + param:push_back(period) + param:push_back(0) + return getoptcodebyno_c(container, undlCode, side, param, timetag, formula) + end + return wrapper +end + +function c_getexerciseinterval() + local container = FormulaCacheContainer() + function wrapper(undlCode, contractType, timetag, formula) + return getexerciseinterval_c(container, undlCode, contractType, timetag, formula) + end + return wrapper +end + +function c_tdate() + local container = FormulaCacheContainer() + function wrapper(timetag, formula) + return tdate_c(container, timetag, formula) + end + return wrapper +end + +function c_tweekday() + local container = FormulaCacheContainer() + function wrapper(timetag, formula) + return tweekday_c(container, timetag, formula) + end + return wrapper +end + +function c_timerat() + local container = FormulaCacheContainer() + function wrapper(dateNum, timeNum, timetag, formula) + return timerat_c(container, dateNum, timeNum, timetag, formula) + end + return wrapper +end + +function c_timerafter() + local container = FormulaCacheContainer() + function wrapper(hh, mm, ss, timetag, formula) + return timerafter_c(container, hh, mm, ss, timetag, formula) + end + return wrapper +end + +function c_deliveryinterval() + local container = FormulaCacheContainer() + function wrapper(timetag, formula) + return deliveryinterval_c(container, timetag, formula) + end + return wrapper +end + +function c_deliveryinterval2() + local container = FormulaCacheContainer() + function wrapper(stock, timetag, formula) + return deliveryinterval2_c(container, stock, timetag, formula) + end + return wrapper +end + +function c_deliveryinterval3() + local container = FormulaCacheContainer() + function wrapper(timetag, formula) + return deliveryinterval3_c(container, timetag, formula) + end + return wrapper +end + +function c_getcbconversionvalue() + local container = FormulaCacheContainer() + function wrapper(code, timetag, formula) + return getcbconversionvalue_c(container, code, timetag, formula) + end + return wrapper +end + +function c_getcbconversionpremium() + local container = FormulaCacheContainer() + function wrapper(code, timetag, formula) + return getcbconversionpremium_c(container, code, timetag, formula) + end + return wrapper +end + +function c_getorderflowdetail() + local container = FormulaCacheContainer() + function wrapper(price, index, timetag, formula) + return getorderflowdetail_c(container, price, index, timetag, formula) + end + return wrapper +end + +function c_getorderflow() + local container = FormulaCacheContainer() + function wrapper(index, timetag, formula) + return getorderflow_c(container, index, timetag, formula) + end + return wrapper +end + +function c_getorderflowunbalance() + local container = FormulaCacheContainer() + function wrapper(threshold, thresholdTimes, barcount, timetag, formula) + return getorderflowunbalance_c(container, threshold, thresholdTimes, barcount, timetag, formula) + end + return wrapper +end + +function c_getorderflowunbalancepricein() + local container = FormulaCacheContainer() + function wrapper(threshold, thresholdTimes, barcount, price1, price2, timetag, formula) + return getorderflowunbalancepricein_c(container, threshold, thresholdTimes, barcount, price1, price2, timetag, formula) + end + return wrapper +end + +function c_getorderflowpoc() + local container = FormulaCacheContainer() + function wrapper(timetag, formula) + return getorderflowpoc_c(container, timetag, formula) + end + return wrapper +end + +function c_getorderflowdelta() + local container = FormulaCacheContainer() + function wrapper(timetag, formula) + return getorderflowdelta_c(container, timetag, formula) + end + return wrapper +end + +function c_getlastfuturemonth() + local container = FormulaCacheContainer() + function wrapper(code, index, timetag, formula) + return getlastfuturemonth_c(container, code, index, timetag, formula) + end + return wrapper +end + +function c_getlastfuturecode() + local container = FormulaCacheContainer() + function wrapper(code, index, timetag, formula) + return getlastfuturecode_c(container, code, index, timetag, formula) + end + return wrapper +end + +function c_extdatablocksplitavg() + local cache = ExtFormulaCache() + function wrapper(name, sector, total, index, timetag, formula) + return extdatablocksplitavg_c(name, sector, total, index, cache, timetag, formula) + end + return wrapper +end + +function c_getcapitalflow() + local container = FormulaCacheContainer() + function wrapper(filed, rank, timetag, formula) + return getcapitalflow_c(filed, rank, timetag, formula, container) + end + return wrapper +end + +function c_getcapitalflowbyholder() + local container = FormulaCacheContainer() + function wrapper(sharedholder, filed, timetag, formula) + return getcapitalflowbyholder_c(sharedholder, filed, timetag, formula, container) + end + return wrapper +end + +function c_getfuturecode() + local container = FormulaCacheContainer() + function wrapper(code, timetag, formula) + return getfuturecode_c(container, code, timetag, formula) + end + return wrapper +end + +function c_winner() + local container = FormulaCacheContainer() + function wrapper(price,timetag,formula) + return winner_cost_c(price,0,container,timetag,formula) + end + return wrapper +end + +function c_cost() + local container = FormulaCacheContainer() + function wrapper(price,timetag,formula) + return winner_cost_c(price,1,container,timetag,formula) + end + return wrapper +end + +function c_findblocklist() + local container = FormulaCacheContainer() + function wrapper(folder, formula) + return findblocklist_c(container, folder, formula) + end + return wrapper +end + +function c_unitofquantity() + local container = FormulaCacheContainer() + function wrapper(code, formula) + return unitofquantity_c(code, container, formula) + end + return wrapper +end + +function c_equalweightindex() + local container = FormulaCacheContainer() + function wrapper(code, formula) + return equalweightindex_c(code, container, formula) + end + return wrapper +end + +function c_isindexorglr() + local container = FormulaCacheContainer() + function wrapper(code, formula) + return isindexorglr_c(code, container, formula) + end + return wrapper +end + +function c_isetfcode() + local container = FormulaCacheContainer() + function wrapper(code, formula) + return isetfcode_c(container, code, formula) + end + return wrapper +end + +function c_isindexcode() + local container = FormulaCacheContainer() + function wrapper(code, formula) + return isindexcode_c(container, code, formula) + end + return wrapper +end + +function c_isfuturecode() + local container = FormulaCacheContainer() + function wrapper(code, formula) + return isfuturecode_c(container, code, formula) + end + return wrapper +end + +function c_upstopprice() + local container = FormulaCacheContainer() + function upstopprice(stockcode, timetag, formula) + return stopprice_c(container, stockcode, 1, timetag, formula) + end + return upstopprice +end + +function c_downstopprice() + local container = FormulaCacheContainer() + function downstopprice(stockcode, timetag, formula) + return stopprice_c(container, stockcode, 2, timetag, formula) + end + return downstopprice +end + +function c_dividfactor() + local container = FormulaCacheContainer() + function func(type, timetag, formula) + return dividfactor_c(container, type, timetag, formula) + end + return func +end + +function c_getinstrumentdetail() + local container = FormulaCacheContainer() + function wrapper(stockcode, fieldname, timetag, formula) + return getinstrumentdetail_c(container, stockcode, fieldname, timetag, formula) + end + return wrapper +end + +function c_limitupperformance() + local container = FormulaCacheContainer() + function wrapper(stockcode, type, timetag, formula) + return limitupperformance_c(container, stockcode, type, timetag, formula) + end + return wrapper +end + +function c_fundnetvalue() + local container = FormulaCacheContainer() + function wrapper(stockcode, type, timetag, formula) + return fundnetvalue_c(container, stockcode, type, timetag, formula) + end + return wrapper +end + +function c_get_etf_statistics() + local container = FormulaCacheContainer() + function fun(stockcode, field, timetag, formula) + return get_etf_statistics_c(container, 1, stockcode, field, timetag, formula) + end + return fun +end + +function c_get_etf_statisticsl2() + local container = FormulaCacheContainer() + function fun(stockcode, field, timetag, formula) + return get_etf_statistics_c(container, 2, stockcode, field, timetag, formula) + end + return fun +end \ No newline at end of file diff --git a/src/xtquant/config/user/root2/lua/FunRef.lua b/src/xtquant/config/user/root2/lua/FunRef.lua new file mode 100644 index 0000000..173a871 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunRef.lua @@ -0,0 +1,657 @@ +------------------------------------------------------------ +-- 引用函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author zhangjin +-- @since 2012-9-18 +----------------------------------------------------------- + +function c_ref() + local history = FormulaDataCacheDouble(0, 0) + function ref(X, distance, timetag, style) + if not(X) then + X = 0 + end + return ref_c(X, distance, timetag, history, style) + end + return ref +end + +function c_barslast() + local lastTrue = -1 + local lastTrue_1 = -1 + local curTimetag = -1 + function barslast(condition, timetag) + if curTimetag ~= timetag then + curTimetag = timetag + lastTrue_1 = lastTrue + else + lastTrue = lastTrue_1 + end + + if not(condition) then + condition = false + end + + if condition then + lastTrue = curTimetag + end + + if lastTrue == -1 then + return 0 + else + return curTimetag - lastTrue + end + end + return barslast +end + +function c_barslasts() + local container = FormulaCacheContainer() + function wrapper(condition, N, timetag) + return barslasts_c(condition, N, container, timetag) + end + return wrapper +end + +function c_count() + local history = FormulaDataCacheBool(0, 0) + function count(condition, N, timetag, type) + if not(condition) then + condition = false + end + return count_c(condition, N, timetag, history, type) + end + return count +end + +function c_ma() + local history = FormulaDataCacheDouble(0, 0) + function ma(X, N, timetag, type) + if not(X) then + X = 0 + end + local avg = ma_c(X, N, timetag, history, type) + return avg; + end + return ma +end + +function c_xma() + local history = FormulaDataCacheDouble(0, 0) + function xma(X, N, timetag, __formula, type) + if not(X) then + X = 0 + end + local avg = xma_c(X, N, timetag, history, __formula, type) + return avg; + end + return xma +end + +function c_ima() + local history = FormulaDataCacheDouble(0, 0) + function ima(X, N, S, timetag, type) + if not(X) then + X = 0 + end + return ima_c(X, N, S, timetag, history, type) + end + return ima +end + + +--求动态移动平均 jch +function c_dma() + local last + local lasttimetag = -1 + local ret = math.huge + function dma(X, A, timetag) + if not(X) then + X = 0 + end + if lasttimetag ~= timetag then --主推数据 + last = ret + lasttimetag = timetag + end + if not(isValid(last)) then + last = X + end + if (A > 1 or A <= 0) then + A = 1 + end + ret = A * X + (1 - A) * last + return ret; + end + return dma +end + +--[[求动态移动平均 +function c_dma() + local last = 0 + local ret + function dma(X, A, timetag) + last, ret = dma_c(X, last, A, timetag); + return ret; + end + return dma +end]]-- + +--求指数平滑移动平均 jch +function c_ema() + local last + local lasttimetag = -1 + local ret = math.huge + function ema(X, N, timetag) + if not(X) then + X = 0 + end + if lasttimetag ~= timetag then --主推数据 + last = ret + lasttimetag = timetag + end + if not(isValid(last)) then + last = X + end + ret = (2 * X + (N - 1) * last) / (N + 1) + return ret; + end + return ema +end + +--[[求指数平滑移动平均 +function c_ema() + local history = FormulaDataCacheDouble(0, 0) + function ema(X, N, timetag) + ret = ema_c(X, last, N, timetag); + return ret; + end + return ema +end +function c_sma() + local last = math.huge + local lasttimetag = -1 + local ret = 0 + function sma(X, N, M, timetag) + if timetag == 0 and isValid(X) then + last = X + end + if not(isValid(X)) then + X = math.huge + end + local ret = (X * M + (N - M) * last) / N; + if isValid(X) and not(isValid(ret)) then + last = X + else + last = ret + end + return ret + end + return sma +end +]]-- +--移动平均 jch +function c_sma() + local last = math.huge + local lasttimetag = -1 + local ret = math.huge + function sma(X, N, M, timetag) + if not(X) then + X = 0 + end + if lasttimetag ~= timetag then + last = ret + lasttimetag = timetag + end + if not(isValid(last)) then + last = X + end + ret = (M * X + (N - M) * last) / N + return ret + end + return sma +end + +--递归移动平均 jch +function c_tma() + local last + local lasttimetag = -1 + local ret = math.huge + function tma(X, N, M, timetag) + if not(X) then + X = 0 + end + if lasttimetag ~= timetag then + last = ret + lasttimetag = timetag + end + if not(isValid(last)) then + last = X + end + ret = N * last + M * X + return ret + end + return tma +end +--[[递归移动平均 +function c_tma() + local last = 0 + function tma(X, N, M, timetag) + if not(isValid(X)) then + X = math.huge; + end + local ret = M * X + N * last; + last = ret + return ret + end + return tma +end]]-- + +function c_sum() + local history = FormulaDataCacheDouble(1) + function sum(X, N, timetag, type) + if not(X) then + X = 0 + end + local ret = sum_c(X, N, history, timetag, type) + return ret + end + return sum +end + +function c_hhv() + local history = FormulaDataCacheDouble(-math.huge, -1) + function hhv(X, N, timetag, style) + if not(X) then + X = 0 + end + local index, max = hhv_c(X, N, timetag, history, style) + return max + end + return hhv +end + +function c_hhvbars() + local history = FormulaDataCacheDouble(-math.huge, -1) + function hhvbars(X, N, timetag, style) + if not(X) then + X = 0 + end + local index, max = hhv_c(X, N, timetag, history, style) + return index + end + return hhvbars +end + +function c_llv() + local history = FormulaDataCacheDouble(math.huge, -1) + function llv(X, N, timetag, type) + if not(X) then + X = 0 + end + local index, min = llv_c(X, N, timetag, history, type) + return min + end + return llv +end + +function c_llvbars() + local history = FormulaDataCacheDouble(math.huge, -1) + function llvbars(X, N, timetag, type) + if not(X) then + X = 0 + end + local index, min = llv_c(X, N, timetag, history, type) + return index + end + return llvbars +end + +function c_filter() + local lastTrue = -1 + local lastTimetag = -1 + local realTimeLastTrue = -1 + function filter(val, N, timetag) + local ret = 0 + if timetag ~= lastTimetag then + lastTrue = realTimeLastTrue + end + if timetag - lastTrue > N then + ret = val + if val > 0 then + realTimeLastTrue = timetag + else + realTimeLastTrue = lastTrue + end + end + lastTimetag = timetag + return ret + end + return filter +end + +function c_sfilter() + local lastX = 0 + local lastCond = 0 + function sfilter(X, cond, timetag) + if cond then + lastCond = timetag + if X then + lastX = timetag + end + return X + else + if lastX > lastCond then + return false + else + if X then + lastX = timetag + end + return X + end + end + end + return sfilter +end + +function c_barscount() + local isValid = false + local first = 0 + function barscount(X, timetag) + if isValid then + return timetag - first + 1 + elseif valid(X) then + isValid = true + first = timetag + return 1 + else + return 0 / 0 + end + end + return barscount +end + +function c_barssincen() + local isFirst = true + local index = 0 + local ret = 0 + local indexArray = {}--存放满足条件的timetag + local indFirst = 0 --indexArray存放timetag的开始位 ,indFirst>=indLast时说明没有满足条件的timetag + local indLast = 0 --indexArray存放timetag的结束位 + function barssincen(condition,n,timetag) + if n < 2 then + return 0 + end + ret = 0 + if timetag >= n-1 and (not isFirst) then --timetag 是从0开始的 + if timetag - index > n-1 then + if indFirst < indLast then + indFirst = indFirst + 1 + index = indexArray[indFirst] + ret = timetag - index + indexArray[indFirst] = nil --释放空间 + end + else + ret = timetag - index + end + end + if condition then + indLast = indLast + 1 + indexArray[indLast] = timetag + if isFirst then + isFirst = false + index = timetag + if index == 0 then --如果第一个K线就满足条件,在此做数组删除处理(避免下个周期还读到index=0) + indexArray[indLast] = nil + indLast = indLast - 1 + end + end + end + + return ret + end + return barssincen +end + +function c_barssince() + local isFirst = false + local index = 0 + function barssince(condition, timetag) + if isFirst then + return timetag - index + elseif condition then + isFirst = true + index = timetag + return 0 + else + return 0 + end + end + return barssince +end + +function c_barssincen() + local isFirst = true + local index = 0 + local ret = 0 + local indexArray = {}--存放满足条件的timetag + local indFirst = 0 --indexArray存放timetag的开始位 ,indFirst>=indLast时说明没有满足条件的timetag + local indLast = 0 --indexArray存放timetag的结束位 + function barssincen(condition,n,timetag) + if n < 2 then + return 0 + end + ret = 0 + if timetag >= n-1 and (not isFirst) then --timetag 是从0开始的 + if timetag - index > n-1 then + if indFirst < indLast then + indFirst = indFirst + 1 + index = indexArray[indFirst] + ret = timetag - index + indexArray[indFirst] = nil --释放空间 + end + else + ret = timetag - index + end + end + if condition then + indLast = indLast + 1 + indexArray[indLast] = timetag + if isFirst then + isFirst = false + index = timetag + if index == 0 then --如果第一个K线就满足条件,在此做数组删除处理(避免下个周期还读到index=0) + indexArray[indLast] = nil + indLast = indLast - 1 + end + end + end + + return ret + end + return barssincen +end + +function currbarscount(timetag, __formula) + local endPos = __formula:getLastBar() + return endPos - timetag + 1 +end + +function drawnull() + return 0 / 0 +end + +function c_tr() + local lastClose = 0 / 0 + function tr(timetag, __formula) + local c = close(timetag, __formula) + local h = high(timetag, __formula) + local l = low(timetag, __formula) + local v1 = h - l + local v2 = h - lastClose + local v3 = lastClose - l + lastClose = c + if v1 > v2 then + if v1 > v3 then + return v1 + else + return v3 + end + else + if v2 > v3 then + return v2 + else + return v3 + end + end + end + return tr +end + +function c_wma() + local history = FormulaDataCacheDouble(2) + --local sum = 0 + --local weightSum = 0 + local avg + function wma(X, N, timetag, tp) + if not(X) then + X = 0 + end + --sum, weightSum, avg = wma_c(X, N, timetag, sum, weightSum, history) + avg = wma_c(X, N, timetag, history, tp) + return avg + end + return wma +end + +function c_trma() + local ma1 = c_ma() + local ma2 = c_ma() + function trma(X, N, timetag, type) + if not(X) then + X = 0 + end + local N1 + local temp, ret + if N % 2 == 1 then + N1 = (N + 1) / 2 + temp = ma1(X, N1, timetag, type) + ret = ma2(temp, N1, timetag, type) + else + N1 = N / 2 + temp = ma1(X, N1, timetag, type) + ret = ma2(temp, N1 + 1, timetag, type) + end + return ret + end + return trma +end + +function c_ret() + local history = FormulaDataCacheDouble(0) + local lastValue + function ret(X, A, timetag, __formula, type) + if not(X) then + X = 0 + end + lastValue = ret_c(X, A, timetag, __formula, history, type) + return lastValue + end + return ret +end + +function c_newhbars() + local history = FormulaDataCacheDouble(0) + function newhbars(X, N, timetag, type) + if not(X) then + X = 0 + end + return newhbars_c(X, N, timetag, history, type) + end + return newhbars +end + +function c_newlbars() + local history = FormulaDataCacheDouble(0) + function newlbars(X, N, timetag, type) + if not(X) then + X = 0 + end + --return newlbars_c(X, N, history) + return newhbars_c( -1 * X, N, timetag, history, type) + end + return newlbars +end + +function c_hod() + local history = FormulaDataCacheDouble(0) + function hod(X, N, timetag) + if not(X) then + X = 0 + end + return hod_c(X, N, timetag, history) + end + return hod +end + +function c_lod() + local history = FormulaDataCacheDouble(0) + function lod(X, N, timetag) + if not(X) then + X = 0 + end + return hod_c( -1 * X, N, timetag, history) + end + return lod +end +--[[ +function c_sumbars() + local history = FormulaDataCacheDouble(2) + local sum = 0.0 + local period = 0 + function sumbars(X, A) + sum, period = sumbars_c(X, A, sum, period, history) + return period + end + return sumbars +end]]-- +function c_sumbars() + local history = FormulaDataCacheDouble(0,0) + function sumbars(X, A, timetag) + if not(X) then + X = 0 + end + local ret = sumbars_c(X, A, timetag, history) + return ret + end + return sumbars +end + +function c_barslastcount() + local container = FormulaCacheContainer() + function wrapper(condition, timetag, __formula) + return barslastcount_c(container, condition, timetag, __formula) + end + return wrapper +end + +function c_mema() + local last + local lasttimetag = -1 + local ret = math.huge + function mema(X, N, timetag) + if not(X) then + X = 0 + end + if lasttimetag ~= timetag then --主推数据 + last = ret + lasttimetag = timetag + end + if not(isValid(last)) then + last = X + end + ret = (X + (N - 1) * last) / N + return ret; + end + return mema +end + diff --git a/src/xtquant/config/user/root2/lua/FunStatistic.lua b/src/xtquant/config/user/root2/lua/FunStatistic.lua new file mode 100644 index 0000000..ead335c --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunStatistic.lua @@ -0,0 +1,395 @@ +------------------------------------------------------------ +-- 统计函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author zhangjin +-- @since 2012-9-18 +----------------------------------------------------------- + +--判别调用 +function poisson(v1,v2,v3,timetag,formula) + if (type(v3) == "number") + then if (v3>0) + then return poisson_c(v1,v2,true,timetag,formula) + else return poisson_c(v1,v2,false,timetag,formula) + end + end + return poisson_c(v1,v2,v3,timetag,formula) +end + +function weibull(v1,v2,v3,v4,timetag,formula) + return weibull_c(v1,v2,v3,v4,timetag,formula) +end + +function expondist(v1,v2,v3,timetag,formula) + return expondist_c(v1,v2,v3,timetag,formula) +end + +function binomdist(v1,v2,v3,v4,timetag,formula) + return binomdist_c(v1,v2,v3,v4,timetag,formula) +end + + +cacheDoubleNum = 4 +--drl2 曲线回归偏离度 +function c_drl2() + local cache1 = FormulaDataCacheDouble(15) + local cache2 = FormulaDataCacheDouble(15) + local cache3 = FormulaDataCacheDouble(15) + function drl2_func(value, N, timetag, __formula) + local ret = drl2(value, cache1, cache2, cache3, N, timetag, __formula) + return ret + end + return drl2_func +end +--forecast2 二次曲线回归预测值。 +function c_forecast2() + local cache1 = FormulaDataCacheDouble(10) + local cache2 = FormulaDataCacheDouble(10) + local cache3 = FormulaDataCacheDouble(10) + function forecast2_func(value, N, timetag, __formula) + local ret = forecast2(value, cache1, cache2, cache3, N, timetag, __formula) + return ret + end + return forecast2_func +end +--slope 曲线回归相关系数 +function c_slope20() + local cache1 = FormulaDataCacheDouble(10) + local cache2 = FormulaDataCacheDouble(10) + local cache3 = FormulaDataCacheDouble(10) + function slope20_func(value, N, timetag, __formula) + local ret = slope20(value, cache1, cache2, cache3,N, timetag, __formula) + return ret + end + return slope20_func +end +function c_slope21() + local cache1 = FormulaDataCacheDouble(10) + local cache2 = FormulaDataCacheDouble(10) + local cache3 = FormulaDataCacheDouble(10) + function slope21_func(value, N, timetag, __formula) + local ret = slope21(value, cache1, cache2, cache3, N, timetag, __formula) + return ret + end + return slope21_func +end +function c_slope22() + local cache1 = FormulaDataCacheDouble(10) + local cache2 = FormulaDataCacheDouble(10) + local cache3 = FormulaDataCacheDouble(10) + function slope22_func(value, N, timetag, __formula) + local ret = slope22(value, cache1, cache2, cache3, N, timetag, __formula) + return ret + end + return slope22_func +end +--drl 直线回归偏离度 +function c_drl() + local cachey = FormulaDataCacheDouble(cacheDoubleNum) + local cachex = FormulaDataCacheDouble(cacheDoubleNum) + function drl_func(value, N, timetag, __formula) + local ret = drl(value, cachey, cachex, N, timetag, __formula) + return ret + end + return drl_func +end +--forecast 线性回归预测值。 +function c_forecast() + local cachey = FormulaDataCacheDouble(cacheDoubleNum) + local cachex = FormulaDataCacheDouble(cacheDoubleNum) + function forecast_func(value, N, timetag, __formula) + local ret = forecast(value, cachey, cachex, N, timetag, __formula) + return ret + end + return forecast_func +end +--slope 线性回归斜率 +function c_slope() + local cachey = FormulaDataCacheDouble(cacheDoubleNum) + local cachex = FormulaDataCacheDouble(cacheDoubleNum) + function slope_func(value, N, timetag, __formula) + local ret = slope(value, cachey, cachex, N, timetag, __formula) + return ret + end + return slope_func +end +--percentrank 返回特定数值在数据集中的百分比排位 +function c_percentrank() + local cache = FormulaOrderCache() + function percentrank_func(value, N, x, significance, timetag, __formula) + local ret = percentrank(value, cache, N, x, significance, timetag, __formula) + return ret + end + return percentrank_func +end +--percentile 返回区域中数值的第 K 个百分点的值 +function c_percentile() + local cache = FormulaOrderCache() + function percentile_func(value, N, k, timetag, __formula) + local ret = percentile(value, cache, N, k, timetag, __formula) + return ret + end + return percentile_func +end +--median 返回区域中数值的中位数 +function c_median() + local cache = FormulaOrderCache() + function median_func(value, N, timetag, __formula) + local ret = percentile(value, cache, N, 0.5, timetag, __formula) + return ret + end + return median_func +end +--trimmean 返回数据的内部平均值 +function c_trimmean() + local cache = FormulaOrderCache() + function trimmean_func(value, N, percent, timetag, __formula) + local ret = trimmean(value, cache, N, percent, timetag, __formula) + return ret + end + return trimmean_func +end +--quartile 返回数据的四分位数 +function c_quartile() + local cache = FormulaOrderCache() + function quartile_func(value, N, quart, timetag, __formula) + local ret = quartile(value, cache, N, quart, timetag, __formula) + return ret + end + return quartile_func +end +--large 数据集中第 k 个最大值 +function c_large() + local cache = FormulaOrderCache() + function large_func(value, N, k, timetag, __formula) + local ret = large(value, cache, N, k, timetag, __formula) + return ret + end + return large_func +end +--small数据集中第 k 个最小值 +function c_small() + local cache = FormulaOrderCache() + function small_func(value, N, k, timetag, __formula) + local ret = small(value, cache, N, k, timetag, __formula) + return ret + end + return small_func +end +--skew分布的偏斜度 +function c_skew() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function skew_func(value, N, timetag, __formula) + local ret = skew(value, cache, N, timetag, __formula) + return ret + end + return skew_func +end +--ftest +function c_ftest() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function ftest_func(value1, value2, N, timetag, __formula) + local ret = ftest(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return ftest_func +end + +--数据集的峰值 +function c_kurt() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function kurt_func(value, N, timetag, __formula) + local ret = kurt(value, cache, N, timetag, __formula) + return ret + end + return kurt_func +end +--几何平均值 +function c_geomean() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function geomean_func(value, N, timetag, __formula) + local ret = geomean(value, cache, N, timetag, __formula) + return ret + end + return geomean_func +end +--调和平均值 +function c_harmean() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function harmean_func(value, N, timetag, __formula) + local ret = harmean(value, cache, N, timetag, __formula) + return ret + end + return harmean_func +end + +-- INTERCEPT(Y,X,N),求序列Y,X的线性回归线截距 +function c_intercept() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function intercept_func(value1, value2, N, timetag, __formula) + local ret = intercept(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return intercept_func +end +-- RSQ(A,B,N),计算A,B序列的N周期乘积矩相关系数的平方. +function c_rsq() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function rsq_func(value1, value2, N, timetag, __formula) + local ret = rsq(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return rsq_func +end + +-- Pearson(皮尔生)乘积矩相关系数 +function c_pearson() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function pearson_func(value1, value2, N, timetag, __formula) + local ret = pearson(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return pearson_func +end +--通过线性回归法计算每个 x 的 y 预测值时所产生的标准误差 +function c_steyx() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function steyx_func(value1, value2, N, timetag, __formula) + local ret = steyx(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return steyx_func +end + +function c_mode() + local dcache = FormulaDataCacheDouble(cacheDoubleNum) + local ccache = FormulaCountCache() + function mode_func(value, N, timetag, __formula) + local ret = mode(value, dcache, ccache, N, timetag, __formula) + return ret + end + return mode_func +end + +function c_covar() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function covar_func(value1, value2, N, timetag, __formula) + local ret = covar(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return covar_func +end + +function c_beta2() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + local cache3 = FormulaDataCacheDouble(cacheDoubleNum) + function beta2_func(value1, value2, N, timetag, __formula) + local ret = beta2(value1, value2, cache1, cache2,cache3, N, timetag, __formula) + return ret + end + return beta2_func +end + +function c_avedev() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + --print("hello") + function avedev_func(value, N, timetag, __formula) + local ret = avedev(value, cache, N, timetag, __formula) + return ret + end + return avedev_func +end + +function c_devsq() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + --print("hello") + function devsq_func(value, N, timetag, __formula) + local ret = devsq(value, cache, N, timetag, __formula) + return ret + end + return devsq_func +end + +function c_relate() + local cache1 = FormulaDataCacheDouble(cacheDoubleNum) + local cache2 = FormulaDataCacheDouble(cacheDoubleNum) + function relate_func(value1, value2, N, timetag, __formula) + local ret = relate(value1, value2, cache1, cache2, N, timetag, __formula) + return ret + end + return relate_func +end + +function c_std() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function std_func(value, N, timetag, __formula, type) + local ret = std(value, cache, N, timetag, __formula, type) + return ret + end + return std_func +end + +function c_var() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function var_func(value, N, timetag, __formula) + local ret = var(value, cache, N, timetag, __formula) + return ret + end + return var_func +end + +function c_stdp() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function stdp_func(value, N, timetag, __formula) + local ret = stdp(value, cache, N, timetag, __formula) + return ret + end + return stdp_func +end + +function c_varp() + local cache = FormulaDataCacheDouble(cacheDoubleNum) + function varp_func(value, N, timetag, __formula) + local ret = varp(value, cache, N, timetag, __formula) + return ret + end + return varp_func +end + +function c_std3() + local history = List.new() + function std_func(value, N, timetag, __formula) + List.pushright(history, value); + local sz = history.last - history.first + 1; + if (sz > N) then + List.popleft(history); + sz = sz - 1 + end + if (sz < N) then + return 0 / 0; + end + sum1 = 0.0; + sum2 = 0.0; + count = 0 + for i = history.first, history.last, 1 do + temp = history[i]; + sum1 = sum1 + temp; + sum2 = sum2 + temp * temp; + count = count + 1; + end + sum1 = sum1 * sum1; + sum1 = sum1 / N; + ret = math.sqrt((sum2-sum1) / (N-1)); + return ret; + end + return std_func; +end + + diff --git a/src/xtquant/config/user/root2/lua/FunString.lua b/src/xtquant/config/user/root2/lua/FunString.lua new file mode 100644 index 0000000..a01016d --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunString.lua @@ -0,0 +1,164 @@ +------------------------------------------------------------ +-- 字符串函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author jiangchanghao +-- @since 2012-10-11 +----------------------------------------------------------- +function lowerstr(str) + return string.lower(str) +end + +function upperstr(str) + return string.upper(str) +end + +function strlen(str) + return string.len(str) +end + +function strleft(str, n) + return string.sub(str, 1, n) +end + +function strright(str, n) + if (n < string.len(str)) + then + return string.sub(str, string.len(str)-n+1, -1) + else + return str + end +end + +function strmid(str, i,j) + if (i <= 0) + then return "" + end + return string.sub(str, i,j+i-1) +end + +function ltrim(str) + s,_ = string.gsub(str,'^ +','') + return s +end + +function rtrim(str) + s, _ = string.gsub(str,' +$','') + return s +end + +function numtostr(num, N) + return string.format(string.format('%s.%if','%',N),num) +end + +function strcat(des,str) + return string.format('%s%s', des, str) +end + +function strtonum(str) + return tonumber(str) +end + +function strtonumex(str, default) + local num = tonumber(str) + if isvalid(num) then + return num + else + return default + end +end + +function strinsert(str, index, str1) + return string.format('%s%s%s',string.sub(str,1,index),str1,string.sub(str,index+1,-1)) +end + +function strremove(str, index, cound) + return string.format('%s%s',string.sub(str,1,index),string.sub(str,index+cound+1,-1)) +end + +function strfind(str, s1, n, timetag) + i,_ = string.find(str,s1,n) + if (i == nil) + then return 0 + else return i + end +end + +function strreplace(str, strold, strnew) + s, _ = string.gsub(str, strold, strnew) + return s +end + +function strtrimleft(str, str1) + s, _ = string.gsub(str,string.format('%s%s%s','^',str1,'+'),'') + return s +end + +function strtrimright(str, str1) + s, _ = string.gsub(str,string.format('%s%s',str1,'+$'),'') + return s +end + +function strcmp(str1,str2) + if (str1 < str2) + then return -1 + else if (str1 == str2) + then return 0 + else return 1 + end + end +end + +function stricmp(str1,str2) + s1 = string.upper(str1) + s2 = string.upper(str2) + if (s1 < s2) + then return -1 + else if (s1 == s2) + then return 0 + else return 1 + end + end +end + +function strncmp(str1,str2,n) + s1 = string.sub(str1,1,n) + s2 = string.sub(str2,1,n) + if (s1 < s2) + then return -1 + else if (s1 == s2) + then return 0 + else return 1 + end + end +end + +function fmt(...) + local tt = {...} + local len = #tt + if (len == 1) then + return tostring(tt[1]) + end + + fc = FmtCache() + fc:setFmtString(tostring(tt[1])) + local i = 2 + repeat + local v = tt[i] + if (type(v) == "number") then + fc:pushDouble(i, v) + elseif (type(v) == "string") then + fc:pushString(i, v) + elseif (type(v) == "boolean") then + if (v) then + fc:pushDouble(i, 1) + else + fc:pushDouble(i, 0) + end + else + fc:pushString(i, "nil") + end + i = i + 1 + until i > len + return fc:getFmtString() +end + diff --git a/src/xtquant/config/user/root2/lua/FunSystem.lua b/src/xtquant/config/user/root2/lua/FunSystem.lua new file mode 100644 index 0000000..e6ea9be --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunSystem.lua @@ -0,0 +1,75 @@ +------------------------------------------------------------ +-- 系统函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author jiangchanghao +-- @since 2012-10-11 +----------------------------------------------------------- + +function c_print() + local pc = PrintCache() + function printout(...) + local tt = {...} + local len = #tt + timetag = tt[len - 1] + formula = tt[len] + if (len == 3 and type(tt[1]) == "nil") then + printOut(pc, formula) + return + end + local i = 1 + repeat + local v = tt[i] + if (type(v) == "string") then + printStr(v, tt[i+1], pc, timetag, formula) + else + if (type(v) == "boolean") then + printBool(v, tt[i+1], pc, timetag, formula) + else + printNum(v, tt[i+1], pc, timetag, formula) + end + end + i = i + 2 + until i >= len - 1 + end + return printout +end + +function c_serialize() + local cc = FormulaCacheContainer() + function ff(...) + local tt = {...} + local len = #tt + + timetag = tt[len - 1] + formula = tt[len] + + serialize_c(-1, 0, 0, cc, timetag, formula) + + if len == 3 then + serialize_c(0, 0, tt[1], cc, timetag, formula) + return serialize_c(1, 0, 0, cc, timetag, formula) + end + + if tt[1] == 'list' then + for i = 2, len - 2, 1 do + serialize_c(0, i - 2, tt[i], cc, timetag, formula) + end + return '[' .. serialize_c(1, 0, 0, cc, timetag, formula) .. ']' + end + + if tt[1] == 'dict' then + for i = 2, len - 2, 2 do + if i + 1 <= len - 2 then + serialize_c(0, tt[i], tt[i + 1], cc, timetag, formula) + end + end + return '{' .. serialize_c(1, 0, 0, cc, timetag, formula) .. '}' + end + + for i = 2, len - 2, 1 do + serialize_c(0, i - 2, tt[i], cc, timetag, formula) + end + return serialize_c(1, 0, 0, cc, timetag, formula) + end + return ff +end diff --git a/src/xtquant/config/user/root2/lua/FunTrader.lua b/src/xtquant/config/user/root2/lua/FunTrader.lua new file mode 100644 index 0000000..8cfdb06 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/FunTrader.lua @@ -0,0 +1,182 @@ +------------------------------------------------------------ +-- 程序化交易函数 +-- 由脚本引擎预先定义,如果有性能问题,可用C++改写 +-- @author zhangjin +-- @since 2012-10-18 +----------------------------------------------------------- +function c_order() + local history = FormulaDataCacheDouble(0, 0); + local lastBasket = '' + function order(opType, chanel, addr, basket, timetag, formula) + if basket == nil then + basket = lastBasket + end + local ret = placeorder_c(opType, chanel, addr, basket, timetag, formula, history) + return ret + end + return order +end + +function c_passorder() + local history = FormulaDataCacheDouble(0, 0); + --orderCode:basketName or stockID + function passorder(opType, orderType, accountid, orderCode, prType, price, volume, quickTrade, strategyName, userOrderId, timetag, formula) + --由于luabind的限制不能处理超过10个参数,这里把accountid和strategyName合并在一起,到了C++里再拆分 + accidAndstrName = accountid.."#"..strategyName.."#"..tostring(quickTrade).."#"..userOrderId + local ret = passorder_c(opType, orderType, accidAndstrName, orderCode, prType, price, volume, timetag, formula, history) + return ret + end + return passorder +end + +function passorder( + optype, ordertype, accountid, accounttype + , marketstock, pricetype, price, volume + , strategyname, quicktrade, remark + , timetag, formula +) + ptable = { + quicktrade = quicktrade + , strategyname = strategyname + , remark = remark + , barpos = timetag + } + + return passorder2_c( + optype, ordertype, accountid, accounttype + , marketstock, pricetype, price, volume + , ptable, formula + ) +end + +function c_trade() + local history = FormulaDataCacheDouble(0, 0); + local tp = tradeparam() + function trade_c(param, address, timetag, formula) + if param then + tp = copyParam(param) + trade(param, 0, timetag, formula, history, address) + else + trade(tp, 1, timetag, formula, history, address) + end + end + return trade_c +end + +function c_hedgestocktrade() + local history = FormulaDataCacheDouble(0, 0); + local tp = tradeparam() + function hedgestocktrade_c(param, address, timetag, formula) + if param then + tp = copyParam(param) + hedgestocktrade(param, 0, timetag, formula, history, address) + else + hedgestocktrade(tp, 1, timetag, formula, history, address) + end + end + return hedgestocktrade_c +end + +function c_cancel() + local history = FormulaDataCacheDouble(0, 0) + function cancel_c(codeNumber, timetag) + cancel(codeNumber, history) + end + return cancel_c +end + +function c_writeorder() + local history = FormulaDataCacheDouble(0, 0) + function writeorder_c(filepath, content, timetag, formula) + return writeorder(filepath, content, timetag, formula, history) + end + return writeorder_c +end + +function positionadjust(positions, weight, channel) + local s = '35,'..channel..'\n' + for i = 0, positions:size() - 1, 1 do + local detail = positions:at(i) + local adjustedVol = detail.m_nVolume * weight + s = s..detail.m_strInstrumentID..'\t'..adjustedVol..'\n' + end + return s +end + +function c_portfoliosell(type) + local history = FormulaDataCacheDouble(0, 0) + function portfoliosell_c(type, timetag, formula) + return portfoliosell(type, timetag, formula, history) + end + return portfoliosell_c +end + +function c_portfoliobuy() + local history = FormulaDataCacheDouble(0, 0); + local lastBasket = '' + function portfoliobuy(opType, chanel, addr, basket, timetag, formula) + if basket == nil then + basket = lastBasket + end + return portfoliobuy_c(opType, chanel, addr, basket, timetag, formula, history) + end + return portfoliobuy +end + +function algo_passorder( + optype, ordertype, accountid, accounttype + , marketstock, pricetype, price, volume + , strategyname, quicktrade, remark + , algoname + , timetag, formula +) + ptable = { + quicktrade = quicktrade + , strategyname = strategyname + , remark = remark + , barpos = timetag + , algoname = algoname + } + + return algo_passorder_c( + optype, ordertype, accountid, accounttype + , marketstock, pricetype, price, volume + , ptable, formula + ) +end + +function cancel_task(taskID, accountID, accountType) + return cancel_task_c(taskID, accountID, accountType, timetag, formula) +end + +function c_readsignal() + local container = FormulaCacheContainer() + function wrapper(filePath, stockCode, timetag, formula) + return readsignal_c(filePath, stockCode, container, timetag, formula) + end + return wrapper +end + +function c_drawsignal() + local container = FormulaCacheContainer() + function wrapper(cond, signalType, drawPrice, timetag, formula) + return drawsignal_c(container, cond, signalType, drawPrice, timetag, formula) + end + return wrapper +end + +function c_cmdprogress() + local container = FormulaCacheContainer() + function wrapper(cmdID, timetag, formula) + return cmdprogress_c(container, cmdID, timetag, formula) + end + return wrapper +end + +function c_cmdstatus() + local container = FormulaCacheContainer() + function wrapper(cmdID, timetag, formula) + return cmdstatus_c(container, cmdID, timetag, formula) + end + return wrapper +end diff --git a/src/xtquant/config/user/root2/lua/MetaType.lua b/src/xtquant/config/user/root2/lua/MetaType.lua new file mode 100644 index 0000000..af70654 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/MetaType.lua @@ -0,0 +1,10 @@ +-------------------------------------------------------------- +-- lua与C++兼容的常量定义,常量值目前为"MetaId_FieldId" +-- @author zhangjin +-- @since 2012-7-17 +--------------------------------------------------------------- + +--K线 +META_FIELD_KLINE_HIGH_PRICE = "1001_1" --最高价 +META_FIELD_KLINE_LOW_PRICE = "1001_2" --最低价 +META_FIELD_KLINE_CLOSE_PRICE = "1001_3" --收盘价 \ No newline at end of file diff --git a/src/xtquant/config/user/root2/lua/config.lua b/src/xtquant/config/user/root2/lua/config.lua new file mode 100644 index 0000000..92b5f61 --- /dev/null +++ b/src/xtquant/config/user/root2/lua/config.lua @@ -0,0 +1,1485 @@ +--lua脚本相对于运行路径的相对路径 +luaScriptPath = "../script/systemlua" +--vb脚本相对于运行路径的相对路径 +vbScriptPath = "../script/vb" + +--[[--------------------------------------------------------------- + 金字塔和lua中的关键词 +-----------------------------------------------------------------]] +keyword = { + {key = "if", value = 1}, + {key = "then", value = 1}, + {key = "else", value = 1}, + {key = "while", value = 1}, + {key = "for", value = 1}, + {key = "to", value = 1}, + {key = "do", value = 1}, + {key = "input", value = 1}, + {key = "begin", value = 1}, + {key = "end", value = 1}, + {key = "variable", value = 1}, + {key = "true", value = 1}, + {key = "false", value = 1}, + {key = "break", value = 1}, + {key = "continue", value = 1}, + {key = "repeat", value = 1}, + {key = "until", value = 1}, + {key = "goto", value = 1}, + {key = "exit", value = 1}, + {key = "global", value = 1}, + --color -- + {key = "colorblack", value = 1}, + {key = "colorblue", value = 1}, + {key = "colorbrown", value = 1}, + {key = "colorcyan", value = 1}, + {key = "colorgray", value = 1}, + {key = "colorgreen", value = 1}, + {key = "colormagenta",value = 1}, + {key = "colorred", value = 1}, + {key = "colorwhite", value = 1}, + {key = "coloryellow",value = 1}, + {key = "colorblack", value = 1}, + {key = "colorblack", value = 1}, + {key = "colorblack", value = 1}, + {key = "colorblack", value = 1}, + {key = "colorlired", value = 1}, + {key = "colorlicyan", value = 1}, + + --only in lua-- + {key = "return", value = 1}, + {key = "nil", value = 1}, + {key = "local", value = 1}, + {key = "in", value = 1}, + {key = "function", value = 1}, + {key = "elseif", value = 1}, + --系统保留的关键词,用于翻译时生成的一些中间变量-- + {key = "_ret", value = 11}, + {key = "func", value = 11}, + {key = "timetag", value = 11}, + {key = "", value = 0}, +} +--[[-------------------------------------------------------------- + 金字塔支持的数据引用周期,需要根据我们的实际情况进行调整 + stkindex是stkindi中用到的period index,其中缺失的是 16:节气线 +----------------------------------------------------------------]] +period = { + {name = "tick", time = 0, cycindex = 0, stkindex = 0}, + {name = "sec1", time = 1000, cycindex = 100, stkindex = -1}, + {name = "sec5", time = 5000, cycindex = 101, stkindex = -1}, + {name = "min1", time = 60000, cycindex = 1, stkindex = 1}, + {name = "min3", time = 180000, cycindex = 17, stkindex = 17}, + {name = "min5", time = 300000, cycindex = 2, stkindex = 2}, + {name = "min10",time = 600000, cycindex = 18, stkindex = 18}, + {name = "min15",time = 900000, cycindex = 3, stkindex = 3}, + {name = "min30",time = 1800000, cycindex = 4, stkindex = 4}, + {name = "min60",time = 3600000, cycindex = 5, stkindex = 5}, + {name = "hour", time = 3600000, cycindex = 102, stkindex = -1}, + {name = "day" , time = 86400000, cycindex = 6, stkindex = 6}, + {name = "week", time = 604800000, cycindex = 7, stkindex = 7}, + {name = "month",time = 2592000000, cycindex = 8, stkindex = 8}, + {name = "quarter",time = 7776000000, cycindex = 14, stkindex = 14}, + {name = "semiyear",time = 15552000000, cycindex = 15, stkindex = 15}, + {name = "year",time = 31536000000, cycindex = 9, stkindex = 9}, + {name = "multimin", time = 5400000, cycindex = 11, stkindex = 11}, + {name = "multihour", time = 7200000, cycindex = 13, stkindex = 13}, + {name = "multisec", time = 30000, cycindex = 12, stkindex = 12}, + {name = "multitick", time = 10, cycindex = 19, stkindex = 19}, + {name = "multiday", time = 0, cycindex = 10, stkindex = 10}, + {name = "default", time = -1, cycindex = -1, stkindex = -1}, +} + +--[[------------------------------------------------------------------ + 系统预定义的公式 + 参数类型说明: + 0 普通参数 + 10 普通参数-数字类型 + 20 普通参数-布尔类型 + 1 引用参数,需要解析为函数 + 2 颜色描述符 + 3 线型描述符 + 100 不定长 + 函数类型(type)说明: + 0 行情函数 + 1 数学函数 + 2 逻辑函数 + 3 控制函数 + 4 引用函数 + 5 统计函数 + 6 绘图函数 + 7 时间函数 + 8 常数函数 + 9 字符串函数 + 10 交易系统 + 11 指标函数 + 12 动态行情 + 13 系统函数 + 14 扩展数据函数 + 15 组合模型参数设置 + 16 组合模型运行函数 + 17 财务数据函数 + 18 分级基金数据函数 + 100 用户自定义 + 函数附加参数类型 (epType) 说明: + 0 不附加参数 + 1 只附加timetag一个参数 + 2 附加timetag和formula两个参数 + 3 只附加formula一个参数 + 返回值类型说明: + 0 double + 1 int + 2 bool + 3 string + 4 vector + 5 vector + 6 vector + 7 vector + 100 void + 200 uncertain + 1001 LuaStruct-ChildResult + 1002 LuaStruct-StockHoldingInfo + 1003 LuaStruct-CPositionDetail + 1004 LuaStruct-CDealDetail + 1005 LuaStruct-COrderDetail + 1006 LuaStruct-paramResult + 1007 LuaStruct-StockGroup + 1008 LuaStruct-priceVolumeData + 1009 LuaStruct-OrderSignal + +--------------------------------------------------------------------]] +formula = { + -------------------------type = 0, 行情函数--------------------------------- + {name = "open", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "o", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "close", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "c", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "low", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "l", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "high", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "h", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "vol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "v", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "iperiod", argus = {}, ret = 0, type = 11, epType = 2, leastParam = 0}, + {name = "period", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "settleprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "settlement", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "amount", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "askprice", argus = {10}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "askvol", argus = {10}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "bidprice", argus = {10}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "bidvol", argus = {10}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "bvol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "svol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "indexc", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "indexo", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "indexh", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "indexl", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "indexv", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "indexa", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "oopen", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "oclose", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "ohigh", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "olow", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "ovol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "oamount", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "osettleprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + --{name = "opena", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "openint", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + --{name = "openv", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "qt", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "callstock",argus = {0,0,0,0}, ret = 0, type = 0, epType = 2, leastParam = 2}, + {name = "callstock2",argus = {0,0,0,0}, ret = 0, type = 100, epType = 2, leastParam = 4}, + {name = "rtclose", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "rtvol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "estimatedprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + ---------------获取当日最新数据的函数-------------------- + {name = "tickopen", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "ticklast", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "tickhigh", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "ticklow", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "tickamount", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "tickvol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "ticklastclose", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "tickopenint", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "tickpe", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "transaction", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "ticktransaction", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + -------------------------type = 1, 数学函数--------------------------------- + {name = "max", argus = {10, 10}, ret = 0, type = 1, epType = 0, leastParam = 2}, + {name = "min", argus = {10, 10}, ret = 0, type = 1, epType = 0, leastParam = 2}, + {name = "abs", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "intpart", argus = {10}, ret = 1, type = 1, epType = 0, leastParam = 1}, + {name = "log", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "rounds", argus = {10, 10}, ret = 0, type = 1, epType = 0, leastParam = 2}, + {name = "acos", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "asin", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "atan", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "cos", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "sin", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "tan", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "sqrt", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "ceiling", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "exp", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "floor", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "ln", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "fracpart", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "mod", argus = {10,10}, ret = 0, type = 1, epType = 0, leastParam = 2}, + {name = "pow", argus = {10,10}, ret = 0, type = 1, epType = 0, leastParam = 2}, + {name = "reverse", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "round", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "sgn", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "rand", argus = {10}, ret = 0, type = 1, epType = 0, leastParam = 1}, + {name = "combin", argus = {10,10}, ret = 0, type = 1, epType = 1, leastParam = 2}, + -------------------------type = 2, 逻辑函数--------------------------------- + {name = "all", argus = {20, 10}, ret = 2, type = 2, epType = 1, leastParam = 2}, + {name = "every", argus = {20, 10}, ret = 2, type = 2, epType = 1, leastParam = 2}, + {name = "iff", argus = {20,0,0}, ret = 200, type = 2, epType = 0, leastParam = 3}, + {name = "ifelse", argus = {20,0,0}, ret = 200, type = 2, epType = 0, leastParam = 3}, + {name = "not", argus = {20}, ret = 2, type = 2, epType = 2, leastParam = 1},--lua 有个not运算符 + {name = "valuewhen",argus = {20,0}, ret = 0, type = 2, epType = 1, leastParam = 2}, + {name = "any", argus = {20,10}, ret = 2, type = 2, epType = 1, leastParam = 2}, + {name = "exist", argus = {20,10}, ret = 2, type = 2, epType = 1, leastParam = 2}, + {name = "cross", argus = {10,10}, ret = 2, type = 2, epType = 1, leastParam = 2}, + {name = "valid", argus = {0}, ret = 2, type = 2, epType = 0, leastParam = 1}, + {name = "between", argus = {10,10,10}, ret = 2, type = 2, epType = 0, leastParam = 3}, + {name = "ifn", argus = {20,0,0}, ret = 200, type = 2, epType = 0, leastParam = 3}, + {name = "isdown", argus = {}, ret = 2, type = 2, epType = 2, leastParam = 0}, + {name = "isequal", argus = {}, ret = 2, type = 2, epType = 2, leastParam = 0}, + {name = "isup", argus = {}, ret = 2, type = 2, epType = 2, leastParam = 0}, + {name = "longcross",argus = {10,10,10}, ret = 2, type = 2, epType = 1, leastParam = 3}, + {name = "islastbar", argus = {}, ret = 2, type = 2, epType = 2, leastParam = 0}, + {name = "last", argus = {20,10,10}, ret = 2, type = 2, epType = 1, leastParam = 3}, + {name = "range", argus = {10,10,10}, ret = 2, type = 2, epType = 0, leastParam = 3}, + {name = "suspend", argus = {10}, ret = 0, type = 2, epType = 2, leastParam = 0}, + {name = "setdataalignmode", argus = {10}, ret = 100, type = 2, epType = 3, leastParam = 1}, + {name = "unitofquantity", argus = {0}, ret = 1, type = 2, epType = 3, leastParam = 1}, + {name = "equalweightindex", argus = {0}, ret = 3, type = 2, epType = 3, leastParam = 1}, + {name = "isindexorglr", argus = {0}, ret = 1, type = 2, epType = 3, leastParam = 1}, + {name = "isetfcode", argus = {0}, ret = 1, type = 2, epType = 3, leastParam = 1}, + {name = "isindexcode", argus = {0}, ret = 1, type = 2, epType = 3, leastParam = 1}, + {name = "isfuturecode", argus = {0}, ret = 1, type = 2, epType = 3, leastParam = 1}, + -------------------------type = 4, 引用函数--------------------------------- + {name = "sma", argus = {10, 10, 10}, ret = 0, type = 4, epType = 1, leastParam = 3}, + {name = "ref", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "barslast", argus = {20}, ret = 0, type = 4, epType = 1, leastParam = 1}, + {name = "barslasts", argus = {20, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "sum", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "hhv", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "count", argus = {20, 10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "ma", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "xma", argus = {1, 10}, ret = 0, type = 4, epType = 2, leastParam = 2}, + {name = "ima", argus = {10,10,10}, ret = 0, type = 4, epType = 1, leastParam = 3}, + {name = "dma", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "ema", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "tma", argus = {10, 10, 10}, ret = 0, type = 4, epType = 1, leastParam = 3}, + {name = "filter", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "llv", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "barscount",argus = {10}, ret = 1, type = 4, epType = 1, leastParam = 1}, + {name = "barssince",argus = {20}, ret = 1, type = 4, epType = 1, leastParam = 1}, + {name = "barssincen",argus = {20,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "currbarscount", argus= {}, ret = 1, type = 4, epType = 2, leastParam = 0}, + {name = "hhvbars", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "llvbars", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "sfilter", argus = {20,20}, ret = 2, type = 4, epType = 1, leastParam = 2}, + {name = "tr", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "trma", argus = {10,10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "wma", argus = {10,10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "todaybar", argus = {}, ret = 1, type = 4, epType = 2, leastParam = 0}, + {name = "ret", argus = {10,10}, ret = 0, type = 4, epType = 2, leastParam = 2}, + {name = "newhbars", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "newlbars", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "refdate", argus = {1,10,10}, ret = 0, type = 4, epType = 2, leastParam = 2}, + {name = "hod", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "lod", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "sumbars", argus = {10,10}, ret = 1, type = 4, epType = 1, leastParam = 2}, + {name = "drawnull", argus = {}, ret = 0, type = 4, epType = 0, leastParam = 0}, + {name = "barpos", argus = {}, ret = 1, type = 4, epType = 2, leastParam = 0}, + {name = "market", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "refx", argus = {1,10}, ret = 0, type = 4, epType = 2, leastParam = 2}, + {name = "refparam", argus = {0,0}, ret = 0, type = 4, epType = 2, leastParam = 1}, + {name = "median", argus = {10,10}, ret = 0, type = 4, epType = 2, leastParam = 2}, + {name = "stkindi", argus = {0,0,10,10,10,10}, ret = 0, type = 4, epType = 0, leastParam = 4}, + {name = "backset", argus = {20,10}, ret = 0, type = 4, epType = 2, leastParam = 2}, + {name = "backsetx", argus = {20,10,0}, ret = 0, type = 4, epType = 2, leastParam = 3}, + --{name = "fliterx", argus = {1,0}, ret = 0, type = 4, epType = 2, leastParam = 2}, -- problems + --{name = "setval", argus = {1,0}, ret = 0, type = 4, epType = 2, leastParam = 2}, -- problems + {name = "setrefxnum", argus = {0}, ret = 100, type = 4, epType = 2, leastParam = 1}, + {name = "barsnext", argus = {20}, ret = 1, type = 4, epType = 2, leastParam = 1}, + {name = "dopen", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "dhigh", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "dlow", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "dclose", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "dvol", argus = {}, ret = 0, type = 4, epType = 2, leastParam = 0}, + {name = "barslastcount", argus = {20}, ret = 1, type = 4, epType = 2, leastParam = 1}, + {name = "mema", argus = {10, 10}, ret = 0, type = 4, epType = 1, leastParam = 2}, + {name = "hod2", argus = {0, 10, 10}, ret = 1, type = 4, epType = 0, leastParam = 3}, + -------------------------type = 6, 绘图函数--------------------------------- + {name = "kline", argus = {10,10,10,10,0}, ret = 100, type = 6, epType = 0, leastParam = 5}, + {name = "drawtext", argus = {20,10,0,2,0}, ret = 100, type = 6, epType = 0, leastParam = 3}, + --{name = "drawtextex",argus = {20,10,10,10,0,2}, ret = 100, type = 6, epType = 0, leastParam = 5}, + {name = "barsset", argus = {20,10,10,10}, ret = 100, type = 6, epType = 0, leastParam = 4}, + --{name = "drawarc", argus = {20,10,20,10,0,0,2,0,3}, ret = 100, type = 6, epType = 0, leastParam = 6}, + {name = "drawbmp", argus = {20,10,0,0}, ret = 100, type = 6, epType = 0, leastParam = 3}, + {name = "drawline", argus = {20,10,20,10,0,2,0,3}, ret = 100, type = 6, epType = 0, leastParam = 5}, + {name = "vertline", argus = {20,10,10,2,4,3},ret = 100, type = 6, epType = 0, leastParam = 1}, + --{name = "stickline",argus = {20,10,10,0,0,2},ret = 100, type = 6, epType = 0, leastParam = 5}, + --{name = "partline", argus = {20,10,2,0,3}, ret = 100, type = 6, epType = 0, leastParam = 2}, + {name = "colorrgb", argus = {10,10,10}, ret = 1, type = 6, epType = 0, leastParam = 3}, + {name = "drawnumber",argus= {20,10,10,10,2,0},ret =100, type = 6, epType = 0, leastParam = 4}, + {name = "drawicon", argus = {20,10,0,0}, ret = 100, type = 6, epType = 0, leastParam = 3}, + {name = "drawband", argus = {10,2,10,2}, ret = 100, type = 6, epType = 0, leastParam = 4}, + {name = "drawgbk_div", argus = {20,2,2,0,0}, ret = 100, type = 6, epType = 0, leastParam = 5}, + {name = "drawstick", argus = {0, 0, 10, 2, 2}, ret = 100, type = 6, epType = 0, leastParam = 5}, + {name = "drawarrow", argus = {0, 0, 0, 0, 0, 0, 2}, ret = 100, type = 6, epType = 0, leastParam = 7}, + {name = "drawrectangle", argus = {0, 0, 0, 0, 0, 0, 2}, ret = 100, type = 6, epType = 0, leastParam = 7}, + {name = "drawtext_fix", argus = {20,10,10,10,0}, ret = 100, type = 6, epType = 0, leastParam = 5}, + {name = "stickline", argus = {20,10,10,10,10}, ret = 100, type = 6, epType = 0, leastParam = 5}, + -------------------------type = 7, 时间函数--------------------------------- + {name = "date", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "ndate", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "tdate", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "hour", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "year", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "day", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "minute", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "month", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "quarter", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "time", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "ntime", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "weekday", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "tweekday", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "timerat", argus = {10,10}, ret = 0, type = 7, epType = 2, leastParam = 2}, + {name = "timerafter", argus = {10,10,10}, ret = 0, type = 7, epType = 2, leastParam = 3}, + {name = "currentdate",argus = {}, ret = 1, type = 7, epType = 0, leastParam = 0}, + {name = "currenttime",argus = {}, ret = 1, type = 7, epType = 0, leastParam = 0}, + {name = "todaymilliseconds",argus = {}, ret = 1, type = 7, epType = 0, leastParam = 0}, + {name = "datediff", argus = {10,10}, ret = 1, type = 7, epType = 0, leastParam = 2}, + {name = "datetod1970",argus = {10}, ret = 1, type = 7, epType = 0, leastParam = 1}, + {name = "dayofweek",argus = {10}, ret = 1, type = 7, epType = 0, leastParam = 1}, + {name = "days1970", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "t0totime", argus = {10}, ret = 1, type = 7, epType = 0, leastParam = 1}, + {name = "timetot0", argus = {10}, ret = 1, type = 7, epType = 0, leastParam = 1}, + {name = "time0", argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "barpos", argus = {}, ret = 1, type = 7, epType = 1, leastParam = 0}, + {name = "barstatus",argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "d1970todate",argus = {10}, ret = 1, type = 7, epType = 0, leastParam = 1}, + {name = "datepos", argus = {0}, ret = 1, type = 7, epType = 2, leastParam = 1}, + {name = "openminutes",argus = {10}, ret = 1, type = 7, epType = 2, leastParam = 1}, + {name = "nextholidaydif",argus = {0}, ret = 1, type = 7, epType = 2, leastParam = 1}, + {name = "nextholidaydifspec",argus = {0,0}, ret = 1, type = 7, epType = 2, leastParam = 1}, + {name = "nextmonthdaydif",argus = {0}, ret = 1, type = 7, epType = 2, leastParam = 1}, + {name = "timefromstart",argus = {}, ret = 1, type = 7, epType = 2, leastParam = 0}, + {name = "currenttradedate",argus = {0}, ret = 1, type = 7, epType = 2, leastParam = 1}, + {name = "inbar", argus = {10,10}, ret = 2, type = 7, epType = 2, leastParam = 2}, + {name = "datetoday",argus = {10}, ret = 1, type = 7, epType = 0, leastParam = 1}, + -------------------------type = 8, 常数函数--------------------------------- + {name = "capital", argus = {}, ret = 0, type = 8, epType = 2, leastParam = 0}, + {name = "capitalTotal", argus = {}, ret = 0, type = 8, epType = 2, leastParam = 0}, + {name = "datacount", argus = {}, ret = 1, type = 8, epType = 2, leastParam = 0}, + {name = "closetime", argus = {10}, ret = 1, type = 8, epType = 2, leastParam = 1}, + {name = "datatype", argus = {}, ret = 1, type = 8, epType = 2, leastParam = 0}, + {name = "datatypemore",argus={10}, ret = 0, type = 8, epType = 0, leastParam = 1}, + {name = "findindexbytime",argus={0}, ret = 0, type = 8, epType = 2, leastParam = 1}, + {name = "mindiff", argus = {}, ret = 0, type = 8, epType = 2, leastParam = 0}, + {name = "opentime", argus = {10}, ret = 1, type = 8, epType = 2, leastParam = 1}, + {name = "volunit", argus = {}, ret = 0, type = 8, epType = 2, leastParam = 0}, + {name = "getinstrumentdetail", argus = {0, 0}, ret = 3, type = 8, epType = 2, leastParam = 2}, + {name = "limitupperformance", argus = {0, 0}, ret = 0, type = 8, epType = 2, leastParam = 2}, + {name = "fundnetvalue", argus = {0, 0}, ret = 0, type = 8, epType = 2, leastParam = 2}, + -------------------------type = 9, 字符串函数-------------------------------- + {name = "lowerstr", argus = {0}, ret = 3, type = 9, epType = 0, leastParam = 1}, + {name = "upperstr", argus = {0}, ret = 3, type = 9, epType = 0, leastParam = 1}, + {name = "strlen", argus = {0}, ret = 1, type = 9, epType = 0, leastParam = 1}, + {name = "strleft", argus = {0,0}, ret = 3, type = 9, epType = 0, leastParam = 2}, + {name = "strmid", argus = {0,0,0}, ret = 3, type = 9, epType = 0, leastParam = 3}, + {name = "strright", argus = {0,0}, ret = 3, type = 9, epType = 0, leastParam = 2}, + {name = "ltrim", argus = {0}, ret = 3, type = 9, epType = 0, leastParam = 1}, + {name = "rtrim", argus = {0}, ret = 3, type = 9, epType = 0, leastParam = 1}, + {name = "numtostr", argus = {0,0}, ret = 3, type = 9, epType = 0, leastParam = 2}, + {name = "strcat", argus = {0,0}, ret = 3, type = 9, epType = 0, leastParam = 2}, + {name = "strtonum", argus = {0}, ret = 0, type = 9, epType = 0, leastParam = 1}, + {name = "strtonumex", argus = {0,0}, ret = 0, type = 9, epType = 0, leastParam = 2}, + {name = "strinsert",argus = {0,0,0}, ret = 3, type = 9, epType = 0, leastParam = 3}, + {name = "strremove",argus = {0,0,0}, ret = 3, type = 9, epType = 0, leastParam = 3}, + {name = "strfind", argus = {0,0,0}, ret = 1, type = 9, epType = 0, leastParam = 3}, + {name = "strreplace",argus = {0,0,0}, ret = 3, type = 9, epType = 0, leastParam = 3}, + {name = "strtrimleft",argus = {0,0}, ret = 3, type = 9, epType = 0, leastParam = 2}, + {name = "strtrimright",argus = {0,0}, ret = 3, type = 9, epType = 0, leastParam = 2}, + {name = "strcmp", argus = {0,0}, ret = 1, type = 9, epType = 0, leastParam = 2}, + {name = "stricmp", argus = {0,0}, ret = 1, type = 9, epType = 0, leastParam = 2}, + {name = "strncmp", argus = {0,0,0}, ret = 1, type = 9, epType = 0, leastParam = 3}, + {name = "stringtofile", argus = {0,0}, ret = 100, type = 9, epType = 0, leastParam = 2}, + {name = "stklabel", argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "marketlabel",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "formulaname",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "blkname", argus = {}, ret = 3, type = 9, epType = 3, leastParam = 0}, + {name = "findblock", argus = {0}, ret = 3, type = 9, epType = 3, leastParam = 1}, + {name = "findindex", argus = {0, 0}, ret = 3, type = 9, epType = 2, leastParam = 2}, + {name = "switchindex", argus = {0, 0}, ret = 3, type = 9, epType = 2, leastParam = 2}, + {name = "convfuture", argus = {0, 0}, ret = 3, type = 9, epType = 3, leastParam = 2}, + {name = "marketname",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "marketlabel1",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "stkname", argus = {0}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "timestamptostr",argus = {10}, ret = 3, type = 9, epType = 0, leastParam = 1}, + {name = "strtotimestamp",argus = {0}, ret = 1, type = 9, epType = 0, leastParam = 1}, + {name = "findblocklist", argus = {0}, ret = 1007, type = 9, epType = 3, leastParam = 1}, + {name = "stockcode",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "stgname",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + --------------------to do fomula----------------------- + --{name = "enginecode",argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + --{name = "username", argus = {}, ret = 3, type = 9, epType = 2, leastParam = 0}, + {name = "inblock", argus = {0}, ret = 1, type = 9, epType = 2, leastParam = 1}, + {name = "inblock2", argus = {0,0}, ret = 1, type = 9, epType = 2, leastParam = 1}, + {name = "get_external_data_single_number", argus = {0,0,0}, ret = 0, type = 9, epType = 0, leastParam = 3}, + {name = "get_external_data_single_string", argus = {0,0,0}, ret = 3, type = 9, epType = 0, leastParam = 3}, + {name = "get_external_data_kline", argus = {0,0,0,0}, ret = 0, type = 9, epType = 0, leastParam = 4}, + {name = "blocksize", argus = {0,0}, ret = 1, type = 9, epType = 2, leastParam = 1}, + {name = "stockbyblockrank", argus = {0,0,0}, ret = 3, type = 9, epType = 2, leastParam = 3}, + {name = "blocksum", argus = {0,0}, ret = 0, type = 9, epType = 2, leastParam = 2}, + {name = "fmt", argus = {100}, ret = 3, type = 9, epType = 0, leastParam = 1}, + -------------------------type = 5, 统计函数--------------------------------- + {name = "deliveryinterval", argus = {}, ret = 1, type = 5, epType = 2, leastParam = 0}, + {name = "deliveryinterval2", argus = {0}, ret = 1, type = 5, epType = 2, leastParam = 0}, + {name = "deliveryinterval3", argus = {}, ret = 1, type = 5, epType = 2, leastParam = 0}, + {name = "expiredate", argus = {0}, ret = 1, type = 5, epType = 3, leastParam = 1}, + {name = "mainexpiredate", argus = {0}, ret = 1, type = 5, epType = 2, leastParam = 1}, + {name = "standardize",argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "avedev", argus = {10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "beta2", argus = {10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "relate", argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "covar", argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "mode", argus ={0,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, --可以考虑建立另一种层面的优化,以map为cache + {name = "std", argus = {10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "var", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "varp", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "stdp", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "devsq", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "steyx", argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "pearson", argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "rsq", argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "intercept",argus ={10,10,10}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "harmean", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "geomean", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "kurt", argus ={10,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "weibull", argus ={0,0,0,20}, ret = 0, type = 5, epType = 2, leastParam = 4}, + {name = "binomdist",argus = {0,0,0,20}, ret = 0, type = 5, epType = 2, leastParam = 4}, + {name = "expondist",argus ={0,0,20}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "fisher", argus ={10}, ret = 0, type = 5, epType = 2, leastParam = 1}, + {name = "fisherinv",argus ={10}, ret = 0, type = 5, epType = 2, leastParam = 1}, + {name = "hypgeomdist",argus ={0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 4}, + {name = "negbinomdist",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "permut",argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "poisson",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "critbinom",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "ftest", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "skew", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "small", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "large", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "quartile",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "trimmean", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "percentile",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + {name = "percentrank",argus ={0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 4}, + {name = "slope", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "forcast", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "drl", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "slope20", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "slope21", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "slope22", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "forcast2", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "drl2", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + {name = "nolot", argus ={0,10}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --------------------------to do formula ------------------------------------- + --{name = "finv", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "beta", argus = {0}, ret = 0, type = 5, epType = 2, leastParam = 1}, + --{name = "betadist", argus = {0,0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 5}, + --{name = "betainf", argus = {0,0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 5}, + --{name = "chidist", argus = {0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "chiinv", argus = {0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "confidence",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "fdist", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "gammadist",argus ={0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 4}, + --{name = "gammainv", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "gammainv", argus ={0}, ret = 0, type = 5, epType = 2, leastParam = 1}, + --{name = "loginv", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "lognormdist",argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "normdist", argus ={0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 4}, + --{name = "norminv", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "normsdist",argus ={0}, ret = 0, type = 5, epType = 2, leastParam = 1}, + --{name = "normsinv", argus ={0}, ret = 0, type = 5, epType = 2, leastParam = 1}, + --{name = "tdist", argus ={0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "alike", argus = {1,1,0}, ret = 0, type = 5, epType = 2, leastParam = 3}, + --{name = "tinv", argus ={0,0}, ret = 0, type = 5, epType = 2, leastParam = 2}, + --{name = "ttest", argus ={1,1,0,0}, ret = 0, type = 5, epType = 2, leastParam = 4}, + --{name = "ztest", argus ={1,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 4}, + --{name = "prob", argus ={0,0,0,0,0}, ret = 0, type = 5, epType = 2, leastParam = 5}, + + -------------------------type = 10, 交易系统-------------------------------- + {name = "order", argus = {0,0,0,0}, ret = 100, type = 10, epType = 2, leastParam = 2}, + -- {name = "passorder", argus = {0,0,0,0,0,10,0,10,0,0}, ret = 1, type = 10, epType = 2, leastParam = 7}, + {name = "passorder", argus = {0,0,0,0,0,0,0,0,0,0,0}, ret = 100, type = 10, epType = 2, leastParam = 11}, + --{name = "trade", argus = {0,0}, ret = 100, type = 10, epType = 2, leastParam = 1}, + {name = "hedgestocktrade", argus = {0,0}, ret = 100, type = 10, epType = 2, leastParam = 1}, + {name = "sleep", argus = {0}, ret = 100, type = 10, epType = 0, leastParam = 1}, + {name = "holding", argus = {0,0,0,0}, ret = 1, type = 10, epType = 3, leastParam = 4}, + {name = "holdings", argus = {0}, ret = 1003, type = 10, epType = 3, leastParam = 1}, + {name = "ordering", argus = {0,0,0,0,0}, ret = 1, type = 10, epType = 3, leastParam = 4}, + {name = "orderings", argus = {0,0}, ret = 1005, type = 10, epType = 3, leastParam = 1}, + {name = "deal", argus = {0,0,0,10,0,0}, ret = 1, type = 10, epType = 3, leastParam = 4}, + {name = "deals", argus = {0,0}, ret = 1004, type = 10, epType = 3, leastParam = 1}, + {name = "account", argus = {}, ret = 3, type = 10, epType = 3, leastParam = 0}, + {name = "accounttype", argus = {}, ret = 3, type = 10, epType = 3, leastParam = 0}, + {name = "taccount", argus = {0,0}, ret = 0, type = 10, epType = 3, leastParam = 2}, + {name = "cancel", argus = {0,0,0}, ret = 100, type = 10, epType = 3, leastParam = 3}, + --{name = "writeorder", argus = {0,0}, ret = 100, type = 10, epType = 2, leastParam = 2}, + {name = "positionadjust", argus = {0,0,0}, ret = 3, type = 10, epType = 0, leastParam = 3}, + {name = "marketvalue", argus = {0,0}, ret = 0, type = 10, epType = 3, leastParam = 2}, + --{name = "run", argus = {}, ret = 100, type = 10, epType = 0, leastParam = 0}, + {name = "loadbasket", argus = {0,0,0}, ret = 1, type = 10, epType = 3, leastParam = 3}, + {name = "stopprice", argus = {10,0,0}, ret = 0, type = 10, epType = 3, leastParam = 1}, + {name = "contractmultiplier", argus = {0}, ret = 1, type = 10, epType = 3, leastParam = 1}, + {name = "dealamounts", argus = {0,0,10}, ret = 0, type = 10, epType = 2, leastParam = 3}, + {name = "algo_passorder", argus = {0,0,0,0,0,0,0,0,0,0,0,0}, ret = 100, type = 10, epType = 2, leastParam = 12}, + {name = "cancel_task", argus = {0,0,0}, ret = 100, type = 10, epType = 3, leastParam = 3}, + {name = "readsignal", argus = {0,0}, ret = 1009, type = 10, epType = 2, leastParam = 2}, + {name = "drawsignal", argus = {20,10,10}, ret = 100, type = 10, epType = 2, leastParam = 3}, + {name = "cmdprogress", argus = {10}, ret = 0, type = 10, epType = 2, leastParam = 1}, + {name = "cmdstatus", argus = {10}, ret = 0, type = 10, epType = 2, leastParam = 1}, + -------------------------type = 11, 指标函数-------------------------------- + {name = "sar", argus = {0,0,0}, ret = 0, type = 11, epType = 2, leastParam = 3}, + {name = "sarturn", argus = {0,0,0}, ret = 0, type = 11, epType = 2, leastParam = 3}, + {name = "getcurrenttrendlinevalue", argus = {0,0,0,0,0}, ret = 0, type = 11, epType = 3, leastParam = 5}, + -------------------------type = 12, 动态行情函数-------------------------------- + {name = "condition", argus = {0,0}, ret = 0, type = 12, epType = 3, leastParam = 2}, + {name = "dynainfo", argus = {0,0,0}, ret = 0, type = 12, epType = 3, leastParam = 1}, + {name = "markettime", argus = {0}, ret = 0, type = 12, epType = 3, leastParam = 0}, + {name = "orderdirection", argus = {}, ret = 1, type = 2, epType = 2, leastParam = 0}, + --{name = "dbidvol", argus = {0}, ret = 0, type = 12, epType = 3, leastParam = 1}, + {name = "blockrank", argus = {0,0,0}, ret = 0, type = 12, epType = 2, leastParam = 3}, + -------------------------type = 13, 系统函数-------------------------------- + {name = "printout", argus = {100}, ret = 100, type = 13, epType = 2, leastParam = 1}, + {name = "setoutput", argus = {0}, ret = 100, type = 13, epType = 2, leastParam = 1}, + {name = "md5", argus = {0}, ret = 3, type = 13, epType = 2, leastParam = 1}, + {name = "crc64", argus = {0,0}, ret = 0, type = 13, epType = 2, leastParam = 2}, + {name = "crc64num", argus = {0,0}, ret = 0, type = 13, epType = 2, leastParam = 2}, + {name = "reqid", argus = {}, ret = 3, type = 13, epType = 2, leastParam = 0}, + {name = "uuid", argus = {}, ret = 1, type = 13, epType = 2, leastParam = 0}, + {name = "isequalv", argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "isgreater",argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "isgreaterequal", argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "isless", argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "islessequal", argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "isvalid", argus = {0}, ret = 2, type = 13, epType = 0, leastParam = 1}, + --{name = "nosorted", argus = {0}, ret = 2, type = 13, epType = 0, leastParam = 1}, + {name = "setdrtype", argus = {0}, ret = 100, type = 13, epType = 0, leastParam = 1}, + {name = "exist1", argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "existrange", argus = {0,0,0}, ret = 2, type = 13, epType = 0, leastParam = 3}, + {name = "removekey", argus = {0,0}, ret = 100, type = 13, epType = 0, leastParam = 2}, + {name = "holdingornot", argus = {0,0}, ret = 2, type = 13, epType = 0, leastParam = 2}, + {name = "tohold", argus = {0,0}, ret = 100, type = 13, epType = 0, leastParam = 2}, + {name = "toabandon", argus = {0,0}, ret = 100, type = 13, epType = 0, leastParam = 2}, + {name = "multisort", argus ={0,0}, ret = 100, type = 13, epType = 0, leastParam = 2}, + {name = "playsound", argus = {20,0,0}, ret = 100, type = 13, epType = 2, leastParam = 3}, + {name = "sendmail", argus={0,0,0,0,0,0,0}, ret = 2, type =13, epType=2, leastParam = 7}, + {name = "customarg", argus = {0}, ret = 3, type = 13, epType = 2, leastParam = 1}, + {name = "setcustomarg", argus = {0,0}, ret = 100, type = 13, epType = 2, leastParam = 2}, + {name = "setshareddata", argus = {0,0}, ret = 100, type = 13, epType = 2, leastParam = 2}, + {name = "getshareddata", argus = {0}, ret = 3, type = 13, epType = 2, leastParam = 1}, + {name = "serialize", argus = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ret = 3, type = 13, epType = 2, leastParam = 1}, + {name = "speak", argus = {0,0,0}, ret = 100, type = 13, epType = 2, leastParam = 3}, + + -------------------------type = 14, 扩展数据函数-------------------------------- + {name = "extdata", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 1}, + {name = "extdatarange", argus = {0,0,0}, ret = 4, type = 14, epType = 2, leastParam = 1}, + {name = "extdatamatch", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 3}, + {name = "extdatarank", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 2}, + {name = "extindexdata", argus = {0,0,0,10,10}, ret = 4, type = 14, epType = 3, leastParam = 5}, + {name = "testrank", argus = {0,0,0}, ret =0, type =14, epType =2, leastParam =2}, + {name = "extdatabigger", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 2}, + {name = "extranktovalue", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 3}, + {name = "extranktocode", argus = {0,0,0}, ret = 3, type = 14, epType = 2, leastParam = 3}, + {name = "extblockranktocode", argus = {0,0,0}, ret = 3, type = 14, epType = 2, leastParam = 3}, + {name = "selfcacheptr", argus = {0}, ret = 0, type =14 ,epType = 3, leastParam = 1}, + {name = "extdatablockrank", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 3}, + {name = "extdatablocksum", argus = {0,0}, ret = 0, type = 14, epType = 2, leastParam = 2}, + {name = "extdatablocksumrange", argus = {0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 3}, + {name = "extdatablocksplitavg", argus = {0,0,0,0}, ret = 0, type = 14, epType = 2, leastParam = 4}, + -------------------------type = 15, 组合模型函数-------------------------------- + {name = "setgroupmaxholding", argus = {0}, ret = 100,type = 15, epType = 3, leastParam = 1}, + {name = "setgroupmode", argus = {0}, ret = 100, type = 15, epType = 3, leastParam = 1}, + {name = "setgroupindex", argus = {0}, ret = 100, type = 15, epType = 3, leastParam = 1}, + {name = "creategroup", argus = {}, ret = 1007, type = 15, epType = 2, leastParam = 0}, + {name = "groupadd", argus = {0,0}, ret = 1, type = 15, epType = 2, leastParam = 2}, + {name = "groupdel", argus = {0,0}, ret = 1, type = 15, epType = 2, leastParam = 2}, + {name = "groupfind", argus = {0,0}, ret = 1, type = 15, epType = 2, leastParam = 2}, + {name = "groupclear", argus = {0}, ret = 100, type = 15, epType = 2, leastParam = 1}, + {name = "groupcount", argus = {0}, ret = 1, type = 15, epType = 2, leastParam = 1}, + {name = "groupat", argus = {0,0}, ret = 3, type = 15, epType = 2, leastParam = 2}, + {name = "getgroupbuy", argus = {0}, ret = 1007, type = 15, epType = 2, leastParam = 1}, + {name = "getgroupsell", argus = {0}, ret = 1007, type = 15, epType = 2, leastParam = 1}, + {name = "getgroupholdings", argus = {0}, ret = 1007, type = 15, epType = 2, leastParam = 1}, + {name = "groupdebug", argus = {0}, ret = 3, type = 15, epType = 2, leastParam = 1}, + {name = "splitgroupbyextdata", argus = {0,0,0,0,0}, ret = 1007, type = 15, epType = 2, leastParam = 5}, + {name = "splitgroupbyextdatatoprank", argus = {0,0,0,0,0}, ret = 1007, type = 15, epType = 2, leastParam = 5}, + {name = "groupadjust", argus = {0}, ret = 1, type = 15, epType = 2, leastParam = 1}, + {name = "getstocklist", argus = {0}, ret = 1007, type = 15, epType = 2, leastParam = 1}, + {name = "getstocklistadjust", argus = {0}, ret = 1007, type = 15, epType = 2, leastParam = 1}, + {name = "getinitgroup", argus = {}, ret = 1007, type = 15, epType = 2, leastParam = 0}, + -------------------------type = 16, 组合模型运行函数-------------------------------- + {name = "getstockinfo", argus = {0,0}, ret = 1001, type = 16, epType = 2, leastParam = 1}, + {name = "getstockinfobyindex", argus = {0,0}, ret = 1001, type = 16, epType = 2, leastParam = 1}, + {name = "isstockinholding", argus = {0}, ret = 2, type = 16, epType = 2, leastParam = 1}, + {name = "isstockinholdingbyindex", argus = {0}, ret = 2, type = 16, epType = 2, leastParam = 1}, + {name = "groupbuy", argus = {0}, ret = 100, type = 16, epType = 2, leastParam = 1}, + {name = "groupbuybyindex", argus = {0}, ret = 100, type = 16, epType = 2, leastParam = 1}, + {name = "groupsell", argus = {0}, ret = 2, type = 16, epType = 2, leastParam = 1}, + {name = "groupsellbyindex", argus = {0}, ret = 2, type = 16, epType = 2, leastParam = 1}, + {name = "grouppossiblebuy", argus = {0}, ret = 100, type = 16, epType = 2, leastParam = 1}, + {name = "grouppossiblebuybyindex", argus = {0}, ret = 100, type = 16, epType = 2, leastParam = 1}, + {name = "grouppossiblesell", argus = {0}, ret = 100, type = 16, epType = 2, leastParam = 1}, + {name = "grouppossiblesellbyindex", argus = {0}, ret = 100, type = 16, epType = 2, leastParam = 1}, + {name = "getholdinginfo", argus = {0}, ret = 1002, type = 16, epType = 2, leastParam = 1}, + {name = "getholdinginfobyindex", argus = {0}, ret = 1002, type = 16, epType = 2, leastParam = 1}, + {name = "getholdingprofit", argus = {0}, ret = 0, type = 16, epType = 2, leastParam = 1}, + {name = "getholdingrise", argus = {0}, ret = 0, type = 16, epType = 2, leastParam = 1}, + {name = "getgrouprise", argus = {0,0}, ret = 0, type = 16, epType = 2, leastParam = 2}, + {name = "getweight", argus = {0}, ret = 0, type = 16, epType = 0, leastParam = 1}, + {name = "setweight", argus = {0}, ret = 100, type = 16, epType = 0, leastParam = 1}, + {name = "weightsum", argus = {0,0}, ret = 0, type = 16, epType = 2, leastParam = 2}, + {name = "groupdealcount", argus = {}, ret = 0, type = 16, epType = 2, leastParam = 0}, + {name = "indynamicbasket", argus = {0}, ret = 2, type = 16, epType = 2, leastParam = 1}, + {name = "checkgroupresult", argus = {}, ret = 0, type = 16, epType = 2, leastParam = 0}, + {name = "getstockdatabyname", argus = {0,0,0}, ret = 0, type = 16, epType = 2, leastParam = 3}, + {name = "getstockdatabyid", argus = {0,0,0}, ret = 0, type = 16, epType = 2, leastParam = 3}, + {name = "setwriteholdings", argus = {0}, ret = 0, type = 16, epType = 2, leastParam = 1}, + -------------------------type = 0, 金融数据库函数-------------------------------- + --{name = "fdbfinancedata", argus = {10,10}, ret = 0, type = 17, epType = 2, leastParam = 2}, + --{name = "fdbfindata", argus = {0,0,10,10}, ret = 0, type = 17, epType = 2, leastParam = 4}, + --{name = "fdbldbdatabyst", argus = {0,0}, ret = 0, type = 17, epType = 2, leastParam = 2}, + + -------------------------type = 0, 投资组合相关函数--------------------------------- + {name = "marketprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "marketavgprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "algoprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "limitprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "marketvol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "algovol", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + {name = "initprice", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + -------------------------type = 17, 财务数据相关函数--------------------------------- + --{name = "", argus = {}, ret = 0, type = 0, epType = 2, leastParam = 0}, + -------------------------type = 18, 分级基金数据相关函数--------------------------------- + {name = "strufund", argus = {0, 0, 10}, ret = 0, type = 18, epType = 2, leastParam = 1}, + --{name = "gradedfund", argus = {0, 0, 10}, ret = 0, type = 18, epType = 2, leastParam = 1}, + + -------------------------type = 19, Sap相关函数--------------------------------- + --{name = "mongodata", argus = {0}, ret = 3, type = 19, epType = 2, leastParam = 1}, + --{name = "mongodata", argus = {0, 0}, ret = 3, type = 19, epType = 2, leastParam = 1}, + --{name = "mongodata", argus = {0, 0, 0}, ret = 3, type = 19, epType = 2, leastParam = 1}, + --{name = "statistic", argus = {0, 0, 0, 10}, ret = 1, type = 19, epType = 3, leastParam = 1}, + --{name = "statistic", argus = {0, 0, 0, 0, 10}, ret = 1, type = 19, epType = 3, leastParam = 1}, + --{name = "getmongoids", argus = {0}, ret = 7, type = 19, epType = 3, leastParam = 1}, + --{name = "getmongoids", argus = {0, 0}, ret = 7, type = 19, epType = 3, leastParam = 1}, + --{name = "mongodatabyid", argus = {0, 0, 0}, ret = 3, type = 19, epType = 3, leastParam = 1}, + --{name = "mongodatabyid", argus = {0, 0, 0, 0}, ret = 3, type = 19, epType = 3, leastParam = 1}, + --{name = "getdouvector", argus = {}, ret = 4, type = 19, epType = 0, leastParam = 0}, + --{name = "getintvector", argus = {}, ret = 5, type = 19, epType = 0, leastParam = 0}, + --{name = "getboovector", argus = {}, ret = 6, type = 19, epType = 0, leastParam = 0}, + --{name = "getstrvector", argus = {}, ret = 7, type = 19, epType = 0, leastParam = 0}, + + -------------------------type = 20, 通达信相关函数过后来加的函数----------------------------------- + {name = "fromopen", argus={}, ret = 1, type = 20, epType = 2, leastParam = 0}, + {name = "buyvol", argus={}, ret = 0, type = 20, epType = 2, leastParam = 0}, + {name = "sellvol", argus={}, ret = 0, type = 20, epType = 2, leastParam = 0}, + {name = "isbuyorder", argus={}, ret = 1, type = 20, epType = 2, leastParam = 0}, + {name = "issellorder", argus={}, ret = 1, type = 20, epType = 2, leastParam = 0}, + + {name = "const", argus = {10}, ret = 0, type = 20, epType = 0, leastParam = 1}, + {name = "zig", argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "peak", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "trough", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "upnday", argus = {10,10}, ret = 1, type = 20, epType = 1, leastParam = 2}, + {name = "downnday", argus = {10,10}, ret = 1, type = 20, epType = 1, leastParam = 2}, + {name = "nday", argus = {10,10}, ret = 1, type = 20, epType = 1, leastParam = 2}, + {name = "turn", argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "callpython", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 3}, + {name = "getfindata", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "getfindatabyperiod", argus = {0,0,10,10,10}, ret = 0, type = 20, epType = 2, leastParam = 5}, + {name = "getfindatayear", argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "contract", argus = {0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "getlonghubang", argus = {0,0,0}, ret = 3, type = 20, epType = 2, leastParam = 3}, + {name = "gettop10shareholder", argus = {0,0,10}, ret = 3, type = 20, epType = 2, leastParam = 3}, + {name = "gettop10shareholderbyperiod", argus = {0,0,0,0,0,0,0}, ret = 3, type = 20, epType = 2, leastParam = 7}, + {name = "getholdernum", argus = {0}, ret = 1, type = 20, epType = 2, leastParam = 1}, + {name = "gettreasury10y", argus = {0,0}, ret = 0, type = 20, epType = 1, leastParam = 1}, + {name = "finance", argus = {10}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "tickvoldistribution", argus = {10,10,10}, ret = 0, type = 20, epType = 2, leastParam = 3}, + {name = "buysellvols",argus = {10}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "transcationstatic",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "transactionstatistic",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "transcationstaticl1",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "transactionstatisticl1",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "northfinancechange",argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "hktstatistics",argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "hktdetails",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "getliangrongtrade",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "gethighlowstat",argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "getquoteaux",argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "external_data",argus = {0,0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 4}, + {name = "iopv", argus = {}, ret = 0, type = 20, epType = 2, leastParam = 0}, + {name = "get_cb_info", argus = {0,0}, ret = 3, type = 20, epType = 3, leastParam = 2}, + {name = "get_cb_info_num", argus = {0,0}, ret = 0, type = 20, epType = 3, leastParam = 2}, + {name = "get_cb_code", argus = {0,10}, ret = 3, type = 20, epType = 3, leastParam = 1}, + {name = "get_cb_convert_price", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "getopendate", argus = {0}, ret = 1, type = 20, epType = 3, leastParam = 1}, + {name = "gethismaincontract", argus = {0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "maincontractchange", argus = {0}, ret = 1, type = 20, epType = 2, leastParam = 1}, + {name = "getrealcontract", argus = {0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "getopenamount", argus = {}, ret = 0, type = 20, epType = 3, leastParam = 0}, + {name = "getopenvol", argus = {}, ret = 0, type = 20, epType = 3, leastParam = 0}, + {name = "getcapitalflow", argus = {0, 0}, ret = 3, type = 20, epType = 2, leastParam = 2}, + {name = "getcapitalflowbyholder", argus = {0, 0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "winner", argus = {10}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "cost", argus = {10}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "upstopprice", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "downstopprice", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "get_etf_statistics", argus = {0, 0}, ret = 0, type = 12, epType = 2, leastParam = 2}, + {name = "get_etf_statisticsl2", argus = {0, 0}, ret = 0, type = 12, epType = 2, leastParam = 2}, + -------------------------type = 21, 因子数据函数-------------------------------- + {name = "multifactor", argus = {0,0,0}, ret = 0, type = 21, epType = 2, leastParam = 1}, + {name = "multifactorrank", argus = {0,0,0}, ret = 0, type = 21, epType = 2, leastParam = 1}, + {name = "paramcombcalc", argus = {100}, ret = 1006, type = 20, epType = 0, leastParam = 1}, + {name = "get_ah_code", argus = {0}, ret = 3, type = 20, epType = 3, leastParam = 1}, + {name = "getoptinfo", argus = {0}, ret = 1006, type = 20, epType = 2, leastParam = 1}, + {name = "getoptlistbyundl", argus = {0, 0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "getoptcode", argus = {0, 0, 0, 0}, ret = 3, type = 20, epType = 2, leastParam = 4}, + {name = "getoptundlcode", argus = {0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "getoptcodebyno", argus = {0, 0, 0, 0, 0, 0, 0, 0}, ret = 3, type = 20, epType = 2, leastParam = 8}, + {name = "getoptcodebyno2", argus = {0, 0, 0, 0, 0, 0, 0, 0}, ret = 3, type = 20, epType = 2, leastParam = 8}, + {name = "getexerciseinterval", argus = {0, 0}, ret = 1, type = 20, epType = 2, leastParam = 2}, + {name = "getoptiv", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 3}, + {name = "getoptivgreek", argus = {0,0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 4}, + {name = "getcbconversionvalue", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "getcbconversionpremium", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "getorderflowdetail", argus = {0, 0}, ret = 1008, type = 20, epType = 2, leastParam = 2}, + {name = "getorderflow", argus = {0}, ret = 1, type = 20, epType = 2, leastParam = 1}, + {name = "getorderflowunbalance", argus = {0, 0, 0}, ret = 1, type = 20, epType = 2, leastParam = 3}, + {name = "getorderflowunbalancepricein", argus = {0, 0, 0, 0, 0}, ret = 1, type = 20, epType = 2, leastParam = 3}, + {name = "getorderflowpoc", argus = {}, ret = 0, type = 20, epType = 2, leastParam = 0}, + {name = "getorderflowdelta", argus = {}, ret = 1, type = 20, epType = 2, leastParam = 0}, + {name = "getlastfuturemonth", argus = {0, 0}, ret = 1, type = 20, epType = 2, leastParam = 2}, + {name = "getlastfuturecode", argus = {0, 0}, ret = 3, type = 20, epType = 2, leastParam = 2}, + {name = "getspotprodgroup", argus = {0}, ret = 1007, type = 20, epType = 2, leastParam = 1}, + {name = "getspotprodinst", argus = {0,0}, ret = 3, type = 20, epType = 2, leastParam = 2}, + {name = "getwarehousereceipt", argus = {0,0}, ret = 0, type = 20, epType = 2, leastParam = 2}, + {name = "getwarehousename", argus = {0,0}, ret = 3, type = 20, epType = 2, leastParam = 2}, + {name = "getfutureseats", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 3}, + {name = "getfutureseatsname", argus = {0,0,0}, ret = 3, type = 20, epType = 2, leastParam = 3}, + {name = "findfutureseats", argus = {0,0,0}, ret = 0, type = 20, epType = 2, leastParam = 3}, + {name = "productcode", argus = {0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "getfuturecode", argus = {0}, ret = 3, type = 20, epType = 2, leastParam = 1}, + {name = "convindex", argus = {0,10}, ret = 3, type = 20, epType = 2, leastParam = 2}, + {name = "isdividdate", argus = {0}, ret = 2, type = 20, epType = 2, leastParam = 1}, + {name = "markettradestatus", argus = {0}, ret = 1, type = 20, epType = 2, leastParam = 1}, + {name = "dividfactor", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, + {name = "stocktype", argus = {0}, ret = 0, type = 20, epType = 2, leastParam = 1}, +} + +index = { + --{name = "kdj", argus = {0, 0, 0}, ret = {"k", "d", "j"}, type = 3, leastParam = 3}, + --{name = "sjyy", argus = {}, ret = {"close1", "open1", "close2", "up1", "ma5"}, type = 3, leastParam = 0}, + --{name = "", argus = {}, ret = {}, type = 0, leastParam = 0}, +} + +--[[ + 系统预定义证券数据引用类型 +--]] +stocktype = { + {name = "vtopen", index = "open"}, + {name = "vthigh", index = "high"}, + {name = "vtlow", index = "low"}, + {name = "vtclose", index = "close"}, + {name = "vtvol", index = "vol"}, + {name = "vtbvol", index = "bvol"}, + {name = "vtsvol", index = "svol"}, + {name = "vtamount", index = "amount"}, + {name = "vtopenint", index = "openint"}, + {name = "vtsettleprice", index = "settleprice"}, + {name = "vtrise", index = "rise"}, + {name = "vtgclose", index = "gclose"}, + {name = "vtestimatedprice", index = "estimatedprice"}, + + {name = "vtadvance", index = ""}, --imposible... + {name = "vtdecline", index = ""}, --imposible... + + {name = "iopvopen", index = "iopvopen"}, + {name = "iopvo", index = "iopvopen"}, + {name = "iopvhigh", index = "iopvhigh"}, + {name = "iopvh", index = "iopvhigh"}, + {name = "iopvlow", index = "iopvlow"}, + {name = "iopvl", index = "iopvlow"}, + {name = "iopvclose", index = "iopvclose"}, + {name = "iopvc", index = "iopvclose"}, +} + +--[[------------------------------------------------------------ + 原始行情数据 +--------------------------------------------------------------]] +marketData = { + {name = "oopen", meta = 3001, field = "00"}, + {name = "ohigh", meta = 3001, field = "01"}, + {name = "olow", meta = 3001, field = "02"}, + {name = "oclose", meta = 3001, field = "03"}, + {name = "ovol", meta = 3001, field = "5"}, + {name = "oamount", meta = 3001, field = "6"}, + {name = "osettleprice", meta = 3001, field = "d"}, + {name = "amount", meta = 3001, field = "6"}, + {name = "indexa", meta = 3001, field = "6"}, + {name = "close", meta = 3001, field = "3"}, + {name = "c", meta = 3001, field = "3"}, + {name = "indexc", meta = 3001, field = "3"}, + {name = "high", meta = 3001, field = "1"}, + {name = "h", meta = 3001, field = "1"}, + {name = "indexh", meta = 3001, field = "1"}, + {name = "low", meta = 3001, field = "2"}, + {name = "l", meta = 3001, field = "2"}, + {name = "indexl", meta = 3001, field = "2"}, + {name = "open", meta = 3001, field = "0"}, + {name = "o", meta = 3001, field = "0"}, + {name = "indexo", meta = 3001, field = "0"}, + {name = "vol", meta = 3001, field = "5"}, + {name = "v", meta = 3001, field = "5"}, + {name = "indexv", meta = 3001, field = "5"}, + {name = "rise", meta = 0, field = "rise"}, + {name = "r", meta = 0, field = "rise"}, + {name = "gclose", meta = 0, field = "gclose"}, + {name = "settleprice", meta = 3001, field = "d"}, + {name = "settlement", meta = 3001, field = "d"}, + {name = "askprice", meta = 3000, field = "f"}, + {name = "askvol", meta = 3000, field = "g"}, + {name = "bidprice", meta = 3000, field = "d"}, + {name = "bidvol", meta = 3000, field = "e"}, + --{name = "opena", meta = 3001, field = "a"}, + {name = "openint", meta = 3001, field = "7"}, + --{name = "openv", meta = 3001, field = "c"}, + {name = "tickopen", meta = 3000, field = "8"}, + {name = "ticklast", meta = 3000, field = "0"}, + {name = "tickhigh", meta = 3000, field = "9"}, + {name = "ticklow", meta = 3000, field = "a"}, + {name = "tickamount", meta = 3000, field = "2"}, + {name = "tickvol", meta = 3000, field = "1"}, + {name = "ticklastclose", meta = 3000, field = "c"}, + {name = "tickopenint", meta = 3000, field = "3"}, + {name = "tickpe", meta = 3000, field = "6"}, + {name = "transaction", meta = 3000, field = "5"}, + {name = "ticktransaction", meta = 3000, field = "5"}, + {name = "qt", meta = 3000, field = "qt"}, --parser is using field, but what info is really request, written in formula::registerRequest + {name = "callstock", meta = 0, field = ""}, --for ease. tmp. jch + {name = "marketprice", meta = 1111, field = "0"}, + {name = "marketavgprice", meta = 1111, field = "1"}, + {name = "algoprice", meta = 1111, field = "2"}, + {name = "limitprice", meta = 1111, field = "3"}, + {name = "marketvol", meta = 1111, field = "4"}, + {name = "algovol", meta = 1111, field = "5"}, + {name = "initprice", meta = 1111, field = "6"}, + {name = "bvol", meta = 4002, field = "1"}, + {name = "svol", meta = 4002, field = "0"}, + {name = "estimatedprice", meta = 3000, field = "q"}, + + {name = "", meta = 1001, field = ""}, + + + {name = "iopvopen", meta = 4011, field = "0"}, + {name = "iopvhigh", meta = 4011, field = "1"}, + {name = "iopvlow", meta = 4011, field = "2"}, + {name = "iopvclose", meta = 4011, field = "3"}, + +} + +hiddenDataNeed = { + {name = "sar", needs = {"high", "low", "close"}}, + {name = "sarturn", needs = {"high", "low", "close"}}, + {name = "tr", needs = {"high", "low", "close"}}, + {name = "isdown", needs = {"open", "close"}}, + {name = "isequal", needs = {"open", "close"}}, + {name = "isup", needs = {"open", "close"}}, + {name = "tickvoldistribution", needs = {"close", "askprice","bidprice","vol"}}, +} + +--[[-------------------------------------------------------------- + 界面画线的类型 +----------------------------------------------------------------]] +drawtype = { + {name = "index", shape = 0 }, --指标线 + {name = "vtsolid", shape = 0 }, --普通线 + {name = "circledot", shape = 1 }, --小圆圈线 + {name = "colorstick", shape = 2 }, --彩色棒状线 + {name = "crossdot", shape = 3 }, --叉状线 + {name = "linedash", shape = 4 }, --长虚线 + {name = "linedashdot", shape = 5 }, --短虚线 + {name = "linedot", shape = 6 }, --虚线 + {name = "nodraw", shape = 7 }, --不画该线,可供调试用 + {name = "pointdot", shape = 8 }, --点状线 + {name = "stick", shape = 9 }, --棒状线 + {name = "volstick", shape = 10}, --成交量棒状线 + {name = "main", shape = 11}, --主图指标线 + {name = "kline", shape = 12}, --K线 + {name = "drawtext", shape = 13}, --输出文字 + {name = "vtslolid", shape = 14}, --垂直线:普通线 + {name = "vtdot", shape = 15}, --垂直线:点线 + {name = "vtdashdot", shape = 16}, --垂直线:虚线和点交替 + {name = "vtdashdotdot", shape = 17}, --垂直线:虚线和两点交替 + {name = "vtdash", shape = 18}, --垂直线:虚线 + {name = "vertline", shape = 19}, + {name = "barset", shape = 20}, + --{name = "drawarc", shape = 21}, + {name = "drawbkbmp", shape = 22}, + {name = "drawbmp", shape = 23}, + {name = "drawellipse", shape = 24}, + {name = "drawgbk", shape = 25}, + {name = "drawicon", shape = 26}, + {name = "drawline", shape = 27}, + {name = "drawnumber", shape = 28}, + {name = "drawrect", shape = 29}, + {name = "drawsl", shape = 30}, + --{name = "drawtextex", shape = 31}, + {name = "explain", shape = 32}, + {name = "fillrgn", shape = 33}, + --{name = "partline", shape = 34}, + {name = "polyline", shape = 35}, + --{name = "stickline", shape = 36}, + {name = "indexBool", shape = 37}, --bool型的指标线 + {name = "noaxis", shape = 38}, + {name = "nodraw", shape = 39}, + {name = "colorstickvol",shape = 40}, + {name = "segmentline", shape = 41}, + {name = "stackvolstick",shape = 42}, + {name = "waibushuju" ,shape = 43}, + {name = "drawband" ,shape = 45}, + {name = "drawgbk_div" ,shape = 46}, + {name = "drawstick" , shape = 47}, + {name = "drawarrow" , shape = 48}, + {name = "drawrectangle",shape = 49}, + + --{name = "precision0", shape = 20}, --todo + {name = "", shape = 0}, +} + +--[[ + 系统预定义颜色 +--]] +color = { + {name = "colorblack", rgb = 0}, + {name = "colorblue", rgb = 16711680}, + {name = "colorbrown", rgb = 16512}, + {name = "colorcyan", rgb = 16777044}, + {name = "colorgray", rgb = 8421504}, + {name = "colorgreen", rgb = 65280}, + {name = "colormagenta", rgb = 16711935}, + {name = "colorred", rgb = 255}, + {name = "colorwhite", rgb = 16777215}, + {name = "coloryellow", rgb = 65535}, + {name = "colorblack", rgb = 0}, + {name = "colorblack", rgb = 0}, + {name = "colorblack", rgb = 0}, + {name = "colorblack", rgb = 0}, + {name = "colorlired", rgb = 6384127}, + {name = "colorlicyan", rgb = 16307079}, +} + + +--[[-------------------------------------------------------------- + 用户未指定颜色时的系统自定义颜色 +----------------------------------------------------------------]] +--[[ +defaultColor = { + 0xff0000, 0xff00ff, 0x808080,0x008000, 0x808000, 0x800080, 0xc0c0c0, 0x008080 +} +--]] +defaultColor = { + 0xffffff, 0x00ffff, 0xff00ff,0x00ff00, 0xff0000, 0x0080ff, 0xff8000, 0x008000, 0x800080, 0xffff00 +} + +--[[-------------------------------------------------------------- + 提供闭包优化的函数 +----------------------------------------------------------------]] +ClosureFormula = { + {formula = "printout", closure = "c_print", closureIndex = 0,everyPeriod = true}, + {formula = "hhv", closure = "c_hhv", closureIndex = 2,everyPeriod = true}, + {formula = "llv", closure = "c_llv", closureIndex = 2,everyPeriod = true}, + {formula = "ma", closure = "c_ma", closureIndex = 2,everyPeriod = true}, + {formula = "ima", closure = "c_ima", closureIndex = 2,everyPeriod = true}, + {formula = "wma", closure = "c_wma", closureIndex = 2,everyPeriod = true}, + {formula = "sma", closure = "c_sma", closureIndex = 0,everyPeriod = true}, + {formula = "dma", closure = "c_dma", closureIndex = 0,everyPeriod = true}, + {formula = "ema", closure = "c_ema", closureIndex = 0,everyPeriod = true}, + {formula = "tma", closure = "c_tma", closureIndex = 0,everyPeriod = true}, + {formula = "count", closure = "c_count", closureIndex = 2,everyPeriod = true}, + {formula = "cross", closure = "c_cross", closureIndex = 0,everyPeriod = true}, + {formula = "any", closure = "c_any", closureIndex = 2,everyPeriod = true}, + {formula = "exist", closure = "c_exist", closureIndex = 2,everyPeriod = true}, + {formula = "valuewhen", closure = "c_valuewhen", closureIndex = 0,everyPeriod = true}, + {formula = "barslast", closure = "c_barslast", closureIndex = 0,everyPeriod = true}, + {formula = "barslasts", closure = "c_barslasts", closureIndex = 0,everyPeriod = true}, + {formula = "filter", closure = "c_filter", closureIndex = 2,everyPeriod = true}, + {formula = "ref", closure = "c_ref", closureIndex = 2,everyPeriod = true}, + {formula = "inblock", closure = "c_inblock", closureIndex = 2,everyPeriod = true}, + {formula = "inblock2", closure = "c_inblock2", closureIndex = 2,everyPeriod = true}, + {formula = "sum", closure = "c_sum", closureIndex = 2,everyPeriod = true}, + {formula = "longcross", closure = "c_longcross", closureIndex = 3,everyPeriod = true}, + {formula = "all", closure = "c_all", closureIndex = 2,everyPeriod = true}, + {formula = "every", closure = "c_all", closureIndex = 2,everyPeriod = true}, + {formula = "barscount", closure = "c_barscount", closureIndex = 0,everyPeriod = true}, + {formula = "barssince", closure = "c_barssince", closureIndex = 0,everyPeriod = true}, + {formula = "barssincen", closure = "c_barssincen", closureIndex = 0,everyPeriod = true}, + {formula = "beta2", closure = "c_beta2", closureIndex = 2,everyPeriod = true}, + {formula = "hhvbars", closure = "c_hhvbars", closureIndex = 2,everyPeriod = true}, + {formula = "llvbars", closure = "c_llvbars", closureIndex = 2,everyPeriod = true}, + {formula = "last", closure = "c_last", closureIndex = 2,everyPeriod = true},--两个参数 + {formula = "sfilter", closure = "c_sfilter", closureIndex = 0,everyPeriod = true}, + {formula = "tr", closure = "c_tr", closureIndex = 0,everyPeriod = true}, + {formula = "trma", closure = "c_trma", closureIndex = 2,everyPeriod = true}, + {formula = "ret", closure = "c_ret", closureIndex = 2,everyPeriod = true}, + {formula = "hod", closure = "c_hod", closureIndex = 2,everyPeriod = true}, + {formula = "lod", closure = "c_lod", closureIndex = 2,everyPeriod = true}, + {formula = "newhbars", closure = "c_newhbars", closureIndex = 2,everyPeriod = true}, + {formula = "newlbars", closure = "c_newlbars", closureIndex = 2,everyPeriod = true}, + {formula = "sumbars", closure = "c_sumbars", closureIndex = 2,everyPeriod = true}, + {formula = "sar", closure = "c_sar", closureIndex = 1,everyPeriod = true}, + {formula = "sarturn", closure = "c_sarturn", closureIndex = 2,everyPeriod = true}, + {formula = "order", closure = "c_order", closureIndex = 0,everyPeriod = false}, + -- {formula = "passorder", closure = "c_passorder", closureIndex = 0,everyPeriod = false}, + --{formula = "trade", closure = "c_trade", closureIndex = 0, everyPeriod = false}, + {formula = "hedgestocktrade", closure = "c_hedgestocktrade", closureIndex = 0, everyPeriod = false}, + {formula = "avedev", closure = "c_avedev", closureIndex = 2,everyPeriod = true}, + {formula = "relate", closure = "c_relate", closureIndex = 2,everyPeriod = true}, + {formula = "mode", closure = "c_mode", closureIndex = 2,everyPeriod = true}, + {formula = "covar", closure = "c_covar", closureIndex = 2,everyPeriod = true}, + {formula = "std", closure = "c_std", closureIndex = 2,everyPeriod = true}, + {formula = "var", closure = "c_var", closureIndex = 2,everyPeriod = true}, + {formula = "varp", closure = "c_varp", closureIndex = 2,everyPeriod = true}, + {formula = "stdp", closure = "c_stdp", closureIndex = 2,everyPeriod = true}, + {formula = "devsq", closure = "c_devsq", closureIndex = 2,everyPeriod = true}, + {formula = "trimmean", closure = "c_trimmean", closureIndex = 2,everyPeriod = true}, + {formula = "steyx", closure = "c_steyx", closureIndex = 2,everyPeriod = true}, + {formula = "pearson", closure = "c_pearson", closureIndex = 2,everyPeriod = true}, + {formula = "rsq", closure = "c_rsq", closureIndex = 2,everyPeriod = true}, + {formula = "intercept", closure = "c_intercept", closureIndex = 2,everyPeriod = true}, + --{formula = "harmean", closure = "c_harmean", closureIndex = 2,everyPeriod = true}, + --{formula = "geomean", closure = "c_geomean", closureIndex = 2,everyPeriod = true}, + {formula = "kurt", closure = "c_kurt", closureIndex = 2,everyPeriod = true}, + {formula = "ftest", closure = "c_ftest", closureIndex = 2,everyPeriod = true}, + {formula = "skew", closure = "c_skew", closureIndex = 2,everyPeriod = true}, + {formula = "small", closure = "c_small", closureIndex = 2,everyPeriod = true}, + {formula = "large", closure = "c_large", closureIndex = 2,everyPeriod = true}, + {formula = "quartile", closure = "c_quartile", closureIndex = 2,everyPeriod = true}, + {formula = "percentile", closure = "c_percentile", closureIndex = 2,everyPeriod = true}, + {formula = "median", closure = "c_median", closureIndex = 2,everyPeriod = true}, + {formula = "trimmean", closure = "c_trimmean", closureIndex = 2,everyPeriod = true}, + {formula = "percentrank", closure = "c_percentrank", closureIndex = 2,everyPeriod = true}, + {formula = "slope", closure = "c_slope", closureIndex = 2,everyPeriod = true}, + {formula = "forcast", closure = "c_forecast", closureIndex = 2,everyPeriod = true}, + {formula = "drl", closure = "c_drl", closureIndex = 2,everyPeriod = true}, + --{formula = "slope20", closure = "c_slope20", closureIndex = 2,everyPeriod = true}, + --{formula = "slope21", closure = "c_slope21", closureIndex = 2,everyPeriod = true}, + --{formula = "slope22", closure = "c_slope22", closureIndex = 2,everyPeriod = true}, + {formula = "forcast2", closure = "c_forecast2", closureIndex = 2,everyPeriod = true}, + {formula = "drl2", closure = "c_drl2", closureIndex = 2,everyPeriod = true}, + {formula = "const", closure = "c_const", closureIndex = 0,everyPeriod = false}, + {formula = "issellorder", closure = "c_issellorder", closureIndex = 1,everyPeriod = true}, + {formula = "isbuyorder", closure = "c_isbuyorder", closureIndex = 1,everyPeriod = true}, + {formula = "sellvol", closure = "c_sellvol", closureIndex = 0,everyPeriod = true}, + {formula = "buyvol", closure = "c_buyvol", closureIndex = 0,everyPeriod = true}, + {formula = "upnday", closure = "c_upnday", closureIndex = 2,everyPeriod = true}, + {formula = "downnday", closure = "c_downnday", closureIndex = 2,everyPeriod = true}, + {formula = "nday", closure = "c_nday", closureIndex = 2,everyPeriod = true}, + {formula = "turn", closure = "c_turn", closureIndex = 1,everyPeriod = true}, + {formula = "callpython", closure = "c_callpython", closureIndex = 1,everyPeriod = true}, + {formula = "getfindatayear", closure = "c_getfindatayear", closureIndex = 1,everyPeriod = true}, + {formula = "getfindata", closure = "c_getfindata", closureIndex = 1,everyPeriod = true}, + {formula = "getfindatabyperiod", closure = "c_getfindatabyperiod", closureIndex = 1,everyPeriod = true}, + {formula = "getlonghubang", closure = "c_get_longhubang", closureIndex = 1,everyPeriod = true}, + {formula = "getholdernum", closure = "c_get_holderNumber", closureIndex = 1,everyPeriod = true}, + {formula = "gettop10shareholder", closure = "c_get_top10shareholder", closureIndex = 1,everyPeriod = true}, + {formula = "gettop10shareholderbyperiod", closure = "c_get_top10shareholderbyperiod", closureIndex = 1,everyPeriod = true}, + {formula = "finance", closure = "c_finance", closureIndex = 1,everyPeriod = true}, + {formula = "tickvoldistribution", closure = "c_tickvoldistribution", closureIndex = 0,everyPeriod = true}, + {formula = "buysellvols", closure = "c_buysellvols", closureIndex = 0,everyPeriod = true}, + {formula = "transcationstatic", closure = "c_transcationstatic", closureIndex = 1,everyPeriod = true}, + {formula = "transactionstatistic", closure = "c_transcationstatic", closureIndex = 1,everyPeriod = true}, + {formula = "transcationstaticl1", closure = "c_transcationstaticl1", closureIndex = 1,everyPeriod = true}, + {formula = "transactionstatisticl1", closure = "c_transcationstaticl1", closureIndex = 1,everyPeriod = true}, + {formula = "external_data", closure = "c_external_data", closureIndex = 1,everyPeriod = true}, + {formula = "iopv", closure = "c_iopv", closureIndex = 0,everyPeriod = true}, + {formula = "get_cb_convert_price", closure = "c_get_cb_convert_price", closureIndex = 0,everyPeriod = true}, + {formula = "gethismaincontract", closure = "c_gethismaincontract",closureIndex = 1,everyPeriod = true}, + {formula = "maincontractchange", closure = "c_maincontractchange",closureIndex = 1,everyPeriod = true}, + {formula = "getrealcontract", closure = "c_getrealcontract",closureIndex = 1,everyPeriod = true}, + {formula = "getopenamount", closure = "c_getopenamount",closureIndex = 1,everyPeriod = true}, + {formula = "getopenvol", closure = "c_getopenvol",closureIndex = 1,everyPeriod = true}, + {formula = "blkname", closure = "c_blkname",closureIndex = 0,everyPeriod = true}, + {formula = "findblock", closure = "c_findblock",closureIndex = 0,everyPeriod = true}, + {formula = "orderdirection", closure = "c_orderdirection",closureIndex = 0,everyPeriod = true}, + {formula = "findindex", closure = "c_findindex",closureIndex = 0,everyPeriod = true}, + {formula = "switchindex", closure = "c_switchindex",closureIndex = 0,everyPeriod = true}, + {formula = "extdatablockrank", closure = "c_extdatablockrank",closureIndex = 0,everyPeriod = true}, + {formula = "extdatablocksum", closure = "c_extdatablocksum",closureIndex = 0,everyPeriod = true}, + {formula = "extdatablocksumrange", closure = "c_extdatablocksumrange",closureIndex = 0,everyPeriod = true}, + {formula = "extblockranktocode", closure = "c_extblockranktocode",closureIndex = 0,everyPeriod = true}, + {formula = "blocksize", closure = "c_blocksize",closureIndex = 0,everyPeriod = true}, + {formula = "stockbyblockrank", closure = "c_stockbyblockrank",closureIndex = 0,everyPeriod = true}, + {formula = "blocksum", closure = "c_blocksum",closureIndex = 0,everyPeriod = true}, + {formula = "blockrank", closure = "c_blockrank",closureIndex = 0,everyPeriod = true}, + {formula = "paramcombcalc", closure = "c_paramcombcalc", closureIndex = 0,everyPeriod = true}, + {formula = "serialize", closure = "c_serialize", closureIndex = 0,everyPeriod = true}, + {formula = "getstocklist", closure = "getstocklist", closureIndex = 0,everyPeriod = true}, + {formula = "getinitgroup", closure = "getinitgroup", closureIndex = 0,everyPeriod = true}, + {formula = "getoptinfo", closure = "c_getoptinfo", closureIndex = 0,everyPeriod = true}, + {formula = "getoptlistbyundl", closure = "c_getoptcodebyundl", closureIndex = 0,everyPeriod = true}, + {formula = "getoptcode", closure = "c_getoptcode", closureIndex = 0,everyPeriod = true}, + {formula = "getoptundlcode", closure = "c_getoptundlcode", closureIndex = 0,everyPeriod = true}, + {formula = "getoptcodebyno", closure = "c_getoptcodebyno", closureIndex = 0,everyPeriod = true}, + {formula = "getoptcodebyno2", closure = "c_getoptcodebyno2", closureIndex = 0,everyPeriod = true}, + {formula = "getexerciseinterval", closure = "c_getexerciseinterval", closureIndex = 0,everyPeriod = true}, + {formula = "tdate", closure = "c_tdate", closureIndex = 0,everyPeriod = true}, + {formula = "tweekday", closure = "c_tweekday", closureIndex = 0,everyPeriod = true}, + {formula = "timerat", closure = "c_timerat", closureIndex = 0,everyPeriod = true}, + {formula = "timerafter", closure = "c_timerafter", closureIndex = 0,everyPeriod = true}, + {formula = "deliveryinterval", closure = "c_deliveryinterval", closureIndex = 0,everyPeriod = true}, + {formula = "deliveryinterval2", closure = "c_deliveryinterval2", closureIndex = 0,everyPeriod = true}, + {formula = "deliveryinterval3", closure = "c_deliveryinterval3", closureIndex = 0,everyPeriod = true}, + {formula = "getcbconversionvalue", closure = "c_getcbconversionvalue", closureIndex = 0,everyPeriod = true}, + {formula = "getcbconversionpremium", closure = "c_getcbconversionpremium", closureIndex = 0,everyPeriod = true}, + {formula = "getorderflowdetail", closure = "c_getorderflowdetail", closureIndex = 0,everyPeriod = true}, + {formula = "getorderflow", closure = "c_getorderflow", closureIndex = 0,everyPeriod = true}, + {formula = "getorderflowunbalance", closure = "c_getorderflowunbalance", closureIndex = 0,everyPeriod = true}, + {formula = "getorderflowunbalancepricein", closure = "c_getorderflowunbalancepricein", closureIndex = 0,everyPeriod = true}, + {formula = "getorderflowpoc", closure = "c_getorderflowpoc", closureIndex = 0,everyPeriod = true}, + {formula = "getorderflowdelta", closure = "c_getorderflowdelta", closureIndex = 0,everyPeriod = true}, + {formula = "getlastfuturemonth", closure = "c_getlastfuturemonth", closureIndex = 0,everyPeriod = true}, + {formula = "getlastfuturecode", closure = "c_getlastfuturecode", closureIndex = 0,everyPeriod = true}, + {formula = "extdatablocksplitavg", closure = "c_extdatablocksplitavg", closureIndex = 0,everyPeriod = true}, + {formula = "getcapitalflow", closure = "c_getcapitalflow", closureIndex = 0,everyPeriod = true}, + {formula = "getcapitalflowbyholder", closure = "c_getcapitalflowbyholder", closureIndex = 0,everyPeriod = true}, + {formula = "getspotprodgroup", closure = "c_getspotprodgroup", closureIndex = 0,everyPeriod = true}, + {formula = "getspotprodinst", closure = "c_getspotprodinst", closureIndex = 0,everyPeriod = true}, + {formula = "getwarehousereceipt", closure = "c_getwarehousereceipt", closureIndex = 0,everyPeriod = true}, + {formula = "getwarehousename", closure = "c_getwarehousename", closureIndex = 0,everyPeriod = true}, + {formula = "getfutureseats", closure = "c_getfutureseats", closureIndex = 0,everyPeriod = true}, + {formula = "getfutureseatsname", closure = "c_getfutureseatsname", closureIndex = 0,everyPeriod = true}, + {formula = "findfutureseats", closure = "c_findfutureseats", closureIndex = 0,everyPeriod = true}, + {formula = "getfuturecode", closure = "c_getfuturecode", closureIndex = 0,everyPeriod = true}, + {formula = "winner", closure = "c_winner", closureIndex = 1, everyPeriod = true}, + {formula = "cost", closure = "c_cost", closureIndex = 1, everyPeriod = true}, + {formula = "findblocklist", closure = "c_findblocklist", closureIndex = 0, everyPeriod = true}, + {formula = "unitofquantity", closure = "c_unitofquantity", closureIndex = 0, everyPeriod = true}, + {formula = "equalweightindex", closure = "c_equalweightindex", closureIndex = 0, everyPeriod = true}, + {formula = "isindexorglr", closure = "c_isindexorglr", closureIndex = 0, everyPeriod = true}, + {formula = "isetfcode", closure = "c_isetfcode", closureIndex = 0, everyPeriod = true}, + {formula = "isindexcode", closure = "c_isindexcode", closureIndex = 0, everyPeriod = true}, + {formula = "isfuturecode", closure = "c_isfuturecode", closureIndex = 0, everyPeriod = true}, + {formula = "upstopprice", closure = "c_upstopprice", closureIndex = 1,everyPeriod = true}, + {formula = "downstopprice", closure = "c_downstopprice", closureIndex = 1,everyPeriod = true}, + {formula = "barslastcount", closure = "c_barslastcount", closureIndex = 0,everyPeriod = true}, + {formula = "dividfactor", closure = "c_dividfactor", closureIndex = 0,everyPeriod = true}, + {formula = "readsignal", closure = "c_readsignal", closureIndex = 0, everyPeriod = true}, + {formula = "drawsignal", closure = "c_drawsignal", closureIndex = 0, everyPeriod = true}, + {formula = "cmdprogress", closure = "c_cmdprogress", closureIndex = 0, everyPeriod = true}, + {formula = "cmdstatus", closure = "c_cmdstatus", closureIndex = 0, everyPeriod = true}, + {formula = "stocktype", closure = "c_stocktype", closureIndex = 0, everyPeriod = true}, + {formula = "convindex", closure = "c_convindex", closureIndex = 0, everyPeriod = true}, + {formula = "mema", closure = "c_mema", closureIndex = 0,everyPeriod = true}, + {formula = "getinstrumentdetail", closure = "c_getinstrumentdetail", closureIndex = 0,everyPeriod = true}, + {formula = "limitupperformance", closure = "c_limitupperformance", closureIndex = 0,everyPeriod = true}, + {formula = "fundnetvalue", closure = "c_fundnetvalue", closureIndex = 0,everyPeriod = true}, + {formula = "get_etf_statistics", closure = "c_get_etf_statistics", closureIndex = 0,everyPeriod = true}, + {formula = "get_etf_statisticsl2", closure = "c_get_etf_statisticsl2", closureIndex = 0,everyPeriod = true}, +} + +ClosureFormula2 = { + {formula = "callstock2", closure = "callstock2", closureIndex = 0,everyPeriod = true}, +} + +--[[-------------------------------------------------------------- + 股票代码的匹配的正则表达式集合 +----------------------------------------------------------------]] +MarketRegex = { + {market = "IF", regex = "(((if|ih|ic|tf|ts|t)([0,1,l,L][0-9]))|((IF|IH|IC|TF|TS|T)([0,1,l,L][0-9])))"}, + {market = "SH", regex = "sh(((60|90|50|58)[0-9]{4})|[0-9]{6}|000300[0-1]{2})"}, + {market = "SH", regex = "SH(((60|90|50|58)[0-9]{4})|([0-9]{6})|000300[0-1]{2})"}, + {market = "SHO", regex = "sho(1000[0-9]{4})"}, + {market = "SHO", regex = "SHO(1000[0-9]{4})"}, + {market = "SZ", regex = "sz(((00|30|20|39|03|18|16|15)[0-9]{4})|([0-9]{6}))"}, + {market = "SZ", regex = "SZ(((00|30|20|39|03|18|16|15)[0-9]{4})|([0-9]{6}))"}, + {market = "SH", regex = "204[0-9]{3}"}, + {market = "SZ", regex = "(((00|30|20|39|03|18|16|15)[0-9]{4})|([0-9]{6}))"}, + {market = "SH", regex = "(((60|90|50|58)[0-9]{4})|([0-9]{6})|000300[0-1]{2})"}, + {market = "SF", regex = "(((CU|AL|RU|FU|ZN|AU|RB|WR|BU|PB|AG|SN|NI|HC|SC|SP)[0-9]{2,4})|H30009.CSI)"}, + {market = "SF", regex = "(((cu|al|ru|fu|zn|au|rb|wr|bu|pb|ag|sn|ni|hc|sc|SP)[0-9]{2,4})|h30009.csi)"}, + {market = "DF", regex = "((a|b|m|c|y|l|v|p|i|bb|fb|pp|cs|j|jd|jm|eg)[0-9]{2,4})"}, + {market = "DF", regex = "((A|B|M|C|Y|L|V|P|I|BB|FB|PP|CS|J|JD|JM|EG)[0-9]{2,4})"}, + {market = "ZF", regex = "((AP|CF|CJ|CY|FG|JR|LR|MA|OI|PM|RI|RM|RS|SF|SM|SR|TA|WH|ZC)[0-9]{2,3})"}, + {market = "ZF", regex = "((ap|cf|cj|cy|fg|jr|lr|ma|oi|pm|ri|rm|rs|sf|sm|sr|ta|wh|zc)[0-9]{2,3})"}, + {market = "OF", regex = "OF(([0-9]{6})|(XT[0-9]{6}))"}, + {market = "OF", regex = "of(([0-9]{6})|(XT[0-9]{6}))"}, + {market = "ED", regex = "wd([0-9]{6}.(OF))"}, + {market = "ED", regex = "WD([0-9]{6}.(OF))"}, + {market = "NASDAQ", regex = "NASDAQ([a-z\\-0-9A-Z_]{1,15})"}, + {market = "NASDAQ", regex = "nasdaq([a-z\\-0-9A-Z_]{1,15})"}, + {market = "NYSE", regex = "NYSE([a-z\\-0-9A-Z_]{1,15})"}, + {market = "NYSE", regex = "nyse([a-z\\-0-9A-Z_]{1,15})"}, + {market = "SEHK", regex = "SEHK((0[0-9]{4}|84602)|([A-Z]{3,6}[0-9]{0,3}))"}, + {market = "SEHK", regex = "sehk((0[0-9]{4}|84602)|([A-Z]{3,6}[0-9]{0,3}))"}, + {market = "YSWP", regex = "yswp(cn|cn[0-9]{4}|cnindex)"}, + {market = "YSWP", regex = "YSWP(CN|CN[0-9]{4}|CNINDEX)"}, + {market = "", regex = ""}, +} + +--[[-------------------------------------------------------------- + 交易所的基本信息 + cnname : 交易所中文名称 +----------------------------------------------------------------]] +MarketBasicInfo = { + {market = "IF", opentime = {093000, 130000}, closetime = {113000, 150000}, mindiff = 0.2, volunit = 1, cnname = "中国金融期货交易所"}, + {market = "SH", opentime = {093000, 130000}, closetime = {113000, 150000}, mindiff = 0.01, volunit = 100, cnname = "上海证券交易所"}, + {market = "SZ", opentime = {093000, 130000}, closetime = {113000, 150000}, mindiff = 0.01, volunit = 100, cnname = "深圳证券交易所"}, + {market = "SHO", opentime = {093000, 130000}, closetime = {113000, 150000}, mindiff = 0.01, volunit = 100, cnname = "上海证券交易所期权"}, + {market = "HGT", opentime = {090000, 130000}, closetime = {120000, 160000}, mindiff = 0.01, volunit = 100, cnname = "沪港通"}, + {market = "SGT", opentime = {090000, 130000}, closetime = {120000, 160000}, mindiff = 0.01, volunit = 100, cnname = "深港通"}, + {market = "NEEQ", opentime = {093000, 130000}, closetime = {113000, 150000}, mindiff = 0.01, volunit = 100, cnname = "新三板"}, + {market = "SF", opentime = {090000, 103000, 133000}, closetime = {101500, 113000, 150000}, nightopentime = {210000, 000000}, nightclosetime = {235959, 023000}, mindiff = 0.1, volunit = 100, cnname = "上海期货交易所"}, + {market = "DF", opentime = {090000, 103000, 133000}, closetime = {101500, 113000, 150000}, nightopentime = {210000}, nightclosetime = {233000}, mindiff = 0.1, volunit = 100, cnname = "大连期货交易所"}, + {market = "ZF", opentime = {090000, 103000, 133000}, closetime = {101500, 113000, 150000}, nightopentime = {210000}, nightclosetime = {233000}, mindiff = 0.1, volunit = 100, cnname = "郑州期货交易所"}, + {market = "YSWP", opentime = {090000}, closetime = {155500}, nightopentime = {164000,000000}, nightclosetime = {235959, 020000}, indexopentime = {093000, 130000}, indexclosetime = {113000, 150000}, mindiff = 0.1, volunit = 100, cnname = "易盛外盘商品期货"}, + {market = "INE", opentime = {090000, 103000, 133000}, closetime = {101500, 113000, 150000}, nightopentime = {210000, 000000}, nightclosetime = {235959, 023000}, mindiff = 0.01, volunit = 100, cnname = "能源中心"}, +} + +MarketNameConfig = { + {market = "CFFEX", cnname = "中国金融期货交易所(CFFEX)"}, + {market = "CZCE", cnname = "郑州期货交易所(CZCE)"}, + {market = "DCE", cnname = "大连商品交易所(DCE)"}, + {market = "SH", cnname = "上海证券交易所(SSE)"}, + {market = "SHFE", cnname = "上海期货交易所(SHFE)"}, + {market = "SZ", cnname = "深圳证劵交易所(SZSE)"}, + {market = "OF", cnname = "开放式基金(OEF)"}, + {market = "SHO", cnname = "上海证券交易所期权(SHO)"}, + {market = "SZO", cnname = "深圳证劵交易所期权(SZO)"}, + {market = "HGT", cnname = "沪港通(HGT)"}, + {market = "SGT", cnname = "深港通(SGT)"}, + {market = "INE", cnname = "能源中心(INE)"}, + {market = "HK", cnname = "香港联合交易所(HK)"}, + {market = "BKZS", cnname = "板块指数(BKZS)"}, + {market = "GI", cnname = "全球市场(GI)"}, + {market = "LSE", cnname = "伦敦证券交易所(LSE)"}, + {market = "BJ", cnname = "北京证劵交易所(BJ)"}, + {market = "GF", cnname = "广州期货交易所(GFEX)"}, +} +--[[-------------------------------------------------------------- + 内部的变量结构 + keyword : param的名称 + items : param的具体内容及其默认值 +----------------------------------------------------------------]] +LuaStructs = { + {keyword = "tradeparam", items = { + {name="ac", isString=true, default=""}, --//账号组名 + {name="potype", isString=false, default=0}, --//分单模式 + {name="market", isString=true, default=""}, --//市场 + {name="stcode", isString=true, default=""}, --//股票代码 + {name="optype", isString=false, default=0}, --//下单方向 + {name="ortype", isString=false, default=0}, --//算法交易类型 + {name="prtype", isString=false, default=0}, --//报价方式 + {name="suprice", isString=false, default=0}, --//单笔超价 + {name="sptype", isString=false, default=0}, --//单笔超价类型 + {name="fiprice", isString=false, default=0}, --//基准价格 + {name="vol", isString=false, default=0}, --//交易总量 + {name="svtype", isString=false, default=0}, --//单笔基准量 + {name="svrate", isString=false, default=0}, --//基准量比例 + {name="orinterval", isString=false, default=0}, --//下单间隔 + {name="delinterval", isString=false, default=0}, --//撤单间隔 + {name="lvmin", isString=false, default=0}, --//尾单最小量 + {name="lvmtype", isString=false, default=0}, --//尾单最小量类型,缺省比例 + {name="spenable", isString=false, default=0}, --//超价启用笔数 + {name="prange", isString=false, default=0}, --//波动区间 + {name="prtype", isString=false, default=0}, --//波动区间类型 + {name="svmax", isString=false, default=0}, --//单笔最大量 + {name="vatime", isString=false, default=0}, --//有效时间,两种格式比如164820, + {name="mocount", isString=false, default=0}, --//最大委托次数 + {name="usernum", isString=true, default="00"}, --//用户自定义的下单编号 + },}, + {keyword = "childresult", items = { + {name = "sell", isString = false, default = 0}, + {name = "buy", isString = false, default = 0}, + {name = "price", isString = false, default = 0}, + {name = "holding", isString = false, default = 0}, + {name = "suspend", isString = false, default = 0}, + {name = "oprice", isString = false, default = 0}, + {name = "ocloseprice", isString = false, default = 0}, + },}, + {keyword = "stockholdinginfo", items = { + {name = "holding", isString = false, default = 0}, + {name = "buyprice", isString = false, default = 0}, + {name = "buydate", isString = false, default = 0}, + {name = "profit", isString = false, default = 0}, + {name = "price", isString = false, default = 0}, + {name = "holdingperiods", isString = false, default = 0}, + {name = "profitrank", isString = false, default = 0}, + {name = "position", isString = false, default = 0}, + },}, + {keyword = "positiondetail", items = { + {name = "exchangeid", isString = true, default = ""}, + {name = "exchangename", isString = true, default = ""}, + {name = "productid", isString = true, default = ""}, + {name = "productname", isString = true, default = ""}, + {name = "instrumentid", isString = true, default = ""}, + {name = "instrumentname", isString = true, default = ""}, + {name = "hedgeflag", isString = false, default = 0}, + {name = "direction", isString = false, default = 0}, + {name = "opendate", isString = true, default = ""}, + {name = "tradeid", isString = true, default = ""}, + {name = "volume", isString = false, default = 0}, + {name = "openprice", isString = false, default = 0}, + {name = "tradingday", isString = true, default = ""}, + {name = "margin", isString = false, default = 0}, + {name = "opencost", isString = false, default = 0}, + {name = "settlementprice", isString = false, default = 0}, + {name = "closevolume", isString = false, default = 0}, + {name = "closeamount", isString = false, default = 0}, + {name = "dloatprofit", isString = false, default = 0}, + {name = "closeprofit", isString = false, default = 0}, + {name = "marketvalue", isString = false, default = 0}, + {name = "positioncost", isString = false, default = 0}, + {name = "positionprofit", isString = false, default = 0}, + {name = "lastsettlementprice", isString = false, default = 0}, + {name = "instrumentvalue", isString = false, default = 0}, + {name = "istoday", isString = false, default = 0}, + {name = "xttag", isString = false, default = 0}, + {name = "stockholder", isString = true, default = ""}, + {name = "frozenvolume", isString = false, default = 0}, + {name = "canusevolume", isString = false, default = 0}, + {name = "onroadvolume", isString = false, default = 0}, + {name = "yesterdayvolume", isString = false, default = 0}, + {name = "lastprice", isString = false, default = 0}, + {name = "profitrate", isString = false, default = 0}, + {name = "futuretradetype", isString = false, default = 0}, + {name = "expiredate", isString = true, default = ""}, + {name = "comtradeid", isString = true, default = ""}, + {name = "legid", isString = false, default = 0}, + {name = "totalcost", isString = false, default = 0}, + {name = "singlecost", isString = false, default = 0}, + {name = "coveredvolume", isString = false, default = 0}, + {name = "sideflag", isString = true, default = ""}, + {name = "referencerate", isString = false, default = 0}, + {name = "structfundvol", isString = false, default = 0}, + {name = "redemptionvolume", isString = false, default = 0}, + {name = "prenablevoume", isString = false, default = 0}, + {name = "realusedmargin", isString = false, default = 0}, + {name = "royalty", isString = false, default = 0}, + },}, + {keyword = "orderdetail", items = { + {name = "exchangeid", isString = true, default = ""}, + {name = "exchangename", isString = true, default = ""}, + {name = "productid", isString = true, default = ""}, + {name = "productname", isString = true, default = ""}, + {name = "instrumentid", isString = true, default = ""}, + {name = "instrumentname", isString = true, default = ""}, + {name = "sessionid", isString = false, default = 0}, + {name = "frontid", isString = false, default = 0}, + {name = "orderref", isString = true, default = ""}, + {name = "orderpricetype", isString = false, default = 0}, + {name = "direction", isString = false, default = 0}, + {name = "offsetflag", isString = false, default = 0}, + {name = "hedgeflag", isString = false, default = 0}, + {name = "limitprice", isString = false, default = 0}, + {name = "volumetotaloriginal", isString = false, default = 0}, + {name = "ordersubmitstatus", isString = false, default = 0}, + {name = "ordersysid", isString = true, default = ""}, + {name = "orderstatus", isString = false, default = 0}, + {name = "volumetraded", isString = false, default = 0}, + {name = "volumetotal", isString = false, default = 0}, + {name = "errorid", isString = false, default = 0}, + {name = "errormsg", isString = true, default = ""}, + {name = "taskid", isString = false, default = 0}, + {name = "frozenmargin", isString = false, default = 0}, + {name = "frozencommission", isString = false, default = 0}, + {name = "insertdate", isString = true, default = ""}, + {name = "inserttime", isString = true, default = ""}, + {name = "xttag", isString = false, default = 0}, + {name = "tradeprice", isString = false, default = 0}, + {name = "cancelamount", isString = false, default = 0}, + {name = "optname", isString = true, default = ""}, + {name = "tradeamount", isString = false, default = 0}, + {name = "entrusttype", isString = false, default = 0}, + {name = "cancelinfo", isString = true, default = ""}, + {name = "undercode", isString = true, default = ""}, + {name = "covereflag", isString = false, default = 0}, + {name = "orderpricermb", isString = false, default = 0}, + {name = "tradeamountrmb", isString = false, default = 0}, + {name = "referencerate", isString = false, default = 0}, + {name = "compactno", isString = true, default = ""}, + {name = "cashgroupprop", isString = false, default = 0}, + {name = "shortoccupedmargin", isString = false, default = 0}, + {name = "xttrade", isString = true, default = ""}, + },}, + {keyword = "dealdetail", items = { + {name = "exchangeid", isString = true, default = ""}, + {name = "exchangename", isString = true, default = ""}, + {name = "productid", isString = true, default = ""}, + {name = "productname", isString = true, default = ""}, + {name = "instrumentid", isString = true, default = ""}, + {name = "instrumentname", isString = true, default = ""}, + {name = "tradeid", isString = true, default = ""}, + {name = "orderref", isString = true, default = ""}, + {name = "ordersysid", isString = true, default = ""}, + {name = "direction", isString = false, default = 0}, + {name = "offsetflag", isString = false, default = 0}, + {name = "hedgeflag", isString = false, default = 0}, + {name = "price", isString = false, default = 0}, + {name = "volume", isString = false, default = 0}, + {name = "tradedate", isString = true, default = ""}, + {name = "tradetime", isString = true, default = ""}, + {name = "comssion", isString = false, default = 0}, + {name = "tradeamount", isString = false, default = 0}, + {name = "taskid", isString = false, default = 0}, + {name = "xttag", isString = false, default = 0}, + {name = "orderpricetype", isString = false, default = 0}, + {name = "optname", isString = true, default = ""}, + {name = "entrusttype", isString = false, default = 0}, + {name = "futuretradetype", isString = false, default = 0}, + {name = "realoffsetflag", isString = false, default = 0}, + {name = "coveredflag", isString = false, default = 0}, + {name = "closetodayvolume", isString = false, default = 0}, + {name = "orderpricermb", isString = false, default = 0}, + {name = "pricermb", isString = false, default = 0}, + {name = "tradeamountrmb", isString = false, default = 0}, + {name = "referencerate", isString = false, default = 0}, + {name = "xttrade", isString = false, default = 0}, + {name = "compactno", isString = true, default = ""}, + {name = "closeprofit", isString = false, default = 0}, + },}, + {keyword = "paramResult", items = { + {name = "zh", isString = true, default = ""}, + {name = "result", isString = false, default = 0}, + {name = "total", isString = false, default = 0}, + {name = "conds", isString = false, default = 0}, + {name = "undlcode", isString = true, default = ""}, + {name = "exerciseprice", isString = false, default = 0}, + {name = "side", isString = true, default = ""}, + {name = "delivdate", isString = false, default = 0}, + {name = "exercisedate", isString = false, default = 0}, + {name = "contracttype", isString = false, default = 0}, + },}, + {keyword = "priceVolumeData", items = { + {name = "time", isString = true, default = ""}, + {name = "size", isString = false, default = 0}, + {name = "index", isString = false, default = 0}, + {name = "price", isString = false, default = 0}, + {name = "buynum", isString = false, default = 0}, + {name = "sellnum", isString = false, default = 0}, + },}, + {keyword = "ordersignal", items = { + {name = "timetag", isString = false, default = 0}, + {name = "time", isString = true, default = ""}, + {name = "date", isString = true, default = ""}, + {name = "stockcode", isString = true, default = ""}, + {name = "operation", isString = true, default = ""}, + {name = "price", isString = false, default = 0}, + {name = "volume", isString = false, default = 0}, + },}, +} diff --git a/src/xtquant/config/user/root2/lua/util.lua b/src/xtquant/config/user/root2/lua/util.lua new file mode 100644 index 0000000..10b631b --- /dev/null +++ b/src/xtquant/config/user/root2/lua/util.lua @@ -0,0 +1,292 @@ +------------------------------------------------------------- +-- 脚本引擎工作期间用到的辅助函数 +-- @author zhangjin +-- @since 2012-7-17 +------------------------------------------------------------- + + +function unpackEx(arglist) + local arg = {} + for k, v in pairs(arglist) do + print(k, v) + table.insert(arg, v) + end + return unpack(arg) +end + +function pack(func, arglist) + f = _G[func] + --print(func, type(f)) + return f(unpack(arglist)) +end + +List = {} +function List.new () + return {first = 0, last = -1} +end + +function List.pushleft (list, value) + local first = list.first - 1 + list.first = first + list[first] = value +end + +function List.pushright (list, value) + local last = list.last + 1 + list.last = last + list[last] = value +end + +function List.popright (list) + local last = list.last + if list.first > last then error("list is empty") end + local value = list[last] + list[last] = nil -- to allow garbage collection + list.last = last - 1 + return value +end + +function List.popleft(list) + local first = list.first + local value = list[first] + list[first] = nil + list.first = first + 1 + return value +end + +function isValid(v) + if type(v) == "number" then + return v == v and -1.7*10^308 < v and v < 1.7*10^308 + else + if type(v) == "nil" then + return false + else + return true + end + end +end + +function isvalid(v) + return isValid(v) +end + +function d2b(v) + return v ~= 0 --此处应该是 double值为正的时候返回true,还是double不为0的时候返回true +end + +function b2d(v) + if v then + return 1 + else + return 0 + end +end + +FLOAT_ERROR = 1e-6; +function isZero(value) + return math.abs(value) <= FLOAT_ERROR; +end + +function isGreaterThanZero(value) + return value > FLOAT_ERROR +end + +function isLessThanZero(value) + return value < -1 * FLOAT_ERROR +end + +function isequalv(left, right) + if type(left)=='string' and type(right)=='string' then + if left==right then + return true + else + return false + end + end + return isZero(left - right) +end + +function isgreater(left, right) + return isGreaterThanZero(left - right) +end + +function isgreaterequal(left, right) + return not(isLessThanZero(left - right)) +end + +function isless(left, right) + return isLessThanZero(left - right) +end + +function islessequal(left, right) + return not(isGreaterThanZero(left - right)) +end + +function isTrue(v) + local s = type(v) + if s == 'boolean' then + return v + else + return v ~= 0 + end +end + +function sortedpairs(t,comparator) + local sortedKeys = {}; + table.foreach(t, function(k,v) table.insert(sortedKeys,k) end); + table.sort(sortedKeys,comparator); + local i = 0; + local function _f(_s,_v) + i = i + 1; + local k = sortedKeys[i]; + if (k) then + return k,t[k]; + end + end + return _f,nil,nil; +end + +function getweight(stock) + local v = __stock2Weight[stock] + if v == nil then + v = 0 + end + return v +end + +function setweight(weight) + __stock2Weight = {} + for k, v in pairs(weight) do + __stock2Weight[k] = v + end +end + +function gettotalweight() + local total = 0 + for k, v in pairs(__stock2Weight) do + if k ~= 0 then + total = total + v; + end + end + return total +end + +function exist1(t, key) + return t[key] ~= nil +end + +function existrange(t, N, key) + local size = #t + return size - N < key +end + +function removekey(t, key) + t[key] = nil +end + +function toabandon(t, key) + t[key] = nil +end + +function tohold(t, key) + t[key] = 1 +end + +function holdingornot(t, val) + for _, v in pairs(t) do + if v == val then + return true + end + end + return false +end + +function sortedByKey(test_table) + local key_table = {} + local tt={} + + for key,_ in pairs(test_table) do + table.insert(key_table,key) + end + + table.sort(key_table) + + return key_table +end + + + +function multi(tbl, keytbl, num, total) + + local localTbl=tbl[num] + local t={} + local tt={} + local ttt={} + + for _,v in pairs(keytbl) do + if t[localTbl[v]] == nil then + t[localTbl[v]]={} + end + table.insert(t[localTbl[v]],v) + end + + for i,v in pairs(t) do + if #(v) > 1 and num+1 <= total then + m=multi(tbl,v,num+1,total) + t[i]=m + end + end + + tt=sortedByKey(t) + + for _,v in pairs(tt) do + n=t[v] + for ii,vv in pairs(n) do + table.insert(ttt,vv) + end + end + return ttt +end + +function oneTable(tab) + local tbl={} + function printTable(tab) + for i,v in pairs(tab) do + if type(v) == "table" then + printTable(v) + else + table.insert(tbl,v) + end + end + end + printTable(tab) + return tbl +end + +function getKeys(tbl) + k={} + for i,v in pairs(tbl) do + for ii,vv in pairs(v) do + k[ii]=0 + end + end + key={} + for i,v in pairs(k) do + table.insert(key,i) + end + return key +end + +function multisort(...) + local tttt={} + local numArgs=select("#", ...) + local tbl={} + for i=1 ,numArgs do + local arg=select(i, ...) + table.insert(tbl,arg) + end + key = getKeys(tbl) + + final = multi(tbl,key,1,#tbl) + return final +end \ No newline at end of file diff --git a/src/xtquant/config/xtquantservice.log4cxx b/src/xtquant/config/xtquantservice.log4cxx new file mode 100644 index 0000000..3ee9372 --- /dev/null +++ b/src/xtquant/config/xtquantservice.log4cxx @@ -0,0 +1,19 @@ +log4j.logger.TTConsole=INFO,ca +log4j.logger.TTStdFile=INFO,fa1 +log4j.logger.TTDbgFile=INFO,fa1 +log4j.logger.datasource=INFO,fa1 +log4j.logger.TTPerformanceFile=INFO,fa1 + +# 文件输出1 +log4j.appender.fa1=org.apache.log4j.DailyRollingFileAppender +log4j.appender.fa1.MaxFileSize=500MB +log4j.appender.fa1.MaxBackupIndex=10 +log4j.appender.fa1.DatePattern='{{XTQAUNT_LOG_DATEPATTERN}}' +log4j.appender.fa1.Append=true +log4j.appender.fa1.layout=org.apache.log4j.PatternLayout +log4j.appender.fa1.layout.ConversionPattern=%d [%p] [%t] %m%n + +# 控制台输出 +log4j.appender.ca=org.apache.log4j.ConsoleAppender +log4j.appender.ca.layout=org.apache.log4j.PatternLayout +log4j.appender.ca.layout.ConversionPattern=%d [%p] [%t] %m%n diff --git a/src/xtquant/config/xtquantservice.lua b/src/xtquant/config/xtquantservice.lua new file mode 100644 index 0000000..6789b72 --- /dev/null +++ b/src/xtquant/config/xtquantservice.lua @@ -0,0 +1,116 @@ +-- ȡClient + +function() + local ret = { + app = { + appName = "XtQuantService", + netThreadNum = 5, + netProcessThreadNum = 1, + dispatcherThreadNum = 5, + reportSeconds = 60, + logPath = "../config/xtquantservice.log4cxx", + logWatch = 1, + appendDate = 1, + timeoutSec = 0, + requestTimeoutSec = 150, + }, + threadPools = { + --datasource + datasource_callback = 10, + datasource_req = 10, + datasource_other = 5, + datasource_tradedate_change = 1, + whole_quote = 2, + -- trade linkage + linkage = 1, + accounts = 5, + persistmini = 1, + subquote = 3, + --python + pystrategy_run = 3, + --msgservice + msg_service = 1, + uicontrol_hpf_data_model = 1, + server_miniquote = 1, + server_vbaservice = 1, + --ָȫ + index_quote = 2, + }, + client_xtservice = { + tagTemplate = "xtservice", + address = g_xtservice_address, + isGetdAddressFromNameServer = 0, + reconnectSecond = -1, + timeoutSecond = 120, + requestTimeoutSecond = 150, + isUseSSL = 0, + sslCaPath = "../data/server.crt", + }, + client_xtmarketinfo = { + tagTemplate = "xtmarketinfo", + isGetdAddressFromNameServer=0, + proxyType=0, + requestTimeoutSecond=150, + proxyNeedCheck=0, + isUsedAloneIO=0, + isUseSSL=0, + address = g_defaultPorts["xtmarketinfo"], + keepAliveCheckSecond=5, + proxyPort=80, + reconnectSecond = 3, + timeoutSecond=120, + }, + server_xtquant = { + tag = "server_miniquote", + address = "0.0.0.0:58610", + isGetdAddressFromNameServer=0, + timeoutSecond=0, + keepAliveCheckSecond=0, + maxConnectionNum = 10, + isAutoBind = 0, + isUseSSL=0, + }, + metaInfo = { + ["2"] = "86400000", + ["1008"] = "0", + ["1009"] = "0", + ["1010"] = "0", + ["1011"] = "0", + ["1801"] = "0", + ["1802"] = "0", + ["1803"] = "60000", + ["1804"] = "0", + ["1806"] = "0", + ["1808"] = "60000,86400000", + ["1820"] = "0", + ["1830"] = "0", + ["2000"] = "86400000", + ["2001"] = "86400000", + ["2002"] = "86400000", + ["2002"] = "86400000", + ["2003"] = "86400000", + ["2004"] = "86400000", + ["2006"] = "86400000", + ["3000"] = "0", + ["3001"] = "60000,300000,3600000,86400000", + ["3002"] = "60000", + ["3004"] = "60000", + ["3013"] = "86400000", + ["3030"] = "0", + ["4000"] = "86400000", + ["4002"] = "60000,300000,3600000,86400000", + ["4011"] = "60000,86400000", + ["4999"] = "86400000", + ["5000"] = "0", + ["5002"] = "86400000", + ["5003"] = "0", + ["9000"] = "0", + }, + config = { + configdir = "../config", + datadir = "../userdata/xtquant", + modeldir = "../config/user", + } + } + return ret +end diff --git a/src/xtquant/config/xtquoterconfig.xml b/src/xtquant/config/xtquoterconfig.xml new file mode 100644 index 0000000..f41c28c --- /dev/null +++ b/src/xtquant/config/xtquoterconfig.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/xtquant/config/xtstocktype.lua b/src/xtquant/config/xtstocktype.lua new file mode 100644 index 0000000..739b140 --- /dev/null +++ b/src/xtquant/config/xtstocktype.lua @@ -0,0 +1,420 @@ +-- 取分类配置 + +-- 全部配置都必须用英文半角标点,不支持中文标点,不支持全角 + +g_stocktype_info = { + -- 存放基础类型 + -- 基础类型必须在同一个市场,跨市场的请用扩展类型,最后两位是市场和对应枚举值,前面随便扩展 + -- 目前enum_EXTRcCategoryID里头已经有的,必须和其保持一致 + -- 格式说明:每个类型有一个英文名,含义,市场,以及序号组成,每行写做: 英文名 = "含义,市场,序号",--注释 + -- 含义是指类型对应的代码段,比如600代码段,就是600***,“|”符号表示或,比如510XXX|520XXX 就是510代码段或者520代码段 当然,也可以直接写死代码,不用号段掩码 + -- 市场目前只有SH,SZ等,必须大写,和目前的MarketType.h定义保持一致 + -- 除开紧急情况,序号和英文名应当保持一致性,一个是枚举的字符串,一个是枚举的值 + baseTypes = { + XT_GE_MARKET_SH = "******,SH,1",--沪市 + XT_GE_MARKET_SZ = "******,SZ,2",--深市 + XT_GE_MARKET_ZJ = "******,IF,3",--中金 + XT_GE_MARKET_SQ = "******,SF,4",--上期 + XT_GE_MARKET_DS = "******,DF,5",--大商 + XT_GE_MARKET_ZS = "******,ZF,6",--郑商 + XT_GE_MARKET_OF = "******,OF,7",--开放基金 + XT_GE_MARKET_OP = "******,SHO,8",--股票期权 + XT_GE_MARKET_NEW3BOARD = "******,NEEQ,9",--新三板 + + XT_GE_SH_A = "60****|65****|688***|689***,SH,101",--沪市A股 + XT_GE_SH_B = "90****,SH,102",--沪市B股 + XT_GE_SH_FUND = "50****,SH,103",--沪市封基 + XT_GE_SH_INDEX = "000***,SH,104", --沪市指数 + XT_GE_SH_ETF = "510***|511***|512***|513***|515***|516***|517***|518***|560***|561***|562***|563***|588***,SH,105", --沪市ETF + XT_GE_SH_WARRANT = "000000,SH,106", --沪市权证 + XT_GE_SH_SUBSCRIBE = "73****|78****|712***|715***|795***,SH,107", --沪市申购 + XT_GE_SH_EXCHANGEABLE_LOAN = "132***|1370**|1371**|1372**|1373**|1374**,SH,108", --沪市可交换公司债券 + XT_GE_SH_EXCHANGEABLE_LOAN_PLEDGE = "133***,SH,109", --沪市可交换公司债券质押券出入库 + XT_GE_SH_EXCHANGEABLE_LOAN_SWAP = "192***,SH,110", --沪市可交换公司债券换股 + XT_GE_SH_PRIVATELY_LOAN_TRANSFER = "1355**|1356**|1357**|1358**|1359**,SH,111", --沪市并购重组私募债券挂牌转让 + XT_GE_SH_SHORTTERM_CORPORATE_LOAN_TRANSFER = "1350**|1351**|1352**|1353**|1354**|1175**|1176**|1177**|1178**|1179**,SH,112", --沪市证券公司短期债券挂牌转让 + XT_GE_SH_ABS = "128***,SH,113", --信贷资产支持证券 + XT_GE_SH_CORPORATE_LOAN_PLEDGE = "102***|134***|154***|164***,SH,114", --沪市公司债券质押券入库 + XT_GE_SH_CORPORATE_BOND = "1230**|1231**|1232**|1233**|1234**|136***|143***|1220**|1221**|1222**|1223**|1224**|155***|163***|175***|185***|188***|1375**|1376**|1377**|1378**|1379**|1385**|1386**|1387**|1388**|1389**|115***|240***|241***,SH,115", --沪市公司债 + XT_GE_SH_PUBLIC_PREFERED_SHARES = "330***,SH,116", --沪市公开发行优先股交易 + XT_GE_SH_NON_PUBLIC_PREFERED_SHARES_TRANSFER = "360***,SH,117",--沪市非公开发行优先股转让 + XT_GE_SH_PUBLIC_PREFERED_SHARES_SUBSCRIBE = "770***,SH,118", --沪市公开发行优先股申购 + XT_GE_SH_PUBLIC_PREFERED_SHARES_PLACEMENTS = "771***,SH,119", --沪市公开发行优先股配股/配售 + XT_GE_SH_PUBLIC_PREFERED_SHARES_SUBSCRIBE_PRICE = "772***,SH,120", --沪市公开发行优先股申购款分配 + XT_GE_SH_PUBLIC_PREFERED_SHARES_SUBSCRIBE_DISTRIBUTION = "773***,SH,121", --沪市公开发行优先股申购配号 + XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_TRUSTEE_SHIP = "201***,SH,122", --沪市国债回购(席位托管方式) + XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_ENTERPRISE = "202***,SH,123", --沪市企业债回购 + XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_BUYOUT = "203***,SH,124", --沪市国债买断式回购 + XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_IMPAWN = "204***,SH,125", --沪市新质押式国债回购 + XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE = "201***|202***|203***|204***,SH,126", -- + XT_GE_SH_GOVERNMENT_LOAN_INTEREST_BEARING = "010***|019***,SH,127", --沪市附息国债 + XT_GE_SH_FINANCIAL_BONDS = "018***,SH,128", --沪市金融债 + XT_GE_SH_GOVERNMENT_LOAN_DISCOUNT = "020***,SH,129", --沪市贴现国债 + XT_GE_SH_CENTRAL_GOVERNMENT_LOAN = "010***|019***|020***,SH,130", --沪市中央政府债(国债) + XT_GE_SH_SEPERATION_BOND = "126***,SH,131", --沪市分离债 + XT_GE_SH_ASSET_SECURITIZATION = "121***,SH,132", --沪市资产证券化 + XT_GE_SH_CREDIT_ASSET_SUPPORTED = "128***,SH,133", --信贷资产支持证券 + XT_GE_SH_ENTERPRISE_BOND = "120***|124***|1270**|1271**|1272**|1273**|1274**|1275**|1276**|1277**|1278**|129***|139***|1225**|1226**|1227**|1228**|1229**|152***|1840**|1841**|1842**|1843**|1844**|1845**|1846**|1847**|270***|271***|272***,SH,134", --沪市企业债(席位托管方式) + XT_GE_SH_CONVERTIBALE_BOND = "1000**|1001**|1002**|1003**|1004**|1005**|1006**|1007**|1008**|110***|112***|113***|1110**|1111**|1112**|1113**|1114**|1180**|1181**|1182**|1183**|1184**,SH,135", --沪市可转债 + XT_GE_SH_LOCAL_GOVERNMENT_LOAN = "130***|140***|147***|157***|160***|173***|171***|186***|101***|109***|198***|230***|231***,SH,136", --沪市地方债 + XT_GE_SH_GOVERNMENT_LOAN = "010***|019***|020***|130***|160***|171***|009***|140***|147***|157***|173***|186***|101***|109***|198***|230***|231***,SH,137", --沪市政府债(国债+地方债) + XT_GE_SH_CPB = "1370**|1371**|1372**|1373**|1374**,SH,138",--上海可交换私募债 + XT_GE_SH_STANDARD_BOND = "888880|SHRQ88,SH,139", --沪市标准券 + XT_GE_SH_CLOSED_ENDED_FUNDS = "500***|5058**,SH,140", --沪市封闭式基金 + XT_GE_SH_POLICY_JRZ = "018***|028***|038***,SH,141", --沪市政策性金融债 + XT_GE_SH_PLEDGE = "09****|102***|103***|104***|105***|106***|107***|108***|133***|134***|141***|144***|148***|153***|154***|158***|161***|164***|172***|174***|176***|187***,SH,142", --沪市上海质押代码 + XT_GE_SH_OLD_GOVERNMENT_LOAN = "009***,SH,143", --2000年前发行国债 + XT_GE_SH_GOVERNMENT_LOAN_DISCOUNT_REPURCHASE = "107***,SH,144", --记账式贴现国债质押式回购标准券入库 + XT_GE_SH_CORPORATE_BOND_REPURCHASE = "1040**|1041**|1042**|1043**|1044**,SH,145", --公司债质押式回购标准券入库 + XT_GE_SH_GOVERNMENT_LOAN_DISTRIBUTE_SALE = "7510**|7511**|7516**|7517**,SH,146", --国债分销 + XT_GE_SH_LOCAL_GOVERNMENT_LOAN_REPURCHASE = "106***|141***|148***|158***|161***|172***|174***,SH,147", --地方政府债质押式回购标准券入库 + XT_GE_SH_LOCAL_GOVERNMENT_LOAN_DISTRIBUTE_SALE = "7514**|7515**,SH,148", --地方政府债分销 + XT_GE_SH_SEPERATION_BOND_REPURCHASE = "1050**|1051**|1052**|1053**|1054**|1055**|1056**|1057*|1058**,SH,149", --分离债质押式回购标准券入库 + XT_GE_SH_BOND_OFFER_REPURCHASE = "205***,SH,150", --债券质押式报价回购 + XT_GE_SH_MS_PRIVATE_PLACEMENT_BOND = "125***|145***|150***|151***,SH,151", --中小企业私募债券在固定收益平台转让 + XT_GE_SH_CROSS_BORDER_ETF = "513**0|5109*0,SH,152", --跨境ETF + XT_GE_SH_CROSS_BORDER_LOF = "501018|501021|501023|501025|50130*|501310|501311|501313,SH,153", --跨境LOF + XT_GE_SH_INNOVATION_CLOSED_ENDED_FUNDS = "5058**,SH,154", --上海创新型封闭式基金 + XT_GE_SF_FIXED_INCOME_ETF = "511***,SH,155",--上海的固定收益类 + XT_GE_SH_GOLD = "518**0,SH,156",--上海黄金 + XT_GE_SH_RTMF = "5198**,SH,157",--上海实时申赎货币基金 + XT_GE_SH_TMF = "5116**|5117**|5118**|5119**,SH,158",--上海交易型货币基金 + XT_GE_SH_STOCK_IPO = "730***|732***|780***|712***|795***|787***|707***,SH,159",--上海股票申购代码 + XT_GE_SH_LOAN_IPO = "733***|783***|754***|7590**|713***|718***,SH,160",--上海债券申购代码 + XT_GE_SH_FUND_IPO = "735***,SH,161",--上海基金申购代码 + XT_GE_SH_NEW_SHARES_DISTRIBUTION = "741***|791***|736***|713***|716***|789***|796***|708***,SH,162",--上海新股配号 + XT_GE_SH_PLACING_FIRST_DISTRIBUTION = "747***|797***,SH,163",--上海配售首发配号 + XT_GE_SH_CONVERTIBLE_BOUND_DISTRIBUTION = "744***|794***|756***|714***|719***,SH,164",--上海可转债资金申购配号 + XT_GE_SH_SUBSCRIPTION_PRICE = "740***|790***|734***,SH,165",--上海申购款 + XT_GE_SH_BONDS_FUNDS = "743***|793***|755***,SH,166",--上海发债款 + XT_GE_SH_SHARES_ALLOTMEN = "700***|701***|702***|760***|742***|717***|762***|785***|797***,SH,167",--上海配股代码 + XT_GE_SH_SHARES_CONVERTIBLE_BOND = "704***|764***|753***|715***|726***,SH,168",--上海配转债代码 + XT_GE_SH_LOF = "501***|502***|506***,SH,169", --上海LOF + XT_GE_SH_GF = "502***,SH,170", --上海分级基金 + XT_GE_SH_XGED = "SHXGED,SH,171", --沪新股额 + XT_GE_SH_SEO = "730***|731***|780***|781***,SH,172",--沪增发股 + XT_GE_SH_PR_CB_ETF = "513031|513501|513101|5109*1|513601|513661|513051,SH,173",--上海跨境ETF申赎代码 + XT_GE_SH_LOAN_ETF = "5110**|5111**|5112**|5113**|5114**|5115**,SH,174",--上海债券ETF + XT_GE_SH_EPB_TRANSFER = "139***,SH,175",--上海企业债券挂牌转让 + XT_GE_SH_CPB_LOAN = "1370**|1371**|1372**|1373**|1374**,SH,176",--上海可交换私募债 + XT_GE_SH_LOAN_REPURCHASE_DAY_1 = "204001,SH,177", --沪市1天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_2 = "204002,SH,178", --沪市2天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_3 = "204003,SH,179", --沪市3天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_4 = "204004,SH,180", --沪市4天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_7 = "204007,SH,181", --沪市7天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_14 = "204014,SH,182", --沪市14天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_28 = "204028,SH,183", --沪市28天回购 + XT_GE_SH_LOAN_REPURCHASE_DAY_28_UPPER = "204091|204182,SH,184", --沪市28天以上回购 + XT_GE_SH_GSF = "502**1|502**2|502**4|502**5|502**7|502008|502018|502028|502038|502058|502049|502050,SH,185", --上海分级基金子基金 + XT_GE_SH_ASS = "121***|1235**|1236**|1237**|1238**|1239**|128***|131***|142***|146***|149***|156***|159***|165***|168***|169***|179***|180***|183***|189***|1931**|1932**|1933**|1934**|1935**|1936**|1937**|1938**|1939**|112***|199***|260***|261***,SH,186",--上海资产支持证券 + XT_GE_SH_LOAN_CBB_SCB_NEW = "733***|783***|754***|7590**|713***|718***,SH,187",--上海可转债可交换债新债申购代码 + --XT_GE_SH_EXCHANGEABLE_BOND = "759***,SH,188",--上海可交换债券 + XT_GE_SH_OPEN_END_FUND = "5190**|5191**|5192**|5193**|5194**|5195**|5196**|5197**|5199**,SH,189",--上海开放式基金申赎代码 + XT_GE_SH_OTHER_MF = "5195**|5199**,SH,190",--上海除交易型和实时申赎型之外的货币市场基金 + XT_GE_SH_ENTERPROSE_SUPPORT_BOND = "142***|131***|149***,SH,191",--企业支持债券(固收系统可交易) + --XT_GE_SH_INNOVATE = "605***,SH,192",--上海创新企业股票 + XT_GE_SH_INNOVATE_DISTRIBUTION = "716***,SH,193",--上海创新企业配号 + --XT_GE_SH_INNOVATE_SUBSCRIBE = "715***,SH,194", --上海创新企业申购 + XT_GE_SH_INNOVATE_ALLOTMEN = "717***,SH,195", --上海创新企业配股 + XT_GE_SH_CDR = "6091**|6092**|6093**|6094**|6095**|6096**|6097**|6098**|6099**|689***,SH,196",--上海CDR + XT_GE_SH_CDR_DISTRIBUTION = "713***|796***,SH,197",--上海CDR配号 + XT_GE_SH_CDR_SUBSCRIBE = "712***|795***,SH,198", --上海CDR申购 + XT_GE_SH_CDR_ALLOTMEN = "714***|797***,SH,199", --上海CDR配股 + XT_GE_SH_LOAN_CR_ETF = "511011|511021|511031|511051|511061|511181|511221|511261|511271|511281|511311|511361|511381|5114*1|5115*1,SH,200", --上海债券etf申赎 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_1 = "205001,SH,201", --沪市1天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_7 = "205007,SH,202", --沪市7天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_14 = "205008,SH,203", --沪市14天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_28 = "205010,SH,204", --沪市28天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_42 = "205042,SH,205", --沪市42天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_63 = "205063,SH,206", --沪市64天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_91 = "205030,SH,207", --沪市91天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_119 = "205119,SH,208", --沪市119天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_154 = "205154,SH,209", --沪市154天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_182 = "205182,SH,210", --沪市182天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_273 = "205273,SH,211", --沪市273天报价回购 + XT_GE_SH_PUBLIC_LOAN_PLEDGE = "144***|176***|187***,SH,212", --沪市公开发行公司债券出入库 + XT_GE_SH_CONVERTIBLE_BOND_STOCK = "181***|190***|191***|1950**|1951**|1952**|1953**|1954**,SH,213", --上海可转债转股 + XT_GE_SH_BOND_RESALE = "1820**|1821**|1822**|1009**,SH,214", --上海债券回售 + XT_GE_SH_BOND_PROTOCOL_REPURCHASE = "206***,SH,215", --债券质押式协议回购 + XT_GE_SH_BOND_TRIPARTITE_REPURCHASE = "207***,SH,216", --债券质押式三方回购 + XT_GE_SH_GOVERNMENT_LOAN_FUTURES = "310***,SH,217", --上海国债期货 + XT_GE_SH_OPEN_GF_LOF = "503***,SH,218", --上海多空分级LOF开放式基金 + XT_GE_SH_OPEN_END_FUND_SUBSCRIPTION = "521***,SH,219", --上海开放式基金认购 + XT_GE_SH_OPEN_END_FUND_CROSS_MARKET = "522***,SH,220", --上海开放式基金跨市场转托管 + XT_GE_SH_OPEN_END_FUND_DIVIDEND = "523***,SH,221", --上海开放式基金分红 + XT_GE_SH_OPEN_END_FUND_CONVERSION = "524***,SH,222", --上海开放式基金基金转换 + XT_GE_SH_FUND_RAISING = "705***,SH,223", --上海基金扩募 + XT_GE_SH_TENDER_OFFER = "706***,SH,224", --上海要约收购 + XT_GE_SH_ONLINE_VOTING = "738***|752***|788***,SH,225", --上海网上投票 + XT_GE_SH_ONLINE_VOTING_B = "938***,SH,226", --上海网上投票(B股) + XT_GE_SH_FUND_SUBSCRIPTION = "745***,SH,227", --上海基金申购款 + XT_GE_SH_FUND_SUBSCRIPTION_DISTRIBUTION = "746***,SH,228", --上海基金申购配号 + XT_GE_SH_INTEREST_RATE_GOVERNMENT_LOAN = "75180*,SH,229", --上海利率招标国债预发行 + XT_GE_SH_PRICE_GOVERNMENT_LOAN = "75181*,SH,230", --上海价格招标国债预发行 + XT_GE_SH_PUBLIC_LOAN_DISTRIBUTE_SALE = "75185*|75186*|75187*|75188*|75189*,SH,231", --上海公开发行公司债券网上分销 + XT_GE_SH_LOAN_ISSUANCE_DISTRIBUTE_SALE = "75197*|75198*|75199*,SH,232", --上海公司债发行分销 + XT_GE_SH_EXCHANGEABLE_BOND_DISTRIBUTION = "7580**,SH,233", --上海可交换债配号 + XT_GE_SH_DESIGNATED_TRANSACTION = "799***,SH,234", --上海指定交易 + XT_GE_SH_NON_PUBLIC_CONVERTIBLE_BOND_STOCK = "1930**,SH,235", --创新创业公司非公开发行可转换公司债券转股 + XT_GE_SH_ONLINE_VOTING_PASSWORD_SERVICE_B = "939***,SH,236", --上海网上投票密码服务(B股) + XT_GE_SH_GOVERNMENT_LOAN_PLEDGE = "090***,SH,237", --上海新国债质押式回购质押券出入库 + XT_GE_SH_COUPON_GOVERNMENT_LOAN_PLEDGE = "091***|099***,SH,238", --上海附息国债出入库 + XT_GE_SH_CONVERTIBALE_BOND_RESALE = "1009**|1820**|1821**,SH,239", --上海可转债回售 + XT_GE_SH_ENTERPRISE_LOAN_PLEDGE = "1059**,SH,240", --沪市企业债出入库 + XT_GE_SH_LOW_CORPORATE_BOND = "1230**|1231**|1232**|1233**|1234**,SH,241", --沪市非担保交收公司债低等级公司债券和定向发行的次级债券等 + XT_GE_SH_ASSET_BACKED_SECURITIES = "1235**|1236**|1237**|1238**|1239**|168***|169***|1931**|1932**|1933**|1934**|1935**|1936**|1937**|1938**|1939**,SH,242", --沪市资产支持证券 + XT_GE_SH_TRANSACTION_ETF = "5100*0|5101*0|5102*0|5104*0|5106*0|5107*0|5108*0,SH,243", --上海ETF交易 + XT_GE_SH_CR_ETF = "5100*1|5101*1|5102*1|5103*1|5104*1|5105*1|5106*1|5107*1|5108*1|5880*1|5881*1|5882*1|5883*1|5884*1|517**1,SH,244", --上海ETF申赎 + XT_GE_SH_FUND_ETF = "5100*2|5101*2|5102*2|5103*2|5104*2|5105*2|5106*2|5107*2|5108*2|5880*2|5881*2|5882*2|5883*2|5884*2,SH,245", --上海ETF沪市资金 + XT_GE_SH_SUBSCRIPTION_ETF = "5100*3|5101*3|5102*3|5103*3|5104*3|5105*3|5106*3|5107*3|5108*3,SH,246", --上海ETF认购 + XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF = "5100*4|5101*4|5102*4|5103*4|5104*4|5105*4|5106*4|5107*4|5108*4,SH,247", --上海ETF认购扣款还款代码 + XT_GE_SH_NON_FUND_ETF = "5100*5|5101*5|5102*5|5103*5|5104*5|5105*5|5106*5|5107*5|5108*5,SH,248", --上海ETF非沪市资金 + XT_GE_SH_TRANSACTION_ETF_CROSS_MARKET = "5103*0|5105*0|512**0|515**0|516**0|560**0|561**0|562**0|563**0|517**0|5883*0|5884*0|5885**|5886**,SH,249", --上海跨市场ETF交易 + XT_GE_SH_CR_ETF_CROSS_MARKET = "5103*1|5105*1|512**1,SH,250", --上海跨市场ETF申赎 + XT_GE_SH_FUND_ETF_CROSS_MARKET = "5103*2|5105*2|512**2,SH,251", --上海跨市场ETF沪市资金 + XT_GE_SH_SUBSCRIPTION_ETF_CROSS_MARKET = "5103*3|5105*3|512**3,SH,252", --上海跨市场ETF认购 + XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_CROSS_MARKET = "5103*4|5105*4|512**4,SH,253", --上海跨市场ETF认购扣款还款 + XT_GE_SH_NON_FUND_ETF_CROSS_MARKET = "5103*5|5105*5|512**5,SH,254", --上海跨市场ETF非沪市资金 + XT_GE_SH_FUND_ETF_CROSS_BORDER = "5109*2|513**2,SH,255", --上海跨境ETF沪市资金 + XT_GE_SH_SUBSCRIPTION_ETF_CROSS_BORDER = "5109*3|513**3,SH,256", --上海跨境ETF认购 + XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_CROSS_BORDER = "5109*4|513**4,SH,257", --上海跨境ETF认购扣款还款 + XT_GE_SH_NON_FUND_ETF_CROSS_BORDER = "5109*5|513**5,SH,258", --上海跨境ETF非沪市资金 + XT_GE_SH_TRANSACTION_ETF_LOAN = "5110*0|5112*0,SH,259", --上海债券ETF交易 + XT_GE_SH_SUBSCRIPTION_ETF_LOAN = "5110*3|5112*3,SH,260", --上海债券ETF认购 + XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_LOAN = "5110*4|5112*4,SH,261", --上海债券ETF认购扣款还款 + XT_GE_SH_NON_FUND_ETF_LOAN = "5110*5|5112*5,SH,262", --上海债券ETF非沪市资金 + XT_GE_SH_TRANSACTION_ETF_CR_LOAN = "5113*0|5114*0|5115*0,SH,263", --上海现金申赎债券ETF交易 + XT_GE_SH_FUND_ETF_CR_LOAN = "5113*2|5114*2|5115*2,SH,264", --上海现金申赎债券ETF沪市资金 + XT_GE_SH_SUBSCRIPTION_ETF_CR_LOAN = "5113*3|5114*3|5115*3,SH,265", --上海现金申赎债券ETF认购 + XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_CR_LOAN = "5113*4|5114*4|5115*4,SH,266", --上海现金申赎债券ETF认购扣款还款 + XT_GE_SH_NON_FUND_ETF_CR_LOAN = "5113*5|5114*5|5115*5,SH,267", --上海现金申赎债券ETF非沪市资金 + XT_GE_SH_TRANSACTION_ETF_MONETARY_FUND= "5116*0|5117*0|5118*0|5119*0,SH,268", --上海货币ETF交易 + XT_GE_SH_SUBSCRIPTION_ETF_MONETARY_FUND= "5116*3|5117*3|5118*3|5119*3,SH,269", --上海货币ETF认购 + XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_MONETARY_FUND= "5116*4|5117*4|5118*4|5119*4,SH,270", --上海货币ETF认购扣款还款 + XT_GE_SH_NON_FUND_ETF_MONETARY_FUND= "5116*5|5117*5|5118*5|5119*5,SH,271", --上海货币ETF非沪市资金 + XT_GE_SH_SUBSCRIPTION_ETF_GOLD = "518**3,SH,272", --上海黄金ETF认购 + XT_GE_SH_SUBSCRIPTION_FUND_ETF_GOLD = "518**4,SH,273", --上海黄金ETF基金认购资金 + XT_GE_SH_CR_FUND_ETF_GOLD = "518**5,SH,274", --上海黄金ETF基金申赎资金 + XT_GE_SH_MONETARY_FUND_SUBSCRIPTION = "5218**,SH,275", --上海新货币式基金认购 + XT_GE_SH_WARRANT_CREATION_CANCEL = "581***,SH,276", --上海权证创设/注销 + XT_GE_SH_TECH_BOARD = "688***|689***,SH,277", --上海科创板 + XT_GE_SH_SUBSCRIPTION_TECH_BOARD = "787***,SH,278", --上海科创板新股申购 + XT_PLEDGE_REPURCHASE_SH_DAY_1 = "206001,SH,279", --上海一天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_7 = "206007,SH,280", --上海七天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_14 = "206014,SH,281", --上海14天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_21 = "206021,SH,282", --上海21天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_30 = "206030,SH,283", --上海30天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_90 = "206090,SH,284", --上海90天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_180 = "206180,SH,285", --上海180天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_270 = "206270,SH,286", --上海270天协议回购 + XT_PLEDGE_REPURCHASE_SH_DAY_365 = "206365,SH,287", --上海365天协议回购 + XT_GE_SH_TECH_BOARD_CDR = "689***,SH,288",--上海科创板CDR + XT_GE_SH_TECH_BOARD_CDR_DISTRIBUTION = "796***,SH,289",--上海科创板CDR配号 + XT_GE_SH_TECH_BOARD_CDR_SUBSCRIBE = "795***,SH,290", --上海科创板CDR申购 + XT_GE_SH_TECH_BOARD_CDR_ALLOTMEN = "797***,SH,291", --上海科创板CDR配股 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_21 = "205021,SH,292", --沪市21天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_35 = "205035,SH,293", --沪市35天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_210 = "205210,SH,294", --沪市210天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_245 = "205245,SH,295", --沪市245天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_301 = "205301,SH,296", --沪市301天报价回购 + XT_GE_SH_QUOTATION_REPURCHASE_DAY_357 = "205357,SH,297", --沪市357天报价回购 + XT_GE_SH_NON_PUBLIC_CORPORATE_LOAN = "125***|135***|145***|150***|151***|162***|166***|167***|177***|178***|194***|196***|197***|1823**|1824**|1825**|1826**|1827**|1828**|1829**|114***|250***|251***|252***|253***|254***,SH,298", --上海非公开发行公司债券 + XT_GE_SH_ENTERPROSE_SUPPORT_AUCTION_BOND = "142***|131***,SH,299",--企业支持债券(竞价系统可交易) + XT_GE_SH_ABS_TRANSFER = "149***,SH,300", --沪市资产支持证券挂牌转让 + XT_GE_SH_TMFR = "5116*1|5117*1|5118*1|5119*1|5195*1|5199*1,SH,301",--上海交易型货币及其它货币基金申赎 + XT_GE_SH_PUBLIC_INFRASTRUCTURE_FUND = "5080**,SH,302",--上海公募基础设施基金 + XT_GE_SH_DIRECTIONAL_CONVERTIBALE_BOND = "1108**|1109**,SH,303", --沪市定向可转债 + XT_GE_SH_50_ETF = "510050,SH,350", + XT_GE_SH_300_ETF = "510300,SH,351", + XT_GE_SH_PUBLIC_CORPORATE_TRADE_LOAN = "115***|136***|143***|163***|175***|185***|188***|240***|241***,SH,352", --上海公开发行公司债现券交易 + XT_GE_SH_TECH_BOARD_ETF = "5880*1|5881*1|5882*1|5883*1|5884*1,SH,353", -- 上海科创板etf申赎 + XT_GE_SH_NON_PUBLIC_CONVERTIBLE_CORPORATE_LOAN = "1108**|1109**,SH,354", -- 上海非公开发行可转换公司债券 + XT_GE_SH_TECH_BOARD_CONVERTIBLE_BOND = "1180**|1181**|1182**|1183**|1184**,SH,355", -- 上海科创板可转债 + XT_GE_SH_TECH_BOARD_CONVERTIBLE_BOND_IPO = "718***,SH,356", -- 上海科创板可转债申购 + XT_GE_SH_TECH_BOARD_CONVERTIBLE_BOND_IPO_DISTRIBUTION = "719***,SH,357", -- 上海科创板可转债申购配号 + XT_GE_SH_TECH_BOARD_SHARES_CONVERTIBLE_BOND = "726***,SH,358", -- 上海科创板可转债配债 + XT_GE_SH_ALLOW_PLEDGE_BOND = "009***|010***|019***|020***|105***|110***|113***|120***|122***|123***|126***|127***|129***|130***|136***|137***|140***|143***|147***|152***|155***|157***|160***|163***|171***|173***|175***|188***,SH,359", --上海允许质押出入库债券 + XT_GE_SH_CPB_SWAP = "1380**|1381**|1382**|1383**|1384**,SH,360",--上海可交换私募债换股 + XT_GE_SH_GOVERNMENT_BANK_FINANCE_LOAN_DISTRIBUTE_SALE = "7512**|7513**,SH,361", --政策性银行金融债券分销 + XT_GE_SH_LOCAL_GOVERNMENT_LOAN_ONLINE_DISTRIBUTE_SALE = "75190*|75191*|75192*|75193*|75194*|75195*|75196*,SH,362", --地方政府债券网上分销 + XT_GE_SH_PLACING = "703***,SH,363", -- 上海配售 + XT_GE_SH_TECH_BOARD_ETF_ETFCODE = "5880*0|5881*0|5882*0|5883*0|5884*0,SH,364", -- 上海科创板etf + XT_GE_SH_MAIN_BOARD = "60****,SH,365", -- 沪市主板 + XT_GE_SH_500_ETF = "510500,SH,366", -- 上海500etf + XT_GE_SH_TECH_BOARD_50_ETF = "588080,SH,367", --科创板50ETF + XT_GE_SH_TECH_50_ETF = "588000,SH,368", --科创50ETF + XT_GE_SH_GOV_ALLOW = "1279**|1848**|1849**,SH,369",--上海政府支持债券 + + XT_GE_SZ_A = "00****|30****,SZ,10001",--深市A股 + XT_GE_SZ_B = "20****,SZ,10002",--深市B股 + XT_GE_SZ_FUND = "15****|16****|18****,SZ,10003",--深市封基 + XT_GE_SZ_MAIN_BOARD = "000***|001***|002***|003***|004***,SZ,10004",--深市主板 + XT_GE_SZ_SME_BOARD = "000000,SZ,10005",--深市中小板 + XT_GE_SZ_GEM_BORAD = "30****,SZ,10006",--深市创业板 + XT_GE_SZ_INDEX = "39****|98****,SZ,10007",--深市指数 + XT_GE_SZ_ETF = "158***|159***,SZ,10008",--深市ETF + XT_GE_SZ_WARRANT = "03****,SZ,10009",--深市权证 + XT_GE_SZ_GLR = "131990,SZ,10010",--深市国债回购(131990不是的,需要业务支持) + XT_GE_SZ_GLIB = "100***|101***|102***|103***|104***|105***|106***|107***,SZ,10011",--深市附息国债 + XT_GE_SZ_GLD = "108***|110***,SZ,10012",--深市贴现国债 + XT_GE_SZ_CB = "112***,SZ,10013",--深市公司债 + XT_GE_SZ_EB = "111***,SZ,10014",--深市企业债 + XT_GE_SZ_SB = "115***,SZ,10015",--深市分离债 + XT_GE_SZ_MSP_PB = "118***|114***|133***|134***,SZ,10016",--深市私募债 + XT_GE_SZ_SFMP = "119***,SZ,10017",--深市专项资金管理规划 + XT_GE_SZ_LGL = "109***|104***|105***|19****|173***,SZ,10018",--深市地方政府债 + XT_GE_SZ_CBB = "121***|122***|123***|124***|125***|126***|127***|128***|129***|10165*|10166*|10167*|10168*|10169*,SZ,10019",--深市可转债 + XT_GE_SZ_STANDAR_B = "131990|131991|SZRQ88,SZ,10020",--深市标准券 + XT_GE_SZ_CEF = "184***,SZ,10021",--深市封闭式基金 + XT_GE_SZ_LOF = "16****,SZ,10022",--深市LOF + XT_GE_SZ_GF = "150***|151***|160516|161207|162509|161715|161816|161812|161819|160417|160718|502040|163406|163109|165310|164809|164808,SZ,10023",--深市分级基金 + XT_GE_SZ_SCB_PB = "117***|1156**|1157**|1158**|1159**,SZ,10024",--深市 中小企业可交换私募债 + XT_GE_SZ_SC_SB = "1189**|1151**|1152**|1153**|1154**|1155**,SZ,10025",--深市证券公司次级债 + XT_GE_SZ_SPB = "1180**|1181**|1182**|1183**|1184**|1185**|1186**|1187**|1188**,SZ,10026",--深市其他中小企业私募债 + XT_GE_SZ_ASS = "1161**|1162**|1163**|1164**|1191**|1192**|1193**|1194**|138***|139***|135***|136***|137***|143***|144***|146***,SZ,10027",--深市企业资产支持证券 + XT_GE_SZ_GSF = "150***|151***,SZ,10028",--深市分级基金子基金 + XT_GE_SZ_CB_ETF = "159920|159941|159954|159960|159963|159605|159607|159612|159615|159632|159636|159655|159711|159712|159718|159726|159735|159740|159741|159742|159747|159750|159751|159776|159788|159792|159822|159823|159850|159866|159892|159519|159696|159699|159509|159513|159506|159501|159659,SZ,10029",--深市跨境ETF + XT_GE_SZ_CB_LOF = "160125|160416|160717|160719|161116|161210|161714|161815|162411|164701|164705|164815|164824|165510|165513|164906|163208|162719|162416|162415|161831|161229|161130|161129|161128|161127|161126|161125|161124|160924|160923|160922|160723|160644|160322|160216|160140|160138|159691|159660|159688|159687,SZ,10030",--深市跨境LOF + XT_GE_SZ_ICEF = "150***,SZ,10031",--深市创新型封闭式基金 + XT_GE_SZ_ZB_CCB = "127***,SZ,10032",--深市主板可转换公司债券 + XT_GE_SZ_CYB_CCB = "123***,SZ,10033",--深市创业板可转换公司债券 + XT_GE_SZ_ZXB_CCB = "128***,SZ,10034",--深市中小板可转换公司债券 + XT_GE_SZ_GLRA = "131***,SZ,10035",--深市国债回购(131900不是的,需要业务支持) + XT_GE_SZ_GOLD = "159934|159937|159812|159830|159831|159832|159833|159834,SZ,10036",--深市黄金 + XT_GE_SZ_RTMF = "1590**,SZ,10037",--深市实时申赎货币基金 + XT_GE_SZ_XGED = "SZXGED,SZ,10038",--深新股额 + XT_GE_SZ_SEO = "07****|37****,SZ,10039",--深增发股 + XT_GE_SZ_SA = "08****|380***,SZ,10040",--深圳配股 + XT_GE_SZ_LOAN_ETF = "159926|159972|159988|159816|159649|159650|159651,SZ,10041",--深圳债券ETF + XT_GE_SZ_LOAN_REPURCHASE_DAY_1 = "131810,SZ,10042", --深市1天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_2 = "131811,SZ,10043", --深市2天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_3 = "131800,SZ,10044", --深市3天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_4 = "131809,SZ,10045", --深市4天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_7 = "131801,SZ,10046", --深市7天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_14 = "131802,SZ,10047", --深市14天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_28 = "131803,SZ,10048", --深市28天回购 + XT_GE_SZ_LOAN_REPURCHASE_DAY_28_UPPER = "131805|131806,SZ,10049", --深市28天以上回购 + XT_GE_SZ_POB = "148***|149***,SZ,10050",--深圳公募公司债券 + XT_GE_BANK_LOAN = "1086**|1087**|1088**|1089**,SZ,10051",--深圳政策性金融债 + XT_GE_SZ_GOV_ALLOW = "1119**|130***,SZ,10052",--政府支持债券 + XT_GE_SZ_INNOVATE_KZZ = "1210**|1211**|1212**|1213**|1214**,SZ,10053",--创新创业可转债 + XT_GE_SZ_LOAN_IPO = "07****|37****|120***,SZ,10054",--深圳债券申购代码 + XT_GE_SZ_ENTERPROSE_SUPPORT_BOND = "116***|119***|138***|139***|135***|136***|137***|143***|144***|146***,SZ,10055",--企业支持债券 + XT_GE_SZ_CDR_ALLOTMEN = "08****,SZ,10056",--深圳CDR配股代码 + XT_PLEDGE_REPURCHASE_SZ_DAY_1 = "131981,SZ,10057", --深圳1天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_7 = "131982,SZ,10058", --深圳7天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_14 = "131983,SZ,10059", --深圳14天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_21 = "131984,SZ,10060", --深圳21天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_30 = "131985,SZ,10061", --深圳30天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_90 = "131986,SZ,10062", --深圳90天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_180 = "131987,SZ,10063", --深圳180天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_270 = "131988,SZ,10064", --深圳270天协议回购 + XT_PLEDGE_REPURCHASE_SZ_DAY_365 = "131989,SZ,10065", --深圳365天协议回购 + XT_GE_SZ_NON_PUBLIC_PREFERED_SHARES_TRANSFER = "140***,SZ,10066", --深市非公开优先股转让 + XT_GE_SZ_LOAN_ISSUANCE_DISTRIBUTE_SALE = "10165*|10166*|10167*|10168*|10169*,SZ,10067", --深市债券分销 + XT_GE_SZ_LOAN_REITS = "1215**|1216**|1217**|1218**|1219**,SZ,10068", --深市债券REITS代码 + XT_GE_SZ_LOAN_DIRECTIONAL = "10165*|10166*|10167*|10168*|10169*|124***,SZ,10069", --深市定向可转债 + XT_GE_SZ_300_ETF = "159919,SZ,10071", + XT_GE_SZ_OLDSHARES_PREFERRED_CONVERTIBLE_BOND = "38****,SZ,10072",--创业板上市公司可转债老股东优先配售代码 + XT_GE_SZ_GEM_BORAD_DR = "3098**|3099**,SZ,10073", --创业板存托凭证代码区间309800-309999 + XT_GE_SZ_MAIN_SME_BORAD_DR = "0010*1|0010*2|0010*3|0010*4|0010*5|0010*6|0010*7|0010*8|0010*9|0011**,SZ,10074", --主板中小板存托凭证代码区间 001001-001199 + XT_GE_SZ_SHARES_CONVERTIBLE_BOND = "08****|38****,SZ,10075",--深市配转债代码 + XT_GE_SZ_PUBLIC_INFRASTRUCTURE_FUND = "180***,SZ,10076",--深市公募基础设施基金 + XT_GE_SZ_DIRECTIONAL_CONVERTIBALE_BOND = "124***,SZ,10077", --深市定向可转债 + XT_GE_SZ_EXCHANGEABLE_LOAN = "120***|117***|1156**|1157**|1158**|1159**,SZ,10078",--深圳可交换公司债 + XT_GE_SZ_ETF_CROSS_MARKET = "159602|159603|159606|159608|159609|159610|159611|159613|159616|159617|159618|159619|159620|159621|159623|159625|159628|159629|159630|159631|159633|159635|159637|159638|159639|159640|159641|159642|159643|159645|159646|159647|159658|159663|159667|159701|159702|159703|159707|159710|159713|159715|159717|159719|159720|159723|159725|159728|159729|159730|159731|159732|159733|159736|159738|159739|159743|159745|159748|159752|159755|159757|159758|159760|159761|159763|159766|159767|159768|159769|159770|159775|159778|159779|159780|159781|159782|159786|159787|159789|159791|159793|159795|159796|159797|159798|159813|159815|159819|159820|159824|159825|159827|159828|159835|159837|159838|159839|159840|159841|159842|159843|159845|159847|159848|159849|159851|159852|159853|159855|159856|159857|159858|159859|159861|159862|159863|159864|159865|159867|159870|159871|159872|159873|159875|159876|159877|159880|159881|159883|159885|159886|159887|159888|159889|159890|159891|159895|159896|159898|159899|159999|159980|159981|159985|159649|159650|159801|159805|159806|159807|159811|159919|159922|159923|159925|159928|159929|159930|159931|159933|159935|159936|159938|159939|159940|159944|159945|159951|159953|159959|159962|159965|159968|159973|159974|159982|159986|159987|159990|159992|159993|159994|159995|159996|159997|159998|159809|159983|159978|159979|159976|159984|159869|159790|159783|159601|159672|159676|159685|159653|159657|159689|159671|159678|159679|159666|159652|159656|159669|159683|159665|159677|159675|159662|159627|159680|159907|159515|159517|159698|159510|159511|159516|159512|159503|159686|159673|159508|159690|159507|159692|159695|159622|159670|159697,SZ,10079",--深圳跨市etf + XT_GE_SZ_100_ETF = "159901,SZ,10080",--深证100ETF股票期权 + XT_GE_SZ_500_ETF = "159922,SZ,10081",--深圳500ETF + XT_GE_SZ_CYB_ETF = "159915,SZ,10082",--创业板ETF + + XT_GE_MARKET_NEW3BOARD_DELISTED = "400***|420***,NEEQ,20000", --两网及退市公司股票 新三板 + XT_GE_NEW3BOARD_PREFERED_SHARES_TRANSFER = "820***,NEEQ,20001", --全国股转非公开优先股转让 + XT_GE_BJ = "43****|83****|87****,BJ,20002",--北交 + XT_GE_BJ_SUBSCRIBE = "889***,BJ,20003",--北交所申购 + }, + --存放扩展类型,能用基础类型描述的,尽量用基础类型,基础类型执行效率高于扩展类型 + --扩展类型可以是基础类型,也可以由基础类型通过简单的&(且)|(或)运算得出,允许用小括号调整运算优先级 + --扩展类型的表达式里可以使用之前已经定义的扩展类型 + --例句 xtf = "(xtd|xta&(xtb&xtc))|((xta&xtb))|xta,303", + extraTypes = { + XT_GE_EXTRA_STOCK_A = "XT_GE_SH_A|XT_GE_SZ_A|XT_GE_BJ,100001",--沪深A股 + XT_GE_EXTRA_STOCK_B = "XT_GE_SH_B|XT_GE_SZ_B,100002",--沪深B股 + XT_GE_EXTRA_STOCK = "XT_GE_EXTRA_STOCK_A|XT_GE_EXTRA_STOCK_B,100003",--沪深狭义股票 + XT_GE_EXTRA_FUND = "XT_GE_SH_FUND|XT_GE_SZ_FUND,100004",--沪深封基 + XT_GE_EXTRA_STOCK_INDEX = "XT_GE_SZ_INDEX|XT_GE_SH_INDEX,100005",--指数 + XT_GE_EXTRA_MARKET_CF = "XT_GE_MARKET_SQ|XT_GE_MARKET_DS|XT_GE_MARKET_ZS,100006",--商品期货 + XT_GE_EXTRA_MARKET_FU = "XT_GE_MARKET_ZJ|XT_GE_EXTRA_MARKET_CF,100007",--期货市场 + XT_GE_EXTRA_MARKET_ST = "XT_GE_MARKET_SH|XT_GE_MARKET_SZ,100008",--股票 + XT_GE_EXTRA_SZ_CGL = "XT_GE_SZ_GLIB|XT_GE_SZ_GLD,100009",--深市中央政府债(国债) + XT_GE_EXTRA_SZ_GL = "XT_GE_EXTRA_SZ_CGL|XT_GE_SZ_LGL,100010",--深市政府债 + XT_GE_EXTRA_SZ_LOAN = "XT_GE_SZ_GLIB|XT_GE_SZ_GLD|XT_GE_SZ_CB|XT_GE_SZ_CBB|XT_GE_SZ_EB|XT_GE_SZ_SB|XT_GE_SZ_MSP_PB|XT_GE_SZ_SFMP|XT_GE_SZ_LGL|XT_GE_SZ_POB|XT_GE_SZ_SCB_PB|XT_GE_SZ_ZB_CCB|XT_GE_SZ_CYB_CCB|XT_GE_SZ_ZXB_CCB|XT_GE_SZ_LOAN_REITS|XT_GE_SZ_LOAN_DIRECTIONAL|XT_GE_SZ_EXCHANGEABLE_LOAN|XT_GE_SZ_ENTERPROSE_SUPPORT_BOND|XT_GE_SZ_GOV_ALLOW,100011",--深市所有债券 + XT_GE_EXTRA_STOCK_EX = "!XT_GE_EXTRA_STOCK_INDEX,100012",--广义的股票 + XT_GE_EXTRA_ETF = "XT_GE_SH_ETF|XT_GE_SZ_ETF,100013",--ETF + XT_GE_EXTRA_CLOSED_ENDED_FUNDS = "XT_GE_SH_CLOSED_ENDED_FUNDS|XT_GE_SZ_CEF,100014",--封闭式基金 + XT_GE_EXTRA_WARRANT = "XT_GE_SH_WARRANT|XT_GE_SZ_WARRANT,100015",--权证 + XT_GE_EXTRA_LOAN = "XT_GE_EXTRA_SH_LOAN|XT_GE_EXTRA_SZ_LOAN,100016",--债券 + XT_GE_EXTRA_SZ_GLR = "XT_GE_SZ_GLRA&(!XT_GE_SZ_GLR),100017",--深市国债回购 + XT_GE_EXTRA_STANDARD_BOND = "XT_GE_SH_STANDARD_BOND|XT_GE_SZ_STANDAR_B,100018",--标准券 + XT_GE_EXTRA_POLICY_JRZ = "XT_GE_SH_POLICY_JRZ,100019", + XT_GE_EXTRA_GLR = "XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE|XT_GE_EXTRA_SZ_GLR,100020",--债券回购 + XT_GE_EXTRA_REPURCHASE_IMPAWN = "XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_IMPAWN|XT_GE_EXTRA_SZ_GLR,100021",--质押式回购 + XT_GE_EXTRA_GOLD = "XT_GE_SH_GOLD|XT_GE_SZ_GOLD,100022",--黄金 + XT_GE_EXTRA_RTMF = "XT_GE_SH_RTMF|XT_GE_SZ_RTMF,100023",--实时申赎货币基金 + XT_GE_EXTRA_MONETARY_FUND = "XT_GE_EXTRA_RTMF|XT_GE_SH_TMF|XT_GE_SH_OTHER_MF,100024",--货币基金 + XT_GE_EXTRA_SH_IPO = "XT_GE_SH_STOCK_IPO|XT_GE_SH_LOAN_IPO|XT_GE_SH_FUND_IPO,100025",--上海申购代码 + XT_GE_EXTRA_CB_ETF = "XT_GE_SZ_CB_ETF|XT_GE_SH_CROSS_BORDER_ETF,100026",--跨境ETF + XT_GE_EXTRA_CB_LOF = "XT_GE_SH_CROSS_BORDER_LOF|XT_GE_SZ_CB_LOF,100027",--跨境LOF + XT_GE_EXTRA_STOCK_TRANABLE = "XT_GE_EXTRA_STOCK|XT_GE_EXTRA_FUND|XT_GE_EXTRA_ETF|XT_GE_EXTRA_WARRANT|XT_GE_SH_SUBSCRIBE|XT_GE_SZ_GEM_BORAD|XT_GE_EXTRA_GLR|XT_GE_EXTRA_LOAN|XT_GE_SF_FIXED_INCOME_ETF|XT_GE_EXTRA_GOLD|XT_GE_EXTRA_MONETARY_FUND|XT_GE_EXTRA_SZ_CGL|XT_GE_SH_CENTRAL_GOVERNMENT_LOAN|XT_GE_SH_LOCAL_GOVERNMENT_LOAN|XT_GE_SZ_LGL|XT_GE_EXTRA_SH_IPO|XT_GE_SH_PLEDGE|XT_GE_EXTRA_CB_ETF|XT_GE_EXTRA_CB_LOF|XT_GE_SH_SHARES_ALLOTMEN|XT_GE_SH_SHARES_CONVERTIBLE_BOND|XT_GE_SZ_CBB|XT_GE_SH_CONVERTIBALE_BOND|XT_GE_SH_SEO|XT_GE_SZ_SEO|XT_GE_SH_LOAN_CBB_SCB_NEW|XT_GE_SZ_LOAN_IPO|XT_GE_SZ_CDR_ALLOTMEN|XT_GE_SH_TECH_BOARD|XT_GE_SH_BOND_OFFER_REPURCHASE|XT_GE_SH_SUBSCRIPTION_TECH_BOARD|XT_GE_SH_TRANSACTION_ETF_CROSS_MARKET|XT_GE_BOND_DISTRIBUTION|XT_GE_SH_PUBLIC_PREFERED_SHARES|XT_GE_SH_NON_PUBLIC_PREFERED_SHARES_TRANSFER|XT_GE_SH_BOND_RESALE|XT_GE_SH_CONVERTIBALE_BOND_RESALE|XT_GE_SH_CONVERTIBLE_BOND_STOCK|XT_GE_SZ_NON_PUBLIC_PREFERED_SHARES_TRANSFER|XT_GE_SZ_SA|XT_GE_SZ_OLDSHARES_PREFERRED_CONVERTIBLE_BOND|XT_GE_SH_ENTERPROSE_SUPPORT_AUCTION_BOND|XT_GE_EXTRA_PUBLIC_INFRASTRUCTURE_FUND|XT_GE_BJ_SUBSCRIBE,100028",--可交易的 + XT_GE_EXTRA_MAIN_BOARD = "XT_GE_SH_MAIN_BOARD|XT_GE_SZ_MAIN_BOARD,100029", --主板 + XT_GE_EXTRA_INTRA_DAY = "XT_GE_EXTRA_LOAN|XT_GE_EXTRA_GOLD|XT_GE_SF_FIXED_INCOME_ETF|XT_GE_EXTRA_WARRANT|XT_GE_EXTRA_CB_ETF|XT_GE_EXTRA_CB_LOF|XT_GE_SH_TMF|XT_GE_SZ_RTMF|XT_GE_EXTRA_LOAN_ETF|XT_GE_EXTRA_MARKET_FU|XT_GE_MARKET_OP,100030", --回转交易 + XT_GE_EXTRA_SH_DISTRIBUTION = "XT_GE_SH_NEW_SHARES_DISTRIBUTION|XT_GE_SH_PLACING_FIRST_DISTRIBUTION|XT_GE_SH_CONVERTIBLE_BOUND_DISTRIBUTION,100031", --上海配号 + XT_GE_EXTRA_XGED = "XT_GE_SH_XGED|XT_GE_SZ_XGED,100032", --沪深新股申购额度 + XT_GE_SHARES_ALLOTMEN = "XT_GE_SH_SHARES_ALLOTMEN|XT_GE_SZ_SA,100033", --沪深配股代码 + XT_GE_EXTRA_FI_ETF = "XT_GE_SF_FIXED_INCOME_ETF,100034", --固定收益:跟踪债券指数的交易型开放式指数基金、交易型货币市场基金 + XT_GE_EXTRA_ST_FIX = "XT_GE_SH_GOVERNMENT_LOAN_INTEREST_BEARING|XT_GE_SZ_GLIB|XT_GE_SH_GOVERNMENT_LOAN_DISCOUNT|XT_GE_SZ_GLD|XT_GE_SH_GOVERNMENT_LOAN|XT_GE_EXTRA_SZ_GL|XT_GE_SZ_EB|XT_GE_SH_ENTERPRISE_BOND|XT_GE_SZ_MSP_PB|XT_GE_SH_CONVERTIBALE_BOND|XT_GE_SZ_CBB|XT_GE_SH_SEPERATION_BOND|XT_GE_SZ_SB|XT_GE_EXTRA_GLR|XT_GE_EXTRA_STANDARD_BOND|XT_GE_EXTRA_MONETARY_FUND|XT_GE_SF_FIXED_INCOME_ETF|XT_GE_SZ_LOAN_REITS|XT_GE_SZ_LOAN_DIRECTIONAL|XT_GE_EXTRA_FICC|XT_GE_EXTRA_LOAN,100035", --固定收益类 + XT_GE_EXTRA_GF = "XT_GE_SH_GF|XT_GE_SZ_GF,100036", --分级基金 + XT_GE_EXTRA_LOF = "XT_GE_SH_LOF|XT_GE_SZ_LOF,100037", --LOF + XT_GE_EXTRA_LOAN_ETF = "XT_GE_SH_LOAN_ETF|XT_GE_SZ_LOAN_ETF,100038", --债券ETF + XT_GE_EXTRA_SH_LOAN = "XT_GE_SH_GOVERNMENT_LOAN_INTEREST_BEARING|XT_GE_SH_GOVERNMENT_LOAN_DISCOUNT|XT_GE_SH_LOCAL_GOVERNMENT_LOAN|XT_GE_SH_CONVERTIBALE_BOND|XT_GE_SH_CORPORATE_BOND|XT_GE_SH_ENTERPRISE_BOND|XT_GE_SH_ASSET_SECURITIZATION|XT_GE_SH_SEPERATION_BOND_REPURCHASE|XT_GE_SH_FINANCIAL_BONDS|XT_GE_SH_CREDIT_ASSET_SUPPORTED|XT_GE_SH_EXCHANGEABLE_LOAN|XT_GE_SH_PRIVATELY_LOAN_TRANSFER|XT_GE_SH_SHORTTERM_CORPORATE_LOAN_TRANSFER|XT_GE_SH_EPB_TRANSFER|XT_GE_SH_CPB|XT_GE_SH_CPB_LOAN|XT_GE_SH_GOVERNMENT_LOAN|XT_GE_SH_SEPERATION_BOND|XT_GE_SH_LOAN_CBB_SCB_NEW|XT_GE_SH_MS_PRIVATE_PLACEMENT_BOND|XT_GE_SH_ENTERPROSE_SUPPORT_BOND|XT_GE_SH_PUBLIC_CORPORATE_TRADE_LOAN|XT_GE_SH_NON_PUBLIC_CORPORATE_LOAN|XT_GE_SH_ASS|XT_GE_SH_GOV_ALLOW,100039", --上海债券 + XT_GE_EXTRA_REPURCHASE_DAY_1 = "XT_GE_SH_LOAN_REPURCHASE_DAY_1|XT_GE_SZ_LOAN_REPURCHASE_DAY_1,100040", --1天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_2 = "XT_GE_SH_LOAN_REPURCHASE_DAY_2|XT_GE_SZ_LOAN_REPURCHASE_DAY_2,100041", --2天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_3 = "XT_GE_SH_LOAN_REPURCHASE_DAY_3|XT_GE_SZ_LOAN_REPURCHASE_DAY_3,100042", --3天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_4 = "XT_GE_SH_LOAN_REPURCHASE_DAY_4|XT_GE_SZ_LOAN_REPURCHASE_DAY_4,100043", --4天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_7 = "XT_GE_SH_LOAN_REPURCHASE_DAY_7|XT_GE_SZ_LOAN_REPURCHASE_DAY_7,100044", --7天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_14 = "XT_GE_SH_LOAN_REPURCHASE_DAY_14|XT_GE_SZ_LOAN_REPURCHASE_DAY_14,100045", --14天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_28 = "XT_GE_SH_LOAN_REPURCHASE_DAY_28|XT_GE_SZ_LOAN_REPURCHASE_DAY_28,100046", --28天逆回购 + XT_GE_EXTRA_REPURCHASE_DAY_28_UPPER = "XT_GE_SH_LOAN_REPURCHASE_DAY_28_UPPER|XT_GE_SZ_LOAN_REPURCHASE_DAY_28_UPPER,100047", --28天以上逆回购 + XT_GE_EXTRA_NOT_CLOSING_AUCTION_MATCH = "XT_GE_EXTRA_SH_LOAN|XT_GE_SH_FUND|XT_GE_SH_ETF|XT_GE_SH_BOND_OFFER_REPURCHASE|XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE|XT_GE_SH_PLEDGE,100070", --上交所不执行收盘集合竞价的品种 + XT_GE_EXTRA_RATE_BOND = "XT_GE_SH_GOVERNMENT_LOAN|XT_GE_EXTRA_SZ_GL|XT_GE_SH_POLICY_JRZ|XT_GE_BANK_LOAN,100080", --利率类债券 + XT_GE_EXTRA_FICC = "XT_GE_SH_CORPORATE_BOND|XT_GE_SH_OLD_GOVERNMENT_LOAN|XT_GE_SH_CENTRAL_GOVERNMENT_LOAN|XT_GE_SH_GOVERNMENT_LOAN_INTEREST_BEARING|XT_GE_SH_CONVERTIBALE_BOND|XT_GE_SH_ENTERPRISE_BOND|XT_GE_SH_MS_PRIVATE_PLACEMENT_BOND|XT_GE_SH_SEPERATION_BOND|XT_GE_SH_CREDIT_ASSET_SUPPORTED|XT_GE_SH_GOVERNMENT_LOAN|XT_GE_SH_ENTERPROSE_SUPPORT_BOND|XT_GE_SH_EXCHANGEABLE_LOAN|XT_GE_SH_PRIVATELY_LOAN_TRANSFER|XT_GE_SH_SHORTTERM_CORPORATE_LOAN_TRANSFER|XT_GE_SH_CPB_LOAN|XT_GE_SH_NON_PUBLIC_CONVERTIBLE_BOND_STOCK|XT_GE_SH_BOND_TRIPARTITE_REPURCHASE|XT_GE_SH_LOW_CORPORATE_BOND|XT_GE_SH_ASSET_BACKED_SECURITIES|XT_GE_SH_LOCAL_GOVERNMENT_LOAN|XT_GE_SH_NON_PUBLIC_CORPORATE_LOAN|XT_GE_SH_ASS|XT_GE_SH_FINANCIAL_BONDS|XT_GE_SH_BOND_PROTOCOL_REPURCHASE|XT_GE_SH_BOND_TRIPARTITE_REPURCHASE|XT_GE_SZ_LOAN_REITS|XT_GE_SZ_LOAN_DIRECTIONAL|XT_GE_SH_PUBLIC_CORPORATE_TRADE_LOAN|XT_GE_SH_PUBLIC_INFRASTRUCTURE_FUND|XT_GE_SH_GOV_ALLOW,100090", --固收 + XT_GE_BOND_DISTRIBUTION = "XT_GE_SH_GOVERNMENT_LOAN_DISTRIBUTE_SALE|XT_GE_SH_LOCAL_GOVERNMENT_LOAN_DISTRIBUTE_SALE|XT_GE_SH_PUBLIC_LOAN_DISTRIBUTE_SALE|XT_GE_SH_LOAN_ISSUANCE_DISTRIBUTE_SALE|XT_GE_SZ_LOAN_ISSUANCE_DISTRIBUTE_SALE|XT_GE_SH_GOVERNMENT_BANK_FINANCE_LOAN_DISTRIBUTE_SALE|XT_GE_SH_LOCAL_GOVERNMENT_LOAN_ONLINE_DISTRIBUTE_SALE,100200", --债券分销 + XT_GE_EXTRA_50_ETF = "XT_GE_SH_50_ETF,100100", + XT_GE_EXTRA_300_ETF = "XT_GE_SH_300_ETF|XT_GE_SZ_300_ETF,100101", + XT_GE_EXTRA_BLOCK_TRADING = "XT_GE_EXTRA_STOCK_TRANABLE|XT_GE_SH_ASS|XT_GE_SZ_ASS,100102", --大宗平台可交易 + XT_GE_EXTRA_PUBLIC_INFRASTRUCTURE_FUND = "XT_GE_SH_PUBLIC_INFRASTRUCTURE_FUND|XT_GE_SZ_PUBLIC_INFRASTRUCTURE_FUND,100103",--公募基础设施基金 + XT_GE_EXTRA_DIRECTIONAL_CONVERTIBALE_BOND = "XT_GE_SH_DIRECTIONAL_CONVERTIBALE_BOND|XT_GE_SZ_DIRECTIONAL_CONVERTIBALE_BOND,100104",--定向可转债 + XT_GE_EXTRA_ALLOW_PLEDGE = "XT_GE_SH_ALLOW_PLEDGE_BOND|XT_GE_EXTRA_SZ_LOAN,100105",--允许质押出入库债券 + XT_GE_EXTRA_SH_CORPORATE_LOAN = "XT_GE_SH_NON_PUBLIC_CONVERTIBLE_CORPORATE_LOAN|XT_GE_SH_CPB|XT_GE_SH_NON_PUBLIC_CORPORATE_LOAN|XT_GE_SH_MS_PRIVATE_PLACEMENT_BOND,100106",--上海私募债 + XT_GE_EXTRA_SZ_CORPORATE_LOAN = "XT_GE_SZ_LOAN_DIRECTIONAL|XT_GE_SZ_SCB_PB|XT_GE_SZ_MSP_PB|XT_GE_SZ_SPB,100107",--深圳私募债 + XT_GE_EXTRA_100_ETF = "XT_GE_SZ_100_ETF,100108",--100ETF股票期权 + XT_GE_EXTRA_500_ETF = "XT_GE_SH_500_ETF|XT_GE_SZ_500_ETF,100109",--500ETF + XT_GE_EXTRA_CYB_ETF = "XT_GE_SZ_CYB_ETF,100110",--创业板ETF + }, + optionTypes + = { + XT_GE_SF_FTOPTION = "au*****?|cu*****?|al*****?|ru*****?|zn*****?|ag*****?|rb*****?|br*****?,SFO,100050", --上期所期权 四位到期数字,一位C/P + XT_GE_ZF_FTOPTION = "SR****?|CF****?|TA****?|MA****?|RM****?|ZC****?|OI****?|PK****?|PX****?|SH****?,ZFO,100051", --郑商所期权 三位到期数字,一位C/P + XT_GE_DF_FTOPTION = "m****-*-?|c****-*-?|i****-*-?|y****-*-?|p****-*-?|j****-*-?|jm****-*-?|pg****-*-?|v****-*-?|l****-*-?|pp****-*-?|a****-*-?|b****-*-?|eg****-*-?|eb****-*-?,DFO,100052", --大商所期权 四位到期数字,一位C/P + XT_GE_IF_FTOPTION = "HO?|IO?|MO?|ZO?|IF?^&&IO?,IFO,100053", --中金所期权,HO\IO期权专用,有IF套利期权合约 + XT_GE_SF_ARBITAGE_FTOPTION = ",SFO,100054",--上期所套利期权 + XT_GE_ZF_ARBITAGE_FTOPTION = ",ZFO,100055",--郑商所套利期权 + XT_GE_DF_ARBITAGE_FTOPTION = ",DFO,100056",--大商所套利期权 + XT_GE_IF_ARBITAGE_FTOPTION = "IF?^&&IO?|HO?^&&?|IO?^&&?,IFO,100057",--中金所套利期权 + XT_GE_INE_FTOPTION = "bc*****?|lu*****?|nr*****?|sc*****?,INE,100058", --能源中心期权 四位到期数字,一位C/P + XT_GE_INE_ARBITAGE_FTOPTION = ",INE,100059",--能源中心套利期权 + XT_GE_GF_FTOPTION = "si*****?|lc*****?,GFO,100060",--广期所期权 + }, + abroadMarkets = { + abroadFutureMarkets = "OSE,LPPM,CBF,LBMA,NYB,EUREX,COMEX,CME,ICE,CBOT,SGX,LME,NYMEX,MX,LIFFE,ASX,HKFE", + }, +} diff --git a/src/xtquant/doc/xtdata.md b/src/xtquant/doc/xtdata.md new file mode 100644 index 0000000..ad58c19 --- /dev/null +++ b/src/xtquant/doc/xtdata.md @@ -0,0 +1,1885 @@ +# XtQuant.XtData 行情模块 + +xtdata是xtquant库中提供行情相关数据的模块,本模块旨在提供精简直接的数据满足量化交易者的数据需求,作为python库的形式可以被灵活添加到各种策略脚本中。 + +主要提供行情数据(历史和实时的K线和分笔)、财务数据、合约基础信息、板块和行业分类信息等通用的行情数据。 + +## 版本信息 + +- 2020-09-01 + - 初稿 +- 2020-09-07 + - 添加获取除权数据的接口`get_divid_factors`,附录添加除权数据字段说明 + - 获取合约信息、获取合约类型接口完善 + - 获取交易日列表接口`get_trading_dates`支持指定日期范围 +- 2020-09-13 + - 添加财务数据接口,调整获取和下载财务数据接口的说明,添加财务数据报表字段列表 + - 将 “补充” 字样调整为 “下载”,“supply” 接口调整为 “download” +- 2020-09-13 + - 将`volumn`拼写错误修正为`volume`,影响范围: + - `tick`和`l2quote`周期行情数据 - 成交量字段 + - 合约基础信息 - 总股本、流通股本 +- 2020-11-23 + - 合约基础信息`CreateDate` `OpenDate`字段类型由`int`调整为`str` + - 添加数据字典部分,添加level2数据字段枚举值说明 +- 2021-07-20 + - 添加新版下载数据接口 + - 下载行情数据 `download_history_data2` + - 下载财务数据 `download_financial_data2` +- 2021-12-30 + - 数据字典调整 + - 委托方向、成交类型添加关于上交所、深交所撤单信息的区分说明 +- 2022-06-27 + - 数据字典调整 + - K线添加前收价、停牌标记字段 +- 2022-09-30 + - 添加交易日历相关接口 + - 获取节假日数据 `get_holidays` + - 获取交易日历 `get_trading_calendar` + - 获取交易时段 `get_trade_times` +- 2023-01-04 + - 添加千档行情获取 +- 2023-01-31 + - 可转债基础信息的下载 `download_cb_data` + - 可转债基础信息的获取 `get_cb_info` +- 2023-02-06 + - 添加连接到指定ip端口的接口 `reconnect` +- 2023-02-07 + - 支持QMT的本地Python模式 + - 优化多个QMT同时存在的场景,自动选择xtdata连接的端口 +- 2023-03-27 + - 新股申购信息获取 `get_ipo_info` +- 2023-04-13 + - 本地python模式下运行VBA函数 +- 2023-07-27 + - 文档部分描述修改 +- 2023-08-21 + - 数据接口支持投研版特色数据 + - 参考 `接口概述` - `常用类型说明` - `周期` - `投研版 - 特色数据` + - 获取合约基础信息 `get_instrument_detail` 返回字段调整 + - 增加 `ExchangeCode` `UniCode` + - 添加获取可用周期列表的接口 `get_period_list` +- 2023-10-11 + - `get_market_data_ex`支持获取ETF申赎清单数据 + - 数据字典添加 现金替代标志 +- 2023-11-09 + - `download_history_data`添加增量下载参数,支持指定起始时间的增量下载 +- 2023-11-22 + - `get_trading_calendar`不再支持`tradetimes`参数 +- 2023-11-27 + - ETF申赎清单信息下载 `download_etf_info` + - ETF申赎清单信息获取 `get_etf_info` +- 2023-11-28 + - 添加节假日下载`download_holiday_data` +- 2023-12-27 + - 获取板块成份股列表接口增加北交所板块 +- 2024-01-19 + - `get_market_data_ex`支持获取期货历史主力合约数据 + - `get_option_detail_data`支持获取商品期权品种的数据 + - `get_market_data_ex`支持获取日线以上周期的K线数据 + - 周线`1w`、月线`1mon`、季度线`1q`、半年线`1hy`、年线`1y` +- 2024-01-22 + - `get_trade_times`改名为`get_trading_time` + - `get_trading_time`更新实现逻辑 +- 2024-01-26 + - 获取合约基础信息 `get_instrument_detail` 支持获取全部合约信息字段 +- 2024-05-15 + - 获取最新交易日k线数据`get_full_kline` +- 2024-05-27 + - `get_stock_list_in_sector` 增加`real_timetag`参数 +- 2024-09-06 + - 增加`subscribe_quote2`,与第一版相比,多一个除权参数 + +## 接口概述 + +### 运行逻辑 + +xtdata提供和MiniQmt的交互接口,本质是和MiniQmt建立连接,由MiniQmt处理行情数据请求,再把结果回传返回到python层。使用的行情服务器以及能获取到的行情数据和MiniQmt是一致的,要检查数据或者切换连接时直接操作MiniQmt即可。 + +对于数据获取接口,使用时需要先确保MiniQmt已有所需要的数据,如果不足可以通过补充数据接口补充,再调用数据获取接口获取。 + +对于订阅接口,直接设置数据回调,数据到来时会由回调返回。订阅接收到的数据一般会保存下来,同种数据不需要再单独补充。 + +### 接口分类 + +- 行情数据(K线数据、分笔数据,订阅和主动获取的接口) + - 功能划分(接口前缀) + - subscribe_ / unsubscribe_ 订阅/反订阅 + - get_ 获取数据 + - download_ 下载数据 + - 常见用法 + - level1数据的历史部分用`download_history_data`补充,实时部分用`subscribe_XXX`订阅,使用`get_XXX`获取 + - level2数据实时部分用`subscribe_XXX`订阅,用`get_l2_XXX`获取。level2函数无历史数据存储,跨交易日后数据清理 +- 财务数据 +- 合约基础信息 +- 基础行情数据板块分类信息等基础信息 + +### 常用类型说明 + +- stock_code - 合约代码 + - 格式为 `code.market`,例如`000001.SZ` `600000.SH` `000300.SH` +- period - 周期,用于表示要获取的周期和具体数据类型, 通过get_period_list()获取更多可支持周期 + - level1数据 + - `tick` - 分笔数据 + - `1m` - 1分钟线 + - `5m` - 5分钟线 + - `15m` - 15分钟线 + - `30m` - 30分钟线 + - `1h` - 1小时线 + - `1d` - 日线 + - `1w` - 周线 + - `1mon` - 月线 + - `1q` - 季度线 + - `1hy` - 半年线 + - `1y` - 年线 + - level2数据 + - `l2quote` - level2实时行情快照 + - `l2order` - level2逐笔委托 + - `l2transaction` - level2逐笔成交 + - `l2quoteaux` - level2实时行情补充(总买总卖) + - `l2orderqueue` - level2委买委卖一档委托队列 + - `l2thousand` - level2千档盘口 + - 投研版 - 特色数据 + - `warehousereceipt` - 期货仓单 + - `futureholderrank` - 期货席位 + - `interactiveqa` - 互动问答 + - 逐笔成交统计 + - `transactioncount1m` - 逐笔成交统计1分钟级 + - `transactioncount1d` - 逐笔成交统计日级 + - `delistchangebond` - 退市可转债信息 + - `replacechangebond` - 待发可转债信息 + - `specialtreatment` - ST 变更历史 + - 港股通(深港通、沪港通)资金流向 + - `northfinancechange1m` - 港股通资金流向1分钟级 + - `northfinancechange1d` - 港股通资金流向日级 + - `dividendplaninfo` - 红利分配方案信息 + - `historycontract` - 过期合约列表 + - `optionhistorycontract` - 期权历史信息 + - `historymaincontract` - 历史主力合约 + - `stoppricedata` - 涨跌停数据 + - `snapshotindex` - 快照指标数据 + - `stocklistchange` - 板块成分股变动历史 + - `limitupperformance` - 涨跌表现 + - `announcement` - 新闻公告 + - `hktstatistics` - 港股持仓统计 + - `hktdetails` - 港股持仓明细 + - `riskfreerate` - 无风险利率 + - `etfstatistics` - etf实时申赎数据level1 + - `etfstatisticsl2` - etf实时申赎数据level2 +- 时间范围,用于指定数据请求范围,表示的范围是`[start_time, end_time]`区间(包含前后边界)中最后不多于`count`个数据 + - start_time - 起始时间,为空则认为是最早的起始时间 + - end_time - 结束时间,为空则认为是最新的结束时间 + - count - 数据个数,大于0为正常限制返回个数,等于0为不需要返回,-1为返回全部 + - 通常以`[start_time = '', end_time = '', count = -1]`表示完整数据范围,但数据请求范围过大会导致返回时间变长,需要按需裁剪请求范围 +- dividend_type - 除权方式,用于K线数据复权计算,对`tick`等其他周期数据无效 + - `none` 不复权 + - `front` 前复权 + - `back` 后复权 + - `front_ratio` 等比前复权 + - `back_ratio` 等比后复权 +- 其他依赖库 numpy、pandas会在数据返回的过程中使用 + - 本模块会尽可能减少对numpy和pandas库的直接依赖,以允许使用者在不同版本的库之间自由切换 + - pandas库中旧的三维数据结构Panel没有被使用,而是以dict嵌套DataFrame代替(后续可能会考虑使用xarray等的方案,也欢迎使用者提供改进建议) + - 后文中会按常用规则分别简写为np、pd,如np.ndarray、pd.DataFrame + +### 请求限制 + +- 全推数据是市场全部合约的切面数据,是高订阅数场景下的有效解决方案。持续订阅全推数据可以获取到每个合约最新分笔数据的推送,且流量和处理效率都优于单股订阅 +- 单股订阅行情是仅返回单股数据的接口,建议单股订阅数量不超过50。如果订阅数较多,建议直接使用全推数据 +- 板块分类信息等静态信息更新频率低,无需频繁下载,按周或按日定期下载更新即可 + + + +## 接口说明 + +### 行情接口 + +#### 订阅单股行情 + +```python +subscribe_quote(stock_code, period='1d', start_time='', end_time='', count=0, callback=None) +``` + +- 释义 + + - 订阅单股的行情数据,返回订阅号 + - 数据推送从callback返回,数据类型和period指定的周期对应 + - 数据范围代表请求的历史部分的数据范围,数据返回后会进入缓存,用于保证数据连续,通常情况仅订阅数据时传`count = 0`即可 + +- 参数 + + - stock_code - string 合约代码 + + - period - string 周期 + + - start_time - string 起始时间 + + - end_time - string 结束时间 + + - count - int 数据个数 + + - callback - 数据推送回调 + + - 回调定义形式为`on_data(datas)`,回调参数`datas`格式为 { stock_code : [data1, data2, ...] } + + ```python + def on_data(datas): + for stock_code in datas: + print(stock_code, datas[stock_code]) + ``` + +- 返回 + + - 订阅号,订阅成功返回`大于0`,失败返回`-1` + +- 备注 + + - 单股订阅数量不宜过多,详见 接口概述-请求限制 + +#### 订阅单股行情2 + +```python +subscribe_quote2(stock_code, period='1d', start_time='', end_time='', count=0, dividend_type=None, callback=None) +``` + +- 释义 + + - 订阅单股的行情数据,返回订阅号 + - 数据推送从callback返回,数据类型和period指定的周期对应 + - 数据范围代表请求的历史部分的数据范围,数据返回后会进入缓存,用于保证数据连续,通常情况仅订阅数据时传`count = 0`即可 + +- 参数 + + - stock_code - string 合约代码 + + - period - string 周期 + + - start_time - string 起始时间 + + - end_time - string 结束时间 + + - count - int 数据个数 + + - dividend_type - string 除权类型"none", "front", "back", "front_ratio", "back_ratio" + + - callback - 数据推送回调 + + - 回调定义形式为`on_data(datas)`,回调参数`datas`格式为 { stock_code : [data1, data2, ...] } + + ```python + def on_data(datas): + for stock_code in datas: + print(stock_code, datas[stock_code]) + ``` + +- 返回 + + - 订阅号,订阅成功返回`大于0`,失败返回`-1` + +- 备注 + + - 单股订阅数量不宜过多,详见 接口概述-请求限制 + - 与第一版相比增加了除权参数dividend_type,默认None + +#### 订阅全推行情 + +```python +subscribe_whole_quote(code_list, callback=None) +``` + +- 释义 + + - 订阅全推行情数据,返回订阅号 + - 数据推送从callback返回,数据类型为分笔数据 + +- 参数 + + - code_list - 代码列表,支持传入市场代码或合约代码两种方式 + + - 传入市场代码代表订阅全市场,示例:`['SH', 'SZ']` + - 传入合约代码代表订阅指定的合约,示例:`['600000.SH', '000001.SZ']` + + - callback - 数据推送回调 + + - 回调定义形式为`on_data(datas)`,回调参数`datas`格式为 { stock1 : data1, stock2 : data2, ... } + + ```python + def on_data(datas): + for stock_code in datas: + print(stock_code, datas[stock_code]) + ``` + +- 返回 + + - 订阅号,订阅成功返回`大于0`,失败返回`-1` + +- 备注 + + - 订阅后会首先返回当前最新的全推数据 + +#### 反订阅行情数据 + +```python +unsubscribe_quote(seq) +``` + +- 释义 + - 反订阅行情数据 +- 参数 + - seq - 订阅时返回的订阅号 +- 返回 + - 无 +- 备注 + - 无 + +#### 阻塞线程接收行情回调 + +```python +run() +``` + +- 释义 + - 阻塞当前线程来维持运行状态,一般用于订阅数据后维持运行状态持续处理回调 +- 参数 + - seq - 订阅时返回的订阅号 +- 返回 + - 无 +- 备注 + - 实现方式为持续循环sleep,并在唤醒时检查连接状态,若连接断开则抛出异常结束循环 + +#### 获取行情数据 + +```python +get_market_data(field_list=[], stock_list=[], period='1d', start_time='', end_time='', count=-1, dividend_type='none', fill_data=True) +``` + +- 释义 + - 从缓存获取行情数据,是主动获取行情的主要接口 +- 参数 + - field_list - list 数据字段列表,传空则为全部字段 + - stock_list - list 合约代码列表 + - period - string 周期 + - start_time - string 起始时间 + - end_time - string 结束时间 + - count - int 数据个数 + - 默认参数,大于等于0时,若指定了start_time,end_time,此时以end_time为基准向前取count条;若start_time,end_time缺省,默认取本地数据最新的count条数据;若start_time,end_time,count都缺省时,默认取本地全部数据 + - dividend_type - string 除权方式 + - fill_data - bool 是否向后填充空缺数据 +- 返回 + - period为`1m` `5m` `1d`等K线周期时 + - 返回dict { field1 : value1, field2 : value2, ... } + - field1, field2, ... :数据字段 + - value1, value2, ... :pd.DataFrame 数据集,index为stock_list,columns为time_list + - 各字段对应的DataFrame维度相同、索引相同 + - period为`tick`分笔周期时 + - 返回dict { stock1 : value1, stock2 : value2, ... } + - stock1, stock2, ... :合约代码 + - value1, value2, ... :np.ndarray 数据集,按数据时间戳`time`增序排列 +- 备注 + - 获取lv2数据时需要数据终端有lv2数据权限 + - 时间范围为闭区间 + +#### 获取本地行情数据 + +```python +get_local_data(field_list=[], stock_code=[], period='1d', start_time='', end_time='', count=-1, + dividend_type='none', fill_data=True, data_dir=data_dir) +``` + +- 释义 + - 从本地数据文件获取行情数据,用于快速批量获取历史部分的行情数据 +- 参数 + - field_list - list 数据字段列表,传空则为全部字段 + - stock_list - list 合约代码列表 + - period - string 周期 + - start_time - string 起始时间 + - end_time - string 结束时间 + - count - int 数据个数 + - dividend_type - string 除权方式 + - fill_data - bool 是否向后填充空缺数据 + - data_dir - string MiniQmt配套路径的userdata_mini路径,用于直接读取数据文件。默认情况下xtdata会通过连接向MiniQmt直接获取此路径,无需额外设置。如果需要调整,可以将数据路径作为`data_dir`传入,也可以直接修改`xtdata.data_dir`以改变默认值 +- 返回 + - period为`1m` `5m` `1d`K线周期时 + - 返回dict { field1 : value1, field2 : value2, ... } + - field1, field2, ... :数据字段 + - value1, value2, ... :pd.DataFrame 数据集,index为stock_list,columns为time_list + - 各字段对应的DataFrame维度相同、索引相同 + - period为`tick`分笔周期时 + - 返回dict { stock1 : value1, stock2 : value2, ... } + - stock1, stock2, ... :合约代码 + - value1, value2, ... :np.ndarray 数据集,按数据时间戳`time`增序排列 +- 备注 + - 仅用于获取level1数据 + +#### 获取全推数据 + +```python +get_full_tick(code_list) +``` + +- 释义 + - 获取全推数据 +- 参数 + - code_list - 代码列表,支持传入市场代码或合约代码两种方式 + - 传入市场代码代表订阅全市场,示例:`['SH', 'SZ']` + - 传入合约代码代表订阅指定的合约,示例:`['600000.SH', '000001.SZ']` +- 返回 + - dict 数据集 { stock1 : data1, stock2 : data2, ... } +- 备注 + - 无 + +#### 获取除权数据 + +```python +get_divid_factors(stock_code, start_time='', end_time='') +``` + +- 释义 + - 获取除权数据 +- 参数 + - stock_code - 合约代码 + - start_time - string 起始时间 + - end_time - string 结束时间 +- 返回 + - pd.DataFrame 数据集 +- 备注 + - 无 + +#### 获取level2行情快照数据 + +```python +get_l2_quote(field_list=[], stock_code='', start_time='', end_time='', count=-1) +``` + +- 释义 + - 获取level2行情快照数据 +- 参数 + - field_list - list 数据字段列表,传空则为全部字段 + - stock_code - string 合约代码 + - start_time - string 起始时间 + - end_time - string 结束时间 + - count - int 数据个数 +- 返回 + - np.ndarray 数据集,按数据时间戳`time`增序排列 +- 备注 + - 需要缓存中有接收过的数据才能获取到 + +#### 获取level2逐笔委托数据 + +```python +get_l2_order(field_list=[], stock_code='', start_time='', end_time='', count=-1) +``` + +- 释义 + - 获取level2逐笔委托数据 +- 参数 + - field_list - list 数据字段列表,传空则为全部字段 + - stock_code - string 合约代码 + - start_time - string 起始时间 + - end_time - string 结束时间 + - count - int 数据个数 +- 返回 + - np.ndarray 数据集,按数据时间戳`time`增序排列 +- 备注 + - 需要缓存中有接收过的数据才能获取到 + +#### 获取level2逐笔成交数据 + +```python +get_l2_transaction(field_list=[], stock_code='', start_time='', end_time='', count=-1) +``` + +- 释义 + - 获取level2逐笔成交数据 +- 参数 + - field_list - list 数据字段列表,传空则为全部字段 + - stock_code - string 合约代码 + - start_time - string 起始时间 + - end_time - string 结束时间 + - count - int 数据个数 +- 返回 + - np.ndarray 数据集,按数据时间戳`time`增序排列 +- 备注 + - 需要缓存中有接收过的数据才能获取到 + +#### 下载历史行情数据 + +```python +download_history_data(stock_code, period, start_time='', end_time='', incrementally = None) +``` + +- 释义 + - 补充历史行情数据 +- 参数 + - stock_code - string 合约代码 + - period - string 周期 + - start_time - string 起始时间 + - end_time - string 结束时间 + - incrementally - 是否增量下载 + - `bool` - 是否增量下载 + - `None` - 使用`start_time`控制,`start_time`为空则增量下载 +- 返回 + - 无 +- 备注 + - 同步执行,补充数据完成后返回 + +```python +download_history_data2(stock_list, period, start_time='', end_time='', callback=None) +``` + +- 释义 + + - 补充历史行情数据,批量版本 + +- 参数 + + - stock_list - list 合约列表 + + - period - string 周期 + + - start_time - string 起始时间 + + - end_time - string 结束时间 + + - callback - func 回调函数 + + - 参数为进度信息dict + + - total - 总下载个数 + - finished - 已完成个数 + - stockcode - 本地下载完成的合约代码 + - message - 本次信息 + + - ```python + def on_progress(data): + print(data) + # {'finished': 1, 'total': 50, 'stockcode': '000001.SZ', 'message': ''} + ``` + +- 返回 + + - 无 + +- 备注 + + - 同步执行,补充数据完成后返回 + - 有任务完成时通过回调函数返回进度信息 + +#### 获取节假日数据 + +```python +get_holidays() +``` + +- 释义 + - 获取截止到当年的节假日日期 +- 参数 + - 无 +- 返回 + - list,为8位的日期字符串格式 +- 备注 + - 无 + +#### 获取交易日历 + +```python +get_trading_calendar(market, start_time = '', end_time = '') +``` + +- 释义 + - 获取指定市场交易日历 +- 参数 + - market - str 市场 + - start_time - str 起始时间,8位字符串。为空表示当前市场首个交易日时间 + - end_time - str 结束时间,8位字符串。为空表示当前时间 +- 返回 + - 返回list,完整的交易日列表 +- 备注 + - 结束时间可以填写未来时间,获取未来交易日。需要下载节假日列表。 + +#### 获取交易时段 + +```python +get_trading_time(stockcode) +``` + +- 释义 + + - 返回指定代码的交易时段 +- 参数 + + - stockcode - str 合约代码(例如`600000.SH`) +- 返回 + + - list,返回交易时段列表,第一位是开始时间,第二位结束时间,第三位交易类型 (2 - 开盘竞价, 3 - 连续交易, 8 - 收盘竞价, 9 - 盘后定价)。时间单位为“秒” +- 备注 + + - 股票代码错误时返回空列表 + + - 跨天时以当前天0点为起始,前一天为负,下一天多86400 + + - ``` + #需要转换为datetime时,可以用以下方法转换 + import datetime as dt + dt.datetime.combine(dt.date.today(), dt.time()) + dt.timedelta(seconds = 34200) + ``` + + + +#### 可转债基础信息的下载 + +```python +download_cb_data() +``` + +- 释义 + - 下载全部可转债信息 +- 参数 + - 无 +- 返回 + - 无 +- 备注 + - 无 + +#### 获取可转债基础信息 + +```python +get_cb_info(stockcode) +``` + +- 释义 + - 返回指定代码的可转债信息 +- 参数 + - stockcode - str 合约代码(例如`600000.SH`) +- 返回 + - dict,可转债信息 +- 备注 + - 需要先下载可转债数据 + + +#### 获取新股申购信息 + +```python +get_ipo_info(start_time, end_time) +``` + +- 释义 + + - 返回所选时间范围的新股申购信息 +- 参数 + - start_time: 开始日期(如:'20230327') + - end_time: 结束日期(如:'20230327') + - start_time 和 end_time 为空则返回全部数据 +- 返回 + - list[dict],新股申购信息 + + - ```python + securityCode - string 证券代码 + codeName - string 代码简称 + market - string 所属市场 + actIssueQty - int 发行总量,单位:股 + onlineIssueQty - int 网上发行量, 单位:股 + onlineSubCode - string 申购代码 + onlineSubMaxQty - int 申购上限, 单位:股 + publishPrice - float 发行价格 + isProfit - int 是否已盈利 0:上市时尚未盈利 1:上市时已盈利 + industryPe - float 行业市盈率 + afterPE - float 发行后市盈率 + ``` + +#### 获取可用周期列表 + +```python +get_period_list() +``` + +- 释义 + + - 返回可用周期列表 +- 参数 + - 无 +- 返回 + - list 周期列表 + +#### ETF申赎清单信息下载 + +```python +download_etf_info() +``` + +- 释义 + - 下载所有ETF申赎清单信息 + +- 参数 + - 无 +- 返回 + - 无 + +#### ETF申赎清单信息获取 + +```python +get_etf_info() +``` + +- 释义 + - 获取所有ETF申赎清单信息 + +- 参数 + - 无 +- 返回 + - dict 所有申赎数据 + +#### 节假日下载 + +```python +download_holiday_data() +``` + +- 释义 + - 下载节假日数据 + +- 参数 + - 无 +- 返回 + - 无 + +#### 获取最新交易日k线数据 + +```python +get_full_kline(field_list = [], stock_list = [], period = '1m' + , start_time = '', end_time = '', count = 1 + , dividend_type = 'none', fill_data = True) +``` + +- 释义 + - 获取最新交易日k线全推数据 + +- 参数 + - 参考`get_market_data`函数 +- 返回 + - dict - {field: DataFrame} + +### 财务数据接口 + +#### 获取财务数据 + +```python +get_financial_data(stock_list, table_list=[], start_time='', end_time='', report_type='report_time') +``` + +- 释义 + + - 获取财务数据 +- 参数 + + - stock_list - list 合约代码列表 + + - table_list - list 财务数据表名称列表 + + - ```python + 'Balance' #资产负债表 + 'Income' #利润表 + 'CashFlow' #现金流量表 + 'Capital' #股本表 + 'Holdernum' #股东数 + 'Top10holder' #十大股东 + 'Top10flowholder' #十大流通股东 + 'Pershareindex' #每股指标 + ``` + + - start_time - string 起始时间 + + - end_time - string 结束时间 + + - report_type - string 报表筛选方式 + + - ```python + 'report_time' #截止日期 + 'announce_time' #披露日期 + ``` +- 返回 + + - dict 数据集 { stock1 : datas1, stock2 : data2, ... } + - stock1, stock2, ... :合约代码 + - datas1, datas2, ... :dict 数据集 { table1 : table_data1, table2 : table_data2, ... } + - table1, table2, ... :财务数据表名 + - table_data1, table_data2, ... :pd.DataFrame 数据集,数据字段详见附录 - 财务数据字段列表 +- 备注 + + - 无 + +#### 下载财务数据 + +```python +download_financial_data(stock_list, table_list=[]) +``` + +- 释义 + - 下载财务数据 +- 参数 + - stock_list - list 合约代码列表 + - table_list - list 财务数据表名列表 +- 返回 + - 无 +- 备注 + - 同步执行,补充数据完成后返回 + +```python +download_financial_data2(stock_list, table_list=[], start_time='', end_time='', callback=None) +``` + +- 释义 + + - 下载财务数据 + +- 参数 + + - stock_list - list 合约代码列表 + + - table_list - list 财务数据表名列表 + + - start_time - string 起始时间 + + - end_time - string 结束时间 + + - 以`m_anntime`披露日期字段,按`[start_time, end_time]`范围筛选 + + - callback - func 回调函数 + + - 参数为进度信息dict + + - total - 总下载个数 + - finished - 已完成个数 + - stockcode - 本地下载完成的合约代码 + - message - 本次信息 + + - ```python + def on_progress(data): + print(data) + # {'finished': 1, 'total': 50, 'stockcode': '000001.SZ', 'message': ''} + ``` + +- 返回 + + - 无 +- 备注 + + - 同步执行,补充数据完成后返回 + +### 基础行情信息 + +#### 获取合约基础信息 + +```python +get_instrument_detail(stock_code, iscomplete) +``` + +- 释义 + + - 获取合约基础信息 + +- 参数 + + - stock_code - string 合约代码 + - iscomplete - bool 是否获取全部字段,默认为False + +- 返回 + + - dict 数据字典,{ field1 : value1, field2 : value2, ... },找不到指定合约时返回`None` + + - iscomplete为False时,返回以下字段 + + ```python + ExchangeID - string 合约市场代码 + InstrumentID - string 合约代码 + InstrumentName - string 合约名称 + ProductID - string 合约的品种ID(期货) + ProductName - string 合约的品种名称(期货) + ProductType - int 合约的类型, 默认-1 + 国内期货市场:1-期货 2-期权(DF SF ZF INE GF) 3-组合套利 4-即期 5-期转现 6-期权(IF) 7-结算价交易(tas) + 沪深股票期权市场:0-认购 1-认沽 + 外盘: + 1-100:期货, 101-200:现货, 201-300:股票相关 + 1:股指期货 + 2:能源期货 + 3:农业期货 + 4:金属期货 + 5:利率期货 + 6:汇率期货 + 7:数字货币期货 + 99:自定义合约期货 + 107:数字货币现货 + 201:股票 + 202:GDR + 203:ETF + 204:ETN + 300:其他 + ExchangeCode - string 交易所代码 + UniCode - string 统一规则代码 + CreateDate - str 上市日期(期货) + OpenDate - str IPO日期(股票) + ExpireDate - int 退市日或者到期日 + PreClose - float 前收盘价格 + SettlementPrice - float 前结算价格 + UpStopPrice - float 当日涨停价 + DownStopPrice - float 当日跌停价 + FloatVolume - float 流通股本 + TotalVolume - float 总股本 + LongMarginRatio - float 多头保证金率 + ShortMarginRatio - float 空头保证金率 + PriceTick - float 最小价格变动单位 + VolumeMultiple - int 合约乘数(对期货以外的品种,默认是1) + MainContract - int 主力合约标记,1、2、3分别表示第一主力合约,第二主力合约,第三主力合约 + LastVolume - int 昨日持仓量 + InstrumentStatus - int 合约停牌状态 + IsTrading - bool 合约是否可交易 + IsRecent - bool 是否是近月合约 + ``` + + - 详细合约信息字段见`附录-合约信息字段列表` + +- 备注 + + - 可用于检查合约代码是否正确 + - 合约基础信息`CreateDate` `OpenDate`字段类型由`int`调整为`str` + +#### 获取合约类型 + +```python +get_instrument_type(stock_code) +``` + +- 释义 + + - 获取合约类型 +- 参数 + + - stock_code - string 合约代码 +- 返回 + + - dict 数据字典,{ type1 : value1, type2 : value2, ... },找不到指定合约时返回`None` + + - type1, type2, ... :string 合约类型 + - value1, value2, ... :bool 是否为该类合约 + + - ```python + 'index' #指数 + 'stock' #股票 + 'fund' #基金 + 'etf' #ETF + ``` +- 备注 + + - 无 + +#### 获取交易日列表 + +```python +get_trading_dates(market, start_time='', end_time='', count=-1) +``` + +- 释义 + - 获取交易日列表 +- 参数 + - market - string 市场代码 + - start_time - string 起始时间 + - end_time - string 结束时间 + - count - int 数据个数 +- 返回 + - list 时间戳列表,[ date1, date2, ... ] +- 备注 + - 无 + +#### 获取板块列表 + +```python +get_sector_list() +``` + +- 释义 + - 获取板块列表 +- 参数 + - 无 +- 返回 + - list 板块列表,[ sector1, sector2, ... ] +- 备注 + - 需要下载板块分类信息 + +#### 获取板块成分股列表 + +```python +get_stock_list_in_sector(sector_name, real_timetag) +``` + +- 释义 + - 获取板块成分股列表 +- 参数 + - sector_name - string 版块名称 + - real_timetag 时间:1512748800000或'20171209',可缺省,缺省时获取最新的成分,不缺省时获取对应时间的历史成分 +- 返回 + - list 成分股列表,[ stock1, stock2, ... ] +- 备注 + - 需要板块分类信息 + +#### 下载板块分类信息 + +```python +download_sector_data() +``` + +- 释义 + - 下载板块分类信息 +- 参数 + - 无 +- 返回 + - 无 +- 备注 + - 同步执行,下载完成后返回 + +#### 创建板块目录节点 + +```python +create_sector_folder(parent_node, folder_name, overwrite) +``` + +- 释义 + - 创建板块目录节点 +- 参数 + - parent_node - string 父节点,’ ‘为 '我的‘ (默认目录) + - folder_name - string 要创建的板块目录名称 + - overwrite- bool 是否覆盖,如果目标节点已存在,为True时跳过,为False时在folder_name后增加数字编号,编号为从1开始自增的第一个不重复的值。 + 默认为True +- 返回 + - folder_name2 - string 实际创建的板块目录名 +- 备注 + - 无 + +#### 创建板块 + +```python +create_sector(parent_node, sector_name, overwrite) +``` + +- 释义 + - 创建板块 +- 参数 + - parent_node - string 父节点,’ ‘为 '我的‘ (默认目录) + - sector_name - string 板块名称 + - overwrite- bool 是否覆盖,如果目标节点已存在,为True时跳过,为False时在sector_name后增加数字编号,编号为从1开始自增的第一个不重复的值。 + 默认为True +- 返回 + - sector_name2 - string 实际创建的板块名 +- 备注 + - 无 + +#### 添加自定义板块 + +```python +add_sector(sector_name, stock_list) +``` + +- 释义 + - 添加自定义板块 +- 参数 + - sector_name - string 板块名称 + - stock_list - list 成分股列表 +- 返回 + - 无 +- 备注 + - 无 + +#### 移除板块成分股 + +```python +remove_stock_from_sector(sector_name, stock_list) +``` + +- 释义 + - 创建板块 +- 参数 + - sector_name - string 板块名称 + - stock_list- list 成分股列表 +- 返回 + - result - bool 操作成功为True,失败为False +- 备注 + - 无 + +#### 移除自定义板块 + +```python +remove_sector(sector_name) +``` + +- 释义 + - 移除自定义板块 +- 参数 + - sector_name - string 板块名称 +- 返回 + - 无 +- 备注 + - 无 + +#### 重置板块 + +```python +reset_sector(sector_name, stock_list) +``` + +- 释义 + - 重置板块 +- 参数 + - sector_name - string 板块名称 + - stock_list- list 成分股列表 +- 返回 + - result - bool 操作成功为True,失败为False +- 备注 + - 无 + +#### 获取指数成分权重信息 + +```python +get_index_weight(index_code) +``` + +- 释义 + - 获取指数成分权重信息 +- 参数 + - index_code - string 指数代码 +- 返回 + - dict 数据字典,{ stock1 : weight1, stock2 : weight2, ... } +- 备注 + - 需要下载指数成分权重信息 + +#### 下载指数成分权重信息 + +```python +download_index_weight() +``` + +- 释义 + - 下载指数成分权重信息 +- 参数 + - 无 +- 返回 + - 无 +- 备注 + - 同步执行,下载完成后返回 + + + +## 附录 + +### 行情数据字段列表 + +#### tick - 分笔数据 + +```python +'time' #时间戳 +'lastPrice' #最新价 +'open' #开盘价 +'high' #最高价 +'low' #最低价 +'lastClose' #前收盘价 +'amount' #成交总额 +'volume' #成交总量 +'pvolume' #原始成交总量 +'stockStatus' #证券状态 +'openInt' #持仓量 +'lastSettlementPrice' #前结算 +'askPrice' #委卖价 +'bidPrice' #委买价 +'askVol' #委卖量 +'bidVol' #委买量 +'transactionNum' #成交笔数 +``` + +#### 1m / 5m / 1d - K线数据 + +```python +'time' #时间戳 +'open' #开盘价 +'high' #最高价 +'low' #最低价 +'close' #收盘价 +'volume' #成交量 +'amount' #成交额 +'settelementPrice' #今结算 +'openInterest' #持仓量 +'preClose' #前收价 +'suspendFlag' #停牌标记 0 - 正常 1 - 停牌 -1 - 当日起复牌 +``` + +#### 除权数据 + +```python +'interest' #每股股利(税前,元) +'stockBonus' #每股红股(股) +'stockGift' #每股转增股本(股) +'allotNum' #每股配股数(股) +'allotPrice' #配股价格(元) +'gugai' #是否股改, 对于股改,在算复权系数时,系统有特殊算法 +'dr' #除权系数 +``` + +#### l2quote - level2实时行情快照 + +```python +'time' #时间戳 +'lastPrice' #最新价 +'open' #开盘价 +'high' #最高价 +'low' #最低价 +'amount' #成交额 +'volume' #成交总量 +'pvolume' #原始成交总量 +'openInt' #持仓量 +'stockStatus' #证券状态 +'transactionNum' #成交笔数 +'lastClose' #前收盘价 +'lastSettlementPrice' #前结算 +'settlementPrice' #今结算 +'pe' #市盈率 +'askPrice' #多档委卖价 +'bidPrice' #多档委买价 +'askVol' #多档委卖量 +'bidVol' #多档委买量 +``` + +#### l2order - level2逐笔委托 + +```python +'time' #时间戳 +'price' #委托价 +'volume' #委托量 +'entrustNo' #委托号 +'entrustType' #委托类型 +'entrustDirection' #委托方向 +``` + +#### l2transaction - level2逐笔成交 + +```python +'time' #时间戳 +'price' #成交价 +'volume' #成交量 +'amount' #成交额 +'tradeIndex' #成交记录号 +'buyNo' #买方委托号 +'sellNo' #卖方委托号 +'tradeType' #成交类型 +'tradeFlag' #成交标志 +``` + +#### l2quoteaux - level2实时行情补充(总买总卖) + +```python +'time' #时间戳 +'avgBidPrice' #委买均价 +'totalBidQuantity' #委买总量 +'avgOffPrice' #委卖均价 +'totalOffQuantity' #委卖总量 +'withdrawBidQuantity' #买入撤单总量 +'withdrawBidAmount' #买入撤单总额 +'withdrawOffQuantity' #卖出撤单总量 +'withdrawOffAmount' #卖出撤单总额 +``` + +#### l2orderqueue - level2委买委卖一档委托队列 + +```python +'time' #时间戳 +'bidLevelPrice' #委买价 +'bidLevelVolume' #委买量 +'offerLevelPrice' #委卖价 +'offerLevelVolume' #委卖量 +'bidLevelNumber' #委买数量 +'offLevelNumber' #委卖数量 +``` + +#### limitupperformance - 涨停连板数据 + +```python +'time' #时间戳 +'openVol' #开盘集合竞价的成交量 +'closeVol' #收盘集合竞价的成交量 +'finalVol' #盘后定价的成交量 +'startUp' #涨停开始时间 +'endUp' #涨停结束时间 +'breakUp' #炸板次数 +'upAmount' #涨停金额 +'startDn' #跌停开始时间 +'endDn' #跌停结束时间 +'breakDn' #开板次数 +'dnAmount' #跌停金额 +'direct' #涨跌方向 0-无 1-涨停 2-跌停 +'sealVolRatio' #封成比 +'sealFreeRatio' #封流比 +'bidPreRatio' #竞昨比 +'sealCount' #几板 +'sealDays' #几天 +'sealBreak' #封板中断天数 +``` + +#### announcement - 公告新闻 + +```python +'time' #时间戳 +'level' #级别 +'security' #证券 +'headline' #标题 +'summary' #摘要 +'format' #格式 txt pdf doc +'content' #内容 +'type' #类型 0-其他 1-财报类 +``` + +#### snapshotindex - 快照指标 + +```python +'time' #时间戳 +'volRatio' #量比 +'speed1' #涨速1分钟 +'speed5' #涨速5分钟 +'gainRate3' #3日涨跌 +'gainRate5' #5日涨跌 +'gainRate10' #10日涨跌 +'turnoverRate3' #3日换手 +'turnoverRate5' #5日换手 +'turnoverRate10' #10日换手 +``` + +### 数据字典 + +#### 证券状态 + +``` +0,10 - 默认为未知 +11 - 开盘前S +12 - 集合竞价时段C +13 - 连续交易T +14 - 休市B +15 - 闭市E +16 - 波动性中断V +17 - 临时停牌P +18 - 收盘集合竞价U +19 - 盘中集合竞价M +20 - 暂停交易至闭市N +21 - 获取字段异常 +22 - 盘后固定价格行情 +23 - 盘后固定价格行情完毕 +``` + +#### 委托类型 + +- level2逐笔委托 - `entrustType` 委托类型 +- level2逐笔成交 - `tradeType` 成交类型 + +``` +0 - 未知 +1 - 正常交易业务 +2 - 即时成交剩余撤销 +3 - ETF基金申报 +4 - 最优五档即时成交剩余撤销 +5 - 全额成交或撤销 +6 - 本方最优价格 +7 - 对手方最优价格 +``` + +#### 委托方向 + +- level2逐笔委托 - `entrustDirection` 委托方向 + - 注:上交所的撤单信息在逐笔委托的委托方向,区分撤买撤卖 + +``` +1 - 买入 +2 - 卖出 +3 - 撤买(上交所) +4 - 撤卖(上交所) +``` + +#### 成交标志 + +- level2逐笔成交 - `tradeFlag` 成交标志 + - 注:深交所的在逐笔成交的成交标志,只有撤单,没有方向 + +``` +0 - 未知 +1 - 外盘 +2 - 内盘 +3 - 撤单(深交所) +``` + +#### 现金替代标志 + +- ETF申赎清单成份股现金替代标志 + +``` +0 - 禁止现金替代(必须有股票) +1 - 允许现金替代(先用股票,股票不足的话用现金替代 +2 - 必须现金替代 +3 - 非沪市(股票)退补现金替代 +4 - 非沪市(股票)必须现金替代 +5 - 非沪深退补现金替代 +6 - 非沪深必须现金替代 +7 - 港市退补现金替代(仅适用于跨沪深ETF产品) +8 - 港市必须现金替代(仅适用于跨沪深港ETF产品) +``` + + +### 财务数据字段列表 + +#### Balance - 资产负债表 + +```python +'m_anntime' #披露日期 +'m_timetag' #截止日期 +'internal_shoule_recv' #内部应收款 +'fixed_capital_clearance' #固定资产清理 +'should_pay_money' #应付分保账款 +'settlement_payment' #结算备付金 +'receivable_premium' #应收保费 +'accounts_receivable_reinsurance' #应收分保账款 +'reinsurance_contract_reserve' #应收分保合同准备金 +'dividends_payable' #应收股利 +'tax_rebate_for_export' #应收出口退税 +'subsidies_receivable' #应收补贴款 +'deposit_receivable' #应收保证金 +'apportioned_cost' #待摊费用 +'profit_and_current_assets_with_deal' #待处理流动资产损益 +'current_assets_one_year' #一年内到期的非流动资产 +'long_term_receivables' #长期应收款 +'other_long_term_investments' #其他长期投资 +'original_value_of_fixed_assets' #固定资产原值 +'net_value_of_fixed_assets' #固定资产净值 +'depreciation_reserves_of_fixed_assets' #固定资产减值准备 +'productive_biological_assets' #生产性生物资产 +'public_welfare_biological_assets' #公益性生物资产 +'oil_and_gas_assets' #油气资产 +'development_expenditure' #开发支出 +'right_of_split_share_distribution' #股权分置流通权 +'other_non_mobile_assets' #其他非流动资产 +'handling_fee_and_commission' #应付手续费及佣金 +'other_payables' #其他应交款 +'margin_payable' #应付保证金 +'internal_accounts_payable' #内部应付款 +'advance_cost' #预提费用 +'insurance_contract_reserve' #保险合同准备金 +'broker_buying_and_selling_securities' #代理买卖证券款 +'acting_underwriting_securities' #代理承销证券款 +'international_ticket_settlement' #国际票证结算 +'domestic_ticket_settlement' #国内票证结算 +'deferred_income' #递延收益 +'short_term_bonds_payable' #应付短期债券 +'long_term_deferred_income' #长期递延收益 +'undetermined_investment_losses' #未确定的投资损失 +'quasi_distribution_of_cash_dividends' #拟分配现金股利 +'provisions_not' #预计负债 +'cust_bank_dep' #吸收存款及同业存放 +'provisions' #预计流动负债 +'less_tsy_stk' #减:库存股 +'cash_equivalents' #货币资金 +'loans_to_oth_banks' #拆出资金 +'tradable_fin_assets' #交易性金融资产 +'derivative_fin_assets' #衍生金融资产 +'bill_receivable' #应收票据 +'account_receivable' #应收账款 +'advance_payment' #预付款项 +'int_rcv' #应收利息 +'other_receivable' #其他应收款 +'red_monetary_cap_for_sale' #买入返售金融资产 +'agency_bus_assets' #以公允价值计量且其变动计入当期损益的金融资产 +'inventories' #存货 +'other_current_assets' #其他流动资产 +'total_current_assets' #流动资产合计 +'loans_and_adv_granted' #发放贷款及垫款 +'fin_assets_avail_for_sale' #可供出售金融资产 +'held_to_mty_invest' #持有至到期投资 +'long_term_eqy_invest' #长期股权投资 +'invest_real_estate' #投资性房地产 +'accumulated_depreciation' #累计折旧 +'fix_assets' #固定资产 +'constru_in_process' #在建工程 +'construction_materials' #工程物资 +'long_term_liabilities' #长期负债 +'intang_assets' #无形资产 +'goodwill' #商誉 +'long_deferred_expense' #长期待摊费用 +'deferred_tax_assets' #递延所得税资产 +'total_non_current_assets' #非流动资产合计 +'tot_assets' #资产总计 +'shortterm_loan' #短期借款 +'borrow_central_bank' #向中央银行借款 +'loans_oth_banks' #拆入资金 +'tradable_fin_liab' #交易性金融负债 +'derivative_fin_liab' #衍生金融负债 +'notes_payable' #应付票据 +'accounts_payable' #应付账款 +'advance_peceipts' #预收账款 +'fund_sales_fin_assets_rp' #卖出回购金融资产款 +'empl_ben_payable' #应付职工薪酬 +'taxes_surcharges_payable' #应交税费 +'int_payable' #应付利息 +'dividend_payable' #应付股利 +'other_payable' #其他应付款 +'non_current_liability_in_one_year' #一年内到期的非流动负债 +'other_current_liability' #其他流动负债 +'total_current_liability' #流动负债合计 +'long_term_loans' #长期借款 +'bonds_payable' #应付债券 +'longterm_account_payable' #长期应付款 +'grants_received' #专项应付款 +'deferred_tax_liab' #递延所得税负债 +'other_non_current_liabilities' #其他非流动负债 +'non_current_liabilities' #非流动负债合计 +'tot_liab' #负债合计 +'cap_stk' #实收资本(或股本) +'cap_rsrv' #资本公积 +'specific_reserves' #专项储备 +'surplus_rsrv' #盈余公积 +'prov_nom_risks' #一般风险准备 +'undistributed_profit' #未分配利润 +'cnvd_diff_foreign_curr_stat' #外币报表折算差额 +'tot_shrhldr_eqy_excl_min_int' #归属于母公司股东权益合计 +'minority_int' #少数股东权益 +'total_equity' #所有者权益合计 +'tot_liab_shrhldr_eqy' #负债和股东权益总计 +``` + +#### Income - 利润表 + +```python +'m_anntime' #披露日期 +'m_timetag' #截止日期 +'revenue_inc' #营业收入 +'earned_premium' #已赚保费 +'real_estate_sales_income' #房地产销售收入 +'total_operating_cost' #营业总成本 +'real_estate_sales_cost' #房地产销售成本 +'research_expenses' #研发费用 +'surrender_value' #退保金 +'net_payments' #赔付支出净额 +'net_withdrawal_ins_con_res' #提取保险合同准备金净额 +'policy_dividend_expenses' #保单红利支出 +'reinsurance_cost' #分保费用 +'change_income_fair_value' #公允价值变动收益 +'futures_loss' #期货损益 +'trust_income' #托管收益 +'subsidize_revenue' #补贴收入 +'other_business_profits' #其他业务利润 +'net_profit_excl_merged_int_inc' #被合并方在合并前实现净利润 +'int_inc' #利息收入 +'handling_chrg_comm_inc' #手续费及佣金收入 +'less_handling_chrg_comm_exp' #手续费及佣金支出 +'other_bus_cost' #其他业务成本 +'plus_net_gain_fx_trans' #汇兑收益 +'il_net_loss_disp_noncur_asset' #非流动资产处置收益 +'inc_tax' #所得税费用 +'unconfirmed_invest_loss' #未确认投资损失 +'net_profit_excl_min_int_inc' #归属于母公司所有者的净利润 +'less_int_exp' #利息支出 +'other_bus_inc' #其他业务收入 +'revenue' #营业总收入 +'total_expense' #营业成本 +'less_taxes_surcharges_ops' #营业税金及附加 +'sale_expense' #销售费用 +'less_gerl_admin_exp' #管理费用 +'financial_expense' #财务费用 +'less_impair_loss_assets' #资产减值损失 +'plus_net_invest_inc' #投资收益 +'incl_inc_invest_assoc_jv_entp' #联营企业和合营企业的投资收益 +'oper_profit' #营业利润 +'plus_non_oper_rev' #营业外收入 +'less_non_oper_exp' #营业外支出 +'tot_profit' #利润总额 +'net_profit_incl_min_int_inc' #净利润 +'net_profit_incl_min_int_inc_after' #净利润(扣除非经常性损益后) +'minority_int_inc' #少数股东损益 +'s_fa_eps_basic' #基本每股收益 +'s_fa_eps_diluted' #稀释每股收益 +'total_income' #综合收益总额 +'total_income_minority' #归属于少数股东的综合收益总额 +'other_compreh_inc' #其他收益 +``` + +#### CashFlow - 现金流量表 + +```python +'m_anntime' #披露日期 +'m_timetag' #截止日期 +'cash_received_ori_ins_contract_pre' #收到原保险合同保费取得的现金 +'net_cash_received_rei_ope' #收到再保险业务现金净额 +'net_increase_insured_funds' #保户储金及投资款净增加额 +'Net' #处置交易性金融资产净增加额 increase_in_disposal +'cash_for_interest' #收取利息、手续费及佣金的现金 +'net_increase_in_repurchase_funds' #回购业务资金净增加额 +'cash_for_payment_original_insurance' #支付原保险合同赔付款项的现金 +'cash_payment_policy_dividends' #支付保单红利的现金 +'disposal_other_business_units' #处置子公司及其他收到的现金 +'cash_received_from_pledges' #减少质押和定期存款所收到的现金 +'cash_paid_for_investments' #投资所支付的现金 +'net_increase_in_pledged_loans' #质押贷款净增加额 +'cash_paid_by_subsidiaries' #取得子公司及其他营业单位支付的现金净额 +'increase_in_cash_paid' #增加质押和定期存款所支付的现金 +'cass_received_sub_abs' #其中子公司吸收现金 +'cass_received_sub_investments' #其中:子公司支付给少数股东的股利、利润 +'minority_shareholder_profit_loss' #少数股东损益 +'unrecognized_investment_losses' #未确认的投资损失 +'ncrease_deferred_income' #递延收益增加(减:减少) +'projected_liability' #预计负债 +'increase_operational_payables' #经营性应付项目的增加 +'reduction_outstanding_amounts_less' #已完工尚未结算款的减少(减:增加) +'reduction_outstanding_amounts_more' #已结算尚未完工款的增加(减:减少) +'goods_sale_and_service_render_cash' #销售商品、提供劳务收到的现金 +'net_incr_dep_cob' #客户存款和同业存放款项净增加额 +'net_incr_loans_central_bank' #向中央银行借款净增加额(万元 +'net_incr_fund_borr_ofi' #向其他金融机构拆入资金净增加额 +'net_incr_fund_borr_ofi' #拆入资金净增加额 +'tax_levy_refund' #收到的税费与返还 +'cash_paid_invest' #投资支付的现金 +'other_cash_recp_ral_oper_act' #收到的其他与经营活动有关的现金 +'stot_cash_inflows_oper_act' #经营活动现金流入小计 +'goods_and_services_cash_paid' #购买商品、接受劳务支付的现金 +'net_incr_clients_loan_adv' #客户贷款及垫款净增加额 +'net_incr_dep_cbob' #存放中央银行和同业款项净增加额 +'handling_chrg_paid' #支付利息、手续费及佣金的现金 +'cash_pay_beh_empl' #支付给职工以及为职工支付的现金 +'pay_all_typ_tax' #支付的各项税费 +'other_cash_pay_ral_oper_act' #支付其他与经营活动有关的现金 +'stot_cash_outflows_oper_act' #经营活动现金流出小计 +'net_cash_flows_oper_act' #经营活动产生的现金流量净额 +'cash_recp_disp_withdrwl_invest' #收回投资所收到的现金 +'cash_recp_return_invest' #取得投资收益所收到的现金 +'net_cash_recp_disp_fiolta' #处置固定资产、无形资产和其他长期投资收到的现金 +'other_cash_recp_ral_inv_act' #收到的其他与投资活动有关的现金 +'stot_cash_inflows_inv_act' #投资活动现金流入小计 +'cash_pay_acq_const_fiolta' #购建固定资产、无形资产和其他长期投资支付的现金 +'other_cash_pay_ral_oper_act' #支付其他与投资的现金 +'stot_cash_outflows_inv_act' #投资活动现金流出小计 +'net_cash_flows_inv_act' #投资活动产生的现金流量净额 +'cash_recp_cap_contrib' #吸收投资收到的现金 +'cash_recp_borrow' #取得借款收到的现金 +'proc_issue_bonds' #发行债券收到的现金 +'other_cash_recp_ral_fnc_act' #收到其他与筹资活动有关的现金 +'stot_cash_inflows_fnc_act' #筹资活动现金流入小计 +'cash_prepay_amt_borr' #偿还债务支付现金 +'cash_pay_dist_dpcp_int_exp' #分配股利、利润或偿付利息支付的现金 +'other_cash_pay_ral_fnc_act' #支付其他与筹资的现金 +'stot_cash_outflows_fnc_act' #筹资活动现金流出小计 +'net_cash_flows_fnc_act' #筹资活动产生的现金流量净额 +'eff_fx_flu_cash' #汇率变动对现金的影响 +'net_incr_cash_cash_equ' #现金及现金等价物净增加额 +'cash_cash_equ_beg_period' #期初现金及现金等价物余额 +'cash_cash_equ_end_period' #期末现金及现金等价物余额 +'net_profit' #净利润 +'plus_prov_depr_assets' #资产减值准备 +'depr_fa_coga_dpba' #固定资产折旧、油气资产折耗、生产性物资折旧 +'amort_intang_assets' #无形资产摊销 +'amort_lt_deferred_exp' #长期待摊费用摊销 +'decr_deferred_exp' #待摊费用的减少 +'incr_acc_exp' #预提费用的增加 +'loss_disp_fiolta' #处置固定资产、无形资产和其他长期资产的损失 +'loss_scr_fa' #固定资产报废损失 +'loss_fv_chg' #公允价值变动损失 +'fin_exp' #财务费用 +'invest_loss' #投资损失 +'decr_deferred_inc_tax_assets' #递延所得税资产减少 +'incr_deferred_inc_tax_liab' #递延所得税负债增加 +'decr_inventories' #存货的减少 +'decr_oper_payable' #经营性应收项目的减少 +'others' #其他 +'im_net_cash_flows_oper_act' #经营活动产生现金流量净额 +'conv_debt_into_cap' #债务转为资本 +'conv_corp_bonds_due_within_1y' #一年内到期的可转换公司债券 +'fa_fnc_leases' #融资租入固定资产 +'end_bal_cash' #现金的期末余额 +'less_beg_bal_cash' #现金的期初余额 +'plus_end_bal_cash_equ' #现金等价物的期末余额 +'less_beg_bal_cash_equ' #现金等价物的期初余额 +'im_net_incr_cash_cash_equ' #现金及现金等价物的净增加额 +'tax_levy_refund' #收到的税费返还 +``` + +#### PershareIndex - 主要指标 + +```python +'s_fa_ocfps' #每股经营活动现金流量 +'s_fa_bps' #每股净资产 +'s_fa_eps_basic' #基本每股收益 +'s_fa_eps_diluted' #稀释每股收益 +'s_fa_undistributedps' #每股未分配利润 +'s_fa_surpluscapitalps' #每股资本公积金 +'adjusted_earnings_per_share' #扣非每股收益 +'du_return_on_equity' #净资产收益率 +'sales_gross_profit' #销售毛利率 +'inc_revenue_rate' #主营收入同比增长 +'du_profit_rate' #净利润同比增长 +'inc_net_profit_rate' #归属于母公司所有者的净利润同比增长 +'adjusted_net_profit_rate' #扣非净利润同比增长 +'inc_total_revenue_annual' #营业总收入滚动环比增长 +'inc_net_profit_to_shareholders_annual' #归属净利润滚动环比增长 +'adjusted_profit_to_profit_annual' #扣非净利润滚动环比增长 +'equity_roe' #加权净资产收益率 +'net_roe' #摊薄净资产收益率 +'total_roe' #摊薄总资产收益率 +'gross_profit' #毛利率 +'net_profit' #净利率 +'actual_tax_rate' #实际税率 +'pre_pay_operate_income' #预收款 / 营业收入 +'sales_cash_flow' #销售现金流 / 营业收入 +'gear_ratio' #资产负债比率 +'inventory_turnover' #存货周转率 +'m_anntime' #公告日 +'m_timetag' #报告截止日 +``` + +#### CapitalStructure - 股本表 + +```python +'total_capital' #总股本 +'circulating_capital' #已上市流通A股 +'restrict_circulating_capital' #限售流通股份 +'m_timetag' #报告截止日 +'m_anntime' #公告日 +``` + +#### TOP10HOLDER/TOP10FLOWHOLDER - 十大股东/十大流通股东 + +```python +'declareDate' #公告日期 +'endDate' #截止日期 +'name' #股东名称 +'type' #股东类型 +'quantity' #持股数量 +'reason' #变动原因 +'ratio' #持股比例 +'nature' #股份性质 +'rank' #持股排名 +``` + +#### SHAREHOLDER - 股东数 + +```python +'declareDate' #公告日期 +'endDate' #截止日期 +'shareholder' #股东总数 +'shareholderA' #A股东户数 +'shareholderB' #B股东户数 +'shareholderH' #H股东户数 +'shareholderFloat' #已流通股东户数 +'shareholderOther' #未流通股东户数 +``` + +### 合约信息字段列表 + +```python +'ExchangeID' #合约市场代码 +'InstrumentID' #合约代码 +'InstrumentName' #合约名称 +'Abbreviation' #合约名称的拼音简写 +'ProductID' #合约的品种ID(期货) +'ProductName' #合约的品种名称(期货) +'UnderlyingCode' #标的合约 +'ExtendName' #扩位名称 +'ExchangeCode' #交易所代码 +'RzrkCode' #rzrk代码 +'UniCode' #统一规则代码 +'CreateDate' #上市日期(期货) +'OpenDate' #IPO日期(股票) +'ExpireDate' #退市日或者到期日 +'PreClose' #前收盘价格 +'SettlementPrice' #前结算价格 +'UpStopPrice' #当日涨停价 +'DownStopPrice' #当日跌停价 +'FloatVolume' #流通股本 +'TotalVolume' #总股本 +'AccumulatedInterest' #自上市付息日起的累积未付利息额(债券) +'LongMarginRatio' #多头保证金率 +'ShortMarginRatio' #空头保证金率 +'PriceTick' #最小变价单位 +'VolumeMultiple' #合约乘数(对期货以外的品种,默认是1) +'MainContract' #主力合约标记,1、2、3分别表示第一主力合约,第二主力合约,第三主力合约 +'MaxMarketOrderVolume' #市价单最大下单量 +'MinMarketOrderVolume' #市价单最小下单量 +'MaxLimitOrderVolume' #限价单最大下单量 +'MinLimitOrderVolume' #限价单最小下单量 +'MaxMarginSideAlgorithm' #上期所大单边的处理算法 +'DayCountFromIPO' #自IPO起经历的交易日总数 +'LastVolume' #昨日持仓量 +'InstrumentStatus' #合约停牌状态 +'IsTrading' #合约是否可交易 +'IsRecent' #是否是近月合约 +'IsContinuous' #是否是连续合约 +'bNotProfitable' #是否非盈利状态 +'bDualClass' #是否同股不同权 +'ContinueType' #连续合约类型 +'secuCategory' #证券分类 +'secuAttri' #证券属性 +'MaxMarketSellOrderVolume' #市价卖单最大单笔下单量 +'MinMarketSellOrderVolume' #市价卖单最小单笔下单量 +'MaxLimitSellOrderVolume' #限价卖单最大单笔下单量 +'MinLimitSellOrderVolume' #限价卖单最小单笔下单量 +'MaxFixedBuyOrderVol' #盘后定价委托数量的上限(买) +'MinFixedBuyOrderVol' #盘后定价委托数量的下限(买) +'MaxFixedSellOrderVol' #盘后定价委托数量的上限(卖) +'MinFixedSellOrderVol' #盘后定价委托数量的下限(卖) +'HSGTFlag' #标识港股是否为沪港通或深港通标的证券。沪港通:0-非标的,1-标的,2-历史标的;深港通:0-非标的,3-标的,4-历史标的,5-是沪港通也是深港通 +'BondParValue' #债券面值 +'QualifiedType' #投资者适当性管理分类 +'PriceTickType' #价差类别(港股用),1-股票,3-债券,4-期权,5-交易所买卖基金 +'tradingStatus' #交易状态 +'OptUnit' #期权合约单位 +'MarginUnit' #期权单位保证金 +'OptUndlCode' #期权标的证券代码或可转债正股标的证券代码 +'OptUndlMarket' #期权标的证券市场或可转债正股标的证券市场 +'OptLotSize' #期权整手数 +'OptExercisePrice' #期权行权价或可转债转股价 +'NeeqExeType' #全国股转转让类型,1-协议转让方式,2-做市转让方式,3-集合竞价+连续竞价转让方式(当前全国股转并未实现),4-集合竞价转让 +'OptExchFixedMargin' #交易所期权合约保证金不变部分 +'OptExchMiniMargin' #交易所期权合约最小保证金 +'Ccy' #币种 +'IbSecType' #IB安全类型,期货或股票 +'OptUndlRiskFreeRate' #期权标的无风险利率 +'OptUndlHistoryRate' #期权标的历史波动率 +'EndDelivDate' #期权行权终止日 +'RegisteredCapital' #注册资本(单位:百万) +'MaxOrderPriceRange' #最大有效申报范围 +'MinOrderPriceRange' #最小有效申报范围 +'VoteRightRatio' #同股同权比例 +'m_nMinRepurchaseDaysLimit' #最小回购天数 +'m_nMaxRepurchaseDaysLimit' #最大回购天数 +'DeliveryYear' #交割年份 +'DeliveryMonth' #交割月 +'ContractType' #标识期权,1-过期,2-当月,3-下月,4-下季,5-隔季,6-隔下季 +'ProductTradeQuota' #期货品种交易配额 +'ContractTradeQuota' #期货合约交易配额 +'ProductOpenInterestQuota' #期货品种持仓配额 +'ContractOpenInterestQuota' #期货合约持仓配额 +'ChargeType' #期货和期权手续费方式,0-未知,1-按元/手,2-按费率 +'ChargeOpen' #开仓手续费率,-1表示没有 +'ChargeClose' #平仓手续费率,-1表示没有 +'ChargeClose' #平仓手续费率,-1表示没有 +'ChargeTodayOpen' #开今仓(日内开仓)手续费率,-1表示没有 +'ChargeTodayClose' #平今仓(日内平仓)手续费率,-1表示没有 +'OptionType' #期权类型,-1为非期权,0为期权认购,1为期权认沽 +'OpenInterestMultiple' #交割月持仓倍数 +``` + +### 代码示例 + +#### 时间戳转换 + +```python +import time +def conv_time(ct): + ''' + conv_time(1476374400000) --> '20161014000000.000' + ''' + local_time = time.localtime(ct / 1000) + data_head = time.strftime('%Y%m%d%H%M%S', local_time) + data_secs = (ct - int(ct)) * 1000 + time_stamp = '%s.%03d' % (data_head, data_secs) + return time_stamp +``` + + + diff --git a/src/xtquant/doc/xttrader.md b/src/xtquant/doc/xttrader.md new file mode 100644 index 0000000..7383f01 --- /dev/null +++ b/src/xtquant/doc/xttrader.md @@ -0,0 +1,1912 @@ +# 入门篇 +## 迅投XtQuant FAQ +### XtQuant能提供哪些服务 +XtQuant是基于迅投MiniQMT衍生出来的一套完善的Python策略运行框架,对外以Python库的形式提供策略交易所需要的行情和交易相关的API接口。 +### XtQuant运行依赖环境 +XtQuant目前提供的库包括Python3.6、3.7、3.8版本,不同版本的python导入时会自动切换。 +在运行使用XtQuant的程序前需要先启动MiniQMT客户端。 + +## 版本信息 +- 2020-09-01 + - 初稿 +- 2020-10-14 + - 持仓结构添加字段 + - 投资备注相关修正 +- 2020-10-21 + - 添加信用交易相关委托类型(order_type)枚举 + - 调整XtQuant运行依赖环境说明,更新多版本支持相关说明 +- 2020-11-13 + - 添加信用交易相关类型定义说明 + - 添加信用交易相关接口说明 + - 添加异步撤单委托反馈结构说明 + - 添加下单失败和撤单失败主推结构说明 + - 添加订阅和反订阅接口 + - 添加创建API实例,注册回调类,准备API环境,创建连接,停止运行,阻塞进程接口说明 + - 调整API接口说明 + - 将接口细分为"系统设置接口",“操作接口”,“查询接口”,"信用相关查询接口",“回调类”等五类 + - 接口返回“None”修改为“无” + - 去掉回调类接口中的示例 + - 添加“备注”项 + - 所有“证券账号”改为“资金账号” + - 英文“,”调整为中文“,” + - 示例代码中增加XtQuant API实例对象,修正没有实例,直接调用的错误 + - 添加股票异步撤单接口说明,将原股票撤单修改为股票同步撤单 +- 2020-11-19 + - 添加账号状态主推接口 + - 添加账号状态数据结构说明 + - 添加账号状态枚举值 + - 回调类接口说明调整 + - 将回调函数定义及函数说明标题调整一致 + - 补充异步下单回报推送、异步撤单回报推送接口说明 +- 2021-07-20 + - 修改回调/主推函数实现机制,提升报撤单回报的速度,降低穿透延时波动 + - `XtQuantTrader.run_forever()`修改实现,支持`Ctrl+C`跳出 +- 2022-06-27 + - 委托查询支持仅查询可撤委托 + - 添加新股申购相关接口 + - `query_new_purchase_limit` 查询新股申购额度 + - `query_ipo_data` 查询新股信息 + - 添加账号信息查询接口 + - `query_account_infos` +- 2022-11-15 + + - 修复`XtQuantTrader.unsubscribe`的实现 +- 2022-11-17 + + - 交易数据字典格式调整 +- 2022-11-28 + - 为主动请求接口的返回增加专用线程以及相关控制,以支持在on_stock_order等推送接口中调用同步请求 + - `XtQuantTrader.set_relaxed_response_order_enabled` +- 2023-07-17 + - 持仓结构`XtPosition` 成本价字段调整 + - `open_price` - 开仓价 + - `avg_price` - 成本价 +- 2023-07-26 + + - 添加资金划拨接口 `fund_transfer` +- 2023-08-11 + - 添加划拨业务查询普通柜台资金接口 `query_com_fund` + - 添加划拨业务查询普通柜台持仓接口 `query_com_position` +- 2023-10-16 + - 添加期货市价的报价类型 + - `xtconstant.MARKET_BEST` - 市价最优价[郑商所] + - `xtconstant.MARKET_CANCEL` - 市价即成剩撤[大商所] + - `xtconstant.MARKET_CANCEL_ALL` - 市价全额成交或撤[大商所] + - `xtconstant.MARKET_CANCEL_1` - 市价最优一档即成剩撤[中金所] + - `xtconstant.MARKET_CANCEL_5` - 市价最优五档即成剩撤[中金所] + - `xtconstant.MARKET_CONVERT_1` - 市价最优一档即成剩转[中金所] + - `xtconstant.MARKET_CONVERT_5` - 市价最优五档即成剩转[中金所] +- 2023-10-20 +- 委托结构`XtOrder`,成交结构`XtTrade`,持仓结构`XtPosition` 新增多空字段 + - `direction` - 多空,股票不需要 +- 委托结构`XtOrder`,成交结构`XtTrade`新增交易操作字段 + - `offset_flag` - 交易操作,用此字段区分股票买卖,期货开、平仓,期权买卖等 +- 2023-11-03 + - 添加券源行情查询接口 `smt_query_quoter` + - 添加库存券约券申请接口 `smt_negotiate_order` + - 添加约券合约查询接口 `smt_query_compact` +- 2024-01-02 + - 委托类型增加ETF申赎 +- 2024-02-29 + - 添加期货持仓统计查询接口`query_position_statistics` +- 2024-04-25 + - 数据结构添加`stock_code1`字段以适配长代码 +- 2024-05-24 + - 添加通用数据导出接口export_data + - 添加通用数据查询接口query_data +- 2024-06-27 + - 添加外部成交导入接口sync_transaction_from_external +- 2024-08-27 + - 成交结构`XtTrade`新增手续费字段 + - `commission` - 手续费 + +## 快速入门 + +### 创建策略 +```Python +#coding=utf-8 +from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback +from xtquant.xttype import StockAccount +from xtquant import xtconstant + + +class MyXtQuantTraderCallback(XtQuantTraderCallback): + def on_disconnected(self): + """ + 连接断开 + :return: + """ + print("connection lost") + def on_stock_order(self, order): + """ + 委托回报推送 + :param order: XtOrder对象 + :return: + """ + print("on order callback:") + print(order.stock_code, order.order_status, order.order_sysid) + def on_stock_asset(self, asset): + """ + 资金变动推送 + :param asset: XtAsset对象 + :return: + """ + print("on asset callback") + print(asset.account_id, asset.cash, asset.total_asset) + def on_stock_trade(self, trade): + """ + 成交变动推送 + :param trade: XtTrade对象 + :return: + """ + print("on trade callback") + print(trade.account_id, trade.stock_code, trade.order_id) + def on_stock_position(self, position): + """ + 持仓变动推送 + :param position: XtPosition对象 + :return: + """ + print("on position callback") + print(position.stock_code, position.volume) + def on_order_error(self, order_error): + """ + 委托失败推送 + :param order_error:XtOrderError 对象 + :return: + """ + print("on order_error callback") + print(order_error.order_id, order_error.error_id, order_error.error_msg) + def on_cancel_error(self, cancel_error): + """ + 撤单失败推送 + :param cancel_error: XtCancelError 对象 + :return: + """ + print("on cancel_error callback") + print(cancel_error.order_id, cancel_error.error_id, cancel_error.error_msg) + def on_order_stock_async_response(self, response): + """ + 异步下单回报推送 + :param response: XtOrderResponse 对象 + :return: + """ + print("on_order_stock_async_response") + print(response.account_id, response.order_id, response.seq) + def on_account_status(self, status): + """ + :param response: XtAccountStatus 对象 + :return: + """ + print("on_account_status") + print(status.account_id, status.account_type, status.status) + +if __name__ == "__main__": + print("demo test") + # path为mini qmt客户端安装目录下userdata_mini路径 + path = 'D:\\迅投极速交易终端 睿智融科版\\userdata_mini' + # session_id为会话编号,策略使用方对于不同的Python策略需要使用不同的会话编号 + session_id = 123456 + xt_trader = XtQuantTrader(path, session_id) + # 创建资金账号为1000000365的证券账号对象 + acc = StockAccount('1000000365') + # StockAccount可以用第二个参数指定账号类型,如沪港通传'HUGANGTONG',深港通传'SHENGANGTONG' + # acc = StockAccount('1000000365','STOCK') + # 创建交易回调类对象,并声明接收回调 + callback = MyXtQuantTraderCallback() + xt_trader.register_callback(callback) + # 启动交易线程 + xt_trader.start() + # 建立交易连接,返回0表示连接成功 + connect_result = xt_trader.connect() + print(connect_result) + # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功 + subscribe_result = xt_trader.subscribe(acc) + print(subscribe_result) + stock_code = '600000.SH' + # 使用指定价下单,接口返回订单编号,后续可以用于撤单操作以及查询委托状态 + print("order using the fix price:") + fix_result_order_id = xt_trader.order_stock(acc, stock_code, xtconstant.STOCK_BUY, 200, xtconstant.FIX_PRICE, 10.5, 'strategy_name', 'remark') + print(fix_result_order_id) + # 使用订单编号撤单 + print("cancel order:") + cancel_order_result = xt_trader.cancel_order_stock(acc, fix_result_order_id) + print(cancel_order_result) + # 使用异步下单接口,接口返回下单请求序号seq,seq可以和on_order_stock_async_response的委托反馈response对应起来 + print("order using async api:") + async_seq = xt_trader.order_stock(acc, stock_code, xtconstant.STOCK_BUY, 200, xtconstant.FIX_PRICE, 10.5, 'strategy_name', 'remark') + print(async_seq) + # 查询证券资产 + print("query asset:") + asset = xt_trader.query_stock_asset(acc) + if asset: + print("asset:") + print("cash {0}".format(asset.cash)) + # 根据订单编号查询委托 + print("query order:") + order = xt_trader.query_stock_order(acc, fix_result_order_id) + if order: + print("order:") + print("order {0}".format(order.order_id)) + # 查询当日所有的委托 + print("query orders:") + orders = xt_trader.query_stock_orders(acc) + print("orders:", len(orders)) + if len(orders) != 0: + print("last order:") + print("{0} {1} {2}".format(orders[-1].stock_code, orders[-1].order_volume, orders[-1].price)) + # 查询当日所有的成交 + print("query trade:") + trades = xt_trader.query_stock_trades(acc) + print("trades:", len(trades)) + if len(trades) != 0: + print("last trade:") + print("{0} {1} {2}".format(trades[-1].stock_code, trades[-1].traded_volume, trades[-1].traded_price)) + # 查询当日所有的持仓 + print("query positions:") + positions = xt_trader.query_stock_positions(acc) + print("positions:", len(positions)) + if len(positions) != 0: + print("last position:") + print("{0} {1} {2}".format(positions[-1].account_id, positions[-1].stock_code, positions[-1].volume)) + # 根据股票代码查询对应持仓 + print("query position:") + position = xt_trader.query_stock_position(acc, stock_code) + if position: + print("position:") + print("{0} {1} {2}".format(position.account_id, position.stock_code, position.volume)) + # 阻塞线程,接收交易推送 + xt_trader.run_forever() +``` + +# 进阶篇 +## XtQuant运行逻辑 +XtQuant封装了策略交易所需要的Python API接口,可以和MiniQMT客户端交互进行报单、撤单、查询资产、查询委托、查询成交、查询持仓以及收到资金、委托、成交和持仓等变动的主推消息。 + +## XtQuant数据字典 +### 交易市场(market_int) + +- 上交所 - `xtconstant.SH_MARKET` +- 深交所 - `xtconstant.SZ_MARKET` +- 北交所 - `xtconstant.MARKET_ENUM_BEIJING` +- 上期所 - `xtconstant.MARKET_ENUM_SHANGHAI_FUTURE` +- 大商所 - `xtconstant.MARKET_ENUM_DALIANG_FUTURE` +- 郑商所 - `xtconstant.MARKET_ENUM_ZHENGZHOU_FUTURE` +- 中金所 - `xtconstant.MARKET_ENUM_INDEX_FUTURE` +- 能源中心 - `xtconstant.MARKET_ENUM_INTL_ENERGY_FUTURE` +- 广期所 - `xtconstant.MARKET_ENUM_GUANGZHOU_FUTURE` +- 上海期权 - `xtconstant.MARKET_ENUM_SHANGHAI_STOCK_OPTION` +- 深圳期权 - `xtconstant.MARKET_ENUM_SHENZHEN_STOCK_OPTION` + +### 交易市场(market_str) + +- 上交所 - `xtconstant.MARKET_SHANGHAI` +- 深交所 - `xtconstant.MARKET_SHENZHEN` +- 北交所 - `xtconstant.MARKET_BEIJING` +- 上期所 - `xtconstant.MARKET_SHANGHAI_FUTURE` +- 大商所 - `xtconstant.MARKET_DALIANG_FUTURE` +- 郑商所 - `xtconstant.MARKET_ZHENGZHOU_FUTURE` +- 中金所 - `xtconstant.MARKET_INDEX_FUTURE` +- 能源中心 - `xtconstant.MARKET_INTL_ENERGY_FUTURE` +- 广期所 - `xtconstant.MARKET_GUANGZHOU_FUTURE` +- 上海期权 - `xtconstant.MARKET_SHANGHAI_STOCK_OPTION` +- 深圳期权 - `xtconstant.MARKET_SHENZHEN_STOCK_OPTION` + +### 账号类型(account_type) + +- 期货 - `xtconstant.FUTURE_ACCOUNT` +- 股票 - `xtconstant.SECURITY_ACCOUNT` +- 信用 - `xtconstant.CREDIT_ACCOUNT` +- 期货期权 - `xtconstant.FUTURE_OPTION_ACCOUNT` +- 股票期权 - `xtconstant.STOCK_OPTION_ACCOUNT` +- 沪港通 - `xtconstant.HUGANGTONG_ACCOUNT` +- 深港通 - `xtconstant.SHENGANGTONG_ACCOUNT` + +### 委托类型(order_type) + +- 股票 + - 买入 - `xtconstant.STOCK_BUY` + - 卖出 - `xtconstant.STOCK_SELL` + +- 信用 + - 担保品买入 - `xtconstant.CREDIT_BUY` + - 担保品卖出 - `xtconstant.CREDIT_SELL` + - 融资买入 - `xtconstant.CREDIT_FIN_BUY` + - 融券卖出 - `xtconstant.CREDIT_SLO_SELL` + - 买券还券 - `xtconstant.CREDIT_BUY_SECU_REPAY` + - 直接还券 - `xtconstant.CREDIT_DIRECT_SECU_REPAY` + - 卖券还款 - `xtconstant.CREDIT_SELL_SECU_REPAY` + - 直接还款 - `xtconstant.CREDIT_DIRECT_CASH_REPAY` + - 专项融资买入 - `xtconstant.CREDIT_FIN_BUY_SPECIAL` + - 专项融券卖出 - `xtconstant.CREDIT_SLO_SELL_SPECIAL` + - 专项买券还券 - `xtconstant.CREDIT_BUY_SECU_REPAY_SPECIAL` + - 专项直接还券 - `xtconstant.CREDIT_DIRECT_SECU_REPAY_SPECIAL` + - 专项卖券还款 - `xtconstant.CREDIT_SELL_SECU_REPAY_SPECIAL` + - 专项直接还款 - `xtconstant.CREDIT_DIRECT_CASH_REPAY_SPECIAL` + +- 期货六键风格 + - 开多 - `xtconstant.FUTURE_OPEN_LONG` + - 平昨多 - `xtconstant.FUTURE_CLOSE_LONG_HISTORY` + - 平今多 - `xtconstant.FUTURE_CLOSE_LONG_TODAY` + - 开空 - `xtconstant.FUTURE_OPEN_SHORT` + - 平昨空 - `xtconstant.FUTURE_CLOSE_SHORT_HISTORY` + - 平今空 - `xtconstant.FUTURE_CLOSE_SHORT_TODAY` + +- 期货四键风格 + - 平多,优先平今 - `xtconstant.FUTURE_CLOSE_LONG_TODAY_FIRST` + - 平多,优先平昨 - `xtconstant.FUTURE_CLOSE_LONG_HISTORY_FIRST` + - 平空,优先平今 - `xtconstant.FUTURE_CLOSE_SHORT_TODAY_FIRST` + - 平空,优先平昨 - `xtconstant.FUTURE_CLOSE_SHORT_HISTORY_FIRST` +- 期货两键风格 + - 卖出,如有多仓,优先平仓,优先平今,如有余量,再开空 - `xtconstant.FUTURE_CLOSE_LONG_TODAY_HISTORY_THEN_OPEN_SHORT` + - 卖出,如有多仓,优先平仓,优先平昨,如有余量,再开空 - `xtconstant.FUTURE_CLOSE_LONG_HISTORY_TODAY_THEN_OPEN_SHORT` + - 买入,如有空仓,优先平仓,优先平今,如有余量,再开多 - `xtconstant.FUTURE_CLOSE_SHORT_TODAY_HISTORY_THEN_OPEN_LONG` + - 买入,如有空仓,优先平仓,优先平昨,如有余量,再开多 - `xtconstant.FUTURE_CLOSE_SHORT_HISTORY_TODAY_THEN_OPEN_LONG` + - 买入,不优先平仓 - `xtconstant.FUTURE_OPEN` + - 卖出,不优先平仓 - `xtconstant.FUTURE_CLOSE` +- 期货 - 跨商品套利 + - 开仓 - `xtconstant.FUTURE_ARBITRAGE_OPEN` + - 平, 优先平昨 - `xtconstant.FUTURE_ARBITRAGE_CLOSE_HISTORY_FIRST` + - 平, 优先平今 - `xtconstant.FUTURE_ARBITRAGE_CLOSE_TODAY_FIRST` +- 期货展期 + - 看多, 优先平昨 - `xtconstant.FUTURE_RENEW_LONG_CLOSE_HISTORY_FIRST` + - 看多,优先平今 - `xtconstant.FUTURE_RENEW_LONG_CLOSE_TODAY_FIRST` + - 看空,优先平昨 - `xtconstant.FUTURE_RENEW_SHORT_CLOSE_HISTORY_FIRST` + - 看空,优先平今 - `xtconstant.FUTURE_RENEW_SHORT_CLOSE_TODAY_FIRST` +- 股票期权 + - 买入开仓,以下用于个股期权交易业务 - `xtconstant.STOCK_OPTION_BUY_OPEN` + - 卖出平仓 - `xtconstant.STOCK_OPTION_SELL_CLOSE` + - 卖出开仓 - `xtconstant.STOCK_OPTION_SELL_OPEN` + - 买入平仓 - `xtconstant.STOCK_OPTION_BUY_CLOSE` + - 备兑开仓 - `xtconstant.STOCK_OPTION_COVERED_OPEN` + - 备兑平仓 - `xtconstant.STOCK_OPTION_COVERED_CLOSE` + - 认购行权 - `xtconstant.STOCK_OPTION_CALL_EXERCISE` + - 认沽行权 - `xtconstant.STOCK_OPTION_PUT_EXERCISE` + - 证券锁定 - `xtconstant.STOCK_OPTION_SECU_LOCK` + - 证券解锁 - `xtconstant.STOCK_OPTION_SECU_UNLOCK` +- 期货期权 + - 期货期权行权 - `xtconstant.OPTION_FUTURE_OPTION_EXERCISE` + +- ETF申赎 + - 申购 - `xtconstant.ETF_ETFBUY ` + - 赎回 - `xtconstant.ETF_ETFSELL ` + +### 报价类型(price_type) + +- 最新价 - `xtconstant.LATEST_PRICE` +- 指定价 - `xtconstant.FIX_PRICE` +- 郑商所 期货 + - 市价最优价 - `xtconstant.MARKET_BEST` +- 大商所 期货 + - 市价即成剩撤 - `xtconstant.MARKET_CANCEL` + - 市价全额成交或撤 - `xtconstant.MARKET_CANCEL_ALL` +- 中金所 期货 + - 市价最优一档即成剩撤 - `xtconstant.MARKET_CANCEL_1` + - 市价最优五档即成剩撤 - `xtconstant.MARKET_CANCEL_5` + - 市价最优一档即成剩转 - `xtconstant.MARKET_CONVERT_1` + - 市价最优五档即成剩转 - `xtconstant.MARKET_CONVERT_5` +- 上交所 股票 + - 最优五档即时成交剩余撤销 - `xtconstant.MARKET_SH_CONVERT_5_CANCEL` + - 最优五档即时成交剩转限价 - `xtconstant.MARKET_SH_CONVERT_5_LIMIT` + - 对手方最优价格委托 - `xtconstant.MARKET_PEER_PRICE_FIRST` + - 本方最优价格委托 - `xtconstant.MARKET_MINE_PRICE_FIRST` +- 深交所 股票 期权 + - 对手方最优价格委托 - `xtconstant.MARKET_PEER_PRICE_FIRST` + - 本方最优价格委托 - `xtconstant.MARKET_MINE_PRICE_FIRST` + - 即时成交剩余撤销委托 - `xtconstant.MARKET_SZ_INSTBUSI_RESTCANCEL` + - 最优五档即时成交剩余撤销 - `xtconstant.MARKET_SZ_CONVERT_5_CANCEL` + - 全额成交或撤销委托 - `xtconstant.MARKET_SZ_FULL_OR_CANCEL` + +### 委托状态(order_status) +枚举变量名|值|含义 +-|-|-| +xtconstant.ORDER_UNREPORTED|48|未报 +xtconstant.ORDER_WAIT_REPORTING|49|待报 +xtconstant.ORDER_REPORTED|50|已报 +xtconstant.ORDER_REPORTED_CANCEL|51|已报待撤 +xtconstant.ORDER_PARTSUCC_CANCEL|52|部成待撤 +xtconstant.ORDER_PART_CANCEL|53|部撤 +xtconstant.ORDER_CANCELED|54|已撤 +xtconstant.ORDER_PART_SUCC|55|部成 +xtconstant.ORDER_SUCCEEDED|56|已成 +xtconstant.ORDER_JUNK|57|废单 +xtconstant.ORDER_UNKNOWN|255|未知 + +### 账号状态(account_status) +枚举变量名|值|含义 +-|-|- +xtconstant.ACCOUNT_STATUS_INVALID|-1|无效 +xtconstant.ACCOUNT_STATUS_OK|0|正常 +xtconstant.ACCOUNT_STATUS_WAITING_LOGIN|1|连接中 +xtconstant.ACCOUNT_STATUSING|2|登陆中 +xtconstant.ACCOUNT_STATUS_FAIL|3|失败 +xtconstant.ACCOUNT_STATUS_INITING|4|初始化中 +xtconstant.ACCOUNT_STATUS_CORRECTING|5|数据刷新校正中 +xtconstant.ACCOUNT_STATUS_CLOSED|6|收盘后 +xtconstant.ACCOUNT_STATUS_ASSIS_FAIL|7|穿透副链接断开 +xtconstant.ACCOUNT_STATUS_DISABLEBYSYS|8|系统停用(总线使用-密码错误超限) +xtconstant.ACCOUNT_STATUS_DISABLEBYUSER|9|用户停用(总线使用) + +### 划拨方向(transfer_direction) + +| 枚举变量名 | 值 | 含义 | +| ----------------------------------------- | ---- | ------------------------------- | +| xtconstant.FUNDS_TRANSFER_NORMAL_TO_SPEED | 510 | 资金划拨-普通柜台到极速柜台 | +| xtconstant.FUNDS_TRANSFER_SPEED_TO_NORMAL | 511 | 资金划拨-极速柜台到普通柜台 | +| xtconstant.NODE_FUNDS_TRANSFER_SH_TO_SZ | 512 | 节点资金划拨-上海节点到深圳节点 | +| xtconstant.NODE_FUNDS_TRANSFER_SZ_TO_SH | 513 | 节点资金划拨-深圳节点到上海节点 | + +### 多空方向(direction) + +| 枚举变量名 | 值 | 含义 | +| ------------------------------- | ---- | ---- | +| xtconstant.DIRECTION_FLAG_LONG | 48 | 多 | +| xtconstant.DIRECTION_FLAG_SHORT | 49 | 空 | + +### 交易操作(offset_flag) + +| 枚举变量名 | 值 | 含义 | +| -------------------------------------- | ---- | ---------- | +| xtconstant.OFFSET_FLAG_OPEN | 48 | 买入,开仓 | +| xtconstant.OFFSET_FLAG_CLOSE | 49 | 卖出,平仓 | +| xtconstant.OFFSET_FLAG_FORCECLOSE | 50 | 强平 | +| xtconstant.OFFSET_FLAG_CLOSETODAY | 51 | 平今 | +| xtconstant.OFFSET_FLAG_ClOSEYESTERDAY | 52 | 平昨 | +| xtconstant.OFFSET_FLAG_FORCEOFF | 53 | 强减 | +| xtconstant.OFFSET_FLAG_LOCALFORCECLOSE | 54 | 本地强平 | + +### XtQuant数据结构说明 + +### 资产XtAsset +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +cash | float | 可用金额 +frozen_cash |float | 冻结金额 +market_value | float | 持仓市值 +total_asset | float | 总资产 + +### 委托XtOrder +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +stock_code | str | 证券代码,例如"600000.SH" +order_id | int | 订单编号 +order_sysid | str | 柜台合同编号 +order_time | int | 报单时间 +order_type | int | 委托类型,参见数据字典 +order_volume | int | 委托数量 +price_type | int | 报价类型,参见数据字典 +price | float | 委托价格 +traded_volume | int | 成交数量 +traded_price | float | 成交均价 +order_status | int | 委托状态,参见数据字典 +status_msg | str | 委托状态描述,如废单原因 +strategy_name | str | 策略名称 +order_remark | str | 委托备注 +direction | int | 多空方向,股票不需要;参见数据字典 +offset_flag | int | 交易操作,用此字段区分股票买卖,期货开、平仓,期权买卖等;参见数据字典 +stock_code1 | str | 证券代码,例如"600000.SH" + +### 成交XtTrade +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +stock_code | str | 证券代码 +order_type | int | 委托类型,参见数据字典 +traded_id | str | 成交编号 +traded_time | int | 成交时间 +traded_price | float | 成交均价 +traded_volume | int | 成交数量 +traded_amount | float | 成交金额 +order_id | int | 订单编号 +order_sysid | str | 柜台合同编号 +strategy_name | str | 策略名称 +order_remark | str | 委托备注 +direction | int | 多空方向,股票不需要;参见数据字典 +offset_flag | int | 交易操作,用此字段区分股票买卖,期货开、平仓,期权买卖等;参见数据字典 +stock_code1 | str | 证券代码,例如"600000.SH" +commission | float | 手续费 + +### 持仓XtPosition +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +stock_code | str | 证券代码 +volume | int | 持仓数量 +can_use_volume | int | 可用数量 +open_price | float | 开仓价 +market_value | float | 市值 +frozen_volume | int | 冻结数量 +on_road_volume | int | 在途股份 +yesterday_volume | int | 昨夜拥股 +avg_price | float | 成本价 +direction | int | 多空方向,股票不需要;参见数据字典 +stock_code1 | str | 证券代码,例如"600000.SH" + +### 期货持仓统计XtPositionStatistics + +| 属性 | 类型 | 注释 | +| ---------------------------------- | -------- | --------------------- | +| `account_id` | `string` | 账户 | +| `exchange_id` | `string` | 市场代码 | +| `exchange_name` | `string` | 市场名称 | +| `product_id` | `string` | 品种代码 | +| `instrument_id` | `string` | 合约代码 | +| `instrument_name` | `string` | 合约名称 | +| `direction` | `int` | 多空 | +| `hedge_flag` | `int` | 投保 | +| `position` | `int` | 持仓 | +| `yesterday_position` | `int` | 昨仓 | +| `today_position` | `int` | 今仓 | +| `can_close_vol` | `int` | 可平 | +| `position_cost` | `float` | 持仓成本 | +| `avg_price` | `float` | 持仓均价 | +| `position_profit` | `float` | 持仓盈亏 | +| `float_profit` | `float` | 浮动盈亏 | +| `open_price` | `float` | 开仓均价 | +| `used_margin` | `float` | 已使用保证金 | +| `used_commission` | `float` | 已使用的手续费 | +| `frozen_margin` | `float` | 冻结保证金 | +| `frozen_commission` | `float` | 冻结手续费 | +| `instrument_value` | `float` | 市值,合约价值 | +| `open_times` | `int` | 开仓次数 | +| `open_volume` | `int` | 总开仓量 中间平仓不减 | +| `cancel_times` | `int` | 撤单次数 | +| `last_price` | `float` | 最新价 | +| `rise_ratio` | `float` | 当日涨幅 | +| `product_name` | `string` | 产品名称 | +| `royalty` | `float` | 权利金市值 | +| `expire_date` | `string` | 到期日 | +| `assest_weight` | `float` | 资产占比 | +| `increase_by_settlement` | `float` | 当日涨幅(结) | +| `margin_ratio` | `float` | 保证金占比 | +| `float_profit_divide_by_used_margin` | `float` | 浮盈比例(保证金) | +| `float_profit_divide_by_balance` | `float` | 浮盈比例(动态权益) | +| `today_profit_loss` | `float` | 当日盈亏(结) | +| `yesterday_init_position` | `int` | 昨日持仓 | +| `frozen_royalty` | `float` | 冻结权利金 | +| `today_close_profit_loss` | `float` | 当日盈亏(收) | +| `close_profit` | `float` | 平仓盈亏 | +| `ft_product_name` | `string` | 品种名称 | +| `open_cost` | `float` | 开仓成本 | + +### 异步下单委托反馈XtOrderResponse + +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +order_id | int | 订单编号 +strategy_name | str | 策略名称 +order_remark | str | 委托备注 +seq | int | 异步下单的请求序号 + +### 异步撤单委托反馈XtCancelOrderResponse + +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +order_id | int | 订单编号 +order_sysid | str | 柜台委托编号 +cancel_result | int | 撤单结果 +seq | int | 异步撤单的请求序号 + +### 下单失败错误XtOrderError +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +order_id | int | 订单编号 +error_id | int | 下单失败错误码 +error_msg | str | 下单失败具体信息 +strategy_name | str | 策略名称 +order_remark | str | 委托备注 + +### 撤单失败错误XtCancelError +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +order_id | int | 订单编号 +market | int | 交易市场 0:上海 1:深圳 +order_sysid | str | 柜台委托编号 +error_id | int | 下单失败错误码 +error_msg | str | 下单失败具体信息 + +### 信用账号资产XtCreditDetail +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +m_nStatus | int |账号状态 +m_nUpdateTime | int |更新时间 +m_nCalcConfig | int |计算参数 +m_dFrozenCash | float |冻结金额 +m_dBalance | float |总资产 +m_dAvailable | float |可用金额 +m_dPositionProfit | float |持仓盈亏 +m_dMarketValue | float |总市值 +m_dFetchBalance | float |可取金额 +m_dStockValue | float |股票市值 +m_dFundValue | float |基金市值 +m_dTotalDebt | float |总负债 +m_dEnableBailBalance | float |可用保证金 +m_dPerAssurescaleValue | float |维持担保比例 +m_dAssureAsset | float |净资产 +m_dFinDebt | float |融资负债 +m_dFinDealAvl | float |融资本金 +m_dFinFee | float |融资息费 +m_dSloDebt | float |融券负债 +m_dSloMarketValue | float |融券市值 +m_dSloFee | float |融券息费 +m_dOtherFare | float |其它费用 +m_dFinMaxQuota | float |融资授信额度 +m_dFinEnableQuota | float |融资可用额度 +m_dFinUsedQuota | float |融资冻结额度 +m_dSloMaxQuota | float |融券授信额度 +m_dSloEnableQuota | float |融券可用额度 +m_dSloUsedQuota | float |融券冻结额度 +m_dSloSellBalance | float |融券卖出资金 +m_dUsedSloSellBalance | float |已用融券卖出资金 +m_dSurplusSloSellBalance | float |剩余融券卖出资金 + +### 负债合约StkCompacts + +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +compact_type | int | 合约类型 +cashgroup_prop | int | 头寸来源 +exchange_id | int | 证券市场 +open_date | int | 开仓日期 +business_vol | int | 合约证券数量 +real_compact_vol | int | 未还合约数量 +ret_end_date | int | 到期日 +business_balance | float | 合约金额 +businessFare | float | 合约息费 +real_compact_balance | float | 未还合约金额 +real_compact_fare | float | 未还合约息费 +repaid_fare | float | 已还息费 +repaid_balance | float | 已还金额 +instrument_id | str | 证券代码 +compact_id | str | 合约编号 +position_str | str | 定位串 + +### 融资融券标的CreditSubjects +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +slo_status | int | 融券状态 +fin_status | int | 融资状态 +exchange_id | int | 证券市场 +slo_ratio | float | 融券保证金比例 +fin_ratio | float | 融资保证金比例 +instrument_id | str | 证券代码 + +### 可融券数据CreditSloCode +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +cashgroup_prop | int | 头寸来源 +exchange_id | int | 证券市场 +enable_amount | int | 融券可融数量 +instrument_id | str | 证券代码 + +### 标的担保品CreditAssure +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +assure_status | int | 是否可做担保 +exchange_id | int | 证券市场 +assure_ratio | float | 担保品折算比例 +instrument_id | str | 证券代码 + +### 账号状态XtAccountStatus +属性|类型|注释 +-|-|- +account_type| int | 账号类型,参见数据字典 +account_id | str | 资金账号 +status | int | 账号状态,参见数据字典 + +### 账号信息XtAccountInfo + +| 属性 | 类型 | 注释 | +| ---------------------- | ---- | ---------------------- | +| account_type | int | 账号类型,参见数据字典 | +| account_id | str | 资金账号 | +| broker_type | int | 同 account_type | +| platform_id | int | 平台号 | +| account_classification | int | 账号分类 | +| login_status | int | 账号状态,参见数据字典 | + +### 约券相关异步接口的反馈XtSmtAppointmentResponse + +| 属性 | 类型 | 注释 | +| -------- | ---- | -------------------------------------- | +| seq | int | 异步请求序号 | +| success | bool | 申请是否成功 | +| msg | str | 反馈信息 | +| apply_id | str | 若申请成功返回资券申请编号,否则返回-1 | + +## XtQuant API说明 + +### 系统设置接口 + +#### 创建API实例 + +```python +XtQuantTrader(path, session_id) +``` +* 释义 + - 创建XtQuant API的实例 +* 参数 + - path - str MiniQMT客户端userdata_mini的完整路径 + - session_id - int 与MiniQMT通信的会话ID,不同的会话要保证不重 +* 返回 + - XtQuant API实例对象 +* 备注 + - 后续对XtQuant API的操作都需要该实例对象 + - 通常情况下只需要创建一个XtQuant API实例 +* 示例 + +```python +path = 'D:\\迅投极速交易终端 睿智融科版\\userdata_mini' +# session_id为会话编号,策略使用方对于不同的Python策略需要使用不同的会话编号 +session_id = 123456 +#后续的所有示例将使用该实例对象 +xt_trader = XtQuantTrader(path, session_id) +``` + +#### 注册回调类 +```python +register_callback(callback) +``` +* 释义 + - 将回调类实例对象注册到API实例中,用以消息回调和主推 +* 参数 + - callback - XtQuantTraderCallback 回调类实例对象 +* 返回 + - 无 +* 备注 + - 无 +* 示例 +```python +# 创建交易回调类对象,并声明接收回调 +class MyXtQuantTraderCallback(XtQuantTraderCallback): + ... + pass +callback = MyXtQuantTraderCallback() +#xt_trader为XtQuant API实例对象 +xt_trader.register_callback(callback) +``` + +#### 准备API环境 +``` +start() +``` +* 释义 + - 启动交易线程,准备交易所需的环境 +* 参数 + - 无 +* 返回 + - 无 +* 备注 + - 无 +* 示例 +```python +# 启动交易线程 +#xt_trader为XtQuant API实例对象 +xt_trader.start() +``` + +#### 创建连接 +```python +connect() +``` +* 释义 + - 连接MiniQMT +* 参数 + - 无 +* 返回 + - 连接结果信息,连接成功返回0,失败返回非0 +* 备注 + - 该连接为一次性连接,断开连接后不会重连,需要再次主动调用 +* 示例 +```python +# 建立交易连接,返回0表示连接成功 +#xt_trader为XtQuant API实例对象 +connect_result = xt_trader.connect() +print(connect_result) +``` + +#### 停止运行 +```python +stop() +``` +* 释义 + - 停止API接口 +* 参数 + - 无 +* 返回 + - 无 +* 备注 + - 无 +* 示例 +```python +#xt_trader为XtQuant API实例对象 +xt_trader.stop() +``` + +#### 阻塞当前线程进入等待状态 +```python +run_forever() +``` +* 释义 + - 阻塞当前线程,进入等待状态,直到stop函数被调用结束阻塞 +* 参数 + - 无 +* 返回 + - 无 +* 备注 + - 无 +* 示例 +```python +#xt_trader为XtQuant API实例对象 +xt_trader.run_forever() +``` + +#### 开启主动请求接口的专用线程 + +```python +set_relaxed_response_order_enabled(enabled) +``` + +* 释义 + + * 控制主动请求接口的返回是否从额外的专用线程返回,以获得宽松的数据时序 + +* 参数 + + - enabled - bool 是否开启,默认为False关闭 + +* 返回 + + - 无 + +* 备注 + + * 如果开启,在on_stock_order等推送回调中调用同步请求不会卡住,但查询和推送的数据在时序上会变得不确定 + + * ``` + timeline t1 t2 t3 t4 + callback push1 push2 push3 resp4 + do query4 ------------------^ + ``` + + * 例如:分别在t1 t2 t3时刻到达三条委托数据,在on_push1中调用同步委托查询接口query_orders() + + * 未开启宽松时序时,查询返回resp4会在t4时刻排队到push3完成之后处理,这使得同步等待结果的查询不能返回而卡住执行 + + * 开启宽松时序时,查询返回的resp4由专用线程返回,程序正常执行,但此时查到的resp4是push3之后的状态,也就是说resp4中的委托要比push2 push3这两个前一时刻推送的数据新,但在更早的t1时刻就进入了处理 + + * 使用中请根据策略实际情况来开启,通常情况下,推荐在on_stock_order等推送回调中使用查询接口的异步版本,如`query_stock_orders_async` + +### 操作接口 + +#### 订阅账号信息 +```python +subscribe(account) +``` +* 释义 + - 订阅账号信息,包括资金账号、委托信息、成交信息、持仓信息 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 订阅结果信息,订阅成功返回0,订阅失败返回-1 +* 备注 + - 无 +* 示例 + - 订阅资金账号1000000365 +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +subscribe_result = xt_trader.subscribe(account) +``` +#### 反订阅账号信息 + +```python +unsubscribe(account) +``` +* 释义 + - 反订阅账号信息 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 反订阅结果信息,订阅成功返回0,订阅失败返回-1 +* 备注 + - 无 +* 示例 + - 订阅资金账号1000000365 +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +unsubscribe_result = xt_trader.unsubscribe(account) +``` +#### 股票同步报单 + +```python +order_stock(account, stock_code, order_type, order_volume, price_type, price, strategy_name, order_remark) +``` +* 释义 + - 对股票进行下单操作 +* 参数 + - account - StockAccount 资金账号 + - stock_code - str 证券代码,如'600000.SH' + - order_type - int 委托类型 + - order_volume - int 委托数量,股票以'股'为单位,债券以'张'为单位 + - price_type - int 报价类型 + - price - float 委托价格 + - strategy_name - str 策略名称 + - order_remark - str 委托备注 +* 返回 + - 系统生成的订单编号,成功委托后的订单编号为大于0的正整数,如果为-1表示委托失败 +* 备注 + - 无 +* 示例 + - 股票资金账号1000000365对浦发银行买入1000股,使用限价价格10.5元, 委托备注为'order_test' +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +order_id = xt_trader.order_stock(account, '600000.SH', xtconstant.STOCK_BUY, 1000, xtconstant.FIX_PRICE, 10.5, 'strategy1', 'order_test') +``` +#### 股票异步报单 + +```python +order_stock_async(account, stock_code, order_type, order_volume, price_type, price, strategy_name, order_remark) +``` +* 释义 + - 对股票进行异步下单操作,异步下单接口如果正常返回了下单请求序号seq,会收到on_order_stock_async_response的委托反馈 +* 参数 + - account - StockAccount 资金账号 + - stock_code - str 证券代码, 如'600000.SH' + - order_type - int 委托类型 + - order_volume - int 委托数量,股票以'股'为单位,债券以'张'为单位 + - price_type - int 报价类型 + - price - float 委托价格 + - strategy_name - str 策略名称 + - order_remark - str 委托备注 +* 返回 + - 返回下单请求序号seq,成功委托后的下单请求序号为大于0的正整数,如果为-1表示委托失败 +* 备注 + - 如果失败,则通过下单失败主推接口返回下单失败信息 +* 示例 + - 股票资金账号1000000365对浦发银行买入1000股,使用限价价格10.5元,委托备注为'order_test' + +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +seq = xt_trader.order_stock_async(account, '600000.SH', xtconstant.STOCK_BUY, 1000, xtconstant.FIX_PRICE, 10.5, 'strategy1', 'order_test') +``` +#### 股票同步撤单 + +```python +cancel_order_stock(account, order_id) +``` +* 释义 + - 根据订单编号对委托进行撤单操作 +* 参数 + - account - StockAccount 资金账号 + - order_id - int 同步下单接口返回的订单编号 +* 返回 + - 返回是否成功发出撤单指令,0: 成功, -1: 表示撤单失败 +* 备注 + - 无 +* 示例 + - 股票资金账号1000000365对订单编号为order_id的委托进行撤单 + +```python +account = StockAccount('1000000365') +order_id = 100 +#xt_trader为XtQuant API实例对象 +cancel_result = xt_trader.cancel_order_stock(account, order_id) +``` +#### 股票同步撤单 + +```python +cancel_order_stock_sysid(account, market, order_sysid) +``` +* 释义 + - 根据券商柜台返回的合同编号对委托进行撤单操作 +* 参数 + - account - StockAccount 资金账号 + - market - int 交易市场 + - order_sysid - str 券商柜台的合同编号 +* 返回 + - 返回是否成功发出撤单指令,0: 成功, -1: 表示撤单失败 +* 备注 + - 无 +* 示例 + - 股票资金账号1000000365对柜台合同编号为order_sysid的上交所委托进行撤单 +```python +account = StockAccount('1000000365') +market = xtconstant.SH_MARKET +order_sysid = "100" +#xt_trader为XtQuant API实例对象 +cancel_result = xt_trader.cancel_order_stock_sysid(account, market, order_sysid) +``` +#### 股票异步撤单 + +```python +cancel_order_stock_async(account, order_id) +``` +* 释义 + - 根据订单编号对委托进行异步撤单操作 +* 参数 + - account - StockAccount 资金账号 + - order_id - int 下单接口返回的订单编号 +* 返回 + - 返回撤单请求序号, 成功委托后的撤单请求序号为大于0的正整数, 如果为-1表示委托失败 +* 备注 + - 如果失败,则通过撤单失败主推接口返回撤单失败信息 +* 示例 + - 股票资金账号1000000365对订单编号为order_id的委托进行异步撤单 + +```python +account = StockAccount('1000000365') +order_id = 100 +#xt_trader为XtQuant API实例对象 +cancel_result = xt_trader.cancel_order_stock_async(account, order_id) +``` +#### 股票异步撤单 + +```python +cancel_order_stock_sysid_async(account, market, order_sysid) +``` +* 释义 + - 根据券商柜台返回的合同编号对委托进行异步撤单操作 +* 参数 + - account - StockAccount 资金账号 + - market - int/str 交易市场 见枚举类型 交易市场(market_int/market_str) + - order_sysid - str 券商柜台的合同编号 +* 返回 + - 返回撤单请求序号, 成功委托后的撤单请求序号为大于0的正整数, 如果为-1表示委托失败 +* 备注 + - 如果失败,则通过撤单失败主推接口返回撤单失败信息 +* 示例 + - 股票资金账号1000000365对柜台合同编号为order_sysid的上交所委托进行异步撤单 +```python +account = StockAccount('1000000365') +market = xtconstant.SH_MARKET +order_sysid = "100" +#xt_trader为XtQuant API实例对象 +cancel_result = xt_trader.cancel_order_stock_sysid_async(account, market, order_sysid) +``` +#### 资金划拨 + +```python +fund_transfer(account, transfer_direction, price) +``` + +* 释义 + - 资金划拨 +* 参数 + - account - StockAccount 资金账号 + - transfer_direction - int 划拨方向,见数据字典划拨方向(transfer_direction)字段说明 + - price - float 划拨金额 +* 返回 + - (success, msg) + - success - bool 划拨操作是否成功 + - msg - str 反馈信息 + +#### 外部交易数据录入 + +```python +sync_transaction_from_external(operation, data_type, account, deal_list) +``` + +* 释义 + + - 通用数据导出 + +* 参数 + + * operation - str 操作类型,有"UPDATE","REPLACE","ADD","DELETE" + * data_type - str 数据类型,有"DEAL" + * account - StockAccount 资金账号 + * deal_list - list 成交列表,每一项是Deal成交对象的参数字典,键名参考官网数据字典,大小写保持一致 + +* 返回 + + - result - dict 结果反馈信息 + +* 示例 + + ```python + deal_list = [ + {'m_strExchangeID':'SF', 'm_strInstrumentID':'ag2407' + , 'm_strTradeID':'123456', 'm_strOrderSysID':'1234566' + , 'm_dPrice':7600, 'm_nVolume':1 + , 'm_strTradeDate': '20240627' + } + ] + resp = xt_trader.sync_transaction_from_external('ADD', 'DEAL', acc, deal_list) + print(resp) + #成功输出示例:{'msg': 'sync transaction from external success'} + #失败输出示例:{'error': {'msg': '[0-0: invalid operation type: ADDD], '}} + ``` + + +### 股票查询接口 + +#### 资产查询 + +```python +query_stock_asset(account) +``` +* 释义 + - 查询资金账号对应的资产 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账号对应的资产对象XtAsset或者None +* 备注 + - 返回None表示查询失败 +* 示例 + - 查询股票资金账号1000000365对应的资产数据 +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +asset = xt_trader.query_stock_asset(account) +``` +#### 委托查询 + +```python +query_stock_orders(account, cancelable_only = False) +``` +* 释义 + - 查询资金账号对应的当日所有委托 +* 参数 + - account - StockAccount 资金账号 + - cancelable_only - bool 仅查询可撤委托 +* 返回 + - 该账号对应的当日所有委托对象XtOrder组成的list或者None +* 备注 + - None表示查询失败或者当日委托列表为空 +* 示例 + - 查询股票资金账号1000000365对应的当日所有委托 +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +orders = xt_trader.query_stock_orders(account, False) +``` +#### 成交查询 + +```python +query_stock_trades(account) +``` +* 释义 + - 查询资金账号对应的当日所有成交 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账号对应的当日所有成交对象XtTrade组成的list或者None +* 备注 + - None表示查询失败或者当日成交列表为空 +* 示例 + - 查询股票资金账号1000000365对应的当日所有成交 +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +trades = xt_trader.query_stock_trades(account) +``` +#### 持仓查询 + +```python +query_stock_positions(account) +``` +* 释义 + - 查询资金账号对应的持仓 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账号对应的最新持仓对象XtPosition组成的list或者None +* 备注 + - None表示查询失败或者当日持仓列表为空 +* 示例 + - 查询股票资金账号1000000365对应的最新持仓 +```python +account = StockAccount('1000000365') +#xt_trader为XtQuant API实例对象 +positions = xt_trader.query_stock_positions(account) +``` +#### 期货持仓统计查询 + +``` +query_position_statistics(account) +``` + +- 释义 + - 查询期货账号的持仓统计 +- 参数 + - account - StockAccount 资金账号 +- 返回 + - 该账号对应的最新持仓对象XtPositionStatistics组成的list或者None +- 备注 + - None表示查询失败或者当日持仓列表为空 +- 示例 + - 查询期货资金账号1000000365对应的最新持仓 + +``` +account = StockAccount('1000000365', 'FUTURE') +#xt_trader为XtQuant API实例对象 +positions = xt_trader.query_position_statistics(account) +``` + +### 信用查询接口 + +#### 信用资产查询 + +```python +query_credit_detail(account) +``` +* 释义 + - 查询信用资金账号对应的资产 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该信用账户对应的资产对象XtCreditDetail组成的list或者None +* 备注 + - None表示查询失败 + - 通常情况下一个资金账号只有一个详细信息数据 +* 示例 + - 查询信用资金账号1208970161对应的资产信息 +```python +account = StockAccount('1208970161', 'CREDIT') +#xt_trader为XtQuant API实例对象 +datas = xt_trader.query_credit_detail(account) +``` +#### 负债合约查询 + +```python +query_stk_compacts(account) +``` +* 释义 + - 查询资金账号对应的负债合约 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账户对应的负债合约对象StkCompacts组成的list或者None +* 备注 + - None表示查询失败或者负债合约列表为空 +* 示例 + - 查询信用资金账号1208970161对应的负债合约 +```python +account = StockAccount('1208970161', 'CREDIT') +#xt_trader为XtQuant API实例对象 +datas = xt_trader.query_stk_compacts(account) +``` +#### 融资融券标的查询 + +```python +query_credit_subjects(account) +``` +* 释义 + - 查询资金账号对应的融资融券标的 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账户对应的融资融券标的对象CreditSubjects组成的list或者None +* 备注 + - None表示查询失败或者融资融券标的列表为空 +* 示例 + - 查询信用资金账号1208970161对应的融资融券标的 +```python +account = StockAccount('1208970161', 'CREDIT') +#xt_trader为XtQuant API实例对象 +datas = xt_trader.query_credit_subjects(account) +``` +#### 可融券数据查询 + +```python +query_credit_slo_code(account) +``` +* 释义 + - 查询资金账号对应的可融券数据 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账户对应的可融券数据对象CreditSloCode组成的list或者None +* 备注 + - None表示查询失败或者可融券数据列表为空 +* 示例 + - 查询信用资金账号1208970161对应的可融券数据 +```python +account = StockAccount('1208970161', 'CREDIT') +#xt_trader为XtQuant API实例对象 +datas = xt_trader.query_credit_slo_code(account) +``` +#### 标的担保品查询 + +```python +query_credit_assure(account) +``` +* 释义 + - 查询资金账号对应的标的担保品 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - 该账户对应的标的担保品对象CreditAssure组成的list或者None +* 备注 + - None表示查询失败或者标的担保品列表为空 +* 示例 + - 查询信用资金账号1208970161对应的标的担保品 +```python +account = StockAccount('1208970161', 'CREDIT') +#xt_trader为XtQuant API实例对象 +datas = xt_trader.query_credit_assure(account) +``` +### 其他查询接口 + +#### 新股申购额度查询 + +```python +query_new_purchase_limit(account) +``` + +* 释义 + - 查询新股申购额度 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - dict 新股申购额度数据集 + - { type1: number1, type2: number2, ... } + - type - str 品种类型 + - `KCB` - 科创板,`SH` - 上海,`SZ` - 深圳 + - number - int 可申购股数 +* 备注 + - 数据仅代表股票申购额度,债券的申购额度固定10000张 + +#### 当日新股信息查询 + +```python +query_ipo_data() +``` + +* 释义 + + - 查询当日新股新债信息 + +* 参数 + + - 无 + +* 返回 + + - dict 新股新债信息数据集 + + - { stock1: info1, stock2: info2, ... } + + - stock - str 品种代码,例如 '301208.SZ' + - info - dict 新股信息 + - name - str 品种名称 + - type - str 品种类型 + - `STOCK` - 股票,`BOND` - 债券 + - minPurchaseNum / maxPurchaseNum - int 最小 / 最大申购额度 + - 单位为股(股票)/ 张(债券) + - purchaseDate - str 申购日期 + - issuePrice - float 发行价 + + - 返回值示例 + + ```python + {'754810.SH': {'name': '丰山发债', 'type': 'BOND', 'maxPurchaseNum': 10000, 'minPurchaseNum': 10, 'purchaseDate': '20220627', 'issuePrice': 100.0}, '301208.SZ': {'name': '中亦科技', 'type': 'STOCK', 'maxPurchaseNum': 16500, 'minPurchaseNum': 500, 'purchaseDate': '20220627', 'issuePrice': 46.06}} + ``` + +* 备注 + + - 无 + +#### 账号信息查询 + +```python +query_account_infos() +``` + +* 释义 + + - 查询所有资金账号 + +* 参数 + + - 无 + +* 返回 + + - list 账号信息列表 + + - [ XtAccountInfo ] + +* 备注 + + - 无 + +#### 普通柜台资金查询 + +```python +query_com_fund(account) +``` + +* 释义 + - 划拨业务查询普通柜台的资金 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - result - dict 资金信息,包含以下字段 + - success - bool + - erro - str + - currentBalance - double 当前余额 + - enableBalance - double 可用余额 + - fetchBalance - double 可取金额 + - interest - double 待入账利息 + - assetBalance - double 总资产 + - fetchCash - double 可取现金 + - marketValue - double 市值 + - debt - double 负债 + +#### 普通柜台持仓查询 + +```python +query_com_position(account) +``` + +* 释义 + - 划拨业务查询普通柜台的持仓 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - result - list 持仓信息列表[position1, position2, ...] + - position - dict 持仓信息,包含以下字段 + - success - bool + - error - str + - stockAccount - str 股东号 + - exchangeType - str 交易市场 + - stockCode - str 证券代码 + - stockName - str 证券名称 + - totalAmt - float 总量 + - enableAmount - float 可用量 + - lastPrice - float 最新价 + - costPrice - float 成本价 + - income - float 盈亏 + - incomeRate - float 盈亏比例 + - marketValue - float 市值 + - costBalance - float 成本总额 + - bsOnTheWayVol - int 买卖在途量 + - prEnableVol - int 申赎可用量 + +#### 通用数据导出 + +```python +export_data(account, result_path, data_type, start_time = None, end_time = None, user_param = {}) +``` + +* 释义 + + - 通用数据导出 + +* 参数 + + - account - StockAccount 资金账号 + - result_path - str 导出路径,包含文件名及.csv后缀,如'C:\\Users\\Desktop\\test\\deal.csv' + - data_type - str 数据类型,如'deal' + - start_time - str 开始时间(可缺省) + - end_time - str 结束时间(可缺省) + - user_param - dict 用户参数(可缺省) + +* 返回 + + - result - dict 结果反馈信息 + +* 示例 + + ```python + resp = xt_trader.export_data(acc, 'C:\\Users\\Desktop\\test\\deal.csv', 'deal') + print(resp) + #成功输出示例:{'msg': 'export success'} + #失败输出示例:{'error': {'errorMsg': 'can not find account info, accountID:2000449 accountType:2'}} + ``` + + + +#### 通用数据查询 + +```python +query_data(account, result_path, data_type, start_time = None, end_time = None, user_param = {}) +``` + +* 释义 + + - 通用数据查询,利用export_data接口导出数据后再读取其中的数据内容,读取完毕后删除导出的文件 + +* 参数 + + 同export_data + +* 返回 + + - result - dict 数据信息 + +* 示例 + + ```python + data = xt_trader.query_data(acc, 'C:\\Users\\Desktop\\test\\deal.csv', 'deal') + print(data) + #成功输出示例: + # account_id account_Type stock_code order_type ... + #0 2003695 2 688488.SH 23 ... + #1 2003695 2 000096.SZ 23 ... + #失败输出示例:{'error': {'errorMsg': 'can not find account info, accountID:2000449 accountType:2'}} + ``` + +### 约券相关接口 + +#### 券源行情查询 + +```python +smt_query_quoter(account) +``` + +* 释义 + - 券源行情查询 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - result - list 券源信息列表[quoter1, quoter2, ...] + - quoter - dict 券源信息,包含以下字段 + - success - bool + - error - str + - finType - str 金融品种 + - stockType - str 证券类型 + - date - int 期限天数 + - code - str 证券代码 + - codeName - str 证券代码名称 + - exchangeType - str 市场 + - fsmpOccupedRate - float 资券占用利率 + - fineRate - float 罚息利率 + - fsmpreendRate - float 资券提前归还利率 + - usedRate - float 资券使用利率 + - unUusedRate - float 资券占用未使用利率 + - initDate - int 交易日期 + - endDate - int 到期日期 + - enableSloAmountT0 - float T+0可融券数量 + - enableSloAmountT3 - float T+3可融券数量 + - srcGroupId - str 来源组编号 + - applyMode - str 资券申请方式,"1":库存券,"2":专项券 + - lowDate - int 最低期限天数 + +#### 库存券约券申请 + +```python +smt_negotiate_order_async(self, account, src_group_id, order_code, date, amount, apply_rate, dict_param={}) +``` + +* 释义 + + - 库存券约券申请的异步接口,异步接口如果正常返回了请求序号seq,会收到on_smt_appointment_async_response的反馈 + +* 参数 + + - account - StockAccount 资金账号 + - src_group_id - str 来源组编号 + - order_code - str 证券代码,如'600000.SH' + - date - int 期限天数 + - amount - int 委托数量 + - apply_rate - float 资券申请利率 + + 注:目前有如下参数通过一个可缺省的字典传递,键名与参数名称相同 + + - dict_param - dict 可缺省的字典参数 + - subFareRate - float 提前归还利率 + - fineRate - float 罚息利率 + +* 返回 + + - 返回请求序号seq,成功发起申请后的请求序号为大于0的正整数,如果为-1表示发起申请失败 + +* 示例 + +```python +account = StockAccount('1000008', 'CREDIT') +dict_param = {'subFareRate':0.1, 'fineRate':0.1} +#xt_trader为XtQuant API实例对象 +seq = xt_trader.smt_negotiate_order_async(account, '', '000001.SZ', 7, 100, 0.2, dict_param) +``` + + + +#### 约券合约查询 + +```python +smt_query_compact(account) +``` + +* 释义 + - 约券合约查询 +* 参数 + - account - StockAccount 资金账号 +* 返回 + - result - list 约券合约信息列表[compact1, compact2, ...] + - compact - dict 券源信息,包含以下字段 + - success - bool + - error - str + - createDate - int 创建日期 + - cashcompactId - str 头寸合约编号 + - oriCashcompactId - str 原头寸合约编号 + - applyId - str 资券申请编号 + - srcGroupId - str 来源组编号 + - comGroupId - str 资券组合编号 + - finType - str 金融品种 + - exchangeType - str 市场 + - code - str 证券代码 + - codeName - str 证券代码名称 + - date - int 期限天数 + - beginCompacAmount - float 期初合约数量 + - beginCompacBalance - float 期初合约金额 + - compacAmount - float 合约数量 + - compacBalance - float 合约金额 + - returnAmount - float 返还数量 + - returnBalance - float 返还金额 + - realBuyAmount - float 回报买入数量 + - fsmpOccupedRate - float 资券占用利率 + - compactInterest - float 合约利息金额 + - compactFineInterest - float 合约罚息金额 + - repaidInterest - float 已还利息 + - repaidFineInterest - float 归还罚息 + - fineRate - float 罚息利率 + - preendRate - float 资券提前归还利率 + - compactType - str 资券合约类型 + - postponeTimes - int 展期次数 + - compactStatus - str 资券合约状态,"0":未归还,"1":部分归还,"2":提前了结,"3":到期了结,"4":逾期了结,"5":逾期,"9":已作废 + - lastInterestDate - int 上次结息日期 + - interestEndDate - int 记息结束日期 + - validDate - int 有效日期 + - dateClear - int 清算日期 + - usedAmount - float 已使用数量 + - usedBalance - float 使用金额 + - usedRate - float 资券使用利率 + - unUusedRate - float 资券占用未使用利率 + - srcGroupName - str 来源组名称 + - repaidDate - int 归还日期 + - preOccupedInterest - float 当日实际应收利息 + - compactInterestx - float 合约总利息 + - enPostponeAmount - float 可展期数量 + - postponeStatus - str 合约展期状态,"0":未审核,"1":审核通过,"2":已撤销,"3":审核不通过 + - applyMode - str 资券申请方式,"1":库存券,"2":专项券 + +### 回调类 + +```python +class MyXtQuantTraderCallback(XtQuantTraderCallback): + def on_disconnected(self): + """ + 连接状态回调 + :return: + """ + print("connection lost") + def on_account_status(self, status): + """ + 账号状态信息推送 + :param response: XtAccountStatus 对象 + :return: + """ + print("on_account_status") + print(status.account_id, status.account_type, status.status) + def on_stock_asset(self, asset): + """ + 资金信息推送 + :param asset: XtAsset对象 + :return: + """ + print("on asset callback") + print(asset.account_id, asset.cash, asset.total_asset) + def on_stock_order(self, order): + """ + 委托信息推送 + :param order: XtOrder对象 + :return: + """ + print("on order callback:") + print(order.stock_code, order.order_status, order.order_sysid) + def on_stock_trade(self, trade): + """ + 成交信息推送 + :param trade: XtTrade对象 + :return: + """ + print("on trade callback") + print(trade.account_id, trade.stock_code, trade.order_id) + def on_stock_position(self, position): + """ + 持仓信息推送 + :param position: XtPosition对象 + :return: + """ + print("on position callback") + print(position.stock_code, position.volume) + def on_order_error(self, order_error): + """ + 下单失败信息推送 + :param order_error:XtOrderError 对象 + :return: + """ + print("on order_error callback") + print(order_error.order_id, order_error.error_id, order_error.error_msg) + def on_cancel_error(self, cancel_error): + """ + 撤单失败信息推送 + :param cancel_error: XtCancelError 对象 + :return: + """ + print("on cancel_error callback") + print(cancel_error.order_id, cancel_error.error_id, cancel_error.error_msg) + def on_order_stock_async_response(self, response): + """ + 异步下单回报推送 + :param response: XtOrderResponse 对象 + :return: + """ + print("on_order_stock_async_response") + print(response.account_id, response.order_id, response.seq) + def on_smt_appointment_async_response(self, response): + """ + :param response: XtAppointmentResponse 对象 + :return: + """ + print("on_smt_appointment_async_response") + print(response.account_id, response.order_sysid, response.error_id, response.error_msg, response.seq) +``` + +#### 连接状态回调 + +```python +on_disconnected() +``` +* 释义 + - 失去连接时推送信息 +* 参数 + - 无 +* 返回 + - 无 +* 备注 + - 无 +#### 账号状态信息推送 + +```python +on_account_status(data) +``` + +* 释义 + - 账号状态信息变动推送 +* 参数 + - data - XtAccountStatus 账号状态信息 +* 返回 + - 无 +* 备注 + - 无 + +#### 资产信息推送 + +```python +on_stock_asset(data) +``` +* 释义 + - 资产信息变动推送 +* 参数 + - data - XtAsset 资产信息 +* 返回 + - 无 +* 备注 + - 无 +#### 委托信息推送 + +```python +on_stock_order(data) +``` +* 释义 + - 委托信息变动推送 +* 参数 + - data - XtOrder 委托信息 +* 返回 + - 无 +* 备注 + - 无 +#### 成交信息推送 + +```python +on_stock_trade(data) +``` +* 释义 + - 成交信息变动推送 +* 参数 + - data - XtTrade 成交信息 +* 返回 + - 无 +* 备注 + - 无 +#### 持仓信息推送 + +```python +on_stock_position(data) +``` +* 释义 + - 持仓信息变动推送 +* 参数 + - data - XtPosition 持仓信息 +* 返回 + - 无 +* 备注 + - 无 +#### 下单失败信息推送 + +```python +on_order_error(data) +``` +* 释义 + - 下单失败信息推送 +* 参数 + - data - XtOrderError 下单失败信息 +* 返回 + - 无 +* 备注 + - 无 +#### 撤单失败信息推送 + +```python +on_cancel_error(data) +``` +* 释义 + - 撤单失败信息的推送 +* 参数 + - data - XtCancelError 撤单失败信息 +* 返回 + - 无 +* 备注 + - 无 +#### 异步下单回报推送 + +```python +on_order_stock_async_response(data) +``` + +* 释义 + - 异步下单回报推送 +* 参数 + - data - XtOrderResponse 异步下单委托反馈 +* 返回 + - 无 +* 备注 + - 无 + +#### 约券相关异步接口的回报推送 + +```python +on_smt_appointment_async_response(data) +``` + +* 释义 + - 异步约券相关接口回报推送 +* 参数 + - data - XtSmtAppointmentResponse 约券相关异步接口的反馈 +* 返回 + - 无 +* 备注 + - 无 \ No newline at end of file diff --git a/src/xtquant/libeay32.dll b/src/xtquant/libeay32.dll new file mode 100644 index 0000000..e27656c Binary files /dev/null and b/src/xtquant/libeay32.dll differ diff --git a/src/xtquant/log4cxx.dll b/src/xtquant/log4cxx.dll new file mode 100644 index 0000000..ee8e880 Binary files /dev/null and b/src/xtquant/log4cxx.dll differ diff --git a/src/xtquant/metatable/__init__.py b/src/xtquant/metatable/__init__.py new file mode 100644 index 0000000..f9ff711 --- /dev/null +++ b/src/xtquant/metatable/__init__.py @@ -0,0 +1,13 @@ +# coding:utf-8 + +from .meta_config import ( + get_metatable_config, + get_metatable_list, + get_metatable_info, + get_metatable_fields, +) + +from . import get_arrow + +get_tabular_data = get_arrow.get_tabular_fe_data +get_tabular_bson = get_arrow.get_tabular_fe_bson diff --git a/src/xtquant/metatable/get_arrow.py b/src/xtquant/metatable/get_arrow.py new file mode 100644 index 0000000..67c2a98 --- /dev/null +++ b/src/xtquant/metatable/get_arrow.py @@ -0,0 +1,373 @@ +from collections import OrderedDict + +from .meta_config import ( + __TABULAR_PERIODS__, + __META_FIELDS__, + __META_TABLES__, + __META_INFO__, + _init_metainfos, +) +from .get_bson import get_tabular_bson_head + +def _get_tabular_feather_single_ori( + codes: list, + table: str, + int_period: int, + start_timetag: int, + end_timetag: int, + count: int = -1, + **kwargs +): + from .. import xtdata + from pyarrow import feather as fe + import os + + CONSTFIELD_TIME = '_time' + CONSTFIELD_CODE = '_stock' + + file_path = os.path.join(xtdata.get_data_dir(), "EP", f"{table}_Xdat2", "data.fe") + if not os.path.exists(file_path): + return + + fe_table = fe.read_table(file_path) + + schema = fe_table.schema + fe_fields = [f.name for f in schema] + def _old_arrow_filter(): + from pyarrow import dataset as ds + nonlocal fe_table, fe_fields + + expressions = [] + if CONSTFIELD_TIME in fe_fields: + if start_timetag > 0: + expressions.append(ds.field(CONSTFIELD_TIME) >= start_timetag) + + if end_timetag > 0: + expressions.append(ds.field(CONSTFIELD_TIME) <= end_timetag) + + if CONSTFIELD_CODE in fe_fields and len(codes) > 0: + expressions.append(ds.field(CONSTFIELD_CODE).isin(codes)) + + if len(expressions) > 0: + expr = expressions[0] + for e in expressions[1:]: + expr = expr & e + return ds.dataset(fe_table).to_table(filter=expr) + else: + return fe_table + + + def _new_arrow_filter(): + from pyarrow import compute as pc + nonlocal fe_table, fe_fields + + expressions = [] + if CONSTFIELD_TIME in fe_fields: + if start_timetag > 0: + expressions.append(pc.field(CONSTFIELD_TIME) >= start_timetag) + if end_timetag > 0: + expressions.append(pc.field(CONSTFIELD_TIME) <= end_timetag) + + if CONSTFIELD_CODE in fe_fields and len(codes) > 0: + expressions.append(pc.field(CONSTFIELD_CODE).isin(codes)) + + if len(expressions) > 0: + expr = expressions[0] + for e in expressions[1:]: + expr = expr & e + return fe_table.filter(expr) + else: + return fe_table + + def do_filter(): + import pyarrow as pa + from distutils import version + nonlocal count + # python3.6 pyarrow-6.0.1 + # python3.7 pyarrow-12.0.1 + # python3.8~12 pyarrow-17.0.0 + paver = version.LooseVersion(pa.__version__) + if paver <= version.LooseVersion('9.0.0'): + _table = _old_arrow_filter() + else: + _table = _new_arrow_filter() + + if count > 0: + start_index = max(0, _table.num_rows - count) + _table = _table.slice(start_index, count) + + return _table + + return do_filter(), fe_fields + + +def _parse_fields(fields): + if not __META_FIELDS__: + _init_metainfos() + + tmp = OrderedDict() # { table: { show_fields: list(), fe_fields: list() } } + for field in fields: + if field.find('.') == -1: + table = field + + if table not in __META_TABLES__: + continue + + if table not in tmp: + tmp[table] = {'show': list(), 'fe': list()} + + metaid = __META_TABLES__[table] + for key, f in __META_INFO__[metaid]['fields'].items(): + if 'G' == key: + tmp[table]['fe'].append('_time') + elif 'S' == key: + tmp[table]['fe'].append('_stock') + else: + tmp[table]['fe'].append(f['modelName']) + + tmp[table]['show'].append(f['modelName']) + + else: + table = field.split('.')[0] + ifield = field.split('.')[1] + + if field not in __META_FIELDS__: + continue + + metaid, key = __META_FIELDS__[field] + + if table not in tmp: + tmp[table] = {'show': list(), 'fe': list()} + + if 'G' == key: + tmp[table]['fe'].append('_time') + elif 'S' == key: + tmp[table]['fe'].append('_stock') + else: + tmp[table]['fe'].append(ifield) + + tmp[table]['show'].append(ifield) + + return [(tb, sd['show'], sd['fe']) for tb, sd in tmp.items()] + +def _parse_keys(fields): + if not __META_FIELDS__: + _init_metainfos() + + tmp = OrderedDict() # { table: { show_keys: list(), fe_fields: list() } } + for field in fields: + if field.find('.') == -1: + table = field + + if table not in __META_TABLES__: + continue + + if table not in tmp: + tmp[table] = {'show': list(), 'fe': list()} + + metaid = __META_TABLES__[table] + for key, f in __META_INFO__[metaid]['fields'].items(): + if 'G' == key: + tmp[table]['fe'].append('_time') + elif 'S' == key: + tmp[table]['fe'].append('_stock') + else: + tmp[table]['fe'].append(f['modelName']) + + tmp[table]['show'].append(key) + + else: + table = field.split('.')[0] + ifield = field.split('.')[1] + + if field not in __META_FIELDS__: + continue + + metaid, key = __META_FIELDS__[field] + + if table not in tmp: + tmp[table] = {'show': list(), 'fe': list()} + + if 'G' == key: + tmp[table]['fe'].append('_time') + elif 'S' == key: + tmp[table]['fe'].append('_stock') + else: + tmp[table]['fe'].append(ifield) + + tmp[table]['show'].append(key) + + return [(tb, sd['show'], sd['fe']) for tb, sd in tmp.items()] + + +def get_tabular_fe_data( + codes: list, + fields: list, + period: str, + start_time: str, + end_time: str, + count: int = -1, + **kwargs +): + import pandas as pd + + time_format = None + if period in ('1m', '5m', '15m', '30m', '60m', '1h'): + time_format = '%Y-%m-%d %H:%M:%S' + elif period in ('1d', '1w', '1mon', '1q', '1hy', '1y'): + time_format = '%Y-%m-%d' + elif period == '': + time_format = '%Y-%m-%d %H:%M:%S.%f' + + if not time_format: + raise Exception('Unsupported period') + + int_period = __TABULAR_PERIODS__[period] + + if not isinstance(count, int) or count == 0: + count = -1 + + table_fields = _parse_fields(fields) + + def datetime_to_timetag(timelabel, format=''): + ''' + timelabel: str '20221231' '20221231235959' + format: str '%Y%m%d' '%Y%m%d%H%M%S' + return: int 1672502399000 + ''' + import datetime as dt + if not format: + format = '%Y%m%d' if len(timelabel) == 8 else '%Y%m%d%H%M%S' + try: + return dt.datetime.strptime(timelabel, format).timestamp() * 1000 + except: + return 0 + + start_timetag = datetime_to_timetag(start_time) + end_timetag = datetime_to_timetag(end_time) + + dfs = [] + ordered_fields = [] + for table, show_fields, fe_fields in table_fields: + fe_table, fe_table_fields = _get_tabular_feather_single_ori(codes, table, int_period, start_timetag, end_timetag, count) + if not fe_table: + continue + + ifields = list(set(fe_table_fields) & set(fe_fields)) + if not ifields: + continue + + fe_table = fe_table.select(ifields) + fe_df = fe_table.to_pandas() + # 补充请求的字段 + default_null_columns = [f for f in fe_fields if f not in fe_table_fields] + for c in default_null_columns: + fe_df.loc[:, c] = pd.NA + + rename_fields = {} + + for i in range(min(len(show_fields), len(fe_fields))): + show_field = f'{table}.{show_fields[i]}' + rename_fields[fe_fields[i]] = show_field + ordered_fields.append(show_field) + + fe_df.rename(columns=rename_fields, inplace=True) + dfs.append(fe_df) + + if not dfs: + return pd.DataFrame() + + result = pd.concat(dfs, ignore_index=True) + return result[ordered_fields] + + +def get_tabular_fe_bson( + codes: list, + fields: list, + period: str, + start_time: str, + end_time: str, + count: int = -1, + **kwargs +): + from .. import xtbson + time_format = None + if period in ('1m', '5m', '15m', '30m', '60m', '1h'): + time_format = '%Y-%m-%d %H:%M:%S' + elif period in ('1d', '1w', '1mon', '1q', '1hy', '1y'): + time_format = '%Y-%m-%d' + elif period == '': + time_format = '%Y-%m-%d %H:%M:%S.%f' + + if not time_format: + raise Exception('Unsupported period') + + int_period = __TABULAR_PERIODS__[period] + + if not isinstance(count, int) or count == 0: + count = -1 + + table_fields = _parse_keys(fields) + + def datetime_to_timetag(timelabel, format=''): + ''' + timelabel: str '20221231' '20221231235959' + format: str '%Y%m%d' '%Y%m%d%H%M%S' + return: int 1672502399000 + ''' + import datetime as dt + if not format: + format = '%Y%m%d' if len(timelabel) == 8 else '%Y%m%d%H%M%S' + try: + return dt.datetime.strptime(timelabel, format).timestamp() * 1000 + except: + return 0 + + start_timetag = datetime_to_timetag(start_time) + end_timetag = datetime_to_timetag(end_time) + + def _get_convert(): + import pyarrow as pa + from distutils import version + # python3.6 pyarrow-6.0.1 + # python3.7 pyarrow-12.0.1 + # python3.8~12 pyarrow-17.0.0 + def _old_arrow_convert(table): + return table.to_pandas().to_dict(orient='records') + + def _new_arrow_convert(table): + return table.to_pylist() + + paver = version.LooseVersion(pa.__version__) + if paver < version.LooseVersion('7.0.0'): + return _old_arrow_convert + else: + return _new_arrow_convert + + convert = _get_convert() + ret_bsons = [] + for table, show_fields, fe_fields in table_fields: + table_head = get_tabular_bson_head(fields) + ret_bsons.append(xtbson.encode(table_head)) + + fe_table, fe_table_fields = _get_tabular_feather_single_ori(codes, table, int_period, start_timetag, end_timetag, count) + + ifields = list() + new_columns = list() + for i in range(len(fe_fields)): + if fe_fields[i] in fe_table_fields: + ifields.append(fe_fields[i]) + new_columns.append(show_fields[i]) + + if not ifields: + continue + + fe_table = fe_table.select(ifields) + fe_table = fe_table.rename_columns(new_columns) # key_column + + fe_datas = convert(fe_table) + for data in fe_datas: + ret_bsons.append(xtbson.encode(data)) + + return ret_bsons + diff --git a/src/xtquant/metatable/get_bson.py b/src/xtquant/metatable/get_bson.py new file mode 100644 index 0000000..83d401a --- /dev/null +++ b/src/xtquant/metatable/get_bson.py @@ -0,0 +1,295 @@ +# coding:utf-8 +from collections import OrderedDict + +from .meta_config import ( + __TABULAR_PERIODS__, + __META_FIELDS__, + __META_TABLES__, + __META_INFO__, + _init_metainfos, + _meta_type, + _check_metatable_key, +) + + +def parse_request_from_fields(fields): + ''' + 根据字段解析metaid和field + ''' + table_field = OrderedDict() # {metaid: {key}} + key2field = OrderedDict() # {metaid: {key: field}} + columns = [] # table.field + if not __META_FIELDS__: + _init_metainfos() + + for field in fields: + if field.find('.') == -1: # 获取整个table的数据 + metaid = __META_TABLES__[field] + if metaid in __META_INFO__: + metainfo = __META_INFO__[metaid] + table = metainfo['modelName'] + meta_table_fields = metainfo.get('fields', {}) + if not meta_table_fields: + continue + + table_field[metaid] = {k: _meta_type(v['type']) for k, v in meta_table_fields.items()} + key2field[metaid] = { + key: f'{table}.{field_info["modelName"]}' for key, field_info in meta_table_fields.items() + } + columns.extend(key2field[metaid].values()) + + elif field in __META_FIELDS__: + metaid, key = __META_FIELDS__[field] + metainfo = __META_INFO__[metaid] + + if metaid not in table_field: + table_field[metaid] = {} + table_field[metaid][key] = _meta_type(metainfo['fields'][key]['type']) + + if metaid not in key2field: + key2field[metaid] = {} + key2field[metaid][key] = field + + columns.append(field) + + return table_field, key2field, columns + + + + +def _get_tabular_data_single_ori( + codes: list, + metaid: int, + keys: list, + int_period: int, + start_time: str, + end_time: str, + count: int = -1, + **kwargs +): + from .. import xtbson, xtdata + import os + CONSTKEY_CODE = 'S' + + ret_datas = [] + + scan_whole = False + scan_whole_filters = dict() # 额外对全市场数据的查询 { field : [codes] } + client = xtdata.get_client() + def read_single(): + nonlocal codes, metaid, int_period, scan_whole, scan_whole_filters, client, keys, ret_datas + if not codes: + scan_whole = True + return + + data_path_dict = xtdata._get_data_file_path(codes, (metaid, int_period)) + print(data_path_dict) + for code, file_path in data_path_dict.items(): + if not file_path: + continue + + if not os.path.exists(file_path): # 如果file_path不存在 + if code == 'XXXXXX.XX': # 不处理代码为XXXXXX.XX的情况 + continue + + if not _check_metatable_key(metaid, CONSTKEY_CODE): # 不处理不含S字段的表 + continue + + if CONSTKEY_CODE not in scan_whole_filters: + scan_whole_filters[CONSTKEY_CODE] = [] + scan_whole = True + scan_whole_filters[CONSTKEY_CODE].append(code) + continue + + bson_datas = client.read_local_data(file_path, start_time, end_time, count) + + for data in bson_datas: + idata = xtbson.decode(data) + ndata = {k: idata[k] for k in keys if k in idata} + ret_datas.append(ndata) + + def read_whole(): + nonlocal scan_whole, scan_whole_filters, metaid, int_period, client, keys, ret_datas + if not scan_whole: + return + + data_path_dict = xtdata._get_data_file_path(['XXXXXX.XX'], (metaid, int_period)) + if 'XXXXXX.XX' not in data_path_dict: + return + file_path = data_path_dict['XXXXXX.XX'] + if not os.path.exists(file_path): + return + + bson_datas = client.read_local_data(file_path, start_time, end_time, -1) + data_c = count + for data in bson_datas: + idata = xtbson.decode(data) + + valid = True + for k, v in scan_whole_filters.items(): + if idata.get(k, None) not in v: + valid = False + break + + if not valid: + continue + + ndata = {k: idata[k] for k in keys if k in idata} + ret_datas.append(ndata) + + data_c -= 1 + if data_c == 0: + break + + read_single() + read_whole() + + return ret_datas + + +def get_tabular_data( + codes: list, + fields: list, + period: str, + start_time: str, + end_time: str, + count: int = -1, + **kwargs +): + import pandas as pd + + time_format = None + if period in ('1m', '5m', '15m', '30m', '60m', '1h'): + time_format = '%Y-%m-%d %H:%M:%S' + elif period in ('1d', '1w', '1mon', '1q', '1hy', '1y'): + time_format = '%Y-%m-%d' + elif period == '': + time_format = '%Y-%m-%d %H:%M:%S.%f' + + if not time_format: + raise Exception('Unsupported period') + + int_period = __TABULAR_PERIODS__[period] + + if not isinstance(count, int) or count == 0: + count = -1 + + table_field, key2field, ori_columns = parse_request_from_fields(fields) + + dfs = [] + + # 额外查询 { metaid : [codes] } + for metaid, keys in table_field.items(): + datas = _get_tabular_data_single_ori(codes, metaid, list(keys.keys()), int_period, start_time, end_time, count) + df = pd.DataFrame(datas) + if df.empty: + continue + + # 补充请求的字段 + default_null_columns = [c for c in keys if c not in df.columns] + for c in default_null_columns: + df.loc[:, c] = keys[c] + + df.rename(columns=key2field[metaid], inplace=True) + dfs.append(df) + + if not dfs: + return pd.DataFrame() + + result = pd.concat(dfs, ignore_index=True) + result = result[ori_columns] + + return result + + +def get_tabular_bson_head( + fields: list +): + ''' + 根据字段解析表头 + ''' + ret = {'modelName': '', 'tableNameCn': '', 'fields': []} + + if not __META_FIELDS__: + _init_metainfos() + + for field in fields: + if field.find('.') == -1: # 获取整个table的数据 + metaid = __META_TABLES__[field] + if metaid not in __META_INFO__: + continue + + metainfo = __META_INFO__[metaid] + meta_table_fields = metainfo.get('fields', {}) + ret['metaId'] = metaid + ret['modelName'] = metainfo['modelName'] + ret['tableNameCn'] = metainfo['tableNameCn'] + if not meta_table_fields: + continue + + for k, v in meta_table_fields.items(): + ret['fields'].append({ + 'key': k, + 'fieldNameCn': v['fieldNameCn'], + 'modelName': v['modelName'], + 'type': v['type'], + 'unit': v['unit'], + }) + + + elif field in __META_FIELDS__: + metaid, key = __META_FIELDS__[field] + metainfo = __META_INFO__[metaid] + ret['metaId'] = metaid + ret['modelName'] = metainfo['modelName'] + ret['tableNameCn'] = metainfo['tableNameCn'] + field_metainfo = metainfo['fields'][key] + ret['fields'].append({ + 'key': key, + 'fieldNameCn': field_metainfo['fieldNameCn'], + 'modelName': field_metainfo['modelName'], + 'type': field_metainfo['type'], + 'unit': field_metainfo['unit'], + }) + + return ret + + +def get_tabular_bson( + codes: list, + fields: list, + period: str, + start_time: str, + end_time: str, + count: int = -1, + **kwargs +): + from .. import xtbson + time_format = None + if period in ('1m', '5m', '15m', '30m', '60m', '1h'): + time_format = '%Y-%m-%d %H:%M:%S' + elif period in ('1d', '1w', '1mon', '1q', '1hy', '1y'): + time_format = '%Y-%m-%d' + elif period == '': + time_format = '%Y-%m-%d %H:%M:%S.%f' + + if not time_format: + raise Exception('Unsupported period') + + int_period = __TABULAR_PERIODS__[period] + + if not isinstance(count, int) or count == 0: + count = -1 + + table_field, key2field, ori_columns = parse_request_from_fields(fields) + + ret_bsons = [] + for metaid, keysinfo in table_field.items(): + table_head = get_tabular_bson_head(fields) + ret_bsons.append(xtbson.encode(table_head)) + datas = _get_tabular_data_single_ori(codes, metaid, list(keysinfo.keys()), int_period, start_time, end_time, count) + for d in datas: + ret_bsons.append(xtbson.encode(d)) + + return ret_bsons + diff --git a/src/xtquant/metatable/meta_config.py b/src/xtquant/metatable/meta_config.py new file mode 100644 index 0000000..0ed877b --- /dev/null +++ b/src/xtquant/metatable/meta_config.py @@ -0,0 +1,198 @@ +#coding:utf8 + +__TABULAR_PERIODS__ = { + '': 0, + '1m': 60000, + '5m': 300000, + '15m': 900000, + '30m': 1800000, + '60m': 3600000, + '1h': 3600000, + '1d': 86400000, + '1w': 604800000, + '1mon': 2592000000, + '1q': 7776000000, + '1hy': 15552000000, + '1y': 31536000000, +} + +__META_INFO__ = {} +__META_FIELDS__ = {} +__META_TABLES__ = {} + +def download_metatable_data(): + ''' + 下载metatable信息 + 通常在客户端启动时自动获取,不需要手工调用 + ''' + from .. import xtdata + cl = xtdata.get_client() + + ret = xtdata._BSON_call_common( + cl.commonControl, 'downloadmetatabledata', {} + ) + return ret + +def _init_metainfos(): + ''' + 初始化metatable + ''' + import traceback + from .. import xtdata, xtbson + + global __META_INFO__ + global __META_FIELDS__ + global __META_TABLES__ + + cl = xtdata.get_client() + result = xtbson.BSON.decode(cl.commonControl('getmetatabledatas', xtbson.BSON.encode({}))) + all_metainfos = result['result'] + + for metainfo in all_metainfos: + if not isinstance(metainfo, dict): + continue + + try: + metaid = metainfo['I'] + __META_INFO__[metaid] = metainfo + + table_name = metainfo.get('modelName', metaid) + table_name_cn = metainfo.get('tableNameCn', '') + + __META_TABLES__[table_name] = metaid + __META_TABLES__[table_name_cn] = metaid + + metainfo_fields = metainfo.get('fields', {}) + # metainfo_fields.pop('G', None) # G公共时间字段特殊处理,跳过 + for key, info in metainfo_fields.items(): + field_name = info['modelName'] + __META_FIELDS__[f'{table_name}.{field_name}'] = (metaid, key) + except: + traceback.print_exc() + continue + return + +def _check_metatable_key(metaid, key): + metainfo = __META_INFO__.get(metaid, None) + if not metainfo: + return False + + fields = metainfo.get('fields', {}) + return key in fields + + +def get_metatable_list(): + ''' + 获取metatable列表 + + return: + { table_code1: table_name1, table_code2: table_name2, ... } + + table_code: str + 数据表代码 + table_name: str + 数据表名称 + ''' + if not __META_INFO__: + _init_metainfos() + + ret = {} + for metaid, metainfo in __META_INFO__.items(): + model_name = metainfo.get('modelName', f'{metaid}') + table_name_desc = metainfo.get('tableNameCn', '') + ret[model_name] = table_name_desc + + return ret + + +def get_metatable_config(table): + ''' + 获取metatable列表原始配置信息 + ''' + if not __META_INFO__: + _init_metainfos() + + if table not in __META_TABLES__: + print(f'[ERROR] Unknown table {table}') + + metaid = __META_TABLES__[table] + return __META_INFO__[metaid] + + +__META_TYPECONV__ = { + 'int': int(), + 'long': int(), + 'double': float(), + 'string': str(), + 'binary': bytes(), +} + + +def _meta_type(t): + try: + return __META_TYPECONV__[t] + except: + raise Exception(f'Unsupported type:{t}') + + +def get_metatable_info(table): + ''' + 获取metatable数据表信息 + + table: str + 数据表代码 table_code 或 数据表名称 table_name + return: dict + { + 'code': table_code + , 'name': table_name + , 'desc': desc + , 'fields': fields + } + + table_code: str + 数据表代码 + table_name: str + 数据表名称 + desc: str + 描述 + fields: dict + { 'code': field_code, 'name': field_name, 'type': field_type } + ''' + info = get_metatable_config(table) + + fields = info.get('fields', {}) + ret = { + 'code': info.get('modelName', ''), + 'name': info.get('tableNameCn', ''), + 'desc': info.get('desc', ''), + 'fields': [ + { + 'code': field_info.get('modelName', ''), + 'name': field_info.get('fieldNameCn', ''), + 'type': type(_meta_type(field_info.get('type', ''))), + } for key, field_info in fields.items() + ], + } + return ret + + +def get_metatable_fields(table): + ''' + 获取metatable数据表字段信息 + + table: str + 数据表代码 table_code 或 数据表名称 table_name + return: pd.DataFrame + columns = ['code', 'name', 'type'] + ''' + import pandas as pd + info = get_metatable_config(table) + + fields = info.get('fields', {}) + ret = pd.DataFrame([{ + 'code': field_info.get('modelName', ''), + 'name': field_info.get('fieldNameCn', ''), + 'type': type(_meta_type(field_info.get('type', ''))), + } for key, field_info in fields.items()]) + return ret + diff --git a/src/xtquant/msvcp140.dll b/src/xtquant/msvcp140.dll new file mode 100644 index 0000000..9ec811f Binary files /dev/null and b/src/xtquant/msvcp140.dll differ diff --git a/src/xtquant/qmttools/__init__.py b/src/xtquant/qmttools/__init__.py new file mode 100644 index 0000000..75688e4 --- /dev/null +++ b/src/xtquant/qmttools/__init__.py @@ -0,0 +1,6 @@ + +from . import functions +from . import contextinfo +from . import stgframe + +from .stgentry import run_file as run_strategy_file diff --git a/src/xtquant/qmttools/contextinfo.py b/src/xtquant/qmttools/contextinfo.py new file mode 100644 index 0000000..6634192 --- /dev/null +++ b/src/xtquant/qmttools/contextinfo.py @@ -0,0 +1,353 @@ +#coding:utf-8 + +from . import functions as _FUNCS_ + +class ContextInfo: + def __init__(this): + #base + this.request_id = '' + this.quote_mode = '' #'realtime' 'history' 'all' + this.trade_mode = '' #'simulation' 'trading' 'backtest' + this.title = '' + this.user_script = '' + + #quote + this.stock_code = '' + this.stockcode = '' + this.market = '' + this.period = '' + this.start_time = '' + this.end_time = '' + this.dividend_type = '' + this.start_time_num = None + this.end_time_num = None + + #bar frame + this.timelist = [] + this.barpos = -1 + this.lastrunbarpos = -1 + this.result = {} + this.push_result = {} + + #backtest + this.asset = 1000000.0 # 初始资金 + this.margin_ratio = 0.05 # 保证金比例 + this.slippage_type = 2 # 滑点类型 + this.slippage = 0.0 # 滑点值 + this.max_vol_rate = 0.0 # 最大成交比例 + this.comsisson_type = 0 # 手续费类型 + this.open_tax = 0.0 # 买入印花税 + this.close_tax = 0.0 # 卖出印花税 + this.min_commission = 0.0 # 最低佣金 + this.open_commission = 0.0 # 买入佣金 + this.close_commission = 0.0 # 平昨佣金 + this.close_today_commission = 0.0 # 平今佣金 + this.benchmark = '000300.SH' # 业绩基准 + + this.do_back_test = None + + #reserved + this.refresh_rate = None + this.fund_name = None + this.link_fund_name = None + this.data_info_level = None + this.time_tick_size = None + this.subscribe_once = False + return + + @property + def start(this): + return this.start_time + + @start.setter + def start(this, value): + this.start_time = value + + @property + def end(this): + return this.end_time + + @end.setter + def end(this, value): + this.end_time = value + + @property + def capital(this): + return this.asset + + @capital.setter + def capital(this, value): + this.asset = value + + ### qmt strategy frame ### + + def init(this): + return + + def after_init(this): + return + + def handlebar(this): + return + + def on_backtest_finished(this): + return + + def stop(this): + return + + def account_callback(this, account_info): + return + + def order_callback(this, order_info): + return + + def deal_callback(this, deal_info): + return + + def position_callback(this, position_info): + return + + def orderError_callback(this, passorder_info, msg): + return + + ### qmt functions - bar ### + + def is_last_bar(this): + return this.barpos >= len(this.timelist) - 1 + + def is_new_bar(this): + return this.barpos > this.lastbarpos + + def get_bar_timetag(this, barpos = None): + try: + return this.timelist[barpos] if barpos is not None else this.timelist[this.barpos] + except Exception as e: + return None + + ### qmt functions - graph ### + + def paint(this, name, value, index = -1, drawstyle = 0, color = '', limit = ''): + vp = {str(this.get_bar_timetag()): value} + + if name not in this.result: + this.result[name] = {} + this.result[name].update(vp) + + if name not in this.push_result: + this.push_result[name] = {} + this.push_result[name].update(vp) + return + + ### qmt functions - quote ### + + def subscribe_quote(this, stock_code = '', period = '', dividend_type = '', result_type = '', callback = None): + if not stock_code: + stock_code = this.stock_code + if not period or period == 'follow': + period = this.period + if not dividend_type or dividend_type == 'follow': + dividend_type = this.dividend_type + return _FUNCS_.subscribe_quote(stock_code, period, dividend_type, 0, result_type, callback) + + def subscribe_whole_quote(this, code_list, callback = None): + return _FUNCS_.subscribe_whole_quote(code_list, callback) + + def unsubscribe_quote(this, subscribe_id): + return _FUNCS_.unsubscribe_quote(subscribe_id) + + def get_market_data( + this, fields = [], stock_code = [], start_time = '', end_time = '' + , skip_paused = True, period = '', dividend_type = '', count = -1 + ): + if not stock_code: + stock_code = [this.stock_code] + if not period or period == 'follow': + period = this.period + if not dividend_type or dividend_type == 'follow': + dividend_type = this.dividend_type + if period != 'tick' and count == -1 and len(fields) == 1: + if not end_time or end_time == 'follow': + if this.barpos >= 0: + end_time = _FUNCS_.timetag_to_datetime(this.get_bar_timetag(this.barpos)) + count = -2 + if period == 'tick' and count == -1 and len(fields) == 1 and start_time == '' and end_time == '': + count = -2 + + return _FUNCS_.get_market_data( + fields, stock_code, start_time, end_time + , skip_paused, period, dividend_type, count + ) + + def get_market_data_ex( + this, fields = [], stock_code = [], period = '' + , start_time = '', end_time = '', count = -1 + , dividend_type = '', fill_data = True, subscribe = True + ): + if not stock_code: + stock_code = [this.stock_code] + if not period or period == 'follow': + period = this.period + if not dividend_type or dividend_type == 'follow': + dividend_type = this.dividend_type + + if not this.subscribe_once and subscribe: + this.subscribe_once = True + if period != "tick": + for stk in stock_code: + _FUNCS_.subscribe_quote(stk, period, dividend_type, -1) + else: + for stk in stock_code: + this.subscribe_whole_quote(stk) + + return _FUNCS_.get_market_data_ex( + fields, stock_code, period + , start_time, end_time, count + , dividend_type, fill_data, subscribe + ) + + def get_full_tick(this, stock_code = []): + if not stock_code: + stock_code = [this.stock_code] + return _FUNCS_.get_full_tick(stock_code) + + def get_divid_factors(this, stock_code = '', date = None): + if not stock_code: + stock_code = this.stock_code + return _FUNCS_.get_divid_factors(stock_code, date) + + ### qmt functions - finance ### + + def get_financial_data(this, field_list, stock_list, start_date, end_date, report_type = 'announce_time'): + raise 'not implemented, use get_raw_financial_data instead' + return + + def get_raw_financial_data(this, field_list, stock_list, start_date, end_date, report_type = 'announce_time'): + return _FUNCS_.get_raw_financial_data(field_list, stock_list, start_date, end_date, report_type) + + ### qmt functions - option ### + + def get_option_detail_data(this, optioncode): + return _FUNCS_.get_option_detail_data(optioncode) + + def get_option_undl_data(this, undl_code_ref): + return _FUNCS_.get_option_undl_data(undl_code_ref) + + def get_option_list(this, undl_code,dedate,opttype = "",isavailavle = False): + return _FUNCS_.get_option_list(undl_code, dedate, opttype, isavailavle) + + def get_option_iv(this, opt_code): + return _FUNCS_.get_opt_iv(opt_code, this.request_id) + + def bsm_price(this, optType, targetPrice, strikePrice, riskFree, sigma, days, dividend = 0): + optionType = "" + if(optType.upper() == "C"): + optionType = "CALL" + if(optType.upper() == "P"): + optionType = "PUT" + if(type(targetPrice) == list): + result = [] + for price in targetPrice: + bsmPrice= _FUNCS_.calc_bsm_price(optionType,strikePrice,float(price),riskFree,sigma,days,dividend, this.request_id) + bsmPrice = round(bsmPrice,4) + result.append(bsmPrice) + return result + else: + bsmPrice = _FUNCS_.calc_bsm_price(optionType,strikePrice,targetPrice,riskFree,sigma,days,dividend, this.request_id) + result = round(bsmPrice,4) + return result + + def bsm_iv(this, optType, targetPrice, strikePrice, optionPrice, riskFree, days, dividend = 0): + if(optType.upper() == "C"): + optionType = "CALL" + if(optType.upper() == "P"): + optionType = "PUT" + result = _FUNCS_.calc_bsm_iv(optionType, strikePrice, targetPrice, optionPrice, riskFree, days, dividend, this.request_id) + result = round(result,4) + return result + + ### qmt functions - static ### + + def get_instrument_detail(this, stock_code = '', iscomplete = False): + if not stock_code: + stock_code = this.stock_code + return _FUNCS_.get_instrument_detail(stock_code, iscomplete) + + get_instrumentdetail = get_instrument_detail # compat + + def get_trading_dates(this, stock_code, start_date, end_date, count, period = '1d'): + return _FUNCS_.get_trading_dates(stock_code, start_date, end_date, count, period) + + def get_stock_list_in_sector(this, sector_name): + return _FUNCS_.get_stock_list_in_sector(sector_name) + + def passorder( + this + , opType, orderType, accountid + , orderCode, prType, modelprice, volume + , strategyName, quickTrade, userOrderId + ): + return _FUNCS_._passorder_impl( + opType, orderType, accountid + , orderCode, prType, modelprice, volume + , strategyName, quickTrade, userOrderId + , this.barpos, this.get_bar_timetag() + , "passorder", "" + , this.request_id + ) + + def set_auto_trade_callback(this, enable): + return _FUNCS_._set_auto_trade_callback_impl(enable, this.request_id) + + def set_account(this, accountid): + return _FUNCS_.set_account(accountid, this.request_id) + + def get_his_st_data(this, stock_code): + return _FUNCS_.get_his_st_data(stock_code) + + ### private ### + + def trade_callback(this, type, result, error): + class DetailData(object): + def __init__(self, _obj): + if _obj: + self.__dict__.update(_obj) + + if type == 'accountcallback': + this.account_callback(DetailData(result)) + elif type == 'ordercallback': + this.order_callback(DetailData(result)) + elif type == 'dealcallback': + this.deal_callback(DetailData(result)) + elif type == 'positioncallback': + this.position_callback(DetailData(result)) + elif type == 'ordererrorcallback': + this.orderError_callback(DetailData(result.get('passorderArg')), result.get('strMsg')) + + return + + def register_callback(this, reqid): + _FUNCS_.register_external_resp_callback(reqid, this.trade_callback) + return + + def get_callback_cache(this, type): + return _FUNCS_._get_callback_cache_impl(type, this.request_id) + + def get_ipo_info(this, start_time = '', end_time = ''): + return _FUNCS_.get_ipo_info(start_time, end_time) + + def get_backtest_index(this, path): + _FUNCS_.get_backtest_index(this.request_id, path) + + def get_group_result(this, path, fields): + _FUNCS_.get_group_result(this.request_id, path, fields) + + def is_suspended_stock(this, stock_code, type): + if this.barpos > len(this.timelist): + return False + + if type == 1 or len(this.timelist) == 0: + inst = this.get_instrument_detail(stock_code) + return inst.get('InstrumentStatus', 0) >= 1 + + return _FUNCS_.is_suspended_stock(stock_code, this.period, this.timelist[this.barpos]) diff --git a/src/xtquant/qmttools/functions.py b/src/xtquant/qmttools/functions.py new file mode 100644 index 0000000..c43a322 --- /dev/null +++ b/src/xtquant/qmttools/functions.py @@ -0,0 +1,537 @@ +#coding:utf-8 + +import datetime as _DT_ + +from xtquant import xtdata +from xtquant import xtbson as _BSON_ + +def datetime_to_timetag(timelabel, format = ''): + ''' + timelabel: str '20221231' '20221231235959' + format: str '%Y%m%d' '%Y%m%d%H%M%S' + return: int 1672502399000 + ''' + if not format: + format = '%Y%m%d' if len(timelabel) == 8 else '%Y%m%d%H%M%S' + return _DT_.datetime.strptime(timelabel, format).timestamp() * 1000 + +def timetag_to_datetime(timetag, format = ''): + ''' + timetag: int 1672502399000 + format: str '%Y%m%d' '%Y%m%d%H%M%S' + return: str '20221231' '20221231235959' + ''' + if not format: + format = '%Y%m%d' if timetag % 86400000 == 57600000 else '%Y%m%d%H%M%S' + return _DT_.datetime.fromtimestamp(timetag / 1000).strftime(format) + +def fetch_ContextInfo(): + import sys + frame = sys._getframe() + while (frame): + loc = list(frame.f_locals.values()) + for val in loc: + if type(val).__name__ == "ContextInfo": + return val + frame = frame.f_back + return None + +def subscribe_quote(stock_code, period, dividend_type, count = 0, result_type = '', callback = None): + return xtdata.subscribe_quote(stock_code, period, '', '', count, callback) + +def subscribe_whole_quote(code_list, callback = None): + return xtdata.subscribe_whole_quote(code_list, callback) + +def unsubscribe_quote(subscribe_id): + return xtdata.unsubscribe_quote(subscribe_id) + +def get_market_data( + fields = [], stock_code = [], start_time = '', end_time = '' + , skip_paused = True, period = '', dividend_type = '', count = -1 +): + res = {} + if period == 'tick': + refixed = False + if count == -2: + refixed = True + count = 1 + if 'quoter' not in fields: + return xtdata.get_market_data_ori( + field_list=fields, stock_list=stock_code, period=period + , start_time=start_time, end_time=end_time, count=count + , dividend_type=dividend_type, fill_data=skip_paused + ) + + fields = [] + data = xtdata.get_market_data_ori( + field_list=fields, stock_list=stock_code, period=period + , start_time=start_time, end_time=end_time, count=count + , dividend_type=dividend_type, fill_data=skip_paused + ) + fields = ['quoter'] + + import pandas as pd + + stime_fmt = '%Y%m%d' if period == '1d' else '%Y%m%d%H%M%S' + for stock in data: + pd_data = pd.DataFrame(data[stock]) + pd_data['stime'] = [timetag_to_datetime(t, stime_fmt) for t in pd_data['time']] + pd_data.index = pd.to_datetime((pd_data['time'] + 28800000) * 1000000) + ans = {} + for j, timetag in enumerate(pd_data['time']): + d_map = {} + for key in pd_data: + d_map[key] = pd_data[key][j] + ans[str(pd_data.index[j])] = {} + ans[str(pd_data.index[j])]['quoter'] = d_map + res[stock] = ans + + oriData = res + # if not pd_data.empty: + # if count > 0: + # return list(pd_data.T.to_dict().values()) + # return pd_data.iloc[-1].to_dict() + # return {} + if refixed: + count = -2 + else: + refixed = False + if count == -2: + refixed = True + count = 1 + index, data = xtdata.get_market_data_ori( + field_list=fields, stock_list=stock_code, period=period + , start_time=start_time, end_time=end_time, count=count + , dividend_type=dividend_type, fill_data=skip_paused + ) + if refixed: + end_time = '' + count = -1 + for i, stock in enumerate(index[0]): + ans = {} + for j, timetag in enumerate(index[1]): + d_map = {} + for key in data: + d_map[key] = data[key][i][j] + ans[timetag] = d_map + res[stock] = ans + oriData = res + + resultDict = {} + for code in oriData: + for timenode in oriData[code]: + values = [] + for field in fields: + values.append(oriData[code][timenode][field]) + key = code + timenode + resultDict[key] = values + + if len(fields) == 1 and len(stock_code) <= 1 and ( + (start_time == '' and end_time == '') or start_time == end_time) and (count == -1 or count == -2): + # if resultDict: + # keys = list(resultDict.keys()) + # if resultDict[keys[-1]]: + # return resultDict[keys[-1]] + for key in resultDict: + return resultDict[key][0] + return -1 + import numpy as np + import pandas as pd + if len(stock_code) <= 1 and start_time == '' and end_time == '' and (count == -1 or count == -2): + for key in resultDict: + result = pd.Series(resultDict[key], index=fields) + return result + if len(stock_code) > 1 and start_time == '' and end_time == '' and (count == -1 or count == -2): + values = [] + for code in stock_code: + if code in oriData: + if not oriData[code]: + values.append([np.nan]) + for timenode in oriData[code]: + key = code + timenode + values.append(resultDict[key]) + else: + values.append([np.nan]) + result = pd.DataFrame(values, index=stock_code, columns=fields) + return result + if len(stock_code) <= 1 and ((start_time != '' or end_time != '') or count >= 0): + values = [] + times = [] + for code in oriData: + for timenode in oriData[code]: + key = code + timenode + times.append(timenode) + values.append(resultDict[key]) + result = pd.DataFrame(values, index=times, columns=fields) + return result + if len(stock_code) > 1 and ((start_time != '' or end_time != '') or count >= 0): + values = {} + for code in stock_code: + times = [] + value = [] + if code in oriData: + for timenode in oriData[code]: + key = code + timenode + times.append(timenode) + value.append(resultDict[key]) + values[code] = pd.DataFrame(value, index=times, columns=fields) + try: + result = pd.Panel(values) + return result + except: + return oriData + return + +def get_market_data_ex( + fields = [], stock_code = [], period = '' + , start_time = '', end_time = '', count = -1 + , dividend_type = '', fill_data = True, subscribe = True +): + res = xtdata.get_market_data_ex( + field_list = fields, stock_list = stock_code, period = period + , start_time = start_time, end_time = end_time, count = count + , dividend_type = dividend_type, fill_data = fill_data + ) + for stock in res: + res[stock].index.name = "stime" + return res + +def get_full_tick(stock_code): + return xtdata.get_full_tick(stock_code) + +def get_divid_factors(stock_code, date = None): + client = xtdata.get_client() + if date: + data = client.get_divid_factors(stock_code, date, date) + else: + data = client.get_divid_factors(stock_code, '19700101', '20380119') + + res = {} + for value in data.values(): + res[value['time']] = list(value.values())[1:] + return res + +def download_history_data(stockcode, period, startTime, endTime): + return xtdata.download_history_data(stockcode, period, startTime, endTime) + +def get_raw_financial_data(field_list, stock_list, start_date, end_date, report_type = 'announce_time'): + client = xtdata.get_client() + data = client.get_financial_data(stock_list, field_list, start_date, end_date, report_type) + + import time + res = {} + for stock in data: + stock_data = data[stock] + res[stock] = {} + + for field in field_list: + fs = field.split('.') + table_data = stock_data.get(fs[0]) + + if not table_data: + continue + + ans = {} + for row_data in table_data: + if row_data.get(report_type, None) == None: + continue + date = time.strftime('%Y%m%d', time.localtime(row_data[report_type] / 1000)) + if start_date == '' or start_date <= date: + if end_date == '' or date <= end_date: + ans[int(row_data[report_type])] = row_data[fs[1]] + res[stock][field] = ans + return res + +#def download_financial_data(stock_list, table_list): #暂不提供 +# return xtdata.download_financial_data(stock_list, table_list) + +def get_instrument_detail(stock_code, iscomplete = False): + return xtdata.get_instrument_detail(stock_code, iscomplete) + +#def get_instrument_type(stock_code): #暂不提供 +# return xtdata.get_instrument_type(stock_code) + +def get_trading_dates(stock_code, start_date, end_date, count = -1, period = '1d'): + if period != '1d': + return [] + market = stock_code.split('.')[0] + trade_dates = xtdata.get_trading_dates(market, start_date, end_date) + if count == -1: + return trade_dates + if count > 0: + return trade_dates[-count:] + return [] + +def get_stock_list_in_sector(sector_name): + return xtdata.get_stock_list_in_sector(sector_name) + +def download_sector_data(): + return xtdata.download_sector_data() + +download_sector_weight = download_sector_data #compat + +def get_his_st_data(stock_code): + return xtdata.get_his_st_data(stock_code) + + +def _passorder_impl( + optype, ordertype, accountid + , ordercode, prtype, modelprice, volume + , strategyName, quickTrade, userOrderId + , barpos, bartime, func, algoName + , requestid +): + data = {} + + data['optype'] = optype + data['ordertype'] = ordertype + data['accountid'] = accountid + data['ordercode'] = ordercode + data['prtype'] = prtype + data['modelprice'] = modelprice + data['volume'] = volume + data['strategyname'] = strategyName + data['remark'] = userOrderId + data['quicktrade'] = quickTrade + data['func'] = func + data['algoname'] = algoName + data['barpos'] = barpos + data['bartime'] = bartime + + client = xtdata.get_client() + client.callFormula(requestid, 'passorder', _BSON_.BSON.encode(data)) + return + + +def passorder( + opType, orderType, accountid + , orderCode, prType, modelprice, volume + , strategyName, quickTrade, userOrderId + , C +): + return C.passorder( + opType, orderType, accountid + , orderCode, prType, modelprice, volume + , strategyName, quickTrade, userOrderId + ) + + +def get_trade_detail_data(accountid, accounttype, datatype, strategyname = ''): + data = {} + + C = fetch_ContextInfo() + if C is None: + raise Exception("contextinfo could not be found in the stack") + request_id = C.request_id + + data['accountid'] = accountid + data['accounttype'] = accounttype + data['datatype'] = datatype + data['strategyname'] = strategyname + + client = xtdata.get_client() + result_bson = client.callFormula(request_id, 'gettradedetail', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + + class DetailData(object): + def __init__(self, _obj): + if _obj: + self.__dict__.update(_obj) + + out = [] + if not result: + return out + + for item in result.get('result'): + out.append(DetailData(item)) + return out + +def register_external_resp_callback(reqid, callback): + client = xtdata.get_client() + + status = [False, 0, 1, ''] + + def on_callback(type, data, error): + try: + result = _BSON_.BSON.decode(data) + callback(type, result, error) + return True + except: + status[0] = True + status[3] = 'exception' + return True + + client.register_external_resp_callback(reqid, on_callback) + +def _set_auto_trade_callback_impl(enable, requestid): + data = {} + data['enable'] = enable + + client = xtdata.get_client() + client.callFormula(requestid, 'setautotradecallback', _BSON_.BSON.encode(data)) + return + +def set_auto_trade_callback(C,enable): + return C.set_auto_trade_callback(enable) + +def set_account(accountid, requestid): + data = {} + data['accountid'] = accountid + + client = xtdata.get_client() + client.callFormula(requestid, 'setaccount', _BSON_.BSON.encode(data)) + return + +def _get_callback_cache_impl(type, requestid): + data = {} + + data['type'] = type + + client = xtdata.get_client() + result_bson = client.callFormula(requestid, 'getcallbackcache', _BSON_.BSON.encode(data)) + return _BSON_.BSON.decode(result_bson) + +def get_account_callback_cache(data, C): + data = C.get_callback_cache("account").get('') + return + +def get_order_callback_cache(data, C): + data = C.get_callback_cache("order") + return + +def get_deal_callback_cache(data, C): + data = C.get_callback_cache("deal") + return + +def get_position_callback_cache(data, C): + data = C.get_callback_cache("position") + return + +def get_ordererror_callback_cache(data, C): + data = C.get_callback_cache("ordererror") + return + +def get_option_detail_data(stock_code): + return xtdata.get_option_detail_data(stock_code) + +def get_option_undl_data(undl_code_ref): + return xtdata.get_option_undl_data(undl_code_ref) + +def get_option_list(undl_code,dedate,opttype = "",isavailavle = False): + return xtdata.get_option_list(undl_code, dedate, opttype, isavailavle) + +def get_opt_iv(opt_code, requestid): + data = {} + data['code'] = opt_code + + client = xtdata.get_client() + result_bson = client.callFormula(requestid, 'getoptiv', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + + out = result.get('result', 0) + return out + +def calc_bsm_price(optionType,strikePrice, targetPrice, riskFree, sigma, days, dividend, requestid): + data = {} + data['optiontype'] = optionType + data['strikeprice'] = strikePrice + data['targetprice'] = targetPrice + data['riskfree'] = riskFree + data['sigma'] = sigma + data['days'] = days + data['dividend'] = dividend + + client = xtdata.get_client() + result_bson = client.callFormula(requestid, 'calcbsmprice', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + + out = result.get('result', 0) + return out + +def calc_bsm_iv(optionType, strikePrice, targetPrice, optionPrice, riskFree, days, dividend, requestid): + data = {} + data['optiontype'] = optionType + data['strikeprice'] = strikePrice + data['targetprice'] = targetPrice + data['optionprice'] = optionPrice + data['riskfree'] = riskFree + data['days'] = days + data['dividend'] = dividend + + client = xtdata.get_client() + result_bson = client.callFormula(requestid, 'calcbsmiv', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + + out = result.get('result', 0) + return out + +def get_ipo_info(start_time, end_time): + return xtdata.get_ipo_info(start_time, end_time) + +def get_backtest_index(requestid, path): + import os + path = os.path.abspath(path) + if not os.path.exists(path): + os.makedirs(path, exist_ok = True) + + data = {'savePath': path} + client = xtdata.get_client() + bresult = client.callFormula(requestid, 'backtestresult', _BSON_.BSON.encode(data)) + return _BSON_.BSON.decode(bresult) + +def get_group_result(requestid, path, fields): + import os + path = os.path.abspath(path) + if not os.path.exists(path): + os.makedirs(path, exist_ok = True) + + data = {'savePath': path, 'fields': fields} + client = xtdata.get_client() + bresult = client.callFormula(requestid, 'groupresult', _BSON_.BSON.encode(data)) + return _BSON_.BSON.decode(bresult) + +def subscribe_formula(formula_name, stock_code, period, start_time = "", end_time = "", count=-1, dividend_type = "none", extend_params = {}, callback = None): + return xtdata.subscribe_formula(formula_name, stock_code, period, start_time, end_time, count, dividend_type, extend_params, callback) + +def call_formula_batch(formula_names, stock_codes, period, start_time = "", end_time = "", count=-1, dividend_type = "none", extend_params = []): + import copy + params = [] + for name in formula_names: + for stock in stock_codes: + param = { + 'formulaname': name, 'stockcode': stock, 'period': period + , 'starttime': start_time, 'endtime': end_time, 'count': count + , 'dividendtype': dividend_type, 'extendparam': {} + , 'create': True, 'datademand': 0 + } + + if extend_params: + for extend in extend_params: + param['extendparam'] = extend + params.append(copy.deepcopy(param)) + else: + params.append(param) + + client = xtdata.get_client() + result = client.commonControl( + 'callformulabatch' + , _BSON_.BSON.encode( + {"params": params} + ) + ) + result = _BSON_.BSON.decode(result) + return result.get("result", {}) + +def is_suspended_stock(stock_code, period, timetag): + client = xtdata.get_client() + + result = client.commonControl( + 'issuspendedstock' + , _BSON_.BSON.encode({ + "stockcode": stock_code + , "period": period + , "timetag": timetag + }) + ) + result = _BSON_.BSON.decode(result) + return result.get('result', True) diff --git a/src/xtquant/qmttools/stgentry.py b/src/xtquant/qmttools/stgentry.py new file mode 100644 index 0000000..7a7501d --- /dev/null +++ b/src/xtquant/qmttools/stgentry.py @@ -0,0 +1,73 @@ +#coding:utf-8 + +from .functions import * + + +def run_file(user_script, param = {}): + import os, sys, time, types + from .contextinfo import ContextInfo + from .stgframe import StrategyLoader + + pypath = param.get('pythonpath') + if pypath: + lib_search = [os.path.abspath(p) for p in pypath.split(';')] + sys.path = lib_search + [p for p in sys.path if p not in lib_search] + + user_module = compile(open(user_script, 'rb').read(), user_script, 'exec', optimize = 2) + #print({'user_module': user_module}) + + try: + pywentrance = param.get('pywentrance', '') + user_variable = compile(open(os.path.join(pywentrance, "..", "user_config.py"), "rb").read(), + "user_config.py", 'exec', optimize=2) + exec(user_variable, globals()) + except Exception as e: + pass + + exec(user_module, globals()) + + _C = ContextInfo() + _C._param = param + _C.user_script = user_script + + def try_set_func(C, func_name): + func = globals().get(func_name) + if func: + C.__setattr__(func_name, types.MethodType(func, C)) + return + + try_set_func(_C, 'init') + try_set_func(_C, 'after_init') + try_set_func(_C, 'handlebar') + try_set_func(_C, 'on_backtest_finished') + try_set_func(_C, 'stop') + + try_set_func(_C, 'account_callback') + try_set_func(_C, 'order_callback') + try_set_func(_C, 'deal_callback') + try_set_func(_C, 'position_callback') + try_set_func(_C, 'orderError_callback') + + loader = StrategyLoader() + + loader.C = _C + + loader.init() + loader.start() + loader.run() + loader.stop() + loader.shutdown() + + mode = _C.trade_mode + if mode == 'backtest': + from .stgframe import BackTestResult + return BackTestResult(_C.request_id) + + if mode in ['simulation', 'trading']: + while True: + time.sleep(2) + from .stgframe import Result + return Result(_C.request_id) + + return None + diff --git a/src/xtquant/qmttools/stgframe.py b/src/xtquant/qmttools/stgframe.py new file mode 100644 index 0000000..d46e1b7 --- /dev/null +++ b/src/xtquant/qmttools/stgframe.py @@ -0,0 +1,312 @@ +#coding:utf-8 + +from xtquant import xtdata +from xtquant import xtbson as _BSON_ + + +class StrategyLoader: + def __init__(this): + this.C = None + this.main_quote_subid = 0 + return + + def init(this): + import os, uuid + from xtquant import xtdata_config + + C = this.C + + C.guid = C._param.get('guid', str(uuid.uuid4())) + C.request_id = C._param.get('requestid', '') + "_" + C.guid + C.quote_mode = C._param.get('quote_mode', 'history') #'realtime' 'history' 'all' + C.trade_mode = C._param.get('trade_mode', 'backtest') #'simulation' 'trading' 'backtest' + C.do_back_test = 1 if C.trade_mode == 'backtest' else 0 + + C.title = C._param.get('title', '') + if not C.title: + C.title = os.path.basename(os.path.abspath(C.user_script).replace('.py', '')) + + C.stock_code = C._param.get('stock_code', '') + C.period = C._param.get('period', '') + C.start_time = C._param.get('start_time', '') + C.end_time = C._param.get('end_time', '') + C.start_time_str = '' + C.end_time_str = '' + if type(C.period) == int: + C.period = { + 0 :'tick' + , 60000 :'1m' + , 180000 :'3m' + , 300000 :'5m' + , 600000 :'10m' + , 900000 :'15m' + , 1800000 :'30m' + , 3600000 :'1h' + , 86400000 :'1d' + , 604800000 :'1w' + , 2592000000 :'1mon' + , 7776000000 :'1q' + , 15552000000 :'1hy' + , 31536000000 :'1y' + }.get(C.period, '') + C.dividend_type = C._param.get('dividend_type', 'none') + + backtest = C._param.get('backtest', {}) + if backtest: + C.asset = backtest.get('asset', 1000000.0) + C.margin_ratio = backtest.get('margin_ratio', 0.05) + C.slippage_type = backtest.get('slippage_type', 2) + C.slippage = backtest.get('slippage', 0.0) + C.max_vol_rate = backtest.get('max_vol_rate', 0.0) + C.comsisson_type = backtest.get('comsisson_type', 0) + C.open_tax = backtest.get('open_tax', 0.0) + C.close_tax = backtest.get('close_tax', 0.0) + C.min_commission = backtest.get('min_commission', 0.0) + C.open_commission = backtest.get('open_commission', 0.0) + C.close_commission = backtest.get('close_commission', 0.0) + C.close_today_commission = backtest.get('close_today_commission', 0.0) + C.benchmark = backtest.get('benchmark', '000300.SH') + + xtdata_config.client_guid = C._param.get('clientguid') + + from .functions import datetime_to_timetag + + if C.start_time: + C.start_time_str = C.start_time.replace('-', '').replace(' ', '').replace(':', '') + C.start_time_num = int(datetime_to_timetag(C.start_time_str)) + if C.end_time: + C.end_time_str = C.end_time.replace('-', '').replace(' ', '').replace(':', '') + C.end_time_num = int(datetime_to_timetag(C.end_time_str)) + + if 1: #register + this.create_formula() + + C.init() + + if 1: #fix param + if '.' in C.stock_code: + pos = C.stock_code.rfind('.') + C.stockcode = C.stock_code[0:pos] + C.market = C.stock_code[pos + 1:].upper() + + if C.stockcode and C.market: + C.stock_code = C.stockcode + '.' + C.market + C.period = C.period.lower() + + if C.stockcode == "" or C.market == "": + raise Exception("股票代码为空") + + if 1: #create view + if not C._param.get('requestid'): + this.create_view(C.title) + + if 1: #post initcomplete + init_result = {} + + config_ar = ['request_id', 'quote_mode', 'trade_mode'] + init_result['config'] = {ar: C.__getattribute__(ar) for ar in config_ar} + + quote_ar = [ + 'stock_code', 'stockcode', 'market', 'period' + , 'start_time', 'end_time', 'dividend_type' + ] + init_result['quote'] = {ar: C.__getattribute__(ar) for ar in quote_ar} + + trade_ar = [] + init_result['trade'] = {ar: C.__getattribute__(ar) for ar in trade_ar} + + backtest_ar = [ + 'start_time', 'end_time', 'asset', 'margin_ratio', 'slippage_type', 'slippage' + , 'max_vol_rate', 'comsisson_type', 'open_tax', 'close_tax' + , 'min_commission', 'open_commission', 'close_commission' + , 'close_today_commission', 'benchmark' + ] + init_result['backtest'] = {ar: C.__getattribute__(ar) for ar in backtest_ar} + + import datetime as dt + if C.start_time: + C.start_time_str = C.start_time.replace('-', '').replace(' ', '').replace(':', '') + C.start_time_num = int(datetime_to_timetag(C.start_time_str)) + init_result['backtest']['start_time'] = dt.datetime.fromtimestamp(C.start_time_num / 1000).strftime('%Y-%m-%d %H:%M:%S') + if C.end_time: + C.end_time_str = C.end_time.replace('-', '').replace(' ', '').replace(':', '') + C.end_time_num = int(datetime_to_timetag(C.end_time_str)) + init_result['backtest']['end_time'] = dt.datetime.fromtimestamp(C.end_time_num / 1000).strftime('%Y-%m-%d %H:%M:%S') + + this.call_formula('initcomplete', init_result) + + if 1: + this.C.register_callback(0) + return + + def shutdown(this): + return + + def start(this): + import time + C = this.C + + if C.quote_mode in ['history', 'all']: + this.load_main_history() + + C.after_init() + this.run_bar() + + if C.quote_mode in ['realtime', 'all']: + this.load_main_realtime() + + if C.trade_mode == 'backtest': + time.sleep(0.4) + C.on_backtest_finished() + return + + def stop(this): + if this.main_quote_subid: + xtdata.unsubscribe_quote(this.main_quote_subid) + + this.C.stop() + return + + def run(this): + C = this.C + + if C.quote_mode in ['realtime', 'all']: + xtdata.run() + return + + def load_main_history(this): + C = this.C + + data = xtdata.get_market_data_ex( + field_list = ['time'], stock_list = [C.stock_code], period = C.period + , start_time = '', end_time = '', count = -1 + , fill_data = False + ) + + C.timelist = list(data[C.stock_code]['time']) + return + + def load_main_realtime(this): + C = this.C + + def on_data(data): + data = data.get(C.stock_code, []) + if data: + tt = data[-1]['time'] + this.on_main_quote(tt) + return + + this.main_quote_subid = xtdata.subscribe_quote( + stock_code = C.stock_code, period = C.period + , start_time = '', end_time = '', count = 0 + , callback = on_data + ) + return + + def on_main_quote(this, timetag): + if not this.C.timelist or this.C.timelist[-1] < timetag: + this.C.timelist.append(timetag) + this.run_bar() + return + + def run_bar(this): + C = this.C + + push_timelist = [] + bar_timelist = [] + + for i in range(max(C.lastrunbarpos, 0), len(C.timelist)): + C.barpos = i + bartime = C.timelist[i] + + push_timelist.append(bartime) + bar_timelist.append(bartime) + + if ( + not C.start_time_num or C.start_time_num <= bartime + ) and ( + not C.end_time_num or bartime <= C.end_time_num + ): + this.call_formula('runbar', {'timelist': bar_timelist}) + bar_timelist = [] + + C.handlebar() + + C.lastrunbarpos = i + + if bar_timelist: + this.call_formula('runbar', {'timelist': bar_timelist}) + bar_timelist = [] + + if 1: + push_result = {} + push_result['timelist'] = push_timelist + push_result['outputs'] = C.push_result + C.push_result = {} + this.call_formula('index', push_result) + return + + def create_formula(this, callback = None): + C = this.C + client = xtdata.get_client() + + data = { + 'formulaname': '', 'stockcode': C.stock_code, 'period': C.period + , 'starttime': C.start_time_str, 'endtime': C.end_time_str, 'count': 1 + , 'dividendtype': C.dividend_type, 'create': True, 'pyrunmode': 1 + , 'title': C.title + , 'historycallback': 1 if callback else 0 + , 'realtimecallback': 1 if callback else 0 + } + + client.subscribeFormula(C.request_id, _BSON_.BSON.encode(data), callback) + + def call_formula(this, func, data): + C = this.C + client = xtdata.get_client() + bresult = client.callFormula(C.request_id, func, _BSON_.BSON.encode(data)) + return _BSON_.BSON.decode(bresult) + + def create_view(this, title): + C = this.C + client = xtdata.get_client() + data = {'viewtype': 0,'title':title, 'groupid':-1,'stockcode':C.market + C.stockcode,'period':C.period,'dividendtype':C.dividend_type} + client.createView(C.request_id, _BSON_.BSON.encode(data)) + return + + +class BackTestResult: + def __init__(self, request_id): + self.request_id = request_id + + def get_backtest_index(self): + import os, pandas as pd, uuid + from .functions import get_backtest_index + + path = f'{os.getenv("TEMP")}/backtest_{uuid.uuid4()}' + get_backtest_index(self.request_id, path) + + ret = pd.read_csv(f'{path}/backtestindex.csv', encoding = 'utf-8') + import shutil + shutil.rmtree(f'{path}') + return ret + + def get_group_result(self, fields = []): + import os, pandas as pd, uuid + from .functions import get_group_result + + path = f'{os.getenv("TEMP")}/backtest_{uuid.uuid4()}' + get_group_result(self.request_id, path, fields) + if not fields: + fields = ['order', 'deal', 'position'] + res = {} + for f in fields: + res[f] = pd.read_csv(f'{path}/{f}.csv', encoding = 'utf-8') + import shutil + shutil.rmtree(path) + return res + +class RealTimeResult: + def __init__(self, request_id): + self.request_id = request_id + diff --git a/src/xtquant/ssleay32.dll b/src/xtquant/ssleay32.dll new file mode 100644 index 0000000..873ef97 Binary files /dev/null and b/src/xtquant/ssleay32.dll differ diff --git a/src/xtquant/vcruntime140.dll b/src/xtquant/vcruntime140.dll new file mode 100644 index 0000000..690f3fb Binary files /dev/null and b/src/xtquant/vcruntime140.dll differ diff --git a/src/xtquant/xtbson/__init__.py b/src/xtquant/xtbson/__init__.py new file mode 100644 index 0000000..3af27fe --- /dev/null +++ b/src/xtquant/xtbson/__init__.py @@ -0,0 +1,7 @@ + +import sys + +if sys.version_info.major == 3 and sys.version_info.minor == 6: + from .bson36 import * +else: + from .bson37 import * diff --git a/src/xtquant/xtbson/bson36/__init__.py b/src/xtquant/xtbson/bson36/__init__.py new file mode 100644 index 0000000..be3eb7b --- /dev/null +++ b/src/xtquant/xtbson/bson36/__init__.py @@ -0,0 +1,1182 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""BSON (Binary JSON) encoding and decoding. + +The mapping from Python types to BSON types is as follows: + +======================================= ============= =================== +Python Type BSON Type Supported Direction +======================================= ============= =================== +None null both +bool boolean both +int [#int]_ int32 / int64 py -> bson +`bson.int64.Int64` int64 both +float number (real) both +str string both +list array both +dict / `SON` object both +datetime.datetime [#dt]_ [#dt2]_ date both +`bson.regex.Regex` regex both +compiled re [#re]_ regex py -> bson +`bson.binary.Binary` binary both +`bson.objectid.ObjectId` oid both +`bson.dbref.DBRef` dbref both +None undefined bson -> py +`bson.code.Code` code both +str symbol bson -> py +bytes [#bytes]_ binary both +======================================= ============= =================== + +.. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending + on its size. A BSON int32 will always decode to a Python int. A BSON + int64 will always decode to a :class:`~bson.int64.Int64`. +.. [#dt] datetime.datetime instances will be rounded to the nearest + millisecond when saved +.. [#dt2] all datetime.datetime instances are treated as *naive*. clients + should always use UTC. +.. [#re] :class:`~bson.regex.Regex` instances and regular expression + objects from ``re.compile()`` are both saved as BSON regular expressions. + BSON regular expressions are decoded as :class:`~bson.regex.Regex` + instances. +.. [#bytes] The bytes type is encoded as BSON binary with + subtype 0. It will be decoded back to bytes. +""" + +import calendar +import datetime +import itertools +import platform +import re +import struct +import sys +import uuid +from codecs import utf_8_decode as _utf_8_decode +from codecs import utf_8_encode as _utf_8_encode +from collections import abc as _abc + +from .binary import ( + ALL_UUID_SUBTYPES, + CSHARP_LEGACY, + JAVA_LEGACY, + OLD_UUID_SUBTYPE, + STANDARD, + UUID_SUBTYPE, + Binary, + UuidRepresentation, +) +from .code import Code +from .codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, _raw_document_class +from .dbref import DBRef +from .decimal128 import Decimal128 +from .errors import InvalidBSON, InvalidDocument, InvalidStringData +from .int64 import Int64 +from .max_key import MaxKey +from .min_key import MinKey +from .objectid import ObjectId +from .regex import Regex +from .son import RE_TYPE, SON +from .timestamp import Timestamp +from .tz_util import utc + +try: + from . import _cbson + + _USE_C = True +except ImportError: + _USE_C = False + + +EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) +EPOCH_NAIVE = datetime.datetime.utcfromtimestamp(0) + + +BSONNUM = b"\x01" # Floating point +BSONSTR = b"\x02" # UTF-8 string +BSONOBJ = b"\x03" # Embedded document +BSONARR = b"\x04" # Array +BSONBIN = b"\x05" # Binary +BSONUND = b"\x06" # Undefined +BSONOID = b"\x07" # ObjectId +BSONBOO = b"\x08" # Boolean +BSONDAT = b"\x09" # UTC Datetime +BSONNUL = b"\x0A" # Null +BSONRGX = b"\x0B" # Regex +BSONREF = b"\x0C" # DBRef +BSONCOD = b"\x0D" # Javascript code +BSONSYM = b"\x0E" # Symbol +BSONCWS = b"\x0F" # Javascript code with scope +BSONINT = b"\x10" # 32bit int +BSONTIM = b"\x11" # Timestamp +BSONLON = b"\x12" # 64bit int +BSONDEC = b"\x13" # Decimal128 +BSONMIN = b"\xFF" # Min key +BSONMAX = b"\x7F" # Max key + + +_UNPACK_FLOAT_FROM = struct.Struct("= obj_end: + raise InvalidBSON("invalid object length") + # If this is the top-level document, validate the total size too. + if position == 0 and obj_size != obj_end: + raise InvalidBSON("invalid object length") + return obj_size, end + + +def _get_object(data, view, position, obj_end, opts, dummy): + """Decode a BSON subdocument to opts.document_class or bson.dbref.DBRef.""" + obj_size, end = _get_object_size(data, position, obj_end) + if _raw_document_class(opts.document_class): + return (opts.document_class(data[position : end + 1], opts), position + obj_size) + + obj = _elements_to_dict(data, view, position + 4, end, opts) + + position += obj_size + # If DBRef validation fails, return a normal doc. + if ( + isinstance(obj.get("$ref"), str) + and "$id" in obj + and isinstance(obj.get("$db"), (str, type(None))) + ): + return (DBRef(obj.pop("$ref"), obj.pop("$id", None), obj.pop("$db", None), obj), position) + return obj, position + + +def _get_array(data, view, position, obj_end, opts, element_name): + """Decode a BSON array to python list.""" + size = _UNPACK_INT_FROM(data, position)[0] + end = position + size - 1 + if data[end] != 0: + raise InvalidBSON("bad eoo") + + position += 4 + end -= 1 + result = [] + + # Avoid doing global and attribute lookups in the loop. + append = result.append + index = data.index + getter = _ELEMENT_GETTER + decoder_map = opts.type_registry._decoder_map + + while position < end: + element_type = data[position] + # Just skip the keys. + position = index(b"\x00", position) + 1 + try: + value, position = getter[element_type]( + data, view, position, obj_end, opts, element_name + ) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if decoder_map: + custom_decoder = decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + append(value) + + if position != end + 1: + raise InvalidBSON("bad array length") + return result, position + 1 + + +def _get_binary(data, view, position, obj_end, opts, dummy1): + """Decode a BSON binary to bson.binary.Binary or python UUID.""" + length, subtype = _UNPACK_LENGTH_SUBTYPE_FROM(data, position) + position += 5 + if subtype == 2: + length2 = _UNPACK_INT_FROM(data, position)[0] + position += 4 + if length2 != length - 4: + raise InvalidBSON("invalid binary (st 2) - lengths don't match!") + length = length2 + end = position + length + if length < 0 or end > obj_end: + raise InvalidBSON("bad binary object length") + + # Convert UUID subtypes to native UUIDs. + if subtype in ALL_UUID_SUBTYPES: + uuid_rep = opts.uuid_representation + binary_value = Binary(data[position:end], subtype) + if ( + (uuid_rep == UuidRepresentation.UNSPECIFIED) + or (subtype == UUID_SUBTYPE and uuid_rep != STANDARD) + or (subtype == OLD_UUID_SUBTYPE and uuid_rep == STANDARD) + ): + return binary_value, end + return binary_value.as_uuid(uuid_rep), end + + # Decode subtype 0 to 'bytes'. + if subtype == 0: + value = data[position:end] + else: + value = Binary(data[position:end], subtype) + + return value, end + + +def _get_oid(data, view, position, dummy0, dummy1, dummy2): + """Decode a BSON ObjectId to bson.objectid.ObjectId.""" + end = position + 12 + return ObjectId(data[position:end]), end + + +def _get_boolean(data, view, position, dummy0, dummy1, dummy2): + """Decode a BSON true/false to python True/False.""" + end = position + 1 + boolean_byte = data[position:end] + if boolean_byte == b"\x00": + return False, end + elif boolean_byte == b"\x01": + return True, end + raise InvalidBSON("invalid boolean value: %r" % boolean_byte) + + +def _get_date(data, view, position, dummy0, opts, dummy1): + """Decode a BSON datetime to python datetime.datetime.""" + return _millis_to_datetime(_UNPACK_LONG_FROM(data, position)[0], opts), position + 8 + + +def _get_code(data, view, position, obj_end, opts, element_name): + """Decode a BSON code to bson.code.Code.""" + code, position = _get_string(data, view, position, obj_end, opts, element_name) + return Code(code), position + + +def _get_code_w_scope(data, view, position, obj_end, opts, element_name): + """Decode a BSON code_w_scope to bson.code.Code.""" + code_end = position + _UNPACK_INT_FROM(data, position)[0] + code, position = _get_string(data, view, position + 4, code_end, opts, element_name) + scope, position = _get_object(data, view, position, code_end, opts, element_name) + if position != code_end: + raise InvalidBSON("scope outside of javascript code boundaries") + return Code(code, scope), position + + +def _get_regex(data, view, position, dummy0, opts, dummy1): + """Decode a BSON regex to bson.regex.Regex or a python pattern object.""" + pattern, position = _get_c_string(data, view, position, opts) + bson_flags, position = _get_c_string(data, view, position, opts) + bson_re = Regex(pattern, bson_flags) + return bson_re, position + + +def _get_ref(data, view, position, obj_end, opts, element_name): + """Decode (deprecated) BSON DBPointer to bson.dbref.DBRef.""" + collection, position = _get_string(data, view, position, obj_end, opts, element_name) + oid, position = _get_oid(data, view, position, obj_end, opts, element_name) + return DBRef(collection, oid), position + + +def _get_timestamp(data, view, position, dummy0, dummy1, dummy2): + """Decode a BSON timestamp to bson.timestamp.Timestamp.""" + inc, timestamp = _UNPACK_TIMESTAMP_FROM(data, position) + return Timestamp(timestamp, inc), position + 8 + + +def _get_int64(data, view, position, dummy0, dummy1, dummy2): + """Decode a BSON int64 to bson.int64.Int64.""" + return Int64(_UNPACK_LONG_FROM(data, position)[0]), position + 8 + + +def _get_decimal128(data, view, position, dummy0, dummy1, dummy2): + """Decode a BSON decimal128 to bson.decimal128.Decimal128.""" + end = position + 16 + return Decimal128.from_bid(data[position:end]), end + + +# Each decoder function's signature is: +# - data: bytes +# - view: memoryview that references `data` +# - position: int, beginning of object in 'data' to decode +# - obj_end: int, end of object to decode in 'data' if variable-length type +# - opts: a CodecOptions +_ELEMENT_GETTER = { + ord(BSONNUM): _get_float, + ord(BSONSTR): _get_string, + ord(BSONOBJ): _get_object, + ord(BSONARR): _get_array, + ord(BSONBIN): _get_binary, + ord(BSONUND): lambda u, v, w, x, y, z: (None, w), # Deprecated undefined + ord(BSONOID): _get_oid, + ord(BSONBOO): _get_boolean, + ord(BSONDAT): _get_date, + ord(BSONNUL): lambda u, v, w, x, y, z: (None, w), + ord(BSONRGX): _get_regex, + ord(BSONREF): _get_ref, # Deprecated DBPointer + ord(BSONCOD): _get_code, + ord(BSONSYM): _get_string, # Deprecated symbol + ord(BSONCWS): _get_code_w_scope, + ord(BSONINT): _get_int, + ord(BSONTIM): _get_timestamp, + ord(BSONLON): _get_int64, + ord(BSONDEC): _get_decimal128, + ord(BSONMIN): lambda u, v, w, x, y, z: (MinKey(), w), + ord(BSONMAX): lambda u, v, w, x, y, z: (MaxKey(), w), +} + + +if _USE_C: + + def _element_to_dict(data, view, position, obj_end, opts): + return _cbson._element_to_dict(data, position, obj_end, opts) + +else: + + def _element_to_dict(data, view, position, obj_end, opts): + """Decode a single key, value pair.""" + element_type = data[position] + position += 1 + element_name, position = _get_c_string(data, view, position, opts) + try: + value, position = _ELEMENT_GETTER[element_type]( + data, view, position, obj_end, opts, element_name + ) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if opts.type_registry._decoder_map: + custom_decoder = opts.type_registry._decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + return element_name, value, position + + +def _raw_to_dict(data, position, obj_end, opts, result): + data, view = get_data_and_view(data) + return _elements_to_dict(data, view, position, obj_end, opts, result) + + +def _elements_to_dict(data, view, position, obj_end, opts, result=None): + """Decode a BSON document into result.""" + if result is None: + result = opts.document_class() + end = obj_end - 1 + while position < end: + key, value, position = _element_to_dict(data, view, position, obj_end, opts) + result[key] = value + if position != obj_end: + raise InvalidBSON("bad object or element length") + return result + + +def _bson_to_dict(data, opts): + """Decode a BSON string to document_class.""" + data, view = get_data_and_view(data) + try: + if _raw_document_class(opts.document_class): + return opts.document_class(data, opts) + _, end = _get_object_size(data, 0, len(data)) + return _elements_to_dict(data, view, 4, end, opts) + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) + + +if _USE_C: + _bson_to_dict = _cbson._bson_to_dict + + +_PACK_FLOAT = struct.Struct(">> import collections # From Python standard library. + >>> import bson + >>> from .codec_options import CodecOptions + >>> data = bson.encode({'a': 1}) + >>> decoded_doc = bson.decode(data) + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.decode(data, codec_options=options) + >>> type(decoded_doc) + + + :Parameters: + - `data`: the BSON to decode. Any bytes-like object that implements + the buffer protocol. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionadded:: 3.9 + """ + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _bson_to_dict(data, codec_options) + + +def decode_all(data, codec_options=DEFAULT_CODEC_OPTIONS): + """Decode BSON data to multiple documents. + + `data` must be a bytes-like object implementing the buffer protocol that + provides concatenated, valid, BSON-encoded documents. + + :Parameters: + - `data`: BSON data + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.9 + Supports bytes-like objects that implement the buffer protocol. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON regular + expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + """ + data, view = get_data_and_view(data) + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + data_len = len(data) + docs = [] + position = 0 + end = data_len - 1 + use_raw = _raw_document_class(codec_options.document_class) + try: + while position < end: + obj_size = _UNPACK_INT_FROM(data, position)[0] + if data_len - position < obj_size: + raise InvalidBSON("invalid object size") + obj_end = position + obj_size - 1 + if data[obj_end] != 0: + raise InvalidBSON("bad eoo") + if use_raw: + docs.append( + codec_options.document_class(data[position : obj_end + 1], codec_options) + ) + else: + docs.append(_elements_to_dict(data, view, position + 4, obj_end, codec_options)) + position += obj_size + return docs + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) + + +if _USE_C: + decode_all = _cbson.decode_all + + +def _decode_selective(rawdoc, fields, codec_options): + if _raw_document_class(codec_options.document_class): + # If document_class is RawBSONDocument, use vanilla dictionary for + # decoding command response. + doc = {} + else: + # Else, use the specified document_class. + doc = codec_options.document_class() + for key, value in rawdoc.items(): + if key in fields: + if fields[key] == 1: + doc[key] = _bson_to_dict(rawdoc.raw, codec_options)[key] + else: + doc[key] = _decode_selective(value, fields[key], codec_options) + else: + doc[key] = value + return doc + + +def _convert_raw_document_lists_to_streams(document): + cursor = document.get("cursor") + if cursor: + for key in ("firstBatch", "nextBatch"): + batch = cursor.get(key) + if batch: + stream = b"".join(doc.raw for doc in batch) + cursor[key] = [stream] + + +def _decode_all_selective(data, codec_options, fields): + """Decode BSON data to a single document while using user-provided + custom decoding logic. + + `data` must be a string representing a valid, BSON-encoded document. + + :Parameters: + - `data`: BSON data + - `codec_options`: An instance of + :class:`~bson.codec_options.CodecOptions` with user-specified type + decoders. If no decoders are found, this method is the same as + ``decode_all``. + - `fields`: Map of document namespaces where data that needs + to be custom decoded lives or None. For example, to custom decode a + list of objects in 'field1.subfield1', the specified value should be + ``{'field1': {'subfield1': 1}}``. If ``fields`` is an empty map or + None, this method is the same as ``decode_all``. + + :Returns: + - `document_list`: Single-member list containing the decoded document. + + .. versionadded:: 3.8 + """ + if not codec_options.type_registry._decoder_map: + return decode_all(data, codec_options) + + if not fields: + return decode_all(data, codec_options.with_options(type_registry=None)) + + # Decode documents for internal use. + from .raw_bson import RawBSONDocument + + internal_codec_options = codec_options.with_options( + document_class=RawBSONDocument, type_registry=None + ) + _doc = _bson_to_dict(data, internal_codec_options) + return [ + _decode_selective( + _doc, + fields, + codec_options, + ) + ] + + +def decode_iter(data, codec_options=DEFAULT_CODEC_OPTIONS): + """Decode BSON data to multiple documents as a generator. + + Works similarly to the decode_all function, but yields one document at a + time. + + `data` must be a string of concatenated, valid, BSON-encoded + documents. + + :Parameters: + - `data`: BSON data + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionadded:: 2.8 + """ + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + position = 0 + end = len(data) - 1 + while position < end: + obj_size = _UNPACK_INT_FROM(data, position)[0] + elements = data[position : position + obj_size] + position += obj_size + + yield _bson_to_dict(elements, codec_options) + + +def decode_file_iter(file_obj, codec_options=DEFAULT_CODEC_OPTIONS): + """Decode bson data from a file to multiple documents as a generator. + + Works similarly to the decode_all function, but reads from the file object + in chunks and parses bson in chunks, yielding one document at a time. + + :Parameters: + - `file_obj`: A file object containing BSON data. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionadded:: 2.8 + """ + while True: + # Read size of next object. + size_data = file_obj.read(4) + if not size_data: + break # Finished with file normaly. + elif len(size_data) != 4: + raise InvalidBSON("cut off in middle of objsize") + obj_size = _UNPACK_INT_FROM(size_data, 0)[0] - 4 + elements = size_data + file_obj.read(max(0, obj_size)) + yield _bson_to_dict(elements, codec_options) + + +def is_valid(bson): + """Check that the given string represents valid :class:`BSON` data. + + Raises :class:`TypeError` if `bson` is not an instance of + :class:`str` (:class:`bytes` in python 3). Returns ``True`` + if `bson` is valid :class:`BSON`, ``False`` otherwise. + + :Parameters: + - `bson`: the data to be validated + """ + if not isinstance(bson, bytes): + raise TypeError("BSON data must be an instance of a subclass of bytes") + + try: + _bson_to_dict(bson, DEFAULT_CODEC_OPTIONS) + return True + except Exception: + return False + + +class BSON(bytes): + """BSON (Binary JSON) data. + + .. warning:: Using this class to encode and decode BSON adds a performance + cost. For better performance use the module level functions + :func:`encode` and :func:`decode` instead. + """ + + @classmethod + def encode(cls, document, check_keys=False, codec_options=DEFAULT_CODEC_OPTIONS): + """Encode a document to a new :class:`BSON` instance. + + A document can be any mapping type (like :class:`dict`). + + Raises :class:`TypeError` if `document` is not a mapping type, + or contains keys that are not instances of + :class:`basestring` (:class:`str` in python 3). Raises + :class:`~bson.errors.InvalidDocument` if `document` cannot be + converted to :class:`BSON`. + + :Parameters: + - `document`: mapping type representing a document + - `check_keys` (optional): check if keys start with '$' or + contain '.', raising :class:`~bson.errors.InvalidDocument` in + either case + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `uuid_subtype` option with `codec_options`. + """ + return cls(encode(document, check_keys, codec_options)) + + def decode(self, codec_options=DEFAULT_CODEC_OPTIONS): + """Decode this BSON data. + + By default, returns a BSON document represented as a Python + :class:`dict`. To use a different :class:`MutableMapping` class, + configure a :class:`~bson.codec_options.CodecOptions`:: + + >>> import collections # From Python standard library. + >>> import bson + >>> from .codec_options import CodecOptions + >>> data = bson.BSON.encode({'a': 1}) + >>> decoded_doc = bson.BSON(data).decode() + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.BSON(data).decode(codec_options=options) + >>> type(decoded_doc) + + + :Parameters: + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON + regular expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + """ + return decode(self, codec_options) + + +def has_c(): + """Is the C extension installed?""" + return _USE_C diff --git a/src/xtquant/xtbson/bson36/_helpers.py b/src/xtquant/xtbson/bson36/_helpers.py new file mode 100644 index 0000000..1221fe8 --- /dev/null +++ b/src/xtquant/xtbson/bson36/_helpers.py @@ -0,0 +1,40 @@ +# Copyright 2021-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Setstate and getstate functions for objects with __slots__, allowing + compatibility with default pickling protocol +""" + + +def _setstate_slots(self, state): + for slot, value in state.items(): + setattr(self, slot, value) + + +def _mangle_name(name, prefix): + if name.startswith("__"): + prefix = "_" + prefix + else: + prefix = "" + return prefix + name + + +def _getstate_slots(self): + prefix = self.__class__.__name__ + ret = dict() + for name in self.__slots__: + mangled_name = _mangle_name(name, prefix) + if hasattr(self, mangled_name): + ret[mangled_name] = getattr(self, mangled_name) + return ret diff --git a/src/xtquant/xtbson/bson36/binary.py b/src/xtquant/xtbson/bson36/binary.py new file mode 100644 index 0000000..e7ad996 --- /dev/null +++ b/src/xtquant/xtbson/bson36/binary.py @@ -0,0 +1,352 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from uuid import UUID +from warnings import warn + +"""Tools for representing BSON binary data. +""" + +BINARY_SUBTYPE = 0 +"""BSON binary subtype for binary data. + +This is the default subtype for binary data. +""" + +FUNCTION_SUBTYPE = 1 +"""BSON binary subtype for functions. +""" + +OLD_BINARY_SUBTYPE = 2 +"""Old BSON binary subtype for binary data. + +This is the old default subtype, the current +default is :data:`BINARY_SUBTYPE`. +""" + +OLD_UUID_SUBTYPE = 3 +"""Old BSON binary subtype for a UUID. + +:class:`uuid.UUID` instances will automatically be encoded +by :mod:`bson` using this subtype when using +:data:`UuidRepresentation.PYTHON_LEGACY`, +:data:`UuidRepresentation.JAVA_LEGACY`, or +:data:`UuidRepresentation.CSHARP_LEGACY`. + +.. versionadded:: 2.1 +""" + +UUID_SUBTYPE = 4 +"""BSON binary subtype for a UUID. + +This is the standard BSON binary subtype for UUIDs. +:class:`uuid.UUID` instances will automatically be encoded +by :mod:`bson` using this subtype when using +:data:`UuidRepresentation.STANDARD`. +""" + + +class UuidRepresentation: + UNSPECIFIED = 0 + """An unspecified UUID representation. + + When configured, :class:`uuid.UUID` instances will **not** be + automatically encoded to or decoded from :class:`~bson.binary.Binary`. + When encoding a :class:`uuid.UUID` instance, an error will be raised. + To encode a :class:`uuid.UUID` instance with this configuration, it must + be wrapped in the :class:`~bson.binary.Binary` class by the application + code. When decoding a BSON binary field with a UUID subtype, a + :class:`~bson.binary.Binary` instance will be returned instead of a + :class:`uuid.UUID` instance. + + See :ref:`unspecified-representation-details` for details. + + .. versionadded:: 3.11 + """ + + STANDARD = UUID_SUBTYPE + """The standard UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary, using RFC-4122 byte order with + binary subtype :data:`UUID_SUBTYPE`. + + See :ref:`standard-representation-details` for details. + + .. versionadded:: 3.11 + """ + + PYTHON_LEGACY = OLD_UUID_SUBTYPE + """The Python legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary, using RFC-4122 byte order with + binary subtype :data:`OLD_UUID_SUBTYPE`. + + See :ref:`python-legacy-representation-details` for details. + + .. versionadded:: 3.11 + """ + + JAVA_LEGACY = 5 + """The Java legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary subtype :data:`OLD_UUID_SUBTYPE`, + using the Java driver's legacy byte order. + + See :ref:`java-legacy-representation-details` for details. + + .. versionadded:: 3.11 + """ + + CSHARP_LEGACY = 6 + """The C#/.net legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary subtype :data:`OLD_UUID_SUBTYPE`, + using the C# driver's legacy byte order. + + See :ref:`csharp-legacy-representation-details` for details. + + .. versionadded:: 3.11 + """ + + +STANDARD = UuidRepresentation.STANDARD +"""An alias for :data:`UuidRepresentation.STANDARD`. + +.. versionadded:: 3.0 +""" + +PYTHON_LEGACY = UuidRepresentation.PYTHON_LEGACY +"""An alias for :data:`UuidRepresentation.PYTHON_LEGACY`. + +.. versionadded:: 3.0 +""" + +JAVA_LEGACY = UuidRepresentation.JAVA_LEGACY +"""An alias for :data:`UuidRepresentation.JAVA_LEGACY`. + +.. versionchanged:: 3.6 + BSON binary subtype 4 is decoded using RFC-4122 byte order. +.. versionadded:: 2.3 +""" + +CSHARP_LEGACY = UuidRepresentation.CSHARP_LEGACY +"""An alias for :data:`UuidRepresentation.CSHARP_LEGACY`. + +.. versionchanged:: 3.6 + BSON binary subtype 4 is decoded using RFC-4122 byte order. +.. versionadded:: 2.3 +""" + +ALL_UUID_SUBTYPES = (OLD_UUID_SUBTYPE, UUID_SUBTYPE) +ALL_UUID_REPRESENTATIONS = ( + UuidRepresentation.UNSPECIFIED, + UuidRepresentation.STANDARD, + UuidRepresentation.PYTHON_LEGACY, + UuidRepresentation.JAVA_LEGACY, + UuidRepresentation.CSHARP_LEGACY, +) +UUID_REPRESENTATION_NAMES = { + UuidRepresentation.UNSPECIFIED: "UuidRepresentation.UNSPECIFIED", + UuidRepresentation.STANDARD: "UuidRepresentation.STANDARD", + UuidRepresentation.PYTHON_LEGACY: "UuidRepresentation.PYTHON_LEGACY", + UuidRepresentation.JAVA_LEGACY: "UuidRepresentation.JAVA_LEGACY", + UuidRepresentation.CSHARP_LEGACY: "UuidRepresentation.CSHARP_LEGACY", +} + +MD5_SUBTYPE = 5 +"""BSON binary subtype for an MD5 hash. +""" + +COLUMN_SUBTYPE = 7 +"""BSON binary subtype for columns. + +.. versionadded:: 4.0 +""" + +USER_DEFINED_SUBTYPE = 128 +"""BSON binary subtype for any user defined structure. +""" + + +class Binary(bytes): + """Representation of BSON binary data. + + This is necessary because we want to represent Python strings as + the BSON string type. We need to wrap binary data so we can tell + the difference between what should be considered binary data and + what should be considered a string when we encode to BSON. + + Raises TypeError if `data` is not an instance of :class:`bytes` + (:class:`str` in python 2) or `subtype` is not an instance of + :class:`int`. Raises ValueError if `subtype` is not in [0, 256). + + .. note:: + In python 3 instances of Binary with subtype 0 will be decoded + directly to :class:`bytes`. + + :Parameters: + - `data`: the binary data to represent. Can be any bytes-like type + that implements the buffer protocol. + - `subtype` (optional): the `binary subtype + `_ + to use + + .. versionchanged:: 3.9 + Support any bytes-like type that implements the buffer protocol. + """ + + _type_marker = 5 + + def __new__(cls, data, subtype=BINARY_SUBTYPE): + if not isinstance(subtype, int): + raise TypeError("subtype must be an instance of int") + if subtype >= 256 or subtype < 0: + raise ValueError("subtype must be contained in [0, 256)") + # Support any type that implements the buffer protocol. + self = bytes.__new__(cls, memoryview(data).tobytes()) + self.__subtype = subtype + return self + + @classmethod + def from_uuid(cls, uuid, uuid_representation=UuidRepresentation.STANDARD): + """Create a BSON Binary object from a Python UUID. + + Creates a :class:`~bson.binary.Binary` object from a + :class:`uuid.UUID` instance. Assumes that the native + :class:`uuid.UUID` instance uses the byte-order implied by the + provided ``uuid_representation``. + + Raises :exc:`TypeError` if `uuid` is not an instance of + :class:`~uuid.UUID`. + + :Parameters: + - `uuid`: A :class:`uuid.UUID` instance. + - `uuid_representation`: A member of + :class:`~bson.binary.UuidRepresentation`. Default: + :const:`~bson.binary.UuidRepresentation.STANDARD`. + See :ref:`handling-uuid-data-example` for details. + + .. versionadded:: 3.11 + """ + if not isinstance(uuid, UUID): + raise TypeError("uuid must be an instance of uuid.UUID") + + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value " "from .binary.UuidRepresentation" + ) + + if uuid_representation == UuidRepresentation.UNSPECIFIED: + raise ValueError( + "cannot encode native uuid.UUID with " + "UuidRepresentation.UNSPECIFIED. UUIDs can be manually " + "converted to bson.Binary instances using " + "bson.Binary.from_uuid() or a different UuidRepresentation " + "can be configured. See the documentation for " + "UuidRepresentation for more information." + ) + + subtype = OLD_UUID_SUBTYPE + if uuid_representation == UuidRepresentation.PYTHON_LEGACY: + payload = uuid.bytes + elif uuid_representation == UuidRepresentation.JAVA_LEGACY: + from_uuid = uuid.bytes + payload = from_uuid[0:8][::-1] + from_uuid[8:16][::-1] + elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: + payload = uuid.bytes_le + else: + # uuid_representation == UuidRepresentation.STANDARD + subtype = UUID_SUBTYPE + payload = uuid.bytes + + return cls(payload, subtype) + + def as_uuid(self, uuid_representation=UuidRepresentation.STANDARD): + """Create a Python UUID from this BSON Binary object. + + Decodes this binary object as a native :class:`uuid.UUID` instance + with the provided ``uuid_representation``. + + Raises :exc:`ValueError` if this :class:`~bson.binary.Binary` instance + does not contain a UUID. + + :Parameters: + - `uuid_representation`: A member of + :class:`~bson.binary.UuidRepresentation`. Default: + :const:`~bson.binary.UuidRepresentation.STANDARD`. + See :ref:`handling-uuid-data-example` for details. + + .. versionadded:: 3.11 + """ + if self.subtype not in ALL_UUID_SUBTYPES: + raise ValueError("cannot decode subtype %s as a uuid" % (self.subtype,)) + + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from " "bson.binary.UuidRepresentation" + ) + + if uuid_representation == UuidRepresentation.UNSPECIFIED: + raise ValueError("uuid_representation cannot be UNSPECIFIED") + elif uuid_representation == UuidRepresentation.PYTHON_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes=self) + elif uuid_representation == UuidRepresentation.JAVA_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes=self[0:8][::-1] + self[8:16][::-1]) + elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes_le=self) + else: + # uuid_representation == UuidRepresentation.STANDARD + if self.subtype == UUID_SUBTYPE: + return UUID(bytes=self) + + raise ValueError( + "cannot decode subtype %s to %s" + % (self.subtype, UUID_REPRESENTATION_NAMES[uuid_representation]) + ) + + @property + def subtype(self): + """Subtype of this binary data.""" + return self.__subtype + + def __getnewargs__(self): + # Work around http://bugs.python.org/issue7382 + data = super(Binary, self).__getnewargs__()[0] + if not isinstance(data, bytes): + data = data.encode("latin-1") + return data, self.__subtype + + def __eq__(self, other): + if isinstance(other, Binary): + return (self.__subtype, bytes(self)) == (other.subtype, bytes(other)) + # We don't return NotImplemented here because if we did then + # Binary("foo") == "foo" would return True, since Binary is a + # subclass of str... + return False + + def __hash__(self): + return super(Binary, self).__hash__() ^ hash(self.__subtype) + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "Binary(%s, %s)" % (bytes.__repr__(self), self.__subtype) diff --git a/src/xtquant/xtbson/bson36/code.py b/src/xtquant/xtbson/bson36/code.py new file mode 100644 index 0000000..5afe276 --- /dev/null +++ b/src/xtquant/xtbson/bson36/code.py @@ -0,0 +1,94 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing JavaScript code in BSON. +""" + +from collections.abc import Mapping as _Mapping + + +class Code(str): + """BSON's JavaScript code type. + + Raises :class:`TypeError` if `code` is not an instance of + :class:`basestring` (:class:`str` in python 3) or `scope` + is not ``None`` or an instance of :class:`dict`. + + Scope variables can be set by passing a dictionary as the `scope` + argument or by using keyword arguments. If a variable is set as a + keyword argument it will override any setting for that variable in + the `scope` dictionary. + + :Parameters: + - `code`: A string containing JavaScript code to be evaluated or another + instance of Code. In the latter case, the scope of `code` becomes this + Code's :attr:`scope`. + - `scope` (optional): dictionary representing the scope in which + `code` should be evaluated - a mapping from identifiers (as + strings) to values. Defaults to ``None``. This is applied after any + scope associated with a given `code` above. + - `**kwargs` (optional): scope variables can also be passed as + keyword arguments. These are applied after `scope` and `code`. + + .. versionchanged:: 3.4 + The default value for :attr:`scope` is ``None`` instead of ``{}``. + + """ + + _type_marker = 13 + + def __new__(cls, code, scope=None, **kwargs): + if not isinstance(code, str): + raise TypeError("code must be an instance of str") + + self = str.__new__(cls, code) + + try: + self.__scope = code.scope + except AttributeError: + self.__scope = None + + if scope is not None: + if not isinstance(scope, _Mapping): + raise TypeError("scope must be an instance of dict") + if self.__scope is not None: + self.__scope.update(scope) + else: + self.__scope = scope + + if kwargs: + if self.__scope is not None: + self.__scope.update(kwargs) + else: + self.__scope = kwargs + + return self + + @property + def scope(self): + """Scope dictionary for this instance or ``None``.""" + return self.__scope + + def __repr__(self): + return "Code(%s, %r)" % (str.__repr__(self), self.__scope) + + def __eq__(self, other): + if isinstance(other, Code): + return (self.__scope, str(self)) == (other.__scope, str(other)) + return False + + __hash__ = None + + def __ne__(self, other): + return not self == other diff --git a/src/xtquant/xtbson/bson36/codec_options.py b/src/xtquant/xtbson/bson36/codec_options.py new file mode 100644 index 0000000..c220d31 --- /dev/null +++ b/src/xtquant/xtbson/bson36/codec_options.py @@ -0,0 +1,389 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for specifying BSON codec options.""" + +import abc +import datetime +import warnings +from collections import namedtuple +from collections.abc import MutableMapping as _MutableMapping + +from .binary import ( + ALL_UUID_REPRESENTATIONS, + UUID_REPRESENTATION_NAMES, + UuidRepresentation, +) + + +def _abstractproperty(func): + return property(abc.abstractmethod(func)) + + +_RAW_BSON_DOCUMENT_MARKER = 101 + + +def _raw_document_class(document_class): + """Determine if a document_class is a RawBSONDocument class.""" + marker = getattr(document_class, "_type_marker", None) + return marker == _RAW_BSON_DOCUMENT_MARKER + + +class TypeEncoder(abc.ABC): + """Base class for defining type codec classes which describe how a + custom type can be transformed to one of the types BSON understands. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + + @_abstractproperty + def python_type(self): + """The Python type to be converted into something serializable.""" + pass + + @abc.abstractmethod + def transform_python(self, value): + """Convert the given Python object into something serializable.""" + pass + + +class TypeDecoder(abc.ABC): + """Base class for defining type codec classes which describe how a + BSON type can be transformed to a custom type. + + Codec classes must implement the ``bson_type`` attribute, and the + ``transform_bson`` method to support decoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + + @_abstractproperty + def bson_type(self): + """The BSON type to be converted into our own type.""" + pass + + @abc.abstractmethod + def transform_bson(self, value): + """Convert the given BSON value into our own type.""" + pass + + +class TypeCodec(TypeEncoder, TypeDecoder): + """Base class for defining type codec classes which describe how a + custom type can be transformed to/from one of the types :mod:`bson` + can already encode/decode. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding, as well as the + ``bson_type`` attribute, and the ``transform_bson`` method to support + decoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + + pass + + +class TypeRegistry(object): + """Encapsulates type codecs used in encoding and / or decoding BSON, as + well as the fallback encoder. Type registries cannot be modified after + instantiation. + + ``TypeRegistry`` can be initialized with an iterable of type codecs, and + a callable for the fallback encoder:: + + >>> from .codec_options import TypeRegistry + >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...], + ... fallback_encoder) + + See :ref:`custom-type-type-registry` documentation for an example. + + :Parameters: + - `type_codecs` (optional): iterable of type codec instances. If + ``type_codecs`` contains multiple codecs that transform a single + python or BSON type, the transformation specified by the type codec + occurring last prevails. A TypeError will be raised if one or more + type codecs modify the encoding behavior of a built-in :mod:`bson` + type. + - `fallback_encoder` (optional): callable that accepts a single, + unencodable python value and transforms it into a type that + :mod:`bson` can encode. See :ref:`fallback-encoder-callable` + documentation for an example. + """ + + def __init__(self, type_codecs=None, fallback_encoder=None): + self.__type_codecs = list(type_codecs or []) + self._fallback_encoder = fallback_encoder + self._encoder_map = {} + self._decoder_map = {} + + if self._fallback_encoder is not None: + if not callable(fallback_encoder): + raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder)) + + for codec in self.__type_codecs: + is_valid_codec = False + if isinstance(codec, TypeEncoder): + self._validate_type_encoder(codec) + is_valid_codec = True + self._encoder_map[codec.python_type] = codec.transform_python + if isinstance(codec, TypeDecoder): + is_valid_codec = True + self._decoder_map[codec.bson_type] = codec.transform_bson + if not is_valid_codec: + raise TypeError( + "Expected an instance of %s, %s, or %s, got %r instead" + % (TypeEncoder.__name__, TypeDecoder.__name__, TypeCodec.__name__, codec) + ) + + def _validate_type_encoder(self, codec): + from . import _BUILT_IN_TYPES + + for pytype in _BUILT_IN_TYPES: + if issubclass(codec.python_type, pytype): + err_msg = ( + "TypeEncoders cannot change how built-in types are " + "encoded (encoder %s transforms type %s)" % (codec, pytype) + ) + raise TypeError(err_msg) + + def __repr__(self): + return "%s(type_codecs=%r, fallback_encoder=%r)" % ( + self.__class__.__name__, + self.__type_codecs, + self._fallback_encoder, + ) + + def __eq__(self, other): + if not isinstance(other, type(self)): + return NotImplemented + return ( + (self._decoder_map == other._decoder_map) + and (self._encoder_map == other._encoder_map) + and (self._fallback_encoder == other._fallback_encoder) + ) + + +_options_base = namedtuple( + "CodecOptions", + ( + "document_class", + "tz_aware", + "uuid_representation", + "unicode_decode_error_handler", + "tzinfo", + "type_registry", + ), +) + + +class CodecOptions(_options_base): + """Encapsulates options used encoding and / or decoding BSON. + + The `document_class` option is used to define a custom type for use + decoding BSON documents. Access to the underlying raw BSON bytes for + a document is available using the :class:`~bson.raw_bson.RawBSONDocument` + type:: + + >>> from .raw_bson import RawBSONDocument + >>> from .codec_options import CodecOptions + >>> codec_options = CodecOptions(document_class=RawBSONDocument) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc.raw + '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00' + + The document class can be any type that inherits from + :class:`~collections.abc.MutableMapping`:: + + >>> class AttributeDict(dict): + ... # A dict that supports attribute access. + ... def __getattr__(self, key): + ... return self[key] + ... def __setattr__(self, key, value): + ... self[key] = value + ... + >>> codec_options = CodecOptions(document_class=AttributeDict) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc._id + ObjectId('5b3016359110ea14e8c58b93') + + See :doc:`/examples/datetimes` for examples using the `tz_aware` and + `tzinfo` options. + + See :doc:`examples/uuid` for examples using the `uuid_representation` + option. + + :Parameters: + - `document_class`: BSON documents returned in queries will be decoded + to an instance of this class. Must be a subclass of + :class:`~collections.abc.MutableMapping`. Defaults to :class:`dict`. + - `tz_aware`: If ``True``, BSON datetimes will be decoded to timezone + aware instances of :class:`~datetime.datetime`. Otherwise they will be + naive. Defaults to ``False``. + - `uuid_representation`: The BSON representation to use when encoding + and decoding instances of :class:`~uuid.UUID`. Defaults to + :data:`~bson.binary.UuidRepresentation.UNSPECIFIED`. New + applications should consider setting this to + :data:`~bson.binary.UuidRepresentation.STANDARD` for cross language + compatibility. See :ref:`handling-uuid-data-example` for details. + - `unicode_decode_error_handler`: The error handler to apply when + a Unicode-related error occurs during BSON decoding that would + otherwise raise :exc:`UnicodeDecodeError`. Valid options include + 'strict', 'replace', 'backslashreplace', 'surrogateescape', and + 'ignore'. Defaults to 'strict'. + - `tzinfo`: A :class:`~datetime.tzinfo` subclass that specifies the + timezone to/from which :class:`~datetime.datetime` objects should be + encoded/decoded. + - `type_registry`: Instance of :class:`TypeRegistry` used to customize + encoding and decoding behavior. + + .. versionchanged:: 4.0 + The default for `uuid_representation` was changed from + :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to + :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + + .. versionadded:: 3.8 + `type_registry` attribute. + + .. warning:: Care must be taken when changing + `unicode_decode_error_handler` from its default value ('strict'). + The 'replace' and 'ignore' modes should not be used when documents + retrieved from the server will be modified in the client application + and stored back to the server. + """ + + def __new__( + cls, + document_class=dict, + tz_aware=False, + uuid_representation=UuidRepresentation.UNSPECIFIED, + unicode_decode_error_handler="strict", + tzinfo=None, + type_registry=None, + ): + if not (issubclass(document_class, _MutableMapping) or _raw_document_class(document_class)): + raise TypeError( + "document_class must be dict, bson.son.SON, " + "bson.raw_bson.RawBSONDocument, or a " + "sublass of collections.abc.MutableMapping" + ) + if not isinstance(tz_aware, bool): + raise TypeError("tz_aware must be True or False") + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value " "from .binary.UuidRepresentation" + ) + if not isinstance(unicode_decode_error_handler, (str, None)): + raise ValueError("unicode_decode_error_handler must be a string " "or None") + if tzinfo is not None: + if not isinstance(tzinfo, datetime.tzinfo): + raise TypeError("tzinfo must be an instance of datetime.tzinfo") + if not tz_aware: + raise ValueError("cannot specify tzinfo without also setting tz_aware=True") + + type_registry = type_registry or TypeRegistry() + + if not isinstance(type_registry, TypeRegistry): + raise TypeError("type_registry must be an instance of TypeRegistry") + + return tuple.__new__( + cls, + ( + document_class, + tz_aware, + uuid_representation, + unicode_decode_error_handler, + tzinfo, + type_registry, + ), + ) + + def _arguments_repr(self): + """Representation of the arguments used to create this object.""" + document_class_repr = "dict" if self.document_class is dict else repr(self.document_class) + + uuid_rep_repr = UUID_REPRESENTATION_NAMES.get( + self.uuid_representation, self.uuid_representation + ) + + return ( + "document_class=%s, tz_aware=%r, uuid_representation=%s, " + "unicode_decode_error_handler=%r, tzinfo=%r, " + "type_registry=%r" + % ( + document_class_repr, + self.tz_aware, + uuid_rep_repr, + self.unicode_decode_error_handler, + self.tzinfo, + self.type_registry, + ) + ) + + def _options_dict(self): + """Dictionary of the arguments used to create this object.""" + # TODO: PYTHON-2442 use _asdict() instead + return { + "document_class": self.document_class, + "tz_aware": self.tz_aware, + "uuid_representation": self.uuid_representation, + "unicode_decode_error_handler": self.unicode_decode_error_handler, + "tzinfo": self.tzinfo, + "type_registry": self.type_registry, + } + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self._arguments_repr()) + + def with_options(self, **kwargs): + """Make a copy of this CodecOptions, overriding some options:: + + >>> from .codec_options import DEFAULT_CODEC_OPTIONS + >>> DEFAULT_CODEC_OPTIONS.tz_aware + False + >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True) + >>> options.tz_aware + True + + .. versionadded:: 3.5 + """ + opts = self._options_dict() + opts.update(kwargs) + return CodecOptions(**opts) + + +DEFAULT_CODEC_OPTIONS = CodecOptions() + + +def _parse_codec_options(options): + """Parse BSON codec options.""" + kwargs = {} + for k in set(options) & { + "document_class", + "tz_aware", + "uuidrepresentation", + "unicode_decode_error_handler", + "tzinfo", + "type_registry", + }: + if k == "uuidrepresentation": + kwargs["uuid_representation"] = options[k] + else: + kwargs[k] = options[k] + return CodecOptions(**kwargs) diff --git a/src/xtquant/xtbson/bson36/dbref.py b/src/xtquant/xtbson/bson36/dbref.py new file mode 100644 index 0000000..1a0f369 --- /dev/null +++ b/src/xtquant/xtbson/bson36/dbref.py @@ -0,0 +1,125 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for manipulating DBRefs (references to MongoDB documents).""" + +from copy import deepcopy + +from ._helpers import _getstate_slots, _setstate_slots +from .son import SON + + +class DBRef(object): + """A reference to a document stored in MongoDB.""" + + __slots__ = "__collection", "__id", "__database", "__kwargs" + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. + _type_marker = 100 + + def __init__(self, collection, id, database=None, _extra={}, **kwargs): + """Initialize a new :class:`DBRef`. + + Raises :class:`TypeError` if `collection` or `database` is not + an instance of :class:`basestring` (:class:`str` in python 3). + `database` is optional and allows references to documents to work + across databases. Any additional keyword arguments will create + additional fields in the resultant embedded document. + + :Parameters: + - `collection`: name of the collection the document is stored in + - `id`: the value of the document's ``"_id"`` field + - `database` (optional): name of the database to reference + - `**kwargs` (optional): additional keyword arguments will + create additional, custom fields + + .. seealso:: The MongoDB documentation on `dbrefs `_. + """ + if not isinstance(collection, str): + raise TypeError("collection must be an instance of str") + if database is not None and not isinstance(database, str): + raise TypeError("database must be an instance of str") + + self.__collection = collection + self.__id = id + self.__database = database + kwargs.update(_extra) + self.__kwargs = kwargs + + @property + def collection(self): + """Get the name of this DBRef's collection.""" + return self.__collection + + @property + def id(self): + """Get this DBRef's _id.""" + return self.__id + + @property + def database(self): + """Get the name of this DBRef's database. + + Returns None if this DBRef doesn't specify a database. + """ + return self.__database + + def __getattr__(self, key): + try: + return self.__kwargs[key] + except KeyError: + raise AttributeError(key) + + def as_doc(self): + """Get the SON document representation of this DBRef. + + Generally not needed by application developers + """ + doc = SON([("$ref", self.collection), ("$id", self.id)]) + if self.database is not None: + doc["$db"] = self.database + doc.update(self.__kwargs) + return doc + + def __repr__(self): + extra = "".join([", %s=%r" % (k, v) for k, v in self.__kwargs.items()]) + if self.database is None: + return "DBRef(%r, %r%s)" % (self.collection, self.id, extra) + return "DBRef(%r, %r, %r%s)" % (self.collection, self.id, self.database, extra) + + def __eq__(self, other): + if isinstance(other, DBRef): + us = (self.__database, self.__collection, self.__id, self.__kwargs) + them = (other.__database, other.__collection, other.__id, other.__kwargs) + return us == them + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __hash__(self): + """Get a hash value for this :class:`DBRef`.""" + return hash( + (self.__collection, self.__id, self.__database, tuple(sorted(self.__kwargs.items()))) + ) + + def __deepcopy__(self, memo): + """Support function for `copy.deepcopy()`.""" + return DBRef( + deepcopy(self.__collection, memo), + deepcopy(self.__id, memo), + deepcopy(self.__database, memo), + deepcopy(self.__kwargs, memo), + ) diff --git a/src/xtquant/xtbson/bson36/decimal128.py b/src/xtquant/xtbson/bson36/decimal128.py new file mode 100644 index 0000000..db0a067 --- /dev/null +++ b/src/xtquant/xtbson/bson36/decimal128.py @@ -0,0 +1,315 @@ +# Copyright 2016-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with the BSON decimal128 type. + +.. versionadded:: 3.4 + +.. note:: The Decimal128 BSON type requires MongoDB 3.4+. +""" + +import decimal +import struct +import sys + +_PACK_64 = struct.Struct("> 49 == 1: + high = high & 0x7FFFFFFFFFFF + high |= _EXPONENT_MASK + high |= (biased_exponent & 0x3FFF) << 47 + else: + high |= biased_exponent << 49 + + if sign: + high |= _SIGN + + return high, low + + +class Decimal128(object): + """BSON Decimal128 type:: + + >>> Decimal128(Decimal("0.0005")) + Decimal128('0.0005') + >>> Decimal128("0.0005") + Decimal128('0.0005') + >>> Decimal128((3474527112516337664, 5)) + Decimal128('0.0005') + + :Parameters: + - `value`: An instance of :class:`decimal.Decimal`, string, or tuple of + (high bits, low bits) from Binary Integer Decimal (BID) format. + + .. note:: :class:`~Decimal128` uses an instance of :class:`decimal.Context` + configured for IEEE-754 Decimal128 when validating parameters. + Signals like :class:`decimal.InvalidOperation`, :class:`decimal.Inexact`, + and :class:`decimal.Overflow` are trapped and raised as exceptions:: + + >>> Decimal128(".13.1") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.InvalidOperation: [] + >>> + >>> Decimal128("1E-6177") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Inexact: [] + >>> + >>> Decimal128("1E6145") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Overflow: [, ] + + To ensure the result of a calculation can always be stored as BSON + Decimal128 use the context returned by + :func:`create_decimal128_context`:: + + >>> import decimal + >>> decimal128_ctx = create_decimal128_context() + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal(".13.3")) + ... + Decimal128('NaN') + >>> + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal("1E-6177")) + ... + Decimal128('0E-6176') + >>> + >>> with decimal.localcontext(DECIMAL128_CTX) as ctx: + ... Decimal128(ctx.create_decimal("1E6145")) + ... + Decimal128('Infinity') + + To match the behavior of MongoDB's Decimal128 implementation + str(Decimal(value)) may not match str(Decimal128(value)) for NaN values:: + + >>> Decimal128(Decimal('NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('sNaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-sNaN')) + Decimal128('NaN') + + However, :meth:`~Decimal128.to_decimal` will return the exact value:: + + >>> Decimal128(Decimal('NaN')).to_decimal() + Decimal('NaN') + >>> Decimal128(Decimal('-NaN')).to_decimal() + Decimal('-NaN') + >>> Decimal128(Decimal('sNaN')).to_decimal() + Decimal('sNaN') + >>> Decimal128(Decimal('-sNaN')).to_decimal() + Decimal('-sNaN') + + Two instances of :class:`Decimal128` compare equal if their Binary + Integer Decimal encodings are equal:: + + >>> Decimal128('NaN') == Decimal128('NaN') + True + >>> Decimal128('NaN').bid == Decimal128('NaN').bid + True + + This differs from :class:`decimal.Decimal` comparisons for NaN:: + + >>> Decimal('NaN') == Decimal('NaN') + False + """ + + __slots__ = ("__high", "__low") + + _type_marker = 19 + + def __init__(self, value): + if isinstance(value, (str, decimal.Decimal)): + self.__high, self.__low = _decimal_to_128(value) + elif isinstance(value, (list, tuple)): + if len(value) != 2: + raise ValueError( + "Invalid size for creation of Decimal128 " + "from list or tuple. Must have exactly 2 " + "elements." + ) + self.__high, self.__low = value + else: + raise TypeError("Cannot convert %r to Decimal128" % (value,)) + + def to_decimal(self): + """Returns an instance of :class:`decimal.Decimal` for this + :class:`Decimal128`. + """ + high = self.__high + low = self.__low + sign = 1 if (high & _SIGN) else 0 + + if (high & _SNAN) == _SNAN: + return decimal.Decimal((sign, (), "N")) + elif (high & _NAN) == _NAN: + return decimal.Decimal((sign, (), "n")) + elif (high & _INF) == _INF: + return decimal.Decimal((sign, (), "F")) + + if (high & _EXPONENT_MASK) == _EXPONENT_MASK: + exponent = ((high & 0x1FFFE00000000000) >> 47) - _EXPONENT_BIAS + return decimal.Decimal((sign, (0,), exponent)) + else: + exponent = ((high & 0x7FFF800000000000) >> 49) - _EXPONENT_BIAS + + arr = bytearray(15) + mask = 0x00000000000000FF + for i in range(14, 6, -1): + arr[i] = (low & mask) >> ((14 - i) << 3) + mask = mask << 8 + + mask = 0x00000000000000FF + for i in range(6, 0, -1): + arr[i] = (high & mask) >> ((6 - i) << 3) + mask = mask << 8 + + mask = 0x0001000000000000 + arr[0] = (high & mask) >> 48 + + # cdecimal only accepts a tuple for digits. + digits = tuple(int(digit) for digit in str(int.from_bytes(arr, "big"))) + + with decimal.localcontext(_DEC128_CTX) as ctx: + return ctx.create_decimal((sign, digits, exponent)) + + @classmethod + def from_bid(cls, value): + """Create an instance of :class:`Decimal128` from Binary Integer + Decimal string. + + :Parameters: + - `value`: 16 byte string (128-bit IEEE 754-2008 decimal floating + point in Binary Integer Decimal (BID) format). + """ + if not isinstance(value, bytes): + raise TypeError("value must be an instance of bytes") + if len(value) != 16: + raise ValueError("value must be exactly 16 bytes") + return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) + + @property + def bid(self): + """The Binary Integer Decimal (BID) encoding of this instance.""" + return _PACK_64(self.__low) + _PACK_64(self.__high) + + def __str__(self): + dec = self.to_decimal() + if dec.is_nan(): + # Required by the drivers spec to match MongoDB behavior. + return "NaN" + return str(dec) + + def __repr__(self): + return "Decimal128('%s')" % (str(self),) + + def __setstate__(self, value): + self.__high, self.__low = value + + def __getstate__(self): + return self.__high, self.__low + + def __eq__(self, other): + if isinstance(other, Decimal128): + return self.bid == other.bid + return NotImplemented + + def __ne__(self, other): + return not self == other diff --git a/src/xtquant/xtbson/bson36/errors.py b/src/xtquant/xtbson/bson36/errors.py new file mode 100644 index 0000000..7333b27 --- /dev/null +++ b/src/xtquant/xtbson/bson36/errors.py @@ -0,0 +1,35 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Exceptions raised by the BSON package.""" + + +class BSONError(Exception): + """Base class for all BSON exceptions.""" + + +class InvalidBSON(BSONError): + """Raised when trying to create a BSON object from invalid data.""" + + +class InvalidStringData(BSONError): + """Raised when trying to encode a string containing non-UTF8 data.""" + + +class InvalidDocument(BSONError): + """Raised when trying to create a BSON object from an invalid document.""" + + +class InvalidId(BSONError): + """Raised when trying to create an ObjectId from invalid data.""" diff --git a/src/xtquant/xtbson/bson36/int64.py b/src/xtquant/xtbson/bson36/int64.py new file mode 100644 index 0000000..d40b195 --- /dev/null +++ b/src/xtquant/xtbson/bson36/int64.py @@ -0,0 +1,37 @@ +# Copyright 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A BSON wrapper for long (int in python3)""" + + +class Int64(int): + """Representation of the BSON int64 type. + + This is necessary because every integral number is an :class:`int` in + Python 3. Small integral numbers are encoded to BSON int32 by default, + but Int64 numbers will always be encoded to BSON int64. + + :Parameters: + - `value`: the numeric value to represent + """ + + __slots__ = () + + _type_marker = 18 + + def __getstate__(self): + return {} + + def __setstate__(self, state): + pass diff --git a/src/xtquant/xtbson/bson36/json_util.py b/src/xtquant/xtbson/bson36/json_util.py new file mode 100644 index 0000000..973e2a3 --- /dev/null +++ b/src/xtquant/xtbson/bson36/json_util.py @@ -0,0 +1,861 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for using Python's :mod:`json` module with BSON documents. + +This module provides two helper methods `dumps` and `loads` that wrap the +native :mod:`json` methods and provide explicit BSON conversion to and from +JSON. :class:`~bson.json_util.JSONOptions` provides a way to control how JSON +is emitted and parsed, with the default being the Relaxed Extended JSON format. +:mod:`~bson.json_util` can also generate Canonical or legacy `Extended JSON`_ +when :const:`CANONICAL_JSON_OPTIONS` or :const:`LEGACY_JSON_OPTIONS` is +provided, respectively. + +.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json.rst + +Example usage (deserialization): + +.. doctest:: + + >>> from .json_util import loads + >>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]') + [{'foo': [1, 2]}, {'bar': {'hello': 'world'}}, {'code': Code('function x() { return 1; }', {})}, {'bin': Binary(b'...', 128)}] + +Example usage with :const:`RELAXED_JSON_OPTIONS` (the default): + +.. doctest:: + + >>> from . import Binary, Code + >>> from .json_util import dumps + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }")}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}]) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`CANONICAL_JSON_OPTIONS`): + +.. doctest:: + + >>> from . import Binary, Code + >>> from .json_util import dumps, CANONICAL_JSON_OPTIONS + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }")}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}], + ... json_options=CANONICAL_JSON_OPTIONS) + '[{"foo": [{"$numberInt": "1"}, {"$numberInt": "2"}]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`LEGACY_JSON_OPTIONS`): + +.. doctest:: + + >>> from . import Binary, Code + >>> from .json_util import dumps, LEGACY_JSON_OPTIONS + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }", {})}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}], + ... json_options=LEGACY_JSON_OPTIONS) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]' + +Alternatively, you can manually pass the `default` to :func:`json.dumps`. +It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code` +instances (as they are extended strings you can't provide custom defaults), +but it will be faster as there is less recursion. + +.. note:: + If your application does not need the flexibility offered by + :class:`JSONOptions` and spends a large amount of time in the `json_util` + module, look to + `python-bsonjs `_ for a nice + performance improvement. `python-bsonjs` is a fast BSON to MongoDB + Extended JSON converter for Python built on top of + `libbson `_. `python-bsonjs` works best + with PyMongo when using :class:`~bson.raw_bson.RawBSONDocument`. +""" + +import base64 +import datetime +import json +import math +import re +import uuid + +import bson +from . import EPOCH_AWARE, RE_TYPE, SON +from .binary import ALL_UUID_SUBTYPES, UUID_SUBTYPE, Binary, UuidRepresentation +from .code import Code +from .codec_options import CodecOptions +from .dbref import DBRef +from .decimal128 import Decimal128 +from .int64 import Int64 +from .max_key import MaxKey +from .min_key import MinKey +from .objectid import ObjectId +from .regex import Regex +from .timestamp import Timestamp +from .tz_util import utc + +_RE_OPT_TABLE = { + "i": re.I, + "l": re.L, + "m": re.M, + "s": re.S, + "u": re.U, + "x": re.X, +} + + +class DatetimeRepresentation: + LEGACY = 0 + """Legacy MongoDB Extended JSON datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": }`, where `dateAsMilliseconds` is + a 64-bit signed integer giving the number of milliseconds since the Unix + epoch UTC. This was the default encoding before PyMongo version 3.4. + + .. versionadded:: 3.4 + """ + + NUMBERLONG = 1 + """NumberLong datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": {"$numberLong": ""}}`, + where `dateAsMilliseconds` is the string representation of a 64-bit signed + integer giving the number of milliseconds since the Unix epoch UTC. + + .. versionadded:: 3.4 + """ + + ISO8601 = 2 + """ISO-8601 datetime representation. + + :class:`datetime.datetime` instances greater than or equal to the Unix + epoch UTC will be encoded to JSON in the format `{"$date": ""}`. + :class:`datetime.datetime` instances before the Unix epoch UTC will be + encoded as if the datetime representation is + :const:`~DatetimeRepresentation.NUMBERLONG`. + + .. versionadded:: 3.4 + """ + + +class JSONMode: + LEGACY = 0 + """Legacy Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces PyMongo's legacy + non-standard JSON output. Consider using + :const:`~bson.json_util.JSONMode.RELAXED` or + :const:`~bson.json_util.JSONMode.CANONICAL` instead. + + .. versionadded:: 3.5 + """ + + RELAXED = 1 + """Relaxed Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Relaxed Extended JSON, + a mostly JSON-like format. Consider using this for things like a web API, + where one is sending a document (or a projection of a document) that only + uses ordinary JSON type primitives. In particular, the ``int``, + :class:`~bson.int64.Int64`, and ``float`` numeric types are represented in + the native JSON number format. This output is also the most human readable + and is useful for debugging and documentation. + + .. seealso:: The specification for Relaxed `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + CANONICAL = 2 + """Canonical Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Canonical Extended + JSON, a type preserving format. Consider using this for things like + testing, where one has to precisely specify expected types in JSON. In + particular, the ``int``, :class:`~bson.int64.Int64`, and ``float`` numeric + types are encoded with type wrappers. + + .. seealso:: The specification for Canonical `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + +class JSONOptions(CodecOptions): + """Encapsulates JSON options for :func:`dumps` and :func:`loads`. + + :Parameters: + - `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects + are encoded to MongoDB Extended JSON's *Strict mode* type + `NumberLong`, ie ``'{"$numberLong": "" }'``. Otherwise they + will be encoded as an `int`. Defaults to ``False``. + - `datetime_representation`: The representation to use when encoding + instances of :class:`datetime.datetime`. Defaults to + :const:`~DatetimeRepresentation.LEGACY`. + - `strict_uuid`: If ``True``, :class:`uuid.UUID` object are encoded to + MongoDB Extended JSON's *Strict mode* type `Binary`. Otherwise it + will be encoded as ``'{"$uuid": "" }'``. Defaults to ``False``. + - `json_mode`: The :class:`JSONMode` to use when encoding BSON types to + Extended JSON. Defaults to :const:`~JSONMode.LEGACY`. + - `document_class`: BSON documents returned by :func:`loads` will be + decoded to an instance of this class. Must be a subclass of + :class:`collections.MutableMapping`. Defaults to :class:`dict`. + - `uuid_representation`: The :class:`~bson.binary.UuidRepresentation` + to use when encoding and decoding instances of :class:`uuid.UUID`. + Defaults to :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + - `tz_aware`: If ``True``, MongoDB Extended JSON's *Strict mode* type + `Date` will be decoded to timezone aware instances of + :class:`datetime.datetime`. Otherwise they will be naive. Defaults + to ``False``. + - `tzinfo`: A :class:`datetime.tzinfo` subclass that specifies the + timezone from which :class:`~datetime.datetime` objects should be + decoded. Defaults to :const:`~bson.tz_util.utc`. + - `args`: arguments to :class:`~bson.codec_options.CodecOptions` + - `kwargs`: arguments to :class:`~bson.codec_options.CodecOptions` + + .. seealso:: The specification for Relaxed and Canonical `Extended JSON`_. + + .. versionchanged:: 4.0 + The default for `json_mode` was changed from :const:`JSONMode.LEGACY` + to :const:`JSONMode.RELAXED`. + The default for `uuid_representation` was changed from + :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to + :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + + .. versionchanged:: 3.5 + Accepts the optional parameter `json_mode`. + + .. versionchanged:: 4.0 + Changed default value of `tz_aware` to False. + """ + + def __new__( + cls, + strict_number_long=None, + datetime_representation=None, + strict_uuid=None, + json_mode=JSONMode.RELAXED, + *args, + **kwargs + ): + kwargs["tz_aware"] = kwargs.get("tz_aware", False) + if kwargs["tz_aware"]: + kwargs["tzinfo"] = kwargs.get("tzinfo", utc) + if datetime_representation not in ( + DatetimeRepresentation.LEGACY, + DatetimeRepresentation.NUMBERLONG, + DatetimeRepresentation.ISO8601, + None, + ): + raise ValueError( + "JSONOptions.datetime_representation must be one of LEGACY, " + "NUMBERLONG, or ISO8601 from DatetimeRepresentation." + ) + self = super(JSONOptions, cls).__new__(cls, *args, **kwargs) + if json_mode not in (JSONMode.LEGACY, JSONMode.RELAXED, JSONMode.CANONICAL): + raise ValueError( + "JSONOptions.json_mode must be one of LEGACY, RELAXED, " + "or CANONICAL from JSONMode." + ) + self.json_mode = json_mode + if self.json_mode == JSONMode.RELAXED: + if strict_number_long: + raise ValueError("Cannot specify strict_number_long=True with" " JSONMode.RELAXED") + if datetime_representation not in (None, DatetimeRepresentation.ISO8601): + raise ValueError( + "datetime_representation must be DatetimeRepresentation." + "ISO8601 or omitted with JSONMode.RELAXED" + ) + if strict_uuid not in (None, True): + raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.ISO8601 + self.strict_uuid = True + elif self.json_mode == JSONMode.CANONICAL: + if strict_number_long not in (None, True): + raise ValueError("Cannot specify strict_number_long=False with" " JSONMode.RELAXED") + if datetime_representation not in (None, DatetimeRepresentation.NUMBERLONG): + raise ValueError( + "datetime_representation must be DatetimeRepresentation." + "NUMBERLONG or omitted with JSONMode.RELAXED" + ) + if strict_uuid not in (None, True): + raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") + self.strict_number_long = True + self.datetime_representation = DatetimeRepresentation.NUMBERLONG + self.strict_uuid = True + else: # JSONMode.LEGACY + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.LEGACY + self.strict_uuid = False + if strict_number_long is not None: + self.strict_number_long = strict_number_long + if datetime_representation is not None: + self.datetime_representation = datetime_representation + if strict_uuid is not None: + self.strict_uuid = strict_uuid + return self + + def _arguments_repr(self): + return ( + "strict_number_long=%r, " + "datetime_representation=%r, " + "strict_uuid=%r, json_mode=%r, %s" + % ( + self.strict_number_long, + self.datetime_representation, + self.strict_uuid, + self.json_mode, + super(JSONOptions, self)._arguments_repr(), + ) + ) + + def _options_dict(self): + # TODO: PYTHON-2442 use _asdict() instead + options_dict = super(JSONOptions, self)._options_dict() + options_dict.update( + { + "strict_number_long": self.strict_number_long, + "datetime_representation": self.datetime_representation, + "strict_uuid": self.strict_uuid, + "json_mode": self.json_mode, + } + ) + return options_dict + + def with_options(self, **kwargs): + """ + Make a copy of this JSONOptions, overriding some options:: + + >>> from .json_util import CANONICAL_JSON_OPTIONS + >>> CANONICAL_JSON_OPTIONS.tz_aware + True + >>> json_options = CANONICAL_JSON_OPTIONS.with_options(tz_aware=False, tzinfo=None) + >>> json_options.tz_aware + False + + .. versionadded:: 3.12 + """ + opts = self._options_dict() + for opt in ("strict_number_long", "datetime_representation", "strict_uuid", "json_mode"): + opts[opt] = kwargs.get(opt, getattr(self, opt)) + opts.update(kwargs) + return JSONOptions(**opts) + + +LEGACY_JSON_OPTIONS = JSONOptions(json_mode=JSONMode.LEGACY) +""":class:`JSONOptions` for encoding to PyMongo's legacy JSON format. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.LEGACY`. + +.. versionadded:: 3.5 +""" + +CANONICAL_JSON_OPTIONS = JSONOptions(json_mode=JSONMode.CANONICAL) +""":class:`JSONOptions` for Canonical Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.CANONICAL`. + +.. versionadded:: 3.5 +""" + +RELAXED_JSON_OPTIONS = JSONOptions(json_mode=JSONMode.RELAXED) +""":class:`JSONOptions` for Relaxed Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.RELAXED`. + +.. versionadded:: 3.5 +""" + +DEFAULT_JSON_OPTIONS = RELAXED_JSON_OPTIONS +"""The default :class:`JSONOptions` for JSON encoding/decoding. + +The same as :const:`RELAXED_JSON_OPTIONS`. + +.. versionchanged:: 4.0 + Changed from :const:`LEGACY_JSON_OPTIONS` to + :const:`RELAXED_JSON_OPTIONS`. + +.. versionadded:: 3.4 +""" + + +def dumps(obj, *args, **kwargs): + """Helper function that wraps :func:`json.dumps`. + + Recursive function that handles all BSON types including + :class:`~bson.binary.Binary` and :class:`~bson.code.Code`. + + :Parameters: + - `json_options`: A :class:`JSONOptions` instance used to modify the + encoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 4.0 + Now outputs MongoDB Relaxed Extended JSON by default (using + :const:`DEFAULT_JSON_OPTIONS`). + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + return json.dumps(_json_convert(obj, json_options), *args, **kwargs) + + +def loads(s, *args, **kwargs): + """Helper function that wraps :func:`json.loads`. + + Automatically passes the object_hook for BSON type conversion. + + Raises ``TypeError``, ``ValueError``, ``KeyError``, or + :exc:`~bson.errors.InvalidId` on invalid MongoDB Extended JSON. + + :Parameters: + - `json_options`: A :class:`JSONOptions` instance used to modify the + decoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 3.5 + Parses Relaxed and Canonical Extended JSON as well as PyMongo's legacy + format. Now raises ``TypeError`` or ``ValueError`` when parsing JSON + type wrappers with values of the wrong type or any extra keys. + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs, json_options) + return json.loads(s, *args, **kwargs) + + +def _json_convert(obj, json_options=DEFAULT_JSON_OPTIONS): + """Recursive helper method that converts BSON types so they can be + converted into json. + """ + if hasattr(obj, "items"): + return SON(((k, _json_convert(v, json_options)) for k, v in obj.items())) + elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)): + return list((_json_convert(v, json_options) for v in obj)) + try: + return default(obj, json_options) + except TypeError: + return obj + + +def object_pairs_hook(pairs, json_options=DEFAULT_JSON_OPTIONS): + return object_hook(json_options.document_class(pairs), json_options) + + +def object_hook(dct, json_options=DEFAULT_JSON_OPTIONS): + if "$oid" in dct: + return _parse_canonical_oid(dct) + if ( + isinstance(dct.get("$ref"), str) + and "$id" in dct + and isinstance(dct.get("$db"), (str, type(None))) + ): + return _parse_canonical_dbref(dct) + if "$date" in dct: + return _parse_canonical_datetime(dct, json_options) + if "$regex" in dct: + return _parse_legacy_regex(dct) + if "$minKey" in dct: + return _parse_canonical_minkey(dct) + if "$maxKey" in dct: + return _parse_canonical_maxkey(dct) + if "$binary" in dct: + if "$type" in dct: + return _parse_legacy_binary(dct, json_options) + else: + return _parse_canonical_binary(dct, json_options) + if "$code" in dct: + return _parse_canonical_code(dct) + if "$uuid" in dct: + return _parse_legacy_uuid(dct, json_options) + if "$undefined" in dct: + return None + if "$numberLong" in dct: + return _parse_canonical_int64(dct) + if "$timestamp" in dct: + tsp = dct["$timestamp"] + return Timestamp(tsp["t"], tsp["i"]) + if "$numberDecimal" in dct: + return _parse_canonical_decimal128(dct) + if "$dbPointer" in dct: + return _parse_canonical_dbpointer(dct) + if "$regularExpression" in dct: + return _parse_canonical_regex(dct) + if "$symbol" in dct: + return _parse_canonical_symbol(dct) + if "$numberInt" in dct: + return _parse_canonical_int32(dct) + if "$numberDouble" in dct: + return _parse_canonical_double(dct) + return dct + + +def _parse_legacy_regex(doc): + pattern = doc["$regex"] + # Check if this is the $regex query operator. + if not isinstance(pattern, (str, bytes)): + return doc + flags = 0 + # PyMongo always adds $options but some other tools may not. + for opt in doc.get("$options", ""): + flags |= _RE_OPT_TABLE.get(opt, 0) + return Regex(pattern, flags) + + +def _parse_legacy_uuid(doc, json_options): + """Decode a JSON legacy $uuid to Python UUID.""" + if len(doc) != 1: + raise TypeError("Bad $uuid, extra field(s): %s" % (doc,)) + if not isinstance(doc["$uuid"], str): + raise TypeError("$uuid must be a string: %s" % (doc,)) + if json_options.uuid_representation == UuidRepresentation.UNSPECIFIED: + return Binary.from_uuid(uuid.UUID(doc["$uuid"])) + else: + return uuid.UUID(doc["$uuid"]) + + +def _binary_or_uuid(data, subtype, json_options): + # special handling for UUID + if subtype in ALL_UUID_SUBTYPES: + uuid_representation = json_options.uuid_representation + binary_value = Binary(data, subtype) + if uuid_representation == UuidRepresentation.UNSPECIFIED: + return binary_value + if subtype == UUID_SUBTYPE: + # Legacy behavior: use STANDARD with binary subtype 4. + uuid_representation = UuidRepresentation.STANDARD + elif uuid_representation == UuidRepresentation.STANDARD: + # subtype == OLD_UUID_SUBTYPE + # Legacy behavior: STANDARD is the same as PYTHON_LEGACY. + uuid_representation = UuidRepresentation.PYTHON_LEGACY + return binary_value.as_uuid(uuid_representation) + + if subtype == 0: + return data + return Binary(data, subtype) + + +def _parse_legacy_binary(doc, json_options): + if isinstance(doc["$type"], int): + doc["$type"] = "%02x" % doc["$type"] + subtype = int(doc["$type"], 16) + if subtype >= 0xFFFFFF80: # Handle mongoexport values + subtype = int(doc["$type"][6:], 16) + data = base64.b64decode(doc["$binary"].encode()) + return _binary_or_uuid(data, subtype, json_options) + + +def _parse_canonical_binary(doc, json_options): + binary = doc["$binary"] + b64 = binary["base64"] + subtype = binary["subType"] + if not isinstance(b64, str): + raise TypeError("$binary base64 must be a string: %s" % (doc,)) + if not isinstance(subtype, str) or len(subtype) > 2: + raise TypeError("$binary subType must be a string at most 2 " "characters: %s" % (doc,)) + if len(binary) != 2: + raise TypeError( + '$binary must include only "base64" and "subType" ' "components: %s" % (doc,) + ) + + data = base64.b64decode(b64.encode()) + return _binary_or_uuid(data, int(subtype, 16), json_options) + + +def _parse_canonical_datetime(doc, json_options): + """Decode a JSON datetime to python datetime.datetime.""" + dtm = doc["$date"] + if len(doc) != 1: + raise TypeError("Bad $date, extra field(s): %s" % (doc,)) + # mongoexport 2.6 and newer + if isinstance(dtm, str): + # Parse offset + if dtm[-1] == "Z": + dt = dtm[:-1] + offset = "Z" + elif dtm[-6] in ("+", "-") and dtm[-3] == ":": + # (+|-)HH:MM + dt = dtm[:-6] + offset = dtm[-6:] + elif dtm[-5] in ("+", "-"): + # (+|-)HHMM + dt = dtm[:-5] + offset = dtm[-5:] + elif dtm[-3] in ("+", "-"): + # (+|-)HH + dt = dtm[:-3] + offset = dtm[-3:] + else: + dt = dtm + offset = "" + + # Parse the optional factional seconds portion. + dot_index = dt.rfind(".") + microsecond = 0 + if dot_index != -1: + microsecond = int(float(dt[dot_index:]) * 1000000) + dt = dt[:dot_index] + + aware = datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S").replace( + microsecond=microsecond, tzinfo=utc + ) + + if offset and offset != "Z": + if len(offset) == 6: + hours, minutes = offset[1:].split(":") + secs = int(hours) * 3600 + int(minutes) * 60 + elif len(offset) == 5: + secs = int(offset[1:3]) * 3600 + int(offset[3:]) * 60 + elif len(offset) == 3: + secs = int(offset[1:3]) * 3600 + if offset[0] == "-": + secs *= -1 + aware = aware - datetime.timedelta(seconds=secs) + + if json_options.tz_aware: + if json_options.tzinfo: + aware = aware.astimezone(json_options.tzinfo) + return aware + else: + return aware.replace(tzinfo=None) + return bson._millis_to_datetime(int(dtm), json_options) + + +def _parse_canonical_oid(doc): + """Decode a JSON ObjectId to bson.objectid.ObjectId.""" + if len(doc) != 1: + raise TypeError("Bad $oid, extra field(s): %s" % (doc,)) + return ObjectId(doc["$oid"]) + + +def _parse_canonical_symbol(doc): + """Decode a JSON symbol to Python string.""" + symbol = doc["$symbol"] + if len(doc) != 1: + raise TypeError("Bad $symbol, extra field(s): %s" % (doc,)) + return str(symbol) + + +def _parse_canonical_code(doc): + """Decode a JSON code to bson.code.Code.""" + for key in doc: + if key not in ("$code", "$scope"): + raise TypeError("Bad $code, extra field(s): %s" % (doc,)) + return Code(doc["$code"], scope=doc.get("$scope")) + + +def _parse_canonical_regex(doc): + """Decode a JSON regex to bson.regex.Regex.""" + regex = doc["$regularExpression"] + if len(doc) != 1: + raise TypeError("Bad $regularExpression, extra field(s): %s" % (doc,)) + if len(regex) != 2: + raise TypeError( + 'Bad $regularExpression must include only "pattern"' + 'and "options" components: %s' % (doc,) + ) + opts = regex["options"] + if not isinstance(opts, str): + raise TypeError( + "Bad $regularExpression options, options must be " "string, was type %s" % (type(opts)) + ) + return Regex(regex["pattern"], opts) + + +def _parse_canonical_dbref(doc): + """Decode a JSON DBRef to bson.dbref.DBRef.""" + return DBRef(doc.pop("$ref"), doc.pop("$id"), database=doc.pop("$db", None), **doc) + + +def _parse_canonical_dbpointer(doc): + """Decode a JSON (deprecated) DBPointer to bson.dbref.DBRef.""" + dbref = doc["$dbPointer"] + if len(doc) != 1: + raise TypeError("Bad $dbPointer, extra field(s): %s" % (doc,)) + if isinstance(dbref, DBRef): + dbref_doc = dbref.as_doc() + # DBPointer must not contain $db in its value. + if dbref.database is not None: + raise TypeError("Bad $dbPointer, extra field $db: %s" % (dbref_doc,)) + if not isinstance(dbref.id, ObjectId): + raise TypeError("Bad $dbPointer, $id must be an ObjectId: %s" % (dbref_doc,)) + if len(dbref_doc) != 2: + raise TypeError("Bad $dbPointer, extra field(s) in DBRef: %s" % (dbref_doc,)) + return dbref + else: + raise TypeError("Bad $dbPointer, expected a DBRef: %s" % (doc,)) + + +def _parse_canonical_int32(doc): + """Decode a JSON int32 to python int.""" + i_str = doc["$numberInt"] + if len(doc) != 1: + raise TypeError("Bad $numberInt, extra field(s): %s" % (doc,)) + if not isinstance(i_str, str): + raise TypeError("$numberInt must be string: %s" % (doc,)) + return int(i_str) + + +def _parse_canonical_int64(doc): + """Decode a JSON int64 to bson.int64.Int64.""" + l_str = doc["$numberLong"] + if len(doc) != 1: + raise TypeError("Bad $numberLong, extra field(s): %s" % (doc,)) + return Int64(l_str) + + +def _parse_canonical_double(doc): + """Decode a JSON double to python float.""" + d_str = doc["$numberDouble"] + if len(doc) != 1: + raise TypeError("Bad $numberDouble, extra field(s): %s" % (doc,)) + if not isinstance(d_str, str): + raise TypeError("$numberDouble must be string: %s" % (doc,)) + return float(d_str) + + +def _parse_canonical_decimal128(doc): + """Decode a JSON decimal128 to bson.decimal128.Decimal128.""" + d_str = doc["$numberDecimal"] + if len(doc) != 1: + raise TypeError("Bad $numberDecimal, extra field(s): %s" % (doc,)) + if not isinstance(d_str, str): + raise TypeError("$numberDecimal must be string: %s" % (doc,)) + return Decimal128(d_str) + + +def _parse_canonical_minkey(doc): + """Decode a JSON MinKey to bson.min_key.MinKey.""" + if type(doc["$minKey"]) is not int or doc["$minKey"] != 1: + raise TypeError("$minKey value must be 1: %s" % (doc,)) + if len(doc) != 1: + raise TypeError("Bad $minKey, extra field(s): %s" % (doc,)) + return MinKey() + + +def _parse_canonical_maxkey(doc): + """Decode a JSON MaxKey to bson.max_key.MaxKey.""" + if type(doc["$maxKey"]) is not int or doc["$maxKey"] != 1: + raise TypeError("$maxKey value must be 1: %s", (doc,)) + if len(doc) != 1: + raise TypeError("Bad $minKey, extra field(s): %s" % (doc,)) + return MaxKey() + + +def _encode_binary(data, subtype, json_options): + if json_options.json_mode == JSONMode.LEGACY: + return SON([("$binary", base64.b64encode(data).decode()), ("$type", "%02x" % subtype)]) + return { + "$binary": SON([("base64", base64.b64encode(data).decode()), ("subType", "%02x" % subtype)]) + } + + +def default(obj, json_options=DEFAULT_JSON_OPTIONS): + # We preserve key order when rendering SON, DBRef, etc. as JSON by + # returning a SON for those types instead of a dict. + if isinstance(obj, ObjectId): + return {"$oid": str(obj)} + if isinstance(obj, DBRef): + return _json_convert(obj.as_doc(), json_options=json_options) + if isinstance(obj, datetime.datetime): + if json_options.datetime_representation == DatetimeRepresentation.ISO8601: + if not obj.tzinfo: + obj = obj.replace(tzinfo=utc) + if obj >= EPOCH_AWARE: + off = obj.tzinfo.utcoffset(obj) + if (off.days, off.seconds, off.microseconds) == (0, 0, 0): + tz_string = "Z" + else: + tz_string = obj.strftime("%z") + millis = int(obj.microsecond / 1000) + fracsecs = ".%03d" % (millis,) if millis else "" + return { + "$date": "%s%s%s" % (obj.strftime("%Y-%m-%dT%H:%M:%S"), fracsecs, tz_string) + } + + millis = bson._datetime_to_millis(obj) + if json_options.datetime_representation == DatetimeRepresentation.LEGACY: + return {"$date": millis} + return {"$date": {"$numberLong": str(millis)}} + if json_options.strict_number_long and isinstance(obj, Int64): + return {"$numberLong": str(obj)} + if isinstance(obj, (RE_TYPE, Regex)): + flags = "" + if obj.flags & re.IGNORECASE: + flags += "i" + if obj.flags & re.LOCALE: + flags += "l" + if obj.flags & re.MULTILINE: + flags += "m" + if obj.flags & re.DOTALL: + flags += "s" + if obj.flags & re.UNICODE: + flags += "u" + if obj.flags & re.VERBOSE: + flags += "x" + if isinstance(obj.pattern, str): + pattern = obj.pattern + else: + pattern = obj.pattern.decode("utf-8") + if json_options.json_mode == JSONMode.LEGACY: + return SON([("$regex", pattern), ("$options", flags)]) + return {"$regularExpression": SON([("pattern", pattern), ("options", flags)])} + if isinstance(obj, MinKey): + return {"$minKey": 1} + if isinstance(obj, MaxKey): + return {"$maxKey": 1} + if isinstance(obj, Timestamp): + return {"$timestamp": SON([("t", obj.time), ("i", obj.inc)])} + if isinstance(obj, Code): + if obj.scope is None: + return {"$code": str(obj)} + return SON([("$code", str(obj)), ("$scope", _json_convert(obj.scope, json_options))]) + if isinstance(obj, Binary): + return _encode_binary(obj, obj.subtype, json_options) + if isinstance(obj, bytes): + return _encode_binary(obj, 0, json_options) + if isinstance(obj, uuid.UUID): + if json_options.strict_uuid: + binval = Binary.from_uuid(obj, uuid_representation=json_options.uuid_representation) + return _encode_binary(binval, binval.subtype, json_options) + else: + return {"$uuid": obj.hex} + if isinstance(obj, Decimal128): + return {"$numberDecimal": str(obj)} + if isinstance(obj, bool): + return obj + if json_options.json_mode == JSONMode.CANONICAL and isinstance(obj, int): + if -(2**31) <= obj < 2**31: + return {"$numberInt": str(obj)} + return {"$numberLong": str(obj)} + if json_options.json_mode != JSONMode.LEGACY and isinstance(obj, float): + if math.isnan(obj): + return {"$numberDouble": "NaN"} + elif math.isinf(obj): + representation = "Infinity" if obj > 0 else "-Infinity" + return {"$numberDouble": representation} + elif json_options.json_mode == JSONMode.CANONICAL: + # repr() will return the shortest string guaranteed to produce the + # original value, when float() is called on it. + return {"$numberDouble": str(repr(obj))} + raise TypeError("%r is not JSON serializable" % obj) diff --git a/src/xtquant/xtbson/bson36/max_key.py b/src/xtquant/xtbson/bson36/max_key.py new file mode 100644 index 0000000..322c539 --- /dev/null +++ b/src/xtquant/xtbson/bson36/max_key.py @@ -0,0 +1,54 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MaxKey type. +""" + + +class MaxKey(object): + """MongoDB internal MaxKey type.""" + + __slots__ = () + + _type_marker = 127 + + def __getstate__(self): + return {} + + def __setstate__(self, state): + pass + + def __eq__(self, other): + return isinstance(other, MaxKey) + + def __hash__(self): + return hash(self._type_marker) + + def __ne__(self, other): + return not self == other + + def __le__(self, other): + return isinstance(other, MaxKey) + + def __lt__(self, dummy): + return False + + def __ge__(self, dummy): + return True + + def __gt__(self, other): + return not isinstance(other, MaxKey) + + def __repr__(self): + return "MaxKey()" diff --git a/src/xtquant/xtbson/bson36/min_key.py b/src/xtquant/xtbson/bson36/min_key.py new file mode 100644 index 0000000..7520dd2 --- /dev/null +++ b/src/xtquant/xtbson/bson36/min_key.py @@ -0,0 +1,54 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MinKey type. +""" + + +class MinKey(object): + """MongoDB internal MinKey type.""" + + __slots__ = () + + _type_marker = 255 + + def __getstate__(self): + return {} + + def __setstate__(self, state): + pass + + def __eq__(self, other): + return isinstance(other, MinKey) + + def __hash__(self): + return hash(self._type_marker) + + def __ne__(self, other): + return not self == other + + def __le__(self, dummy): + return True + + def __lt__(self, other): + return not isinstance(other, MinKey) + + def __ge__(self, other): + return isinstance(other, MinKey) + + def __gt__(self, dummy): + return False + + def __repr__(self): + return "MinKey()" diff --git a/src/xtquant/xtbson/bson36/objectid.py b/src/xtquant/xtbson/bson36/objectid.py new file mode 100644 index 0000000..6507e0e --- /dev/null +++ b/src/xtquant/xtbson/bson36/objectid.py @@ -0,0 +1,285 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with MongoDB `ObjectIds +`_. +""" + +import binascii +import calendar +import datetime +import os +import struct +import threading +import time +from random import SystemRandom + +from .errors import InvalidId +from .tz_util import utc + +_MAX_COUNTER_VALUE = 0xFFFFFF + + +def _raise_invalid_id(oid): + raise InvalidId( + "%r is not a valid ObjectId, it must be a 12-byte input" + " or a 24-character hex string" % oid + ) + + +def _random_bytes(): + """Get the 5-byte random field of an ObjectId.""" + return os.urandom(5) + + +class ObjectId(object): + """A MongoDB ObjectId.""" + + _pid = os.getpid() + + _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) + _inc_lock = threading.Lock() + + __random = _random_bytes() + + __slots__ = ("__id",) + + _type_marker = 7 + + def __init__(self, oid=None): + """Initialize a new ObjectId. + + An ObjectId is a 12-byte unique identifier consisting of: + + - a 4-byte value representing the seconds since the Unix epoch, + - a 5-byte random value, + - a 3-byte counter, starting with a random value. + + By default, ``ObjectId()`` creates a new unique identifier. The + optional parameter `oid` can be an :class:`ObjectId`, or any 12 + :class:`bytes`. + + For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId + specification but they are acceptable input:: + + >>> ObjectId(b'foo-bar-quux') + ObjectId('666f6f2d6261722d71757578') + + `oid` can also be a :class:`str` of 24 hex digits:: + + >>> ObjectId('0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + + Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor + 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. + + :Parameters: + - `oid` (optional): a valid ObjectId. + + .. seealso:: The MongoDB documentation on `ObjectIds`_. + + .. versionchanged:: 3.8 + :class:`~bson.objectid.ObjectId` now implements the `ObjectID + specification version 0.2 + `_. + """ + if oid is None: + self.__generate() + elif isinstance(oid, bytes) and len(oid) == 12: + self.__id = oid + else: + self.__validate(oid) + + @classmethod + def from_datetime(cls, generation_time): + """Create a dummy ObjectId instance with a specific generation time. + + This method is useful for doing range queries on a field + containing :class:`ObjectId` instances. + + .. warning:: + It is not safe to insert a document containing an ObjectId + generated using this method. This method deliberately + eliminates the uniqueness guarantee that ObjectIds + generally provide. ObjectIds generated with this method + should be used exclusively in queries. + + `generation_time` will be converted to UTC. Naive datetime + instances will be treated as though they already contain UTC. + + An example using this helper to get documents where ``"_id"`` + was generated before January 1, 2010 would be: + + >>> gen_time = datetime.datetime(2010, 1, 1) + >>> dummy_id = ObjectId.from_datetime(gen_time) + >>> result = collection.find({"_id": {"$lt": dummy_id}}) + + :Parameters: + - `generation_time`: :class:`~datetime.datetime` to be used + as the generation time for the resulting ObjectId. + """ + if generation_time.utcoffset() is not None: + generation_time = generation_time - generation_time.utcoffset() + timestamp = calendar.timegm(generation_time.timetuple()) + oid = struct.pack(">I", int(timestamp)) + b"\x00\x00\x00\x00\x00\x00\x00\x00" + return cls(oid) + + @classmethod + def is_valid(cls, oid): + """Checks if a `oid` string is valid or not. + + :Parameters: + - `oid`: the object id to validate + + .. versionadded:: 2.3 + """ + if not oid: + return False + + try: + ObjectId(oid) + return True + except (InvalidId, TypeError): + return False + + @classmethod + def _random(cls): + """Generate a 5-byte random number once per process.""" + pid = os.getpid() + if pid != cls._pid: + cls._pid = pid + cls.__random = _random_bytes() + return cls.__random + + def __generate(self): + """Generate a new value for this ObjectId.""" + + # 4 bytes current time + oid = struct.pack(">I", int(time.time())) + + # 5 bytes random + oid += ObjectId._random() + + # 3 bytes inc + with ObjectId._inc_lock: + oid += struct.pack(">I", ObjectId._inc)[1:4] + ObjectId._inc = (ObjectId._inc + 1) % (_MAX_COUNTER_VALUE + 1) + + self.__id = oid + + def __validate(self, oid): + """Validate and use the given id for this ObjectId. + + Raises TypeError if id is not an instance of + (:class:`basestring` (:class:`str` or :class:`bytes` + in python 3), ObjectId) and InvalidId if it is not a + valid ObjectId. + + :Parameters: + - `oid`: a valid ObjectId + """ + if isinstance(oid, ObjectId): + self.__id = oid.binary + elif isinstance(oid, str): + if len(oid) == 24: + try: + self.__id = bytes.fromhex(oid) + except (TypeError, ValueError): + _raise_invalid_id(oid) + else: + _raise_invalid_id(oid) + else: + raise TypeError( + "id must be an instance of (bytes, str, ObjectId), " "not %s" % (type(oid),) + ) + + @property + def binary(self): + """12-byte binary representation of this ObjectId.""" + return self.__id + + @property + def generation_time(self): + """A :class:`datetime.datetime` instance representing the time of + generation for this :class:`ObjectId`. + + The :class:`datetime.datetime` is timezone aware, and + represents the generation time in UTC. It is precise to the + second. + """ + timestamp = struct.unpack(">I", self.__id[0:4])[0] + return datetime.datetime.fromtimestamp(timestamp, utc) + + def __getstate__(self): + """return value of object for pickling. + needed explicitly because __slots__() defined. + """ + return self.__id + + def __setstate__(self, value): + """explicit state set from pickling""" + # Provide backwards compatability with OIDs + # pickled with pymongo-1.9 or older. + if isinstance(value, dict): + oid = value["_ObjectId__id"] + else: + oid = value + # ObjectIds pickled in python 2.x used `str` for __id. + # In python 3.x this has to be converted to `bytes` + # by encoding latin-1. + if isinstance(oid, str): + self.__id = oid.encode("latin-1") + else: + self.__id = oid + + def __str__(self): + return binascii.hexlify(self.__id).decode() + + def __repr__(self): + return "ObjectId('%s')" % (str(self),) + + def __eq__(self, other): + if isinstance(other, ObjectId): + return self.__id == other.binary + return NotImplemented + + def __ne__(self, other): + if isinstance(other, ObjectId): + return self.__id != other.binary + return NotImplemented + + def __lt__(self, other): + if isinstance(other, ObjectId): + return self.__id < other.binary + return NotImplemented + + def __le__(self, other): + if isinstance(other, ObjectId): + return self.__id <= other.binary + return NotImplemented + + def __gt__(self, other): + if isinstance(other, ObjectId): + return self.__id > other.binary + return NotImplemented + + def __ge__(self, other): + if isinstance(other, ObjectId): + return self.__id >= other.binary + return NotImplemented + + def __hash__(self): + """Get a hash value for this :class:`ObjectId`.""" + return hash(self.__id) diff --git a/src/xtquant/xtbson/bson36/raw_bson.py b/src/xtquant/xtbson/bson36/raw_bson.py new file mode 100644 index 0000000..12e0328 --- /dev/null +++ b/src/xtquant/xtbson/bson36/raw_bson.py @@ -0,0 +1,174 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing raw BSON documents. + +Inserting and Retrieving RawBSONDocuments +========================================= + +Example: Moving a document between different databases/collections + +.. doctest:: + + >>> import bson + >>> from pymongo import MongoClient + >>> from .raw_bson import RawBSONDocument + >>> client = MongoClient(document_class=RawBSONDocument) + >>> client.drop_database('db') + >>> client.drop_database('replica_db') + >>> db = client.db + >>> result = db.test.insert_many([{'_id': 1, 'a': 1}, + ... {'_id': 2, 'b': 1}, + ... {'_id': 3, 'c': 1}, + ... {'_id': 4, 'd': 1}]) + >>> replica_db = client.replica_db + >>> for doc in db.test.find(): + ... print(f"raw document: {doc.raw}") + ... print(f"decoded document: {bson.decode(doc.raw)}") + ... result = replica_db.test.insert_one(doc) + raw document: b'...' + decoded document: {'_id': 1, 'a': 1} + raw document: b'...' + decoded document: {'_id': 2, 'b': 1} + raw document: b'...' + decoded document: {'_id': 3, 'c': 1} + raw document: b'...' + decoded document: {'_id': 4, 'd': 1} + +For use cases like moving documents across different databases or writing binary +blobs to disk, using raw BSON documents provides better speed and avoids the +overhead of decoding or encoding BSON. +""" + +from collections.abc import Mapping as _Mapping + +from . import _get_object_size, _raw_to_dict +from .codec_options import _RAW_BSON_DOCUMENT_MARKER +from .codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT +from .son import SON + + +class RawBSONDocument(_Mapping): + """Representation for a MongoDB document that provides access to the raw + BSON bytes that compose it. + + Only when a field is accessed or modified within the document does + RawBSONDocument decode its bytes. + """ + + __slots__ = ("__raw", "__inflated_doc", "__codec_options") + _type_marker = _RAW_BSON_DOCUMENT_MARKER + + def __init__(self, bson_bytes, codec_options=None): + """Create a new :class:`RawBSONDocument` + + :class:`RawBSONDocument` is a representation of a BSON document that + provides access to the underlying raw BSON bytes. Only when a field is + accessed or modified within the document does RawBSONDocument decode + its bytes. + + :class:`RawBSONDocument` implements the ``Mapping`` abstract base + class from the standard library so it can be used like a read-only + ``dict``:: + + >>> from . import encode + >>> raw_doc = RawBSONDocument(encode({'_id': 'my_doc'})) + >>> raw_doc.raw + b'...' + >>> raw_doc['_id'] + 'my_doc' + + :Parameters: + - `bson_bytes`: the BSON bytes that compose this document + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. The default is + :attr:`DEFAULT_RAW_BSON_OPTIONS`. + + .. versionchanged:: 3.8 + :class:`RawBSONDocument` now validates that the ``bson_bytes`` + passed in represent a single bson document. + + .. versionchanged:: 3.5 + If a :class:`~bson.codec_options.CodecOptions` is passed in, its + `document_class` must be :class:`RawBSONDocument`. + """ + self.__raw = bson_bytes + self.__inflated_doc = None + # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, + # it refers to this class RawBSONDocument. + if codec_options is None: + codec_options = DEFAULT_RAW_BSON_OPTIONS + elif codec_options.document_class is not RawBSONDocument: + raise TypeError( + "RawBSONDocument cannot use CodecOptions with document " + "class %s" % (codec_options.document_class,) + ) + self.__codec_options = codec_options + # Validate the bson object size. + _get_object_size(bson_bytes, 0, len(bson_bytes)) + + @property + def raw(self): + """The raw BSON bytes composing this document.""" + return self.__raw + + def items(self): + """Lazily decode and iterate elements in this document.""" + return self.__inflated.items() + + @property + def __inflated(self): + if self.__inflated_doc is None: + # We already validated the object's size when this document was + # created, so no need to do that again. + # Use SON to preserve ordering of elements. + self.__inflated_doc = _inflate_bson(self.__raw, self.__codec_options) + return self.__inflated_doc + + def __getitem__(self, item): + return self.__inflated[item] + + def __iter__(self): + return iter(self.__inflated) + + def __len__(self): + return len(self.__inflated) + + def __eq__(self, other): + if isinstance(other, RawBSONDocument): + return self.__raw == other.raw + return NotImplemented + + def __repr__(self): + return "RawBSONDocument(%r, codec_options=%r)" % (self.raw, self.__codec_options) + + +def _inflate_bson(bson_bytes, codec_options): + """Inflates the top level fields of a BSON document. + + :Parameters: + - `bson_bytes`: the BSON bytes that compose this document + - `codec_options`: An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. + """ + # Use SON to preserve ordering of elements. + return _raw_to_dict(bson_bytes, 4, len(bson_bytes) - 1, codec_options, SON()) + + +DEFAULT_RAW_BSON_OPTIONS = DEFAULT.with_options(document_class=RawBSONDocument) +"""The default :class:`~bson.codec_options.CodecOptions` for +:class:`RawBSONDocument`. +""" diff --git a/src/xtquant/xtbson/bson36/regex.py b/src/xtquant/xtbson/bson36/regex.py new file mode 100644 index 0000000..98899d6 --- /dev/null +++ b/src/xtquant/xtbson/bson36/regex.py @@ -0,0 +1,131 @@ +# Copyright 2013-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB regular expressions. +""" + +import re + +from ._helpers import _getstate_slots, _setstate_slots +from .son import RE_TYPE + + +def str_flags_to_int(str_flags): + flags = 0 + if "i" in str_flags: + flags |= re.IGNORECASE + if "l" in str_flags: + flags |= re.LOCALE + if "m" in str_flags: + flags |= re.MULTILINE + if "s" in str_flags: + flags |= re.DOTALL + if "u" in str_flags: + flags |= re.UNICODE + if "x" in str_flags: + flags |= re.VERBOSE + + return flags + + +class Regex(object): + """BSON regular expression data.""" + + __slots__ = ("pattern", "flags") + + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + + _type_marker = 11 + + @classmethod + def from_native(cls, regex): + """Convert a Python regular expression into a ``Regex`` instance. + + Note that in Python 3, a regular expression compiled from a + :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable + to store this flag in a BSON regular expression, unset it first:: + + >>> pattern = re.compile('.*') + >>> regex = Regex.from_native(pattern) + >>> regex.flags ^= re.UNICODE + >>> db.collection.insert_one({'pattern': regex}) + + :Parameters: + - `regex`: A regular expression object from ``re.compile()``. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. + + .. _PCRE: http://www.pcre.org/ + """ + if not isinstance(regex, RE_TYPE): + raise TypeError("regex must be a compiled regular expression, not %s" % type(regex)) + + return Regex(regex.pattern, regex.flags) + + def __init__(self, pattern, flags=0): + """BSON regular expression data. + + This class is useful to store and retrieve regular expressions that are + incompatible with Python's regular expression dialect. + + :Parameters: + - `pattern`: string + - `flags`: (optional) an integer bitmask, or a string of flag + characters like "im" for IGNORECASE and MULTILINE + """ + if not isinstance(pattern, (str, bytes)): + raise TypeError("pattern must be a string, not %s" % type(pattern)) + self.pattern = pattern + + if isinstance(flags, str): + self.flags = str_flags_to_int(flags) + elif isinstance(flags, int): + self.flags = flags + else: + raise TypeError("flags must be a string or int, not %s" % type(flags)) + + def __eq__(self, other): + if isinstance(other, Regex): + return self.pattern == other.pattern and self.flags == other.flags + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "Regex(%r, %r)" % (self.pattern, self.flags) + + def try_compile(self): + """Compile this :class:`Regex` as a Python regular expression. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. :meth:`try_compile()` may raise + :exc:`re.error`. + + .. _PCRE: http://www.pcre.org/ + """ + return re.compile(self.pattern, self.flags) diff --git a/src/xtquant/xtbson/bson36/son.py b/src/xtquant/xtbson/bson36/son.py new file mode 100644 index 0000000..9ad37b5 --- /dev/null +++ b/src/xtquant/xtbson/bson36/son.py @@ -0,0 +1,184 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for creating and manipulating SON, the Serialized Ocument Notation. + +Regular dictionaries can be used instead of SON objects, but not when the order +of keys is important. A SON object can be used just like a normal Python +dictionary.""" + +import copy +import re +from collections.abc import Mapping as _Mapping + +# This sort of sucks, but seems to be as good as it gets... +# This is essentially the same as re._pattern_type +RE_TYPE = type(re.compile("")) + + +class SON(dict): + """SON data. + + A subclass of dict that maintains ordering of keys and provides a + few extra niceties for dealing with SON. SON provides an API + similar to collections.OrderedDict. + """ + + def __init__(self, data=None, **kwargs): + self.__keys = [] + dict.__init__(self) + self.update(data) + self.update(kwargs) + + def __new__(cls, *args, **kwargs): + instance = super(SON, cls).__new__(cls, *args, **kwargs) + instance.__keys = [] + return instance + + def __repr__(self): + result = [] + for key in self.__keys: + result.append("(%r, %r)" % (key, self[key])) + return "SON([%s])" % ", ".join(result) + + def __setitem__(self, key, value): + if key not in self.__keys: + self.__keys.append(key) + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + self.__keys.remove(key) + dict.__delitem__(self, key) + + def copy(self): + other = SON() + other.update(self) + return other + + # TODO this is all from UserDict.DictMixin. it could probably be made more + # efficient. + # second level definitions support higher levels + def __iter__(self): + for k in self.__keys: + yield k + + def has_key(self, key): + return key in self.__keys + + def iterkeys(self): + return self.__iter__() + + # fourth level uses definitions from lower levels + def itervalues(self): + for _, v in self.items(): + yield v + + def values(self): + return [v for _, v in self.items()] + + def clear(self): + self.__keys = [] + super(SON, self).clear() + + def setdefault(self, key, default=None): + try: + return self[key] + except KeyError: + self[key] = default + return default + + def pop(self, key, *args): + if len(args) > 1: + raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) + try: + value = self[key] + except KeyError: + if args: + return args[0] + raise + del self[key] + return value + + def popitem(self): + try: + k, v = next(iter(self.items())) + except StopIteration: + raise KeyError("container is empty") + del self[k] + return (k, v) + + def update(self, other=None, **kwargs): + # Make progressively weaker assumptions about "other" + if other is None: + pass + elif hasattr(other, "items"): + for k, v in other.items(): + self[k] = v + elif hasattr(other, "keys"): + for k in other.keys(): + self[k] = other[k] + else: + for k, v in other: + self[k] = v + if kwargs: + self.update(kwargs) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def __eq__(self, other): + """Comparison to another SON is order-sensitive while comparison to a + regular dictionary is order-insensitive. + """ + if isinstance(other, SON): + return len(self) == len(other) and list(self.items()) == list(other.items()) + return self.to_dict() == other + + def __ne__(self, other): + return not self == other + + def __len__(self): + return len(self.__keys) + + def to_dict(self): + """Convert a SON document to a normal Python dictionary instance. + + This is trickier than just *dict(...)* because it needs to be + recursive. + """ + + def transform_value(value): + if isinstance(value, list): + return [transform_value(v) for v in value] + elif isinstance(value, _Mapping): + return dict([(k, transform_value(v)) for k, v in value.items()]) + else: + return value + + return transform_value(dict(self)) + + def __deepcopy__(self, memo): + out = SON() + val_id = id(self) + if val_id in memo: + return memo.get(val_id) + memo[val_id] = out + for k, v in self.items(): + if not isinstance(v, RE_TYPE): + v = copy.deepcopy(v, memo) + out[k] = v + return out diff --git a/src/xtquant/xtbson/bson36/timestamp.py b/src/xtquant/xtbson/bson36/timestamp.py new file mode 100644 index 0000000..59247f8 --- /dev/null +++ b/src/xtquant/xtbson/bson36/timestamp.py @@ -0,0 +1,122 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB internal Timestamps. +""" + +import calendar +import datetime + +from ._helpers import _getstate_slots, _setstate_slots +from .tz_util import utc + +UPPERBOUND = 4294967296 + + +class Timestamp(object): + """MongoDB internal timestamps used in the opLog.""" + + __slots__ = ("__time", "__inc") + + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + + _type_marker = 17 + + def __init__(self, time, inc): + """Create a new :class:`Timestamp`. + + This class is only for use with the MongoDB opLog. If you need + to store a regular timestamp, please use a + :class:`~datetime.datetime`. + + Raises :class:`TypeError` if `time` is not an instance of + :class: `int` or :class:`~datetime.datetime`, or `inc` is not + an instance of :class:`int`. Raises :class:`ValueError` if + `time` or `inc` is not in [0, 2**32). + + :Parameters: + - `time`: time in seconds since epoch UTC, or a naive UTC + :class:`~datetime.datetime`, or an aware + :class:`~datetime.datetime` + - `inc`: the incrementing counter + """ + if isinstance(time, datetime.datetime): + if time.utcoffset() is not None: + time = time - time.utcoffset() + time = int(calendar.timegm(time.timetuple())) + if not isinstance(time, int): + raise TypeError("time must be an instance of int") + if not isinstance(inc, int): + raise TypeError("inc must be an instance of int") + if not 0 <= time < UPPERBOUND: + raise ValueError("time must be contained in [0, 2**32)") + if not 0 <= inc < UPPERBOUND: + raise ValueError("inc must be contained in [0, 2**32)") + + self.__time = time + self.__inc = inc + + @property + def time(self): + """Get the time portion of this :class:`Timestamp`.""" + return self.__time + + @property + def inc(self): + """Get the inc portion of this :class:`Timestamp`.""" + return self.__inc + + def __eq__(self, other): + if isinstance(other, Timestamp): + return self.__time == other.time and self.__inc == other.inc + else: + return NotImplemented + + def __hash__(self): + return hash(self.time) ^ hash(self.inc) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) < (other.time, other.inc) + return NotImplemented + + def __le__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) <= (other.time, other.inc) + return NotImplemented + + def __gt__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) > (other.time, other.inc) + return NotImplemented + + def __ge__(self, other): + if isinstance(other, Timestamp): + return (self.time, self.inc) >= (other.time, other.inc) + return NotImplemented + + def __repr__(self): + return "Timestamp(%s, %s)" % (self.__time, self.__inc) + + def as_datetime(self): + """Return a :class:`~datetime.datetime` instance corresponding + to the time portion of this :class:`Timestamp`. + + The returned datetime's timezone is UTC. + """ + return datetime.datetime.fromtimestamp(self.__time, utc) diff --git a/src/xtquant/xtbson/bson36/tz_util.py b/src/xtquant/xtbson/bson36/tz_util.py new file mode 100644 index 0000000..6cfb230 --- /dev/null +++ b/src/xtquant/xtbson/bson36/tz_util.py @@ -0,0 +1,51 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Timezone related utilities for BSON.""" + +from datetime import timedelta, tzinfo + +ZERO = timedelta(0) + + +class FixedOffset(tzinfo): + """Fixed offset timezone, in minutes east from UTC. + + Implementation based from the Python `standard library documentation + `_. + Defining __getinitargs__ enables pickling / copying. + """ + + def __init__(self, offset, name): + if isinstance(offset, timedelta): + self.__offset = offset + else: + self.__offset = timedelta(minutes=offset) + self.__name = name + + def __getinitargs__(self): + return self.__offset, self.__name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return ZERO + + +utc = FixedOffset(0, "UTC") +"""Fixed offset timezone representing UTC.""" diff --git a/src/xtquant/xtbson/bson37/__init__.py b/src/xtquant/xtbson/bson37/__init__.py new file mode 100644 index 0000000..fe664da --- /dev/null +++ b/src/xtquant/xtbson/bson37/__init__.py @@ -0,0 +1,1404 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""BSON (Binary JSON) encoding and decoding. + +The mapping from Python types to BSON types is as follows: + +======================================= ============= =================== +Python Type BSON Type Supported Direction +======================================= ============= =================== +None null both +bool boolean both +int [#int]_ int32 / int64 py -> bson +`bson.int64.Int64` int64 both +float number (real) both +str string both +list array both +dict / `SON` object both +datetime.datetime [#dt]_ [#dt2]_ date both +`bson.regex.Regex` regex both +compiled re [#re]_ regex py -> bson +`bson.binary.Binary` binary both +`bson.objectid.ObjectId` oid both +`bson.dbref.DBRef` dbref both +None undefined bson -> py +`bson.code.Code` code both +str symbol bson -> py +bytes [#bytes]_ binary both +======================================= ============= =================== + +.. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending + on its size. A BSON int32 will always decode to a Python int. A BSON + int64 will always decode to a :class:`~bson.int64.Int64`. +.. [#dt] datetime.datetime instances will be rounded to the nearest + millisecond when saved +.. [#dt2] all datetime.datetime instances are treated as *naive*. clients + should always use UTC. +.. [#re] :class:`~bson.regex.Regex` instances and regular expression + objects from ``re.compile()`` are both saved as BSON regular expressions. + BSON regular expressions are decoded as :class:`~bson.regex.Regex` + instances. +.. [#bytes] The bytes type is encoded as BSON binary with + subtype 0. It will be decoded back to bytes. +""" + +import datetime +import itertools +import os +import re +import struct +import sys +import uuid +from codecs import utf_8_decode as _utf_8_decode +from codecs import utf_8_encode as _utf_8_encode +from collections import abc as _abc +from typing import ( + IO, + TYPE_CHECKING, + Any, + BinaryIO, + Callable, + Dict, + Generator, + Iterator, + List, + Mapping, + MutableMapping, + NoReturn, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + cast, +) + +from .binary import ( + ALL_UUID_SUBTYPES, + CSHARP_LEGACY, + JAVA_LEGACY, + OLD_UUID_SUBTYPE, + STANDARD, + UUID_SUBTYPE, + Binary, + UuidRepresentation, +) +from .code import Code +from .codec_options import ( + DEFAULT_CODEC_OPTIONS, + CodecOptions, + DatetimeConversion, + _DocumentType, + _raw_document_class, +) +from .datetime_ms import ( + EPOCH_AWARE, + EPOCH_NAIVE, + DatetimeMS, + _datetime_to_millis, + _millis_to_datetime, + utc, +) +from .dbref import DBRef +from .decimal128 import Decimal128 +from .errors import InvalidBSON, InvalidDocument, InvalidStringData +from .int64 import Int64 +from .max_key import MaxKey +from .min_key import MinKey +from .objectid import ObjectId +from .regex import Regex +from .son import RE_TYPE, SON +from .timestamp import Timestamp + +# Import some modules for type-checking only. +if TYPE_CHECKING: + from array import array + from mmap import mmap + +try: + from . import _cbson # type: ignore[attr-defined] + + _USE_C = True +except ImportError: + _USE_C = False + +__all__ = [ + "ALL_UUID_SUBTYPES", + "CSHARP_LEGACY", + "JAVA_LEGACY", + "OLD_UUID_SUBTYPE", + "STANDARD", + "UUID_SUBTYPE", + "Binary", + "UuidRepresentation", + "Code", + "DEFAULT_CODEC_OPTIONS", + "CodecOptions", + "DBRef", + "Decimal128", + "InvalidBSON", + "InvalidDocument", + "InvalidStringData", + "Int64", + "MaxKey", + "MinKey", + "ObjectId", + "Regex", + "RE_TYPE", + "SON", + "Timestamp", + "utc", + "EPOCH_AWARE", + "EPOCH_NAIVE", + "BSONNUM", + "BSONSTR", + "BSONOBJ", + "BSONARR", + "BSONBIN", + "BSONUND", + "BSONOID", + "BSONBOO", + "BSONDAT", + "BSONNUL", + "BSONRGX", + "BSONREF", + "BSONCOD", + "BSONSYM", + "BSONCWS", + "BSONINT", + "BSONTIM", + "BSONLON", + "BSONDEC", + "BSONMIN", + "BSONMAX", + "get_data_and_view", + "gen_list_name", + "encode", + "decode", + "decode_all", + "decode_iter", + "decode_file_iter", + "is_valid", + "BSON", + "has_c", + "DatetimeConversion", + "DatetimeMS", +] + +BSONNUM = b"\x01" # Floating point +BSONSTR = b"\x02" # UTF-8 string +BSONOBJ = b"\x03" # Embedded document +BSONARR = b"\x04" # Array +BSONBIN = b"\x05" # Binary +BSONUND = b"\x06" # Undefined +BSONOID = b"\x07" # ObjectId +BSONBOO = b"\x08" # Boolean +BSONDAT = b"\x09" # UTC Datetime +BSONNUL = b"\x0A" # Null +BSONRGX = b"\x0B" # Regex +BSONREF = b"\x0C" # DBRef +BSONCOD = b"\x0D" # Javascript code +BSONSYM = b"\x0E" # Symbol +BSONCWS = b"\x0F" # Javascript code with scope +BSONINT = b"\x10" # 32bit int +BSONTIM = b"\x11" # Timestamp +BSONLON = b"\x12" # 64bit int +BSONDEC = b"\x13" # Decimal128 +BSONMIN = b"\xFF" # Min key +BSONMAX = b"\x7F" # Max key + + +_UNPACK_FLOAT_FROM = struct.Struct(" Tuple[Any, memoryview]: + if isinstance(data, (bytes, bytearray)): + return data, memoryview(data) + view = memoryview(data) + return view.tobytes(), view + + +def _raise_unknown_type(element_type: int, element_name: str) -> NoReturn: + """Unknown type helper.""" + raise InvalidBSON( + "Detected unknown BSON type %r for fieldname '%s'. Are " + "you using the latest driver version?" % (chr(element_type).encode(), element_name) + ) + + +def _get_int( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[int, int]: + """Decode a BSON int32 to python int.""" + return _UNPACK_INT_FROM(data, position)[0], position + 4 + + +def _get_c_string(data: Any, view: Any, position: int, opts: CodecOptions) -> Tuple[str, int]: + """Decode a BSON 'C' string to python str.""" + end = data.index(b"\x00", position) + return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1 + + +def _get_float( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[float, int]: + """Decode a BSON double to python float.""" + return _UNPACK_FLOAT_FROM(data, position)[0], position + 8 + + +def _get_string( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, dummy: Any +) -> Tuple[str, int]: + """Decode a BSON string to python str.""" + length = _UNPACK_INT_FROM(data, position)[0] + position += 4 + if length < 1 or obj_end - position < length: + raise InvalidBSON("invalid string length") + end = position + length - 1 + if data[end] != 0: + raise InvalidBSON("invalid end of string") + return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1 + + +def _get_object_size(data: Any, position: int, obj_end: int) -> Tuple[int, int]: + """Validate and return a BSON document's size.""" + try: + obj_size = _UNPACK_INT_FROM(data, position)[0] + except struct.error as exc: + raise InvalidBSON(str(exc)) + end = position + obj_size - 1 + if data[end] != 0: + raise InvalidBSON("bad eoo") + if end >= obj_end: + raise InvalidBSON("invalid object length") + # If this is the top-level document, validate the total size too. + if position == 0 and obj_size != obj_end: + raise InvalidBSON("invalid object length") + return obj_size, end + + +def _get_object( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, dummy: Any +) -> Tuple[Any, int]: + """Decode a BSON subdocument to opts.document_class or bson.dbref.DBRef.""" + obj_size, end = _get_object_size(data, position, obj_end) + if _raw_document_class(opts.document_class): + return (opts.document_class(data[position : end + 1], opts), position + obj_size) + + obj = _elements_to_dict(data, view, position + 4, end, opts) + + position += obj_size + # If DBRef validation fails, return a normal doc. + if ( + isinstance(obj.get("$ref"), str) + and "$id" in obj + and isinstance(obj.get("$db"), (str, type(None))) + ): + return (DBRef(obj.pop("$ref"), obj.pop("$id", None), obj.pop("$db", None), obj), position) + return obj, position + + +def _get_array( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str +) -> Tuple[Any, int]: + """Decode a BSON array to python list.""" + size = _UNPACK_INT_FROM(data, position)[0] + end = position + size - 1 + if data[end] != 0: + raise InvalidBSON("bad eoo") + + position += 4 + end -= 1 + result: List[Any] = [] + + # Avoid doing global and attribute lookups in the loop. + append = result.append + index = data.index + getter = _ELEMENT_GETTER + decoder_map = opts.type_registry._decoder_map + + while position < end: + element_type = data[position] + # Just skip the keys. + position = index(b"\x00", position) + 1 + try: + value, position = getter[element_type]( + data, view, position, obj_end, opts, element_name + ) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if decoder_map: + custom_decoder = decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + append(value) + + if position != end + 1: + raise InvalidBSON("bad array length") + return result, position + 1 + + +def _get_binary( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, dummy1: Any +) -> Tuple[Union[Binary, uuid.UUID], int]: + """Decode a BSON binary to bson.binary.Binary or python UUID.""" + length, subtype = _UNPACK_LENGTH_SUBTYPE_FROM(data, position) + position += 5 + if subtype == 2: + length2 = _UNPACK_INT_FROM(data, position)[0] + position += 4 + if length2 != length - 4: + raise InvalidBSON("invalid binary (st 2) - lengths don't match!") + length = length2 + end = position + length + if length < 0 or end > obj_end: + raise InvalidBSON("bad binary object length") + + # Convert UUID subtypes to native UUIDs. + if subtype in ALL_UUID_SUBTYPES: + uuid_rep = opts.uuid_representation + binary_value = Binary(data[position:end], subtype) + if ( + (uuid_rep == UuidRepresentation.UNSPECIFIED) + or (subtype == UUID_SUBTYPE and uuid_rep != STANDARD) + or (subtype == OLD_UUID_SUBTYPE and uuid_rep == STANDARD) + ): + return binary_value, end + return binary_value.as_uuid(uuid_rep), end + + # Decode subtype 0 to 'bytes'. + if subtype == 0: + value = data[position:end] + else: + value = Binary(data[position:end], subtype) + + return value, end + + +def _get_oid( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[ObjectId, int]: + """Decode a BSON ObjectId to bson.objectid.ObjectId.""" + end = position + 12 + return ObjectId(data[position:end]), end + + +def _get_boolean( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[bool, int]: + """Decode a BSON true/false to python True/False.""" + end = position + 1 + boolean_byte = data[position:end] + if boolean_byte == b"\x00": + return False, end + elif boolean_byte == b"\x01": + return True, end + raise InvalidBSON("invalid boolean value: %r" % boolean_byte) + + +def _get_date( + data: Any, view: Any, position: int, dummy0: int, opts: CodecOptions, dummy1: Any +) -> Tuple[Union[datetime.datetime, DatetimeMS], int]: + """Decode a BSON datetime to python datetime.datetime.""" + return _millis_to_datetime(_UNPACK_LONG_FROM(data, position)[0], opts), position + 8 + + +def _get_code( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str +) -> Tuple[Code, int]: + """Decode a BSON code to bson.code.Code.""" + code, position = _get_string(data, view, position, obj_end, opts, element_name) + return Code(code), position + + +def _get_code_w_scope( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str +) -> Tuple[Code, int]: + """Decode a BSON code_w_scope to bson.code.Code.""" + code_end = position + _UNPACK_INT_FROM(data, position)[0] + code, position = _get_string(data, view, position + 4, code_end, opts, element_name) + scope, position = _get_object(data, view, position, code_end, opts, element_name) + if position != code_end: + raise InvalidBSON("scope outside of javascript code boundaries") + return Code(code, scope), position + + +def _get_regex( + data: Any, view: Any, position: int, dummy0: Any, opts: CodecOptions, dummy1: Any +) -> Tuple[Regex, int]: + """Decode a BSON regex to bson.regex.Regex or a python pattern object.""" + pattern, position = _get_c_string(data, view, position, opts) + bson_flags, position = _get_c_string(data, view, position, opts) + bson_re = Regex(pattern, bson_flags) + return bson_re, position + + +def _get_ref( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions, element_name: str +) -> Tuple[DBRef, int]: + """Decode (deprecated) BSON DBPointer to bson.dbref.DBRef.""" + collection, position = _get_string(data, view, position, obj_end, opts, element_name) + oid, position = _get_oid(data, view, position, obj_end, opts, element_name) + return DBRef(collection, oid), position + + +def _get_timestamp( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[Timestamp, int]: + """Decode a BSON timestamp to bson.timestamp.Timestamp.""" + inc, timestamp = _UNPACK_TIMESTAMP_FROM(data, position) + return Timestamp(timestamp, inc), position + 8 + + +def _get_int64( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[Int64, int]: + """Decode a BSON int64 to bson.int64.Int64.""" + return Int64(_UNPACK_LONG_FROM(data, position)[0]), position + 8 + + +def _get_decimal128( + data: Any, view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[Decimal128, int]: + """Decode a BSON decimal128 to bson.decimal128.Decimal128.""" + end = position + 16 + return Decimal128.from_bid(data[position:end]), end + + +# Each decoder function's signature is: +# - data: bytes +# - view: memoryview that references `data` +# - position: int, beginning of object in 'data' to decode +# - obj_end: int, end of object to decode in 'data' if variable-length type +# - opts: a CodecOptions +_ELEMENT_GETTER: Dict[int, Callable[..., Tuple[Any, int]]] = { + ord(BSONNUM): _get_float, + ord(BSONSTR): _get_string, + ord(BSONOBJ): _get_object, + ord(BSONARR): _get_array, + ord(BSONBIN): _get_binary, + ord(BSONUND): lambda u, v, w, x, y, z: (None, w), # Deprecated undefined + ord(BSONOID): _get_oid, + ord(BSONBOO): _get_boolean, + ord(BSONDAT): _get_date, + ord(BSONNUL): lambda u, v, w, x, y, z: (None, w), + ord(BSONRGX): _get_regex, + ord(BSONREF): _get_ref, # Deprecated DBPointer + ord(BSONCOD): _get_code, + ord(BSONSYM): _get_string, # Deprecated symbol + ord(BSONCWS): _get_code_w_scope, + ord(BSONINT): _get_int, + ord(BSONTIM): _get_timestamp, + ord(BSONLON): _get_int64, + ord(BSONDEC): _get_decimal128, + ord(BSONMIN): lambda u, v, w, x, y, z: (MinKey(), w), + ord(BSONMAX): lambda u, v, w, x, y, z: (MaxKey(), w), +} + + +if _USE_C: + + def _element_to_dict( + data: Any, + view: Any, + position: int, + obj_end: int, + opts: CodecOptions, + raw_array: bool = False, + ) -> Any: + return _cbson._element_to_dict(data, position, obj_end, opts, raw_array) + +else: + + def _element_to_dict( + data: Any, + view: Any, + position: int, + obj_end: int, + opts: CodecOptions, + raw_array: bool = False, + ) -> Any: + """Decode a single key, value pair.""" + element_type = data[position] + position += 1 + element_name, position = _get_c_string(data, view, position, opts) + if raw_array and element_type == ord(BSONARR): + _, end = _get_object_size(data, position, len(data)) + return element_name, view[position : end + 1], end + 1 + try: + value, position = _ELEMENT_GETTER[element_type]( + data, view, position, obj_end, opts, element_name + ) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if opts.type_registry._decoder_map: + custom_decoder = opts.type_registry._decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + return element_name, value, position + + +_T = TypeVar("_T", bound=MutableMapping[Any, Any]) + + +def _raw_to_dict( + data: Any, position: int, obj_end: int, opts: CodecOptions, result: _T, raw_array: bool = False +) -> _T: + data, view = get_data_and_view(data) + return _elements_to_dict(data, view, position, obj_end, opts, result, raw_array=raw_array) + + +def _elements_to_dict( + data: Any, + view: Any, + position: int, + obj_end: int, + opts: CodecOptions, + result: Any = None, + raw_array: bool = False, +) -> Any: + """Decode a BSON document into result.""" + if result is None: + result = opts.document_class() + end = obj_end - 1 + while position < end: + key, value, position = _element_to_dict( + data, view, position, obj_end, opts, raw_array=raw_array + ) + result[key] = value + if position != obj_end: + raise InvalidBSON("bad object or element length") + return result + + +def _bson_to_dict(data: Any, opts: CodecOptions) -> Any: + """Decode a BSON string to document_class.""" + data, view = get_data_and_view(data) + try: + if _raw_document_class(opts.document_class): + return opts.document_class(data, opts) + _, end = _get_object_size(data, 0, len(data)) + return _elements_to_dict(data, view, 4, end, opts) + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) + + +if _USE_C: + _bson_to_dict = _cbson._bson_to_dict # noqa: F811 + + +_PACK_FLOAT = struct.Struct(" Generator[bytes, None, None]: + """Generate "keys" for encoded lists in the sequence + b"0\x00", b"1\x00", b"2\x00", ... + + The first 1000 keys are returned from a pre-built cache. All + subsequent keys are generated on the fly. + """ + for name in _LIST_NAMES: + yield name + + counter = itertools.count(1000) + while True: + yield (str(next(counter)) + "\x00").encode("utf8") + + +def _make_c_string_check(string: Union[str, bytes]) -> bytes: + """Make a 'C' string, checking for embedded NUL characters.""" + if isinstance(string, bytes): + if b"\x00" in string: + raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") + try: + _utf_8_decode(string, None, True) + return string + b"\x00" + except UnicodeError: + raise InvalidStringData("strings in documents must be valid UTF-8: %r" % string) + else: + if "\x00" in string: + raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") + return _utf_8_encode(string)[0] + b"\x00" + + +def _make_c_string(string: Union[str, bytes]) -> bytes: + """Make a 'C' string.""" + if isinstance(string, bytes): + try: + _utf_8_decode(string, None, True) + return string + b"\x00" + except UnicodeError: + raise InvalidStringData("strings in documents must be valid UTF-8: %r" % string) + else: + return _utf_8_encode(string)[0] + b"\x00" + + +def _make_name(string: str) -> bytes: + """Make a 'C' string suitable for a BSON key.""" + # Keys can only be text in python 3. + if "\x00" in string: + raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") + return _utf_8_encode(string)[0] + b"\x00" + + +def _encode_float(name: bytes, value: float, dummy0: Any, dummy1: Any) -> bytes: + """Encode a float.""" + return b"\x01" + name + _PACK_FLOAT(value) + + +def _encode_bytes(name: bytes, value: bytes, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python bytes.""" + # Python3 special case. Store 'bytes' as BSON binary subtype 0. + return b"\x05" + name + _PACK_INT(len(value)) + b"\x00" + value + + +def _encode_mapping(name: bytes, value: Any, check_keys: bool, opts: CodecOptions) -> bytes: + """Encode a mapping type.""" + if _raw_document_class(value): + return b"\x03" + name + value.raw + data = b"".join([_element_to_bson(key, val, check_keys, opts) for key, val in value.items()]) + return b"\x03" + name + _PACK_INT(len(data) + 5) + data + b"\x00" + + +def _encode_dbref(name: bytes, value: DBRef, check_keys: bool, opts: CodecOptions) -> bytes: + """Encode bson.dbref.DBRef.""" + buf = bytearray(b"\x03" + name + b"\x00\x00\x00\x00") + begin = len(buf) - 4 + + buf += _name_value_to_bson(b"$ref\x00", value.collection, check_keys, opts) + buf += _name_value_to_bson(b"$id\x00", value.id, check_keys, opts) + if value.database is not None: + buf += _name_value_to_bson(b"$db\x00", value.database, check_keys, opts) + for key, val in value._DBRef__kwargs.items(): + buf += _element_to_bson(key, val, check_keys, opts) + + buf += b"\x00" + buf[begin : begin + 4] = _PACK_INT(len(buf) - begin) + return bytes(buf) + + +def _encode_list(name: bytes, value: Sequence[Any], check_keys: bool, opts: CodecOptions) -> bytes: + """Encode a list/tuple.""" + lname = gen_list_name() + data = b"".join([_name_value_to_bson(next(lname), item, check_keys, opts) for item in value]) + return b"\x04" + name + _PACK_INT(len(data) + 5) + data + b"\x00" + + +def _encode_text(name: bytes, value: str, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python str.""" + bvalue = _utf_8_encode(value)[0] + return b"\x02" + name + _PACK_INT(len(bvalue) + 1) + bvalue + b"\x00" + + +def _encode_binary(name: bytes, value: Binary, dummy0: Any, dummy1: Any) -> bytes: + """Encode bson.binary.Binary.""" + subtype = value.subtype + if subtype == 2: + value = _PACK_INT(len(value)) + value # type: ignore + return b"\x05" + name + _PACK_LENGTH_SUBTYPE(len(value), subtype) + value + + +def _encode_uuid(name: bytes, value: uuid.UUID, dummy: Any, opts: CodecOptions) -> bytes: + """Encode uuid.UUID.""" + uuid_representation = opts.uuid_representation + binval = Binary.from_uuid(value, uuid_representation=uuid_representation) + return _encode_binary(name, binval, dummy, opts) + + +def _encode_objectid(name: bytes, value: ObjectId, dummy: Any, dummy1: Any) -> bytes: + """Encode bson.objectid.ObjectId.""" + return b"\x07" + name + value.binary + + +def _encode_bool(name: bytes, value: bool, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python boolean (True/False).""" + return b"\x08" + name + (value and b"\x01" or b"\x00") + + +def _encode_datetime(name: bytes, value: datetime.datetime, dummy0: Any, dummy1: Any) -> bytes: + """Encode datetime.datetime.""" + millis = _datetime_to_millis(value) + return b"\x09" + name + _PACK_LONG(millis) + + +def _encode_datetime_ms(name: bytes, value: DatetimeMS, dummy0: Any, dummy1: Any) -> bytes: + """Encode datetime.datetime.""" + millis = int(value) + return b"\x09" + name + _PACK_LONG(millis) + + +def _encode_none(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: + """Encode python None.""" + return b"\x0A" + name + + +def _encode_regex(name: bytes, value: Regex, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python regex or bson.regex.Regex.""" + flags = value.flags + # Python 3 common case + if flags == re.UNICODE: + return b"\x0B" + name + _make_c_string_check(value.pattern) + b"u\x00" + elif flags == 0: + return b"\x0B" + name + _make_c_string_check(value.pattern) + b"\x00" + else: + sflags = b"" + if flags & re.IGNORECASE: + sflags += b"i" + if flags & re.LOCALE: + sflags += b"l" + if flags & re.MULTILINE: + sflags += b"m" + if flags & re.DOTALL: + sflags += b"s" + if flags & re.UNICODE: + sflags += b"u" + if flags & re.VERBOSE: + sflags += b"x" + sflags += b"\x00" + return b"\x0B" + name + _make_c_string_check(value.pattern) + sflags + + +def _encode_code(name: bytes, value: Code, dummy: Any, opts: CodecOptions) -> bytes: + """Encode bson.code.Code.""" + cstring = _make_c_string(value) + cstrlen = len(cstring) + if value.scope is None: + return b"\x0D" + name + _PACK_INT(cstrlen) + cstring + scope = _dict_to_bson(value.scope, False, opts, False) + full_length = _PACK_INT(8 + cstrlen + len(scope)) + return b"\x0F" + name + full_length + _PACK_INT(cstrlen) + cstring + scope + + +def _encode_int(name: bytes, value: int, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python int.""" + if -2147483648 <= value <= 2147483647: + return b"\x10" + name + _PACK_INT(value) + else: + try: + return b"\x12" + name + _PACK_LONG(value) + except struct.error: + raise OverflowError("BSON can only handle up to 8-byte ints") + + +def _encode_timestamp(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes: + """Encode bson.timestamp.Timestamp.""" + return b"\x11" + name + _PACK_TIMESTAMP(value.inc, value.time) + + +def _encode_long(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python long (python 2.x)""" + try: + return b"\x12" + name + _PACK_LONG(value) + except struct.error: + raise OverflowError("BSON can only handle up to 8-byte ints") + + +def _encode_decimal128(name: bytes, value: Decimal128, dummy0: Any, dummy1: Any) -> bytes: + """Encode bson.decimal128.Decimal128.""" + return b"\x13" + name + value.bid + + +def _encode_minkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: + """Encode bson.min_key.MinKey.""" + return b"\xFF" + name + + +def _encode_maxkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: + """Encode bson.max_key.MaxKey.""" + return b"\x7F" + name + + +# Each encoder function's signature is: +# - name: utf-8 bytes +# - value: a Python data type, e.g. a Python int for _encode_int +# - check_keys: bool, whether to check for invalid names +# - opts: a CodecOptions +_ENCODERS = { + bool: _encode_bool, + bytes: _encode_bytes, + datetime.datetime: _encode_datetime, + DatetimeMS: _encode_datetime_ms, + dict: _encode_mapping, + float: _encode_float, + int: _encode_int, + list: _encode_list, + str: _encode_text, + tuple: _encode_list, + type(None): _encode_none, + uuid.UUID: _encode_uuid, + Binary: _encode_binary, + Int64: _encode_long, + Code: _encode_code, + DBRef: _encode_dbref, + MaxKey: _encode_maxkey, + MinKey: _encode_minkey, + ObjectId: _encode_objectid, + Regex: _encode_regex, + RE_TYPE: _encode_regex, + SON: _encode_mapping, + Timestamp: _encode_timestamp, + Decimal128: _encode_decimal128, + # Special case. This will never be looked up directly. + _abc.Mapping: _encode_mapping, +} + + +_MARKERS = { + 5: _encode_binary, + 7: _encode_objectid, + 11: _encode_regex, + 13: _encode_code, + 17: _encode_timestamp, + 18: _encode_long, + 100: _encode_dbref, + 127: _encode_maxkey, + 255: _encode_minkey, +} + + +_BUILT_IN_TYPES = tuple(t for t in _ENCODERS) + + +def _name_value_to_bson( + name: bytes, + value: Any, + check_keys: bool, + opts: CodecOptions, + in_custom_call: bool = False, + in_fallback_call: bool = False, +) -> bytes: + """Encode a single name, value pair.""" + # First see if the type is already cached. KeyError will only ever + # happen once per subtype. + try: + return _ENCODERS[type(value)](name, value, check_keys, opts) # type: ignore + except KeyError: + pass + + # Second, fall back to trying _type_marker. This has to be done + # before the loop below since users could subclass one of our + # custom types that subclasses a python built-in (e.g. Binary) + marker = getattr(value, "_type_marker", None) + if isinstance(marker, int) and marker in _MARKERS: + func = _MARKERS[marker] + # Cache this type for faster subsequent lookup. + _ENCODERS[type(value)] = func + return func(name, value, check_keys, opts) # type: ignore + + # Third, check if a type encoder is registered for this type. + # Note that subtypes of registered custom types are not auto-encoded. + if not in_custom_call and opts.type_registry._encoder_map: + custom_encoder = opts.type_registry._encoder_map.get(type(value)) + if custom_encoder is not None: + return _name_value_to_bson( + name, custom_encoder(value), check_keys, opts, in_custom_call=True + ) + + # Fourth, test each base type. This will only happen once for + # a subtype of a supported base type. Unlike in the C-extensions, this + # is done after trying the custom type encoder because checking for each + # subtype is expensive. + for base in _BUILT_IN_TYPES: + if isinstance(value, base): + func = _ENCODERS[base] + # Cache this type for faster subsequent lookup. + _ENCODERS[type(value)] = func + return func(name, value, check_keys, opts) # type: ignore + + # As a last resort, try using the fallback encoder, if the user has + # provided one. + fallback_encoder = opts.type_registry._fallback_encoder + if not in_fallback_call and fallback_encoder is not None: + return _name_value_to_bson( + name, fallback_encoder(value), check_keys, opts, in_fallback_call=True + ) + + raise InvalidDocument("cannot encode object: %r, of type: %r" % (value, type(value))) + + +def _element_to_bson(key: Any, value: Any, check_keys: bool, opts: CodecOptions) -> bytes: + """Encode a single key, value pair.""" + if not isinstance(key, str): + raise InvalidDocument("documents must have only string keys, key was %r" % (key,)) + if check_keys: + if key.startswith("$"): + raise InvalidDocument("key %r must not start with '$'" % (key,)) + if "." in key: + raise InvalidDocument("key %r must not contain '.'" % (key,)) + + name = _make_name(key) + return _name_value_to_bson(name, value, check_keys, opts) + + +def _dict_to_bson(doc: Any, check_keys: bool, opts: CodecOptions, top_level: bool = True) -> bytes: + """Encode a document to BSON.""" + if _raw_document_class(doc): + return cast(bytes, doc.raw) + try: + elements = [] + if top_level and "_id" in doc: + elements.append(_name_value_to_bson(b"_id\x00", doc["_id"], check_keys, opts)) + for key, value in doc.items(): + if not top_level or key != "_id": + elements.append(_element_to_bson(key, value, check_keys, opts)) + except AttributeError: + raise TypeError("encoder expected a mapping type but got: %r" % (doc,)) + + encoded = b"".join(elements) + return _PACK_INT(len(encoded) + 5) + encoded + b"\x00" + + +if _USE_C: + _dict_to_bson = _cbson._dict_to_bson # noqa: F811 + + +_CODEC_OPTIONS_TYPE_ERROR = TypeError("codec_options must be an instance of CodecOptions") + + +_DocumentIn = Mapping[str, Any] +_ReadableBuffer = Union[bytes, memoryview, "mmap", "array"] + + +def encode( + document: _DocumentIn, + check_keys: bool = False, + codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS, +) -> bytes: + """Encode a document to BSON. + + A document can be any mapping type (like :class:`dict`). + + Raises :class:`TypeError` if `document` is not a mapping type, + or contains keys that are not instances of + :class:`basestring` (:class:`str` in python 3). Raises + :class:`~bson.errors.InvalidDocument` if `document` cannot be + converted to :class:`BSON`. + + :Parameters: + - `document`: mapping type representing a document + - `check_keys` (optional): check if keys start with '$' or + contain '.', raising :class:`~bson.errors.InvalidDocument` in + either case + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionadded:: 3.9 + """ + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _dict_to_bson(document, check_keys, codec_options) + + +def decode( + data: _ReadableBuffer, codec_options: "Optional[CodecOptions[_DocumentType]]" = None +) -> _DocumentType: + """Decode BSON to a document. + + By default, returns a BSON document represented as a Python + :class:`dict`. To use a different :class:`MutableMapping` class, + configure a :class:`~bson.codec_options.CodecOptions`:: + + >>> import collections # From Python standard library. + >>> import bson + >>> from .codec_options import CodecOptions + >>> data = bson.encode({'a': 1}) + >>> decoded_doc = bson.decode(data) + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.decode(data, codec_options=options) + >>> type(decoded_doc) + + + :Parameters: + - `data`: the BSON to decode. Any bytes-like object that implements + the buffer protocol. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionadded:: 3.9 + """ + opts: CodecOptions = codec_options or DEFAULT_CODEC_OPTIONS + if not isinstance(opts, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _bson_to_dict(data, opts) + + +def _decode_all(data: _ReadableBuffer, opts: "CodecOptions[_DocumentType]") -> List[_DocumentType]: + """Decode a BSON data to multiple documents.""" + data, view = get_data_and_view(data) + data_len = len(data) + docs: List[_DocumentType] = [] + position = 0 + end = data_len - 1 + use_raw = _raw_document_class(opts.document_class) + try: + while position < end: + obj_size = _UNPACK_INT_FROM(data, position)[0] + if data_len - position < obj_size: + raise InvalidBSON("invalid object size") + obj_end = position + obj_size - 1 + if data[obj_end] != 0: + raise InvalidBSON("bad eoo") + if use_raw: + docs.append(opts.document_class(data[position : obj_end + 1], opts)) # type: ignore + else: + docs.append(_elements_to_dict(data, view, position + 4, obj_end, opts)) + position += obj_size + return docs + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) + + +if _USE_C: + _decode_all = _cbson._decode_all # noqa: F811 + + +def decode_all( + data: _ReadableBuffer, codec_options: "Optional[CodecOptions[_DocumentType]]" = None +) -> List[_DocumentType]: + """Decode BSON data to multiple documents. + + `data` must be a bytes-like object implementing the buffer protocol that + provides concatenated, valid, BSON-encoded documents. + + :Parameters: + - `data`: BSON data + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.9 + Supports bytes-like objects that implement the buffer protocol. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON regular + expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + """ + opts = codec_options or DEFAULT_CODEC_OPTIONS + if not isinstance(opts, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _decode_all(data, opts) # type: ignore[arg-type] + + +def _decode_selective(rawdoc: Any, fields: Any, codec_options: Any) -> Mapping[Any, Any]: + if _raw_document_class(codec_options.document_class): + # If document_class is RawBSONDocument, use vanilla dictionary for + # decoding command response. + doc = {} + else: + # Else, use the specified document_class. + doc = codec_options.document_class() + for key, value in rawdoc.items(): + if key in fields: + if fields[key] == 1: + doc[key] = _bson_to_dict(rawdoc.raw, codec_options)[key] + else: + doc[key] = _decode_selective(value, fields[key], codec_options) + else: + doc[key] = value + return doc + + +def _array_of_documents_to_buffer(view: memoryview) -> bytes: + # Extract the raw bytes of each document. + position = 0 + _, end = _get_object_size(view, position, len(view)) + position += 4 + buffers: List[memoryview] = [] + append = buffers.append + while position < end - 1: + # Just skip the keys. + while view[position] != 0: + position += 1 + position += 1 + obj_size, _ = _get_object_size(view, position, end) + append(view[position : position + obj_size]) + position += obj_size + if position != end: + raise InvalidBSON("bad object or element length") + return b"".join(buffers) + + +if _USE_C: + _array_of_documents_to_buffer = _cbson._array_of_documents_to_buffer # noqa: F811 + + +def _convert_raw_document_lists_to_streams(document: Any) -> None: + """Convert raw array of documents to a stream of BSON documents.""" + cursor = document.get("cursor") + if not cursor: + return + for key in ("firstBatch", "nextBatch"): + batch = cursor.get(key) + if not batch: + continue + data = _array_of_documents_to_buffer(batch) + if data: + cursor[key] = [data] + else: + cursor[key] = [] + + +def _decode_all_selective(data: Any, codec_options: CodecOptions, fields: Any) -> List[Any]: + """Decode BSON data to a single document while using user-provided + custom decoding logic. + + `data` must be a string representing a valid, BSON-encoded document. + + :Parameters: + - `data`: BSON data + - `codec_options`: An instance of + :class:`~bson.codec_options.CodecOptions` with user-specified type + decoders. If no decoders are found, this method is the same as + ``decode_all``. + - `fields`: Map of document namespaces where data that needs + to be custom decoded lives or None. For example, to custom decode a + list of objects in 'field1.subfield1', the specified value should be + ``{'field1': {'subfield1': 1}}``. If ``fields`` is an empty map or + None, this method is the same as ``decode_all``. + + :Returns: + - `document_list`: Single-member list containing the decoded document. + + .. versionadded:: 3.8 + """ + if not codec_options.type_registry._decoder_map: + return decode_all(data, codec_options) + + if not fields: + return decode_all(data, codec_options.with_options(type_registry=None)) + + # Decode documents for internal use. + from .raw_bson import RawBSONDocument + + internal_codec_options = codec_options.with_options( + document_class=RawBSONDocument, type_registry=None + ) + _doc = _bson_to_dict(data, internal_codec_options) + return [ + _decode_selective( + _doc, + fields, + codec_options, + ) + ] + + +def decode_iter( + data: bytes, codec_options: "Optional[CodecOptions[_DocumentType]]" = None +) -> Iterator[_DocumentType]: + """Decode BSON data to multiple documents as a generator. + + Works similarly to the decode_all function, but yields one document at a + time. + + `data` must be a string of concatenated, valid, BSON-encoded + documents. + + :Parameters: + - `data`: BSON data + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionadded:: 2.8 + """ + opts = codec_options or DEFAULT_CODEC_OPTIONS + if not isinstance(opts, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + position = 0 + end = len(data) - 1 + while position < end: + obj_size = _UNPACK_INT_FROM(data, position)[0] + elements = data[position : position + obj_size] + position += obj_size + + yield _bson_to_dict(elements, opts) + + +def decode_file_iter( + file_obj: Union[BinaryIO, IO], codec_options: "Optional[CodecOptions[_DocumentType]]" = None +) -> Iterator[_DocumentType]: + """Decode bson data from a file to multiple documents as a generator. + + Works similarly to the decode_all function, but reads from the file object + in chunks and parses bson in chunks, yielding one document at a time. + + :Parameters: + - `file_obj`: A file object containing BSON data. + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionadded:: 2.8 + """ + opts = codec_options or DEFAULT_CODEC_OPTIONS + while True: + # Read size of next object. + size_data = file_obj.read(4) + if not size_data: + break # Finished with file normaly. + elif len(size_data) != 4: + raise InvalidBSON("cut off in middle of objsize") + obj_size = _UNPACK_INT_FROM(size_data, 0)[0] - 4 + elements = size_data + file_obj.read(max(0, obj_size)) + yield _bson_to_dict(elements, opts) + + +def is_valid(bson: bytes) -> bool: + """Check that the given string represents valid :class:`BSON` data. + + Raises :class:`TypeError` if `bson` is not an instance of + :class:`str` (:class:`bytes` in python 3). Returns ``True`` + if `bson` is valid :class:`BSON`, ``False`` otherwise. + + :Parameters: + - `bson`: the data to be validated + """ + if not isinstance(bson, bytes): + raise TypeError("BSON data must be an instance of a subclass of bytes") + + try: + _bson_to_dict(bson, DEFAULT_CODEC_OPTIONS) + return True + except Exception: + return False + + +class BSON(bytes): + """BSON (Binary JSON) data. + + .. warning:: Using this class to encode and decode BSON adds a performance + cost. For better performance use the module level functions + :func:`encode` and :func:`decode` instead. + """ + + @classmethod + def encode( + cls: Type["BSON"], + document: _DocumentIn, + check_keys: bool = False, + codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS, + ) -> "BSON": + """Encode a document to a new :class:`BSON` instance. + + A document can be any mapping type (like :class:`dict`). + + Raises :class:`TypeError` if `document` is not a mapping type, + or contains keys that are not instances of + :class:`basestring` (:class:`str` in python 3). Raises + :class:`~bson.errors.InvalidDocument` if `document` cannot be + converted to :class:`BSON`. + + :Parameters: + - `document`: mapping type representing a document + - `check_keys` (optional): check if keys start with '$' or + contain '.', raising :class:`~bson.errors.InvalidDocument` in + either case + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `uuid_subtype` option with `codec_options`. + """ + return cls(encode(document, check_keys, codec_options)) + + def decode(self, codec_options: "CodecOptions[_DocumentType]" = DEFAULT_CODEC_OPTIONS) -> _DocumentType: # type: ignore[override,assignment] + """Decode this BSON data. + + By default, returns a BSON document represented as a Python + :class:`dict`. To use a different :class:`MutableMapping` class, + configure a :class:`~bson.codec_options.CodecOptions`:: + + >>> import collections # From Python standard library. + >>> import bson + >>> from .codec_options import CodecOptions + >>> data = bson.BSON.encode({'a': 1}) + >>> decoded_doc = bson.BSON(data).decode() + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.BSON(data).decode(codec_options=options) + >>> type(decoded_doc) + + + :Parameters: + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON + regular expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + """ + return decode(self, codec_options) + + +def has_c() -> bool: + """Is the C extension installed?""" + return _USE_C + + +def _after_fork(): + """Releases the ObjectID lock child.""" + if ObjectId._inc_lock.locked(): + ObjectId._inc_lock.release() + + +if hasattr(os, "register_at_fork"): + # This will run in the same thread as the fork was called. + # If we fork in a critical region on the same thread, it should break. + # This is fine since we would never call fork directly from a critical region. + os.register_at_fork(after_in_child=_after_fork) diff --git a/src/xtquant/xtbson/bson37/_helpers.py b/src/xtquant/xtbson/bson37/_helpers.py new file mode 100644 index 0000000..ee3b0f1 --- /dev/null +++ b/src/xtquant/xtbson/bson37/_helpers.py @@ -0,0 +1,41 @@ +# Copyright 2021-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Setstate and getstate functions for objects with __slots__, allowing + compatibility with default pickling protocol +""" +from typing import Any, Mapping + + +def _setstate_slots(self: Any, state: Any) -> None: + for slot, value in state.items(): + setattr(self, slot, value) + + +def _mangle_name(name: str, prefix: str) -> str: + if name.startswith("__"): + prefix = "_" + prefix + else: + prefix = "" + return prefix + name + + +def _getstate_slots(self: Any) -> Mapping[Any, Any]: + prefix = self.__class__.__name__ + ret = dict() + for name in self.__slots__: + mangled_name = _mangle_name(name, prefix) + if hasattr(self, mangled_name): + ret[mangled_name] = getattr(self, mangled_name) + return ret diff --git a/src/xtquant/xtbson/bson37/binary.py b/src/xtquant/xtbson/bson37/binary.py new file mode 100644 index 0000000..b4d38a2 --- /dev/null +++ b/src/xtquant/xtbson/bson37/binary.py @@ -0,0 +1,364 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, Any, Tuple, Type, Union +from uuid import UUID + +"""Tools for representing BSON binary data. +""" + +BINARY_SUBTYPE = 0 +"""BSON binary subtype for binary data. + +This is the default subtype for binary data. +""" + +FUNCTION_SUBTYPE = 1 +"""BSON binary subtype for functions. +""" + +OLD_BINARY_SUBTYPE = 2 +"""Old BSON binary subtype for binary data. + +This is the old default subtype, the current +default is :data:`BINARY_SUBTYPE`. +""" + +OLD_UUID_SUBTYPE = 3 +"""Old BSON binary subtype for a UUID. + +:class:`uuid.UUID` instances will automatically be encoded +by :mod:`bson` using this subtype when using +:data:`UuidRepresentation.PYTHON_LEGACY`, +:data:`UuidRepresentation.JAVA_LEGACY`, or +:data:`UuidRepresentation.CSHARP_LEGACY`. + +.. versionadded:: 2.1 +""" + +UUID_SUBTYPE = 4 +"""BSON binary subtype for a UUID. + +This is the standard BSON binary subtype for UUIDs. +:class:`uuid.UUID` instances will automatically be encoded +by :mod:`bson` using this subtype when using +:data:`UuidRepresentation.STANDARD`. +""" + + +if TYPE_CHECKING: + from array import array as _array + from mmap import mmap as _mmap + + +class UuidRepresentation: + UNSPECIFIED = 0 + """An unspecified UUID representation. + + When configured, :class:`uuid.UUID` instances will **not** be + automatically encoded to or decoded from :class:`~bson.binary.Binary`. + When encoding a :class:`uuid.UUID` instance, an error will be raised. + To encode a :class:`uuid.UUID` instance with this configuration, it must + be wrapped in the :class:`~bson.binary.Binary` class by the application + code. When decoding a BSON binary field with a UUID subtype, a + :class:`~bson.binary.Binary` instance will be returned instead of a + :class:`uuid.UUID` instance. + + See :ref:`unspecified-representation-details` for details. + + .. versionadded:: 3.11 + """ + + STANDARD = UUID_SUBTYPE + """The standard UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary, using RFC-4122 byte order with + binary subtype :data:`UUID_SUBTYPE`. + + See :ref:`standard-representation-details` for details. + + .. versionadded:: 3.11 + """ + + PYTHON_LEGACY = OLD_UUID_SUBTYPE + """The Python legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary, using RFC-4122 byte order with + binary subtype :data:`OLD_UUID_SUBTYPE`. + + See :ref:`python-legacy-representation-details` for details. + + .. versionadded:: 3.11 + """ + + JAVA_LEGACY = 5 + """The Java legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary subtype :data:`OLD_UUID_SUBTYPE`, + using the Java driver's legacy byte order. + + See :ref:`java-legacy-representation-details` for details. + + .. versionadded:: 3.11 + """ + + CSHARP_LEGACY = 6 + """The C#/.net legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from . binary subtype :data:`OLD_UUID_SUBTYPE`, + using the C# driver's legacy byte order. + + See :ref:`csharp-legacy-representation-details` for details. + + .. versionadded:: 3.11 + """ + + +STANDARD = UuidRepresentation.STANDARD +"""An alias for :data:`UuidRepresentation.STANDARD`. + +.. versionadded:: 3.0 +""" + +PYTHON_LEGACY = UuidRepresentation.PYTHON_LEGACY +"""An alias for :data:`UuidRepresentation.PYTHON_LEGACY`. + +.. versionadded:: 3.0 +""" + +JAVA_LEGACY = UuidRepresentation.JAVA_LEGACY +"""An alias for :data:`UuidRepresentation.JAVA_LEGACY`. + +.. versionchanged:: 3.6 + BSON binary subtype 4 is decoded using RFC-4122 byte order. +.. versionadded:: 2.3 +""" + +CSHARP_LEGACY = UuidRepresentation.CSHARP_LEGACY +"""An alias for :data:`UuidRepresentation.CSHARP_LEGACY`. + +.. versionchanged:: 3.6 + BSON binary subtype 4 is decoded using RFC-4122 byte order. +.. versionadded:: 2.3 +""" + +ALL_UUID_SUBTYPES = (OLD_UUID_SUBTYPE, UUID_SUBTYPE) +ALL_UUID_REPRESENTATIONS = ( + UuidRepresentation.UNSPECIFIED, + UuidRepresentation.STANDARD, + UuidRepresentation.PYTHON_LEGACY, + UuidRepresentation.JAVA_LEGACY, + UuidRepresentation.CSHARP_LEGACY, +) +UUID_REPRESENTATION_NAMES = { + UuidRepresentation.UNSPECIFIED: "UuidRepresentation.UNSPECIFIED", + UuidRepresentation.STANDARD: "UuidRepresentation.STANDARD", + UuidRepresentation.PYTHON_LEGACY: "UuidRepresentation.PYTHON_LEGACY", + UuidRepresentation.JAVA_LEGACY: "UuidRepresentation.JAVA_LEGACY", + UuidRepresentation.CSHARP_LEGACY: "UuidRepresentation.CSHARP_LEGACY", +} + +MD5_SUBTYPE = 5 +"""BSON binary subtype for an MD5 hash. +""" + +COLUMN_SUBTYPE = 7 +"""BSON binary subtype for columns. + +.. versionadded:: 4.0 +""" + +USER_DEFINED_SUBTYPE = 128 +"""BSON binary subtype for any user defined structure. +""" + + +class Binary(bytes): + """Representation of BSON binary data. + + This is necessary because we want to represent Python strings as + the BSON string type. We need to wrap binary data so we can tell + the difference between what should be considered binary data and + what should be considered a string when we encode to BSON. + + Raises TypeError if `data` is not an instance of :class:`bytes` + (:class:`str` in python 2) or `subtype` is not an instance of + :class:`int`. Raises ValueError if `subtype` is not in [0, 256). + + .. note:: + In python 3 instances of Binary with subtype 0 will be decoded + directly to :class:`bytes`. + + :Parameters: + - `data`: the binary data to represent. Can be any bytes-like type + that implements the buffer protocol. + - `subtype` (optional): the `binary subtype + `_ + to use + + .. versionchanged:: 3.9 + Support any bytes-like type that implements the buffer protocol. + """ + + _type_marker = 5 + __subtype: int + + def __new__( + cls: Type["Binary"], + data: Union[memoryview, bytes, "_mmap", "_array"], + subtype: int = BINARY_SUBTYPE, + ) -> "Binary": + if not isinstance(subtype, int): + raise TypeError("subtype must be an instance of int") + if subtype >= 256 or subtype < 0: + raise ValueError("subtype must be contained in [0, 256)") + # Support any type that implements the buffer protocol. + self = bytes.__new__(cls, memoryview(data).tobytes()) + self.__subtype = subtype + return self + + @classmethod + def from_uuid( + cls: Type["Binary"], uuid: UUID, uuid_representation: int = UuidRepresentation.STANDARD + ) -> "Binary": + """Create a BSON Binary object from a Python UUID. + + Creates a :class:`~bson.binary.Binary` object from a + :class:`uuid.UUID` instance. Assumes that the native + :class:`uuid.UUID` instance uses the byte-order implied by the + provided ``uuid_representation``. + + Raises :exc:`TypeError` if `uuid` is not an instance of + :class:`~uuid.UUID`. + + :Parameters: + - `uuid`: A :class:`uuid.UUID` instance. + - `uuid_representation`: A member of + :class:`~bson.binary.UuidRepresentation`. Default: + :const:`~bson.binary.UuidRepresentation.STANDARD`. + See :ref:`handling-uuid-data-example` for details. + + .. versionadded:: 3.11 + """ + if not isinstance(uuid, UUID): + raise TypeError("uuid must be an instance of uuid.UUID") + + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from .binary.UuidRepresentation" + ) + + if uuid_representation == UuidRepresentation.UNSPECIFIED: + raise ValueError( + "cannot encode native uuid.UUID with " + "UuidRepresentation.UNSPECIFIED. UUIDs can be manually " + "converted to bson.Binary instances using " + "bson.Binary.from_uuid() or a different UuidRepresentation " + "can be configured. See the documentation for " + "UuidRepresentation for more information." + ) + + subtype = OLD_UUID_SUBTYPE + if uuid_representation == UuidRepresentation.PYTHON_LEGACY: + payload = uuid.bytes + elif uuid_representation == UuidRepresentation.JAVA_LEGACY: + from_uuid = uuid.bytes + payload = from_uuid[0:8][::-1] + from_uuid[8:16][::-1] + elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: + payload = uuid.bytes_le + else: + # uuid_representation == UuidRepresentation.STANDARD + subtype = UUID_SUBTYPE + payload = uuid.bytes + + return cls(payload, subtype) + + def as_uuid(self, uuid_representation: int = UuidRepresentation.STANDARD) -> UUID: + """Create a Python UUID from this BSON Binary object. + + Decodes this binary object as a native :class:`uuid.UUID` instance + with the provided ``uuid_representation``. + + Raises :exc:`ValueError` if this :class:`~bson.binary.Binary` instance + does not contain a UUID. + + :Parameters: + - `uuid_representation`: A member of + :class:`~bson.binary.UuidRepresentation`. Default: + :const:`~bson.binary.UuidRepresentation.STANDARD`. + See :ref:`handling-uuid-data-example` for details. + + .. versionadded:: 3.11 + """ + if self.subtype not in ALL_UUID_SUBTYPES: + raise ValueError("cannot decode subtype %s as a uuid" % (self.subtype,)) + + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from .binary.UuidRepresentation" + ) + + if uuid_representation == UuidRepresentation.UNSPECIFIED: + raise ValueError("uuid_representation cannot be UNSPECIFIED") + elif uuid_representation == UuidRepresentation.PYTHON_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes=self) + elif uuid_representation == UuidRepresentation.JAVA_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes=self[0:8][::-1] + self[8:16][::-1]) + elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes_le=self) + else: + # uuid_representation == UuidRepresentation.STANDARD + if self.subtype == UUID_SUBTYPE: + return UUID(bytes=self) + + raise ValueError( + "cannot decode subtype %s to %s" + % (self.subtype, UUID_REPRESENTATION_NAMES[uuid_representation]) + ) + + @property + def subtype(self) -> int: + """Subtype of this binary data.""" + return self.__subtype + + def __getnewargs__(self) -> Tuple[bytes, int]: # type: ignore[override] + # Work around http://bugs.python.org/issue7382 + data = super(Binary, self).__getnewargs__()[0] + if not isinstance(data, bytes): + data = data.encode("latin-1") + return data, self.__subtype + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Binary): + return (self.__subtype, bytes(self)) == (other.subtype, bytes(other)) + # We don't return NotImplemented here because if we did then + # Binary("foo") == "foo" would return True, since Binary is a + # subclass of str... + return False + + def __hash__(self) -> int: + return super(Binary, self).__hash__() ^ hash(self.__subtype) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __repr__(self): + return "Binary(%s, %s)" % (bytes.__repr__(self), self.__subtype) diff --git a/src/xtquant/xtbson/bson37/code.py b/src/xtquant/xtbson/bson37/code.py new file mode 100644 index 0000000..b732e82 --- /dev/null +++ b/src/xtquant/xtbson/bson37/code.py @@ -0,0 +1,101 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing JavaScript code in BSON. +""" + +from collections.abc import Mapping as _Mapping +from typing import Any, Mapping, Optional, Type, Union + + +class Code(str): + """BSON's JavaScript code type. + + Raises :class:`TypeError` if `code` is not an instance of + :class:`basestring` (:class:`str` in python 3) or `scope` + is not ``None`` or an instance of :class:`dict`. + + Scope variables can be set by passing a dictionary as the `scope` + argument or by using keyword arguments. If a variable is set as a + keyword argument it will override any setting for that variable in + the `scope` dictionary. + + :Parameters: + - `code`: A string containing JavaScript code to be evaluated or another + instance of Code. In the latter case, the scope of `code` becomes this + Code's :attr:`scope`. + - `scope` (optional): dictionary representing the scope in which + `code` should be evaluated - a mapping from identifiers (as + strings) to values. Defaults to ``None``. This is applied after any + scope associated with a given `code` above. + - `**kwargs` (optional): scope variables can also be passed as + keyword arguments. These are applied after `scope` and `code`. + + .. versionchanged:: 3.4 + The default value for :attr:`scope` is ``None`` instead of ``{}``. + + """ + + _type_marker = 13 + __scope: Union[Mapping[str, Any], None] + + def __new__( + cls: Type["Code"], + code: Union[str, "Code"], + scope: Optional[Mapping[str, Any]] = None, + **kwargs: Any + ) -> "Code": + if not isinstance(code, str): + raise TypeError("code must be an instance of str") + + self = str.__new__(cls, code) + + try: + self.__scope = code.scope # type: ignore + except AttributeError: + self.__scope = None + + if scope is not None: + if not isinstance(scope, _Mapping): + raise TypeError("scope must be an instance of dict") + if self.__scope is not None: + self.__scope.update(scope) # type: ignore + else: + self.__scope = scope + + if kwargs: + if self.__scope is not None: + self.__scope.update(kwargs) # type: ignore + else: + self.__scope = kwargs + + return self + + @property + def scope(self) -> Optional[Mapping[str, Any]]: + """Scope dictionary for this instance or ``None``.""" + return self.__scope + + def __repr__(self): + return "Code(%s, %r)" % (str.__repr__(self), self.__scope) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Code): + return (self.__scope, str(self)) == (other.__scope, str(other)) + return False + + __hash__: Any = None + + def __ne__(self, other: Any) -> bool: + return not self == other diff --git a/src/xtquant/xtbson/bson37/codec_options.py b/src/xtquant/xtbson/bson37/codec_options.py new file mode 100644 index 0000000..5dd0e03 --- /dev/null +++ b/src/xtquant/xtbson/bson37/codec_options.py @@ -0,0 +1,462 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for specifying BSON codec options.""" + +import abc +import datetime +import enum +from collections.abc import MutableMapping as _MutableMapping +from typing import ( + Any, + Callable, + Dict, + Iterable, + Mapping, + NamedTuple, + Optional, + Type, + TypeVar, + Union, + cast, +) + +from .binary import ( + ALL_UUID_REPRESENTATIONS, + UUID_REPRESENTATION_NAMES, + UuidRepresentation, +) + + +def _abstractproperty(func: Callable[..., Any]) -> property: + return property(abc.abstractmethod(func)) + + +_RAW_BSON_DOCUMENT_MARKER = 101 + + +def _raw_document_class(document_class: Any) -> bool: + """Determine if a document_class is a RawBSONDocument class.""" + marker = getattr(document_class, "_type_marker", None) + return marker == _RAW_BSON_DOCUMENT_MARKER + + +class TypeEncoder(abc.ABC): + """Base class for defining type codec classes which describe how a + custom type can be transformed to one of the types BSON understands. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + + @_abstractproperty + def python_type(self) -> Any: + """The Python type to be converted into something serializable.""" + pass + + @abc.abstractmethod + def transform_python(self, value: Any) -> Any: + """Convert the given Python object into something serializable.""" + pass + + +class TypeDecoder(abc.ABC): + """Base class for defining type codec classes which describe how a + BSON type can be transformed to a custom type. + + Codec classes must implement the ``bson_type`` attribute, and the + ``transform_bson`` method to support decoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + + @_abstractproperty + def bson_type(self) -> Any: + """The BSON type to be converted into our own type.""" + pass + + @abc.abstractmethod + def transform_bson(self, value: Any) -> Any: + """Convert the given BSON value into our own type.""" + pass + + +class TypeCodec(TypeEncoder, TypeDecoder): + """Base class for defining type codec classes which describe how a + custom type can be transformed to/from one of the types :mod:`bson` + can already encode/decode. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding, as well as the + ``bson_type`` attribute, and the ``transform_bson`` method to support + decoding. + + See :ref:`custom-type-type-codec` documentation for an example. + """ + + pass + + +_Codec = Union[TypeEncoder, TypeDecoder, TypeCodec] +_Fallback = Callable[[Any], Any] +_DocumentType = TypeVar("_DocumentType", bound=Mapping[str, Any]) + + +class TypeRegistry(object): + """Encapsulates type codecs used in encoding and / or decoding BSON, as + well as the fallback encoder. Type registries cannot be modified after + instantiation. + + ``TypeRegistry`` can be initialized with an iterable of type codecs, and + a callable for the fallback encoder:: + + >>> from .codec_options import TypeRegistry + >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...], + ... fallback_encoder) + + See :ref:`custom-type-type-registry` documentation for an example. + + :Parameters: + - `type_codecs` (optional): iterable of type codec instances. If + ``type_codecs`` contains multiple codecs that transform a single + python or BSON type, the transformation specified by the type codec + occurring last prevails. A TypeError will be raised if one or more + type codecs modify the encoding behavior of a built-in :mod:`bson` + type. + - `fallback_encoder` (optional): callable that accepts a single, + unencodable python value and transforms it into a type that + :mod:`bson` can encode. See :ref:`fallback-encoder-callable` + documentation for an example. + """ + + def __init__( + self, + type_codecs: Optional[Iterable[_Codec]] = None, + fallback_encoder: Optional[_Fallback] = None, + ) -> None: + self.__type_codecs = list(type_codecs or []) + self._fallback_encoder = fallback_encoder + self._encoder_map: Dict[Any, Any] = {} + self._decoder_map: Dict[Any, Any] = {} + + if self._fallback_encoder is not None: + if not callable(fallback_encoder): + raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder)) + + for codec in self.__type_codecs: + is_valid_codec = False + if isinstance(codec, TypeEncoder): + self._validate_type_encoder(codec) + is_valid_codec = True + self._encoder_map[codec.python_type] = codec.transform_python + if isinstance(codec, TypeDecoder): + is_valid_codec = True + self._decoder_map[codec.bson_type] = codec.transform_bson + if not is_valid_codec: + raise TypeError( + "Expected an instance of %s, %s, or %s, got %r instead" + % (TypeEncoder.__name__, TypeDecoder.__name__, TypeCodec.__name__, codec) + ) + + def _validate_type_encoder(self, codec: _Codec) -> None: + from . import _BUILT_IN_TYPES + + for pytype in _BUILT_IN_TYPES: + if issubclass(cast(TypeCodec, codec).python_type, pytype): + err_msg = ( + "TypeEncoders cannot change how built-in types are " + "encoded (encoder %s transforms type %s)" % (codec, pytype) + ) + raise TypeError(err_msg) + + def __repr__(self): + return "%s(type_codecs=%r, fallback_encoder=%r)" % ( + self.__class__.__name__, + self.__type_codecs, + self._fallback_encoder, + ) + + def __eq__(self, other: Any) -> Any: + if not isinstance(other, type(self)): + return NotImplemented + return ( + (self._decoder_map == other._decoder_map) + and (self._encoder_map == other._encoder_map) + and (self._fallback_encoder == other._fallback_encoder) + ) + + +class DatetimeConversion(int, enum.Enum): + """Options for decoding BSON datetimes.""" + + DATETIME = 1 + """Decode a BSON UTC datetime as a :class:`datetime.datetime`. + + BSON UTC datetimes that cannot be represented as a + :class:`~datetime.datetime` will raise an :class:`OverflowError` + or a :class:`ValueError`. + + .. versionadded 4.3 + """ + + DATETIME_CLAMP = 2 + """Decode a BSON UTC datetime as a :class:`datetime.datetime`, clamping + to :attr:`~datetime.datetime.min` and :attr:`~datetime.datetime.max`. + + .. versionadded 4.3 + """ + + DATETIME_MS = 3 + """Decode a BSON UTC datetime as a :class:`~bson.datetime_ms.DatetimeMS` + object. + + .. versionadded 4.3 + """ + + DATETIME_AUTO = 4 + """Decode a BSON UTC datetime as a :class:`datetime.datetime` if possible, + and a :class:`~bson.datetime_ms.DatetimeMS` if not. + + .. versionadded 4.3 + """ + + +class _BaseCodecOptions(NamedTuple): + document_class: Type[Mapping[str, Any]] + tz_aware: bool + uuid_representation: int + unicode_decode_error_handler: str + tzinfo: Optional[datetime.tzinfo] + type_registry: TypeRegistry + datetime_conversion: Optional[DatetimeConversion] + + +class CodecOptions(_BaseCodecOptions): + """Encapsulates options used encoding and / or decoding BSON. + + The `document_class` option is used to define a custom type for use + decoding BSON documents. Access to the underlying raw BSON bytes for + a document is available using the :class:`~bson.raw_bson.RawBSONDocument` + type:: + + >>> from .raw_bson import RawBSONDocument + >>> from .codec_options import CodecOptions + >>> codec_options = CodecOptions(document_class=RawBSONDocument) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc.raw + '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00' + + The document class can be any type that inherits from + :class:`~collections.abc.MutableMapping`:: + + >>> class AttributeDict(dict): + ... # A dict that supports attribute access. + ... def __getattr__(self, key): + ... return self[key] + ... def __setattr__(self, key, value): + ... self[key] = value + ... + >>> codec_options = CodecOptions(document_class=AttributeDict) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc._id + ObjectId('5b3016359110ea14e8c58b93') + + See :doc:`/examples/datetimes` for examples using the `tz_aware` and + `tzinfo` options. + + See :doc:`/examples/uuid` for examples using the `uuid_representation` + option. + + :Parameters: + - `document_class`: BSON documents returned in queries will be decoded + to an instance of this class. Must be a subclass of + :class:`~collections.abc.MutableMapping`. Defaults to :class:`dict`. + - `tz_aware`: If ``True``, BSON datetimes will be decoded to timezone + aware instances of :class:`~datetime.datetime`. Otherwise they will be + naive. Defaults to ``False``. + - `uuid_representation`: The BSON representation to use when encoding + and decoding instances of :class:`~uuid.UUID`. Defaults to + :data:`~bson.binary.UuidRepresentation.UNSPECIFIED`. New + applications should consider setting this to + :data:`~bson.binary.UuidRepresentation.STANDARD` for cross language + compatibility. See :ref:`handling-uuid-data-example` for details. + - `unicode_decode_error_handler`: The error handler to apply when + a Unicode-related error occurs during BSON decoding that would + otherwise raise :exc:`UnicodeDecodeError`. Valid options include + 'strict', 'replace', 'backslashreplace', 'surrogateescape', and + 'ignore'. Defaults to 'strict'. + - `tzinfo`: A :class:`~datetime.tzinfo` subclass that specifies the + timezone to/from which :class:`~datetime.datetime` objects should be + encoded/decoded. + - `type_registry`: Instance of :class:`TypeRegistry` used to customize + encoding and decoding behavior. + - `datetime_conversion`: Specifies how UTC datetimes should be decoded + within BSON. Valid options include 'datetime_ms' to return as a + DatetimeMS, 'datetime' to return as a datetime.datetime and + raising a ValueError for out-of-range values, 'datetime_auto' to + return DatetimeMS objects when the underlying datetime is + out-of-range and 'datetime_clamp' to clamp to the minimum and + maximum possible datetimes. Defaults to 'datetime'. + .. versionchanged:: 4.0 + The default for `uuid_representation` was changed from + :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to + :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + + .. versionadded:: 3.8 + `type_registry` attribute. + + .. warning:: Care must be taken when changing + `unicode_decode_error_handler` from its default value ('strict'). + The 'replace' and 'ignore' modes should not be used when documents + retrieved from the server will be modified in the client application + and stored back to the server. + """ + + def __new__( + cls: Type["CodecOptions"], + document_class: Optional[Type[Mapping[str, Any]]] = None, + tz_aware: bool = False, + uuid_representation: Optional[int] = UuidRepresentation.UNSPECIFIED, + unicode_decode_error_handler: str = "strict", + tzinfo: Optional[datetime.tzinfo] = None, + type_registry: Optional[TypeRegistry] = None, + datetime_conversion: Optional[DatetimeConversion] = DatetimeConversion.DATETIME, + ) -> "CodecOptions": + doc_class = document_class or dict + # issubclass can raise TypeError for generic aliases like SON[str, Any]. + # In that case we can use the base class for the comparison. + is_mapping = False + try: + is_mapping = issubclass(doc_class, _MutableMapping) + except TypeError: + if hasattr(doc_class, "__origin__"): + is_mapping = issubclass(doc_class.__origin__, _MutableMapping) # type: ignore[union-attr] + if not (is_mapping or _raw_document_class(doc_class)): + raise TypeError( + "document_class must be dict, bson.son.SON, " + "bson.raw_bson.RawBSONDocument, or a " + "subclass of collections.abc.MutableMapping" + ) + if not isinstance(tz_aware, bool): + raise TypeError("tz_aware must be True or False") + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from .binary.UuidRepresentation" + ) + if not isinstance(unicode_decode_error_handler, str): + raise ValueError("unicode_decode_error_handler must be a string") + if tzinfo is not None: + if not isinstance(tzinfo, datetime.tzinfo): + raise TypeError("tzinfo must be an instance of datetime.tzinfo") + if not tz_aware: + raise ValueError("cannot specify tzinfo without also setting tz_aware=True") + + type_registry = type_registry or TypeRegistry() + + if not isinstance(type_registry, TypeRegistry): + raise TypeError("type_registry must be an instance of TypeRegistry") + + return tuple.__new__( + cls, + ( + doc_class, + tz_aware, + uuid_representation, + unicode_decode_error_handler, + tzinfo, + type_registry, + datetime_conversion, + ), + ) + + def _arguments_repr(self) -> str: + """Representation of the arguments used to create this object.""" + document_class_repr = "dict" if self.document_class is dict else repr(self.document_class) + + uuid_rep_repr = UUID_REPRESENTATION_NAMES.get( + self.uuid_representation, self.uuid_representation + ) + + return ( + "document_class=%s, tz_aware=%r, uuid_representation=%s, " + "unicode_decode_error_handler=%r, tzinfo=%r, " + "type_registry=%r, datetime_conversion=%s" + % ( + document_class_repr, + self.tz_aware, + uuid_rep_repr, + self.unicode_decode_error_handler, + self.tzinfo, + self.type_registry, + self.datetime_conversion, + ) + ) + + def _options_dict(self) -> Dict[str, Any]: + """Dictionary of the arguments used to create this object.""" + # TODO: PYTHON-2442 use _asdict() instead + return { + "document_class": self.document_class, + "tz_aware": self.tz_aware, + "uuid_representation": self.uuid_representation, + "unicode_decode_error_handler": self.unicode_decode_error_handler, + "tzinfo": self.tzinfo, + "type_registry": self.type_registry, + "datetime_conversion": self.datetime_conversion, + } + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self._arguments_repr()) + + def with_options(self, **kwargs: Any) -> "CodecOptions": + """Make a copy of this CodecOptions, overriding some options:: + + >>> from .codec_options import DEFAULT_CODEC_OPTIONS + >>> DEFAULT_CODEC_OPTIONS.tz_aware + False + >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True) + >>> options.tz_aware + True + + .. versionadded:: 3.5 + """ + opts = self._options_dict() + opts.update(kwargs) + return CodecOptions(**opts) + + +DEFAULT_CODEC_OPTIONS = CodecOptions() + + +def _parse_codec_options(options: Any) -> CodecOptions: + """Parse BSON codec options.""" + kwargs = {} + for k in set(options) & { + "document_class", + "tz_aware", + "uuidrepresentation", + "unicode_decode_error_handler", + "tzinfo", + "type_registry", + "datetime_conversion", + }: + if k == "uuidrepresentation": + kwargs["uuid_representation"] = options[k] + else: + kwargs[k] = options[k] + return CodecOptions(**kwargs) diff --git a/src/xtquant/xtbson/bson37/codec_options.pyi b/src/xtquant/xtbson/bson37/codec_options.pyi new file mode 100644 index 0000000..2424516 --- /dev/null +++ b/src/xtquant/xtbson/bson37/codec_options.pyi @@ -0,0 +1,108 @@ +# Copyright 2022-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Workaround for https://bugs.python.org/issue43923. +Ideally we would have done this with a single class, but +generic subclasses *must* take a parameter, and prior to Python 3.9 +or in Python 3.7 and 3.8 with `from __future__ import annotations`, +you get the error: "TypeError: 'type' object is not subscriptable". +""" + +import datetime +import abc +import enum +from typing import Tuple, Generic, Optional, Mapping, Any, TypeVar, Type, Dict, Iterable, Tuple, MutableMapping, Callable, Union + + +class TypeEncoder(abc.ABC, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def python_type(self) -> Any: ... + @abc.abstractmethod + def transform_python(self, value: Any) -> Any: ... + +class TypeDecoder(abc.ABC, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def bson_type(self) -> Any: ... + @abc.abstractmethod + def transform_bson(self, value: Any) -> Any: ... + +class TypeCodec(TypeEncoder, TypeDecoder, metaclass=abc.ABCMeta): ... + +Codec = Union[TypeEncoder, TypeDecoder, TypeCodec] +Fallback = Callable[[Any], Any] + +class TypeRegistry: + _decoder_map: Dict[Any, Any] + _encoder_map: Dict[Any, Any] + _fallback_encoder: Optional[Fallback] + + def __init__(self, type_codecs: Optional[Iterable[Codec]] = ..., fallback_encoder: Optional[Fallback] = ...) -> None: ... + def __eq__(self, other: Any) -> Any: ... + + +_DocumentType = TypeVar("_DocumentType", bound=Mapping[str, Any]) + +class DatetimeConversion(int, enum.Enum): + DATETIME = ... + DATETIME_CLAMP = ... + DATETIME_MS = ... + DATETIME_AUTO = ... + +class CodecOptions(Tuple, Generic[_DocumentType]): + document_class: Type[_DocumentType] + tz_aware: bool + uuid_representation: int + unicode_decode_error_handler: Optional[str] + tzinfo: Optional[datetime.tzinfo] + type_registry: TypeRegistry + datetime_conversion: Optional[int] + + def __new__( + cls: Type[CodecOptions], + document_class: Optional[Type[_DocumentType]] = ..., + tz_aware: bool = ..., + uuid_representation: Optional[int] = ..., + unicode_decode_error_handler: Optional[str] = ..., + tzinfo: Optional[datetime.tzinfo] = ..., + type_registry: Optional[TypeRegistry] = ..., + datetime_conversion: Optional[int] = ..., + ) -> CodecOptions[_DocumentType]: ... + + # CodecOptions API + def with_options(self, **kwargs: Any) -> CodecOptions[_DocumentType]: ... + + def _arguments_repr(self) -> str: ... + + def _options_dict(self) -> Dict[Any, Any]: ... + + # NamedTuple API + @classmethod + def _make(cls, obj: Iterable) -> CodecOptions[_DocumentType]: ... + + def _asdict(self) -> Dict[str, Any]: ... + + def _replace(self, **kwargs: Any) -> CodecOptions[_DocumentType]: ... + + _source: str + _fields: Tuple[str] + + +DEFAULT_CODEC_OPTIONS: CodecOptions[MutableMapping[str, Any]] +_RAW_BSON_DOCUMENT_MARKER: int + +def _raw_document_class(document_class: Any) -> bool: ... + +def _parse_codec_options(options: Any) -> CodecOptions: ... diff --git a/src/xtquant/xtbson/bson37/datetime_ms.py b/src/xtquant/xtbson/bson37/datetime_ms.py new file mode 100644 index 0000000..e8bdf40 --- /dev/null +++ b/src/xtquant/xtbson/bson37/datetime_ms.py @@ -0,0 +1,158 @@ +# Copyright 2022-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Tools for representing the BSON datetime type. + +.. versionadded:: 4.3 +""" + +import calendar +import datetime +import functools +from typing import Any, Union, cast + +from .codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, DatetimeConversion +from .tz_util import utc + +EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) +EPOCH_NAIVE = datetime.datetime.utcfromtimestamp(0) + + +class DatetimeMS: + """Represents a BSON UTC datetime.""" + + __slots__ = ("_value",) + + def __init__(self, value: Union[int, datetime.datetime]): + """Represents a BSON UTC datetime. + + BSON UTC datetimes are defined as an int64 of milliseconds since the + Unix epoch. The principal use of DatetimeMS is to represent + datetimes outside the range of the Python builtin + :class:`~datetime.datetime` class when + encoding/decoding BSON. + + To decode UTC datetimes as a ``DatetimeMS``, `datetime_conversion` in + :class:`~bson.CodecOptions` must be set to 'datetime_ms' or + 'datetime_auto'. See :ref:`handling-out-of-range-datetimes` for + details. + + :Parameters: + - `value`: An instance of :class:`datetime.datetime` to be + represented as milliseconds since the Unix epoch, or int of + milliseconds since the Unix epoch. + """ + if isinstance(value, int): + if not (-(2**63) <= value <= 2**63 - 1): + raise OverflowError("Must be a 64-bit integer of milliseconds") + self._value = value + elif isinstance(value, datetime.datetime): + self._value = _datetime_to_millis(value) + else: + raise TypeError(f"{type(value)} is not a valid type for DatetimeMS") + + def __hash__(self) -> int: + return hash(self._value) + + def __repr__(self) -> str: + return type(self).__name__ + "(" + str(self._value) + ")" + + def __lt__(self, other: Union["DatetimeMS", int]) -> bool: + return self._value < other + + def __le__(self, other: Union["DatetimeMS", int]) -> bool: + return self._value <= other + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DatetimeMS): + return self._value == other._value + return False + + def __ne__(self, other: Any) -> bool: + if isinstance(other, DatetimeMS): + return self._value != other._value + return True + + def __gt__(self, other: Union["DatetimeMS", int]) -> bool: + return self._value > other + + def __ge__(self, other: Union["DatetimeMS", int]) -> bool: + return self._value >= other + + _type_marker = 9 + + def as_datetime(self, codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS) -> datetime.datetime: + """Create a Python :class:`~datetime.datetime` from this DatetimeMS object. + + :Parameters: + - `codec_options`: A CodecOptions instance for specifying how the + resulting DatetimeMS object will be formatted using ``tz_aware`` + and ``tz_info``. Defaults to + :const:`~bson.codec_options.DEFAULT_CODEC_OPTIONS`. + """ + return cast(datetime.datetime, _millis_to_datetime(self._value, codec_options)) + + def __int__(self) -> int: + return self._value + + +# Inclusive and exclusive min and max for timezones. +# Timezones are hashed by their offset, which is a timedelta +# and therefore there are more than 24 possible timezones. +@functools.lru_cache(maxsize=None) +def _min_datetime_ms(tz=datetime.timezone.utc): + return _datetime_to_millis(datetime.datetime.min.replace(tzinfo=tz)) + + +@functools.lru_cache(maxsize=None) +def _max_datetime_ms(tz=datetime.timezone.utc): + return _datetime_to_millis(datetime.datetime.max.replace(tzinfo=tz)) + + +def _millis_to_datetime(millis: int, opts: CodecOptions) -> Union[datetime.datetime, DatetimeMS]: + """Convert milliseconds since epoch UTC to datetime.""" + if ( + opts.datetime_conversion == DatetimeConversion.DATETIME + or opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP + or opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO + ): + tz = opts.tzinfo or datetime.timezone.utc + if opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP: + millis = max(_min_datetime_ms(tz), min(millis, _max_datetime_ms(tz))) + elif opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO: + if not (_min_datetime_ms(tz) <= millis <= _max_datetime_ms(tz)): + return DatetimeMS(millis) + + diff = ((millis % 1000) + 1000) % 1000 + seconds = (millis - diff) // 1000 + micros = diff * 1000 + + if opts.tz_aware: + dt = EPOCH_AWARE + datetime.timedelta(seconds=seconds, microseconds=micros) + if opts.tzinfo: + dt = dt.astimezone(tz) + return dt + else: + return EPOCH_NAIVE + datetime.timedelta(seconds=seconds, microseconds=micros) + elif opts.datetime_conversion == DatetimeConversion.DATETIME_MS: + return DatetimeMS(millis) + else: + raise ValueError("datetime_conversion must be an element of DatetimeConversion") + + +def _datetime_to_millis(dtm: datetime.datetime) -> int: + """Convert datetime to milliseconds since epoch UTC.""" + if dtm.utcoffset() is not None: + dtm = dtm - dtm.utcoffset() # type: ignore + return int(calendar.timegm(dtm.timetuple()) * 1000 + dtm.microsecond // 1000) diff --git a/src/xtquant/xtbson/bson37/dbref.py b/src/xtquant/xtbson/bson37/dbref.py new file mode 100644 index 0000000..d08eb88 --- /dev/null +++ b/src/xtquant/xtbson/bson37/dbref.py @@ -0,0 +1,133 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for manipulating DBRefs (references to MongoDB documents).""" + +from copy import deepcopy +from typing import Any, Mapping, Optional + +from ._helpers import _getstate_slots, _setstate_slots +from .son import SON + + +class DBRef(object): + """A reference to a document stored in MongoDB.""" + + __slots__ = "__collection", "__id", "__database", "__kwargs" + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. + _type_marker = 100 + + def __init__( + self, + collection: str, + id: Any, + database: Optional[str] = None, + _extra: Optional[Mapping[str, Any]] = None, + **kwargs: Any + ) -> None: + """Initialize a new :class:`DBRef`. + + Raises :class:`TypeError` if `collection` or `database` is not + an instance of :class:`basestring` (:class:`str` in python 3). + `database` is optional and allows references to documents to work + across databases. Any additional keyword arguments will create + additional fields in the resultant embedded document. + + :Parameters: + - `collection`: name of the collection the document is stored in + - `id`: the value of the document's ``"_id"`` field + - `database` (optional): name of the database to reference + - `**kwargs` (optional): additional keyword arguments will + create additional, custom fields + + .. seealso:: The MongoDB documentation on `dbrefs `_. + """ + if not isinstance(collection, str): + raise TypeError("collection must be an instance of str") + if database is not None and not isinstance(database, str): + raise TypeError("database must be an instance of str") + + self.__collection = collection + self.__id = id + self.__database = database + kwargs.update(_extra or {}) + self.__kwargs = kwargs + + @property + def collection(self) -> str: + """Get the name of this DBRef's collection.""" + return self.__collection + + @property + def id(self) -> Any: + """Get this DBRef's _id.""" + return self.__id + + @property + def database(self) -> Optional[str]: + """Get the name of this DBRef's database. + + Returns None if this DBRef doesn't specify a database. + """ + return self.__database + + def __getattr__(self, key: Any) -> Any: + try: + return self.__kwargs[key] + except KeyError: + raise AttributeError(key) + + def as_doc(self) -> SON[str, Any]: + """Get the SON document representation of this DBRef. + + Generally not needed by application developers + """ + doc = SON([("$ref", self.collection), ("$id", self.id)]) + if self.database is not None: + doc["$db"] = self.database + doc.update(self.__kwargs) + return doc + + def __repr__(self): + extra = "".join([", %s=%r" % (k, v) for k, v in self.__kwargs.items()]) + if self.database is None: + return "DBRef(%r, %r%s)" % (self.collection, self.id, extra) + return "DBRef(%r, %r, %r%s)" % (self.collection, self.id, self.database, extra) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DBRef): + us = (self.__database, self.__collection, self.__id, self.__kwargs) + them = (other.__database, other.__collection, other.__id, other.__kwargs) + return us == them + return NotImplemented + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __hash__(self) -> int: + """Get a hash value for this :class:`DBRef`.""" + return hash( + (self.__collection, self.__id, self.__database, tuple(sorted(self.__kwargs.items()))) + ) + + def __deepcopy__(self, memo: Any) -> "DBRef": + """Support function for `copy.deepcopy()`.""" + return DBRef( + deepcopy(self.__collection, memo), + deepcopy(self.__id, memo), + deepcopy(self.__database, memo), + deepcopy(self.__kwargs, memo), + ) diff --git a/src/xtquant/xtbson/bson37/decimal128.py b/src/xtquant/xtbson/bson37/decimal128.py new file mode 100644 index 0000000..ab2d1a2 --- /dev/null +++ b/src/xtquant/xtbson/bson37/decimal128.py @@ -0,0 +1,314 @@ +# Copyright 2016-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with the BSON decimal128 type. + +.. versionadded:: 3.4 +""" + +import decimal +import struct +from typing import Any, Sequence, Tuple, Type, Union + +_PACK_64 = struct.Struct(" decimal.Context: + """Returns an instance of :class:`decimal.Context` appropriate + for working with IEEE-754 128-bit decimal floating point values. + """ + opts = _CTX_OPTIONS.copy() + opts["traps"] = [] + return decimal.Context(**opts) # type: ignore + + +def _decimal_to_128(value: _VALUE_OPTIONS) -> Tuple[int, int]: + """Converts a decimal.Decimal to BID (high bits, low bits). + + :Parameters: + - `value`: An instance of decimal.Decimal + """ + with decimal.localcontext(_DEC128_CTX) as ctx: + value = ctx.create_decimal(value) + + if value.is_infinite(): + return _NINF if value.is_signed() else _PINF + + sign, digits, exponent = value.as_tuple() + + if value.is_nan(): + if digits: + raise ValueError("NaN with debug payload is not supported") + if value.is_snan(): + return _NSNAN if value.is_signed() else _PSNAN + return _NNAN if value.is_signed() else _PNAN + + significand = int("".join([str(digit) for digit in digits])) + bit_length = significand.bit_length() + + high = 0 + low = 0 + for i in range(min(64, bit_length)): + if significand & (1 << i): + low |= 1 << i + + for i in range(64, bit_length): + if significand & (1 << i): + high |= 1 << (i - 64) + + biased_exponent = exponent + _EXPONENT_BIAS + + if high >> 49 == 1: + high = high & 0x7FFFFFFFFFFF + high |= _EXPONENT_MASK + high |= (biased_exponent & 0x3FFF) << 47 + else: + high |= biased_exponent << 49 + + if sign: + high |= _SIGN + + return high, low + + +class Decimal128(object): + """BSON Decimal128 type:: + + >>> Decimal128(Decimal("0.0005")) + Decimal128('0.0005') + >>> Decimal128("0.0005") + Decimal128('0.0005') + >>> Decimal128((3474527112516337664, 5)) + Decimal128('0.0005') + + :Parameters: + - `value`: An instance of :class:`decimal.Decimal`, string, or tuple of + (high bits, low bits) from Binary Integer Decimal (BID) format. + + .. note:: :class:`~Decimal128` uses an instance of :class:`decimal.Context` + configured for IEEE-754 Decimal128 when validating parameters. + Signals like :class:`decimal.InvalidOperation`, :class:`decimal.Inexact`, + and :class:`decimal.Overflow` are trapped and raised as exceptions:: + + >>> Decimal128(".13.1") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.InvalidOperation: [] + >>> + >>> Decimal128("1E-6177") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Inexact: [] + >>> + >>> Decimal128("1E6145") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Overflow: [, ] + + To ensure the result of a calculation can always be stored as BSON + Decimal128 use the context returned by + :func:`create_decimal128_context`:: + + >>> import decimal + >>> decimal128_ctx = create_decimal128_context() + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal(".13.3")) + ... + Decimal128('NaN') + >>> + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal("1E-6177")) + ... + Decimal128('0E-6176') + >>> + >>> with decimal.localcontext(DECIMAL128_CTX) as ctx: + ... Decimal128(ctx.create_decimal("1E6145")) + ... + Decimal128('Infinity') + + To match the behavior of MongoDB's Decimal128 implementation + str(Decimal(value)) may not match str(Decimal128(value)) for NaN values:: + + >>> Decimal128(Decimal('NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('sNaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-sNaN')) + Decimal128('NaN') + + However, :meth:`~Decimal128.to_decimal` will return the exact value:: + + >>> Decimal128(Decimal('NaN')).to_decimal() + Decimal('NaN') + >>> Decimal128(Decimal('-NaN')).to_decimal() + Decimal('-NaN') + >>> Decimal128(Decimal('sNaN')).to_decimal() + Decimal('sNaN') + >>> Decimal128(Decimal('-sNaN')).to_decimal() + Decimal('-sNaN') + + Two instances of :class:`Decimal128` compare equal if their Binary + Integer Decimal encodings are equal:: + + >>> Decimal128('NaN') == Decimal128('NaN') + True + >>> Decimal128('NaN').bid == Decimal128('NaN').bid + True + + This differs from :class:`decimal.Decimal` comparisons for NaN:: + + >>> Decimal('NaN') == Decimal('NaN') + False + """ + + __slots__ = ("__high", "__low") + + _type_marker = 19 + + def __init__(self, value: _VALUE_OPTIONS) -> None: + if isinstance(value, (str, decimal.Decimal)): + self.__high, self.__low = _decimal_to_128(value) + elif isinstance(value, (list, tuple)): + if len(value) != 2: + raise ValueError( + "Invalid size for creation of Decimal128 " + "from list or tuple. Must have exactly 2 " + "elements." + ) + self.__high, self.__low = value # type: ignore + else: + raise TypeError("Cannot convert %r to Decimal128" % (value,)) + + def to_decimal(self) -> decimal.Decimal: + """Returns an instance of :class:`decimal.Decimal` for this + :class:`Decimal128`. + """ + high = self.__high + low = self.__low + sign = 1 if (high & _SIGN) else 0 + + if (high & _SNAN) == _SNAN: + return decimal.Decimal((sign, (), "N")) # type: ignore + elif (high & _NAN) == _NAN: + return decimal.Decimal((sign, (), "n")) # type: ignore + elif (high & _INF) == _INF: + return decimal.Decimal((sign, (), "F")) # type: ignore + + if (high & _EXPONENT_MASK) == _EXPONENT_MASK: + exponent = ((high & 0x1FFFE00000000000) >> 47) - _EXPONENT_BIAS + return decimal.Decimal((sign, (0,), exponent)) + else: + exponent = ((high & 0x7FFF800000000000) >> 49) - _EXPONENT_BIAS + + arr = bytearray(15) + mask = 0x00000000000000FF + for i in range(14, 6, -1): + arr[i] = (low & mask) >> ((14 - i) << 3) + mask = mask << 8 + + mask = 0x00000000000000FF + for i in range(6, 0, -1): + arr[i] = (high & mask) >> ((6 - i) << 3) + mask = mask << 8 + + mask = 0x0001000000000000 + arr[0] = (high & mask) >> 48 + + # cdecimal only accepts a tuple for digits. + digits = tuple(int(digit) for digit in str(int.from_bytes(arr, "big"))) + + with decimal.localcontext(_DEC128_CTX) as ctx: + return ctx.create_decimal((sign, digits, exponent)) + + @classmethod + def from_bid(cls: Type["Decimal128"], value: bytes) -> "Decimal128": + """Create an instance of :class:`Decimal128` from Binary Integer + Decimal string. + + :Parameters: + - `value`: 16 byte string (128-bit IEEE 754-2008 decimal floating + point in Binary Integer Decimal (BID) format). + """ + if not isinstance(value, bytes): + raise TypeError("value must be an instance of bytes") + if len(value) != 16: + raise ValueError("value must be exactly 16 bytes") + return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) # type: ignore + + @property + def bid(self) -> bytes: + """The Binary Integer Decimal (BID) encoding of this instance.""" + return _PACK_64(self.__low) + _PACK_64(self.__high) + + def __str__(self) -> str: + dec = self.to_decimal() + if dec.is_nan(): + # Required by the drivers spec to match MongoDB behavior. + return "NaN" + return str(dec) + + def __repr__(self): + return "Decimal128('%s')" % (str(self),) + + def __setstate__(self, value: Tuple[int, int]) -> None: + self.__high, self.__low = value + + def __getstate__(self) -> Tuple[int, int]: + return self.__high, self.__low + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Decimal128): + return self.bid == other.bid + return NotImplemented + + def __ne__(self, other: Any) -> bool: + return not self == other diff --git a/src/xtquant/xtbson/bson37/errors.py b/src/xtquant/xtbson/bson37/errors.py new file mode 100644 index 0000000..7333b27 --- /dev/null +++ b/src/xtquant/xtbson/bson37/errors.py @@ -0,0 +1,35 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Exceptions raised by the BSON package.""" + + +class BSONError(Exception): + """Base class for all BSON exceptions.""" + + +class InvalidBSON(BSONError): + """Raised when trying to create a BSON object from invalid data.""" + + +class InvalidStringData(BSONError): + """Raised when trying to encode a string containing non-UTF8 data.""" + + +class InvalidDocument(BSONError): + """Raised when trying to create a BSON object from an invalid document.""" + + +class InvalidId(BSONError): + """Raised when trying to create an ObjectId from invalid data.""" diff --git a/src/xtquant/xtbson/bson37/int64.py b/src/xtquant/xtbson/bson37/int64.py new file mode 100644 index 0000000..ed4dfa5 --- /dev/null +++ b/src/xtquant/xtbson/bson37/int64.py @@ -0,0 +1,39 @@ +# Copyright 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A BSON wrapper for long (int in python3)""" + +from typing import Any + + +class Int64(int): + """Representation of the BSON int64 type. + + This is necessary because every integral number is an :class:`int` in + Python 3. Small integral numbers are encoded to BSON int32 by default, + but Int64 numbers will always be encoded to BSON int64. + + :Parameters: + - `value`: the numeric value to represent + """ + + __slots__ = () + + _type_marker = 18 + + def __getstate__(self) -> Any: + return {} + + def __setstate__(self, state: Any) -> None: + pass diff --git a/src/xtquant/xtbson/bson37/json_util.py b/src/xtquant/xtbson/bson37/json_util.py new file mode 100644 index 0000000..3d8d13e --- /dev/null +++ b/src/xtquant/xtbson/bson37/json_util.py @@ -0,0 +1,903 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for using Python's :mod:`json` module with BSON documents. + +This module provides two helper methods `dumps` and `loads` that wrap the +native :mod:`json` methods and provide explicit BSON conversion to and from +JSON. :class:`~bson.json_util.JSONOptions` provides a way to control how JSON +is emitted and parsed, with the default being the Relaxed Extended JSON format. +:mod:`~bson.json_util` can also generate Canonical or legacy `Extended JSON`_ +when :const:`CANONICAL_JSON_OPTIONS` or :const:`LEGACY_JSON_OPTIONS` is +provided, respectively. + +.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json.rst + +Example usage (deserialization): + +.. doctest:: + + >>> from .json_util import loads + >>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]') + [{'foo': [1, 2]}, {'bar': {'hello': 'world'}}, {'code': Code('function x() { return 1; }', {})}, {'bin': Binary(b'...', 128)}] + +Example usage with :const:`RELAXED_JSON_OPTIONS` (the default): + +.. doctest:: + + >>> from . import Binary, Code + >>> from .json_util import dumps + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }")}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}]) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`CANONICAL_JSON_OPTIONS`): + +.. doctest:: + + >>> from . import Binary, Code + >>> from .json_util import dumps, CANONICAL_JSON_OPTIONS + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }")}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}], + ... json_options=CANONICAL_JSON_OPTIONS) + '[{"foo": [{"$numberInt": "1"}, {"$numberInt": "2"}]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`LEGACY_JSON_OPTIONS`): + +.. doctest:: + + >>> from . import Binary, Code + >>> from .json_util import dumps, LEGACY_JSON_OPTIONS + >>> dumps([{'foo': [1, 2]}, + ... {'bar': {'hello': 'world'}}, + ... {'code': Code("function x() { return 1; }", {})}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}], + ... json_options=LEGACY_JSON_OPTIONS) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]' + +Alternatively, you can manually pass the `default` to :func:`json.dumps`. +It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code` +instances (as they are extended strings you can't provide custom defaults), +but it will be faster as there is less recursion. + +.. note:: + If your application does not need the flexibility offered by + :class:`JSONOptions` and spends a large amount of time in the `json_util` + module, look to + `python-bsonjs `_ for a nice + performance improvement. `python-bsonjs` is a fast BSON to MongoDB + Extended JSON converter for Python built on top of + `libbson `_. `python-bsonjs` works best + with PyMongo when using :class:`~bson.raw_bson.RawBSONDocument`. +""" + +import base64 +import datetime +import json +import math +import re +import uuid +from typing import Any, Dict, Mapping, Optional, Sequence, Tuple, Type, Union, cast + +from .binary import ALL_UUID_SUBTYPES, UUID_SUBTYPE, Binary, UuidRepresentation +from .code import Code +from .codec_options import CodecOptions, DatetimeConversion +from .datetime_ms import ( + EPOCH_AWARE, + DatetimeMS, + _datetime_to_millis, + _max_datetime_ms, + _millis_to_datetime, +) +from .dbref import DBRef +from .decimal128 import Decimal128 +from .int64 import Int64 +from .max_key import MaxKey +from .min_key import MinKey +from .objectid import ObjectId +from .regex import Regex +from .son import RE_TYPE, SON +from .timestamp import Timestamp +from .tz_util import utc + +_RE_OPT_TABLE = { + "i": re.I, + "l": re.L, + "m": re.M, + "s": re.S, + "u": re.U, + "x": re.X, +} + + +class DatetimeRepresentation: + LEGACY = 0 + """Legacy MongoDB Extended JSON datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": }`, where `dateAsMilliseconds` is + a 64-bit signed integer giving the number of milliseconds since the Unix + epoch UTC. This was the default encoding before PyMongo version 3.4. + + .. versionadded:: 3.4 + """ + + NUMBERLONG = 1 + """NumberLong datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": {"$numberLong": ""}}`, + where `dateAsMilliseconds` is the string representation of a 64-bit signed + integer giving the number of milliseconds since the Unix epoch UTC. + + .. versionadded:: 3.4 + """ + + ISO8601 = 2 + """ISO-8601 datetime representation. + + :class:`datetime.datetime` instances greater than or equal to the Unix + epoch UTC will be encoded to JSON in the format `{"$date": ""}`. + :class:`datetime.datetime` instances before the Unix epoch UTC will be + encoded as if the datetime representation is + :const:`~DatetimeRepresentation.NUMBERLONG`. + + .. versionadded:: 3.4 + """ + + +class JSONMode: + LEGACY = 0 + """Legacy Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces PyMongo's legacy + non-standard JSON output. Consider using + :const:`~bson.json_util.JSONMode.RELAXED` or + :const:`~bson.json_util.JSONMode.CANONICAL` instead. + + .. versionadded:: 3.5 + """ + + RELAXED = 1 + """Relaxed Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Relaxed Extended JSON, + a mostly JSON-like format. Consider using this for things like a web API, + where one is sending a document (or a projection of a document) that only + uses ordinary JSON type primitives. In particular, the ``int``, + :class:`~bson.int64.Int64`, and ``float`` numeric types are represented in + the native JSON number format. This output is also the most human readable + and is useful for debugging and documentation. + + .. seealso:: The specification for Relaxed `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + CANONICAL = 2 + """Canonical Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Canonical Extended + JSON, a type preserving format. Consider using this for things like + testing, where one has to precisely specify expected types in JSON. In + particular, the ``int``, :class:`~bson.int64.Int64`, and ``float`` numeric + types are encoded with type wrappers. + + .. seealso:: The specification for Canonical `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + +class JSONOptions(CodecOptions): + """Encapsulates JSON options for :func:`dumps` and :func:`loads`. + + :Parameters: + - `strict_number_long`: If ``True``, :class:`~bson.int64.Int64` objects + are encoded to MongoDB Extended JSON's *Strict mode* type + `NumberLong`, ie ``'{"$numberLong": "" }'``. Otherwise they + will be encoded as an `int`. Defaults to ``False``. + - `datetime_representation`: The representation to use when encoding + instances of :class:`datetime.datetime`. Defaults to + :const:`~DatetimeRepresentation.LEGACY`. + - `strict_uuid`: If ``True``, :class:`uuid.UUID` object are encoded to + MongoDB Extended JSON's *Strict mode* type `Binary`. Otherwise it + will be encoded as ``'{"$uuid": "" }'``. Defaults to ``False``. + - `json_mode`: The :class:`JSONMode` to use when encoding BSON types to + Extended JSON. Defaults to :const:`~JSONMode.LEGACY`. + - `document_class`: BSON documents returned by :func:`loads` will be + decoded to an instance of this class. Must be a subclass of + :class:`collections.MutableMapping`. Defaults to :class:`dict`. + - `uuid_representation`: The :class:`~bson.binary.UuidRepresentation` + to use when encoding and decoding instances of :class:`uuid.UUID`. + Defaults to :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + - `tz_aware`: If ``True``, MongoDB Extended JSON's *Strict mode* type + `Date` will be decoded to timezone aware instances of + :class:`datetime.datetime`. Otherwise they will be naive. Defaults + to ``False``. + - `tzinfo`: A :class:`datetime.tzinfo` subclass that specifies the + timezone from which :class:`~datetime.datetime` objects should be + decoded. Defaults to :const:`~bson.tz_util.utc`. + - `datetime_conversion`: Specifies how UTC datetimes should be decoded + within BSON. Valid options include 'datetime_ms' to return as a + DatetimeMS, 'datetime' to return as a datetime.datetime and + raising a ValueError for out-of-range values, 'datetime_auto' to + return DatetimeMS objects when the underlying datetime is + out-of-range and 'datetime_clamp' to clamp to the minimum and + maximum possible datetimes. Defaults to 'datetime'. See + :ref:`handling-out-of-range-datetimes` for details. + - `args`: arguments to :class:`~bson.codec_options.CodecOptions` + - `kwargs`: arguments to :class:`~bson.codec_options.CodecOptions` + + .. seealso:: The specification for Relaxed and Canonical `Extended JSON`_. + + .. versionchanged:: 4.0 + The default for `json_mode` was changed from :const:`JSONMode.LEGACY` + to :const:`JSONMode.RELAXED`. + The default for `uuid_representation` was changed from + :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to + :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + + .. versionchanged:: 3.5 + Accepts the optional parameter `json_mode`. + + .. versionchanged:: 4.0 + Changed default value of `tz_aware` to False. + """ + + json_mode: int + strict_number_long: bool + datetime_representation: int + strict_uuid: bool + + def __new__( + cls: Type["JSONOptions"], + strict_number_long: Optional[bool] = None, + datetime_representation: Optional[int] = None, + strict_uuid: Optional[bool] = None, + json_mode: int = JSONMode.RELAXED, + *args: Any, + **kwargs: Any + ) -> "JSONOptions": + kwargs["tz_aware"] = kwargs.get("tz_aware", False) + if kwargs["tz_aware"]: + kwargs["tzinfo"] = kwargs.get("tzinfo", utc) + if datetime_representation not in ( + DatetimeRepresentation.LEGACY, + DatetimeRepresentation.NUMBERLONG, + DatetimeRepresentation.ISO8601, + None, + ): + raise ValueError( + "JSONOptions.datetime_representation must be one of LEGACY, " + "NUMBERLONG, or ISO8601 from DatetimeRepresentation." + ) + self = cast(JSONOptions, super(JSONOptions, cls).__new__(cls, *args, **kwargs)) + if json_mode not in (JSONMode.LEGACY, JSONMode.RELAXED, JSONMode.CANONICAL): + raise ValueError( + "JSONOptions.json_mode must be one of LEGACY, RELAXED, " + "or CANONICAL from JSONMode." + ) + self.json_mode = json_mode + if self.json_mode == JSONMode.RELAXED: + if strict_number_long: + raise ValueError("Cannot specify strict_number_long=True with JSONMode.RELAXED") + if datetime_representation not in (None, DatetimeRepresentation.ISO8601): + raise ValueError( + "datetime_representation must be DatetimeRepresentation." + "ISO8601 or omitted with JSONMode.RELAXED" + ) + if strict_uuid not in (None, True): + raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.ISO8601 + self.strict_uuid = True + elif self.json_mode == JSONMode.CANONICAL: + if strict_number_long not in (None, True): + raise ValueError("Cannot specify strict_number_long=False with JSONMode.RELAXED") + if datetime_representation not in (None, DatetimeRepresentation.NUMBERLONG): + raise ValueError( + "datetime_representation must be DatetimeRepresentation." + "NUMBERLONG or omitted with JSONMode.RELAXED" + ) + if strict_uuid not in (None, True): + raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") + self.strict_number_long = True + self.datetime_representation = DatetimeRepresentation.NUMBERLONG + self.strict_uuid = True + else: # JSONMode.LEGACY + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.LEGACY + self.strict_uuid = False + if strict_number_long is not None: + self.strict_number_long = strict_number_long + if datetime_representation is not None: + self.datetime_representation = datetime_representation + if strict_uuid is not None: + self.strict_uuid = strict_uuid + return self + + def _arguments_repr(self) -> str: + return ( + "strict_number_long=%r, " + "datetime_representation=%r, " + "strict_uuid=%r, json_mode=%r, %s" + % ( + self.strict_number_long, + self.datetime_representation, + self.strict_uuid, + self.json_mode, + super(JSONOptions, self)._arguments_repr(), + ) + ) + + def _options_dict(self) -> Dict[Any, Any]: + # TODO: PYTHON-2442 use _asdict() instead + options_dict = super(JSONOptions, self)._options_dict() + options_dict.update( + { + "strict_number_long": self.strict_number_long, + "datetime_representation": self.datetime_representation, + "strict_uuid": self.strict_uuid, + "json_mode": self.json_mode, + } + ) + return options_dict + + def with_options(self, **kwargs: Any) -> "JSONOptions": + """ + Make a copy of this JSONOptions, overriding some options:: + + >>> from .json_util import CANONICAL_JSON_OPTIONS + >>> CANONICAL_JSON_OPTIONS.tz_aware + True + >>> json_options = CANONICAL_JSON_OPTIONS.with_options(tz_aware=False, tzinfo=None) + >>> json_options.tz_aware + False + + .. versionadded:: 3.12 + """ + opts = self._options_dict() + for opt in ("strict_number_long", "datetime_representation", "strict_uuid", "json_mode"): + opts[opt] = kwargs.get(opt, getattr(self, opt)) + opts.update(kwargs) + return JSONOptions(**opts) + + +LEGACY_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.LEGACY) +""":class:`JSONOptions` for encoding to PyMongo's legacy JSON format. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.LEGACY`. + +.. versionadded:: 3.5 +""" + +CANONICAL_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.CANONICAL) +""":class:`JSONOptions` for Canonical Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.CANONICAL`. + +.. versionadded:: 3.5 +""" + +RELAXED_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.RELAXED) +""":class:`JSONOptions` for Relaxed Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.RELAXED`. + +.. versionadded:: 3.5 +""" + +DEFAULT_JSON_OPTIONS: JSONOptions = RELAXED_JSON_OPTIONS +"""The default :class:`JSONOptions` for JSON encoding/decoding. + +The same as :const:`RELAXED_JSON_OPTIONS`. + +.. versionchanged:: 4.0 + Changed from :const:`LEGACY_JSON_OPTIONS` to + :const:`RELAXED_JSON_OPTIONS`. + +.. versionadded:: 3.4 +""" + + +def dumps(obj: Any, *args: Any, **kwargs: Any) -> str: + """Helper function that wraps :func:`json.dumps`. + + Recursive function that handles all BSON types including + :class:`~bson.binary.Binary` and :class:`~bson.code.Code`. + + :Parameters: + - `json_options`: A :class:`JSONOptions` instance used to modify the + encoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 4.0 + Now outputs MongoDB Relaxed Extended JSON by default (using + :const:`DEFAULT_JSON_OPTIONS`). + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + return json.dumps(_json_convert(obj, json_options), *args, **kwargs) + + +def loads(s: str, *args: Any, **kwargs: Any) -> Any: + """Helper function that wraps :func:`json.loads`. + + Automatically passes the object_hook for BSON type conversion. + + Raises ``TypeError``, ``ValueError``, ``KeyError``, or + :exc:`~bson.errors.InvalidId` on invalid MongoDB Extended JSON. + + :Parameters: + - `json_options`: A :class:`JSONOptions` instance used to modify the + decoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 4.0 + Now loads :class:`datetime.datetime` instances as naive by default. To + load timezone aware instances utilize the `json_options` parameter. + See :ref:`tz_aware_default_change` for an example. + + .. versionchanged:: 3.5 + Parses Relaxed and Canonical Extended JSON as well as PyMongo's legacy + format. Now raises ``TypeError`` or ``ValueError`` when parsing JSON + type wrappers with values of the wrong type or any extra keys. + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs, json_options) + return json.loads(s, *args, **kwargs) + + +def _json_convert(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: + """Recursive helper method that converts BSON types so they can be + converted into json. + """ + if hasattr(obj, "items"): + return SON(((k, _json_convert(v, json_options)) for k, v in obj.items())) + elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)): + return list((_json_convert(v, json_options) for v in obj)) + try: + return default(obj, json_options) + except TypeError: + return obj + + +def object_pairs_hook( + pairs: Sequence[Tuple[str, Any]], json_options: JSONOptions = DEFAULT_JSON_OPTIONS +) -> Any: + return object_hook(json_options.document_class(pairs), json_options) + + +def object_hook(dct: Mapping[str, Any], json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: + if "$oid" in dct: + return _parse_canonical_oid(dct) + if ( + isinstance(dct.get("$ref"), str) + and "$id" in dct + and isinstance(dct.get("$db"), (str, type(None))) + ): + return _parse_canonical_dbref(dct) + if "$date" in dct: + return _parse_canonical_datetime(dct, json_options) + if "$regex" in dct: + return _parse_legacy_regex(dct) + if "$minKey" in dct: + return _parse_canonical_minkey(dct) + if "$maxKey" in dct: + return _parse_canonical_maxkey(dct) + if "$binary" in dct: + if "$type" in dct: + return _parse_legacy_binary(dct, json_options) + else: + return _parse_canonical_binary(dct, json_options) + if "$code" in dct: + return _parse_canonical_code(dct) + if "$uuid" in dct: + return _parse_legacy_uuid(dct, json_options) + if "$undefined" in dct: + return None + if "$numberLong" in dct: + return _parse_canonical_int64(dct) + if "$timestamp" in dct: + tsp = dct["$timestamp"] + return Timestamp(tsp["t"], tsp["i"]) + if "$numberDecimal" in dct: + return _parse_canonical_decimal128(dct) + if "$dbPointer" in dct: + return _parse_canonical_dbpointer(dct) + if "$regularExpression" in dct: + return _parse_canonical_regex(dct) + if "$symbol" in dct: + return _parse_canonical_symbol(dct) + if "$numberInt" in dct: + return _parse_canonical_int32(dct) + if "$numberDouble" in dct: + return _parse_canonical_double(dct) + return dct + + +def _parse_legacy_regex(doc: Any) -> Any: + pattern = doc["$regex"] + # Check if this is the $regex query operator. + if not isinstance(pattern, (str, bytes)): + return doc + flags = 0 + # PyMongo always adds $options but some other tools may not. + for opt in doc.get("$options", ""): + flags |= _RE_OPT_TABLE.get(opt, 0) + return Regex(pattern, flags) + + +def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + """Decode a JSON legacy $uuid to Python UUID.""" + if len(doc) != 1: + raise TypeError("Bad $uuid, extra field(s): %s" % (doc,)) + if not isinstance(doc["$uuid"], str): + raise TypeError("$uuid must be a string: %s" % (doc,)) + if json_options.uuid_representation == UuidRepresentation.UNSPECIFIED: + return Binary.from_uuid(uuid.UUID(doc["$uuid"])) + else: + return uuid.UUID(doc["$uuid"]) + + +def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + # special handling for UUID + if subtype in ALL_UUID_SUBTYPES: + uuid_representation = json_options.uuid_representation + binary_value = Binary(data, subtype) + if uuid_representation == UuidRepresentation.UNSPECIFIED: + return binary_value + if subtype == UUID_SUBTYPE: + # Legacy behavior: use STANDARD with binary subtype 4. + uuid_representation = UuidRepresentation.STANDARD + elif uuid_representation == UuidRepresentation.STANDARD: + # subtype == OLD_UUID_SUBTYPE + # Legacy behavior: STANDARD is the same as PYTHON_LEGACY. + uuid_representation = UuidRepresentation.PYTHON_LEGACY + return binary_value.as_uuid(uuid_representation) + + if subtype == 0: + return cast(uuid.UUID, data) + return Binary(data, subtype) + + +def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + if isinstance(doc["$type"], int): + doc["$type"] = "%02x" % doc["$type"] + subtype = int(doc["$type"], 16) + if subtype >= 0xFFFFFF80: # Handle mongoexport values + subtype = int(doc["$type"][6:], 16) + data = base64.b64decode(doc["$binary"].encode()) + return _binary_or_uuid(data, subtype, json_options) + + +def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + binary = doc["$binary"] + b64 = binary["base64"] + subtype = binary["subType"] + if not isinstance(b64, str): + raise TypeError("$binary base64 must be a string: %s" % (doc,)) + if not isinstance(subtype, str) or len(subtype) > 2: + raise TypeError("$binary subType must be a string at most 2 characters: %s" % (doc,)) + if len(binary) != 2: + raise TypeError('$binary must include only "base64" and "subType" components: %s' % (doc,)) + + data = base64.b64decode(b64.encode()) + return _binary_or_uuid(data, int(subtype, 16), json_options) + + +def _parse_canonical_datetime( + doc: Any, json_options: JSONOptions +) -> Union[datetime.datetime, DatetimeMS]: + """Decode a JSON datetime to python datetime.datetime.""" + dtm = doc["$date"] + if len(doc) != 1: + raise TypeError("Bad $date, extra field(s): %s" % (doc,)) + # mongoexport 2.6 and newer + if isinstance(dtm, str): + # Parse offset + if dtm[-1] == "Z": + dt = dtm[:-1] + offset = "Z" + elif dtm[-6] in ("+", "-") and dtm[-3] == ":": + # (+|-)HH:MM + dt = dtm[:-6] + offset = dtm[-6:] + elif dtm[-5] in ("+", "-"): + # (+|-)HHMM + dt = dtm[:-5] + offset = dtm[-5:] + elif dtm[-3] in ("+", "-"): + # (+|-)HH + dt = dtm[:-3] + offset = dtm[-3:] + else: + dt = dtm + offset = "" + + # Parse the optional factional seconds portion. + dot_index = dt.rfind(".") + microsecond = 0 + if dot_index != -1: + microsecond = int(float(dt[dot_index:]) * 1000000) + dt = dt[:dot_index] + + aware = datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S").replace( + microsecond=microsecond, tzinfo=utc + ) + + if offset and offset != "Z": + if len(offset) == 6: + hours, minutes = offset[1:].split(":") + secs = int(hours) * 3600 + int(minutes) * 60 + elif len(offset) == 5: + secs = int(offset[1:3]) * 3600 + int(offset[3:]) * 60 + elif len(offset) == 3: + secs = int(offset[1:3]) * 3600 + if offset[0] == "-": + secs *= -1 + aware = aware - datetime.timedelta(seconds=secs) + + if json_options.tz_aware: + if json_options.tzinfo: + aware = aware.astimezone(json_options.tzinfo) + if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS: + return DatetimeMS(aware) + return aware + else: + aware_tzinfo_none = aware.replace(tzinfo=None) + if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS: + return DatetimeMS(aware_tzinfo_none) + return aware_tzinfo_none + return _millis_to_datetime(int(dtm), json_options) + + +def _parse_canonical_oid(doc: Any) -> ObjectId: + """Decode a JSON ObjectId to bson.objectid.ObjectId.""" + if len(doc) != 1: + raise TypeError("Bad $oid, extra field(s): %s" % (doc,)) + return ObjectId(doc["$oid"]) + + +def _parse_canonical_symbol(doc: Any) -> str: + """Decode a JSON symbol to Python string.""" + symbol = doc["$symbol"] + if len(doc) != 1: + raise TypeError("Bad $symbol, extra field(s): %s" % (doc,)) + return str(symbol) + + +def _parse_canonical_code(doc: Any) -> Code: + """Decode a JSON code to bson.code.Code.""" + for key in doc: + if key not in ("$code", "$scope"): + raise TypeError("Bad $code, extra field(s): %s" % (doc,)) + return Code(doc["$code"], scope=doc.get("$scope")) + + +def _parse_canonical_regex(doc: Any) -> Regex: + """Decode a JSON regex to bson.regex.Regex.""" + regex = doc["$regularExpression"] + if len(doc) != 1: + raise TypeError("Bad $regularExpression, extra field(s): %s" % (doc,)) + if len(regex) != 2: + raise TypeError( + 'Bad $regularExpression must include only "pattern"' + 'and "options" components: %s' % (doc,) + ) + opts = regex["options"] + if not isinstance(opts, str): + raise TypeError( + "Bad $regularExpression options, options must be string, was type %s" % (type(opts)) + ) + return Regex(regex["pattern"], opts) + + +def _parse_canonical_dbref(doc: Any) -> DBRef: + """Decode a JSON DBRef to bson.dbref.DBRef.""" + return DBRef(doc.pop("$ref"), doc.pop("$id"), database=doc.pop("$db", None), **doc) + + +def _parse_canonical_dbpointer(doc: Any) -> Any: + """Decode a JSON (deprecated) DBPointer to bson.dbref.DBRef.""" + dbref = doc["$dbPointer"] + if len(doc) != 1: + raise TypeError("Bad $dbPointer, extra field(s): %s" % (doc,)) + if isinstance(dbref, DBRef): + dbref_doc = dbref.as_doc() + # DBPointer must not contain $db in its value. + if dbref.database is not None: + raise TypeError("Bad $dbPointer, extra field $db: %s" % (dbref_doc,)) + if not isinstance(dbref.id, ObjectId): + raise TypeError("Bad $dbPointer, $id must be an ObjectId: %s" % (dbref_doc,)) + if len(dbref_doc) != 2: + raise TypeError("Bad $dbPointer, extra field(s) in DBRef: %s" % (dbref_doc,)) + return dbref + else: + raise TypeError("Bad $dbPointer, expected a DBRef: %s" % (doc,)) + + +def _parse_canonical_int32(doc: Any) -> int: + """Decode a JSON int32 to python int.""" + i_str = doc["$numberInt"] + if len(doc) != 1: + raise TypeError("Bad $numberInt, extra field(s): %s" % (doc,)) + if not isinstance(i_str, str): + raise TypeError("$numberInt must be string: %s" % (doc,)) + return int(i_str) + + +def _parse_canonical_int64(doc: Any) -> Int64: + """Decode a JSON int64 to bson.int64.Int64.""" + l_str = doc["$numberLong"] + if len(doc) != 1: + raise TypeError("Bad $numberLong, extra field(s): %s" % (doc,)) + return Int64(l_str) + + +def _parse_canonical_double(doc: Any) -> float: + """Decode a JSON double to python float.""" + d_str = doc["$numberDouble"] + if len(doc) != 1: + raise TypeError("Bad $numberDouble, extra field(s): %s" % (doc,)) + if not isinstance(d_str, str): + raise TypeError("$numberDouble must be string: %s" % (doc,)) + return float(d_str) + + +def _parse_canonical_decimal128(doc: Any) -> Decimal128: + """Decode a JSON decimal128 to bson.decimal128.Decimal128.""" + d_str = doc["$numberDecimal"] + if len(doc) != 1: + raise TypeError("Bad $numberDecimal, extra field(s): %s" % (doc,)) + if not isinstance(d_str, str): + raise TypeError("$numberDecimal must be string: %s" % (doc,)) + return Decimal128(d_str) + + +def _parse_canonical_minkey(doc: Any) -> MinKey: + """Decode a JSON MinKey to bson.min_key.MinKey.""" + if type(doc["$minKey"]) is not int or doc["$minKey"] != 1: + raise TypeError("$minKey value must be 1: %s" % (doc,)) + if len(doc) != 1: + raise TypeError("Bad $minKey, extra field(s): %s" % (doc,)) + return MinKey() + + +def _parse_canonical_maxkey(doc: Any) -> MaxKey: + """Decode a JSON MaxKey to bson.max_key.MaxKey.""" + if type(doc["$maxKey"]) is not int or doc["$maxKey"] != 1: + raise TypeError("$maxKey value must be 1: %s", (doc,)) + if len(doc) != 1: + raise TypeError("Bad $minKey, extra field(s): %s" % (doc,)) + return MaxKey() + + +def _encode_binary(data: bytes, subtype: int, json_options: JSONOptions) -> Any: + if json_options.json_mode == JSONMode.LEGACY: + return SON([("$binary", base64.b64encode(data).decode()), ("$type", "%02x" % subtype)]) + return { + "$binary": SON([("base64", base64.b64encode(data).decode()), ("subType", "%02x" % subtype)]) + } + + +def default(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: + # We preserve key order when rendering SON, DBRef, etc. as JSON by + # returning a SON for those types instead of a dict. + if isinstance(obj, ObjectId): + return {"$oid": str(obj)} + if isinstance(obj, DBRef): + return _json_convert(obj.as_doc(), json_options=json_options) + if isinstance(obj, datetime.datetime): + if json_options.datetime_representation == DatetimeRepresentation.ISO8601: + if not obj.tzinfo: + obj = obj.replace(tzinfo=utc) + assert obj.tzinfo is not None + if obj >= EPOCH_AWARE: + off = obj.tzinfo.utcoffset(obj) + if (off.days, off.seconds, off.microseconds) == (0, 0, 0): # type: ignore + tz_string = "Z" + else: + tz_string = obj.strftime("%z") + millis = int(obj.microsecond / 1000) + fracsecs = ".%03d" % (millis,) if millis else "" + return { + "$date": "%s%s%s" % (obj.strftime("%Y-%m-%dT%H:%M:%S"), fracsecs, tz_string) + } + + millis = _datetime_to_millis(obj) + if json_options.datetime_representation == DatetimeRepresentation.LEGACY: + return {"$date": millis} + return {"$date": {"$numberLong": str(millis)}} + if isinstance(obj, DatetimeMS): + if ( + json_options.datetime_representation == DatetimeRepresentation.ISO8601 + and 0 <= int(obj) <= _max_datetime_ms() + ): + return default(obj.as_datetime(), json_options) + elif json_options.datetime_representation == DatetimeRepresentation.LEGACY: + return {"$date": str(int(obj))} + return {"$date": {"$numberLong": str(int(obj))}} + if json_options.strict_number_long and isinstance(obj, Int64): + return {"$numberLong": str(obj)} + if isinstance(obj, (RE_TYPE, Regex)): + flags = "" + if obj.flags & re.IGNORECASE: + flags += "i" + if obj.flags & re.LOCALE: + flags += "l" + if obj.flags & re.MULTILINE: + flags += "m" + if obj.flags & re.DOTALL: + flags += "s" + if obj.flags & re.UNICODE: + flags += "u" + if obj.flags & re.VERBOSE: + flags += "x" + if isinstance(obj.pattern, str): + pattern = obj.pattern + else: + pattern = obj.pattern.decode("utf-8") + if json_options.json_mode == JSONMode.LEGACY: + return SON([("$regex", pattern), ("$options", flags)]) + return {"$regularExpression": SON([("pattern", pattern), ("options", flags)])} + if isinstance(obj, MinKey): + return {"$minKey": 1} + if isinstance(obj, MaxKey): + return {"$maxKey": 1} + if isinstance(obj, Timestamp): + return {"$timestamp": SON([("t", obj.time), ("i", obj.inc)])} + if isinstance(obj, Code): + if obj.scope is None: + return {"$code": str(obj)} + return SON([("$code", str(obj)), ("$scope", _json_convert(obj.scope, json_options))]) + if isinstance(obj, Binary): + return _encode_binary(obj, obj.subtype, json_options) + if isinstance(obj, bytes): + return _encode_binary(obj, 0, json_options) + if isinstance(obj, uuid.UUID): + if json_options.strict_uuid: + binval = Binary.from_uuid(obj, uuid_representation=json_options.uuid_representation) + return _encode_binary(binval, binval.subtype, json_options) + else: + return {"$uuid": obj.hex} + if isinstance(obj, Decimal128): + return {"$numberDecimal": str(obj)} + if isinstance(obj, bool): + return obj + if json_options.json_mode == JSONMode.CANONICAL and isinstance(obj, int): + if -(2**31) <= obj < 2**31: + return {"$numberInt": str(obj)} + return {"$numberLong": str(obj)} + if json_options.json_mode != JSONMode.LEGACY and isinstance(obj, float): + if math.isnan(obj): + return {"$numberDouble": "NaN"} + elif math.isinf(obj): + representation = "Infinity" if obj > 0 else "-Infinity" + return {"$numberDouble": representation} + elif json_options.json_mode == JSONMode.CANONICAL: + # repr() will return the shortest string guaranteed to produce the + # original value, when float() is called on it. + return {"$numberDouble": str(repr(obj))} + raise TypeError("%r is not JSON serializable" % obj) diff --git a/src/xtquant/xtbson/bson37/max_key.py b/src/xtquant/xtbson/bson37/max_key.py new file mode 100644 index 0000000..b4f38d0 --- /dev/null +++ b/src/xtquant/xtbson/bson37/max_key.py @@ -0,0 +1,55 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MaxKey type. +""" +from typing import Any + + +class MaxKey(object): + """MongoDB internal MaxKey type.""" + + __slots__ = () + + _type_marker = 127 + + def __getstate__(self) -> Any: + return {} + + def __setstate__(self, state: Any) -> None: + pass + + def __eq__(self, other: Any) -> bool: + return isinstance(other, MaxKey) + + def __hash__(self) -> int: + return hash(self._type_marker) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __le__(self, other: Any) -> bool: + return isinstance(other, MaxKey) + + def __lt__(self, dummy: Any) -> bool: + return False + + def __ge__(self, dummy: Any) -> bool: + return True + + def __gt__(self, other: Any) -> bool: + return not isinstance(other, MaxKey) + + def __repr__(self): + return "MaxKey()" diff --git a/src/xtquant/xtbson/bson37/min_key.py b/src/xtquant/xtbson/bson37/min_key.py new file mode 100644 index 0000000..babc655 --- /dev/null +++ b/src/xtquant/xtbson/bson37/min_key.py @@ -0,0 +1,55 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MinKey type. +""" +from typing import Any + + +class MinKey(object): + """MongoDB internal MinKey type.""" + + __slots__ = () + + _type_marker = 255 + + def __getstate__(self) -> Any: + return {} + + def __setstate__(self, state: Any) -> None: + pass + + def __eq__(self, other: Any) -> bool: + return isinstance(other, MinKey) + + def __hash__(self) -> int: + return hash(self._type_marker) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __le__(self, dummy: Any) -> bool: + return True + + def __lt__(self, other: Any) -> bool: + return not isinstance(other, MinKey) + + def __ge__(self, other: Any) -> bool: + return isinstance(other, MinKey) + + def __gt__(self, dummy: Any) -> bool: + return False + + def __repr__(self): + return "MinKey()" diff --git a/src/xtquant/xtbson/bson37/objectid.py b/src/xtquant/xtbson/bson37/objectid.py new file mode 100644 index 0000000..beda531 --- /dev/null +++ b/src/xtquant/xtbson/bson37/objectid.py @@ -0,0 +1,286 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with MongoDB ObjectIds. +""" + +import binascii +import calendar +import datetime +import os +import struct +import threading +import time +from random import SystemRandom +from typing import Any, NoReturn, Optional, Type, Union + +from .errors import InvalidId +from .tz_util import utc + +_MAX_COUNTER_VALUE = 0xFFFFFF + + +def _raise_invalid_id(oid: str) -> NoReturn: + raise InvalidId( + "%r is not a valid ObjectId, it must be a 12-byte input" + " or a 24-character hex string" % oid + ) + + +def _random_bytes() -> bytes: + """Get the 5-byte random field of an ObjectId.""" + return os.urandom(5) + + +class ObjectId(object): + """A MongoDB ObjectId.""" + + _pid = os.getpid() + + _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) + _inc_lock = threading.Lock() + + __random = _random_bytes() + + __slots__ = ("__id",) + + _type_marker = 7 + + def __init__(self, oid: Optional[Union[str, "ObjectId", bytes]] = None) -> None: + """Initialize a new ObjectId. + + An ObjectId is a 12-byte unique identifier consisting of: + + - a 4-byte value representing the seconds since the Unix epoch, + - a 5-byte random value, + - a 3-byte counter, starting with a random value. + + By default, ``ObjectId()`` creates a new unique identifier. The + optional parameter `oid` can be an :class:`ObjectId`, or any 12 + :class:`bytes`. + + For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId + specification but they are acceptable input:: + + >>> ObjectId(b'foo-bar-quux') + ObjectId('666f6f2d6261722d71757578') + + `oid` can also be a :class:`str` of 24 hex digits:: + + >>> ObjectId('0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + + Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor + 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. + + :Parameters: + - `oid` (optional): a valid ObjectId. + + .. seealso:: The MongoDB documentation on `ObjectIds `_. + + .. versionchanged:: 3.8 + :class:`~bson.objectid.ObjectId` now implements the `ObjectID + specification version 0.2 + `_. + """ + if oid is None: + self.__generate() + elif isinstance(oid, bytes) and len(oid) == 12: + self.__id = oid + else: + self.__validate(oid) + + @classmethod + def from_datetime(cls: Type["ObjectId"], generation_time: datetime.datetime) -> "ObjectId": + """Create a dummy ObjectId instance with a specific generation time. + + This method is useful for doing range queries on a field + containing :class:`ObjectId` instances. + + .. warning:: + It is not safe to insert a document containing an ObjectId + generated using this method. This method deliberately + eliminates the uniqueness guarantee that ObjectIds + generally provide. ObjectIds generated with this method + should be used exclusively in queries. + + `generation_time` will be converted to UTC. Naive datetime + instances will be treated as though they already contain UTC. + + An example using this helper to get documents where ``"_id"`` + was generated before January 1, 2010 would be: + + >>> gen_time = datetime.datetime(2010, 1, 1) + >>> dummy_id = ObjectId.from_datetime(gen_time) + >>> result = collection.find({"_id": {"$lt": dummy_id}}) + + :Parameters: + - `generation_time`: :class:`~datetime.datetime` to be used + as the generation time for the resulting ObjectId. + """ + offset = generation_time.utcoffset() + if offset is not None: + generation_time = generation_time - offset + timestamp = calendar.timegm(generation_time.timetuple()) + oid = struct.pack(">I", int(timestamp)) + b"\x00\x00\x00\x00\x00\x00\x00\x00" + return cls(oid) + + @classmethod + def is_valid(cls: Type["ObjectId"], oid: Any) -> bool: + """Checks if a `oid` string is valid or not. + + :Parameters: + - `oid`: the object id to validate + + .. versionadded:: 2.3 + """ + if not oid: + return False + + try: + ObjectId(oid) + return True + except (InvalidId, TypeError): + return False + + @classmethod + def _random(cls) -> bytes: + """Generate a 5-byte random number once per process.""" + pid = os.getpid() + if pid != cls._pid: + cls._pid = pid + cls.__random = _random_bytes() + return cls.__random + + def __generate(self) -> None: + """Generate a new value for this ObjectId.""" + + # 4 bytes current time + oid = struct.pack(">I", int(time.time())) + + # 5 bytes random + oid += ObjectId._random() + + # 3 bytes inc + with ObjectId._inc_lock: + oid += struct.pack(">I", ObjectId._inc)[1:4] + ObjectId._inc = (ObjectId._inc + 1) % (_MAX_COUNTER_VALUE + 1) + + self.__id = oid + + def __validate(self, oid: Any) -> None: + """Validate and use the given id for this ObjectId. + + Raises TypeError if id is not an instance of + (:class:`basestring` (:class:`str` or :class:`bytes` + in python 3), ObjectId) and InvalidId if it is not a + valid ObjectId. + + :Parameters: + - `oid`: a valid ObjectId + """ + if isinstance(oid, ObjectId): + self.__id = oid.binary + elif isinstance(oid, str): + if len(oid) == 24: + try: + self.__id = bytes.fromhex(oid) + except (TypeError, ValueError): + _raise_invalid_id(oid) + else: + _raise_invalid_id(oid) + else: + raise TypeError( + "id must be an instance of (bytes, str, ObjectId), not %s" % (type(oid),) + ) + + @property + def binary(self) -> bytes: + """12-byte binary representation of this ObjectId.""" + return self.__id + + @property + def generation_time(self) -> datetime.datetime: + """A :class:`datetime.datetime` instance representing the time of + generation for this :class:`ObjectId`. + + The :class:`datetime.datetime` is timezone aware, and + represents the generation time in UTC. It is precise to the + second. + """ + timestamp = struct.unpack(">I", self.__id[0:4])[0] + return datetime.datetime.fromtimestamp(timestamp, utc) + + def __getstate__(self) -> bytes: + """return value of object for pickling. + needed explicitly because __slots__() defined. + """ + return self.__id + + def __setstate__(self, value: Any) -> None: + """explicit state set from pickling""" + # Provide backwards compatability with OIDs + # pickled with pymongo-1.9 or older. + if isinstance(value, dict): + oid = value["_ObjectId__id"] + else: + oid = value + # ObjectIds pickled in python 2.x used `str` for __id. + # In python 3.x this has to be converted to `bytes` + # by encoding latin-1. + if isinstance(oid, str): + self.__id = oid.encode("latin-1") + else: + self.__id = oid + + def __str__(self) -> str: + return binascii.hexlify(self.__id).decode() + + def __repr__(self): + return "ObjectId('%s')" % (str(self),) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id == other.binary + return NotImplemented + + def __ne__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id != other.binary + return NotImplemented + + def __lt__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id < other.binary + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id <= other.binary + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id > other.binary + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id >= other.binary + return NotImplemented + + def __hash__(self) -> int: + """Get a hash value for this :class:`ObjectId`.""" + return hash(self.__id) diff --git a/src/xtquant/xtbson/bson37/py.typed b/src/xtquant/xtbson/bson37/py.typed new file mode 100644 index 0000000..0f40570 --- /dev/null +++ b/src/xtquant/xtbson/bson37/py.typed @@ -0,0 +1,2 @@ +# PEP-561 Support File. +# "Package maintainers who wish to support type checking of their code MUST add a marker file named py.typed to their package supporting typing". diff --git a/src/xtquant/xtbson/bson37/raw_bson.py b/src/xtquant/xtbson/bson37/raw_bson.py new file mode 100644 index 0000000..3739351 --- /dev/null +++ b/src/xtquant/xtbson/bson37/raw_bson.py @@ -0,0 +1,196 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing raw BSON documents. + +Inserting and Retrieving RawBSONDocuments +========================================= + +Example: Moving a document between different databases/collections + +.. doctest:: + + >>> import bson + >>> from pymongo import MongoClient + >>> from .raw_bson import RawBSONDocument + >>> client = MongoClient(document_class=RawBSONDocument) + >>> client.drop_database('db') + >>> client.drop_database('replica_db') + >>> db = client.db + >>> result = db.test.insert_many([{'_id': 1, 'a': 1}, + ... {'_id': 2, 'b': 1}, + ... {'_id': 3, 'c': 1}, + ... {'_id': 4, 'd': 1}]) + >>> replica_db = client.replica_db + >>> for doc in db.test.find(): + ... print(f"raw document: {doc.raw}") + ... print(f"decoded document: {bson.decode(doc.raw)}") + ... result = replica_db.test.insert_one(doc) + raw document: b'...' + decoded document: {'_id': 1, 'a': 1} + raw document: b'...' + decoded document: {'_id': 2, 'b': 1} + raw document: b'...' + decoded document: {'_id': 3, 'c': 1} + raw document: b'...' + decoded document: {'_id': 4, 'd': 1} + +For use cases like moving documents across different databases or writing binary +blobs to disk, using raw BSON documents provides better speed and avoids the +overhead of decoding or encoding BSON. +""" + +from typing import Any, ItemsView, Iterator, Mapping, Optional + +from . import _get_object_size, _raw_to_dict +from .codec_options import _RAW_BSON_DOCUMENT_MARKER +from .codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT +from .codec_options import CodecOptions +from .son import SON + + +def _inflate_bson( + bson_bytes: bytes, codec_options: CodecOptions, raw_array: bool = False +) -> Mapping[Any, Any]: + """Inflates the top level fields of a BSON document. + + :Parameters: + - `bson_bytes`: the BSON bytes that compose this document + - `codec_options`: An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. + """ + # Use SON to preserve ordering of elements. + return _raw_to_dict( + bson_bytes, 4, len(bson_bytes) - 1, codec_options, SON(), raw_array=raw_array + ) + + +class RawBSONDocument(Mapping[str, Any]): + """Representation for a MongoDB document that provides access to the raw + BSON bytes that compose it. + + Only when a field is accessed or modified within the document does + RawBSONDocument decode its bytes. + """ + + __slots__ = ("__raw", "__inflated_doc", "__codec_options") + _type_marker = _RAW_BSON_DOCUMENT_MARKER + + def __init__(self, bson_bytes: bytes, codec_options: Optional[CodecOptions] = None) -> None: + """Create a new :class:`RawBSONDocument` + + :class:`RawBSONDocument` is a representation of a BSON document that + provides access to the underlying raw BSON bytes. Only when a field is + accessed or modified within the document does RawBSONDocument decode + its bytes. + + :class:`RawBSONDocument` implements the ``Mapping`` abstract base + class from the standard library so it can be used like a read-only + ``dict``:: + + >>> from . import encode + >>> raw_doc = RawBSONDocument(encode({'_id': 'my_doc'})) + >>> raw_doc.raw + b'...' + >>> raw_doc['_id'] + 'my_doc' + + :Parameters: + - `bson_bytes`: the BSON bytes that compose this document + - `codec_options` (optional): An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. The default is + :attr:`DEFAULT_RAW_BSON_OPTIONS`. + + .. versionchanged:: 3.8 + :class:`RawBSONDocument` now validates that the ``bson_bytes`` + passed in represent a single bson document. + + .. versionchanged:: 3.5 + If a :class:`~bson.codec_options.CodecOptions` is passed in, its + `document_class` must be :class:`RawBSONDocument`. + """ + self.__raw = bson_bytes + self.__inflated_doc: Optional[Mapping[str, Any]] = None + # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, + # it refers to this class RawBSONDocument. + if codec_options is None: + codec_options = DEFAULT_RAW_BSON_OPTIONS + elif not issubclass(codec_options.document_class, RawBSONDocument): + raise TypeError( + "RawBSONDocument cannot use CodecOptions with document " + "class %s" % (codec_options.document_class,) + ) + self.__codec_options = codec_options + # Validate the bson object size. + _get_object_size(bson_bytes, 0, len(bson_bytes)) + + @property + def raw(self) -> bytes: + """The raw BSON bytes composing this document.""" + return self.__raw + + def items(self) -> ItemsView[str, Any]: + """Lazily decode and iterate elements in this document.""" + return self.__inflated.items() + + @property + def __inflated(self) -> Mapping[str, Any]: + if self.__inflated_doc is None: + # We already validated the object's size when this document was + # created, so no need to do that again. + # Use SON to preserve ordering of elements. + self.__inflated_doc = self._inflate_bson(self.__raw, self.__codec_options) + return self.__inflated_doc + + @staticmethod + def _inflate_bson(bson_bytes: bytes, codec_options: CodecOptions) -> Mapping[Any, Any]: + return _inflate_bson(bson_bytes, codec_options) + + def __getitem__(self, item: str) -> Any: + return self.__inflated[item] + + def __iter__(self) -> Iterator[str]: + return iter(self.__inflated) + + def __len__(self) -> int: + return len(self.__inflated) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, RawBSONDocument): + return self.__raw == other.raw + return NotImplemented + + def __repr__(self): + return "%s(%r, codec_options=%r)" % ( + self.__class__.__name__, + self.raw, + self.__codec_options, + ) + + +class _RawArrayBSONDocument(RawBSONDocument): + """A RawBSONDocument that only expands sub-documents and arrays when accessed.""" + + @staticmethod + def _inflate_bson(bson_bytes: bytes, codec_options: CodecOptions) -> Mapping[Any, Any]: + return _inflate_bson(bson_bytes, codec_options, raw_array=True) + + +DEFAULT_RAW_BSON_OPTIONS: CodecOptions = DEFAULT.with_options(document_class=RawBSONDocument) +_RAW_ARRAY_BSON_OPTIONS: CodecOptions = DEFAULT.with_options(document_class=_RawArrayBSONDocument) +"""The default :class:`~bson.codec_options.CodecOptions` for +:class:`RawBSONDocument`. +""" diff --git a/src/xtquant/xtbson/bson37/regex.py b/src/xtquant/xtbson/bson37/regex.py new file mode 100644 index 0000000..f0d2466 --- /dev/null +++ b/src/xtquant/xtbson/bson37/regex.py @@ -0,0 +1,135 @@ +# Copyright 2013-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB regular expressions. +""" + +import re +from typing import Any, Generic, Pattern, Type, TypeVar, Union + +from ._helpers import _getstate_slots, _setstate_slots +from .son import RE_TYPE + + +def str_flags_to_int(str_flags: str) -> int: + flags = 0 + if "i" in str_flags: + flags |= re.IGNORECASE + if "l" in str_flags: + flags |= re.LOCALE + if "m" in str_flags: + flags |= re.MULTILINE + if "s" in str_flags: + flags |= re.DOTALL + if "u" in str_flags: + flags |= re.UNICODE + if "x" in str_flags: + flags |= re.VERBOSE + + return flags + + +_T = TypeVar("_T", str, bytes) + + +class Regex(Generic[_T]): + """BSON regular expression data.""" + + __slots__ = ("pattern", "flags") + + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + + _type_marker = 11 + + @classmethod + def from_native(cls: Type["Regex"], regex: "Pattern[_T]") -> "Regex[_T]": + """Convert a Python regular expression into a ``Regex`` instance. + + Note that in Python 3, a regular expression compiled from a + :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable + to store this flag in a BSON regular expression, unset it first:: + + >>> pattern = re.compile('.*') + >>> regex = Regex.from_native(pattern) + >>> regex.flags ^= re.UNICODE + >>> db.collection.insert_one({'pattern': regex}) + + :Parameters: + - `regex`: A regular expression object from ``re.compile()``. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. + + .. _PCRE: http://www.pcre.org/ + """ + if not isinstance(regex, RE_TYPE): + raise TypeError("regex must be a compiled regular expression, not %s" % type(regex)) + + return Regex(regex.pattern, regex.flags) + + def __init__(self, pattern: _T, flags: Union[str, int] = 0) -> None: + """BSON regular expression data. + + This class is useful to store and retrieve regular expressions that are + incompatible with Python's regular expression dialect. + + :Parameters: + - `pattern`: string + - `flags`: (optional) an integer bitmask, or a string of flag + characters like "im" for IGNORECASE and MULTILINE + """ + if not isinstance(pattern, (str, bytes)): + raise TypeError("pattern must be a string, not %s" % type(pattern)) + self.pattern: _T = pattern + + if isinstance(flags, str): + self.flags = str_flags_to_int(flags) + elif isinstance(flags, int): + self.flags = flags + else: + raise TypeError("flags must be a string or int, not %s" % type(flags)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Regex): + return self.pattern == other.pattern and self.flags == other.flags + else: + return NotImplemented + + __hash__ = None # type: ignore + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __repr__(self): + return "Regex(%r, %r)" % (self.pattern, self.flags) + + def try_compile(self) -> "Pattern[_T]": + """Compile this :class:`Regex` as a Python regular expression. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. :meth:`try_compile()` may raise + :exc:`re.error`. + + .. _PCRE: http://www.pcre.org/ + """ + return re.compile(self.pattern, self.flags) diff --git a/src/xtquant/xtbson/bson37/son.py b/src/xtquant/xtbson/bson37/son.py new file mode 100644 index 0000000..e4238b4 --- /dev/null +++ b/src/xtquant/xtbson/bson37/son.py @@ -0,0 +1,208 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for creating and manipulating SON, the Serialized Ocument Notation. + +Regular dictionaries can be used instead of SON objects, but not when the order +of keys is important. A SON object can be used just like a normal Python +dictionary.""" + +import copy +import re +from collections.abc import Mapping as _Mapping +from typing import ( + Any, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Pattern, + Tuple, + Type, + TypeVar, + Union, +) + +# This sort of sucks, but seems to be as good as it gets... +# This is essentially the same as re._pattern_type +RE_TYPE: Type[Pattern[Any]] = type(re.compile("")) + +_Key = TypeVar("_Key") +_Value = TypeVar("_Value") +_T = TypeVar("_T") + + +class SON(Dict[_Key, _Value]): + """SON data. + + A subclass of dict that maintains ordering of keys and provides a + few extra niceties for dealing with SON. SON provides an API + similar to collections.OrderedDict. + """ + + __keys: List[Any] + + def __init__( + self, + data: Optional[Union[Mapping[_Key, _Value], Iterable[Tuple[_Key, _Value]]]] = None, + **kwargs: Any + ) -> None: + self.__keys = [] + dict.__init__(self) + self.update(data) + self.update(kwargs) + + def __new__(cls: Type["SON[_Key, _Value]"], *args: Any, **kwargs: Any) -> "SON[_Key, _Value]": + instance = super(SON, cls).__new__(cls, *args, **kwargs) + instance.__keys = [] + return instance + + def __repr__(self): + result = [] + for key in self.__keys: + result.append("(%r, %r)" % (key, self[key])) + return "SON([%s])" % ", ".join(result) + + def __setitem__(self, key: _Key, value: _Value) -> None: + if key not in self.__keys: + self.__keys.append(key) + dict.__setitem__(self, key, value) + + def __delitem__(self, key: _Key) -> None: + self.__keys.remove(key) + dict.__delitem__(self, key) + + def copy(self) -> "SON[_Key, _Value]": + other: SON[_Key, _Value] = SON() + other.update(self) + return other + + # TODO this is all from UserDict.DictMixin. it could probably be made more + # efficient. + # second level definitions support higher levels + def __iter__(self) -> Iterator[_Key]: + for k in self.__keys: + yield k + + def has_key(self, key: _Key) -> bool: + return key in self.__keys + + def iterkeys(self) -> Iterator[_Key]: + return self.__iter__() + + # fourth level uses definitions from lower levels + def itervalues(self) -> Iterator[_Value]: + for _, v in self.items(): + yield v + + def values(self) -> List[_Value]: # type: ignore[override] + return [v for _, v in self.items()] + + def clear(self) -> None: + self.__keys = [] + super(SON, self).clear() + + def setdefault(self, key: _Key, default: _Value) -> _Value: # type: ignore[override] + try: + return self[key] + except KeyError: + self[key] = default + return default + + def pop(self, key: _Key, *args: Union[_Value, _T]) -> Union[_Value, _T]: + if len(args) > 1: + raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) + try: + value = self[key] + except KeyError: + if args: + return args[0] + raise + del self[key] + return value + + def popitem(self) -> Tuple[_Key, _Value]: + try: + k, v = next(iter(self.items())) + except StopIteration: + raise KeyError("container is empty") + del self[k] + return (k, v) + + def update(self, other: Optional[Any] = None, **kwargs: _Value) -> None: # type: ignore[override] + # Make progressively weaker assumptions about "other" + if other is None: + pass + elif hasattr(other, "items"): + for k, v in other.items(): + self[k] = v + elif hasattr(other, "keys"): + for k in other.keys(): + self[k] = other[k] + else: + for k, v in other: + self[k] = v + if kwargs: + self.update(kwargs) + + def get(self, key: _Key, default: Optional[Union[_Value, _T]] = None) -> Union[_Value, _T, None]: # type: ignore[override] + try: + return self[key] + except KeyError: + return default + + def __eq__(self, other: Any) -> bool: + """Comparison to another SON is order-sensitive while comparison to a + regular dictionary is order-insensitive. + """ + if isinstance(other, SON): + return len(self) == len(other) and list(self.items()) == list(other.items()) + return self.to_dict() == other + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __len__(self) -> int: + return len(self.__keys) + + def to_dict(self) -> Dict[_Key, _Value]: + """Convert a SON document to a normal Python dictionary instance. + + This is trickier than just *dict(...)* because it needs to be + recursive. + """ + + def transform_value(value: Any) -> Any: + if isinstance(value, list): + return [transform_value(v) for v in value] + elif isinstance(value, _Mapping): + return dict([(k, transform_value(v)) for k, v in value.items()]) + else: + return value + + return transform_value(dict(self)) + + def __deepcopy__(self, memo: Dict[int, "SON[_Key, _Value]"]) -> "SON[_Key, _Value]": + out: SON[_Key, _Value] = SON() + val_id = id(self) + if val_id in memo: + return memo[val_id] + memo[val_id] = out + for k, v in self.items(): + if not isinstance(v, RE_TYPE): + v = copy.deepcopy(v, memo) + out[k] = v + return out diff --git a/src/xtquant/xtbson/bson37/timestamp.py b/src/xtquant/xtbson/bson37/timestamp.py new file mode 100644 index 0000000..3eb1534 --- /dev/null +++ b/src/xtquant/xtbson/bson37/timestamp.py @@ -0,0 +1,124 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB internal Timestamps. +""" + +import calendar +import datetime +from typing import Any, Union + +from ._helpers import _getstate_slots, _setstate_slots +from .tz_util import utc + +UPPERBOUND = 4294967296 + + +class Timestamp(object): + """MongoDB internal timestamps used in the opLog.""" + + __slots__ = ("__time", "__inc") + + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + + _type_marker = 17 + + def __init__(self, time: Union[datetime.datetime, int], inc: int) -> None: + """Create a new :class:`Timestamp`. + + This class is only for use with the MongoDB opLog. If you need + to store a regular timestamp, please use a + :class:`~datetime.datetime`. + + Raises :class:`TypeError` if `time` is not an instance of + :class: `int` or :class:`~datetime.datetime`, or `inc` is not + an instance of :class:`int`. Raises :class:`ValueError` if + `time` or `inc` is not in [0, 2**32). + + :Parameters: + - `time`: time in seconds since epoch UTC, or a naive UTC + :class:`~datetime.datetime`, or an aware + :class:`~datetime.datetime` + - `inc`: the incrementing counter + """ + if isinstance(time, datetime.datetime): + offset = time.utcoffset() + if offset is not None: + time = time - offset + time = int(calendar.timegm(time.timetuple())) + if not isinstance(time, int): + raise TypeError("time must be an instance of int") + if not isinstance(inc, int): + raise TypeError("inc must be an instance of int") + if not 0 <= time < UPPERBOUND: + raise ValueError("time must be contained in [0, 2**32)") + if not 0 <= inc < UPPERBOUND: + raise ValueError("inc must be contained in [0, 2**32)") + + self.__time = time + self.__inc = inc + + @property + def time(self) -> int: + """Get the time portion of this :class:`Timestamp`.""" + return self.__time + + @property + def inc(self) -> int: + """Get the inc portion of this :class:`Timestamp`.""" + return self.__inc + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return self.__time == other.time and self.__inc == other.inc + else: + return NotImplemented + + def __hash__(self) -> int: + return hash(self.time) ^ hash(self.inc) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __lt__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) < (other.time, other.inc) + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) <= (other.time, other.inc) + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) > (other.time, other.inc) + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) >= (other.time, other.inc) + return NotImplemented + + def __repr__(self): + return "Timestamp(%s, %s)" % (self.__time, self.__inc) + + def as_datetime(self) -> datetime.datetime: + """Return a :class:`~datetime.datetime` instance corresponding + to the time portion of this :class:`Timestamp`. + + The returned datetime's timezone is UTC. + """ + return datetime.datetime.fromtimestamp(self.__time, utc) diff --git a/src/xtquant/xtbson/bson37/tz_util.py b/src/xtquant/xtbson/bson37/tz_util.py new file mode 100644 index 0000000..8106c77 --- /dev/null +++ b/src/xtquant/xtbson/bson37/tz_util.py @@ -0,0 +1,52 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Timezone related utilities for BSON.""" + +from datetime import datetime, timedelta, tzinfo +from typing import Optional, Tuple, Union + +ZERO: timedelta = timedelta(0) + + +class FixedOffset(tzinfo): + """Fixed offset timezone, in minutes east from UTC. + + Implementation based from the Python `standard library documentation + `_. + Defining __getinitargs__ enables pickling / copying. + """ + + def __init__(self, offset: Union[float, timedelta], name: str) -> None: + if isinstance(offset, timedelta): + self.__offset = offset + else: + self.__offset = timedelta(minutes=offset) + self.__name = name + + def __getinitargs__(self) -> Tuple[timedelta, str]: + return self.__offset, self.__name + + def utcoffset(self, dt: Optional[datetime]) -> timedelta: + return self.__offset + + def tzname(self, dt: Optional[datetime]) -> str: + return self.__name + + def dst(self, dt: Optional[datetime]) -> timedelta: + return ZERO + + +utc: FixedOffset = FixedOffset(0, "UTC") +"""Fixed offset timezone representing UTC.""" diff --git a/src/xtquant/xtconn.py b/src/xtquant/xtconn.py new file mode 100644 index 0000000..f82b485 --- /dev/null +++ b/src/xtquant/xtconn.py @@ -0,0 +1,196 @@ +#coding:utf-8 + +from .xtdatacenter import try_create_client + +### config +localhost = '127.0.0.1' + +### function +status_callback = None + +def try_create_connection(addr): + ''' + addr: 'localhost:58610' + ''' + ip, port = addr.split(':') + if not ip: + ip = localhost + if not port: + raise Exception('invalid port') + + cl = try_create_client() + cl.set_config_addr(addr) + + global status_callback + if status_callback: + cl.registerCommonControlCallback("watchxtquantstatus", status_callback) + + ec, msg = cl.connect() + if ec < 0: + raise Exception((ec, msg)) + return cl + + +def create_connection(addr): + try: + return try_create_connection(addr) + except Exception as e: + return None + + +def scan_all_server_instance(): + ''' + 扫描当前环境下所有XTQuant服务实例 + + return: list + [ config1, config2,... ] + + config: dict + { + 'ip': '127.0.0.1', 'port': 58610, + 'is_running': False, + 'client_type': 'research', + 'data_dir': 'xtquant_server/datadir', + } + ''' + + import os, sys + import json + + result = [] + + try: + config_dir = os.path.abspath(os.path.join(os.environ['USERPROFILE'], '.xtquant')) + + for f in os.scandir(config_dir): + full_path = f.path + + f_xtdata_cfg = os.path.join(full_path, 'xtdata.cfg') + if not os.path.exists(f_xtdata_cfg): + continue + + try: + config = json.load(open(f_xtdata_cfg, 'r', encoding = 'utf-8')) + + ip = config.get('ip', None) + if not ip: + config['ip'] = localhost + + port = config.get('port', None) + if not port: + continue + + except Exception as e: + continue + + is_running = False + + f_running_status = os.path.join(full_path, 'running_status') + if os.path.exists(f_running_status): + try: + os.remove(f_running_status) + except PermissionError: + is_running = True + except Exception as e: + pass + + config['is_running'] = is_running + + result.append(config) + + except Exception as e: + pass + + return result + + +def get_internal_server_addr(): + ''' + 获取内部XTQuant服务地址 + + return: str + '127.0.0.1:58610' + ''' + try: + from .xtdatacenter import get_local_server_port + local_server_port = get_local_server_port() + if local_server_port: + return f'127.0.0.1:{local_server_port}' + except: + pass + return None + + +def scan_available_server_addr(): + ''' + 扫描当前环境下可用的XTQuant服务实例 + + return: list + [ '0.0.0.0:58610', '0.0.0.0:58611', ... ] + ''' + + import os, sys + import json + + result = [] + + internal_server_addr = get_internal_server_addr() + if internal_server_addr: + result.append(internal_server_addr) + + try: + result_scan = [] + + inst_list = scan_all_server_instance() + + for config in inst_list: + try: + if not config.get('is_running', False): + continue + + ip = config.get('ip', None) + port = config.get('port', None) + if not ip or not port: + continue + + addr = f'{ip}:{port}' + + root_dir = os.path.normpath(config.get('root_dir', '')) + if root_dir and os.path.normpath(sys.executable).find(root_dir) == 0: + result_scan.insert(0, addr) + else: + result_scan.append(addr) + + except Exception as e: + continue + + except Exception as e: + pass + + result += result_scan + + result = list(dict.fromkeys(result)) + + return result + + +def connect_any(addr_list, start_port, end_port): + ''' + addr_list: [ addr, ... ] + addr: 'localhost:58610' + ''' + for addr in addr_list: + try: + port = int(addr.split(':')[1]) + if start_port > port or port > end_port: + continue + + cl = create_connection(addr) + if cl: + return cl + except Exception as e: + continue + + return None + + diff --git a/src/xtquant/xtconstant.py b/src/xtquant/xtconstant.py new file mode 100644 index 0000000..009bf68 --- /dev/null +++ b/src/xtquant/xtconstant.py @@ -0,0 +1,1173 @@ +#coding=utf-8 + + +""" +常量定义模块 +""" + + +""" +账号类型 +""" +# 期货 +FUTURE_ACCOUNT = 1 +# 股票 +SECURITY_ACCOUNT = 2 +# 信用 +CREDIT_ACCOUNT = 3 +# 期货期权 +FUTURE_OPTION_ACCOUNT = 5 +# 股票期权 +STOCK_OPTION_ACCOUNT = 6 +# 沪港通 +HUGANGTONG_ACCOUNT = 7 +# 美股收益互换 +INCOME_SWAP_ACCOUNT = 8 +# 全国股转账号 +NEW3BOARD_ACCOUNT = 10 +# 深港通 +SHENGANGTONG_ACCOUNT = 11 +# 场外理财账户 +AT_OFFSITEBANKING = 13 +# 期货外盘 +AT_OUTTER_FUTURE = 1001 +# IB +AT_IB = 1002 +# 场外托管 +AT_NS_TRUSTBANK = 15001 +# 银行间账号 +AT_INTERBANK = 15002 +# 银行账号 +AT_BANK = 15003 +# 场外账号 +AT_OTC = 15005 + +ACCOUNT_TYPE_DICT = { + FUTURE_ACCOUNT : 'FUTURE', + SECURITY_ACCOUNT : 'STOCK', + CREDIT_ACCOUNT : 'CREDIT', + FUTURE_OPTION_ACCOUNT : 'FUTURE_OPTION', + STOCK_OPTION_ACCOUNT : 'STOCK_OPTION', + HUGANGTONG_ACCOUNT : 'HUGANGTONG', + SHENGANGTONG_ACCOUNT : 'SHENGANGTONG', + NEW3BOARD_ACCOUNT : 'NEW3BOARD', + INCOME_SWAP_ACCOUNT: 'INCOME_SWAP', + AT_OFFSITEBANKING : 'OFFSITEBANKING', + AT_OUTTER_FUTURE : 'OUTTER_FUTURE', + AT_IB : 'IB', + AT_NS_TRUSTBANK : 'NS_TRUSTBANK', + AT_INTERBANK : 'INTERBANK', + AT_BANK : 'BANK', + AT_OTC : 'OTC', +} + + +""" +委托类型 +""" +#/ *期货六键风格 * / +FUTURE_OPEN_LONG = 0 # 开多 +FUTURE_CLOSE_LONG_HISTORY = 1 # 平昨多 +FUTURE_CLOSE_LONG_TODAY = 2 # 平今多 +FUTURE_OPEN_SHORT = 3 # 开空 +FUTURE_CLOSE_SHORT_HISTORY = 4 # 平昨空 +FUTURE_CLOSE_SHORT_TODAY = 5 # 平今空 +# / *期货四键风格 * / +FUTURE_CLOSE_LONG_TODAY_FIRST = 6 # 平多,优先平今 +FUTURE_CLOSE_LONG_HISTORY_FIRST = 7 # 平多,优先平昨 +FUTURE_CLOSE_SHORT_TODAY_FIRST = 8 # 平空,优先平今 +FUTURE_CLOSE_SHORT_HISTORY_FIRST = 9 # 平空,优先平昨 + +# / *期货两键风格 * / +FUTURE_CLOSE_LONG_TODAY_HISTORY_THEN_OPEN_SHORT = 10 # 卖出,如有多仓,优先平仓,优先平今,如有余量,再开空 +FUTURE_CLOSE_LONG_HISTORY_TODAY_THEN_OPEN_SHORT = 11 # 卖出,如有多仓,优先平仓,优先平昨,如有余量,再开空 +FUTURE_CLOSE_SHORT_TODAY_HISTORY_THEN_OPEN_LONG = 12 # 买入,如有空仓,优先平仓,优先平今,如有余量,再开多 +FUTURE_CLOSE_SHORT_HISTORY_TODAY_THEN_OPEN_LONG = 13 # 买入,如有空仓,优先平仓,优先平昨,如有余量,再开多 +FUTURE_OPEN = 14 # 买入,不优先平仓 +FUTURE_CLOSE = 15 # 卖出,不优先平仓 + +# / *期货 - 跨商品套利 * / +FUTURE_ARBITRAGE_OPEN = 16 # 开仓 +FUTURE_ARBITRAGE_CLOSE_HISTORY_FIRST = 17 # 平, 优先平昨 +FUTURE_ARBITRAGE_CLOSE_TODAY_FIRST = 18 # 平, 优先平今 + +# / *期货展期 * / +FUTURE_RENEW_LONG_CLOSE_HISTORY_FIRST = 19 # 看多, 优先平昨 +FUTURE_RENEW_LONG_CLOSE_TODAY_FIRST = 20 # 看多,优先平今 +FUTURE_RENEW_SHORT_CLOSE_HISTORY_FIRST = 21 # 看空,优先平昨 +FUTURE_RENEW_SHORT_CLOSE_TODAY_FIRST = 22 # 看空,优先平今 + +# / *股票期权 * / +STOCK_OPTION_BUY_OPEN = 48 # 买入开仓,以下用于个股期权交易业务 +STOCK_OPTION_SELL_CLOSE = 49 # 卖出平仓 +STOCK_OPTION_SELL_OPEN = 50 # 卖出开仓 +STOCK_OPTION_BUY_CLOSE = 51 # 买入平仓 +STOCK_OPTION_COVERED_OPEN = 52 # 备兑开仓 +STOCK_OPTION_COVERED_CLOSE = 53 # 备兑平仓 +STOCK_OPTION_CALL_EXERCISE = 54 # 认购行权 +STOCK_OPTION_PUT_EXERCISE = 55 # 认沽行权 +STOCK_OPTION_SECU_LOCK = 56 # 证券锁定 +STOCK_OPTION_SECU_UNLOCK = 57 # 证券解锁 + +# /*期货期权*/ +OPTION_FUTURE_OPTION_EXERCISE = 100 # 期货期权行权 + +# /*组合期货*/ +COMPOSE_OPEN_LONG = 110 # 组合开多 +COMPOSE_OPEN_SHORT = 111 # 组合开空 +COMPOSE_CLOSE_LONG_TODAY_FIRST = 112 # 组合平多-优先平今 +COMPOSE_CLOSE_LONG_HISTORY_FIRST = 113 # 组合平多-优先平昨 +COMPOSE_CLOSE_SHORT_TODAY_FIRST = 114 # 组合平空-优先平今 +COMPOSE_CLOSE_SHORT_HISTORY_FIRST = 115 # 组合平空-优先平昨 +COMPOSE_ONEKEY_FUTURE_TODAY_FIRST = 116 # 一键期货-优先平今 +COMPOSE_ONEKEY_FUTURE_HISTORY_FIRST = 117 # 一键期货-优先平昨 +COMPOSE_FUTURE_ADJUST_TODAY_FIRST = 118 # 组合调仓-优先平今 +COMPOSE_FUTURE_ADJUST_HISTORY_FIRST = 119 # 组合调仓-优先平昨 + +# / *组合期权 * / +COMPOSE_OPTION_COMB_EXERCISE = 137 # 组合行权 +COMPOSE_OPTION_BUILD_COMB_STRATEGY = 138 # 构建组合策略 +COMPOSE_OPTION_RELEASE_COMB_STRATEGY = 139 # 解除组合策略 + +# /*期货套利*/ +FUTURE_HEDGE = 400 # 期货套利 + +# /*ETF申赎*/ +ETF_PURCHASE = 134 # 申购 +ETF_REDEMPTION = 135 # 赎回 + +STOCK_BUY = 23 +STOCK_SELL = 24 +CREDIT_BUY = 23 #担保品买入 +CREDIT_SELL = 24 #担保品卖出 +CREDIT_FIN_BUY = 27 #融资买入 +CREDIT_SLO_SELL = 28 #融券卖出 +CREDIT_BUY_SECU_REPAY = 29 #买券还券 +CREDIT_DIRECT_SECU_REPAY = 30 #直接还券 +CREDIT_SELL_SECU_REPAY = 31 #卖券还款 +CREDIT_DIRECT_CASH_REPAY = 32 #直接还款 +CREDIT_FIN_BUY_SPECIAL = 40 #专项融资买入 +CREDIT_SLO_SELL_SPECIAL = 41 #专项融券卖出 +CREDIT_BUY_SECU_REPAY_SPECIAL = 42 #专项买券还券 +CREDIT_DIRECT_SECU_REPAY_SPECIAL = 43 #专项直接还券 +CREDIT_SELL_SECU_REPAY_SPECIAL = 44 #专项卖券还款 +CREDIT_DIRECT_CASH_REPAY_SPECIAL = 45 #专项直接还款 + +ORDER_TYPE_SET = { + STOCK_BUY + , STOCK_SELL + , CREDIT_BUY + , CREDIT_SELL + , CREDIT_FIN_BUY + , CREDIT_SLO_SELL + , CREDIT_BUY_SECU_REPAY + , CREDIT_DIRECT_SECU_REPAY + , CREDIT_SELL_SECU_REPAY + , CREDIT_DIRECT_CASH_REPAY + , CREDIT_FIN_BUY_SPECIAL + , CREDIT_SLO_SELL_SPECIAL + , CREDIT_BUY_SECU_REPAY_SPECIAL + , CREDIT_DIRECT_SECU_REPAY_SPECIAL + , CREDIT_SELL_SECU_REPAY_SPECIAL + , CREDIT_DIRECT_CASH_REPAY_SPECIAL +} + +""" +报价类型 +""" +# 最新价 +LATEST_PRICE = 5 +# 指定价/限价 +FIX_PRICE = 11 +# 市价最优价[郑商所][期货] +MARKET_BEST = 18 +# 市价即成剩撤[大商所][期货] +MARKET_CANCEL = 19 +# 市价全额成交或撤[大商所][期货] +MARKET_CANCEL_ALL = 20 +# 市价最优一档即成剩撤[中金所][期货] +MARKET_CANCEL_1 = 21 +# 市价最优五档即成剩撤[中金所][期货] +MARKET_CANCEL_5 = 22 +# 市价最优一档即成剩转[中金所][期货] +MARKET_CONVERT_1 = 23 +# 市价最优五档即成剩转[中金所][期货] +MARKET_CONVERT_5 = 24 +# 最优五档即时成交剩余撤销[上交所][股票] +MARKET_SH_CONVERT_5_CANCEL = 42 +# 最优五档即时成交剩转限价[上交所][股票] +MARKET_SH_CONVERT_5_LIMIT = 43 +# 对手方最优价格委托[上交所[股票]][深交所[股票][期权]] +MARKET_PEER_PRICE_FIRST = 44 +# 本方最优价格委托[上交所[股票]][深交所[股票][期权]] +MARKET_MINE_PRICE_FIRST = 45 +# 即时成交剩余撤销委托[深交所][股票][期权] +MARKET_SZ_INSTBUSI_RESTCANCEL = 46 +# 最优五档即时成交剩余撤销[深交所][股票][期权] +MARKET_SZ_CONVERT_5_CANCEL = 47 +# 全额成交或撤销委托[深交所][股票][期权] +MARKET_SZ_FULL_OR_CANCEL = 48 + + +""" +市场类型 +""" +# 上海市场 +SH_MARKET = 0 +# 深圳市场 +SZ_MARKET = 1 +# 北交所 +MARKET_ENUM_BEIJING = 70 +# 上期所 +MARKET_ENUM_SHANGHAI_FUTURE = 3 +# 大商所 +MARKET_ENUM_DALIANG_FUTURE = 4 +# 郑商所 +MARKET_ENUM_ZHENGZHOU_FUTURE = 5 +# 中金所 +MARKET_ENUM_INDEX_FUTURE = 2 +# 能源中心 +MARKET_ENUM_INTL_ENERGY_FUTURE = 6 +# 广期所 +MARKET_ENUM_GUANGZHOU_FUTURE = 75 +# 上海期权 +MARKET_ENUM_SHANGHAI_STOCK_OPTION = 7 +# 深圳期权 +MARKET_ENUM_SHENZHEN_STOCK_OPTION = 67 + +""" +市场类型-字符串 +""" +# 上交所 +MARKET_SHANGHAI = 'SH' +# 深交所 +MARKET_SHENZHEN = 'SZ' +# 北交所 +MARKET_BEIJING = 'BJ' +# 上期所 +MARKET_SHANGHAI_FUTURE = 'SF' +# 大商所 +MARKET_DALIANG_FUTURE = 'DF' +# 郑商所 +MARKET_ZHENGZHOU_FUTURE = 'ZF' +# 中金所 +MARKET_INDEX_FUTURE = 'IF' +# 能源中心 +MARKET_INTL_ENERGY_FUTURE = 'INE' +# 广期所 +MARKET_GUANGZHOU_FUTURE = 'GF' +# 上海期权 +MARKET_SHANGHAI_STOCK_OPTION = 'SHO' +# 深圳期权 +MARKET_SHENZHEN_STOCK_OPTION = 'SZO' + +MARKET_STR_TO_ENUM_MAPPING = { + MARKET_SHANGHAI : SH_MARKET, + MARKET_SHENZHEN : SZ_MARKET, + MARKET_BEIJING : MARKET_ENUM_BEIJING, + MARKET_SHANGHAI_FUTURE : MARKET_ENUM_SHANGHAI_FUTURE, + MARKET_DALIANG_FUTURE : MARKET_ENUM_DALIANG_FUTURE, + MARKET_ZHENGZHOU_FUTURE : MARKET_ENUM_ZHENGZHOU_FUTURE, + MARKET_INDEX_FUTURE : MARKET_ENUM_INDEX_FUTURE, + MARKET_INTL_ENERGY_FUTURE: MARKET_ENUM_INTL_ENERGY_FUTURE, + MARKET_GUANGZHOU_FUTURE : MARKET_ENUM_GUANGZHOU_FUTURE, + MARKET_SHANGHAI_STOCK_OPTION : MARKET_ENUM_SHANGHAI_STOCK_OPTION, + MARKET_SHENZHEN_STOCK_OPTION : MARKET_ENUM_SHENZHEN_STOCK_OPTION, +} + + +""" +委托状态 +""" +# 未报 +ORDER_UNREPORTED = 48 +# 待报 +ORDER_WAIT_REPORTING = 49 +# 已报 +ORDER_REPORTED = 50 +# 已报待撤 +ORDER_REPORTED_CANCEL = 51 +# 部成待撤 +ORDER_PARTSUCC_CANCEL = 52 +# 部撤 +ORDER_PART_CANCEL = 53 +# 已撤 +ORDER_CANCELED = 54 +# 部成 +ORDER_PART_SUCC = 55 +# 已成 +ORDER_SUCCEEDED = 56 +# 废单 +ORDER_JUNK = 57 +# 未知 +ORDER_UNKNOWN = 255 + + +""" +账号状态 +""" +#无效 +ACCOUNT_STATUS_INVALID = -1 +#正常 +ACCOUNT_STATUS_OK = 0 +#连接中 +ACCOUNT_STATUS_WAITING_LOGIN = 1 +#登陆中 +ACCOUNT_STATUSING = 2 +#失败 +ACCOUNT_STATUS_FAIL = 3 +#初始化中 +ACCOUNT_STATUS_INITING = 4 +#数据刷新校正中 +ACCOUNT_STATUS_CORRECTING = 5 +#收盘后 +ACCOUNT_STATUS_CLOSED = 6 +#穿透副链接断开 +ACCOUNT_STATUS_ASSIS_FAIL = 7 +#系统停用(总线使用-密码错误超限) +ACCOUNT_STATUS_DISABLEBYSYS = 8 +#用户停用(总线使用) +ACCOUNT_STATUS_DISABLEBYUSER = 9 + +""" +指令交易类型 +""" +#无效值 +TDT_INVALID = 0x00000000 +#股票交易 +TDT_STOCK = 0x00000001 +#期货交易 +TDT_FUTURE = 0x00000002 +#组合交易 +TDT_COMPOSE = 0x00000004 +#信用交易 +TDT_CREDIT = 0x00000008 +#黄金T+D交易 +TDT_GOLD = 0x00000010 +#股票期权 +TDT_STK_OPTION = 0x00000020 +#外盘 +TDT_OUTTER = 0x00000040 +#沪港通 +TDT_HUGANGTONG = 0x00000080 +#全国股转 +TDT_NEW3BOARD = 0x00000100 +#场外业务 +TDT_NON_STANDARD = 0x00000200 +#期货期权 +TDT_FUTURE_OPTION = 0x00000400 +#电子盘交易 +TDT_ELECTRONIC = 0x00000800 +#ETF申赎 +TDT_ETF = 0x00001000 +#全业务 +TDT_PB_FULL = 0x00002000 +#深港通 +TDT_SHENGANGTONG = 0x00004000 +#银行间业务 +TDT_INTERBANK = 0x00008000 +#风控员界面 +TDT_RISK = 0x00010000 +#日内回转界面 +TDT_PF_REVOLVING = 0x00020000 +#数字货币 +TDT_DIGICOIN = 0x00040000 +#银行账号 +TDT_BANK = 0x00040001 +#投组管理 +TDT_PORTFOLIO = 0x00080000 +#固收业务 +TDT_FICC = 0x00100000 +#券商理财 +TDT_BROKER_FINANCING = 0x00200000 +#转融通 +TDT_LMT = 0x00400000 +#收益互换 +TDT_INCOME_SWAP = 0x00800000 +#快捷交易 +TDT_FAST_TRADE = 0x01000000 +#转托管 +TDT_TOC = 0x02000000 +#利率互换 +TDT_IRS = 0x04000000 +#债券投标 +TDT_BTD = 0x08000000 +#网下新股 +TDT_OFF_IPO = 0x10000000 +#融券通 +TDT_SECLENDING = 0x20000000 +#转账录入 +TDT_TRANSFER = 0x40000000 + +#超int上限 +#这里的数值是相关的业务全部求和的 例如TDT_NOT_COMPOSE = 上面全部相加 减去TDT_COMPOSE TDT_NORMALTRADE是上面所有的去掉TDT_NON_STANDARD以及TDT_INTERBANK +#不包含组合交易的其他交易类型 +TDT_NOT_COMPOSE = 0x20F0DFFB +#普通交易类型,不包含非标业务和银行间业务 +TDT_NORMALTRADE = 0x3FF07DFF + +#占位 +#所有交易界面 +TDT_TRADE = 0x7FF4FFFF +#所有界面 +TDT_ALL = 0x3FF3FFFF +#综合交易界面 +TDT_INTEGRATED = 0x000C5DFF + + +""" +指令状态 +""" +#风控检查中 +OCS_CHECKING = -1 +#审批中 +OCS_APPROVING = 0 +#已驳回 +OCS_REJECTED = 1 +#运行中 +OCS_RUNNING = 2 +#撤销中 +OCS_CANCELING_DEPRECATED = 3 +#已完成 +OCS_FINISHED = 4 +#已撤销 +OCS_STOPPED = 5 +#强制撤销 +OCS_FORCE_COMPLETED_DEPRECATED = 6 +#风控驳回 +OCS_CHECKFAILED = 7 +#撤销审批中 +OCS_CANCELING_APPROVING = 8 +#撤销驳回 +OCS_CANCELING_REJECTED = 9 +#预埋指令 +OCS_PRESETTING = 10 +#撤销指令至撤销人 +OCS_CANCEL_TO_USER = 11 +#驳回指令至上一级 +OCS_CANCEL_REJECT_TO_UPPER_LEVEL = 12 +#补单 +OCS_PATCH_RUNNNING = 13 +#暂停指令并暂停任务 +OCS_PAUSE_PAUSEORDER = 14 +#暂停指令并撤销任务 +OCS_PAUSE_CANCELORDER = 15 +#场外:转账确认 +OCS_NS_TRANSFERCONFIRM = 101 +#场外:已结算 +OCS_NS_SETTLEUP = 102 +#场外:撤销中 +OCS_NS_CANCELING = 103 +#场外:认购确认中 +OCS_OTC_SUBSCRIBE_CONFIRMING = 104 +#场外:TA确认中 +OCS_OTC_TA_CONFIRMING = 105 +#场外:基金成立中 +OCS_OTC_FUND_ESTABLISHING = 106 +#场外:交收确认中 +OCS_OTC_SETTLE_CONFIRMING = 107 +#场外:询价成交确认中 +OCS_OTC_QUERY_CONFIRMING = 108 +#场外:存款成交确认中 +OCS_OTC_DEPOSIT_CONFIRMING = 109 +#场外:支取成交确认中 +OCS_OTC_DRAW_CONFIRMING = 110 +#已完成 +OCS_INTERBANK_DEAL_FINISHED = 201 +#已结束 +OCS_INTERBANK_SETTLE_FINISHED = 202 +#置为完成 +OCS_INTERBANK_MANAUL_FINISHED = 203 +#锁定中 +OCS_INTERBANK_LOCK = 204 +#任务创建运行 +OCS_TASK_STATUS_RUNNING = 1001 +#任务被撤销了 +OCS_TASK_STATUS_CANCELED = 1002 +#任务完成 +OCS_TASK_STATUS_FINISHED = 1003 +#任务被暂停了 +OCS_TASK_STATUS_PAUSED = 1004 +#指令回溯 +OCS_INTERBANK_CMD_ROLLBACK = 1100 +#指令解锁 +OCS_INTERBANK_CMD_UNLOCK = 1101 +#目前只作留痕用 +OCS_CANCELING_ORDER = 1300 +#改价中 +OCS_CHANGE_PRICE_ING = 1400 +#改价成功 +OCS_CHANGE_PRICE_SUCCESS = 1401 +#改价失败 +OCS_CHANGE_PRICE_FAIL = 1402 +#改触价 +OCS_CHANGE_PRICE_TRIGGER_PRICE = 1403 +#算法指令改单中 +OCS_CHANGE_COMMAND = 1404 +#普通指令准备改单中 +OCS_INORDER_TO_CHANGE_COMMAND_NORMAL = 1405 +#普通指令改单中 +OCS_CHANGE_COMMAND_NORMAL = 1406 +#风控员确认中 +OCS_RISKCONTROLER_APPROVING = 1407 +#无效值 +OCS_CMD_INVALID = -2 + +""" +指令操作类型 +""" +#未知 +OPT_INVALID = -1 +#开多 +OPT_OPEN_LONG = 0 +#平昨多,平多 +OPT_CLOSE_LONG_HISTORY = 1 +#平今多 +OPT_CLOSE_LONG_TODAY = 2 +#开空 +OPT_OPEN_SHORT = 3 +#平昨空,平空 +OPT_CLOSE_SHORT_HISTORY = 4 +#平今空 +OPT_CLOSE_SHORT_TODAY = 5 +#平多优先平今 +OPT_CLOSE_LONG_TODAY_FIRST = 6 +#平多优先平昨 +OPT_CLOSE_LONG_HISTORY_FIRST = 7 +#平空优先平今 +OPT_CLOSE_SHORT_TODAY_FIRST = 8 +#平空优先平昨 +OPT_CLOSE_SHORT_HISTORY_FIRST = 9 +#卖出优先平今 +OPT_CLOSE_LONG_TODAY_HISTORY_THEN_OPEN_SHORT = 10 +#卖出优先平昨 +OPT_CLOSE_LONG_HISTORY_TODAY_THEN_OPEN_SHORT = 11 +#买入优先平今 +OPT_CLOSE_SHORT_TODAY_HISTORY_THEN_OPEN_LONG = 12 +#买入优先平昨 +OPT_CLOSE_SHORT_HISTORY_TODAY_THEN_OPEN_LONG = 13 +#平多 +OPT_CLOSE_LONG = 14 +#平空 +OPT_CLOSE_SHORT = 15 +#买入,开仓,买入 +OPT_OPEN = 16 +#卖出,平仓,卖出 +OPT_CLOSE = 17 +#买入 +OPT_BUY = 18 +#卖出 +OPT_SELL = 19 +#融资买入 +OPT_FIN_BUY = 20 +#融券卖出 +OPT_SLO_SELL = 21 +#买券还券 +OPT_BUY_SECU_REPAY = 22 +#直接还券 +OPT_DIRECT_SECU_REPAY = 23 +#卖券还款 +OPT_SELL_CASH_REPAY = 24 +#直接还款 +OPT_DIRECT_CASH_REPAY = 25 +#基金申购 +OPT_FUND_SUBSCRIBE = 26 +#基金赎回 +OPT_FUND_REDEMPTION = 27 +#基金合并 +OPT_FUND_MERGE = 28 +#基金分拆 +OPT_FUND_SPLIT = 29 +#质押入库 +OPT_PLEDGE_IN = 30 +#质押出库 +OPT_PLEDGE_OUT = 31 +#买入开仓 +OPT_OPTION_BUY_OPEN = 32 +#卖出平仓 +OPT_OPTION_SELL_CLOSE = 33 +#卖出开仓 +OPT_OPTION_SELL_OPEN = 34 +#买入平仓 +OPT_OPTION_BUY_CLOSE = 35 +#备兑开仓 +OPT_OPTION_COVERED_OPEN = 36 +#备兑平仓 +OPT_OPTION_COVERED_CLOSE = 37 +#认购行权 +OPT_OPTION_CALL_EXERCISE = 38 +#认沽行权 +OPT_OPTION_PUT_EXERCISE = 39 +#证券锁定 +OPT_OPTION_SECU_LOCK = 40 +#证券解锁 +OPT_OPTION_SECU_UNLOCK = 41 +#定价买入 +OPT_N3B_PRICE_BUY = 42 +#定价卖出 +OPT_N3B_PRICE_SELL = 43 +#成交确认买入 +OPT_N3B_CONFIRM_BUY = 44 +#成交确认卖出 +OPT_N3B_CONFIRM_SELL = 45 +#互报成交确认买入 +OPT_N3B_REPORT_CONFIRM_BUY = 46 +#互报成交确认卖出 +OPT_N3B_REPORT_CONFIRM_SELL = 47 +#限价买入 +OPT_N3B_LIMIT_PRICE_BUY = 48 +#限价卖出 +OPT_N3B_LIMIT_PRICE_SELL = 49 +#期货期权行权 +OPT_FUTURE_OPTION_EXERCISE = 50 +#可转债转股 +OPT_CONVERT_BONDS = 51 +#可转债回售 +OPT_SELL_BACK_BONDS = 52 +#股票配股 +OPT_STK_ALLOTMENT = 53 +#股票增发 +OPT_STK_INCREASE_SHARE = 54 +#担保品划入 +OPT_COLLATERAL_TRANSFER_IN = 55 +#担保品划出 +OPT_COLLATERAL_TRANSFER_OUT = 56 +#意向申报买入 +OPT_BLOCK_INTENTION_BUY = 57 +#意向申报卖出 +OPT_BLOCK_INTENTION_SELL = 58 +#定价申报买入 +OPT_BLOCK_PRICE_BUY = 59 +#定价申报卖出 +OPT_BLOCK_PRICE_SELL = 60 +#成交申报买入 +OPT_BLOCK_CONFIRM_BUY = 61 +#成交申报卖出 +OPT_BLOCK_CONFIRM_SELL = 62 +#盘后定价买入 +OPT_BLOCK_CLOSE_PRICE_BUY = 63 +#盘后定价卖出 +OPT_BLOCK_CLOSE_PRICE_SELL = 64 +#黄金交割买 +OPT_GOLD_PRICE_DELIVERY_BUY = 65 +#黄金交割卖 +OPT_GOLD_PRICE_DELIVERY_SELL = 66 +#黄金中立仓买 +OPT_GOLD_PRICE_MIDDLE_BUY = 67 +#黄金中立仓卖 +OPT_GOLD_PRICE_MIDDLE_SELL = 68 +#组合交易一键买卖 +OPT_COMPOSE_ONEKEY_BUYSELL = 69 +#组合交易港股通买入 +OPT_COMPOSE_GGT_BUY = 70 +#组合交易港股通卖出 +OPT_COMPOSE_GGT_SELL = 71 +#零股卖出 +OPT_ODD_SELL = 72 +#成份股买入 +OPT_ETF_STOCK_BUY = 73 +#成份股卖出 +OPT_ETF_STOCK_SELL = 74 +# +OPT_OTC_FUND_BEGIN = 200 +#认购 +OPT_OTC_FUND_SUBSCRIBE = OPT_OTC_FUND_BEGIN +#申购 +OPT_OTC_FUND_PURCHASE = 201 +#赎回 +OPT_OTC_FUND_REDEMPTION = 202 +#转换 +OPT_OTC_FUND_CONVERT = 203 +#分红方式变更 +OPT_OTC_FUND_BONUS_TYPE_UPDATE = 204 +#协议存款 +OPT_OTC_CONTRACTUAL_DEPOSIT = 205 +#非协议存款 +OPT_OTC_NON_CONTRACTUAL_DEPOSIT = 206 +#协议存款询价 +OPT_OTC_CONTRACTUAL_DEPOSIT_ASK = 207 +#非协议存款询价 +OPT_OTC_NON_CONTRACTUAL_DEPOSIT_ASK = 208 +#场外非协议活期存款 +OPT_OTC_NON_CONTRACTUAL_DEPOSIT_CUR = 209 +#存单支取 +OPT_OTC_DRAW_DEPOSIT = 210 +#网下询价 +OPT_OTC_STOCK_INQUIRY = 230 +#网下申购 +OPT_OTC_STOCK_PURCHASE = 231 +# 场外操作 +OPT_OPTION_NS_BEGIN = 1001 +#入金 +OPT_OPTION_NS_DEPOSIT = OPT_OPTION_NS_BEGIN +#出金 +OPT_OPTION_NS_WITHDRAW = 1002 +#互转 +OPT_OPTION_NS_INOUT = 1003 +#ETF申购 +OPT_ETF_PURCHASE = 1004 +#ETF赎回 +OPT_ETF_REDEMPTION = 1005 +#外盘买入 +OPT_OUTER_BUY = 1006 +#外盘卖出 +OPT_OUTER_SELL = 1007 +#外盘可平买仓 +OPT_OUTER_CAN_CLOSE_BUY = 1008 +#外盘可平卖仓 +OPT_OUTER_CAN_CLOSE_SELL = 1009 +#专项融券卖出 +OPT_SLO_SELL_SPECIAL = 1010 +#专项买券还券 +OPT_BUY_SECU_REPAY_SPECIAL = 1011 +#专项直接还券 +OPT_DIRECT_SECU_REPAY_SPECIAL = 1012 +#限价买入 +OPT_NEEQ_O3B_LIMIT_PRICE_BUY = 1013 +#限价卖出 +OPT_NEEQ_O3B_LIMIT_PRICE_SELL = 1014 +#现券买入 +OPT_IBANK_BOND_BUY = 1015 +#现券卖出 +OPT_IBANK_BOND_SELL = 1016 +#质押式融资回购 +OPT_IBANK_FUND_REPURCHASE = 1017 +#质押式融券回购 +OPT_IBANK_BOND_REPURCHASE = 1018 +#质押式融资购回 +OPT_IBANK_BOND_REPAY = 1019 +#质押式融券购回 +OPT_IBANK_FUND_RETRIEVE = 1020 +#融券息费 +OPT_INTEREST_FEE = 1021 +#专项融资买入 +OPT_FIN_BUY_SPECIAL = 1022 +#专项卖券还款 +OPT_SELL_CASH_REPAY_SPECIAL = 1023 +#专项直接还款 +OPT_DIRECT_CASH_REPAY_SPECIAL = 1024 +#货币基金申购 +OPT_FUND_PRICE_BUY = 1025 +#货币基金赎回 +OPT_FUND_PRICE_SELL = 1026 +#集合竞价买入 +OPT_N3B_CALL_AUCTION_BUY = 1027 +#集合竞价卖出 +OPT_N3B_CALL_AUCTION_SELL = 1028 +#盘后协议买入 +OPT_N3B_AFTER_HOURS_BUY = 1029 +#盘后协议卖出 +OPT_N3B_AFTER_HOURS_SELL = 1030 +#ETF套利 +OPT_ETF_HEDGE = 1031 +#报价回购买入 +OPT_QUOTATION_REPURCHASE_BUY = 1032 +#报价回购终止续做 +OPT_QUOTATION_REPURCHASE_STOP = 1033 +#报价回购提前购回 +OPT_QUOTATION_REPURCHASE_BEFORE = 1034 +#报价回购购回预约 +OPT_QUOTATION_REPURCHASE_RESERVATION = 1035 +#报价回购取消预约 +OPT_QUOTATION_REPURCHASE_CANCEL = 1036 +#成交申报配对买入 +OPT_BLOCK_CONFIRM_MATCH_BUY = 1037 +#成交申报配对卖出 +OPT_BLOCK_CONFIRM_MATCH_SELL = 1038 +#期货期权放弃行权 +OPT_FUTURE_OPTION_ABANDON = 1039 +#一键划转 +OPT_ONEKEY_TRANSFER = 1040 +#一键划入 +OPT_ONEKEY_TRANSFER_IN = 1041 +#一键划出 +OPT_ONEKEY_TRANSFER_OUT = 1042 +#盘后定价买入 +OPT_AFTER_FIX_BUY = 1043 +#盘后定价卖出 +OPT_AFTER_FIX_SELL = 1044 +#成交申报正回购 +OPT_AGREEMENT_REPURCHASE_TRANSACTION_DEC_FORWARD = 1045 +#成交申报逆回购 +OPT_AGREEMENT_REPURCHASE_TRANSACTION_DEC_REVERSE = 1046 +#到期确认 +OPT_AGREEMENT_REPURCHASE_EXPIRE_CONFIRM = 1047 +#提前购回正回购 +OPT_AGREEMENT_REPURCHASE_ADVANCE_REPURCHASE = 1048 +#提前购回逆回购 +OPT_AGREEMENT_REPURCHASE_ADVANCE_REVERSE = 1049 +#到期续做正回购 +OPT_AGREEMENT_REPURCHASE_EXPIRE_RENEW = 1050 +#到期续做逆回购 +OPT_AGREEMENT_REPURCHASE_EXPIRE_REVERSE = 1051 +#现券买入 +OPT_TRANSACTION_IN_CASH_BUY = 1052 +#现券卖出 +OPT_TRANSACTION_IN_CASH_SELL = 1053 +#买断式融资回购 +OPT_OUTRIGHT_REPO_FUND_REPURCHASE = 1054 +#买断式融券回购 +OPT_OUTRIGHT_REPO_BOND_REPURCHASE = 1055 +#买断式融资购回 +OPT_OUTRIGHT_REPO_BOND_REPAY = 1056 +#买断式融券购回 +OPT_OUTRIGHT_REPO_FUND_RETRIEVE = 1057 +#分销买入 +OPT_DISTRIBUTION_BUYING = 1058 +#固定利率换浮动利率 +OPT_FIXRATE_TO_FLOATINGRATE = 1059 +#浮动利率换固定利率 +OPT_FLOATINGRATE_TO_FIXRATE = 1060 +#银行间转出托管 +OPT_IBANK_TRANSFER_OUT = 1061 +#银行间转入托管 +OPT_IBANK_TRANSFER_IN = 1062 +#意向申报正回购买入 +OPT_AGREEMENT_REPURCHASE_INTENTION_BUY = 1063 +#意向申报逆回购卖出 +OPT_AGREEMENT_REPURCHASE_INTENTION_SELL = 1064 +#协议回购成交申报确认 +OPT_AGREEMENT_REPURCHASE_BIZ_APPLY_CONFIRM = 1065 +#协议回购成交申报拒绝 +OPT_AGREEMENT_REPURCHASE_BIZ_APPLY_REJECT = 1066 +#协议回购到期续做申报确认 +OPT_AGREEMENT_REPURCHASE_CONTINUE_CONFIRM = 1067 +#协议回购到期续做申报拒绝 +OPT_AGREEMENT_REPURCHASE_CONTINUE_REJECT = 1068 +#协议回购换券申报 +OPT_AGREEMENT_REPURCHASE_INTENTION_CHANGE_BONDS = 1069 +#协议回购换券申报确认 +OPT_AGREEMENT_REPURCHASE_INTENTION_CHANGE_BONDS_CONFIRM = 1070 +#协议回购换券申报拒绝 +OPT_AGREEMENT_REPURCHASE_INTENTION_CHANGE_BONDS_REJECT = 1071 +#协议回购正回购提前终止申报确认 +OPT_AGREEMENT_REPURCHASE_STOP_AHEAD_CONFIRM = 1072 +#协议回购正回购提前终止申报拒绝 +OPT_AGREEMENT_REPURCHASE_STOP_AHEAD_REJECT = 1073 +#协议回购正回购方解除质押申报 +OPT_AGREEMENT_REPURCHASE_RELEASE_PLEDGE = 1074 +#协议回购正回购解除质押申报确认 +OPT_AGREEMENT_REPURCHASE_RELEASE_PLEDGE_CONFIRM = 1075 +#协议回购正回购解除质押申报拒绝 +OPT_AGREEMENT_REPURCHASE_RELEASE_PLEDGE_REJECT = 1076 +#到期确认卖出 +OPT_AGREEMENT_REPURCHASE_EXPIRE_CONFIRM_SELL = 1077 +# 债券分销 +OPT_LOAN_DISTRIBUTION_BUY = 1078 +#优先股竞价买入 +OPT_PREFERENCE_SHARES_BIDDING_BUY = 1079 +#优先股竞价卖出 +OPT_PREFERENCE_SHARES_BIDDING_SELL = 1080 +# 债券转托管 +OPT_TOC_BOND = 1081 +# 基金转托管 +OPT_TOC_FUND = 1082 +#同业拆入 +OPT_IBANK_BORROW = 1083 +#同业拆出 +OPT_IBANK_LOAN = 1084 +#拆入还款 +OPT_IBANK_BORROW_REPAY = 1085 +#拆出还款 +OPT_IBANK_LOAN_REPAY = 1086 +#理财产品申购 +OPT_FINANCIAL_PRODUCT_BUY = 1087 +#理财产品赎回 +OPT_FINANCIAL_PRODUCT_SELL = 1088 +#组合行权 +OPT_OPTION_COMB_EXERCISE = 1089 +#构建组合策略 +OPT_OPTION_BUILD_COMB_STRATEGY = 1090 +#解除组合策略 +OPT_OPTION_RELEASE_COMB_STRATEGY = 1091 +#协议回购逆回购提前终止申报确认 +OPT_AGREEMENT_REPURCHASE_REVERSE_STOP_AHEAD_CONFIRM = 1092 +#协议回购逆回购提前终止申报拒绝 +OPT_AGREEMENT_REPURCHASE_REVERSE_STOP_AHEAD_REJECT = 1093 +#协议回购逆回购方解除质押申报 +OPT_AGREEMENT_REPURCHASE_REVERSE_RELEASE_PLEDGE = 1094 +#协议回购逆回购解除质押申报确认 +OPT_AGREEMENT_REPURCHASE_REVERSE_RELEASE_PLEDGE_CONFIRM = 1095 +#协议回购逆回购解除质押申报拒绝 +OPT_AGREEMENT_REPURCHASE_REVERSE_RELEASE_PLEDGE_REJECT = 1096 +#债券投标 +OPT_BOND_TENDER = 1097 +#理财产品认购 +OPT_FINANCIAL_PRODUCT_CALL = 1098 +#北交所买入 +OPT_NEEQ_O3B_CONTINUOUS_AUCTION_BUY = 1099 +#北交所卖出 +OPT_NEEQ_O3B_CONTINUOUS_AUCTION_SELL = 1100 +#询价申报 +OPT_NEEQ_O3B_ASK_PRICE = 1101 +#申购申报 +OPT_NEEQ_O3B_PRICE_CONFIRM = 1102 +#大宗交易买入 +OPT_NEEQ_O3B_BLOCKTRADING_BUY = 1103 +#大宗交易卖出 +OPT_NEEQ_O3B_BLOCKTRADING_SELL = 1104 +#转融通非约定出借申报 +OPT_LMT_LOAN_SET = 1105 +#转融通约定出借申报 +OPT_LMT_LOAN_CONVENTION = 1106 +#转融通出借展期 +OPT_LMT_LOAN_RENEWAL = 1107 +#转融通出借提前了结 +OPT_LMT_LOAN_SETTLE_EARLY = 1108 +#跨市场ETF场内申购 +OPT_CROSS_MARKET_IN_ETF_PURCHASE = 1109 +#跨市场ETF场内赎回 +OPT_CROSS_MARKET_IN_ETF_REDEMPTION = 1110 +#跨市场ETF场外申购 +OPT_CROSS_MARKET_OUT_ETF_PURCHASE = 1111 +#跨市场ETF场外申购 +OPT_CROSS_MARKET_OUT_ETF_REDEMPTION = 1112 +#券源预约 +OPT_CREDIT_APPOINTMENT = 1113 +#网下申购-公开发行询价 +OPT_OFF_IPO_PUB_PRICE = 1114 +#网下申购-公开发行申购 +OPT_OFF_IPO_PUB_PURCHASE = 1115 +#网下申购-非公开发行询价 +OPT_OFF_IPO_NON_PUB_PRICE = 1116 +#网下申购-非公开发行申购 +OPT_OFF_IPO_NON_PUB_PURCHASE = 1117 +#债券回售 +OPT_IBANK_PUT = 1118 +#债券借贷融入 +OPT_IBANK_BOND_BORROW = 1119 +#债券借贷融出 +OPT_IBANK_BOND_LEND = 1120 +#债券借贷融入购回 +OPT_IBANK_BOND_BORROW_REPAY = 1121 +#债券借贷融出购回 +OPT_IBANK_BOND_LEND_RETRIEVE = 1122 +#债券借贷-质押券置换 +OPT_IBANK_BOND_DISPLACE = 1123 +#融券通-预约融券融入 +OPT_LENDING_INTEGRATE_INTO = 1124 +#融券通-预约融券融出 +OPT_LENDING_MELT_OUT = 1125 +#点击成交申报买入 +OPT_FICC_MANUAL_DECLARE_BUY = 1126 +#点击成交申报卖出 +OPT_FICC_MANUAL_DECLARE_SELL = 1127 +#点击成交确认买入 +OPT_FICC_MANUAL_CONFIRM_BUY_CONFIRM = 1128 +#点击成交拒绝买入 +OPT_FICC_MANUAL_CONFIRM_BUY_REJECT = 1129 +#点击成交确认卖出 +OPT_FICC_MANUAL_CONFIRM_SELL_CONFIRM = 1130 +#点击成交拒绝卖出 +OPT_FICC_MANUAL_CONFIRM_SELL_REJECT = 1131 +#协商成交申报买入 +OPT_FICC_CONSULT_DECLARE_BUY = 1132 +#协商成交申报卖出 +OPT_FICC_CONSULT_DECLARE_SELL = 1133 +#协商成交确认买入 +OPT_FICC_CONSULT_CONFIRM_BUY_CONFIRM = 1134 +#协商成交拒绝买入 +OPT_FICC_CONSULT_CONFIRM_BUY_REJECT = 1135 +#协商成交确认卖出 +OPT_FICC_CONSULT_CONFIRM_SELL_CONFIRM = 1136 +#协商成交拒绝卖出 +OPT_FICC_CONSULT_CONFIRM_SELL_REJECT = 1137 +#询价成交申报买入 +OPT_FICC_ENQUIRY_DECLARE_BUY = 1138 +#询价成交申报卖出 +OPT_FICC_ENQUIRY_DECLARE_SELL = 1139 +#询价成交报价回复确认买入 +OPT_FICC_ENQUIRY_REPLAY_BUY_CONFIRM = 1140 +#询价成交报价回复拒绝买入 +OPT_FICC_ENQUIRY_REPLAY_BUY_REJECT = 1141 +#询价成交报价回复确认卖出 +OPT_FICC_ENQUIRY_REPLAY_SELL_CONFIRM = 1142 +#询价成交报价回复拒绝卖出 +OPT_FICC_ENQUIRY_REPLAY_SELL_REJECT = 1143 +#询价成交询价成交确认买入 +OPT_FICC_ENQUIRY_INQUIRY_BUY_CONFIRM = 1144 +#询价成交询价成交拒绝买入 +OPT_FICC_ENQUIRY_INQUIRY_BUY_REJECT = 1145 +#询价成交询价成交确认卖出 +OPT_FICC_ENQUIRY_INQUIRY_SELL_CONFIRM = 1146 +#询价成交询价成交拒绝卖出 +OPT_FICC_ENQUIRY_INQUIRY_SELL_REJECT = 1147 +#竞买成交竞买预约买入 +OPT_FICC_BINDDING_RESERVE_BUY = 1148 +#竞买成交竞买预约卖出 +OPT_FICC_BINDDING_RESERVE_SELL = 1149 +#竞买成交竞买申报买入 +OPT_FICC_BINDDING_DECLARE_BUY = 1150 +#竞买成交竞买申报卖出 +OPT_FICC_BINDDING_DECLARE_SELL = 1151 +#竞买成交应价申报买入 +OPT_FICC_BINDDING_PRICE_DECLARE_BUY = 1152 +#竞买成交应价申报卖出 +OPT_FICC_BINDDING_PRICE_DECLARE_SELL = 1153 +#买入优先平仓 +OPT_OPTION_BUY_CLOSE_THEN_OPEN = 1154 +#卖出优先平仓 +OPT_OPTION_SELL_CLOSE_THEN_OPEN = 1155 +#资金划入 +OPT_FUND_TRANSFER_IN = 1156 +#资金划出 +OPT_FUND_TRANSFER_OUT = 1157 +#基金认购 +OPT_STOCK_FUND_SUBSCRIBE = 1158 +#现货买入 +OPT_OTC_SPOT_GOODS_BUY = 1159 +#现货卖出 +OPT_OTC_SPOT_GOODS_SELL = 1160 +#即时还券 +OPT_TIMELY_SECU_REPAY = 1161 +#专项即时还券 +OPT_TIMELY_SECU_REPAY_SPECIAL = 1162 +#协商成交合并确认买入 +OPT_FICC_CONSULT_MERGE_BUY_CONFIRM = 1163 +#协商成交合并拒绝买入 +OPT_FICC_CONSULT_MERGE_BUY_REJECT = 1164 +#协商成交合并确认卖出 +OPT_FICC_CONSULT_MERGE_SELL_CONFIRM = 1165 +#协商成交合并拒绝卖出 +OPT_FICC_CONSULT_MERGE_SELL_REJECT = 1166 +#买入交易解除申报 +OPT_FICC_TRADECANCEL_DECLARE_BUY = 1167 +#卖出交易解除申报 +OPT_FICC_TRADECANCEL_DECLARE_SELL = 1168 +#买入交易解除接受 +OPT_FICC_TRADECANCEL_CONFIRM_BUY_CONFIRM = 1169 +#买入交易解除拒绝 +OPT_FICC_TRADECANCEL_CONFIRM_BUY_REJECT = 1170 +#卖出交易解除接受 +OPT_FICC_TRADECANCEL_CONFIRM_SELL_CONFIRM = 1171 +#卖出交易解除拒绝 +OPT_FICC_TRADECANCEL_CONFIRM_SELL_REJECT = 1172 + + +DIRECTION_FLAG_BUY = 48 #买入 +DIRECTION_FLAG_SELL = 49 #卖出 +def getDirectionByOpType(opt): + if opt in ( + OPT_BUY, OPT_OPEN_LONG, OPT_CLOSE_SHORT_TODAY_HISTORY_THEN_OPEN_LONG, OPT_CLOSE_SHORT_HISTORY_TODAY_THEN_OPEN_LONG, + OPT_CLOSE_SHORT_TODAY, OPT_CLOSE_SHORT_HISTORY, OPT_CLOSE_SHORT_TODAY_FIRST, OPT_CLOSE_SHORT_HISTORY_FIRST, + OPT_FIN_BUY, OPT_FIN_BUY_SPECIAL, OPT_BUY_SECU_REPAY, OPT_BUY_SECU_REPAY_SPECIAL, OPT_OPTION_BUY_CLOSE, + OPT_OPTION_BUY_OPEN, OPT_OPTION_COVERED_CLOSE, OPT_OPTION_CALL_EXERCISE, OPT_N3B_PRICE_BUY, OPT_N3B_CONFIRM_BUY, + OPT_N3B_REPORT_CONFIRM_BUY, OPT_N3B_LIMIT_PRICE_BUY, OPT_NEEQ_O3B_LIMIT_PRICE_BUY, OPT_FUND_SUBSCRIBE, + OPT_FUND_PRICE_BUY, OPT_ETF_PURCHASE, OPT_FUND_MERGE, OPT_OUTER_BUY, OPT_IBANK_BOND_BUY, OPT_DISTRIBUTION_BUYING, + OPT_IBANK_FUND_REPURCHASE, OPT_IBANK_BOND_REPAY, OPT_GOLD_PRICE_MIDDLE_BUY, OPT_GOLD_PRICE_DELIVERY_BUY, + OPT_BLOCK_INTENTION_BUY, OPT_BLOCK_PRICE_BUY, OPT_BLOCK_CONFIRM_BUY, OPT_BLOCK_CONFIRM_MATCH_BUY, + OPT_BLOCK_CLOSE_PRICE_BUY, OPT_COLLATERAL_TRANSFER_IN, OPT_N3B_CALL_AUCTION_BUY, OPT_N3B_AFTER_HOURS_BUY, + OPT_CLOSE_SHORT, OPT_PLEDGE_OUT, OPT_AFTER_FIX_BUY, OPT_QUOTATION_REPURCHASE_BUY, OPT_OPTION_SECU_LOCK, + OPT_NEEQ_O3B_CONTINUOUS_AUCTION_BUY, OPT_NEEQ_O3B_ASK_PRICE, OPT_NEEQ_O3B_PRICE_CONFIRM, + OPT_NEEQ_O3B_BLOCKTRADING_BUY, OPT_TRANSACTION_IN_CASH_BUY, OPT_OUTRIGHT_REPO_FUND_REPURCHASE, + OPT_OUTRIGHT_REPO_BOND_REPAY, OPT_AGREEMENT_REPURCHASE_TRANSACTION_DEC_FORWARD, + OPT_AGREEMENT_REPURCHASE_ADVANCE_REPURCHASE, OPT_AGREEMENT_REPURCHASE_EXPIRE_RENEW, + OPT_AGREEMENT_REPURCHASE_INTENTION_BUY, OPT_FINANCIAL_PRODUCT_BUY, OPT_FINANCIAL_PRODUCT_CALL, + OPT_OPTION_RELEASE_COMB_STRATEGY, OPT_OTC_NON_CONTRACTUAL_DEPOSIT, OPT_OTC_CONTRACTUAL_DEPOSIT, + OPT_OTC_FUND_SUBSCRIBE, OPT_OTC_FUND_PURCHASE, OPT_OTC_CONTRACTUAL_DEPOSIT_ASK, OPT_OTC_NON_CONTRACTUAL_DEPOSIT_ASK, + OPT_CONVERT_BONDS, OPT_OFF_IPO_PUB_PRICE, OPT_OFF_IPO_PUB_PURCHASE, OPT_OFF_IPO_NON_PUB_PRICE, + OPT_OFF_IPO_NON_PUB_PURCHASE, OPT_OPTION_BUY_CLOSE_THEN_OPEN, OPT_FICC_MANUAL_DECLARE_BUY, + OPT_FICC_MANUAL_CONFIRM_BUY_CONFIRM, OPT_FICC_MANUAL_CONFIRM_BUY_REJECT, OPT_FICC_CONSULT_DECLARE_BUY, + OPT_FICC_CONSULT_CONFIRM_BUY_CONFIRM, OPT_FICC_CONSULT_CONFIRM_BUY_REJECT, OPT_FICC_ENQUIRY_DECLARE_BUY, + OPT_FICC_ENQUIRY_REPLAY_BUY_CONFIRM, OPT_FICC_ENQUIRY_REPLAY_BUY_REJECT, OPT_FICC_ENQUIRY_INQUIRY_BUY_CONFIRM, + OPT_FICC_ENQUIRY_INQUIRY_BUY_REJECT, OPT_FICC_BINDDING_RESERVE_BUY, OPT_FICC_BINDDING_DECLARE_BUY, + OPT_FICC_BINDDING_PRICE_DECLARE_BUY, OPT_FUND_TRANSFER_IN): + return DIRECTION_FLAG_BUY + else: + return DIRECTION_FLAG_SELL + +'''执行顺序''' +#主动腿比例优先 +EESO_ActiveFirst = 0 +#同时报单 +EESO_ConcurrentlyOrder = 1 +#主动腿完全优先 +EESO_ActiveFirstFull = 2 + +'''比较标志''' +#无效 +EFHST_INVALID = -1 +#大于 +EFHST_GREATER = 0 +#大于等于 +EFHST_GREATER_EQUAL = 1 +#小于 +EFHST_LESS = 2 +#小于等于 +EFHST_LESS_EQUAL = 3 +#无 +EFHST_NONE_SYMBOL = 4 + +'''报价类型''' +PRTP_INVALID = -1 +#卖5 +PRTP_SALE5 = 0 +#卖4 +PRTP_SALE4 = 1 +#卖3 +PRTP_SALE3 = 2 +#卖2 +PRTP_SALE2 = 3 +#卖1 +PRTP_SALE1 = 4 +#最新价 +PRTP_LATEST = 5 +#买1 +PRTP_BUY1 = 6 +#买2 +PRTP_BUY2 = 7 +#买3 +PRTP_BUY3 = 8 +#买4 +PRTP_BUY4 = 9 +#买5 +PRTP_BUY5 = 10 +#指定价 +PRTP_FIX = 11 +#市价_涨跌停价 +PRTP_MARKET = 12 +#挂单价 +PRTP_HANG = 13 +#对手价 +PRTP_COMPETE = 14 + +""" +划拨方向 +""" +#/ *资金划拨* / +FUNDS_TRANSFER_NORMAL_TO_SPEED = 510 # 资金划拨-普通柜台到极速柜台 +FUNDS_TRANSFER_SPEED_TO_NORMAL = 511 # 资金划拨-极速柜台到普通柜台 +NODE_FUNDS_TRANSFER_SH_TO_SZ = 512 # 节点资金划拨-上海节点到深圳节点 +NODE_FUNDS_TRANSFER_SZ_TO_SH = 513 # 节点资金划拨-深圳节点到上海节点 +#/ *股份划拨* / +SECU_TRANSFER_NORMAL_TO_SPEED = 520 # 股份划拨-普通柜台划到极速柜台 +SECU_TRANSFER_SPEED_TO_NORMAL = 521 # 股份划拨-极速柜台划到普通柜台 + +""" +股份划拨类型 +""" +#股票账户客户划拨 +TRANS_TRANSFER_SHARE = 0 +#融资融券账户专项头寸划拨 +TRANS_TRANSFER_SPECIAL_POSITIONS = 1 +#融资融券账户客户划拨 +TRANS_TRANSFER_CREDIT_SHARE = 2 + +""" +多空方向,股票不需要 +""" +#多 +DIRECTION_FLAG_LONG = 48 +#空 +DIRECTION_FLAG_SHORT = 49 + +""" +交易操作,用此字段区分股票买卖,期货开、平仓,期权买卖等 +""" +OFFSET_FLAG_OPEN = 48 +OFFSET_FLAG_CLOSE = 49 +OFFSET_FLAG_FORCECLOSE = 50 +OFFSET_FLAG_CLOSETODAY = 51 +OFFSET_FLAG_ClOSEYESTERDAY = 52 +OFFSET_FLAG_FORCEOFF = 53 +OFFSET_FLAG_LOCALFORCECLOSE = 54 \ No newline at end of file diff --git a/src/xtquant/xtdata.ini b/src/xtquant/xtdata.ini new file mode 100644 index 0000000..c3846c7 --- /dev/null +++ b/src/xtquant/xtdata.ini @@ -0,0 +1,17 @@ +[app] +appName=IPythonApiClient +netThreadNum=8 +dispatcherThreadNum=8 +logPath=xtdata.log4cxx +logWatch=0 +reportSeconds=20 +appendDate=1 + +[client_xtdata] +tagTemplate=xtdata +address=127.0.0.1:58610 +timeoutSecond=0 +keepAliveCheckSecond=0 +reconnectSecond=3 +requestTimeoutSecond=150 +watchlog=1 diff --git a/src/xtquant/xtdata.log4cxx b/src/xtquant/xtdata.log4cxx new file mode 100644 index 0000000..1ccf735 --- /dev/null +++ b/src/xtquant/xtdata.log4cxx @@ -0,0 +1,18 @@ +log4j.logger.TTConsole=ERROR,ca +log4j.logger.TTStdFile=ERROR,ca +log4j.logger.TTDbgFile=ERROR,ca + +# 文件输出1 +#log4j.appender.fa1=org.apache.log4j.DailyRollingFileAppender +#log4j.appender.fa1.datePattern='.'yyyy-MM-dd +#log4j.appender.fa1.MaxFileSize=500MB +#log4j.appender.fa1.MaxBackupIndex=10 +#log4j.appender.fa1.File=log/xtquant.log +#log4j.appender.fa1.Append=true +#log4j.appender.fa1.layout=org.apache.log4j.PatternLayout +#log4j.appender.fa1.layout.ConversionPattern=%d [%p] [%t] %m%n + +# 控制台输出 +log4j.appender.ca=org.apache.log4j.ConsoleAppender +log4j.appender.ca.layout=org.apache.log4j.PatternLayout +log4j.appender.ca.layout.ConversionPattern=%d [%p] [%t] %m%n diff --git a/src/xtquant/xtdata.py b/src/xtquant/xtdata.py new file mode 100644 index 0000000..e53534b --- /dev/null +++ b/src/xtquant/xtdata.py @@ -0,0 +1,3352 @@ +#coding:utf-8 + +import os as _OS_ +import time as _TIME_ +import traceback as _TRACEBACK_ + +from . import xtbson as _BSON_ + + + +__all__ = [ + 'subscribe_quote' + , 'subscribe_whole_quote' + , 'unsubscribe_quote' + , 'run' + , 'get_market_data' + , 'get_local_data' + , 'get_full_tick' + , 'get_divid_factors' + , 'get_l2_quote' + , 'get_l2_order' + , 'get_l2_transaction' + , 'download_history_data' + , 'get_financial_data' + , 'download_financial_data' + , 'get_instrument_detail' + , 'get_instrument_type' + , 'get_trading_dates' + , 'get_sector_list' + , 'get_stock_list_in_sector' + , 'download_sector_data' + , 'add_sector' + , 'remove_sector' + , 'get_index_weight' + , 'download_index_weight' + , 'get_holidays' + , 'get_trading_calendar' + , 'get_trading_time' + , 'get_etf_info' + , 'download_etf_info' + , 'get_main_contract' + , 'download_history_contracts' + , 'download_cb_data' + , 'get_cb_info' + , 'create_sector_folder' + , 'create_sector' + , 'remove_stock_from_sector' + , 'reset_sector' + , 'get_period_list' + , 'download_his_st_data' +] + +def try_except(func): + import sys + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception: + exc_type, exc_instance, exc_traceback = sys.exc_info() + formatted_traceback = ''.join(_TRACEBACK_.format_tb(exc_traceback)) + message = '\n{0} raise {1}:{2}'.format( + formatted_traceback, + exc_type.__name__, + exc_instance + ) + # raise exc_type(message) + # print(message) + return None + + return wrapper + + +### config + +debug_mode = 0 + +default_data_dir = '../userdata_mini/datadir' +__data_dir_from_server = default_data_dir +data_dir = None + +enable_hello = True + + +### connection + +__client = None +__client_last_spec = ('', None) + +__hk_broke_info = {} +__download_version = None + + +def connect(ip = '', port = None, remember_if_success = True): + global __client + global __data_dir_from_server + global __download_version + + if __client: + if __client.is_connected(): + return __client + + __client.shutdown() + __client = None + __data_dir_from_server = default_data_dir + + from . import xtconn + + if not port and (ip != '' and ip != '127.0.0.1' and ip != 'localhost'): + raise Exception("远程地址不支持仅输入IP") + + if isinstance(port, int): + if ip: + server_list = [f'{ip}:{port}'] + else: + server_list = [f'127.0.0.1:{port}', f'localhost:{port}'] + else: + server_list = xtconn.scan_available_server_addr() + + default_addr = '127.0.0.1:58610' + if not default_addr in server_list: + server_list.append(default_addr) + + start_port = 0 + end_port = 65535 + + if isinstance(port, tuple): + start_port = port[0] + end_port = port[1] + + if start_port > end_port: + start_port, end_port = end_port, start_port + + __client = xtconn.connect_any(server_list, start_port, end_port) + + if not __client or not __client.is_connected(): + raise Exception("无法连接xtquant服务,请检查QMT-投研版或QMT-极简版是否开启") + + if remember_if_success: + global __client_last_spec + __client_last_spec = (ip, port) + + __data_dir_from_server = __client.get_data_dir() + if __data_dir_from_server == "": + __data_dir_from_server = _OS_.path.join(__client.get_app_dir(), default_data_dir) + + __data_dir_from_server = _OS_.path.abspath(__data_dir_from_server) + + try: + __download_version = _BSON_call_common( + __client.commonControl, 'getapiversion', {} + ).get('downloadversion', None) + except: + pass + + hello() + return __client + + +def reconnect(ip = '', port = None, remember_if_success = True): + global __client + global __data_dir_from_server + + if __client: + __client.shutdown() + __client = None + __data_dir_from_server = default_data_dir + + return connect(ip, port, remember_if_success) + + +def disconnect(): + global __client + global __data_dir_from_server + + if __client: + __client.shutdown() + __client = None + __data_dir_from_server = default_data_dir + return + + +def get_client(): + global __client + + if not __client or not __client.is_connected(): + global __client_last_spec + + ip, port = __client_last_spec + __client = connect(ip, port, False) + + return __client + + +def hello(): + global __client + global enable_hello + + if not enable_hello: + return + + server_info = None + peer_addr = None + __data_dir_from_server = None + + try: + server_info = _BSON_.BSON.decode(__client.get_server_tag()) + peer_addr = __client.get_peer_addr() + __data_dir_from_server = __client.get_data_dir() + except Exception as e: + pass + + print( +f'''***** xtdata连接成功 ***** +服务信息: {server_info} +服务地址: {peer_addr} +数据路径: {__data_dir_from_server} +设置xtdata.enable_hello = False可隐藏此消息 +''' + ) + return + + +def get_current_data_dir(): + global data_dir + global __data_dir_from_server + return data_dir if data_dir != None else __data_dir_from_server + + +__meta_field_list = {} + +def get_field_list(metaid): + global __meta_field_list + + if not __meta_field_list: + x = open(_OS_.path.join(_OS_.path.dirname(__file__), 'config', 'metaInfo.json'), 'r', encoding="utf-8").read() + metainfo = eval(x) + + for meta in metainfo: + filed_dict = {} + metadetail = metainfo.get(str(meta), {}) + filed = metadetail.get('fields', {}) + for key in filed: + filed_dict[key] = filed[key].get('desc', key) + + filed_dict['G'] = 'time' + filed_dict['S'] = 'stock' + __meta_field_list[meta] = filed_dict + + return __meta_field_list.get(str(metaid), {}) + + +### utils + +def create_array(shape, dtype_tuple, capsule, size): + import numpy as np + import ctypes + + ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.POINTER(ctypes.c_char) + ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p] + buff = ctypes.pythonapi.PyCapsule_GetPointer(capsule, None) + base_type = size * buff._type_ + + for dim in shape[::-1]: + base_type = dim * base_type + p_arr_type = ctypes.POINTER(base_type) + obj = ctypes.cast(buff, p_arr_type).contents + obj._base = capsule + return np.ndarray(shape = shape, dtype = np.dtype(dtype_tuple), buffer = obj) + +from .xtdatacenter import register_create_nparray as __register_create_nparray +__register_create_nparray(create_array) + + +def _BSON_call_common(interface, func, param): + return _BSON_.BSON.decode(interface(func, _BSON_.BSON.encode(param))) + + +### function + +def get_stock_list_in_sector(sector_name, real_timetag = -1): + ''' + 获取板块成份股,支持客户端左侧板块列表中任意的板块,包括自定义板块 + :param sector_name: (str)板块名称 + :real_timetag: 时间:1512748800000 或 ‘20171209’,可缺省,缺省为获取最新成份,不缺省时获取对应时间的历史成份 + :return: list + ''' + client = get_client() + for illegalstr in ['\\', '/', ':', '*', '?', '"', '<', '>', '|']: + sector_name = sector_name.replace(illegalstr, '') + + if isinstance(real_timetag, str): + if real_timetag != '': + real_timetag = int(_TIME_.mktime(_TIME_.strptime(real_timetag, '%Y%m%d')) * 1000) + else: + real_timetag = -1 + + return client.get_stock_list_in_sector(sector_name, real_timetag) + + +def get_index_weight(index_code): + ''' + 获取某只股票在某指数中的绝对权重 + :param index_code: (str)指数名称 + :return: dict + ''' + client = get_client() + return client.get_weight_in_index(index_code) + + +def get_financial_data(stock_list, table_list=[], start_time='', end_time='', report_type='report_time'): + ''' + 获取财务数据 + :param stock_list: (list)合约代码列表 + :param table_list: (list)报表名称列表 + :param start_time: (str)起始时间 + :param end_time: (str)结束时间 + :param report_type: (str) 时段筛选方式 'announce_time' / 'report_time' + :return: + field: list[str] + date: list[int] + stock: list[str] + value: list[list[float]] + ''' + client = get_client() + all_table = { + 'Balance' : 'ASHAREBALANCESHEET' + , 'Income' : 'ASHAREINCOME' + , 'CashFlow' : 'ASHARECASHFLOW' + , 'Capital' : 'CAPITALSTRUCTURE' + , 'HolderNum' : 'SHAREHOLDER' + , 'Top10Holder' : 'TOP10HOLDER' + , 'Top10FlowHolder' : 'TOP10FLOWHOLDER' + , 'PershareIndex' : 'PERSHAREINDEX' + } + + if not table_list: + table_list = list(all_table.keys()) + + all_table_upper = {table.upper() : all_table[table] for table in all_table} + req_list = [] + names = {} + for table in table_list: + req_table = all_table_upper.get(table.upper(), table) + req_list.append(req_table) + names[req_table] = table + + data = {} + sl_len = 20 + stock_list2 = [stock_list[i : i + sl_len] for i in range(0, len(stock_list), sl_len)] + for sl in stock_list2: + data2 = client.get_financial_data(sl, req_list, start_time, end_time, report_type) + for s in data2: + data[s] = data2[s] + + import math + def conv_date(data, key, key2): + if key in data: + tmp_data = data[key] + if math.isnan(tmp_data): + if key2 not in data or math.isnan(data[key2]): + data[key] = '' + else: + tmp_data = data[key2] + data[key] = _TIME_.strftime('%Y%m%d', _TIME_.localtime(tmp_data / 1000)) + return + + result = {} + import pandas as pd + for stock in data: + stock_data = data[stock] + result[stock] = {} + for table in stock_data: + table_data = stock_data[table] + for row_data in table_data: + conv_date(row_data, 'm_anntime', 'm_timetag') + conv_date(row_data, 'm_timetag', '') + conv_date(row_data, 'declareDate', '') + conv_date(row_data, 'endDate', '') + result[stock][names.get(table, table)] = pd.DataFrame(table_data) + return result + + +def get_market_data_ori( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True, enable_read_from_server = True + , data_dir = None +): + client = get_client() + enable_read_from_local = period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', 'tick', '1w', '1mon', '1q', '1hy', '1y'} + global debug_mode + + if data_dir == None: + data_dir = get_current_data_dir() + + return client.get_market_data3(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data, 'v2', enable_read_from_local, enable_read_from_server, debug_mode, data_dir) + + +def get_market_data( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True +): + ''' + 获取历史行情数据 + :param field_list: 行情数据字段列表,[]为全部字段 + K线可选字段: + "time" #时间戳 + "open" #开盘价 + "high" #最高价 + "low" #最低价 + "close" #收盘价 + "volume" #成交量 + "amount" #成交额 + "settle" #今结算 + "openInterest" #持仓量 + 分笔可选字段: + "time" #时间戳 + "lastPrice" #最新价 + "open" #开盘价 + "high" #最高价 + "low" #最低价 + "lastClose" #前收盘价 + "amount" #成交总额 + "volume" #成交总量 + "pvolume" #原始成交总量 + "stockStatus" #证券状态 + "openInt" #持仓量 + "lastSettlementPrice" #前结算 + "askPrice1", "askPrice2", "askPrice3", "askPrice4", "askPrice5" #卖一价~卖五价 + "bidPrice1", "bidPrice2", "bidPrice3", "bidPrice4", "bidPrice5" #买一价~买五价 + "askVol1", "askVol2", "askVol3", "askVol4", "askVol5" #卖一量~卖五量 + "bidVol1", "bidVol2", "bidVol3", "bidVol4", "bidVol5" #买一量~买五量 + :param stock_list: 股票代码 "000001.SZ" + :param period: 周期 分笔"tick" 分钟线"1m"/"5m"/"15m" 日线"1d" + Level2行情快照"l2quote" Level2行情快照补充"l2quoteaux" Level2逐笔委托"l2order" Level2逐笔成交"l2transaction" Level2大单统计"l2transactioncount" Level2委买委卖队列"l2orderqueue" + Level1逐笔成交统计一分钟“transactioncount1m” Level1逐笔成交统计日线“transactioncount1d” + 期货仓单“warehousereceipt” 期货席位“futureholderrank” 互动问答“interactiveqa” + :param start_time: 起始时间 "20200101" "20200101093000" + :param end_time: 结束时间 "20201231" "20201231150000" + :param count: 数量 -1全部/n: 从结束时间向前数n个 + :param dividend_type: 除权类型"none" "front" "back" "front_ratio" "back_ratio" + :param fill_data: 对齐时间戳时是否填充数据,仅对K线有效,分笔周期不对齐时间戳 + 为True时,以缺失数据的前一条数据填充 + open、high、low、close 为前一条数据的close + amount、volume为0 + settle、openInterest 和前一条数据相同 + 为False时,缺失数据所有字段填NaN + :return: 数据集,分笔数据和K线数据格式不同 + period为'tick'时:{stock1 : value1, stock2 : value2, ...} + stock1, stock2, ... : 合约代码 + value1, value2, ... : np.ndarray 数据列表,按time增序排列 + period为其他K线周期时:{field1 : value1, field2 : value2, ...} + field1, field2, ... : 数据字段 + value1, value2, ... : pd.DataFrame 字段对应的数据,各字段维度相同,index为stock_list,columns为time_list + ''' + if period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', '1w', '1mon', '1q', '1hy', '1y'}: + import pandas as pd + index, data = get_market_data_ori(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data) + + result = {} + for field in data: + result[field] = pd.DataFrame(data[field], index = index[0], columns = index[1]) + return result + + return get_market_data_ori(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data) + + +def get_market_data_ex_ori( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True, enable_read_from_server = True + , data_dir = None +): + client = get_client() + enable_read_from_local = period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', 'tick', '1w', '1mon', '1q', '1hy', '1y'} + global debug_mode + + if data_dir == None: + data_dir = get_current_data_dir() + + return client.get_market_data3(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data, 'v3', enable_read_from_local, enable_read_from_server, debug_mode, data_dir) + + +def get_market_data_ex( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True +): + if period == 'hkbrokerqueue' or period == 'hkbrokerqueue2' or period == (1820, 0): + showbrokename = period == 'hkbrokerqueue2' + return get_broker_queue_data(stock_list, start_time, end_time, count, showbrokename) + + period = _get_tuple_period(period) or period + if isinstance(period, tuple): + return _get_market_data_ex_tuple_period(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data) + + if period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', '1w', '1mon', '1q', '1hy', '1y'}: + return _get_market_data_ex_ori_221207(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data) + + import pandas as pd + result = {} + + ifield = 'time' + query_field_list = field_list if (not field_list) or (ifield in field_list) else [ifield] + field_list + ori_data = get_market_data_ex_ori(query_field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data) + + if not ori_data: + return result + + fl = field_list + stime_fmt = '%Y%m%d' if period == '1d' else '%Y%m%d%H%M%S' + if fl: + fl2 = fl if ifield in fl else [ifield] + fl + for s in ori_data: + sdata = pd.DataFrame(ori_data[s], columns = fl2) + sdata2 = sdata[fl] + sdata2.index = [timetag_to_datetime(t, stime_fmt) for t in sdata[ifield]] + result[s] = sdata2 + else: + needconvert, metaid = _needconvert_period(period) + if needconvert: + convert_field_list = get_field_list(metaid) + + for s in ori_data: + odata = ori_data[s] + if convert_field_list: + convert_data_list = [] + for data in odata: + convert_data = _convert_component_info(data, convert_field_list) + convert_data_list.append(convert_data) + odata = convert_data_list + + sdata = pd.DataFrame(odata) + sdata.index = [timetag_to_datetime(t, stime_fmt) for t in sdata[ifield]] + result[s] = sdata + else: + for s in ori_data: + sdata = pd.DataFrame(ori_data[s]) + sdata.index = [timetag_to_datetime(t, stime_fmt) for t in sdata[ifield]] + result[s] = sdata + + return result + + +def _get_market_data_ex_ori_221207( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True, enable_read_from_server = True + , data_dir = None +): + import numpy as np + import pandas as pd + client = get_client() + enable_read_from_local = period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', 'tick', '1w', '1mon', '1q', '1hy', '1y'} + global debug_mode + + if data_dir == None: + data_dir = get_current_data_dir() + + ret = client.get_market_data3(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data, 'v4', enable_read_from_local, + enable_read_from_server, debug_mode, data_dir) + result = {} + for stock, index, npdatas in ret: + data = {field: np.frombuffer(b, fi) for field, fi, b in npdatas} + result[stock] = pd.DataFrame(data=data, index=index) + return result + +def _get_market_data_ex_221207( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True, enable_read_from_server = True +): + ifield = 'time' + query_field_list = field_list if (not field_list) or (ifield in field_list) else [ifield] + field_list + + if period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', '1w', '1mon', '1q', '1hy', '1y'}: + ori_data = _get_market_data_ex_ori_221207(query_field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data, enable_read_from_server) + else: + ori_data = get_market_data_ex_ori(query_field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data, enable_read_from_server) + + import pandas as pd + result = {} + + fl = field_list + + if fl: + fl2 = fl if ifield in fl else [ifield] + fl + for s in ori_data: + sdata = pd.DataFrame(ori_data[s], columns = fl2) + sdata2 = sdata[fl] + sdata2.index = pd.to_datetime((sdata[ifield] + 28800000) * 1000000) + result[s] = sdata2 + else: + for s in ori_data: + sdata = pd.DataFrame(ori_data[s]) + sdata.index = pd.to_datetime((sdata[ifield] + 28800000) * 1000000) + result[s] = sdata + + return result + + +get_market_data3 = _get_market_data_ex_221207 + +def _get_data_file_path(stocklist, period, date = '20380119'): + + if isinstance(period, tuple): + metaid, periodNum = period + periodstr = '' + else: + periodstr = period + metaid = -1 + periodNum = -1 + + data = { + 'stocklist': stocklist + , 'period': periodstr + , 'metaid':metaid + , 'periodNum':periodNum + , 'date':date + } + + client = get_client() + + path_result = _BSON_call_common( + client.commonControl, 'getdatafilepath' + , data + ) + return path_result.get('result', {}) + +__TUPLE_PERIODS = { + 'warehousereceipt' : (4015,86400000, '') + , 'futureholderrank' : (5008,86400000, '') + , 'interactiveqa' : (7011, 86400000, '') + , 'dividendplaninfo' : (2025, 86401000, '') + , 'etfredemptionlist' : (2004, 86401000, '') + , 'historymaincontract' : (5004, 86400000, '') + , 'brokerqueue' : (1820,0, '港股委托经纪人队列') + , 'brokerqueue2' : (1820,0, '港股委托经纪人队列(对结果进行转换)') + , 'brokerinfo' : (2038,86401000, '') + , 'delistchangebond' : (4020, 86401000, '') + , 'replacechangebond' : (4021, 86401000, '') + , 'optionhistorycontract' : (9502, 86400000, '') + , 'etfiopv1m' : (4011, 60000, '') + , 'etfiopv1d' : (4011, 86400000, '') + , 'announcement' : (9000, 86400000, '') + , 'hktdetails' : (2033, 86400000, '') + , 'stocklistchange' : (2012, 86400000, '') + , 'riskfreerate' : (2032, 86400000, '') + #以下内置已加 + , 'etfstatistics': (3030, 0, '') + , 'etfstatisticsl2': (1830, 0, '') + , 'northfinancechange1m': (3006, 60000, '') + , 'northfinancechange1d': (3006, 86400000, '') + , 'stoppricedata': (9506, 86400000, '') +} + +__STR_PERIODS = { + (3001,60000) : '1m' + , (3001,300000) : '5m' + , (3001,900000) : '15m' + , (3001,1800000) : '30m' + , (3001,3600000) : '60m' + , (3001,3600000) : '1h' + , (3001,86400000) : '1d' +} + +def _needconvert_period(period): + datas = { + 'snapshotindex' : 3004 + , 'etfiopv' : 4011 + } + return period in datas, datas.get(period, -1) + +def _get_tuple_period(period): + if isinstance(period, tuple): + return __STR_PERIODS.get(period, None) + else: + res = __TUPLE_PERIODS.get(period, None) + if res: + metaid, p, desc = res + return (metaid, p) + else: + return None + + +def _get_market_data_ex_tuple_period_ori( + stock_list = [], period = () + , start_time = '', end_time = '' + , count = -1 +): + client = get_client() + + data_path_dict = _get_data_file_path(stock_list, period) + + import pandas as pd + + ori_data = {} + for stockcode in data_path_dict: + file_name = data_path_dict[stockcode] + data_list = client.read_local_data(file_name, start_time, end_time, count) + + cdata_list = [] + for data in data_list: + cdata_list.append(_BSON_.BSON.decode(data)) + + ori_data[stockcode] = cdata_list + + return ori_data + + +def _convert_component_info(data, convert_field_list): + if not isinstance(data, dict): + return data + + new_data = {} + for key, value in data.items(): + name = convert_field_list.get(key, key) + if isinstance(value, dict): + new_data[name] = _convert_component_info(value, convert_field_list) + elif isinstance(value, list): + new_data[name] = [_convert_component_info(item, convert_field_list) for item in value] + else: + new_data[name] = value + + return new_data + +def _get_market_data_ex_tuple_period( + field_list = [], stock_list = [], period = '1d' + , start_time = '', end_time = '', count = -1 + , dividend_type = 'none', fill_data = True, enable_read_from_server = True +): + if not isinstance(period, tuple): + return {} + + all_data = _get_market_data_ex_tuple_period_ori(stock_list, period, start_time, end_time, count) + + metaid, periodNum = period + convert_field_list = get_field_list(metaid) + + import pandas as pd + + ori_data = {} + for stockcode,data_list in all_data.items(): + if convert_field_list: + convert_data_list = [] + for data in data_list: + convert_data = _convert_component_info(data, convert_field_list) + convert_data_list.append(convert_data) + data_list = convert_data_list + ori_data[stockcode] = pd.DataFrame(data_list) + + return ori_data + + +def get_data_dir(): + cl = get_client() + return _OS_.path.abspath(cl.get_data_dir()) + + +def get_local_data(field_list=[], stock_list=[], period='1d', start_time='', end_time='', count=-1, + dividend_type='none', fill_data=True, data_dir=None): + if data_dir == None: + data_dir = get_current_data_dir() + + if period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', '1w', '1mon', '1q', '1hy', '1y'}: + return _get_market_data_ex_ori_221207(field_list, stock_list, period, start_time, end_time, count, + dividend_type, fill_data, False, data_dir) + + import pandas as pd + result = {} + + ifield = 'time' + query_field_list = field_list if (not field_list) or (ifield in field_list) else [ifield] + field_list + ori_data = get_market_data_ex_ori(query_field_list, stock_list, period, start_time, end_time, count, dividend_type, + fill_data, False, data_dir) + + if not ori_data: + return result + + fl = field_list + stime_fmt = '%Y%m%d' if period == '1d' else '%Y%m%d%H%M%S' + if fl: + fl2 = fl if ifield in fl else [ifield] + fl + for s in ori_data: + sdata = pd.DataFrame(ori_data[s], columns = fl2) + sdata2 = sdata[fl] + sdata2.index = [timetag_to_datetime(t, stime_fmt) for t in sdata[ifield]] + result[s] = sdata2 + else: + for s in ori_data: + sdata = pd.DataFrame(ori_data[s]) + sdata.index = [timetag_to_datetime(t, stime_fmt) for t in sdata[ifield]] + result[s] = sdata + + return result + + +def get_l2_quote(field_list=[], stock_code='', start_time='', end_time='', count=-1): + ''' + level2实时行情 + ''' + global debug_mode + client = get_client() + datas = client.get_market_data3(field_list, [stock_code], 'l2quote', start_time, end_time, count, 'none', False, '', False, True, debug_mode, '') + if datas: + return datas[stock_code] + return None + + +def get_l2_order(field_list=[], stock_code='', start_time='', end_time='', count=-1): + ''' + level2逐笔委托 + ''' + global debug_mode + client = get_client() + datas = client.get_market_data3(field_list, [stock_code], 'l2order', start_time, end_time, count, 'none', False, '', False, True, debug_mode, '') + if datas: + return datas[stock_code] + return None + + +def get_l2_transaction(field_list=[], stock_code='', start_time='', end_time='', count=-1): + ''' + level2逐笔成交 + ''' + global debug_mode + client = get_client() + datas = client.get_market_data3(field_list, [stock_code], 'l2transaction', start_time, end_time, count, 'none', False, '', False, True, debug_mode, '') + if datas: + return datas[stock_code] + return None + + +def get_divid_factors(stock_code, start_time='', end_time=''): + ''' + 获取除权除息日及对应的权息 + :param stock_code: (str)股票代码 + :param date: (str)日期 + :return: pd.DataFrame 数据集 + ''' + client = get_client() + datas = client.get_divid_factors(stock_code, start_time, end_time) + import pandas as pd + datas = pd.DataFrame(datas).T + return datas + + +@try_except +def getDividFactors(stock_code, date): + client = get_client() + resData = client.get_divid_factors(stock_code, date) + res = {resData[i]: [resData[i + 1][j] for j in + range(0, len(resData[i + 1]), 1)] for i in range(0, len(resData), 2)} + if isinstance(res, dict): + for k, v in res.items(): + if isinstance(v, list) and len(v) > 5: + v[5] = int(v[5]) + return res + + +def get_main_contract(code_market: str, start_time: str = "", end_time: str = ""): + ''' + 获取主力合约/历史主力合约 + 注意:获取历史主力合约需要先调用下载函数xtdata.download_history_data(symbol, 'historymaincontract', '', '') + Args: + code_market: 主力连续合约code,如"IF00.IF","AP00.ZF" + start_time: 开始时间(可不填),格式为"%Y%m%d",默认为"" + end_time: 结束时间(可不填),格式为"%Y%m%d",默认为"" + Return: + str:默认取当前主力合约代码 + str:当指定start_time,不指定end_time时,返回指定日期主力合约代码 + pd.Series:当指定start_time,end_time,返回区间内主力合约组成的Series,index为时间戳 + Example: + xtdata.get_main_contract("AP00.ZF") # 取当前主力合约 + + xtdata.get_main_contract("AP00.ZF","20230101") # 取历史某一天主力合约 + + xtdata.get_main_contract("AP00.ZF","20230101","20240306") # 取某个时间段的主力合约序列 + ''' + period = 'historymaincontract' + marker_code = code_market.split(".")[1] + + if start_time == "" and end_time == "": + client = get_client() + return client.get_main_contract(code_market) + "." + marker_code + elif start_time and end_time == "": + # 当指定start_time时,返回指定日期主力合约代码\n + data = get_market_data_ex([], [code_market], period)[code_market] + s_timetag = datetime_to_timetag(start_time, "%Y%m%d") + + data = data.loc[data.iloc[:, 0] < s_timetag] + if data.shape[0] > 0: + return data['合约在交易所的代码'].iloc[-1] + "." + marker_code + else: + return '' + elif start_time and end_time: + import pandas as pd + data = get_market_data_ex([], [code_market], period)[code_market] + s_timetag = datetime_to_timetag(start_time, "%Y%m%d") + e_timetag = datetime_to_timetag(end_time, "%Y%m%d") + + data = data.loc[(data.iloc[:, 0] <= e_timetag) & (data.iloc[:, 0] >= s_timetag)] + if data.shape[0] > 0: + index = data.iloc[:, 0] + values = data['合约在交易所的代码'] + "." + marker_code + + res = pd.Series(index=index.values, data=values.values) + res = res.loc[res.ne(res.shift(1))] + return res + else: + return '' + + +def get_sec_main_contract(code_market: str, start_time: str = "", end_time: str = ""): + ''' + 获取次主力合约/历史次主力合约 + 注意:获取历史次主力合约需要先调用下载函数xtdata.download_history_data(symbol, 'historymaincontract', '', '') + Args: + code_market: 主力连续合约code,如"IF00.IF","AP00.ZF" + start_time: 开始时间(可不填),格式为"%Y%m%d",默认为"" + end_time: 结束时间(可不填),格式为"%Y%m%d",默认为"" + Return: + str:默认取当前次主力合约代码 + str:当指定start_time,不指定end_time时,返回指定日期次主力合约代码 + pd.Series:当指定start_time,end_time,返回区间内次主力合约组成的Series,index为时间戳 + Example: + xtdata.get_sec_main_contract("AP00.ZF") # 取当前次主力合约 + + xtdata.get_sec_main_contract("AP00.ZF","20230101") # 取历史某一天次主力合约 + + xtdata.get_sec_main_contract("AP00.ZF","20230101","20240306") # 取某个时间段的次主力合约序列 + ''' + period = 'historymaincontract' + marker_code = code_market.split(".")[1] + + if start_time == "" and end_time == "": + client = get_client() + code = code_market.split(".")[0] + if code.endswith('00'): + return client.get_main_contract(code + '1' + '.' + marker_code) + "." + marker_code + elif start_time and end_time == "": + # 当指定start_time时,返回指定日期主力合约代码\n + data = get_market_data_ex([], [code_market], period)[code_market] + s_timetag = datetime_to_timetag(start_time, "%Y%m%d") + + data = data.loc[data.iloc[:, 0] < s_timetag] + if data.shape[0] > 0: + return data['次主力合约代码'].iloc[-1] + "." + marker_code + else: + return '' + elif start_time and end_time: + import pandas as pd + data = get_market_data_ex([], [code_market], period)[code_market] + s_timetag = datetime_to_timetag(start_time, "%Y%m%d") + e_timetag = datetime_to_timetag(end_time, "%Y%m%d") + + data = data.loc[(data.iloc[:, 0] <= e_timetag) & (data.iloc[:, 0] >= s_timetag)] + if data.shape[0] > 0: + index = data.iloc[:, 0] + values = data['次主力合约代码'] + "." + marker_code + + res = pd.Series(index=index.values, data=values.values) + res = res.loc[res.ne(res.shift(1))] + return res + else: + return '' + + +def datetime_to_timetag(datetime, format = "%Y%m%d%H%M%S"): + if len(datetime) == 8: + format = "%Y%m%d" + timetag = _TIME_.mktime(_TIME_.strptime(datetime, format)) + return timetag * 1000 + +def timetag_to_datetime(timetag, format): + ''' + 将毫秒时间转换成日期时间 + :param timetag: (int)时间戳毫秒数 + :param format: (str)时间格式 + :return: str + ''' + return timetagToDateTime(timetag, format) + + +@try_except +def timetagToDateTime(timetag, format): + import time + timetag = timetag / 1000 + time_local = _TIME_.localtime(timetag) + return _TIME_.strftime(format, time_local) + + +def get_trading_dates(market, start_time='', end_time='', count=-1): + ''' + 根据市场获取交易日列表 + : param market: 市场代码 e.g. 'SH','SZ','IF','DF','SF','ZF'等 + : param start_time: 起始时间 '20200101' + : param end_time: 结束时间 '20201231' + : param count: 数据个数,-1为全部数据 + :return list(long) 毫秒数的时间戳列表 + ''' + client = get_client() + datas = client.get_trading_dates_by_market(market, start_time, end_time, count) + return datas + + +def get_full_tick(code_list): + ''' + 获取盘口tick数据 + :param code_list: (list)stock.market组成的股票代码列表 + :return: dict + {'stock.market': {dict}} + ''' + import json + + client = get_client() + resp_json = client.get_full_tick(code_list) + return json.loads(resp_json) + + +def subscribe_callback_wrapper(callback): + import traceback + def subscribe_callback(datas): + try: + if type(datas) == bytes: + datas = _BSON_.BSON.decode(datas) + if callback: + callback(datas) + except: + print('subscribe callback error:', callback) + _TRACEBACK_.print_exc() + return subscribe_callback + +def subscribe_callback_wrapper_1820(callback): + import traceback + def subscribe_callback(datas): + try: + if type(datas) == bytes: + datas = _BSON_.BSON.decode(datas) + datas = _covert_hk_broke_data(datas) + if callback: + callback(datas) + except: + print('subscribe callback error:', callback) + _TRACEBACK_.print_exc() + + return subscribe_callback + + +def subscribe_callback_wrapper_convert(callback, metaid): + import traceback + convert_field_list = get_field_list(metaid) + def subscribe_callback(datas): + try: + if type(datas) == bytes: + datas = _BSON_.BSON.decode(datas) + if convert_field_list: + for s in datas: + sdata = datas[s] + convert_data_list = [] + for data in sdata: + convert_data = _convert_component_info(data, convert_field_list) + convert_data_list.append(convert_data) + datas[s] = convert_data_list + if callback: + callback(datas) + except: + print('subscribe callback error:', callback) + _TRACEBACK_.print_exc() + + return subscribe_callback + +def subscribe_quote(stock_code, period='1d', start_time='', end_time='', count=0, callback=None): + ''' + 订阅股票行情数据 + :param stock_code: 股票代码 e.g. "000001.SZ" + :param period: 周期 分笔"tick" 分钟线"1m"/"5m" 日线"1d"等周期 + :param start_time: 开始时间,格式YYYYMMDD/YYYYMMDDhhmmss/YYYYMMDDhhmmss.milli,e.g."20200427" "20200427093000" "20200427093000.000" + 若取某日全量历史数据,时间需要具体到秒,e.g."20200427093000" + :param end_time: 结束时间 同“开始时间” + :param count: 数量 -1全部/n: 从结束时间向前数n个 + :param callback: + 订阅回调函数onSubscribe(datas) + :param datas: {stock : [data1, data2, ...]} 数据字典 + :return: int 订阅序号 + ''' + return subscribe_quote2(stock_code, period, start_time, end_time, count, None, callback) + +def subscribe_quote2(stock_code, period='1d', start_time='', end_time='', count=0, dividend_type = None, callback=None): + ''' + 订阅股票行情数据第二版 + 与第一版相比增加了除权参数dividend_type,默认None + + :param stock_code: 股票代码 e.g. "000001.SZ" + :param period: 周期 分笔"tick" 分钟线"1m"/"5m" 日线"1d"等周期 + :param start_time: 开始时间,格式YYYYMMDD/YYYYMMDDhhmmss/YYYYMMDDhhmmss.milli,e.g."20200427" "20200427093000" "20200427093000.000" + 若取某日全量历史数据,时间需要具体到秒,e.g."20200427093000" + :param end_time: 结束时间 同“开始时间” + :param count: 数量 -1全部/n: 从结束时间向前数n个 + :param dividend_type: 除权类型"none" "front" "back" "front_ratio" "back_ratio" + :param callback: + 订阅回调函数onSubscribe(datas) + :param datas: {stock : [data1, data2, ...]} 数据字典 + :return: int 订阅序号 + ''' + if callback: + needconvert, metaid = _needconvert_period(period) + if needconvert: + callback = subscribe_callback_wrapper_convert(callback, metaid) + elif period == 'brokerqueue2': + callback = subscribe_callback_wrapper_1820(callback) + else: + callback = subscribe_callback_wrapper(callback) + + period = _get_tuple_period(period) or period + if isinstance(period, tuple): + metaid, periodNum = period + meta = {'stockCode': stock_code, 'period': period, 'metaid': metaid, 'periodnum': periodNum, 'dividendtype': dividend_type} + else: + meta = {'stockCode': stock_code, 'period': period, 'metaid': -1, 'periodnum': -1, 'dividendtype': dividend_type} + region = {'startTime': start_time, 'endTime': end_time, 'count': count} + + client = get_client() + return client.subscribe_quote(_BSON_.BSON.encode(meta), _BSON_.BSON.encode(region), callback) + + +def subscribe_l2thousand(stock_code, gear_num = 0, callback = None): + ''' + 订阅千档盘口 + ''' + if callback: + callback = subscribe_callback_wrapper(callback) + + meta = {'stockCode': stock_code, 'period': 'l2thousand', 'metaid': -1, 'periodnum': -1} + region = {'thousandGearNum': gear_num, 'thousandDetailGear': 0, 'thousandDetailNum': 0} + + client = get_client() + return client.subscribe_quote(_BSON_.BSON.encode(meta), _BSON_.BSON.encode(region), callback) + + +def subscribe_l2thousand_queue( + stock_code, callback = None + , gear = None + , price = None +): + ''' + 根据档位或价格订阅千档 + stock_code: 股票代码 e.g. "000001.SZ" + callback: + 订阅回调函数onSubscribe(datas) + gear: 按档位订阅 eg. + price: 单个价格:float, 价格范围:eg.[8.66, 8.88], 一组价格list + return: int 订阅序号 + 例: + def on_data(datas): + for stock_code in datas: + print(stock_code, datas[stock_code]) + subscribe_l2thousand_queue(‘000001.SZ’, callback = on_data, gear = 3)#订阅买卖3档数据 + subscribe_l2thousand_queue(‘000001.SZ’, callback = on_data, price = (8.68, 8.88))#订阅[8.68, 8.88]价格区间的数据 + ''' + + if callback: + callback = subscribe_callback_wrapper(callback) + + if gear is not None and price is not None: + raise Exception('不能同时订阅档位和价格!') + + if gear is None: + gear = 0 + if price is not None: + if isinstance(price, float): + price = [int(price * 10000)] + elif isinstance(price, tuple): + sprice, eprice = price + price = [i for i in range(int(sprice * 10000), int((eprice + 0.01) * 10000), int(0.01 * 10000))] + else: + price = [i * 10000 for i in price] + price.sort() + + meta = {'stockCode': stock_code, 'isSubscribeByType': True, 'gear': gear, 'price': price, 'period': 'l2thousand', + 'metaid': -1, 'periodnum': -1} + region = {'thousandDetailGear': 1000, 'thousandDetailNum': 1000} + + client = get_client() + return client.subscribe_quote(_BSON_.BSON.encode(meta), _BSON_.BSON.encode(region), callback) + + +def get_l2thousand_queue(stock_code, gear = None, price = None): + ''' + 根据档位或价格获取前档 + stock_code 股票代码 e.g. "000001.SZ" + gear: Optional[int], + price: Optional[list(float), tuple(2)] + ''' + if gear is None: + gear = 0 + if price is not None: + if isinstance(price, float): + price = [int(price * 10000)] + elif isinstance(price, tuple): + sprice, eprice = price + price = [i for i in range(int(sprice * 10000), int((eprice + 0.01) * 10000), int(0.01 * 10000))] + else: + price = [i * 10000 for i in price] + price.sort() + + client = get_client() + + data = {} + data['stockcode'] = stock_code + data['period'] = 'l2thousand' + data['gear'] = gear + data['price'] = price + + result_bson = client.commonControl('getl2thousandqueue', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + + +def subscribe_whole_quote(code_list, callback=None): + ''' + 订阅全推数据 + :param code_list: 市场代码列表 ["SH", "SZ"] + :param callback: + 订阅回调函数onSubscribe(datas) + :param datas: {stock1 : data1, stock2 : data2, ...} 数据字典 + :return: int 订阅序号 + ''' + if callback: + callback = subscribe_callback_wrapper(callback) + + client = get_client() + return client.subscribe_whole_quote(code_list, callback) + + +def unsubscribe_quote(seq): + ''' + :param seq: 订阅接口subscribe_quote返回的订阅号 + :return: + ''' + client = get_client() + return client.unsubscribe_quote(seq) + + +def run(): + '''阻塞线程接收行情回调''' + import time + client = get_client() + while True: + _TIME_.sleep(3) + if not client.is_connected(): + raise Exception('行情服务连接断开') + break + return + +def create_sector_folder(parent_node,folder_name,overwrite = True): + ''' + 创建板块目录节点 + :parent_node str: 父节点,''为'我的' (默认目录) + :sector_name str: 要创建的板块目录名称 + :overwrite bool:是否覆盖 True为跳过,False为在folder_name后增加数字编号,编号为从1开始自增的第一个不重复的值 + ''' + client = get_client() + data = {} + data['parent'] = parent_node + data['foldername'] = folder_name + data['overwrite'] = overwrite + result_bson = client.commonControl('createsectorfolder', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + +def create_sector(parent_node,sector_name,overwrite = True): + ''' + 创建板块 + :parent_node str: 父节点,''为'我的' (默认目录) + :sector_name str: 要创建的板块名 + :overwrite bool:是否覆盖 True为跳过,False为在sector_name后增加数字编号,编号为从1开始自增的第一个不重复的值 + ''' + client = get_client() + data = {} + data['parent'] = parent_node + data['sectorname'] = sector_name + data['overwrite'] = overwrite + result_bson = client.commonControl('createsector', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + +def get_sector_list(): + ''' + 获取板块列表 + :return: (list[str]) + ''' + client = get_client() + return client.get_sector_list() + + +def add_sector(sector_name, stock_list): + ''' + 增加自定义板块 + :param sector_name: 板块名称 e.g. "我的自选" + :param stock_list: (list)stock.market组成的股票代码列表 + ''' + client = get_client() + data = {} + data['sectorname'] = sector_name + data['stocklist'] = stock_list + result_bson = client.commonControl('addsector', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + +def remove_stock_from_sector(sector_name, stock_list): + ''' + 移除板块成分股 + :param sector_name: 板块名称 e.g. "我的自选" + :stock_list: (list)stock.market组成的股票代码列表 + ''' + client = get_client() + data = {} + data['sectorname'] = sector_name + data['stocklist'] = stock_list + result_bson = client.commonControl('removestockfromsector', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + +def remove_sector(sector_name): + ''' + 删除自定义板块 + :param sector_name: 板块名称 e.g. "我的自选" + ''' + client = get_client() + data = {} + data['sectorname'] = sector_name + result_bson = client.commonControl('removesector', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + +def reset_sector(sector_name, stock_list): + ''' + 重置板块 + :param sector_name: 板块名称 e.g. "我的自选" + :stock_list: (list)stock.market组成的股票代码列表 + ''' + client = get_client() + data = {} + data['sectorname'] = sector_name + data['stocklist'] = stock_list + result_bson = client.commonControl('resetsector', _BSON_.BSON.encode(data)) + result = _BSON_.BSON.decode(result_bson) + return result.get('result') + +def _get_instrument_detail(stock_code): + from . import xtutil + + client = get_client() + inst = client.get_instrument_detail(stock_code) + if not inst: + return None + + inst = xtutil.read_from_bson_buffer(inst) + + if len(inst) != 1: + return None + return inst[0] + +def get_instrument_detail(stock_code, iscomplete = False): + ''' + 获取合约信息 + :param stock_code: 股票代码 e.g. "600000.SH" + :return: dict + ExchangeID(str):合约市场代码 + , InstrumentID(str):合约代码 + , InstrumentName(str):合约名称 + , ProductID(str):合约的品种ID(期货) + , ProductName(str):合约的品种名称(期货) + , ProductType(str):合约的类型 + , ExchangeCode(str):交易所代码 + , UniCode(str):统一规则代码 + , CreateDate(str):上市日期(期货) + , OpenDate(str):IPO日期(股票) + , ExpireDate(str):退市日或者到期日 + , PreClose(double):前收盘价格 + , SettlementPrice(double):前结算价格 + , UpStopPrice(double):当日涨停价 + , DownStopPrice(double):当日跌停价 + , FloatVolume(double):流通股本 + , TotalVolume(double):总股本 + , LongMarginRatio(double):多头保证金率 + , ShortMarginRatio(double):空头保证金率 + , PriceTick(double):最小变价单位 + , VolumeMultiple(int):合约乘数(对期货以外的品种,默认是1) + , MainContract(int):主力合约标记 + , LastVolume(int):昨日持仓量 + , InstrumentStatus(int):合约停牌状态 + , IsTrading(bool):合约是否可交易 + , IsRecent(bool):是否是近月合约, + ''' + + inst = _get_instrument_detail(stock_code) + if not inst: + return None + + if iscomplete: + if 'ExtendInfo' in inst: + for field in inst['ExtendInfo']: + inst[field] = inst['ExtendInfo'][field] + del inst['ExtendInfo'] + + def convNum2Str(field): + if field in inst and isinstance(inst[field], int): + inst[field] = str(inst[field]) + + convNum2Str('CreateDate') + convNum2Str('OpenDate') + convNum2Str('ExpireDate') + convNum2Str('EndDelivDate') + + if inst.get('FloatVolume', None) is None: + inst['FloatVolume'] = inst.get('FloatVolumn') + + if inst.get('TotalVolume', None) is None: + inst['TotalVolume'] = inst.get('TotalVolumn') + + return inst + + field_list = [ + 'ExchangeID' + , 'InstrumentID' + , 'InstrumentName' + , 'ProductID' + , 'ProductName' + , 'ProductType' + , 'ExchangeCode' + , 'UniCode' + , 'CreateDate' + , 'OpenDate' + , 'ExpireDate' + , 'PreClose' + , 'SettlementPrice' + , 'UpStopPrice' + , 'DownStopPrice' + , 'FloatVolume' + , 'TotalVolume' + , 'LongMarginRatio' + , 'ShortMarginRatio' + , 'PriceTick' + , 'VolumeMultiple' + , 'MainContract' + , 'LastVolume' + , 'InstrumentStatus' + , 'IsTrading' + , 'IsRecent' + ] + ret = {} + for field in field_list: + ret[field] = inst.get(field) + + exfield_list = [ + 'ProductTradeQuota' + , 'ContractTradeQuota' + , 'ProductOpenInterestQuota' + , 'ContractOpenInterestQuota' + ] + inst_ex = inst.get('ExtendInfo', {}) + for field in exfield_list: + ret[field] = inst_ex.get(field) + + def convNum2Str(field): + if field in ret and isinstance(ret[field], int): + ret[field] = str(ret[field]) + convNum2Str('CreateDate') + convNum2Str('OpenDate') + convNum2Str('ExpireDate') + + if ret.get('FloatVolume', None) is None: + ret['FloatVolume'] = inst.get('FloatVolumn') + + if ret.get('TotalVolume', None) is None: + ret['TotalVolume'] = inst.get('TotalVolumn') + + return ret + + +def download_index_weight(): + ''' + 下载指数权重数据 + ''' + client = get_client() + client.down_index_weight() + + +def download_history_contracts(): + ''' + 下载过期合约数据 + ''' + client = get_client() + client.down_history_contracts() + + +def _download_history_data_by_metaid(stock_code, metaid, period, start_time = '', end_time = '', incrementally = True): + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'downloadmetadata' + , { + 'stockcode': stock_code + , 'metaid': metaid + , 'period': period + , 'starttime': start_time + , 'endtime': end_time + , 'incrementally': incrementally + } + ) + return + + +def _download_history_data(stock_code, period, start_time = '', end_time = ''): + cl = get_client() + cl.supply_history_data(stock_code, period, start_time, end_time) + return + + +def download_history_data(stock_code, period, start_time = '', end_time = '', incrementally = None): + ''' + :param stock_code: str 品种代码,例如:'000001.SZ' + :param period: str 数据周期 + :param start_time: str 开始时间 + 格式为 YYYYMMDD 或 YYYYMMDDhhmmss 或 '' + 例如:'20230101' '20231231235959' + 空字符串代表全部,自动扩展到完整范围 + :param end_time: str 结束时间 格式同开始时间 + :param incrementally: 是否增量下载 + bool: 是否增量下载 + None: 使用start_time控制,start_time为空则增量下载 + ''' + + client = get_client() + + if __download_version is None: + if incrementally is None: + incrementally = False if start_time else True + + period = _get_tuple_period(period) or period + + if isinstance(period, tuple): + metaid, periodNum = period + return _download_history_data_by_metaid(stock_code, metaid, periodNum, start_time, end_time, incrementally) + + return _download_history_data(stock_code, period, start_time, end_time) + else: + return download_history_data2([stock_code], period, start_time, end_time, None, incrementally) + + +supply_history_data = download_history_data + + +def download_history_data2(stock_list, period, start_time='', end_time='', callback=None, incrementally = None): + ''' + :param stock_list: 股票代码列表 e.g. ["000001.SZ"] + :param period: 周期 分笔"tick" 分钟线"1m"/"5m" 日线"1d" + :param start_time: 开始时间,格式YYYYMMDD/YYYYMMDDhhmmss/YYYYMMDDhhmmss.milli,e.g."20200427" "20200427093000" "20200427093000.000" + 若取某日全量历史数据,时间需要具体到秒,e.g."20200427093000" + :param end_time: 结束时间 同上,若是未来某时刻会被视作当前时间 + :return: bool 是否成功 + ''' + client = get_client() + + if isinstance(stock_list, str): + stock_list = [stock_list] + + if incrementally is None: + incrementally = False if start_time else True + + param = {'incrementally' : incrementally} + + period = _get_tuple_period(period) or period + if isinstance(period, tuple): + metaid, periodNum = period + period = '' + param['metaid'] = metaid + param['period'] = periodNum + + status = [False, 0, 1, ''] + def on_progress(data): + try: + finished = data['finished'] + total = data['total'] + done = (finished >= total) + status[0] = done + status[1] = finished + status[2] = total + + try: + if callback: + callback(data) + except: + pass + + return done + except: + status[0] = True + status[3] = 'exception' + return True + result = client.supply_history_data2(stock_list, period, start_time, end_time, _BSON_.BSON.encode(param), on_progress) + if not result: + import time + try: + while not status[0] and client.is_connected(): + _TIME_.sleep(0.1) + except: + if status[1] < status[2]: + client.stop_supply_history_data2() + _TRACEBACK_.print_exc() + if not client.is_connected(): + raise Exception('行情服务连接断开') + if status[3]: + raise Exception('下载数据失败:' + status[3]) + return + + +def download_financial_data(stock_list, table_list=[], start_time='', end_time='', incrementally = None): + ''' + :param stock_list: 股票代码列表 + :param table_list: 财务数据表名列表,[]为全部表 + 可选范围:['Balance','Income','CashFlow','Capital','Top10FlowHolder','Top10Holder','HolderNum','PershareIndex'] + :param start_time: 开始时间,格式YYYYMMDD,e.g."20200427" + :param end_time: 结束时间 同上,若是未来某时刻会被视作当前时间 + ''' + client = get_client() + if not table_list: + table_list = ['Balance','Income','CashFlow','Capital','Top10FlowHolder','Top10Holder','HolderNum','PershareIndex'] + + for table in table_list: + download_history_data2(stock_list, table, start_time, end_time, None, incrementally) + + +def download_financial_data2(stock_list, table_list=[], start_time='', end_time='', callback=None): + ''' + :param stock_list: 股票代码列表 + :param table_list: 财务数据表名列表,[]为全部表 + 可选范围:['Balance','Income','CashFlow','Capital','Top10FlowHolder','Top10Holder','HolderNum','PershareIndex'] + :param start_time: 开始时间,格式YYYYMMDD,e.g."20200427" + :param end_time: 结束时间 同上,若是未来某时刻会被视作当前时间 + ''' + client = get_client() + if not table_list: + table_list = ['Balance','Income','CashFlow','Capital','Top10FlowHolder','Top10Holder','HolderNum','PershareIndex'] + + data = {} + data['total'] = len(table_list) * len(stock_list) + finish = 0 + for stock_code in stock_list: + for table in table_list: + client.supply_history_data(stock_code, table, start_time, end_time) + + finish = finish + 1 + try: + data['finished'] = finish + callback(data) + except: + pass + + if not client.is_connected(): + raise Exception('行情服务连接断开') + break + + +def get_instrument_type(stock_code, variety_list = None): + ''' + 判断证券类型 + :param stock_code: 股票代码 e.g. "600000.SH" + :return: dict{str : bool} {类型名:是否属于该类型} + ''' + client = get_client() + v_dct = client.get_stock_type(stock_code)#默认处理得到全部品种的信息 + if not v_dct: + return {} + v_dct1 = {} + if variety_list == None or len(variety_list) == 0:#返回该stock_code所有的品种的T/None(False) + v_dct1={k: v for k, v in v_dct.items() if v} + return v_dct1 + + for v in variety_list: + if v in v_dct: + v_dct1[v] = v_dct[v] + return v_dct1 + +get_stock_type = get_instrument_type + + +def download_sector_data(): + ''' + 下载行业板块数据 + ''' + client = get_client() + client.down_all_sector_data() + +def download_holiday_data(incrementally = True): + cl = get_client() + + inst = _BSON_call_common( + cl.commonControl + , 'downloadholidaydata' + , { + 'incrementally': incrementally + } + ) + return inst + +def get_holidays(): + ''' + 获取节假日列表 + :return: 8位int型日期 + ''' + client = get_client() + return [str(d) for d in client.get_holidays()] + + +def get_market_last_trade_date(market): + client = get_client() + return client.get_market_last_trade_date(market) + +def get_trading_calendar(market, start_time = '', end_time = ''): + ''' + 获取指定市场交易日历 + :param market: str 市场 + :param start_time: str 起始时间 '20200101' + :param end_time: str 结束时间 '20201231' + :return: + ''' + import datetime as dt + + if market not in ["SH", "SZ"]: + raise Exception("暂不支持除SH,SZ以外市场的交易日历") + + client = get_client() + + tdl = client.get_trading_dates_by_market(market, '', '', -1) + tdl = [dt.datetime.fromtimestamp(tt / 1000) for tt in tdl] + if not tdl: + raise Exception('交易日列表为空') + + download_holiday_data(incrementally=True) + hl = client.get_holidays() + if not hl: + raise Exception(f'节假日数据为空') + hl = [dt.datetime(hh // 10000, ((hh // 100) % 100), hh % 100, 0, 0) for hh in hl] + + if start_time: + ss = dt.datetime.strptime(start_time, '%Y%m%d') + ts = max(ss - dt.timedelta(days = 1), tdl[-1]) + else: + ss = tdl[0] + ts = tdl[-1] + + if end_time: + te = dt.datetime.strptime(end_time, '%Y%m%d') + else: + te = dt.datetime(hl[-1].year, 12, 31, 0, 0) + + if hl[-1].year < te.year: + raise Exception(f'end_time({end_time}) 超出现有节假日数据({hl[-1].year}1231)') + + hdset = set(hl) + + res = [tt for tt in tdl if tt >= ss] + tt = ts + dt.timedelta(days = 1) + while tt <= te: + if tt not in hdset and tt.weekday() < 5: + res.append(tt) + + tt += dt.timedelta(days = 1) + + return [tt.strftime('%Y%m%d') for tt in res] + + +def get_trading_time(stockcode): + ''' + 返回指定股票的交易时段 + :param stockcode: 代码.市场 例如 '600000.SH' + :return: 返回交易时段列表,第一位是开始时间,第二位结束时间,第三位交易类型 (2 - 开盘竞价, 3 - 连续交易, 8 - 收盘竞价, 9 - 盘后定价) + :note: 需要转换为datetime时,可以用以下方法转换 + import datetime as dt + dt.datetime.combine(dt.date.today(), dt.time()) + dt.timedelta(seconds = 34200) + ''' + cl = get_client() + + split_codes = stockcode.rsplit('.', 1) + if len(split_codes) == 2: + code = split_codes[0] + market = split_codes[1] + else: + return [] + + inst = _BSON_call_common( + cl.commonControl, 'gettradingtime', { + 'market': market + , 'code': code + } + ) + return inst.get('result', []) + +def is_stock_type(stock, tag): + client = get_client() + return client.is_stock_type(stock, tag) + +def download_cb_data(): + client = get_client() + return client.down_cb_data() + +def get_cb_info(stockcode): + client = get_client() + inst = client.get_cb_info(stockcode) + return _BSON_.BSON.decode(inst) + +def get_option_detail_data(optioncode): + inst = _get_instrument_detail(optioncode) + if not inst: + return None + + ret = {} + market = inst.get('ExchangeID') + if market == 'SHO' or market == "SZO" \ + or ((market == "CFFEX" or market == "IF") and inst.get('InstrumentID').find('-') >= 0) \ + or (market in ['SF', 'SHFE', 'DF', 'DCE', 'INE', 'GF', 'GFEX', 'ZF', 'CZCE'] and inst.get('ExtendInfo', {}).get('OptionType') in [0, 1]): + field_list = [ + 'ExchangeID' + , 'InstrumentID' + , 'InstrumentName' + , 'ProductID' + , 'ProductType' + , 'OpenDate' + , 'CreateDate' + , 'ExpireDate' + , 'PreClose' + , 'SettlementPrice' + , 'UpStopPrice' + , 'DownStopPrice' + , 'LongMarginRatio' + , 'ShortMarginRatio' + , 'PriceTick' + , 'VolumeMultiple' + , 'MaxMarketOrderVolume' + , 'MinMarketOrderVolume' + , 'MaxLimitOrderVolume' + , 'MinLimitOrderVolume' + ] + ret = {} + for field in field_list: + ret[field] = inst.get(field) + + exfield_list = [ + 'OptUnit' + , 'MarginUnit' + , 'OptUndlCode' + , 'OptUndlMarket' + , 'OptUndlCodeFull' + , 'OptExercisePrice' + , 'NeeqExeType' + , 'OptUndlRiskFreeRate' + , 'OptUndlHistoryRate' + , 'EndDelivDate' + ] + inst_ex = inst.get('ExtendInfo', {}) + for field in exfield_list: + ret[field] = inst_ex.get(field) + + def convNum2Str(field): + if field in ret and isinstance(ret[field], int): + ret[field] = str(ret[field]) + + convNum2Str('ExpireDate') + convNum2Str('CreateDate') + convNum2Str('OpenDate') + convNum2Str('EndDelivDate') + + if 1: + optType = '' + + if not optType: + instrumentName = inst.get('InstrumentName') + if '购' in instrumentName: + optType = 'CALL' + elif '沽' in instrumentName: + optType = 'PUT' + + if not optType: + OptionType = inst.get('ExtendInfo').get('OptionType') + if OptionType == 0: + optType = 'CALL' + elif OptionType == 1: + optType = 'PUT' + + ret['optType'] = optType + + ret['OptUndlCodeFull'] = ret['OptUndlCode'] + '.' + ret['OptUndlMarket'] + + ProductCode = ret['ProductID'] + if ProductCode.endswith('_o'): + ProductCode = ProductCode[:-2] + '.' + ret['OptUndlMarket'] + elif market in ['ZF', 'CZCE']: + ProductCode = ProductCode[:-1] + '.' + ret['OptUndlMarket'] + else: + ProductCode = ret['OptUndlCodeFull'] + ret['ProductCode'] = ProductCode + return ret + + +def get_option_undl_data(undl_code_ref): + def get_option_undl(opt_code): + inst = get_option_detail_data(opt_code) + if inst and 'OptUndlCode' in inst and 'OptUndlMarket' in inst: + return inst['OptUndlCode'] + '.' + inst['OptUndlMarket'] + return '' + + if undl_code_ref: + c_undl_code_ref = undl_code_ref + inst = get_instrument_detail(undl_code_ref) + if inst and 'UniCode' in inst: + marketcodeList = undl_code_ref.split('.') + if (len(marketcodeList) != 2): + return [] + c_undl_code_ref = inst['UniCode'] + '.' + marketcodeList[1] + + opt_list = [] + if undl_code_ref.endswith('.SH'): + if undl_code_ref == "000016.SH" or undl_code_ref == "000300.SH" or undl_code_ref == "000852.SH" or undl_code_ref == "000905.SH": + opt_list = get_stock_list_in_sector('中金所') + else: + opt_list = get_stock_list_in_sector('上证期权') + if undl_code_ref.endswith('.SZ'): + opt_list = get_stock_list_in_sector('深证期权') + if undl_code_ref.endswith('.SF') or undl_code_ref.endswith('.SHFE'): + opt_list = get_stock_list_in_sector('上期所期权') + if undl_code_ref.endswith('.ZF') or undl_code_ref.endswith('.CZCE'): + opt_list = get_stock_list_in_sector('郑商所期权') + if undl_code_ref.endswith('.DF') or undl_code_ref.endswith('.DCE'): + opt_list = get_stock_list_in_sector('大商所期权') + if undl_code_ref.endswith('.GF') or undl_code_ref.endswith('.GFEX'): + opt_list = get_stock_list_in_sector('广期所期权') + if undl_code_ref.endswith('.INE'): + opt_list = get_stock_list_in_sector('能源中心期权') + data = [] + for opt_code in opt_list: + undl_code = get_option_undl(opt_code) + if undl_code == c_undl_code_ref: + data.append(opt_code) + return data + else: + opt_list = [] + category_list = ['上证期权', '深证期权', '中金所', '上期所期权', '郑商所期权', '大商所期权', '广期所期权', '能源中心期权'] + for category in category_list: + one_list = get_stock_list_in_sector(category) + opt_list += one_list + + result = {} + for opt_code in opt_list: + undl_code = get_option_undl(opt_code) + if undl_code: + if undl_code in result: + result[undl_code].append(opt_code) + else: + result[undl_code] = [opt_code] + return result + + +def get_option_list(undl_code, dedate, opttype = "", isavailavle = False): + result = [] + + marketcodeList = undl_code.split('.') + if (len(marketcodeList) != 2): + return [] + undlCode = marketcodeList[0] + undlMarket = marketcodeList[1] + inst_data = get_instrument_detail(undl_code) + if inst_data and 'UniCode' in inst_data: + undlCode = inst_data['UniCode'] + market = "" + if (undlMarket == "SH"): + if undlCode == "000016" or undlCode == "000300" or undlCode == "000852" or undlCode == "000905": + market = 'IF' + else: + market = "SHO" + elif (undlMarket == "SZ"): + market = "SZO" + else: + market = undlMarket + if (opttype.upper() == "C"): + opttype = "CALL" + elif (opttype.upper() == "P"): + opttype = "PUT" + optList = [] + if market == 'SHO': + optList += get_stock_list_in_sector('上证期权') + optList += get_stock_list_in_sector('过期上证期权') + elif market == 'SZO': + optList += get_stock_list_in_sector('深证期权') + optList += get_stock_list_in_sector('过期深证期权') + elif market == 'IF': + optList += get_stock_list_in_sector('中金所') + optList += get_stock_list_in_sector('过期中金所') + elif market == 'SF' or market == 'SHFE': + optList += get_stock_list_in_sector('上期所期权') + optList += get_stock_list_in_sector('过期上期所') + elif market == 'ZF' or market == 'CZCE': + optList += get_stock_list_in_sector('郑商所期权') + optList += get_stock_list_in_sector('过期郑商所') + elif market == 'DF' or market == 'DCE': + optList += get_stock_list_in_sector('大商所期权') + optList += get_stock_list_in_sector('过期大商所') + elif market == 'GF' or market == 'GFEX': + optList += get_stock_list_in_sector('广期所期权') + optList += get_stock_list_in_sector('过期广期所') + elif market == 'INE': + optList += get_stock_list_in_sector('能源中心期权') + optList += get_stock_list_in_sector('过期能源中心') + for opt in optList: + if (opt.find(market) < 0): + continue + inst = get_option_detail_data(opt) + if not inst: + continue + if (opttype.upper() != "" and opttype.upper() != inst["optType"]): + continue + if ((len(dedate) == 6 and inst['ExpireDate'].find(dedate) < 0)): + continue + if (len(dedate) == 8): # option is trade,guosen demand + createDate = inst['CreateDate'] + openDate = inst['OpenDate'] + if (createDate > '0'): + openDate = min(openDate, createDate) + if (openDate < '20150101' or openDate > dedate): + continue + endDate = inst['ExpireDate'] + if (isavailavle and endDate < dedate): + continue + if inst['OptUndlCode'].find(undlCode) >= 0: + result.append(opt) + return result + + +def get_his_option_list(undl_code, dedate): + ''' + 获取历史上某日的指定品种期权信息列表 + :param undl_code: (str)标的代码,格式 stock.market e.g."000300.SH" + :param date: (str)日期 格式YYYYMMDD,e.g."20200427" + :return: dataframe + ''' + if not dedate: + return None + + data = get_his_option_list_batch(undl_code, dedate, dedate) + return data.get(dedate, None) + + +def get_his_option_list_batch(undl_code, start_time = '', end_time = ''): + ''' + 获取历史上某段时间的指定品种期权信息列表 + :param undl_code: (str)标的代码,格式 stock.market e.g."000300.SH" + :param start_time,start_time: (str)日期 格式YYYYMMDD,e.g."20200427" + :return: {date : dataframe} + ''' + split_codes = undl_code.rsplit('.', 1) + if len(split_codes) == 2: + stockcode = split_codes[0] + market = split_codes[1] + + optmarket = market + optcode = stockcode + product = stockcode + + isstockopt = False + + if market == 'SH': + if undl_code in ["000016.SH", "000300.SH", "000852.SH", "000905.SH"]: + optmarket = 'IF' + else: + optmarket = 'SHO' + optcode = 'XXXXXX' + isstockopt = True + elif market == 'SZ': + optmarket = 'SZO' + optcode = 'XXXXXX' + isstockopt = True + else: + detail = get_instrument_detail(undl_code) + if detail: + optcode = detail.get('ProductID', optcode) + product = optcode + + code = optcode + '.' + optmarket + + end_time1 = end_time + if end_time: + import datetime as dt + time_tag = int(dt.datetime.strptime(end_time, '%Y%m%d').timestamp() * 1000) + time_tag = time_tag + 31 * 86400000 + end_time1 = timetag_to_datetime(time_tag, '%Y%m%d') + + data_all = get_market_data_ex( + [] + , [code] + , period='optionhistorycontract' + , start_time = start_time + , end_time = end_time1 + ).get(code, None) + + if data_all.empty: + return {} + + if isstockopt: + select = f'''标的市场 == '{market}' and 标的编码 == '{stockcode}' ''' + data_all = data_all.loc[data_all.eval(select)].reset_index() + + data_all['期权完整代码'] = data_all['期权编码'] + '.' + data_all['期权市场'] + data_all['标的完整代码'] = data_all['标的编码'] + '.' + data_all['标的市场'] + data_all['期货品种'] = product + + date_list = get_trading_dates(optmarket, start_time, end_time) + + result = {} + min_opne_date = 0 + for timetag in date_list: + dedate = int(timetag_to_datetime(timetag, '%Y%m%d')) + + if dedate < min_opne_date: + continue + + data1 = data_all.loc[data_all['time'] >= timetag].reset_index() + if data1.empty: + continue + + data_time = data1.loc[0]['time'] + select = f'''time == {data_time} and 上市日 <= {dedate} and 到期日 >= {dedate} and 方向 != '' ''' + + data2 = data_all.loc[data_all.eval(select)].reset_index().drop(['index', 'time'], axis=1) + if data2.empty: + select = f'''time == {data_time} ''' + data3 = data_all.loc[data_all.eval(select)].reset_index() + min_opne_date = data3['上市日'].min() + else: + result[str(dedate)] = data2 + + return result + + +def get_ipo_info(start_time = '', end_time = ''): + client = get_client() + data = client.get_ipo_info(start_time, end_time) + pylist = [ + 'securityCode' #证券代码 + , 'codeName' #代码简称 + , 'market' #所属市场 + , 'actIssueQty' #发行总量 单位:股 + , 'onlineIssueQty' #网上发行量 单位:股 + , 'onlineSubCode' #申购代码 + , 'onlineSubMaxQty' #申购上限 单位:股 + , 'publishPrice' #发行价格 + , 'startDate' #申购开始日期 + , 'onlineSubMinQty' #最小申购数,单位:股 + , 'isProfit' #是否已盈利 0:上市时尚未盈利 1:上市时已盈利 + , 'industryPe' #行业市盈率 + , 'beforePE' #发行前市盈率 + , 'afterPE' #发行后市盈率 + , 'listedDate' #上市日期 + , 'declareDate' #中签号公布日期 + , 'paymentDate' #中签缴款日 + , 'lwr' #中签率 + ] + result = [] + for datadict in data: + resdict = {} + for field in pylist: + resdict[field] = datadict.get(field) + result.append(resdict) + return result + + +def get_markets(): + ''' + 获取所有可选的市场 + 返回 dict + { <市场代码>: <市场名称>, ... } + ''' + return { + 'SH': '上交所' + , 'SZ': '深交所' + , 'BJ': '北交所' + , 'HK': '港交所' + , 'HGT': '沪港通' + , 'SGT': '深港通' + , 'IF': '中金所' + , 'SF': '上期所' + , 'DF': '大商所' + , 'ZF': '郑商所' + , 'GF': '广期所' + , 'INE': '能源交易所' + , 'SHO': '上证期权' + , 'SZO': '深证期权' + , 'BKZS': '板块指数' + } + + +def get_wp_market_list(): + ''' + 获取所有外盘的市场 + 返回 list + ''' + return _BSON_call_common(get_client().commonControl, 'getwpmarketlist', {}) + + +def get_his_st_data(stock_code): + fileName = _OS_.path.join(get_data_dir(), '..', 'data', 'SH_XXXXXX_2011_86400000.csv') + + try: + with open(fileName, "r") as f: + datas = f.readlines() + except: + return {} + + status = [] + for data in datas: + cols = data.split(',') + if len(cols) >= 4 and cols[0] == stock_code: + status.append((cols[2], cols[3])) + + if not status: + return {} + + result = {} + i = 0 + while i < len(status): + start = status[i][0] + flag = status[i][1] + + i += 1 + + end = '20380119' + if i < len(status): + end = status[i][0] + + realStatus = '' + if (flag == '1'): + realStatus = 'ST' + elif (flag == '2'): + realStatus = '*ST' + elif (flag == '3'): + realStatus = 'PT' + else: + continue + + if realStatus not in result: + result[realStatus] = [] + result[realStatus].append([start, end]) + + return result + + +def subscribe_formula(formula_name, stock_code, period, start_time = '', end_time = '', count = -1, dividend_type = None, extend_param = {}, callback = None): + cl = get_client() + + result = _BSON_.BSON.decode(cl.commonControl('createrequestid', _BSON_.BSON.encode({}))) + request_id = result['result'] + + data = { + 'formulaname': formula_name, 'stockcode': stock_code, 'period': period + , 'starttime': start_time, 'endtime': end_time, 'count': count + , 'dividendtype': dividend_type if dividend_type else 'none' + , 'extendparam': extend_param + , 'create': True + } + + if callback: + callback = subscribe_callback_wrapper(callback) + + cl.subscribeFormula(request_id, _BSON_.BSON.encode(data), callback) + return request_id + + +def get_formula_result(request_id, start_time = '', end_time = '', count = -1): + cl = get_client() + res = _BSON_.BSON.decode( + cl.commonControl( + 'getformularesult' + , _BSON_.BSON.encode({ + 'requestid': request_id + , 'starttime': start_time + , 'endtime': end_time + , 'count': count + }) + ) + ) + return res + + +def bind_formula(request_id, callback = None): + cl = get_client() + + if callback: + callback = subscribe_callback_wrapper(callback) + + cl.subscribeFormula(request_id, _BSON_.BSON.encode({}), callback) + return + + +def unsubscribe_formula(request_id): + cl = get_client() + cl.unsubscribeFormula(request_id) + return + + +def call_formula( + formula_name, stock_code, period + , start_time = '', end_time = '', count = -1 + , dividend_type = None, extend_param = {} +): + cl = get_client() + + result = _BSON_.BSON.decode(cl.commonControl('createrequestid', _BSON_.BSON.encode({}))) + request_id = result['result'] + + data = { + 'formulaname': formula_name, 'stockcode': stock_code, 'period': period + , 'starttime': start_time, 'endtime': end_time, 'count': count + , 'dividendtype': dividend_type if dividend_type else 'none' + , 'extendparam': extend_param + , 'create': True + } + + data = cl.subscribeFormulaSync(request_id, _BSON_.BSON.encode(data)) + return _BSON_.BSON.decode(data) + +gmd = get_market_data +gmd2 = get_market_data_ex +gmd3 = get_market_data3 +gld = get_local_data +t2d = timetag_to_datetime +gsl = get_stock_list_in_sector + + +def reset_market_trading_day_list(market, datas): + cl = get_client() + + result = _BSON_call_common( + cl.custom_data_control, 'createmarketchange' + , { + 'market': market + } + ) + cid = result['cid'] + + result = _BSON_call_common( + cl.custom_data_control, 'addtradingdaytochange' + , { + 'cid': cid + , 'datas': datas + , 'coverall': True + } + ) + + result = _BSON_call_common( + cl.custom_data_control, 'finishmarketchange' + , { + 'cid': cid + #, 'abort': False + , 'notifyupdate': True + } + ) + return + + +def reset_market_stock_list(market, datas): + cl = get_client() + + result = _BSON_call_common( + cl.custom_data_control, 'createmarketchange' + , { + 'market': market + } + ) + cid = result['cid'] + + result = _BSON_call_common( + cl.custom_data_control, 'addstocktochange' + , { + 'cid': cid + , 'datas': datas + , 'coverall': True + } + ) + + result = _BSON_call_common( + cl.custom_data_control, 'finishmarketchange' + , { + 'cid': cid + #, 'abort': False + , 'notifyupdate': True + } + ) + return + + +def push_custom_data(meta, datas, coverall = False): + cl = get_client() + if period in ['1m', '5m', '15m', '30m', '60m', '1h', '2h', '3h', '4h', '1d']: + type = 3001 + + ans = [] + fields = get_field_name(type) + for data in datas: + params = {} + for k, v in data.items(): + if k in fields: + params[fields[k]] = v + ans.append(params) + + result = _BSON_call_common( + cl.custom_data_control, 'pushcustomdata' + , { + "meta": meta + , 'datas': datas + , 'coverall': coverall + } + ) + return + +def get_period_list(): + client = get_client() + result = _BSON_.BSON.decode(client.commonControl('getperiodlist', _BSON_.BSON.encode({}))) + p_list = result['result'] + + result = [] + for k, v in __TUPLE_PERIODS.items(): + if len(v) >= 3 and v[2]: + flag = True + for i in p_list: + if k == i.get('name', None): + flag = False + break + if flag: + result.append({'name': k, 'desc': v[2]}) + + result.extend(p_list) + return result + + +def gen_factor_index( + data_name, formula_name, vars, sector_list + , start_time = '', end_time = '', period = '1d' + , dividend_type = 'none' +): + ''' + 生成因子指数扩展数据 + ''' + running_info = { + 'result':{} + ,'finished':0 + ,'total':0 + } + def onPushProgress(data): + running_info['finished'] = data.get('finished',-1) + if running_info['finished'] == -1: + running_info['result'] = data + else: + running_info['total'] = data.get('total', -1) + + callback = subscribe_callback_wrapper(onPushProgress) + + cl = get_client() + cl.registerCommonControlCallback("genfactorindex", callback) + _BSON_call_common( + cl.commonControl + , 'genfactorindex' + , { + 'data_name': data_name + , 'formula_name': formula_name + , 'vars': vars + , 'sector_list': sector_list + , 'start_time': start_time + , 'end_time': end_time + , 'period': period + , 'dividend_type': dividend_type + } + ) + + last_finished = running_info['finished'] + time_count = 0 + while not running_info['result']: + _TIME_.sleep(1) + if last_finished != running_info['finished']: + last_finished = running_info['finished'] + time_count = 0 + else: + time_count += 1 + if time_count > 20: + time_count = 0 + if get_client(): + print(f'因子指数扩展数据生成进度长时间未更新,' + f'当前进度{running_info["finished"]}/{running_info["total"]}') + + return running_info['result'] + + +def create_formula(formula_name, formula_content, formula_params = {}): + ''' + 创建策略 + + formula_name: str 策略名称 + formula_content: str 策略内容 + formula_params: dict 策略参数 + + 返回: None + 如果成功,返回None + 如果失败,会抛出异常信息 + ''' + data = { + 'formula_name': formula_name + ,'content': formula_content + } + + if formula_params: + data.update(formula_params) + + return _BSON_call_common( + get_client().commonControl + , 'createformula' + , data + ) + + +def import_formula(formula_name, file_path): + ''' + 导入策略 + + formula_name: str 策略名称 + file_path: str 文件路径 + 一般为.rzrk文件,可以从qmt客户端导出得到 + ''' + return _BSON_call_common(get_client().commonControl + , 'importformula' + , {'formula_name': formula_name, 'file_path': file_path} + ) + + +def del_formula(formula_name): + ''' + 删除策略 + + formula_name: str 策略名称 + ''' + return _BSON_call_common(get_client().commonControl + , 'delformula' + , {'formula_name': formula_name} + ) + + +def get_formulas(): + ''' + 查询所有的策略 + ''' + return _BSON_call_common(get_client().commonControl, 'getformulas', {}) + + +def read_feather(file_path): + ''' + 读取feather格式的arrow文件 + :param file_path: (str) + :return: param_bin: (dict), df: (pandas.DataFrame) + ''' + import sys + if sys.version_info.major > 2: + from pyarrow import feather + if sys.version_info.minor > 6: + table = feather.read_table(source=file_path, columns=None, memory_map=True, use_threads=True) + else: + table = feather.read_table(source=file_path, columns=None, memory_map=True) + + metadata = table.schema.metadata + param_bin_bytes = metadata.get(b'param_bin') + #param_str_bytes = metadata.get(b'param_str') + + param_bin = _BSON_.BSON.decode(param_bin_bytes) + #param_str = param_str_bytes.decode('utf-8') + df = table.to_pandas(use_threads=True) + return param_bin, df + + return None, None + + +def write_feather(dest_path, param, df): + ''' + 将panads.DataFrame转换为arrow.Table以feather格式写入文件 + :param dest_path: (str)路径 + :param param: (dict) schema的metadata + :param df: (pandas.DataFrame) 数据 + :return: (bool) 成功/失败 + ''' + import json, sys + if sys.version_info.major > 2: + from pyarrow import feather, Schema, Table + schema = Schema.from_pandas(df).with_metadata({ + 'param_bin' : _BSON_.BSON.encode(param), + 'param_str' : json.dumps(param) + }) + table = Table.from_pandas(df, schema=schema) + feather.write_feather(table, dest_path) + return True + + return False + + +class QuoteServer: + def __init__(self, info = {}): + ''' + info: { + 'ip': '218.16.123.121' + , 'port': 55300 + , 'username': 'test' + , 'pwd': 'testpwd' + } + ''' + self.info = info + + ip = info.get('ip', None) + port = info.get('port', None) + + if not ip or not port: + raise f'invalid address, ip:{ip}, port:{port}' + return + + def _BSON_call_common(self, interface, func, param): + return _BSON_.BSON.decode(interface(func, _BSON_.BSON.encode(param))) + + def __str__(self): + return str(self.info) + + def connect(self): + ''' + 连接到这个地址 + ''' + cl = get_client() + + return self._BSON_call_common( + cl.commonControl, 'quoteserverconnect' + , { + 'ip': self.info['ip'] + , 'port': self.info['port'] + , 'info': self.info + , 'operation': 'login' + } + ) + + def disconnect(self): + ''' + 断开连接 + ''' + cl = get_client() + + result = self._BSON_call_common( + cl.commonControl, 'quoteserverconnect' + , { + 'ip': self.info['ip'] + , 'port': self.info['port'] + , 'info': self.info + , 'operation': 'logout' + } + ) + return + + def set_key(self, key_list = []): + ''' + 设置数据key到这个地址,后续会使用这个地址获取key对应的市场数据 + + key_list: [key, ...] + key: + f'{market}_{level}' + market: + SH, SZ, ... + level: + 'L1' # level 1 + 'L2' # level 2 + ''' + cl = get_client() + + result = self._BSON_call_common( + cl.commonControl, 'quoteserversetkey' + , { + 'ip': self.info['ip'] + , 'port': self.info['port'] + , 'info': self.info + , 'keys': key_list + } + ) + return + + def test_load(self): + ''' + 获取这个地址的负载情况 + ''' + cl = get_client() + + result = self._BSON_call_common( + cl.commonControl, 'testload' + , { + 'ip': self.info['ip'] + , 'port': self.info['port'] + , 'info': self.info + } + ) + return result + + def get_available_quote_key(self): + ''' + 获取这个地址可支持的数据key + ''' + cl = get_client() + + inst = self._BSON_call_common( + cl.commonControl, 'getavailablekey' + , { + 'ip': self.info['ip'] + , 'port': self.info['port'] + , 'info': self.info + } + ) + result = inst.get('result', []) + + return result + + def get_server_list(self): + ''' + 获取这个地址的服务器组列表 + ''' + cl = get_client() + + inst = self._BSON_call_common( + cl.commonControl, 'getserverlist' + , { + 'ip': self.info['ip'] + , 'port': self.info['port'] + , 'info': self.info + } + ) + + inst = inst.get('result', []) + + result = [QuoteServer(info) for info in inst] + return result + + +def get_quote_server_config(): + ''' + 获取连接配置 + + result: [info, ...] + ''' + cl = get_client() + + inst = _BSON_call_common( + cl.commonControl, 'getquoteserverconfig', {} + ) + inst = inst.get('result', []) + + result = [QuoteServer(info) for info in inst] + return result + + +def get_quote_server_status(): + ''' + 获取当前全局连接状态 + + result: { + quote_key: info + , ... + } + ''' + cl = get_client() + + inst = _BSON_call_common( + cl.commonControl, 'getquoteserverstatus', {} + ) + inst = inst.get('result', []) + + result = {} + for pair in inst: + key = pair.get('key', '') + info = pair.get('info', {}) + result[key] = QuoteServer(info) + return result + + +def watch_quote_server_status(callback): + ''' + 监控全局连接状态变化 + + def callback(info): + #info: {address : 'ip:port', status: ''} + #status: 'connected', 'disconnected' + return + ''' + cl = get_client() + + if callback: + callback = subscribe_callback_wrapper(callback) + + cl.registerCommonControlCallback("watchquoteserverstatus", callback) + _BSON_call_common(cl.commonControl, "watchquoteserverstatus", {}) + return + +def fetch_quote(root_path, key_list): + root_path = _OS_.path.abspath(root_path) + cl = get_client() + inst = _BSON_call_common( + cl.commonControl, 'fetchquote', { + 'dir': root_path + } + ) + + config_dir = _OS_.path.join(root_path, 'userdata_mini', 'users', 'xtquoterconfig.xml') + if not _OS_.path.isfile(config_dir): + return + + import xml.etree.ElementTree as ET + + tree = ET.parse(config_dir) + quoter_server_list = tree.find('QuoterServers') + quoter_server_list = quoter_server_list.findall('QuoterServer') + + qs_infos = {} + for server in quoter_server_list: + quoter_type = server.attrib['quotertype'] + if quoter_type != '0': + continue + + info = {'ip': server.attrib['address'], 'port': int(server.attrib['port']), 'username': server.attrib['username'], 'pwd': server.attrib['password']} + qs = QuoteServer(info) + relate_servers = qs.get_server_list() + for rs in relate_servers: + keys = rs.info.get('keys', []) + keys = ['0_' + key for key in keys if '0_' + key in key_list] + + if keys: + addr = (rs.info['ip'], rs.info['port']) + rs.info['keys'] = keys + qs_infos[addr] = rs + + servers = {} + for qs in qs_infos.values(): + qs.info.update(qs.test_load()) + for key in qs.info['keys']: + if key not in servers: + servers[key] = [] + servers[key].append(qs) + + for p in servers.items(): + p[1].sort(key = lambda x: x.info.get('delay', 1000000.0)) + + for key_servers in servers.items(): + for qs in key_servers[1]: + if qs.info['load'] != 1000000.0 and qs.info['delay'] != 1000000.0 and qs.info['accessible']: + qs.set_key([key_servers[0]]) + break + + return + + +def get_etf_info(): + period = _get_tuple_period('etfredemptionlist') + if not isinstance(period, tuple): + return {} + + all_data = _get_market_data_ex_tuple_period_ori(['XXXXXX.SH', 'XXXXXX.SZ'], period) + + metaid, periodNum = period + convert_field_list = get_field_list(metaid) + + result = {} + for stockcode, data_list in all_data.items(): + market = stockcode.split('.')[1] + + for data in data_list: + convert_data = {'market': market} + + if convert_field_list: + data = _convert_component_info(data, convert_field_list) + convert_data.update(data) + + stock_market = '' + if '基金代码' in data: + stock_market = data['基金代码'] + '.' + market + + convert_market = {'1': 'SH', '2': 'SZ', '3': 'HK', '4': 'BJ'} + if '成份股信息' in convert_data: + for sub_data in convert_data['成份股信息']: + if '成份股所属市场' in sub_data and '成份股代码' in sub_data and str(sub_data['成份股所属市场']) in convert_market: + sub_data['成份股代码'] = sub_data['成份股代码'] + '.' + convert_market[str(sub_data['成份股所属市场'])] + sub_data['成份股所属市场'] = convert_market[str(sub_data['成份股所属市场'])] + + if stock_market: + result[stock_market] = convert_data + + return result + + +def download_etf_info(): + for stock_code in ['XXXXXX.SH', 'XXXXXX.SZ']: + download_history_data(stock_code, 'etfredemptionlist', '', '') + + return + + +def download_his_st_data(): + ''' + 下载历史st数据 + ''' + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'downloadhisstdata', {} + ) + return result + + +def get_hk_broker_dict(): + global __hk_broke_info + + if not __hk_broke_info: + data = _get_market_data_ex_tuple_period_ori(['XXXXXX.HK'], (2038, 86401000), '', '') + + for a in data['XXXXXX.HK']: + list = a['1'] + name = a['0'] + for id in list: + __hk_broke_info[id] = name + + return __hk_broke_info + + +def _covert_hk_broke_data(ori_data = {}): + broke_dict = get_hk_broker_dict() + for s in ori_data: + sdata = ori_data[s] + for data in sdata: + for Broker in data['bidbrokerqueues']: + bidBrokerQueues = Broker['brokers'] + listbid = [] + for brokerid in bidBrokerQueues: + brokername = broke_dict.get(brokerid, '') + listbid.append((brokerid, brokername)) + Broker['brokers'] = listbid + + for Broker in data['askbrokerqueues']: + askBrokerQueues = Broker['brokers'] + listask = [] + for brokerid in askBrokerQueues: + brokername = broke_dict.get(brokerid, '') + listask.append((brokerid, brokername)) + Broker['brokers'] = listask + + return ori_data + + +def get_broker_queue_data(stock_list = [], start_time = '', end_time = '', count = -1, show_broker_name = False): + ori_data = get_market_data_ex_ori([], stock_list, 'hkbrokerqueue', start_time, end_time, count) + + if show_broker_name: + return _covert_hk_broke_data(ori_data) + return ori_data + + +def watch_xtquant_status(callback): + ''' + 监控xtquant连接状态变化 + + def callback(info): + #info: {address : 'ip:port', status: ''} + #status: 'connected', 'disconnected' + return + ''' + if callback: + callback = subscribe_callback_wrapper(callback) + + from . import xtconn + xtconn.status_callback = callback + return + + +def get_full_kline(field_list = [], stock_list = [], period = '1m' + , start_time = '', end_time = '', count = 1 + , dividend_type = 'none', fill_data = True): + ''' + k线全推获取最新交易日数据 + ''' + cl = get_client() + + all_data = _BSON_call_common( + cl.commonControl + , 'getfullkline' + , { + "stocklist": stock_list + , "period": period + , "starttime": start_time + , "endtime": end_time + , "count": count + , "dividendtype": dividend_type + , "fillData": fill_data + , "fields": field_list + } + ) + + import pandas as pd + data = all_data.get('result', {}) + index = all_data.get('stock', []) + column = all_data.get('stime', []) + + result = {} + for field in data: + result[field] = pd.DataFrame(data[field], index = index, columns = column) + return result + + +def generate_index_data( + formula_name, formula_param = {} + , stock_list = [], period = '1d', dividend_type = 'none' + , start_time = '', end_time = '' + , fill_mode = 'fixed', fill_value = float('nan') + , result_path = None +): + ''' + formula_name: + str 模型名称 + formula_param: + dict 模型参数 + 例如 {'param1': 1.0, 'param2': 'sym'} + stock_list: + list 股票列表 + period: + str 周期 + '1m' '5m' '1d' + dividend_type: + str 复权方式 + 'none' - 不复权 + 'front_ratio' - 等比前复权 + 'back_ratio' - 等比后复权 + start_time: + str 起始时间 '20240101' '20240101000000' + '' - '19700101' + end_time: + str 结束时间 '20241231' '20241231235959' + '' - '20380119' + fill_mode: + str 空缺填充方式 + 'fixed' - 固定值填充 + 'forward' - 向前延续 + fill_value: + float 填充数值 + float('nan') - 以NaN填充 + result_path: + str 结果文件路径,feather格式 + ''' + cl = get_client() + + result = _BSON_call_common(cl.commonControl, 'createrequestid', {}) + request_id = result['result'] + + result = _BSON_call_common( + cl.commonControl + , 'generateindexdata' + , { + 'requestid': request_id + , 'formulaname': formula_name + , 'formulaparam': formula_param + , 'stocklist': stock_list + , 'period': period + , 'dividendtype': dividend_type + , 'starttime': start_time + , 'endtime': end_time + , 'fillmode': fill_mode + , 'fillvalue': fill_value + , 'resultpath': result_path + } + ) + + taskid = result['taskid'] + + status = _BSON_call_common(cl.commonControl, 'querytaskstatus', {'taskid': taskid}) + + from tqdm import tqdm + + with tqdm(total = 1.0, dynamic_ncols = True) as pbar: + totalcount = 1.0 + finishedcount = 0.0 + + if not status.get('done', True): + import time + + while not status.get('done', True): + totalcount = status.get('totalcount', 1.0) + finishedcount = status.get('finishedcount', 0.0) + + pbar.total = totalcount + pbar.update(finishedcount - pbar.n) + + time.sleep(0.5) + status = _BSON_call_common(cl.commonControl, 'querytaskstatus', {'taskid': taskid}) + + pbar.update(totalcount - pbar.n) + + if status.get('errorcode', None): + raise Exception(status) + + return + + +def download_tabular_data(stock_list, period, start_time = '', end_time = '', incrementally = None, download_type = 'validationbypage', source = ''): + ''' + 下载表数据,可以按条数或按时间范围下载 + + stock_list: + list 股票列表 + period: + str 周期 + '1m' '5m' '1d' + start_time: + str 起始时间 '20240101' '20240101000000' + '' - '19700101' + end_time: + str 结束时间 '20241231' '20241231235959' + '' - '20380119' + incrementally: + bool 是否增量 + 'fixed' - 固定值填充 + 'forward' - 向前延续 + download_type: + str 下载类型 + 'bypage' - 按条数下载 + 'byregion' - 按时间范围下载 + 'validatebypage' - 数据校验按条数下载 + source: + str 指定下载地址 + ''' + if incrementally is None: + incrementally = False if start_time else True + + if isinstance(stock_list, str): + stock_list = [stock_list] + + if isinstance(period, tuple): + metaid, periodNum = period + periodstr = '' + else: + periodstr = period + metaid = -1 + periodNum = -1 + + param = { + 'stocklist': stock_list + , 'period': periodstr + , 'metaid': metaid + , 'periodNum': periodNum + , 'starttime' : start_time + , 'endtime' : end_time + , 'incrementally': incrementally + , 'type': download_type + , 'source': source + } + + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'downloadtabulardata', param + ) + + seq = result['seq'] + + + status = _BSON_call_common( + cl.commonControl, 'getdownloadworkprogress', {'seq' : seq} + ) + + from tqdm import tqdm + + with tqdm(total=1.0, dynamic_ncols=True) as pbar: + if not status.get('done', True): + import time + + while not status.get('done', True): + #print(status) + totalcount = status.get('totalcount', 1.0) + finishedcount = status.get('finishedcount', 0.0) + percentage = finishedcount / max(totalcount, 1.0) + + pbar.update(percentage - pbar.n) + + _TIME_.sleep(1) + status = _BSON_call_common( + cl.commonControl, 'getdownloadworkprogress', {'seq': seq} + ) + + pbar.update(1.0 - pbar.n) + + if status.get('errormsg', None): + raise Exception(status) + + return + +def get_trading_contract_list(stockcode, date = None): + ''' + 获取当前主力合约可交易标的列表 + + stockcode: + str, 合约代码,需要用主力合约 + date: + str, 查询日期, 8位日期格式,默认为最新交易日 + ''' + split_codes = stockcode.rsplit('.', 1) + if len(split_codes) == 2: + code = split_codes[0] + market = split_codes[1].upper() + else: + return [] + + if not date: + date = timetag_to_datetime(get_market_last_trade_date(market), "%Y%m%d") + date = int(date[:8]) + + result_set = set() + product_id = code.replace('00', '') + + stock_list = [] + + if market == 'IF' or market == 'CFFEX': + stock_list += get_stock_list_in_sector('中金所') + stock_list += get_stock_list_in_sector('过期中金所') + elif market == 'SF' or market == 'SHFE': + stock_list += get_stock_list_in_sector('上期所') + stock_list += get_stock_list_in_sector('过期上期所') + elif market == 'DF' or market == 'DCE': + stock_list += get_stock_list_in_sector('大商所') + stock_list += get_stock_list_in_sector('过期大商所') + elif market == 'ZF' or market == 'CZCE': + stock_list += get_stock_list_in_sector('郑商所') + stock_list += get_stock_list_in_sector('过期郑商所') + elif market == 'GF' or market == 'GFEX': + stock_list += get_stock_list_in_sector('广期所') + stock_list += get_stock_list_in_sector('过期广期所') + elif market == 'INE': + stock_list += get_stock_list_in_sector('能源中心') + stock_list += get_stock_list_in_sector('过期能源中心') + + for it in stock_list: + inst = get_instrument_detail(it) + if not inst: + continue + if inst['ProductID'] == product_id: + s_date = int(inst['OpenDate']) + e_date = inst['ExpireDate'] + if s_date and e_date and s_date <= date <= e_date: + result_set.add(inst['ExchangeCode'] + '.' + market) + + return list(result_set) + + +def get_trading_period(stock_code): + ''' + 获取合约最新交易时间段 + stock_code: 合约市场代码,例如:600000.SH + 返回值:dict + {market, codeRegex, product, category, tradings: [type, bartime:[dayoffset, start, end]]} + market:市场 + codeRegex:代码匹配规则 + product:产品类型 + category:证券分类 + codeRegex, product, category,三个规则,每次只有一个规则有数据。数据中*代表任意 + tradings, list: + type:交易类型(2盘前竞价,3连续交易,8尾盘竞价) + dayoffset:交易日偏移 + start, int:开始时间,时分秒 + end, int:结束时间,时分秒 + ''' + cl = get_client() + + result = _BSON_call_common( + cl.commonControl + , 'getopenclosetradetimebystock' + , { + "stockMarket" : stock_code + } + ) + + return result + + +def get_kline_trading_period(stock_code): + ''' + 与交易时间相似,区别在于把尾盘竞价和盘中交易合并 + ''' + cl = get_client() + + result = _BSON_call_common( + cl.commonControl + , 'getopencloseklinetimebystock' + , { + "stockMarket": stock_code + } + ) + + return result + + +def get_all_trading_periods(): + ''' + 获取全部市场划分出来的交易时间段 + ''' + cl = get_client() + + result = _BSON_call_common( + cl.commonControl + , 'getopenclosealltradetime' + , { + } + ) + + return result.get('result', []) + + +def get_all_kline_trading_periods(): + ''' + 获取全部市场划分出来的K线交易时间段 + ''' + cl = get_client() + + result = _BSON_call_common( + cl.commonControl + , 'getopencloseallklinetime' + , { + } + ) + + return result.get('result', []) + + +def get_authorized_market_list(): + ''' + 获取所有已授权的市场 + 返回 list + ''' + return _BSON_call_common(get_client().commonControl, 'getauthorizedmarketlist', {}).get('result', []) + + +from .metatable import * + diff --git a/src/xtquant/xtdata_config.py b/src/xtquant/xtdata_config.py new file mode 100644 index 0000000..dbe27ca --- /dev/null +++ b/src/xtquant/xtdata_config.py @@ -0,0 +1 @@ +client_guid = "" \ No newline at end of file diff --git a/src/xtquant/xtdatacenter.py b/src/xtquant/xtdatacenter.py new file mode 100644 index 0000000..ae785b6 --- /dev/null +++ b/src/xtquant/xtdatacenter.py @@ -0,0 +1,300 @@ +#coding:utf-8 + +import os as _OS_ + +from . import datacenter as __dc + +__all__ = [ + 'set_token', + 'set_data_home_dir', + 'init', + 'shutdown', + 'listen', + 'get_local_server_port', + 'register_create_nparray', + 'try_create_client', + 'RPCClient', +] + +### config + +__curdir = _OS_.path.dirname(_OS_.path.abspath(__file__)) + +__rpc_config_dir = _OS_.path.join(__curdir, 'config') +__rpc_config_file = _OS_.path.join(__curdir, 'xtdata.ini') +__rpc_init_status = __dc.rpc_init(__rpc_config_dir) +if __rpc_init_status < 0: + raise Exception(f'rpc init failed, error_code:{__rpc_init_status}, configdir:{__rpc_config_dir}') + +__config_dir = _OS_.path.join(__curdir, 'config') +__data_home_dir = 'data' + +__quote_token = '' + +init_complete = False + +### function +get_local_server_port = __dc.get_local_server_port +register_create_nparray = __dc.register_create_nparray +RPCClient = __dc.IPythonApiClient + +def try_create_client(): + ''' + 尝试创建RPCClient,如果失败,会抛出异常 + ''' + cl = RPCClient() + cl.init() + + ec = cl.load_config(__rpc_config_file, 'client_xtdata') + if ec < 0: + raise f'load config failed, file:{__rpc_config_file}' + return cl + + +def set_token(token = ''): + ''' + 设置用于登录行情服务的token,此接口应该先于init调用 + token获取地址:https://xuntou.net/#/userInfo?product=xtquant + 迅投投研服务平台 - 用户中心 - 个人设置 - 接口TOKEN + ''' + global __quote_token + __quote_token = token + return + + +def set_data_home_dir(data_home_dir): + ''' + 设置数据存储目录,此接口应该先于init调用 + datacenter启动后,会在data_home_dir目录下建立若干目录存储数据 + 如果不设置存储目录,会使用默认路径 + 在datacenter作为独立行情服务的场景下,data_home_dir可以任意设置 + 如果想使用现有数据,data_home_dir对应QMT的f'{安装目录}',或对应极简模式的f'{安装目录}/userdata_mini' + ''' + global __data_home_dir + __data_home_dir = data_home_dir + return + + +def set_config_dir(config_dir): + ''' + 设置配置文件目录,此接口应该先于init调用 + 通常情况配置文件内置,不需要调用这个接口 + ''' + global __config_dir + __config_dir = config_dir + return + + +def set_kline_mirror_enabled(enable): + ''' + 设置K线全推功能是否开启,此接口应该先于init调用 + 此功能默认关闭,启用后,实时K线数据将优先从K线全推获取 + 此功能仅vip用户可用 + ''' + __dc.set_kline_mirror_enabled(['SH', 'SZ'] if enable else []) + return + + +def set_kline_mirror_markets(markets): + ''' + 设置开启指定市场的K线全推,此接口应该先于init调用 + 此功能默认关闭,启用后,实时K线数据将优先从K线全推获取 + 此功能仅vip用户可用 + + markets: list, 市场列表 + 例如 ['SH', 'SZ', 'BJ'] 为开启上交所、深交所、北交所的K线全推 + ''' + __dc.set_kline_mirror_enabled(markets) + return + + +def set_allow_optmize_address(allow_list = []): + ''' + 设置连接池,行情仅从连接池内的地址中选择连接,此接口应该先于init调用 + 地址格式为'127.0.0.1:55300' + 设置为空时,行情从全部的可用地址中选择连接 + ''' + __dc.set_allow_optmize_address(allow_list) + return + + +def set_wholequote_market_list(market_list = []): + ''' + 设置启动时加载全推行情的市场,此接口应该先于init调用 + 未设置时启动时不加载全推行情 + 未加载全推行情的市场,会在实际使用数据的时候加载 + + markets: list, 市场列表 + 例如 ['SH', 'SZ', 'BJ'] 为启动时加载上交所、深交所、北交所的全推行情 + ''' + __dc.set_wholequote_market_list(market_list) + return + + +def set_future_realtime_mode(enable): + ''' + 设置期货周末夜盘是否使用实际时间,此接口应该先于init调用 + ''' + __dc.set_future_realtime_mode(enable) + return + + +def set_init_markets(markets = []): + ''' + 设置初始化的市场列表,仅加载列表市场的合约,此接口应该先于init调用 + + markets: list, 市场列表 + 例如 ['SH', 'SZ', 'BJ'] 为加载上交所、深交所、北交所的合约 + 传空list时,加载全部市场的合约 + + 未设置时,默认加载全部市场的合约 + ''' + __dc.set_watch_market_list(markets) + return + + +def set_index_mirror_enabled(enable): + ''' + 设置指标全推功能是否开启,此接口应该先于init调用 + 此功能默认关闭 + ''' + __dc.set_index_mirror_enabled(["SH", "SZ", "SHO", "SZO", "IF", "DF", "SF", "ZF", "GF", "INE"] if enable else []) + return + + +def set_index_mirror_markets(markets): + ''' + 设置开启指定市场的指标全推,此接口应该先于init调用 + 此功能默认关闭 + + markets: list, 市场列表 + 例如 ['SH', 'SZ', 'BJ'] 为开启上交所、深交所、北交所的指标全推 + ''' + __dc.set_index_mirror_enabled(markets) + return + + +def init(start_local_service = True): + ''' + 初始化行情模块 + start_local_service: bool + 如果start_local_service为True,会额外启动一个默认本地监听,以支持datacenter作为独立行情服务时的xtdata内置连接 + ''' + import time + + __dc.set_config_dir(__config_dir) + __dc.set_data_home_dir(__data_home_dir) + __dc.set_token(__quote_token) + __dc.log_init() + __dc.start_init_quote() + + status = __dc.get_status() + while not status.get('init_done', False): + status = __dc.get_status() + time.sleep(0.5) + + from . import xtbson as bson + + result = __dc.fetch_auth_markets() + + if result['done'] == 0: + status = bson.decode(__dc.fetch_server_list_status()) + + status_show = {} + + for info in status.values(): + srv_addr = info['loginparam']['ip'] + ':' + str(info['loginparam']['port']) + + if info['errorcode'] != 0: + status_show[srv_addr] = info['boerror'] + else: + status_show[srv_addr] = info['resultdesc'] + + raise ConnectionError(f'行情连接初始化异常, 当前状态:{status_show}') + + market_keys = result.get('markets', []) + ''' + market_keys = [ + 'SH', 'SZ' + , 'IF', 'SF', 'DF', 'ZF', 'GF', 'INE' + ] + ''' + + result = __dc.fetch_init_result([f'0_{mkt}_L1' for mkt in market_keys]) + + for mkt, boinfo in result.items(): + info = bson.decode(boinfo) + + if info['done'] == 1: + if info['errorcode'] != 0: + srv_addr = info['loginparam']['ip'] + ':' + str(info['loginparam']['port']) + error = info['boerror'] + + raise ConnectionError(f'行情连接初始化异常 {mkt} {srv_addr} {error}') + + if info['resultcode'] != 0: + srv_addr = info['loginparam']['ip'] + ':' + str(info['loginparam']['port']) + error = info['resultdesc'] + reason = info['reason'] + + raise ConnectionError(f'行情连接初始化异常 {mkt} {srv_addr} {error} {reason}') + else: + status = bson.decode(__dc.fetch_server_list_status()) + + status_show = {} + + for info in status.values(): + srv_addr = info['loginparam']['ip'] + ':' + str(info['loginparam']['port']) + + if info['errorcode'] != 0: + status_show[srv_addr] = info['boerror'] + else: + status_show[srv_addr] = info['resultdesc'] + + raise ConnectionError(f'行情连接初始化异常 {mkt}, 当前状态:{status_show}') + + global init_complete + init_complete = True + + if start_local_service: + listen('127.0.0.1', 58609) + return + + +def shutdown(): + ''' + 关闭行情模块,停止所有服务和监听端口 + ''' + __dc.shutdown() + return + + +def listen(ip = '0.0.0.0', port = 58610): + ''' + 独立行情服务模式,启动监听端口,支持xtdata.connect接入 + ip: + str, '0.0.0.0' + port: + int, 指定监听端口 + tuple, 指定监听端口范围,从port[0]至port[1]逐个尝试监听 + 返回: + (ip, port), 表示监听的结果 + 示例: + from xtquant import xtdatacenter as xtdc + ip, port = xtdc.listen('0.0.0.0', 58610) + ip, port = xtdc.listen('0.0.0.0', (58610, 58620)) + ''' + global init_complete + if not init_complete: + raise Exception("尚未初始化, 请优先调用init进行初始化") + + if isinstance(port, tuple): + port_start, port_end = port + result = __dc.listen(ip, port_start, port_end) + else: + result = __dc.listen(ip, port, port) + + if result[1] == 0: + raise OSError(f'监听端口失败: {port}, 请检查端口是否被占用') + + return result diff --git a/src/xtquant/xtextend.py b/src/xtquant/xtextend.py new file mode 100644 index 0000000..dac9a76 --- /dev/null +++ b/src/xtquant/xtextend.py @@ -0,0 +1,138 @@ + +class FileLock: + def __init__(this, path, auto_lock = False): + this.path = path + this.fhandle = None + if auto_lock: + this.lock() + return + + def is_lock(this): + import os + if os.path.exists(this.path): + try: + os.remove(this.path) + return False + except Exception as e: + return True + return False + + def lock(this): + if this.fhandle: + raise this.fhandle + try: + this.fhandle = open(this.path, 'w') + except Exception as e: + return False + return True + + def unlock(this): + if not this.fhandle: + raise this.fhandle + this.fhandle.close() + this.fhandle = None + return True + + def clean(this): + import os + if not os.path.exists(this.path): + return True + try: + if os.path.isfile(this.path): + os.remove(this.path) + return True + except Exception as e: + pass + return False + +class Extender: + from ctypes import c_float, c_short + value_type = c_float + rank_type = c_short + + def __init__(self, base_dir): + import os + self.base_dir = os.path.join(base_dir, 'EP') + + def read_config(self): + import json, os + data = None + with open(os.path.join(self.file, 'config'), 'r', encoding='utf-8') as f: + data = json.loads(f.read()) + + if data: + self.stocklist = [] + for i in range(1, len(data['stocklist']), 2): + for stock in data['stocklist'][i]: + self.stocklist.append("%s.%s" % (stock, data['stocklist'][i - 1])) + + self.timedatelist = data['tradedatelist'] + + def read_data(self, data, time_indexs, stock_length): + from ctypes import c_float, c_short, sizeof, cast, POINTER + res = {} + num = (sizeof(self.value_type) + sizeof(self.rank_type)) * stock_length + for time_index in time_indexs: + index = num * time_index + value_data = data[index: index + sizeof(self.value_type) * stock_length] + values = cast(value_data, POINTER(c_float)) + rank_data = data[index + sizeof(self.value_type) * stock_length: index + num] + ranks = cast(rank_data, POINTER(c_short)) + res[self.timedatelist[time_index]] = [(round(values[i], 3), ranks[i]) for i in range(stock_length)] + + return res + + def format_time(self, times): + import time + if type(times) == str: + return int(time.mktime(time.strptime(times, '%Y%m%d'))) * 1000 + elif type(times) == int: + if times < 0: + return self.timedatelist[times] + elif times < ((1 << 31) - 1): + return times * 1000 + else: + return times + + def show_extend_data(self, file, times): + import time, os + self.file = os.path.join(self.base_dir, file + '_Xdat') + if not os.path.isdir(self.file): + return "No such file" + + fs = FileLock(os.path.join(self.file, 'filelock'), False) + + while fs.is_lock(): + print('文件被占用') + time.sleep(1) + fs.lock() + + self.read_config() + + time_list = [] + + if not times: + time_list = self.timedatelist + elif type(times) == list: + time_list.extend([self.format_time(i) for i in times]) + else: + time_list.append(self.format_time(times)) + + + time_index = [self.timedatelist.index(time) for time in time_list if self.timedatelist.count(time) != 0] + + stock_length = len(self.stocklist) + data = None + with open(os.path.join(self.file, 'data'), 'rb') as f: + data = f.read() + fs.unlock() + res = self.read_data(data, time_index, stock_length) + return self.stocklist, res + + +def show_extend_data(file, times): + import os + from . import xtdata as xd + exd = Extender(os.path.join(xd.init_data_dir(), '..', 'datadir')) + + return exd.show_extend_data(file, times) diff --git a/src/xtquant/xtstocktype.py b/src/xtquant/xtstocktype.py new file mode 100644 index 0000000..19f8d8a --- /dev/null +++ b/src/xtquant/xtstocktype.py @@ -0,0 +1,373 @@ +XT_GE_BANK_LOAN= 10051 +XT_GE_BJ= 20002 +XT_GE_BOND_DISTRIBUTION= 100200 +XT_GE_DF_ARBITAGE_FTOPTION= 100056 +XT_GE_DF_FTOPTION= 100052 +XT_GE_EXTRA_300_ETF= 100101 +XT_GE_EXTRA_50_ETF= 100100 +XT_GE_EXTRA_ALLOW_PLEDGE= 100105 +XT_GE_EXTRA_BLOCK_TRADING= 100102 +XT_GE_EXTRA_CB_ETF= 100026 +XT_GE_EXTRA_CB_LOF= 100027 +XT_GE_EXTRA_CLOSED_ENDED_FUNDS= 100014 +XT_GE_EXTRA_DIRECTIONAL_CONVERTIBALE_BOND= 100104 +XT_GE_EXTRA_ETF= 100013 +XT_GE_EXTRA_FICC= 100090 +XT_GE_EXTRA_FI_ETF= 100034 +XT_GE_EXTRA_FUND= 100004 +XT_GE_EXTRA_GF= 100036 +XT_GE_EXTRA_GLR= 100020 +XT_GE_EXTRA_GOLD= 100022 +XT_GE_EXTRA_INTRA_DAY= 100030 +XT_GE_EXTRA_LOAN= 100016 +XT_GE_EXTRA_LOAN_ETF= 100038 +XT_GE_EXTRA_LOF= 100037 +XT_GE_EXTRA_MAIN_BOARD= 100029 +XT_GE_EXTRA_MARKET_CF= 100006 +XT_GE_EXTRA_MARKET_FU= 100007 +XT_GE_EXTRA_MARKET_ST= 100008 +XT_GE_EXTRA_MONETARY_FUND= 100024 +XT_GE_EXTRA_NOT_CLOSING_AUCTION_MATCH= 100070 +XT_GE_EXTRA_POLICY_JRZ= 100019 +XT_GE_EXTRA_PUBLIC_INFRASTRUCTURE_FUND= 100103 +XT_GE_EXTRA_RATE_BOND= 100080 +XT_GE_EXTRA_REPURCHASE_DAY_1= 100040 +XT_GE_EXTRA_REPURCHASE_DAY_14= 100045 +XT_GE_EXTRA_REPURCHASE_DAY_2= 100041 +XT_GE_EXTRA_REPURCHASE_DAY_28= 100046 +XT_GE_EXTRA_REPURCHASE_DAY_28_UPPER= 100047 +XT_GE_EXTRA_REPURCHASE_DAY_3= 100042 +XT_GE_EXTRA_REPURCHASE_DAY_4= 100043 +XT_GE_EXTRA_REPURCHASE_DAY_7= 100044 +XT_GE_EXTRA_REPURCHASE_IMPAWN= 100021 +XT_GE_EXTRA_RTMF= 100023 +XT_GE_EXTRA_SH_CORPORATE_LOAN= 100106 +XT_GE_EXTRA_SH_DISTRIBUTION= 100031 +XT_GE_EXTRA_SH_IPO= 100025 +XT_GE_EXTRA_SH_LOAN= 100039 +XT_GE_EXTRA_STANDARD_BOND= 100018 +XT_GE_EXTRA_STOCK= 100003 +XT_GE_EXTRA_STOCK_A= 100001 +XT_GE_EXTRA_STOCK_B= 100002 +XT_GE_EXTRA_STOCK_EX= 100012 +XT_GE_EXTRA_STOCK_INDEX= 100005 +XT_GE_EXTRA_STOCK_TRANABLE= 100028 +XT_GE_EXTRA_ST_FIX= 100035 +XT_GE_EXTRA_SZ_CGL= 100009 +XT_GE_EXTRA_SZ_CORPORATE_LOAN= 100107 +XT_GE_EXTRA_SZ_GL= 100010 +XT_GE_EXTRA_SZ_GLR= 100017 +XT_GE_EXTRA_SZ_LOAN= 100011 +XT_GE_EXTRA_WARRANT= 100015 +XT_GE_EXTRA_XGED= 100032 +XT_GE_IF_ARBITAGE_FTOPTION= 100057 +XT_GE_IF_FTOPTION= 100053 +XT_GE_INE_FTOPTION= 100058 +XT_GE_MARKET_DS= 5 +XT_GE_MARKET_NEW3BOARD= 9 +XT_GE_MARKET_NEW3BOARD_DELISTED= 20000 +XT_GE_MARKET_OF= 7 +XT_GE_MARKET_OP= 8 +XT_GE_MARKET_SH= 1 +XT_GE_MARKET_SQ= 4 +XT_GE_MARKET_SZ= 2 +XT_GE_MARKET_ZJ= 3 +XT_GE_MARKET_ZS= 6 +XT_GE_NEW3BOARD_PREFERED_SHARES_TRANSFER= 20001 +XT_GE_SF_ARBITAGE_FTOPTION= 100054 +XT_GE_SF_FIXED_INCOME_ETF= 155 +XT_GE_SF_FTOPTION= 100050 +XT_GE_SHARES_ALLOTMEN= 100033 +XT_GE_SH_300_ETF= 351 +XT_GE_SH_50_ETF= 350 +XT_GE_SH_A= 101 +XT_GE_SH_ABS= 113 +XT_GE_SH_ABS_TRANSFER= 300 +XT_GE_SH_ALLOW_PLEDGE_BOND= 359 +XT_GE_SH_ASS= 186 +XT_GE_SH_ASSET_BACKED_SECURITIES= 242 +XT_GE_SH_ASSET_SECURITIZATION= 132 +XT_GE_SH_B= 102 +XT_GE_SH_BONDS_FUNDS= 166 +XT_GE_SH_BOND_OFFER_REPURCHASE= 150 +XT_GE_SH_BOND_PROTOCOL_REPURCHASE= 215 +XT_GE_SH_BOND_RESALE= 214 +XT_GE_SH_BOND_TRIPARTITE_REPURCHASE= 216 +XT_GE_SH_CDR= 196 +XT_GE_SH_CDR_ALLOTMEN= 199 +XT_GE_SH_CDR_DISTRIBUTION= 197 +XT_GE_SH_CDR_SUBSCRIBE= 198 +XT_GE_SH_CENTRAL_GOVERNMENT_LOAN= 130 +XT_GE_SH_CLOSED_ENDED_FUNDS= 140 +XT_GE_SH_CONVERTIBALE_BOND= 135 +XT_GE_SH_CONVERTIBALE_BOND_RESALE= 239 +XT_GE_SH_CONVERTIBLE_BOND_STOCK= 213 +XT_GE_SH_CONVERTIBLE_BOUND_DISTRIBUTION= 164 +XT_GE_SH_CORPORATE_BOND= 115 +XT_GE_SH_CORPORATE_BOND_REPURCHASE= 145 +XT_GE_SH_CORPORATE_LOAN_PLEDGE= 114 +XT_GE_SH_COUPON_GOVERNMENT_LOAN_PLEDGE= 238 +XT_GE_SH_CPB= 138 +XT_GE_SH_CPB_LOAN= 176 +XT_GE_SH_CPB_SWAP= 360 +XT_GE_SH_CREDIT_ASSET_SUPPORTED= 133 +XT_GE_SH_CROSS_BORDER_ETF= 152 +XT_GE_SH_CROSS_BORDER_LOF= 153 +XT_GE_SH_CR_ETF= 244 +XT_GE_SH_CR_ETF_CROSS_MARKET= 250 +XT_GE_SH_CR_FUND_ETF_GOLD= 274 +XT_GE_SH_DESIGNATED_TRANSACTION= 234 +XT_GE_SH_DIRECTIONAL_CONVERTIBALE_BOND= 303 +XT_GE_SH_ENTERPRISE_BOND= 134 +XT_GE_SH_ENTERPRISE_LOAN_PLEDGE= 240 +XT_GE_SH_ENTERPROSE_SUPPORT_AUCTION_BOND= 299 +XT_GE_SH_ENTERPROSE_SUPPORT_BOND= 191 +XT_GE_SH_EPB_TRANSFER= 175 +XT_GE_SH_ETF= 105 +XT_GE_SH_EXCHANGEABLE_BOND_DISTRIBUTION= 233 +XT_GE_SH_EXCHANGEABLE_LOAN= 108 +XT_GE_SH_EXCHANGEABLE_LOAN_PLEDGE= 109 +XT_GE_SH_EXCHANGEABLE_LOAN_SWAP= 110 +XT_GE_SH_FINANCIAL_BONDS= 128 +XT_GE_SH_FUND= 103 +XT_GE_SH_FUND_ETF= 245 +XT_GE_SH_FUND_ETF_CROSS_BORDER= 255 +XT_GE_SH_FUND_ETF_CROSS_MARKET= 251 +XT_GE_SH_FUND_ETF_CR_LOAN= 264 +XT_GE_SH_FUND_IPO= 161 +XT_GE_SH_FUND_RAISING= 223 +XT_GE_SH_FUND_SUBSCRIPTION= 227 +XT_GE_SH_FUND_SUBSCRIPTION_DISTRIBUTION= 228 +XT_GE_SH_GF= 170 +XT_GE_SH_GOLD= 156 +XT_GE_SH_GOVERNMENT_BANK_FINANCE_LOAN_DISTRIBUTE_SALE= 361 +XT_GE_SH_GOVERNMENT_LOAN= 137 +XT_GE_SH_GOVERNMENT_LOAN_DISCOUNT= 129 +XT_GE_SH_GOVERNMENT_LOAN_DISCOUNT_REPURCHASE= 144 +XT_GE_SH_GOVERNMENT_LOAN_DISTRIBUTE_SALE= 146 +XT_GE_SH_GOVERNMENT_LOAN_FUTURES= 217 +XT_GE_SH_GOVERNMENT_LOAN_INTEREST_BEARING= 127 +XT_GE_SH_GOVERNMENT_LOAN_PLEDGE= 237 +XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE= 126 +XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_BUYOUT= 124 +XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_ENTERPRISE= 123 +XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_IMPAWN= 125 +XT_GE_SH_GOVERNMENT_LOAN_REPURCHASE_TRUSTEE_SHIP= 122 +XT_GE_SH_GSF= 185 +XT_GE_SH_INDEX= 104 +XT_GE_SH_INNOVATE_ALLOTMEN= 195 +XT_GE_SH_INNOVATE_DISTRIBUTION= 193 +XT_GE_SH_INNOVATION_CLOSED_ENDED_FUNDS= 154 +XT_GE_SH_INTEREST_RATE_GOVERNMENT_LOAN= 229 +XT_GE_SH_LOAN_CBB_SCB_NEW= 187 +XT_GE_SH_LOAN_CR_ETF= 200 +XT_GE_SH_LOAN_ETF= 174 +XT_GE_SH_LOAN_IPO= 160 +XT_GE_SH_LOAN_ISSUANCE_DISTRIBUTE_SALE= 232 +XT_GE_SH_LOAN_REPURCHASE_DAY_1= 177 +XT_GE_SH_LOAN_REPURCHASE_DAY_14= 182 +XT_GE_SH_LOAN_REPURCHASE_DAY_2= 178 +XT_GE_SH_LOAN_REPURCHASE_DAY_28= 183 +XT_GE_SH_LOAN_REPURCHASE_DAY_28_UPPER= 184 +XT_GE_SH_LOAN_REPURCHASE_DAY_3= 179 +XT_GE_SH_LOAN_REPURCHASE_DAY_4= 180 +XT_GE_SH_LOAN_REPURCHASE_DAY_7= 181 +XT_GE_SH_LOCAL_GOVERNMENT_LOAN= 136 +XT_GE_SH_LOCAL_GOVERNMENT_LOAN_DISTRIBUTE_SALE= 148 +XT_GE_SH_LOCAL_GOVERNMENT_LOAN_ONLINE_DISTRIBUTE_SALE= 362 +XT_GE_SH_LOCAL_GOVERNMENT_LOAN_REPURCHASE= 147 +XT_GE_SH_LOF= 169 +XT_GE_SH_LOW_CORPORATE_BOND= 241 +XT_GE_SH_MONETARY_FUND_SUBSCRIPTION= 275 +XT_GE_SH_MS_PRIVATE_PLACEMENT_BOND= 151 +XT_GE_SH_NEW_SHARES_DISTRIBUTION= 162 +XT_GE_SH_NON_FUND_ETF= 248 +XT_GE_SH_NON_FUND_ETF_CROSS_BORDER= 258 +XT_GE_SH_NON_FUND_ETF_CROSS_MARKET= 254 +XT_GE_SH_NON_FUND_ETF_CR_LOAN= 267 +XT_GE_SH_NON_FUND_ETF_LOAN= 262 +XT_GE_SH_NON_FUND_ETF_MONETARY_FUND= 271 +XT_GE_SH_NON_PUBLIC_CONVERTIBLE_BOND_STOCK= 235 +XT_GE_SH_NON_PUBLIC_CONVERTIBLE_CORPORATE_LOAN= 354 +XT_GE_SH_NON_PUBLIC_CORPORATE_LOAN= 298 +XT_GE_SH_NON_PUBLIC_PREFERED_SHARES_TRANSFER= 117 +XT_GE_SH_OLD_GOVERNMENT_LOAN= 143 +XT_GE_SH_ONLINE_VOTING= 225 +XT_GE_SH_ONLINE_VOTING_B= 226 +XT_GE_SH_ONLINE_VOTING_PASSWORD_SERVICE_B= 236 +XT_GE_SH_OPEN_END_FUND= 189 +XT_GE_SH_OPEN_END_FUND_CONVERSION= 222 +XT_GE_SH_OPEN_END_FUND_CROSS_MARKET= 220 +XT_GE_SH_OPEN_END_FUND_DIVIDEND= 221 +XT_GE_SH_OPEN_END_FUND_SUBSCRIPTION= 219 +XT_GE_SH_OPEN_GF_LOF= 218 +XT_GE_SH_OTHER_MF= 190 +XT_GE_SH_PLACING= 363 +XT_GE_SH_PLACING_FIRST_DISTRIBUTION= 163 +XT_GE_SH_PLEDGE= 142 +XT_GE_SH_POLICY_JRZ= 141 +XT_GE_SH_PRICE_GOVERNMENT_LOAN= 230 +XT_GE_SH_PRIVATELY_LOAN_TRANSFER= 111 +XT_GE_SH_PR_CB_ETF= 173 +XT_GE_SH_PUBLIC_CORPORATE_TRADE_LOAN= 352 +XT_GE_SH_PUBLIC_INFRASTRUCTURE_FUND= 302 +XT_GE_SH_PUBLIC_LOAN_DISTRIBUTE_SALE= 231 +XT_GE_SH_PUBLIC_LOAN_PLEDGE= 212 +XT_GE_SH_PUBLIC_PREFERED_SHARES= 116 +XT_GE_SH_PUBLIC_PREFERED_SHARES_PLACEMENTS= 119 +XT_GE_SH_PUBLIC_PREFERED_SHARES_SUBSCRIBE= 118 +XT_GE_SH_PUBLIC_PREFERED_SHARES_SUBSCRIBE_DISTRIBUTION= 121 +XT_GE_SH_PUBLIC_PREFERED_SHARES_SUBSCRIBE_PRICE= 120 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_1= 201 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_119= 208 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_14= 203 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_154= 209 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_182= 210 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_21= 292 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_210= 294 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_245= 295 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_273= 211 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_28= 204 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_301= 296 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_35= 293 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_357= 297 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_42= 205 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_63= 206 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_7= 202 +XT_GE_SH_QUOTATION_REPURCHASE_DAY_91= 207 +XT_GE_SH_RTMF= 157 +XT_GE_SH_SEO= 172 +XT_GE_SH_SEPERATION_BOND= 131 +XT_GE_SH_SEPERATION_BOND_REPURCHASE= 149 +XT_GE_SH_SHARES_ALLOTMEN= 167 +XT_GE_SH_SHARES_CONVERTIBLE_BOND= 168 +XT_GE_SH_SHORTTERM_CORPORATE_LOAN_TRANSFER= 112 +XT_GE_SH_STANDARD_BOND= 139 +XT_GE_SH_STOCK_IPO= 159 +XT_GE_SH_SUBSCRIBE= 107 +XT_GE_SH_SUBSCRIPTION_ETF= 246 +XT_GE_SH_SUBSCRIPTION_ETF_CROSS_BORDER= 256 +XT_GE_SH_SUBSCRIPTION_ETF_CROSS_MARKET= 252 +XT_GE_SH_SUBSCRIPTION_ETF_CR_LOAN= 265 +XT_GE_SH_SUBSCRIPTION_ETF_GOLD= 272 +XT_GE_SH_SUBSCRIPTION_ETF_LOAN= 260 +XT_GE_SH_SUBSCRIPTION_ETF_MONETARY_FUND= 269 +XT_GE_SH_SUBSCRIPTION_FUND_ETF_GOLD= 273 +XT_GE_SH_SUBSCRIPTION_PRICE= 165 +XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF= 247 +XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_CROSS_BORDER= 257 +XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_CROSS_MARKET= 253 +XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_CR_LOAN= 266 +XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_LOAN= 261 +XT_GE_SH_SUBSCRIPTION_REPAYMENT_ETF_MONETARY_FUND= 270 +XT_GE_SH_SUBSCRIPTION_TECH_BOARD= 278 +XT_GE_SH_TECH_BOARD= 277 +XT_GE_SH_TECH_BOARD_CDR= 288 +XT_GE_SH_TECH_BOARD_CDR_ALLOTMEN= 291 +XT_GE_SH_TECH_BOARD_CDR_DISTRIBUTION= 289 +XT_GE_SH_TECH_BOARD_CDR_SUBSCRIBE= 290 +XT_GE_SH_TECH_BOARD_CONVERTIBLE_BOND= 355 +XT_GE_SH_TECH_BOARD_CONVERTIBLE_BOND_IPO= 356 +XT_GE_SH_TECH_BOARD_CONVERTIBLE_BOND_IPO_DISTRIBUTION= 357 +XT_GE_SH_TECH_BOARD_ETF= 353 +XT_GE_SH_TECH_BOARD_ETF_ETFCODE= 364 +XT_GE_SH_TECH_BOARD_SHARES_CONVERTIBLE_BOND= 358 +XT_GE_SH_TENDER_OFFER= 224 +XT_GE_SH_TMF= 158 +XT_GE_SH_TMFR= 301 +XT_GE_SH_TRANSACTION_ETF= 243 +XT_GE_SH_TRANSACTION_ETF_CROSS_MARKET= 249 +XT_GE_SH_TRANSACTION_ETF_CR_LOAN= 263 +XT_GE_SH_TRANSACTION_ETF_LOAN= 259 +XT_GE_SH_TRANSACTION_ETF_MONETARY_FUND= 268 +XT_GE_SH_WARRANT= 106 +XT_GE_SH_WARRANT_CREATION_CANCEL= 276 +XT_GE_SH_XGED= 171 +XT_GE_SZ_300_ETF= 10071 +XT_GE_SZ_A= 10001 +XT_GE_SZ_ASS= 10027 +XT_GE_SZ_B= 10002 +XT_GE_SZ_CB= 10013 +XT_GE_SZ_CBB= 10019 +XT_GE_SZ_CB_ETF= 10029 +XT_GE_SZ_CB_LOF= 10030 +XT_GE_SZ_CDR_ALLOTMEN= 10056 +XT_GE_SZ_CEF= 10021 +XT_GE_SZ_CYB_CCB= 10033 +XT_GE_SZ_DIRECTIONAL_CONVERTIBALE_BOND= 10077 +XT_GE_SZ_EB= 10014 +XT_GE_SZ_ENTERPROSE_SUPPORT_BOND= 10055 +XT_GE_SZ_ETF= 10008 +XT_GE_SZ_ETF_CROSS_MARKET= 10079 +XT_GE_SZ_EXCHANGEABLE_LOAN= 10078 +XT_GE_SZ_FUND= 10003 +XT_GE_SZ_GEM_BORAD= 10006 +XT_GE_SZ_GEM_BORAD_DR= 10073 +XT_GE_SZ_GF= 10023 +XT_GE_SZ_GLD= 10012 +XT_GE_SZ_GLIB= 10011 +XT_GE_SZ_GLR= 10010 +XT_GE_SZ_GLRA= 10035 +XT_GE_SZ_GOLD= 10036 +XT_GE_SZ_GOV_ALLOW= 10052 +XT_GE_SZ_GSF= 10028 +XT_GE_SZ_ICEF= 10031 +XT_GE_SZ_INDEX= 10007 +XT_GE_SZ_INNOVATE_KZZ= 10053 +XT_GE_SZ_LGL= 10018 +XT_GE_SZ_LOAN_DIRECTIONAL= 10069 +XT_GE_SZ_LOAN_ETF= 10041 +XT_GE_SZ_LOAN_IPO= 10054 +XT_GE_SZ_LOAN_ISSUANCE_DISTRIBUTE_SALE= 10067 +XT_GE_SZ_LOAN_REITS= 10068 +XT_GE_SZ_LOAN_REPURCHASE_DAY_1= 10042 +XT_GE_SZ_LOAN_REPURCHASE_DAY_14= 10047 +XT_GE_SZ_LOAN_REPURCHASE_DAY_2= 10043 +XT_GE_SZ_LOAN_REPURCHASE_DAY_28= 10048 +XT_GE_SZ_LOAN_REPURCHASE_DAY_28_UPPER= 10049 +XT_GE_SZ_LOAN_REPURCHASE_DAY_3= 10044 +XT_GE_SZ_LOAN_REPURCHASE_DAY_4= 10045 +XT_GE_SZ_LOAN_REPURCHASE_DAY_7= 10046 +XT_GE_SZ_LOF= 10022 +XT_GE_SZ_MAIN_BOARD= 10004 +XT_GE_SZ_MAIN_SME_BORAD_DR= 10074 +XT_GE_SZ_MSP_PB= 10016 +XT_GE_SZ_NON_PUBLIC_PREFERED_SHARES_TRANSFER= 10066 +XT_GE_SZ_OLDSHARES_PREFERRED_CONVERTIBLE_BOND= 10072 +XT_GE_SZ_POB= 10050 +XT_GE_SZ_PUBLIC_INFRASTRUCTURE_FUND= 10076 +XT_GE_SZ_RTMF= 10037 +XT_GE_SZ_SA= 10040 +XT_GE_SZ_SB= 10015 +XT_GE_SZ_SCB_PB= 10024 +XT_GE_SZ_SC_SB= 10025 +XT_GE_SZ_SEO= 10039 +XT_GE_SZ_SFMP= 10017 +XT_GE_SZ_SHARES_CONVERTIBLE_BOND= 10075 +XT_GE_SZ_SME_BOARD= 10005 +XT_GE_SZ_SPB= 10026 +XT_GE_SZ_STANDAR_B= 10020 +XT_GE_SZ_WARRANT= 10009 +XT_GE_SZ_XGED= 10038 +XT_GE_SZ_ZB_CCB= 10032 +XT_GE_SZ_ZXB_CCB= 10034 +XT_GE_ZF_ARBITAGE_FTOPTION= 100055 +XT_GE_ZF_FTOPTION= 100051 +XT_PLEDGE_REPURCHASE_SH_DAY_1= 279 +XT_PLEDGE_REPURCHASE_SH_DAY_14= 281 +XT_PLEDGE_REPURCHASE_SH_DAY_180= 285 +XT_PLEDGE_REPURCHASE_SH_DAY_21= 282 +XT_PLEDGE_REPURCHASE_SH_DAY_270= 286 +XT_PLEDGE_REPURCHASE_SH_DAY_30= 283 +XT_PLEDGE_REPURCHASE_SH_DAY_365= 287 +XT_PLEDGE_REPURCHASE_SH_DAY_7= 280 +XT_PLEDGE_REPURCHASE_SH_DAY_90= 284 +XT_PLEDGE_REPURCHASE_SZ_DAY_1= 10057 +XT_PLEDGE_REPURCHASE_SZ_DAY_14= 10059 +XT_PLEDGE_REPURCHASE_SZ_DAY_180= 10063 +XT_PLEDGE_REPURCHASE_SZ_DAY_21= 10060 +XT_PLEDGE_REPURCHASE_SZ_DAY_270= 10064 +XT_PLEDGE_REPURCHASE_SZ_DAY_30= 10061 +XT_PLEDGE_REPURCHASE_SZ_DAY_365= 10065 +XT_PLEDGE_REPURCHASE_SZ_DAY_7= 10058 +XT_PLEDGE_REPURCHASE_SZ_DAY_90= 10062 diff --git a/src/xtquant/xttools.py b/src/xtquant/xttools.py new file mode 100644 index 0000000..6e559a9 --- /dev/null +++ b/src/xtquant/xttools.py @@ -0,0 +1,11 @@ +#coding:utf-8 + +def init_pyside2_path(): + try: + import os, PySide2 + dirname = os.path.dirname(PySide2.__file__) + plugin_path = os.path.join(dirname, 'plugins', 'platforms') + os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path + return True, None + except Exception as e: + return False, e diff --git a/src/xtquant/xttrader.py b/src/xtquant/xttrader.py new file mode 100644 index 0000000..db27699 --- /dev/null +++ b/src/xtquant/xttrader.py @@ -0,0 +1,1476 @@ +#coding=utf-8 + +from . import xtpythonclient as _XTQC_ +from . import xttype as _XTTYPE_ +from . import xtbson as bson +from . import xtconstant as _XTCONST_ + +def title(s = None): + import inspect + if not s: + s = inspect.stack()[1].function + print('-' * 33 + s + '-' * 33) + return + +def cp(s = None): + import inspect + st = inspect.stack() + pos = {'title':st[1].function, 'line':st[1].lineno} + print('-' * 33 + f'{pos}, {s}' + '-' * 33) + return + +# 交易回调类 +class XtQuantTraderCallback(object): + def on_connected(self): + """ + 连接成功推送 + """ + pass + + def on_disconnected(self): + """ + 连接断开推送 + """ + pass + + def on_account_status(self, status): + """ + :param status: XtAccountStatus对象 + :return: + """ + pass + + def on_stock_asset(self, asset): + """ + :param asset: XtAsset对象 + :return: + """ + pass + + def on_stock_order(self, order): + """ + :param order: XtOrder对象 + :return: + """ + pass + + def on_stock_trade(self, trade): + """ + :param trade: XtTrade对象 + :return: + """ + pass + + def on_stock_position(self, position): + """ + :param position: XtPosition对象 + :return: + """ + pass + + def on_order_error(self, order_error): + """ + :param order_error: XtOrderError 对象 + :return: + """ + pass + + def on_cancel_error(self, cancel_error): + """ + :param cancel_error:XtCancelError 对象 + :return: + """ + pass + + def on_order_stock_async_response(self, response): + """ + :param response: XtOrderResponse 对象 + :return: + """ + pass + + def on_cancel_order_stock_async_response(self, response): + """ + :param response: XtCancelOrderResponse 对象 + :return: + """ + pass + + def on_smt_appointment_async_response(self, response): + """ + :param response: XtSmtAppointmentResponse 对象 + :return: + """ + pass + +class XtQuantTrader(object): + def __init__(self, path, session, callback=None): + """ + :param path: mini版迅投极速交易客户端安装路径下,userdata文件夹具体路径 + :param session: 当前任务执行所属的会话id + :param callback: 回调方法 + """ + import asyncio + from threading import current_thread + + self.async_client = _XTQC_.XtQuantAsyncClient(path.encode('gb18030'), 'xtquant', session) + self.callback = callback + + self.connected = False + + self.loop = asyncio.new_event_loop() + if "MainThread" == current_thread().name: + self.oldloop = asyncio.get_event_loop() + asyncio.set_event_loop(self.loop) + self.cbs = {} + + self.executor = None + self.resp_executor = None + + self.relaxed_resp_order_enabled = False + self.relaxed_resp_executor = None + + self.queuing_order_seq = set() # 发起委托的seq,获取resp时移除 + self.handled_async_order_stock_order_id = set() # 已处理了返回的委托order_id + self.queuing_order_errors_byseq = {} # 队列中的委托失败信息,在对应委托尚未返回(检测seq或者order_id)时存入,等待回调error_callback + self.queuing_order_errors_byid = {} + + self.handled_async_cancel_order_stock_order_id = set() + self.handled_async_cancel_order_stock_order_sys_id = set() + self.queuing_cancel_errors_by_order_id = {} + self.queuing_cancel_errors_by_order_sys_id = {} + + + ######################### + #push + def on_common_push_callback_wrapper(argc, callback): + if argc == 0: + def on_push_data(): + self.executor.submit(callback) + return on_push_data + elif argc == 1: + def on_push_data(data): + self.executor.submit(callback, data) + return on_push_data + elif argc == 2: + def on_push_data(data1, data2): + self.executor.submit(callback, data1, data2) + return on_push_data + else: + return None + + #response + def on_common_resp_callback(seq, resp): + callback = self.cbs.pop(seq, None) + if callback: + self.resp_executor.submit(callback, resp) + return + + self.async_client.bindOnSubscribeRespCallback(on_common_resp_callback) + self.async_client.bindOnUnsubscribeRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryStockAssetCallback(on_common_resp_callback) + self.async_client.bindOnQueryStockOrdersCallback(on_common_resp_callback) + self.async_client.bindOnQueryStockTradesCallback(on_common_resp_callback) + self.async_client.bindOnQueryStockPositionsCallback(on_common_resp_callback) + self.async_client.bindOnQueryCreditDetailRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryStkCompactsRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryCreditSubjectsRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryCreditSloCodeRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryCreditAssureRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryNewPurchaseLimitCallback(on_common_resp_callback) + self.async_client.bindOnQueryIPODataCallback(on_common_resp_callback) + self.async_client.bindOnTransferRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryComFundRespCallback(on_common_resp_callback) + self.async_client.bindOnSmtQueryQuoterRespCallback(on_common_resp_callback) + self.async_client.bindOnSmtQueryOrderRespCallback(on_common_resp_callback) + self.async_client.bindOnSmtQueryCompactRespCallback(on_common_resp_callback) + self.async_client.bindOnQueryPositionStatisticsRespCallback(on_common_resp_callback) + self.async_client.bindOnExportDataRespCallback(on_common_resp_callback) + self.async_client.bindOnSyncTransactionFromExternalRespCallback(on_common_resp_callback) + + self.async_client.bindOnQueryAccountInfosCallback(on_common_resp_callback) + self.async_client.bindOnQueryAccountStatusCallback(on_common_resp_callback) + ######################### + + enable_push = 1 + + #order push + + def on_push_OrderStockAsyncResponse(seq, resp): + callback = self.cbs.pop(seq, None) + if callback: + resp = _XTTYPE_.XtOrderResponse(resp.m_strAccountID, resp.m_nOrderID, resp.m_strStrategyName, resp.m_strOrderRemark, resp.m_strErrorMsg, seq) + callback(resp) + self.queuing_order_seq.discard(seq) + e = self.queuing_order_errors_byseq.pop(seq, None) + if not e: + e = self.queuing_order_errors_byid.pop(resp.order_id, None) + if e is not None: + self.callback.on_order_error(e) + else: + self.handled_async_order_stock_order_id.add(resp.order_id) + return + + if enable_push: + self.async_client.bindOnOrderStockRespCallback(on_common_push_callback_wrapper(2, on_push_OrderStockAsyncResponse)) + + def on_push_CancelOrderStockAsyncResponse(seq, resp): + callback = self.cbs.pop(seq, None) + if callback: + resp = _XTTYPE_.XtCancelOrderResponse(resp.m_strAccountID, resp.m_nCancelResult, resp.m_nOrderID, resp.m_strOrderSysID, seq, resp.m_strErrorMsg) + callback(resp) + + if not resp.order_sysid: + e = self.queuing_cancel_errors_by_order_id.pop(resp.order_id, None) + if e is not None: + self.handled_async_cancel_order_stock_order_id.discard(resp.order_id) + self.callback.on_cancel_error(e) + else: + self.handled_async_cancel_order_stock_order_id.add(resp.order_id) + else: + e = self.queuing_cancel_errors_by_order_sys_id.pop(resp.order_sysid, None) + if e is not None: + self.handled_async_cancel_order_stock_order_sys_id.discard(resp.order_sysid) + self.callback.on_cancel_error(e) + else: + self.handled_async_cancel_order_stock_order_sys_id.add(resp.order_sysid) + return + + if enable_push: + self.async_client.bindOnCancelOrderStockRespCallback(on_common_push_callback_wrapper(2, on_push_CancelOrderStockAsyncResponse)) + + def on_push_disconnected(): + if self.callback: + self.callback.on_disconnected() + + if enable_push: + self.async_client.bindOnDisconnectedCallback(on_common_push_callback_wrapper(0, on_push_disconnected)) + + def on_push_AccountStatus(data): + data = _XTTYPE_.XtAccountStatus(data.m_strAccountID, data.m_nAccountType, data.m_nStatus) + self.callback.on_account_status(data) + + if enable_push: + self.async_client.bindOnUpdateAccountStatusCallback(on_common_push_callback_wrapper(1, on_push_AccountStatus)) + + def on_push_StockAsset(data): + self.callback.on_stock_asset(data) + + if enable_push: + self.async_client.bindOnStockAssetCallback(on_common_push_callback_wrapper(1, on_push_StockAsset)) + + def on_push_OrderStock(data): + self.callback.on_stock_order(data) + + if enable_push: + self.async_client.bindOnStockOrderCallback(on_common_push_callback_wrapper(1, on_push_OrderStock)) + + def on_push_StockTrade(data): + self.callback.on_stock_trade(data) + + if enable_push: + self.async_client.bindOnStockTradeCallback(on_common_push_callback_wrapper(1, on_push_StockTrade)) + + def on_push_StockPosition(data): + self.callback.on_stock_position(data) + + if enable_push: + self.async_client.bindOnStockPositionCallback(on_common_push_callback_wrapper(1, on_push_StockPosition)) + + def on_push_OrderError(data): + if data.seq not in self.queuing_order_seq or data.order_id in self.handled_async_order_stock_order_id: + self.handled_async_order_stock_order_id.discard(data.order_id) + self.callback.on_order_error(data) + else: + self.queuing_order_errors_byseq[data.seq] = data + self.queuing_order_errors_byid[data.order_id] = data + + if enable_push: + self.async_client.bindOnOrderErrorCallback(on_common_push_callback_wrapper(1, on_push_OrderError)) + + def on_push_CancelError(data): + if data.order_id in self.handled_async_cancel_order_stock_order_id: + self.handled_async_cancel_order_stock_order_id.discard(data.order_id) + self.callback.on_cancel_error(data) + elif data.order_sysid in self.handled_async_cancel_order_stock_order_sys_id: + self.handled_async_cancel_order_stock_order_sys_id.discard(data.order_sysid) + self.callback.on_cancel_error(data) + else: + self.queuing_cancel_errors_by_order_id[data.order_id] = data + self.queuing_cancel_errors_by_order_sys_id[data.order_sysid] = data + + if enable_push: + self.async_client.bindOnCancelErrorCallback(on_common_push_callback_wrapper(1, on_push_CancelError)) + + def on_push_SmtAppointmentAsyncResponse(seq, resp): + callback = self.cbs.pop(seq, None) + if callback: + resp = _XTTYPE_.XtSmtAppointmentResponse(seq, resp.m_bSuccess, resp.m_strMsg, resp.m_strApplyID) + callback(resp) + return + + if enable_push: + self.async_client.bindOnSmtAppointmentRespCallback(on_common_push_callback_wrapper(2, on_push_SmtAppointmentAsyncResponse)) + + ######################## + + def common_op_async_with_seq(self, seq, callable, callback): + self.cbs[seq] = callback + + def apply(func, *args): + return func(*args) + apply(*callable) + + return seq + + def set_timeout(self, timeout=0): + self.async_client.setTimeout(timeout) + + def common_op_sync_with_seq(self, seq, callable): + from concurrent.futures import Future + future = Future() + self.cbs[seq] = lambda resp:future.set_result(resp) + + def apply(func, *args): + return func(*args) + apply(*callable) + + return future.result() + + ######################### + + + def __del__(self): + import asyncio + from threading import current_thread + if "MainThread" == current_thread().name: + asyncio.set_event_loop(self.oldloop) + + def register_callback(self, callback): + self.callback = callback + + def start(self): + from concurrent.futures import ThreadPoolExecutor + self.async_client.init() + self.async_client.start() + self.executor = ThreadPoolExecutor(max_workers = 1) + self.relaxed_resp_executor = ThreadPoolExecutor(max_workers = 1) + self.resp_executor = self.relaxed_resp_executor if self.relaxed_resp_order_enabled else self.executor + return + + def stop(self): + self.async_client.stop() + self.loop.call_soon_threadsafe(self.loop.stop) + self.executor.shutdown(wait = True) + self.relaxed_resp_executor.shutdown(wait = True) + return + + def connect(self): + result = self.async_client.connect() + self.connected = result == 0 + return result + + def sleep(self, time): + import asyncio + async def sleep_coroutine(time): + await asyncio.sleep(time) + asyncio.run_coroutine_threadsafe(sleep_coroutine(time), self.loop).result() + + def run_forever(self): + import time + while True: + time.sleep(2) + return + + def set_relaxed_response_order_enabled(self, enabled): + self.relaxed_resp_order_enabled = enabled + self.resp_executor = self.relaxed_resp_executor if self.relaxed_resp_order_enabled else self.executor + return + + def subscribe(self, account): + req = _XTQC_.SubscribeReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.subscribeWithSeq, seq, req) + ) + + def unsubscribe(self, account): + req = _XTQC_.UnsubscribeReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.unsubscribeWithSeq, seq, req) + ) + + def order_stock_async(self, account, stock_code, order_type, order_volume, price_type, price, strategy_name='', + order_remark=''): + """ + :param account: 证券账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param order_type: 委托类型, 23:买, 24:卖 + :param order_volume: 委托数量, 股票以'股'为单位, 债券以'张'为单位 + :param price_type: 报价类型, 详见帮助手册 + :param price: 报价价格, 如果price_type为指定价, 那price为指定的价格, 否则填0 + :param strategy_name: 策略名称 + :param order_remark: 委托备注 + :return: 返回下单请求序号, 成功委托后的下单请求序号为大于0的正整数, 如果为-1表示委托失败 + """ + req = _XTQC_.OrderStockReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strStockCode = stock_code + req.m_nOrderType = order_type + req.m_nOrderVolume = int(order_volume) + req.m_nPriceType = price_type + req.m_dPrice = price + req.m_strStrategyName = strategy_name + req.m_strOrderRemark = order_remark + req.m_strOrderRemarkNew = order_remark + req.m_dOrderAmount = order_volume + req.m_strStockCode1 = stock_code + + seq = self.async_client.nextSeq() + self.queuing_order_seq.add(seq) + self.cbs[seq] = self.callback.on_order_stock_async_response + self.async_client.orderStockWithSeq(seq, req) + return seq + + def order_stock(self, account, stock_code, order_type, order_volume, price_type, price, strategy_name='', + order_remark=''): + """ + :param account: 证券账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param order_type: 委托类型, 23:买, 24:卖 + :param order_volume: 委托数量, 股票以'股'为单位, 债券以'张'为单位 + :param price_type: 报价类型, 详见帮助手册 + :param price: 报价价格, 如果price_type为指定价, 那price为指定的价格, 否则填0 + :param strategy_name: 策略名称 + :param order_remark: 委托备注 + :return: 返回下单请求序号, 成功委托后的下单请求序号为大于0的正整数, 如果为-1表示委托失败 + """ + req = _XTQC_.OrderStockReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strStockCode = stock_code + req.m_nOrderType = order_type + req.m_nOrderVolume = int(order_volume) + req.m_nPriceType = price_type + req.m_dPrice = price + req.m_strStrategyName = strategy_name + req.m_strOrderRemark = order_remark + req.m_strOrderRemarkNew = order_remark + req.m_dOrderAmount = order_volume + req.m_strStockCode1 = stock_code + + seq = self.async_client.nextSeq() + self.queuing_order_seq.add(seq) + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.orderStockWithSeq, seq, req) + ) + return resp.order_id + + def cancel_order_stock(self, account, order_id): + """ + :param account: 证券账号 + :param order_id: 委托编号, 报单时返回的编号 + :return: 返回撤单成功或者失败, 0:成功, -1:撤单失败 + """ + req = _XTQC_.CancelOrderStockReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_nOrderID = order_id + + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.cancelOrderStockWithSeq, seq, req) + ) + return resp.cancel_result + + def cancel_order_stock_async(self, account, order_id): + """ + :param account: 证券账号 + :param order_id: 委托编号, 报单时返回的编号 + :return: 返回撤单请求序号, 成功委托后的撤单请求序号为大于0的正整数, 如果为-1表示撤单失败 + """ + req = _XTQC_.CancelOrderStockReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_nOrderID = order_id + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_cancel_order_stock_async_response + self.async_client.cancelOrderStockWithSeq(seq, req) + return seq + + def cancel_order_stock_sysid(self, account, market, sysid): + """ + :param account:证券账号 + :param market: 交易市场 0:上海 1:深圳 + :param sysid: 柜台合同编号 + :return:返回撤单成功或者失败, 0:成功, -1:撤单失败 + """ + req = _XTQC_.CancelOrderStockReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + if isinstance(market, str): + req.m_strMarket = market + req.m_nMarket = _XTCONST_.MARKET_STR_TO_ENUM_MAPPING.get(market, -1) + else: + req.m_nMarket = market + req.m_strOrderSysID = sysid + + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.cancelOrderStockWithSeq, seq, req) + ) + return resp.cancel_result + + def cancel_order_stock_sysid_async(self, account, market, sysid): + """ + :param account:证券账号 + :param market: 交易市场 0:上海 1:深圳 + :param sysid: 柜台编号 + :return:返回撤单请求序号, 成功委托后的撤单请求序号为大于0的正整数, 如果为-1表示撤单失败 + """ + req = _XTQC_.CancelOrderStockReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + if isinstance(market, str): + req.m_strMarket = market + req.m_nMarket = _XTCONST_.MARKET_STR_TO_ENUM_MAPPING.get(market, -1) + else: + req.m_nMarket = market + req.m_strOrderSysID = sysid + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_cancel_order_stock_async_response + self.async_client.cancelOrderStockWithSeq(seq, req) + return seq + + def query_account_infos(self): + """ + :return: 返回账号列表 + """ + req = _XTQC_.QueryAccountInfosReq() + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryAccountInfosWithSeq, seq, req) + ) + + query_account_info = query_account_infos + + def query_account_infos_async(self, callback): + """ + :return: 返回账号列表 + """ + req = _XTQC_.QueryAccountInfosReq() + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryAccountInfosWithSeq, seq, req) + , callback + ) + + def query_account_status(self): + """ + :return: 返回账号状态 + """ + req = _XTQC_.QueryAccountStatusReq() + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryAccountStatusWithSeq, seq, req) + ) + + def query_account_status_async(self, callback): + """ + :return: 返回账号状态 + """ + req = _XTQC_.QueryAccountStatusReq() + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryAccountStatusWithSeq, seq, req) + , callback + ) + + def query_stock_asset(self, account): + """ + :param account: 证券账号 + :return: 返回当前证券账号的资产数据 + """ + req = _XTQC_.QueryStockAssetReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.queryStockAssetWithSeq, seq, req) + ) + + if resp and len(resp): + return resp[0] + return None + + def query_stock_asset_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回当前证券账号的资产数据 + """ + req = _XTQC_.QueryStockAssetReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + def _cb(resp): + callback(resp[0] if resp else None) + resp = self.common_op_async_with_seq( + seq, + (self.async_client.queryStockAssetWithSeq, seq, req) + , _cb + ) + return + + def query_stock_order(self, account, order_id): + """ + :param account: 证券账号 + :param order_id: 订单编号,同步报单接口返回的编号 + :return: 返回订单编号对应的委托对象 + """ + req = _XTQC_.QueryStockOrdersReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_nOrderID = order_id + + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.queryStockOrdersWithSeq, seq, req) + ) + if resp and len(resp): + return resp[0] + return None + + def query_stock_orders(self, account, cancelable_only = False): + """ + :param account: 证券账号 + :param cancelable_only: 仅查询可撤委托 + :return: 返回当日所有委托的委托对象组成的list + """ + req = _XTQC_.QueryStockOrdersReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_bCanCancel = cancelable_only + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryStockOrdersWithSeq, seq, req) + ) + + def query_stock_orders_async(self, account, callback, cancelable_only = False): + """ + :param account: 证券账号 + :param cancelable_only: 仅查询可撤委托 + :return: 返回当日所有委托的委托对象组成的list + """ + req = _XTQC_.QueryStockOrdersReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_bCanCancel = cancelable_only + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryStockOrdersWithSeq, seq, req) + , callback + ) + + def query_stock_trades(self, account): + """ + :param account: 证券账号 + :return: 返回当日所有成交的成交对象组成的list + """ + req = _XTQC_.QueryStockTradesReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryStockTradesWithSeq, seq, req) + ) + + def query_stock_trades_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回当日所有成交的成交对象组成的list + """ + req = _XTQC_.QueryStockTradesReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryStockTradesWithSeq, seq, req) + , callback + ) + + def query_stock_position(self, account, stock_code): + """ + :param account: 证券账号 + :param stock_code: 证券代码, 例如"600000.SH" + :return: 返回证券代码对应的持仓对象 + """ + req = _XTQC_.QueryStockPositionsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strStockCode = stock_code + req.m_strStockCode1 = stock_code + + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.queryStockPositionsWithSeq, seq, req) + ) + if resp and len(resp): + return resp[0] + return None + + def query_stock_positions(self, account): + """ + :param account: 证券账号 + :return: 返回当日所有持仓的持仓对象组成的list + """ + req = _XTQC_.QueryStockPositionsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryStockPositionsWithSeq, seq, req) + ) + + def query_stock_positions_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回当日所有持仓的持仓对象组成的list + """ + req = _XTQC_.QueryStockPositionsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq + , (self.async_client.queryStockPositionsWithSeq, seq, req) + , callback + ) + + def query_credit_detail(self, account): + """ + :param account: 证券账号 + :return: 返回当前证券账号的资产数据 + """ + req = _XTQC_.QueryCreditDetailReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryCreditDetailWithSeq, seq, req) + ) + + def query_credit_detail_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回当前证券账号的资产数据 + """ + req = _XTQC_.QueryCreditDetailReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryCreditDetailWithSeq, seq, req) + , callback + ) + + def query_stk_compacts(self, account): + """ + :param account: 证券账号 + :return: 返回负债合约 + """ + req = _XTQC_.QueryStkCompactsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryStkCompactsWithSeq, seq, req) + ) + + def query_stk_compacts_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回负债合约 + """ + req = _XTQC_.QueryStkCompactsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryStkCompactsWithSeq, seq, req) + , callback + ) + + def query_credit_subjects(self, account): + """ + :param account: 证券账号 + :return: 返回融资融券标的 + """ + req = _XTQC_.QueryCreditSubjectsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryCreditSubjectsWithSeq, seq, req) + ) + + def query_credit_subjects_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回融资融券标的 + """ + req = _XTQC_.QueryCreditSubjectsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryCreditSubjectsWithSeq, seq, req) + , callback + ) + + def query_credit_slo_code(self, account): + """ + :param account: 证券账号 + :return: 返回可融券数据 + """ + req = _XTQC_.QueryCreditSloCodeReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryCreditSloCodeWithSeq, seq, req) + ) + + def query_credit_slo_code_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回可融券数据 + """ + req = _XTQC_.QueryCreditSloCodeReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryCreditSloCodeWithSeq, seq, req) + , callback + ) + + def query_credit_assure(self, account): + """ + :param account: 证券账号 + :return: 返回标的担保品 + """ + req = _XTQC_.QueryCreditAssureReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryCreditAssureWithSeq, seq, req) + ) + + def query_credit_assure_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回标的担保品 + """ + req = _XTQC_.QueryCreditAssureReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryCreditAssureWithSeq, seq, req) + , callback + ) + + def query_new_purchase_limit(self, account): + """ + :param account: 证券账号 + :return: 返回账户新股申购额度数据 + """ + req = _XTQC_.QueryNewPurchaseLimitReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + new_purchase_limit_list = self.common_op_sync_with_seq( + seq, + (self.async_client.queryNewPurchaseLimitWithSeq, seq, req) + ) + new_purchase_limit_result = dict() + for item in new_purchase_limit_list: + new_purchase_limit_result[item.m_strNewPurchaseLimitKey] = item.m_nNewPurchaseLimitValue + return new_purchase_limit_result + + def query_new_purchase_limit_async(self, account, callback): + """ + :param account: 证券账号 + :return: 返回账户新股申购额度数据 + """ + req = _XTQC_.QueryNewPurchaseLimitReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryNewPurchaseLimitWithSeq, seq, req) + , callback + ) + + def query_ipo_data(self): + """ + :return: 返回新股新债信息 + """ + req = _XTQC_.QueryIPODataReq() + req.m_strIPOType = '' + + seq = self.async_client.nextSeq() + ipo_data_list = self.common_op_sync_with_seq( + seq, + (self.async_client.queryIPODataWithSeq, seq, req) + ) + ipo_data_result = dict() + for item in ipo_data_list: + ipo_data_result[item.m_strIPOCode] = { + 'name': item.m_strIPOName, + 'type': item.m_strIPOType, + 'maxPurchaseNum': item.m_nMaxPurchaseNum, + 'minPurchaseNum': item.m_nMinPurchaseNum, + 'purchaseDate': item.m_strPurchaseDate, + 'issuePrice': item.m_dIssuePrice, + } + return ipo_data_result + + def query_ipo_data_async(self, callback): + """ + :return: 返回新股新债信息 + """ + req = _XTQC_.QueryIPODataReq() + req.m_strIPOType = '' + + seq = self.async_client.nextSeq() + return self.common_op_async_with_seq( + seq, + (self.async_client.queryIPODataWithSeq, seq, req) + , callback + ) + + def fund_transfer(self, account, transfer_direction, price): + """ + :param account: 证券账号 + :param transfer_direction: 划拨方向 + :param price: 划拨金额 + :return: 返回划拨操作结果 + """ + req = _XTQC_.TransferParam() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_nOrderType = transfer_direction + req.m_dPrice = price + + seq = self.async_client.nextSeq() + transfer_result = self.common_op_sync_with_seq( + seq, + (self.async_client.transferWithSeq, seq, req) + ) + return transfer_result.m_bSuccess, transfer_result.m_strMsg + + def secu_transfer(self, account, transfer_direction, stock_code, volume, transfer_type): + """ + :param account: 证券账号 + :param transfer_direction: 划拨方向 + :param stock_code: 证券代码, 例如"SH600000" + :param volume: 划拨数量, 股票以'股'为单位, 债券以'张'为单位 + :param transfer_type: 划拨类型 + :return: 返回划拨操作结果 + """ + req = _XTQC_.TransferParam() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_nOrderType = transfer_direction + req.m_strStockCode = stock_code + req.m_nOrderVolume = volume + req.m_nCreditTransferType = transfer_type + req.m_strStockCode1 = stock_code + + seq = self.async_client.nextSeq() + transfer_result = self.common_op_sync_with_seq( + seq, + (self.async_client.transferWithSeq, seq, req) + ) + return transfer_result.m_bSuccess, transfer_result.m_strMsg + + def query_com_fund(self, account): + """ + :param account: 证券账号 + :return: 返回普通柜台资金信息 + """ + req = _XTQC_.QueryComFundReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + fund_list = self.common_op_sync_with_seq( + seq, + (self.async_client.queryComFundWithSeq, seq, req) + ) + result = dict() + if fund_list[0]: + result = { + 'success': fund_list[0].m_bSuccess, + 'error': fund_list[0].m_strMsg, + 'currentBalance': fund_list[0].m_dCurrentBalance, + 'enableBalance': fund_list[0].m_dEnableBalance, + 'fetchBalance': fund_list[0].m_dFetchBalance, + 'interest': fund_list[0].m_dInterest, + 'assetBalance': fund_list[0].m_dAssetBalance, + 'fetchCash': fund_list[0].m_dFetchCash, + 'marketValue': fund_list[0].m_dMarketValue, + 'debt': fund_list[0].m_dDebt, + } + return result + + def query_com_position(self, account): + """ + :param account: 证券账号 + :return: 返回普通柜台持仓信息 + """ + req = _XTQC_.QueryComPositionReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + position_list = self.common_op_sync_with_seq( + seq, + (self.async_client.queryComPositionWithSeq, seq, req) + ) + result = list() + for item in position_list: + result.append( + { + 'success': item.m_bSuccess, + 'error': item.m_strMsg, + 'stockAccount': item.m_strAccountID, + 'exchangeType': item.m_strExchangeType, + 'stockCode': item.m_strStockCode, + 'stockName': item.m_strStockName, + 'totalAmt': item.m_dTotalAmt, + 'enableAmount': item.m_dEnableAmount, + 'lastPrice': item.m_dLastPrice, + 'costPrice': item.m_dCostPrice, + 'income': item.m_dIncome, + 'incomeRate': item.m_dIncomeRate, + 'marketValue': item.m_dMarketValue, + 'costBalance': item.m_dCostBalance, + 'bsOnTheWayVol': item.m_nBsOnTheWayVol, + 'prEnableVol': item.m_nPrEnableVol, + 'stockCode1': item.m_strStockCode1, + }) + return result + + def smt_query_quoter(self, account): + """ + :param account: 证券账号 + :return: 返回券源行情信息 + """ + req = _XTQC_.SmtQueryQuoterReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + quoter_list = self.common_op_sync_with_seq( + seq, + (self.async_client.smtQueryQuoterWithSeq, seq, req) + ) + result = list() + for item in quoter_list: + result.append( + { + 'success': item.m_bSuccess, + 'error': item.m_strMsg, + 'finType': item.m_strFinType, + 'stockType': item.m_strStockType, + 'date': item.m_nDate, + 'code': item.m_strCode, + 'codeName': item.m_strCodeName, + 'exchangeType': item.m_strExchangeType, + 'fsmpOccupedRate': item.m_dFsmpOccupedRate, + 'fineRate': item.m_dFineRate, + 'fsmpreendRate': item.m_dFsmpreendRate, + 'usedRate': item.m_dUsedRate, + 'unUusedRate': item.m_dUnUusedRate, + 'initDate': item.m_nInitDate, + 'endDate': item.m_nEndDate, + 'enableSloAmountT0': item.m_dEnableSloAmountT0, + 'enableSloAmountT3': item.m_dEnableSloAmountT3, + 'srcGroupId': item.m_strSrcGroupID, + 'applyMode': item.m_strApplyMode, + 'lowDate': item.m_nLowDate + }) + return result + + def smt_negotiate_order_async(self, account, src_group_id, order_code, date, amount, apply_rate, dict_param={}): + """ + :param account: 证券账号 + :param src_group_id: 来源组编号 + :param order_code: 证券代码,如'600000.SH' + :param date: 期限天数 + :param amount: 委托数量 + :param apply_rate: 资券申请利率 + :return: 返回约券请求序号, 成功请求后的序号为大于0的正整数, 如果为-1表示请求失败 + 注: + 目前有如下参数通过一个可缺省的字典传递,键名与参数名称相同 + subFareRate: 提前归还利率 + fineRate: 罚息利率 + """ + req = _XTQC_.SmtNegotiateOrderReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strSrcGroupID = src_group_id + req.m_strOrderCode = order_code + req.m_nDate = date + req.m_nAmount = amount + req.m_dApplyRate = apply_rate + + if 'subFareRate' in dict_param: + req.m_dSubFareRate = dict_param['subFareRate'] + if 'fineRate' in dict_param: + req.m_dFineRate = dict_param['fineRate'] + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_smt_appointment_async_response + self.async_client.smtNegotiateOrderWithSeq(seq, req) + return seq + + def smt_appointment_order_async(self, account, order_code, date, amount, apply_rate): + """ + :param account: 证券账号 + :param order_code: 证券代码,如'600000.SH' + :param date: 期限天数 + :param amount: 委托数量 + :param apply_rate: 资券申请利率 + :return: 返回约券请求序号, 成功请求后的序号为大于0的正整数, 如果为-1表示请求失败 + """ + req = _XTQC_.SmtAppointmentOrderReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strOrderCode = order_code + req.m_nDate = date + req.m_nAmount = amount + req.m_dApplyRate = apply_rate + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_smt_appointment_async_response + self.async_client.smtAppointmentOrderWithSeq(seq, req) + return seq + + def smt_appointment_cancel_async(self, account, apply_id): + """ + :param account: 证券账号 + :param apply_id: 资券申请编号 + :return: 返回约券撤单请求序号, 成功请求后的序号为大于0的正整数, 如果为-1表示请求失败 + """ + req = _XTQC_.SmtAppointmentCancelReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strApplyId = applyId + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_smt_appointment_async_response + self.async_client.smtAppointmentCancelWithSeq(seq, req) + return seq + + def smt_query_order(self, account): + """ + :param account: 证券账号 + :return: 返回券源行情信息 + """ + req = _XTQC_.SmtQueryOrderReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + order_list = self.common_op_sync_with_seq( + seq, + (self.async_client.smtQueryOrderWithSeq, seq, req) + ) + result = list() + for item in order_list: + result.append( + { + 'success': item.m_bSuccess, + 'error': item.m_strMsg, + 'initDate': item.m_nInitDate, + 'currDate': item.m_nCurrDate, + 'currTime': item.m_nCurrTime, + 'applyId': item.m_strApplyID, + 'srcGroupId': item.m_strSrcGroupID, + 'cashcompactId': item.m_strCashcompactID, + 'applyMode': item.m_strApplyMode, + 'finType': item.m_strFinType, + 'exchangeType': item.m_strExchangeType, + 'code': item.m_strCode, + 'codeName': item.m_strCodeName, + 'date': item.m_nDate, + 'applyRate': item.m_dApplyRate, + 'entrustBalance': item.m_dEntrustBalance, + 'entrustAmount': item.m_dEntrustAmount, + 'businessBalance': item.m_dBusinessBalance, + 'businessAmount': item.m_dBusinessAmount, + 'validDate': item.m_nValidDate, + 'dateClear': item.m_nDateClear, + 'entrustNo': item.m_strEntrustNo, + 'applyStatus': item.m_strApplyStatus, + 'usedRate': item.m_dUsedRate, + 'unUusedRate': item.m_dUnUusedRate, + 'comGroupId': item.m_strComGroupID + }) + return result + + def smt_query_compact(self, account): + """ + :param account: 证券账号 + :return: 返回券源行情信息 + """ + req = _XTQC_.SmtQueryCompactReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + compact_list = self.common_op_sync_with_seq( + seq, + (self.async_client.smtQueryCompactWithSeq, seq, req) + ) + result = list() + for item in compact_list: + result.append( + { + 'success': item.m_bSuccess, + 'error': item.m_strMsg, + 'createDate': item.m_nCreateDate, + 'cashcompactId': item.m_strCashcompactID, + 'oriCashcompactId': item.m_strOriCashcompactID, + 'applyId': item.m_strApplyID, + 'srcGroupId': item.m_strSrcGroupID, + 'comGroupId': item.m_strComGroupID, + 'finType': item.m_strFinType, + 'exchangeType': item.m_strExchangeType, + 'code': item.m_strCode, + 'codeName': item.m_strCodeName, + 'date': item.m_nDate, + 'beginCompacAmount': item.m_dBeginCompacAmount, + 'beginCompacBalance': item.m_dBeginCompacBalance, + 'compacAmount': item.m_dCompacAmount, + 'compacBalance': item.m_dCompacBalance, + 'returnAmount': item.m_dReturnAmount, + 'returnBalance': item.m_dReturnBalance, + 'realBuyAmount': item.m_dRealBuyAmount, + 'fsmpOccupedRate': item.m_dFsmpOccupedRate, + 'compactInterest': item.m_dCompactInterest, + 'compactFineInterest': item.m_dCompactFineInterest, + 'repaidInterest': item.m_dRepaidInterest, + 'repaidFineInterest': item.m_dRepaidFineInterest, + 'fineRate': item.m_dFineRate, + 'preendRate': item.m_dPreendRate, + 'compactType': item.m_strCompactType, + 'postponeTimes': item.m_nPostponeTimes, + 'compactStatus': item.m_strCompactStatus, + 'lastInterestDate': item.m_nLastInterestDate, + 'interestEndDate': item.m_nInterestEndDate, + 'validDate': item.m_nValidDate, + 'dateClear': item.m_nDateClear, + 'usedAmount': item.m_dUsedAmount, + 'usedBalance': item.m_dUsedBalance, + 'usedRate': item.m_dUsedRate, + 'unUusedRate': item.m_dUnUusedRate, + 'srcGroupName': item.m_strSrcGroupName, + 'repaidDate': item.m_nRepaidDate, + 'preOccupedInterest': item.m_dPreOccupedInterest, + 'compactInterestx': item.m_dCompactInterestx, + 'enPostponeAmount': item.m_dEnPostponeAmount, + 'postponeStatus': item.m_strPostponeStatus, + 'applyMode': item.m_strApplyMode + }) + return result + + def smt_compact_renewal_async(self, account, cash_compact_id, order_code, defer_days, defer_num, apply_rate): + """ + :param account: 证券账号 + :param cash_compact_id: 头寸合约编号 + :param order_code: 证券代码,如'600000.SH' + :param defer_days: 申请展期天数 + :param defer_num: 申请展期数量 + :param apply_rate: 资券申请利率 + :return: 返回约券展期请求序号, 成功请求后的序号为大于0的正整数, 如果为-1表示请求失败 + """ + req = _XTQC_.SmtCompactRenewalReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strCompactId = cash_compact_id + req.m_strOrderCode = order_code + req.m_nDeferDays = defer_days + req.m_nDeferNum = defer_num + req.m_dApplyRate = apply_rate + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_smt_appointment_async_response + self.async_client.smtCompactRenewalWithSeq(seq, req) + return seq + + def smt_compact_return_async(self, account, src_group_id, cash_compact_id, order_code, occur_amount): + """ + :param account: 证券账号 + :param src_group_id: 来源组编号 + :param cash_compact_id: 头寸合约编号 + :param order_code: 证券代码,如'600000.SH' + :param occur_amount: 发生数量 + :return: 返回约券归还请求序号, 成功请求后的序号为大于0的正整数, 如果为-1表示请求失败 + """ + req = _XTQC_.SmtCompactReturnReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + req.m_strSrcGroupId = src_group_id + req.m_strCompactId = cash_compact_id + req.m_strOrderCode = order_code + req.m_nOccurAmount = occur_amount + + seq = self.async_client.nextSeq() + self.cbs[seq] = self.callback.on_smt_appointment_async_response + self.async_client.smtCompactReturnWithSeq(seq, req) + return seq + + def query_position_statistics(self, account): + """ + :param account: 证券账号 + :return: 返回当日所有持仓统计的持仓对象组成的list + """ + req = _XTQC_.QueryPositionStatisticsReq() + req.m_nAccountType = account.account_type + req.m_strAccountID = account.account_id + + seq = self.async_client.nextSeq() + return self.common_op_sync_with_seq( + seq, + (self.async_client.queryPositionStatisticsWithSeq, seq, req) + ) + + def export_data(self, account, result_path, data_type, start_time = None, end_time = None, user_param = {}): + """ + :param account: 证券账号 + :param result_path: 导出路径,包含文件名及.csv后缀,如'C:\\Users\\Desktop\\test\\deal.csv' + :param data_type: 数据类型,如'deal' + :param start_time: 开始时间 + :param end_time: 结束时间 + :param user_param: 用户参数 + :return: 返回dict格式的结果反馈信息 + """ + fix_param = dict() + fix_param['accountID'] = account.account_id + fix_param['accountType'] = account.account_type + fix_param['resultPath'] = result_path + fix_param['dataType'] = data_type + fix_param['startTime'] = start_time + fix_param['endTime'] = end_time + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.exportDataWithSeq, seq, bson.BSON.encode(fix_param), bson.BSON.encode(user_param)) + ) + import json + result = json.loads(resp) + return result + + def query_data(self, account, result_path, data_type, start_time = None, end_time = None, user_param = {}): + """ + 入参同export_data + :return: 返回dict格式的数据信息 + """ + result = self.export_data(account, result_path, data_type, start_time, end_time, user_param) + if 'error' in result.keys(): + return result + else: + import pandas as pd + import os + data = pd.read_csv(result_path) + os.remove(result_path) + return data + + def sync_transaction_from_external(self, operation, data_type, account, deal_list): + """ + :param operation: 操作类型,有"UPDATE","REPLACE","ADD","DELETE" + :param data_type: 数据类型,有"DEAL" + :param account: 证券账号 + :param deal_list: 成交列表,每一项是Deal成交对象的参数字典,键名参考官网数据字典,大小写保持一致 + :return: 返回dict格式的结果反馈信息 + """ + fix_param = dict() + fix_param['operation'] = operation + fix_param['dataType'] = data_type + fix_param['accountID'] = account.account_id + fix_param['accountType'] = account.account_type + bson_list = [bson.BSON.encode(it) for it in deal_list] + seq = self.async_client.nextSeq() + resp = self.common_op_sync_with_seq( + seq, + (self.async_client.syncTransactionFromExternalWithSeq, seq, bson.BSON.encode(fix_param), bson_list) + ) + import json + result = json.loads(resp) + return result + diff --git a/src/xtquant/xttype.py b/src/xtquant/xttype.py new file mode 100644 index 0000000..67a77e1 --- /dev/null +++ b/src/xtquant/xttype.py @@ -0,0 +1,373 @@ +#coding=utf-8 +from . import xtconstant as _XTCONST_ + +""" +定义Python的数据结构,给Python策略使用 +包含股票+信用 +""" + +class StockAccount(object): + """ + 定义证券账号类, 用于证券账号的报撤单等 + """ + def __new__(cls, account_id, account_type = 'STOCK'): + """ + :param account_id: 资金账号 + :return: 若资金账号不为字符串,返回类型错误 + """ + if not isinstance(account_id, str): + return u"资金账号必须为字符串类型" + return super(StockAccount, cls).__new__(cls) + + def __init__(self, account_id, account_type = 'STOCK'): + """ + :param account_id: 资金账号 + """ + account_type = account_type.upper() + for int_type, str_type in _XTCONST_.ACCOUNT_TYPE_DICT.items(): + if account_type == str_type: + self.account_type = int_type + self.account_id = account_id + return + raise Exception("不支持的账号类型:{}!".format(account_type)) + + +class XtAsset(object): + """ + 迅投股票账号资金结构 + """ + def __init__(self, account_id, cash, frozen_cash, market_value, total_asset): + """ + :param account_id: 资金账号 + :param cash: 可用 + :param frozen_cash: 冻结 + :param market_value: 持仓市值 + :param total_asset: 总资产 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.cash = cash + self.frozen_cash = frozen_cash + self.market_value = market_value + self.total_asset = total_asset + + +class XtOrder(object): + """ + 迅投股票委托结构 + """ + def __init__(self, account_id, stock_code, + order_id, order_sysid, order_time, order_type, order_volume, + price_type, price, traded_volume, traded_price, + order_status, status_msg, strategy_name, order_remark, direction, offset_flag, + stock_code1): + """ + :param account_id: 资金账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param order_id: 委托编号 + :param order_sysid: 柜台编号 + :param order_time: 报单时间 + :param order_type: 委托类型, 23:买, 24:卖 + :param order_volume: 委托数量, 股票以'股'为单位, 债券以'张'为单位 + :param price_type: 报价类型, 详见帮助手册 + :param price: 报价价格,如果price_type为指定价, 那price为指定的价格,否则填0 + :param traded_volume: 成交数量, 股票以'股'为单位, 债券以'张'为单位 + :param traded_price: 成交均价 + :param order_status: 委托状态 + :param status_msg: 委托状态描述, 如废单原因 + :param strategy_name: 策略名称 + :param order_remark: 委托备注 + :param direction: 多空, 股票不需要 + :param offset_flag: 交易操作,用此字段区分股票买卖,期货开、平仓,期权买卖等 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.stock_code = stock_code + self.order_id = order_id + self.order_sysid = order_sysid + self.order_time = order_time + self.order_type = order_type + self.order_volume = order_volume + self.price_type = price_type + self.price = price + self.traded_volume = traded_volume + self.traded_price = traded_price + self.order_status = order_status + self.status_msg = status_msg + self.strategy_name = strategy_name + self.order_remark = order_remark + self.direction = direction + self.offset_flag = offset_flag + self.stock_code1 = stock_code1 + + +class XtTrade(object): + """ + 迅投股票成交结构 + """ + def __init__(self, account_id, stock_code, + order_type, traded_id, traded_time, traded_price, traded_volume, traded_amount, + order_id, order_sysid, strategy_name, order_remark, direction, offset_flag, + stock_code1, commission): + """ + :param account_id: 资金账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param order_type: 委托类型 + :param traded_id: 成交编号 + :param traded_time: 成交时间 + :param traded_price: 成交均价 + :param traded_volume: 成交数量, 股票以'股'为单位, 债券以'张'为单位 + :param traded_amount: 成交金额 + :param order_id: 委托编号 + :param order_sysid: 柜台编号 + :param strategy_name: 策略名称 + :param order_remark: 委托备注 + :param direction: 多空, 股票不需要 + :param offset_flag: 交易操作,用此字段区分股票买卖,期货开、平仓,期权买卖等 + :param commission: 手续费 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.order_type = order_type + self.stock_code = stock_code + self.traded_id = traded_id + self.traded_time = traded_time + self.traded_price = traded_price + self.traded_volume = traded_volume + self.traded_amount = traded_amount + self.order_id = order_id + self.order_sysid = order_sysid + self.strategy_name = strategy_name + self.order_remark = order_remark + self.direction = direction + self.offset_flag = offset_flag + self.stock_code1 = stock_code1 + self.commission = commission + + +class XtPosition(object): + """ + 迅投股票持仓结构 + """ + def __init__(self, account_id, stock_code, + volume, can_use_volume, open_price, market_value, + frozen_volume, on_road_volume, yesterday_volume, avg_price, direction, + stock_code1): + """ + :param account_id: 资金账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param volume: 持仓数量,股票以'股'为单位, 债券以'张'为单位 + :param can_use_volume: 可用数量, 股票以'股'为单位, 债券以'张'为单位 + :param open_price: 开仓价 + :param market_value: 市值 + :param frozen_volume: 冻结数量 + :param on_road_volume: 在途股份 + :param yesterday_volume: 昨夜拥股 + :param avg_price: 成本价 + :param direction: 多空, 股票不需要 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.stock_code = stock_code + self.volume = volume + self.can_use_volume = can_use_volume + self.open_price = open_price + self.market_value = market_value + self.frozen_volume = frozen_volume + self.on_road_volume = on_road_volume + self.yesterday_volume = yesterday_volume + self.avg_price = avg_price + self.direction = direction + self.stock_code1 = stock_code1 + + +class XtOrderError(object): + """ + 迅投股票委托失败结构 + """ + def __init__(self, account_id, order_id, + error_id=None, error_msg=None, + strategy_name=None, order_remark=None): + """ + :param account_id: 资金账号 + :param order_id: 订单编号 + :param error_id: 报单失败错误码 + :param error_msg: 报单失败具体信息 + :param strategy_name: 策略名称 + :param order_remark: 委托备注 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.order_id = order_id + self.error_id = error_id + self.error_msg = error_msg + self.strategy_name = strategy_name + self.order_remark = order_remark + + +class XtCancelError(object): + """ + 迅投股票委托撤单失败结构 + """ + def __init__(self, account_id, order_id, market, order_sysid, + error_id=None, error_msg=None): + """ + :param account_id: 资金账号 + :param order_id: 订单编号 + :param market: 交易市场 0:上海 1:深圳 + :param order_sysid: 柜台委托编号 + :param error_id: 撤单失败错误码 + :param error_msg: 撤单失败具体信息 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.order_id = order_id + self.market = market + self.order_sysid = order_sysid + self.error_id = error_id + self.error_msg = error_msg + + +class XtOrderResponse(object): + """ + 迅投异步下单接口对应的委托反馈 + """ + def __init__(self, account_id, order_id, strategy_name, order_remark, error_msg, seq): + """ + :param account_id: 资金账号 + :param order_id: 订单编号 + :param strategy_name: 策略名称 + :param order_remark: 委托备注 + :param seq: 下单请求序号 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.order_id = order_id + self.strategy_name = strategy_name + self.order_remark = order_remark + self.error_msg = error_msg + self.seq = seq + +class XtCancelOrderResponse(object): + """ + 迅投异步委托撤单请求返回结构 + """ + def __init__(self, account_id, cancel_result, order_id, order_sysid, seq, error_msg): + """ + :param account_id: 资金账号 + :param cancel_result: 撤单结果 + :param order_id: 订单编号 + :param order_sysid: 柜台委托编号 + :param seq: 撤单请求序号 + :param error_msg: 撤单反馈信息 + """ + self.account_type = _XTCONST_.SECURITY_ACCOUNT + self.account_id = account_id + self.cancel_result = cancel_result + self.order_id = order_id + self.order_sysid = order_sysid + self.seq = seq + self.error_msg = error_msg + + +class XtCreditOrder(XtOrder): + """ + 迅投信用委托结构 + """ + def __init__(self, account_id, stock_code, + order_id, order_time, order_type, order_volume, + price_type, price, traded_volume, traded_price, + order_status, status_msg, order_remark, contract_no, + stock_code1): + """ + :param account_id: 资金账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param order_id: 委托编号 + :param order_time: 报单时间 + :param order_type: 委托类型, 23:买, 24:卖 + :param order_volume: 委托数量, 股票以'股'为单位, 债券以'张'为单位 + :param price_type: 报价类型, 详见帮助手册 + :param price: 报价价格,如果price_type为指定价, 那price为指定的价格,否则填0 + :param traded_volume: 成交数量, 股票以'股'为单位, 债券以'张'为单位 + :param traded_price: 成交均价 + :param order_status: 委托状态 + :param status_msg: 委托状态描述, 如废单原因 + :param order_remark: 委托备注 + :param contract_no: 两融合同编号 + """ + self.account_type = _XTCONST_.CREDIT_ACCOUNT + self.account_id = account_id + self.stock_code = stock_code + self.order_id = order_id + self.order_time = order_time + self.order_type = order_type + self.order_volume = order_volume + self.price_type = price_type + self.price = price + self.traded_volume = traded_volume + self.traded_price = traded_price + self.order_status = order_status + self.status_msg = status_msg + self.order_remark = order_remark + self.contract_no = contract_no + self.stock_code1 = stock_code1 + + +class XtCreditDeal(object): + """ + 迅投信用成交结构 + """ + def __init__(self, account_id, stock_code, + traded_id, traded_time, traded_price, + traded_volume, order_id, contract_no, + stock_code1): + """ + :param account_id: 资金账号 + :param stock_code: 证券代码, 例如"600000.SH" + :param traded_id: 成交编号 + :param traded_time: 成交时间 + :param traded_price: 成交均价 + :param traded_volume: 成交数量, 股票以'股'为单位, 债券以'张'为单位 + :param order_id: 委托编号 + :param contract_no: 两融合同编号 + """ + self.account_type = _XTCONST_.CREDIT_ACCOUNT + self.account_id = account_id + self.stock_code = stock_code + self.traded_id = traded_id + self.traded_time = traded_time + self.traded_price = traded_price + self.traded_volume = traded_volume + self.order_id = order_id + self.contract_no = contract_no + self.stock_code1 = stock_code1 + +class XtAccountStatus(object): + """ + 迅投账号状态结构 + """ + def __init__(self, account_id, account_type, status): + """ + :param account_id: 资金账号 + :param account_type: 账号状态 + :param status: 账号状态,详细见账号状态定义 + """ + self.account_type = account_type + self.account_id = account_id + self.status = status + +class XtSmtAppointmentResponse(object): + """ + 迅投约券相关异步接口的反馈 + """ + def __init__(self, seq, success, msg, apply_id): + """ + :param seq: 异步请求序号 + :param success: 申请是否成功 + :param msg: 反馈信息 + :param apply_id: 若申请成功返回资券申请编号 + """ + self.seq = seq + self.success = success + self.msg = msg + self.apply_id = apply_id diff --git a/src/xtquant/xtutil.py b/src/xtquant/xtutil.py new file mode 100644 index 0000000..5501c8c --- /dev/null +++ b/src/xtquant/xtutil.py @@ -0,0 +1,52 @@ +#coding:utf-8 + +from . import xtbson as _BSON_ + + +def read_from_bson_buffer(buffer): + import ctypes as ct + result = [] + + pos = 0 + while 1: + if pos + 4 < len(buffer): + dlen_buf = buffer[pos : pos + 4] + else: + break + + dlen = ct.cast(dlen_buf, ct.POINTER(ct.c_int32))[0] + if dlen >= 5: + try: + data_buf = buffer[pos : pos + dlen] + pos += dlen + + result.append(_BSON_.decode(data_buf)) + except Exception as e: + pass + else: + break + + return result + + +def write_to_bson_buffer(data_list): + buffer = b'' + + for data in data_list: + buffer += _BSON_.encode(data) + + return buffer + + +def read_from_feather_file(file): + import feather as fe + meta = {} + return meta, fe.read_dataframe(file) + + +def write_to_feather_file(data, file, meta = None): + import feather as fe + if not meta: + meta = {} + return + diff --git a/src/xtquant/xtview.py b/src/xtquant/xtview.py new file mode 100644 index 0000000..b9f8408 --- /dev/null +++ b/src/xtquant/xtview.py @@ -0,0 +1,326 @@ +#coding:utf-8 + +from . import xtbson as _BSON_ + +### connection + +__client = None +__client_last_spec = ('', None) + + +def connect(ip = '', port = None, remember_if_success = True): + global __client + + if __client: + if __client.is_connected(): + return __client + + __client.shutdown() + __client = None + + from . import xtconn + + if not ip: + ip = 'localhost' + + if port: + server_list = [f'{ip}:{port}'] + __client = xtconn.connect_any(server_list) + else: + server_list = xtconn.scan_available_server_addr() + + default_addr = 'localhost:58610' + if not default_addr in server_list: + server_list.append(default_addr) + + __client = xtconn.connect_any(server_list) + + if not __client or not __client.is_connected(): + raise Exception("无法连接xtquant服务,请检查QMT-投研版或QMT-极简版是否开启") + + if remember_if_success: + global __client_last_spec + __client_last_spec = (ip, port) + + return __client + + +def reconnect(ip = '', port = None, remember_if_success = True): + global __client + + if __client: + __client.shutdown() + __client = None + + return connect(ip, port, remember_if_success) + + +def get_client(): + global __client + + if not __client or not __client.is_connected(): + global __client_last_spec + + ip, port = __client_last_spec + __client = connect(ip, port, False) + + return __client + + +### utils +def try_except(func): + import sys + import traceback + + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception: + exc_type, exc_instance, exc_traceback = sys.exc_info() + formatted_traceback = ''.join(traceback.format_tb(exc_traceback)) + message = '\n{0} raise {1}:{2}'.format( + formatted_traceback, + exc_type.__name__, + exc_instance + ) + # raise exc_type(message) + print(message) + return None + + return wrapper + +def _BSON_call_common(interface, func, param): + return _BSON_.BSON.decode(interface(func, _BSON_.BSON.encode(param))) + +def create_view(viewID, view_type, title, group_id): + client = get_client() + return client.createView(viewID, view_type, title, group_id) + +#def reset_view(viewID): +# return + +def close_view(viewID): + client = get_client() + return client.closeView(viewID) + +#def set_view_index(viewID, datas): +# ''' +# 设置模型指标属性 +# index: { "output1": { "datatype": se::OutputDataType } } +# ''' +# client = get_client() +# return client.setViewIndex(viewID, datas) + +def push_view_data(viewID, datas): + ''' + 推送模型结果数据 + datas: { "timetags: [t1, t2, ...], "outputs": { "output1": [value1, value2, ...], ... }, "overwrite": "full/increase" } + ''' + client = get_client() + bresult = client.pushViewData(viewID, 'index', _BSON_.BSON.encode(datas)) + return _BSON_.BSON.decode(bresult) + +def switch_graph_view(stock_code = None, period = None, dividendtype = None, graphtype = None): + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'switchgraphview' + , { + "stockcode": stock_code + , "period": period + , "dividendtype": dividendtype + , "graphtype": graphtype + } + ) + +def add_schedule(schedule_name, begin_time = '', finish_time = '', interval = 60, run = False, only_work_date = False, always_run = False): + """ + ToDo: 向客户端添加调度任务 + Args: + schedule_name(str): 调度任务的方案名 + + begin_time(str) : 定时下载开始的时间 格式为'%H%M%S' + + interval(int) : 下载任务执行间隔,单位为秒, 例如要每24小时执行一次则填写 60*60*24 + + run(bool) : 是否自动运行, 默认为False + + only_work_date(bool) : 是否仅工作日运行, 默认为False + + always_run(bool) : 当前时间超过设定时间的情况下是否运行, 默认为False + + Return: + None + Example:: + + # 向客户端添加一个每日下载沪深A股市场的日K任务 + from xtquant import xtview, xtdata + stock_list = xtdata.get_stock_list_in_sector("沪深A股") + xtview.add_schedule( + schedule_name = "test计划", + begin_time ="150500", + interval = 60*60*24, + run = True, + only_work_date = True, + always_run = False) + + """ + + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'addschedule' + , { + 'name': schedule_name + , 'starttime': -1 if begin_time == '' else int(begin_time) + , 'endtime': -1 + , 'interval': interval * 1000 + , 'run': run + , 'onlyworkdate': only_work_date + , 'alwaysrun': always_run + } + ) + +def add_schedule_download_task(schedule_name, stock_code = [], period = '', recentday = 0, start_time = '', end_time = '', incrementally = False): + ''' + Args: + stock_code(list) : 下载标的的code组成的list + + period(str) : 下载数据的周期, 参考gmd + + recentday(int) : *仅在非增量的情况下生效* + 下载当日往前 N 个周期的数据, 当此参数不为0时,start_time不生效, 否则下载全部数据 + + start_time(str) : 下载数据的起始时间 格式为 '%Y%m%d' 或者 '%Y%m%d%H%M%S' + + end_time(str) : 下载数据的结束时间 格式为 '%Y%m%d' 或者 '%Y%m%d%H%M%S' + + incrementally(bool) : 是否增量下载, 默认为False + + Return: + None + Example:: + # 向客户端现存的调度方案中添加一个下载任务 + xtview.add_schedule_download_task( + schedule_name = "test计划", + stock_code = stock_list + period = "1d" ) + """ + ''' + + d_stockcode = {} + for stock in stock_code: + sp_stock = stock.split('.') + if len(sp_stock) == 2: + if sp_stock[1] not in d_stockcode: + d_stockcode[sp_stock[1]] = [] + + d_stockcode[sp_stock[1]].append(sp_stock[0]) + else: + d_stockcode[stock] = [] + + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'addscheduledownloadtask' + , { + 'name': schedule_name + , 'market': list(d_stockcode.keys()) + , 'stockcode': list(d_stockcode.values()) + , 'period': period + , 'recentday': recentday + , 'starttime': start_time + , 'endtime': end_time + , 'incrementally': incrementally + } + ) + return + +def modify_schedule_task(schedule_name, begin_time = '', finish_time = '', interval = 60, run = False, only_work_date = False, always_run = False): + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'modifyschedule' + , { + 'name': schedule_name + , 'starttime': -1 if begin_time == '' else int(begin_time) + , 'endtime': -1 + , 'interval': interval * 1000 + , 'run': run + , 'onlyworkdate': only_work_date + , 'alwaysrun': always_run + } + ) + +def remove_schedule(schedule_name): + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'removeschedule' + , { + 'name': schedule_name + } + ) + return + +def remove_schedule_download_task(schedule_name, task_id): + cl = get_client() + + result = _BSON_call_common( + cl.commonControl, 'removescheduledownloadtask' + , { + 'name': schedule_name + , 'taskids': task_id + } + ) + return + +def query_schedule_task(): + cl = get_client() + + inst = _BSON_call_common( + cl.commonControl, 'queryschedule', {} + ) + + return inst.get('result', []) + + +def push_xtview_data(data_type, time, datas): + cl = get_client() + timeData = 0 + types = [] + numericDatas = [] + stringDatas = [] + if type(time) == int: + name_list = list(datas.keys()) + value_list = [] + for name in name_list: + value_list.append([datas[name]]) + timeData = [time] + if type(time) == list: + time_list = time + name_list = list(datas.keys()) + value_list = list(datas.values()) + timeData = time_list + + for value in value_list: + if isinstance(value[0], str): + stringDatas.append(value) + types.append(3) + else: + numericDatas.append(value) + types.append(0) + + result = _BSON_call_common( + cl.custom_data_control, 'pushxtviewdata' + , { + 'dataType': data_type + ,'timetags': timeData + , 'names' : name_list + , 'types' : types + , 'numericDatas' : numericDatas + , 'stringDatas' : stringDatas + } + ) + return + diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/test_real_trader.py b/tests/test_real_trader.py deleted file mode 100644 index 0b76ddb..0000000 --- a/tests/test_real_trader.py +++ /dev/null @@ -1,59 +0,0 @@ -import unittest -from unittest.mock import patch, MagicMock -import os -import sys -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from src.real_trader import RealTrader - -class TestRealTrader(unittest.TestCase): - def setUp(self): - self.trader = RealTrader() - - def test_init(self): - """测试初始化""" - self.assertEqual(self.trader._exe_path, r"C:\\ths\\xiadan.exe") - - @patch('easytrader.use') - def test_login(self, mock_use): - """测试登录功能""" - mock_trader = MagicMock() - mock_use.return_value = mock_trader - - self.trader.login() - - mock_use.assert_called_once_with("ths5.19") - mock_trader.enable_type_keys_for_editor.assert_called_once() - mock_trader.prepare.assert_called_once_with( - user=self.trader._ACCOUNT, - password=self.trader._PASSWORD, - exe_path=self.trader._exe_path - ) - - @patch('easytrader.use') - def test_get_balance(self, mock_use): - """测试获取余额""" - mock_trader = MagicMock() - mock_use.return_value = mock_trader - expected_balance = {'资金余额': 1000} - mock_trader.balance = expected_balance - - self.trader.login() - balance = self.trader.get_balance() - - self.assertEqual(balance, expected_balance) - - @patch('easytrader.use') - def test_get_positions(self, mock_use): - """测试获取持仓""" - mock_trader = MagicMock() - mock_use.return_value = mock_trader - expected_positions = [{'证券代码': '000001', '证券名称': '平安银行'}] - mock_trader.position = expected_positions - - self.trader.login() - positions = self.trader.get_positions() - - self.assertEqual(positions, expected_positions) - -if __name__ == '__main__': - unittest.main() diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..7c107bb --- /dev/null +++ b/uv.lock @@ -0,0 +1,447 @@ +version = 1 +revision = 1 +requires-python = ">=3.10.11" + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, + { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, + { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, + { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, + { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, + { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, + { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, + { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, + { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, + { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, + { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, + { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, + { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "deprecated" +version = "1.2.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 }, +] + +[[package]] +name = "flask" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979 }, +] + +[[package]] +name = "flask-limiter" +version = "3.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "limits" }, + { name = "ordered-set" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/75/92b237dd4f6e19196bc73007fff288ab1d4c64242603f3c401ff8fc58a42/flask_limiter-3.12.tar.gz", hash = "sha256:f9e3e3d0c4acd0d1ffbfa729e17198dd1042f4d23c130ae160044fc930e21300", size = 303162 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/ba/40dafa278ee6a4300179d2bf59a1aa415165c26f74cfa17462132996186b/flask_limiter-3.12-py3-none-any.whl", hash = "sha256:b94c9e9584df98209542686947cf647f1ede35ed7e4ab564934a2bb9ed46b143", size = 28490 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "limits" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/94/a04e64f487a56f97aff67c53df609cc19d5c3f3e7e5697ec8a1ff8413829/limits-5.1.0.tar.gz", hash = "sha256:b298e4af0b47997da03cbeee9df027ddc2328f8630546125e81083bb56311827", size = 94655 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/00/876a5ec60addda62ee13ac4b588a5afc0d1a86a431645a91711ceae834cf/limits-5.1.0-py3-none-any.whl", hash = "sha256:f368d4572ac3ef8190cb8b9911ed481175a0b4189894a63cac95cae39ebeb147", size = 60472 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "ordered-set" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ca/bfac8bc689799bcca4157e0e0ced07e70ce125193fc2e166d2e685b7e2fe/ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8", size = 12826 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/55/af02708f230eb77084a299d7b08175cff006dea4f2721074b92cdb0296c0/ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", size = 7634 }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "real-trader" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "flask" }, + { name = "flask-limiter" }, + { name = "requests" }, + { name = "schedule" }, +] + +[package.metadata] +requires-dist = [ + { name = "flask", specifier = ">=3.1.0" }, + { name = "flask-limiter", specifier = ">=3.12" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "schedule", specifier = ">=1.2.2" }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "schedule" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/91/b525790063015759f34447d4cf9d2ccb52cdee0f1dd6ff8764e863bcb74c/schedule-1.2.2.tar.gz", hash = "sha256:15fe9c75fe5fd9b9627f3f19cc0ef1420508f9f9a46f45cd0769ef75ede5f0b7", size = 26452 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/a7/84c96b61fd13205f2cafbe263cdb2745965974bdf3e0078f121dfeca5f02/schedule-1.2.2-py3-none-any.whl", hash = "sha256:5bef4a2a0183abf44046ae0d164cadcac21b1db011bdd8102e4a0c1e91e06a7d", size = 12220 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, +] + +[[package]] +name = "werkzeug" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307 }, + { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486 }, + { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777 }, + { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314 }, + { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947 }, + { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778 }, + { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716 }, + { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548 }, + { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334 }, + { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427 }, + { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774 }, + { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 }, + { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 }, + { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 }, + { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 }, + { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 }, + { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 }, + { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 }, + { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 }, + { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, + { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, + { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +]