15 ? 'bg-emerald-950/50 border-emerald-700' :
summary.score < -15 ? 'bg-red-950/50 border-red-700' :
'bg-yellow-950/50 border-yellow-700'
}`}>
15 ? 'text-emerald-400' :
summary.score < -15 ? 'text-red-400' :
'text-yellow-400'
}`}>
{summary.overallSignal}
{t('Skor:', 'Score:')}
15 ? 'bg-emerald-500' :
summary.score < -15 ? 'bg-red-500' : 'bg-yellow-500'
}`}
style={{ width: `${Math.abs(summary.score)}%`, marginLeft: summary.score < 0 ? `${100 - Math.abs(summary.score)}%` : '0' }}
/>
0 ? 'text-emerald-400' : summary.score < 0 ? 'text-red-400' : 'text-gray-400'}`}>
{summary.score > 0 ? '+' : ''}{summary.score}
{/* ─── Controls ────────────────────────── */}
{/* Period selector */}
{PERIODS.map((p) => (
))}
{/* Overlay toggles */}
{OVERLAYS.map((o) => (
))}
{/* S/R toggle */}
{/* Fibonacci toggle */}
{/* Pattern markers toggle */}
{/* ─── Main Price Chart ────────────────── */}
v.toFixed(2)}
tickLine={{ stroke: '#4b5563' }}
axisLine={{ stroke: '#374151' }}
width={65}
/>
} />
{/* Bollinger Bands Area */}
{overlays.has('bollinger') && (
<>
>
)}
{/* Support/Resistance lines */}
{showSR && supportResistance.map((sr, i) => (
= 3 ? 2 : 1}
opacity={0.7}
label={{
value: `${sr.type === 'support' ? 'D' : 'R'}: ${cs}${sr.price.toFixed(2)}`,
fill: sr.type === 'support' ? '#22c55e' : '#ef4444',
fontSize: 10,
position: sr.type === 'support' ? 'insideBottomRight' : 'insideTopRight',
}}
/>
))}
{/* Fibonacci levels */}
{showFib && fibonacci.map((fib, i) => (
))}
{/* Price line (close) */}
{/* Candlestick-like body bars */}
{chartData.map((entry, i) => (
|
))}
{/* Moving averages overlays */}
{overlays.has('sma20') && (
)}
{overlays.has('sma50') && (
)}
{overlays.has('sma200') && (
)}
{overlays.has('ema') && (
<>
>
)}
{/* Pattern markers */}
{showPatterns && chartData.some((d) => d.hasPattern) && (
d.hasPattern ? d.high * 1.01 : undefined}
stroke="none"
dot={(props: { key?: string; cx: number; cy: number; payload?: { hasPattern?: boolean; patternInfo?: Array<{ type: string; name: string }> } }) => {
if (!props.payload?.hasPattern) return
const pattern = props.payload.patternInfo?.[0]
const color = pattern?.type === 'bullish' ? '#22c55e' : pattern?.type === 'bearish' ? '#ef4444' : '#f59e0b'
return (
{pattern?.type === 'bullish' ? '▲' : pattern?.type === 'bearish' ? '▼' : '◆'}
)
}}
connectNulls={false}
isAnimationActive={false}
/>
)}
''}
/>
{/* ─── Sub-Panel (RSI / MACD / Volume / Stochastic) ─ */}
{/* Sub-panel tabs */}
{subPanels.map((sp) => (
))}
{/* Volume sub-panel — each component must be a direct child of ComposedChart (no Fragment wrapper) */}
{subPanel === 'volume' && v >= 1e6 ? `${(v / 1e6).toFixed(1)}M` : v >= 1e3 ? `${(v / 1e3).toFixed(0)}K` : `${v}`} axisLine={{ stroke: '#374151' }} tickLine={false} width={50} />}
{subPanel === 'volume' && } />}
{subPanel === 'volume' && (
{chartData.map((entry, i) => (
|
))}
)}
{/* RSI sub-panel */}
{subPanel === 'rsi' && }
{subPanel === 'rsi' && } />}
{subPanel === 'rsi' && }
{subPanel === 'rsi' && }
{subPanel === 'rsi' && }
{subPanel === 'rsi' && }
{subPanel === 'rsi' && }
{/* MACD sub-panel */}
{subPanel === 'macd' && }
{subPanel === 'macd' && } />}
{subPanel === 'macd' && }
{subPanel === 'macd' && (
{chartData.map((entry, i) => (
= 0 ? 'rgba(34,197,94,0.7)' : 'rgba(239,68,68,0.7)'} />
))}
|
)}
{subPanel === 'macd' && }
{subPanel === 'macd' && }
{/* Stochastic sub-panel */}
{subPanel === 'stochastic' && }
{subPanel === 'stochastic' && } />}
{subPanel === 'stochastic' && }
{subPanel === 'stochastic' && }
{subPanel === 'stochastic' && }
{subPanel === 'stochastic' && }
{/* ─── Analysis Tabs ───────────────────── */}
{[
{ key: 'summary' as const, label: t('Özet & Sebepler', 'Summary & Reasons') },
{ key: 'formations' as const, label: `${t('Formasyonlar', 'Patterns')} (${chartFormations.length})` },
{ key: 'candles' as const, label: `${t('Mum Formasyonları', 'Candlestick Patterns')} (${candlestickPatterns.length})` },
].map((tab) => (
))}
{/* Summary Tab */}
{activeTab === 'summary' && (
{/* Trend info */}
{t('Trend Analizi', 'Trend Analysis')}
{trend.description}
{t('Trend Gücü:', 'Trend Strength:')}
60 ? 'bg-emerald-500' : trend.strength > 40 ? 'bg-yellow-500' : 'bg-red-500'}`}
style={{ width: `${trend.strength}%` }}
/>
{trend.strength}/100
{/* Signal reasons */}
{t('Sinyal Sebepleri', 'Signal Reasons')}
{summary.reasons.map((reason, i) => (
●
{reason}
))}
{/* Support & Resistance */}
{supportResistance.length > 0 && (
{t('Destek & Direnç Seviyeleri', 'Support & Resistance Levels')}
{supportResistance.map((sr, i) => (
{sr.type === 'support' ? t('Destek', 'Support') : t('Direnç', 'Resistance')}
{cs}{sr.price.toFixed(2)}
{t('Güç:', 'Strength:')}
{Array.from({ length: 5 }).map((_, j) => (
))}
))}
)}
{/* Fibonacci */}
{fibonacci.length > 0 && (
{t('Fibonacci Düzeltme Seviyeleri', 'Fibonacci Retracement Levels')}
{fibonacci.map((fib, i) => (
{fib.label}
{cs}{fib.price.toFixed(2)}
))}
)}
)}
{/* Chart Formations Tab */}
{activeTab === 'formations' && (
{chartFormations.length === 0 ? (
{t('Bu periyotta grafik formasyonu tespit edilmedi.', 'No chart pattern was detected in this period.')}
) : (
chartFormations.map((f, i) => (
{f.type === 'bullish' ? '▲' : f.type === 'bearish' ? '▼' : '◆'}
{f.name}
({f.nameEn})
{f.reliability === 'high' ? t('Yüksek', 'High') : f.reliability === 'medium' ? t('Orta', 'Medium') : t('Düşük', 'Low')} {t('güvenilirlik', 'reliability')}
{f.description}
{t('Başlangıç:', 'Start:')} {formatDate(f.startDate)}
{t('Bitiş:', 'End:')} {formatDate(f.endDate)}
{f.targetPrice && (
{t('Hedef:', 'Target:')} {cs}{f.targetPrice.toFixed(2)}
)}
))
)}
)}
{/* Candlestick Patterns Tab */}
{activeTab === 'candles' && (
{candlestickPatterns.length === 0 ? (
{t('Bu periyotta mum formasyonu tespit edilmedi.', 'No candlestick pattern was detected in this period.')}
) : (
candlestickPatterns.slice(-15).reverse().map((p, i) => (
{p.name}
({p.nameEn})
{p.reliability === 'high' ? '★★★' : p.reliability === 'medium' ? '★★' : '★'}
{formatDate(p.date)}
{p.description}
))
)}
)}
)
}
// ─── Helper Components ─────────────────────
function TrendBadge({ label, trend, isUS = false }: { label: string; trend: string; isUS?: boolean }) {
const trendText = isUS
? trend === 'Yükseliş' ? 'Uptrend' : trend === 'Düşüş' ? 'Downtrend' : trend === 'Yatay' ? 'Sideways' : trend
: trend
const color = trend === 'Yükseliş' || trend === 'Uptrend' ? 'text-emerald-400 bg-emerald-950/50' :
trend === 'Düşüş' || trend === 'Downtrend' ? 'text-red-400 bg-red-950/50' :
'text-yellow-400 bg-yellow-950/50'
return (
{label}: {trendText}
)
}
function formatDateShort(dateStr: string, locale = 'tr-TR'): string {
const d = new Date(dateStr)
return new Intl.DateTimeFormat(locale, { day: '2-digit', month: 'short' }).format(d)
}
// ─── Custom Tooltips ───────────────────────
interface ChartDataPoint {
open?: number; close?: number; high?: number; low?: number;
volume?: number; bullish?: boolean; dateFormatted?: string;
sma20?: number; sma50?: number; rsi?: number;
macd?: number; macdSignal?: number; macdHist?: number;
stochK?: number; stochD?: number;
hasPattern?: boolean; patternInfo?: Array<{ type: string; name: string }>;
[key: string]: unknown;
}
interface TooltipProps { active?: boolean; payload?: Array<{ payload: ChartDataPoint }>; label?: string }
function PriceTooltip({ active, payload, label, currencySymbol = '₺' }: TooltipProps & { currencySymbol?: string }) {
if (!active || !payload?.length) return null
const d = payload[0]?.payload
if (!d) return null
const cs = currencySymbol
return (
{label}
Açılış:
{cs}{d.open?.toFixed(2)}
Yüksek:
{cs}{d.high?.toFixed(2)}
Düşük:
{cs}{d.low?.toFixed(2)}
Kapanış:
{cs}{d.close?.toFixed(2)}
Hacim:
{formatNumber(d.volume || 0)}
{d.sma20 && (
{d.sma20 &&
SMA20{cs}{d.sma20.toFixed(2)}
}
{d.sma50 &&
SMA50{cs}{d.sma50.toFixed(2)}
}
{d.rsi != null &&
RSI{d.rsi.toFixed(1)}
}
)}
{d.hasPattern && (d.patternInfo?.length ?? 0) > 0 && (
{d.patternInfo?.map((p: { type: string; name: string }, i: number) => (
● {p.name}
))}
)}
)
}
function SubTooltip({ active, payload, type }: TooltipProps & { type: string }) {
if (!active || !payload?.length) return null
const d = payload[0]?.payload
if (!d) return null
return (
{d.dateFormatted}
{type === 'volume' && (
Hacim: {formatNumber(d.volume || 0)}
)}
{type === 'rsi' && d.rsi != null && (
70 ? 'text-red-400' : d.rsi < 30 ? 'text-emerald-400' : 'text-orange-400'}`}>
RSI: {d.rsi.toFixed(1)}
)}
{type === 'macd' && (
{d.macd != null &&
MACD: {d.macd.toFixed(4)}
}
{d.macdSignal != null &&
Sinyal: {d.macdSignal.toFixed(4)}
}
{d.macdHist != null &&
= 0 ? 'text-emerald-400' : 'text-red-400'}`}>Hist: {d.macdHist.toFixed(4)}
}
)}
{type === 'stochastic' && (
{d.stochK != null &&
%K: {d.stochK.toFixed(1)}
}
{d.stochD != null &&
%D: {d.stochD.toFixed(1)}
}
)}
)
}