A1+A2 — CodeStrategyEditor backtest call
Was: POST /api/backtest with { symbol, strategyCode, mode: 'code' }
Now: POST /api/backtest/run with { symbols: [s], strategyConfig: {
type: 'code', language: 'javascript', code } }
The backend route is /api/backtest/run (not /api/backtest), and
/api/backtest/run validates `symbols[]` and `strategyConfig`, not the
ad-hoc fields we were sending. Also unwraps the { success, results }
envelope the engine returns and surfaces success:false errors.
A3 — VisualRuleBuilder save shape
Was: hand-rolled fetch to /api/profiles with { name, symbol, strategyType,
visualRules, description } — backend's saveTradeProfileForUser ignored
all of that and either 400'd or persisted a half-empty row.
Now: uses the canonical createTradeProfile() helper from lib/profileApi
with the documented TradeProfilePayload shape. Visual rules go inside
strategy_config.{type:'visual', version:1, rules:[...]} so the engine
can fan out to a visual interpreter without conflicting with the
existing rule-based engine. Allocated capital + risk pct pulled from
botState.settings so the profile inherits the user's current sizing.
is_active defaults false so the user activates explicitly.
A4+A5 — RightPanel.NewsFeed auth + runtime
Was: raw fetch() to import.meta.env.VITE_TRADING_API_URL with no
Authorization header → 401 on every render in any environment that
requires auth, and prod-broken where the runtime resolver is the
only source of truth for the API base URL.
Now: uses fetchNews() from lib/marketApi which already carries the
platform Bearer token and routes through tradingRuntime.tradingApiUrl.
Adds an error state in the UI for visibility instead of silently
leaving the panel blank.
Verified: web/ tsc --noEmit passes. No behavioural change to non-affected
code paths (RightPanel portfolio summary, ResearchView other tabs, etc.).
Refs: docs/AUDIT_REDESIGN.md items A1–A5.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
145 lines
4.9 KiB
TypeScript
145 lines
4.9 KiB
TypeScript
import { useState } from 'react';
|
|
import { useAppContext } from '../context/AppContext';
|
|
import { SignalsTab } from '../tabs/SignalsTab';
|
|
import { BacktestTab } from '../tabs/BacktestTab';
|
|
import { MyStrategiesTab } from '../tabs/MyStrategiesTab';
|
|
import { VisualRuleBuilder, type VisualRule } from '../components/strategy/VisualRuleBuilder';
|
|
import { CodeStrategyEditor } from '../components/strategy/CodeStrategyEditor';
|
|
import { createTradeProfile } from '../lib/profileApi';
|
|
|
|
type ResearchTab = 'Strategies' | 'Visual Builder' | 'Code Editor' | 'Signals' | 'Backtesting';
|
|
|
|
// Sub-tab pill styles
|
|
function SubTab({
|
|
label, active, onClick,
|
|
}: { label: string; active: boolean; onClick: () => void }) {
|
|
return (
|
|
<button
|
|
onClick={onClick}
|
|
style={{
|
|
padding: '8px 16px',
|
|
border: 'none',
|
|
borderBottom: active ? '2px solid #2563EB' : '2px solid transparent',
|
|
background: 'transparent',
|
|
color: active ? '#2563EB' : '#6B7280',
|
|
fontSize: 13,
|
|
fontWeight: active ? 700 : 500,
|
|
cursor: 'pointer',
|
|
marginBottom: -1,
|
|
transition: 'all 0.15s',
|
|
fontFamily: 'inherit',
|
|
}}
|
|
>
|
|
{label}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
export function ResearchView() {
|
|
const { botState, connected, showBacktestTab, isAdmin, activeSymbol } = useAppContext();
|
|
const [tab, setTab] = useState<ResearchTab>('Strategies');
|
|
|
|
const tabs: ResearchTab[] = [
|
|
'Strategies',
|
|
'Visual Builder',
|
|
'Code Editor',
|
|
...(isAdmin ? ['Signals' as ResearchTab] : []),
|
|
...(showBacktestTab ? ['Backtesting' as ResearchTab] : []),
|
|
];
|
|
|
|
// Save a visual-builder strategy via the canonical createTradeProfile helper.
|
|
// Backend `saveTradeProfileForUser` expects the TradeProfilePayload shape:
|
|
// { name, symbols, allocated_capital, risk_per_trade_percent, is_active,
|
|
// strategy_config: { ... } }
|
|
// Visual rules go inside strategy_config.rules so the strategy engine can
|
|
// route to the visual interpreter (alongside the existing rule-based engine).
|
|
const handleSaveVisualStrategy = async (name: string, rules: VisualRule[]) => {
|
|
const fallbackSymbol = Object.keys(botState.symbols)[0] ?? 'SPY';
|
|
const symbol = activeSymbol || fallbackSymbol;
|
|
const totalCapital = botState.settings?.totalCapital ?? 1000;
|
|
const riskPct = botState.settings?.riskPerTrade ?? 1;
|
|
|
|
await createTradeProfile({
|
|
name,
|
|
symbols: symbol,
|
|
allocated_capital: totalCapital,
|
|
risk_per_trade_percent: riskPct,
|
|
is_active: false, // user activates explicitly from MyStrategiesTab
|
|
strategy_config: {
|
|
type: 'visual',
|
|
version: 1,
|
|
rules: rules.map(r => ({
|
|
indicator: r.indicator,
|
|
condition: r.condition,
|
|
value: r.value,
|
|
action: r.action,
|
|
quantity: r.quantity,
|
|
quantityType: r.quantityType,
|
|
})),
|
|
},
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<h2 style={{ fontSize: 22, fontWeight: 800, color: '#111827', margin: '0 0 20px' }}>Research</h2>
|
|
|
|
<div style={{
|
|
display: 'flex', gap: 4, marginBottom: 20,
|
|
borderBottom: '1px solid #E5E7EB',
|
|
}}>
|
|
{tabs.map(t => (
|
|
<SubTab key={t} label={t} active={tab === t} onClick={() => setTab(t)} />
|
|
))}
|
|
</div>
|
|
|
|
{tab === 'Strategies' && (
|
|
<MyStrategiesTab
|
|
botState={botState}
|
|
alerts={botState.alerts}
|
|
previewAsCustomer={false}
|
|
/>
|
|
)}
|
|
|
|
{tab === 'Visual Builder' && (
|
|
<div>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<div style={{ fontSize: 15, fontWeight: 700, color: '#111827', marginBottom: 4 }}>
|
|
Visual Rule Builder
|
|
</div>
|
|
<div style={{ fontSize: 13, color: '#6B7280' }}>
|
|
Build a trading strategy by composing IF/THEN rules. Drag rows to reorder. Click "Save Strategy" to store it.
|
|
</div>
|
|
</div>
|
|
<VisualRuleBuilder
|
|
symbol={activeSymbol || Object.keys(botState.symbols)[0] || 'SPY'}
|
|
onSave={handleSaveVisualStrategy}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{tab === 'Code Editor' && (
|
|
<div>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<div style={{ fontSize: 15, fontWeight: 700, color: '#111827', marginBottom: 4 }}>
|
|
Code Strategy Editor
|
|
</div>
|
|
<div style={{ fontSize: 13, color: '#6B7280' }}>
|
|
Write a custom strategy function in JavaScript. Click "Run Backtest" to test it against historical data.
|
|
</div>
|
|
</div>
|
|
<CodeStrategyEditor symbol={activeSymbol || Object.keys(botState.symbols)[0] || 'SPY'} />
|
|
</div>
|
|
)}
|
|
|
|
{tab === 'Signals' && (
|
|
<SignalsTab botState={botState} connected={connected} />
|
|
)}
|
|
|
|
{tab === 'Backtesting' && (
|
|
<BacktestTab previewAsCustomer={false} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|