first commit

This commit is contained in:
hayato5246
2026-03-31 19:32:59 +09:00
commit d10b794c9f
78 changed files with 1671595 additions and 0 deletions

132
services/order_service.go Normal file
View File

@@ -0,0 +1,132 @@
package services
import (
"encoding/json"
"fmt"
)
// OrderService 주식 주문 서비스 (매수/매도/정정/취소)
type OrderService struct {
client *KiwoomClient
}
var orderService *OrderService
// GetOrderService 주문 서비스 싱글턴 반환
func GetOrderService() *OrderService {
if orderService == nil {
orderService = &OrderService{client: GetKiwoomClient()}
}
return orderService
}
// OrderRequest 주문 요청 파라미터
type OrderRequest struct {
Exchange string // KRX, NXT, SOR
Code string // 종목코드
Qty string // 주문수량
Price string // 주문단가 (시장가 시 빈 문자열)
TradeTP string // 0:보통, 3:시장가, 5:조건부지정가, 6:최유리, 7:최우선
}
// OrderResult 주문 응답
type OrderResult struct {
OrderNo string `json:"orderNo"`
ReturnCode int `json:"returnCode"`
ReturnMsg string `json:"returnMsg"`
}
// Buy 매수주문 (kt10000)
func (s *OrderService) Buy(req OrderRequest) (*OrderResult, error) {
body := map[string]string{
"dmst_stex_tp": req.Exchange,
"stk_cd": req.Code,
"ord_qty": req.Qty,
"ord_uv": req.Price,
"trde_tp": req.TradeTP,
"cond_uv": "",
}
respBody, err := s.client.post("kt10000", "/api/dostk/ordr", body)
if err != nil {
return nil, fmt.Errorf("매수주문 실패: %w", err)
}
return parseOrderResult(respBody)
}
// Sell 매도주문 (kt10001)
func (s *OrderService) Sell(req OrderRequest) (*OrderResult, error) {
body := map[string]string{
"dmst_stex_tp": req.Exchange,
"stk_cd": req.Code,
"ord_qty": req.Qty,
"ord_uv": req.Price,
"trde_tp": req.TradeTP,
"cond_uv": "",
}
respBody, err := s.client.post("kt10001", "/api/dostk/ordr", body)
if err != nil {
return nil, fmt.Errorf("매도주문 실패: %w", err)
}
return parseOrderResult(respBody)
}
// Modify 정정주문 (kt10002)
func (s *OrderService) Modify(exchange, origOrdNo, code, qty, price string) (*OrderResult, error) {
body := map[string]string{
"dmst_stex_tp": exchange,
"orig_ord_no": origOrdNo,
"stk_cd": code,
"mdfy_qty": qty,
"mdfy_uv": price,
"mdfy_cond_uv": "",
}
respBody, err := s.client.post("kt10002", "/api/dostk/ordr", body)
if err != nil {
return nil, fmt.Errorf("정정주문 실패: %w", err)
}
return parseOrderResult(respBody)
}
// Cancel 취소주문 (kt10003)
// qty: "0" = 전량취소
func (s *OrderService) Cancel(exchange, origOrdNo, code, qty string) (*OrderResult, error) {
body := map[string]string{
"dmst_stex_tp": exchange,
"orig_ord_no": origOrdNo,
"stk_cd": code,
"cncl_qty": qty,
}
respBody, err := s.client.post("kt10003", "/api/dostk/ordr", body)
if err != nil {
return nil, fmt.Errorf("취소주문 실패: %w", err)
}
return parseOrderResult(respBody)
}
// parseOrderResult 주문 응답 JSON 파싱 공통 함수
func parseOrderResult(body []byte) (*OrderResult, error) {
var result struct {
OrdNo string `json:"ord_no"`
ReturnCode int `json:"return_code"`
ReturnMsg string `json:"return_msg"`
}
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("주문 응답 파싱 실패: %w", err)
}
if result.ReturnCode != 0 {
return nil, fmt.Errorf("주문 오류: %s", result.ReturnMsg)
}
return &OrderResult{
OrderNo: result.OrdNo,
ReturnCode: result.ReturnCode,
ReturnMsg: result.ReturnMsg,
}, nil
}