ohmydata 0.0.2__tar.gz → 0.0.4__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.
- {ohmydata-0.0.2 → ohmydata-0.0.4}/CHANGELOG.md +13 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/PKG-INFO +31 -1
- {ohmydata-0.0.2 → ohmydata-0.0.4}/README.md +30 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/pyproject.toml +1 -1
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/__init__.py +1 -1
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/tushare/__init__.py +10 -0
- ohmydata-0.0.4/src/ohmydata/providers/tushare/recipes/__init__.py +27 -0
- ohmydata-0.0.4/src/ohmydata/providers/tushare/recipes/weighted_dividend_yield.py +231 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/uv.lock +1 -1
- ohmydata-0.0.2/src/ohmydata/providers/tushare/recipes/__init__.py +0 -15
- {ohmydata-0.0.2 → ohmydata-0.0.4}/.gitignore +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/__init__.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/errors.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/policy.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/provenance.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/rate_limit.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/snapshot.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/core/specs.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/__init__.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/tushare/client.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/tushare/endpoints.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/tushare/errors.py +0 -0
- {ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/tushare/recipes/etf_adjusted_bars.py +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.4 — 2026-08-01
|
|
4
|
+
|
|
5
|
+
- Added the explicit `NORMALIZE_SUPPORTED` weighted-dividend-yield coverage
|
|
6
|
+
policy. It normalizes only over finite supported weight, preserves the
|
|
7
|
+
original coverage metadata, reports a distinct formula identifier, and
|
|
8
|
+
leaves minimum-coverage acceptance to the caller.
|
|
9
|
+
|
|
10
|
+
## 0.0.3 — 2026-07-31
|
|
11
|
+
|
|
12
|
+
- Added offline weighted portfolio and index dividend-yield recipes with
|
|
13
|
+
explicit finite-coverage policies, provider-native units, and deterministic
|
|
14
|
+
formula metadata.
|
|
15
|
+
|
|
3
16
|
## Unreleased — 0.0.2
|
|
4
17
|
|
|
5
18
|
- Fixed adjusted ETF bars to tolerate Tushare factor-date supersets for the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ohmydata
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Offline-first market-data SDK scaffold
|
|
5
5
|
Author: OMD contributors
|
|
6
6
|
Requires-Python: <3.13,>=3.11
|
|
@@ -99,6 +99,36 @@ request = AdjustedEtfBarsRequest(
|
|
|
99
99
|
)
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
### Offline weighted dividend yield recipes
|
|
103
|
+
|
|
104
|
+
`build_portfolio_dividend_yield` and `build_index_dividend_yield` calculate a
|
|
105
|
+
provider-semantic weighted yield from already-downloaded Pandas frames. Portfolio
|
|
106
|
+
`mkv` is yuan; index `weight` and `daily_basic.dv_ttm` are provider percentages.
|
|
107
|
+
The returned `dividend_yield` is a decimal ratio (`sum((w_i / W) * dv_ttm_i) / 100`),
|
|
108
|
+
where `W` is the provider-native total weight.
|
|
109
|
+
Choose `DividendYieldCoveragePolicy.REQUIRE_COMPLETE` to reject missing finite
|
|
110
|
+
yield coverage, `PRESERVE_INCOMPLETE` to return `None`, or the explicitly named
|
|
111
|
+
`NORMALIZE_SUPPORTED` policy to divide only by finite supported weight while
|
|
112
|
+
still reporting the original `finite_weight_coverage`. Callers own any minimum
|
|
113
|
+
coverage threshold and must not present a normalized partial estimate as full
|
|
114
|
+
coverage. Zero supported coverage remains unknown. Inputs are not modified, and
|
|
115
|
+
dates are identity checks only: the recipe does not infer point-in-time
|
|
116
|
+
availability or report selection.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from ohmydata.providers.tushare import (
|
|
120
|
+
DividendYieldCoveragePolicy,
|
|
121
|
+
build_index_dividend_yield,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
result = build_index_dividend_yield(
|
|
125
|
+
index_weights_df,
|
|
126
|
+
daily_basic_df,
|
|
127
|
+
DividendYieldCoveragePolicy.REQUIRE_COMPLETE,
|
|
128
|
+
)
|
|
129
|
+
print(result.dividend_yield)
|
|
130
|
+
```
|
|
131
|
+
|
|
102
132
|
## Local checks
|
|
103
133
|
|
|
104
134
|
```bash
|
|
@@ -88,6 +88,36 @@ request = AdjustedEtfBarsRequest(
|
|
|
88
88
|
)
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
### Offline weighted dividend yield recipes
|
|
92
|
+
|
|
93
|
+
`build_portfolio_dividend_yield` and `build_index_dividend_yield` calculate a
|
|
94
|
+
provider-semantic weighted yield from already-downloaded Pandas frames. Portfolio
|
|
95
|
+
`mkv` is yuan; index `weight` and `daily_basic.dv_ttm` are provider percentages.
|
|
96
|
+
The returned `dividend_yield` is a decimal ratio (`sum((w_i / W) * dv_ttm_i) / 100`),
|
|
97
|
+
where `W` is the provider-native total weight.
|
|
98
|
+
Choose `DividendYieldCoveragePolicy.REQUIRE_COMPLETE` to reject missing finite
|
|
99
|
+
yield coverage, `PRESERVE_INCOMPLETE` to return `None`, or the explicitly named
|
|
100
|
+
`NORMALIZE_SUPPORTED` policy to divide only by finite supported weight while
|
|
101
|
+
still reporting the original `finite_weight_coverage`. Callers own any minimum
|
|
102
|
+
coverage threshold and must not present a normalized partial estimate as full
|
|
103
|
+
coverage. Zero supported coverage remains unknown. Inputs are not modified, and
|
|
104
|
+
dates are identity checks only: the recipe does not infer point-in-time
|
|
105
|
+
availability or report selection.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from ohmydata.providers.tushare import (
|
|
109
|
+
DividendYieldCoveragePolicy,
|
|
110
|
+
build_index_dividend_yield,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
result = build_index_dividend_yield(
|
|
114
|
+
index_weights_df,
|
|
115
|
+
daily_basic_df,
|
|
116
|
+
DividendYieldCoveragePolicy.REQUIRE_COMPLETE,
|
|
117
|
+
)
|
|
118
|
+
print(result.dividend_yield)
|
|
119
|
+
```
|
|
120
|
+
|
|
91
121
|
## Local checks
|
|
92
122
|
|
|
93
123
|
```bash
|
|
@@ -17,7 +17,12 @@ from .recipes import (
|
|
|
17
17
|
AdjustedEtfBarsRequest,
|
|
18
18
|
AdjustedEtfBarsResult,
|
|
19
19
|
AdjustmentCoveragePolicy,
|
|
20
|
+
DividendYieldCoveragePolicy,
|
|
21
|
+
DividendYieldWeightSource,
|
|
22
|
+
WeightedDividendYieldResult,
|
|
20
23
|
build_adjusted_etf_bars,
|
|
24
|
+
build_index_dividend_yield,
|
|
25
|
+
build_portfolio_dividend_yield,
|
|
21
26
|
fetch_adjusted_etf_bars,
|
|
22
27
|
)
|
|
23
28
|
|
|
@@ -26,6 +31,8 @@ __all__ = [
|
|
|
26
31
|
"AdjustedEtfBarsResult",
|
|
27
32
|
"AdjustmentCoveragePolicy",
|
|
28
33
|
"DailyBasicRequest",
|
|
34
|
+
"DividendYieldCoveragePolicy",
|
|
35
|
+
"DividendYieldWeightSource",
|
|
29
36
|
"EmptyPolicy",
|
|
30
37
|
"FundAdjustmentRequest",
|
|
31
38
|
"FundBasicRequest",
|
|
@@ -38,7 +45,10 @@ __all__ = [
|
|
|
38
45
|
"TradeCalendarRequest",
|
|
39
46
|
"TushareClient",
|
|
40
47
|
"TushareFetchResult",
|
|
48
|
+
"WeightedDividendYieldResult",
|
|
41
49
|
"build_adjusted_etf_bars",
|
|
50
|
+
"build_index_dividend_yield",
|
|
51
|
+
"build_portfolio_dividend_yield",
|
|
42
52
|
"classify_tushare_exception",
|
|
43
53
|
"fetch_adjusted_etf_bars",
|
|
44
54
|
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .etf_adjusted_bars import (
|
|
2
|
+
AdjustedEtfBarsRequest,
|
|
3
|
+
AdjustedEtfBarsResult,
|
|
4
|
+
AdjustmentCoveragePolicy,
|
|
5
|
+
build_adjusted_etf_bars,
|
|
6
|
+
fetch_adjusted_etf_bars,
|
|
7
|
+
)
|
|
8
|
+
from .weighted_dividend_yield import (
|
|
9
|
+
DividendYieldCoveragePolicy,
|
|
10
|
+
DividendYieldWeightSource,
|
|
11
|
+
WeightedDividendYieldResult,
|
|
12
|
+
build_index_dividend_yield,
|
|
13
|
+
build_portfolio_dividend_yield,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AdjustedEtfBarsRequest",
|
|
18
|
+
"AdjustedEtfBarsResult",
|
|
19
|
+
"AdjustmentCoveragePolicy",
|
|
20
|
+
"DividendYieldCoveragePolicy",
|
|
21
|
+
"DividendYieldWeightSource",
|
|
22
|
+
"WeightedDividendYieldResult",
|
|
23
|
+
"build_adjusted_etf_bars",
|
|
24
|
+
"build_index_dividend_yield",
|
|
25
|
+
"build_portfolio_dividend_yield",
|
|
26
|
+
"fetch_adjusted_etf_bars",
|
|
27
|
+
]
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Offline weighted dividend-yield recipes for Tushare-native frames."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import numbers
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from ....core import CoverageError, SchemaMismatchError
|
|
12
|
+
|
|
13
|
+
PORTFOLIO_FORMULA_IDENTIFIER = "fund_portfolio_mkv_weighted_daily_basic_dv_ttm_v1"
|
|
14
|
+
INDEX_FORMULA_IDENTIFIER = "index_weight_weighted_daily_basic_dv_ttm_v1"
|
|
15
|
+
PORTFOLIO_SUPPORTED_FORMULA_IDENTIFIER = (
|
|
16
|
+
"fund_portfolio_mkv_supported_weight_normalized_daily_basic_dv_ttm_v1"
|
|
17
|
+
)
|
|
18
|
+
INDEX_SUPPORTED_FORMULA_IDENTIFIER = (
|
|
19
|
+
"index_weight_supported_weight_normalized_daily_basic_dv_ttm_v1"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DividendYieldCoveragePolicy(str, Enum):
|
|
24
|
+
REQUIRE_COMPLETE = "REQUIRE_COMPLETE"
|
|
25
|
+
PRESERVE_INCOMPLETE = "PRESERVE_INCOMPLETE"
|
|
26
|
+
NORMALIZE_SUPPORTED = "NORMALIZE_SUPPORTED"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DividendYieldWeightSource(str, Enum):
|
|
30
|
+
FUND_PORTFOLIO = "FUND_PORTFOLIO"
|
|
31
|
+
INDEX_WEIGHT = "INDEX_WEIGHT"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class WeightedDividendYieldResult:
|
|
36
|
+
dividend_yield: float | None
|
|
37
|
+
finite_weight_coverage: float
|
|
38
|
+
provider_total_weight: float
|
|
39
|
+
provider_supported_weight: float
|
|
40
|
+
constituent_count: int
|
|
41
|
+
supported_constituent_count: int
|
|
42
|
+
weight_source: DividendYieldWeightSource
|
|
43
|
+
coverage_policy: DividendYieldCoveragePolicy
|
|
44
|
+
formula_identifier: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _pd() -> Any:
|
|
48
|
+
try:
|
|
49
|
+
import pandas as pd
|
|
50
|
+
except ImportError as exc:
|
|
51
|
+
raise ImportError("install ohmydata[tushare]") from exc
|
|
52
|
+
return pd
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _finite_real(value: Any) -> bool:
|
|
56
|
+
return (
|
|
57
|
+
isinstance(value, numbers.Real)
|
|
58
|
+
and not isinstance(value, bool) # type: ignore[reportUnnecessaryIsInstance]
|
|
59
|
+
and math.isfinite(float(value))
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _key_valid(value: Any) -> bool:
|
|
64
|
+
return isinstance(value, str) and bool(value)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _validate_frame(frame: Any, name: str, required: tuple[str, ...]) -> Any:
|
|
68
|
+
pd = _pd()
|
|
69
|
+
if not isinstance(frame, pd.DataFrame):
|
|
70
|
+
raise TypeError(f"{name} must be a Pandas DataFrame")
|
|
71
|
+
missing = [field for field in required if field not in frame.columns]
|
|
72
|
+
if missing:
|
|
73
|
+
raise SchemaMismatchError("required recipe fields are missing")
|
|
74
|
+
return frame.copy(deep=True)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _validate_daily(frame: Any) -> Any:
|
|
78
|
+
daily = _validate_frame(frame, "daily_basic_frame", ("ts_code", "trade_date", "dv_ttm"))
|
|
79
|
+
if daily.empty:
|
|
80
|
+
return daily
|
|
81
|
+
if daily["ts_code"].isna().any() or any(not _key_valid(x) for x in daily["ts_code"].tolist()):
|
|
82
|
+
raise SchemaMismatchError("invalid daily-basic symbol")
|
|
83
|
+
if daily["ts_code"].duplicated().any():
|
|
84
|
+
raise SchemaMismatchError("duplicate daily-basic symbol")
|
|
85
|
+
if daily["trade_date"].isna().any() or daily["trade_date"].nunique(dropna=False) != 1:
|
|
86
|
+
raise SchemaMismatchError("daily-basic frame must contain exactly one trade date")
|
|
87
|
+
return daily
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _validate_policy(policy: Any) -> DividendYieldCoveragePolicy:
|
|
91
|
+
if not isinstance(policy, DividendYieldCoveragePolicy): # type: ignore[reportUnnecessaryIsInstance]
|
|
92
|
+
raise TypeError("coverage_policy must DividendYieldCoveragePolicy")
|
|
93
|
+
return policy
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _calculate(
|
|
97
|
+
weights: Any,
|
|
98
|
+
daily_frame: Any,
|
|
99
|
+
policy: DividendYieldCoveragePolicy,
|
|
100
|
+
source: DividendYieldWeightSource,
|
|
101
|
+
key: str,
|
|
102
|
+
formula_identifier: str,
|
|
103
|
+
supported_formula_identifier: str,
|
|
104
|
+
weight_field: str,
|
|
105
|
+
minimum: float,
|
|
106
|
+
) -> WeightedDividendYieldResult:
|
|
107
|
+
_validate_policy(policy)
|
|
108
|
+
daily = _validate_daily(daily_frame)
|
|
109
|
+
required = (
|
|
110
|
+
("ts_code", "end_date", "symbol", "mkv")
|
|
111
|
+
if source is DividendYieldWeightSource.FUND_PORTFOLIO
|
|
112
|
+
else ("index_code", "con_code", "trade_date", "weight")
|
|
113
|
+
)
|
|
114
|
+
frame = _validate_frame(weights, "weight_frame", required)
|
|
115
|
+
if frame.empty:
|
|
116
|
+
raise CoverageError("weight frame is empty")
|
|
117
|
+
if source is DividendYieldWeightSource.FUND_PORTFOLIO:
|
|
118
|
+
if frame["ts_code"].isna().any() or frame["ts_code"].nunique(dropna=False) != 1:
|
|
119
|
+
raise SchemaMismatchError("portfolio frame must contain exactly one fund")
|
|
120
|
+
if frame["end_date"].isna().any() or frame["end_date"].nunique(dropna=False) != 1:
|
|
121
|
+
raise SchemaMismatchError("portfolio frame must contain exactly one report date")
|
|
122
|
+
if frame["symbol"].isna().any() or any(not _key_valid(x) for x in frame["symbol"].tolist()):
|
|
123
|
+
raise SchemaMismatchError("invalid portfolio symbol")
|
|
124
|
+
if frame["symbol"].duplicated().any():
|
|
125
|
+
raise SchemaMismatchError("duplicate portfolio symbol")
|
|
126
|
+
else:
|
|
127
|
+
if frame["index_code"].isna().any() or frame["index_code"].nunique(dropna=False) != 1:
|
|
128
|
+
raise SchemaMismatchError("index frame must contain exactly one index")
|
|
129
|
+
if frame["trade_date"].isna().any() or frame["trade_date"].nunique(dropna=False) != 1:
|
|
130
|
+
raise SchemaMismatchError("index frame must contain exactly one trade date")
|
|
131
|
+
if frame["con_code"].isna().any() or any(
|
|
132
|
+
not _key_valid(x) for x in frame["con_code"].tolist()
|
|
133
|
+
):
|
|
134
|
+
raise SchemaMismatchError("invalid index constituent")
|
|
135
|
+
if frame["con_code"].duplicated().any():
|
|
136
|
+
raise SchemaMismatchError("duplicate index constituent")
|
|
137
|
+
values = frame[weight_field].tolist()
|
|
138
|
+
invalid_weight = any(not _finite_real(value) or float(value) < minimum for value in values)
|
|
139
|
+
if source is DividendYieldWeightSource.FUND_PORTFOLIO:
|
|
140
|
+
invalid_weight = invalid_weight or any(
|
|
141
|
+
_finite_real(value) and float(value) <= 0 for value in values
|
|
142
|
+
)
|
|
143
|
+
if invalid_weight:
|
|
144
|
+
raise SchemaMismatchError("invalid provider weight")
|
|
145
|
+
total = float(sum(float(value) for value in values))
|
|
146
|
+
if total <= 0 or not math.isfinite(total):
|
|
147
|
+
raise CoverageError("provider total weight must be positive")
|
|
148
|
+
# A zero-weight index constituent is not coverage-relevant.
|
|
149
|
+
positive = frame.loc[frame[weight_field].astype(float) > 0, [key, weight_field]].copy()
|
|
150
|
+
lookup = {row.ts_code: row.dv_ttm for row in daily.itertuples(index=False)}
|
|
151
|
+
supported_weight = 0.0
|
|
152
|
+
supported_count = 0
|
|
153
|
+
weighted_sum = 0.0
|
|
154
|
+
for row in positive.itertuples(index=False):
|
|
155
|
+
weight = float(getattr(row, weight_field))
|
|
156
|
+
value = lookup.get(getattr(row, key))
|
|
157
|
+
if _finite_real(value):
|
|
158
|
+
supported_weight += weight
|
|
159
|
+
supported_count += 1
|
|
160
|
+
weighted_sum += (weight / total) * float(value) # type: ignore[arg-type]
|
|
161
|
+
coverage = supported_weight / total
|
|
162
|
+
complete = math.isclose(coverage, 1.0, rel_tol=0.0, abs_tol=1e-12)
|
|
163
|
+
if not complete and policy is DividendYieldCoveragePolicy.REQUIRE_COMPLETE:
|
|
164
|
+
raise CoverageError("finite dividend-yield weight coverage is incomplete")
|
|
165
|
+
if complete:
|
|
166
|
+
result_yield = weighted_sum / 100.0
|
|
167
|
+
elif policy is DividendYieldCoveragePolicy.NORMALIZE_SUPPORTED and supported_weight > 0:
|
|
168
|
+
result_yield = (weighted_sum / coverage) / 100.0
|
|
169
|
+
else:
|
|
170
|
+
result_yield = None
|
|
171
|
+
effective_formula_identifier = (
|
|
172
|
+
supported_formula_identifier
|
|
173
|
+
if policy is DividendYieldCoveragePolicy.NORMALIZE_SUPPORTED
|
|
174
|
+
else formula_identifier
|
|
175
|
+
)
|
|
176
|
+
return WeightedDividendYieldResult(
|
|
177
|
+
result_yield,
|
|
178
|
+
coverage,
|
|
179
|
+
total,
|
|
180
|
+
supported_weight,
|
|
181
|
+
len(frame),
|
|
182
|
+
supported_count,
|
|
183
|
+
source,
|
|
184
|
+
policy,
|
|
185
|
+
effective_formula_identifier,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def build_portfolio_dividend_yield(
|
|
190
|
+
weight_frame: Any,
|
|
191
|
+
daily_basic_frame: Any,
|
|
192
|
+
coverage_policy: DividendYieldCoveragePolicy,
|
|
193
|
+
) -> WeightedDividendYieldResult:
|
|
194
|
+
return _calculate(
|
|
195
|
+
weight_frame,
|
|
196
|
+
daily_basic_frame,
|
|
197
|
+
coverage_policy,
|
|
198
|
+
DividendYieldWeightSource.FUND_PORTFOLIO,
|
|
199
|
+
"symbol",
|
|
200
|
+
PORTFOLIO_FORMULA_IDENTIFIER,
|
|
201
|
+
PORTFOLIO_SUPPORTED_FORMULA_IDENTIFIER,
|
|
202
|
+
"mkv",
|
|
203
|
+
0.0,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def build_index_dividend_yield(
|
|
208
|
+
weight_frame: Any,
|
|
209
|
+
daily_basic_frame: Any,
|
|
210
|
+
coverage_policy: DividendYieldCoveragePolicy,
|
|
211
|
+
) -> WeightedDividendYieldResult:
|
|
212
|
+
return _calculate(
|
|
213
|
+
weight_frame,
|
|
214
|
+
daily_basic_frame,
|
|
215
|
+
coverage_policy,
|
|
216
|
+
DividendYieldWeightSource.INDEX_WEIGHT,
|
|
217
|
+
"con_code",
|
|
218
|
+
INDEX_FORMULA_IDENTIFIER,
|
|
219
|
+
INDEX_SUPPORTED_FORMULA_IDENTIFIER,
|
|
220
|
+
"weight",
|
|
221
|
+
0.0,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
__all__ = [
|
|
226
|
+
"DividendYieldCoveragePolicy",
|
|
227
|
+
"DividendYieldWeightSource",
|
|
228
|
+
"WeightedDividendYieldResult",
|
|
229
|
+
"build_index_dividend_yield",
|
|
230
|
+
"build_portfolio_dividend_yield",
|
|
231
|
+
]
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from .etf_adjusted_bars import (
|
|
2
|
-
AdjustedEtfBarsRequest,
|
|
3
|
-
AdjustedEtfBarsResult,
|
|
4
|
-
AdjustmentCoveragePolicy,
|
|
5
|
-
build_adjusted_etf_bars,
|
|
6
|
-
fetch_adjusted_etf_bars,
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
__all__ = [
|
|
10
|
-
"AdjustedEtfBarsRequest",
|
|
11
|
-
"AdjustedEtfBarsResult",
|
|
12
|
-
"AdjustmentCoveragePolicy",
|
|
13
|
-
"build_adjusted_etf_bars",
|
|
14
|
-
"fetch_adjusted_etf_bars",
|
|
15
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ohmydata-0.0.2 → ohmydata-0.0.4}/src/ohmydata/providers/tushare/recipes/etf_adjusted_bars.py
RENAMED
|
File without changes
|