File size: 6,517 Bytes
8ce9e3c
69ffea5
8ce9e3c
 
69ffea5
f654869
69ffea5
f654869
9bd80bc
9fef9dc
f654869
fc7893c
 
 
 
 
 
 
 
 
9bd80bc
 
 
 
 
7b6c581
9fef9dc
f654869
 
9fef9dc
10cf7e3
f654869
 
fc7893c
 
 
f654869
 
8ce9e3c
 
 
 
69ffea5
fc7893c
69ffea5
 
 
 
 
9bd80bc
69ffea5
 
 
 
 
 
1b21664
f654869
9fef9dc
69ffea5
f654869
69ffea5
9fef9dc
 
 
 
f654869
 
9fef9dc
 
71e0e89
1b21664
69ffea5
 
 
9fef9dc
 
 
f654869
fc7893c
f654869
 
69ffea5
 
 
 
 
f654869
69ffea5
 
 
 
 
fc7893c
9bd80bc
69ffea5
 
f654869
fc7893c
9bd80bc
 
 
 
 
 
 
f654869
 
fc7893c
f654869
 
 
69ffea5
 
 
 
 
 
 
 
 
fc7893c
9bd80bc
69ffea5
 
f654869
fc7893c
9bd80bc
 
 
 
 
 
 
 
 
 
 
 
 
 
f654869
 
fc7893c
f654869
71e0e89
1b21664
71e0e89
1b21664
 
 
 
 
 
 
9fef9dc
 
 
f654869
9bd80bc
f654869
9fef9dc
f654869
 
 
69ffea5
 
f654869
 
69ffea5
 
 
 
 
 
1b21664
 
 
 
69ffea5
9bd80bc
1b21664
 
9bd80bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f654869
 
9fef9dc
 
f654869
9fef9dc
8ce9e3c
71e0e89
fc7893c
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# daily.py

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime as dt
import traceback
import io
import time

from . import persist
from . import backblaze as b2


# ============================================================
# CONFIG
# ============================================================
IMAGE_FORMAT = "png"
IMAGE_EXT = "png"
DPI = 150
IMAGE_QUALITY = 85   # compression quality (PNG is lossless)

# Public download base (DISPLAY ONLY, no change to backblaze.py)
B2_PUBLIC_BASE = "https://f005.backblazeb2.com/file/eshanhf"
# ⚠️ replace f005 if your Backblaze console shows different


