AppSetting.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. namespace SharedKernel;
  2. public sealed class AppSettings
  3. {
  4. public AppSection App { get; init; } = new();
  5. public ConnectionStringsSection ConnectionStrings { get; init; } = new();
  6. public RedisSection Redis { get; init; } = new();
  7. public SmtpSection SMTP { get; init; } = new();
  8. public JwtSection JWT { get; init; } = new();
  9. public ForwardedHeadersSection ForwardedHeaders { get; init; } = new();
  10. public CorsPolicySection CorsPolicy { get; init; } = new();
  11. public YouTubeSection YouTube { get; init; } = new();
  12. public BackgroundJobsSection BackgroundJobs { get; init; } = new();
  13. public EncryptionSection Encryption { get; init; } = new();
  14. public FeaturesSection Features { get; init; } = new();
  15. public StockDataSection StockData { get; init; } = new();
  16. public KRXCoKrSection KRXCoKr { get; init; } = new();
  17. public OpenDartSection OpenDart { get; init; } = new();
  18. public KoreaEximSection KoreaExim { get; init; } = new();
  19. public KosisSection Kosis { get; init; } = new();
  20. public SeibroSection Seibro { get; init; } = new();
  21. public WorldIndexSection WorldIndex { get; init; } = new();
  22. public MarketQuoteSection MarketQuote { get; init; } = new();
  23. public OAuthSection OAuth { get; init; } = new();
  24. public sealed class AppSection
  25. {
  26. public string Name { get; init; } = string.Empty;
  27. public string Company { get; init; } = string.Empty;
  28. public string BaseURL { get; init; } = string.Empty;
  29. public string ApiURL { get; init; } = string.Empty;
  30. public string FrontURL { get; init; } = string.Empty;
  31. }
  32. public sealed class ConnectionStringsSection
  33. {
  34. public string DefaultConnection { get; init; } = string.Empty;
  35. public int Timeout { get; init; }
  36. }
  37. public class RedisSection
  38. {
  39. public string DefaultConnection { get; init; } = string.Empty;
  40. public string CachePrefix { get; init; } = string.Empty;
  41. public string AuthTicketPrefix { get; init; } = string.Empty;
  42. public string DataProtectionKey { get; init; } = string.Empty;
  43. public TimeSpan DefaultKeyLifetime { get; init; }
  44. }
  45. public class SmtpSection
  46. {
  47. public string Host { get; init; } = default!;
  48. public int Port { get; init; } = 587;
  49. public string? User { get; init; }
  50. public string? Password { get; init; }
  51. public bool UseStartTls { get; init; } = true;
  52. public string FromEmail { get; init; } = default!;
  53. public string FromName { get; init; } = "no-reply";
  54. }
  55. public class JwtSection
  56. {
  57. public string SecretKey { get; init; } = string.Empty;
  58. public string Issuer { get; init; } = string.Empty;
  59. public string Audience { get; init; } = string.Empty;
  60. public int AccessTokenExpiration { get; init; }
  61. public int RefreshTokenExpiration { get; init; }
  62. }
  63. public class ForwardedHeadersSection
  64. {
  65. public int? ForwardLimit { get; init; } = default;
  66. public List<string> KnownProxies { get; init; } = [];
  67. public List<string> KnownNetworks { get; init; } = [];
  68. }
  69. public class CorsPolicySection
  70. {
  71. public string Name { get; init; } = string.Empty;
  72. public long PreflightMaxAgeSeconds { get; init; } = 0;
  73. public List<string> AllowedOrigins { get; init; } = [];
  74. }
  75. public class YouTubeSection
  76. {
  77. public string HubUrl { get; init; } = "https://pubsubhubbub.appspot.com/subscribe";
  78. public string CallbackUrl { get; init; } = string.Empty;
  79. public string HmacSecret { get; init; } = string.Empty;
  80. public int FeedPollingIntervalMinutes { get; init; } = 3;
  81. }
  82. public class BackgroundJobsSection
  83. {
  84. public bool LiveChat { get; init; } = true;
  85. public bool PubSubRenewal { get; init; } = true;
  86. public bool FeedPolling { get; init; } = true;
  87. public bool LiveViewerPoller { get; init; } = true;
  88. public bool ChannelCacheRefresh { get; init; } = true;
  89. public bool YouTubeDailyAggregator { get; init; } = true;
  90. public bool YouTubeStaleDataPurge { get; init; } = true;
  91. /// <summary>결제 자동 대사 배치 (Toss NeedsReconciliation 재확인 — PaymentReconcileService). Features:Channel 게이트와 무관.</summary>
  92. public bool PaymentReconcile { get; init; } = true;
  93. /// <summary>모의투자 체결·스냅샷 배치 (PaperFillService — d4 M2). Features:Channel 게이트와 무관, 기본 false (StockData 적재 가동 후 활성화).</summary>
  94. public bool PaperFill { get; init; } = false;
  95. /// <summary>예측 채점 배치 (PredictionSettlementService — d2 M4). Web.Api 전용, 기본 false (StockData T+1 종가 적재 가동 후 활성화).</summary>
  96. public bool PredictionSettlement { get; init; } = false;
  97. /// <summary>장전 브리핑 자동 발행 배치 (DailyBriefingPublisher — d2 M3, 07:30 KST). Web.Api 전용, 기본 false (StockData 지수·공시 적재 가동 후 활성화).</summary>
  98. public bool DailyBriefing { get; init; } = false;
  99. }
  100. /// <summary>
  101. /// 기능 단위 활성화 플래그. 코드/스키마는 보존하고 노출만 차단하는 soft-off 스위치.
  102. /// Channel=false 시 채널/스튜디오/YouTube 관련 API 와 백그라운드 서비스 전체 비활성 (기본 false).
  103. /// </summary>
  104. public class FeaturesSection
  105. {
  106. public bool Channel { get; init; } = false;
  107. }
  108. /// <summary>
  109. /// 회원 PII(주소/연락처 등) 필드 암호화에 사용하는 AES-GCM 키 묶음.
  110. /// Keys 딕셔너리는 KeyVersion -> Base64(32바이트) 매핑. CurrentKeyVersion 은 신규 암호화에 사용할 버전.
  111. /// 키 회전 시 Keys 에 새 버전 추가 + CurrentKeyVersion 갱신. 복호화는 row 의 KeyVersion 컬럼으로 매핑하여 과거 키 사용.
  112. /// </summary>
  113. public class EncryptionSection
  114. {
  115. public int CurrentKeyVersion { get; init; } = 1;
  116. public Dictionary<string, string> Keys { get; init; } = new();
  117. }
  118. /// <summary>
  119. /// 소셜 로그인 OAuth 설정 (개미투자 D5 — Naver/Kakao). 키는 각 개발자센터 발급 후 채움 (빈 값이면 해당 provider 로그인 비활성).
  120. /// Google 은 DB Config(External.GoogleClientId) 현행 유지 — 여기 포함하지 않는다.
  121. /// </summary>
  122. public class OAuthSection
  123. {
  124. public OAuthProviderSection Naver { get; init; } = new();
  125. public OAuthProviderSection Kakao { get; init; } = new();
  126. public class OAuthProviderSection
  127. {
  128. /// <summary>네이버: Client ID / 카카오: REST API 키</summary>
  129. public string ClientId { get; init; } = string.Empty;
  130. /// <summary>네이버: Client Secret(필수) / 카카오: 콘솔에서 활성화한 경우만 (빈 값이면 미전송)</summary>
  131. public string ClientSecret { get; init; } = string.Empty;
  132. /// <summary>기본 redirect URI — 요청에서 미전달 시 사용. 개발자센터에 등록한 값과 일치해야 함</summary>
  133. public string RedirectUri { get; init; } = string.Empty;
  134. /// <summary>
  135. /// 카카오 Admin 키 — "연결 끊기(unlink)" webhook 요청의 Authorization 헤더 대조에 사용.
  136. /// 카카오 개발자콘솔의 앱 Admin 키를 그대로 넣는다 (빈 값이면 disconnect 검증 비활성 → 항상 거부).
  137. /// 네이버는 사용하지 않음.
  138. /// </summary>
  139. public string AdminKey { get; init; } = string.Empty;
  140. }
  141. }
  142. /// <summary>
  143. /// 주식 데이터 수집 배치 설정 (개미투자 D1). 플래그 기본 false — API 키 발급/운영 결정 후 활성화.
  144. /// Features:Channel 게이트와 무관하게 별도 등록 (AddStockDataServices).
  145. /// </summary>
  146. public class StockDataSection
  147. {
  148. /// <summary>종목 마스터 동기화 배치 (StockMasterSyncService) 활성화</summary>
  149. public bool MasterSync { get; init; } = false;
  150. /// <summary>T+1 일별 시세 수집 배치 (DailyPriceSyncService) 활성화</summary>
  151. public bool DailyPriceSync { get; init; } = false;
  152. /// <summary>마스터 동기화 실행 시각 (KST, "HH:mm")</summary>
  153. public string MasterSyncTime { get; init; } = "07:30";
  154. /// <summary>일별 시세 수집 실행 시각 (KST, "HH:mm") — 금융위 API 는 영업일+1 13시 이후 반영</summary>
  155. public string DailyPriceSyncTime { get; init; } = "13:10";
  156. public DataGoKrSection DataGoKr { get; init; } = new();
  157. /// <summary>공공데이터포털 (data.go.kr) 금융위 API</summary>
  158. public class DataGoKrSection
  159. {
  160. /// <summary>서비스 키 (Decoding 원본 키 — 요청 시 URL 인코딩). 비어 있으면 배치는 로그만 남기고 skip.</summary>
  161. public string ServiceKey { get; init; } = string.Empty;
  162. public string BaseUrl { get; init; } = "https://apis.data.go.kr";
  163. /// <summary>페이지당 행 수 (numOfRows)</summary>
  164. public int PageSize { get; init; } = 1000;
  165. }
  166. }
  167. /// <summary>
  168. /// KRX 한국거래소 OpenAPI (data-dbg.krx.co.kr) 지수 데이터 수집 설정 (개미투자 D1).
  169. /// data.go.kr(StockDataSection)과는 별개 시스템 — 별도 API 키 + AUTH_KEY 헤더 인증.
  170. /// IndexSync 기본 false — API 키 발급/운영 결정 후 활성화. Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록.
  171. /// </summary>
  172. public class KRXCoKrSection
  173. {
  174. /// <summary>KRX OpenAPI 인증 키 (AUTH_KEY 헤더). 비어 있으면 배치는 로그만 남기고 skip. tracked 파일엔 빈 값만.</summary>
  175. public string ApiKey { get; init; } = string.Empty;
  176. public string BaseUrl { get; init; } = "https://data-dbg.krx.co.kr";
  177. /// <summary>지수 일별시세 수집 배치 (IndexPriceSyncService) 활성화 — KOSPI/KOSDAQ/KRX + 파생상품지수(idx/drvprod_dd_trd) 4개 엔드포인트 한 배치</summary>
  178. public bool IndexSync { get; init; } = false;
  179. /// <summary>지수 수집 실행 시각 (KST, "HH:mm") — KRX 지수는 장 마감(15:30) 이후 확정, 여유 두고 18:30</summary>
  180. public string IndexSyncTime { get; init; } = "18:30";
  181. /// <summary>채권지수 일별시세 수집 배치 (BondIndexPriceSyncService, idx/bon_dd_trd) 활성화 — 응답 shape 이 지수와 달라 별도 배치</summary>
  182. public bool BondIndexSync { get; init; } = false;
  183. /// <summary>채권지수 수집 실행 시각 (KST, "HH:mm") — 지수 수집(18:30) 뒤 여유 두고 18:35</summary>
  184. public string BondIndexSyncTime { get; init; } = "18:35";
  185. /// <summary>주식(종목) 마스터 + 일별매매 수집 배치 (KrxStockMasterSyncService/KrxDailyPriceSyncService) 활성화 — D1 KRX 주식 데이터 수집</summary>
  186. public bool StockSync { get; init; } = false;
  187. /// <summary>종목 마스터 동기화 실행 시각 (KST, "HH:mm") — 장 시작 전 07:40</summary>
  188. public string MasterSyncTime { get; init; } = "07:40";
  189. /// <summary>주식 일별매매 수집 실행 시각 (KST, "HH:mm") — KRX 일별매매는 장 마감(15:30) 이후 확정, 여유 두고 18:10</summary>
  190. public string StockSyncTime { get; init; } = "18:10";
  191. /// <summary>증권상품(ETF/ETN/ELW) 일별매매 수집 배치 (KrxEtpSyncService) 활성화 — KRX 증권상품(ETF/ETN/ELW) 수집</summary>
  192. public bool EtpSync { get; init; } = false;
  193. /// <summary>증권상품(ETF/ETN/ELW) 일별매매 수집 실행 시각 (KST, "HH:mm") — 장 마감(15:30) 이후 확정, 주식 수집(18:10) 뒤 여유 두고 18:20</summary>
  194. public string EtpSyncTime { get; init; } = "18:20";
  195. /// <summary>신주인수권증권/증서 일별매매 수집 배치 (KrxWarrantSyncService) 활성화 — sw_bydd_trd(증권)+sr_bydd_trd(증서)</summary>
  196. public bool WarrantSync { get; init; } = false;
  197. /// <summary>신주인수권증권/증서 일별매매 수집 실행 시각 (KST, "HH:mm") — 증권상품 수집(18:20) 뒤 여유 두고 18:25</summary>
  198. public string WarrantSyncTime { get; init; } = "18:25";
  199. /// <summary>채권(국채전문유통/일반채권/소액채권) 일별매매 수집 배치 (KrxBondSyncService) 활성화 — kts/bnd/smb_bydd_trd 3개 엔드포인트</summary>
  200. public bool BondSync { get; init; } = false;
  201. /// <summary>채권 일별매매 수집 실행 시각 (KST, "HH:mm") — 신주인수권 수집(18:25) 뒤 여유 두고 18:40</summary>
  202. public string BondSyncTime { get; init; } = "18:40";
  203. /// <summary>파생상품(선물 3 + 옵션 3) 일별매매 수집 배치 (KrxDerivativeSyncService) 활성화 — fut/eqsfu/eqkfu + opt/eqsop/eqkop 6개 엔드포인트</summary>
  204. public bool DerivativeSync { get; init; } = false;
  205. /// <summary>파생상품 일별매매 수집 실행 시각 (KST, "HH:mm") — 채권 수집(18:40, 3ep×백필)이 5분 슬롯을 넘을 수 있어 여유 두고 19:00</summary>
  206. public string DerivativeSyncTime { get; init; } = "19:00";
  207. /// <summary>일반상품(석유/금/배출권) 일별매매 수집 배치 (KrxCommoditySyncService) 활성화 — gen/oil+gold+ets_bydd_trd 3개 엔드포인트</summary>
  208. public bool CommoditySync { get; init; } = false;
  209. /// <summary>일반상품 일별매매 수집 실행 시각 (KST, "HH:mm") — 파생상품 수집(19:00, 6ep×백필)이 무거워 여유 두고 19:15</summary>
  210. public string CommoditySyncTime { get; init; } = "19:15";
  211. /// <summary>ESG 데이터(ESG 증권상품 + ESG 지수 + 사회책임투자채권) 수집 배치 (KrxEsgSyncService) 활성화 — esg/esg_etp_info+esg_index_info+sri_bond_info 3개 엔드포인트</summary>
  212. public bool EsgSync { get; init; } = false;
  213. /// <summary>ESG 데이터 수집 실행 시각 (KST, "HH:mm") — 일반상품 수집(19:15) 뒤 여유 두고 19:30</summary>
  214. public string EsgSyncTime { get; init; } = "19:30";
  215. /// <summary>백필 대상 기간(연) — endDate 로부터 과거로 이만큼 채운다 (기본 3년). KrxBackfill 공용 헬퍼가 사용</summary>
  216. public int BackfillYears { get; init; } = 3;
  217. /// <summary>1회 실행당 최대 백필 fetch 수 — quota 보호용 상한 (기본 60). 여러 날에 걸쳐 나눠 채운다</summary>
  218. public int BackfillMaxPerRun { get; init; } = 60;
  219. }
  220. /// <summary>
  221. /// OpenDART(전자공시, opendart.fss.or.kr) 공시 수집 설정 (개미투자 D1). KRX/data.go.kr 과는 별개 시스템 —
  222. /// 인증은 query param crtfc_key (헤더 아님). DisclosureSync 기본 false — API 키 발급/운영 결정 후 활성화.
  223. /// Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록. tracked 파일엔 ApiKey 빈 값만.
  224. /// </summary>
  225. public class OpenDartSection
  226. {
  227. /// <summary>OpenDART 인증 키 (crtfc_key query param). 비어 있으면 배치는 로그만 남기고 skip.</summary>
  228. public string ApiKey { get; init; } = string.Empty;
  229. public string BaseUrl { get; init; } = "https://opendart.fss.or.kr";
  230. /// <summary>공시 목록 수집 배치 (DisclosureSyncService) 활성화</summary>
  231. public bool DisclosureSync { get; init; } = false;
  232. /// <summary>공시 수집 실행 시각 (KST, "HH:mm") — 전영업일 공시 반영 여유 두고 08:30</summary>
  233. public string DisclosureSyncTime { get; init; } = "08:30";
  234. /// <summary>백필 대상 기간(일) — today 로부터 과거로 이만큼 접수일 범위를 훑는다 (기본 30일 — 공시는 건수가 많아 최근 30일 권장)</summary>
  235. public int BackfillDays { get; init; } = 30;
  236. }
  237. /// <summary>
  238. /// 한국수출입은행(koreaexim.go.kr) OpenAPI 거시·환율 수집 설정 (개미투자 D1). KRX/data.go.kr/OpenDART 와 별개 시스템 —
  239. /// 인증은 query param authkey, 데이터 종류별(AP01 환율/AP02 대출금리/AP03 국제금리)로 별도 키가 발급된다.
  240. /// MacroSync 기본 false — API 키 발급/운영 결정 후 활성화. Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록.
  241. /// tracked 파일엔 각 키 빈 값만 (실키는 gitignore 된 appsettings.Development.json / Jenkins Secret File).
  242. /// 주의(koreaexim quirk): 주말/공휴일과 영업일 오전 ~11시 이전엔 빈 배열([])을 반환하고, 키 오류·만료 시 result 코드 2~4 또는 빈 응답이 온다 → 파서가 안전하게 0건 처리.
  243. /// </summary>
  244. public class KoreaEximSection
  245. {
  246. /// <summary>현재환율(AP01) 인증 키 (authkey query param). 비어 있으면 환율 수집 skip.</summary>
  247. public string ExchangeKey { get; init; } = string.Empty;
  248. /// <summary>대출금리(AP02) 인증 키. 비어 있으면 대출금리 수집 skip.</summary>
  249. public string LoanRateKey { get; init; } = string.Empty;
  250. /// <summary>국제금리(AP03) 인증 키. 비어 있으면 국제금리 수집 skip.</summary>
  251. public string IntlRateKey { get; init; } = string.Empty;
  252. public string BaseUrl { get; init; } = "https://www.koreaexim.go.kr/site/program/financial/exchangeJSON";
  253. /// <summary>거시·환율 수집 배치 (MacroDataSyncService) 활성화</summary>
  254. public bool MacroSync { get; init; } = false;
  255. /// <summary>수집 실행 시각 (KST, "HH:mm") — koreaexim 은 영업일 ~11시 이후 반영되므로 여유 두고 11:30</summary>
  256. public string MacroSyncTime { get; init; } = "11:30";
  257. /// <summary>백필 대상 기간(일) — today 로부터 과거로 이만큼 훑으며 미적재 영업일을 채운다 (기본 30일)</summary>
  258. public int MacroBackfillDays { get; init; } = 30;
  259. /// <summary>1회 실행당 최대 백필 fetch 수 — quota 보호용 상한 (기본 60). 여러 날에 걸쳐 나눠 채운다</summary>
  260. public int BackfillMaxPerRun { get; init; } = 60;
  261. }
  262. /// <summary>
  263. /// SEIBro OpenAPI(한국예탁결제원 seibro.or.kr) 수집 설정 (개미투자 D1). KRX/data.go.kr/OpenDART 와 별개 시스템 —
  264. /// 인증은 query param key, 응답은 XML(value 속성). 일일 호출 한도는 카테고리 단위(기업/주식/채권/파생/외화)로 관리되며
  265. /// 배치 예산(*Budget)은 실한도의 80% 헤드룸 (docs/SEIBro/readme.md §1). 모든 *Sync 플래그 기본 false —
  266. /// API 키 발급/운영 결정 후 활성화. tracked 파일엔 ApiKey 빈 값만 (실키는 gitignore 된 appsettings.Development.json / Jenkins Secret File).
  267. /// </summary>
  268. public class SeibroSection
  269. {
  270. /// <summary>SEIBro OpenAPI 인증 키 (key query param). 비어 있으면 배치는 로그만 남기고 skip.</summary>
  271. public string ApiKey { get; init; } = string.Empty;
  272. /// <summary>SEIBro OpenPlatform 엔드포인트 — https 연결 불가 시 SeibroHttp 가 http 로 1회 fallback</summary>
  273. public string BaseUrl { get; init; } = "https://seibro.or.kr/OpenPlatform/callOpenAPI.jsp";
  274. /// <summary>Wave 0 — 발행회사번호(ISSUCO_CUSTNO) 조인키 부트스트랩 + 종목 보강 배치 (SeibroIssuerSyncService)</summary>
  275. public bool IssuerSync { get; init; } = false;
  276. /// <summary>Wave 1 — 배당·권리 수집 배치</summary>
  277. public bool DividendSync { get; init; } = false;
  278. /// <summary>Wave 2 — 수급 이벤트(대차·보호예수·증감·유통변경·비상장) 수집 배치</summary>
  279. public bool SupplySync { get; init; } = false;
  280. /// <summary>Wave 3 — 기업행위(총회·상호변경·대금·단주·CB/BW 행사) 수집 배치</summary>
  281. public bool CorpActionSync { get; init; } = false;
  282. /// <summary>Wave 4 — 채권·단기금융 수집 배치</summary>
  283. public bool BondSync { get; init; } = false;
  284. /// <summary>Wave 5 — 파생결합증권(ELS/DLS) 수집 배치</summary>
  285. public bool DerivSync { get; init; } = false;
  286. /// <summary>Wave 6 — 외화증권 수집 배치</summary>
  287. public bool ForeignSync { get; init; } = false;
  288. /// <summary>호출 사이 지연(ms) — quota·서버 보호</summary>
  289. public int DelayMs { get; init; } = 200;
  290. /// <summary>백필 대상 기간(연) — 날짜 스윕 배치가 과거로 이만큼 채운다</summary>
  291. public int BackfillYears { get; init; } = 3;
  292. /// <summary>기업 정보 카테고리 일일 예산 (실한도 50,000 의 80%)</summary>
  293. public int CorpBudget { get; init; } = 40000;
  294. /// <summary>주식 카테고리 일일 예산 (실한도 10,000 의 80%) — getXrcStk*(CB/BW 행사)도 주식 소속</summary>
  295. public int StockBudget { get; init; } = 8000;
  296. /// <summary>채권 카테고리 일일 예산 (실한도 10,000 의 80%)</summary>
  297. public int BondBudget { get; init; } = 8000;
  298. /// <summary>파생결합증권 카테고리 일일 예산 (실한도 10,000 의 80%)</summary>
  299. public int DerivBudget { get; init; } = 8000;
  300. /// <summary>외화증권 카테고리 일일 예산 (실한도 20,000 의 80%)</summary>
  301. public int ForeignBudget { get; init; } = 15000;
  302. /// <summary>대차거래 3년 백필 대상 종목 수 (시가총액 상위 N) — 당일 수집은 전 상장종목</summary>
  303. public int LendingBackfillTopN { get; init; } = 300;
  304. /// <summary>외화증권 국가별 수집 대상 국가코드 (Wave 6)</summary>
  305. public string[] ForeignNations { get; init; } = ["US", "JP", "HK", "CN", "E1"];
  306. /// <summary>외화증권 종목별 수집 seed ISIN (기본 빈 = 국가별만 수집)</summary>
  307. public string[] ForeignIsins { get; init; } = [];
  308. /// <summary>발행회사번호 부트스트랩 실행 시각 (KST, "HH:mm") — SEIBro 는 T+1 새벽 갱신 추정, KRX(18시대)와 분리해 06:30</summary>
  309. public string IssuerSyncTime { get; init; } = "06:30";
  310. /// <summary>배당·권리 수집 실행 시각 (KST, "HH:mm") — IssuerSync(06:30) 다음 슬롯 06:40 (wave 당 10분 간격 스태거)</summary>
  311. public string DividendSyncTime { get; init; } = "06:40";
  312. /// <summary>수급 이벤트 수집 실행 시각 (KST, "HH:mm") — DividendSync(06:40) 다음 슬롯 07:00</summary>
  313. public string SupplySyncTime { get; init; } = "07:00";
  314. /// <summary>기업행위·총회 수집 실행 시각 (KST, "HH:mm") — SupplySync(07:00) 다음 슬롯 07:20</summary>
  315. public string CorpActionSyncTime { get; init; } = "07:20";
  316. /// <summary>채권·단기금융 수집 실행 시각 (KST, "HH:mm") — CorpActionSync(07:20) 다음 슬롯 07:40</summary>
  317. public string BondSyncTime { get; init; } = "07:40";
  318. /// <summary>파생결합증권 수집 실행 시각 (KST, "HH:mm") — BondSync(07:40) 다음 슬롯 08:00</summary>
  319. public string DerivSyncTime { get; init; } = "08:00";
  320. /// <summary>외화증권 수집 실행 시각 (KST, "HH:mm") — DerivSync(08:00) 다음 슬롯 08:20</summary>
  321. public string ForeignSyncTime { get; init; } = "08:20";
  322. }
  323. /// <summary>
  324. /// KOSIS(국가통계포털, kosis.kr) OpenAPI 거시경제지표 수집 설정 (개미투자 거시 대시보드). KRX/data.go.kr/koreaexim/SEIBro 와 별개 시스템 —
  325. /// 인증은 query param apiKey, 응답은 JSON(statisticsData.do). 지표 정의(Indicators)를 config 로 두어 tblId/itmId/분류코드 교정이 코드 변경 없이 가능하게 한다.
  326. /// Sync 기본 false — API 키 발급/운영 결정 후 활성화. Features:Channel 게이트와 무관하게 AddStockDataServices 에서 등록.
  327. /// tracked 파일엔 ApiKey 빈 값만 (실키는 gitignore 된 appsettings.Development.json / Jenkins Secret File).
  328. /// KOSIS quirk: 통계표 하나에 여러 분류(C1 시도별/지수별)가 섞여 오므로 지표별 ClassCode(C1) 필터로 원하는 한 계열만 고른다.
  329. /// 미고시 시점은 DT 빈값("")/"-" 으로 오며 파서·수집이 skip. tblId 오류 시 { err, errMsg } JSON → 파서가 안전하게 0건 처리.
  330. /// </summary>
  331. public sealed class KosisSection
  332. {
  333. /// <summary>KOSIS OpenAPI 인증 키 (apiKey query param). 비어 있으면 배치는 로그만 남기고 skip.</summary>
  334. public string ApiKey { get; init; } = string.Empty;
  335. public string BaseUrl { get; init; } = "https://kosis.kr/openapi/Param/statisticsParameterData.do";
  336. /// <summary>거시경제지표 수집 배치 (KosisSyncService) 활성화</summary>
  337. public bool Sync { get; init; } = false;
  338. /// <summary>수집 실행 시각 (KST, "HH:mm") — KOSIS 지표는 월/분기 갱신이라 일 배치는 새벽 여유 시각 07:50</summary>
  339. public string SyncTime { get; init; } = "07:50";
  340. /// <summary>백필 대상 기간(연) — today 로부터 과거로 이만큼의 시점 범위를 조회한다 (기본 5년). 지표당 콜 수가 적어 여유 있게.</summary>
  341. public int BackfillYears { get; init; } = 5;
  342. /// <summary>지표 순회 시 호출 사이 지연(ms) — quota·서버 보호</summary>
  343. public int DelayMs { get; init; } = 300;
  344. /// <summary>
  345. /// 수집 대상 지표 정의 목록. 각 항목의 tblId/itmId/분류코드 교정은 여기 config 에서만 하면 되고 코드 변경 불필요.
  346. /// 기본 6종 (사용자 확정) — 고용률/실업률/취업자수(경제활동인구조사 DT_1DA7004S 동일표, itmId T90/T80/T30)·소비자물가지수(CPI)·수출액·수입액(무역통계 DT_1R11006_FRM101, itmId …T1/…T2). 환율은 KOSIS 제외 — koreaexim AP01 일별 매매기준율 사용(2026-07-06 원화 24h 거래 전환으로 일별/실시간 필요).
  347. /// 이 C# 기본값은 의도적으로 빈 목록([]) — .NET 구성 바인더가 List 에 append 하므로 여기에 항목을 넣으면 JSON(appsettings) 의 Indicators 와 중복(2배)된다. 실제 6종은 각 환경 appsettings(및 배포 템플릿)에 정의한다.
  348. /// </summary>
  349. public List<KosisIndicator> Indicators { get; init; } = [];
  350. /// <summary>
  351. /// 단일 거시지표 정의. Cycle 은 KOSIS prdSe (M=월, Q=분기, Y=연) — Period 형식(월 YYYYMM / 분기 YYYYQ / 연 YYYY)도 이로 결정된다.
  352. /// KOSIS 통계표는 여러 분류가 섞여 오므로 ItemId(itmId)·ObjL1(objL1)·ClassCode(응답 C1) 로 원하는 한 계열만 특정한다.
  353. /// - ItemId/ObjL1 은 요청 파라미터(미지정 시 "ALL"), ClassCode 는 응답 필터(비면 첫 계열 사용).
  354. /// </summary>
  355. public sealed class KosisIndicator
  356. {
  357. /// <summary>지표 내부코드 (MacroIndicator.Code) — 예: CPI</summary>
  358. public string Code { get; init; } = string.Empty;
  359. /// <summary>지표 표시명 — 예: 소비자물가지수</summary>
  360. public string Name { get; init; } = string.Empty;
  361. /// <summary>기관 ID (orgId) — 예: 통계청 101, 한국은행 301</summary>
  362. public string OrgId { get; init; } = string.Empty;
  363. /// <summary>통계표 ID (tblId) — 예: DT_1J22003</summary>
  364. public string TblId { get; init; } = string.Empty;
  365. /// <summary>항목 ID (itmId) — 미지정 시 "ALL"</summary>
  366. public string ItmId { get; init; } = "ALL";
  367. /// <summary>분류1 (objL1) — 미지정 시 "ALL"</summary>
  368. public string ObjL1 { get; init; } = "ALL";
  369. /// <summary>주기 (prdSe): M=월, Q=분기, Y=연</summary>
  370. public string Cycle { get; init; } = "M";
  371. /// <summary>응답 분류코드(C1) 필터 — 통계표에 여러 계열이 섞여 올 때 이 C1 값 행만 채택. 비면 응답 첫 계열(첫 시점의 첫 행 C1)로 자동 고정.</summary>
  372. public string ClassCode { get; init; } = string.Empty;
  373. }
  374. }
  375. /// <summary>
  376. /// 세계 주요국 증시지수 스냅샷 수집 설정 (메인 세계지도 핀). 무료·무키 소스(Yahoo Finance v8 chart) 를 심볼별로 하루 1회 조회해
  377. /// 심볼당 최신 종가 1행 + 스파크라인을 upsert 한다. 국내(코스피)는 별도로 IndexDailyPrice 실데이터에서 병합하므로 여기 Items 에 넣지 않는다.
  378. /// Enabled 기본 false — 배포 시 아웃바운드 도달성 확인 후 활성화. tracked 파일엔 Items 를 두지 않고 각 환경 appsettings 에 정의(중복 append 방지, KOSIS 와 동일 정책).
  379. /// </summary>
  380. public sealed class WorldIndexSection
  381. {
  382. /// <summary>세계지수 수집 배치 (WorldIndexSyncService) 활성화</summary>
  383. public bool Enabled { get; init; } = false;
  384. /// <summary>시세 호스트 (Yahoo Finance v8 — 경로·쿼리는 서비스가 구성)</summary>
  385. public string BaseUrl { get; init; } = "https://query1.finance.yahoo.com";
  386. /// <summary>수집 실행 시각 (KST, "HH:mm") — 미국장 마감(익일 새벽 KST) 이후 확정치 확보 위해 07:00</summary>
  387. public string SyncTime { get; init; } = "07:00";
  388. /// <summary>호출 실패 시 재시도 간격 분 (기본 60)</summary>
  389. public int RetryDelayMinutes { get; init; } = 60;
  390. /// <summary>
  391. /// 수집 대상 지수 목록 — 나라당 대표 지수 1종(핀 1개). Symbol 은 Yahoo 표기(예: ^GSPC). CountryCode 는 ISO2(프론트 좌표맵 키).
  392. /// C# 기본값은 의도적으로 빈 목록([]) — 구성 바인더가 List 에 append 하므로 실제 목록은 각 환경 appsettings 에 정의한다(KOSIS 와 동일).
  393. /// </summary>
  394. public List<WorldIndexItem> Items { get; init; } = [];
  395. /// <summary>단일 세계지수 정의.</summary>
  396. public sealed class WorldIndexItem
  397. {
  398. /// <summary>Yahoo Finance 심볼 — 예: ^GSPC(S&amp;P500), ^N225(닛케이225), ^HSI(항셍), 000001.SS(상하이종합), ^FTSE(FTSE100), ^GDAXI(DAX), ^FCHI(CAC40)</summary>
  399. public string Symbol { get; init; } = string.Empty;
  400. /// <summary>지수 표시명 — 예: S&amp;P 500</summary>
  401. public string Name { get; init; } = string.Empty;
  402. /// <summary>국가 코드 (ISO2, 대문자) — 프론트 EXCHANGE_GEO 좌표맵 키. 나라당 1개(핀 중복 방지)</summary>
  403. public string CountryCode { get; init; } = string.Empty;
  404. /// <summary>거래소·시장 표기 — 예: NYSE·Nasdaq</summary>
  405. public string ExchangeName { get; init; } = string.Empty;
  406. }
  407. }
  408. /// <summary>
  409. /// 주요 종목·상품 시세 스냅샷 수집 설정 (주요종목현황·상품선물 그리드). WorldIndex 와 동일 소스(Yahoo Finance v8 chart, 무키) — 거래량 포함.
  410. /// Enabled 기본 false. tracked 파일엔 Items 를 두지 않고 각 환경 appsettings 에 정의(중복 append 방지, KOSIS/WorldIndex 와 동일 정책).
  411. /// </summary>
  412. public sealed class MarketQuoteSection
  413. {
  414. /// <summary>시세 수집 배치 (MarketQuoteSyncService) 활성화</summary>
  415. public bool Enabled { get; init; } = false;
  416. /// <summary>시세 호스트 (Yahoo Finance v8 — 경로·쿼리는 서비스가 구성)</summary>
  417. public string BaseUrl { get; init; } = "https://query1.finance.yahoo.com";
  418. /// <summary>수집 실행 시각 (KST, "HH:mm") — 세계지수(07:00) 직후 07:10</summary>
  419. public string SyncTime { get; init; } = "07:10";
  420. /// <summary>호출 실패 시 재시도 간격 분 (기본 60)</summary>
  421. public int RetryDelayMinutes { get; init; } = 60;
  422. /// <summary>
  423. /// 수집 대상 목록 — 종목/상품 단위. Symbol 은 Yahoo 표기(예: AAPL, CL=F). GroupCode 는 프론트 탭 키(예: us-major).
  424. /// C# 기본값은 빈 목록([]) — 실제 목록은 각 환경 appsettings 에 정의(WorldIndex 와 동일).
  425. /// </summary>
  426. public List<MarketQuoteItem> Items { get; init; } = [];
  427. /// <summary>단일 시세 항목 정의.</summary>
  428. public sealed class MarketQuoteItem
  429. {
  430. /// <summary>Yahoo Finance 심볼 — 예: AAPL(Apple), CL=F(WTI), GC=F(금), USDKRW=X(원/달러)</summary>
  431. public string Symbol { get; init; } = string.Empty;
  432. /// <summary>표시명 — 예: Apple</summary>
  433. public string Name { get; init; } = string.Empty;
  434. /// <summary>카테고리 — Stock|Commodity|Futures|Fx|BondYield (대소문자 무시, 미매칭 시 Stock)</summary>
  435. public string Category { get; init; } = "Stock";
  436. /// <summary>프론트 그룹 키 — 예: us-major, asia-major, commodity</summary>
  437. public string GroupCode { get; init; } = string.Empty;
  438. /// <summary>국가 코드 (ISO2). 상품·선물 등은 빈값.</summary>
  439. public string CountryCode { get; init; } = string.Empty;
  440. }
  441. }
  442. }