설정 db 저장
This commit is contained in:
@@ -16,14 +16,25 @@ public class BacktestSettingsController {
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<BacktestSettingsDto> get(
|
||||
@RequestHeader(value = "X-User-Id", required = false) String userIdHeader,
|
||||
@RequestHeader(value = "X-Device-Id", defaultValue = "default") String deviceId) {
|
||||
return ResponseEntity.ok(service.get(deviceId));
|
||||
return ResponseEntity.ok(service.get(parseUserId(userIdHeader), deviceId));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<BacktestSettingsDto> save(
|
||||
@RequestHeader(value = "X-User-Id", required = false) String userIdHeader,
|
||||
@RequestHeader(value = "X-Device-Id", defaultValue = "default") String deviceId,
|
||||
@RequestBody BacktestSettingsDto dto) {
|
||||
return ResponseEntity.ok(service.save(deviceId, dto));
|
||||
return ResponseEntity.ok(service.save(parseUserId(userIdHeader), deviceId, dto));
|
||||
}
|
||||
|
||||
private Long parseUserId(String header) {
|
||||
if (header == null || header.isBlank()) return null;
|
||||
try {
|
||||
return Long.parseLong(header);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ public class GcBacktestSettings {
|
||||
@Column(name = "device_id", length = 100)
|
||||
private String deviceId;
|
||||
|
||||
@Column(name = "user_id")
|
||||
private Long userId;
|
||||
|
||||
// ── 자본 설정 ──────────────────────────────────────────────────────────────
|
||||
@Column(name = "initial_capital", nullable = false, precision = 20, scale = 2)
|
||||
@Builder.Default
|
||||
|
||||
@@ -7,4 +7,6 @@ import java.util.Optional;
|
||||
|
||||
public interface GcBacktestSettingsRepository extends JpaRepository<GcBacktestSettings, Long> {
|
||||
Optional<GcBacktestSettings> findFirstByDeviceIdOrderByUpdatedAtDesc(String deviceId);
|
||||
|
||||
Optional<GcBacktestSettings> findFirstByUserIdOrderByUpdatedAtDesc(Long userId);
|
||||
}
|
||||
|
||||
@@ -3,32 +3,99 @@ package com.goldenchart.service;
|
||||
import com.goldenchart.dto.BacktestSettingsDto;
|
||||
import com.goldenchart.entity.GcBacktestSettings;
|
||||
import com.goldenchart.repository.GcBacktestSettingsRepository;
|
||||
import com.goldenchart.trading.TradingAccess;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BacktestSettingsService {
|
||||
|
||||
private final GcBacktestSettingsRepository repo;
|
||||
|
||||
/** device_id 기준 설정 조회 — 없으면 기본값 반환 */
|
||||
/** userId 우선, 없으면 deviceId — 레거시 user:{id}·기기별 행 자동 승격 */
|
||||
@Transactional(readOnly = true)
|
||||
public BacktestSettingsDto get(String deviceId) {
|
||||
return repo.findFirstByDeviceIdOrderByUpdatedAtDesc(deviceId)
|
||||
public BacktestSettingsDto get(Long userId, String deviceId) {
|
||||
return findEntity(userId, deviceId)
|
||||
.map(this::toDto)
|
||||
.orElseGet(BacktestSettingsDto::new);
|
||||
}
|
||||
|
||||
/** upsert — 기존 설정이 있으면 덮어쓰기, 없으면 새 행 삽입 */
|
||||
@Transactional
|
||||
public BacktestSettingsDto save(String deviceId, BacktestSettingsDto dto) {
|
||||
GcBacktestSettings entity = repo
|
||||
.findFirstByDeviceIdOrderByUpdatedAtDesc(deviceId)
|
||||
.orElseGet(GcBacktestSettings::new);
|
||||
/**
|
||||
* 레거시 호출 — scopeKey가 {@code user:{userId}} 이면 userId 조회로 위임.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public BacktestSettingsDto get(String scopeKey) {
|
||||
if (scopeKey != null && scopeKey.startsWith("user:")) {
|
||||
try {
|
||||
long uid = Long.parseLong(scopeKey.substring(5));
|
||||
return get(uid, null);
|
||||
} catch (NumberFormatException ignored) {
|
||||
/* fall through */
|
||||
}
|
||||
}
|
||||
return get(null, scopeKey);
|
||||
}
|
||||
|
||||
entity.setDeviceId(deviceId);
|
||||
@Transactional
|
||||
public BacktestSettingsDto save(Long userId, String deviceId, BacktestSettingsDto dto) {
|
||||
GcBacktestSettings entity = findOrCreate(userId, deviceId);
|
||||
applyFields(entity, dto, userId, deviceId);
|
||||
return toDto(repo.save(entity));
|
||||
}
|
||||
|
||||
// ── 조회·생성 ─────────────────────────────────────────────────────────────
|
||||
|
||||
private Optional<GcBacktestSettings> findEntity(Long userId, String deviceId) {
|
||||
if (userId != null && userId > 0) {
|
||||
Optional<GcBacktestSettings> byUser = repo.findFirstByUserIdOrderByUpdatedAtDesc(userId);
|
||||
if (byUser.isPresent()) return byUser;
|
||||
|
||||
if (deviceId != null && !deviceId.isBlank()) {
|
||||
Optional<GcBacktestSettings> byDevice = repo.findFirstByDeviceIdOrderByUpdatedAtDesc(deviceId);
|
||||
if (byDevice.isPresent()) {
|
||||
return Optional.of(promoteToUser(byDevice.get(), userId));
|
||||
}
|
||||
}
|
||||
|
||||
Optional<GcBacktestSettings> legacy = repo.findFirstByDeviceIdOrderByUpdatedAtDesc(
|
||||
TradingAccess.accountDeviceKey(userId));
|
||||
if (legacy.isPresent()) {
|
||||
return Optional.of(promoteToUser(legacy.get(), userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
if (deviceId != null && !deviceId.isBlank()) {
|
||||
return repo.findFirstByDeviceIdOrderByUpdatedAtDesc(deviceId);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private GcBacktestSettings findOrCreate(Long userId, String deviceId) {
|
||||
return findEntity(userId, deviceId)
|
||||
.orElseGet(() -> GcBacktestSettings.builder()
|
||||
.userId(userId != null && userId > 0 ? userId : null)
|
||||
.deviceId(deviceId)
|
||||
.build());
|
||||
}
|
||||
|
||||
/** 기기별 행을 로그인 사용자 소유로 1회 승격 (다른 PC와 공유) */
|
||||
private GcBacktestSettings promoteToUser(GcBacktestSettings entity, Long userId) {
|
||||
entity.setUserId(userId);
|
||||
return repo.save(entity);
|
||||
}
|
||||
|
||||
private void applyFields(GcBacktestSettings entity, BacktestSettingsDto dto,
|
||||
Long userId, String deviceId) {
|
||||
if (userId != null && userId > 0) {
|
||||
entity.setUserId(userId);
|
||||
}
|
||||
if (deviceId != null && !deviceId.isBlank()) {
|
||||
entity.setDeviceId(deviceId);
|
||||
}
|
||||
entity.setInitialCapital(dto.getInitialCapital());
|
||||
entity.setCommissionType(dto.getCommissionType());
|
||||
entity.setCommissionRate(dto.getCommissionRate());
|
||||
@@ -58,8 +125,6 @@ public class BacktestSettingsService {
|
||||
"SCAN_SIGNALS".equals(dto.getTradeExecutionMode())
|
||||
? "SCAN_SIGNALS"
|
||||
: "BACKTEST_ENGINE");
|
||||
|
||||
return toDto(repo.save(entity));
|
||||
}
|
||||
|
||||
// ── 변환 헬퍼 ─────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -95,7 +95,7 @@ public class LiveRiskMonitorService {
|
||||
GcAppSettings app = appSettingsRepo.findByUserId(userId).orElse(null);
|
||||
if (app == null) continue;
|
||||
|
||||
BacktestSettingsDto risk = backtestSettingsService.get(cacheKey);
|
||||
BacktestSettingsDto risk = backtestSettingsService.get(userId, null);
|
||||
double avg = pos[1];
|
||||
double pnlPct = (tradePrice - avg) / avg * 100.0;
|
||||
|
||||
|
||||
@@ -389,11 +389,8 @@ public class LiveStrategyEvaluator {
|
||||
if (!globalLive && Boolean.TRUE.equals(s.getIsLiveCheck())) {
|
||||
return s.getPositionMode() != null ? s.getPositionMode() : "LONG_ONLY";
|
||||
}
|
||||
String deviceKey = s.getUserId() != null
|
||||
? TradingAccess.accountDeviceKey(s.getUserId())
|
||||
: s.getDeviceId();
|
||||
if (deviceKey != null && !deviceKey.isBlank()) {
|
||||
BacktestSettingsDto bt = backtestSettingsService.get(deviceKey);
|
||||
if (s.getUserId() != null || (s.getDeviceId() != null && !s.getDeviceId().isBlank())) {
|
||||
BacktestSettingsDto bt = backtestSettingsService.get(s.getUserId(), s.getDeviceId());
|
||||
if (bt.getPositionMode() != null) {
|
||||
return "SIGNAL_ONLY".equals(bt.getPositionMode()) ? "SIGNAL_ONLY" : "LONG_ONLY";
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class RealtimePositionMonitor {
|
||||
double avg = pos.getAvgPrice().doubleValue();
|
||||
if (avg <= 0) continue;
|
||||
|
||||
BacktestSettingsDto risk = backtestSettingsService.get(TradingAccess.accountDeviceKey(userId));
|
||||
BacktestSettingsDto risk = backtestSettingsService.get(userId, null);
|
||||
double pnlPct = (tradePrice - avg) / avg * 100.0;
|
||||
|
||||
if (Boolean.TRUE.equals(risk.getStopLossEnabled())
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 백테스트 설정: 로그인 사용자(user_id) 단위 공유 (기존 device_id 전용 → AppSettings와 동일 패턴)
|
||||
ALTER TABLE gc_backtest_settings
|
||||
ADD COLUMN user_id BIGINT NULL AFTER device_id;
|
||||
|
||||
-- 레거시 user:{id} device_id 행 → user_id 승격
|
||||
UPDATE gc_backtest_settings
|
||||
SET user_id = CAST(SUBSTRING(device_id, 6) AS UNSIGNED)
|
||||
WHERE device_id LIKE 'user:%'
|
||||
AND user_id IS NULL;
|
||||
|
||||
CREATE INDEX idx_gc_backtest_settings_user_id ON gc_backtest_settings (user_id);
|
||||
Reference in New Issue
Block a user