httptrading 1.0.3__tar.gz → 1.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {httptrading-1.0.3 → httptrading-1.0.6}/PKG-INFO +8 -8
- {httptrading-1.0.3 → httptrading-1.0.6}/README.md +7 -7
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/broker/base.py +1 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/broker/futu_sec.py +19 -16
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/broker/interactive_brokers.py +6 -7
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/broker/longbridge.py +19 -16
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/broker/tiger.py +12 -13
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/http_server.py +32 -24
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/model.py +4 -4
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading.egg-info/PKG-INFO +8 -8
- {httptrading-1.0.3 → httptrading-1.0.6}/pyproject.toml +1 -1
- {httptrading-1.0.3 → httptrading-1.0.6}/LICENSE +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/__init__.py +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/broker/__init__.py +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/tool/__init__.py +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/tool/leaky_bucket.py +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/tool/locate.py +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading/tool/time.py +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading.egg-info/SOURCES.txt +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading.egg-info/dependency_links.txt +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading.egg-info/requires.txt +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/httptrading.egg-info/top_level.txt +0 -0
- {httptrading-1.0.3 → httptrading-1.0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: httptrading
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.6
|
4
4
|
Summary: 统一交易通道的接口服务
|
5
5
|
Author-email: songwei <github@songwei.name>
|
6
6
|
License: MIT
|
@@ -187,10 +187,10 @@ GET /httptrading/api/{instanceId}/market/quote
|
|
187
187
|
| 参数 | 说明 | 举例 |
|
188
188
|
|-----------|---------|--------------------|
|
189
189
|
| tradeType | 说明标的的品种 | Securities: 证券 |
|
190
|
-
|
|
190
|
+
| symbol | 代码 | QQQ, 00700, 000001 |
|
191
191
|
| region | 以国家区分代码 | US, HK, CN |
|
192
192
|
|
193
|
-
举例 ?tradeType=Securities®ion=CN&
|
193
|
+
举例 ?tradeType=Securities®ion=CN&symbol=000001 参数的结果:
|
194
194
|
```json lines
|
195
195
|
{
|
196
196
|
"type": "apiResponse",
|
@@ -205,7 +205,7 @@ GET /httptrading/api/{instanceId}/market/quote
|
|
205
205
|
"type": "contract",
|
206
206
|
"tradeType": "Securities",
|
207
207
|
"region": "CN",
|
208
|
-
"
|
208
|
+
"symbol": "000001"
|
209
209
|
},
|
210
210
|
"currency": "CNY", // 币种
|
211
211
|
"isTradable": true, // 此时是否可交易, 比如受到停牌熔断影响
|
@@ -313,7 +313,7 @@ GET /httptrading/api/{instanceId}/position/state
|
|
313
313
|
"type": "contract",
|
314
314
|
"tradeType": "Securities",
|
315
315
|
"region": "US",
|
316
|
-
"
|
316
|
+
"symbol": "QQQ"
|
317
317
|
},
|
318
318
|
"unit": "Share",
|
319
319
|
"currency": "USD",
|
@@ -333,7 +333,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
333
333
|
| 参数 | 说明 | 举例 |
|
334
334
|
|-------------|----------|--------------------------------------------------|
|
335
335
|
| tradeType | 说明标的的品种 | Securities: 证券 |
|
336
|
-
|
|
336
|
+
| symbol | 代码 | QQQ, 00700, 000001 |
|
337
337
|
| region | 以国家区分代码 | US, HK, CN |
|
338
338
|
| price | 限价 | 市价单不填此项 |
|
339
339
|
| qty | 订单数量, 整数 | |
|
@@ -346,7 +346,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
346
346
|
```json lines
|
347
347
|
{
|
348
348
|
"tradeType": "Securities",
|
349
|
-
"
|
349
|
+
"symbol": "AAPL",
|
350
350
|
"region": "US",
|
351
351
|
"price": 200.00,
|
352
352
|
"qty": 12,
|
@@ -369,7 +369,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
369
369
|
"orderId": "69788888", // 订单号
|
370
370
|
"args": { // 传递的参数
|
371
371
|
"tradeType": "Securities",
|
372
|
-
"
|
372
|
+
"symbol": "AAPL",
|
373
373
|
"region": "US",
|
374
374
|
"price": 200,
|
375
375
|
"qty": 12,
|
@@ -172,10 +172,10 @@ GET /httptrading/api/{instanceId}/market/quote
|
|
172
172
|
| 参数 | 说明 | 举例 |
|
173
173
|
|-----------|---------|--------------------|
|
174
174
|
| tradeType | 说明标的的品种 | Securities: 证券 |
|
175
|
-
|
|
175
|
+
| symbol | 代码 | QQQ, 00700, 000001 |
|
176
176
|
| region | 以国家区分代码 | US, HK, CN |
|
177
177
|
|
178
|
-
举例 ?tradeType=Securities®ion=CN&
|
178
|
+
举例 ?tradeType=Securities®ion=CN&symbol=000001 参数的结果:
|
179
179
|
```json lines
|
180
180
|
{
|
181
181
|
"type": "apiResponse",
|
@@ -190,7 +190,7 @@ GET /httptrading/api/{instanceId}/market/quote
|
|
190
190
|
"type": "contract",
|
191
191
|
"tradeType": "Securities",
|
192
192
|
"region": "CN",
|
193
|
-
"
|
193
|
+
"symbol": "000001"
|
194
194
|
},
|
195
195
|
"currency": "CNY", // 币种
|
196
196
|
"isTradable": true, // 此时是否可交易, 比如受到停牌熔断影响
|
@@ -298,7 +298,7 @@ GET /httptrading/api/{instanceId}/position/state
|
|
298
298
|
"type": "contract",
|
299
299
|
"tradeType": "Securities",
|
300
300
|
"region": "US",
|
301
|
-
"
|
301
|
+
"symbol": "QQQ"
|
302
302
|
},
|
303
303
|
"unit": "Share",
|
304
304
|
"currency": "USD",
|
@@ -318,7 +318,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
318
318
|
| 参数 | 说明 | 举例 |
|
319
319
|
|-------------|----------|--------------------------------------------------|
|
320
320
|
| tradeType | 说明标的的品种 | Securities: 证券 |
|
321
|
-
|
|
321
|
+
| symbol | 代码 | QQQ, 00700, 000001 |
|
322
322
|
| region | 以国家区分代码 | US, HK, CN |
|
323
323
|
| price | 限价 | 市价单不填此项 |
|
324
324
|
| qty | 订单数量, 整数 | |
|
@@ -331,7 +331,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
331
331
|
```json lines
|
332
332
|
{
|
333
333
|
"tradeType": "Securities",
|
334
|
-
"
|
334
|
+
"symbol": "AAPL",
|
335
335
|
"region": "US",
|
336
336
|
"price": 200.00,
|
337
337
|
"qty": 12,
|
@@ -354,7 +354,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
354
354
|
"orderId": "69788888", // 订单号
|
355
355
|
"args": { // 传递的参数
|
356
356
|
"tradeType": "Securities",
|
357
|
-
"
|
357
|
+
"symbol": "AAPL",
|
358
358
|
"region": "US",
|
359
359
|
"price": 200,
|
360
360
|
"qty": 12,
|
@@ -106,24 +106,24 @@ class Futu(SecuritiesBroker):
|
|
106
106
|
@classmethod
|
107
107
|
def code_to_contract(cls, code) -> Contract | None:
|
108
108
|
region = ''
|
109
|
-
|
109
|
+
symbol = ''
|
110
110
|
if m := re.match(r'^US\.(\S+)$', code):
|
111
111
|
region = 'US'
|
112
|
-
|
112
|
+
symbol = m.groups()[0]
|
113
113
|
if m := re.match(r'^HK\.(\d{5})$', code):
|
114
114
|
region = 'HK'
|
115
|
-
|
115
|
+
symbol = m.groups()[0]
|
116
116
|
if m := re.match(r'^SH\.(\d{6})$', code):
|
117
117
|
region = 'CN'
|
118
|
-
|
118
|
+
symbol = m.groups()[0]
|
119
119
|
if m := re.match(r'^SZ\.(\d{6})$', code):
|
120
120
|
region = 'CN'
|
121
|
-
|
122
|
-
if not region or not
|
121
|
+
symbol = m.groups()[0]
|
122
|
+
if not region or not symbol:
|
123
123
|
return None
|
124
124
|
return Contract(
|
125
125
|
trade_type=TradeType.Securities,
|
126
|
-
|
126
|
+
symbol=symbol,
|
127
127
|
region=region,
|
128
128
|
)
|
129
129
|
|
@@ -131,16 +131,16 @@ class Futu(SecuritiesBroker):
|
|
131
131
|
def contract_to_code(cls, contract: Contract) -> str | None:
|
132
132
|
if contract.trade_type != TradeType.Securities:
|
133
133
|
return None
|
134
|
-
region,
|
134
|
+
region, symbol = contract.region, contract.symbol
|
135
135
|
code = None
|
136
|
-
if region == 'CN' and re.match(r'^[56]\d{5}$',
|
137
|
-
code = f'SH.{
|
138
|
-
elif region == 'CN' and re.match(r'^[013]\d{5}$',
|
139
|
-
code = f'SZ.{
|
140
|
-
elif region == 'HK' and re.match(r'^\d{5}$',
|
141
|
-
code = f'HK.{
|
142
|
-
elif region == 'US' and re.match(r'^\w+$',
|
143
|
-
code = f'US.{
|
136
|
+
if region == 'CN' and re.match(r'^[56]\d{5}$', symbol):
|
137
|
+
code = f'SH.{symbol}'
|
138
|
+
elif region == 'CN' and re.match(r'^[013]\d{5}$', symbol):
|
139
|
+
code = f'SZ.{symbol}'
|
140
|
+
elif region == 'HK' and re.match(r'^\d{5}$', symbol):
|
141
|
+
code = f'HK.{symbol}'
|
142
|
+
elif region == 'US' and re.match(r'^\w+$', symbol):
|
143
|
+
code = f'US.{symbol}'
|
144
144
|
return code
|
145
145
|
|
146
146
|
@classmethod
|
@@ -320,6 +320,7 @@ class Futu(SecuritiesBroker):
|
|
320
320
|
direction: str,
|
321
321
|
qty: int,
|
322
322
|
price: float = None,
|
323
|
+
full_args: dict = None,
|
323
324
|
**kwargs
|
324
325
|
) -> str:
|
325
326
|
from futu import RET_OK, TrdSide, OrderType as FutuOrderType, TimeInForce as FutuTimeInForce, Session
|
@@ -398,6 +399,7 @@ class Futu(SecuritiesBroker):
|
|
398
399
|
direction: str,
|
399
400
|
qty: int,
|
400
401
|
price: float = None,
|
402
|
+
full_args: dict = None,
|
401
403
|
**kwargs
|
402
404
|
) -> str:
|
403
405
|
return await self.call_sync(lambda : self._place_order(
|
@@ -408,6 +410,7 @@ class Futu(SecuritiesBroker):
|
|
408
410
|
direction=direction,
|
409
411
|
qty=qty,
|
410
412
|
price=price,
|
413
|
+
full_args=full_args,
|
411
414
|
**kwargs
|
412
415
|
))
|
413
416
|
|
@@ -62,21 +62,17 @@ class InteractiveBrokers(SecuritiesBroker):
|
|
62
62
|
symbol = contract.symbol
|
63
63
|
trade_type = TradeType.Securities
|
64
64
|
region = ''
|
65
|
-
ticker = ''
|
66
65
|
if re.match(r'^[01356]\d{5}$', symbol):
|
67
66
|
region = 'CN'
|
68
|
-
ticker = symbol
|
69
67
|
if re.match(r'^\d{5}$', symbol):
|
70
68
|
region = 'HK'
|
71
|
-
ticker = symbol
|
72
69
|
if re.match(r'^\w{1,5}$', symbol):
|
73
70
|
region = 'US'
|
74
|
-
|
75
|
-
if not region or not ticker:
|
71
|
+
if not region:
|
76
72
|
return None
|
77
73
|
return Contract(
|
78
74
|
trade_type=trade_type,
|
79
|
-
|
75
|
+
symbol=symbol,
|
80
76
|
region=region,
|
81
77
|
)
|
82
78
|
|
@@ -86,7 +82,7 @@ class InteractiveBrokers(SecuritiesBroker):
|
|
86
82
|
if contract in self._ib_contracts:
|
87
83
|
return self._ib_contracts[contract]
|
88
84
|
currency = self.contract_to_currency(contract)
|
89
|
-
ib_contract = ib_insync.Stock(contract.
|
85
|
+
ib_contract = ib_insync.Stock(contract.symbol, 'SMART', currency=currency)
|
90
86
|
client = self._client
|
91
87
|
client.qualifyContracts(*[ib_contract, ])
|
92
88
|
self._ib_contracts[contract] = ib_contract
|
@@ -205,6 +201,7 @@ class InteractiveBrokers(SecuritiesBroker):
|
|
205
201
|
direction: str,
|
206
202
|
qty: int,
|
207
203
|
price: float = None,
|
204
|
+
full_args: dict = None,
|
208
205
|
**kwargs
|
209
206
|
) -> str:
|
210
207
|
import ib_insync
|
@@ -274,6 +271,7 @@ class InteractiveBrokers(SecuritiesBroker):
|
|
274
271
|
direction: str,
|
275
272
|
qty: int,
|
276
273
|
price: float = None,
|
274
|
+
full_args: dict = None,
|
277
275
|
**kwargs
|
278
276
|
) -> str:
|
279
277
|
return await self.call_async(self._place_order(
|
@@ -284,6 +282,7 @@ class InteractiveBrokers(SecuritiesBroker):
|
|
284
282
|
direction=direction,
|
285
283
|
qty=qty,
|
286
284
|
price=price,
|
285
|
+
full_args=full_args,
|
287
286
|
**kwargs
|
288
287
|
))
|
289
288
|
|
@@ -188,24 +188,24 @@ class LongBridge(SecuritiesBroker):
|
|
188
188
|
@classmethod
|
189
189
|
def symbol_to_contract(cls, symbol: str) -> Contract | None:
|
190
190
|
region = ''
|
191
|
-
|
191
|
+
contract_symbol = ''
|
192
192
|
if m := re.match(r'^(\S+)\.US$', symbol):
|
193
193
|
region = 'US'
|
194
|
-
|
194
|
+
contract_symbol = m.groups()[0]
|
195
195
|
if m := re.match(r'^(\d{5})\.HK$', symbol):
|
196
196
|
region = 'HK'
|
197
|
-
|
197
|
+
contract_symbol = m.groups()[0]
|
198
198
|
if m := re.match(r'^(\d{6})\.SH$', symbol):
|
199
199
|
region = 'CN'
|
200
|
-
|
200
|
+
contract_symbol = m.groups()[0]
|
201
201
|
if m := re.match(r'^(\d{6})\.SZ$', symbol):
|
202
202
|
region = 'CN'
|
203
|
-
|
204
|
-
if not region or not
|
203
|
+
contract_symbol = m.groups()[0]
|
204
|
+
if not region or not contract_symbol:
|
205
205
|
return None
|
206
206
|
return Contract(
|
207
207
|
trade_type=TradeType.Securities,
|
208
|
-
|
208
|
+
symbol=contract_symbol,
|
209
209
|
region=region,
|
210
210
|
)
|
211
211
|
|
@@ -213,16 +213,16 @@ class LongBridge(SecuritiesBroker):
|
|
213
213
|
def contract_to_symbol(cls, contract: Contract) -> str:
|
214
214
|
if contract.trade_type != TradeType.Securities:
|
215
215
|
raise Exception(f'不能支持的交易品种{contract}映射为交易代码')
|
216
|
-
region,
|
216
|
+
region, symbol = contract.region, contract.symbol
|
217
217
|
code = None
|
218
|
-
if region == 'CN' and re.match(r'^[56]\d{5}$',
|
219
|
-
code = f'{
|
220
|
-
elif region == 'CN' and re.match(r'^[013]\d{5}$',
|
221
|
-
code = f'{
|
222
|
-
elif region == 'HK' and re.match(r'^\d{5}$',
|
223
|
-
code = f'{
|
224
|
-
elif region == 'US' and re.match(r'^\w+$',
|
225
|
-
code = f'{
|
218
|
+
if region == 'CN' and re.match(r'^[56]\d{5}$', symbol):
|
219
|
+
code = f'{symbol}.SH'
|
220
|
+
elif region == 'CN' and re.match(r'^[013]\d{5}$', symbol):
|
221
|
+
code = f'{symbol}.SZ'
|
222
|
+
elif region == 'HK' and re.match(r'^\d{5}$', symbol):
|
223
|
+
code = f'{symbol}.HK'
|
224
|
+
elif region == 'US' and re.match(r'^\w+$', symbol):
|
225
|
+
code = f'{symbol}.US'
|
226
226
|
if not code:
|
227
227
|
raise Exception(f'不能映射{contract}为交易代码')
|
228
228
|
return code
|
@@ -310,6 +310,7 @@ class LongBridge(SecuritiesBroker):
|
|
310
310
|
direction: str,
|
311
311
|
qty: int,
|
312
312
|
price: float = None,
|
313
|
+
full_args: dict = None,
|
313
314
|
**kwargs
|
314
315
|
) -> str:
|
315
316
|
from longport.openapi import OrderType as LbOrderType, OrderSide, TimeInForceType, OutsideRTH
|
@@ -382,6 +383,7 @@ class LongBridge(SecuritiesBroker):
|
|
382
383
|
direction: str,
|
383
384
|
qty: int,
|
384
385
|
price: float = None,
|
386
|
+
full_args: dict = None,
|
385
387
|
**kwargs
|
386
388
|
) -> str:
|
387
389
|
return await self.call_sync(lambda : self._place_order(
|
@@ -392,6 +394,7 @@ class LongBridge(SecuritiesBroker):
|
|
392
394
|
direction=direction,
|
393
395
|
qty=qty,
|
394
396
|
price=price,
|
397
|
+
full_args=full_args,
|
395
398
|
**kwargs
|
396
399
|
))
|
397
400
|
|
@@ -120,21 +120,17 @@ class Tiger(SecuritiesBroker):
|
|
120
120
|
@classmethod
|
121
121
|
def symbol_to_contract(cls, symbol) -> Contract | None:
|
122
122
|
region = ''
|
123
|
-
ticker = ''
|
124
123
|
if re.match(r'^[01356]\d{5}$', symbol):
|
125
124
|
region = 'CN'
|
126
|
-
ticker = symbol
|
127
125
|
if re.match(r'^\d{5}$', symbol):
|
128
126
|
region = 'HK'
|
129
|
-
ticker = symbol
|
130
127
|
if re.match(r'^\w{1,5}$', symbol):
|
131
128
|
region = 'US'
|
132
|
-
|
133
|
-
if not region or not ticker:
|
129
|
+
if not region:
|
134
130
|
return None
|
135
131
|
return Contract(
|
136
132
|
trade_type=TradeType.Securities,
|
137
|
-
|
133
|
+
symbol=symbol,
|
138
134
|
region=region,
|
139
135
|
)
|
140
136
|
|
@@ -142,14 +138,14 @@ class Tiger(SecuritiesBroker):
|
|
142
138
|
def contract_to_symbol(cls, contract: Contract) -> str | None:
|
143
139
|
if contract.trade_type != TradeType.Securities:
|
144
140
|
return None
|
145
|
-
region,
|
141
|
+
region, contract_symbol = contract.region, contract.symbol
|
146
142
|
symbol = None
|
147
|
-
if region == 'CN' and re.match(r'^[01356]\d{5}$',
|
148
|
-
symbol =
|
149
|
-
elif region == 'HK' and re.match(r'^\d{5}$',
|
150
|
-
symbol =
|
151
|
-
elif region == 'US' and re.match(r'^\w{1,5}$',
|
152
|
-
symbol =
|
143
|
+
if region == 'CN' and re.match(r'^[01356]\d{5}$', contract_symbol):
|
144
|
+
symbol = contract_symbol
|
145
|
+
elif region == 'HK' and re.match(r'^\d{5}$', contract_symbol):
|
146
|
+
symbol = contract_symbol
|
147
|
+
elif region == 'US' and re.match(r'^\w{1,5}$', contract_symbol):
|
148
|
+
symbol = contract_symbol
|
153
149
|
return symbol
|
154
150
|
|
155
151
|
def _positions(self):
|
@@ -281,6 +277,7 @@ class Tiger(SecuritiesBroker):
|
|
281
277
|
direction: str,
|
282
278
|
qty: int,
|
283
279
|
price: float = None,
|
280
|
+
full_args: dict = None,
|
284
281
|
**kwargs
|
285
282
|
) -> str:
|
286
283
|
if contract.trade_type != TradeType.Securities:
|
@@ -360,6 +357,7 @@ class Tiger(SecuritiesBroker):
|
|
360
357
|
direction: str,
|
361
358
|
qty: int,
|
362
359
|
price: float = None,
|
360
|
+
full_args: dict = None,
|
363
361
|
**kwargs
|
364
362
|
) -> str:
|
365
363
|
return await self.call_sync(lambda: self._place_order(
|
@@ -370,6 +368,7 @@ class Tiger(SecuritiesBroker):
|
|
370
368
|
direction=direction,
|
371
369
|
qty=qty,
|
372
370
|
price=price,
|
371
|
+
full_args=full_args,
|
373
372
|
**kwargs
|
374
373
|
))
|
375
374
|
|
@@ -31,11 +31,11 @@ class HttpTradingView(web.View):
|
|
31
31
|
params: dict = await self.request.json() if from_json else self.request.query
|
32
32
|
trade_type = TradeType[params.get('tradeType', '--')]
|
33
33
|
region = params.get('region', '--')
|
34
|
-
|
34
|
+
symbol = params.get('symbol', '--')
|
35
35
|
contract = Contract(
|
36
36
|
trade_type=trade_type,
|
37
37
|
region=region,
|
38
|
-
|
38
|
+
symbol=symbol,
|
39
39
|
)
|
40
40
|
return contract
|
41
41
|
|
@@ -84,6 +84,7 @@ class PlaceOrderView(HttpTradingView):
|
|
84
84
|
qty=qty,
|
85
85
|
price=price,
|
86
86
|
json=body_d,
|
87
|
+
full_args=body_d,
|
87
88
|
)
|
88
89
|
return self.response_api(broker, {
|
89
90
|
'orderId': order_id,
|
@@ -162,28 +163,33 @@ class MarketStatusView(HttpTradingView):
|
|
162
163
|
})
|
163
164
|
|
164
165
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
if token not in broker.tokens:
|
166
|
+
def create_auth_middleware(token_header: str):
|
167
|
+
assert isinstance(token_header, str)
|
168
|
+
assert token_header
|
169
|
+
|
170
|
+
@web.middleware
|
171
|
+
async def _auth_middleware(request: web.Request, handler):
|
172
|
+
instance_id = request.match_info.get('instance_id', '')
|
173
|
+
token = request.headers.get(token_header, '')
|
174
|
+
if not instance_id:
|
175
|
+
raise web.HTTPNotFound
|
176
|
+
if not token:
|
177
|
+
raise web.HTTPNotFound
|
178
|
+
if len(token) < 16 or len(token) > 64:
|
179
179
|
raise web.HTTPNotFound
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
180
|
+
for broker in HttpTradingView.brokers():
|
181
|
+
if broker.instance_id != instance_id:
|
182
|
+
continue
|
183
|
+
if token not in broker.tokens:
|
184
|
+
raise web.HTTPNotFound
|
185
|
+
setattr(request, '__current_broker__', broker)
|
186
|
+
break
|
187
|
+
else:
|
188
|
+
raise web.HTTPNotFound
|
189
|
+
response: web.Response = await handler(request)
|
190
|
+
delattr(request, '__current_broker__')
|
191
|
+
return response
|
192
|
+
return _auth_middleware
|
187
193
|
|
188
194
|
|
189
195
|
@web.middleware
|
@@ -218,6 +224,7 @@ def run(
|
|
218
224
|
brokers: list[BaseBroker],
|
219
225
|
std_apis: Callable[[], list[web.RouteDef]] = None,
|
220
226
|
extend_apis: list[web.RouteDef] = None,
|
227
|
+
token_header: str = 'HT-TOKEN',
|
221
228
|
**kwargs
|
222
229
|
) -> None:
|
223
230
|
"""
|
@@ -226,11 +233,12 @@ def run(
|
|
226
233
|
@param brokers: 需要控制的交易通道对象列表
|
227
234
|
@param std_apis: 如果需要替换默认提供的接口, 这里提供工厂函数的回调
|
228
235
|
@param extend_apis: 如果需要增加自定义接口, 这里传入 RouteDef 列表
|
236
|
+
@param token_header: 定制 token 凭据的 header 键名
|
229
237
|
@param kwargs: 其他的参数将传给 aiohttp.web.run_app 函数
|
230
238
|
"""
|
231
239
|
app = web.Application(
|
232
240
|
middlewares=[
|
233
|
-
|
241
|
+
create_auth_middleware(token_header=token_header),
|
234
242
|
exception_middleware,
|
235
243
|
],
|
236
244
|
)
|
@@ -55,16 +55,16 @@ class Contract:
|
|
55
55
|
"""
|
56
56
|
Contract 定义了交易品种的精确描述.
|
57
57
|
根据交易种类, 区分为证券和加密货币;
|
58
|
-
根据
|
58
|
+
根据 symbol 设置交易标的的代码;
|
59
59
|
对于支持多个市场的交易通道, 例如证券, 需要额外提供 region 加以区分标的的所属市场.
|
60
60
|
"""
|
61
61
|
trade_type: TradeType
|
62
|
-
|
62
|
+
symbol: str
|
63
63
|
region: str
|
64
64
|
|
65
65
|
@property
|
66
66
|
def unique_pair(self):
|
67
|
-
return self.trade_type, self.
|
67
|
+
return self.trade_type, self.symbol, self.region,
|
68
68
|
|
69
69
|
def __hash__(self):
|
70
70
|
return self.unique_pair.__hash__()
|
@@ -183,7 +183,7 @@ class JsonDefault:
|
|
183
183
|
'type': 'contract',
|
184
184
|
'tradeType': obj.trade_type.name,
|
185
185
|
'region': obj.region,
|
186
|
-
'
|
186
|
+
'symbol': obj.symbol,
|
187
187
|
}
|
188
188
|
if isinstance(obj, Cash):
|
189
189
|
return {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: httptrading
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.6
|
4
4
|
Summary: 统一交易通道的接口服务
|
5
5
|
Author-email: songwei <github@songwei.name>
|
6
6
|
License: MIT
|
@@ -187,10 +187,10 @@ GET /httptrading/api/{instanceId}/market/quote
|
|
187
187
|
| 参数 | 说明 | 举例 |
|
188
188
|
|-----------|---------|--------------------|
|
189
189
|
| tradeType | 说明标的的品种 | Securities: 证券 |
|
190
|
-
|
|
190
|
+
| symbol | 代码 | QQQ, 00700, 000001 |
|
191
191
|
| region | 以国家区分代码 | US, HK, CN |
|
192
192
|
|
193
|
-
举例 ?tradeType=Securities®ion=CN&
|
193
|
+
举例 ?tradeType=Securities®ion=CN&symbol=000001 参数的结果:
|
194
194
|
```json lines
|
195
195
|
{
|
196
196
|
"type": "apiResponse",
|
@@ -205,7 +205,7 @@ GET /httptrading/api/{instanceId}/market/quote
|
|
205
205
|
"type": "contract",
|
206
206
|
"tradeType": "Securities",
|
207
207
|
"region": "CN",
|
208
|
-
"
|
208
|
+
"symbol": "000001"
|
209
209
|
},
|
210
210
|
"currency": "CNY", // 币种
|
211
211
|
"isTradable": true, // 此时是否可交易, 比如受到停牌熔断影响
|
@@ -313,7 +313,7 @@ GET /httptrading/api/{instanceId}/position/state
|
|
313
313
|
"type": "contract",
|
314
314
|
"tradeType": "Securities",
|
315
315
|
"region": "US",
|
316
|
-
"
|
316
|
+
"symbol": "QQQ"
|
317
317
|
},
|
318
318
|
"unit": "Share",
|
319
319
|
"currency": "USD",
|
@@ -333,7 +333,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
333
333
|
| 参数 | 说明 | 举例 |
|
334
334
|
|-------------|----------|--------------------------------------------------|
|
335
335
|
| tradeType | 说明标的的品种 | Securities: 证券 |
|
336
|
-
|
|
336
|
+
| symbol | 代码 | QQQ, 00700, 000001 |
|
337
337
|
| region | 以国家区分代码 | US, HK, CN |
|
338
338
|
| price | 限价 | 市价单不填此项 |
|
339
339
|
| qty | 订单数量, 整数 | |
|
@@ -346,7 +346,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
346
346
|
```json lines
|
347
347
|
{
|
348
348
|
"tradeType": "Securities",
|
349
|
-
"
|
349
|
+
"symbol": "AAPL",
|
350
350
|
"region": "US",
|
351
351
|
"price": 200.00,
|
352
352
|
"qty": 12,
|
@@ -369,7 +369,7 @@ POST /httptrading/api/{instanceId}/order/place
|
|
369
369
|
"orderId": "69788888", // 订单号
|
370
370
|
"args": { // 传递的参数
|
371
371
|
"tradeType": "Securities",
|
372
|
-
"
|
372
|
+
"symbol": "AAPL",
|
373
373
|
"region": "US",
|
374
374
|
"price": 200,
|
375
375
|
"qty": 12,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|