Miruzen commited on
Commit
a3bd9f8
Β·
verified Β·
1 Parent(s): 43491a9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -39
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from fastapi import FastAPI
2
  from pydantic import BaseModel
3
  import pandas as pd
@@ -5,6 +6,9 @@ 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",
@@ -12,20 +16,12 @@ app = FastAPI(
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)
@@ -38,28 +34,29 @@ def ema_manual(prices, span):
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:
@@ -67,7 +64,6 @@ def analyze_trend(latest_row):
67
  else:
68
  trend = "neutral"
69
 
70
- # Momentum
71
  diff = abs(ema20 - ema50) / ema50 * 100
72
  if diff > 0.3:
73
  strength = "strong"
@@ -76,7 +72,6 @@ def analyze_trend(latest_row):
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:
@@ -91,31 +86,26 @@ def analyze_trend(latest_row):
91
  "ema_gap_percent": round(diff, 3)
92
  }
93
 
94
-
95
- # ===============================
96
- # Endpoint: /analyze (grafik & data)
97
- # ===============================
98
- # ===============================
99
- # Endpoint: /analyze (grafik & data)
100
- # ===============================
101
  @app.post("/analyze")
102
  def analyze_ema(input_data: DateRange):
103
  try:
 
104
  start_date = pd.to_datetime(input_data.start_date)
105
  end_date = pd.to_datetime(input_data.end_date)
106
-
107
- # Tambahkan 60 hari ke belakang agar cukup untuk EMA50
108
  extended_start = start_date - timedelta(days=60)
109
 
110
- df = yf.download(PAIR, start=extended_start, end=end_date + timedelta(days=1))
 
 
111
  if df.empty:
 
112
  return {"status": "error", "message": "Data tidak ditemukan untuk rentang tanggal tersebut"}
113
 
114
  df = df.reset_index()[["Date", "Close"]]
115
  df.rename(columns={"Date": "date", "Close": "close"}, inplace=True)
116
 
117
- # Pastikan minimal 50 data
118
  if len(df) < 50:
 
119
  return {"status": "error", "message": f"Data terlalu sedikit ({len(df)} hari). Butuh minimal 50 hari untuk EMA50."}
120
 
121
  df["EMA20"] = ema_manual(df["close"], 20)
@@ -134,7 +124,7 @@ def analyze_ema(input_data: DateRange):
134
  "min_close": float(close_min),
135
  "max_close": float(close_max),
136
  }
137
-
138
  return {
139
  "status": "ok",
140
  "pair": PAIR,
@@ -145,27 +135,27 @@ def analyze_ema(input_data: DateRange):
145
  }
146
 
147
  except Exception as e:
 
148
  return {"status": "error", "message": str(e)}
149
 
150
-
151
- # ===============================
152
- # Endpoint: /summary (analisis tren)
153
- # ===============================
154
  @app.post("/summary")
155
  def ema_summary(input_data: DateRange):
156
  try:
 
157
  start_date = pd.to_datetime(input_data.start_date)
158
  end_date = pd.to_datetime(input_data.end_date)
159
  extended_start = start_date - timedelta(days=60)
160
 
161
- df = yf.download(PAIR, start=extended_start, end=end_date + timedelta(days=1))
162
  if df.empty:
 
163
  return {"status": "error", "message": "Data tidak ditemukan"}
164
 
165
  df = df.reset_index()[["Date", "Close"]]
166
  df.rename(columns={"Date": "date", "Close": "close"}, inplace=True)
167
 
168
  if len(df) < 50:
 
169
  return {"status": "error", "message": f"Data terlalu sedikit ({len(df)} hari). Butuh minimal 50 hari untuk EMA50."}
170
 
171
  df["EMA20"] = ema_manual(df["close"], 20)
@@ -174,7 +164,7 @@ def ema_summary(input_data: DateRange):
174
 
175
  latest = df.iloc[-1]
176
  analysis = analyze_trend(latest)
177
-
178
  return {
179
  "status": "ok",
180
  "pair": PAIR,
@@ -186,8 +176,5 @@ def ema_summary(input_data: DateRange):
186
  }
187
 
188
  except Exception as e:
189
- return {"status": "error", "message": str(e)}
190
-
191
- @app.get("/")
192
- def root():
193
- return {"message": "Model B API (EMA + Trend Summary) aktif πŸš€"}
 
1
+ import logging
2
  from fastapi import FastAPI
3
  from pydantic import BaseModel
4
  import pandas as pd
 
6
  import yfinance as yf
7
  from datetime import datetime, timedelta
8
 
9
+ # Konfigurasi logging
10
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11
+
12
  app = FastAPI(
13
  title="Model B – EMA & Dynamic Scaling API",
14
  description="API untuk menghitung EMA, normalisasi, dan analisis tren otomatis berdasarkan data yfinance",
 
16
  )
17
 
18
  PAIR = "EURUSD=X"
19
+ BASE_WINDOW = 60
 
20
 
 
 
 
21
  class DateRange(BaseModel):
22
  start_date: str
23
  end_date: str
24
 
 
 
 
 
25
  def ema_manual(prices, span):
26
  ema = [np.nan] * len(prices)
27
  alpha = 2 / (span + 1)
 
34
  ema[i] = alpha * prices[i] + (1 - alpha) * ema[i - 1]
35
  return ema
