| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- using Domain.Entities.Stocks.ValueObject;
- using Infrastructure.StockData;
- namespace Application.Tests;
- [TestClass]
- public sealed class KrxFuturesParserTests
- {
- [TestMethod]
- public void ParseDaily_General_MapsFields_WithSettleAndOpenInterest()
- {
- // KRX OpenAPI(fut_bydd_trd) 실검증 샘플 — 정산가(SETL_PRC)·현물가(SPOT_PRC)·미결제약정(ACC_OPNINT_QTY), 8자리 영숫자 코드
- const string json = """
- {"OutBlock_1":[
- {"BAS_DD":"20250102","PROD_NM":"10년국채 선물","MKT_NM":"정규","ISU_CD":"167W3000","ISU_NM":"10년국채 F 202503 (주간)","TDD_CLSPRC":"117.67","CMPPREVDD_PRC":"0.75","TDD_OPNPRC":"117.10","TDD_HGPRC":"117.71","TDD_LWPRC":"116.82","SPOT_PRC":"117.76","SETL_PRC":"117.67","ACC_TRDVOL":"107306","ACC_TRDVAL":"12592876260000","ACC_OPNINT_QTY":"215549"}
- ]}
- """;
- var rows = KrxFuturesParser.ParseDaily(json, FuturesKind.General);
- Assert.AreEqual(1, rows.Count);
- var fut = rows[0];
- Assert.AreEqual(FuturesKind.General, fut.FuturesKind, "상품군은 호출 인자로 결정");
- Assert.AreEqual("167W3000", fut.IsuCode, "8자리 영숫자 코드 원문 유지");
- Assert.AreEqual("10년국채 F 202503 (주간)", fut.IsuName);
- Assert.AreEqual(new DateOnly(2025, 1, 2), fut.TradeDate);
- Assert.AreEqual(117.67m, fut.Close);
- Assert.AreEqual(117.10m, fut.Open);
- Assert.AreEqual(117.71m, fut.High);
- Assert.AreEqual(116.82m, fut.Low);
- Assert.AreEqual(0.75m, fut.ChangeAmount);
- Assert.AreEqual(117.76m, fut.SpotPrice, "현물가 (SPOT_PRC)");
- Assert.AreEqual(117.67m, fut.SettlePrice, "정산가 (SETL_PRC)");
- Assert.AreEqual(107306L, fut.Volume);
- Assert.AreEqual(12592876260000L, fut.TradeValue);
- Assert.AreEqual(215549L, fut.OpenInterest, "미결제약정 (ACC_OPNINT_QTY)");
- Assert.AreEqual("10년국채 선물", fut.ProductName);
- Assert.AreEqual("정규", fut.MarketName);
- }
- [TestMethod]
- public void ParseDaily_StockKospi_And_StockKosdaq_KindFromArgument()
- {
- // 주식선물(유가) eqsfu_stk_bydd_trd 샘플 — kind 는 호출 인자로 결정
- const string kospiJson = """
- {"OutBlock_1":[
- {"BAS_DD":"20250102","PROD_NM":"BGF리테일 선물","MKT_NM":"정규","ISU_CD":"1FRW1000","ISU_NM":"BGF리테일 F 202501 ","TDD_CLSPRC":"101800.00","CMPPREVDD_PRC":"-1300.00","TDD_OPNPRC":"102000.00","TDD_HGPRC":"103500.00","TDD_LWPRC":"100800.00","SPOT_PRC":"101500.00","SETL_PRC":"101800.00","ACC_TRDVOL":"908","ACC_TRDVAL":"924753000","ACC_OPNINT_QTY":"3172"}
- ]}
- """;
- var kospi = KrxFuturesParser.ParseDaily(kospiJson, FuturesKind.StockKospi);
- Assert.AreEqual(1, kospi.Count);
- Assert.AreEqual(FuturesKind.StockKospi, kospi[0].FuturesKind);
- Assert.AreEqual(101800.00m, kospi[0].Close);
- Assert.AreEqual(-1300.00m, kospi[0].ChangeAmount, "음수 대비");
- Assert.AreEqual(3172L, kospi[0].OpenInterest);
- // 코스닥 선물 eqkfu_ksq_bydd_trd 샘플
- const string ksqJson = """
- {"OutBlock_1":[
- {"BAS_DD":"20250102","PROD_NM":"CJ ENM 선물","MKT_NM":"정규","ISU_CD":"1DTW1000","ISU_NM":"CJ ENM F 202501 ","TDD_CLSPRC":"53300.00","CMPPREVDD_PRC":"-700.00","TDD_OPNPRC":"54300.00","TDD_HGPRC":"54700.00","TDD_LWPRC":"53000.00","SPOT_PRC":"53700.00","SETL_PRC":"53300.00","ACC_TRDVOL":"2945","ACC_TRDVAL":"1572553000","ACC_OPNINT_QTY":"6906"}
- ]}
- """;
- var ksq = KrxFuturesParser.ParseDaily(ksqJson, FuturesKind.StockKosdaq);
- Assert.AreEqual(1, ksq.Count);
- Assert.AreEqual(FuturesKind.StockKosdaq, ksq[0].FuturesKind);
- Assert.AreEqual("1DTW1000", ksq[0].IsuCode);
- Assert.AreEqual(6906L, ksq[0].OpenInterest);
- }
- [TestMethod]
- public void ParseDaily_NoTrade_EmptyPricesZero_MissingSettleAndOpenInterestNull()
- {
- // 미체결 종목 — 종가/시고저/대비 "" → 0, SETL_PRC "" → null, ACC_OPNINT_QTY "" → null (스프레드 종목)
- const string json = """
- {"OutBlock_1":[
- {"BAS_DD":"20250102","PROD_NM":"10년국채 선물","MKT_NM":"정규","ISU_CD":"467W3W6S","ISU_NM":"10년국채 SP 2503-2506 (주간)","TDD_CLSPRC":"","CMPPREVDD_PRC":"","TDD_OPNPRC":"","TDD_HGPRC":"","TDD_LWPRC":"","SPOT_PRC":"117.76","SETL_PRC":"","ACC_TRDVOL":"0","ACC_TRDVAL":"0","ACC_OPNINT_QTY":""}
- ]}
- """;
- var rows = KrxFuturesParser.ParseDaily(json, FuturesKind.General);
- Assert.AreEqual(1, rows.Count);
- var fut = rows[0];
- Assert.AreEqual(0m, fut.Close, "미체결 종가는 0");
- Assert.AreEqual(0m, fut.Open);
- Assert.AreEqual(0m, fut.ChangeAmount, "빈 대비는 0");
- Assert.AreEqual(117.76m, fut.SpotPrice, "현물가는 채워짐");
- Assert.IsNull(fut.SettlePrice, "\"\" 정산가는 null");
- Assert.IsNull(fut.OpenInterest, "\"\" 미결제약정은 null (0 과 구분)");
- }
- [TestMethod]
- public void ParseDaily_EmptyOrMissingBlock_ReturnsEmpty()
- {
- // basDd 미반영 — OutBlock_1 이 빈 배열
- Assert.AreEqual(0, KrxFuturesParser.ParseDaily("""{"OutBlock_1":[]}""", FuturesKind.General).Count);
- // 인증 실패 등 OutBlock_1 노드 자체가 없는 응답도 안전하게 0건
- Assert.AreEqual(0, KrxFuturesParser.ParseDaily("""{"respMsg":"Unauthorized API Call","respCode":"401"}""", FuturesKind.StockKospi).Count);
- }
- }
|