Skip to content

Commit 12bcf77

Browse files
committed
Merge branch 'feat_funding' into 'master'
Feat funding See merge request server/openapi/openapi-python-sdk!229
2 parents 9fbe615 + c3612b7 commit 12bcf77

9 files changed

Lines changed: 165 additions & 55 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 3.3.1 (2024-12-31)
2+
### New
3+
- `TradeClient.get_funding_history` 出入金历史查询
4+
### Mod
5+
- `QuoteClient.get_bars` 增加 `trade_session` 参数
6+
17
## 3.3.0 (2024-12-17)
28
### New
39
- `QuoteClient.get_stock_fundamental` 获取股票基础数据, 如 ROE, PB

tigeropen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
55
@author: gaoan
66
"""
7-
__VERSION__ = '3.3.0'
7+
__VERSION__ = '3.3.1'

tigeropen/common/consts/service_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
CANCEL_SEGMENT_FUND = "cancel_segment_fund"
3333
PLACE_FOREX_ORDER = "place_forex_order"
3434
ESTIMATE_TRADABLE_QUANTITY = "estimate_tradable_quantity"
35+
TRANSFER_FUND = "transfer_fund"
3536

3637
USER_LICENSE = "user_license"
3738

tigeropen/quote/quote_client.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
OPTION_KLINE, OPTION_TRADE_TICK, FUTURE_KLINE, FUTURE_TICK, FUTURE_CONTRACT_BY_EXCHANGE_CODE, \
2525
FUTURE_TRADING_DATE, QUOTE_SHORTABLE_STOCKS, FUTURE_REAL_TIME_QUOTE, \
2626
FUTURE_CURRENT_CONTRACT, QUOTE_REAL_TIME, QUOTE_STOCK_TRADE, FINANCIAL_DAILY, FINANCIAL_REPORT, CORPORATE_ACTION, \
27-
QUOTE_DEPTH, INDUSTRY_LIST, INDUSTRY_STOCKS, STOCK_INDUSTRY, STOCK_DETAIL
27+
QUOTE_DEPTH, INDUSTRY_LIST, INDUSTRY_STOCKS, STOCK_INDUSTRY, STOCK_DETAIL, FUTURE_CONTINUOUS_CONTRACTS
2828
from tigeropen.common.exceptions import ApiException
2929
from tigeropen.common.request import OpenApiRequest
3030
from tigeropen.common.util.common_utils import eastern, get_enum_value, date_str_to_timestamp
@@ -370,7 +370,7 @@ def get_stock_details(self, symbols, lang=None):
370370
raise ApiException(response.code, response.message)
371371
return None
372372

373-
def get_timeline(self, symbols, include_hour_trading=False, begin_time=-1, lang=None, **kwargs):
373+
def get_timeline(self, symbols, include_hour_trading=False, begin_time=-1, lang=None, trade_session=None, **kwargs):
374374
"""
375375
获取当日分时数据
376376
:param symbols: 股票代号列表
@@ -392,6 +392,7 @@ def get_timeline(self, symbols, include_hour_trading=False, begin_time=-1, lang=
392392
params.include_hour_trading = include_hour_trading
393393
params.begin_time = begin_time
394394
params.lang = get_enum_value(lang) if lang else get_enum_value(self._lang)
395+
params.trade_session = get_enum_value(trade_session)
395396
if 'version' in kwargs:
396397
params.version = kwargs.get('version')
397398
else:
@@ -435,7 +436,7 @@ def get_timeline_history(self, symbols, date, right=QuoteRight.BR):
435436
raise ApiException(response.code, response.message)
436437

437438
def get_bars(self, symbols, period=BarPeriod.DAY, begin_time=-1, end_time=-1, right=QuoteRight.BR, limit=251,
438-
lang=None, page_token=None):
439+
lang=None, page_token=None, trade_session=None):
439440
"""
440441
获取K线数据
441442
:param symbols: 股票代号列表
@@ -465,7 +466,7 @@ def get_bars(self, symbols, period=BarPeriod.DAY, begin_time=-1, end_time=-1, ri
465466
params.limit = limit
466467
params.lang = get_enum_value(lang) if lang else get_enum_value(self._lang)
467468
params.page_token = page_token if len(params.symbols) == 1 else None
468-
469+
params.trade_session = get_enum_value(trade_session)
469470
request = OpenApiRequest(KLINE, biz_model=params)
470471
response_content = self.__fetch_data(request)
471472
if response_content:
@@ -477,7 +478,7 @@ def get_bars(self, symbols, period=BarPeriod.DAY, begin_time=-1, end_time=-1, ri
477478
raise ApiException(response.code, response.message)
478479

479480
def get_bars_by_page(self, symbol, period=BarPeriod.DAY, begin_time=-1, end_time=-1, total=10000, page_size=1000,
480-
right=QuoteRight.BR, time_interval=2, lang=None):
481+
right=QuoteRight.BR, time_interval=2, lang=None, trade_session=None):
481482
"""
482483
request bats by page
483484
:param symbol: symbol of stock.
@@ -504,7 +505,7 @@ def get_bars_by_page(self, symbol, period=BarPeriod.DAY, begin_time=-1, end_time
504505
page_size = total - current
505506
current += page_size
506507
bars = self.get_bars(symbols=symbol, period=period, begin_time=begin_time, end_time=end_time, right=right,
507-
limit=page_size, lang=lang, page_token=next_page_token)
508+
limit=page_size, lang=lang, trade_session=trade_session, page_token=next_page_token)
508509
if bars.empty:
509510
result_df = bars
510511
break
@@ -1046,6 +1047,31 @@ def get_future_contract(self, contract_code, lang=None):
10461047
raise ApiException(response.code, response.message)
10471048
return None
10481049

