Files
robinhood/frontend/src/components/AdvancedIndicatorsPanel.tsx
T
Claude e82cf3a5ee Phase 4: Advanced Technical Indicators Management
Implemented comprehensive indicator management system for traders:

Backend (indicators.py):
- GET /api/indicators/available: All available indicators by category
- GET /api/indicators/categories: List of indicator categories
- GET /api/indicators/category/{category}: Indicators in specific category
- GET /api/indicators/{indicator_id}: Detailed indicator information
- GET /api/indicators/default: Recommended setup for gold trading
- GET /api/indicators/presets: 5 pre-configured trading setups
- POST /api/indicators/preset/{preset_id}/apply: Apply preset configuration
- POST /api/indicators/custom: Create custom indicator configuration
- GET /api/indicators/recommendations: Market condition-based recommendations
- POST /api/indicators/calculate/{indicator}: Calculate indicator values
- GET /api/indicators/alerts/golden-cross: Golden cross alerts
- GET /api/indicators/alerts/death-cross: Death cross alerts
- GET /api/indicators/alerts/divergence: Price/indicator divergence alerts
- GET /api/indicators/cheat-sheet: Quick reference guide

Indicator Categories:
1. Moving Averages: SMA, EMA, WMA with multiple periods
2. Oscillators: RSI, Stochastic, MACD, KDJ
3. Volatility: Bollinger Bands, ATR, Keltner Channels
4. Support/Resistance: Pivot Points, Fibonacci Retracement
5. Volume: OBV, CMF, Volume Profile

Pre-configured Presets:
- Scalping Setup (1-5 min): EMA 5/10, RSI, MACD, BB
- Swing Trading Setup (4h-1D): SMA 50/200, RSI, MACD, Pivot
- Position Trading Setup (1D+): SMA 50/200, RSI, BB, Fibonacci
- Volatility Focus: BB, ATR, Keltner Channel, OBV
- Momentum Focus: RSI, Stochastic, MACD, KDJ

Features:
- Market condition recommendations (trending/ranging/volatile/calm)
- Timeframe-specific setups (scalping/swing/position)
- Quick reference cheat sheet for all indicators
- Signal alerts: Golden/Death Cross, Divergences
- Indicator calculation engine for backtesting

Frontend (AdvancedIndicatorsPanel.tsx):
- Three main tabs: Presets, Custom Setup, Quick Guide
- Preset selector with one-click application
- Custom indicator builder with drag-select
- Category-based organization
- Type-based color coding
- Indicator details and parameters
- Selected indicators summary
- Pro tips and best practices
- Legend for indicator types

Integration:
- Added Indicators tab to main navigation
- Full TypeScript support
- Responsive layout for all screen sizes
- Real-time preset switching
- Custom configuration persistence

Trading Presets Include:
- Setup recommendations for different timeframes
- Indicator period suggestions
- Signal confirmation rules
- Best practices for each trading style

Note: Backend uses mock calculations. In production, integrate with:
- TA-Lib for technical analysis
- Real-time price data feeds
- WebSocket for live indicator calculations
2025-11-16 06:00:18 +00:00

