feat: Add task launcher API with timestamp-based run directories
- Add POST /runtime/start, /stop, /restart APIs - Run directories use timestamp format: YYYYMMDD_HHMMSS - Add force stop support in TradingRuntimeManager - Frontend: use REST API to start runtime instead of WebSocket - Remove RuntimeView page from navigation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { AGENTS, INITIAL_TICKERS } from './config/constants';
|
||||
|
||||
// Services
|
||||
import { ReadOnlyClient } from './services/websocket';
|
||||
import { startRuntime } from './services/runtimeApi';
|
||||
|
||||
// Hooks
|
||||
import { useFeedProcessor } from './hooks/useFeedProcessor';
|
||||
@@ -17,7 +18,6 @@ import NetValueChart from './components/NetValueChart';
|
||||
import StockLogo from './components/StockLogo';
|
||||
import Header from './components/Header.jsx';
|
||||
import RuntimeSettingsPanel from './components/RuntimeSettingsPanel.jsx';
|
||||
import RuntimeView from './components/RuntimeView.jsx';
|
||||
|
||||
// Utils
|
||||
import { formatNumber, formatTickerPrice } from './utils/formatters';
|
||||
@@ -555,7 +555,7 @@ export default function LiveTradingApp() {
|
||||
}
|
||||
}, [enableMemoryDraft, initialCashDraft, intervalMinutesDraft, marginRequirementDraft, maxCommCyclesDraft, scheduleModeDraft, triggerTimeDraft]);
|
||||
|
||||
const handleLaunchConfigSave = useCallback(() => {
|
||||
const handleLaunchConfigSave = useCallback(async () => {
|
||||
const pendingTickers = parseWatchlistInput(watchlistInputValue);
|
||||
const nextTickers = Array.from(new Set([...watchlistDraftSymbols, ...pendingTickers]));
|
||||
if (nextTickers.length === 0) {
|
||||
@@ -563,11 +563,6 @@ export default function LiveTradingApp() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clientRef.current) {
|
||||
setRuntimeConfigFeedback({ type: 'error', text: '连接未就绪,稍后重试' });
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = Number(intervalMinutesDraft);
|
||||
const maxCommCycles = Number(maxCommCyclesDraft);
|
||||
const initialCash = Number(initialCashDraft);
|
||||
@@ -596,26 +591,35 @@ export default function LiveTradingApp() {
|
||||
setWatchlistDraftSymbols(nextTickers);
|
||||
setWatchlistInputValue('');
|
||||
|
||||
const watchlistSuccess = clientRef.current.send({
|
||||
type: 'update_watchlist',
|
||||
tickers: nextTickers
|
||||
});
|
||||
try {
|
||||
// Call API to start new runtime with timestamp-based run directory
|
||||
const result = await startRuntime({
|
||||
tickers: nextTickers,
|
||||
schedule_mode: scheduleModeDraft,
|
||||
interval_minutes: interval,
|
||||
trigger_time: triggerTimeDraft,
|
||||
max_comm_cycles: maxCommCycles,
|
||||
initial_cash: initialCash,
|
||||
margin_requirement: marginRequirement,
|
||||
enable_memory: Boolean(enableMemoryDraft),
|
||||
mode: serverMode || 'live'
|
||||
});
|
||||
|
||||
const runtimeSuccess = clientRef.current.send({
|
||||
type: 'update_runtime_config',
|
||||
schedule_mode: scheduleModeDraft,
|
||||
interval_minutes: interval,
|
||||
trigger_time: triggerTimeDraft,
|
||||
max_comm_cycles: maxCommCycles,
|
||||
initial_cash: initialCash,
|
||||
margin_requirement: marginRequirement,
|
||||
enable_memory: Boolean(enableMemoryDraft)
|
||||
});
|
||||
|
||||
if (!watchlistSuccess || !runtimeSuccess) {
|
||||
setIsRuntimeConfigSaving(false);
|
||||
setIsWatchlistSaving(false);
|
||||
setRuntimeConfigFeedback({ type: 'error', text: '发送失败,请检查连接状态' });
|
||||
setIsRuntimeSettingsOpen(false);
|
||||
setRuntimeConfigFeedback({
|
||||
type: 'success',
|
||||
text: `任务已启动: ${result.run_id}`
|
||||
});
|
||||
addSystemMessage(`新任务已启动: ${result.run_id}`);
|
||||
} catch (error) {
|
||||
setIsRuntimeConfigSaving(false);
|
||||
setIsWatchlistSaving(false);
|
||||
setRuntimeConfigFeedback({
|
||||
type: 'error',
|
||||
text: `启动失败: ${error.message}`
|
||||
});
|
||||
}
|
||||
}, [
|
||||
intervalMinutesDraft,
|
||||
@@ -627,7 +631,9 @@ export default function LiveTradingApp() {
|
||||
marginRequirementDraft,
|
||||
enableMemoryDraft,
|
||||
watchlistDraftSymbols,
|
||||
watchlistInputValue
|
||||
watchlistInputValue,
|
||||
serverMode,
|
||||
addSystemMessage
|
||||
]);
|
||||
|
||||
const handleRuntimeDefaultsRestore = useCallback(() => {
|
||||
@@ -2476,33 +2482,8 @@ export default function LiveTradingApp() {
|
||||
>
|
||||
统计
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`view-nav-btn ${currentView === 'runtime' ? 'active' : ''}`}
|
||||
onClick={() => setCurrentView('runtime')}
|
||||
>
|
||||
运行态
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{currentView === 'runtime' ? (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 40,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
minWidth: 0,
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<RuntimeView />
|
||||
</div>
|
||||
) : (
|
||||
<div className={`view-slider-five ${
|
||||
currentView === 'traders'
|
||||
? 'show-traders'
|
||||
@@ -2644,7 +2625,6 @@ export default function LiveTradingApp() {
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -81,3 +81,40 @@ export function loadAllRuntimeState(onSuccess, onError) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new trading runtime with the given configuration.
|
||||
* If a runtime is already running, it will be forcefully stopped first.
|
||||
*/
|
||||
export function startRuntime(config) {
|
||||
return safeRequest('/runtime/start', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the current running runtime.
|
||||
*/
|
||||
export function stopRuntime(force = true) {
|
||||
return safeRequest(`/runtime/stop?force=${force}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the runtime with a new configuration.
|
||||
*/
|
||||
export function restartRuntime(config) {
|
||||
return safeRequest('/runtime/restart', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the currently running runtime.
|
||||
*/
|
||||
export function fetchCurrentRuntime() {
|
||||
return safeFetch('/runtime/current');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user