feat: Phase 3 — indicateurs techniques calibrés par horizon option
- technical_indicators.py (nouveau) : compute_indicators() calcule RSI, MA fast/slow, Bollinger Bands, ATR — périodes calibrées automatiquement selon horizon_days - config.py : endpoints GET/PUT /config/tech-indicators (activé, liste, auto-calibration) - useApi.ts : useTechIndicatorsConfig + useSaveTechIndicatorsConfig hooks - Config.tsx : carte "Indicateurs techniques" dans Options—Paramètres avec toggles - auto_cycle.py : compute top-5 tickers à chaque cycle si tech_indicators_enabled=true - ai_analyzer.py : tech_indicators_block injecté dans suggestion + scoring prompts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -491,6 +491,25 @@ export const useSaveOptionsGate = () => {
|
||||
})
|
||||
}
|
||||
|
||||
export const useTechIndicatorsConfig = () =>
|
||||
useQuery({
|
||||
queryKey: ['tech-indicators-config'],
|
||||
queryFn: () => api.get('/config/tech-indicators').then(r => r.data),
|
||||
staleTime: 60_000,
|
||||
})
|
||||
|
||||
export const useSaveTechIndicatorsConfig = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (body: {
|
||||
tech_indicators_enabled?: boolean
|
||||
tech_indicators_list?: string
|
||||
tech_indicators_auto_calibrate?: boolean
|
||||
}) => api.put('/config/tech-indicators', body).then(r => r.data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['tech-indicators-config'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export const useSimPortfolioRisk = () =>
|
||||
useQuery({
|
||||
queryKey: ['journal-portfolio-risk'],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { useSources, useUpdateSources, useUpdateApiKeys, useConfig, useAiStatus, useAnalysisConfig, useSaveAnalysisConfig, useCycleStatus, useUpdateCycleConfig, useTriggerCycle, useRiskProfiles, useUpsertProfile, useDeleteProfile, useExitDefaults, useSaveExitDefaults, useOptionsGate, useSaveOptionsGate } from '../hooks/useApi'
|
||||
import { useSources, useUpdateSources, useUpdateApiKeys, useConfig, useAiStatus, useAnalysisConfig, useSaveAnalysisConfig, useCycleStatus, useUpdateCycleConfig, useTriggerCycle, useRiskProfiles, useUpsertProfile, useDeleteProfile, useExitDefaults, useSaveExitDefaults, useOptionsGate, useSaveOptionsGate, useTechIndicatorsConfig, useSaveTechIndicatorsConfig } from '../hooks/useApi'
|
||||
import { Settings, Key, Globe, CheckCircle, XCircle, AlertCircle, Save, Eye, EyeOff, Brain, SlidersHorizontal, RefreshCw, Zap, Plus, Trash2, Pencil, X, Lock, Gauge, DollarSign, TrendingUp, ShieldAlert } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
@@ -361,6 +361,13 @@ export default function Config() {
|
||||
const [ivGateExtreme, setIvGateExtreme] = useState(80)
|
||||
const [ivGateSkew, setIvGateSkew] = useState(8)
|
||||
|
||||
// Tech indicators local state
|
||||
const { data: techIndicatorsData } = useTechIndicatorsConfig()
|
||||
const { mutate: saveTechIndicators, isPending: savingTechIndicators } = useSaveTechIndicatorsConfig()
|
||||
const [techEnabled, setTechEnabled] = useState(true)
|
||||
const [techAutoCalibrate, setTechAutoCalibrate] = useState(true)
|
||||
const [techList, setTechList] = useState<string[]>(['rsi', 'ma', 'bollinger', 'atr'])
|
||||
|
||||
// Analysis config local state
|
||||
const [analysisTopN, setAnalysisTopN] = useState(10)
|
||||
const [analysisCategoryDefault, setAnalysisCategoryDefault] = useState('all')
|
||||
@@ -454,6 +461,15 @@ export default function Config() {
|
||||
}
|
||||
}, [optionsGateData])
|
||||
|
||||
useEffect(() => {
|
||||
if (techIndicatorsData) {
|
||||
setTechEnabled(techIndicatorsData.tech_indicators_enabled ?? true)
|
||||
setTechAutoCalibrate(techIndicatorsData.tech_indicators_auto_calibrate ?? true)
|
||||
const list = (techIndicatorsData.tech_indicators_list || 'rsi,ma,bollinger,atr').split(',').map((s: string) => s.trim()).filter(Boolean)
|
||||
setTechList(list)
|
||||
}
|
||||
}, [techIndicatorsData])
|
||||
|
||||
const displaySources = localSources ?? sources ?? {}
|
||||
|
||||
const toggleSource = (key: string) => {
|
||||
@@ -1275,6 +1291,85 @@ export default function Config() {
|
||||
{savingExitDefaults ? 'Sauvegarde...' : 'Sauvegarder les seuils'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* ── Indicateurs techniques du sous-jacent ── */}
|
||||
<div className="card border-purple-700/30 bg-purple-900/5">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h2 className="text-base font-bold text-white flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4 text-purple-400" /> Indicateurs techniques du sous-jacent
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setTechEnabled(!techEnabled)}
|
||||
className={clsx('text-xs px-3 py-1 rounded border font-semibold transition-all', {
|
||||
'bg-purple-600 border-purple-500 text-white': techEnabled,
|
||||
'bg-dark-700 border-slate-700 text-slate-400': !techEnabled,
|
||||
})}>
|
||||
{techEnabled ? '✓ Activé' : '○ Désactivé'}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mb-4">
|
||||
Injecte RSI, MA, Bollinger et ATR dans le contexte IA à chaque cycle. Les périodes sont calibrées automatiquement selon l'horizon de l'option analysée.
|
||||
</p>
|
||||
|
||||
<div className={clsx('space-y-4 transition-opacity', !techEnabled && 'opacity-40 pointer-events-none')}>
|
||||
{/* Indicateurs à activer */}
|
||||
<div>
|
||||
<label className="text-xs text-slate-400 font-medium block mb-2">Indicateurs actifs</label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{[
|
||||
{ key: 'rsi', label: 'RSI', desc: 'Momentum / surachat-survente' },
|
||||
{ key: 'ma', label: 'MA fast/slow', desc: 'Tendance & golden/death cross' },
|
||||
{ key: 'bollinger', label: 'Bollinger', desc: 'Position dans les bandes de vol' },
|
||||
{ key: 'atr', label: 'ATR', desc: 'Volatilité réalisée récente' },
|
||||
].map(({ key, label, desc }) => {
|
||||
const active = techList.includes(key)
|
||||
return (
|
||||
<button
|
||||
key={key}
|
||||
title={desc}
|
||||
onClick={() => setTechList(prev =>
|
||||
active ? prev.filter(k => k !== key) : [...prev, key]
|
||||
)}
|
||||
className={clsx('text-xs px-3 py-1.5 rounded border font-semibold transition-all', {
|
||||
'bg-purple-700 border-purple-500 text-white': active,
|
||||
'bg-dark-700 border-slate-700 text-slate-400 hover:border-purple-600': !active,
|
||||
})}>
|
||||
{active ? '✓ ' : ''}{label}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<p className="text-[10px] text-slate-600 mt-1.5">Survolez pour voir la description de chaque indicateur.</p>
|
||||
</div>
|
||||
|
||||
{/* Calibration auto */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-slate-300 font-medium">Calibration automatique selon horizon</p>
|
||||
<p className="text-[10px] text-slate-500">≤30j → RSI14, MA20/50 | ≤90j → RSI21, MA50/100 | >90j → RSI28, MA100/200</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setTechAutoCalibrate(!techAutoCalibrate)}
|
||||
className={clsx('text-xs px-3 py-1 rounded border font-semibold transition-all flex-shrink-0 ml-4', {
|
||||
'bg-purple-600 border-purple-500 text-white': techAutoCalibrate,
|
||||
'bg-dark-700 border-slate-700 text-slate-400': !techAutoCalibrate,
|
||||
})}>
|
||||
{techAutoCalibrate ? '✓ Auto' : '○ Fixe'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => saveTechIndicators(
|
||||
{ tech_indicators_enabled: techEnabled, tech_indicators_list: techList.join(','), tech_indicators_auto_calibrate: techAutoCalibrate },
|
||||
{ onSuccess: () => { setSavedMsg('Indicateurs techniques sauvegardés'); setTimeout(() => setSavedMsg(''), 2000) } }
|
||||
)}
|
||||
disabled={savingTechIndicators}
|
||||
className="mt-4 flex items-center gap-1.5 bg-purple-700 hover:bg-purple-600 disabled:opacity-40 text-white px-4 py-2 rounded text-sm font-semibold">
|
||||
<Save className="w-4 h-4" />
|
||||
{savingTechIndicators ? 'Sauvegarde...' : 'Sauvegarder les indicateurs'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user