Skip to content

Commit df5fb50

Browse files
authored
quant 관련 API (#12)
1 parent ec97b12 commit df5fb50

29 files changed

Lines changed: 927 additions & 7 deletions
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
package com.backend.crame.domain.apikey.dto;
22

3-
public record ApiKeyResponse(String nickName, String publicKey, String secretKey) {
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
@Schema(description = "API 키 응답")
6+
public record ApiKeyResponse(
7+
@Schema(description = "닉네임")
8+
String nickName,
9+
10+
@Schema(description = "공개 키")
11+
String publicKey,
12+
13+
@Schema(description = "비밀 키 (마스킹됨)")
14+
String secretKey,
15+
16+
@Schema(description = "전략 정보", example = "AI 딥러닝")
17+
String strategyInfo
18+
) {
419
}

src/main/java/com/backend/crame/domain/apikey/entitiy/ApiKey.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import org.springframework.data.mongodb.core.mapping.Document;
55
import org.springframework.data.mongodb.core.mapping.Field;
66

7+
import com.backend.crame.domain.quant.entity.AiStrategy;
8+
import com.backend.crame.domain.quant.entity.AlgorithmStrategy;
9+
import com.backend.crame.domain.quant.entity.TradingType;
710
import com.backend.crame.global.utils.BaseTimeEntity;
811

912
import lombok.AccessLevel;
@@ -33,6 +36,18 @@ public class ApiKey extends BaseTimeEntity {
3336

3437
private String secretKey;
3538

39+
@Setter
40+
@Field("trading_type")
41+
private TradingType tradingType;
42+
43+
@Setter
44+
@Field("algorithm_strategy")
45+
private AlgorithmStrategy algorithmStrategy;
46+
47+
@Setter
48+
@Field("ai_strategy")
49+
private AiStrategy aiStrategy;
50+
3651
@Builder
3752
private ApiKey(String key_uuid, String user_uuid, String nickname, String publicKey, String secretKey,String keyVersion){
3853
this.uuid = key_uuid;
@@ -43,5 +58,14 @@ private ApiKey(String key_uuid, String user_uuid, String nickname, String public
4358
this.keyVersion = keyVersion;
4459
}
4560

61+
// 전략 정보를 문자열로 반환하는 헬퍼 메서드
62+
public String getStrategyDisplayName() {
63+
if (tradingType == TradingType.ALGORITHM && algorithmStrategy != null) {
64+
return tradingType.getDisplayName() + " " + algorithmStrategy.getDisplayName();
65+
} else if (tradingType == TradingType.AI && aiStrategy != null) {
66+
return tradingType.getDisplayName() + " " + aiStrategy.getDisplayName();
67+
}
68+
return tradingType != null ? tradingType.getDisplayName() : "";
69+
}
4670

4771
}

src/main/java/com/backend/crame/domain/apikey/service/ApiKeyService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public Mono<ApiKeyResponse> postNewKey(CustomPrincipal principal, ApiKeyRequest
5757
.map(key -> {
5858
String decrypted = aesGcmCrypto.decrypt(key.getSecretKey());
5959
String masked = maskSecret(decrypted);
60-
return new ApiKeyResponse(key.getNickname(), key.getPublicKey(), masked);
60+
return new ApiKeyResponse(key.getNickname(), key.getPublicKey(), masked, key.getStrategyDisplayName());
6161
});
6262
}
6363

@@ -87,7 +87,8 @@ public Mono<List<ApiKeyResponse>> getKeysByUser(CustomPrincipal customPrincipal)
8787
return new ApiKeyResponse(
8888
key.getNickname(),
8989
key.getPublicKey(),
90-
masked
90+
masked,
91+
key.getStrategyDisplayName()
9192
);
9293
})
9394
.collectList();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.backend.crame.domain.quant.controller;
2+
3+
import java.util.List;
4+
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
7+
import org.springframework.web.bind.annotation.GetMapping;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
import com.backend.crame.domain.quant.dto.PortfolioResponse;
12+
import com.backend.crame.domain.quant.service.PortfolioService;
13+
import com.backend.crame.domain.token.entity.CustomPrincipal;
14+
import com.backend.crame.global.response.BaseResponse;
15+
import com.backend.crame.global.response.dto.ResponseDto;
16+
import com.backend.crame.global.response.enums.SuccessCode;
17+
18+
import io.swagger.v3.oas.annotations.Operation;
19+
import io.swagger.v3.oas.annotations.tags.Tag;
20+
import lombok.RequiredArgsConstructor;
21+
import reactor.core.publisher.Mono;
22+
23+
@RestController
24+
@RequestMapping("/api/v1/quant/portfolio")
25+
@Tag(name = "포트폴리오 관련 API", description = "사용자 포트폴리오 현황 관련 API입니다.")
26+
@RequiredArgsConstructor
27+
public class PortfolioController {
28+
29+
private final PortfolioService portfolioService;
30+
31+
@GetMapping()
32+
@Operation(summary = "포트폴리오 현황 조회 API", description = "사용자의 포트폴리오 현황을 조회합니다.")
33+
public Mono<ResponseEntity<ResponseDto<List<PortfolioResponse>>>> getUserPortfolios(
34+
@AuthenticationPrincipal CustomPrincipal principal) {
35+
return portfolioService.getUserPortfolios(principal)
36+
.map(data -> BaseResponse.success(SuccessCode.GET_SUCCESS, data));
37+
}
38+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.backend.crame.domain.quant.controller;
2+
3+
import java.util.List;
4+
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
7+
import org.springframework.web.bind.annotation.GetMapping;
8+
import org.springframework.web.bind.annotation.PatchMapping;
9+
import org.springframework.web.bind.annotation.RequestBody;
10+
import org.springframework.web.bind.annotation.RequestMapping;
11+
import org.springframework.web.bind.annotation.RestController;
12+
13+
import com.backend.crame.domain.apikey.dto.ApiKeyResponse;
14+
import com.backend.crame.domain.quant.dto.ApiKeyStrategyRequest;
15+
import com.backend.crame.domain.quant.service.QuantApiKeyService;
16+
import com.backend.crame.domain.token.entity.CustomPrincipal;
17+
import com.backend.crame.global.response.BaseResponse;
18+
import com.backend.crame.global.response.dto.ResponseDto;
19+
import com.backend.crame.global.response.enums.SuccessCode;
20+
21+
import io.swagger.v3.oas.annotations.Operation;
22+
import io.swagger.v3.oas.annotations.tags.Tag;
23+
import lombok.RequiredArgsConstructor;
24+
import reactor.core.publisher.Mono;
25+
26+
@RestController
27+
@RequestMapping("/api/v1/quant/apikey")
28+
@Tag(name = "Quant API 키 관련 API", description = "Quant API 키 전략 설정 관련 API입니다.")
29+
@RequiredArgsConstructor
30+
public class QuantApiKeyController {
31+
32+
private final QuantApiKeyService quantApiKeyService;
33+
34+
@PatchMapping("/strategy")
35+
@Operation(summary = "API 키 전략 설정 API", description = "특정 API 키에 트레이딩 전략을 설정합니다.")
36+
public Mono<ResponseEntity<ResponseDto<ApiKeyResponse>>> setApiKeyStrategy(
37+
@AuthenticationPrincipal CustomPrincipal principal,
38+
@RequestBody ApiKeyStrategyRequest request) {
39+
return quantApiKeyService.setApiKeyStrategy(principal, request)
40+
.map(data -> BaseResponse.success(SuccessCode.UPDATE_SUCCESS, data));
41+
}
42+
43+
@GetMapping("/strategy")
44+
@Operation(summary = "API 키 전략 정보 조회 API", description = "사용자의 모든 API 키와 설정된 전략 정보를 조회합니다.")
45+
public Mono<ResponseEntity<ResponseDto<List<ApiKeyResponse>>>> getApiKeysWithStrategy(
46+
@AuthenticationPrincipal CustomPrincipal principal) {
47+
return quantApiKeyService.getApiKeysWithStrategy(principal)
48+
.map(data -> BaseResponse.success(SuccessCode.GET_SUCCESS, data));
49+
}
50+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.backend.crame.domain.quant.controller;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PatchMapping;
7+
import org.springframework.web.bind.annotation.RequestBody;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
import com.backend.crame.domain.quant.dto.ModelSelectionRequest;
12+
import com.backend.crame.domain.quant.dto.SubscriptionUpdateRequest;
13+
import com.backend.crame.domain.quant.dto.UserSubscriptionResponse;
14+
import com.backend.crame.domain.quant.service.QuantUserService;
15+
import com.backend.crame.domain.token.entity.CustomPrincipal;
16+
import com.backend.crame.global.response.BaseResponse;
17+
import com.backend.crame.global.response.dto.ResponseDto;
18+
import com.backend.crame.global.response.enums.SuccessCode;
19+
20+
import io.swagger.v3.oas.annotations.Operation;
21+
import io.swagger.v3.oas.annotations.tags.Tag;
22+
import lombok.RequiredArgsConstructor;
23+
import reactor.core.publisher.Mono;
24+
25+
@RestController
26+
@RequestMapping("/api/v1/quant/user")
27+
@Tag(name = "Quant 사용자 관련 API", description = "Quant 사용자 구독 및 모델 선택 관련 API입니다.")
28+
@RequiredArgsConstructor
29+
public class QuantUserController {
30+
31+
private final QuantUserService quantUserService;
32+
33+
@PatchMapping("/subscription")
34+
@Operation(summary = "구독 상태 업데이트 API", description = "프리미엄 구독 상태를 업데이트합니다.")
35+
public Mono<ResponseEntity<ResponseDto<UserSubscriptionResponse>>> updateSubscription(
36+
@AuthenticationPrincipal CustomPrincipal principal,
37+
@RequestBody SubscriptionUpdateRequest request) {
38+
return quantUserService.updateSubscription(principal, request)
39+
.map(data -> BaseResponse.success(SuccessCode.UPDATE_SUCCESS, data));
40+
}
41+
42+
@PatchMapping("/model")
43+
@Operation(summary = "선택 모델 업데이트 API", description = "사용자가 선택한 트레이딩 모델을 업데이트합니다.")
44+
public Mono<ResponseEntity<ResponseDto<UserSubscriptionResponse>>> updateSelectedModel(
45+
@AuthenticationPrincipal CustomPrincipal principal,
46+
@RequestBody ModelSelectionRequest request) {
47+
return quantUserService.updateSelectedModel(principal, request)
48+
.map(data -> BaseResponse.success(SuccessCode.UPDATE_SUCCESS, data));
49+
}
50+
51+
@GetMapping("/subscription")
52+
@Operation(summary = "구독 상태 조회 API", description = "현재 사용자의 구독 상태와 선택 모델을 조회합니다.")
53+
public Mono<ResponseEntity<ResponseDto<UserSubscriptionResponse>>> getSubscriptionStatus(
54+
@AuthenticationPrincipal CustomPrincipal principal) {
55+
return quantUserService.getSubscriptionStatus(principal)
56+
.map(data -> BaseResponse.success(SuccessCode.GET_SUCCESS, data));
57+
}
58+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.backend.crame.domain.quant.controller;
2+
3+
import java.time.LocalDate;
4+
import java.util.List;
5+
6+
import org.springframework.format.annotation.DateTimeFormat;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
import org.springframework.web.bind.annotation.RequestMapping;
11+
import org.springframework.web.bind.annotation.RequestParam;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
import com.backend.crame.domain.quant.dto.TradingHistoryResponse;
15+
import com.backend.crame.domain.quant.service.TradingHistoryService;
16+
import com.backend.crame.domain.token.entity.CustomPrincipal;
17+
import com.backend.crame.global.response.BaseResponse;
18+
import com.backend.crame.global.response.dto.ResponseDto;
19+
import com.backend.crame.global.response.enums.SuccessCode;
20+
21+
import io.swagger.v3.oas.annotations.Operation;
22+
import io.swagger.v3.oas.annotations.Parameter;
23+
import io.swagger.v3.oas.annotations.tags.Tag;
24+
import lombok.RequiredArgsConstructor;
25+
import reactor.core.publisher.Mono;
26+
27+
@RestController
28+
@RequestMapping("/api/v1/quant/trading-history")
29+
@Tag(name = "거래 내역 관련 API", description = "사용자 거래 내역 조회 관련 API입니다.")
30+
@RequiredArgsConstructor
31+
public class TradingHistoryController {
32+
33+
private final TradingHistoryService tradingHistoryService;
34+
35+
@GetMapping()
36+
@Operation(summary = "거래 내역 조회 API", description = "사용자의 모든 거래 내역을 조회합니다.")
37+
public Mono<ResponseEntity<ResponseDto<List<TradingHistoryResponse>>>> getUserTradingHistory(
38+
@AuthenticationPrincipal CustomPrincipal principal) {
39+
return tradingHistoryService.getUserTradingHistory(principal)
40+
.map(data -> BaseResponse.success(SuccessCode.GET_SUCCESS, data));
41+
}
42+
43+
@GetMapping("/period")
44+
@Operation(summary = "기간별 거래 내역 조회 API", description = "특정 기간의 거래 내역을 조회합니다.")
45+
public Mono<ResponseEntity<ResponseDto<List<TradingHistoryResponse>>>> getUserTradingHistoryByPeriod(
46+
@AuthenticationPrincipal CustomPrincipal principal,
47+
@Parameter(description = "시작 날짜", example = "2025-01-01")
48+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
49+
@Parameter(description = "종료 날짜", example = "2025-01-31")
50+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
51+
return tradingHistoryService.getUserTradingHistoryByPeriod(principal, startDate, endDate)
52+
.map(data -> BaseResponse.success(SuccessCode.GET_SUCCESS, data));
53+
}
54+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.backend.crame.domain.quant.dto;
2+
3+
import com.backend.crame.domain.quant.entity.AiStrategy;
4+
import com.backend.crame.domain.quant.entity.AlgorithmStrategy;
5+
import com.backend.crame.domain.quant.entity.TradingType;
6+
7+
import io.swagger.v3.oas.annotations.media.Schema;
8+
9+
@Schema(description = "API 키 전략 설정 요청")
10+
public record ApiKeyStrategyRequest(
11+
@Schema(description = "공개 키", example = "your-public-key")
12+
String publicKey,
13+
14+
@Schema(description = "트레이딩 타입", example = "ALGORITHM")
15+
TradingType tradingType,
16+
17+
@Schema(description = "알고리즘 전략 (트레이딩 타입이 ALGORITHM일 때)", example = "STATISTICAL_ARBITRAGE")
18+
AlgorithmStrategy algorithmStrategy,
19+
20+
@Schema(description = "AI 전략 (트레이딩 타입이 AI일 때)", example = "DEEP_LEARNING")
21+
AiStrategy aiStrategy
22+
) {
23+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.backend.crame.domain.quant.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
@Schema(description = "모델 선택 요청")
6+
public record ModelSelectionRequest(
7+
@Schema(description = "선택한 모델", example = "AI 딥러닝")
8+
String selectedModel
9+
) {
10+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.backend.crame.domain.quant.dto;
2+
3+
import java.math.BigDecimal;
4+
5+
import com.backend.crame.domain.quant.entity.TradingType;
6+
7+
import io.swagger.v3.oas.annotations.media.Schema;
8+
9+
@Schema(description = "포트폴리오 응답")
10+
public record PortfolioResponse(
11+
@Schema(description = "포트폴리오 ID")
12+
String uuid,
13+
14+
@Schema(description = "트레이딩 종류", example = "알고리즘")
15+
String tradingType,
16+
17+
@Schema(description = "전략 종류", example = "알고리즘 통계적 차익 거래")
18+
String strategyType,
19+
20+
@Schema(description = "금액")
21+
BigDecimal amount,
22+
23+
@Schema(description = "손익")
24+
BigDecimal profitLoss,
25+
26+
@Schema(description = "수익률")
27+
BigDecimal profitRate
28+
) {
29+
}

0 commit comments

Comments
 (0)