1050+
def get_future_continuous_contracts(self, type_=None, lang=None):
1051+
"""
1052+
Get Future Continuous Contracts
1053+
:param type_: 'CL'
1054+
:param lang: zh_CN,zh_TW,en_US
1055+
:return: pandas.DataFrame
1056+
contract_code continuous contract_month currency display_multiplier exchange exchange_code first_notice_date last_bidding_close_time last_trading_date min_tick multiplier name symbol trade type
1057+
0 CLmain False USD 1 NYMEX NYMEX 0 0.01 1000.0 WTI原油主连 CL True CL
1058+
1059+
"""
1060+
params = FutureContractParams()
1061+
params.type = type_
1062+
params.lang = get_enum_value(lang) if lang else get_enum_value(self._lang)
1063+
1064+
request = OpenApiRequest(FUTURE_CONTINUOUS_CONTRACTS, biz_model=params)
1065+
response_content = self.__fetch_data(request)
1066+
if response_content:
1067+
response = FutureContractResponse()
1068+
response.parse_response_content(response_content, skip_main=False)
1069+
if response.is_success():
1070+
return response.contracts
1071+
else:
1072+
raise ApiException(response.code, response.message)
1073+
return None
1074+
10491075
def get_future_trading_times(self, identifier, trading_date=None):
10501076
"""
10511077
查询指定期货合约的交易时间
@@ -1859,4 +1885,6 @@ def get_stock_fundamental(self, symbols, market):
18591885
if response.is_success():
18601886
return response.result
18611887
else:
1862-
raise ApiException(response.code, response.message)
1888+
raise ApiException(response.code, response.message)
1889+
1890+

tigeropen/quote/response/future_contract_response.py

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,38 @@
77
import pandas as pd
88

99
from tigeropen.common.response import TigerResponse
10+
from tigeropen.common.util import string_utils
1011

11-
COLUMNS = ['contract_code', 'symbol', 'type', 'name', 'contract_month', 'multiplier', 'exchange', 'exchange_code', 'currency',
12-
'first_notice_date', 'last_bidding_close_time', 'last_trading_date', 'trade', 'continuous', 'min_tick']
13-
CONTRACT_FIELD_MAPPINGS = {'contractCode': 'contract_code', 'exchangeCode': 'exchange_code', 'ibCode': 'symbol',
14-
'contractMonth': 'contract_month', 'firstNoticeDate': 'first_notice_date',
15-
'lastBiddingCloseTime': 'last_bidding_close_time', 'lastTradingDate': 'last_trading_date',
16-
'minTick': 'min_tick'}
12+
CONTRACT_FIELD_MAPPINGS = {'ibCode': 'symbol'}
13+
MAIN_CONTRACT_CODE_SUFFIX = 'main'
14+
CONTRACT_CODE_COLUMN = 'contract_code'
1715

1816

1917
class FutureContractResponse(TigerResponse):
2018
def __init__(self):
2119
super(FutureContractResponse, self).__init__()
22-
self.contracts = []
20+
self.contracts = pd.DataFrame()
2321
self._is_success = None
2422

25-
def parse_response_content(self, response_content):
23+
def parse_response_content(self, response_content, skip_main=True):
2624
response = super(FutureContractResponse, self).parse_response_content(response_content)
2725
if 'is_success' in response:
2826
self._is_success = response['is_success']
2927

3028
if self.data:
31-
contract_items = []
3229
if isinstance(self.data, list):
33-
for item in self.data:
34-
item_values = self.parse_contract(item)
35-
contract_code = item_values.get('contract_code')
36-
if contract_code is None or contract_code.endswith('main'):
37-
continue
38-
contract_items.append([item_values.get(tag) for tag in COLUMNS])
30+
self.contracts = pd.DataFrame(self.data)
3931
elif isinstance(self.data, dict):
40-
item_values = self.parse_contract(self.data)
41-
contract_code = item_values.get('contract_code')
42-
if contract_code and not contract_code.endswith('main'):
43-
contract_items.append([item_values.get(tag) for tag in COLUMNS])
44-
45-
self.contracts = pd.DataFrame(contract_items, columns=COLUMNS)
46-
47-
@staticmethod
48-
def parse_contract(item):
49-
item_values = dict()
50-
for key, value in item.items():
51-
if value is None:
52-
continue
53-
if key in ('lastBiddingCloseTime', 'firstNoticeDate') and (value == 0 or value == ''):
54-
continue
55-
tag = CONTRACT_FIELD_MAPPINGS[key] if key in CONTRACT_FIELD_MAPPINGS else key
56-
item_values[tag] = value
57-
return item_values
32+
self.contracts = pd.DataFrame([self.data])
33+
34+
column_map = dict()
35+
for key in self.contracts.columns:
36+
column_map[key] = CONTRACT_FIELD_MAPPINGS.get(key, string_utils.camel_to_underline(key))
37+
38+
self.contracts = self.contracts.rename(columns=column_map)
39+
40+
# 重新排列列,将 contract_code 作为第一列,其余列按字母排序
41+
remaining_columns = sorted([col for col in self.contracts.columns if col != CONTRACT_CODE_COLUMN])
42+
all_columns = [CONTRACT_CODE_COLUMN] + remaining_columns
43+
self.contracts = self.contracts.reindex(columns=all_columns)
44+

tigeropen/quote/response/option_expirations_response.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@
66
"""
77
import pandas as pd
88
from tigeropen.common.response import TigerResponse
9-
9+
from tigeropen.common.util.string_utils import camel_to_underline
10+
FIELD_MAP = {
11+
'dates': 'date',
12+
'timestamps': 'timestamp',
13+
'periodTags': 'period_tag',
14+
'optionSymbols': 'option_symbol'
15+
}
1016

