모의투자 로직 변경
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package com.goldenchart.trading;
|
||||
|
||||
/**
|
||||
* 로그인 사용자(userId) 전용 매매·모의·전략 접근 규칙.
|
||||
* 게스트(앱 입장만 한 비로그인)는 deviceId만으로 매매 기능을 사용할 수 없다.
|
||||
*/
|
||||
public final class TradingAccess {
|
||||
|
||||
private TradingAccess() {}
|
||||
|
||||
/** 모의 계좌 device_id 컬럼(NOT NULL UK)용 합성 키 — 실제 기기와 무관 */
|
||||
public static String accountDeviceKey(long userId) {
|
||||
return "user:" + userId;
|
||||
}
|
||||
|
||||
public static long requireUserId(Long userId) {
|
||||
if (userId == null || userId <= 0) {
|
||||
throw new TradingAccessDeniedException();
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
public static boolean isRegisteredUser(Long userId) {
|
||||
return userId != null && userId > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.goldenchart.trading;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* 게스트(비로그인) 또는 미등록 사용자가 매매·모의·전략 API에 접근할 때.
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public class TradingAccessDeniedException extends RuntimeException {
|
||||
|
||||
public static final String DEFAULT_MESSAGE =
|
||||
"매매·모의투자·투자전략·자동매매 기능은 정식 로그인 후 이용할 수 있습니다.";
|
||||
|
||||
public TradingAccessDeniedException() {
|
||||
super(DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
public TradingAccessDeniedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.goldenchart.trading;
|
||||
|
||||
import com.goldenchart.entity.GcAppSettings;
|
||||
import com.goldenchart.service.AppSettingsService;
|
||||
import com.goldenchart.trading.TradingAccess;
|
||||
import com.goldenchart.service.LiveTradingService;
|
||||
import com.goldenchart.service.PaperTradingService;
|
||||
import com.goldenchart.trading.pipeline.OrderRequest;
|
||||
@@ -21,32 +22,34 @@ public class TradingExecutionService {
|
||||
private final PaperTradingService paperTradingService;
|
||||
private final LiveTradingService liveTradingService;
|
||||
|
||||
public void executeSignal(String deviceId, Long userId, String market,
|
||||
public void executeSignal(Long userId, String market,
|
||||
Long strategyId, String side, double price) {
|
||||
GcAppSettings app = appSettingsService.getEntity(userId, deviceId);
|
||||
if (!TradingAccess.isRegisteredUser(userId)) return;
|
||||
GcAppSettings app = appSettingsService.getEntity(userId, null);
|
||||
TradingMode mode = TradingMode.fromString(app.getTradingMode());
|
||||
|
||||
if (mode.usePaper() && Boolean.TRUE.equals(app.getPaperTradingEnabled())) {
|
||||
paperTradingService.tryExecuteOnSignal(deviceId, userId, market, strategyId, side, price);
|
||||
paperTradingService.tryExecuteOnSignal(userId, market, strategyId, side, price);
|
||||
}
|
||||
if (mode.useLive()) {
|
||||
liveTradingService.tryExecuteOnSignal(
|
||||
deviceId, userId, market, strategyId, side, price, "STRATEGY");
|
||||
userId, market, strategyId, side, price, "STRATEGY");
|
||||
}
|
||||
}
|
||||
|
||||
public void executeRiskExit(OrderRequest req) {
|
||||
GcAppSettings app = appSettingsService.getEntity(req.userId(), req.deviceId());
|
||||
if (!TradingAccess.isRegisteredUser(req.userId())) return;
|
||||
GcAppSettings app = appSettingsService.getEntity(req.userId(), null);
|
||||
TradingMode mode = TradingMode.fromString(app.getTradingMode());
|
||||
String source = req.kind() == OrderRequest.OrderKind.STOP_LOSS ? "STOP_LOSS" : "TAKE_PROFIT";
|
||||
|
||||
if (mode.usePaper() && Boolean.TRUE.equals(app.getPaperTradingEnabled())) {
|
||||
paperTradingService.tryExecuteOnSignal(
|
||||
req.deviceId(), req.userId(), req.market(), null, "SELL", req.price());
|
||||
req.userId(), req.market(), null, "SELL", req.price());
|
||||
}
|
||||
if (mode.useLive()) {
|
||||
liveTradingService.tryExecuteOnSignal(
|
||||
req.deviceId(), req.userId(), req.market(), null, "SELL", req.price(), source);
|
||||
req.userId(), req.market(), null, "SELL", req.price(), source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.goldenchart.trading.pipeline;
|
||||
|
||||
import com.goldenchart.trading.TradingAccess;
|
||||
import com.goldenchart.trading.TradingExecutionService;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@@ -64,9 +65,11 @@ public class OrderExecutionQueue {
|
||||
}
|
||||
}
|
||||
|
||||
public void submitSignal(String deviceId, Long userId, String market,
|
||||
public void submitSignal(Long userId, String market,
|
||||
Long strategyId, String signal, double price) {
|
||||
submit(OrderRequest.strategySignal(deviceId, userId, market, strategyId, signal, price));
|
||||
if (!TradingAccess.isRegisteredUser(userId)) return;
|
||||
submit(OrderRequest.strategySignal(
|
||||
TradingAccess.accountDeviceKey(userId), userId, market, strategyId, signal, price));
|
||||
}
|
||||
|
||||
private void runLoop() {
|
||||
@@ -88,12 +91,12 @@ public class OrderExecutionQueue {
|
||||
}
|
||||
|
||||
private void executeNow(OrderRequest req) {
|
||||
if (req.deviceId() == null || req.deviceId().isBlank()) return;
|
||||
if (!TradingAccess.isRegisteredUser(req.userId())) return;
|
||||
if (!"BUY".equals(req.side()) && !"SELL".equals(req.side())) return;
|
||||
try {
|
||||
if (req.kind() == OrderRequest.OrderKind.STRATEGY_SIGNAL) {
|
||||
tradingExecutionService.executeSignal(
|
||||
req.deviceId(), req.userId(), req.market(),
|
||||
req.userId(), req.market(),
|
||||
req.strategyId(), req.side(), req.price());
|
||||
} else {
|
||||
tradingExecutionService.executeRiskExit(req);
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.goldenchart.repository.GcPaperAccountRepository;
|
||||
import com.goldenchart.repository.GcPaperPositionRepository;
|
||||
import com.goldenchart.service.AppSettingsService;
|
||||
import com.goldenchart.service.BacktestSettingsService;
|
||||
import com.goldenchart.trading.TradingAccess;
|
||||
import com.goldenchart.trading.TradingExecutionService;
|
||||
import com.goldenchart.trading.TradingMode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -53,13 +54,12 @@ public class RealtimePositionMonitor {
|
||||
GcPaperAccount account = accountRepo.findById(pos.getAccountId()).orElse(null);
|
||||
if (account == null) continue;
|
||||
|
||||
String deviceId = account.getDeviceId();
|
||||
Long userId = account.getUserId();
|
||||
if (deviceId == null || deviceId.isBlank()) continue;
|
||||
if (userId == null || userId <= 0) continue;
|
||||
|
||||
GcAppSettings app;
|
||||
try {
|
||||
app = appSettingsService.getEntity(userId, deviceId);
|
||||
app = appSettingsService.getEntity(userId, null);
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class RealtimePositionMonitor {
|
||||
double avg = pos.getAvgPrice().doubleValue();
|
||||
if (avg <= 0) continue;
|
||||
|
||||
BacktestSettingsDto risk = backtestSettingsService.get(deviceId);
|
||||
BacktestSettingsDto risk = backtestSettingsService.get(TradingAccess.accountDeviceKey(userId));
|
||||
double pnlPct = (tradePrice - avg) / avg * 100.0;
|
||||
|
||||
if (Boolean.TRUE.equals(risk.getStopLossEnabled())
|
||||
@@ -84,7 +84,7 @@ public class RealtimePositionMonitor {
|
||||
lastExitMs.put(cooldownKey, now);
|
||||
double sl = risk.getStopLossPct().doubleValue();
|
||||
tradingExecutionService.executeRiskExit(
|
||||
OrderRequest.stopLoss(deviceId, userId, market, tradePrice, sl));
|
||||
OrderRequest.stopLoss(TradingAccess.accountDeviceKey(userId), userId, market, tradePrice, sl));
|
||||
log.info("[SL] {} @ {} pnl={}% (limit -{}%)", market, tradePrice,
|
||||
String.format("%.2f", pnlPct), sl);
|
||||
return;
|
||||
@@ -96,7 +96,7 @@ public class RealtimePositionMonitor {
|
||||
lastExitMs.put(cooldownKey, now);
|
||||
double tp = risk.getTakeProfitPct().doubleValue();
|
||||
tradingExecutionService.executeRiskExit(
|
||||
OrderRequest.takeProfit(deviceId, userId, market, tradePrice, tp));
|
||||
OrderRequest.takeProfit(TradingAccess.accountDeviceKey(userId), userId, market, tradePrice, tp));
|
||||
log.info("[TP] {} @ {} pnl={}% (target +{}%)", market, tradePrice,
|
||||
String.format("%.2f", pnlPct), tp);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user