import React, { useState } from 'react'; import { ShieldCheck, Scale, Zap, DollarSign, Clock, ChevronRight, ChevronLeft, CheckCircle2, AlertTriangle, Info, Wallet, Target, Lock } from 'lucide-react'; import { RISK_STYLE_TEMPLATES } from '../lib/RiskStyleTemplates'; import type { RiskStyleTemplate } from '../lib/RiskStyleTemplates'; import { getUserTier, TIER_POLICIES, isFeatureAllowed } from '../lib/TierPolicy'; import { useAuth } from './AuthContext'; import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel'; import { useBacktestFeatureGate } from '../backtest/useBacktestFeatureGate'; import { createTradeProfile, updateTradeProfile } from '../lib/profileApi'; interface WizardState { step: number; riskStyle: RiskStyleTemplate | null; assets: string[]; capital: number; profitTarget: number; hours: '24/7' | 'London + New York' | 'Asia only'; } const ASSETS = [ { id: 'BTC/USDT', label: 'Bitcoin (BTC)' }, { id: 'ETH/USDT', label: 'Ethereum (ETH)' }, { id: 'SOL/USDT', label: 'Solana (SOL)' }, ]; const SESSION_MAP = { '24/7': 'LDN,NY,TOK,SYD', 'London + New York': 'LDN,NY', 'Asia only': 'TOK,SYD' }; const buildStrategyConfig = (state: WizardState) => ({ rules: [ { ruleId: 'RiskManagementRule', enabled: true, ruleType: 'mandatory', params: {} }, { ruleId: 'SessionRule', enabled: state.hours !== '24/7', ruleType: 'mandatory', params: { sessions: SESSION_MAP[state.hours] } }, ...((state.riskStyle?.mandatoryRules || []) .filter((ruleId) => !['RiskManagementRule', 'SessionRule'].includes(ruleId)) .map((ruleId) => ({ ruleId, enabled: true, ruleType: 'mandatory', params: {} }))), ...((state.riskStyle?.votingRules || []).map((ruleId) => ({ ruleId, enabled: true, ruleType: 'voting', params: {} }))) ], riskLimits: { maxDailyLossUsd: Math.floor(state.capital * 0.05), dailyProfitTargetUsd: state.profitTarget, maxOpenTrades: 3, maxConsecutiveLosses: 3 }, execution: { orderType: 'market', cooldownMinutes: 60, minRulePassRatio: state.riskStyle?.minRulePassRatio || 1, entryMode: 'both' } }); export const StrategyWizard: React.FC<{ onComplete: () => void; editingProfile?: any; profile?: any; previewAsCustomer?: boolean; }> = ({ onComplete, editingProfile, profile: userProfile, previewAsCustomer = false }) => { const { user } = useAuth(); const tier = getUserTier(userProfile); const policy = TIER_POLICIES[tier]; // Initialize state from existing profile if editing const getInitialState = (): WizardState => { if (editingProfile) { const config = editingProfile.strategy_config; const passRatio = config?.execution?.minRulePassRatio || 1.0; const style = RISK_STYLE_TEMPLATES.find(t => t.minRulePassRatio === passRatio) || RISK_STYLE_TEMPLATES[1]; let hours: WizardState['hours'] = '24/7'; const sessions = config?.rules?.find((r: any) => r.ruleId === 'SessionRule')?.params?.sessions; if (sessions === 'LDN,NY') hours = 'London + New York'; if (sessions === 'TOK,SYD') hours = 'Asia only'; return { step: 1, riskStyle: style, assets: String(editingProfile.symbols).split(','), capital: editingProfile.allocated_capital, profitTarget: config?.riskLimits?.dailyProfitTargetUsd || 100, hours }; } return { step: 1, riskStyle: null, assets: ['BTC/USDT'], capital: 1000, profitTarget: 100, hours: '24/7' }; }; const [state, setState] = useState(getInitialState()); const [loading, setLoading] = useState(false); const [showBacktest, setShowBacktest] = useState(false); const { enabled: backtestEnabled, loading: backtestGateLoading } = useBacktestFeatureGate({ previewAsCustomer }); const next = () => setState(s => ({ ...s, step: s.step + 1 })); const back = () => setState(s => ({ ...s, step: s.step - 1 })); const handleSave = async () => { if (!user || !state.riskStyle) return; setLoading(true); const strategy_config = buildStrategyConfig(state); const payload = { name: editingProfile?.name || `${state.riskStyle.label.split(' ')[1]} Bot (${state.assets.join(',')})`, user_id: user.id, allocated_capital: state.capital, risk_per_trade_percent: state.riskStyle.riskPerTrade, symbols: state.assets.join(','), is_active: editingProfile ? editingProfile.is_active : false, // Preserve status if editing strategy_config }; let result; if (editingProfile) { result = await updateTradeProfile(editingProfile.id, payload).then(() => ({ error: null as any })).catch((error) => ({ error })); } else { result = await createTradeProfile(payload).then(() => ({ error: null as any })).catch((error) => ({ error })); } setLoading(false); if (result.error) { alert('Error saving strategy: ' + result.error.message); } else { onComplete(); } }; return (
{/* Progress Bar */}
{[1, 2, 3, 4, 5].map(i => (
= i ? 'bg-[#00ff88] text-black scale-110' : 'bg-zinc-800 text-zinc-500 border border-zinc-700' }`} > {state.step > i ? : i}
))}
{/* Step 1: Risk Style */} {state.step === 1 && (

How should this bot behave?

Select a pre-configured risk style. High-frequency options seek more opportunities but require more flexibility.

{RISK_STYLE_TEMPLATES.map(style => { const isLocked = !isFeatureAllowed(tier, 'risk_style', style.id); return (
{isLocked && (
Pro/Elite Only
)}
); })}
)} {/* Step 2: Assets & Capital */} {state.step === 2 && (

Assets & Capital

Define what to trade and how much capital to use.

{ASSETS.map(asset => ( ))}
setState({ ...state, capital: Number(e.target.value) })} className="w-full bg-zinc-900 border-2 border-white/5 rounded-xl py-4 pl-10 pr-4 text-white font-bold outline-none focus:border-[#00ff88]/50 transition-all" />

Total USD balance this bot is allowed to manage.

{ const val = Number(e.target.value); if (isFeatureAllowed(tier, 'profit_target', val)) { setState({ ...state, profitTarget: val }); } }} className={`w-full bg-zinc-900 border-2 border-white/5 rounded-xl py-4 pl-10 pr-4 text-white font-bold outline-none focus:border-[#00ff88]/50 transition-all ${tier === 'free' ? 'opacity-50 cursor-not-allowed' : ''}`} />

Once this profit is reached, the bot automatically pauses for the day to lock in gains.

)} {/* Step 3: Trading Hours */} {state.step === 3 && (

Trading Hours

When should the bot look for signals?

{(['24/7', 'London + New York', 'Asia only'] as const).map(option => ( ))}
)} {/* Step 4: Safety Confirmation */} {state.step === 4 && (

Safety Confirmation

Review the built-in safeguards protecting your capital.

Auto-Protective Stop Loss Enabled (Mandatory)
Position Sizing Limit Max {state.riskStyle?.riskPerTrade}% per trade
Daily Recovery Halt -${Math.floor(state.capital * 0.05)} (5%)
Market Volatility Guard Smart ATR Check

Risk management and safety rules are coded into the engine core. These cannot be disabled or bypassed, ensuring your bot always operates within safety boundaries.

)} {/* Step 5: Review & Create */} {state.step === 5 && (

Ready to Deploy

Verify your bot strategy one last time.

{/* Decorative background element */}
{state.riskStyle?.id === 'safe' ? : state.riskStyle?.id === 'balanced' ? : }

{state.riskStyle?.label} Bot

{state.assets.map(a => ( {a.split('/')[0]} ))}
Target Capital
${state.capital}
Profit Threshold
${state.profitTarget}/day
Risk Level
{(state.riskStyle?.riskPerTrade || 0)}% per trade
Active Schedule
{state.hours}

Bots are created in PAUSED mode by default. You must manually enable trading from your dashboard.

{!backtestGateLoading && backtestEnabled && ( )}
{!backtestGateLoading && backtestEnabled && showBacktest && (
setShowBacktest(false)} />
)}
)}
); };