1117
class OptionExpirationsResponse(TigerResponse):
1218
def __init__(self):
1319
super(OptionExpirationsResponse, self).__init__()
14-
self.expirations = None
20+
self.expirations = pd.DataFrame()
1521
self._is_success = None
1622

1723
def parse_response_content(self, response_content):
@@ -20,16 +26,9 @@ def parse_response_content(self, response_content):
2026
self._is_success = response['is_success']
2127

2228
if self.data and isinstance(self.data, list):
23-
self.expirations = pd.DataFrame()
2429
for item in self.data:
25-
symbol = item.get('symbol')
26-
dates = item.get('dates')
2730
item.pop('count', None)
28-
29-
if symbol and dates:
30-
self.expirations = pd.concat([self.expirations, pd.DataFrame(item)])
31-
32-
self.expirations.rename(
33-
columns={'dates': 'date', 'timestamps': 'timestamp',
34-
'periodTags': 'period_tag'}, inplace=True)
31+
self.expirations = pd.concat([self.expirations, pd.DataFrame(item)])
32+
column_map = {col: FIELD_MAP.get(col, camel_to_underline(col)) for col in self.expirations.columns.to_list()}
33+
self.expirations.rename(columns=column_map, inplace=True)
3534
self.expirations.reset_index(inplace=True, drop=True)