341 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useState } from 'react';
import { Settings, Zap, BookOpen, Grid3X3, Check } from 'lucide-react';
import axios from 'axios';
interface Indicator {
id: string;
name: string;
periods?: number[];
default_period?: number;
description?: string;
type: string;
}
interface IndicatorCategory {
name: string;
description: string;
indicators: Indicator[];
}
interface Preset {
id: string;
name: string;
indicators: string[];
description: string;
}
export default function AdvancedIndicatorsPanel() {
const [categories, setCategories] = useState<Record<string, IndicatorCategory>>({});
const [presets, setPresets] = useState<Preset[]>([]);
const [selectedIndicators, setSelectedIndicators] = useState<Set<string>>(new Set());
const [activeTab, setActiveTab] = useState<'presets' | 'custom' | 'guide'>('presets');
const [loading, setLoading] = useState(true);
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
const [cheatSheet, setCheatSheet] = useState<any>(null);
// Fetch indicators and presets
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
// Fetch available indicators
const indicatorsResponse = await axios.get('/api/indicators/available');
setCategories(indicatorsResponse.data.categories || {});
// Fetch presets
const presetsResponse = await axios.get('/api/indicators/presets');
setPresets(presetsResponse.data.presets || []);
// Fetch cheat sheet
const cheatSheetResponse = await axios.get('/api/indicators/cheat-sheet');
setCheatSheet(cheatSheetResponse.data || {});
} catch (error) {
console.error('Error fetching indicators data:', error);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
const handleIndicatorToggle = (indicatorId: string) => {
const newSet = new Set(selectedIndicators);
if (newSet.has(indicatorId)) {
newSet.delete(indicatorId);
} else {
newSet.add(indicatorId);
}
setSelectedIndicators(newSet);
};
const handlePresetSelect = (presetId: string) => {
const preset = presets.find((p) => p.id === presetId);
if (preset) {
setSelectedIndicators(new Set(preset.indicators));
setSelectedPreset(presetId);
}
};
const getTypeColor = (type: string): string => {
switch (type) {
case 'trend':
return 'bg-blue-900 text-blue-300';
case 'momentum':
return 'bg-purple-900 text-purple-300';
case 'volatility':
return 'bg-orange-900 text-orange-300';
case 'level':
return 'bg-green-900 text-green-300';
case 'volume':
return 'bg-pink-900 text-pink-300';
default:
return 'bg-gray-700 text-gray-300';
}
};
if (loading) {
return (
<div className="card">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Settings className="w-5 h-5 text-blue-500" />
Advanced Indicators
</h3>
<div className="text-center text-gray-400 py-8">Loading indicators...</div>
</div>
);
}
return (
<div className="space-y-4">
<div className="card">
<div className="mb-6">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Settings className="w-5 h-5 text-blue-500" />
Advanced Technical Indicators
</h3>
{/* Tabs */}
<div className="flex gap-2 mb-4">
<button
onClick={() => setActiveTab('presets')}
className={`px-4 py-2 rounded-lg font-medium transition ${
activeTab === 'presets'
? 'bg-blue-600 text-white'
: 'bg-dark-bg text-gray-400 hover:text-gray-200 border border-dark-border'
}`}
>
<Zap className="w-4 h-4 inline mr-2" />
Presets
</button>
<button
onClick={() => setActiveTab('custom')}
className={`px-4 py-2 rounded-lg font-medium transition ${
activeTab === 'custom'
? 'bg-blue-600 text-white'
: 'bg-dark-bg text-gray-400 hover:text-gray-200 border border-dark-border'
}`}
>
<Grid3X3 className="w-4 h-4 inline mr-2" />
Custom Setup ({selectedIndicators.size})
</button>
<button
onClick={() => setActiveTab('guide')}
className={`px-4 py-2 rounded-lg font-medium transition ${
activeTab === 'guide'
? 'bg-blue-600 text-white'
: 'bg-dark-bg text-gray-400 hover:text-gray-200 border border-dark-border'
}`}
>
<BookOpen className="w-4 h-4 inline mr-2" />
Quick Guide
</button>
</div>
</div>
{/* Presets Tab */}
{activeTab === 'presets' && (
<div className="space-y-3">
{presets.map((preset) => (
<div
key={preset.id}
onClick={() => handlePresetSelect(preset.id)}
className={`p-4 rounded-lg border cursor-pointer transition ${
selectedPreset === preset.id
? 'bg-blue-900 border-blue-500'
: 'bg-dark-bg border-dark-border hover:border-blue-500'
}`}
>
<div className="flex items-start justify-between mb-2">
<div className="flex-1">
<h4 className="font-semibold text-gray-200 mb-1 flex items-center gap-2">
{preset.name}
{selectedPreset === preset.id && <Check className="w-4 h-4 text-green-500" />}
</h4>
<p className="text-sm text-gray-400">{preset.description}</p>
</div>
<span className="bg-blue-900 text-blue-300 text-xs px-2 py-1 rounded">
{preset.indicators.length} indicators
</span>
</div>
<div className="flex flex-wrap gap-1 mt-2">
{preset.indicators.map((ind) => (
<span key={ind} className="bg-gray-700 text-gray-300 text-xs px-2 py-1 rounded">
{ind}
</span>
))}
</div>
</div>
))}
</div>
)}
{/* Custom Setup Tab */}
{activeTab === 'custom' && (
<div className="space-y-4">
{Object.entries(categories).map(([categoryKey, category]) => (
<div key={categoryKey} className="bg-dark-bg rounded-lg p-4 border border-dark-border">
<h4 className="font-semibold text-gray-200 mb-3 text-sm">
{category.name}
</h4>
<p className="text-xs text-gray-400 mb-3">{category.description}</p>
<div className="space-y-2">
{category.indicators.map((indicator) => (
<label key={indicator.id} className="flex items-start gap-3 cursor-pointer">
<input
type="checkbox"
checked={selectedIndicators.has(indicator.id)}
onChange={() => handleIndicatorToggle(indicator.id)}
className="mt-1 w-4 h-4"
/>
<div className="flex-1">
<p className="font-medium text-gray-200 text-sm">{indicator.name}</p>
<div className="flex gap-2 mt-1 flex-wrap">
<span className={`text-xs px-2 py-1 rounded ${getTypeColor(indicator.type)}`}>
{indicator.type}
</span>
{indicator.periods && indicator.default_period && (
<span className="text-xs bg-gray-700 text-gray-300 px-2 py-1 rounded">
Period: {indicator.default_period}
</span>
)}
</div>
</div>
</label>
))}
</div>
</div>
))}
{selectedIndicators.size > 0 && (
<div className="bg-green-900 bg-opacity-20 border border-green-700 rounded-lg p-4">
<h4 className="font-semibold text-green-400 mb-2">Configuration Ready</h4>
<p className="text-sm text-gray-300 mb-3">
You've selected {selectedIndicators.size} indicator(s). Click below to apply.
</p>
<button className="w-full bg-green-600 hover:bg-green-700 text-white font-medium py-2 rounded-lg transition">
<Check className="w-4 h-4 inline mr-2" />
Apply Configuration
</button>
</div>
)}
</div>
)}
{/* Quick Guide Tab */}
{activeTab === 'guide' && cheatSheet && (
<div className="space-y-4">
{Object.entries(cheatSheet).map(([key, value]) => (
<div key={key} className="bg-dark-bg rounded-lg p-4 border border-dark-border">
<h4 className="font-semibold text-gray-200 mb-3 capitalize">
{key.replace(/_/g, ' ')}
</h4>
<div className="space-y-2">
{typeof value === 'object' &&
!Array.isArray(value) &&
Object.entries(value).map(([subKey, subValue]) => (
<div key={subKey} className="text-sm">
<p className="font-medium text-blue-400">{subKey}</p>
<p className="text-gray-400 text-xs mt-1">{String(subValue)}</p>
</div>
))}
</div>
</div>
))}
</div>
)}
</div>
{/* Selected Indicators Summary */}
{selectedIndicators.size > 0 && activeTab !== 'guide' && (
<div className="card bg-blue-900 bg-opacity-20 border border-blue-700">
<h3 className="text-lg font-semibold mb-3 text-blue-400">Currently Selected</h3>
<div className="flex flex-wrap gap-2">
{Array.from(selectedIndicators).map((ind) => (
<div
key={ind}
className="bg-blue-900 border border-blue-700 text-blue-300 text-sm px-3 py-2 rounded-lg flex items-center gap-2"
>
{ind}
<button
onClick={() => {
const newSet = new Set(selectedIndicators);
newSet.delete(ind);
setSelectedIndicators(newSet);
}}
className="text-blue-400 hover:text-blue-200 font-bold"
>
×
</button>
</div>
))}
</div>
</div>
)}
{/* Indicator Types Legend */}
<div className="card">
<h3 className="font-semibold text-gray-300 mb-3 text-sm">Indicator Types</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-blue-600 rounded-full"></div>
<span className="text-xs text-gray-400">Trend</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-purple-600 rounded-full"></div>
<span className="text-xs text-gray-400">Momentum</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-orange-600 rounded-full"></div>
<span className="text-xs text-gray-400">Volatility</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-green-600 rounded-full"></div>
<span className="text-xs text-gray-400">Support/Resistance</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-pink-600 rounded-full"></div>
<span className="text-xs text-gray-400">Volume</span>
</div>
</div>
</div>
{/* Best Practices */}
<div className="card bg-yellow-900 bg-opacity-20 border border-yellow-700">
<h3 className="text-lg font-semibold mb-3 text-yellow-400">Pro Tips</h3>
<ul className="space-y-2 text-sm text-gray-300">
<li> Use 2-3 indicators maximum to avoid signal conflicts</li>
<li> Combine different indicator types (trend + momentum + volatility)</li>
<li> Scalping: Use fast periods (5, 10, 14)</li>
<li> Swing/Position: Use standard periods (20, 50, 200)</li>
<li> Always confirm signals with price action and volume</li>
<li> Use presets as a starting point, customize based on your style</li>
</ul>
</div>
</div>
);
}