안녕하세요. 주식 투자 관련 지식을 공유하는 인천고래입니다.
일전에 크몽에서 개발의뢰를 받아서 만든 '재무제표 데이터 수집기'가 있습니다.
이 프로그램은 재무분석을 하기 위해 만들어졌으며
기본적인 기업현황 정보와, 재무상태표, 현금흐름표의 데이터를 취합하도록 되어 있고
최신 보고서는 DART를 활용하여 OpenAPI를 통해서 가져오도록 구현되어 있습니다.
현재 만들어진 UI 형태는 아래와 같습니다.
정상적으로 잘 동작을 하던 프로그램인데
이번 반기보고서를 취합하는 과정에서 오류(신규 보고서를 못 가져오는 문제)가 발생이 되었었는데
그 이유는 반기보고서를 가져오는 날짜를 fix 해 둔 것이 문제였습니다.
for y in years:
# 1Q: 매년 5월 1일
events.append((datetime(y, 5, 1), y, '11013'))
# Half: 매년 9월 1일
events.append((datetime(y, 9, 1), y, '11012'))
# 3Q: 매년 11월 1일
events.append((datetime(y, 11, 1), y, '11014'))
# Annual(4Q): 다음 연도 4월 1일
events.append((datetime(y + 1, 4, 1), y, '11011'))
이처럼 반기 보고서를 9월 1일로 설정을 해 두었으니 8월 16일 이후에 프로그램을 돌려도 반기보고서가 취합이 안 되는 이유가 발생을 했던 것이었습니다.
물론 프로그램만의 문제만 있는 것이 아니라
DART에서도 보고서가 올라오면 OpenAPI가 가져갈 수 있도록 데이터화해야 하는데
공식적인 답변은 실시간 데이터로도 제공이 되지만 재무데이터의 경우 후처리 성격이 강하기 때문에 바로 제공이 안 된다는 문제도 있었습니다.
암튼 날짜는 공식적으로 프로그램에 하드코딩으로 15일 이후로 해 놓을 수도 있지만
유동성을 주기 위해 프로그램 설정 파일(_config.json)에서 설정을 할 수 있게 변경을 해 두었습니다.
관련 코드는 아래와 같습니다.
# 보고서 순서 정하기
def _cutoff_dt_from_config(code: str, base_year: int) -> datetime:
"""
config.report_cutoffs[code]의 month/day(+buffer)를 읽어 해당 년도의 컷오프 datetime 생성
"""
rc = (config.get_config_value('report_cutoffs') or {}).get(code, {})
base = _DEFAULT_CUTOFFS[code]
m = int(rc.get('month', base['month']))
d = int(rc.get('day', base['day']))
b = int(rc.get('buffer_days', base['buffer_days']))
return datetime(base_year, m, d) + timedelta(days=b)
# ─────────────────────────────────────────────────────────
# 1) 월별 "코드" 우선순위 (직관형)
# ─────────────────────────────────────────────────────────
def get_report_priority(today: datetime = None):
"""
reprt_code:
11011 = 연간(사업보고서) / 11012 = 반기 / 11013 = 1분기 / 11014 = 3분기
- 하드코딩된 '월 구간' 대신, 컨피그 컷오프 일자를 사용해 버킷을 판단
- 기존 우선순위 규칙을 유지:
3Q 이후: 3Q → 반기 → 1Q → 연간
반기 이후: 반기 → 1Q → 3Q → 연간
1Q 이후: 1Q → 3Q → 반기 → 연간
연간 이후~연간 이전: 연간 → 3Q → 반기 → 1Q
"""
if today is None:
today = datetime.now()
Y = today.year
c_yr = _cutoff_dt_from_config('11011', Y) # 연간 컷오프(보통 4/1, 그 해에 '전년도' 결산)
c_1q = _cutoff_dt_from_config('11013', Y) # 1Q 컷오프(보통 5/16)
c_half= _cutoff_dt_from_config('11012', Y) # 반기 컷오프(보통 8/16)
c_3q = _cutoff_dt_from_config('11014', Y) # 3Q 컷오프(보통 11/16)
if today >= c_3q:
return ['11014','11012','11013','11011']
elif today >= c_half:
return ['11012','11013','11014','11011']
elif today >= c_1q:
return ['11013','11014','11012','11011']
elif today >= c_yr:
return ['11011','11014','11012','11013']
else:
# 연초(연간 이전) 케이스
return ['11011','11014','11012','11013']
# ─────────────────────────────────────────────────────────
# 2) 코드별 "그때 조회해야 할 bsns_year" 계산
# (출시 시점에 맞춰 불필요한 실패 호출 최소화)
# ─────────────────────────────────────────────────────────
def _year_for_code(code: str, today: datetime) -> int:
"""
config.report_cutoffs[code]의 month/day(+buffer)를 사용해 bsns_year 산출.
- 11013/11012/11014: cutoff 이후면 올해, 이전이면 작년
- 11011(연간): cutoff 이후면 전년도, 이전이면 전전년도
"""
Y = today.year
cut_cfg = (config.get_config_value('report_cutoffs') or {}).get(code, {})
base = _DEFAULT_CUTOFFS[code]
m = int(cut_cfg.get('month', base['month']))
d = int(cut_cfg.get('day', base['day']))
b = int(cut_cfg.get('buffer_days', base['buffer_days']))
cutoff = datetime(Y, m, d) + timedelta(days=b)
if code == '11011': # 연간(사업보고서)
return (Y - 1) if today >= cutoff else (Y - 2)
else: # 1Q/반기/3Q
return Y if today >= cutoff else (Y - 1)
현재 수정 반영된 코드는 위와 같으며 중간에 '반기 보고서'가 연간 보고서로 잘못 매핑이 된 경우가 있었는데
DART의 반기 표기가 종종 “반기말”(= “기말” 포함, “분기” 문구 없음)로 들어옵니다.
이게 확인을 해 보니 if '기말' in th_nm and '분기' not in th_nm: 조건으로 분류를 하고 있었더라고요
이 경우가 위 조건에 걸려 반기 데이터가 연간 시트로 잘못 들어가는 현상이 발생이 되었었고
이를 수정하여 반기(=2분기)를 분기 시트로 인식하고, 연간 시트에서는 반기를 제외하도록 하였습니다. ^^
간단한 듯하면서도 어려운 게 프로그램인 것 같습니다.
항상 최선의 결과물을 드리려고 노력은 하지만
인간인지라~ 간혹 생각하지도 못한 오류가 발생하는 코드를 작성하는 게 가슴 아플 따름입니다. 크읍~
이 글과 코드가 도움이 되기를 바라며
오늘도 행복한 하루를 보내셨으면 좋겠습니다. ^^
★ 해당 프로그램(재무제표 데이터 수집기)은 판매용입니다. 구입 희망하시면 댓글로 문의 남겨주세요~
감사합니다.
주식 투자에 어려움을 겪고 계신다거나 관심 종목 추출이 어려우시다면
프로그램 제작을 의뢰해 보세요. 원하시는 결과 이상의 프로그램을 받아 보실 수 있습니다.
한 방에 주식 데이터 만들기 - 크몽
인천고래 전문가의 IT·프로그래밍 서비스를 만나보세요. <p>퀀트 매매, 수익률 높은 매매, 확률 높은 매매, 잃지 않는 매매 등<...
kmong.com
아래의 글은 제가 작성한 글로
다른 보조지표에 대해서는 아래의 링크 글을 통해 자세히 알아볼 수 있습니다.
보조지표 리스트 (추세, 모멘텀, 채널, 변동성, 거래량, 기타 지표)
안녕하세요. 주식을 통해 삶을 영위할 수 있는 방법을 찾으며 인생 후반을 준비하고 있는 인천고래입니다.이전부터 보조지표에 대해서 글을 작성해 왔지만 중요한 것 위주로 작성을 하다보니
i-whale.com
아래의 글은 제가 작성한 글로
단기적인 스윙 및 세력 매집 분석에 용이한 기준봉에 대해서는 아래의 링크 글을 통해 자세히 알아 볼 수 있습니다.
'주식 기준봉' 카테고리의 글 목록
주식 투자에 필요한 교육 내용을 제공하고 시장 정보 및 통계 등 수록하고 기록함을 원칙으로 하되 데이터마이닝을 통해 객관적인 자료를 구축하여 보다 경제적 자유를 얻기 위하여 사이트를
i-whale.com
'Quant' 카테고리의 다른 글
백테스팅 - 보유 기간이 길어지면서 MeanBarsToHit가 커지는 현상에 대하여 (0) | 2025.06.26 |
---|---|
백테스팅 - 보유 기간에 따른 수익 극대화(MeanRet & E-Ratio & MeanBarsToHit) (1) | 2025.06.25 |
백테스팅 필수 지표 완전 정복 — MFE·MAE부터 E-Ratio까지 한 번에 이해하기 (0) | 2025.06.17 |
자동매매 TLine 백테스팅 리포트(필터 단계별 성능·위험 분석) (0) | 2025.06.04 |
키움 openAPI 한글 깨짐 문제 해결 (0) | 2024.06.27 |