├── __pycache__ ├── config.cpython-36.pyc ├── qmtAccount.cpython-36.pyc ├── qmtCallback.cpython-36.pyc ├── qmtData.cpython-36.pyc ├── qmtTrader.cpython-36.pyc ├── qmt_pb2.cpython-36.pyc └── qmt_pb2_grpc.cpython-36.pyc ├── config.py ├── qmt.proto ├── qmt └── _pb2.py ├── qmtAccount.py ├── qmtCallback.py ├── qmtClient.py ├── qmtData.py ├── qmtDownloader.py ├── qmtServer.py ├── qmtTrader.py ├── qmt_pb2.py ├── qmt_pb2_grpc.py ├── readme.md └── xtquant ├── IPythonApiClient.cp310-win_amd64.pyd ├── IPythonApiClient.cp311-win_amd64.pyd ├── IPythonApiClient.cp36-win_amd64.pyd ├── IPythonApiClient.cp37-win_amd64.pyd ├── IPythonApiClient.cp38-win_amd64.pyd ├── IPythonApiClient.cp39-win_amd64.pyd ├── __init__.py ├── __pycache__ ├── __init__.cpython-36.pyc ├── xtconstant.cpython-36.pyc ├── xtdata.cpython-36.pyc ├── xtdata_config.cpython-36.pyc ├── xttrader.cpython-36.pyc └── xttype.cpython-36.pyc ├── doc ├── xtdata.pdf └── xttrader.pdf ├── libeay32.dll ├── log4cxx.dll ├── msvcp140.dll ├── qmttools ├── __init__.py ├── contextinfo.py ├── functions.py ├── stgentry.py └── stgframe.py ├── ssleay32.dll ├── vcruntime140.dll ├── xtbson ├── __init__.py ├── __pycache__ │ └── __init__.cpython-36.pyc ├── bson36 │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── _helpers.cpython-36.pyc │ │ ├── binary.cpython-36.pyc │ │ ├── code.cpython-36.pyc │ │ ├── codec_options.cpython-36.pyc │ │ ├── dbref.cpython-36.pyc │ │ ├── decimal128.cpython-36.pyc │ │ ├── errors.cpython-36.pyc │ │ ├── int64.cpython-36.pyc │ │ ├── max_key.cpython-36.pyc │ │ ├── min_key.cpython-36.pyc │ │ ├── objectid.cpython-36.pyc │ │ ├── regex.cpython-36.pyc │ │ ├── son.cpython-36.pyc │ │ ├── timestamp.cpython-36.pyc │ │ └── tz_util.cpython-36.pyc │ ├── _cbson.cp36-win_amd64.pyd │ ├── _helpers.py │ ├── binary.py │ ├── code.py │ ├── codec_options.py │ ├── dbref.py │ ├── decimal128.py │ ├── errors.py │ ├── int64.py │ ├── json_util.py │ ├── max_key.py │ ├── min_key.py │ ├── objectid.py │ ├── raw_bson.py │ ├── regex.py │ ├── son.py │ ├── timestamp.py │ └── tz_util.py └── bson37 │ ├── __init__.py │ ├── _cbson.cp310-win_amd64.pyd │ ├── _cbson.cp311-win_amd64.pyd │ ├── _cbson.cp37-win_amd64.pyd │ ├── _cbson.cp38-win_amd64.pyd │ ├── _cbson.cp39-win_amd64.pyd │ ├── _helpers.py │ ├── binary.py │ ├── code.py │ ├── codec_options.py │ ├── codec_options.pyi │ ├── datetime_ms.py │ ├── dbref.py │ ├── decimal128.py │ ├── errors.py │ ├── int64.py │ ├── json_util.py │ ├── max_key.py │ ├── min_key.py │ ├── objectid.py │ ├── py.typed │ ├── raw_bson.py │ ├── regex.py │ ├── son.py │ ├── timestamp.py │ └── tz_util.py ├── xtconstant.py ├── xtdata.ini ├── xtdata.log4cxx ├── xtdata.py ├── xtdata_config.py ├── xtextend.py ├── xtpythonclient.cp310-win_amd64.pyd ├── xtpythonclient.cp311-win_amd64.pyd ├── xtpythonclient.cp36-win_amd64.pyd ├── xtpythonclient.cp37-win_amd64.pyd ├── xtpythonclient.cp38-win_amd64.pyd ├── xtpythonclient.cp39-win_amd64.pyd ├── xtstocktype.py ├── xttools.py ├── xttrader.py ├── xttype.py └── xtview.py /__pycache__/config.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/config.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/qmtAccount.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/qmtAccount.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/qmtCallback.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/qmtCallback.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/qmtData.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/qmtData.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/qmtTrader.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/qmtTrader.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/qmt_pb2.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/qmt_pb2.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/qmt_pb2_grpc.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/__pycache__/qmt_pb2_grpc.cpython-36.pyc -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | server='[::]:50051' 2 | path = 'C:\\国金证券QMT交易端\\userdata_mini' 3 | account_id='1010573639' 4 | account_type='STOCK' 5 | -------------------------------------------------------------------------------- /qmt.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package qmt; 4 | 5 | // 价格请求消息 6 | message PriceRequest { 7 | string code = 1; 8 | } 9 | 10 | // 价格响应消息 11 | message PriceResponse { 12 | double last_price = 1; 13 | } 14 | 15 | // 每日信息请求消息 16 | message DailyInfoRequest { 17 | string code = 1; 18 | } 19 | 20 | // 每日信息响应消息 21 | message DailyInfoResponse { 22 | string ts_code = 1; 23 | double open = 2; 24 | double high = 3; 25 | double low = 4; 26 | double close = 5; 27 | double volume = 6; 28 | double amount = 7; 29 | string name = 8; 30 | double vwap = 9; 31 | int32 stop = 10; 32 | double up_limit = 11; 33 | double down_limit = 12; 34 | } 35 | 36 | // 订单请求消息 37 | message OrderRequest { 38 | string code = 1; 39 | int32 amount = 2; 40 | double price = 3; 41 | string strategy = 4; 42 | string remark = 5; 43 | } 44 | 45 | // 订单响应消息 46 | message OrderResponse { 47 | int32 seq = 1; 48 | } 49 | 50 | // 查询订单请求消息 51 | message QueryOrdersRequest { 52 | 53 | } 54 | 55 | // 订单详情消息 56 | message OrderDetail { 57 | string stock_code = 1; 58 | string order_id = 2; 59 | string order_sysid = 3; 60 | string order_time = 4; 61 | int32 order_type = 5; 62 | int32 order_volume = 6; 63 | int32 price_type = 7; 64 | double price = 8; 65 | int32 traded_volume = 9; 66 | double traded_price = 10; 67 | int32 order_status = 11; 68 | string status_msg = 12; 69 | string strategy_name = 13; 70 | string order_remark = 14; 71 | } 72 | 73 | // 订单列表响应消息 74 | message OrdersResponse { 75 | repeated OrderDetail orders = 1; 76 | } 77 | 78 | // 资产请求消息 79 | message AssetRequest { 80 | 81 | } 82 | 83 | // 资产响应消息 84 | message AssetResponse { 85 | string user = 1; 86 | double cash = 2; 87 | double frozen_cash = 3; 88 | double market_value = 4; 89 | double total_value = 5; 90 | } 91 | 92 | // 持仓响应消息 93 | message PositionsResponse { 94 | repeated PositionDetail positions = 1; 95 | } 96 | 97 | // 持仓详情消息 98 | message PositionDetail { 99 | string stock_code = 1; 100 | int32 volume = 2; 101 | int32 can_use_volume = 3; 102 | double open_price = 4; 103 | double market_value = 5; 104 | int32 frozen_volume = 6; 105 | double avg_price = 7; 106 | } 107 | 108 | 109 | 110 | // 定义服务 111 | service QmtService { 112 | rpc GetPrice(PriceRequest) returns (PriceResponse) {} 113 | rpc GetDailyInfo(DailyInfoRequest) returns (DailyInfoResponse) {} 114 | rpc OrderBuy(OrderRequest) returns (OrderResponse) {} 115 | rpc OrderSell(OrderRequest) returns (OrderResponse) {} 116 | rpc QueryOrders(QueryOrdersRequest) returns (OrdersResponse) {} 117 | rpc CancelOrders(QueryOrdersRequest) returns (OrdersResponse) {} 118 | rpc RetryOrders(QueryOrdersRequest) returns (OrdersResponse) {} 119 | rpc GetAsset(AssetRequest) returns (AssetResponse) {} 120 | rpc GetPositions(AssetRequest) returns (PositionsResponse) {} 121 | } 122 | -------------------------------------------------------------------------------- /qmt/_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: qmt..proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | # @@protoc_insertion_point(imports) 11 | 12 | _sym_db = _symbol_database.Default() 13 | 14 | 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nqmt..proto\x12\x03qmt\"\x1c\n\x0cPriceRequest\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\"#\n\rPriceResponse\x12\x12\n\nlast_price\x18\x01 \x01(\x01\" \n\x10\x44\x61ilyInfoRequest\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\"\xcc\x01\n\x11\x44\x61ilyInfoResponse\x12\x0f\n\x07ts_code\x18\x01 \x01(\t\x12\x0c\n\x04open\x18\x02 \x01(\x01\x12\x0c\n\x04high\x18\x03 \x01(\x01\x12\x0b\n\x03low\x18\x04 \x01(\x01\x12\r\n\x05\x63lose\x18\x05 \x01(\x01\x12\x0e\n\x06volume\x18\x06 \x01(\x01\x12\x0e\n\x06\x61mount\x18\x07 \x01(\x01\x12\x0c\n\x04name\x18\x08 \x01(\t\x12\x0c\n\x04vwap\x18\t \x01(\x01\x12\x0c\n\x04stop\x18\n \x01(\x05\x12\x10\n\x08up_limit\x18\x0b \x01(\x01\x12\x12\n\ndown_limit\x18\x0c \x01(\x01\"]\n\x0cOrderRequest\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x05\x12\r\n\x05price\x18\x03 \x01(\x01\x12\x10\n\x08strategy\x18\x04 \x01(\t\x12\x0e\n\x06remark\x18\x05 \x01(\t\"\x1c\n\rOrderResponse\x12\x0b\n\x03seq\x18\x01 \x01(\x05\"\x14\n\x12QueryOrdersRequest\"\xad\x02\n\x0bOrderDetail\x12\x12\n\nstock_code\x18\x01 \x01(\t\x12\x10\n\x08order_id\x18\x02 \x01(\t\x12\x13\n\x0border_sysid\x18\x03 \x01(\t\x12\x12\n\norder_time\x18\x04 \x01(\t\x12\x12\n\norder_type\x18\x05 \x01(\x05\x12\x14\n\x0corder_volume\x18\x06 \x01(\x05\x12\x12\n\nprice_type\x18\x07 \x01(\x05\x12\r\n\x05price\x18\x08 \x01(\x01\x12\x15\n\rtraded_volume\x18\t \x01(\x05\x12\x14\n\x0ctraded_price\x18\n \x01(\x01\x12\x14\n\x0corder_status\x18\x0b \x01(\x05\x12\x12\n\nstatus_msg\x18\x0c \x01(\t\x12\x15\n\rstrategy_name\x18\r \x01(\t\x12\x14\n\x0corder_remark\x18\x0e \x01(\t\"2\n\x0eOrdersResponse\x12 \n\x06orders\x18\x01 \x03(\x0b\x32\x10.qmt.OrderDetail\"\x0e\n\x0c\x41ssetRequest\"k\n\rAssetResponse\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x0c\n\x04\x63\x61sh\x18\x02 \x01(\x01\x12\x13\n\x0b\x66rozen_cash\x18\x03 \x01(\x01\x12\x14\n\x0cmarket_value\x18\x04 \x01(\x01\x12\x13\n\x0btotal_value\x18\x05 \x01(\x01\";\n\x11PositionsResponse\x12&\n\tpositions\x18\x01 \x03(\x0b\x32\x13.qmt.PositionDetail\"\x8d\x01\n\x0ePositionDetail\x12\x12\n\nstock_code\x18\x01 \x01(\t\x12\x0e\n\x06volume\x18\x02 \x01(\x05\x12\x16\n\x0e\x63\x61n_use_volume\x18\x03 \x01(\x05\x12\x12\n\nopen_price\x18\x04 \x01(\x01\x12\x14\n\x0cmarket_value\x18\x05 \x01(\x01\x12\x15\n\rfrozen_volume\x18\x06 \x01(\x05\x32\x9d\x04\n\nQmtService\x12\x33\n\x08GetPrice\x12\x11.qmt.PriceRequest\x1a\x12.qmt.PriceResponse\"\x00\x12?\n\x0cGetDailyInfo\x12\x15.qmt.DailyInfoRequest\x1a\x16.qmt.DailyInfoResponse\"\x00\x12\x33\n\x08OrderBuy\x12\x11.qmt.OrderRequest\x1a\x12.qmt.OrderResponse\"\x00\x12\x34\n\tOrderSell\x12\x11.qmt.OrderRequest\x1a\x12.qmt.OrderResponse\"\x00\x12=\n\x0bQueryOrders\x12\x17.qmt.QueryOrdersRequest\x1a\x13.qmt.OrdersResponse\"\x00\x12>\n\x0c\x43\x61ncelOrders\x12\x17.qmt.QueryOrdersRequest\x1a\x13.qmt.OrdersResponse\"\x00\x12=\n\x0bRetryOrders\x12\x17.qmt.QueryOrdersRequest\x1a\x13.qmt.OrdersResponse\"\x00\x12\x33\n\x08GetAsset\x12\x11.qmt.AssetRequest\x1a\x12.qmt.AssetResponse\"\x00\x12;\n\x0cGetPositions\x12\x11.qmt.AssetRequest\x1a\x16.qmt.PositionsResponse\"\x00\x62\x06proto3') 18 | 19 | 20 | 21 | _PRICEREQUEST = DESCRIPTOR.message_types_by_name['PriceRequest'] 22 | _PRICERESPONSE = DESCRIPTOR.message_types_by_name['PriceResponse'] 23 | _DAILYINFOREQUEST = DESCRIPTOR.message_types_by_name['DailyInfoRequest'] 24 | _DAILYINFORESPONSE = DESCRIPTOR.message_types_by_name['DailyInfoResponse'] 25 | _ORDERREQUEST = DESCRIPTOR.message_types_by_name['OrderRequest'] 26 | _ORDERRESPONSE = DESCRIPTOR.message_types_by_name['OrderResponse'] 27 | _QUERYORDERSREQUEST = DESCRIPTOR.message_types_by_name['QueryOrdersRequest'] 28 | _ORDERDETAIL = DESCRIPTOR.message_types_by_name['OrderDetail'] 29 | _ORDERSRESPONSE = DESCRIPTOR.message_types_by_name['OrdersResponse'] 30 | _ASSETREQUEST = DESCRIPTOR.message_types_by_name['AssetRequest'] 31 | _ASSETRESPONSE = DESCRIPTOR.message_types_by_name['AssetResponse'] 32 | _POSITIONSRESPONSE = DESCRIPTOR.message_types_by_name['PositionsResponse'] 33 | _POSITIONDETAIL = DESCRIPTOR.message_types_by_name['PositionDetail'] 34 | PriceRequest = _reflection.GeneratedProtocolMessageType('PriceRequest', (_message.Message,), { 35 | 'DESCRIPTOR' : _PRICEREQUEST, 36 | '__module__' : 'qmt._pb2' 37 | # @@protoc_insertion_point(class_scope:qmt.PriceRequest) 38 | }) 39 | _sym_db.RegisterMessage(PriceRequest) 40 | 41 | PriceResponse = _reflection.GeneratedProtocolMessageType('PriceResponse', (_message.Message,), { 42 | 'DESCRIPTOR' : _PRICERESPONSE, 43 | '__module__' : 'qmt._pb2' 44 | # @@protoc_insertion_point(class_scope:qmt.PriceResponse) 45 | }) 46 | _sym_db.RegisterMessage(PriceResponse) 47 | 48 | DailyInfoRequest = _reflection.GeneratedProtocolMessageType('DailyInfoRequest', (_message.Message,), { 49 | 'DESCRIPTOR' : _DAILYINFOREQUEST, 50 | '__module__' : 'qmt._pb2' 51 | # @@protoc_insertion_point(class_scope:qmt.DailyInfoRequest) 52 | }) 53 | _sym_db.RegisterMessage(DailyInfoRequest) 54 | 55 | DailyInfoResponse = _reflection.GeneratedProtocolMessageType('DailyInfoResponse', (_message.Message,), { 56 | 'DESCRIPTOR' : _DAILYINFORESPONSE, 57 | '__module__' : 'qmt._pb2' 58 | # @@protoc_insertion_point(class_scope:qmt.DailyInfoResponse) 59 | }) 60 | _sym_db.RegisterMessage(DailyInfoResponse) 61 | 62 | OrderRequest = _reflection.GeneratedProtocolMessageType('OrderRequest', (_message.Message,), { 63 | 'DESCRIPTOR' : _ORDERREQUEST, 64 | '__module__' : 'qmt._pb2' 65 | # @@protoc_insertion_point(class_scope:qmt.OrderRequest) 66 | }) 67 | _sym_db.RegisterMessage(OrderRequest) 68 | 69 | OrderResponse = _reflection.GeneratedProtocolMessageType('OrderResponse', (_message.Message,), { 70 | 'DESCRIPTOR' : _ORDERRESPONSE, 71 | '__module__' : 'qmt._pb2' 72 | # @@protoc_insertion_point(class_scope:qmt.OrderResponse) 73 | }) 74 | _sym_db.RegisterMessage(OrderResponse) 75 | 76 | QueryOrdersRequest = _reflection.GeneratedProtocolMessageType('QueryOrdersRequest', (_message.Message,), { 77 | 'DESCRIPTOR' : _QUERYORDERSREQUEST, 78 | '__module__' : 'qmt._pb2' 79 | # @@protoc_insertion_point(class_scope:qmt.QueryOrdersRequest) 80 | }) 81 | _sym_db.RegisterMessage(QueryOrdersRequest) 82 | 83 | OrderDetail = _reflection.GeneratedProtocolMessageType('OrderDetail', (_message.Message,), { 84 | 'DESCRIPTOR' : _ORDERDETAIL, 85 | '__module__' : 'qmt._pb2' 86 | # @@protoc_insertion_point(class_scope:qmt.OrderDetail) 87 | }) 88 | _sym_db.RegisterMessage(OrderDetail) 89 | 90 | OrdersResponse = _reflection.GeneratedProtocolMessageType('OrdersResponse', (_message.Message,), { 91 | 'DESCRIPTOR' : _ORDERSRESPONSE, 92 | '__module__' : 'qmt._pb2' 93 | # @@protoc_insertion_point(class_scope:qmt.OrdersResponse) 94 | }) 95 | _sym_db.RegisterMessage(OrdersResponse) 96 | 97 | AssetRequest = _reflection.GeneratedProtocolMessageType('AssetRequest', (_message.Message,), { 98 | 'DESCRIPTOR' : _ASSETREQUEST, 99 | '__module__' : 'qmt._pb2' 100 | # @@protoc_insertion_point(class_scope:qmt.AssetRequest) 101 | }) 102 | _sym_db.RegisterMessage(AssetRequest) 103 | 104 | AssetResponse = _reflection.GeneratedProtocolMessageType('AssetResponse', (_message.Message,), { 105 | 'DESCRIPTOR' : _ASSETRESPONSE, 106 | '__module__' : 'qmt._pb2' 107 | # @@protoc_insertion_point(class_scope:qmt.AssetResponse) 108 | }) 109 | _sym_db.RegisterMessage(AssetResponse) 110 | 111 | PositionsResponse = _reflection.GeneratedProtocolMessageType('PositionsResponse', (_message.Message,), { 112 | 'DESCRIPTOR' : _POSITIONSRESPONSE, 113 | '__module__' : 'qmt._pb2' 114 | # @@protoc_insertion_point(class_scope:qmt.PositionsResponse) 115 | }) 116 | _sym_db.RegisterMessage(PositionsResponse) 117 | 118 | PositionDetail = _reflection.GeneratedProtocolMessageType('PositionDetail', (_message.Message,), { 119 | 'DESCRIPTOR' : _POSITIONDETAIL, 120 | '__module__' : 'qmt._pb2' 121 | # @@protoc_insertion_point(class_scope:qmt.PositionDetail) 122 | }) 123 | _sym_db.RegisterMessage(PositionDetail) 124 | 125 | _QMTSERVICE = DESCRIPTOR.services_by_name['QmtService'] 126 | if _descriptor._USE_C_DESCRIPTORS == False: 127 | 128 | DESCRIPTOR._options = None 129 | _PRICEREQUEST._serialized_start=19 130 | _PRICEREQUEST._serialized_end=47 131 | _PRICERESPONSE._serialized_start=49 132 | _PRICERESPONSE._serialized_end=84 133 | _DAILYINFOREQUEST._serialized_start=86 134 | _DAILYINFOREQUEST._serialized_end=118 135 | _DAILYINFORESPONSE._serialized_start=121 136 | _DAILYINFORESPONSE._serialized_end=325 137 | _ORDERREQUEST._serialized_start=327 138 | _ORDERREQUEST._serialized_end=420 139 | _ORDERRESPONSE._serialized_start=422 140 | _ORDERRESPONSE._serialized_end=450 141 | _QUERYORDERSREQUEST._serialized_start=452 142 | _QUERYORDERSREQUEST._serialized_end=472 143 | _ORDERDETAIL._serialized_start=475 144 | _ORDERDETAIL._serialized_end=776 145 | _ORDERSRESPONSE._serialized_start=778 146 | _ORDERSRESPONSE._serialized_end=828 147 | _ASSETREQUEST._serialized_start=830 148 | _ASSETREQUEST._serialized_end=844 149 | _ASSETRESPONSE._serialized_start=846 150 | _ASSETRESPONSE._serialized_end=953 151 | _POSITIONSRESPONSE._serialized_start=955 152 | _POSITIONSRESPONSE._serialized_end=1014 153 | _POSITIONDETAIL._serialized_start=1017 154 | _POSITIONDETAIL._serialized_end=1158 155 | _QMTSERVICE._serialized_start=1161 156 | _QMTSERVICE._serialized_end=1702 157 | # @@protoc_insertion_point(module_scope) 158 | -------------------------------------------------------------------------------- /qmtAccount.py: -------------------------------------------------------------------------------- 1 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 2 | from xtquant.xttype import StockAccount 3 | from xtquant import xtconstant 4 | from xtquant import xtdata 5 | 6 | def get_asset(xt_trader,account): 7 | print(f"正在查询资产信息") 8 | asset_tmp=xt_trader.query_stock_asset(account) 9 | asset={ 10 | "user":asset_tmp.account_id, 11 | "cash":asset_tmp.cash, 12 | "frozen_cash":asset_tmp.frozen_cash, 13 | "market_value":asset_tmp.market_value, 14 | "total_value":asset_tmp.total_asset 15 | } 16 | print(asset) 17 | return asset 18 | 19 | def get_positions(xt_trader,account): 20 | print(f"正在查询持仓信息") 21 | p_tmp=xt_trader.query_stock_positions(account) 22 | positions=[] 23 | for pos in p_tmp: 24 | positions.append({ 25 | "stock_code":pos.stock_code, 26 | "volume":pos.volume, 27 | "can_use_volume":pos.can_use_volume, 28 | "open_price":pos.open_price, 29 | "market_value":pos.market_value, 30 | "frozen_volume":pos.frozen_volume, 31 | "avg_price":pos.open_price 32 | }) 33 | print(positions) 34 | return positions 35 | -------------------------------------------------------------------------------- /qmtCallback.py: -------------------------------------------------------------------------------- 1 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 2 | from xtquant.xttype import StockAccount 3 | from xtquant import xtconstant 4 | from xtquant import xtdata 5 | import requests 6 | from datetime import datetime, timedelta 7 | import json 8 | import random 9 | 10 | 11 | class MyXtQuantTraderCallback(XtQuantTraderCallback): 12 | def on_disconnected(self): 13 | """ 14 | 连接断开 15 | :return: 16 | """ 17 | print("connection lost") 18 | def on_stock_order(self, order): 19 | """ 20 | 委托回报推送 21 | :param order: XtOrder对象 22 | :return: 23 | """ 24 | print("on order callback:") 25 | print(order.stock_code, order.order_status, order.order_sysid) 26 | def on_stock_asset(self, asset): 27 | """ 28 | 资金变动推送 注意,该回调函数目前不生效 29 | :param asset: XtAsset对象 30 | :return: 31 | """ 32 | print("on asset callback") 33 | print(asset.account_id, asset.cash, asset.total_asset) 34 | def on_stock_trade(self, trade): 35 | """ 36 | 成交变动推送 37 | :param trade: XtTrade对象 38 | :return: 39 | """ 40 | print("on trade callback") 41 | print(trade.account_id, trade.stock_code, trade.order_id) 42 | def on_stock_position(self, position): 43 | """ 44 | 持仓变动推送 注意,该回调函数目前不生效 45 | :param position: XtPosition对象 46 | :return: 47 | """ 48 | print("on position callback") 49 | print(position.stock_code, position.volume) 50 | def on_order_error(self, order_error): 51 | """ 52 | 委托失败推送 53 | :param order_error:XtOrderError 对象 54 | :return: 55 | """ 56 | print("on order_error callback") 57 | print(order_error.order_id, order_error.error_id, order_error.error_msg) 58 | def on_cancel_error(self, cancel_error): 59 | """ 60 | 撤单失败推送 61 | :param cancel_error: XtCancelError 对象 62 | :return: 63 | """ 64 | print("on cancel_error callback") 65 | print(cancel_error.order_id, cancel_error.error_id, cancel_error.error_msg) 66 | def on_order_stock_async_response(self, response): 67 | """ 68 | 异步下单回报推送 69 | :param response: XtOrderResponse 对象 70 | :return: 71 | """ 72 | print("on_order_stock_async_response") 73 | print(response.account_id, response.order_id, response.seq) 74 | def on_account_status(self, status): 75 | """ 76 | :param response: XtAccountStatus 对象 77 | :return: 78 | """ 79 | print("on_account_status") 80 | print(status.account_id, status.account_type, status.status) 81 | 82 | def on_data(datas): 83 | print(123) 84 | for stock_code in datas: 85 | print(stock_code, datas[stock_code]) 86 | -------------------------------------------------------------------------------- /qmtClient.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | import qmt_pb2 3 | import qmt_pb2_grpc 4 | 5 | class qmtClient(): 6 | def __init__(self) -> None: 7 | self.channel = grpc.insecure_channel('192.168.8.119:50051') 8 | # 使用该通道创建一个存根 9 | self.stub = qmt_pb2_grpc.QmtServiceStub(self.channel) 10 | 11 | def protobuf_to_dict(self,obj): 12 | # Base case for recursion termination 13 | if not hasattr(obj, 'DESCRIPTOR'): 14 | return obj 15 | 16 | result = {} 17 | for field in obj.DESCRIPTOR.fields: 18 | value = getattr(obj, field.name) 19 | # Convert repeated message fields to list of dicts 20 | if field.message_type and field.label == field.LABEL_REPEATED: 21 | result[field.name] = [self.protobuf_to_dict(v) for v in value] 22 | # Convert non-repeated message fields to dict 23 | elif field.message_type: 24 | result[field.name] = self.protobuf_to_dict(value) 25 | # Convert repeated scalar fields to list 26 | elif field.label == field.LABEL_REPEATED: 27 | result[field.name] = list(value) 28 | # Convert scalar fields directly 29 | else: 30 | result[field.name] = value 31 | return result 32 | 33 | def assetSync(self): 34 | response= self.stub.GetAsset(qmt_pb2.AssetRequest()) 35 | asset= self.protobuf_to_dict(response) 36 | return asset 37 | 38 | 39 | def getPrice(self,code): 40 | response=self.stub.GetPrice(qmt_pb2.PriceRequest(code=code)) 41 | asset= self.protobuf_to_dict(response) 42 | return asset['last_price'] 43 | 44 | def getInfo(self,code): 45 | response=self.stub.GetDailyInfo(qmt_pb2.DailyInfoRequest(code=code)) 46 | info=self.protobuf_to_dict(response) 47 | info['downLimit']=info['down_limit'] 48 | info['upLimit']=info['up_limit'] 49 | return info 50 | 51 | def OrderBuy(self,code, amount, price=0, strategy='strategy', remark='remark'): 52 | response=self.stub.OrderBuy(qmt_pb2.OrderRequest(code=code, amount=amount, price=price, strategy=strategy, remark=remark)) 53 | seq=self.protobuf_to_dict(response) 54 | return seq['seq'] 55 | 56 | def OrderSell(self,code, amount, price=0, strategy='strategy', remark='remark'): 57 | response=self.stub.OrderSell(qmt_pb2.OrderRequest(code=code, amount=amount, price=price, strategy=strategy, remark=remark)) 58 | seq=self.protobuf_to_dict(response) 59 | return seq 60 | 61 | def QueryOrders(self): 62 | response=self.stub.QueryOrders(qmt_pb2.QueryOrdersRequest()) 63 | orders=self.protobuf_to_dict(response) 64 | return orders 65 | 66 | def CancelOrders(self): 67 | response=self.stub.CancelOrders(qmt_pb2.QueryOrdersRequest()) 68 | return True 69 | 70 | def RetryOrders(self): 71 | response=self.stub.RetryOrders(qmt_pb2.QueryOrdersRequest()) 72 | return True 73 | 74 | def getAsset(self): 75 | response=self.stub.GetAsset(qmt_pb2.AssetRequest()) 76 | asset=self.protobuf_to_dict(response) 77 | return asset 78 | 79 | def GetPositions(self): 80 | response=self.stub.GetPositions(qmt_pb2.AssetRequest()) 81 | positions=self.protobuf_to_dict(response) 82 | return positions 83 | 84 | qclient=qmtClient() 85 | -------------------------------------------------------------------------------- /qmtData.py: -------------------------------------------------------------------------------- 1 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 2 | from xtquant.xttype import StockAccount 3 | from xtquant import xtconstant 4 | from xtquant import xtdata 5 | import requests 6 | from datetime import datetime, timedelta 7 | import json 8 | import random 9 | 10 | def get_price(code): 11 | print(f"正在查询{code}当前价格") 12 | res = xtdata.get_full_tick([code]) 13 | #print(res[code]) # {'timetag': '20240228 11:30:01', 'lastPrice': 1686, 'open': 1688.92, 'high': 1696.57, 'low': 1674.01, 'lastClose': 1689.5, 'amount': 3139981100, 'volume': 18649, 'pvolume': 1864909, 'stockStatus': 0, 'openInt': 13, 'settlementPrice': 0, 'lastSettlementPrice': 0, 'askPrice': [1688.5, 1688.53, 1688.84, 0, 0], 'bidPrice': [1686.12, 1686, 1685.26, 0, 0], 'askVol': [98, 3, 1, 0, 0], 'bidVol': [1, 1, 1, 0, 0]} 14 | print(res[code]['lastPrice']) 15 | return (res[code]['lastPrice']) 16 | 17 | 18 | 19 | def get_daily_info(code): 20 | print(f"正在查询{code}行情信息") 21 | quote=xtdata.get_full_tick([code]) 22 | detail=xtdata.get_instrument_detail(code) 23 | info={ 24 | 'ts_code':code, 25 | 'open':quote[code]['open'], 26 | 'high':quote[code]['high'], 27 | 'low':quote[code]['low'], 28 | 'close':quote[code]['lastPrice'], 29 | 'volume':quote[code]['amount'], 30 | 'amount':quote[code]['volume'], 31 | 'name':detail['InstrumentName'], 32 | 'vwap':0, 33 | 'stop':detail['InstrumentStatus'], 34 | 'up_limit':detail['UpStopPrice'], 35 | 'down_limit':detail['DownStopPrice'], 36 | } 37 | print(info) 38 | return info 39 | 40 | 41 | 42 | def subscribe(xt_trader,code): 43 | # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功 44 | subscribe_result = xt_trader.subscribe(acc) 45 | if subscribe_result != 0: 46 | print('账号订阅失败 %d'%subscribe_result) 47 | 48 | sub=xtdata.subscribe_whole_quote(['SH', 'SZ'], callback=None) 49 | print(1) 50 | xtdata.subscribe_quote(['002624'], period='1d', start_time='', end_time='', count=0, callback=None) 51 | print(2) 52 | try: 53 | x=xtdata.get_market_data(field_list=[], stock_list=['002624'], period='1d', start_time='', end_time='', count=-1, dividend_type='none', fill_data=True) 54 | except: 55 | print("------------------") 56 | print(3) 57 | print(x) 58 | -------------------------------------------------------------------------------- /qmtDownloader.py: -------------------------------------------------------------------------------- 1 | # qmt_server.py 2 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 3 | from xtquant.xttype import StockAccount 4 | from xtquant import xtconstant 5 | from xtquant import xtdata 6 | import requests 7 | from datetime import datetime, timedelta 8 | import json 9 | import random 10 | import time 11 | import config 12 | import pandas as pd 13 | 14 | path = config.path 15 | # session_id为会话编号,策略使用方对于不同的Python策略需要使用不同的会话编号 16 | session_id = random.randint(0,9999999999) 17 | xt_trader = XtQuantTrader(path, session_id) 18 | account = StockAccount(config.account_id,config.account_type) 19 | xt_trader.start() 20 | # 建立交易连接,返回0表示连接成功 21 | connect_result = xt_trader.connect() 22 | if connect_result != 0: 23 | print(connect_result) 24 | import sys 25 | sys.exit('链接失败,程序即将退出 %d'%connect_result) 26 | 27 | ret=xtdata.download_history_data(stock_code='000001.SZ', period='1m', start_time='20240401', end_time='20240410') 28 | data = xtdata.get_local_data(field_list=[], stock_code=['000001.SZ'], period='1m', count=10) 29 | 30 | print(ret) 31 | print(data) -------------------------------------------------------------------------------- /qmtServer.py: -------------------------------------------------------------------------------- 1 | # qmt_server.py 2 | from concurrent import futures 3 | import grpc 4 | import qmt_pb2 5 | import qmt_pb2_grpc 6 | import qmtAccount 7 | import qmtTrader 8 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 9 | from xtquant.xttype import StockAccount 10 | from xtquant import xtconstant 11 | from xtquant import xtdata 12 | import requests 13 | from datetime import datetime, timedelta 14 | import json 15 | import random 16 | import qmtData 17 | import qmtCallback 18 | import time 19 | import config 20 | 21 | class QmtServiceServicer(qmt_pb2_grpc.QmtServiceServicer): 22 | def __init__(self): 23 | # path为mini qmt客户端安装目录下userdata_mini路径 24 | path = config.path 25 | # session_id为会话编号,策略使用方对于不同的Python策略需要使用不同的会话编号 26 | session_id = random.randint(0,9999999999) 27 | xt_trader = XtQuantTrader(path, session_id) 28 | account = StockAccount(config.account_id,config.account_type) 29 | callback = qmtCallback.MyXtQuantTraderCallback() 30 | xt_trader.register_callback(callback) 31 | # 启动交易线程 32 | xt_trader.start() 33 | # 建立交易连接,返回0表示连接成功 34 | connect_result = xt_trader.connect() 35 | if connect_result != 0: 36 | print(connect_result) 37 | import sys 38 | sys.exit('链接失败,程序即将退出 %d'%connect_result) 39 | self.xt_trader=xt_trader 40 | self.account=account 41 | 42 | 43 | 44 | def GetPrice(self, request, context): 45 | code=request.code 46 | price=qmtData.get_price(code) 47 | return qmt_pb2.PriceResponse(last_price=price) 48 | 49 | def GetDailyInfo(self, request, context): 50 | code=request.code 51 | info=qmtData.get_daily_info(code) 52 | return qmt_pb2.DailyInfoResponse(**info) 53 | 54 | def OrderBuy(self, request, context): 55 | code=request.code 56 | amount=request.amount 57 | price=request.price 58 | seq=qmtTrader.order_buy(xt_trader=self.xt_trader,account=self.account,code=code,amount=amount,price=price) 59 | return qmt_pb2.OrderResponse(seq=seq) 60 | 61 | def OrderSell(self, request, context): 62 | code=request.code 63 | amount=request.amount 64 | price=request.price 65 | seq=qmtTrader.order_sell(xt_trader=self.xt_trader,account=self.account,code=code,amount=amount,price=price) 66 | return qmt_pb2.OrderResponse(seq=seq) 67 | 68 | def QueryOrders(self, request, context): 69 | orders=qmtTrader.query_orders(self.xt_trader,self.account) 70 | return qmt_pb2.OrdersResponse(orders=orders) 71 | 72 | def CancelOrders(self, request, context): 73 | qmtTrader.cancel_orders(self.xt_trader,self.account) 74 | return qmt_pb2.OrdersResponse() 75 | 76 | def RetryOrders(self, request, context): 77 | qmtTrader.retry_orders(self.xt_trader,self.account) 78 | return qmt_pb2.OrdersResponse() 79 | 80 | def GetAsset(self, request, context): 81 | asset=qmtAccount.get_asset(self.xt_trader,self.account) 82 | return qmt_pb2.AssetResponse(**asset) 83 | 84 | def GetPositions(self, request, context): 85 | positions=qmtAccount.get_positions(self.xt_trader,self.account) 86 | return qmt_pb2.PositionsResponse(positions=positions) 87 | 88 | 89 | 90 | 91 | def serve(): 92 | server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) 93 | qmt_pb2_grpc.add_QmtServiceServicer_to_server(QmtServiceServicer(), server) 94 | server.add_insecure_port(config.server) 95 | server.start() 96 | print(f"服务已启动!正在监听{config.server}") 97 | server.wait_for_termination() 98 | 99 | if __name__ == '__main__': 100 | serve() -------------------------------------------------------------------------------- /qmtTrader.py: -------------------------------------------------------------------------------- 1 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 2 | from xtquant import xtconstant 3 | from xtquant import xtdata 4 | import time 5 | import qmtData 6 | 7 | def order_buy(xt_trader,account,code,amount,price=0,strategy="Finhack-QMT",remark="autoBuy"): 8 | 9 | if int(price)==9999: 10 | info=qmtData.get_daily_info(code) 11 | lastprice=info['close'] 12 | price=info['up_limit'] 13 | if lastprice*1.020: 19 | seq=xt_trader.order_stock_async(account, code, xtconstant.STOCK_BUY, amount, xtconstant.FIX_PRICE, price, strategy, remark) 20 | else: 21 | seq=xt_trader.order_stock_async(account, code, xtconstant.STOCK_BUY, amount, xtconstant.LATEST_PRICE, price, strategy, remark) 22 | print(f"下单买入{code}共计{amount}股,挂单价{price}") 23 | return seq 24 | 25 | 26 | def order_sell(xt_trader,account,code,amount,price=0,strategy="Finhack-QMT",remark="autoSell"): 27 | if int(price)==-1: 28 | info=qmtData.get_daily_info(code) 29 | lastprice=info['close'] 30 | down_limit=info['down_limit'] 31 | if lastprice*0.98>down_limit: 32 | price=round(lastprice*0.98,2) 33 | else: 34 | price=round(down_limit,2) 35 | seq=xt_trader.order_stock_async(account, code, xtconstant.STOCK_SELL, amount, xtconstant.FIX_PRICE, price, strategy, remark) 36 | elif price>0: 37 | seq=xt_trader.order_stock_async(account, code, xtconstant.STOCK_SELL, amount, xtconstant.FIX_PRICE, price, strategy, remark) 38 | else: 39 | seq=xt_trader.order_stock_async(account, code, xtconstant.STOCK_SELL, amount, xtconstant.LATEST_PRICE, price, strategy,remark) 40 | print(f"下单卖出{code}共计{amount}股,挂单价{price}") 41 | return seq 42 | 43 | def query_orders(xt_trader,account): 44 | o_tmp = xt_trader.query_stock_orders(account, False) 45 | orders=[] 46 | for o in o_tmp: 47 | orders.append({ 48 | "stock_code":o.stock_code, 49 | "order_id":o.order_id, 50 | "order_sysid":o.order_sysid, 51 | "order_time":o.order_time, 52 | "order_type":o.order_type, 53 | "order_volume":o.order_volume, 54 | "price_type":o.price_type, 55 | "price":o.price, 56 | "traded_volume":o.traded_volume, 57 | "traded_price":o.traded_price, 58 | "order_status":o.order_status, 59 | "status_msg":o.status_msg, 60 | "strategy_name":o.strategy_name, 61 | "order_remark":o.order_remark, 62 | #"direction":o.direction, 63 | #"offset_flag":o.offset_flag 64 | }) 65 | print("正在查询订单") 66 | print(orders) 67 | return orders 68 | 69 | 70 | def cancel_orders(xt_trader,account): 71 | print("取消订单") 72 | orders=query_orders(xt_trader,account) 73 | for order in orders: 74 | print(order) 75 | if order['order_status'] not in [53,54,56,57,255]: 76 | xt_trader.cancel_order_stock_async(account, order['order_id']) 77 | pass 78 | 79 | 80 | def retry_orders(xt_trader,account): 81 | print("重新挂单") 82 | orders=query_orders(xt_trader,account) 83 | for order in orders: 84 | if order['order_status'] not in [53,54,56,57,255]: 85 | print(order) 86 | 87 | xt_trader.cancel_order_stock_async(account, order['order_id']) 88 | time.sleep(1) 89 | xt_trader.order_stock_async(account,order['stock_code'], order['order_type'], order['order_volume']-order['traded_volume'], xtconstant.LATEST_PRICE, order['price'], order['strategy_name'],order['order_remark']) 90 | pass 91 | 92 | 93 | # 委托状态(order_status) 94 | # 枚举变量名 值 含义 95 | # xtconstant.ORDER_UNREPORTED 48 未报 96 | # xtconstant.ORDER_WAIT_REPORTING 49 待报 97 | # xtconstant.ORDER_REPORTED 50 已报 98 | # xtconstant.ORDER_REPORTED_CANCEL 51 已报待撤 99 | # xtconstant.ORDER_PARTSUCC_CANCEL 52 部成待撤 100 | # xtconstant.ORDER_PART_CANCEL 53 部撤 101 | # xtconstant.ORDER_CANCELED 54 已撤 102 | # xtconstant.ORDER_PART_SUCC 55 部成 103 | # xtconstant.ORDER_SUCCEEDED 56 已成 104 | # xtconstant.ORDER_JUNK 57 废单 105 | # xtconstant.ORDER_UNKNOWN 255 未知 106 | -------------------------------------------------------------------------------- /qmt_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: qmt.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | # @@protoc_insertion_point(imports) 11 | 12 | _sym_db = _symbol_database.Default() 13 | 14 | 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tqmt.proto\x12\x03qmt\"\x1c\n\x0cPriceRequest\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\"#\n\rPriceResponse\x12\x12\n\nlast_price\x18\x01 \x01(\x01\" \n\x10\x44\x61ilyInfoRequest\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\"\xcc\x01\n\x11\x44\x61ilyInfoResponse\x12\x0f\n\x07ts_code\x18\x01 \x01(\t\x12\x0c\n\x04open\x18\x02 \x01(\x01\x12\x0c\n\x04high\x18\x03 \x01(\x01\x12\x0b\n\x03low\x18\x04 \x01(\x01\x12\r\n\x05\x63lose\x18\x05 \x01(\x01\x12\x0e\n\x06volume\x18\x06 \x01(\x01\x12\x0e\n\x06\x61mount\x18\x07 \x01(\x01\x12\x0c\n\x04name\x18\x08 \x01(\t\x12\x0c\n\x04vwap\x18\t \x01(\x01\x12\x0c\n\x04stop\x18\n \x01(\x05\x12\x10\n\x08up_limit\x18\x0b \x01(\x01\x12\x12\n\ndown_limit\x18\x0c \x01(\x01\"]\n\x0cOrderRequest\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x05\x12\r\n\x05price\x18\x03 \x01(\x01\x12\x10\n\x08strategy\x18\x04 \x01(\t\x12\x0e\n\x06remark\x18\x05 \x01(\t\"\x1c\n\rOrderResponse\x12\x0b\n\x03seq\x18\x01 \x01(\x05\"\x14\n\x12QueryOrdersRequest\"\xad\x02\n\x0bOrderDetail\x12\x12\n\nstock_code\x18\x01 \x01(\t\x12\x10\n\x08order_id\x18\x02 \x01(\t\x12\x13\n\x0border_sysid\x18\x03 \x01(\t\x12\x12\n\norder_time\x18\x04 \x01(\t\x12\x12\n\norder_type\x18\x05 \x01(\x05\x12\x14\n\x0corder_volume\x18\x06 \x01(\x05\x12\x12\n\nprice_type\x18\x07 \x01(\x05\x12\r\n\x05price\x18\x08 \x01(\x01\x12\x15\n\rtraded_volume\x18\t \x01(\x05\x12\x14\n\x0ctraded_price\x18\n \x01(\x01\x12\x14\n\x0corder_status\x18\x0b \x01(\x05\x12\x12\n\nstatus_msg\x18\x0c \x01(\t\x12\x15\n\rstrategy_name\x18\r \x01(\t\x12\x14\n\x0corder_remark\x18\x0e \x01(\t\"2\n\x0eOrdersResponse\x12 \n\x06orders\x18\x01 \x03(\x0b\x32\x10.qmt.OrderDetail\"\x0e\n\x0c\x41ssetRequest\"k\n\rAssetResponse\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x0c\n\x04\x63\x61sh\x18\x02 \x01(\x01\x12\x13\n\x0b\x66rozen_cash\x18\x03 \x01(\x01\x12\x14\n\x0cmarket_value\x18\x04 \x01(\x01\x12\x13\n\x0btotal_value\x18\x05 \x01(\x01\";\n\x11PositionsResponse\x12&\n\tpositions\x18\x01 \x03(\x0b\x32\x13.qmt.PositionDetail\"\xa0\x01\n\x0ePositionDetail\x12\x12\n\nstock_code\x18\x01 \x01(\t\x12\x0e\n\x06volume\x18\x02 \x01(\x05\x12\x16\n\x0e\x63\x61n_use_volume\x18\x03 \x01(\x05\x12\x12\n\nopen_price\x18\x04 \x01(\x01\x12\x14\n\x0cmarket_value\x18\x05 \x01(\x01\x12\x15\n\rfrozen_volume\x18\x06 \x01(\x05\x12\x11\n\tavg_price\x18\x07 \x01(\x01\x32\x9d\x04\n\nQmtService\x12\x33\n\x08GetPrice\x12\x11.qmt.PriceRequest\x1a\x12.qmt.PriceResponse\"\x00\x12?\n\x0cGetDailyInfo\x12\x15.qmt.DailyInfoRequest\x1a\x16.qmt.DailyInfoResponse\"\x00\x12\x33\n\x08OrderBuy\x12\x11.qmt.OrderRequest\x1a\x12.qmt.OrderResponse\"\x00\x12\x34\n\tOrderSell\x12\x11.qmt.OrderRequest\x1a\x12.qmt.OrderResponse\"\x00\x12=\n\x0bQueryOrders\x12\x17.qmt.QueryOrdersRequest\x1a\x13.qmt.OrdersResponse\"\x00\x12>\n\x0c\x43\x61ncelOrders\x12\x17.qmt.QueryOrdersRequest\x1a\x13.qmt.OrdersResponse\"\x00\x12=\n\x0bRetryOrders\x12\x17.qmt.QueryOrdersRequest\x1a\x13.qmt.OrdersResponse\"\x00\x12\x33\n\x08GetAsset\x12\x11.qmt.AssetRequest\x1a\x12.qmt.AssetResponse\"\x00\x12;\n\x0cGetPositions\x12\x11.qmt.AssetRequest\x1a\x16.qmt.PositionsResponse\"\x00\x62\x06proto3') 18 | 19 | 20 | 21 | _PRICEREQUEST = DESCRIPTOR.message_types_by_name['PriceRequest'] 22 | _PRICERESPONSE = DESCRIPTOR.message_types_by_name['PriceResponse'] 23 | _DAILYINFOREQUEST = DESCRIPTOR.message_types_by_name['DailyInfoRequest'] 24 | _DAILYINFORESPONSE = DESCRIPTOR.message_types_by_name['DailyInfoResponse'] 25 | _ORDERREQUEST = DESCRIPTOR.message_types_by_name['OrderRequest'] 26 | _ORDERRESPONSE = DESCRIPTOR.message_types_by_name['OrderResponse'] 27 | _QUERYORDERSREQUEST = DESCRIPTOR.message_types_by_name['QueryOrdersRequest'] 28 | _ORDERDETAIL = DESCRIPTOR.message_types_by_name['OrderDetail'] 29 | _ORDERSRESPONSE = DESCRIPTOR.message_types_by_name['OrdersResponse'] 30 | _ASSETREQUEST = DESCRIPTOR.message_types_by_name['AssetRequest'] 31 | _ASSETRESPONSE = DESCRIPTOR.message_types_by_name['AssetResponse'] 32 | _POSITIONSRESPONSE = DESCRIPTOR.message_types_by_name['PositionsResponse'] 33 | _POSITIONDETAIL = DESCRIPTOR.message_types_by_name['PositionDetail'] 34 | PriceRequest = _reflection.GeneratedProtocolMessageType('PriceRequest', (_message.Message,), { 35 | 'DESCRIPTOR' : _PRICEREQUEST, 36 | '__module__' : 'qmt_pb2' 37 | # @@protoc_insertion_point(class_scope:qmt.PriceRequest) 38 | }) 39 | _sym_db.RegisterMessage(PriceRequest) 40 | 41 | PriceResponse = _reflection.GeneratedProtocolMessageType('PriceResponse', (_message.Message,), { 42 | 'DESCRIPTOR' : _PRICERESPONSE, 43 | '__module__' : 'qmt_pb2' 44 | # @@protoc_insertion_point(class_scope:qmt.PriceResponse) 45 | }) 46 | _sym_db.RegisterMessage(PriceResponse) 47 | 48 | DailyInfoRequest = _reflection.GeneratedProtocolMessageType('DailyInfoRequest', (_message.Message,), { 49 | 'DESCRIPTOR' : _DAILYINFOREQUEST, 50 | '__module__' : 'qmt_pb2' 51 | # @@protoc_insertion_point(class_scope:qmt.DailyInfoRequest) 52 | }) 53 | _sym_db.RegisterMessage(DailyInfoRequest) 54 | 55 | DailyInfoResponse = _reflection.GeneratedProtocolMessageType('DailyInfoResponse', (_message.Message,), { 56 | 'DESCRIPTOR' : _DAILYINFORESPONSE, 57 | '__module__' : 'qmt_pb2' 58 | # @@protoc_insertion_point(class_scope:qmt.DailyInfoResponse) 59 | }) 60 | _sym_db.RegisterMessage(DailyInfoResponse) 61 | 62 | OrderRequest = _reflection.GeneratedProtocolMessageType('OrderRequest', (_message.Message,), { 63 | 'DESCRIPTOR' : _ORDERREQUEST, 64 | '__module__' : 'qmt_pb2' 65 | # @@protoc_insertion_point(class_scope:qmt.OrderRequest) 66 | }) 67 | _sym_db.RegisterMessage(OrderRequest) 68 | 69 | OrderResponse = _reflection.GeneratedProtocolMessageType('OrderResponse', (_message.Message,), { 70 | 'DESCRIPTOR' : _ORDERRESPONSE, 71 | '__module__' : 'qmt_pb2' 72 | # @@protoc_insertion_point(class_scope:qmt.OrderResponse) 73 | }) 74 | _sym_db.RegisterMessage(OrderResponse) 75 | 76 | QueryOrdersRequest = _reflection.GeneratedProtocolMessageType('QueryOrdersRequest', (_message.Message,), { 77 | 'DESCRIPTOR' : _QUERYORDERSREQUEST, 78 | '__module__' : 'qmt_pb2' 79 | # @@protoc_insertion_point(class_scope:qmt.QueryOrdersRequest) 80 | }) 81 | _sym_db.RegisterMessage(QueryOrdersRequest) 82 | 83 | OrderDetail = _reflection.GeneratedProtocolMessageType('OrderDetail', (_message.Message,), { 84 | 'DESCRIPTOR' : _ORDERDETAIL, 85 | '__module__' : 'qmt_pb2' 86 | # @@protoc_insertion_point(class_scope:qmt.OrderDetail) 87 | }) 88 | _sym_db.RegisterMessage(OrderDetail) 89 | 90 | OrdersResponse = _reflection.GeneratedProtocolMessageType('OrdersResponse', (_message.Message,), { 91 | 'DESCRIPTOR' : _ORDERSRESPONSE, 92 | '__module__' : 'qmt_pb2' 93 | # @@protoc_insertion_point(class_scope:qmt.OrdersResponse) 94 | }) 95 | _sym_db.RegisterMessage(OrdersResponse) 96 | 97 | AssetRequest = _reflection.GeneratedProtocolMessageType('AssetRequest', (_message.Message,), { 98 | 'DESCRIPTOR' : _ASSETREQUEST, 99 | '__module__' : 'qmt_pb2' 100 | # @@protoc_insertion_point(class_scope:qmt.AssetRequest) 101 | }) 102 | _sym_db.RegisterMessage(AssetRequest) 103 | 104 | AssetResponse = _reflection.GeneratedProtocolMessageType('AssetResponse', (_message.Message,), { 105 | 'DESCRIPTOR' : _ASSETRESPONSE, 106 | '__module__' : 'qmt_pb2' 107 | # @@protoc_insertion_point(class_scope:qmt.AssetResponse) 108 | }) 109 | _sym_db.RegisterMessage(AssetResponse) 110 | 111 | PositionsResponse = _reflection.GeneratedProtocolMessageType('PositionsResponse', (_message.Message,), { 112 | 'DESCRIPTOR' : _POSITIONSRESPONSE, 113 | '__module__' : 'qmt_pb2' 114 | # @@protoc_insertion_point(class_scope:qmt.PositionsResponse) 115 | }) 116 | _sym_db.RegisterMessage(PositionsResponse) 117 | 118 | PositionDetail = _reflection.GeneratedProtocolMessageType('PositionDetail', (_message.Message,), { 119 | 'DESCRIPTOR' : _POSITIONDETAIL, 120 | '__module__' : 'qmt_pb2' 121 | # @@protoc_insertion_point(class_scope:qmt.PositionDetail) 122 | }) 123 | _sym_db.RegisterMessage(PositionDetail) 124 | 125 | _QMTSERVICE = DESCRIPTOR.services_by_name['QmtService'] 126 | if _descriptor._USE_C_DESCRIPTORS == False: 127 | 128 | DESCRIPTOR._options = None 129 | _PRICEREQUEST._serialized_start=18 130 | _PRICEREQUEST._serialized_end=46 131 | _PRICERESPONSE._serialized_start=48 132 | _PRICERESPONSE._serialized_end=83 133 | _DAILYINFOREQUEST._serialized_start=85 134 | _DAILYINFOREQUEST._serialized_end=117 135 | _DAILYINFORESPONSE._serialized_start=120 136 | _DAILYINFORESPONSE._serialized_end=324 137 | _ORDERREQUEST._serialized_start=326 138 | _ORDERREQUEST._serialized_end=419 139 | _ORDERRESPONSE._serialized_start=421 140 | _ORDERRESPONSE._serialized_end=449 141 | _QUERYORDERSREQUEST._serialized_start=451 142 | _QUERYORDERSREQUEST._serialized_end=471 143 | _ORDERDETAIL._serialized_start=474 144 | _ORDERDETAIL._serialized_end=775 145 | _ORDERSRESPONSE._serialized_start=777 146 | _ORDERSRESPONSE._serialized_end=827 147 | _ASSETREQUEST._serialized_start=829 148 | _ASSETREQUEST._serialized_end=843 149 | _ASSETRESPONSE._serialized_start=845 150 | _ASSETRESPONSE._serialized_end=952 151 | _POSITIONSRESPONSE._serialized_start=954 152 | _POSITIONSRESPONSE._serialized_end=1013 153 | _POSITIONDETAIL._serialized_start=1016 154 | _POSITIONDETAIL._serialized_end=1176 155 | _QMTSERVICE._serialized_start=1179 156 | _QMTSERVICE._serialized_end=1720 157 | # @@protoc_insertion_point(module_scope) 158 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 由于finhack框架是Linux下的,而QMT是运行在Windows下的,因此这两个之间需要进行通信,所以封装了一个适合低频策略的简单的QMT接口,主要就是实现了持仓查询和买卖接口。 2 | 3 | 具体都需要啥依赖自己看着装吧,缺啥装啥,然后由于通信是使用grpc,因此必须安装下面这俩 4 | 5 | pip install grpcio grpcio-tools 6 | 7 | 里面的qmt_pb2和qmt_pb2_grpc是通过grpc_tools生成的,不同版本可能会有问题,因此建议装完grpc_tools重新使用下面命令生成一下 8 | 9 | python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. .\qmt.proto 10 | 11 | 在config.py里配置好QMT的路径,以及资金账号,运行 python qmtServer.py 启动服务,其它机器使用qmtClient调用 12 | 13 | 代码测试过,问题不大,有问题自己解决吧,就不过多演示了 14 | 15 | from qmtClient import qclient 16 | 17 | print('------------------------') 18 | asset=qclient.getAsset() 19 | print(asset) 20 | price=qclient.getPrice("002624.SZ") 21 | print(price) 22 | info=qclient.getInfo("002624.SZ") 23 | print(info) 24 | 25 | seq=qclient.OrderBuy(code="002624.SZ",amount=100,price=11)#限价买入 26 | seq=qclient.OrderBuy(code="002624.SZ",amount=100)#市价买入 27 | seq=qclient.RetryOrders() 28 | seq=qclient.CancelOrders() 29 | 30 | seq=qclient.OrderSell(code="002624.SZ",amount=100,price=13)#限价卖出 31 | seq=qclient.OrderSell(code="002624.SZ",amount=100)#市价卖出 32 | seq=qclient.CancelOrders() 33 | seq=qclient.RetryOrders() 34 | 35 | orders=qclient.GetPositions() 36 | print(orders) 37 | 38 | positions=qclient.GetPositions() 39 | print(positions) 40 | 41 | ![image](https://github.com/FinHackCN/finhack-qmt/assets/6196607/89c2cbc1-3a77-40e1-8e6e-b51e6c284b68) 42 | -------------------------------------------------------------------------------- /xtquant/IPythonApiClient.cp310-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/IPythonApiClient.cp310-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/IPythonApiClient.cp311-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/IPythonApiClient.cp311-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/IPythonApiClient.cp36-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/IPythonApiClient.cp36-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/IPythonApiClient.cp37-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/IPythonApiClient.cp37-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/IPythonApiClient.cp38-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/IPythonApiClient.cp38-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/IPythonApiClient.cp39-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/IPythonApiClient.cp39-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__init__.py -------------------------------------------------------------------------------- /xtquant/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/__pycache__/xtconstant.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__pycache__/xtconstant.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/__pycache__/xtdata.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__pycache__/xtdata.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/__pycache__/xtdata_config.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__pycache__/xtdata_config.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/__pycache__/xttrader.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__pycache__/xttrader.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/__pycache__/xttype.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/__pycache__/xttype.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/doc/xtdata.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/doc/xtdata.pdf -------------------------------------------------------------------------------- /xtquant/doc/xttrader.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/doc/xttrader.pdf -------------------------------------------------------------------------------- /xtquant/libeay32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/libeay32.dll -------------------------------------------------------------------------------- /xtquant/log4cxx.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/log4cxx.dll -------------------------------------------------------------------------------- /xtquant/msvcp140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/msvcp140.dll -------------------------------------------------------------------------------- /xtquant/qmttools/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from . import functions 3 | from . import contextinfo 4 | from . import stgframe 5 | 6 | from .stgentry import run_file as run_strategy_file 7 | -------------------------------------------------------------------------------- /xtquant/qmttools/contextinfo.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import datetime as dt 4 | import threading 5 | 6 | import pandas as pd 7 | 8 | from . import functions 9 | 10 | class ContextInfo: 11 | def __init__(this): 12 | #base 13 | this.request_id = '' 14 | this.quote_mode = '' #'realtime' 'history' 'all' 15 | this.trade_mode = '' #'simulation' 'trading' 'backtest' 16 | this.title = '' 17 | this.user_script = '' 18 | 19 | #quote 20 | this.stock_code = '' 21 | this.stockcode = '' 22 | this.market = '' 23 | this.period = '' 24 | this.start_time = '' 25 | this.end_time = '' 26 | this.dividend_type = '' 27 | 28 | #bar frame 29 | this.timelist = [] 30 | this.barpos = -1 31 | this.lastrunbarpos = -1 32 | this.result = {} 33 | this.push_result = {} 34 | 35 | #backtest 36 | this.asset = 1000000.0 # 初始资金 37 | this.margin_ratio = 0.05 # 保证金比例 38 | this.slippage_type = 2 # 滑点类型 39 | this.slippage = 0.0 # 滑点值 40 | this.max_vol_rate = 1.0 # 最大成交比例 41 | this.comsisson_type = 0 # 手续费类型 42 | this.open_tax = 0.0 # 买入印花税 43 | this.close_tax = 0.0 # 卖出印花税 44 | this.min_commission = 0.0 # 最低佣金 45 | this.open_commission = 0.0 # 买入佣金 46 | this.close_commission = 0.0 # 平昨佣金 47 | this.close_today_commission = 0.0 # 平今佣金 48 | this.benchmark = '000300.SH' # 业绩基准 49 | 50 | this.capital = None 51 | this.do_back_test = None 52 | 53 | #reserved 54 | this.refresh_rate = None 55 | this.fund_name = None 56 | this.link_fund_name = None 57 | this.data_info_level = None 58 | this.time_tick_size = None 59 | return 60 | 61 | ### qmt strategy frame ### 62 | 63 | def init(this): 64 | return 65 | 66 | def after_init(this): 67 | return 68 | 69 | def handlebar(this): 70 | return 71 | 72 | def stop(this): 73 | return 74 | 75 | def account_callback(this, account_info): 76 | return 77 | 78 | def order_callback(this, order_info): 79 | return 80 | 81 | def deal_callback(this, deal_info): 82 | return 83 | 84 | def position_callback(this, position_info): 85 | return 86 | 87 | def orderError_callback(this, passorder_info, msg): 88 | return 89 | 90 | ### qmt functions - bar ### 91 | 92 | def is_last_bar(this): 93 | return this.barpos >= len(this.timelist) - 1 94 | 95 | def is_new_bar(this): 96 | return this.barpos > this.lastbarpos 97 | 98 | def get_bar_timetag(this, barpos = None): 99 | try: 100 | return this.timelist[barpos] if barpos is not None else this.timelist[this.barpos] 101 | except Exception as e: 102 | return None 103 | 104 | ### qmt functions - graph ### 105 | 106 | def paint(this, name, value, index = -1, drawstyle = 0, color = '', limit = ''): 107 | vp = {str(this.get_bar_timetag()): value} 108 | 109 | if name not in this.result: 110 | this.result[name] = {} 111 | this.result[name].update(vp) 112 | 113 | if name not in this.push_result: 114 | this.push_result[name] = {} 115 | this.push_result[name].update(vp) 116 | return 117 | 118 | ### qmt functions - quote ### 119 | 120 | def subscribe_quote(this, stock_code = '', period = '', dividend_type = '', result_type = '', callback = None): 121 | if not stock_code: 122 | stock_code = this.stock_code 123 | if not period or period == 'follow': 124 | period = this.period 125 | if not dividend_type or dividend_type == 'follow': 126 | dividend_type = this.dividend_type 127 | return functions.subscribe_quote(stock_code, period, dividend_type, result_type, callback) 128 | 129 | def subscribe_whole_quote(this, code_list, callback = None): 130 | return functions.subscribe_whole_quote(code_list, callback) 131 | 132 | def unsubscribe_quote(this, subscribe_id): 133 | return functions.unsubscribe_quote(subscribe_id) 134 | 135 | def get_market_data( 136 | this, fields = [], stock_code = [], start_time = '', end_time = '' 137 | , skip_paused = True, period = '', dividend_type = '', count = -1 138 | ): 139 | if not stock_code: 140 | stock_code = [this.stock_code] 141 | if not period or period == 'follow': 142 | period = this.period 143 | if not dividend_type or dividend_type == 'follow': 144 | dividend_type = this.dividend_type 145 | if period != 'tick' and count == -1 and len(fields) == 1: 146 | if not end_time or end_time == 'follow': 147 | if this.barpos >= 0: 148 | end_time = functions.timetag_to_datetime(this.get_bar_timetag(this.barpos)) 149 | count = -2 150 | if period == 'tick' and count == -1 and len(fields) == 1 and start_time == '' and end_time == '': 151 | count = -2 152 | 153 | return functions.get_market_data( 154 | fields, stock_code, start_time, end_time 155 | , skip_paused, period, dividend_type, count 156 | ) 157 | 158 | def get_market_data_ex( 159 | this, fields = [], stock_code = [], period = '' 160 | , start_time = '', end_time = '', count = -1 161 | , dividend_type = '', fill_data = True, subscribe = True 162 | ): 163 | if not stock_code: 164 | stock_code = [this.stock_code] 165 | if not period or period == 'follow': 166 | period = this.period 167 | if not dividend_type or dividend_type == 'follow': 168 | dividend_type = this.dividend_type 169 | 170 | return functions.get_market_data_ex( 171 | fields, stock_code, period 172 | , start_time, end_time, count 173 | , dividend_type, fill_data, subscribe 174 | ) 175 | 176 | def get_full_tick(this, stock_code = []): 177 | if not stock_code: 178 | stock_code = [this.stock_code] 179 | return functions.get_full_tick(stock_code) 180 | 181 | def get_divid_factors(this, stock_code = '', date = None): 182 | if not stock_code: 183 | stock_code = this.stock_code 184 | return functions.get_divid_factors(stock_code, date) 185 | 186 | ### qmt functions - finance ### 187 | 188 | def get_financial_data(this, field_list, stock_list, start_date, end_date, report_type = 'announce_time'): 189 | raise 'not implemented, use get_raw_financial_data instead' 190 | return 191 | 192 | def get_raw_financial_data(this, field_list, stock_list, start_date, end_date, report_type = 'announce_time'): 193 | return functions.get_raw_financial_data(field_list, stock_list, start_date, end_date, report_type) 194 | 195 | ### qmt functions - static ### 196 | 197 | def get_instrument_detail(this, stock_code = ''): 198 | if not stock_code: 199 | stock_code = this.stock_code 200 | return functions.get_instrument_detail(stock_code) 201 | 202 | get_instrumentdetail = get_instrument_detail # compat 203 | 204 | def get_trading_dates(this, stock_code, start_date, end_date, count, period = '1d'): 205 | return functions.get_trading_dates(stock_code, start_date, end_date, count, period) 206 | 207 | def get_stock_list_in_sector(this, sector_name): 208 | return functions.get_stock_list_in_sector(sector_name) 209 | 210 | def passorder(this, opType, orderType, accountid, orderCode, prType, modelprice, volume): 211 | return functions._passorder_impl(opType, orderType, accountid, orderCode, prType, modelprice, volume, this.get_bar_timetag(), this.request_id) 212 | 213 | def set_auto_trade_callback(this, enable): 214 | return functions._set_auto_trade_callback_impl(enable, this.request_id) 215 | 216 | def set_account(this, accountid): 217 | return functions.set_account(accountid, this.request_id) 218 | 219 | ### private ### 220 | 221 | def trade_callback(this, type, result, error): 222 | class DetailData(object): 223 | def __init__(self, _obj): 224 | if _obj: 225 | self.__dict__.update(_obj) 226 | 227 | if type == 'accountcallback': 228 | this.account_callback(DetailData(result)) 229 | elif type == 'ordercallback': 230 | this.order_callback(DetailData(result)) 231 | elif type == 'dealcallback': 232 | this.deal_callback(DetailData(result)) 233 | elif type == 'positioncallback': 234 | this.position_callback(DetailData(result)) 235 | elif type == 'ordererrorcallback': 236 | this.orderError_callback(DetailData(result.get('passorderArg')), result.get('strMsg')) 237 | 238 | return 239 | 240 | def register_callback(this, reqid): 241 | functions.register_external_resp_callback(reqid, this.trade_callback) 242 | return 243 | 244 | def get_callback_cache(this, type): 245 | return functions._get_callback_cache_impl(type, this.request_id) 246 | -------------------------------------------------------------------------------- /xtquant/qmttools/stgentry.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import os, sys 4 | import types 5 | 6 | from .functions import * 7 | from .contextinfo import ContextInfo 8 | from .stgframe import StrategyLoader 9 | 10 | 11 | def run_file(user_script, param = {}): 12 | pypath = param.get('pythonpath') 13 | if pypath: 14 | lib_search = [os.path.abspath(p) for p in pypath.split(';')] 15 | sys.path = lib_search + [p for p in sys.path if p not in lib_search] 16 | 17 | user_module = compile(open(user_script, 'rb').read(), user_script, 'exec', optimize = 2) 18 | #print({'user_module': user_module}) 19 | 20 | user_locals = {} 21 | 22 | try: 23 | pywentrance = param.get('pywentrance', '') 24 | user_variable = compile(open(os.path.join(pywentrance, "..", "user_config.py"), "rb").read(), 25 | "user_config.py", 'exec', optimize=2) 26 | exec(user_variable, globals(), user_locals) 27 | except Exception as e: 28 | pass 29 | 30 | exec(user_module, globals(), user_locals) 31 | #print('user_locals', user_locals) 32 | 33 | globals().update(user_locals) 34 | 35 | _C = ContextInfo() 36 | _C._param = param 37 | _C.user_script = user_script 38 | 39 | def try_set_func(C, func_name): 40 | func = user_locals.get(func_name) 41 | if func: 42 | C.__setattr__(func_name, types.MethodType(func, C)) 43 | return 44 | 45 | try_set_func(_C, 'init') 46 | try_set_func(_C, 'after_init') 47 | try_set_func(_C, 'handlebar') 48 | try_set_func(_C, 'stop') 49 | 50 | try_set_func(_C, 'account_callback') 51 | try_set_func(_C, 'order_callback') 52 | try_set_func(_C, 'deal_callback') 53 | try_set_func(_C, 'position_callback') 54 | try_set_func(_C, 'orderError_callback') 55 | 56 | loader = StrategyLoader() 57 | 58 | loader.C = _C 59 | 60 | loader.init() 61 | loader.start() 62 | loader.run() 63 | loader.stop() 64 | loader.shutdown() 65 | 66 | return 67 | -------------------------------------------------------------------------------- /xtquant/qmttools/stgframe.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | from . import contextinfo 4 | from . import functions 5 | 6 | import os 7 | import uuid 8 | 9 | from xtquant import xtdata 10 | from xtquant import xtbson as bson 11 | from xtquant import xtdata_config 12 | 13 | class StrategyLoader: 14 | def __init__(this): 15 | this.C = None 16 | this.main_quote_subid = 0 17 | return 18 | 19 | def init(this): 20 | C = this.C 21 | 22 | C.guid = C._param.get('guid', str(uuid.uuid4())) 23 | C.request_id = C._param.get('requestid', C.guid) 24 | C.quote_mode = C._param.get('quote_mode', 'history') #'realtime' 'history' 'all' 25 | C.trade_mode = C._param.get('trade_mode', 'backtest') #'simulation' 'trading' 'backtest' 26 | C.title = C._param.get('title', '') 27 | 28 | C.stock_code = C._param.get('stock_code', '') 29 | C.period = C._param.get('period', '') 30 | if type(C.period) == int: 31 | C.period = { 32 | 0 :'tick' 33 | , 60000 :'1m' 34 | , 180000 :'3m' 35 | , 300000 :'5m' 36 | , 600000 :'10m' 37 | , 900000 :'15m' 38 | , 1800000 :'30m' 39 | , 3600000 :'1h' 40 | , 86400000 :'1d' 41 | , 604800000 :'1w' 42 | , 2592000000 :'1mon' 43 | , 7776000000 :'1q' 44 | , 15552000000 :'1hy' 45 | , 31536000000 :'1y' 46 | }.get(C.period, '') 47 | C.dividend_type = C._param.get('dividend_type', 'none') 48 | 49 | functions._request_id = C.request_id 50 | xtdata_config.client_guid = C._param.get('clientguid') 51 | 52 | if 1: #register 53 | this.create_formula() 54 | 55 | C.init() 56 | 57 | if 1: #fix param 58 | if '.' in C.stock_code: 59 | pos = C.stock_code.rfind('.') 60 | C.stockcode = C.stock_code[0:pos] 61 | C.market = C.stock_code[pos + 1:].upper() 62 | 63 | if C.stockcode and C.market: 64 | C.stock_code = C.stockcode + '.' + C.market 65 | C.period = C.period.lower() 66 | 67 | if 1: #create view 68 | if not C._param.get('requestid'): 69 | if not C.title: 70 | C.title = os.path.basename(os.path.abspath(C.user_script).replace('.py','')) 71 | this.create_view(C.title) 72 | 73 | if 1: #post initcomplete 74 | init_result = {} 75 | 76 | config_ar = ['request_id', 'quote_mode', 'trade_mode'] 77 | init_result['config'] = {ar: C.__getattribute__(ar) for ar in config_ar} 78 | 79 | quote_ar = [ 80 | 'stock_code', 'stockcode', 'market', 'period' 81 | , 'start_time', 'end_time', 'dividend_type' 82 | ] 83 | init_result['quote'] = {ar: C.__getattribute__(ar) for ar in quote_ar} 84 | 85 | trade_ar = [] 86 | init_result['trade'] = {ar: C.__getattribute__(ar) for ar in trade_ar} 87 | 88 | backtest_ar = [ 89 | 'asset', 'margin_ratio', 'slippage_type', 'slippage' 90 | , 'max_vol_rate', 'comsisson_type', 'open_tax', 'close_tax' 91 | , 'min_commission', 'open_commission', 'close_commission' 92 | , 'close_today_commission', 'benchmark' 93 | ] 94 | init_result['backtest'] = {ar: C.__getattribute__(ar) for ar in backtest_ar} 95 | 96 | this.call_formula('initcomplete', init_result) 97 | 98 | if 1: 99 | this.C.register_callback(0) 100 | return 101 | 102 | def shutdown(this): 103 | return 104 | 105 | def start(this): 106 | C = this.C 107 | 108 | if C.quote_mode in ['history', 'all']: 109 | this.load_main_history() 110 | 111 | C.after_init() 112 | this.run_bar() 113 | 114 | if C.quote_mode in ['realtime', 'all']: 115 | this.load_main_realtime() 116 | return 117 | 118 | def stop(this): 119 | if this.main_quote_subid: 120 | xtdata.unsubscribe_quote(this.main_quote_subid) 121 | 122 | this.C.stop() 123 | return 124 | 125 | def run(this): 126 | C = this.C 127 | 128 | if C.quote_mode in ['realtime', 'all']: 129 | xtdata.run() 130 | return 131 | 132 | def load_main_history(this): 133 | C = this.C 134 | 135 | data = xtdata.get_market_data_ex( 136 | field_list = ['time'], stock_list = [C.stock_code], period = C.period 137 | , start_time = '', end_time = '', count = -1 138 | , fill_data = False 139 | ) 140 | 141 | C.timelist = list(data[C.stock_code]['time']) 142 | return 143 | 144 | def load_main_realtime(this): 145 | C = this.C 146 | 147 | def on_data(data): 148 | data = data.get(C.stock_code, []) 149 | if data: 150 | tt = data[-1]['time'] 151 | this.on_main_quote(tt) 152 | return 153 | 154 | this.main_quote_subid = xtdata.subscribe_quote( 155 | stock_code = C.stock_code, period = C.period 156 | , start_time = '', end_time = '', count = 0 157 | , callback = on_data 158 | ) 159 | return 160 | 161 | def on_main_quote(this, timetag): 162 | if not this.C.timelist or this.C.timelist[-1] < timetag: 163 | this.C.timelist.append(timetag) 164 | this.run_bar() 165 | return 166 | 167 | def run_bar(this): 168 | C = this.C 169 | 170 | push_timelist = [] 171 | for i in range(max(C.lastrunbarpos, 0), len(C.timelist)): 172 | C.barpos = i 173 | bartime = C.timelist[i] 174 | push_timelist.append(bartime) 175 | 176 | this.call_formula('runbar', {'timelist': [bartime]}) 177 | 178 | C.handlebar() 179 | C.lastrunbarpos = i 180 | 181 | if 1: 182 | push_result = {} 183 | push_result['timelist'] = push_timelist 184 | push_result['outputs'] = C.push_result 185 | C.push_result = {} 186 | this.call_formula('index', push_result) 187 | return 188 | 189 | def create_formula(this): 190 | C = this.C 191 | client = xtdata.get_client() 192 | data = {'formulaname': C.guid} 193 | client.createFormula(C.request_id, bson.BSON.encode(data)) 194 | return 195 | 196 | def call_formula(this, func, data): 197 | C = this.C 198 | client = xtdata.get_client() 199 | bresult = client.callFormula(C.request_id, func, bson.BSON.encode(data)) 200 | return bson.BSON.decode(bresult) 201 | 202 | def create_view(this, title): 203 | C = this.C 204 | client = xtdata.get_client() 205 | data = {'viewtype': 0,'title':title, 'groupid':-1,'stockcode':C.market + C.stockcode,'period':C.period} 206 | client.createView(C.request_id, bson.BSON.encode(data)) 207 | return 208 | 209 | -------------------------------------------------------------------------------- /xtquant/ssleay32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/ssleay32.dll -------------------------------------------------------------------------------- /xtquant/vcruntime140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/vcruntime140.dll -------------------------------------------------------------------------------- /xtquant/xtbson/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | 4 | if sys.version_info.major == 3 and sys.version_info.minor == 6: 5 | from .bson36 import * 6 | else: 7 | from .bson37 import * 8 | -------------------------------------------------------------------------------- /xtquant/xtbson/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/_helpers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/_helpers.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/binary.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/binary.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/code.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/code.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/codec_options.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/codec_options.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/dbref.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/dbref.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/decimal128.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/decimal128.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/errors.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/errors.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/int64.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/int64.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/max_key.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/max_key.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/min_key.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/min_key.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/objectid.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/objectid.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/regex.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/regex.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/son.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/son.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/timestamp.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/timestamp.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/__pycache__/tz_util.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/__pycache__/tz_util.cpython-36.pyc -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/_cbson.cp36-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson36/_cbson.cp36-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/_helpers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Setstate and getstate functions for objects with __slots__, allowing 16 | compatibility with default pickling protocol 17 | """ 18 | 19 | 20 | def _setstate_slots(self, state): 21 | for slot, value in state.items(): 22 | setattr(self, slot, value) 23 | 24 | 25 | def _mangle_name(name, prefix): 26 | if name.startswith("__"): 27 | prefix = "_" + prefix 28 | else: 29 | prefix = "" 30 | return prefix + name 31 | 32 | 33 | def _getstate_slots(self): 34 | prefix = self.__class__.__name__ 35 | ret = dict() 36 | for name in self.__slots__: 37 | mangled_name = _mangle_name(name, prefix) 38 | if hasattr(self, mangled_name): 39 | ret[mangled_name] = getattr(self, mangled_name) 40 | return ret 41 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/code.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing JavaScript code in BSON. 16 | """ 17 | 18 | from collections.abc import Mapping as _Mapping 19 | 20 | 21 | class Code(str): 22 | """BSON's JavaScript code type. 23 | 24 | Raises :class:`TypeError` if `code` is not an instance of 25 | :class:`basestring` (:class:`str` in python 3) or `scope` 26 | is not ``None`` or an instance of :class:`dict`. 27 | 28 | Scope variables can be set by passing a dictionary as the `scope` 29 | argument or by using keyword arguments. If a variable is set as a 30 | keyword argument it will override any setting for that variable in 31 | the `scope` dictionary. 32 | 33 | :Parameters: 34 | - `code`: A string containing JavaScript code to be evaluated or another 35 | instance of Code. In the latter case, the scope of `code` becomes this 36 | Code's :attr:`scope`. 37 | - `scope` (optional): dictionary representing the scope in which 38 | `code` should be evaluated - a mapping from identifiers (as 39 | strings) to values. Defaults to ``None``. This is applied after any 40 | scope associated with a given `code` above. 41 | - `**kwargs` (optional): scope variables can also be passed as 42 | keyword arguments. These are applied after `scope` and `code`. 43 | 44 | .. versionchanged:: 3.4 45 | The default value for :attr:`scope` is ``None`` instead of ``{}``. 46 | 47 | """ 48 | 49 | _type_marker = 13 50 | 51 | def __new__(cls, code, scope=None, **kwargs): 52 | if not isinstance(code, str): 53 | raise TypeError("code must be an instance of str") 54 | 55 | self = str.__new__(cls, code) 56 | 57 | try: 58 | self.__scope = code.scope 59 | except AttributeError: 60 | self.__scope = None 61 | 62 | if scope is not None: 63 | if not isinstance(scope, _Mapping): 64 | raise TypeError("scope must be an instance of dict") 65 | if self.__scope is not None: 66 | self.__scope.update(scope) 67 | else: 68 | self.__scope = scope 69 | 70 | if kwargs: 71 | if self.__scope is not None: 72 | self.__scope.update(kwargs) 73 | else: 74 | self.__scope = kwargs 75 | 76 | return self 77 | 78 | @property 79 | def scope(self): 80 | """Scope dictionary for this instance or ``None``.""" 81 | return self.__scope 82 | 83 | def __repr__(self): 84 | return "Code(%s, %r)" % (str.__repr__(self), self.__scope) 85 | 86 | def __eq__(self, other): 87 | if isinstance(other, Code): 88 | return (self.__scope, str(self)) == (other.__scope, str(other)) 89 | return False 90 | 91 | __hash__ = None 92 | 93 | def __ne__(self, other): 94 | return not self == other 95 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/dbref.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for manipulating DBRefs (references to MongoDB documents).""" 16 | 17 | from copy import deepcopy 18 | 19 | from ._helpers import _getstate_slots, _setstate_slots 20 | from .son import SON 21 | 22 | 23 | class DBRef(object): 24 | """A reference to a document stored in MongoDB.""" 25 | 26 | __slots__ = "__collection", "__id", "__database", "__kwargs" 27 | __getstate__ = _getstate_slots 28 | __setstate__ = _setstate_slots 29 | # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. 30 | _type_marker = 100 31 | 32 | def __init__(self, collection, id, database=None, _extra={}, **kwargs): 33 | """Initialize a new :class:`DBRef`. 34 | 35 | Raises :class:`TypeError` if `collection` or `database` is not 36 | an instance of :class:`basestring` (:class:`str` in python 3). 37 | `database` is optional and allows references to documents to work 38 | across databases. Any additional keyword arguments will create 39 | additional fields in the resultant embedded document. 40 | 41 | :Parameters: 42 | - `collection`: name of the collection the document is stored in 43 | - `id`: the value of the document's ``"_id"`` field 44 | - `database` (optional): name of the database to reference 45 | - `**kwargs` (optional): additional keyword arguments will 46 | create additional, custom fields 47 | 48 | .. seealso:: The MongoDB documentation on `dbrefs `_. 49 | """ 50 | if not isinstance(collection, str): 51 | raise TypeError("collection must be an instance of str") 52 | if database is not None and not isinstance(database, str): 53 | raise TypeError("database must be an instance of str") 54 | 55 | self.__collection = collection 56 | self.__id = id 57 | self.__database = database 58 | kwargs.update(_extra) 59 | self.__kwargs = kwargs 60 | 61 | @property 62 | def collection(self): 63 | """Get the name of this DBRef's collection.""" 64 | return self.__collection 65 | 66 | @property 67 | def id(self): 68 | """Get this DBRef's _id.""" 69 | return self.__id 70 | 71 | @property 72 | def database(self): 73 | """Get the name of this DBRef's database. 74 | 75 | Returns None if this DBRef doesn't specify a database. 76 | """ 77 | return self.__database 78 | 79 | def __getattr__(self, key): 80 | try: 81 | return self.__kwargs[key] 82 | except KeyError: 83 | raise AttributeError(key) 84 | 85 | def as_doc(self): 86 | """Get the SON document representation of this DBRef. 87 | 88 | Generally not needed by application developers 89 | """ 90 | doc = SON([("$ref", self.collection), ("$id", self.id)]) 91 | if self.database is not None: 92 | doc["$db"] = self.database 93 | doc.update(self.__kwargs) 94 | return doc 95 | 96 | def __repr__(self): 97 | extra = "".join([", %s=%r" % (k, v) for k, v in self.__kwargs.items()]) 98 | if self.database is None: 99 | return "DBRef(%r, %r%s)" % (self.collection, self.id, extra) 100 | return "DBRef(%r, %r, %r%s)" % (self.collection, self.id, self.database, extra) 101 | 102 | def __eq__(self, other): 103 | if isinstance(other, DBRef): 104 | us = (self.__database, self.__collection, self.__id, self.__kwargs) 105 | them = (other.__database, other.__collection, other.__id, other.__kwargs) 106 | return us == them 107 | return NotImplemented 108 | 109 | def __ne__(self, other): 110 | return not self == other 111 | 112 | def __hash__(self): 113 | """Get a hash value for this :class:`DBRef`.""" 114 | return hash( 115 | (self.__collection, self.__id, self.__database, tuple(sorted(self.__kwargs.items()))) 116 | ) 117 | 118 | def __deepcopy__(self, memo): 119 | """Support function for `copy.deepcopy()`.""" 120 | return DBRef( 121 | deepcopy(self.__collection, memo), 122 | deepcopy(self.__id, memo), 123 | deepcopy(self.__database, memo), 124 | deepcopy(self.__kwargs, memo), 125 | ) 126 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/decimal128.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for working with the BSON decimal128 type. 16 | 17 | .. versionadded:: 3.4 18 | 19 | .. note:: The Decimal128 BSON type requires MongoDB 3.4+. 20 | """ 21 | 22 | import decimal 23 | import struct 24 | import sys 25 | 26 | _PACK_64 = struct.Struct("> 49 == 1: 107 | high = high & 0x7FFFFFFFFFFF 108 | high |= _EXPONENT_MASK 109 | high |= (biased_exponent & 0x3FFF) << 47 110 | else: 111 | high |= biased_exponent << 49 112 | 113 | if sign: 114 | high |= _SIGN 115 | 116 | return high, low 117 | 118 | 119 | class Decimal128(object): 120 | """BSON Decimal128 type:: 121 | 122 | >>> Decimal128(Decimal("0.0005")) 123 | Decimal128('0.0005') 124 | >>> Decimal128("0.0005") 125 | Decimal128('0.0005') 126 | >>> Decimal128((3474527112516337664, 5)) 127 | Decimal128('0.0005') 128 | 129 | :Parameters: 130 | - `value`: An instance of :class:`decimal.Decimal`, string, or tuple of 131 | (high bits, low bits) from Binary Integer Decimal (BID) format. 132 | 133 | .. note:: :class:`~Decimal128` uses an instance of :class:`decimal.Context` 134 | configured for IEEE-754 Decimal128 when validating parameters. 135 | Signals like :class:`decimal.InvalidOperation`, :class:`decimal.Inexact`, 136 | and :class:`decimal.Overflow` are trapped and raised as exceptions:: 137 | 138 | >>> Decimal128(".13.1") 139 | Traceback (most recent call last): 140 | File "", line 1, in 141 | ... 142 | decimal.InvalidOperation: [] 143 | >>> 144 | >>> Decimal128("1E-6177") 145 | Traceback (most recent call last): 146 | File "", line 1, in 147 | ... 148 | decimal.Inexact: [] 149 | >>> 150 | >>> Decimal128("1E6145") 151 | Traceback (most recent call last): 152 | File "", line 1, in 153 | ... 154 | decimal.Overflow: [, ] 155 | 156 | To ensure the result of a calculation can always be stored as BSON 157 | Decimal128 use the context returned by 158 | :func:`create_decimal128_context`:: 159 | 160 | >>> import decimal 161 | >>> decimal128_ctx = create_decimal128_context() 162 | >>> with decimal.localcontext(decimal128_ctx) as ctx: 163 | ... Decimal128(ctx.create_decimal(".13.3")) 164 | ... 165 | Decimal128('NaN') 166 | >>> 167 | >>> with decimal.localcontext(decimal128_ctx) as ctx: 168 | ... Decimal128(ctx.create_decimal("1E-6177")) 169 | ... 170 | Decimal128('0E-6176') 171 | >>> 172 | >>> with decimal.localcontext(DECIMAL128_CTX) as ctx: 173 | ... Decimal128(ctx.create_decimal("1E6145")) 174 | ... 175 | Decimal128('Infinity') 176 | 177 | To match the behavior of MongoDB's Decimal128 implementation 178 | str(Decimal(value)) may not match str(Decimal128(value)) for NaN values:: 179 | 180 | >>> Decimal128(Decimal('NaN')) 181 | Decimal128('NaN') 182 | >>> Decimal128(Decimal('-NaN')) 183 | Decimal128('NaN') 184 | >>> Decimal128(Decimal('sNaN')) 185 | Decimal128('NaN') 186 | >>> Decimal128(Decimal('-sNaN')) 187 | Decimal128('NaN') 188 | 189 | However, :meth:`~Decimal128.to_decimal` will return the exact value:: 190 | 191 | >>> Decimal128(Decimal('NaN')).to_decimal() 192 | Decimal('NaN') 193 | >>> Decimal128(Decimal('-NaN')).to_decimal() 194 | Decimal('-NaN') 195 | >>> Decimal128(Decimal('sNaN')).to_decimal() 196 | Decimal('sNaN') 197 | >>> Decimal128(Decimal('-sNaN')).to_decimal() 198 | Decimal('-sNaN') 199 | 200 | Two instances of :class:`Decimal128` compare equal if their Binary 201 | Integer Decimal encodings are equal:: 202 | 203 | >>> Decimal128('NaN') == Decimal128('NaN') 204 | True 205 | >>> Decimal128('NaN').bid == Decimal128('NaN').bid 206 | True 207 | 208 | This differs from :class:`decimal.Decimal` comparisons for NaN:: 209 | 210 | >>> Decimal('NaN') == Decimal('NaN') 211 | False 212 | """ 213 | 214 | __slots__ = ("__high", "__low") 215 | 216 | _type_marker = 19 217 | 218 | def __init__(self, value): 219 | if isinstance(value, (str, decimal.Decimal)): 220 | self.__high, self.__low = _decimal_to_128(value) 221 | elif isinstance(value, (list, tuple)): 222 | if len(value) != 2: 223 | raise ValueError( 224 | "Invalid size for creation of Decimal128 " 225 | "from list or tuple. Must have exactly 2 " 226 | "elements." 227 | ) 228 | self.__high, self.__low = value 229 | else: 230 | raise TypeError("Cannot convert %r to Decimal128" % (value,)) 231 | 232 | def to_decimal(self): 233 | """Returns an instance of :class:`decimal.Decimal` for this 234 | :class:`Decimal128`. 235 | """ 236 | high = self.__high 237 | low = self.__low 238 | sign = 1 if (high & _SIGN) else 0 239 | 240 | if (high & _SNAN) == _SNAN: 241 | return decimal.Decimal((sign, (), "N")) 242 | elif (high & _NAN) == _NAN: 243 | return decimal.Decimal((sign, (), "n")) 244 | elif (high & _INF) == _INF: 245 | return decimal.Decimal((sign, (), "F")) 246 | 247 | if (high & _EXPONENT_MASK) == _EXPONENT_MASK: 248 | exponent = ((high & 0x1FFFE00000000000) >> 47) - _EXPONENT_BIAS 249 | return decimal.Decimal((sign, (0,), exponent)) 250 | else: 251 | exponent = ((high & 0x7FFF800000000000) >> 49) - _EXPONENT_BIAS 252 | 253 | arr = bytearray(15) 254 | mask = 0x00000000000000FF 255 | for i in range(14, 6, -1): 256 | arr[i] = (low & mask) >> ((14 - i) << 3) 257 | mask = mask << 8 258 | 259 | mask = 0x00000000000000FF 260 | for i in range(6, 0, -1): 261 | arr[i] = (high & mask) >> ((6 - i) << 3) 262 | mask = mask << 8 263 | 264 | mask = 0x0001000000000000 265 | arr[0] = (high & mask) >> 48 266 | 267 | # cdecimal only accepts a tuple for digits. 268 | digits = tuple(int(digit) for digit in str(int.from_bytes(arr, "big"))) 269 | 270 | with decimal.localcontext(_DEC128_CTX) as ctx: 271 | return ctx.create_decimal((sign, digits, exponent)) 272 | 273 | @classmethod 274 | def from_bid(cls, value): 275 | """Create an instance of :class:`Decimal128` from Binary Integer 276 | Decimal string. 277 | 278 | :Parameters: 279 | - `value`: 16 byte string (128-bit IEEE 754-2008 decimal floating 280 | point in Binary Integer Decimal (BID) format). 281 | """ 282 | if not isinstance(value, bytes): 283 | raise TypeError("value must be an instance of bytes") 284 | if len(value) != 16: 285 | raise ValueError("value must be exactly 16 bytes") 286 | return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) 287 | 288 | @property 289 | def bid(self): 290 | """The Binary Integer Decimal (BID) encoding of this instance.""" 291 | return _PACK_64(self.__low) + _PACK_64(self.__high) 292 | 293 | def __str__(self): 294 | dec = self.to_decimal() 295 | if dec.is_nan(): 296 | # Required by the drivers spec to match MongoDB behavior. 297 | return "NaN" 298 | return str(dec) 299 | 300 | def __repr__(self): 301 | return "Decimal128('%s')" % (str(self),) 302 | 303 | def __setstate__(self, value): 304 | self.__high, self.__low = value 305 | 306 | def __getstate__(self): 307 | return self.__high, self.__low 308 | 309 | def __eq__(self, other): 310 | if isinstance(other, Decimal128): 311 | return self.bid == other.bid 312 | return NotImplemented 313 | 314 | def __ne__(self, other): 315 | return not self == other 316 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/errors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Exceptions raised by the BSON package.""" 16 | 17 | 18 | class BSONError(Exception): 19 | """Base class for all BSON exceptions.""" 20 | 21 | 22 | class InvalidBSON(BSONError): 23 | """Raised when trying to create a BSON object from invalid data.""" 24 | 25 | 26 | class InvalidStringData(BSONError): 27 | """Raised when trying to encode a string containing non-UTF8 data.""" 28 | 29 | 30 | class InvalidDocument(BSONError): 31 | """Raised when trying to create a BSON object from an invalid document.""" 32 | 33 | 34 | class InvalidId(BSONError): 35 | """Raised when trying to create an ObjectId from invalid data.""" 36 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/int64.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A BSON wrapper for long (int in python3)""" 16 | 17 | 18 | class Int64(int): 19 | """Representation of the BSON int64 type. 20 | 21 | This is necessary because every integral number is an :class:`int` in 22 | Python 3. Small integral numbers are encoded to BSON int32 by default, 23 | but Int64 numbers will always be encoded to BSON int64. 24 | 25 | :Parameters: 26 | - `value`: the numeric value to represent 27 | """ 28 | 29 | __slots__ = () 30 | 31 | _type_marker = 18 32 | 33 | def __getstate__(self): 34 | return {} 35 | 36 | def __setstate__(self, state): 37 | pass 38 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/max_key.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Representation for the MongoDB internal MaxKey type. 16 | """ 17 | 18 | 19 | class MaxKey(object): 20 | """MongoDB internal MaxKey type.""" 21 | 22 | __slots__ = () 23 | 24 | _type_marker = 127 25 | 26 | def __getstate__(self): 27 | return {} 28 | 29 | def __setstate__(self, state): 30 | pass 31 | 32 | def __eq__(self, other): 33 | return isinstance(other, MaxKey) 34 | 35 | def __hash__(self): 36 | return hash(self._type_marker) 37 | 38 | def __ne__(self, other): 39 | return not self == other 40 | 41 | def __le__(self, other): 42 | return isinstance(other, MaxKey) 43 | 44 | def __lt__(self, dummy): 45 | return False 46 | 47 | def __ge__(self, dummy): 48 | return True 49 | 50 | def __gt__(self, other): 51 | return not isinstance(other, MaxKey) 52 | 53 | def __repr__(self): 54 | return "MaxKey()" 55 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/min_key.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Representation for the MongoDB internal MinKey type. 16 | """ 17 | 18 | 19 | class MinKey(object): 20 | """MongoDB internal MinKey type.""" 21 | 22 | __slots__ = () 23 | 24 | _type_marker = 255 25 | 26 | def __getstate__(self): 27 | return {} 28 | 29 | def __setstate__(self, state): 30 | pass 31 | 32 | def __eq__(self, other): 33 | return isinstance(other, MinKey) 34 | 35 | def __hash__(self): 36 | return hash(self._type_marker) 37 | 38 | def __ne__(self, other): 39 | return not self == other 40 | 41 | def __le__(self, dummy): 42 | return True 43 | 44 | def __lt__(self, other): 45 | return not isinstance(other, MinKey) 46 | 47 | def __ge__(self, other): 48 | return isinstance(other, MinKey) 49 | 50 | def __gt__(self, dummy): 51 | return False 52 | 53 | def __repr__(self): 54 | return "MinKey()" 55 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/objectid.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for working with MongoDB `ObjectIds 16 | `_. 17 | """ 18 | 19 | import binascii 20 | import calendar 21 | import datetime 22 | import os 23 | import struct 24 | import threading 25 | import time 26 | from random import SystemRandom 27 | 28 | from .errors import InvalidId 29 | from .tz_util import utc 30 | 31 | _MAX_COUNTER_VALUE = 0xFFFFFF 32 | 33 | 34 | def _raise_invalid_id(oid): 35 | raise InvalidId( 36 | "%r is not a valid ObjectId, it must be a 12-byte input" 37 | " or a 24-character hex string" % oid 38 | ) 39 | 40 | 41 | def _random_bytes(): 42 | """Get the 5-byte random field of an ObjectId.""" 43 | return os.urandom(5) 44 | 45 | 46 | class ObjectId(object): 47 | """A MongoDB ObjectId.""" 48 | 49 | _pid = os.getpid() 50 | 51 | _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) 52 | _inc_lock = threading.Lock() 53 | 54 | __random = _random_bytes() 55 | 56 | __slots__ = ("__id",) 57 | 58 | _type_marker = 7 59 | 60 | def __init__(self, oid=None): 61 | """Initialize a new ObjectId. 62 | 63 | An ObjectId is a 12-byte unique identifier consisting of: 64 | 65 | - a 4-byte value representing the seconds since the Unix epoch, 66 | - a 5-byte random value, 67 | - a 3-byte counter, starting with a random value. 68 | 69 | By default, ``ObjectId()`` creates a new unique identifier. The 70 | optional parameter `oid` can be an :class:`ObjectId`, or any 12 71 | :class:`bytes`. 72 | 73 | For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId 74 | specification but they are acceptable input:: 75 | 76 | >>> ObjectId(b'foo-bar-quux') 77 | ObjectId('666f6f2d6261722d71757578') 78 | 79 | `oid` can also be a :class:`str` of 24 hex digits:: 80 | 81 | >>> ObjectId('0123456789ab0123456789ab') 82 | ObjectId('0123456789ab0123456789ab') 83 | 84 | Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor 85 | 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. 86 | 87 | :Parameters: 88 | - `oid` (optional): a valid ObjectId. 89 | 90 | .. seealso:: The MongoDB documentation on `ObjectIds`_. 91 | 92 | .. versionchanged:: 3.8 93 | :class:`~bson.objectid.ObjectId` now implements the `ObjectID 94 | specification version 0.2 95 | `_. 97 | """ 98 | if oid is None: 99 | self.__generate() 100 | elif isinstance(oid, bytes) and len(oid) == 12: 101 | self.__id = oid 102 | else: 103 | self.__validate(oid) 104 | 105 | @classmethod 106 | def from_datetime(cls, generation_time): 107 | """Create a dummy ObjectId instance with a specific generation time. 108 | 109 | This method is useful for doing range queries on a field 110 | containing :class:`ObjectId` instances. 111 | 112 | .. warning:: 113 | It is not safe to insert a document containing an ObjectId 114 | generated using this method. This method deliberately 115 | eliminates the uniqueness guarantee that ObjectIds 116 | generally provide. ObjectIds generated with this method 117 | should be used exclusively in queries. 118 | 119 | `generation_time` will be converted to UTC. Naive datetime 120 | instances will be treated as though they already contain UTC. 121 | 122 | An example using this helper to get documents where ``"_id"`` 123 | was generated before January 1, 2010 would be: 124 | 125 | >>> gen_time = datetime.datetime(2010, 1, 1) 126 | >>> dummy_id = ObjectId.from_datetime(gen_time) 127 | >>> result = collection.find({"_id": {"$lt": dummy_id}}) 128 | 129 | :Parameters: 130 | - `generation_time`: :class:`~datetime.datetime` to be used 131 | as the generation time for the resulting ObjectId. 132 | """ 133 | if generation_time.utcoffset() is not None: 134 | generation_time = generation_time - generation_time.utcoffset() 135 | timestamp = calendar.timegm(generation_time.timetuple()) 136 | oid = struct.pack(">I", int(timestamp)) + b"\x00\x00\x00\x00\x00\x00\x00\x00" 137 | return cls(oid) 138 | 139 | @classmethod 140 | def is_valid(cls, oid): 141 | """Checks if a `oid` string is valid or not. 142 | 143 | :Parameters: 144 | - `oid`: the object id to validate 145 | 146 | .. versionadded:: 2.3 147 | """ 148 | if not oid: 149 | return False 150 | 151 | try: 152 | ObjectId(oid) 153 | return True 154 | except (InvalidId, TypeError): 155 | return False 156 | 157 | @classmethod 158 | def _random(cls): 159 | """Generate a 5-byte random number once per process.""" 160 | pid = os.getpid() 161 | if pid != cls._pid: 162 | cls._pid = pid 163 | cls.__random = _random_bytes() 164 | return cls.__random 165 | 166 | def __generate(self): 167 | """Generate a new value for this ObjectId.""" 168 | 169 | # 4 bytes current time 170 | oid = struct.pack(">I", int(time.time())) 171 | 172 | # 5 bytes random 173 | oid += ObjectId._random() 174 | 175 | # 3 bytes inc 176 | with ObjectId._inc_lock: 177 | oid += struct.pack(">I", ObjectId._inc)[1:4] 178 | ObjectId._inc = (ObjectId._inc + 1) % (_MAX_COUNTER_VALUE + 1) 179 | 180 | self.__id = oid 181 | 182 | def __validate(self, oid): 183 | """Validate and use the given id for this ObjectId. 184 | 185 | Raises TypeError if id is not an instance of 186 | (:class:`basestring` (:class:`str` or :class:`bytes` 187 | in python 3), ObjectId) and InvalidId if it is not a 188 | valid ObjectId. 189 | 190 | :Parameters: 191 | - `oid`: a valid ObjectId 192 | """ 193 | if isinstance(oid, ObjectId): 194 | self.__id = oid.binary 195 | elif isinstance(oid, str): 196 | if len(oid) == 24: 197 | try: 198 | self.__id = bytes.fromhex(oid) 199 | except (TypeError, ValueError): 200 | _raise_invalid_id(oid) 201 | else: 202 | _raise_invalid_id(oid) 203 | else: 204 | raise TypeError( 205 | "id must be an instance of (bytes, str, ObjectId), " "not %s" % (type(oid),) 206 | ) 207 | 208 | @property 209 | def binary(self): 210 | """12-byte binary representation of this ObjectId.""" 211 | return self.__id 212 | 213 | @property 214 | def generation_time(self): 215 | """A :class:`datetime.datetime` instance representing the time of 216 | generation for this :class:`ObjectId`. 217 | 218 | The :class:`datetime.datetime` is timezone aware, and 219 | represents the generation time in UTC. It is precise to the 220 | second. 221 | """ 222 | timestamp = struct.unpack(">I", self.__id[0:4])[0] 223 | return datetime.datetime.fromtimestamp(timestamp, utc) 224 | 225 | def __getstate__(self): 226 | """return value of object for pickling. 227 | needed explicitly because __slots__() defined. 228 | """ 229 | return self.__id 230 | 231 | def __setstate__(self, value): 232 | """explicit state set from pickling""" 233 | # Provide backwards compatability with OIDs 234 | # pickled with pymongo-1.9 or older. 235 | if isinstance(value, dict): 236 | oid = value["_ObjectId__id"] 237 | else: 238 | oid = value 239 | # ObjectIds pickled in python 2.x used `str` for __id. 240 | # In python 3.x this has to be converted to `bytes` 241 | # by encoding latin-1. 242 | if isinstance(oid, str): 243 | self.__id = oid.encode("latin-1") 244 | else: 245 | self.__id = oid 246 | 247 | def __str__(self): 248 | return binascii.hexlify(self.__id).decode() 249 | 250 | def __repr__(self): 251 | return "ObjectId('%s')" % (str(self),) 252 | 253 | def __eq__(self, other): 254 | if isinstance(other, ObjectId): 255 | return self.__id == other.binary 256 | return NotImplemented 257 | 258 | def __ne__(self, other): 259 | if isinstance(other, ObjectId): 260 | return self.__id != other.binary 261 | return NotImplemented 262 | 263 | def __lt__(self, other): 264 | if isinstance(other, ObjectId): 265 | return self.__id < other.binary 266 | return NotImplemented 267 | 268 | def __le__(self, other): 269 | if isinstance(other, ObjectId): 270 | return self.__id <= other.binary 271 | return NotImplemented 272 | 273 | def __gt__(self, other): 274 | if isinstance(other, ObjectId): 275 | return self.__id > other.binary 276 | return NotImplemented 277 | 278 | def __ge__(self, other): 279 | if isinstance(other, ObjectId): 280 | return self.__id >= other.binary 281 | return NotImplemented 282 | 283 | def __hash__(self): 284 | """Get a hash value for this :class:`ObjectId`.""" 285 | return hash(self.__id) 286 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/raw_bson.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing raw BSON documents. 16 | 17 | Inserting and Retrieving RawBSONDocuments 18 | ========================================= 19 | 20 | Example: Moving a document between different databases/collections 21 | 22 | .. doctest:: 23 | 24 | >>> import bson 25 | >>> from pymongo import MongoClient 26 | >>> from .raw_bson import RawBSONDocument 27 | >>> client = MongoClient(document_class=RawBSONDocument) 28 | >>> client.drop_database('db') 29 | >>> client.drop_database('replica_db') 30 | >>> db = client.db 31 | >>> result = db.test.insert_many([{'_id': 1, 'a': 1}, 32 | ... {'_id': 2, 'b': 1}, 33 | ... {'_id': 3, 'c': 1}, 34 | ... {'_id': 4, 'd': 1}]) 35 | >>> replica_db = client.replica_db 36 | >>> for doc in db.test.find(): 37 | ... print(f"raw document: {doc.raw}") 38 | ... print(f"decoded document: {bson.decode(doc.raw)}") 39 | ... result = replica_db.test.insert_one(doc) 40 | raw document: b'...' 41 | decoded document: {'_id': 1, 'a': 1} 42 | raw document: b'...' 43 | decoded document: {'_id': 2, 'b': 1} 44 | raw document: b'...' 45 | decoded document: {'_id': 3, 'c': 1} 46 | raw document: b'...' 47 | decoded document: {'_id': 4, 'd': 1} 48 | 49 | For use cases like moving documents across different databases or writing binary 50 | blobs to disk, using raw BSON documents provides better speed and avoids the 51 | overhead of decoding or encoding BSON. 52 | """ 53 | 54 | from collections.abc import Mapping as _Mapping 55 | 56 | from . import _get_object_size, _raw_to_dict 57 | from .codec_options import _RAW_BSON_DOCUMENT_MARKER 58 | from .codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT 59 | from .son import SON 60 | 61 | 62 | class RawBSONDocument(_Mapping): 63 | """Representation for a MongoDB document that provides access to the raw 64 | BSON bytes that compose it. 65 | 66 | Only when a field is accessed or modified within the document does 67 | RawBSONDocument decode its bytes. 68 | """ 69 | 70 | __slots__ = ("__raw", "__inflated_doc", "__codec_options") 71 | _type_marker = _RAW_BSON_DOCUMENT_MARKER 72 | 73 | def __init__(self, bson_bytes, codec_options=None): 74 | """Create a new :class:`RawBSONDocument` 75 | 76 | :class:`RawBSONDocument` is a representation of a BSON document that 77 | provides access to the underlying raw BSON bytes. Only when a field is 78 | accessed or modified within the document does RawBSONDocument decode 79 | its bytes. 80 | 81 | :class:`RawBSONDocument` implements the ``Mapping`` abstract base 82 | class from the standard library so it can be used like a read-only 83 | ``dict``:: 84 | 85 | >>> from . import encode 86 | >>> raw_doc = RawBSONDocument(encode({'_id': 'my_doc'})) 87 | >>> raw_doc.raw 88 | b'...' 89 | >>> raw_doc['_id'] 90 | 'my_doc' 91 | 92 | :Parameters: 93 | - `bson_bytes`: the BSON bytes that compose this document 94 | - `codec_options` (optional): An instance of 95 | :class:`~bson.codec_options.CodecOptions` whose ``document_class`` 96 | must be :class:`RawBSONDocument`. The default is 97 | :attr:`DEFAULT_RAW_BSON_OPTIONS`. 98 | 99 | .. versionchanged:: 3.8 100 | :class:`RawBSONDocument` now validates that the ``bson_bytes`` 101 | passed in represent a single bson document. 102 | 103 | .. versionchanged:: 3.5 104 | If a :class:`~bson.codec_options.CodecOptions` is passed in, its 105 | `document_class` must be :class:`RawBSONDocument`. 106 | """ 107 | self.__raw = bson_bytes 108 | self.__inflated_doc = None 109 | # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, 110 | # it refers to this class RawBSONDocument. 111 | if codec_options is None: 112 | codec_options = DEFAULT_RAW_BSON_OPTIONS 113 | elif codec_options.document_class is not RawBSONDocument: 114 | raise TypeError( 115 | "RawBSONDocument cannot use CodecOptions with document " 116 | "class %s" % (codec_options.document_class,) 117 | ) 118 | self.__codec_options = codec_options 119 | # Validate the bson object size. 120 | _get_object_size(bson_bytes, 0, len(bson_bytes)) 121 | 122 | @property 123 | def raw(self): 124 | """The raw BSON bytes composing this document.""" 125 | return self.__raw 126 | 127 | def items(self): 128 | """Lazily decode and iterate elements in this document.""" 129 | return self.__inflated.items() 130 | 131 | @property 132 | def __inflated(self): 133 | if self.__inflated_doc is None: 134 | # We already validated the object's size when this document was 135 | # created, so no need to do that again. 136 | # Use SON to preserve ordering of elements. 137 | self.__inflated_doc = _inflate_bson(self.__raw, self.__codec_options) 138 | return self.__inflated_doc 139 | 140 | def __getitem__(self, item): 141 | return self.__inflated[item] 142 | 143 | def __iter__(self): 144 | return iter(self.__inflated) 145 | 146 | def __len__(self): 147 | return len(self.__inflated) 148 | 149 | def __eq__(self, other): 150 | if isinstance(other, RawBSONDocument): 151 | return self.__raw == other.raw 152 | return NotImplemented 153 | 154 | def __repr__(self): 155 | return "RawBSONDocument(%r, codec_options=%r)" % (self.raw, self.__codec_options) 156 | 157 | 158 | def _inflate_bson(bson_bytes, codec_options): 159 | """Inflates the top level fields of a BSON document. 160 | 161 | :Parameters: 162 | - `bson_bytes`: the BSON bytes that compose this document 163 | - `codec_options`: An instance of 164 | :class:`~bson.codec_options.CodecOptions` whose ``document_class`` 165 | must be :class:`RawBSONDocument`. 166 | """ 167 | # Use SON to preserve ordering of elements. 168 | return _raw_to_dict(bson_bytes, 4, len(bson_bytes) - 1, codec_options, SON()) 169 | 170 | 171 | DEFAULT_RAW_BSON_OPTIONS = DEFAULT.with_options(document_class=RawBSONDocument) 172 | """The default :class:`~bson.codec_options.CodecOptions` for 173 | :class:`RawBSONDocument`. 174 | """ 175 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/regex.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing MongoDB regular expressions. 16 | """ 17 | 18 | import re 19 | 20 | from ._helpers import _getstate_slots, _setstate_slots 21 | from .son import RE_TYPE 22 | 23 | 24 | def str_flags_to_int(str_flags): 25 | flags = 0 26 | if "i" in str_flags: 27 | flags |= re.IGNORECASE 28 | if "l" in str_flags: 29 | flags |= re.LOCALE 30 | if "m" in str_flags: 31 | flags |= re.MULTILINE 32 | if "s" in str_flags: 33 | flags |= re.DOTALL 34 | if "u" in str_flags: 35 | flags |= re.UNICODE 36 | if "x" in str_flags: 37 | flags |= re.VERBOSE 38 | 39 | return flags 40 | 41 | 42 | class Regex(object): 43 | """BSON regular expression data.""" 44 | 45 | __slots__ = ("pattern", "flags") 46 | 47 | __getstate__ = _getstate_slots 48 | __setstate__ = _setstate_slots 49 | 50 | _type_marker = 11 51 | 52 | @classmethod 53 | def from_native(cls, regex): 54 | """Convert a Python regular expression into a ``Regex`` instance. 55 | 56 | Note that in Python 3, a regular expression compiled from a 57 | :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable 58 | to store this flag in a BSON regular expression, unset it first:: 59 | 60 | >>> pattern = re.compile('.*') 61 | >>> regex = Regex.from_native(pattern) 62 | >>> regex.flags ^= re.UNICODE 63 | >>> db.collection.insert_one({'pattern': regex}) 64 | 65 | :Parameters: 66 | - `regex`: A regular expression object from ``re.compile()``. 67 | 68 | .. warning:: 69 | Python regular expressions use a different syntax and different 70 | set of flags than MongoDB, which uses `PCRE`_. A regular 71 | expression retrieved from the server may not compile in 72 | Python, or may match a different set of strings in Python than 73 | when used in a MongoDB query. 74 | 75 | .. _PCRE: http://www.pcre.org/ 76 | """ 77 | if not isinstance(regex, RE_TYPE): 78 | raise TypeError("regex must be a compiled regular expression, not %s" % type(regex)) 79 | 80 | return Regex(regex.pattern, regex.flags) 81 | 82 | def __init__(self, pattern, flags=0): 83 | """BSON regular expression data. 84 | 85 | This class is useful to store and retrieve regular expressions that are 86 | incompatible with Python's regular expression dialect. 87 | 88 | :Parameters: 89 | - `pattern`: string 90 | - `flags`: (optional) an integer bitmask, or a string of flag 91 | characters like "im" for IGNORECASE and MULTILINE 92 | """ 93 | if not isinstance(pattern, (str, bytes)): 94 | raise TypeError("pattern must be a string, not %s" % type(pattern)) 95 | self.pattern = pattern 96 | 97 | if isinstance(flags, str): 98 | self.flags = str_flags_to_int(flags) 99 | elif isinstance(flags, int): 100 | self.flags = flags 101 | else: 102 | raise TypeError("flags must be a string or int, not %s" % type(flags)) 103 | 104 | def __eq__(self, other): 105 | if isinstance(other, Regex): 106 | return self.pattern == other.pattern and self.flags == other.flags 107 | else: 108 | return NotImplemented 109 | 110 | __hash__ = None 111 | 112 | def __ne__(self, other): 113 | return not self == other 114 | 115 | def __repr__(self): 116 | return "Regex(%r, %r)" % (self.pattern, self.flags) 117 | 118 | def try_compile(self): 119 | """Compile this :class:`Regex` as a Python regular expression. 120 | 121 | .. warning:: 122 | Python regular expressions use a different syntax and different 123 | set of flags than MongoDB, which uses `PCRE`_. A regular 124 | expression retrieved from the server may not compile in 125 | Python, or may match a different set of strings in Python than 126 | when used in a MongoDB query. :meth:`try_compile()` may raise 127 | :exc:`re.error`. 128 | 129 | .. _PCRE: http://www.pcre.org/ 130 | """ 131 | return re.compile(self.pattern, self.flags) 132 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/son.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for creating and manipulating SON, the Serialized Ocument Notation. 16 | 17 | Regular dictionaries can be used instead of SON objects, but not when the order 18 | of keys is important. A SON object can be used just like a normal Python 19 | dictionary.""" 20 | 21 | import copy 22 | import re 23 | from collections.abc import Mapping as _Mapping 24 | 25 | # This sort of sucks, but seems to be as good as it gets... 26 | # This is essentially the same as re._pattern_type 27 | RE_TYPE = type(re.compile("")) 28 | 29 | 30 | class SON(dict): 31 | """SON data. 32 | 33 | A subclass of dict that maintains ordering of keys and provides a 34 | few extra niceties for dealing with SON. SON provides an API 35 | similar to collections.OrderedDict. 36 | """ 37 | 38 | def __init__(self, data=None, **kwargs): 39 | self.__keys = [] 40 | dict.__init__(self) 41 | self.update(data) 42 | self.update(kwargs) 43 | 44 | def __new__(cls, *args, **kwargs): 45 | instance = super(SON, cls).__new__(cls, *args, **kwargs) 46 | instance.__keys = [] 47 | return instance 48 | 49 | def __repr__(self): 50 | result = [] 51 | for key in self.__keys: 52 | result.append("(%r, %r)" % (key, self[key])) 53 | return "SON([%s])" % ", ".join(result) 54 | 55 | def __setitem__(self, key, value): 56 | if key not in self.__keys: 57 | self.__keys.append(key) 58 | dict.__setitem__(self, key, value) 59 | 60 | def __delitem__(self, key): 61 | self.__keys.remove(key) 62 | dict.__delitem__(self, key) 63 | 64 | def copy(self): 65 | other = SON() 66 | other.update(self) 67 | return other 68 | 69 | # TODO this is all from UserDict.DictMixin. it could probably be made more 70 | # efficient. 71 | # second level definitions support higher levels 72 | def __iter__(self): 73 | for k in self.__keys: 74 | yield k 75 | 76 | def has_key(self, key): 77 | return key in self.__keys 78 | 79 | def iterkeys(self): 80 | return self.__iter__() 81 | 82 | # fourth level uses definitions from lower levels 83 | def itervalues(self): 84 | for _, v in self.items(): 85 | yield v 86 | 87 | def values(self): 88 | return [v for _, v in self.items()] 89 | 90 | def clear(self): 91 | self.__keys = [] 92 | super(SON, self).clear() 93 | 94 | def setdefault(self, key, default=None): 95 | try: 96 | return self[key] 97 | except KeyError: 98 | self[key] = default 99 | return default 100 | 101 | def pop(self, key, *args): 102 | if len(args) > 1: 103 | raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) 104 | try: 105 | value = self[key] 106 | except KeyError: 107 | if args: 108 | return args[0] 109 | raise 110 | del self[key] 111 | return value 112 | 113 | def popitem(self): 114 | try: 115 | k, v = next(iter(self.items())) 116 | except StopIteration: 117 | raise KeyError("container is empty") 118 | del self[k] 119 | return (k, v) 120 | 121 | def update(self, other=None, **kwargs): 122 | # Make progressively weaker assumptions about "other" 123 | if other is None: 124 | pass 125 | elif hasattr(other, "items"): 126 | for k, v in other.items(): 127 | self[k] = v 128 | elif hasattr(other, "keys"): 129 | for k in other.keys(): 130 | self[k] = other[k] 131 | else: 132 | for k, v in other: 133 | self[k] = v 134 | if kwargs: 135 | self.update(kwargs) 136 | 137 | def get(self, key, default=None): 138 | try: 139 | return self[key] 140 | except KeyError: 141 | return default 142 | 143 | def __eq__(self, other): 144 | """Comparison to another SON is order-sensitive while comparison to a 145 | regular dictionary is order-insensitive. 146 | """ 147 | if isinstance(other, SON): 148 | return len(self) == len(other) and list(self.items()) == list(other.items()) 149 | return self.to_dict() == other 150 | 151 | def __ne__(self, other): 152 | return not self == other 153 | 154 | def __len__(self): 155 | return len(self.__keys) 156 | 157 | def to_dict(self): 158 | """Convert a SON document to a normal Python dictionary instance. 159 | 160 | This is trickier than just *dict(...)* because it needs to be 161 | recursive. 162 | """ 163 | 164 | def transform_value(value): 165 | if isinstance(value, list): 166 | return [transform_value(v) for v in value] 167 | elif isinstance(value, _Mapping): 168 | return dict([(k, transform_value(v)) for k, v in value.items()]) 169 | else: 170 | return value 171 | 172 | return transform_value(dict(self)) 173 | 174 | def __deepcopy__(self, memo): 175 | out = SON() 176 | val_id = id(self) 177 | if val_id in memo: 178 | return memo.get(val_id) 179 | memo[val_id] = out 180 | for k, v in self.items(): 181 | if not isinstance(v, RE_TYPE): 182 | v = copy.deepcopy(v, memo) 183 | out[k] = v 184 | return out 185 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/timestamp.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing MongoDB internal Timestamps. 16 | """ 17 | 18 | import calendar 19 | import datetime 20 | 21 | from ._helpers import _getstate_slots, _setstate_slots 22 | from .tz_util import utc 23 | 24 | UPPERBOUND = 4294967296 25 | 26 | 27 | class Timestamp(object): 28 | """MongoDB internal timestamps used in the opLog.""" 29 | 30 | __slots__ = ("__time", "__inc") 31 | 32 | __getstate__ = _getstate_slots 33 | __setstate__ = _setstate_slots 34 | 35 | _type_marker = 17 36 | 37 | def __init__(self, time, inc): 38 | """Create a new :class:`Timestamp`. 39 | 40 | This class is only for use with the MongoDB opLog. If you need 41 | to store a regular timestamp, please use a 42 | :class:`~datetime.datetime`. 43 | 44 | Raises :class:`TypeError` if `time` is not an instance of 45 | :class: `int` or :class:`~datetime.datetime`, or `inc` is not 46 | an instance of :class:`int`. Raises :class:`ValueError` if 47 | `time` or `inc` is not in [0, 2**32). 48 | 49 | :Parameters: 50 | - `time`: time in seconds since epoch UTC, or a naive UTC 51 | :class:`~datetime.datetime`, or an aware 52 | :class:`~datetime.datetime` 53 | - `inc`: the incrementing counter 54 | """ 55 | if isinstance(time, datetime.datetime): 56 | if time.utcoffset() is not None: 57 | time = time - time.utcoffset() 58 | time = int(calendar.timegm(time.timetuple())) 59 | if not isinstance(time, int): 60 | raise TypeError("time must be an instance of int") 61 | if not isinstance(inc, int): 62 | raise TypeError("inc must be an instance of int") 63 | if not 0 <= time < UPPERBOUND: 64 | raise ValueError("time must be contained in [0, 2**32)") 65 | if not 0 <= inc < UPPERBOUND: 66 | raise ValueError("inc must be contained in [0, 2**32)") 67 | 68 | self.__time = time 69 | self.__inc = inc 70 | 71 | @property 72 | def time(self): 73 | """Get the time portion of this :class:`Timestamp`.""" 74 | return self.__time 75 | 76 | @property 77 | def inc(self): 78 | """Get the inc portion of this :class:`Timestamp`.""" 79 | return self.__inc 80 | 81 | def __eq__(self, other): 82 | if isinstance(other, Timestamp): 83 | return self.__time == other.time and self.__inc == other.inc 84 | else: 85 | return NotImplemented 86 | 87 | def __hash__(self): 88 | return hash(self.time) ^ hash(self.inc) 89 | 90 | def __ne__(self, other): 91 | return not self == other 92 | 93 | def __lt__(self, other): 94 | if isinstance(other, Timestamp): 95 | return (self.time, self.inc) < (other.time, other.inc) 96 | return NotImplemented 97 | 98 | def __le__(self, other): 99 | if isinstance(other, Timestamp): 100 | return (self.time, self.inc) <= (other.time, other.inc) 101 | return NotImplemented 102 | 103 | def __gt__(self, other): 104 | if isinstance(other, Timestamp): 105 | return (self.time, self.inc) > (other.time, other.inc) 106 | return NotImplemented 107 | 108 | def __ge__(self, other): 109 | if isinstance(other, Timestamp): 110 | return (self.time, self.inc) >= (other.time, other.inc) 111 | return NotImplemented 112 | 113 | def __repr__(self): 114 | return "Timestamp(%s, %s)" % (self.__time, self.__inc) 115 | 116 | def as_datetime(self): 117 | """Return a :class:`~datetime.datetime` instance corresponding 118 | to the time portion of this :class:`Timestamp`. 119 | 120 | The returned datetime's timezone is UTC. 121 | """ 122 | return datetime.datetime.fromtimestamp(self.__time, utc) 123 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson36/tz_util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Timezone related utilities for BSON.""" 16 | 17 | from datetime import timedelta, tzinfo 18 | 19 | ZERO = timedelta(0) 20 | 21 | 22 | class FixedOffset(tzinfo): 23 | """Fixed offset timezone, in minutes east from UTC. 24 | 25 | Implementation based from the Python `standard library documentation 26 | `_. 27 | Defining __getinitargs__ enables pickling / copying. 28 | """ 29 | 30 | def __init__(self, offset, name): 31 | if isinstance(offset, timedelta): 32 | self.__offset = offset 33 | else: 34 | self.__offset = timedelta(minutes=offset) 35 | self.__name = name 36 | 37 | def __getinitargs__(self): 38 | return self.__offset, self.__name 39 | 40 | def utcoffset(self, dt): 41 | return self.__offset 42 | 43 | def tzname(self, dt): 44 | return self.__name 45 | 46 | def dst(self, dt): 47 | return ZERO 48 | 49 | 50 | utc = FixedOffset(0, "UTC") 51 | """Fixed offset timezone representing UTC.""" 52 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/_cbson.cp310-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson37/_cbson.cp310-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/_cbson.cp311-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson37/_cbson.cp311-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/_cbson.cp37-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson37/_cbson.cp37-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/_cbson.cp38-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson37/_cbson.cp38-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/_cbson.cp39-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtbson/bson37/_cbson.cp39-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/_helpers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Setstate and getstate functions for objects with __slots__, allowing 16 | compatibility with default pickling protocol 17 | """ 18 | from typing import Any, Mapping 19 | 20 | 21 | def _setstate_slots(self: Any, state: Any) -> None: 22 | for slot, value in state.items(): 23 | setattr(self, slot, value) 24 | 25 | 26 | def _mangle_name(name: str, prefix: str) -> str: 27 | if name.startswith("__"): 28 | prefix = "_" + prefix 29 | else: 30 | prefix = "" 31 | return prefix + name 32 | 33 | 34 | def _getstate_slots(self: Any) -> Mapping[Any, Any]: 35 | prefix = self.__class__.__name__ 36 | ret = dict() 37 | for name in self.__slots__: 38 | mangled_name = _mangle_name(name, prefix) 39 | if hasattr(self, mangled_name): 40 | ret[mangled_name] = getattr(self, mangled_name) 41 | return ret 42 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/code.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing JavaScript code in BSON. 16 | """ 17 | 18 | from collections.abc import Mapping as _Mapping 19 | from typing import Any, Mapping, Optional, Type, Union 20 | 21 | 22 | class Code(str): 23 | """BSON's JavaScript code type. 24 | 25 | Raises :class:`TypeError` if `code` is not an instance of 26 | :class:`basestring` (:class:`str` in python 3) or `scope` 27 | is not ``None`` or an instance of :class:`dict`. 28 | 29 | Scope variables can be set by passing a dictionary as the `scope` 30 | argument or by using keyword arguments. If a variable is set as a 31 | keyword argument it will override any setting for that variable in 32 | the `scope` dictionary. 33 | 34 | :Parameters: 35 | - `code`: A string containing JavaScript code to be evaluated or another 36 | instance of Code. In the latter case, the scope of `code` becomes this 37 | Code's :attr:`scope`. 38 | - `scope` (optional): dictionary representing the scope in which 39 | `code` should be evaluated - a mapping from identifiers (as 40 | strings) to values. Defaults to ``None``. This is applied after any 41 | scope associated with a given `code` above. 42 | - `**kwargs` (optional): scope variables can also be passed as 43 | keyword arguments. These are applied after `scope` and `code`. 44 | 45 | .. versionchanged:: 3.4 46 | The default value for :attr:`scope` is ``None`` instead of ``{}``. 47 | 48 | """ 49 | 50 | _type_marker = 13 51 | __scope: Union[Mapping[str, Any], None] 52 | 53 | def __new__( 54 | cls: Type["Code"], 55 | code: Union[str, "Code"], 56 | scope: Optional[Mapping[str, Any]] = None, 57 | **kwargs: Any 58 | ) -> "Code": 59 | if not isinstance(code, str): 60 | raise TypeError("code must be an instance of str") 61 | 62 | self = str.__new__(cls, code) 63 | 64 | try: 65 | self.__scope = code.scope # type: ignore 66 | except AttributeError: 67 | self.__scope = None 68 | 69 | if scope is not None: 70 | if not isinstance(scope, _Mapping): 71 | raise TypeError("scope must be an instance of dict") 72 | if self.__scope is not None: 73 | self.__scope.update(scope) # type: ignore 74 | else: 75 | self.__scope = scope 76 | 77 | if kwargs: 78 | if self.__scope is not None: 79 | self.__scope.update(kwargs) # type: ignore 80 | else: 81 | self.__scope = kwargs 82 | 83 | return self 84 | 85 | @property 86 | def scope(self) -> Optional[Mapping[str, Any]]: 87 | """Scope dictionary for this instance or ``None``.""" 88 | return self.__scope 89 | 90 | def __repr__(self): 91 | return "Code(%s, %r)" % (str.__repr__(self), self.__scope) 92 | 93 | def __eq__(self, other: Any) -> bool: 94 | if isinstance(other, Code): 95 | return (self.__scope, str(self)) == (other.__scope, str(other)) 96 | return False 97 | 98 | __hash__: Any = None 99 | 100 | def __ne__(self, other: Any) -> bool: 101 | return not self == other 102 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/codec_options.pyi: -------------------------------------------------------------------------------- 1 | # Copyright 2022-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Workaround for https://bugs.python.org/issue43923. 16 | Ideally we would have done this with a single class, but 17 | generic subclasses *must* take a parameter, and prior to Python 3.9 18 | or in Python 3.7 and 3.8 with `from __future__ import annotations`, 19 | you get the error: "TypeError: 'type' object is not subscriptable". 20 | """ 21 | 22 | import datetime 23 | import abc 24 | import enum 25 | from typing import Tuple, Generic, Optional, Mapping, Any, TypeVar, Type, Dict, Iterable, Tuple, MutableMapping, Callable, Union 26 | 27 | 28 | class TypeEncoder(abc.ABC, metaclass=abc.ABCMeta): 29 | @property 30 | @abc.abstractmethod 31 | def python_type(self) -> Any: ... 32 | @abc.abstractmethod 33 | def transform_python(self, value: Any) -> Any: ... 34 | 35 | class TypeDecoder(abc.ABC, metaclass=abc.ABCMeta): 36 | @property 37 | @abc.abstractmethod 38 | def bson_type(self) -> Any: ... 39 | @abc.abstractmethod 40 | def transform_bson(self, value: Any) -> Any: ... 41 | 42 | class TypeCodec(TypeEncoder, TypeDecoder, metaclass=abc.ABCMeta): ... 43 | 44 | Codec = Union[TypeEncoder, TypeDecoder, TypeCodec] 45 | Fallback = Callable[[Any], Any] 46 | 47 | class TypeRegistry: 48 | _decoder_map: Dict[Any, Any] 49 | _encoder_map: Dict[Any, Any] 50 | _fallback_encoder: Optional[Fallback] 51 | 52 | def __init__(self, type_codecs: Optional[Iterable[Codec]] = ..., fallback_encoder: Optional[Fallback] = ...) -> None: ... 53 | def __eq__(self, other: Any) -> Any: ... 54 | 55 | 56 | _DocumentType = TypeVar("_DocumentType", bound=Mapping[str, Any]) 57 | 58 | class DatetimeConversion(int, enum.Enum): 59 | DATETIME = ... 60 | DATETIME_CLAMP = ... 61 | DATETIME_MS = ... 62 | DATETIME_AUTO = ... 63 | 64 | class CodecOptions(Tuple, Generic[_DocumentType]): 65 | document_class: Type[_DocumentType] 66 | tz_aware: bool 67 | uuid_representation: int 68 | unicode_decode_error_handler: Optional[str] 69 | tzinfo: Optional[datetime.tzinfo] 70 | type_registry: TypeRegistry 71 | datetime_conversion: Optional[int] 72 | 73 | def __new__( 74 | cls: Type[CodecOptions], 75 | document_class: Optional[Type[_DocumentType]] = ..., 76 | tz_aware: bool = ..., 77 | uuid_representation: Optional[int] = ..., 78 | unicode_decode_error_handler: Optional[str] = ..., 79 | tzinfo: Optional[datetime.tzinfo] = ..., 80 | type_registry: Optional[TypeRegistry] = ..., 81 | datetime_conversion: Optional[int] = ..., 82 | ) -> CodecOptions[_DocumentType]: ... 83 | 84 | # CodecOptions API 85 | def with_options(self, **kwargs: Any) -> CodecOptions[_DocumentType]: ... 86 | 87 | def _arguments_repr(self) -> str: ... 88 | 89 | def _options_dict(self) -> Dict[Any, Any]: ... 90 | 91 | # NamedTuple API 92 | @classmethod 93 | def _make(cls, obj: Iterable) -> CodecOptions[_DocumentType]: ... 94 | 95 | def _asdict(self) -> Dict[str, Any]: ... 96 | 97 | def _replace(self, **kwargs: Any) -> CodecOptions[_DocumentType]: ... 98 | 99 | _source: str 100 | _fields: Tuple[str] 101 | 102 | 103 | DEFAULT_CODEC_OPTIONS: CodecOptions[MutableMapping[str, Any]] 104 | _RAW_BSON_DOCUMENT_MARKER: int 105 | 106 | def _raw_document_class(document_class: Any) -> bool: ... 107 | 108 | def _parse_codec_options(options: Any) -> CodecOptions: ... 109 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/datetime_ms.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. 14 | 15 | """Tools for representing the BSON datetime type. 16 | 17 | .. versionadded:: 4.3 18 | """ 19 | 20 | import calendar 21 | import datetime 22 | import functools 23 | from typing import Any, Union, cast 24 | 25 | from .codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, DatetimeConversion 26 | from .tz_util import utc 27 | 28 | EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) 29 | EPOCH_NAIVE = datetime.datetime.utcfromtimestamp(0) 30 | 31 | 32 | class DatetimeMS: 33 | """Represents a BSON UTC datetime.""" 34 | 35 | __slots__ = ("_value",) 36 | 37 | def __init__(self, value: Union[int, datetime.datetime]): 38 | """Represents a BSON UTC datetime. 39 | 40 | BSON UTC datetimes are defined as an int64 of milliseconds since the 41 | Unix epoch. The principal use of DatetimeMS is to represent 42 | datetimes outside the range of the Python builtin 43 | :class:`~datetime.datetime` class when 44 | encoding/decoding BSON. 45 | 46 | To decode UTC datetimes as a ``DatetimeMS``, `datetime_conversion` in 47 | :class:`~bson.CodecOptions` must be set to 'datetime_ms' or 48 | 'datetime_auto'. See :ref:`handling-out-of-range-datetimes` for 49 | details. 50 | 51 | :Parameters: 52 | - `value`: An instance of :class:`datetime.datetime` to be 53 | represented as milliseconds since the Unix epoch, or int of 54 | milliseconds since the Unix epoch. 55 | """ 56 | if isinstance(value, int): 57 | if not (-(2**63) <= value <= 2**63 - 1): 58 | raise OverflowError("Must be a 64-bit integer of milliseconds") 59 | self._value = value 60 | elif isinstance(value, datetime.datetime): 61 | self._value = _datetime_to_millis(value) 62 | else: 63 | raise TypeError(f"{type(value)} is not a valid type for DatetimeMS") 64 | 65 | def __hash__(self) -> int: 66 | return hash(self._value) 67 | 68 | def __repr__(self) -> str: 69 | return type(self).__name__ + "(" + str(self._value) + ")" 70 | 71 | def __lt__(self, other: Union["DatetimeMS", int]) -> bool: 72 | return self._value < other 73 | 74 | def __le__(self, other: Union["DatetimeMS", int]) -> bool: 75 | return self._value <= other 76 | 77 | def __eq__(self, other: Any) -> bool: 78 | if isinstance(other, DatetimeMS): 79 | return self._value == other._value 80 | return False 81 | 82 | def __ne__(self, other: Any) -> bool: 83 | if isinstance(other, DatetimeMS): 84 | return self._value != other._value 85 | return True 86 | 87 | def __gt__(self, other: Union["DatetimeMS", int]) -> bool: 88 | return self._value > other 89 | 90 | def __ge__(self, other: Union["DatetimeMS", int]) -> bool: 91 | return self._value >= other 92 | 93 | _type_marker = 9 94 | 95 | def as_datetime(self, codec_options: CodecOptions = DEFAULT_CODEC_OPTIONS) -> datetime.datetime: 96 | """Create a Python :class:`~datetime.datetime` from this DatetimeMS object. 97 | 98 | :Parameters: 99 | - `codec_options`: A CodecOptions instance for specifying how the 100 | resulting DatetimeMS object will be formatted using ``tz_aware`` 101 | and ``tz_info``. Defaults to 102 | :const:`~bson.codec_options.DEFAULT_CODEC_OPTIONS`. 103 | """ 104 | return cast(datetime.datetime, _millis_to_datetime(self._value, codec_options)) 105 | 106 | def __int__(self) -> int: 107 | return self._value 108 | 109 | 110 | # Inclusive and exclusive min and max for timezones. 111 | # Timezones are hashed by their offset, which is a timedelta 112 | # and therefore there are more than 24 possible timezones. 113 | @functools.lru_cache(maxsize=None) 114 | def _min_datetime_ms(tz=datetime.timezone.utc): 115 | return _datetime_to_millis(datetime.datetime.min.replace(tzinfo=tz)) 116 | 117 | 118 | @functools.lru_cache(maxsize=None) 119 | def _max_datetime_ms(tz=datetime.timezone.utc): 120 | return _datetime_to_millis(datetime.datetime.max.replace(tzinfo=tz)) 121 | 122 | 123 | def _millis_to_datetime(millis: int, opts: CodecOptions) -> Union[datetime.datetime, DatetimeMS]: 124 | """Convert milliseconds since epoch UTC to datetime.""" 125 | if ( 126 | opts.datetime_conversion == DatetimeConversion.DATETIME 127 | or opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP 128 | or opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO 129 | ): 130 | tz = opts.tzinfo or datetime.timezone.utc 131 | if opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP: 132 | millis = max(_min_datetime_ms(tz), min(millis, _max_datetime_ms(tz))) 133 | elif opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO: 134 | if not (_min_datetime_ms(tz) <= millis <= _max_datetime_ms(tz)): 135 | return DatetimeMS(millis) 136 | 137 | diff = ((millis % 1000) + 1000) % 1000 138 | seconds = (millis - diff) // 1000 139 | micros = diff * 1000 140 | 141 | if opts.tz_aware: 142 | dt = EPOCH_AWARE + datetime.timedelta(seconds=seconds, microseconds=micros) 143 | if opts.tzinfo: 144 | dt = dt.astimezone(tz) 145 | return dt 146 | else: 147 | return EPOCH_NAIVE + datetime.timedelta(seconds=seconds, microseconds=micros) 148 | elif opts.datetime_conversion == DatetimeConversion.DATETIME_MS: 149 | return DatetimeMS(millis) 150 | else: 151 | raise ValueError("datetime_conversion must be an element of DatetimeConversion") 152 | 153 | 154 | def _datetime_to_millis(dtm: datetime.datetime) -> int: 155 | """Convert datetime to milliseconds since epoch UTC.""" 156 | if dtm.utcoffset() is not None: 157 | dtm = dtm - dtm.utcoffset() # type: ignore 158 | return int(calendar.timegm(dtm.timetuple()) * 1000 + dtm.microsecond // 1000) 159 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/dbref.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for manipulating DBRefs (references to MongoDB documents).""" 16 | 17 | from copy import deepcopy 18 | from typing import Any, Mapping, Optional 19 | 20 | from ._helpers import _getstate_slots, _setstate_slots 21 | from .son import SON 22 | 23 | 24 | class DBRef(object): 25 | """A reference to a document stored in MongoDB.""" 26 | 27 | __slots__ = "__collection", "__id", "__database", "__kwargs" 28 | __getstate__ = _getstate_slots 29 | __setstate__ = _setstate_slots 30 | # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. 31 | _type_marker = 100 32 | 33 | def __init__( 34 | self, 35 | collection: str, 36 | id: Any, 37 | database: Optional[str] = None, 38 | _extra: Optional[Mapping[str, Any]] = None, 39 | **kwargs: Any 40 | ) -> None: 41 | """Initialize a new :class:`DBRef`. 42 | 43 | Raises :class:`TypeError` if `collection` or `database` is not 44 | an instance of :class:`basestring` (:class:`str` in python 3). 45 | `database` is optional and allows references to documents to work 46 | across databases. Any additional keyword arguments will create 47 | additional fields in the resultant embedded document. 48 | 49 | :Parameters: 50 | - `collection`: name of the collection the document is stored in 51 | - `id`: the value of the document's ``"_id"`` field 52 | - `database` (optional): name of the database to reference 53 | - `**kwargs` (optional): additional keyword arguments will 54 | create additional, custom fields 55 | 56 | .. seealso:: The MongoDB documentation on `dbrefs `_. 57 | """ 58 | if not isinstance(collection, str): 59 | raise TypeError("collection must be an instance of str") 60 | if database is not None and not isinstance(database, str): 61 | raise TypeError("database must be an instance of str") 62 | 63 | self.__collection = collection 64 | self.__id = id 65 | self.__database = database 66 | kwargs.update(_extra or {}) 67 | self.__kwargs = kwargs 68 | 69 | @property 70 | def collection(self) -> str: 71 | """Get the name of this DBRef's collection.""" 72 | return self.__collection 73 | 74 | @property 75 | def id(self) -> Any: 76 | """Get this DBRef's _id.""" 77 | return self.__id 78 | 79 | @property 80 | def database(self) -> Optional[str]: 81 | """Get the name of this DBRef's database. 82 | 83 | Returns None if this DBRef doesn't specify a database. 84 | """ 85 | return self.__database 86 | 87 | def __getattr__(self, key: Any) -> Any: 88 | try: 89 | return self.__kwargs[key] 90 | except KeyError: 91 | raise AttributeError(key) 92 | 93 | def as_doc(self) -> SON[str, Any]: 94 | """Get the SON document representation of this DBRef. 95 | 96 | Generally not needed by application developers 97 | """ 98 | doc = SON([("$ref", self.collection), ("$id", self.id)]) 99 | if self.database is not None: 100 | doc["$db"] = self.database 101 | doc.update(self.__kwargs) 102 | return doc 103 | 104 | def __repr__(self): 105 | extra = "".join([", %s=%r" % (k, v) for k, v in self.__kwargs.items()]) 106 | if self.database is None: 107 | return "DBRef(%r, %r%s)" % (self.collection, self.id, extra) 108 | return "DBRef(%r, %r, %r%s)" % (self.collection, self.id, self.database, extra) 109 | 110 | def __eq__(self, other: Any) -> bool: 111 | if isinstance(other, DBRef): 112 | us = (self.__database, self.__collection, self.__id, self.__kwargs) 113 | them = (other.__database, other.__collection, other.__id, other.__kwargs) 114 | return us == them 115 | return NotImplemented 116 | 117 | def __ne__(self, other: Any) -> bool: 118 | return not self == other 119 | 120 | def __hash__(self) -> int: 121 | """Get a hash value for this :class:`DBRef`.""" 122 | return hash( 123 | (self.__collection, self.__id, self.__database, tuple(sorted(self.__kwargs.items()))) 124 | ) 125 | 126 | def __deepcopy__(self, memo: Any) -> "DBRef": 127 | """Support function for `copy.deepcopy()`.""" 128 | return DBRef( 129 | deepcopy(self.__collection, memo), 130 | deepcopy(self.__id, memo), 131 | deepcopy(self.__database, memo), 132 | deepcopy(self.__kwargs, memo), 133 | ) 134 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/errors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Exceptions raised by the BSON package.""" 16 | 17 | 18 | class BSONError(Exception): 19 | """Base class for all BSON exceptions.""" 20 | 21 | 22 | class InvalidBSON(BSONError): 23 | """Raised when trying to create a BSON object from invalid data.""" 24 | 25 | 26 | class InvalidStringData(BSONError): 27 | """Raised when trying to encode a string containing non-UTF8 data.""" 28 | 29 | 30 | class InvalidDocument(BSONError): 31 | """Raised when trying to create a BSON object from an invalid document.""" 32 | 33 | 34 | class InvalidId(BSONError): 35 | """Raised when trying to create an ObjectId from invalid data.""" 36 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/int64.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A BSON wrapper for long (int in python3)""" 16 | 17 | from typing import Any 18 | 19 | 20 | class Int64(int): 21 | """Representation of the BSON int64 type. 22 | 23 | This is necessary because every integral number is an :class:`int` in 24 | Python 3. Small integral numbers are encoded to BSON int32 by default, 25 | but Int64 numbers will always be encoded to BSON int64. 26 | 27 | :Parameters: 28 | - `value`: the numeric value to represent 29 | """ 30 | 31 | __slots__ = () 32 | 33 | _type_marker = 18 34 | 35 | def __getstate__(self) -> Any: 36 | return {} 37 | 38 | def __setstate__(self, state: Any) -> None: 39 | pass 40 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/max_key.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Representation for the MongoDB internal MaxKey type. 16 | """ 17 | from typing import Any 18 | 19 | 20 | class MaxKey(object): 21 | """MongoDB internal MaxKey type.""" 22 | 23 | __slots__ = () 24 | 25 | _type_marker = 127 26 | 27 | def __getstate__(self) -> Any: 28 | return {} 29 | 30 | def __setstate__(self, state: Any) -> None: 31 | pass 32 | 33 | def __eq__(self, other: Any) -> bool: 34 | return isinstance(other, MaxKey) 35 | 36 | def __hash__(self) -> int: 37 | return hash(self._type_marker) 38 | 39 | def __ne__(self, other: Any) -> bool: 40 | return not self == other 41 | 42 | def __le__(self, other: Any) -> bool: 43 | return isinstance(other, MaxKey) 44 | 45 | def __lt__(self, dummy: Any) -> bool: 46 | return False 47 | 48 | def __ge__(self, dummy: Any) -> bool: 49 | return True 50 | 51 | def __gt__(self, other: Any) -> bool: 52 | return not isinstance(other, MaxKey) 53 | 54 | def __repr__(self): 55 | return "MaxKey()" 56 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/min_key.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Representation for the MongoDB internal MinKey type. 16 | """ 17 | from typing import Any 18 | 19 | 20 | class MinKey(object): 21 | """MongoDB internal MinKey type.""" 22 | 23 | __slots__ = () 24 | 25 | _type_marker = 255 26 | 27 | def __getstate__(self) -> Any: 28 | return {} 29 | 30 | def __setstate__(self, state: Any) -> None: 31 | pass 32 | 33 | def __eq__(self, other: Any) -> bool: 34 | return isinstance(other, MinKey) 35 | 36 | def __hash__(self) -> int: 37 | return hash(self._type_marker) 38 | 39 | def __ne__(self, other: Any) -> bool: 40 | return not self == other 41 | 42 | def __le__(self, dummy: Any) -> bool: 43 | return True 44 | 45 | def __lt__(self, other: Any) -> bool: 46 | return not isinstance(other, MinKey) 47 | 48 | def __ge__(self, other: Any) -> bool: 49 | return isinstance(other, MinKey) 50 | 51 | def __gt__(self, dummy: Any) -> bool: 52 | return False 53 | 54 | def __repr__(self): 55 | return "MinKey()" 56 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/objectid.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for working with MongoDB ObjectIds. 16 | """ 17 | 18 | import binascii 19 | import calendar 20 | import datetime 21 | import os 22 | import struct 23 | import threading 24 | import time 25 | from random import SystemRandom 26 | from typing import Any, NoReturn, Optional, Type, Union 27 | 28 | from .errors import InvalidId 29 | from .tz_util import utc 30 | 31 | _MAX_COUNTER_VALUE = 0xFFFFFF 32 | 33 | 34 | def _raise_invalid_id(oid: str) -> NoReturn: 35 | raise InvalidId( 36 | "%r is not a valid ObjectId, it must be a 12-byte input" 37 | " or a 24-character hex string" % oid 38 | ) 39 | 40 | 41 | def _random_bytes() -> bytes: 42 | """Get the 5-byte random field of an ObjectId.""" 43 | return os.urandom(5) 44 | 45 | 46 | class ObjectId(object): 47 | """A MongoDB ObjectId.""" 48 | 49 | _pid = os.getpid() 50 | 51 | _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) 52 | _inc_lock = threading.Lock() 53 | 54 | __random = _random_bytes() 55 | 56 | __slots__ = ("__id",) 57 | 58 | _type_marker = 7 59 | 60 | def __init__(self, oid: Optional[Union[str, "ObjectId", bytes]] = None) -> None: 61 | """Initialize a new ObjectId. 62 | 63 | An ObjectId is a 12-byte unique identifier consisting of: 64 | 65 | - a 4-byte value representing the seconds since the Unix epoch, 66 | - a 5-byte random value, 67 | - a 3-byte counter, starting with a random value. 68 | 69 | By default, ``ObjectId()`` creates a new unique identifier. The 70 | optional parameter `oid` can be an :class:`ObjectId`, or any 12 71 | :class:`bytes`. 72 | 73 | For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId 74 | specification but they are acceptable input:: 75 | 76 | >>> ObjectId(b'foo-bar-quux') 77 | ObjectId('666f6f2d6261722d71757578') 78 | 79 | `oid` can also be a :class:`str` of 24 hex digits:: 80 | 81 | >>> ObjectId('0123456789ab0123456789ab') 82 | ObjectId('0123456789ab0123456789ab') 83 | 84 | Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor 85 | 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. 86 | 87 | :Parameters: 88 | - `oid` (optional): a valid ObjectId. 89 | 90 | .. seealso:: The MongoDB documentation on `ObjectIds `_. 91 | 92 | .. versionchanged:: 3.8 93 | :class:`~bson.objectid.ObjectId` now implements the `ObjectID 94 | specification version 0.2 95 | `_. 97 | """ 98 | if oid is None: 99 | self.__generate() 100 | elif isinstance(oid, bytes) and len(oid) == 12: 101 | self.__id = oid 102 | else: 103 | self.__validate(oid) 104 | 105 | @classmethod 106 | def from_datetime(cls: Type["ObjectId"], generation_time: datetime.datetime) -> "ObjectId": 107 | """Create a dummy ObjectId instance with a specific generation time. 108 | 109 | This method is useful for doing range queries on a field 110 | containing :class:`ObjectId` instances. 111 | 112 | .. warning:: 113 | It is not safe to insert a document containing an ObjectId 114 | generated using this method. This method deliberately 115 | eliminates the uniqueness guarantee that ObjectIds 116 | generally provide. ObjectIds generated with this method 117 | should be used exclusively in queries. 118 | 119 | `generation_time` will be converted to UTC. Naive datetime 120 | instances will be treated as though they already contain UTC. 121 | 122 | An example using this helper to get documents where ``"_id"`` 123 | was generated before January 1, 2010 would be: 124 | 125 | >>> gen_time = datetime.datetime(2010, 1, 1) 126 | >>> dummy_id = ObjectId.from_datetime(gen_time) 127 | >>> result = collection.find({"_id": {"$lt": dummy_id}}) 128 | 129 | :Parameters: 130 | - `generation_time`: :class:`~datetime.datetime` to be used 131 | as the generation time for the resulting ObjectId. 132 | """ 133 | offset = generation_time.utcoffset() 134 | if offset is not None: 135 | generation_time = generation_time - offset 136 | timestamp = calendar.timegm(generation_time.timetuple()) 137 | oid = struct.pack(">I", int(timestamp)) + b"\x00\x00\x00\x00\x00\x00\x00\x00" 138 | return cls(oid) 139 | 140 | @classmethod 141 | def is_valid(cls: Type["ObjectId"], oid: Any) -> bool: 142 | """Checks if a `oid` string is valid or not. 143 | 144 | :Parameters: 145 | - `oid`: the object id to validate 146 | 147 | .. versionadded:: 2.3 148 | """ 149 | if not oid: 150 | return False 151 | 152 | try: 153 | ObjectId(oid) 154 | return True 155 | except (InvalidId, TypeError): 156 | return False 157 | 158 | @classmethod 159 | def _random(cls) -> bytes: 160 | """Generate a 5-byte random number once per process.""" 161 | pid = os.getpid() 162 | if pid != cls._pid: 163 | cls._pid = pid 164 | cls.__random = _random_bytes() 165 | return cls.__random 166 | 167 | def __generate(self) -> None: 168 | """Generate a new value for this ObjectId.""" 169 | 170 | # 4 bytes current time 171 | oid = struct.pack(">I", int(time.time())) 172 | 173 | # 5 bytes random 174 | oid += ObjectId._random() 175 | 176 | # 3 bytes inc 177 | with ObjectId._inc_lock: 178 | oid += struct.pack(">I", ObjectId._inc)[1:4] 179 | ObjectId._inc = (ObjectId._inc + 1) % (_MAX_COUNTER_VALUE + 1) 180 | 181 | self.__id = oid 182 | 183 | def __validate(self, oid: Any) -> None: 184 | """Validate and use the given id for this ObjectId. 185 | 186 | Raises TypeError if id is not an instance of 187 | (:class:`basestring` (:class:`str` or :class:`bytes` 188 | in python 3), ObjectId) and InvalidId if it is not a 189 | valid ObjectId. 190 | 191 | :Parameters: 192 | - `oid`: a valid ObjectId 193 | """ 194 | if isinstance(oid, ObjectId): 195 | self.__id = oid.binary 196 | elif isinstance(oid, str): 197 | if len(oid) == 24: 198 | try: 199 | self.__id = bytes.fromhex(oid) 200 | except (TypeError, ValueError): 201 | _raise_invalid_id(oid) 202 | else: 203 | _raise_invalid_id(oid) 204 | else: 205 | raise TypeError( 206 | "id must be an instance of (bytes, str, ObjectId), not %s" % (type(oid),) 207 | ) 208 | 209 | @property 210 | def binary(self) -> bytes: 211 | """12-byte binary representation of this ObjectId.""" 212 | return self.__id 213 | 214 | @property 215 | def generation_time(self) -> datetime.datetime: 216 | """A :class:`datetime.datetime` instance representing the time of 217 | generation for this :class:`ObjectId`. 218 | 219 | The :class:`datetime.datetime` is timezone aware, and 220 | represents the generation time in UTC. It is precise to the 221 | second. 222 | """ 223 | timestamp = struct.unpack(">I", self.__id[0:4])[0] 224 | return datetime.datetime.fromtimestamp(timestamp, utc) 225 | 226 | def __getstate__(self) -> bytes: 227 | """return value of object for pickling. 228 | needed explicitly because __slots__() defined. 229 | """ 230 | return self.__id 231 | 232 | def __setstate__(self, value: Any) -> None: 233 | """explicit state set from pickling""" 234 | # Provide backwards compatability with OIDs 235 | # pickled with pymongo-1.9 or older. 236 | if isinstance(value, dict): 237 | oid = value["_ObjectId__id"] 238 | else: 239 | oid = value 240 | # ObjectIds pickled in python 2.x used `str` for __id. 241 | # In python 3.x this has to be converted to `bytes` 242 | # by encoding latin-1. 243 | if isinstance(oid, str): 244 | self.__id = oid.encode("latin-1") 245 | else: 246 | self.__id = oid 247 | 248 | def __str__(self) -> str: 249 | return binascii.hexlify(self.__id).decode() 250 | 251 | def __repr__(self): 252 | return "ObjectId('%s')" % (str(self),) 253 | 254 | def __eq__(self, other: Any) -> bool: 255 | if isinstance(other, ObjectId): 256 | return self.__id == other.binary 257 | return NotImplemented 258 | 259 | def __ne__(self, other: Any) -> bool: 260 | if isinstance(other, ObjectId): 261 | return self.__id != other.binary 262 | return NotImplemented 263 | 264 | def __lt__(self, other: Any) -> bool: 265 | if isinstance(other, ObjectId): 266 | return self.__id < other.binary 267 | return NotImplemented 268 | 269 | def __le__(self, other: Any) -> bool: 270 | if isinstance(other, ObjectId): 271 | return self.__id <= other.binary 272 | return NotImplemented 273 | 274 | def __gt__(self, other: Any) -> bool: 275 | if isinstance(other, ObjectId): 276 | return self.__id > other.binary 277 | return NotImplemented 278 | 279 | def __ge__(self, other: Any) -> bool: 280 | if isinstance(other, ObjectId): 281 | return self.__id >= other.binary 282 | return NotImplemented 283 | 284 | def __hash__(self) -> int: 285 | """Get a hash value for this :class:`ObjectId`.""" 286 | return hash(self.__id) 287 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/py.typed: -------------------------------------------------------------------------------- 1 | # PEP-561 Support File. 2 | # "Package maintainers who wish to support type checking of their code MUST add a marker file named py.typed to their package supporting typing". 3 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/raw_bson.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing raw BSON documents. 16 | 17 | Inserting and Retrieving RawBSONDocuments 18 | ========================================= 19 | 20 | Example: Moving a document between different databases/collections 21 | 22 | .. doctest:: 23 | 24 | >>> import bson 25 | >>> from pymongo import MongoClient 26 | >>> from .raw_bson import RawBSONDocument 27 | >>> client = MongoClient(document_class=RawBSONDocument) 28 | >>> client.drop_database('db') 29 | >>> client.drop_database('replica_db') 30 | >>> db = client.db 31 | >>> result = db.test.insert_many([{'_id': 1, 'a': 1}, 32 | ... {'_id': 2, 'b': 1}, 33 | ... {'_id': 3, 'c': 1}, 34 | ... {'_id': 4, 'd': 1}]) 35 | >>> replica_db = client.replica_db 36 | >>> for doc in db.test.find(): 37 | ... print(f"raw document: {doc.raw}") 38 | ... print(f"decoded document: {bson.decode(doc.raw)}") 39 | ... result = replica_db.test.insert_one(doc) 40 | raw document: b'...' 41 | decoded document: {'_id': 1, 'a': 1} 42 | raw document: b'...' 43 | decoded document: {'_id': 2, 'b': 1} 44 | raw document: b'...' 45 | decoded document: {'_id': 3, 'c': 1} 46 | raw document: b'...' 47 | decoded document: {'_id': 4, 'd': 1} 48 | 49 | For use cases like moving documents across different databases or writing binary 50 | blobs to disk, using raw BSON documents provides better speed and avoids the 51 | overhead of decoding or encoding BSON. 52 | """ 53 | 54 | from typing import Any, ItemsView, Iterator, Mapping, Optional 55 | 56 | from . import _get_object_size, _raw_to_dict 57 | from .codec_options import _RAW_BSON_DOCUMENT_MARKER 58 | from .codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT 59 | from .codec_options import CodecOptions 60 | from .son import SON 61 | 62 | 63 | def _inflate_bson( 64 | bson_bytes: bytes, codec_options: CodecOptions, raw_array: bool = False 65 | ) -> Mapping[Any, Any]: 66 | """Inflates the top level fields of a BSON document. 67 | 68 | :Parameters: 69 | - `bson_bytes`: the BSON bytes that compose this document 70 | - `codec_options`: An instance of 71 | :class:`~bson.codec_options.CodecOptions` whose ``document_class`` 72 | must be :class:`RawBSONDocument`. 73 | """ 74 | # Use SON to preserve ordering of elements. 75 | return _raw_to_dict( 76 | bson_bytes, 4, len(bson_bytes) - 1, codec_options, SON(), raw_array=raw_array 77 | ) 78 | 79 | 80 | class RawBSONDocument(Mapping[str, Any]): 81 | """Representation for a MongoDB document that provides access to the raw 82 | BSON bytes that compose it. 83 | 84 | Only when a field is accessed or modified within the document does 85 | RawBSONDocument decode its bytes. 86 | """ 87 | 88 | __slots__ = ("__raw", "__inflated_doc", "__codec_options") 89 | _type_marker = _RAW_BSON_DOCUMENT_MARKER 90 | 91 | def __init__(self, bson_bytes: bytes, codec_options: Optional[CodecOptions] = None) -> None: 92 | """Create a new :class:`RawBSONDocument` 93 | 94 | :class:`RawBSONDocument` is a representation of a BSON document that 95 | provides access to the underlying raw BSON bytes. Only when a field is 96 | accessed or modified within the document does RawBSONDocument decode 97 | its bytes. 98 | 99 | :class:`RawBSONDocument` implements the ``Mapping`` abstract base 100 | class from the standard library so it can be used like a read-only 101 | ``dict``:: 102 | 103 | >>> from . import encode 104 | >>> raw_doc = RawBSONDocument(encode({'_id': 'my_doc'})) 105 | >>> raw_doc.raw 106 | b'...' 107 | >>> raw_doc['_id'] 108 | 'my_doc' 109 | 110 | :Parameters: 111 | - `bson_bytes`: the BSON bytes that compose this document 112 | - `codec_options` (optional): An instance of 113 | :class:`~bson.codec_options.CodecOptions` whose ``document_class`` 114 | must be :class:`RawBSONDocument`. The default is 115 | :attr:`DEFAULT_RAW_BSON_OPTIONS`. 116 | 117 | .. versionchanged:: 3.8 118 | :class:`RawBSONDocument` now validates that the ``bson_bytes`` 119 | passed in represent a single bson document. 120 | 121 | .. versionchanged:: 3.5 122 | If a :class:`~bson.codec_options.CodecOptions` is passed in, its 123 | `document_class` must be :class:`RawBSONDocument`. 124 | """ 125 | self.__raw = bson_bytes 126 | self.__inflated_doc: Optional[Mapping[str, Any]] = None 127 | # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, 128 | # it refers to this class RawBSONDocument. 129 | if codec_options is None: 130 | codec_options = DEFAULT_RAW_BSON_OPTIONS 131 | elif not issubclass(codec_options.document_class, RawBSONDocument): 132 | raise TypeError( 133 | "RawBSONDocument cannot use CodecOptions with document " 134 | "class %s" % (codec_options.document_class,) 135 | ) 136 | self.__codec_options = codec_options 137 | # Validate the bson object size. 138 | _get_object_size(bson_bytes, 0, len(bson_bytes)) 139 | 140 | @property 141 | def raw(self) -> bytes: 142 | """The raw BSON bytes composing this document.""" 143 | return self.__raw 144 | 145 | def items(self) -> ItemsView[str, Any]: 146 | """Lazily decode and iterate elements in this document.""" 147 | return self.__inflated.items() 148 | 149 | @property 150 | def __inflated(self) -> Mapping[str, Any]: 151 | if self.__inflated_doc is None: 152 | # We already validated the object's size when this document was 153 | # created, so no need to do that again. 154 | # Use SON to preserve ordering of elements. 155 | self.__inflated_doc = self._inflate_bson(self.__raw, self.__codec_options) 156 | return self.__inflated_doc 157 | 158 | @staticmethod 159 | def _inflate_bson(bson_bytes: bytes, codec_options: CodecOptions) -> Mapping[Any, Any]: 160 | return _inflate_bson(bson_bytes, codec_options) 161 | 162 | def __getitem__(self, item: str) -> Any: 163 | return self.__inflated[item] 164 | 165 | def __iter__(self) -> Iterator[str]: 166 | return iter(self.__inflated) 167 | 168 | def __len__(self) -> int: 169 | return len(self.__inflated) 170 | 171 | def __eq__(self, other: Any) -> bool: 172 | if isinstance(other, RawBSONDocument): 173 | return self.__raw == other.raw 174 | return NotImplemented 175 | 176 | def __repr__(self): 177 | return "%s(%r, codec_options=%r)" % ( 178 | self.__class__.__name__, 179 | self.raw, 180 | self.__codec_options, 181 | ) 182 | 183 | 184 | class _RawArrayBSONDocument(RawBSONDocument): 185 | """A RawBSONDocument that only expands sub-documents and arrays when accessed.""" 186 | 187 | @staticmethod 188 | def _inflate_bson(bson_bytes: bytes, codec_options: CodecOptions) -> Mapping[Any, Any]: 189 | return _inflate_bson(bson_bytes, codec_options, raw_array=True) 190 | 191 | 192 | DEFAULT_RAW_BSON_OPTIONS: CodecOptions = DEFAULT.with_options(document_class=RawBSONDocument) 193 | _RAW_ARRAY_BSON_OPTIONS: CodecOptions = DEFAULT.with_options(document_class=_RawArrayBSONDocument) 194 | """The default :class:`~bson.codec_options.CodecOptions` for 195 | :class:`RawBSONDocument`. 196 | """ 197 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/regex.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing MongoDB regular expressions. 16 | """ 17 | 18 | import re 19 | from typing import Any, Generic, Pattern, Type, TypeVar, Union 20 | 21 | from ._helpers import _getstate_slots, _setstate_slots 22 | from .son import RE_TYPE 23 | 24 | 25 | def str_flags_to_int(str_flags: str) -> int: 26 | flags = 0 27 | if "i" in str_flags: 28 | flags |= re.IGNORECASE 29 | if "l" in str_flags: 30 | flags |= re.LOCALE 31 | if "m" in str_flags: 32 | flags |= re.MULTILINE 33 | if "s" in str_flags: 34 | flags |= re.DOTALL 35 | if "u" in str_flags: 36 | flags |= re.UNICODE 37 | if "x" in str_flags: 38 | flags |= re.VERBOSE 39 | 40 | return flags 41 | 42 | 43 | _T = TypeVar("_T", str, bytes) 44 | 45 | 46 | class Regex(Generic[_T]): 47 | """BSON regular expression data.""" 48 | 49 | __slots__ = ("pattern", "flags") 50 | 51 | __getstate__ = _getstate_slots 52 | __setstate__ = _setstate_slots 53 | 54 | _type_marker = 11 55 | 56 | @classmethod 57 | def from_native(cls: Type["Regex"], regex: "Pattern[_T]") -> "Regex[_T]": 58 | """Convert a Python regular expression into a ``Regex`` instance. 59 | 60 | Note that in Python 3, a regular expression compiled from a 61 | :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable 62 | to store this flag in a BSON regular expression, unset it first:: 63 | 64 | >>> pattern = re.compile('.*') 65 | >>> regex = Regex.from_native(pattern) 66 | >>> regex.flags ^= re.UNICODE 67 | >>> db.collection.insert_one({'pattern': regex}) 68 | 69 | :Parameters: 70 | - `regex`: A regular expression object from ``re.compile()``. 71 | 72 | .. warning:: 73 | Python regular expressions use a different syntax and different 74 | set of flags than MongoDB, which uses `PCRE`_. A regular 75 | expression retrieved from the server may not compile in 76 | Python, or may match a different set of strings in Python than 77 | when used in a MongoDB query. 78 | 79 | .. _PCRE: http://www.pcre.org/ 80 | """ 81 | if not isinstance(regex, RE_TYPE): 82 | raise TypeError("regex must be a compiled regular expression, not %s" % type(regex)) 83 | 84 | return Regex(regex.pattern, regex.flags) 85 | 86 | def __init__(self, pattern: _T, flags: Union[str, int] = 0) -> None: 87 | """BSON regular expression data. 88 | 89 | This class is useful to store and retrieve regular expressions that are 90 | incompatible with Python's regular expression dialect. 91 | 92 | :Parameters: 93 | - `pattern`: string 94 | - `flags`: (optional) an integer bitmask, or a string of flag 95 | characters like "im" for IGNORECASE and MULTILINE 96 | """ 97 | if not isinstance(pattern, (str, bytes)): 98 | raise TypeError("pattern must be a string, not %s" % type(pattern)) 99 | self.pattern: _T = pattern 100 | 101 | if isinstance(flags, str): 102 | self.flags = str_flags_to_int(flags) 103 | elif isinstance(flags, int): 104 | self.flags = flags 105 | else: 106 | raise TypeError("flags must be a string or int, not %s" % type(flags)) 107 | 108 | def __eq__(self, other: Any) -> bool: 109 | if isinstance(other, Regex): 110 | return self.pattern == other.pattern and self.flags == other.flags 111 | else: 112 | return NotImplemented 113 | 114 | __hash__ = None # type: ignore 115 | 116 | def __ne__(self, other: Any) -> bool: 117 | return not self == other 118 | 119 | def __repr__(self): 120 | return "Regex(%r, %r)" % (self.pattern, self.flags) 121 | 122 | def try_compile(self) -> "Pattern[_T]": 123 | """Compile this :class:`Regex` as a Python regular expression. 124 | 125 | .. warning:: 126 | Python regular expressions use a different syntax and different 127 | set of flags than MongoDB, which uses `PCRE`_. A regular 128 | expression retrieved from the server may not compile in 129 | Python, or may match a different set of strings in Python than 130 | when used in a MongoDB query. :meth:`try_compile()` may raise 131 | :exc:`re.error`. 132 | 133 | .. _PCRE: http://www.pcre.org/ 134 | """ 135 | return re.compile(self.pattern, self.flags) 136 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/son.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009-present MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for creating and manipulating SON, the Serialized Ocument Notation. 16 | 17 | Regular dictionaries can be used instead of SON objects, but not when the order 18 | of keys is important. A SON object can be used just like a normal Python 19 | dictionary.""" 20 | 21 | import copy 22 | import re 23 | from collections.abc import Mapping as _Mapping 24 | from typing import ( 25 | Any, 26 | Dict, 27 | Iterable, 28 | Iterator, 29 | List, 30 | Mapping, 31 | Optional, 32 | Pattern, 33 | Tuple, 34 | Type, 35 | TypeVar, 36 | Union, 37 | ) 38 | 39 | # This sort of sucks, but seems to be as good as it gets... 40 | # This is essentially the same as re._pattern_type 41 | RE_TYPE: Type[Pattern[Any]] = type(re.compile("")) 42 | 43 | _Key = TypeVar("_Key") 44 | _Value = TypeVar("_Value") 45 | _T = TypeVar("_T") 46 | 47 | 48 | class SON(Dict[_Key, _Value]): 49 | """SON data. 50 | 51 | A subclass of dict that maintains ordering of keys and provides a 52 | few extra niceties for dealing with SON. SON provides an API 53 | similar to collections.OrderedDict. 54 | """ 55 | 56 | __keys: List[Any] 57 | 58 | def __init__( 59 | self, 60 | data: Optional[Union[Mapping[_Key, _Value], Iterable[Tuple[_Key, _Value]]]] = None, 61 | **kwargs: Any 62 | ) -> None: 63 | self.__keys = [] 64 | dict.__init__(self) 65 | self.update(data) 66 | self.update(kwargs) 67 | 68 | def __new__(cls: Type["SON[_Key, _Value]"], *args: Any, **kwargs: Any) -> "SON[_Key, _Value]": 69 | instance = super(SON, cls).__new__(cls, *args, **kwargs) 70 | instance.__keys = [] 71 | return instance 72 | 73 | def __repr__(self): 74 | result = [] 75 | for key in self.__keys: 76 | result.append("(%r, %r)" % (key, self[key])) 77 | return "SON([%s])" % ", ".join(result) 78 | 79 | def __setitem__(self, key: _Key, value: _Value) -> None: 80 | if key not in self.__keys: 81 | self.__keys.append(key) 82 | dict.__setitem__(self, key, value) 83 | 84 | def __delitem__(self, key: _Key) -> None: 85 | self.__keys.remove(key) 86 | dict.__delitem__(self, key) 87 | 88 | def copy(self) -> "SON[_Key, _Value]": 89 | other: SON[_Key, _Value] = SON() 90 | other.update(self) 91 | return other 92 | 93 | # TODO this is all from UserDict.DictMixin. it could probably be made more 94 | # efficient. 95 | # second level definitions support higher levels 96 | def __iter__(self) -> Iterator[_Key]: 97 | for k in self.__keys: 98 | yield k 99 | 100 | def has_key(self, key: _Key) -> bool: 101 | return key in self.__keys 102 | 103 | def iterkeys(self) -> Iterator[_Key]: 104 | return self.__iter__() 105 | 106 | # fourth level uses definitions from lower levels 107 | def itervalues(self) -> Iterator[_Value]: 108 | for _, v in self.items(): 109 | yield v 110 | 111 | def values(self) -> List[_Value]: # type: ignore[override] 112 | return [v for _, v in self.items()] 113 | 114 | def clear(self) -> None: 115 | self.__keys = [] 116 | super(SON, self).clear() 117 | 118 | def setdefault(self, key: _Key, default: _Value) -> _Value: # type: ignore[override] 119 | try: 120 | return self[key] 121 | except KeyError: 122 | self[key] = default 123 | return default 124 | 125 | def pop(self, key: _Key, *args: Union[_Value, _T]) -> Union[_Value, _T]: 126 | if len(args) > 1: 127 | raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) 128 | try: 129 | value = self[key] 130 | except KeyError: 131 | if args: 132 | return args[0] 133 | raise 134 | del self[key] 135 | return value 136 | 137 | def popitem(self) -> Tuple[_Key, _Value]: 138 | try: 139 | k, v = next(iter(self.items())) 140 | except StopIteration: 141 | raise KeyError("container is empty") 142 | del self[k] 143 | return (k, v) 144 | 145 | def update(self, other: Optional[Any] = None, **kwargs: _Value) -> None: # type: ignore[override] 146 | # Make progressively weaker assumptions about "other" 147 | if other is None: 148 | pass 149 | elif hasattr(other, "items"): 150 | for k, v in other.items(): 151 | self[k] = v 152 | elif hasattr(other, "keys"): 153 | for k in other.keys(): 154 | self[k] = other[k] 155 | else: 156 | for k, v in other: 157 | self[k] = v 158 | if kwargs: 159 | self.update(kwargs) 160 | 161 | def get(self, key: _Key, default: Optional[Union[_Value, _T]] = None) -> Union[_Value, _T, None]: # type: ignore[override] 162 | try: 163 | return self[key] 164 | except KeyError: 165 | return default 166 | 167 | def __eq__(self, other: Any) -> bool: 168 | """Comparison to another SON is order-sensitive while comparison to a 169 | regular dictionary is order-insensitive. 170 | """ 171 | if isinstance(other, SON): 172 | return len(self) == len(other) and list(self.items()) == list(other.items()) 173 | return self.to_dict() == other 174 | 175 | def __ne__(self, other: Any) -> bool: 176 | return not self == other 177 | 178 | def __len__(self) -> int: 179 | return len(self.__keys) 180 | 181 | def to_dict(self) -> Dict[_Key, _Value]: 182 | """Convert a SON document to a normal Python dictionary instance. 183 | 184 | This is trickier than just *dict(...)* because it needs to be 185 | recursive. 186 | """ 187 | 188 | def transform_value(value: Any) -> Any: 189 | if isinstance(value, list): 190 | return [transform_value(v) for v in value] 191 | elif isinstance(value, _Mapping): 192 | return dict([(k, transform_value(v)) for k, v in value.items()]) 193 | else: 194 | return value 195 | 196 | return transform_value(dict(self)) 197 | 198 | def __deepcopy__(self, memo: Dict[int, "SON[_Key, _Value]"]) -> "SON[_Key, _Value]": 199 | out: SON[_Key, _Value] = SON() 200 | val_id = id(self) 201 | if val_id in memo: 202 | return memo[val_id] 203 | memo[val_id] = out 204 | for k, v in self.items(): 205 | if not isinstance(v, RE_TYPE): 206 | v = copy.deepcopy(v, memo) 207 | out[k] = v 208 | return out 209 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/timestamp.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tools for representing MongoDB internal Timestamps. 16 | """ 17 | 18 | import calendar 19 | import datetime 20 | from typing import Any, Union 21 | 22 | from ._helpers import _getstate_slots, _setstate_slots 23 | from .tz_util import utc 24 | 25 | UPPERBOUND = 4294967296 26 | 27 | 28 | class Timestamp(object): 29 | """MongoDB internal timestamps used in the opLog.""" 30 | 31 | __slots__ = ("__time", "__inc") 32 | 33 | __getstate__ = _getstate_slots 34 | __setstate__ = _setstate_slots 35 | 36 | _type_marker = 17 37 | 38 | def __init__(self, time: Union[datetime.datetime, int], inc: int) -> None: 39 | """Create a new :class:`Timestamp`. 40 | 41 | This class is only for use with the MongoDB opLog. If you need 42 | to store a regular timestamp, please use a 43 | :class:`~datetime.datetime`. 44 | 45 | Raises :class:`TypeError` if `time` is not an instance of 46 | :class: `int` or :class:`~datetime.datetime`, or `inc` is not 47 | an instance of :class:`int`. Raises :class:`ValueError` if 48 | `time` or `inc` is not in [0, 2**32). 49 | 50 | :Parameters: 51 | - `time`: time in seconds since epoch UTC, or a naive UTC 52 | :class:`~datetime.datetime`, or an aware 53 | :class:`~datetime.datetime` 54 | - `inc`: the incrementing counter 55 | """ 56 | if isinstance(time, datetime.datetime): 57 | offset = time.utcoffset() 58 | if offset is not None: 59 | time = time - offset 60 | time = int(calendar.timegm(time.timetuple())) 61 | if not isinstance(time, int): 62 | raise TypeError("time must be an instance of int") 63 | if not isinstance(inc, int): 64 | raise TypeError("inc must be an instance of int") 65 | if not 0 <= time < UPPERBOUND: 66 | raise ValueError("time must be contained in [0, 2**32)") 67 | if not 0 <= inc < UPPERBOUND: 68 | raise ValueError("inc must be contained in [0, 2**32)") 69 | 70 | self.__time = time 71 | self.__inc = inc 72 | 73 | @property 74 | def time(self) -> int: 75 | """Get the time portion of this :class:`Timestamp`.""" 76 | return self.__time 77 | 78 | @property 79 | def inc(self) -> int: 80 | """Get the inc portion of this :class:`Timestamp`.""" 81 | return self.__inc 82 | 83 | def __eq__(self, other: Any) -> bool: 84 | if isinstance(other, Timestamp): 85 | return self.__time == other.time and self.__inc == other.inc 86 | else: 87 | return NotImplemented 88 | 89 | def __hash__(self) -> int: 90 | return hash(self.time) ^ hash(self.inc) 91 | 92 | def __ne__(self, other: Any) -> bool: 93 | return not self == other 94 | 95 | def __lt__(self, other: Any) -> bool: 96 | if isinstance(other, Timestamp): 97 | return (self.time, self.inc) < (other.time, other.inc) 98 | return NotImplemented 99 | 100 | def __le__(self, other: Any) -> bool: 101 | if isinstance(other, Timestamp): 102 | return (self.time, self.inc) <= (other.time, other.inc) 103 | return NotImplemented 104 | 105 | def __gt__(self, other: Any) -> bool: 106 | if isinstance(other, Timestamp): 107 | return (self.time, self.inc) > (other.time, other.inc) 108 | return NotImplemented 109 | 110 | def __ge__(self, other: Any) -> bool: 111 | if isinstance(other, Timestamp): 112 | return (self.time, self.inc) >= (other.time, other.inc) 113 | return NotImplemented 114 | 115 | def __repr__(self): 116 | return "Timestamp(%s, %s)" % (self.__time, self.__inc) 117 | 118 | def as_datetime(self) -> datetime.datetime: 119 | """Return a :class:`~datetime.datetime` instance corresponding 120 | to the time portion of this :class:`Timestamp`. 121 | 122 | The returned datetime's timezone is UTC. 123 | """ 124 | return datetime.datetime.fromtimestamp(self.__time, utc) 125 | -------------------------------------------------------------------------------- /xtquant/xtbson/bson37/tz_util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2015 MongoDB, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Timezone related utilities for BSON.""" 16 | 17 | from datetime import datetime, timedelta, tzinfo 18 | from typing import Optional, Tuple, Union 19 | 20 | ZERO: timedelta = timedelta(0) 21 | 22 | 23 | class FixedOffset(tzinfo): 24 | """Fixed offset timezone, in minutes east from UTC. 25 | 26 | Implementation based from the Python `standard library documentation 27 | `_. 28 | Defining __getinitargs__ enables pickling / copying. 29 | """ 30 | 31 | def __init__(self, offset: Union[float, timedelta], name: str) -> None: 32 | if isinstance(offset, timedelta): 33 | self.__offset = offset 34 | else: 35 | self.__offset = timedelta(minutes=offset) 36 | self.__name = name 37 | 38 | def __getinitargs__(self) -> Tuple[timedelta, str]: 39 | return self.__offset, self.__name 40 | 41 | def utcoffset(self, dt: Optional[datetime]) -> timedelta: 42 | return self.__offset 43 | 44 | def tzname(self, dt: Optional[datetime]) -> str: 45 | return self.__name 46 | 47 | def dst(self, dt: Optional[datetime]) -> timedelta: 48 | return ZERO 49 | 50 | 51 | utc: FixedOffset = FixedOffset(0, "UTC") 52 | """Fixed offset timezone representing UTC.""" 53 | -------------------------------------------------------------------------------- /xtquant/xtconstant.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | 3 | 4 | """ 5 | 常量定义模块 6 | """ 7 | 8 | 9 | """ 10 | 账号类型 11 | """ 12 | # 期货 13 | FUTURE_ACCOUNT = 1 14 | # 股票 15 | SECURITY_ACCOUNT = 2 16 | # 信用 17 | CREDIT_ACCOUNT = 3 18 | # 期货期权 19 | FUTURE_OPTION_ACCOUNT = 5 20 | # 股票期权 21 | STOCK_OPTION_ACCOUNT = 6 22 | # 沪港通 23 | HUGANGTONG_ACCOUNT = 7 24 | # 深港通 25 | SHENGANGTONG_ACCOUNT = 11 26 | 27 | """ 28 | 委托类型 29 | """ 30 | #/ *期货六键风格 * / 31 | FUTURE_OPEN_LONG = 0 # 开多 32 | FUTURE_CLOSE_LONG_HISTORY = 1 # 平昨多 33 | FUTURE_CLOSE_LONG_TODAY = 2 # 平今多 34 | FUTURE_OPEN_SHORT = 3 # 开空 35 | FUTURE_CLOSE_SHORT_HISTORY = 4 # 平昨空 36 | FUTURE_CLOSE_SHORT_TODAY = 5 # 平今空 37 | # / *期货四键风格 * / 38 | FUTURE_CLOSE_LONG_TODAY_FIRST = 6 # 平多,优先平今 39 | FUTURE_CLOSE_LONG_HISTORY_FIRST = 7 # 平多,优先平昨 40 | FUTURE_CLOSE_SHORT_TODAY_FIRST = 8 # 平空,优先平今 41 | FUTURE_CLOSE_SHORT_HISTORY_FIRST = 9 # 平空,优先平昨 42 | 43 | # / *期货两键风格 * / 44 | FUTURE_CLOSE_LONG_TODAY_HISTORY_THEN_OPEN_SHORT = 10 # 卖出,如有多仓,优先平仓,优先平今,如有余量,再开空 45 | FUTURE_CLOSE_LONG_HISTORY_TODAY_THEN_OPEN_SHORT = 11 # 卖出,如有多仓,优先平仓,优先平昨,如有余量,再开空 46 | FUTURE_CLOSE_SHORT_TODAY_HISTORY_THEN_OPEN_LONG = 12 # 买入,如有空仓,优先平仓,优先平今,如有余量,再开多 47 | FUTURE_CLOSE_SHORT_HISTORY_TODAY_THEN_OPEN_LONG = 13 # 买入,如有空仓,优先平仓,优先平昨,如有余量,再开多 48 | FUTURE_OPEN = 14 # 买入,不优先平仓 49 | FUTURE_CLOSE = 15 # 卖出,不优先平仓 50 | 51 | # / *期货 - 跨商品套利 * / 52 | FUTURE_ARBITRAGE_OPEN = 16 # 开仓 53 | FUTURE_ARBITRAGE_CLOSE_HISTORY_FIRST = 17 # 平, 优先平昨 54 | FUTURE_ARBITRAGE_CLOSE_TODAY_FIRST = 18 # 平, 优先平今 55 | 56 | # / *期货展期 * / 57 | FUTURE_RENEW_LONG_CLOSE_HISTORY_FIRST = 19 # 看多, 优先平昨 58 | FUTURE_RENEW_LONG_CLOSE_TODAY_FIRST = 20 # 看多,优先平今 59 | FUTURE_RENEW_SHORT_CLOSE_HISTORY_FIRST = 21 # 看空,优先平昨 60 | FUTURE_RENEW_SHORT_CLOSE_TODAY_FIRST = 22 # 看空,优先平今 61 | 62 | # / *股票期权 * / 63 | STOCK_OPTION_BUY_OPEN = 48 # 买入开仓,以下用于个股期权交易业务 64 | STOCK_OPTION_SELL_CLOSE = 49 # 卖出平仓 65 | STOCK_OPTION_SELL_OPEN = 50 # 卖出开仓 66 | STOCK_OPTION_BUY_CLOSE = 51 # 买入平仓 67 | STOCK_OPTION_COVERED_OPEN = 52 # 备兑开仓 68 | STOCK_OPTION_COVERED_CLOSE = 53 # 备兑平仓 69 | STOCK_OPTION_CALL_EXERCISE = 54 # 认购行权 70 | STOCK_OPTION_PUT_EXERCISE = 55 # 认沽行权 71 | STOCK_OPTION_SECU_LOCK = 56 # 证券锁定 72 | STOCK_OPTION_SECU_UNLOCK = 57 # 证券解锁 73 | 74 | # /*期货期权*/ 75 | OPTION_FUTURE_OPTION_EXERCISE = 100 # 期货期权行权 76 | 77 | STOCK_BUY = 23 78 | STOCK_SELL = 24 79 | CREDIT_BUY = 23 #担保品买入 80 | CREDIT_SELL = 24 #担保品卖出 81 | CREDIT_FIN_BUY = 27 #融资买入 82 | CREDIT_SLO_SELL = 28 #融券卖出 83 | CREDIT_BUY_SECU_REPAY = 29 #买券还券 84 | CREDIT_DIRECT_SECU_REPAY = 30 #直接还券 85 | CREDIT_SELL_SECU_REPAY = 31 #卖券还款 86 | CREDIT_DIRECT_CASH_REPAY = 32 #直接还款 87 | CREDIT_FIN_BUY_SPECIAL = 40 #专项融资买入 88 | CREDIT_SLO_SELL_SPECIAL = 41 #专项融券卖出 89 | CREDIT_BUY_SECU_REPAY_SPECIAL = 42 #专项买券还券 90 | CREDIT_DIRECT_SECU_REPAY_SPECIAL = 43 #专项直接还券 91 | CREDIT_SELL_SECU_REPAY_SPECIAL = 44 #专项卖券还款 92 | CREDIT_DIRECT_CASH_REPAY_SPECIAL = 45 #专项直接还款 93 | 94 | ORDER_TYPE_SET = { 95 | STOCK_BUY 96 | , STOCK_SELL 97 | , CREDIT_BUY 98 | , CREDIT_SELL 99 | , CREDIT_FIN_BUY 100 | , CREDIT_SLO_SELL 101 | , CREDIT_BUY_SECU_REPAY 102 | , CREDIT_DIRECT_SECU_REPAY 103 | , CREDIT_SELL_SECU_REPAY 104 | , CREDIT_DIRECT_CASH_REPAY 105 | , CREDIT_FIN_BUY_SPECIAL 106 | , CREDIT_SLO_SELL_SPECIAL 107 | , CREDIT_BUY_SECU_REPAY_SPECIAL 108 | , CREDIT_DIRECT_SECU_REPAY_SPECIAL 109 | , CREDIT_SELL_SECU_REPAY_SPECIAL 110 | , CREDIT_DIRECT_CASH_REPAY_SPECIAL 111 | } 112 | 113 | """ 114 | 报价类型 115 | """ 116 | # 最新价 117 | LATEST_PRICE = 5 118 | # 指定价/限价 119 | FIX_PRICE = 11 120 | # 最优五档即时成交剩余撤销[上交所][股票] 121 | MARKET_SH_CONVERT_5_CANCEL = 42 122 | # 最优五档即时成交剩转限价[上交所][股票] 123 | MARKET_SH_CONVERT_5_LIMIT = 43 124 | # 对手方最优价格委托[上交所[股票]][深交所[股票][期权]] 125 | MARKET_PEER_PRICE_FIRST = 44 126 | # 本方最优价格委托[上交所[股票]][深交所[股票][期权]] 127 | MARKET_MINE_PRICE_FIRST = 45 128 | # 即时成交剩余撤销委托[深交所][股票][期权] 129 | MARKET_SZ_INSTBUSI_RESTCANCEL = 46 130 | # 最优五档即时成交剩余撤销[深交所][股票][期权] 131 | MARKET_SZ_CONVERT_5_CANCEL = 47 132 | # 全额成交或撤销委托[深交所][股票][期权] 133 | MARKET_SZ_FULL_OR_CANCEL = 48 134 | 135 | 136 | """ 137 | 市场类型 138 | """ 139 | # 上海市场 140 | SH_MARKET = 0 141 | # 深圳市场 142 | SZ_MARKET = 1 143 | 144 | 145 | """ 146 | 委托状态 147 | """ 148 | # 未报 149 | ORDER_UNREPORTED = 48 150 | # 待报 151 | ORDER_WAIT_REPORTING = 49 152 | # 已报 153 | ORDER_REPORTED = 50 154 | # 已报待撤 155 | ORDER_REPORTED_CANCEL = 51 156 | # 部成待撤 157 | ORDER_PARTSUCC_CANCEL = 52 158 | # 部撤 159 | ORDER_PART_CANCEL = 53 160 | # 已撤 161 | ORDER_CANCELED = 54 162 | # 部成 163 | ORDER_PART_SUCC = 55 164 | # 已成 165 | ORDER_SUCCEEDED = 56 166 | # 废单 167 | ORDER_JUNK = 57 168 | # 未知 169 | ORDER_UNKNOWN = 255 170 | 171 | 172 | """ 173 | 账号状态 174 | """ 175 | #无效 176 | ACCOUNT_STATUS_INVALID = -1 177 | #正常 178 | ACCOUNT_STATUS_OK = 0 179 | #连接中 180 | ACCOUNT_STATUS_WAITING_LOGIN = 1 181 | #登陆中 182 | ACCOUNT_STATUSING = 2 183 | #失败 184 | ACCOUNT_STATUS_FAIL = 3 185 | #初始化中 186 | ACCOUNT_STATUS_INITING = 4 187 | #数据刷新校正中 188 | ACCOUNT_STATUS_CORRECTING = 5 189 | #收盘后 190 | ACCOUNT_STATUS_CLOSED = 6 191 | #穿透副链接断开 192 | ACCOUNT_STATUS_ASSIS_FAIL = 7 193 | #系统停用(总线使用-密码错误超限) 194 | ACCOUNT_STATUS_DISABLEBYSYS = 8 195 | #用户停用(总线使用) 196 | ACCOUNT_STATUS_DISABLEBYUSER = 9 -------------------------------------------------------------------------------- /xtquant/xtdata.ini: -------------------------------------------------------------------------------- 1 | [app] 2 | appName=IPythonApiClient 3 | netThreadNum=8 4 | dispatcherThreadNum=8 5 | logPath=xtdata.log4cxx 6 | logWatch=0 7 | reportSeconds=20 8 | appendDate=1 9 | 10 | [client_xtdata] 11 | tagTemplate=xtdata 12 | address=127.0.0.1:58610 13 | timeoutSecond=0 14 | keepAliveCheckSecond=0 15 | reconnectSecond=3 16 | requestTimeoutSecond=150 17 | watchlog=1 18 | -------------------------------------------------------------------------------- /xtquant/xtdata.log4cxx: -------------------------------------------------------------------------------- 1 | log4j.logger.TTConsole=ERROR,ca 2 | log4j.logger.TTStdFile=ERROR,ca 3 | log4j.logger.TTDbgFile=ERROR,ca 4 | 5 | # 文件输出1 6 | #log4j.appender.fa1=org.apache.log4j.DailyRollingFileAppender 7 | #log4j.appender.fa1.datePattern='.'yyyy-MM-dd 8 | #log4j.appender.fa1.MaxFileSize=500MB 9 | #log4j.appender.fa1.MaxBackupIndex=10 10 | #log4j.appender.fa1.File=log/xtquant.log 11 | #log4j.appender.fa1.Append=true 12 | #log4j.appender.fa1.layout=org.apache.log4j.PatternLayout 13 | #log4j.appender.fa1.layout.ConversionPattern=%d [%p] [%t] %m%n 14 | 15 | # 控制台输出 16 | log4j.appender.ca=org.apache.log4j.ConsoleAppender 17 | log4j.appender.ca.layout=org.apache.log4j.PatternLayout 18 | log4j.appender.ca.layout.ConversionPattern=%d [%p] [%t] %m%n 19 | -------------------------------------------------------------------------------- /xtquant/xtdata_config.py: -------------------------------------------------------------------------------- 1 | client_guid = "" -------------------------------------------------------------------------------- /xtquant/xtextend.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | from ctypes import * 5 | 6 | class FileLock: 7 | def __init__(this, path, auto_lock = False): 8 | this.path = path 9 | this.fhandle = None 10 | if auto_lock: 11 | this.lock() 12 | return 13 | 14 | def is_lock(this): 15 | if os.path.exists(this.path): 16 | try: 17 | os.remove(this.path) 18 | return False 19 | except Exception as e: 20 | return True 21 | return False 22 | 23 | def lock(this): 24 | if this.fhandle: 25 | raise this.fhandle 26 | try: 27 | this.fhandle = open(this.path, 'w') 28 | except Exception as e: 29 | return False 30 | return True 31 | 32 | def unlock(this): 33 | if not this.fhandle: 34 | raise this.fhandle 35 | this.fhandle.close() 36 | this.fhandle = None 37 | return True 38 | 39 | def clean(this): 40 | if not os.path.exists(this.path): 41 | return True 42 | try: 43 | if os.path.isfile(this.path): 44 | os.remove(this.path) 45 | return True 46 | except Exception as e: 47 | pass 48 | return False 49 | 50 | class Extender: 51 | value_type = c_float 52 | rank_type = c_short 53 | 54 | def __init__(self, base_dir): 55 | self.base_dir = os.path.join(base_dir, 'EP') 56 | 57 | def read_config(self): 58 | data = None 59 | with open(os.path.join(self.file, 'config'), 'r', encoding='utf-8') as f: 60 | data = json.loads(f.read()) 61 | 62 | if data: 63 | self.stocklist = [] 64 | for i in range(1, len(data['stocklist']), 2): 65 | for stock in data['stocklist'][i]: 66 | self.stocklist.append("%s.%s" % (stock, data['stocklist'][i - 1])) 67 | 68 | self.timedatelist = data['tradedatelist'] 69 | 70 | def read_data(self, data, time_indexs, stock_length): 71 | res = {} 72 | num = (sizeof(self.value_type) + sizeof(self.rank_type)) * stock_length 73 | for time_index in time_indexs: 74 | index = num * time_index 75 | value_data = data[index: index + sizeof(self.value_type) * stock_length] 76 | values = cast(value_data, POINTER(c_float)) 77 | rank_data = data[index + sizeof(self.value_type) * stock_length: index + num] 78 | ranks = cast(rank_data, POINTER(c_short)) 79 | res[self.timedatelist[time_index]] = [(round(values[i], 3), ranks[i]) for i in range(stock_length)] 80 | 81 | return res 82 | 83 | def format_time(self, times): 84 | if type(times) == str: 85 | return int(time.mktime(time.strptime(times, '%Y%m%d'))) * 1000 86 | elif type(times) == int: 87 | if times < 0: 88 | return self.timedatelist[times] 89 | elif times < ((1 << 31) - 1): 90 | return times * 1000 91 | else: 92 | return times 93 | 94 | def show_extend_data(self, file, times): 95 | self.file = os.path.join(self.base_dir, file + '_Xdat') 96 | if not os.path.isdir(self.file): 97 | return "No such file" 98 | 99 | fs = FileLock(os.path.join(self.file, 'filelock'), False) 100 | 101 | while fs.is_lock(): 102 | print('文件被占用') 103 | time.sleep(1) 104 | fs.lock() 105 | 106 | self.read_config() 107 | 108 | time_list = [] 109 | 110 | if not times: 111 | time_list = self.timedatelist 112 | elif type(times) == list: 113 | time_list.extend([self.format_time(i) for i in times]) 114 | else: 115 | time_list.append(self.format_time(times)) 116 | 117 | 118 | time_index = [self.timedatelist.index(time) for time in time_list if self.timedatelist.count(time) != 0] 119 | 120 | stock_length = len(self.stocklist) 121 | data = None 122 | with open(os.path.join(self.file, 'data'), 'rb') as f: 123 | data = f.read() 124 | fs.unlock() 125 | res = self.read_data(data, time_index, stock_length) 126 | return self.stocklist, res 127 | 128 | from . import xtdata as xd 129 | 130 | def show_extend_data(file, times): 131 | exd = Extender(os.path.join(xd.init_data_dir(), '..', 'datadir')) 132 | 133 | return exd.show_extend_data(file, times) 134 | -------------------------------------------------------------------------------- /xtquant/xtpythonclient.cp310-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtpythonclient.cp310-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtpythonclient.cp311-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtpythonclient.cp311-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtpythonclient.cp36-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtpythonclient.cp36-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtpythonclient.cp37-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtpythonclient.cp37-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtpythonclient.cp38-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtpythonclient.cp38-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xtpythonclient.cp39-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FinHackCN/finhack-qmt/a63f2b3a75f994e0d5ea9ffa71206648f52148d7/xtquant/xtpythonclient.cp39-win_amd64.pyd -------------------------------------------------------------------------------- /xtquant/xttools.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | def init_pyside2_path(): 4 | try: 5 | import os, PySide2 6 | dirname = os.path.dirname(PySide2.__file__) 7 | plugin_path = os.path.join(dirname, 'plugins', 'platforms') 8 | os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path 9 | return True, None 10 | except Exception as e: 11 | return False, e 12 | -------------------------------------------------------------------------------- /xtquant/xtview.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import os, sys 4 | import datetime as dt 5 | import traceback 6 | import json 7 | 8 | from . import xtbson as bson 9 | 10 | from .IPythonApiClient import IPythonApiClient as RPCClient 11 | 12 | def try_except(func): 13 | def wrapper(*args, **kwargs): 14 | try: 15 | return func(*args, **kwargs) 16 | except Exception: 17 | exc_type, exc_instance, exc_traceback = sys.exc_info() 18 | formatted_traceback = ''.join(traceback.format_tb(exc_traceback)) 19 | message = '\n{0} raise {1}:{2}'.format( 20 | formatted_traceback, 21 | exc_type.__name__, 22 | exc_instance 23 | ) 24 | # raise exc_type(message) 25 | print(message) 26 | return None 27 | 28 | return wrapper 29 | 30 | 31 | CLIENT = None 32 | 33 | from os.path import abspath, dirname 34 | __curdir = dirname(abspath(__file__)) 35 | 36 | __rpc_config = __curdir + '/xtdata.ini' 37 | __xtdata_config = __curdir + '/xtdata.ini' 38 | 39 | from .IPythonApiClient import rpc_init 40 | __rpc_init_status = rpc_init(__rpc_config) 41 | if __rpc_init_status < 0: 42 | print(f'rpc初始化失败,配置文件:{__rpc_config}') 43 | 44 | def get_client(): 45 | global CLIENT 46 | if not CLIENT: 47 | CLIENT = RPCClient('client_xtdata', __xtdata_config) 48 | 49 | try: 50 | CLIENT.load_config(__xtdata_config) 51 | config = json.load(open(os.path.join('..', 'config', 'xtquantconfig.json'))) 52 | port = config['port'] 53 | CLIENT.set_remote_addr('localhost', port) 54 | CLIENT.reset() 55 | CLIENT.connect_ex() 56 | except Exception as e: 57 | pass 58 | 59 | if not CLIENT.is_connected(): 60 | CLIENT.load_config(__xtdata_config) 61 | CLIENT.reset() 62 | 63 | if not CLIENT.is_connected(): 64 | succ, errmsg = CLIENT.connect_ex() 65 | if not succ: 66 | raise Exception("无法连接服务!") 67 | return CLIENT 68 | 69 | 70 | def reconnect(ip = 'localhost', port = 58610): 71 | global CLIENT 72 | CLIENT = None 73 | if not CLIENT: 74 | CLIENT = RPCClient('client_xtdata', __xtdata_config) 75 | 76 | CLIENT.load_config(__xtdata_config) 77 | CLIENT.set_remote_addr(ip, port) 78 | CLIENT.reset() 79 | CLIENT.connect() 80 | 81 | if not CLIENT.is_connected(): 82 | if not CLIENT.connect(): 83 | raise Exception("无法连接服务!") 84 | return 85 | 86 | def create_view(viewID, view_type, title, group_id): 87 | client = get_client() 88 | return client.createView(viewID, view_type, title, group_id) 89 | 90 | #def reset_view(viewID): 91 | # return 92 | 93 | def close_view(viewID): 94 | client = get_client() 95 | return client.closeView(viewID) 96 | 97 | #def set_view_index(viewID, datas): 98 | # ''' 99 | # 设置模型指标属性 100 | # index: { "output1": { "datatype": se::OutputDataType } } 101 | # ''' 102 | # client = get_client() 103 | # return client.setViewIndex(viewID, datas) 104 | 105 | def push_view_data(viewID, datas): 106 | ''' 107 | 推送模型结果数据 108 | datas: { "timetags: [t1, t2, ...], "outputs": { "output1": [value1, value2, ...], ... }, "overwrite": "full/increase" } 109 | ''' 110 | client = get_client() 111 | bresult = client.pushViewData(viewID, 'index', bson.BSON.encode(datas)) 112 | return bson.BSON.decode(bresult) 113 | --------------------------------------------------------------------------------