Files
goldenChart/frontend/src/components/paper/PaperLeftSettingsTab.tsx
T
2026-06-04 13:00:59 +09:00

193 lines
7.0 KiB
TypeScript

import React, { useMemo } from 'react';
import { useAppSettings } from '../../hooks/useAppSettings';
import { resetPaperAccount, type PaperAllocationDto } from '../../utils/backendApi';
import PaperSplitPanel from './PaperSplitPanel';
import PaperSymbolStageSettings from './PaperSymbolStageSettings';
interface Props {
selectedMarket?: string;
allocations?: PaperAllocationDto[];
onSettingsSaved?: () => void;
onAccountReset?: () => void;
onOpenSettings?: () => void;
}
const SettingsFieldRow: React.FC<{ label: string; children: React.ReactNode }> = ({ label, children }) => (
<div className="vtd-target-strategy-field">
<span className="vtd-target-strategy-label">{label}</span>
{children}
</div>
);
const PaperLeftSettingsTab: React.FC<Props> = ({
selectedMarket,
allocations = [],
onSettingsSaved,
onAccountReset,
onOpenSettings,
}) => {
const { defaults, save, isLoaded } = useAppSettings();
const selectedAlloc = useMemo(
() => allocations.find(a => a.symbol === selectedMarket),
[allocations, selectedMarket],
);
if (!isLoaded) {
return <p className="ptd-muted">설정 로딩…</p>;
}
const patch = (p: Parameters<typeof save>[0]) => {
save(p);
onSettingsSaved?.();
};
const handleReset = async () => {
if (!window.confirm('모의투자 계좌를 초기화합니다. 계속할까요?')) return;
const res = await resetPaperAccount();
if (res) onAccountReset?.();
};
const top = (
<div className="ptd-settings-panel">
{onOpenSettings && (
<button type="button" className="bps-btn bps-btn--ghost ptd-settings-link" onClick={onOpenSettings}>
설정 모의투자에서 전체 관리
</button>
)}
<div className="vtd-target-item ptd-item-card ptd-settings-card">
<div className="ptd-item-card-metrics">
<SettingsFieldRow label="모의투자 활성화">
<label className="vtd-trade-meta-value ptd-settings-value-box ptd-settings-value-box--check">
<input
type="checkbox"
checked={defaults.paperTradingEnabled}
onChange={e => patch({ paperTradingEnabled: e.target.checked })}
/>
</label>
</SettingsFieldRow>
<SettingsFieldRow label="자동매매">
<label className="vtd-trade-meta-value ptd-settings-value-box ptd-settings-value-box--check">
<input
type="checkbox"
checked={defaults.paperAutoTradeEnabled}
onChange={e => patch({ paperAutoTradeEnabled: e.target.checked })}
/>
</label>
</SettingsFieldRow>
<SettingsFieldRow label="매수 예산 (%)">
<input
type="number"
className="vtd-trade-meta-value ptd-settings-input"
min={1}
max={100}
value={defaults.paperAutoTradeBudgetPct}
disabled={!defaults.paperAutoTradeEnabled}
onChange={e => patch({ paperAutoTradeBudgetPct: Number(e.target.value) })}
/>
</SettingsFieldRow>
<SettingsFieldRow label="미체결 주문 처리">
<select
className="vtd-trade-meta-value ptd-settings-input ptd-settings-select"
value={defaults.paperOrderFillMode}
onChange={e => patch({ paperOrderFillMode: e.target.value as 'LIMIT_ONLY' | 'NEXT_TICK' | 'AUTO_CANCEL' })}
>
<option value="LIMIT_ONLY">A. 지정가 유지 (조건 충족 체결)</option>
<option value="NEXT_TICK">B. 다음 강제 체결 (시장가 동일)</option>
<option value="AUTO_CANCEL">C. 허용폭 초과 자동 취소</option>
</select>
</SettingsFieldRow>
{defaults.paperOrderFillMode === 'AUTO_CANCEL' && (
<SettingsFieldRow label="허용 이탈폭 (%)">
<input
type="number"
className="vtd-trade-meta-value ptd-settings-input"
min={0.1}
max={50}
step={0.1}
value={defaults.paperOrderAutoCancelPct}
onChange={e => patch({ paperOrderAutoCancelPct: Number(e.target.value) })}
/>
</SettingsFieldRow>
)}
</div>
</div>
</div>
);
const bottom = (
<div className="ptd-settings-panel ptd-settings-panel--account ptd-settings-panel--stages">
{selectedMarket ? (
<PaperSymbolStageSettings
symbol={selectedMarket}
allocation={selectedAlloc}
onSaved={onSettingsSaved}
/>
) : (
<p className="ptd-muted ptd-item-card-empty">중앙 투자금 관리에서 종목을 선택하면 분할 매매 단계를 설정할 있습니다.</p>
)}
<div className="ptd-settings-panel-divider" aria-hidden />
<div className="vtd-target-item ptd-item-card ptd-settings-card">
<div className="ptd-item-card-metrics">
<SettingsFieldRow label="초기 자본 (KRW)">
<input
type="number"
className="vtd-trade-meta-value ptd-settings-input"
min={100000}
step={100000}
value={defaults.paperInitialCapital}
onChange={e => patch({ paperInitialCapital: Number(e.target.value) })}
/>
</SettingsFieldRow>
<SettingsFieldRow label="수수료율 (%)">
<input
type="number"
className="vtd-trade-meta-value ptd-settings-input"
min={0}
max={1}
step={0.01}
value={defaults.paperFeeRatePct}
onChange={e => patch({ paperFeeRatePct: Number(e.target.value) })}
/>
</SettingsFieldRow>
<SettingsFieldRow label="슬리피지 (%)">
<input
type="number"
className="vtd-trade-meta-value ptd-settings-input"
min={0}
max={5}
step={0.01}
value={defaults.paperSlippagePct}
onChange={e => patch({ paperSlippagePct: Number(e.target.value) })}
/>
</SettingsFieldRow>
<SettingsFieldRow label="최소 주문 (KRW)">
<input
type="number"
className="vtd-trade-meta-value ptd-settings-input"
min={1000}
step={1000}
value={defaults.paperMinOrderKrw}
onChange={e => patch({ paperMinOrderKrw: Number(e.target.value) })}
/>
</SettingsFieldRow>
</div>
</div>
<button type="button" className="bps-btn bps-btn--danger ptd-settings-reset" onClick={() => void handleReset()}>
계좌 초기화
</button>
</div>
);
return (
<PaperSplitPanel
className="ptd-split-panel--left"
topTitle="운영 · 자동매매"
bottomTitle="종목별 분할 매매 · 계좌"
top={top}
bottom={bottom}
/>
);
};
export default PaperLeftSettingsTab;