Skip to content

Commit be977b9

Browse files
author
方佳
committed
Merge branch 'main_merge_260718' into 'main'
[OpenAPI&FIX-260718 version]: 【Trading API】支持期权合约档案接口 See merge request webull/webull-openapi-python-sdk!37
2 parents 10889b5 + cbea25f commit be977b9

14 files changed

Lines changed: 152 additions & 38 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Subscription to real-time information: Subscribe to order status changes, market
3232
| Webull MX | https://www.webull.com.mx/open-api-management |
3333
| Webull ZA | https://www.webull.co.za/open-api-management |
3434
| Webull EU | https://www.webull.eu/open-api-management |
35-
- Requires Python 3.8 through 3.13.
35+
- Requires Python 3.8 through 3.14.
3636

3737
## Interface Protocol
3838

samples/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.0.13'
1+
__version__ = '2.0.14'

samples/data/data_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@
265265
if res.status_code == 200:
266266
print('get_forecast_eps:', res.json())
267267

268+
# Option Instrument - Query option contracts by underlying symbol and filters
269+
res = data_client.instrument.get_option_contracts(Category.US_OPTION.name,"AAPL")
270+
if res.status_code == 200:
271+
print('get_option_contracts:', res.json())
272+
268273
# Screener - Market Sectors
269274
res = data_client.screener.get_market_sectors(Category.US_STOCK.name)
270275
if res.status_code == 200:

setup.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,28 @@
2424

2525
# cryptography
2626
"cryptography>=3.4,<42; python_version<'3.12'",
27-
"cryptography>=41.0,<43; python_version>='3.12'",
27+
"cryptography>=41.0,<43; python_version>='3.12' and python_version<'3.14'",
28+
"cryptography>=43.0,<55; python_version>='3.14'",
2829

2930
# protobuf
3031
"protobuf>=4.21.12,<5; python_version<'3.12'",
3132
"protobuf>=4.25.0,<6; python_version>='3.12'",
3233

3334
# grpc
3435
"grpcio>=1.51.1,<1.60; python_version<'3.12'",
35-
"grpcio>=1.60.0,<1.70; python_version>='3.12'"
36+
"grpcio>=1.60.0,<1.70; python_version>='3.12' and python_version<'3.14'",
37+
"grpcio>=1.75.1,<2; python_version>='3.14'"
3638
]
3739

3840
extras_require = {
3941
"dev": [
40-
"grpcio-tools>=1.60,<1.70",
42+
"grpcio-tools>=1.60,<1.70; python_version<'3.14'",
43+
"grpcio-tools>=1.75.1,<2; python_version>='3.14'",
4144
]
4245
}
4346