# ============================================================
# MAIN
# ============================================================
def fetch_daily(symbol, date_end, date_start):
    key = f"daily_{symbol}"

    # --------------------------------------------------------
    # Cache
    # --------------------------------------------------------
    if persist.exists(key, "html"):
        cached = persist.load(key, "html")
        if cached:
            return cached

    try:
        # ----------------------------------------------------
        # Date conversion
        # ----------------------------------------------------
        start = dt.strptime(date_start, "%d-%m-%Y").strftime("%Y-%m-%d")
        end   = dt.strptime(date_end, "%d-%m-%Y").strftime("%Y-%m-%d")

        # ----------------------------------------------------
        # Fetch data
        # ----------------------------------------------------
        df = yf.download(symbol + ".NS", start=start, end=end)

        if isinstance(df.columns, pd.MultiIndex):
            df.columns = df.columns.get_level_values(0)

        if df.empty:
            return "<h3>No daily data found</h3>"

        # ----------------------------------------------------
        # Clean data
        # ----------------------------------------------------
        df = df.reset_index()
        df["Date"] = pd.to_datetime(df["Date"], errors="coerce")
        df = df.dropna(subset=["Date"])

        for c in ["Open", "High", "Low", "Close", "Volume"]:
            df[c] = pd.to_numeric(df[c], errors="coerce")

        df = df.dropna()
        df["DateStr"] = df["Date"].dt.strftime("%d-%b-%Y")

        # ----------------------------------------------------
        # Indicators
        # ----------------------------------------------------
        df["MA20"] = df["Close"].rolling(20).mean()
        df["MA50"] = df["Close"].rolling(50).mean()

        # ====================================================
        # PRICE + VOLUME CHART
        # ====================================================
        buf = io.BytesIO()

        plt.figure(figsize=(14, 6))
        plt.plot(df["Date"], df["Close"], label="Close", linewidth=2)

        vol_scaled = df["Volume"] / df["Volume"].max() * df["Close"].max()
        plt.bar(df["Date"], vol_scaled, alpha=0.25, label="Volume")

        plt.title(f"{symbol} Price & Volume")
        plt.legend()
        plt.grid(True)
        plt.tight_layout()

        plt.savefig(buf, format=IMAGE_FORMAT, dpi=DPI, bbox_inches="tight")
        plt.close()

        buf.seek(0)
        price_key = f"daily/{symbol}_price_volume.{IMAGE_EXT}"

        b2.upload_image_compressed(
            "eshanhf",
            price_key,
            buf.getvalue(),
            quality=IMAGE_QUALITY
        )

        # ====================================================
        # MOVING AVERAGE CHART
        # ====================================================
        buf = io.BytesIO()

        plt.figure(figsize=(14, 6))
        plt.plot(df["Date"], df["Close"], label="Close", linewidth=2)
        plt.plot(df["Date"], df["MA20"], label="MA20")
        plt.plot(df["Date"], df["MA50"], label="MA50")

        plt.title(f"{symbol} Moving Averages")
        plt.legend()
        plt.grid(True)
        plt.tight_layout()

        plt.savefig(buf, format=IMAGE_FORMAT, dpi=DPI, bbox_inches="tight")
        plt.close()

        buf.seek(0)
        ma_key = f"daily/{symbol}_ma.{IMAGE_EXT}"

        b2.upload_image_compressed(
            "eshanhf",
            ma_key,
            buf.getvalue(),
            quality=IMAGE_QUALITY
        )

        # ----------------------------------------------------
        # Cache-busting timestamp
        # ----------------------------------------------------
        ts = int(time.time())
        price_url = f"{B2_PUBLIC_BASE}/{price_key}?v={ts}"
        ma_url    = f"{B2_PUBLIC_BASE}/{ma_key}?v={ts}"

        # ====================================================
        # TABLE (Last 100 days)
        # ====================================================
        rows = ""
        for r in df.tail(100).itertuples():
            rows += f"""
            <tr>
                <td>{r.DateStr}</td>
                <td>{r.Open:.2f}</td>
                <td>{r.High:.2f}</td>
                <td>{r.Low:.2f}</td>
                <td>{r.Close:.2f}</td>
                <td>{int(r.Volume)}</td>
            </tr>
            """

        # ====================================================
        # FINAL HTML (IMAGE WAIT + RETRY)
        # ====================================================
        html = f"""
<div id="daily_dashboard">

<h2>{symbol} – Daily Analysis</h2>

<h3>Price Table (Last 100 Days)</h3>
<table style="border-collapse:collapse;width:100%;" border="1" cellpadding="6">
<tr style="background:#f0f0f0;font-weight:bold;">
    <th>Date</th>
    <th>Open</th>
    <th>High</th>
    <th>Low</th>
    <th>Close</th>
    <th>Volume</th>
</tr>
{rows}
</table>

<h3>Price & Volume</h3>
<img data-src="{price_url}" style="width:100%;max-width:1200px;">

<h3>Moving Averages</h3>
<img data-src="{ma_url}" style="width:100%;max-width:1200px;">

<script>
function loadWithRetry(img, retries = 6, delay = 800) {{
    let attempt = 0;

    function tryLoad() {{
        attempt++;
        img.src = img.dataset.src + "&retry=" + attempt;
    }}

    img.onerror = function() {{
        if (attempt < retries) {{
            setTimeout(tryLoad, delay);
        }} else {{
            img.alt = "Image failed to load";
        }}
    }};

    tryLoad();
}}

document.querySelectorAll("img[data-src]").forEach(img => {{
    loadWithRetry(img);
}});
</script>

</div>
"""

        persist.save(key, html, "html")
        return html

    except Exception:
        return f"<pre>{traceback.format_exc()}</pre>"