모의투자 로직 변경

This commit is contained in:
Macbook
2026-05-31 14:05:46 +09:00
parent ead97dad5d
commit d6eedf19bb
33 changed files with 1456 additions and 330 deletions
+61 -13
View File
@@ -86,6 +86,13 @@ import LiveStrategyPanel from './components/LiveStrategyPanel';
import { TradeNotificationProvider } from './contexts/TradeNotificationContext';
import { VerificationIssueNotificationProvider } from './contexts/VerificationIssueNotificationContext';
import { LiveSignalNotifier, type LiveSignalNotifierHandle } from './components/LiveSignalNotifier';
import { FormalLoginGate } from './components/FormalLoginGate';
import {
isFormalLogin,
isTradingMenuPage,
isTradingSettingsCategory,
FORMAL_LOGIN_REQUIRED_MSG,
} from './utils/tradingAccess';
import { NotifyTopMenuBar, ChartToolbarNotify } from './components/NotifyUiBindings';
import { AppNotificationLayer } from './components/AppNotificationLayer';
import TradeNotificationListPage from './components/TradeNotificationListPage';
@@ -163,7 +170,13 @@ function App() {
const [guestMode, setGuestMode] = useState(() =>
isAppEntered() && !getAuthSession(),
);
const formalLogin = isFormalLogin(authUser, guestMode);
const [loginOpen, setLoginOpen] = useState(false);
const requireFormalLogin = useCallback(() => {
window.alert(FORMAL_LOGIN_REQUIRED_MSG);
setLoginOpen(true);
}, []);
const [appDownloadOpen, setAppDownloadOpen] = useState(false);
// 전역 지표 파라미터 + 시각 설정 DB 동기화
@@ -593,13 +606,17 @@ function App() {
}, []);
const guardedSetMenuPage = useCallback((page: MenuPage) => {
if (!formalLogin && isTradingMenuPage(page)) {
requireFormalLogin();
return;
}
if (page === 'notifications' && !canMenu('notifications')) return;
if (page !== 'notifications' && !canMenu(page)) {
setMenuPage(firstAllowedTopMenu(menuPermissions));
return;
}
setMenuPage(page);
}, [canMenu, menuPermissions]);
}, [canMenu, menuPermissions, formalLogin, requireFormalLogin]);
useEffect(() => {
if (!menuPermsLoaded) return;
@@ -814,12 +831,13 @@ function App() {
/** STOMP 구독 대상 — 가상투자 실행 중이면 투자대상 종목, 아니면 관심종목 */
const monitoredMarkets = useMemo(() => {
if (!formalLogin) return [];
if (isVirtualActive) return virtualMonitoredMarkets;
if (!appDefaults.liveStrategyCheck || !appDefaults.liveStrategyId) return [];
const set = new Set(watchlist);
if (symbol) set.add(symbol);
return [...set];
}, [isVirtualActive, virtualMonitoredMarkets, watchlist, symbol, appDefaults.liveStrategyCheck, appDefaults.liveStrategyId]);
}, [formalLogin, isVirtualActive, virtualMonitoredMarkets, watchlist, symbol, appDefaults.liveStrategyCheck, appDefaults.liveStrategyId]);
const [marketSubscriptions, setMarketSubscriptions] = useState<{ market: string; candleType: string }[]>([]);
@@ -1693,7 +1711,7 @@ function App() {
markets={monitoredMarkets}
marketSubscriptions={liveStompSubscriptions}
activeMarket={symbol}
enabled={monitoredMarkets.length > 0}
enabled={formalLogin && monitoredMarkets.length > 0}
popupEnabled={appDefaults.tradeAlertPopup ?? true}
chartMarkersActive={menuPage === 'chart'}
onMarkersChange={markers => {
@@ -1737,19 +1755,26 @@ function App() {
)}
{menuPage === 'strategy' && (
<StrategyPage theme={theme} activeIndicators={indicators} />
formalLogin
? <StrategyPage theme={theme} activeIndicators={indicators} />
: <FormalLoginGate onLogin={requireFormalLogin} />
)}
{menuPage === 'strategy-editor' && (
<StrategyEditorPage theme={theme} />
formalLogin
? <StrategyEditorPage theme={theme} />
: <FormalLoginGate onLogin={requireFormalLogin} />
)}
{/* ── 백테스팅 이력 화면 ─────────────────────────────────────────── */}
{menuPage === 'backtest' && (
<BacktestHistoryPage theme={theme} />
formalLogin
? <BacktestHistoryPage theme={theme} />
: <FormalLoginGate onLogin={requireFormalLogin} />
)}
{menuPage === 'paper' && (
formalLogin ? (
<PaperTradingPage
theme={theme}
tickers={marketTickers}
@@ -1759,6 +1784,7 @@ function App() {
paperAutoTradeEnabled={paperAutoTradeEnabled}
onPaperOrderFilled={handlePaperOrderFilled}
onOpenSettings={() => {
if (!formalLogin) { requireFormalLogin(); return; }
setSettingsInitialCategory('paper');
setMenuPage('settings');
}}
@@ -1767,9 +1793,13 @@ function App() {
setMenuPage('chart');
}}
/>
) : (
<FormalLoginGate onLogin={requireFormalLogin} title="투자관리" />
)
)}
{menuPage === 'virtual' && (
formalLogin ? (
<VirtualTradingPage
theme={theme}
tickers={marketTickers}
@@ -1780,6 +1810,9 @@ function App() {
onPaperAutoTradeEnabled={v => saveAppDef({ paperAutoTradeEnabled: v })}
onPaperOrderFilled={handlePaperOrderFilled}
/>
) : (
<FormalLoginGate onLogin={requireFormalLogin} title="가상매매" />
)
)}
{menuPage === 'trend-search' && (
@@ -1802,6 +1835,7 @@ function App() {
{/* ── 설정 화면 ──────────────────────────────────────────────────── */}
{menuPage === 'notifications' && canMenu('notifications') && (
formalLogin ? (
<TradeNotificationListPage
theme={theme}
tickers={marketTickers}
@@ -1814,10 +1848,15 @@ function App() {
setMenuPage('chart');
}}
/>
) : (
<FormalLoginGate onLogin={requireFormalLogin} title="알림목록" />
)
)}
{menuPage === 'settings' && canMenu('settings') && (
<SettingsPage
isFormalLogin={formalLogin}
onRequireFormalLogin={requireFormalLogin}
theme={theme}
menuPermissions={menuPermissions}
magnetMode={magnetMode}
@@ -2034,7 +2073,7 @@ function App() {
backtestActive={btMatchesChart}
backtestRunning={btRunning}
backtestRunningId={undefined}
onRunBacktest={menuPage === 'chart' && layoutDef.count === 1
onRunBacktest={menuPage === 'chart' && layoutDef.count === 1 && formalLogin
? async (opts) => {
if (opts.strategyName) setBtStrategyName(opts.strategyName);
if (useUpbit && (isLoading || bars.length === 0)) return;
@@ -2066,16 +2105,25 @@ function App() {
if (appDefaults.btAutoPopup) setShowBtResult(true);
}
: undefined}
onOpenBtSettings={() => setShowBtSettings(true)}
onOpenBtResults={() => setShowBtResult(true)}
onOpenBtSettings={() => {
if (!formalLogin) { requireFormalLogin(); return; }
setShowBtSettings(true);
}}
onOpenBtResults={() => {
if (!formalLogin) { requireFormalLogin(); return; }
setShowBtResult(true);
}}
hasBtResult={btMatchesChart}
onClearBtMarkers={() => {
btMarkerPayloadRef.current = null;
btClear();
managerRef.current?.clearBacktestMarkers();
}}
onToggleLiveStrategy={() => setShowLivePanel(v => !v)}
liveStrategyActive={showLivePanel || monitoredMarkets.length > 0}
onToggleLiveStrategy={() => {
if (!formalLogin) { requireFormalLogin(); return; }
setShowLivePanel(v => !v);
}}
liveStrategyActive={formalLogin && (showLivePanel || monitoredMarkets.length > 0)}
showChartQuote={useUpbit}
chartLiveQuote={liveQuote}
chartQuoteLoading={isLoading}
@@ -2584,8 +2632,8 @@ function App() {
onMarketSelect={handleTradeMarketSelect}
availableKrw={paperCashBalance}
availableCoinQty={paperCoinQty}
paperTradingEnabled={paperTradingEnabled}
paperAutoTradeEnabled={paperAutoTradeEnabled}
paperTradingEnabled={formalLogin && paperTradingEnabled}
paperAutoTradeEnabled={formalLogin && paperAutoTradeEnabled}
onPaperOrderFilled={() => {
refreshPaperAccount(symbol);
handlePaperOrderFilled();