Miruzen commited on
Commit
6425a88
Β·
verified Β·
1 Parent(s): 648c45a

/summary trend

Browse files
Files changed (1) hide show
  1. app.py +179 -118
app.py CHANGED
@@ -1,118 +1,179 @@
1
- from fastapi import FastAPI
2
- from pydantic import BaseModel
3
- import pandas as pd
4
- import numpy as np
5
- import yfinance as yf
6
- from datetime import datetime, timedelta
7
-
8
- app = FastAPI(
9
- title="Model B – EMA & Dynamic Scaling API",
10
- description="API untuk menghitung EMA, normalisasi, dan update harga min/max otomatis berdasarkan data yfinance",
11
- version="2.0"
12
- )
13
-
14
- PAIR = "EURUSD=X"
15
- BASE_WINDOW = 60 # jumlah hari data terakhir untuk update min/max
16
-
17
-
18
- # ===============================
19
- # Data Model
20
- # ===============================
21
- class DateRange(BaseModel):
22
- start_date: str
23
- end_date: str
24
-
25
-
26
- # ===============================
27
- # Helper Functions
28
- # ===============================
29
- def ema_manual(prices, span):
30
- """Manual calculation of EMA (tanpa pandas ewm)."""
31
- ema = [np.nan] * len(prices)
32
- alpha = 2 / (span + 1)
33
- for i in range(len(prices)):
34
- if i < span - 1:
35
- ema[i] = np.nan
36
- elif i == span - 1:
37
- ema[i] = np.mean(prices[:span])
38
- else:
39
- ema[i] = alpha * prices[i] + (1 - alpha) * ema[i - 1]
40
- return ema
41
-
42
-
43
- def get_dynamic_minmax():
44
- """Ambil data terbaru dari yfinance untuk menentukan min & max close (update otomatis)."""
45
- today = datetime.now().date()
46
- start = today - timedelta(days=BASE_WINDOW)
47
- df = yf.download(PAIR, start=start, end=today + timedelta(days=1))
48
- if df.empty:
49
- raise ValueError("Gagal mengambil data harga terbaru.")
50
- close_min = df["Close"].min()
51
- close_max = df["Close"].max()
52
- return close_min, close_max
53
-
54
-
55
- def normalize_close(value, close_min, close_max):
56
- """Normalisasi nilai close ke skala 0–1."""
57
- return (value - close_min) / (close_max - close_min)
58
-
59
-
60
- # ===============================
61
- # Endpoint utama
62
- # ===============================
63
- @app.post("/analyze")
64
- def analyze_ema(input_data: DateRange):
65
- try:
66
- # Ambil tanggal dari input
67
- start_date = pd.to_datetime(input_data.start_date)
68
- end_date = pd.to_datetime(input_data.end_date)
69
-
70
- if end_date <= start_date:
71
- return {"status": "error", "message": "Tanggal akhir harus lebih besar dari tanggal awal"}
72
-
73
- # Download data EUR/USD
74
- df = yf.download(PAIR, start=start_date, end=end_date + timedelta(days=1))
75
- if df.empty:
76
- return {"status": "error", "message": "Data tidak ditemukan untuk rentang tanggal tersebut"}
77
-
78
- df = df.reset_index()[["Date", "Close"]]
79
- df.rename(columns={"Date": "date", "Close": "close"}, inplace=True)
80
-
81
- # Hitung EMA
82
- df["EMA20"] = ema_manual(df["close"], 20)
83
- df["EMA50"] = ema_manual(df["close"], 50)
84
- df = df.dropna().reset_index(drop=True)
85
-
86
- # Update nilai min dan max (otomatis)
87
- close_min, close_max = get_dynamic_minmax()
88
-
89
- # Normalisasi close
90
- df["norm_close"] = df["close"].apply(lambda x: normalize_close(x, close_min, close_max))
91
-
92
- # Format respons JSON
93
- chart_data = {
94
- "dates": df["date"].dt.strftime("%Y-%m-%d").tolist(),
95
- "close": df["close"].round(6).tolist(),
96
- "EMA20": df["EMA20"].round(6).tolist(),
97
- "EMA50": df["EMA50"].round(6).tolist(),
98
- "norm_close": df["norm_close"].round(6).tolist(),
99
- "min_close": float(close_min),
100
- "max_close": float(close_max),
101
- }
102
-
103
- return {
104
- "status": "ok",
105
- "pair": PAIR,
106
- "start_date": str(start_date.date()),
107
- "end_date": str(end_date.date()),
108
- "data_points": len(df),
109
- "chart_data": chart_data
110
- }
111
-
112
- except Exception as e:
113
- return {"status": "error", "message": str(e)}
114
-
115
-
116
- @app.get("/")
117
- def root():
118
- return {"message": "Model B API (EMA + Dynamic Normalization) aktif πŸš€"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from pydantic import BaseModel
3
+ import pandas as pd
4
+ import numpy as np
5
+ import yfinance as yf
6
+ from datetime import datetime, timedelta
7
+
8
+ app = FastAPI(
9
+ title="Model B – EMA & Dynamic Scaling API",
10
+ description="API untuk menghitung EMA, normalisasi, dan analisis tren otomatis berdasarkan data yfinance",
11
+ version="2.1"
12
+ )
13
+
14
+ PAIR = "EURUSD=X"
15
+ BASE_WINDOW = 60 # jumlah hari data untuk update min/max otomatis
16
+
17
+
18
+ # ===============================
19
+ # Data Model
20
+ # ===============================
21
+ class DateRange(BaseModel):
22
+ start_date: str
23
+ end_date: str
24
+
25
+
26
+ # ===============================
27
+ # Helper Functions
28
+ # ===============================
29
+ def ema_manual(prices, span):
30
+ ema = [np.nan] * len(prices)
31
+ alpha = 2 / (span + 1)
32
+ for i in range(len(prices)):
33
+ if i < span - 1:
34
+ ema[i] = np.nan
35
+ elif i == span - 1:
36
+ ema[i] = np.mean(prices[:span])
37
+ else:
38
+ ema[i] = alpha * prices[i] + (1 - alpha) * ema[i - 1]
39
+ return ema
40
+
41
+
42
+ def get_dynamic_minmax():
43
+ today = datetime.now().date()
44
+ start = today - timedelta(days=BASE_WINDOW)
45
+ df = yf.download(PAIR, start=start, end=today + timedelta(days=1))
46
+ if df.empty:
47
+ raise ValueError("Gagal mengambil data harga terbaru.")
48
+ close_min = df["Close"].min()
49
+ close_max = df["Close"].max()
50
+ return close_min, close_max
51
+
52
+
53
+ def normalize_close(value, close_min, close_max):
54
+ return (value - close_min) / (close_max - close_min)
55
+
56
+
57
+ def analyze_trend(latest_row):
58
+ ema20 = latest_row["EMA20"]
59
+ ema50 = latest_row["EMA50"]
60
+ close = latest_row["close"]
61
+
62
+ # Analisis arah tren
63
+ if ema20 > ema50:
64
+ trend = "bullish"
65
+ elif ema20 < ema50:
66
+ trend = "bearish"
67
+ else:
68
+ trend = "neutral"
69
+
70
+ # Momentum
71
+ diff = abs(ema20 - ema50) / ema50 * 100
72
+ if diff > 0.3:
73
+ strength = "strong"
74
+ elif diff > 0.1:
75
+ strength = "moderate"
76
+ else:
77
+ strength = "weak"
78
+
79
+ # Posisi harga terhadap EMA
80
+ if close > ema20 and close > ema50:
81
+ price_position = "above both EMA β€” possible continuation"
82
+ elif close < ema20 and close < ema50:
83
+ price_position = "below both EMA β€” possible correction"
84
+ else:
85
+ price_position = "between EMAs β€” indecision zone"
86
+
87
+ return {
88
+ "trend": trend,
89
+ "strength": strength,
90
+ "price_position": price_position,
91
+ "ema_gap_percent": round(diff, 3)
92
+ }
93
+
94
+
95
+ # ===============================
96
+ # Endpoint: /analyze (grafik & data)
97
+ # ===============================
98
+ @app.post("/analyze")
99
+ def analyze_ema(input_data: DateRange):
100
+ try:
101
+ start_date = pd.to_datetime(input_data.start_date)
102
+ end_date = pd.to_datetime(input_data.end_date)
103
+
104
+ if end_date <= start_date:
105
+ return {"status": "error", "message": "Tanggal akhir harus lebih besar dari tanggal awal"}
106
+
107
+ df = yf.download(PAIR, start=start_date, end=end_date + timedelta(days=1))
108
+ if df.empty:
109
+ return {"status": "error", "message": "Data tidak ditemukan untuk rentang tanggal tersebut"}
110
+
111
+ df = df.reset_index()[["Date", "Close"]]
112
+ df.rename(columns={"Date": "date", "Close": "close"}, inplace=True)
113
+
114
+ df["EMA20"] = ema_manual(df["close"], 20)
115
+ df["EMA50"] = ema_manual(df["close"], 50)
116
+ df = df.dropna().reset_index(drop=True)
117
+
118
+ close_min, close_max = get_dynamic_minmax()
119
+ df["norm_close"] = df["close"].apply(lambda x: normalize_close(x, close_min, close_max))
120
+
121
+ chart_data = {
122
+ "dates": df["date"].dt.strftime("%Y-%m-%d").tolist(),
123
+ "close": df["close"].round(6).tolist(),
124
+ "EMA20": df["EMA20"].round(6).tolist(),
125
+ "EMA50": df["EMA50"].round(6).tolist(),
126
+ "norm_close": df["norm_close"].round(6).tolist(),
127
+ "min_close": float(close_min),
128
+ "max_close": float(close_max),
129
+ }
130
+
131
+ return {
132
+ "status": "ok",
133
+ "pair": PAIR,
134
+ "start_date": str(start_date.date()),
135
+ "end_date": str(end_date.date()),
136
+ "data_points": len(df),
137
+ "chart_data": chart_data
138
+ }
139
+
140
+ except Exception as e:
141
+ return {"status": "error", "message": str(e)}
142
+
143
+
144
+ # ===============================
145
+ # Endpoint: /summary (analisis tren)
146
+ # ===============================
147
+ @app.post("/summary")
148
+ def ema_summary(input_data: DateRange):
149
+ try:
150
+ df = yf.download(PAIR, start=input_data.start_date, end=input_data.end_date)
151
+ if df.empty:
152
+ return {"status": "error", "message": "Data tidak ditemukan"}
153
+
154
+ df = df.reset_index()[["Date", "Close"]]
155
+ df.rename(columns={"Date": "date", "Close": "close"}, inplace=True)
156
+ df["EMA20"] = ema_manual(df["close"], 20)
157
+ df["EMA50"] = ema_manual(df["close"], 50)
158
+ df = df.dropna().reset_index(drop=True)
159
+
160
+ latest = df.iloc[-1]
161
+ analysis = analyze_trend(latest)
162
+
163
+ return {
164
+ "status": "ok",
165
+ "pair": PAIR,
166
+ "as_of_date": latest["date"].strftime("%Y-%m-%d"),
167
+ "close": round(float(latest["close"]), 6),
168
+ "EMA20": round(float(latest["EMA20"]), 6),
169
+ "EMA50": round(float(latest["EMA50"]), 6),
170
+ "trend_analysis": analysis
171
+ }
172
+
173
+ except Exception as e:
174
+ return {"status": "error", "message": str(e)}
175
+
176
+
177
+ @app.get("/")
178
+ def root():
179
+ return {"message": "Model B API (EMA + Trend Summary) aktif πŸš€"}