tigeropen/trade/request/model.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,3 +1440,44 @@ def to_openapi_dict(self):
14401440
if self.stop_price:
14411441
params['stop_price'] = self.stop_price
14421442
return params
1443+
1444+
class FundingHistoryParams(BaseParams):
1445+
def __init__(self):
1446+
super().__init__()
1447+
self._account = None
1448+
self._secret_key = None
1449+
self._seg_type = None
1450+
1451+
@property
1452+
def account(self):
1453+
return self._account
1454+
1455+
@account.setter
1456+
def account(self, value):
1457+
self._account = value
1458+
1459+
@property
1460+
def secret_key(self):
1461+
return self._secret_key
1462+
1463+
@secret_key.setter
1464+
def secret_key(self, value):
1465+
self._secret_key = value
1466+
1467+
@property
1468+
def seg_type(self):
1469+
return self._seg_type
1470+
1471+
@seg_type.setter
1472+
def seg_type(self, value):
1473+
self._seg_type = value
1474+
1475+
def to_openapi_dict(self):
1476+
params = super().to_openapi_dict()
1477+
if self.account:
1478+
params['account'] = self.account
1479+
if self.secret_key:
1480+
params['secret_key'] = self.secret_key
1481+
if self.seg_type:
1482+
params['seg_type'] = self.seg_type
1483+
return params
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Created on 2024/03/21
4+
"""
5+
import pandas as pd
6+
7+
from tigeropen.common.response import TigerResponse
8+
from tigeropen.common.util.string_utils import camel_to_underline
9+
10+
11+
class FundingHistoryResponse(TigerResponse):
12+
def __init__(self):
13+
super(FundingHistoryResponse, self).__init__()
14+
self.data = None
15+
self._is_success = None
16+
17+
def parse_response_content(self, response_content):
18+
response = super(FundingHistoryResponse, self).parse_response_content(response_content)
19+
if 'is_success' in response:
20+
self._is_success = response['is_success']
21+
22+
if self.data:
23+
if isinstance(self.data, list):
24+
# 将驼峰命名转换为下划线命名
25+
items = [{camel_to_underline(k): v for k, v in item.items()} for item in self.data]
26+
self.data = pd.DataFrame(items)
27+
else:
28+
# 单个记录的情况
29+
items = {camel_to_underline(k): v for k, v in self.data.items()}
30+
self.data = pd.DataFrame([items])

tigeropen/trade/trade_client.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from tigeropen.common.consts import THREAD_LOCAL, SecurityType, Market, Currency, Language, OPEN_API_SERVICE_VERSION_V3
1010
from tigeropen.common.consts.service_types import CONTRACTS, ACCOUNTS, POSITIONS, ASSETS, ORDERS, ORDER_NO, \
1111
CANCEL_ORDER, MODIFY_ORDER, PLACE_ORDER, ACTIVE_ORDERS, INACTIVE_ORDERS, FILLED_ORDERS, CONTRACT, PREVIEW_ORDER, \
12-
PRIME_ASSETS, ORDER_TRANSACTIONS, QUOTE_CONTRACT, ANALYTICS_ASSET, SEGMENT_FUND_AVAILABLE, SEGMENT_FUND_HISTORY, \
12+
PRIME_ASSETS, ORDER_TRANSACTIONS, QUOTE_CONTRACT, ANALYTICS_ASSET, SEGMENT_FUND_AVAILABLE, SEGMENT_FUND_HISTORY, TRANSFER_FUND, \
1313
TRANSFER_SEGMENT_FUND, CANCEL_SEGMENT_FUND, PLACE_FOREX_ORDER, ESTIMATE_TRADABLE_QUANTITY
1414
from tigeropen.common.exceptions import ApiException
1515
from tigeropen.common.util.common_utils import get_enum_value, date_str_to_timestamp
@@ -19,7 +19,7 @@
1919
from tigeropen.trade.domain.order import Order
2020
from tigeropen.trade.request.model import ContractParams, AccountsParams, AssetParams, PositionParams, OrdersParams, \
2121
OrderParams, PlaceModifyOrderParams, CancelOrderParams, TransactionsParams, AnalyticsAssetParams, SegmentFundParams, \
22-
ForexTradeOrderParams, EstimateTradableQuantityModel
22+
ForexTradeOrderParams, EstimateTradableQuantityModel, FundingHistoryParams
2323
from tigeropen.trade.response.account_profile_response import ProfilesResponse
2424
from tigeropen.trade.response.analytics_asset_response import AnalyticsAssetResponse
2525
from tigeropen.trade.response.assets_response import AssetsResponse
@@ -34,6 +34,7 @@
3434
SegmentFundHistoryResponse, SegmentFundCancelResponse
3535
from tigeropen.trade.response.segment_fund_response import SegmentFundTransferResponse
3636
from tigeropen.trade.response.transactions_response import TransactionsResponse
37+
from tigeropen.trade.response.funding_history_response import FundingHistoryResponse
3738

3839

3940
class TradeClient(TigerOpenClient):
@@ -898,6 +899,23 @@ def get_estimate_tradable_quantity(self, order, seg_type=None):
898899
else:
899900
raise ApiException(response.code, response.message)
900901
return None
902+
903+
def get_funding_history(self, seg_type=None):
904+
params = FundingHistoryParams()
905+
params.account = self._account
906+
params.secret_key = self._secret_key
907+
params.seg_type = get_enum_value(seg_type)
908+
params.lang = get_enum_value(self._lang)
909+
request = OpenApiRequest(TRANSFER_FUND, biz_model=params)
910+
response_content = self.__fetch_data(request)
911+
if response_content:
912+
response = FundingHistoryResponse()
913+
response.parse_response_content(response_content)
914+
if response.is_success():
915+
return response.data
916+
else:
917+
raise ApiException(response.code, response.message)
918+
return None
901919

902920
def __fetch_data(self, request):
903921
try:

0 commit comments

Comments
 (0)