36
 
 
37
  def get_dynamic_minmax():
38
  today = datetime.now().date()
39
  start = today - timedelta(days=BASE_WINDOW)
40
+ logging.info(f"Mengunduh data untuk min/max: start={start}, end={today + timedelta(days=1)}")
41
+ df = yf.download(PAIR, start=start, end=today + timedelta(days=1), auto_adjust=True)
42
  if df.empty:
43
+ logging.error("Gagal mengambil data harga terbaru untuk min/max.")
44
  raise ValueError("Gagal mengambil data harga terbaru.")
45
  close_min = df["Close"].min()
46
  close_max = df["Close"].max()
47
+ logging.info(f"Min/Max Close: {close_min}/{close_max}")
48
  return close_min, close_max
49
 
 
50
  def normalize_close(value, close_min, close_max):
51
+ if close_max == close_min: # Hindari pembagian dengan nol
52
+ return 1.0 # Atau nilai default lain yang masuk akal
53
  return (value - close_min) / (close_max - close_min)
54
 
 
55
  def analyze_trend(latest_row):
56
  ema20 = latest_row["EMA20"]
57
  ema50 = latest_row["EMA50"]
58
  close = latest_row["close"]
59
 
 
60
  if ema20 > ema50:
61
  trend = "bullish"
62
  elif ema20 < ema50:
 
64
  else:
65
  trend = "neutral"
66
 
 
67
  diff = abs(ema20 - ema50) / ema50 * 100
68
  if diff > 0.3:
69
  strength = "strong"
 
72
  else:
73
  strength = "weak"
74
 
 
75
  if close > ema20 and close > ema50:
76
  price_position = "above both EMA β€” possible continuation"
77
  elif close < ema20 and close < ema50:
 
86
  "ema_gap_percent": round(diff, 3)
87
  }
88
 
 
 
 
 
 
 
 
89
  @app.post("/analyze")
90
  def analyze_ema(input_data: DateRange):
91
  try:
92
+ logging.info(f"Menerima permintaan /analyze dengan start_date={input_data.start_date}, end_date={input_data.end_date}")
93
  start_date = pd.to_datetime(input_data.start_date)
94
  end_date = pd.to_datetime(input_data.end_date)
 
 
95
  extended_start = start_date - timedelta(days=60)
96
 
97
+ logging.info(f"Mengunduh data dari yfinance: start={extended_start}, end={end_date + timedelta(days=1)}")
98
+ df = yf.download(PAIR, start=extended_start, end=end_date + timedelta(days=1), auto_adjust=True)
99
+
100
  if df.empty:
101
+ logging.warning("Data tidak ditemukan untuk rentang tanggal yang diperluas.")
102
  return {"status": "error", "message": "Data tidak ditemukan untuk rentang tanggal tersebut"}
103
 
104
  df = df.reset_index()[["Date", "Close"]]
105
  df.rename(columns={"Date": "date", "Close": "close"}, inplace=True)
106
 
 
107
  if len(df) < 50:
108
+ logging.warning(f"Data terlalu sedikit ({len(df)} hari) untuk EMA50.")
109
  return {"status": "error", "message": f"Data terlalu sedikit ({len(df)} hari). Butuh minimal 50 hari untuk EMA50."}
110
 
111
  df["EMA20"] = ema_manual(df["close"], 20)
 
124
  "min_close": float(close_min),
125
  "max_close": float(close_max),
126
  }
127
+ logging.info(f"Analisis /analyze berhasil, {len(df)} data point.")
128
  return {
129
  "status": "ok",
130
  "pair": PAIR,
 
135
  }
136
 
137
  except Exception as e:
138
+ logging.error(f"Error di /analyze: {e}", exc_info=True) # exc_info=True akan mencetak traceback
139
  return {"status": "error", "message": str(e)}
140
 
 
 
 
 
141
  @app.post("/summary")
142
  def ema_summary(input_data: DateRange):
143
  try:
144
+ logging.info(f"Menerima permintaan /summary dengan start_date={input_data.start_date}, end_date={input_data.end_date}")
145
  start_date = pd.to_datetime(input_data.start_date)
146
  end_date = pd.to_datetime(input_data.end_date)
147
  extended_start = start_date - timedelta(days=60)
148
 
149
+ df = yf.download(PAIR, start=extended_start, end=end_date + timedelta(days=1), auto_adjust=True)
150
  if df.empty:
151
+ logging.warning("Data tidak ditemukan untuk rentang tanggal yang diperluas.")
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
 
157
  if len(df) < 50:
158
+ logging.warning(f"Data terlalu sedikit ({len(df)} hari) untuk EMA50.")
159
  return {"status": "error", "message": f"Data terlalu sedikit ({len(df)} hari). Butuh minimal 50 hari untuk EMA50."}
160
 
161
  df["EMA20"] = ema_manual(df["close"], 20)
 
164
 
165
  latest = df.iloc[-1]
166
  analysis = analyze_trend(latest)
167
+ logging.info(f"Analisis /summary berhasil, tanggal terakhir: {latest['date'].strftime('%Y-%m-%d')}")
168
  return {
169
  "status": "ok",
170
  "pair": PAIR,
 
176
  }
177
 
178
  except Exception as e:
179
+ logging.error(f"Error di /summary: {e}", exc_info=True)
180
+ return {"status": "error", "message": str(e)}