feat: implement token address confirmation dialog and limit backtest duration
This commit is contained in:
@@ -223,6 +223,8 @@ def chat(
|
||||
thinking=result.get("thinking"),
|
||||
strategy_config=bot.strategy_config if result.get("strategy_updated") else None,
|
||||
success=result.get("success", False),
|
||||
strategy_needs_confirmation=result.get("strategy_needs_confirmation", False),
|
||||
strategy_data=result.get("strategy_data") if result.get("strategy_needs_confirmation") else None,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -149,6 +149,8 @@ class BotChatResponse(BaseModel):
|
||||
thinking: Optional[str] = None
|
||||
strategy_config: Optional[dict] = None
|
||||
success: bool = False
|
||||
strategy_needs_confirmation: Optional[bool] = False
|
||||
strategy_data: Optional[dict] = None
|
||||
|
||||
|
||||
class SignalResponse(BaseModel):
|
||||
|
||||
@@ -130,4 +130,6 @@ export interface BotChatResponse {
|
||||
thinking: string | null;
|
||||
strategy_config: StrategyConfig | null;
|
||||
success: boolean;
|
||||
strategy_needs_confirmation?: boolean;
|
||||
strategy_data?: StrategyConfig | null;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
let botId = $derived($page.params.id);
|
||||
let isSending = $state(false);
|
||||
let showStrategy = $state(false);
|
||||
|
||||
// Token address confirmation modal state
|
||||
let showTokenConfirm = $state(false);
|
||||
let pendingStrategyData = $state<any>(null);
|
||||
let tokenAddressInput = $state('');
|
||||
let confirmingMessage = $state('');
|
||||
|
||||
onMount(async () => {
|
||||
if (!$isAuthenticated && !$isLoading) {
|
||||
@@ -55,6 +61,15 @@
|
||||
const response = await api.bots.chat(botId, message, controller.signal);
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// Check if token address confirmation is needed
|
||||
if (response.strategy_needs_confirmation && response.strategy_data) {
|
||||
// Show token confirmation modal
|
||||
pendingStrategyData = response.strategy_data;
|
||||
confirmingMessage = response.response;
|
||||
tokenAddressInput = '';
|
||||
showTokenConfirm = true;
|
||||
}
|
||||
|
||||
// Add assistant response with thinking
|
||||
addMessage({ role: 'assistant', content: response.response, thinking: response.thinking || null });
|
||||
|
||||
@@ -76,6 +91,56 @@
|
||||
function toggleStrategy() {
|
||||
showStrategy = !showStrategy;
|
||||
}
|
||||
|
||||
async function confirmTokenAddress() {
|
||||
if (!tokenAddressInput.trim() || !pendingStrategyData) {
|
||||
showTokenConfirm = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the pending strategy with the token address
|
||||
const updatedStrategy = { ...pendingStrategyData };
|
||||
|
||||
// Update conditions with token address
|
||||
if (updatedStrategy.conditions) {
|
||||
updatedStrategy.conditions = updatedStrategy.conditions.map((cond: any) => ({
|
||||
...cond,
|
||||
token_address: tokenAddressInput.trim()
|
||||
}));
|
||||
}
|
||||
|
||||
// Update actions with token address
|
||||
if (updatedStrategy.actions) {
|
||||
updatedStrategy.actions = updatedStrategy.actions.map((action: any) => ({
|
||||
...action,
|
||||
token_address: tokenAddressInput.trim()
|
||||
}));
|
||||
}
|
||||
|
||||
try {
|
||||
// Update bot with the strategy
|
||||
await api.bots.update(botId, { strategy_config: updatedStrategy });
|
||||
|
||||
// Refresh bot data
|
||||
const bot = await api.bots.get(botId);
|
||||
setCurrentBot(bot);
|
||||
|
||||
// Add success message
|
||||
addMessage({ role: 'assistant', content: `Perfect! I've saved your strategy with the token address. You can now run backtests!`, thinking: null });
|
||||
} catch (e) {
|
||||
addMessage({ role: 'assistant', content: 'Failed to save strategy. Please try again.', thinking: null });
|
||||
}
|
||||
|
||||
showTokenConfirm = false;
|
||||
pendingStrategyData = null;
|
||||
tokenAddressInput = '';
|
||||
}
|
||||
|
||||
function cancelTokenConfirm() {
|
||||
showTokenConfirm = false;
|
||||
pendingStrategyData = null;
|
||||
tokenAddressInput = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -83,6 +148,20 @@
|
||||
</svelte:head>
|
||||
|
||||
<main>
|
||||
{#if showTokenConfirm}
|
||||
<div class="modal-overlay" onclick={cancelTokenConfirm}>
|
||||
<div class="modal-content" onclick={(e) => e.stopPropagation()}>
|
||||
<h3>Confirm Token Address</h3>
|
||||
<p class="modal-message">{confirmingMessage}</p>
|
||||
<p class="modal-hint">Please enter the BSC contract address for the token:</p>
|
||||
<input type="text" class="token-input" bind:value={tokenAddressInput} placeholder="0x..."/>
|
||||
<div class="modal-actions">
|
||||
<button class="btn btn-secondary" onclick={cancelTokenConfirm}>Cancel</button>
|
||||
<button class="btn btn-primary" onclick={confirmTokenAddress} disabled={!tokenAddressInput.trim()}>Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<header>
|
||||
<div class="header-left">
|
||||
<a href="/dashboard" class="back-link">← Dashboard</a>
|
||||
@@ -200,4 +279,88 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: rgba(20, 20, 20, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16px;
|
||||
padding: 1.5rem;
|
||||
max-width: 450px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin: 0 0 1rem;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.modal-message {
|
||||
color: #ccc;
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.modal-hint {
|
||||
color: #888;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.token-input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.token-input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@@ -16,14 +16,15 @@
|
||||
let selectedBacktest = $state<Backtest | null>(null);
|
||||
|
||||
onMount(async () => {
|
||||
// Set default dates (yesterday to 30 days ago)
|
||||
// Set default dates - yesterday only (1 day range for fast testing)
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const thirtyDaysAgo = new Date();
|
||||
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
||||
|
||||
endDate = yesterday.toISOString().split('T')[0];
|
||||
startDate = thirtyDaysAgo.toISOString().split('T')[0];
|
||||
// Set max date to yesterday
|
||||
const maxDate = yesterday.toISOString().split('T')[0];
|
||||
|
||||
endDate = maxDate;
|
||||
startDate = maxDate; // Same day = 1 day backtest
|
||||
|
||||
if (!$isAuthenticated && !$isLoading) {
|
||||
goto('/login');
|
||||
@@ -55,6 +56,17 @@
|
||||
|
||||
async function startBacktest() {
|
||||
if (!startDate || !endDate) return;
|
||||
|
||||
// Validate date range (max 7 days)
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
const daysDiff = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (daysDiff > 7) {
|
||||
setBacktestError('Maximum backtest duration is 7 days for fast testing');
|
||||
return;
|
||||
}
|
||||
|
||||
setBacktestError(null);
|
||||
setBacktestLoading(true);
|
||||
isRunning = true;
|
||||
@@ -125,10 +137,10 @@
|
||||
<div class="field">
|
||||
<label for="timeframe">Timeframe</label>
|
||||
<select id="timeframe" bind:value={timeframe}>
|
||||
<option value="1m">1 minute</option>
|
||||
<option value="5m">5 minutes</option>
|
||||
<option value="15m">15 minutes</option>
|
||||
<option value="1h">1 hour</option>
|
||||
|
||||
|
||||
|
||||
<option value="1h">1 hour (recommended)</option>
|
||||
<option value="4h">4 hours</option>
|
||||
<option value="1d">1 day</option>
|
||||
</select>
|
||||
|
||||
Reference in New Issue
Block a user