| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- using System.ComponentModel.DataAnnotations;
- using Domain.Entities.Stocks.ValueObject;
- namespace Domain.Entities.Stocks;
- /// <summary>
- /// 옵션(일반옵션/주식옵션유가/주식옵션코스닥) 일별매매 시세 (KRX OpenAPI drv 엔드포인트) — (OptionsKind, Code, TradeDate, Session) UNIQUE.
- /// 일반옵션(opt_bydd_trd)은 MKT_NM 필드 없이 동일 ISU_CD 로 정규·야간 세션 행을 각각 반환한다
- /// (라이브 재현: 18,908행 = 9,454 코드 × 2, 종목명 "(정규)"/"(야간)" 표기) — Session(종목명 파싱)을 UQ 에 포함해 각각 저장한다.
- /// 세 상품군을 한 테이블에 담고 OptionsKind 로 구분한다. 옵션은 선물과 달리 콜/풋(RGHT_TP_NM)·행사가(strike)·내재변동성(IMP_VOLT)을 갖는다:
- /// - 가격/전일대비: TDD_CLSPRC/OPNPRC/HGPRC/LWPRC + CMPPREVDD_PRC. 미체결 시 종가/시고저는 "" → 0.
- /// - 옵션 고유: RightType(RGHT_TP_NM Call/Put), StrikePrice(행사가 — 응답 필드 없이 ISU_NM 에서 파싱, nullable),
- /// ImpliedVolatility(IMP_VOLT 내재변동성 %, nullable), NextDayBasePrice(NXTDD_BAS_PRC 익일정산가, nullable),
- /// OpenInterest(ACC_OPNINT_QTY 미결제약정, nullable).
- /// 행사가는 별도 필드가 없어 종목명에서 추출한다:
- /// 지수옵션 "미니코스피 C 202501 232.5 (정규)" → 232.5, 주식옵션 "HD한국조선 C 202501 150,000( 10)" → 150000.
- /// 종목코드(ISU_CD)는 8자리 영숫자(예: 205W1232) — 6자리 정규화 없이 원문 유지. 가격은 decimal(18,2).
- /// </summary>
- public class OptionsDailyTrade
- {
- [Key]
- public long ID { get; private set; }
- /// <summary>옵션 상품군 구분 (1=일반옵션, 2=주식옵션유가, 3=주식옵션코스닥)</summary>
- public OptionsKind OptionsKind { get; private set; }
- /// <summary>종목코드 (ISU_CD) — 8자리 영숫자(예: 205W1232)</summary>
- public string IsuCode { get; private set; } = default!;
- /// <summary>종목명 (ISU_NM)</summary>
- public string IsuName { get; private set; } = default!;
- /// <summary>거래일 (BAS_DD)</summary>
- public DateOnly TradeDate { get; private set; }
- /// <summary>매매 세션 (1=정규, 2=야간) — 종목명 "(정규)"/"(야간)" 파싱 (옵션 응답엔 MKT_NM 없음). 동일 종목코드가 세션별 별도 행</summary>
- public MarketSession Session { get; private set; } = MarketSession.Regular;
- /// <summary>권리유형 (RGHT_TP_NM — Call/Put)</summary>
- public OptionRightType RightType { get; private set; }
- /// <summary>행사가 (ISU_NM 에서 파싱) — 추출 실패 시 null</summary>
- public decimal? StrikePrice { get; private set; }
- /// <summary>종가 (TDD_CLSPRC) — 미체결 시 0</summary>
- public decimal Close { get; private set; }
- /// <summary>시가 (TDD_OPNPRC) — 미체결 시 0</summary>
- public decimal Open { get; private set; }
- /// <summary>고가 (TDD_HGPRC) — 미체결 시 0</summary>
- public decimal High { get; private set; }
- /// <summary>저가 (TDD_LWPRC) — 미체결 시 0</summary>
- public decimal Low { get; private set; }
- /// <summary>전일 대비 (CMPPREVDD_PRC) — 미제공("") 시 0</summary>
- public decimal ChangeAmount { get; private set; }
- /// <summary>내재변동성 % (IMP_VOLT) — 미제공 시 null</summary>
- public decimal? ImpliedVolatility { get; private set; }
- /// <summary>익일정산가 (NXTDD_BAS_PRC) — 미제공 시 null</summary>
- public decimal? NextDayBasePrice { get; private set; }
- /// <summary>거래량 (ACC_TRDVOL)</summary>
- public long Volume { get; private set; }
- /// <summary>거래대금 (ACC_TRDVAL, 원)</summary>
- public long TradeValue { get; private set; }
- /// <summary>미결제약정 (ACC_OPNINT_QTY) — 미제공("") 시 null</summary>
- public long? OpenInterest { get; private set; }
- /// <summary>상품구분 (PROD_NM 예: "미니코스피200 옵션")</summary>
- public string? ProductName { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- private OptionsDailyTrade() { }
- public static OptionsDailyTrade Create(
- OptionsKind optionsKind,
- string isuCode,
- string isuName,
- DateOnly tradeDate,
- MarketSession session,
- OptionRightType rightType,
- decimal? strikePrice,
- decimal close,
- decimal open,
- decimal high,
- decimal low,
- decimal changeAmount,
- decimal? impliedVolatility,
- decimal? nextDayBasePrice,
- long volume,
- long tradeValue,
- long? openInterest,
- string? productName = null
- ) {
- if (!Enum.IsDefined(optionsKind))
- {
- throw new ArgumentOutOfRangeException(nameof(optionsKind));
- }
- if (!Enum.IsDefined(session))
- {
- throw new ArgumentOutOfRangeException(nameof(session));
- }
- if (!Enum.IsDefined(rightType))
- {
- throw new ArgumentOutOfRangeException(nameof(rightType));
- }
- if (string.IsNullOrWhiteSpace(isuCode))
- {
- throw new ArgumentException("isuCode required", nameof(isuCode));
- }
- if (string.IsNullOrWhiteSpace(isuName))
- {
- throw new ArgumentException("isuName required", nameof(isuName));
- }
- return new OptionsDailyTrade
- {
- OptionsKind = optionsKind,
- IsuCode = isuCode.Trim(),
- IsuName = isuName.Trim(),
- TradeDate = tradeDate,
- Session = session,
- RightType = rightType,
- StrikePrice = strikePrice,
- Close = close,
- Open = open,
- High = high,
- Low = low,
- ChangeAmount = changeAmount,
- ImpliedVolatility = impliedVolatility,
- NextDayBasePrice = nextDayBasePrice,
- Volume = volume,
- TradeValue = tradeValue,
- OpenInterest = openInterest,
- ProductName = productName
- };
- }
- /// <summary>동일 (OptionsKind, IsuCode, TradeDate, Session) 재수집 시 값 갱신 (upsert) — Session 은 키의 일부라 갱신하지 않는다</summary>
- public void Update(
- string isuName,
- OptionRightType rightType,
- decimal? strikePrice,
- decimal close,
- decimal open,
- decimal high,
- decimal low,
- decimal changeAmount,
- decimal? impliedVolatility,
- decimal? nextDayBasePrice,
- long volume,
- long tradeValue,
- long? openInterest,
- string? productName = null
- ) {
- IsuName = isuName.Trim();
- RightType = rightType;
- StrikePrice = strikePrice;
- Close = close;
- Open = open;
- High = high;
- Low = low;
- ChangeAmount = changeAmount;
- ImpliedVolatility = impliedVolatility;
- NextDayBasePrice = nextDayBasePrice;
- Volume = volume;
- TradeValue = tradeValue;
- OpenInterest = openInterest;
- ProductName = productName;
- }
- }
|