Skip to content

Commit 4c5149f

Browse files
author
方佳
committed
Merge branch 'main_260509_merge' into 'main'
feat:Added market data APIs for Company Profiles, Analyst Target Prices, Analyst Ratings, Watchlists, Rankings, and more. See merge request webull/webull-openapi-python-sdk!28
2 parents cfee89e + ca27fef commit 4c5149f

37 files changed

Lines changed: 1517 additions & 14 deletions

‎samples/__init__.py‎

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

‎samples/data/data_client.py‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,36 @@
112112

113113
res = data_client.instrument.get_futures_instrument_by_code("ES", Category.US_FUTURES.name, ContractType.MONTHLY.name)
114114
if res.status_code == 200:
115-
print('get_futures_instrument_by_code:', res.json())
115+
print('get_futures_instrument_by_code:', res.json())
116+
117+
res = data_client.instrument.get_company_profile("AAPL", Category.US_STOCK.name)
118+
if res.status_code == 200:
119+
print('get_company_profile:', res.json())
120+
121+
res = data_client.instrument.get_analyst_target_price("AAPL", Category.US_STOCK.name)
122+
if res.status_code == 200:
123+
print('get_analyst_target_price:', res.json())
124+
125+
res = data_client.instrument.get_analyst_rating("AAPL", Category.US_STOCK.name)
126+
if res.status_code == 200:
127+
print('get_analyst_rating:', res.json())
128+
129+
# NOII (Net Order Imbalance Indicator) - Opening imbalance bars
130+
res = data_client.market_data.get_noii_bars("AAPL", Category.US_STOCK.name, "PRE_OPEN")
131+
if res.status_code == 200:
132+
print('get_noii_bars (opening):', res.json())
133+
134+
# NOII - Closing imbalance bars
135+
res = data_client.market_data.get_noii_bars("AAPL", Category.US_STOCK.name, "PRE_CLOSE")
136+
if res.status_code == 200:
137+
print('get_noii_bars (closing):', res.json())
138+
139+
# NOII Snapshot - Opening imbalance
140+
res = data_client.market_data.get_noii_snapshot("AAPL", Category.US_STOCK.name, "PRE_OPEN")
141+
if res.status_code == 200:
142+
print('get_noii_snapshot (opening):', res.json())
143+
144+
# NOII Snapshot - Closing imbalance
145+
res = data_client.market_data.get_noii_snapshot("AAPL", Category.US_STOCK.name, "PRE_CLOSE")
146+
if res.status_code == 200:
147+
print('get_noii_snapshot (closing):', res.json())

‎samples/screener/__init__.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2022 Webull
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+
# coding=utf-8
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Copyright 2022 Webull
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+
# coding=utf-8
16+
17+
from webull.core.client import ApiClient
18+
from webull.data.data_client import DataClient
19+
20+
optional_api_endpoint = "<api_endpoint>"
21+
your_app_key = "<your_app_key>"
22+
your_app_secret = "<your_app_secret>"
23+
region_id = "<region_id>"
24+
25+
api_client = ApiClient(your_app_key, your_app_secret, region_id)
26+
api_client.add_endpoint(region_id, optional_api_endpoint)
27+
28+
29+
if __name__ == '__main__':
30+
data_client = DataClient(api_client)
31+
32+
# Get top gainers for today (stocks with highest price increase)
33+
res = data_client.screener.get_gainers_losers(
34+
rank_type="DAY_1",
35+
category="US_STOCK",
36+
sort_by="CHANGE_RATIO",
37+
direction="DESC",
38+
page_size=10
39+
)
40+
if res.status_code == 200:
41+
print('get_gainers (day):', res.json())
42+
43+
# Get top losers for today (stocks with highest price decrease)
44+
res = data_client.screener.get_gainers_losers(
45+
rank_type="DAY_1",
46+
category="US_STOCK",
47+
sort_by="CHANGE_RATIO",
48+
direction="ASC",
49+
page_size=10
50+
)
51+
if res.status_code == 200:
52+
print('get_losers (day):', res.json())
53+
54+
# Get pre-market movers
55+
res = data_client.screener.get_gainers_losers(
56+
rank_type="PRE_MARKET",
57+
category="US_STOCK",
58+
sort_by="CHANGE_RATIO",
59+
direction="DESC",
60+
page_size=20
61+
)
62+
if res.status_code == 200:
63+
print('get_pre_market_movers:', res.json())
64+
65+
# Get 52-week top performers
66+
res = data_client.screener.get_gainers_losers(
67+
rank_type="WEEK_52",
68+
category="US_STOCK",
69+
sort_by="CHANGE_RATIO",
70+
direction="DESC",
71+
page_size=10
72+
)
73+
if res.status_code == 200:
74+
print('get_52_week_top_performers:', res.json())
75+
76+
# Get most active stocks by volume
77+
res = data_client.screener.get_most_active(
78+
category="US_STOCK",
79+
rank_type="VOLUME",
80+
sort_by="VOLUME",
81+
direction="DESC",
82+
page_size=10
83+
)
84+
if res.status_code == 200:
85+
print('get_most_active_by_volume:', res.json())
86+
87+
# Get stocks with unusual trading activity (high relative volume)
88+
res = data_client.screener.get_most_active(
89+
category="US_STOCK",
90+
rank_type="RELATIVE_VOLUME_10D",
91+
sort_by="RELATIVE_VOLUME_10D",
92+
direction="DESC",
93+
page_size=10
94+
)
95+
if res.status_code == 200:
96+
print('get_unusual_volume_activity:', res.json())
97+
98+
# Get most active stocks by turnover amount
99+
res = data_client.screener.get_most_active(
100+
category="US_STOCK",
101+
rank_type="TURNOVER",
102+
sort_by="TURNOVER",
103+
direction="DESC",
104+
page_size=10
105+
)
106+
if res.status_code == 200:
107+
print('get_most_active_by_turnover:', res.json())
108+
109+
# Get stocks with high price amplitude
110+
res = data_client.screener.get_most_active(
111+
category="US_STOCK",
112+
rank_type="AMPLITUDE",
113+
sort_by="AMPLITUDE",
114+
direction="DESC",
115+
page_size=10
116+
)
117+
if res.status_code == 200:
118+
print('get_high_amplitude_stocks:', res.json())

