Miruzen commited on
Commit
648c45a
Β·
verified Β·
1 Parent(s): 5508cc2

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +13 -0
  2. app.py +118 -0
  3. requirements.txt +5 -0
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN apt-get update && apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender1 \
7
+ && pip install --no-cache-dir -r requirements.txt
8
+
9
+ COPY . .
10
+
11
+ EXPOSE 7860
12
+
13
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 πŸš€"}
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ pandas
4
+ numpy
5
+ yfinance