Skip to content

Commit e37c654

Browse files
author
方佳
committed
Merge branch 'main_merge_261017' into 'main'
[API-1816]: add market data api and start_time and end_time for bars/list See merge request webull/webull-openapi-python-sdk!48
2 parents a485198 + 6a8d634 commit e37c654

30 files changed

Lines changed: 756 additions & 11 deletions

‎samples/__init__.py‎

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

‎samples/data/data_client.py‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@
5959
if res.status_code == 200:
6060
print('get_crypto_history_bar:', res.json())
6161

62+
# Crypto historical bars within a time range using start_time and end_time (timestamps in milliseconds).
63+
res = data_client.crypto_market_data.get_crypto_history_bar(
64+
"BTCUSD", Category.US_CRYPTO.name, Timespan.M1.name,
65+
start_time=1711262998500, end_time=1711349398500)
66+
if res.status_code == 200:
67+
print('get_crypto_history_bar with time range:', res.json())
68+
6269
res = data_client.market_data.get_snapshot('AAPL', Category.US_STOCK.name, extend_hour_required=True, overnight_required=True)
6370
if res.status_code == 200:
6471
print('get_snapshot:', res.json())
@@ -94,6 +101,13 @@
94101
if res.status_code == 200:
95102
print('get_futures_history_bars:', res.json())
96103

104+
# Futures historical bars within a time range using start_time and end_time (timestamps in milliseconds).
105+
res = data_client.futures_market_data.get_futures_history_bars(
106+
'SILZ5,6BM6', Category.US_FUTURES.name, Timespan.M1.name,
107+
start_time=1711262998500, end_time=1711349398500)
108+
if res.status_code == 200:
109+
print('get_futures_history_bars with time range:', res.json())
110+
97111
res = data_client.futures_market_data.get_futures_tick("SILZ5", Category.US_FUTURES.name, count=10)
98112
if res.status_code == 200:
99113
print('get_futures_tick:', res.json())
@@ -156,6 +170,13 @@
156170
if res.status_code == 200:
157171
print('get_option_history_bars:', res.json())
158172

173+
# Option historical bars within a time range using start_time and end_time (timestamps in milliseconds).
174+
res = data_client.option_market_data.get_option_history_bars(
175+
'AAPL260522C00300000', Category.US_OPTION.name, Timespan.M1.name,
176+
start_time=1711262998500, end_time=1711349398500)
177+
if res.status_code == 200:
178+
print('get_option_history_bars with time range:', res.json())
179+
159180
# Option market data - Tick
160181
# Rate limit: 60 requests per minute
161182
res = data_client.option_market_data.get_option_tick("AAPL260522C00300000", Category.US_OPTION.name, count=30)
@@ -292,3 +313,61 @@
292313
res = data_client.screener.get_52whl(Category.US_STOCK.name, rank_type="NEW_HIGH")
293314
if res.status_code == 200:
294315
print('get_52whl:', res.json())
316+
317+
# ==================== Screeners ====================
318+
# Market sentiment (fear & greed) index
319+
sentiment_res = data_client.screener.get_fear_and_greed_index(Category.US_STOCK.name)
320+
if sentiment_res.status_code == 200:
321+
print('get_fear_and_greed_index:', sentiment_res.json())
322+
323+
# Stock unusual activities
324+
unusual_res = data_client.screener.get_stock_unusual_activities("NVDA", Category.US_STOCK.name, count=10)
325+
if unusual_res.status_code == 200:
326+
print('get_stock_unusual_activities:', unusual_res.json())
327+
328+
# ==================== Fundamentals ====================
329+
# Macro economic calendar
330+
calendar_res = data_client.fundamentals.get_economic_calendars("US", "2026-09-01", "2026-09-08")
331+
if calendar_res.status_code == 200:
332+
print('get_economic_calendars:', calendar_res.json())
333+
334+
# Institution search -> 13F holdings -> holding changes
335+
institutions_res = data_client.fundamentals.get_institution_search("Bridgewater")
336+
if institutions_res.status_code == 200:
337+
print('get_institution_search:', institutions_res.json())
338+
339+
holdings_res = data_client.fundamentals.get_institution_holdings("0001738828")
340+
if holdings_res.status_code == 200:
341+
print('get_institution_holdings:', holdings_res.json())
342+
343+
holding_changes_res = data_client.fundamentals.get_institution_holding_changes("0001738828", "OPEN", count=10)
344+
if holding_changes_res.status_code == 200:
345+
print('get_institution_holding_changes:', holding_changes_res.json())
346+
347+
# Insider transactions and holdings overview
348+
insider_tx_res = data_client.fundamentals.get_insider_transactions("NVDA", Category.US_STOCK.name)
349+
if insider_tx_res.status_code == 200:
350+
print('get_insider_transactions:', insider_tx_res.json())
351+
352+
insider_overview_res = data_client.fundamentals.get_insider_holdings(
353+
"NVDA", Category.US_STOCK.name, compare_months=1)
354+
if insider_overview_res.status_code == 200:
355+
print('get_insider_holdings:', insider_overview_res.json())
356+
357+
# ==================== News ====================
358+
# Search news by keyword
359+
search_news_res = data_client.news.get_news_search("Starlink")
360+
if search_news_res.status_code == 200:
361+
print('get_news_search:', search_news_res.json())
362+
363+
# Stock news list
364+
stock_news_res = data_client.news.get_stock_news("TSLA", Category.US_STOCK.name)
365+
if stock_news_res.status_code == 200:
366+
print('get_stock_news:', stock_news_res.json())
367+
368+
# News detail (use a news_id returned by the list/search APIs)
369+
news_items = (stock_news_res.json().get('data') or []) if stock_news_res.status_code == 200 else []
370+
if news_items:
371+
news_detail_res = data_client.news.get_news_detail(news_items[0].get('news_id'))
372+
if news_detail_res.status_code == 200:
373+
print('get_news_detail:', news_detail_res.json())

