Skip to content

Commit abe5668

Browse files
author
方佳
committed
Merge branch 'main_merge_260921' into 'main'
feat: support transfers activities for Webull US See merge request webull/webull-openapi-python-sdk!46
2 parents 6d14187 + 5635e5d commit abe5668

11 files changed

Lines changed: 251 additions & 6 deletions

File tree

‎samples/__init__.py‎

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

‎samples/trade/trade_client_v3.py‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from webull.core.client import ApiClient
2020
from webull.trade.trade_client import TradeClient
21+
from webull.trade.common.transfer_activities_param import TransferActivitiesParam
2122

2223
optional_api_endpoint = "<api_endpoint>"
2324
your_app_key = "<your_app_key>"
@@ -888,3 +889,40 @@
888889
res = trade_client.order_v3.get_order_detail(account_id, trailing_stop_loss_limit_client_order_id)
889890
if res.status_code == 200:
890891
print('get trailing stop loss limit order detail res:', res.json())
892+
893+
# ============================================================
894+
# Position Transfers Example (US only)
895+
# ============================================================
896+
897+
# First page: pass no pagination_key and no filters.
898+
res = trade_client.activity.list_transfers_activities(TransferActivitiesParam(account_id))
899+
if res.status_code == 200:
900+
print('list transfers activities res:', res.json())
901+
902+
# Iterate all pages using the returned pagination_key until it is absent.
903+
pagination_key = (res.json() or {}).get('pagination_key') if res.status_code == 200 else None
904+
while pagination_key:
905+
res = trade_client.activity.list_transfers_activities(
906+
TransferActivitiesParam(account_id, pagination_key=pagination_key))
907+
if res.status_code != 200:
908+
break
909+
print('list transfers activities next page res:', res.json())
910+
pagination_key = (res.json() or {}).get('pagination_key')
911+
912+
# Filtered query: ACATS/crypto incoming transfers, FULL type, within a time range.
913+
res = trade_client.activity.list_transfers_activities(
914+
TransferActivitiesParam(
915+
account_id,
916+
transfer_method="ACATS,CRYPTO_TRANSFER",
917+
direction="INCOMING",
918+
status="PENDING,COMPLETED",
919+
acats_transfer_types="FULL",
920+
start_time="2026-08-01T00:00:00Z",
921+
end_time="2026-08-31T23:59:59Z"))
922+
if res.status_code == 200:
923+
print('list transfers activities filtered res:', res.json())
924+
925+
# Get a single transfer record by account_id and transfer_id.
926+
res = trade_client.activity.get_transfer_activity_detail(account_id, transfer_id="<your_transfer_id>")
927+
if res.status_code == 200:
928+
print('get transfer activity res:', 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.1'
1+
__version__ = '3.0.2'

‎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.1'
1+
__version__ = '3.0.2'
22

33
import logging
44

‎webull/core/utils/validation.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,11 @@ def assert_integer_positive(integer, name):
4646
if isinstance(integer, int) and integer > 0:
4747
return
4848
raise ClientException(error_code.SDK_INVALID_PARAMETER,
49-
"{0} should be a positive integer.".format(name))
49+
"{0} should be a positive integer.".format(name))
50+
51+
52+
def assert_string_not_empty(value, name):
53+
if isinstance(value, str) and value.strip():
54+
return
55+
raise ClientException(error_code.SDK_INVALID_PARAMETER,
56+
"{0} should not be empty.".format(name))

‎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.1'
3+
__version__ = '3.0.2'

‎webull/trade/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.0.1'
1+
__version__ = '3.0.2'
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.utils import validation
18+
19+
20+
class TransferActivitiesParam:
21+
"""
22+
Parameter object for :meth:`Activity.list_transfers_activities`.
23+
24+
:param account_id: Account ID (required).
25+
:param transfer_method: Transfer method filter, comma-separated (e.g. ACATS,CRYPTO_TRANSFER) (optional).
26+
:param direction: Direction filter: INCOMING or OUTGOING (optional).
27+
:param status: Status filter, comma-separated: PENDING, COMPLETED, REJECTED, FAILED, CANCELLED (optional).
28+
:param acats_transfer_types: ACATS transfer types filter, comma-separated: FULL, PARTIAL, RESIDUAL, RECLAIM, OTHER (optional).
29+
:param start_time: ISO8601 UTC start time of transfer create time (optional).
30+
:param end_time: ISO8601 UTC end time of transfer create time (optional).
31+
:param pagination_key: Pagination key from the previous page response; not returned on the last page (optional).
32+
"""
33+
34+
def __init__(self, account_id, transfer_method=None, direction=None, status=None,
35+
acats_transfer_types=None, start_time=None, end_time=None, pagination_key=None):
36+
validation.assert_string_not_empty(account_id, "account_id")
37+
self.account_id = account_id
38+
self.transfer_method = transfer_method
39+
self.direction = direction
40+
self.status = status
41+
self.acats_transfer_types = acats_transfer_types
42+
self.start_time = start_time
43+
self.end_time = end_time
44+
self.pagination_key = pagination_key
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
from webull.core.request import ApiRequest
17+
18+
19+
class TransferActivitiesDetailRequest(ApiRequest):
20+
def __init__(self):
21+
ApiRequest.__init__(self, "/trading/activities/transfer-activities/get", version='v3', method="GET",
22+
query_params={})
23+
24+
def set_account_id(self, account_id):
25+
self.add_query_param("account_id", account_id)
26+
27+
def set_transfer_id(self, transfer_id):
28+
self.add_query_param("transfer_id", transfer_id)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
from webull.core.request import ApiRequest
17+
from webull.core.utils import validation
18+
19+
20+
class TransferActivitiesRequest(ApiRequest):
21+
def __init__(self):
22+
ApiRequest.__init__(self, "/trading/activities/transfer-activities/list", version='v3', method="GET",
23+
query_params={})
24+
25+
def set_account_id(self, account_id):
26+
validation.assert_string_not_empty(account_id, "account_id")
27+
self.add_query_param("account_id", account_id)
28+
29+
def set_transfer_method(self, transfer_method):
30+
if transfer_method is not None:
31+
self.add_query_param("transfer_method", transfer_method)
32+
33+
def set_direction(self, direction):
34+
if direction is not None:
35+
self.add_query_param("direction", direction)
36+
37+
def set_status(self, status):
38+
if status is not None:
39+
self.add_query_param("status", status)
40+
41+
def set_acats_transfer_types(self, acats_transfer_types):
42+
if acats_transfer_types is not None:
43+
self.add_query_param("acats_transfer_types", acats_transfer_types)
44+
45+
def set_start_time(self, start_time):
46+
if start_time is not None:
47+
self.add_query_param("start_time", start_time)
48+
49+
def set_end_time(self, end_time):
50+
if end_time is not None:
51+
self.add_query_param("end_time", end_time)
52+
53+
def set_pagination_key(self, pagination_key):
54+
if pagination_key:
55+
self.add_query_param("pagination_key", pagination_key)

0 commit comments

Comments
 (0)