pyjpx-etf 0.2.0__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.
- pyjpx_etf-0.2.0/.claude/CLAUDE.md +103 -0
- pyjpx_etf-0.2.0/.claude/settings.local.json +33 -0
- pyjpx_etf-0.2.0/.github/workflows/publish.yml +26 -0
- pyjpx_etf-0.2.0/.gitignore +16 -0
- pyjpx_etf-0.2.0/LICENSE +21 -0
- pyjpx_etf-0.2.0/PKG-INFO +122 -0
- pyjpx_etf-0.2.0/README.md +95 -0
- pyjpx_etf-0.2.0/docs/api/config.md +7 -0
- pyjpx_etf-0.2.0/docs/api/etf.md +3 -0
- pyjpx_etf-0.2.0/docs/api/exceptions.md +9 -0
- pyjpx_etf-0.2.0/docs/getting-started.md +68 -0
- pyjpx_etf-0.2.0/docs/guide/etf.md +96 -0
- pyjpx_etf-0.2.0/docs/index.md +60 -0
- pyjpx_etf-0.2.0/examples/quickstart.ipynb +87 -0
- pyjpx_etf-0.2.0/mkdocs.yml +46 -0
- pyjpx_etf-0.2.0/pyproject.toml +62 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/__init__.py +19 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/_internal/__init__.py +0 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/_internal/fees.py +129 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/_internal/fetcher.py +74 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/_internal/master.py +101 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/_internal/parser.py +99 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/cli.py +107 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/config.py +57 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/etf.py +138 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/exceptions.py +17 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/models.py +38 -0
- pyjpx_etf-0.2.0/src/pyjpx_etf/py.typed +0 -0
- pyjpx_etf-0.2.0/tests/__init__.py +0 -0
- pyjpx_etf-0.2.0/tests/conftest.py +16 -0
- pyjpx_etf-0.2.0/tests/integration/__init__.py +0 -0
- pyjpx_etf-0.2.0/tests/integration/test_etf_real.py +75 -0
- pyjpx_etf-0.2.0/tests/unit/__init__.py +0 -0
- pyjpx_etf-0.2.0/tests/unit/test_cli.py +233 -0
- pyjpx_etf-0.2.0/tests/unit/test_config.py +55 -0
- pyjpx_etf-0.2.0/tests/unit/test_etf.py +225 -0
- pyjpx_etf-0.2.0/tests/unit/test_fees.py +189 -0
- pyjpx_etf-0.2.0/tests/unit/test_fetcher.py +174 -0
- pyjpx_etf-0.2.0/tests/unit/test_master.py +147 -0
- pyjpx_etf-0.2.0/tests/unit/test_models.py +69 -0
- pyjpx_etf-0.2.0/tests/unit/test_parser.py +74 -0
- pyjpx_etf-0.2.0/uv.lock +1644 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# pyjpx-etf
|
|
2
|
+
|
|
3
|
+
A clean, beginner-friendly Python library for fetching JPX ETF portfolio composition (PCF) data.
|
|
4
|
+
|
|
5
|
+
## Quick Start Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Setup
|
|
9
|
+
uv sync
|
|
10
|
+
|
|
11
|
+
# Run unit tests
|
|
12
|
+
uv run pytest tests/unit/ -v
|
|
13
|
+
|
|
14
|
+
# Run integration tests (hits live endpoints, requires JST business hours)
|
|
15
|
+
uv run pytest tests/integration/ --integration -v
|
|
16
|
+
|
|
17
|
+
# Format & lint
|
|
18
|
+
uv run ruff format .
|
|
19
|
+
uv run ruff check .
|
|
20
|
+
|
|
21
|
+
# Serve docs
|
|
22
|
+
uv run mkdocs serve
|
|
23
|
+
|
|
24
|
+
# Build docs (strict)
|
|
25
|
+
uv run mkdocs build --strict
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/pyjpx_etf/
|
|
32
|
+
├── __init__.py # Public API: ETF class, config, exceptions, models
|
|
33
|
+
├── etf.py # ETF class (main entry, lazy-loaded) + _resolve_japanese_names()
|
|
34
|
+
├── models.py # ETFInfo, Holding frozen dataclasses
|
|
35
|
+
├── config.py # Provider URLs, timeout, delay, lang (mutable singleton, lang validated)
|
|
36
|
+
├── exceptions.py # PyJPXETFError → ETFNotFoundError, FetchError, ParseError
|
|
37
|
+
├── cli.py # CLI entry point: `etf <code|alias> [--en] [-a]`, aliases (topix, 225, sox, fang, jpsox1, jpsox2)
|
|
38
|
+
└── _internal/
|
|
39
|
+
├── fetcher.py # I/O only: HTTP GET → raw CSV text (provider fallback)
|
|
40
|
+
├── parser.py # Pure parse: CSV text → models (no I/O)
|
|
41
|
+
├── master.py # JPX master list: fetch XLS (_fetch_master_xls) + parse (_parse_master_xls), 2-tier cache
|
|
42
|
+
└── fees.py # JPX ETF fees (信託報酬): fetch HTML (_fetch_fee_html) + parse (_parse_fee_html), 2-tier cache
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Key Design Patterns
|
|
46
|
+
|
|
47
|
+
- **Fetch/Parse split**: `_internal/fetcher.py` does HTTP only, `_internal/parser.py` does CSV parsing only. `_internal/master.py` and `_internal/fees.py` also follow this split.
|
|
48
|
+
- **Lazy loading**: ETF data fetched on first `.info` or `.holdings` access
|
|
49
|
+
- **Provider fallback**: Try ICE first, then Solactive. CSV content validated (rejects HTML 200s)
|
|
50
|
+
- **Error precedence**: ETFNotFoundError only if *all* providers return 404; any server/network error → FetchError
|
|
51
|
+
- **Name resolution**: Extracted to `_resolve_japanese_names()` in `etf.py` — keeps `_load()` focused on fetch+parse
|
|
52
|
+
- **Config validation**: `config.lang` only accepts `"ja"` or `"en"`, raises `ValueError` otherwise
|
|
53
|
+
|
|
54
|
+
## Data Sources
|
|
55
|
+
|
|
56
|
+
| Provider | URL Pattern | Covers |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| ICE Data Services | `https://inav.ice.com/pcf-download/{code}.csv` | Majority of TSE ETFs |
|
|
59
|
+
| Solactive AG | `https://www.solactive.com/downloads/etfservices/tse-pcf/single/{code}.csv` | Global X Japan ETFs |
|
|
60
|
+
|
|
61
|
+
- Available 7:50–23:55 JST on business days
|
|
62
|
+
- ICE returns HTML (not 404) outside hours and for unknown codes — fetcher handles this
|
|
63
|
+
- Solactive CSVs use `\r\n` line endings — parser normalizes this
|
|
64
|
+
|
|
65
|
+
### Verified ETF Codes
|
|
66
|
+
|
|
67
|
+
| Code | Name | Provider |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| 1306 | TOPIX ETF | ICE |
|
|
70
|
+
| 1321 | Nikkei 225 ETF | ICE |
|
|
71
|
+
| 1348 | MAXIS TOPIX ETF | ICE |
|
|
72
|
+
| 2564 | Global X MSCI SuperDividend Japan ETF | Solactive |
|
|
73
|
+
| 2627 | Global X E-Commerce Japan ETF | Solactive |
|
|
74
|
+
| 2644 | Global X Japan Semiconductor ETF | Solactive |
|
|
75
|
+
| 2243 | Global X US Tech Top 20 ETF | Solactive |
|
|
76
|
+
| 200A | 日経半導体株 ETF | ICE |
|
|
77
|
+
| 316A | iShares 日経半導体株 ETF | ICE |
|
|
78
|
+
|
|
79
|
+
## API
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import pyjpx_etf as etf
|
|
83
|
+
|
|
84
|
+
e = etf.ETF("1306")
|
|
85
|
+
e.info # ETFInfo dataclass
|
|
86
|
+
e.info.name # "TOPIX ETF"
|
|
87
|
+
e.info.to_dict() # dict of all fields
|
|
88
|
+
e.nav # total fund NAV in yen (int)
|
|
89
|
+
e.fee # 0.06 (trust fee %, or None)
|
|
90
|
+
e.holdings # list[Holding]
|
|
91
|
+
e.to_dataframe() # pd.DataFrame with weights
|
|
92
|
+
|
|
93
|
+
etf.config.timeout = 60
|
|
94
|
+
etf.config.request_delay = 0.5
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Dependencies
|
|
98
|
+
|
|
99
|
+
- `requests>=2.32` — HTTP
|
|
100
|
+
- `pandas>=2.0` — DataFrame output
|
|
101
|
+
- `xlrd>=2.0` — required by `pd.read_excel` for JPX master `.xls` files
|
|
102
|
+
- `lxml>=5.0` — required by `pd.read_html` for JPX ETF fee page
|
|
103
|
+
- No Pydantic, no async
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Skill(tasks-init)",
|
|
5
|
+
"Bash(uv sync 2>&1)",
|
|
6
|
+
"Bash(uv run pytest tests/unit/ -v --tb=short 2>&1)",
|
|
7
|
+
"Bash(uv run pytest --tb=short 2>&1)",
|
|
8
|
+
"Bash(uv run mkdocs build --strict 2>&1)",
|
|
9
|
+
"Bash(uv run ruff check --select F401,F841 2>&1)",
|
|
10
|
+
"Bash(uv run pytest --tb=short -v 2>&1)",
|
|
11
|
+
"Bash(uv run python -c \"\nimport requests\n\n# Try Solactive first \\(Global X Japan\\)\nurl = 'https://www.solactive.com/downloads/etfservices/tse-pcf/single/2644.csv'\nr = requests.get\\(url, timeout=30\\)\nprint\\(f'Solactive status: {r.status_code}'\\)\nif r.status_code == 200:\n print\\('--- First 30 lines ---'\\)\n for i, line in enumerate\\(r.text.split\\('\\\\n'\\)[:30]\\):\n print\\(f'{i:3}: {repr\\(line\\)}'\\)\nelse:\n # Try ICE\n url2 = 'https://inav.ice.com/pcf-download/2644.csv'\n r2 = requests.get\\(url2, timeout=30\\)\n print\\(f'ICE status: {r2.status_code}'\\)\n if r2.status_code == 200:\n print\\('--- First 30 lines ---'\\)\n for i, line in enumerate\\(r2.text.split\\('\\\\n'\\)[:30]\\):\n print\\(f'{i:3}: {repr\\(line\\)}'\\)\n\")",
|
|
12
|
+
"Bash(uv run python -c \"\nimport requests\nurl = 'https://www.solactive.com/downloads/etfservices/tse-pcf/single/2644.csv'\nr = requests.get\\(url, timeout=30\\)\ntext = r.text\n# Show exact bytes around blank line\nidx = text.find\\('20260227'\\)\nsnippet = text[idx:idx+50]\nprint\\('Bytes around section break:', repr\\(snippet\\)\\)\n\n# Try the normalize\nnorm = text.replace\\('\\\\r\\\\n', '\\\\n'\\).strip\\(\\)\nsections = norm.split\\('\\\\n\\\\n'\\)\nprint\\(f'Sections after normalize: {len\\(sections\\)}'\\)\nif len\\(sections\\) >= 2:\n print\\(f'Section 1 lines: {sections[0].count\\(chr\\(10\\)\\)+1}'\\)\n print\\(f'Section 2 lines: {sections[1].count\\(chr\\(10\\)\\)+1}'\\)\n\")",
|
|
13
|
+
"Bash(uv run python -c \"import pyjpx_etf._internal.parser; print\\(pyjpx_etf._internal.parser.__file__\\)\")",
|
|
14
|
+
"Bash(uv run python -c \"\nimport inspect, pyjpx_etf._internal.parser as p\nsrc = inspect.getsource\\(p.parse_pcf\\)\nprint\\(src[:300]\\)\n\")",
|
|
15
|
+
"Bash(find /Users/soshimizutani/dev/pyjpx-etf/src -name \"*.pyc\" -delete && uv run pytest tests/unit/ --tb=short -v 2>&1)",
|
|
16
|
+
"Bash(find /Users/soshimizutani/dev/pyjpx-etf -name \"*.pyc\" -not -path '*/.venv/*' -delete && uv run pytest tests/integration/ --integration --tb=short -v 2>&1)",
|
|
17
|
+
"Skill(audit)",
|
|
18
|
+
"Bash(uv run mkdocs gh-deploy --strict)",
|
|
19
|
+
"Bash(uv run pytest tests/unit/ --tb=short 2>&1)",
|
|
20
|
+
"Bash(uv run etf 1306 2>&1)",
|
|
21
|
+
"Bash(uv run python -c \"\nfrom pyjpx_etf._internal.fetcher import fetch_pcf\ncsv = fetch_pcf\\('1306'\\)\n# Show header rows and first few data lines\nlines = csv.replace\\('\\\\r\\\\n', '\\\\n'\\).strip\\(\\).split\\('\\\\n'\\)\nfor line in lines[:3]:\n print\\(line\\)\nprint\\('...'\\)\nsections = csv.replace\\('\\\\r\\\\n', '\\\\n'\\).strip\\(\\).split\\('\\\\n\\\\n'\\)\nholdings_lines = sections[1].split\\('\\\\n'\\)\nfor line in holdings_lines[:5]:\n print\\(line\\)\n\" 2>&1)",
|
|
22
|
+
"Bash(uv run etf 1306 --en 2>&1)",
|
|
23
|
+
"Bash(uv run pytest tests/unit/test_master.py -v --tb=short 2>&1)",
|
|
24
|
+
"Bash(uv run pytest tests/unit/test_etf.py -v --tb=short 2>&1)",
|
|
25
|
+
"WebSearch",
|
|
26
|
+
"Bash(uv run pytest tests/unit/ -v 2>&1)",
|
|
27
|
+
"Bash(uv run etf 2042)",
|
|
28
|
+
"Bash(uv run pytest tests/unit/ --tb=short -q)",
|
|
29
|
+
"Bash(uv run ruff check --select F401,F841 src/)",
|
|
30
|
+
"WebFetch(domain:api.github.com)"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.13"
|
|
18
|
+
|
|
19
|
+
- name: Install build tools
|
|
20
|
+
run: pip install build
|
|
21
|
+
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: python -m build
|
|
24
|
+
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
pyjpx_etf-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 obichan117
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pyjpx_etf-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyjpx-etf
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A clean, beginner-friendly Python library for fetching JPX ETF portfolio composition (PCF) data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/obichan117/pyjpx-etf
|
|
6
|
+
Project-URL: Documentation, https://obichan117.github.io/pyjpx-etf
|
|
7
|
+
Project-URL: Repository, https://github.com/obichan117/pyjpx-etf
|
|
8
|
+
Author: obichan117
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: etf,finance,japan,jpx,pcf,portfolio
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: lxml>=5.0
|
|
23
|
+
Requires-Dist: pandas>=2.0
|
|
24
|
+
Requires-Dist: requests>=2.32
|
|
25
|
+
Requires-Dist: xlrd>=2.0
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# pyjpx-etf
|
|
29
|
+
|
|
30
|
+
A clean, beginner-friendly Python library for fetching JPX ETF portfolio composition (PCF) data.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install pyjpx-etf
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import pyjpx_etf as etf
|
|
42
|
+
|
|
43
|
+
e = etf.ETF("1306")
|
|
44
|
+
print(e.info.name) # "TOPIX連動型上場投資信託"
|
|
45
|
+
print(e.nav) # total fund NAV in yen
|
|
46
|
+
print(e.fee) # trust fee (%) e.g. 0.06
|
|
47
|
+
print(e.holdings[:3])
|
|
48
|
+
# [Holding(code='7203', name='トヨタ自動車', ...),
|
|
49
|
+
# Holding(code='8306', name='三菱UFJフィナンシャル・グループ', ...),
|
|
50
|
+
# Holding(code='6758', name='ソニーグループ', ...)]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## CLI
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
$ etf 1306
|
|
57
|
+
|
|
58
|
+
1306 — TOPIX連動型上場投資信託 (2026-02-27)
|
|
59
|
+
Nav: 5170億 信託報酬: 0.06%
|
|
60
|
+
|
|
61
|
+
Code Name Weight
|
|
62
|
+
───── ────────────────────────────────── ──────
|
|
63
|
+
7203 トヨタ自動車 3.7%
|
|
64
|
+
8306 三菱UFJフィナンシャル・グループ 3.3%
|
|
65
|
+
6501 日立製作所 2.4%
|
|
66
|
+
...
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Aliases
|
|
70
|
+
|
|
71
|
+
Use shorthand aliases instead of codes:
|
|
72
|
+
|
|
73
|
+
| Alias | Code | ETF |
|
|
74
|
+
|-------|------|-----|
|
|
75
|
+
| `etf topix` | 1306 | TOPIX連動型上場投資信託 |
|
|
76
|
+
| `etf 225` | 1321 | 日経225連動型上場投資信託 |
|
|
77
|
+
| `etf core30` | 1311 | TOPIX Core30連動型上場投資信託 |
|
|
78
|
+
| `etf div50` | 1489 | 日経平均高配当株50指数連動型ETF |
|
|
79
|
+
| `etf div70` | 1577 | 野村日本株高配当70連動型ETF |
|
|
80
|
+
| `etf div100` | 1698 | 上場インデックスファンド日本高配当 |
|
|
81
|
+
| `etf pbr` | 2080 | PBR1倍割れ解消推進ETF |
|
|
82
|
+
| `etf fang` | 2243 | Global X US Tech Top 20 ETF |
|
|
83
|
+
| `etf sox` | 2644 | Global X 半導体関連-日本株式 ETF |
|
|
84
|
+
| `etf jpsox1` | 200A | 日経半導体株 ETF |
|
|
85
|
+
| `etf jpsox2` | 316A | iShares 日経半導体株 ETF |
|
|
86
|
+
|
|
87
|
+
### Options
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
$ etf sox --en # English names
|
|
91
|
+
$ etf topix -a # all holdings (not just top 10)
|
|
92
|
+
$ etf 1306 -a --en # combine options
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Language Setting
|
|
96
|
+
|
|
97
|
+
Names default to Japanese. Switch to English via config or CLI flag:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import pyjpx_etf as etf
|
|
101
|
+
|
|
102
|
+
# English names
|
|
103
|
+
etf.config.lang = "en"
|
|
104
|
+
e = etf.ETF("1306")
|
|
105
|
+
print(e.info.name) # "TOPIX ETF"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
import pyjpx_etf as etf
|
|
112
|
+
|
|
113
|
+
etf.config.timeout = 60
|
|
114
|
+
etf.config.request_delay = 0.5
|
|
115
|
+
etf.config.lang = "en" # "ja" (default) or "en"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Documentation
|
|
119
|
+
|
|
120
|
+
[Full documentation](https://obichan117.github.io/pyjpx-etf)
|
|
121
|
+
|
|
122
|
+
[](https://colab.research.google.com/github/obichan117/pyjpx-etf/blob/main/examples/quickstart.ipynb)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# pyjpx-etf
|
|
2
|
+
|
|
3
|
+
A clean, beginner-friendly Python library for fetching JPX ETF portfolio composition (PCF) data.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pyjpx-etf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import pyjpx_etf as etf
|
|
15
|
+
|
|
16
|
+
e = etf.ETF("1306")
|
|
17
|
+
print(e.info.name) # "TOPIX連動型上場投資信託"
|
|
18
|
+
print(e.nav) # total fund NAV in yen
|
|
19
|
+
print(e.fee) # trust fee (%) e.g. 0.06
|
|
20
|
+
print(e.holdings[:3])
|
|
21
|
+
# [Holding(code='7203', name='トヨタ自動車', ...),
|
|
22
|
+
# Holding(code='8306', name='三菱UFJフィナンシャル・グループ', ...),
|
|
23
|
+
# Holding(code='6758', name='ソニーグループ', ...)]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## CLI
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
$ etf 1306
|
|
30
|
+
|
|
31
|
+
1306 — TOPIX連動型上場投資信託 (2026-02-27)
|
|
32
|
+
Nav: 5170億 信託報酬: 0.06%
|
|
33
|
+
|
|
34
|
+
Code Name Weight
|
|
35
|
+
───── ────────────────────────────────── ──────
|
|
36
|
+
7203 トヨタ自動車 3.7%
|
|
37
|
+
8306 三菱UFJフィナンシャル・グループ 3.3%
|
|
38
|
+
6501 日立製作所 2.4%
|
|
39
|
+
...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Aliases
|
|
43
|
+
|
|
44
|
+
Use shorthand aliases instead of codes:
|
|
45
|
+
|
|
46
|
+
| Alias | Code | ETF |
|
|
47
|
+
|-------|------|-----|
|
|
48
|
+
| `etf topix` | 1306 | TOPIX連動型上場投資信託 |
|
|
49
|
+
| `etf 225` | 1321 | 日経225連動型上場投資信託 |
|
|
50
|
+
| `etf core30` | 1311 | TOPIX Core30連動型上場投資信託 |
|
|
51
|
+
| `etf div50` | 1489 | 日経平均高配当株50指数連動型ETF |
|
|
52
|
+
| `etf div70` | 1577 | 野村日本株高配当70連動型ETF |
|
|
53
|
+
| `etf div100` | 1698 | 上場インデックスファンド日本高配当 |
|
|
54
|
+
| `etf pbr` | 2080 | PBR1倍割れ解消推進ETF |
|
|
55
|
+
| `etf fang` | 2243 | Global X US Tech Top 20 ETF |
|
|
56
|
+
| `etf sox` | 2644 | Global X 半導体関連-日本株式 ETF |
|
|
57
|
+
| `etf jpsox1` | 200A | 日経半導体株 ETF |
|
|
58
|
+
| `etf jpsox2` | 316A | iShares 日経半導体株 ETF |
|
|
59
|
+
|
|
60
|
+
### Options
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
$ etf sox --en # English names
|
|
64
|
+
$ etf topix -a # all holdings (not just top 10)
|
|
65
|
+
$ etf 1306 -a --en # combine options
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Language Setting
|
|
69
|
+
|
|
70
|
+
Names default to Japanese. Switch to English via config or CLI flag:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import pyjpx_etf as etf
|
|
74
|
+
|
|
75
|
+
# English names
|
|
76
|
+
etf.config.lang = "en"
|
|
77
|
+
e = etf.ETF("1306")
|
|
78
|
+
print(e.info.name) # "TOPIX ETF"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Configuration
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import pyjpx_etf as etf
|
|
85
|
+
|
|
86
|
+
etf.config.timeout = 60
|
|
87
|
+
etf.config.request_delay = 0.5
|
|
88
|
+
etf.config.lang = "en" # "ja" (default) or "en"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
[Full documentation](https://obichan117.github.io/pyjpx-etf)
|
|
94
|
+
|
|
95
|
+
[](https://colab.research.google.com/github/obichan117/pyjpx-etf/blob/main/examples/quickstart.ipynb)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install pyjpx-etf
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Basic Usage
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import pyjpx_etf as etf
|
|
13
|
+
|
|
14
|
+
e = etf.ETF("1306")
|
|
15
|
+
|
|
16
|
+
# ETF metadata (Japanese names by default)
|
|
17
|
+
print(e.info.name) # "TOPIX連動型上場投資信託"
|
|
18
|
+
print(e.info.cash_component) # 496973797639.0
|
|
19
|
+
print(e.info.shares_outstanding) # 8133974978
|
|
20
|
+
print(e.info.date) # datetime.date(2026, 2, 27)
|
|
21
|
+
|
|
22
|
+
# Holdings
|
|
23
|
+
for h in e.holdings[:3]:
|
|
24
|
+
print(f"{h.name}: {h.weight:.2%}")
|
|
25
|
+
# トヨタ自動車: 3.80%
|
|
26
|
+
# 三菱UFJフィナンシャル・グループ: 2.50%
|
|
27
|
+
# ソニーグループ: 2.30%
|
|
28
|
+
|
|
29
|
+
# Full DataFrame
|
|
30
|
+
df = e.to_dataframe()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Language
|
|
34
|
+
|
|
35
|
+
Names default to Japanese (`config.lang = "ja"`). Switch to English:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
etf.config.lang = "en"
|
|
39
|
+
e = etf.ETF("1306")
|
|
40
|
+
print(e.info.name) # "TOPIX ETF"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The CLI uses `--en` for English:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
$ etf 1306 --en
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Configuration
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import pyjpx_etf as etf
|
|
53
|
+
|
|
54
|
+
etf.config.timeout = 60 # HTTP timeout in seconds
|
|
55
|
+
etf.config.request_delay = 0.5 # Delay between provider retries
|
|
56
|
+
etf.config.lang = "en" # "ja" (default) or "en"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Data Sources
|
|
60
|
+
|
|
61
|
+
pyjpx-etf fetches PCF (Portfolio Composition File) CSVs from:
|
|
62
|
+
|
|
63
|
+
| Provider | Covers |
|
|
64
|
+
|---|---|
|
|
65
|
+
| ICE Data Services | Majority of TSE ETFs |
|
|
66
|
+
| Solactive AG | Global X Japan ETFs |
|
|
67
|
+
|
|
68
|
+
Data is updated daily on business days, available 7:50–23:55 JST.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# ETF Data
|
|
2
|
+
|
|
3
|
+
## The ETF Class
|
|
4
|
+
|
|
5
|
+
The `ETF` class is the main entry point. It lazily fetches data on first access.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import pyjpx_etf as etf
|
|
9
|
+
|
|
10
|
+
e = etf.ETF("1306")
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### ETF Info
|
|
14
|
+
|
|
15
|
+
Access metadata via the `info` property. Names are Japanese by default:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
e.info.code # "1306"
|
|
19
|
+
e.info.name # "TOPIX連動型上場投資信託"
|
|
20
|
+
e.info.cash_component # Fund cash component
|
|
21
|
+
e.info.shares_outstanding # Shares outstanding
|
|
22
|
+
e.info.date # Fund date as datetime.date
|
|
23
|
+
|
|
24
|
+
# Convert to dict
|
|
25
|
+
dict_info = e.info.to_dict()
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Holdings
|
|
29
|
+
|
|
30
|
+
Access constituent holdings:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
for h in e.holdings[:3]:
|
|
34
|
+
print(f"{h.code} {h.name}: {h.weight:.2%}")
|
|
35
|
+
# 7203 トヨタ自動車: 3.80%
|
|
36
|
+
# 8306 三菱UFJフィナンシャル・グループ: 2.50%
|
|
37
|
+
# 6758 ソニーグループ: 2.30%
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each `Holding` has: `code`, `name`, `isin`, `exchange`, `currency`, `shares`, `price`, `weight`.
|
|
41
|
+
|
|
42
|
+
### NAV
|
|
43
|
+
|
|
44
|
+
Total fund net asset value (cash + holdings market value) in yen:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
e.nav # 515997003139 (in yen)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Fee (信託報酬)
|
|
51
|
+
|
|
52
|
+
Trust fee (信託報酬) from the JPX ETF list page. Independent of PCF data:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
e.fee # 0.06 (means 0.06%)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Returns `None` if the fee is unavailable for the given ETF code.
|
|
59
|
+
|
|
60
|
+
### DataFrame Output
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
df = e.to_dataframe()
|
|
64
|
+
# Columns: code, name, isin, exchange, currency, shares, price, weight
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Language
|
|
68
|
+
|
|
69
|
+
Set `config.lang` before creating an `ETF` instance:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import pyjpx_etf as etf
|
|
73
|
+
|
|
74
|
+
# Japanese (default)
|
|
75
|
+
e = etf.ETF("1306")
|
|
76
|
+
e.info.name # "TOPIX連動型上場投資信託"
|
|
77
|
+
|
|
78
|
+
# English
|
|
79
|
+
etf.config.lang = "en"
|
|
80
|
+
e = etf.ETF("1306")
|
|
81
|
+
e.info.name # "TOPIX ETF"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Error Handling
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from pyjpx_etf import ETFNotFoundError, FetchError, ParseError
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
e = etf.ETF("9999")
|
|
91
|
+
_ = e.info
|
|
92
|
+
except ETFNotFoundError:
|
|
93
|
+
print("ETF not found")
|
|
94
|
+
except FetchError:
|
|
95
|
+
print("Network error")
|
|
96
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# pyjpx-etf
|
|
2
|
+
|
|
3
|
+
A clean, beginner-friendly Python library for fetching JPX ETF portfolio composition (PCF) data.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Simple API** — yfinance-style `ETF("1306")` interface
|
|
8
|
+
- **Japanese names by default** — powered by the JPX master list
|
|
9
|
+
- **No authentication** — uses free, public PCF CSV endpoints
|
|
10
|
+
- **Lightweight** — just `requests`, `pandas`, and `xlrd`
|
|
11
|
+
- **Auto provider detection** — tries ICE Data Services, then Solactive
|
|
12
|
+
|
|
13
|
+
## Quick Example
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import pyjpx_etf as etf
|
|
17
|
+
|
|
18
|
+
e = etf.ETF("1306")
|
|
19
|
+
print(e.info.name) # "TOPIX連動型上場投資信託"
|
|
20
|
+
print(e.top())
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
code name weight
|
|
25
|
+
0 7203 トヨタ自動車 3.8
|
|
26
|
+
1 8306 三菱UFJフィナンシャル・グループ 2.5
|
|
27
|
+
2 6758 ソニーグループ 2.3
|
|
28
|
+
...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### CLI
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
$ etf 1306
|
|
35
|
+
|
|
36
|
+
1306 — NEXT FUNDS TOPIX連動型上場投信 (2026-02-27)
|
|
37
|
+
|
|
38
|
+
Code Name Weight
|
|
39
|
+
───── ────────────────────────────────── ──────
|
|
40
|
+
7203 トヨタ自動車 3.7%
|
|
41
|
+
8306 三菱UFJフィナンシャル・グループ 3.3%
|
|
42
|
+
6501 日立製作所 2.4%
|
|
43
|
+
8316 三井住友フィナンシャルグループ 2.3%
|
|
44
|
+
6758 ソニーグループ 2.1%
|
|
45
|
+
8058 三菱商事 2.0%
|
|
46
|
+
8411 みずほフィナンシャルグループ 1.8%
|
|
47
|
+
8035 東京エレクトロン 1.7%
|
|
48
|
+
7011 三菱重工業 1.7%
|
|
49
|
+
6857 アドバンテスト 1.6%
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use `--en` for English names: `etf 1306 --en`
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install pyjpx-etf
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
[](https://colab.research.google.com/github/obichan117/pyjpx-etf/blob/main/examples/quickstart.ipynb)
|