File size: 8,435 Bytes
656ac31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import type { Candle, CandlestickPattern } from './types'

function bodySize(c: Candle) { return Math.abs(c.close - c.open) }
function totalRange(c: Candle) { return c.high - c.low }
function upperWick(c: Candle) { return c.high - Math.max(c.open, c.close) }
function lowerWick(c: Candle) { return Math.min(c.open, c.close) - c.low }
function isBullish(c: Candle) { return c.close > c.open }
function isBearish(c: Candle) { return c.close < c.open }

export function detectCandlestickPatterns(candles: Candle[]): CandlestickPattern[] {
  const patterns: CandlestickPattern[] = []
  if (candles.length < 3) return patterns

  // Only check last 30 candles for recency
  const start = Math.max(0, candles.length - 30)

  for (let i = start; i < candles.length; i++) {
    const c = candles[i]
    const body = bodySize(c)
    const range = totalRange(c)
    const uWick = upperWick(c)
    const lWick = lowerWick(c)
    const avgRange = candles.slice(Math.max(0, i - 10), i).reduce((s, x) => s + totalRange(x), 0) / Math.min(10, i || 1)

    // Doji
    if (range > 0 && body / range < 0.1 && range > avgRange * 0.5) {
      patterns.push({
        index: i, date: c.date, name: 'Doji', nameEn: 'Doji',
        type: 'neutral', reliability: 'medium',
        description: 'Açılış ve kapanış fiyatı neredeyse eşit. Piyasada kararsızlık sinyali verir. Trend dönüşü olabilir.',
      })
    }

    // Hammer (bullish)
    if (lWick > body * 2 && uWick < body * 0.5 && body > 0 && range > avgRange * 0.5) {
      patterns.push({
        index: i, date: c.date, name: 'Çekiç', nameEn: 'Hammer',
        type: 'bullish', reliability: 'high',
        description: 'Düşüş trendinde güçlü alıcı tepkisi. Uzun alt gölge, satıcıların geri püskürtüldüğünü gösterir. Yükseliş dönüş sinyali.',
      })
    }

    // Inverted Hammer
    if (uWick > body * 2 && lWick < body * 0.5 && body > 0 && range > avgRange * 0.5) {
      patterns.push({
        index: i, date: c.date, name: 'Ters Çekiç', nameEn: 'Inverted Hammer',
        type: 'bullish', reliability: 'medium',
        description: 'Düşüş trendinde potansiyel dönüş. Alıcılar fiyatı yukarı itmeye çalışıyor.',
      })
    }

    // Shooting Star (bearish)
    if (uWick > body * 2 && lWick < body * 0.3 && isBearish(c) && range > avgRange * 0.5 && i > 0 && candles[i - 1].close < c.high) {
      patterns.push({
        index: i, date: c.date, name: 'Kayan Yıldız', nameEn: 'Shooting Star',
        type: 'bearish', reliability: 'high',
        description: 'Yükseliş trendinde satıcı baskısı. Fiyat yukarı gidip geri dönmüş. Düşüş dönüş sinyali.',
      })
    }

    // Marubozu (strong trend candle)
    if (body > avgRange * 1.5 && uWick < body * 0.05 && lWick < body * 0.05) {
      patterns.push({
        index: i, date: c.date,
        name: isBullish(c) ? 'Boğa Marubozu' : 'Ayı Marubozu',
        nameEn: isBullish(c) ? 'Bullish Marubozu' : 'Bearish Marubozu',
        type: isBullish(c) ? 'bullish' : 'bearish', reliability: 'high',
        description: isBullish(c)
          ? 'Gölgesiz güçlü yükseliş mumu. Alıcılar tam kontrol. Güçlü yükseliş momentumu.'
          : 'Gölgesiz güçlü düşüş mumu. Satıcılar tam kontrol. Güçlü düşüş momentumu.',
      })
    }

    // Two-candle patterns
    if (i > 0) {
      const prev = candles[i - 1]

      // Bullish Engulfing
      if (isBearish(prev) && isBullish(c) && c.open <= prev.close && c.close >= prev.open && bodySize(c) > bodySize(prev) * 1.2) {
        patterns.push({
          index: i, date: c.date, name: 'Boğa Yutma', nameEn: 'Bullish Engulfing',
          type: 'bullish', reliability: 'high',
          description: 'Önceki düşüş mumunu tamamen yutan yükseliş mumu. Güçlü trend dönüş sinyali. Alıcılar kontrolü ele aldı.',
        })
      }

      // Bearish Engulfing
      if (isBullish(prev) && isBearish(c) && c.open >= prev.close && c.close <= prev.open && bodySize(c) > bodySize(prev) * 1.2) {
        patterns.push({
          index: i, date: c.date, name: 'Ayı Yutma', nameEn: 'Bearish Engulfing',
          type: 'bearish', reliability: 'high',
          description: 'Önceki yükseliş mumunu tamamen yutan düşüş mumu. Güçlü trend dönüş sinyali. Satıcılar kontrolü ele aldı.',
        })
      }

      // Piercing Line
      if (isBearish(prev) && isBullish(c) && c.open < prev.low && c.close > (prev.open + prev.close) / 2 && c.close < prev.open) {
        patterns.push({
          index: i, date: c.date, name: 'Delici Çizgi', nameEn: 'Piercing Line',
          type: 'bullish', reliability: 'medium',
          description: 'Boşluk aşağı açılıp önceki mumun ortasının üstünde kapandı. Alıcılar güç kazanıyor.',
        })
      }

      // Dark Cloud Cover
      if (isBullish(prev) && isBearish(c) && c.open > prev.high && c.close < (prev.open + prev.close) / 2 && c.close > prev.open) {
        patterns.push({
          index: i, date: c.date, name: 'Kara Bulut', nameEn: 'Dark Cloud Cover',
          type: 'bearish', reliability: 'medium',
          description: 'Boşluk yukarı açılıp önceki mumun ortasının altında kapandı. Satıcılar güç kazanıyor.',
        })
      }

      // Tweezer Bottom
      if (Math.abs(c.low - prev.low) / c.low < 0.002 && isBearish(prev) && isBullish(c)) {
        patterns.push({
          index: i, date: c.date, name: 'Cımbız Dibi', nameEn: 'Tweezer Bottom',
          type: 'bullish', reliability: 'medium',
          description: 'İki ardışık mum aynı dip seviyesine dokundu. Güçlü destek ve potansiyel dönüş noktası.',
        })
      }

      // Tweezer Top
      if (Math.abs(c.high - prev.high) / c.high < 0.002 && isBullish(prev) && isBearish(c)) {
        patterns.push({
          index: i, date: c.date, name: 'Cımbız Tepe', nameEn: 'Tweezer Top',
          type: 'bearish', reliability: 'medium',
          description: 'İki ardışık mum aynı tepe seviyesine dokundu. Güçlü direnç ve potansiyel dönüş noktası.',
        })
      }
    }

    // Three-candle patterns
    if (i >= 2) {
      const c2 = candles[i - 1]
      const c1 = candles[i - 2]

      // Morning Star
      if (isBearish(c1) && bodySize(c2) < bodySize(c1) * 0.3 && isBullish(c) &&
          c.close > (c1.open + c1.close) / 2 && bodySize(c) > bodySize(c1) * 0.5) {
        patterns.push({
          index: i, date: c.date, name: 'Sabah Yıldızı', nameEn: 'Morning Star',
          type: 'bullish', reliability: 'high',
          description: 'Üç mumlu güçlü yükseliş dönüş formasyonu. Düşüşten sonra kararsızlık, ardından güçlü alım. Güvenilir dönüş sinyali.',
        })
      }

      // Evening Star
      if (isBullish(c1) && bodySize(c2) < bodySize(c1) * 0.3 && isBearish(c) &&
          c.close < (c1.open + c1.close) / 2 && bodySize(c) > bodySize(c1) * 0.5) {
        patterns.push({
          index: i, date: c.date, name: 'Akşam Yıldızı', nameEn: 'Evening Star',
          type: 'bearish', reliability: 'high',
          description: 'Üç mumlu güçlü düşüş dönüş formasyonu. Yükselişten sonra kararsızlık, ardından güçlü satış. Güvenilir dönüş sinyali.',
        })
      }

      // Three White Soldiers
      if (isBullish(c1) && isBullish(c2) && isBullish(c) &&
          c2.close > c1.close && c.close > c2.close &&
          bodySize(c1) > avgRange * 0.3 && bodySize(c2) > avgRange * 0.3 && bodySize(c) > avgRange * 0.3) {
        patterns.push({
          index: i, date: c.date, name: 'Üç Beyaz Asker', nameEn: 'Three White Soldiers',
          type: 'bullish', reliability: 'high',
          description: 'Art arda üç güçlü yükseliş mumu. Alıcılar tam kontrol, güçlü devam sinyali.',
        })
      }

      // Three Black Crows
      if (isBearish(c1) && isBearish(c2) && isBearish(c) &&
          c2.close < c1.close && c.close < c2.close &&
          bodySize(c1) > avgRange * 0.3 && bodySize(c2) > avgRange * 0.3 && bodySize(c) > avgRange * 0.3) {
        patterns.push({
          index: i, date: c.date, name: 'Üç Kara Karga', nameEn: 'Three Black Crows',
          type: 'bearish', reliability: 'high',
          description: 'Art arda üç güçlü düşüş mumu. Satıcılar tam kontrol, güçlü düşüş devam sinyali.',
        })
      }
    }
  }

  return patterns
}