‎samples/data/data_client_event.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@
6464
if res.status_code == 200:
6565
print('get_event_bars:', res.json())
6666

67+
# Query bars within a time range using start_time and end_time (timestamps in milliseconds).
68+
res = data_client.event_market_data.get_event_bars(
69+
"KXRATECUTCOUNT-25DEC31-T10", Timespan.M1, Category.US_EVENT.name, 200, False,
70+
start_time=1711262998500, end_time=1711349398500)
71+
if res.status_code == 200:
72+
print('get_event_bars with time range:', res.json())
73+
6774
res = data_client.event_market_data.get_event_tick("KXRATECUTCOUNT-25DEC31-T10", Category.US_EVENT.name, 30)
6875
if res.status_code == 200:
6976
print('get_event_tick:', res.json())

‎webull/__init__.py‎

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

‎webull/core/__init__.py‎

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

33
import logging
44

‎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__ = '3.0.3'
3+
__version__ = '3.0.4'

‎webull/data/data_client.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from webull.data.quotes.fundamentals import Fundamentals
2828
from webull.data.quotes.screener import Screener
2929
from webull.data.quotes.watchlist import Watchlist
30+
from webull.data.quotes.news import News
3031
from webull.data.quotes.community import Community
3132

3233

@@ -44,6 +45,7 @@ def __init__(self, api_client):
4445
self.screener = Screener(api_client)
4546
self.watchlist = Watchlist(api_client)
4647
self.community = Community(api_client)
48+
self.news = News(api_client)
4749

4850
def _init_logger(self, api_client):
4951
# No logger configured, using default console and local file logging.

‎webull/data/quotes/crypto_market_data.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class CryptoMarketData:
2020
def __init__(self, api_client):
2121
self.client = api_client
2222