‎samples/watchlist/__init__.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2022 Webull
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.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright 2022 Webull
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+
# coding=utf-8
16+
17+
from webull.core.client import ApiClient
18+
from webull.data.data_client import DataClient
19+
20+
optional_api_endpoint = "<api_endpoint>"
21+
your_app_key = "<your_app_key>"
22+
your_app_secret = "<your_app_secret>"
23+
region_id = "<region_id>"
24+
25+
api_client = ApiClient(your_app_key, your_app_secret, region_id)
26+
api_client.add_endpoint(region_id, optional_api_endpoint)
27+
28+
29+
if __name__ == '__main__':
30+
data_client = DataClient(api_client)
31+
32+
# Get all watchlists
33+
res = data_client.watchlist.get_watchlist()
34+
if res.status_code == 200:
35+
print('get_watchlist:', res.json())
36+
37+
# Create a new watchlist (max 20 watchlists, shared with retail)
38+
res = data_client.watchlist.create_watchlist(name="Tech Stocks", sort=1)
39+
if res.status_code == 200:
40+
print('create_watchlist:', res.json())
41+
watchlist_id = res.json().get("watchlist_id")
42+
43+
# Update the watchlist
44+
res = data_client.watchlist.update_watchlist(
45+
watchlist_id=watchlist_id,
46+
name="My Favorites",
47+
sort=2
48+
)
49+
if res.status_code == 200:
50+
print('update_watchlist:', res.json())
51+
52+
# Add instruments to the watchlist (max 1000 instruments total)
53+
instruments = [
54+
{"symbol": "AAPL", "category": "US_STOCK", "sort": 1},
55+
{"symbol": "TSLA", "category": "US_STOCK", "sort": 2}
56+
]
57+
res = data_client.watchlist.add_instruments(watchlist_id, instruments)
58+
if res.status_code == 200:
59+
print('add_instruments:', res.json())
60+
61+
# Get instruments in the watchlist
62+
res = data_client.watchlist.get_instruments(watchlist_id)
63+
if res.status_code == 200:
64+
print('get_instruments:', res.json())
65+
66+
# Update instruments sort order
67+
instruments = [
68+
{"symbol": "AAPL", "category": "US_STOCK", "sort": 3},
69+
{"symbol": "TSLA", "category": "US_STOCK", "sort": 1}
70+
]
71+
res = data_client.watchlist.update_instruments(watchlist_id, instruments)
72+
if res.status_code == 200:
73+
print('update_instruments:', res.json())
74+
75+
# Remove instruments from the watchlist
76+
instruments = [
77+
{"symbol": "AAPL", "category": "US_STOCK"},
78+
{"symbol": "TSLA", "category": "US_STOCK"}
79+
]
80+
res = data_client.watchlist.remove_instruments(watchlist_id, instruments)
81+
if res.status_code == 200:
82+
print('remove_instruments:', res.json())
83+
84+
# Delete the watchlist (irreversible)
85+
res = data_client.watchlist.delete_watchlist(watchlist_id)
86+
if res.status_code == 200:
87+
print('delete_watchlist:', res.json())

‎webull/__init__.py‎

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

‎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.5'
1+
__version__ = '2.0.6'
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__ = '2.0.5'
3+
__version__ = '2.0.6'

‎webull/data/data_client.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from webull.data.quotes.futures_market_data import FuturesMarketData
2424
from webull.data.quotes.instrument import Instrument
2525
from webull.data.quotes.market_data import MarketData
26+
from webull.data.quotes.screener import Screener
27+
from webull.data.quotes.watchlist import Watchlist
2628

2729

2830
class DataClient:
@@ -34,6 +36,8 @@ def __init__(self, api_client):
3436
self.crypto_market_data = CryptoMarketData(api_client)
3537
self.futures_market_data = FuturesMarketData(api_client)
3638
self.event_market_data = EventMarketData(api_client)
39+
self.screener = Screener(api_client)
40+
self.watchlist = Watchlist(api_client)
3741

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

0 commit comments

Comments
 (0)