DYPMS 0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dypms-0.1/LICENSE +20 -0
- dypms-0.1/PKG-INFO +14 -0
- dypms-0.1/README.md +1 -0
- dypms-0.1/pyproject.toml +21 -0
- dypms-0.1/setup.cfg +4 -0
- dypms-0.1/src/DYPMS/__init__.py +27 -0
- dypms-0.1/src/DYPMS/api/__init__.py +0 -0
- dypms-0.1/src/DYPMS/api/get_simulate_portfolio_list.py +41 -0
- dypms-0.1/src/DYPMS/api/get_simulate_portfolio_net.py +48 -0
- dypms-0.1/src/DYPMS/api/get_simulate_portfolio_performance_indicators.py +58 -0
- dypms-0.1/src/DYPMS/api/get_simulate_portfolio_position.py +169 -0
- dypms-0.1/src/DYPMS/api/get_simulate_portfolio_position_hierarchy.py +97 -0
- dypms-0.1/src/DYPMS/api/get_style_attr_trend.py +120 -0
- dypms-0.1/src/DYPMS/api/heartbeat.py +13 -0
- dypms-0.1/src/DYPMS/api/util.py +17 -0
- dypms-0.1/src/DYPMS/client.py +45 -0
- dypms-0.1/src/DYPMS/enums.py +56 -0
- dypms-0.1/src/DYPMS.egg-info/PKG-INFO +14 -0
- dypms-0.1/src/DYPMS.egg-info/SOURCES.txt +19 -0
- dypms-0.1/src/DYPMS.egg-info/dependency_links.txt +1 -0
- dypms-0.1/src/DYPMS.egg-info/top_level.txt +1 -0
dypms-0.1/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
通联数据机密
|
|
2
|
+
--------------------------------------------------------------------
|
|
3
|
+
通联数据股份公司版权所有 © 2013-$today.year
|
|
4
|
+
|
|
5
|
+
注意:本文所载所有信息均属于通联数据股份公司资产。本文所包含的知识和技术概念均属于
|
|
6
|
+
通联数据产权,并可能由中国、美国和其他国家专利或申请中的专利所覆盖,并受商业秘密或
|
|
7
|
+
版权法保护。
|
|
8
|
+
除非事先获得通联数据股份公司书面许可,严禁传播文中信息或复制本材料。
|
|
9
|
+
|
|
10
|
+
DataYes CONFIDENTIAL
|
|
11
|
+
--------------------------------------------------------------------
|
|
12
|
+
Copyright © 2013-$today.year DataYes, All Rights Reserved.
|
|
13
|
+
|
|
14
|
+
NOTICE: All information contained herein is the property of DataYes
|
|
15
|
+
Incorporated. The intellectual and technical concepts contained herein are
|
|
16
|
+
proprietary to DataYes Incorporated, and may be covered by China, U.S. and
|
|
17
|
+
Other Countries Patents, patents in process, and are protected by trade
|
|
18
|
+
secret or copyright law.
|
|
19
|
+
Dissemination of this information or reproduction of this material is
|
|
20
|
+
strictly forbidden unless prior written permission is obtained from DataYes.
|
dypms-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DYPMS
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: 通联数据PMS系统数据SDK
|
|
5
|
+
Author-email: songjiangduo <jiangduo.song@datayes.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.6
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
可用于调取DataYes!pro中的模拟组合数据
|
dypms-0.1/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
可用于调取DataYes!pro中的模拟组合数据
|
dypms-0.1/pyproject.toml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "DYPMS"
|
|
7
|
+
version = "0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "songjiangduo", email = "jiangduo.song@datayes.com" }
|
|
10
|
+
]
|
|
11
|
+
description = "通联数据PMS系统数据SDK"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.6"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICENSE"]
|
|
21
|
+
|
dypms-0.1/setup.cfg
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .client import Client
|
|
2
|
+
|
|
3
|
+
__all__ = ['Client']
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_simulate_portfolio_list(*args, **kwargs):
|
|
7
|
+
return Client.get_instance().get_simulate_portfolio_list(*args, **kwargs)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_simulate_portfolio_position(*args, **kwargs):
|
|
11
|
+
return Client.get_instance().get_simulate_portfolio_position(*args, **kwargs)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_simulate_portfolio_position_hierarchy(*args, **kwargs):
|
|
15
|
+
return Client.get_instance().get_simulate_portfolio_position_hierarchy(*args, **kwargs)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_simulate_portfolio_performance_indicators(*args, **kwargs):
|
|
19
|
+
return Client.get_instance().get_simulate_portfolio_performance_indicators(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_simulate_portfolio_net(*args, **kwargs):
|
|
23
|
+
return Client.get_instance().get_simulate_portfolio_net(*args, **kwargs)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_style_attr_trend(*args, **kwargs):
|
|
27
|
+
return Client.get_instance().get_style_attr_trend(*args, **kwargs)
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .util import get_response_json_with_check
|
|
4
|
+
|
|
5
|
+
field_mapping = {
|
|
6
|
+
'accountName': 'simulate_portfolio_name', # 产品名称
|
|
7
|
+
'accountCode': 'simulate_portfolio_id', # 产品代码
|
|
8
|
+
'openDate': 'establishment_date', # 成立日期
|
|
9
|
+
'category': 'product_type', # 账户类型作为产品类型
|
|
10
|
+
'reportingBenchmark': 'benchmark', # 参考基准
|
|
11
|
+
'user': 'creator', # 用户作为创建人
|
|
12
|
+
'netValueStartDate': 'NAV_start_date', # 净值开始日期
|
|
13
|
+
'netValueDate': 'latest_NAV_date', # 最新净值日期
|
|
14
|
+
'navFrequency': 'NAV_update_frequency' # 净值更新频率
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_simulate_portfolio_list(client,
|
|
19
|
+
simulate_portfolio_id=None):
|
|
20
|
+
|
|
21
|
+
url = f"{client.base_url}/lib/simulate/portfolio/v1/list"
|
|
22
|
+
headers = client.get_headers()
|
|
23
|
+
data = "[]"
|
|
24
|
+
if simulate_portfolio_id:
|
|
25
|
+
data = "[\"" + simulate_portfolio_id + "\"]"
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
response = requests.post(url, headers=headers, data=data)
|
|
29
|
+
r = get_response_json_with_check(response)
|
|
30
|
+
|
|
31
|
+
rows = []
|
|
32
|
+
for item in r.get('list'):
|
|
33
|
+
row = {}
|
|
34
|
+
for api_field, our_field in field_mapping.items():
|
|
35
|
+
row[our_field] = item.get(api_field, None)
|
|
36
|
+
rows.append(row)
|
|
37
|
+
|
|
38
|
+
df = pd.DataFrame(rows)
|
|
39
|
+
return df
|
|
40
|
+
except Exception as e:
|
|
41
|
+
raise e
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .util import get_response_json_with_check
|
|
4
|
+
|
|
5
|
+
field_mapping = {
|
|
6
|
+
'date': '净值日期',
|
|
7
|
+
'unitNav': '单位净值',
|
|
8
|
+
'accumNav': '累计净值',
|
|
9
|
+
'adjustNav': '复权净值'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_simulate_portfolio_net(client,
|
|
14
|
+
simulate_portfolio_id,
|
|
15
|
+
start_date,
|
|
16
|
+
end_date):
|
|
17
|
+
"""
|
|
18
|
+
查询模拟组合净值
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
client: DYPMS客户端实例
|
|
22
|
+
simulate_portfolio_id: 模拟组合代码,必传
|
|
23
|
+
start_date: 开始日期,必传
|
|
24
|
+
end_date: 结束日期,必传
|
|
25
|
+
"""
|
|
26
|
+
url = f"{client.base_url}/lib/simulate/portfolio/v1/nav"
|
|
27
|
+
headers = client.get_headers()
|
|
28
|
+
params = {
|
|
29
|
+
'accountCode': simulate_portfolio_id,
|
|
30
|
+
'startDate': start_date,
|
|
31
|
+
'endDate': end_date
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
response = requests.get(url, headers=headers, params=params)
|
|
36
|
+
r = get_response_json_with_check(response)
|
|
37
|
+
|
|
38
|
+
rows = []
|
|
39
|
+
for item in r.get('list'):
|
|
40
|
+
row = {}
|
|
41
|
+
for api_field, our_field in field_mapping.items():
|
|
42
|
+
row[our_field] = item.get(api_field, None)
|
|
43
|
+
rows.append(row)
|
|
44
|
+
|
|
45
|
+
df = pd.DataFrame(rows)
|
|
46
|
+
return df
|
|
47
|
+
except Exception as e:
|
|
48
|
+
raise e
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .util import get_response_json_with_check
|
|
4
|
+
|
|
5
|
+
field_mapping = {
|
|
6
|
+
'accountCode': 'simulate_portfolio_id',
|
|
7
|
+
'benchmark': 'benchmark',
|
|
8
|
+
'startDate': 'start_date',
|
|
9
|
+
'endDate': 'end_date',
|
|
10
|
+
'totalReturn': 'cumulative_return',
|
|
11
|
+
'activeReturn': 'active_return',
|
|
12
|
+
'latestWeekReturn': 'recent_week_return',
|
|
13
|
+
'thisWeekReturn': 'weekly_return',
|
|
14
|
+
'latestMonthReturn': 'recent_month_return',
|
|
15
|
+
'thisMonthReturn': 'monthly_return',
|
|
16
|
+
'ytdReturn': 'YTD_return',
|
|
17
|
+
'annualTotalReturn': 'annualized_total_return',
|
|
18
|
+
'annualActiveReturn': 'annualized_active_return',
|
|
19
|
+
'annualTotalRisk': 'annualized_total_risk',
|
|
20
|
+
'annualActiveRisk': 'annualized_active_risk',
|
|
21
|
+
'maxDrawdown': 'maximum_drawdown',
|
|
22
|
+
'sharpRatio': 'sharpe_ratio',
|
|
23
|
+
'infoRatio': 'information_ratio',
|
|
24
|
+
'sortinoRatio': 'sortino_ratio',
|
|
25
|
+
'calmarRatio': 'calmar_ratio'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_simulate_portfolio_performance_indicators(client,
|
|
30
|
+
simulate_portfolio_id):
|
|
31
|
+
"""
|
|
32
|
+
查询模拟组合业绩指标
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
client: DYPMS客户端实例
|
|
36
|
+
simulate_portfolio_id: 模拟组合代码,必传
|
|
37
|
+
"""
|
|
38
|
+
url = f"{client.base_url}/lib/simulate/portfolio/v1/perf"
|
|
39
|
+
headers = client.get_headers()
|
|
40
|
+
params = {'accountCode': simulate_portfolio_id}
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
response = requests.get(url, headers=headers, params=params)
|
|
44
|
+
r = get_response_json_with_check(response)
|
|
45
|
+
|
|
46
|
+
# 转换单个对象为DataFrame
|
|
47
|
+
data = r.get('data')
|
|
48
|
+
if data:
|
|
49
|
+
row = {}
|
|
50
|
+
for api_field, our_field in field_mapping.items():
|
|
51
|
+
row[our_field] = data.get(api_field, None)
|
|
52
|
+
df = pd.DataFrame([row])
|
|
53
|
+
else:
|
|
54
|
+
df = pd.DataFrame()
|
|
55
|
+
|
|
56
|
+
return df
|
|
57
|
+
except Exception as e:
|
|
58
|
+
raise e
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .util import get_response_json_with_check
|
|
4
|
+
from ..enums import PenetrateType, PositionField
|
|
5
|
+
|
|
6
|
+
default_fields = [
|
|
7
|
+
'assetCategory',
|
|
8
|
+
'date',
|
|
9
|
+
'assetName',
|
|
10
|
+
'assetCode',
|
|
11
|
+
'securityType',
|
|
12
|
+
'exchangeCd',
|
|
13
|
+
'netPrice',
|
|
14
|
+
'aiRate',
|
|
15
|
+
'chg',
|
|
16
|
+
'amount',
|
|
17
|
+
'marketValueLocal',
|
|
18
|
+
'weight',
|
|
19
|
+
'dailyProfitValueLocal',
|
|
20
|
+
'dailyProfitRate',
|
|
21
|
+
'floatingProfitValueLocal',
|
|
22
|
+
'floatingProfitRate',
|
|
23
|
+
'cumulativeProfitValueLocal',
|
|
24
|
+
'cumulativeProfitRate',
|
|
25
|
+
'totalBuyCostLocal',
|
|
26
|
+
'realizedAiLocal',
|
|
27
|
+
'realizedProfitValueLocal',
|
|
28
|
+
'channel',
|
|
29
|
+
'positionTime'
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
field_enum_mapping = {
|
|
33
|
+
PositionField.CLASS: 'assetCategory',
|
|
34
|
+
PositionField.DATE: 'date',
|
|
35
|
+
PositionField.ASSET_NAME: 'assetName',
|
|
36
|
+
PositionField.ASSET_CODE: 'assetCode',
|
|
37
|
+
PositionField.SECURITY_TYPE: 'securityType',
|
|
38
|
+
PositionField.TRADING_MARKET: 'exchangeCd',
|
|
39
|
+
PositionField.CURRENT_CLEAN_PRICE: 'netPrice',
|
|
40
|
+
PositionField.ACCRUED_INTEREST: 'aiRate',
|
|
41
|
+
PositionField.PRICE_CHANGE: 'chg',
|
|
42
|
+
PositionField.POSITION_QUANTITY: 'amount',
|
|
43
|
+
PositionField.POSITION_MARKET_VALUE: 'marketValueLocal',
|
|
44
|
+
PositionField.POSITION_WEIGHT_NET_ASSETS: 'weight',
|
|
45
|
+
PositionField.DAILY_PROFIT_LOSS: 'dailyProfitValueLocal',
|
|
46
|
+
PositionField.DAILY_PROFIT_LOSS_RATE: 'dailyProfitRate',
|
|
47
|
+
PositionField.FLOATING_PROFIT_LOSS: 'floatingProfitValueLocal',
|
|
48
|
+
PositionField.FLOATING_PROFIT_LOSS_RATE: 'floatingProfitRate',
|
|
49
|
+
PositionField.CUMULATIVE_PROFIT_LOSS: 'cumulativeProfitValueLocal',
|
|
50
|
+
PositionField.CUMULATIVE_PROFIT_LOSS_RATE: 'cumulativeProfitRate',
|
|
51
|
+
PositionField.POSITION_COST: 'totalBuyCostLocal',
|
|
52
|
+
PositionField.INTEREST_INCOME: 'realizedAiLocal',
|
|
53
|
+
PositionField.REALIZED_PROFIT_LOSS: 'realizedProfitValueLocal',
|
|
54
|
+
PositionField.MATURITY_DATE: 'dueDate',
|
|
55
|
+
PositionField.TRADING_CHANNEL: 'channel',
|
|
56
|
+
PositionField.POSITION_DIRECTION: 'direction',
|
|
57
|
+
PositionField.LATEST_PRICE: 'price',
|
|
58
|
+
PositionField.POSITION_WEIGHT_TOTAL_ASSETS: 'weightTotal',
|
|
59
|
+
PositionField.POSITION_WEIGHT_TOTAL_COST: 'weightCost',
|
|
60
|
+
PositionField.COST_PRICE: 'buyCost',
|
|
61
|
+
PositionField.AMORTIZED_COST: 'cost',
|
|
62
|
+
PositionField.VALUATION_EXCHANGE_RATE: 'fxRate',
|
|
63
|
+
PositionField.MARKET_QUOTATION_TIME: 'positionTime',
|
|
64
|
+
PositionField.POSITION_BUILDING_DATE: 'holdDate',
|
|
65
|
+
PositionField.ISSUING_ENTITY: 'partyFullName',
|
|
66
|
+
PositionField.REMAINING_MATURITY: 'yearToMaturity',
|
|
67
|
+
PositionField.BOND_RATING_AGENCY: 'nominalRatingInst',
|
|
68
|
+
PositionField.BOND_RATING: 'nominalRating',
|
|
69
|
+
PositionField.MARGIN_REQUIREMENT: 'margin',
|
|
70
|
+
PositionField.CITY: 'city',
|
|
71
|
+
PositionField.PROVINCE: 'province',
|
|
72
|
+
PositionField.ISSUER_RATING_YY: 'instRatingYY',
|
|
73
|
+
PositionField.ISSUER_RATING_DATE: 'instRatingDate',
|
|
74
|
+
PositionField.ISSUER_RATING: 'instRating',
|
|
75
|
+
PositionField.BOND_RATING_DATE: 'nominalRatingDate',
|
|
76
|
+
PositionField.ISSUER_RATING_AGENCY: 'instRatingInst'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
field_mapping = {
|
|
80
|
+
'assetCategory': 'class',
|
|
81
|
+
'date': 'date',
|
|
82
|
+
'assetName': 'asset_name',
|
|
83
|
+
'assetCode': 'asset_code',
|
|
84
|
+
'securityType': 'security_type',
|
|
85
|
+
'exchangeCd': 'trading_market',
|
|
86
|
+
'netPrice': 'current_clean_price',
|
|
87
|
+
'aiRate': 'accrued_interest',
|
|
88
|
+
'chg': 'price_change',
|
|
89
|
+
'amount': 'position_quantity',
|
|
90
|
+
'marketValueLocal': 'position_market_value',
|
|
91
|
+
'weight': 'position_weight_(net_assets)',
|
|
92
|
+
'dailyProfitValueLocal': 'daily_profit_loss',
|
|
93
|
+
'dailyProfitRate': 'daily_profit_loss_rate',
|
|
94
|
+
'floatingProfitValueLocal': 'floating_profit_loss',
|
|
95
|
+
'floatingProfitRate': 'floating_profit_loss_rate',
|
|
96
|
+
'cumulativeProfitValueLocal': 'cumulative_profit_loss',
|
|
97
|
+
'cumulativeProfitRate': 'cumulative_profit_loss_rate',
|
|
98
|
+
'totalBuyCostLocal': 'position_cost',
|
|
99
|
+
'realizedAiLocal': 'interest_income',
|
|
100
|
+
'realizedProfitValueLocal': 'realized_profit_loss',
|
|
101
|
+
'dueDate': 'maturity_date',
|
|
102
|
+
'channel': 'trading_channel',
|
|
103
|
+
'direction': 'position_direction',
|
|
104
|
+
'price': 'latest_price',
|
|
105
|
+
'weightTotal': 'position_weight_(total_assets)',
|
|
106
|
+
'weightCost': 'position_weight_(total_cost)',
|
|
107
|
+
'buyCost': 'cost_price',
|
|
108
|
+
'cost': 'amortized_cost',
|
|
109
|
+
'fxRate': 'valuation_exchange_rate',
|
|
110
|
+
'positionTime': 'market_quotation_time',
|
|
111
|
+
'holdDate': 'position_building_date',
|
|
112
|
+
'partyFullName': 'issuing_entity',
|
|
113
|
+
'yearToMaturity': 'remaining_maturity',
|
|
114
|
+
'nominalRatingInst': 'bond_rating_agency',
|
|
115
|
+
'nominalRating': 'bond_rating',
|
|
116
|
+
'margin': 'margin_requirement',
|
|
117
|
+
'city': 'city',
|
|
118
|
+
'province': 'province',
|
|
119
|
+
'instRatingYY': 'issuer_rating_(YY)',
|
|
120
|
+
'instRatingDate': 'issuer_rating_date',
|
|
121
|
+
'instRating': 'issuer_rating',
|
|
122
|
+
'nominalRatingDate': 'bond_rating_date',
|
|
123
|
+
'instRatingInst': 'issuer_rating_agency'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_simulate_portfolio_position(client,
|
|
128
|
+
simulate_portfolio_id,
|
|
129
|
+
start_date=None,
|
|
130
|
+
end_date=None,
|
|
131
|
+
penetrate_style=PenetrateType.NO_PENETRATE):
|
|
132
|
+
"""
|
|
133
|
+
查询模拟组合持仓
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
client: DYPMS客户端实例
|
|
137
|
+
simulate_portfolio_id: 模拟组合代码,必传
|
|
138
|
+
start_date: 开始日期
|
|
139
|
+
end_date: 结束日期
|
|
140
|
+
penetrate_style: 穿透方式,枚举PenetrateType
|
|
141
|
+
"""
|
|
142
|
+
url = f"{client.base_url}/lib/simulate/portfolio/v1/position"
|
|
143
|
+
headers = client.get_headers()
|
|
144
|
+
|
|
145
|
+
data = {
|
|
146
|
+
'accountCode': simulate_portfolio_id,
|
|
147
|
+
'startDate': start_date,
|
|
148
|
+
'endDate': end_date,
|
|
149
|
+
'penetrateWay': penetrate_style.name if penetrate_style else PenetrateType.NO_PENETRATE,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# 移除None值
|
|
153
|
+
data = {k: v for k, v in data.items() if v is not None}
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
response = requests.post(url, headers=headers, json=data)
|
|
157
|
+
r = get_response_json_with_check(response)
|
|
158
|
+
|
|
159
|
+
rows = []
|
|
160
|
+
for item in r.get('list'):
|
|
161
|
+
row = {}
|
|
162
|
+
for api_field, our_field in field_mapping.items():
|
|
163
|
+
row[our_field] = item.get(api_field, None)
|
|
164
|
+
rows.append(row)
|
|
165
|
+
|
|
166
|
+
df = pd.DataFrame(rows)
|
|
167
|
+
return df
|
|
168
|
+
except Exception as e:
|
|
169
|
+
raise e
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .util import get_response_json_with_check
|
|
4
|
+
|
|
5
|
+
field_mapping = {
|
|
6
|
+
'symbol': 'symbol',
|
|
7
|
+
'symbolName': 'symbol_name',
|
|
8
|
+
'securityId': 'security_id',
|
|
9
|
+
'exchangeCd': 'exchange_code',
|
|
10
|
+
'channel': 'channel',
|
|
11
|
+
'ticker': 'ticker',
|
|
12
|
+
'mdSecurityId': 'md_security_id',
|
|
13
|
+
'securityType': 'security_type',
|
|
14
|
+
'positionDate': 'position_date',
|
|
15
|
+
'direction': 'direction',
|
|
16
|
+
|
|
17
|
+
'value': 'market_value',
|
|
18
|
+
'positionValue': 'position_value',
|
|
19
|
+
'closePrice': 'close_price',
|
|
20
|
+
'amount': 'quantity',
|
|
21
|
+
'canSellQuantity': 'available_sell_quantity',
|
|
22
|
+
'totalBuyCost': 'total_buy_cost',
|
|
23
|
+
'buyCost': 'buy_cost',
|
|
24
|
+
'cost': 'holding_cost',
|
|
25
|
+
'profit': 'realized_profit',
|
|
26
|
+
'dailyProfitValue': 'daily_pnl_value',
|
|
27
|
+
'dailyProfitRate': 'daily_pnl_rate',
|
|
28
|
+
'floatingProfitValue': 'unrealized_pnl_value',
|
|
29
|
+
'floatingProfitRate': 'unrealized_pnl_rate',
|
|
30
|
+
'cumulativeProfitValue': 'cumulative_pnl_value',
|
|
31
|
+
'cumulativeProfitRate': 'cumulative_pnl_rate',
|
|
32
|
+
'cumulativeProfitRateTw': 'cumulative_pnl_rate_tw', # TW 可能指 Time-Weighted
|
|
33
|
+
'weight': 'weight',
|
|
34
|
+
'coveredQuantity': 'covered_quantity',
|
|
35
|
+
'buyCostHke': 'buy_cost_hke',
|
|
36
|
+
'costHke': 'holding_cost_hke',
|
|
37
|
+
'orgBuyCost': 'original_buy_cost',
|
|
38
|
+
'orgCost': 'original_holding_cost',
|
|
39
|
+
'margin': 'margin',
|
|
40
|
+
'dueDate': 'due_date',
|
|
41
|
+
'aiRate': 'accrued_interest_rate',
|
|
42
|
+
'netPrice': 'net_price',
|
|
43
|
+
'positionTime': 'position_time',
|
|
44
|
+
'changePct': 'price_change_pct',
|
|
45
|
+
'partialWeight': 'partial_weight',
|
|
46
|
+
'marginWeight': 'margin_weight',
|
|
47
|
+
'netPricePnlRate': 'net_price_pnl_rate',
|
|
48
|
+
'fundReportDate': 'fund_report_date',
|
|
49
|
+
'includeFofPerspective': 'include_fof_perspective',
|
|
50
|
+
'category1': 'category_level_1',
|
|
51
|
+
'category2': 'category_level_2',
|
|
52
|
+
'cumulativeDividendValue': 'cumulative_dividend_value',
|
|
53
|
+
'freezeAllotCash': 'frozen_allotment_cash',
|
|
54
|
+
'receivableShareAmount': 'receivable_shares',
|
|
55
|
+
'receivableDividendValue': 'receivable_dividend_value',
|
|
56
|
+
'informationId': 'information_id',
|
|
57
|
+
'positionReason': 'position_reason',
|
|
58
|
+
'firstHoldingDate': 'first_holding_date',
|
|
59
|
+
'transCurrCd': 'transaction_currency_code',
|
|
60
|
+
'fxRate': 'exchange_rate',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_simulate_portfolio_position_hierarchy(client,
|
|
65
|
+
simulate_portfolio_id,
|
|
66
|
+
date):
|
|
67
|
+
"""
|
|
68
|
+
查询模拟组合持仓平铺
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
client: DYPMS客户端实例
|
|
72
|
+
simulate_portfolio_id: 模拟组合代码,必传
|
|
73
|
+
date: 特定日期,必传
|
|
74
|
+
"""
|
|
75
|
+
url = f"{client.base_url}/lib/simulate/portfolio/v1/positionHierarchy"
|
|
76
|
+
headers = client.get_headers()
|
|
77
|
+
|
|
78
|
+
data = {
|
|
79
|
+
'accountCode': simulate_portfolio_id,
|
|
80
|
+
'date': date
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
response = requests.post(url, headers=headers, json=data)
|
|
85
|
+
r = get_response_json_with_check(response)
|
|
86
|
+
|
|
87
|
+
rows = []
|
|
88
|
+
for item in r.get('list'):
|
|
89
|
+
row = {}
|
|
90
|
+
for api_field, our_field in field_mapping.items():
|
|
91
|
+
row[our_field] = item.get(api_field, None)
|
|
92
|
+
rows.append(row)
|
|
93
|
+
|
|
94
|
+
df = pd.DataFrame(rows)
|
|
95
|
+
return df
|
|
96
|
+
except Exception as e:
|
|
97
|
+
raise e
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from .util import get_response_json_with_check2
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
field_mapping = {
|
|
8
|
+
'date': 'date',
|
|
9
|
+
'name': 'style_factor',
|
|
10
|
+
'value': 'value' # 这里需要根据实际返回的字段名进行调整
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_style_attr_trend(client, simulate_portfolio_id):
|
|
15
|
+
"""
|
|
16
|
+
获取风格归因趋势
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
client: DYPMS客户端实例
|
|
20
|
+
simulate_portfolio_id: 组合代码,必传
|
|
21
|
+
"""
|
|
22
|
+
# 1. 调用计算接口
|
|
23
|
+
calc_url = f"{client.base_url}/lib/simulate/portfolio/v1/styleAttrTrend"
|
|
24
|
+
headers = client.get_headers()
|
|
25
|
+
params = {
|
|
26
|
+
'accountCode': simulate_portfolio_id
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# 发送计算请求
|
|
31
|
+
response = requests.get(calc_url, headers=headers, params=params)
|
|
32
|
+
calc_result = get_response_json_with_check2(response)
|
|
33
|
+
|
|
34
|
+
# 获取计算任务ID
|
|
35
|
+
task_id = calc_result.get('id')
|
|
36
|
+
if not task_id:
|
|
37
|
+
raise Exception("计算接口未返回有效的任务ID")
|
|
38
|
+
|
|
39
|
+
# 2. 轮询接口,每隔3秒一次,最多轮询10分钟
|
|
40
|
+
polling_url = f"{client.base_url}/lib/common/v1/polling"
|
|
41
|
+
max_polling_time = 10 * 60 # 10分钟
|
|
42
|
+
polling_interval = 3 # 3秒
|
|
43
|
+
start_time = time.time()
|
|
44
|
+
|
|
45
|
+
while True:
|
|
46
|
+
# 检查是否超时
|
|
47
|
+
elapsed_time = time.time() - start_time
|
|
48
|
+
if elapsed_time > max_polling_time:
|
|
49
|
+
raise Exception(f"轮询超时,超过{max_polling_time}秒")
|
|
50
|
+
|
|
51
|
+
# 发送轮询请求
|
|
52
|
+
polling_params = {'id': task_id}
|
|
53
|
+
polling_response = requests.get(polling_url, headers=headers, params=polling_params)
|
|
54
|
+
polling_result = get_response_json_with_check2(polling_response)
|
|
55
|
+
|
|
56
|
+
# 检查轮询结果
|
|
57
|
+
if polling_result.get('error'):
|
|
58
|
+
raise Exception(f"计算失败: {polling_result.get('errorMsg', '未知错误')}")
|
|
59
|
+
|
|
60
|
+
if polling_result.get('complete'):
|
|
61
|
+
final_result = polling_result.get('finalResult')
|
|
62
|
+
if not final_result:
|
|
63
|
+
raise Exception("计算完成但未返回结果数据")
|
|
64
|
+
|
|
65
|
+
# 3. 解析结果并转换为DataFrame
|
|
66
|
+
# 初始化数据存储结构
|
|
67
|
+
data_by_date_factor = {}
|
|
68
|
+
|
|
69
|
+
# 定义数据类型与字段的映射关系
|
|
70
|
+
data_mappings = [
|
|
71
|
+
('portfolioReturn', 'accumulated_style_return'),
|
|
72
|
+
('activeReturn', 'active_accumulated_style_return'),
|
|
73
|
+
('portfolioWeight', 'style_exposure'), # weight就是暴露
|
|
74
|
+
('activeWeight', 'active_style_exposure') # weight就是暴露
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# 处理所有数据类型
|
|
78
|
+
for data_key, field_name in data_mappings:
|
|
79
|
+
data_list = final_result.get(data_key, [])
|
|
80
|
+
for item in data_list:
|
|
81
|
+
date = item.get('date')
|
|
82
|
+
factor_name = item.get('name')
|
|
83
|
+
factor_type = item.get('factorType')
|
|
84
|
+
value = item.get('value')
|
|
85
|
+
|
|
86
|
+
# 只处理风格因子
|
|
87
|
+
if factor_type != 'style':
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
# 确保数据结构存在
|
|
91
|
+
key = (date, factor_name)
|
|
92
|
+
if key not in data_by_date_factor:
|
|
93
|
+
data_by_date_factor[key] = {
|
|
94
|
+
'date': date,
|
|
95
|
+
'style_factor': factor_name,
|
|
96
|
+
'style_exposure': 0.0,
|
|
97
|
+
'active_style_exposure': 0.0,
|
|
98
|
+
'accumulated_style_return': 0.0,
|
|
99
|
+
'active_accumulated_style_return': 0.0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# 设置对应字段的值
|
|
103
|
+
data_by_date_factor[key][field_name] = value
|
|
104
|
+
|
|
105
|
+
# 转换为数据行列表
|
|
106
|
+
rows = list(data_by_date_factor.values())
|
|
107
|
+
|
|
108
|
+
# 转换为DataFrame
|
|
109
|
+
df = pd.DataFrame(rows)
|
|
110
|
+
|
|
111
|
+
# 按日期降序,因子名称降序排序
|
|
112
|
+
df = df.sort_values(by=['date', 'style_factor'], ascending=[False, False])
|
|
113
|
+
|
|
114
|
+
return df
|
|
115
|
+
|
|
116
|
+
# 等待指定时间后继续轮询
|
|
117
|
+
time.sleep(polling_interval)
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
raise e
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from .util import get_response_json_with_check
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def heartbeat(client):
|
|
6
|
+
|
|
7
|
+
url = f"{client.base_url}/lib/monitor/v1/heartbeat"
|
|
8
|
+
headers = client.get_headers()
|
|
9
|
+
try:
|
|
10
|
+
response = requests.get(url, headers=headers)
|
|
11
|
+
get_response_json_with_check(response)
|
|
12
|
+
except Exception as e:
|
|
13
|
+
raise e
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
def get_response_json_with_check(response):
|
|
3
|
+
if response.status_code != 200:
|
|
4
|
+
response.raise_for_status()
|
|
5
|
+
r = response.json()
|
|
6
|
+
if r.get('code') == -403:
|
|
7
|
+
raise Exception("token认证失败")
|
|
8
|
+
if r.get('code') != 'S00000':
|
|
9
|
+
raise Exception(r.get('message'))
|
|
10
|
+
return r
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 仅校验http状态码,不校验业务状态码(适用于比较老的接口)
|
|
14
|
+
def get_response_json_with_check2(response):
|
|
15
|
+
if response.status_code != 200:
|
|
16
|
+
response.raise_for_status()
|
|
17
|
+
return response.json()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from .api.heartbeat import heartbeat
|
|
2
|
+
from .api.get_simulate_portfolio_list import get_simulate_portfolio_list
|
|
3
|
+
from .api.get_simulate_portfolio_position import get_simulate_portfolio_position
|
|
4
|
+
from .api.get_simulate_portfolio_position_hierarchy import get_simulate_portfolio_position_hierarchy
|
|
5
|
+
from .api.get_simulate_portfolio_performance_indicators import get_simulate_portfolio_performance_indicators
|
|
6
|
+
from .api.get_simulate_portfolio_net import get_simulate_portfolio_net
|
|
7
|
+
from .api.get_style_attr_trend import get_style_attr_trend
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Client:
|
|
11
|
+
|
|
12
|
+
_instance = None
|
|
13
|
+
|
|
14
|
+
def __init__(self, token='', env='prd'):
|
|
15
|
+
self.token = token
|
|
16
|
+
if env == 'prd':
|
|
17
|
+
self.base_url = "https://gw.datayes.com/aladdin_mof"
|
|
18
|
+
elif env == 'qa':
|
|
19
|
+
self.base_url = "https://gw.datayes-stg.com/mom_aladdin_qa"
|
|
20
|
+
elif env == 'stg':
|
|
21
|
+
self.base_url = "https://gw.datayes-stg.com/mom_aladdin_stg"
|
|
22
|
+
else:
|
|
23
|
+
raise ValueError("error env")
|
|
24
|
+
heartbeat(self)
|
|
25
|
+
Client._instance = self
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def get_instance():
|
|
29
|
+
if Client._instance is None:
|
|
30
|
+
raise RuntimeError("Client未初始化,请先实例化Client")
|
|
31
|
+
return Client._instance
|
|
32
|
+
|
|
33
|
+
def get_headers(self):
|
|
34
|
+
return {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
'Authorization': f'Bearer {self.token}'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Client.get_simulate_portfolio_list = get_simulate_portfolio_list
|
|
41
|
+
Client.get_simulate_portfolio_position = get_simulate_portfolio_position
|
|
42
|
+
Client.get_simulate_portfolio_position_hierarchy = get_simulate_portfolio_position_hierarchy
|
|
43
|
+
Client.get_simulate_portfolio_performance_indicators = get_simulate_portfolio_performance_indicators
|
|
44
|
+
Client.get_simulate_portfolio_net = get_simulate_portfolio_net
|
|
45
|
+
Client.get_style_attr_trend = get_style_attr_trend
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PenetrateType(Enum):
|
|
5
|
+
NO_PENETRATE = "不穿透"
|
|
6
|
+
FOF_QUARTER = "FOF穿透(季报/中报/年报)"
|
|
7
|
+
FOF_SEMI = "FOF穿透(中报/年报)"
|
|
8
|
+
MOM = "MOM穿透"
|
|
9
|
+
ALL = "全部穿透"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PositionField(Enum):
|
|
13
|
+
CLASS = "资产分类"
|
|
14
|
+
DATE = "日期"
|
|
15
|
+
ASSET_NAME = "资产名称"
|
|
16
|
+
ASSET_CODE = "资产代码"
|
|
17
|
+
SECURITY_TYPE = "证券品种"
|
|
18
|
+
TRADING_MARKET = "交易市场"
|
|
19
|
+
CURRENT_CLEAN_PRICE = "当前净价"
|
|
20
|
+
ACCRUED_INTEREST = "应计利息"
|
|
21
|
+
PRICE_CHANGE = "涨跌"
|
|
22
|
+
POSITION_QUANTITY = "持仓数量"
|
|
23
|
+
POSITION_MARKET_VALUE = "持仓市值"
|
|
24
|
+
POSITION_WEIGHT_NET_ASSETS = "持仓权重(净资产)"
|
|
25
|
+
DAILY_PROFIT_LOSS = "当日盈亏"
|
|
26
|
+
DAILY_PROFIT_LOSS_RATE = "当日盈亏率"
|
|
27
|
+
FLOATING_PROFIT_LOSS = "浮动盈亏"
|
|
28
|
+
FLOATING_PROFIT_LOSS_RATE = "浮动盈亏率"
|
|
29
|
+
CUMULATIVE_PROFIT_LOSS = "累计盈亏"
|
|
30
|
+
CUMULATIVE_PROFIT_LOSS_RATE = "累计盈亏率"
|
|
31
|
+
POSITION_COST = "持仓成本"
|
|
32
|
+
INTEREST_INCOME = "利息收入"
|
|
33
|
+
REALIZED_PROFIT_LOSS = "已实现盈亏"
|
|
34
|
+
MATURITY_DATE = "到期日"
|
|
35
|
+
TRADING_CHANNEL = "交易通道"
|
|
36
|
+
POSITION_DIRECTION = "持仓方向"
|
|
37
|
+
LATEST_PRICE = "最新价"
|
|
38
|
+
POSITION_WEIGHT_TOTAL_ASSETS = "持仓权重(总资产)"
|
|
39
|
+
POSITION_WEIGHT_TOTAL_COST = "持仓权重(总成本)"
|
|
40
|
+
COST_PRICE = "成本价格"
|
|
41
|
+
AMORTIZED_COST = "摊薄成本"
|
|
42
|
+
VALUATION_EXCHANGE_RATE = "估值汇率"
|
|
43
|
+
MARKET_QUOTATION_TIME = "行情时间"
|
|
44
|
+
POSITION_BUILDING_DATE = "建仓日期"
|
|
45
|
+
ISSUING_ENTITY = "发行主体"
|
|
46
|
+
REMAINING_MATURITY = "剩余期限"
|
|
47
|
+
BOND_RATING_AGENCY = "债项评级机构"
|
|
48
|
+
BOND_RATING = "债项评级"
|
|
49
|
+
MARGIN_REQUIREMENT = "占用保证金"
|
|
50
|
+
CITY = "城市"
|
|
51
|
+
PROVINCE = "省份"
|
|
52
|
+
ISSUER_RATING_YY = "主体评级(YY)"
|
|
53
|
+
ISSUER_RATING_DATE = "主体评级日期"
|
|
54
|
+
ISSUER_RATING = "主体评级"
|
|
55
|
+
BOND_RATING_DATE = "债项评级日期"
|
|
56
|
+
ISSUER_RATING_AGENCY = "主体评级机构"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DYPMS
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: 通联数据PMS系统数据SDK
|
|
5
|
+
Author-email: songjiangduo <jiangduo.song@datayes.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.6
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
可用于调取DataYes!pro中的模拟组合数据
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/DYPMS/__init__.py
|
|
5
|
+
src/DYPMS/client.py
|
|
6
|
+
src/DYPMS/enums.py
|
|
7
|
+
src/DYPMS.egg-info/PKG-INFO
|
|
8
|
+
src/DYPMS.egg-info/SOURCES.txt
|
|
9
|
+
src/DYPMS.egg-info/dependency_links.txt
|
|
10
|
+
src/DYPMS.egg-info/top_level.txt
|
|
11
|
+
src/DYPMS/api/__init__.py
|
|
12
|
+
src/DYPMS/api/get_simulate_portfolio_list.py
|
|
13
|
+
src/DYPMS/api/get_simulate_portfolio_net.py
|
|
14
|
+
src/DYPMS/api/get_simulate_portfolio_performance_indicators.py
|
|
15
|
+
src/DYPMS/api/get_simulate_portfolio_position.py
|
|
16
|
+
src/DYPMS/api/get_simulate_portfolio_position_hierarchy.py
|
|
17
|
+
src/DYPMS/api/get_style_attr_trend.py
|
|
18
|
+
src/DYPMS/api/heartbeat.py
|
|
19
|
+
src/DYPMS/api/util.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DYPMS
|