133 lines
3.4 KiB
Go
133 lines
3.4 KiB
Go
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
|
|
}
|