Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: 해외 주식 매수, 매도 기능 구현(#67) #68

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,28 @@
public class KisProperties {

private String url;
private String orderTrId;
private String sellTrId;
private String getBalanceTrId;
private String getBalanceRealizedProfitAndLossTrId;
private String getPriceTrId;
private String findDomesticStockPriceChartId;
private Domestic domestic;
private International international;

@Getter
@Setter
public static class Domestic {
private String buyTrId;
private String sellTrId;
private String findBalanceTrId;
private String findBalanceRealizedProfitAndLossTrId;
private String findPriceTrId;
private String findDomesticStockPriceChartId;
}

@Getter
@Setter
public static class International {
private String buyTrId;
private String sellTrId;
private String findBalanceTrId;
private String findBalanceRealizedProfitAndLossTrId;
private String findPriceTrId;
private String findDomesticStockPriceChartId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
import org.springframework.transaction.annotation.Transactional;

import kakaobootcamp.backend.domains.autoTrade.AutoTradeService;
import kakaobootcamp.backend.domains.stock.service.StockService;
import kakaobootcamp.backend.domains.stock.service.DomesticStockService;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SchedulingService {

private final AutoTradeService autoTradeService;
private final StockService stockService;
private final DomesticStockService domesticStockService;

@Async
@Scheduled(cron = "0 0 11 * * MON-FRI")
Expand All @@ -24,6 +24,6 @@ public void doAutoTrade() {
@Async
@Scheduled(cron = "0 0 20 * * MON-SAT")
public void updateSuggestedKeywords() {
stockService.updateDomesticStocks();
domesticStockService.updateDomesticStocks();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
import kakaobootcamp.backend.domains.member.MemberService;
import kakaobootcamp.backend.domains.member.domain.AutoTradeState;
import kakaobootcamp.backend.domains.member.domain.Member;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.GetStockBalanceResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.GetStockBalanceResponse.Output1;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.OrderStockRequest;
import kakaobootcamp.backend.domains.stock.service.StockService;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindStockBalanceResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindStockBalanceResponse.Output1;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.OrderStockRequest;
import kakaobootcamp.backend.domains.stock.service.DomesticStockService;
import kakaobootcamp.backend.domains.transaction.TransactionService;
import kakaobootcamp.backend.domains.transaction.domain.Transaction;
import lombok.RequiredArgsConstructor;
Expand All @@ -29,7 +29,7 @@
public class AutoTradeService {

private final AiServerService aiServerService;
private final StockService stockService;
private final DomesticStockService domesticStockService;
private final TransactionService transactionService;
private final MemberService memberService;
private final BrokerService brokerService;
Expand All @@ -52,7 +52,7 @@ private void executeAutoTradeForTransaction(Transaction transaction) {
}

// 현재 보유 주식량 및 예수금 조회
GetStockBalanceResponse stockBalanceResponse = stockService.getStockBalance(member, "", "");
FindStockBalanceResponse stockBalanceResponse = domesticStockService.findStockBalance(member, "", "");

// 거래 가능 금액 계산
int availableBalance = calculateAvailableBalance(
Expand Down Expand Up @@ -127,9 +127,9 @@ private void placeOrder(Member member, String productNumber, int quantity, boole
.build();

if (isBuy) {
stockService.orderStock(member, request);
domesticStockService.orderStock(member, request, true);
} else {
stockService.sellStock(member, request);
domesticStockService.orderStock(member, request, false);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,31 @@
import kakaobootcamp.backend.common.dto.ErrorResponse;
import kakaobootcamp.backend.common.util.memberLoader.MemberLoader;
import kakaobootcamp.backend.domains.member.domain.Member;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.FindDomesticStockPopularChartResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.FindDomesticStockPriceChartResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.FindSuggestedKeywordResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.GetStockBalanceRealizedProfitAndLossResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.GetStockBalanceResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.GetStockPriceResponse;
import kakaobootcamp.backend.domains.stock.dto.StockDTO.OrderStockRequest;
import kakaobootcamp.backend.domains.stock.service.StockService;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindDomesticStockPopularChartResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindStockBalanceRealizedProfitAndLossResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindStockBalanceResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindStockPriceChartResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindStockPriceResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.FindSuggestedKeywordResponse;
import kakaobootcamp.backend.domains.stock.dto.DomesticStockDTO.OrderStockRequest;
import kakaobootcamp.backend.domains.stock.service.DomesticStockService;
import lombok.RequiredArgsConstructor;

@Tag(name = "STOCK API", description = "주식에 대한 API입니다.")
@Tag(name = "DOMESTIC STOCK API", description = "국내 주식에 대한 API입니다.")
@RestController
@RequestMapping("/api/stocks")
@RequestMapping("/api/stocks/domestic")
@RequiredArgsConstructor
public class StockController {
public class DomesticStockController {

private final MemberLoader memberLoader;
private final StockService stockService;
private final DomesticStockService domesticStockService;

// 삭제될 API
@PostMapping("/buy")
public ResponseEntity<DataResponse<Void>> buyStock(OrderStockRequest request) {
Member member = memberLoader.getMember();

stockService.orderStock(member, request);
domesticStockService.orderStock(member, request, true);

return ResponseEntity.ok(DataResponse.ok());
}
Expand All @@ -58,13 +58,13 @@ public ResponseEntity<DataResponse<Void>> buyStock(OrderStockRequest request) {
public ResponseEntity<DataResponse<Void>> sellStock(OrderStockRequest request) {
Member member = memberLoader.getMember();

stockService.sellStock(member, request);
domesticStockService.orderStock(member, request, false);

return ResponseEntity.ok(DataResponse.ok());
}

@GetMapping("/recommendations")
public ResponseEntity<DataResponse<List<FindDomesticStockPopularChartResponse>>> getStockRecommendations(
public ResponseEntity<DataResponse<List<FindDomesticStockPopularChartResponse>>> findStockRecommendations(
@RequestParam("size") @Min(value = 1, message = "size는 1이상이어야 합니다.") @Max(value = 10, message = "size는 10이하이어야 합니다.") int size,
@RequestParam("page") @Min(value = 0, message = "page는 0이상이어야 합니다.") int page) {
Pageable pageable = PageRequest.of(page, size);
Expand Down Expand Up @@ -117,12 +117,12 @@ public ResponseEntity<DataResponse<List<FindDomesticStockPopularChartResponse>>>
)
}
)
public ResponseEntity<DataResponse<GetStockBalanceResponse>> getStockBalance(
public ResponseEntity<DataResponse<FindStockBalanceResponse>> findStockBalance(
@RequestParam(name = "CTX_AREA_FK100", defaultValue = "") String fk,
@RequestParam(name = "CTX_AREA_NK100", defaultValue = "") String nk) {
Member member = memberLoader.getMember();

GetStockBalanceResponse response = stockService.getStockBalance(member, fk, nk);
FindStockBalanceResponse response = domesticStockService.findStockBalance(member, fk, nk);

return ResponseEntity.ok(DataResponse.from(response));
}
Expand Down Expand Up @@ -155,11 +155,11 @@ public ResponseEntity<DataResponse<GetStockBalanceResponse>> getStockBalance(
)
}
)
public ResponseEntity<DataResponse<GetStockBalanceRealizedProfitAndLossResponse>> getBalanceRealizedProfitAndLoss() {
public ResponseEntity<DataResponse<FindStockBalanceRealizedProfitAndLossResponse>> findBalanceRealizedProfitAndLoss() {
Member member = memberLoader.getMember();

GetStockBalanceRealizedProfitAndLossResponse response = stockService
.getBalanceRealizedProfitAndLoss(member);
FindStockBalanceRealizedProfitAndLossResponse response = domesticStockService
.findBalanceRealizedProfitAndLoss(member);

return ResponseEntity.ok(DataResponse.from(response));
}
Expand Down Expand Up @@ -191,11 +191,11 @@ public ResponseEntity<DataResponse<GetStockBalanceRealizedProfitAndLossResponse>
)
}
)
public ResponseEntity<DataResponse<GetStockPriceResponse>> getStockPrice(
public ResponseEntity<DataResponse<FindStockPriceResponse>> findStockPrice(
@RequestParam("productNumber") String productNumber) {
Member member = memberLoader.getMember();

GetStockPriceResponse response = stockService.getStockPrice(member, productNumber);
FindStockPriceResponse response = domesticStockService.findStockPrice(member, productNumber);

return ResponseEntity.ok(DataResponse.from(response));
}
Expand Down Expand Up @@ -229,14 +229,14 @@ public ResponseEntity<DataResponse<GetStockPriceResponse>> getStockPrice(
)
public ResponseEntity<DataResponse<List<FindSuggestedKeywordResponse>>> findSuggestedKeywords(
@RequestParam("keyword") String keyword) {
List<FindSuggestedKeywordResponse> responses = stockService.findSuggestedKeywords(keyword);
List<FindSuggestedKeywordResponse> responses = domesticStockService.findSuggestedKeywords(keyword);

return ResponseEntity.ok(DataResponse.from(responses));
}

@GetMapping("/update/removed")
public ResponseEntity<?> updateRemovedStocks() {
stockService.updateDomesticStocks();
domesticStockService.updateDomesticStocks();

return ResponseEntity.ok().build();
}
Expand Down Expand Up @@ -269,12 +269,12 @@ public ResponseEntity<?> updateRemovedStocks() {
)
}
)
public ResponseEntity<DataResponse<FindDomesticStockPriceChartResponse>> findDomesticStockPriceChart(
public ResponseEntity<DataResponse<FindStockPriceChartResponse>> findStockPriceChart(
@RequestParam("productNumber") String productNumber,
@Pattern(regexp = "D|W|M|Y", message = "periodCode는 D, W, M, Y 중 하나여야 합니다.") @RequestParam("periodCode") String periodCode) {
Member member = memberLoader.getMember();

FindDomesticStockPriceChartResponse response = stockService.findDomesticStockPriceChart(member, productNumber,
FindStockPriceChartResponse response = domesticStockService.findStockPriceChart(member, productNumber,
periodCode);

return ResponseEntity.ok(DataResponse.from(response));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package kakaobootcamp.backend.domains.stock.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.tags.Tag;
import kakaobootcamp.backend.common.dto.DataResponse;
import kakaobootcamp.backend.common.util.memberLoader.MemberLoader;
import kakaobootcamp.backend.domains.member.domain.Member;
import kakaobootcamp.backend.domains.stock.dto.InternationalStockDTO.InternationalOrderStockRequest;
import kakaobootcamp.backend.domains.stock.service.InternationalStockService;
import lombok.RequiredArgsConstructor;

@Tag(name = "INTERNATIONAL STOCK API", description = "해외 주식에 대한 API입니다.")
@RestController
@RequestMapping("/api/stock/international")
@RequiredArgsConstructor
public class InternationalStockController {

private final MemberLoader memberLoader;
private final InternationalStockService internationalStockService;

// 삭제될 API
@PostMapping("/buy")
public ResponseEntity<DataResponse<Void>> buyStock(InternationalOrderStockRequest request) {
Member member = memberLoader.getMember();

internationalStockService.orderStock(member, request, true);

return ResponseEntity.ok(DataResponse.ok());
}

@PostMapping("/sell")
public ResponseEntity<DataResponse<Void>> sellStock(InternationalOrderStockRequest request) {
Member member = memberLoader.getMember();

internationalStockService.orderStock(member, request, false);

return ResponseEntity.ok(DataResponse.ok());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

public class StockDTO {
public class DomesticStockDTO {

@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public static class OrderStockRequest {

@JsonProperty("PDNO")
private String PDNO; // 종목코드 (6자리, 최대 12자리)

Expand All @@ -36,15 +34,6 @@ public static class OrderStockRequest {
private String ORD_UNPR; // 주문단가 (최대 19자리)
}

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class KisBaseResponse {

private String rt_cd; // 응답 코드
private String msg_cd; // 메시지 코드
private String msg1; // 메시지 내용
}

@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
Expand Down Expand Up @@ -94,7 +83,7 @@ public static class Output {

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class GetStockBalanceResponse extends KisBaseResponse {
public static class FindStockBalanceResponse extends KisBaseResponse {

private String ctx_area_fk100; // 연속조회검색조건100
private String ctx_area_nk100; // 연속조회키100
Expand Down Expand Up @@ -164,7 +153,7 @@ public static class Output2 {

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class GetStockBalanceRealizedProfitAndLossResponse extends KisBaseResponse {
public static class FindStockBalanceRealizedProfitAndLossResponse extends KisBaseResponse {

private List<Output1> output1; // 응답상세1 (Object Array)
private List<Output2> output2; // 응답상세2 (Object)
Expand Down Expand Up @@ -233,7 +222,7 @@ public static class Output2 {

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class GetStockPriceResponse extends KisBaseResponse {
public static class FindStockPriceResponse extends KisBaseResponse {

private Output output; // 응답상세

Expand Down Expand Up @@ -411,7 +400,7 @@ public static FindSuggestedKeywordResponse from(DomesticStock domesticStock) {

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class FindDomesticStockPriceChartResponse extends KisBaseResponse {
public static class FindStockPriceChartResponse extends KisBaseResponse {

private Output1 output1; // 응답 상세
private List<Output2> output2; // 일별 데이터 배열
Expand Down
Loading