4447
setup_args = {
45-
'python_requires':'>=3.8,<3.14',
48+
'python_requires':'>=3.8,<3.15',
4649
'version': VERSION,
4750
'author': AUTHOR,
4851
'author_email': AUTHOR_EMAIL,

webull/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.0.13'
1+
__version__ = '2.0.14'

webull/core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = '2.0.13'
1+
__version__ = '2.0.14'
22

33
import logging
44

webull/core/auth/composer/default_signature_composer.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ def _refresh_sign_headers(host, headers, app_key_id, signer_spec):
5959
sign_headers[hd.APP_KEY] = app_key_id
6060
sign_headers[hd.TIMESTAMP] = common.get_iso_8601_date()
6161

62-
if common.is_not_upgrade_api_host(host):
63-
signer_spec = sha_hmac256_new
64-
else:
65-
signer_spec = sha_hmac1
62+
signer_spec = sha_hmac256_new
6663

6764
sign_headers[hd.SIGN_VERSION] = signer_spec.get_signer_version()
6865
sign_headers[hd.SIGN_ALGORITHM] = signer_spec.get_signer_name()

webull/core/utils/common.py

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import base64
1717
import hashlib
1818
import socket
19-
from datetime import datetime
19+
from datetime import datetime, timezone
2020
import uuid
2121
import json
2222
from webull.core.compat import ensure_bytes, ensure_string
@@ -33,21 +33,21 @@ def get_uuid():
3333
def get_iso_8601_date(dt_as_utc=None):
3434
if dt_as_utc:
3535
return dt_as_utc.strftime(FORMAT_ISO_8601)
36-
d = datetime.utcnow()
36+
d = datetime.now(timezone.utc)
3737
return d.strftime(FORMAT_ISO_8601)
3838

3939
def get_iso_8601_date_with_millis(dt_as_utc=None):
4040
if dt_as_utc:
4141
d = dt_as_utc
4242
else:
43-
d = datetime.utcnow()
43+
d = datetime.now(timezone.utc)
4444
ret = d.strftime(FORMAT_ISO_8601_MILLIS)
4545
if len(ret) != 27:
4646
raise RuntimeError("failed to convent timestamp, result: %s" % ret)
4747
return ret[:-4] + ret[-1:]
4848

4949
def parse_timestamp_to_dt(timestamp_of_millis):
50-
return datetime.utcfromtimestamp(timestamp_of_millis / 1000.0)
50+
return datetime.fromtimestamp(timestamp_of_millis / 1000.0, tz=timezone.utc).replace(tzinfo=None)
5151

5252
def md5_sum(content):
5353
content_bytes = ensure_bytes(content)
@@ -63,17 +63,4 @@ def sha256_hex(content):
6363
return hashlib.sha256(content_bytes).hexdigest()
6464

6565
def json_dumps_compact(content):
66-
return json.dumps(content, ensure_ascii=False, separators=(',', ':'))
67-
68-
def is_not_upgrade_api_host(host):
69-
upgrade_hosts = {
70-
"api.webull.com","events-api.webull.com",
71-
"api.webull.hk","events-api.webull.hk",
72-
"pre-openapi-us-alb.webullbroker.com","pre-openapi-us-events.webullbroker.com",
73-
"pre-openapi-alb.webullbroker.com","pre-openapi-events.webullbroker.com",
74-
"us-openapi-alb.uat.webullbroker.com","us-openapi-events.uat.webullbroker.com",
75-
"hk-openapi.uat.webullbroker.com", "hk-openapi-events-api.uat.webullbroker.com",
76-
"api.sandbox.webull.hk", "events-api.sandbox.webull.hk"
77-
78-
}
79-
return host not in upgrade_hosts
66+
return json.dumps(content, ensure_ascii=False, separators=(',', ':'))

webull/data/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# coding=utf-8
22

3-
__version__ = '2.0.13'
3+
__version__ = '2.0.14'

webull/data/quotes/instrument.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from webull.data.request.get_futures_products_request import GetFuturesProductsRequest
2626
from webull.data.request.get_futures_instruments_by_code_request import GetFuturesInstrumentsByCodeRequest
2727
from webull.data.request.get_futures_product_class import GetFuturesProductClassRequest
28+
from webull.data.request.get_option_contracts_request import GetOptionContractsRequest
2829

2930

3031
class Instrument:
@@ -218,6 +219,48 @@ def get_event_instrument(self, series_symbol, event_symbol=None, symbols=None, e
218219
response = self.client.get_response(event_instrument_request)
219220
return response
220221

222+
def get_option_contracts(self, category=Category.US_OPTION.name, underlying_symbols=None, status=None,
223+
start_date=None, end_date=None, root_symbol=None, option_symbol=None,
224+
option_type=None, style=None, strike_price_gte=None, strike_price_lte=None,
225+
ppind=None, show_deliverables=None, page_size=10, last_instrument_id=None):
226+
"""
227+
Query option contracts list by conditions such as underlying_symbols, status, expiration date, etc.
228+
229+
:param category: Market category. Value: US_OPTION.
230+
:param underlying_symbols: Underlying symbol(s), comma-separated for multiple, e.g. AAPL,MSFT.
231+
:param status: Contract status: LISTING (default), DELISTING.
232+
:param start_date: Exact expiration date, format YYYY-MM-DD.
233+
:param end_date: Expiration date lower bound (inclusive), format YYYY-MM-DD.
234+
:param root_symbol: Root symbol filter (series symbol, e.g. SPXW), mainly for index options and non-standard contracts after CA.
235+
:param option_symbol: Option symbol, e.g. AAPL250620C00150000.
236+
:param option_type: Contract type: CALL / PUT.
237+
:param style: Exercise style: AMERICAN / EUROPEAN.
238+
:param strike_price_gte: Strike price lower bound (inclusive).
239+
:param strike_price_lte: Strike price upper bound (inclusive).
240+
:param ppind: Penny Program Indicator: true = Penny Pilot, false = non-Penny Pilot.
241+
:param show_deliverables: Whether to return deliverables array in response: true / false, default false.
242+
:param page_size: Page size, default 10, max 1000.
243+
:param last_instrument_id: Last instrument_id from previous page for pagination.
244+
"""
245+
request = GetOptionContractsRequest()
246+
request.set_category(category)
247+
request.set_underlying_symbols(underlying_symbols)
248+
request.set_status(status)
249+
request.set_start_date(start_date)
250+
request.set_end_date(end_date)
251+
request.set_root_symbol(root_symbol)
252+
request.set_option_symbol(option_symbol)
253+
request.set_option_type(option_type)
254+
request.set_style(style)
255+
request.set_strike_price_gte(strike_price_gte)
256+
request.set_strike_price_lte(strike_price_lte)
257+
request.set_ppind(ppind)
258+
request.set_show_deliverables(show_deliverables)
259+
request.set_page_size(page_size)
260+
request.set_last_instrument_id(last_instrument_id)
261+
response = self.client.get_response(request)
262+
return response
263+
221264
def get_company_profile(self, symbol, category=Category.US_STOCK.name):
222265
"""
223266
Get company profile for one instrument.

0 commit comments

Comments
 (0)