23-
def get_crypto_history_bar(self, symbols, category, timespan, count='200', real_time_required=None):
23+
def get_crypto_history_bar(self, symbols, category, timespan, count='200', real_time_required=None,
24+
start_time=None, end_time=None):
2425
"""
2526
Returns to Instrument in the window aggregated data.
2627
According to the last N K-lines of the stock code, it supports various granularity K-lines such as m1 and m5.
@@ -33,13 +34,17 @@ def get_crypto_history_bar(self, symbols, category, timespan, count='200', real_
3334
:param count: The number of lines: the default is 200, and the maximum limit is 1200
3435
:param real_time_required: Returns the latest trade quote data. By default, the most recent market data is returned.
3536
By default, only intraday candlestick data is returned.
37+
:param start_time: Optional start time as a timestamp in milliseconds, used to specify the beginning of the time range for bar data.
38+
:param end_time: Optional end time as a timestamp in milliseconds, used to specify the end of the time range for bar data.
3639
"""
3740
crypto_history_bar_request = GetCryptoHistoricalBarsRequest()
3841
crypto_history_bar_request.set_symbols(symbols)
3942
crypto_history_bar_request.set_category(category)
4043
crypto_history_bar_request.set_timespan(timespan)
4144
crypto_history_bar_request.set_count(count)
4245
crypto_history_bar_request.set_real_time_required(real_time_required)
46+
crypto_history_bar_request.set_start_time(start_time)
47+
crypto_history_bar_request.set_end_time(end_time)
4348
response = self.client.get_response(crypto_history_bar_request)
4449
return response
4550

‎webull/data/quotes/event_market_data.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ def get_event_depth(self, symbol, category=Category.US_EVENT.name, depth=10):
5252
response = self.client.get_response(quote_request)
5353
return response
5454

55-
def get_event_bars(self, symbols, timespan, category=Category.US_EVENT.name, count=200, real_time_required=False):
55+
def get_event_bars(self, symbols, timespan, category=Category.US_EVENT.name, count=200, real_time_required=False,
56+
start_time=None, end_time=None):
5657
"""
5758
Get the current order book for a specific event instrument.
5859
The order book shows all active bid orders for both yes and no sides of a binary market.
@@ -63,13 +64,17 @@ def get_event_bars(self, symbols, timespan, category=Category.US_EVENT.name, cou
6364
:param category: default is US_EVENT, currently only US_EVENT is supported.
6465
:param count: Number of entries, default 200 entries, maximum 1200 entries.
6566
:param real_time_required: Does it include the latest data? The k-line that has not been finalized is not included by default. It is false and only used for minutes.
67+
:param start_time: Optional start time as a timestamp in milliseconds, used to specify the beginning of the time range for bar data.
68+
:param end_time: Optional end time as a timestamp in milliseconds, used to specify the end of the time range for bar data.
6669
"""
6770
quote_request = GetEventBarsRequest()
6871
quote_request.set_symbols(symbols)
6972
quote_request.set_category(category)
7073
quote_request.set_timespan(timespan)
7174
quote_request.set_count(count)
7275
quote_request.set_real_time_required(real_time_required)
76+
quote_request.set_start_time(start_time)
77+
quote_request.set_end_time(end_time)
7378
response = self.client.get_response(quote_request)
7479
return response
7580

‎webull/data/quotes/fundamentals.py‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
from webull.data.request.get_financials_balance_sheet_request import GetFinancialsBalanceSheetRequest
3737
from webull.data.request.get_financials_alert_request import GetFinancialsAlertRequest
3838
from webull.data.request.get_forecast_eps_request import GetForecastEpsRequest
39+
from webull.data.request.get_institution_search_request import GetInstitutionSearchRequest
40+
from webull.data.request.get_institution_holdings_request import GetInstitutionHoldingsRequest
41+
from webull.data.request.get_institution_holding_changes_request import GetInstitutionHoldingChangesRequest
42+
from webull.data.request.get_insider_transactions_request import GetInsiderTransactionsRequest
43+
from webull.data.request.get_insider_holdings_request import GetInsiderHoldingsRequest
44+
from webull.data.request.get_economic_calendar_request import GetEconomicCalendarRequest
3945

4046

