Spaces:
Sleeping
Sleeping
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,341 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
EURUSD Pattern AI - yfinance + Gradio
|
| 3 |
-
Candlestick pattern tespiti ve AI yorumu
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import gradio as gr
|
| 7 |
-
import yfinance as yf
|
| 8 |
-
import pandas as pd
|
| 9 |
-
import numpy as np
|
| 10 |
-
import plotly.graph_objects as go
|
| 11 |
-
from datetime import datetime, timedelta
|
| 12 |
-
|
| 13 |
-
# ─── PATTERN TESPİT FONKSİYONLARI ───────────────────────────────────────────
|
| 14 |
-
|
| 15 |
-
def detect_patterns(df):
|
| 16 |
-
"""Temel candlestick patternleri tespit eder."""
|
| 17 |
-
patterns = []
|
| 18 |
-
df = df.copy()
|
| 19 |
-
|
| 20 |
-
o = df["Open"].values
|
| 21 |
-
h = df["High"].values
|
| 22 |
-
l = df["Low"].values
|
| 23 |
-
c = df["Close"].values
|
| 24 |
-
dates = df.index
|
| 25 |
-
|
| 26 |
-
for i in range(2, len(df)):
|
| 27 |
-
found = []
|
| 28 |
-
|
| 29 |
-
body = abs(c[i] - o[i])
|
| 30 |
-
body_prev = abs(c[i-1] - o[i-1])
|
| 31 |
-
rng = h[i] - l[i]
|
| 32 |
-
upper_wick = h[i] - max(c[i], o[i])
|
| 33 |
-
lower_wick = min(c[i], o[i]) - l[i]
|
| 34 |
-
|
| 35 |
-
# Doji
|
| 36 |
-
if body < rng * 0.1:
|
| 37 |
-
found.append("🔵 Doji")
|
| 38 |
-
|
| 39 |
-
# Hammer
|
| 40 |
-
if (lower_wick > body * 2 and upper_wick < body * 0.5
|
| 41 |
-
and c[i-1] < o[i-1]):
|
| 42 |
-
found.append("🟢 Hammer (Bullish)")
|
| 43 |
-
|
| 44 |
-
# Shooting Star
|
| 45 |
-
if (upper_wick > body * 2 and lower_wick < body * 0.5
|
| 46 |
-
and c[i-1] > o[i-1]):
|
| 47 |
-
found.append("🔴 Shooting Star (Bearish)")
|
| 48 |
-
|
| 49 |
-
# Bullish Engulfing
|
| 50 |
-
if (c[i-1] < o[i-1] and c[i] > o[i]
|
| 51 |
-
and c[i] > o[i-1] and o[i] < c[i-1]):
|
| 52 |
-
found.append("🟢 Bullish Engulfing")
|
| 53 |
-
|
| 54 |
-
# Bearish Engulfing
|
| 55 |
-
if (c[i-1] > o[i-1] and c[i] < o[i]
|
| 56 |
-
and c[i] < o[i-1] and o[i] > c[i-1]):
|
| 57 |
-
found.append("🔴 Bearish Engulfing")
|
| 58 |
-
|
| 59 |
-
# Morning Star
|
| 60 |
-
if (i >= 2 and c[i-2] < o[i-2]
|
| 61 |
-
and body_prev < abs(c[i-2] - o[i-2]) * 0.3
|
| 62 |
-
and c[i] > o[i]
|
| 63 |
-
and c[i] > (c[i-2] + o[i-2]) / 2):
|
| 64 |
-
found.append("🟢 Morning Star")
|
| 65 |
-
|
| 66 |
-
# Evening Star
|
| 67 |
-
if (i >= 2 and c[i-2] > o[i-2]
|
| 68 |
-
and body_prev < abs(c[i-2] - o[i-2]) * 0.3
|
| 69 |
-
and c[i] < o[i]
|
| 70 |
-
and c[i] < (c[i-2] + o[i-2]) / 2):
|
| 71 |
-
found.append("🔴 Evening Star")
|
| 72 |
-
|
| 73 |
-
# Three White Soldiers
|
| 74 |
-
if (i >= 2
|
| 75 |
-
and c[i] > o[i] and c[i-1] > o[i-1] and c[i-2] > o[i-2]
|
| 76 |
-
and c[i] > c[i-1] > c[i-2]):
|
| 77 |
-
found.append("🟢 Three White Soldiers")
|
| 78 |
-
|
| 79 |
-
# Three Black Crows
|
| 80 |
-
if (i >= 2
|
| 81 |
-
and c[i] < o[i] and c[i-1] < o[i-1] and c[i-2] < o[i-2]
|
| 82 |
-
and c[i] < c[i-1] < c[i-2]):
|
| 83 |
-
found.append("🔴 Three Black Crows")
|
| 84 |
-
|
| 85 |
-
# Spinning Top
|
| 86 |
-
if (body < rng * 0.3 and upper_wick > body and lower_wick > body):
|
| 87 |
-
found.append("🔵 Spinning Top")
|
| 88 |
-
|
| 89 |
-
if found:
|
| 90 |
-
patterns.append({
|
| 91 |
-
"Tarih": dates[i].strftime("%Y-%m-%d %H:%M") if hasattr(dates[i], "hour") else str(dates[i])[:10],
|
| 92 |
-
"Pattern": ", ".join(found),
|
| 93 |
-
"Close": round(c[i], 5),
|
| 94 |
-
"Yön": "↑ Bullish" if any("Bullish" in p or "Hammer" in p or "Soldier" in p or "Morning" in p for p in found)
|
| 95 |
-
else ("↓ Bearish" if any("Bearish" in p or "Star" in p or "Crow" in p or "Evening" in p for p in found)
|
| 96 |
-
else "↔ Nötr"),
|
| 97 |
-
})
|
| 98 |
-
|
| 99 |
-
return patterns[-30:] if len(patterns) > 30 else patterns # son 30 pattern
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
def calculate_indicators(df):
|
| 103 |
-
"""RSI, EMA, Bollinger hesaplar."""
|
| 104 |
-
df = df.copy()
|
| 105 |
-
|
| 106 |
-
# EMA
|
| 107 |
-
df["EMA20"] = df["Close"].ewm(span=20).mean()
|
| 108 |
-
df["EMA50"] = df["Close"].ewm(span=50).mean()
|
| 109 |
-
|
| 110 |
-
# RSI
|
| 111 |
-
delta = df["Close"].diff()
|
| 112 |
-
gain = delta.clip(lower=0).rolling(14).mean()
|
| 113 |
-
loss = (-delta.clip(upper=0)).rolling(14).mean()
|
| 114 |
-
rs = gain / loss
|
| 115 |
-
df["RSI"] = 100 - (100 / (1 + rs))
|
| 116 |
-
|
| 117 |
-
# Bollinger
|
| 118 |
-
df["BB_mid"] = df["Close"].rolling(20).mean()
|
| 119 |
-
std = df["Close"].rolling(20).std()
|
| 120 |
-
df["BB_upper"] = df["BB_mid"] + 2 * std
|
| 121 |
-
df["BB_lower"] = df["BB_mid"] - 2 * std
|
| 122 |
-
|
| 123 |
-
# MACD
|
| 124 |
-
ema12 = df["Close"].ewm(span=12).mean()
|
| 125 |
-
ema26 = df["Close"].ewm(span=26).mean()
|
| 126 |
-
df["MACD"] = ema12 - ema26
|
| 127 |
-
df["Signal"] = df["MACD"].ewm(span=9).mean()
|
| 128 |
-
|
| 129 |
-
return df
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
def build_chart(df, patterns):
|
| 133 |
-
"""Plotly candlestick + indikatör grafiği oluşturur."""
|
| 134 |
-
fig = go.Figure()
|
| 135 |
-
|
| 136 |
-
# Candlestick
|
| 137 |
-
fig.add_trace(go.Candlestick(
|
| 138 |
-
x=df.index, open=df["Open"], high=df["High"],
|
| 139 |
-
low=df["Low"], close=df["Close"],
|
| 140 |
-
name="EUR/USD", increasing_line_color="#26a69a",
|
| 141 |
-
decreasing_line_color="#ef5350"
|
| 142 |
-
))
|
| 143 |
-
|
| 144 |
-
# EMA
|
| 145 |
-
fig.add_trace(go.Scatter(x=df.index, y=df["EMA20"], name="EMA 20",
|
| 146 |
-
line=dict(color="#FFA726", width=1.2)))
|
| 147 |
-
fig.add_trace(go.Scatter(x=df.index, y=df["EMA50"], name="EMA 50",
|
| 148 |
-
line=dict(color="#AB47BC", width=1.2)))
|
| 149 |
-
|
| 150 |
-
# Bollinger
|
| 151 |
-
fig.add_trace(go.Scatter(x=df.index, y=df["BB_upper"], name="BB Üst",
|
| 152 |
-
line=dict(color="#607D8B", dash="dot", width=1)))
|
| 153 |
-
fig.add_trace(go.Scatter(x=df.index, y=df["BB_lower"], name="BB Alt",
|
| 154 |
-
line=dict(color="#607D8B", dash="dot", width=1),
|
| 155 |
-
fill="tonexty", fillcolor="rgba(96,125,139,0.07)"))
|
| 156 |
-
|
| 157 |
-
# Pattern işaretleri
|
| 158 |
-
for p in patterns[-10:]:
|
| 159 |
-
try:
|
| 160 |
-
dt = pd.to_datetime(p["Tarih"])
|
| 161 |
-
row = df[df.index.date == dt.date()] if hasattr(df.index[0], "date") else df[df.index == p["Tarih"]]
|
| 162 |
-
if not row.empty:
|
| 163 |
-
y_pos = row["High"].values[0] * 1.001
|
| 164 |
-
color = "#26a69a" if "↑" in p["Yön"] else ("#ef5350" if "↓" in p["Yön"] else "#FFA726")
|
| 165 |
-
fig.add_annotation(x=row.index[0], y=y_pos,
|
| 166 |
-
text=p["Pattern"].split(",")[0][2:],
|
| 167 |
-
showarrow=True, arrowhead=2,
|
| 168 |
-
font=dict(size=9, color=color),
|
| 169 |
-
arrowcolor=color)
|
| 170 |
-
except Exception:
|
| 171 |
-
pass
|
| 172 |
-
|
| 173 |
-
fig.update_layout(
|
| 174 |
-
title="EUR/USD – Candlestick + İndikatörler",
|
| 175 |
-
template="plotly_dark",
|
| 176 |
-
xaxis_rangeslider_visible=False,
|
| 177 |
-
height=550,
|
| 178 |
-
legend=dict(orientation="h", y=1.02),
|
| 179 |
-
margin=dict(l=40, r=20, t=50, b=20),
|
| 180 |
-
font=dict(family="Inter, sans-serif"),
|
| 181 |
-
paper_bgcolor="#0f1117",
|
| 182 |
-
plot_bgcolor="#0f1117",
|
| 183 |
-
)
|
| 184 |
-
return fig
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
def ai_summary(df, patterns):
|
| 188 |
-
"""Kural tabanlı AI yorumu üretir."""
|
| 189 |
-
if df is None or df.empty:
|
| 190 |
-
return "Veri yok."
|
| 191 |
-
|
| 192 |
-
latest = df.iloc[-1]
|
| 193 |
-
rsi = round(latest["RSI"], 1)
|
| 194 |
-
macd = latest["MACD"]
|
| 195 |
-
signal = latest["Signal"]
|
| 196 |
-
close = round(latest["Close"], 5)
|
| 197 |
-
ema20 = round(latest["EMA20"], 5)
|
| 198 |
-
ema50 = round(latest["EMA50"], 5)
|
| 199 |
-
bb_upper = round(latest["BB_upper"], 5)
|
| 200 |
-
bb_lower = round(latest["BB_lower"], 5)
|
| 201 |
-
|
| 202 |
-
lines = [f"### 🤖 AI Pattern & İndikatör Analizi\n"]
|
| 203 |
-
lines.append(f"**Son Fiyat:** {close} | **RSI(14):** {rsi}")
|
| 204 |
-
lines.append(f"**EMA20:** {ema20} | **EMA50:** {ema50}")
|
| 205 |
-
lines.append(f"**BB Üst:** {bb_upper} | **BB Alt:** {bb_lower}\n")
|
| 206 |
-
|
| 207 |
-
# Trend
|
| 208 |
-
if close > ema20 > ema50:
|
| 209 |
-
lines.append("📈 **Trend:** Güçlü Yükseliş – Fiyat her iki EMA'nın üzerinde.")
|
| 210 |
-
elif close < ema20 < ema50:
|
| 211 |
-
lines.append("📉 **Trend:** Güçlü Düşüş – Fiyat her iki EMA'nın altında.")
|
| 212 |
-
elif close > ema20:
|
| 213 |
-
lines.append("↗️ **Trend:** Kısa vadeli yükseliş, ancak EMA50 henüz kırılmadı.")
|
| 214 |
-
else:
|
| 215 |
-
lines.append("↘️ **Trend:** Kısa vadeli zayıflık.")
|
| 216 |
-
|
| 217 |
-
# RSI
|
| 218 |
-
if rsi > 70:
|
| 219 |
-
lines.append(f"⚠️ **RSI {rsi}** – Aşırı alım bölgesi, düzeltme riski.")
|
| 220 |
-
elif rsi < 30:
|
| 221 |
-
lines.append(f"✅ **RSI {rsi}** – Aşırı satım bölgesi, toparlanma ihtimali.")
|
| 222 |
-
else:
|
| 223 |
-
lines.append(f"🔵 **RSI {rsi}** – Nötr bölgede.")
|
| 224 |
-
|
| 225 |
-
# MACD
|
| 226 |
-
if macd > signal:
|
| 227 |
-
lines.append("✅ **MACD** Sinyal üzerinde – Momentum pozitif.")
|
| 228 |
-
else:
|
| 229 |
-
lines.append("⚠️ **MACD** Sinyal altında – Momentum negatif.")
|
| 230 |
-
|
| 231 |
-
# Bollinger
|
| 232 |
-
if close > bb_upper:
|
| 233 |
-
lines.append("🔴 Fiyat **Bollinger Üst Bandı** dışında – Aşırı alım.")
|
| 234 |
-
elif close < bb_lower:
|
| 235 |
-
lines.append("🟢 Fiyat **Bollinger Alt Bandı** dışında – Aşırı satım.")
|
| 236 |
-
|
| 237 |
-
# Son patternler
|
| 238 |
-
if patterns:
|
| 239 |
-
last = patterns[-3:]
|
| 240 |
-
lines.append(f"\n**Son Tespit Edilen Patternler:**")
|
| 241 |
-
for p in reversed(last):
|
| 242 |
-
lines.append(f"- `{p['Tarih']}` → {p['Pattern']} {p['Yön']}")
|
| 243 |
-
|
| 244 |
-
# Genel yorum
|
| 245 |
-
bull_count = sum(1 for p in patterns[-10:] if "↑" in p.get("Yön", ""))
|
| 246 |
-
bear_count = sum(1 for p in patterns[-10:] if "↓" in p.get("Yön", ""))
|
| 247 |
-
lines.append(f"\n**Son 10 Pattern:** 🟢 {bull_count} Bullish | 🔴 {bear_count} Bearish")
|
| 248 |
-
|
| 249 |
-
if bull_count > bear_count and rsi < 65 and macd > signal:
|
| 250 |
-
lines.append("\n> 💡 **Genel Görünüm:** Birden fazla gösterge **yükseliş** yönünde hizalanmış.")
|
| 251 |
-
elif bear_count > bull_count and rsi > 40 and macd < signal:
|
| 252 |
-
lines.append("\n> 💡 **Genel Görünüm:** Birden fazla gösterge **düşüş** yönünde hizalanmış.")
|
| 253 |
-
else:
|
| 254 |
-
lines.append("\n> 💡 **Genel Görünüm:** Sinyaller karışık, dikkatli izlenebilir.")
|
| 255 |
-
|
| 256 |
-
lines.append("\n---\n*Bu analiz yatırım tavsiyesi değildir.*")
|
| 257 |
-
return "\n".join(lines)
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
# ─── ANA FONKSİYON ───────────────────────────────────────────────────────────
|
| 261 |
-
|
| 262 |
-
def analyze(period, interval):
|
| 263 |
-
"""Veri çek, analiz et, grafik + tablo + özet döndür."""
|
| 264 |
-
try:
|
| 265 |
-
ticker = "EURUSD=X"
|
| 266 |
-
df = yf.download(ticker, period=period, interval=interval,
|
| 267 |
-
auto_adjust=True, progress=False)
|
| 268 |
-
|
| 269 |
-
if df.empty:
|
| 270 |
-
return None, pd.DataFrame(), "⚠️ Veri alınamadı. Dönem/interval kombinasyonu geçersiz olabilir."
|
| 271 |
-
|
| 272 |
-
# Çok seviyeli sütunları düzleştir
|
| 273 |
-
if isinstance(df.columns, pd.MultiIndex):
|
| 274 |
-
df.columns = df.columns.get_level_values(0)
|
| 275 |
-
|
| 276 |
-
df = calculate_indicators(df)
|
| 277 |
-
patterns = detect_patterns(df)
|
| 278 |
-
fig = build_chart(df, patterns)
|
| 279 |
-
summary = ai_summary(df, patterns)
|
| 280 |
-
|
| 281 |
-
pat_df = pd.DataFrame(patterns) if patterns else pd.DataFrame(
|
| 282 |
-
columns=["Tarih", "Pattern", "Close", "Yön"])
|
| 283 |
-
|
| 284 |
-
return fig, pat_df, summary
|
| 285 |
-
|
| 286 |
-
except Exception as e:
|
| 287 |
-
return None, pd.DataFrame(), f"❌ Hata: {str(e)}"
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
# ─── GRADIO ARAYÜZÜ ──────────────────────────────────────────────────────────
|
| 291 |
-
|
| 292 |
-
PERIOD_CHOICES = ["1d", "5d", "1mo", "3mo", "6mo", "1y"]
|
| 293 |
-
INTERVAL_CHOICES = ["15m", "30m", "1h", "4h", "1d"]
|
| 294 |
-
|
| 295 |
-
with gr.Blocks(
|
| 296 |
-
title="EUR/USD Pattern AI",
|
| 297 |
-
theme=gr.themes.Base(
|
| 298 |
-
primary_hue="teal",
|
| 299 |
-
neutral_hue="slate",
|
| 300 |
-
font=[gr.themes.GoogleFont("Inter"), "sans-serif"],
|
| 301 |
-
),
|
| 302 |
-
css="""
|
| 303 |
-
body { background: #0f1117 !important; }
|
| 304 |
-
.gradio-container { max-width: 1100px !important; }
|
| 305 |
-
#title { text-align: center; padding: 16px 0 4px; }
|
| 306 |
-
#subtitle { text-align: center; color: #94a3b8; margin-bottom: 8px; font-size: 0.9em; }
|
| 307 |
-
"""
|
| 308 |
-
) as demo:
|
| 309 |
-
|
| 310 |
-
gr.HTML("<h1 id='title'>📊 EUR/USD Pattern AI</h1>")
|
| 311 |
-
gr.HTML("<p id='subtitle'>yfinance • Candlestick Pattern Tespiti • Teknik İndikatörler</p>")
|
| 312 |
-
|
| 313 |
-
with gr.Row():
|
| 314 |
-
period_dd = gr.Dropdown(PERIOD_CHOICES, value="1mo", label="Dönem")
|
| 315 |
-
interval_dd = gr.Dropdown(INTERVAL_CHOICES, value="1d", label="Interval")
|
| 316 |
-
run_btn = gr.Button("🔍 Analiz Et", variant="primary", scale=1)
|
| 317 |
-
|
| 318 |
-
chart_out = gr.Plot(label="Grafik")
|
| 319 |
-
summary_out = gr.Markdown(label="AI Yorumu")
|
| 320 |
-
table_out = gr.Dataframe(
|
| 321 |
-
label="Tespit Edilen Patternler",
|
| 322 |
-
headers=["Tarih", "Pattern", "Close", "Yön"],
|
| 323 |
-
interactive=False,
|
| 324 |
-
wrap=True,
|
| 325 |
-
)
|
| 326 |
-
|
| 327 |
-
run_btn.click(
|
| 328 |
-
fn=analyze,
|
| 329 |
-
inputs=[period_dd, interval_dd],
|
| 330 |
-
outputs=[chart_out, table_out, summary_out],
|
| 331 |
-
)
|
| 332 |
-
|
| 333 |
-
# Sayfa yüklenince otomatik çalıştır
|
| 334 |
-
demo.load(
|
| 335 |
-
fn=analyze,
|
| 336 |
-
inputs=[period_dd, interval_dd],
|
| 337 |
-
outputs=[chart_out, table_out, summary_out],
|
| 338 |
-
)
|
| 339 |
-
|
| 340 |
-
if __name__ == "__main__":
|
| 341 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|