FnGuide DataGuide 6μμ λ΄λ³΄λΈ CSV/Excel νμΌμ μλ κ°μ§νκ³ , μ μ λ κ΄κ³ν ν μ΄λΈ(CSV/Parquet)λ‘ λ³ννλ©°, λ³νλ λ°μ΄ν°λ₯Ό νλ‘κ·Έλλ° λ°©μμΌλ‘ μ½μ μ μκ² ν΄μ£Όλ Python λΌμ΄λΈλ¬λ¦¬.
2025λ 12μ, FnGuide DataGuideκ° λ²μ 5μμ 6μΌλ‘ μ λ°μ΄νΈλμμ΅λλ€. κΈ°μ‘΄μ DataGuideμμ μμ λ‘ λ°μ΄ν°λ₯Ό λ΄λ³΄λΈ λ€ Pythonμμ νμ©νλ κ³Όμ μλ μ¬λ¬ λΆνΈν¨μ΄ μμμ΅λλ€:
- λ μ§κ° 컬λΌμ νΌμ³μ§λ ꡬ쑰 -- λλ λ°μ΄ν° λ΄λ³΄λ΄κΈ° μ λ μ§κ° μ΄(column)λ‘ λμ΄λμ΄ μ²λ¦¬κ° λ²κ±°λ‘μ
- λΉκ΄κ³ν λ μ΄μμ -- μΌλ°μ μΈ κ΄κ³ν DB(long format)μ λ€λ₯Έ κ΅¬μ‘°λΌ λΆμμ λ°λ‘ μ°κΈ° μ΄λ €μ
- λΉ μ’ λͺ© λ°μ΄ν° -- μμ₯ νμ§ λ±μΌλ‘ λ°μ΄ν°κ° μλ μ’ λͺ©λ μ 체 κΈ°κ°μ κ±Έμ³ null νμ΄ μ‘΄μ¬
- λλ¦° λ‘λ© μλ -- ν° νμΌμ κ·Έλλ‘ μ½μΌλ©΄ λΆνμν λ°μ΄ν°κΉμ§ μ€μΊ
DataGuide 6 μ λ°μ΄νΈλ₯Ό κ³κΈ°λ‘, λ΄λ³΄λΈ νμΌλ§ λ£μΌλ©΄ μ μ λ CSV/Parquet DBλ‘ μλ λ³νν΄ μ£Όλ λΌμ΄λΈλ¬λ¦¬λ₯Ό λ§λ€κ³ μ μ΄ νλ‘μ νΈλ₯Ό μμνμ΅λλ€.
# μμ§ PyPIμ λ°°ν¬λμ§ μμμΌλ―λ‘ μ μ₯μλ₯Ό ν΄λ‘ νμ¬ μ€μΉν©λλ€.
git clone https://github.com/jaepil-choi/fn-dg6-ingest.git
cd fn-dg6-ingest
# uv μ¬μ© (κΆμ₯)
uv pip install -e .
# λλ pip
pip install -e .DataGuide 6μμ λ΄λ³΄λΈ νμΌμ νλ‘μ νΈ λ£¨νΈμ inputs/ ν΄λμ λ£μΌμΈμ.
fn-dg6-ingest/
inputs/ # β μ¬κΈ°μ DG6 νμΌμ λ£μΌμΈμ
dataguide_kse+kosdaq_ohlcv_from(20160101)_to(20260207).csv
dataguide_kse+kosdaq_sales-consensus_from(20180101)_to(20260207).csv
dataguide_etfconst(kodex200)_from(20250101)_to(20260207).csv
μ§μ ν¬λ§·:
| ν¬λ§· | μμ | μ€λͺ |
|---|---|---|
| Time Series Wide | OHLCV, λ§€μΆ μ»¨μΌμμ€ | λ μ§κ° μ΄(column)λ‘ νΌμ³μ§ νν |
| Misc (ETF ꡬμ±μ’ λͺ© λ±) | ETF ꡬμ±μ’ λͺ© | μ΄λ―Έ κ΄κ³ν(long) ννμΈ λ°μ΄ν° |
import fn_dg6_ingest
# ββ open(): λ¨μΌ μ§μ
μ ββ
# μ΅μ΄ μ€ν: ν¬λ§· κ°μ§ β μ€μ μμ± β λ°μ΄ν° λ³ν β Dataset λ°ν
ds = fn_dg6_ingest.open(
"inputs/dataguide_kse+kosdaq_ohlcv_from(20160101)_to(20260207).csv",
output_dir="outputs/kse+kosdaq_ohlcv",
)
# λ λ²μ§Έ νΈμΆ: μ΄λ―Έ λ°μ΄ν°κ° μ‘΄μ¬νλ©΄ λ³νμ 건λλ°κ³ λ°λ‘ Dataset λ°ν (λ©±λ±μ±)
ds = fn_dg6_ingest.open(
"inputs/dataguide_kse+kosdaq_ohlcv_from(20160101)_to(20260207).csv",
output_dir="outputs/kse+kosdaq_ohlcv",
)
# ββ λ°μ΄ν° μ½κΈ° ββ
df = ds.load() # μ 체 λ‘λ
df = ds.load(codes=["A005930"], items=["μμ μ£Όκ°(μ)"]) # μΌμ±μ μ μ’
κ°λ§
df = ds.load(date_from="2024-01-01", date_to="2025-12-31") # λ μ§ λ²μ
# ββ λ©νλ°μ΄ν° μ‘°ν ββ
meta = ds.load_meta() # _meta λ©ν ν
μ΄λΈ
info = ds.describe() # λΉ λ₯Έ λ©νλ°μ΄ν° (λ°μ΄ν° μ€μΊ μμ΄)λλ λ°λͺ¨ μ€ν¬λ¦½νΈλ‘ μ 체 μ λ ₯ νμΌμ μΌκ΄ μ²λ¦¬:
uv run python scripts/run_ingest.py # λ©±λ±: λ°μ΄ν° μμΌλ©΄ 건λλ
uv run python scripts/run_ingest.py --force # κ°μ μ¬λΉλopen()μ μ
λ ₯μ λ°λΌ λ€νμ μΌλ‘ λμν©λλ€:
| μΈμ μ ν | λμ |
|---|---|
| DG6 μλ³Έ νμΌ (CSV/Excel) | μ΅μ΄ μ€ν: ν¬λ§· κ°μ§ β μ€μ μμ± β μΆλ ₯ λΉλ. Dataset λ°ν. μ΄λ―Έ μΆλ ₯μ΄ μ‘΄μ¬νλ©΄ λΉλ 건λλ. |
| κΈ°μ‘΄ YAML μ€μ νμΌ | μ€μ λ‘λ β Dataset λ°ν. νμ΄νλΌμΈ μ€ν μμ. |
# μ€μ νμΌμμ μ§μ μ΄κΈ°
ds = fn_dg6_ingest.open("outputs/kse+kosdaq_ohlcv.yaml")
df = ds.load()μ€μ νμΌλ§ λ¨Όμ λ§λ€κ³ μΆμΌλ©΄ run_immediately=Falseλ₯Ό μ§μ νμΈμ:
ds = fn_dg6_ingest.open(
"inputs/your_file.csv",
output_dir="outputs/",
run_immediately=False, # μ€μ νμΌλ§ μμ±, λ°μ΄ν° λ³νμ λμ€μ
)
ds.ingest() # νμν λ μλ λΉλμλ μμ±λ fnconfig.yamlμ νΈμ§νμ¬ ν
μ΄λΈ λΆν , μΆλ ₯ ν¬λ§· λ±μ μ‘°μ ν μ μμ΅λλ€.
# outputs/kse+kosdaq_ohlcv.yaml (μλ μμ±λ μ€μ )
source:
input_path: inputs/dataguide_kse+kosdaq_ohlcv_from(20160101)_to(20260207).csv
detected_format: timeseries_wide
metadata:
frequency: μΌκ°
currency: μν
sort_order: μ€λ¦μ°¨μ
non_business_days: μ μΈ
include_weekends: μ μΈ
period_start: '20160101'
period_end: μ΅κ·ΌμΌμ(20260206)
calendar_basis: true
output:
output_dir: outputs/kse+kosdaq_ohlcv
output_format: parquet # csv λλ parquet
normalize_units: true # (μ²μ)β(μ) λ± λ¨μ μλ νμ°
drop_empty_entities: true # μ 체 NaNμΈ μ’
λͺ© μ κ±°
tables:
default:
- μμ μκ°(μ)
- μμ κ³ κ°(μ)
- μμ μ κ°(μ)
- μμ μ£Όκ°(μ)
- κ±°λλ(μ£Ό)
- κ±°λλκΈ(μ)
# μ¬λ¬ ν
μ΄λΈλ‘ λΆν νλ €λ©΄ κ·Έλ£Ήμ μΆκ°νμΈμ:
# ohlcv:
# - μμ μκ°(μ)
# - μμ κ³ κ°(μ)
# - μμ μ κ°(μ)
# - μμ μ£Όκ°(μ)
# volume:
# - κ±°λλ(μ£Ό)
# - κ±°λλκΈ(μ)νΈμ§ ν Datasetμ ν΅ν΄ μ¬λΉλν©λλ€:
ds = fn_dg6_ingest.open("outputs/kse+kosdaq_ohlcv.yaml")
ds.ingest() # λ³κ²½λ μ€μ μΌλ‘ μ¬λΉλ
# λΆν λ ν
μ΄λΈ λ‘λ
df = ds.load(table="ohlcv")λλ Pythonμμ νλ‘κ·Έλλ° λ°©μμΌλ‘ μ€μ μ μμ ν μλ μμ΅λλ€:
ds = fn_dg6_ingest.open("outputs/kse+kosdaq_ohlcv.yaml")
ds.config.tables = {
"ohlcv": ["μμ μκ°(μ)", "μμ κ³ κ°(μ)", "μμ μ κ°(μ)", "μμ μ£Όκ°(μ)"],
"volume": ["κ±°λλ(μ£Ό)", "κ±°λλκΈ(μ)"],
}
ds.config.output.output_format = "csv"
ds.save_config() # YAMLμ λ°μ
ds.ingest() # μ¬λΉλ β ohlcv.csv, volume.csv, _meta.csvfn_dg6_ingest.open()μ΄ λ°ννλ νΈλ€ κ°μ²΄. κ²½λ‘λ₯Ό ν λ²λ§ μ§μ νλ©΄ μ΄ν λͺ¨λ μμ
μ μ΄ νΈλ€μ ν΅ν΄ μνν©λλ€.
ds = fn_dg6_ingest.open("outputs/kse+kosdaq_ohlcv.yaml")
ds.config # IngestConfig (νμ±λ μ€μ )
ds.config_path # YAML νμΌ κ²½λ‘
ds.output_dir # μΆλ ₯ λλ ν 리 κ²½λ‘Parquetμ **μ΄ μ ν(column pruning)**κ³Ό **μ μ΄ νΈμλ€μ΄(predicate pushdown)**μ νμ©νμ¬ λμ©λ λ°μ΄ν°μμ νμν λΆλΆλ§ ν¨μ¨μ μΌλ‘ μ½μ΅λλ€.
df = ds.load() # μ 체 λ°μ΄ν°
df = ds.load(table="ohlcv") # νΉμ ν
μ΄λΈ
df = ds.load(codes=["A005930"]) # μ’
λͺ© μ½λλ‘ νν°
df = ds.load(date_from="2024-01-01", date_to="2025-12-31") # λ μ§ λ²μ
df = ds.load(items=["μμ μ£Όκ°(μ)", "κ±°λλ(μ£Ό)"]) # μμ΄ν
(컬λΌ) μ ν
df = ds.load(codes=["A005930"], date_from="2024-01-01",
items=["μμ μ£Όκ°(μ)"]) # μ‘°ν©ν
μ΄λΈμ΄ 1κ°λ©΄ DataFrame, μ¬λ¬ κ°λ©΄ dict[str, DataFrame]μ λ°νν©λλ€.
meta = ds.load_meta() # _meta DataFrame (20κ° μ»¬λΌ)Parquet νμΌ νΈν°μμ μ€ν€λ§μ ν μλ₯Ό μ½μ΄ λ°μ΄ν°λ₯Ό μ€μΊνμ§ μκ³ λΉ λ₯΄κ² μ 보λ₯Ό μ 곡ν©λλ€.
info = ds.describe()
info.tables # ["default"] λλ ["ohlcv", "volume"]
info.items # {"default": ["μμ μκ°(μ)", ...]}
info.shape # {"default": (7613009, 9)}
info.date_range # ("20160101", "μ΅κ·ΌμΌμ(20260206)")
info.entities # 3071
info.format_name # "timeseries_wide"ds.ingest() # νμ¬ μ€μ μΌλ‘ μΆλ ₯ μ¬μμ±ds.config.tables = {"ohlcv": [...], "volume": [...]}
ds.save_config() # YAMLμ λ°μμΈ κ°μ§ DataGuide 6 νμΌμ λν΄ scripts/run_ingest.pyλ₯Ό μ€νν κ²°κ³Όμ
λλ€.
| νλͺ© | κ° |
|---|---|
| κ°μ§ ν¬λ§· | timeseries_wide |
| μΆλ ₯ νμΌ | default.parquet (113.8 MB) |
| ν x μ΄ | 7,613,009 x 9 |
| λ©λͺ¨λ¦¬ | 786.3 MB |
| κΈ°κ° | 2016-01-01 ~ 2026-02-06 (μΌκ°) |
| μ’ λͺ© | 4,071κ° μ€ 1,000κ° λΉ μ’ λͺ© μ κ±° β 3,071κ° |
μ½λ μ½λλͺ
date μμ μκ°(μ) μμ κ³ κ°(μ) μμ μ κ°(μ) μμ μ£Όκ°(μ) κ±°λλ(μ£Ό) κ±°λλκΈ(μ)
A000020 λνμ½ν 2015-12-30 8180.0 8180.0 8020.0 8140.0 166761.0 1.348911e+09
A000020 λνμ½ν 2016-01-04 8130.0 8150.0 7920.0 8140.0 281440.0 2.265829e+09
A000020 λνμ½ν 2016-01-05 8040.0 8250.0 8000.0 8190.0 243179.0 1.981977e+09
A000020 λνμ½ν 2016-01-06 8200.0 8590.0 8110.0 8550.0 609906.0 5.129946e+09
A000020 λνμ½ν 2016-01-07 8470.0 8690.0 8190.0 8380.0 704752.0 5.919556e+09
| νλͺ© | κ° |
|---|---|
| κ°μ§ ν¬λ§· | timeseries_wide |
| μΆλ ₯ νμΌ | default.parquet (13.0 MB) |
| ν x μ΄ | 4,913,310 x 13 |
| λ©λͺ¨λ¦¬ | 661.4 MB |
| κΈ°κ° | 2018-01-01 ~ 2026-02-06 (μΌκ°) |
| μ’ λͺ© | 4,071κ° μ€ 1,602κ° λΉ μ’ λͺ© μ κ±° β 2,469κ° |
| λ¨μ νμ° | λ§€μΆμ‘(μ΅μ) β λ§€μΆμ‘(μ) (x100,000,000) λ± 7κ° μμ΄ν
|
λ¨μ νμ°μ΄ μ μ©λ _meta ν μμ:
| μμ΄ν λͺ | μμ΄ν λͺ _normalized | unit_original | unit_multiplier |
|---|---|---|---|
| λ§€μΆμ‘(μ΅μ) | λ§€μΆμ‘(μ) | μ΅μ | 100,000,000 |
| λ§€μΆμ‘(Fwd.12M)(μ΅μ) | λ§€μΆμ‘(Fwd.12M)(μ) | μ΅μ | 100,000,000 |
| μΆμ κΈ°κ΄μ | μΆμ κΈ°κ΄μ | NaN | 1 |
| νλͺ© | κ° |
|---|---|
| κ°μ§ ν¬λ§· | misc_etf |
| μΆλ ₯ νμΌ | default.parquet (380.4 KB) |
| ν x μ΄ | 53,836 x 8 |
| λ©λͺ¨λ¦¬ | 5.9 MB |
| κΈ°κ° | 2025-01-01 ~ 2026-02-06 (μΌκ°) |
date ETFμ½λ ETFλͺ
ꡬμ±μ’
λͺ©μ½λ ꡬμ±μ’
λͺ© μ£Όμμ(κ³μ½μ) κΈμ‘ κΈμ‘κΈ°μ€ κ΅¬μ±λΉμ€(%)
2025-01-02 A069500 KODEX 200 μννκΈ NaN 9945243 0.62
2025-01-02 A069500 KODEX 200 A000080 νμ΄νΈμ§λ‘ 47.0 914620 0.06
2025-01-02 A069500 KODEX 200 A000100 μ νμν 91.0 10765300 0.67
2025-01-02 A069500 KODEX 200 A000120 CJλνν΅μ΄ 17.0 1429700 0.09
2025-01-02 A069500 KODEX 200 A000660 SKνμ΄λμ€ 851.0 145691200 9.09
outputs/
βββ etfconst(kodex200).yaml # μ€μ νμΌ
βββ etfconst(kodex200)/
β βββ default.parquet # λ°μ΄ν° ν
μ΄λΈ
β βββ _meta.parquet # λ©ν ν
μ΄λΈ
βββ kse+kosdaq_ohlcv.yaml
βββ kse+kosdaq_ohlcv/
β βββ default.parquet
β βββ _meta.parquet
βββ kse+kosdaq_sales-consensus.yaml
βββ kse+kosdaq_sales-consensus/
βββ default.parquet
βββ _meta.parquet
λͺ¨λ λ°μ΄ν°μ
μ _meta.parquetκ° ν¨κ» μΆλ ₯λ©λλ€. κ° μμ΄ν
μ μΆμ², λ¨μ νμ° μ΄λ ₯, μ κ±°λ μ’
λͺ© μ λ±μ κΈ°λ‘ν©λλ€.
| Column | μμ | μ€λͺ |
|---|---|---|
table_name |
default |
μμ μΆλ ₯ ν μ΄λΈλͺ |
source_file |
dataguide_kse+kosdaq_ohlcv_...csv |
μλ³Έ νμΌλͺ |
source_hash |
523e45d8... |
μλ³Έ νμΌ SHA-256 |
source_last_updated |
2026-02-07 15:46:56 |
DG6 Refresh μκ° |
detected_format |
timeseries_wide |
κ°μ§λ λ μ΄μμ |
μμ΄ν
μ½λ |
S410000650 |
DG6 μμ΄ν μ½λ |
μμ΄ν
λͺ
|
μμ μκ°(μ) |
μλ³Έ μμ΄ν λͺ |
μμ΄ν
λͺ
_normalized |
μμ μκ°(μ) |
λ¨μ νμ° ν μ΄λ¦ |
μ ν |
SSC |
μ’ λͺ© μ ν |
μ§κ³μ£ΌκΈ° |
μΌκ° |
μ§κ³ μ£ΌκΈ° |
unit_original |
μ΅μ |
μλ³Έ λ¨μ μ λ―Έμ¬ |
unit_multiplier |
100000000 |
μ μ©λ νμ° λ°°μ |
entities_total |
4071 |
μλ³Έ μ’ λͺ© μ |
entities_dropped |
1000 |
μ κ±°λ λΉ μ’ λͺ© μ |
processed_at |
2026-02-11T02:15:02+00:00 |
μ²λ¦¬ μκ° (UTC) |
- Dataset νΈλ€ --
open()ν λ²μΌλ‘ κ²½λ‘λ₯Ό κΈ°μ΅νκ³ ,load()/describe()/ingest()λ₯Ό ν΅ν© μ 곡 - λ©±λ± open() -- μΆλ ₯ λ°μ΄ν°κ° μ΄λ―Έ μ‘΄μ¬νλ©΄ λ³νμ 건λλ°κ³ μ¦μ
Datasetλ°ν (force=Trueλ‘ κ°μ μ¬λΉλ κ°λ₯) - Parquet λ€μ΄ν°λΈ νν°λ§ -- μ΄ μ ν(column pruning) + μ μ΄ νΈμλ€μ΄(predicate pushdown)μΌλ‘ λμ©λ λ°μ΄ν° ν¨μ¨ μ½κΈ°
- ν¬λ§· μλ κ°μ§ -- λ μ΄μμ YAML κΈ°λ° μ’ν νμ§λ‘ Time Series / Misc ν¬λ§·μ μλ νλ³
- λ¨μ μλ νμ° --
(μ²μ),(μ΅μ),(μμ΅μ)λ±μ κΈμ‘ λ¨μλ₯Ό(μ)κΈ°μ€μΌλ‘ νμ° - λΉ μ’ λͺ© μ κ±° -- μ κΈ°κ° λ°μ΄ν°κ° μλ μ’ λͺ©μ μλ μ μΈ (OHLCV: 4,071κ° μ€ 1,000κ° μ κ±°)
- Config-First μν¬νλ‘μ° --
fnconfig.yamlνλλ‘ ν μ΄λΈ λΆν , μΆλ ₯ ν¬λ§·, μ μ μ΅μ μ μ μ΄ - λ©ν ν
μ΄λΈ --
_metaν μ΄λΈλ‘ μ²λ¦¬ μ΄λ ₯ μΆμ (μλ³Έ ν΄μ, λ¨μ νμ° λ΄μ, μ’ λͺ© ν΅κ³)
| μ€ν¬λ¦½νΈ | μ€λͺ |
|---|---|
scripts/run_ingest.py |
inputs/μ μ 체 νμΌμ μΌκ΄ μ²λ¦¬ (λ©±λ±, --forceλ‘ κ°μ μ¬λΉλ) |
scripts/inspect_outputs.py |
Dataset APIλ₯Ό μ¬μ©νμ¬ outputs/μ κ²°κ³Όλ¬Ό μμ± λ° μν μΆλ ₯ |
# μ 체 νμΌ μΌκ΄ μ²λ¦¬ (λ°μ΄ν° μμΌλ©΄ 건λλ)
uv run python scripts/run_ingest.py
# κ°μ μ¬λΉλ
uv run python scripts/run_ingest.py --force
# μΆλ ₯ κ²°κ³Ό μ 체 μ κ²
uv run python scripts/inspect_outputs.py
# νΉμ λ°μ΄ν°μ
λ§ μ κ² (YAML μ€μ κ²½λ‘λ‘ μ§μ )
uv run python scripts/inspect_outputs.py outputs/kse+kosdaq_ohlcv.yaml# λ¨μ ν
μ€νΈ (195κ°, ~3μ΄)
uv run python -m pytest tests/unit/ -v
# ν΅ν© ν
μ€νΈ (31κ°, inputs/ μ μ€μ νμΌ νμ, ~42λΆ)
uv run python -m pytest tests/integration/ -v -m integration
# μ 체
uv run python -m pytest -v- Python >= 3.14
- pandas, pyarrow, pydantic, pyyaml, openpyxl