4147
class Fundamentals:
@@ -352,3 +358,96 @@ def get_forecast_eps(self, symbol, category=Category.US_STOCK.name):
352358
request.set_category(category)
353359
response = self.client.get_response(request)
354360
return response
361+
362+
def get_institution_search(self, keyword, pagination_key=None):
363+
"""
364+
Search US institutional investors by name or SEC CIK number. US only.
365+
366+
:param keyword: Search keyword (institution name or CIK), required.
367+
:param pagination_key: Pagination key from the previous page response; not returned on the last page.
368+
"""
369+
request = GetInstitutionSearchRequest()
370+
request.set_keyword(keyword)
371+
request.set_pagination_key(pagination_key)
372+
response = self.client.get_response(request)
373+
return response
374+
375+
def get_institution_holdings(self, cik, pagination_key=None):
376+
"""
377+
Query a US institution's 13F holdings. US only.
378+
379+
:param cik: Institution CIK number, required.
380+
:param pagination_key: Pagination key from the previous page response; not returned on the last page.
381+
"""
382+
request = GetInstitutionHoldingsRequest()
383+
request.set_cik(cik)
384+
request.set_pagination_key(pagination_key)
385+
response = self.client.get_response(request)
386+
return response
387+
388+
def get_institution_holding_changes(self, cik, position, count=None, pagination_key=None):
389+
"""
390+
Query US institutional holding changes filtered by position type. US only.
391+
392+
:param cik: Fund CIK number, required.
393+
:param position: Position type: OPEN, CLOSE, or CHANGE, required.
394+
:param count: Page size, optional, default 10, max 50.
395+
:param pagination_key: Pagination key from the previous page response; not returned on the last page.
396+
"""
397+
request = GetInstitutionHoldingChangesRequest()
398+
request.set_cik(cik)
399+
request.set_position(position)
400+
request.set_count(count)
401+
request.set_pagination_key(pagination_key)
402+
response = self.client.get_response(request)
403+
return response
404+
405+
def get_insider_transactions(self, symbol, category=Category.US_STOCK.name, side=None, pagination_key=None):
406+
"""
407+
Query insider transaction records for a stock.
408+
409+
:param symbol: Stock symbol, e.g. NVDA, required.
410+
:param category: Market category. Supported: US_STOCK (default), HK_STOCK.
411+
:param side: Transaction side filter: BUY or SELL, None to return all.
412+
:param pagination_key: Pagination key from the previous page response; not returned on the last page.
413+
"""
414+
request = GetInsiderTransactionsRequest()
415+
request.set_symbol(symbol)
416+
request.set_category(category)
417+
request.set_side(side)
418+
request.set_pagination_key(pagination_key)
419+
response = self.client.get_response(request)
420+
return response
421+
422+
def get_insider_holdings(self, symbol, category=Category.US_STOCK.name, compare_months=None):
423+
"""
424+
Query the insider ownership statistics overview for a stock.
425+
426+
:param symbol: Stock symbol, e.g. NVDA, required.
427+
:param category: Market category. Supported: US_STOCK (default), HK_STOCK.
428+
:param compare_months: Comparison period in months: 1 (default) or 3.
429+
"""
430+
request = GetInsiderHoldingsRequest()
431+
request.set_symbol(symbol)
432+
request.set_category(category)
433+
request.set_compare_months(compare_months)
434+
response = self.client.get_response(request)
435+
return response
436+
437+
def get_economic_calendars(self, region=None, start_date=None, end_date=None, pagination_key=None):
438+
"""
439+
Query the macroeconomic event calendar for an optional region and date range.
440+
Results are cursor-paginated, up to 30 events per page.
441+
442+
:param region: Region code: US (default), CN, EU, JP, GB.
443+
:param start_date: Start date in yyyy-MM-dd format, None defaults to today.
444+
:param end_date: End date in yyyy-MM-dd format, None defaults to start date + 7 days.
445+
:param pagination_key: Pagination key from the previous page response; not returned on the last page.
446+
"""
447+
request = GetEconomicCalendarRequest()
448+
request.set_region(region)
449+
request.set_start_date(start_date)
450+
request.set_end_date(end_date)
451+
request.set_pagination_key(pagination_key)
452+
response = self.client.get_response(request)
453+
return response

0 commit comments

Comments
 (0)