가상투자 설정 수정

This commit is contained in:
Macbook
2026-05-25 22:30:54 +09:00
parent cae1c3a624
commit 1e950c7db4
24 changed files with 1015 additions and 362 deletions
+30 -23
View File
@@ -72,6 +72,8 @@ interface SettingsPageProps {
onLiveSettingsChange?: (settings: LiveStrategySettingsDto) => void;
watchlistCount?: number;
monitoredMarkets?: string[];
/** 가상투자 세션 실행 중 — 전략 설정은 가상투자 화면에서 */
virtualDriven?: boolean;
/** Main 탭 보조지표 기본 파라미터 (DB) */
getIndicatorParams?: (type: string, defaults: Record<string, number | string | boolean>) => Record<string, number | string | boolean>;
saveIndicatorParams?: (type: string, params: Record<string, number | string | boolean>) => void;
@@ -219,7 +221,7 @@ const ALL_CATEGORIES: Category[] = [
{ id: 'indicators', label: '보조지표 설정', icon: <IcIndicators />, desc: '지표 추가 Main 탭 보조지표 기본 파라미터·색상·기준선' },
{ id: 'backtest', label: '백테스팅', icon: <IcBacktest />, desc: '백테스팅 실행 옵션, 자본·비용·리스크 관리 설정' },
{ id: 'strategy', label: '전략 설정', icon: <IcStrategy />, desc: '투자 전략 기본 파라미터 및 조건 설정' },
{ id: 'paper', label: '모의투자', icon: <IcPaper />, desc: '가상 자본, 수수료, 자동매매 ON/OFF 등 모의투자 설정' },
{ id: 'paper', label: '가상투자', icon: <IcPaper />, desc: '가상 자본, 수수료, 자동매매 ON/OFF 등 가상투자 설정' },
{ id: 'alert', label: '알림 설정', icon: <IcAlert />, desc: '가격 알림, 신호 알림, 알림 방식 설정' },
{ id: 'network', label: '네트워크', icon: <IcNetwork />, desc: 'API 서버 주소, WebSocket, 연결 상태' },
{ id: 'admin', label: '관리자 설정', icon: <IcAdmin />, desc: '사용자·역할·메뉴 접근 권한 관리 (관리자 비밀번호 필요)' },
@@ -332,16 +334,16 @@ const PaperPanel: React.FC<PaperPanelProps> = ({
return (
<>
<SettingSection title="매매 운영 모드">
<SettingRow label="실행 대상" desc="모의투자만, 실거래(업비트 API)만, 또는 둘 다 병행할 수 있습니다.">
<SettingRow label="실행 대상" desc="가상투자만, 실거래(업비트 API)만, 또는 둘 다 병행할 수 있습니다.">
<select className="stg-select" value={tradingMode} onChange={e => onTradingMode?.(e.target.value)}>
<option value="PAPER"></option>
<option value="PAPER"></option>
<option value="LIVE"></option>
<option value="BOTH"> + </option>
</select>
</SettingRow>
</SettingSection>
<SettingSection title="모의투자 사용">
<SettingRow label="모의투자 활성화" desc="켜면 차트 매수·매도와 전략 시그널이 가상 계좌로 체결됩니다.">
<SettingSection title="가상투자 사용">
<SettingRow label="가상투자 활성화" desc="켜면 가상투자·실시간 차트 매매 패널·전략 Match 자동매매가 가상 계좌로 체결됩니다.">
<label className="stg-toggle">
<input type="checkbox" checked={paperTradingEnabled} onChange={e => onPaperTradingEnabled?.(e.target.checked)} />
<span className="stg-toggle-slider" />
@@ -350,24 +352,16 @@ const PaperPanel: React.FC<PaperPanelProps> = ({
</SettingSection>
<SettingSection title="자동매매">
<SettingRow
label="자동매매"
desc={
paperAutoTradeEnabled
? 'ON: 실시간 전략이 켜진 상태에서 BUY/SELL 시그널 발생 시 아래 조건으로 모의 계좌에 자동 체결됩니다.'
: 'OFF: 시그널은 알림·차트 표시만 하며, 매매는 차트 우측 매수·매도 패널에서 직접 주문합니다.'
}
label="자동매매 ON/OFF"
desc="가상투자 화면 타이틀바에서 변경합니다. Match 충족 시 자동 매수·매도 여부를 제어합니다."
>
<label className="stg-toggle">
<input type="checkbox" checked={paperAutoTradeEnabled} onChange={e => onPaperAutoTradeEnabled?.(e.target.checked)} />
<span className="stg-toggle-slider" />
</label>
<span className={`stg-badge ${paperAutoTradeEnabled ? 'stg-badge--on' : 'stg-badge--off'}`}>
{paperAutoTradeEnabled ? 'ON' : 'OFF'}
{paperAutoTradeEnabled ? 'ON (타이틀바)' : 'OFF (타이틀바)'}
</span>
</SettingRow>
<SettingRow
label="자동 매수 예산 (%)"
desc="BUY 시그널 시 주문가능 현금 중 사용 비율. SELL 시그널은 해당 종목 보유 수량 전량 매도."
desc="Match 매수 시 주문가능 현금 중 사용 비율. Match 매도는 해당 종목 보유 수량 전량 매도."
>
<input
type="number"
@@ -381,7 +375,7 @@ const PaperPanel: React.FC<PaperPanelProps> = ({
/>
</SettingRow>
{!paperAutoTradeEnabled && (
<p className="stg-hint"> . .</p>
<p className="stg-hint"> ON/OFF는 . OFF Match· .</p>
)}
</SettingSection>
<SettingSection title="계좌·비용">
@@ -722,6 +716,7 @@ interface StrategyPanelProps {
liveSettings?: LiveStrategySettingsDto;
watchlistCount?: number;
monitoredMarkets?: string[];
virtualDriven?: boolean;
onClearLiveMarkers?: () => void;
tradeAlertPopup?: boolean;
onTradeAlertPopup?: (v: boolean) => void;
@@ -734,6 +729,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
liveSettings: liveSettingsProp,
watchlistCount = 0,
monitoredMarkets = [],
virtualDriven = false,
onClearLiveMarkers,
tradeAlertPopup = true,
onTradeAlertPopup,
@@ -763,6 +759,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
}, [liveSettingsProp]);
const persistLive = useCallback(async (patch: Partial<LiveStrategySettingsDto>) => {
if (virtualDriven) return;
const next: LiveStrategySettingsDto = { ...liveSettings, ...patch, market: liveMarket };
setLiveSaving(true);
try {
@@ -775,7 +772,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
} finally {
setLiveSaving(false);
}
}, [liveSettings, liveMarket, onClearLiveMarkers, onLiveSettingsChange]);
}, [liveSettings, liveMarket, onClearLiveMarkers, onLiveSettingsChange, virtualDriven]);
const isOn = liveSettings.isLiveCheck;
const execType = liveSettings.executionType;
@@ -786,9 +783,16 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
<>
{/* ── 실시간 전략 체크 ─────────────────────────────────────────────── */}
<SettingSection title="실시간 전략 체크">
{virtualDriven && (
<p className="stg-hint" style={{ marginBottom: 12 }}>
. ·· <strong></strong> .
</p>
)}
<SettingRow
label="모니터링 현황"
desc="★ 관심종목으로 등록한 종목이 자동으로 전략 체크 대상입니다. 실시간 체크를 켜고 전략을 선택하세요."
desc={virtualDriven
? "가상투자 투자대상 종목이 백엔드에서 실시간 전략 체크 대상입니다."
: "★ 관심종목으로 등록한 종목이 자동으로 전략 체크 대상입니다. 실시간 체크를 켜고 전략을 선택하세요."}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'flex-end' }}>
<span style={{ fontSize: 12, fontWeight: 600 }}>
@@ -810,6 +814,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
<input
type="checkbox"
checked={isOn}
disabled={virtualDriven}
onChange={e => persistLive({ isLiveCheck: e.target.checked })}
/>
<span className="stg-toggle-slider" />
@@ -830,7 +835,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
>
<select
className="stg-select"
disabled={!isOn}
disabled={virtualDriven || !isOn}
value={stratId ?? ''}
onChange={e => persistLive({ strategyId: e.target.value ? Number(e.target.value) : null })}
style={{ opacity: isOn ? 1 : 0.45 }}
@@ -854,7 +859,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
name="stg-exec-type"
value="CANDLE_CLOSE"
checked={execType === 'CANDLE_CLOSE'}
disabled={!isOn}
disabled={virtualDriven || !isOn}
onChange={() => persistLive({ executionType: 'CANDLE_CLOSE' })}
/>
<span className="stg-radio-option-text">
@@ -868,7 +873,7 @@ const StrategyPanel: React.FC<StrategyPanelProps> = ({
name="stg-exec-type"
value="REALTIME_TICK"
checked={execType === 'REALTIME_TICK'}
disabled={!isOn}
disabled={virtualDriven || !isOn}
onChange={() => persistLive({ executionType: 'REALTIME_TICK' })}
/>
<span className="stg-radio-option-text">
@@ -1334,6 +1339,7 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
onLiveSettingsChange,
watchlistCount = 0,
monitoredMarkets = [],
virtualDriven = false,
getIndicatorParams,
saveIndicatorParams,
getIndicatorVisual,
@@ -1500,6 +1506,7 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
liveSettings={liveSettings}
watchlistCount={watchlistCount}
monitoredMarkets={monitoredMarkets}
virtualDriven={virtualDriven}
onClearLiveMarkers={onClearLiveMarkers}
tradeAlertPopup={tradeAlertPopup}
onTradeAlertPopup={onTradeAlertPopup}