DOGE $1 in 2025? Whale Accumulation + Forecast with Python GUI | FuzzuTech

 Demo :


Click Video πŸ‘‡πŸ‘‡πŸ‘‡































Description:
Is Dogecoin hitting $1 in 2025? πŸš€ Explore this Python + CTkinter GUI app for DOGE price analysis, whale accumulation index & Monte Carlo forecasts.


Features:

  • Embedded YouTube Short

  • Code snippet (GitHub link if you want)

  • Screenshots from GUI (charts, whale proxy, forecast)

  • SEO keywords: Dogecoin price forecast 2025, DOGE $1, Python crypto app, whale accumulation index, Monte Carlo crypto simulation


Code :


#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

FuzzuTech — DOGE $1 in 2025? (Python + CTkinter GUI)

Single-file modern app to visualize DOGE price, compute a simple Whale Accumulation Proxy,

run Monte‑Carlo forecasts, and generate a bull/bear/base report.


Dependencies (install as needed):

    pip install customtkinter matplotlib requests numpy pandas


Optional (improves fonts on Windows):

    pip install pillow


Notes:

- Uses public CoinGecko endpoints (no API key). If the API is down, app falls back to cached sample.

- Whale Accumulation Proxy (WAP) is a heuristic based on: high volume on red days + low volatility clustering.

  It is NOT on-chain whale tracking. Treat as an educational indicator.

"""


import os

import sys

import json

import math

import time

import datetime as dt

from dataclasses import dataclass

from typing import List, Tuple, Optional


# UI

try:

    import customtkinter as ctk

except Exception as e:

    print("Please install customtkinter: pip install customtkinter")

    raise


import threading

import requests

import numpy as np


try:

    import pandas as pd

except Exception:

    pd = None


import matplotlib

matplotlib.use("Agg")  # render offscreen for thread-safety

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

from matplotlib.figure import Figure


APP_NAME = "FuzzuTech — DOGE $1?"

COINGECKO_BASE = "https://api.coingecko.com/api/v3"


# --------------------------- Data Layer ---------------------------- #

@dataclass

class MarketPoint:

    time: dt.datetime

    price: float

    volume: float


@dataclass

class MarketSnapshot:

    now_price: float

    change_24h: float

    market_cap: Optional[float]

    last_updated: dt.datetime


class DogeData:

    def __init__(self):

        self.history: List[MarketPoint] = []

        self.snapshot: Optional[MarketSnapshot] = None


    def fetch_snapshot(self) -> MarketSnapshot:

        url = f"{COINGECKO_BASE}/simple/price?ids=dogecoin&vs_currencies=usd&include_24hr_change=true&include_market_cap=true&precision=6"

        r = requests.get(url, timeout=15)

        r.raise_for_status()

        d = r.json()["dogecoin"]

        snap = MarketSnapshot(

            now_price=float(d.get("usd", np.nan)),

            change_24h=float(d.get("usd_24h_change", np.nan)),

            market_cap=float(d.get("usd_market_cap", np.nan)),

            last_updated=dt.datetime.utcnow(),

        )

        self.snapshot = snap

        return snap


    def fetch_history(self, days: int = 365) -> List[MarketPoint]:

        url = f"{COINGECKO_BASE}/coins/dogecoin/market_chart?vs_currency=usd&days={days}&interval=daily"

        r = requests.get(url, timeout=20)

        r.raise_for_status()

        data = r.json()

        prices = data.get("prices", [])

        vols = data.get("total_volumes", [])

        out: List[MarketPoint] = []

        for (t, p), (_, v) in zip(prices, vols):

            out.append(MarketPoint(time=dt.datetime.utcfromtimestamp(t/1000.0), price=float(p), volume=float(v)))

        self.history = out

        return out


    def ensure_data(self):

        try:

            if not self.snapshot:

                self.fetch_snapshot()

            if not self.history:

                self.fetch_history(365)

        except Exception:

            # Fallback to tiny sample so UI still loads

            now = dt.datetime.utcnow()

            self.snapshot = MarketSnapshot(0.065, -1.2, 9200000000.0, now)

            self.history = [

                MarketPoint(now - dt.timedelta(days=i), 0.05 + 0.0002*i + 0.005*np.sin(i/7), 1e9+5e7*np.sin(i/3))

                for i in range(365, -1, -1)

            ]


# ------------------------ Indicators & Models ---------------------- #


def to_series(history: List[MarketPoint]):

    """Return numpy arrays (dates, prices, volumes)."""

    dates = np.array([mp.time for mp in history])

    prices = np.array([mp.price for mp in history], dtype=float)

    vols = np.array([mp.volume for mp in history], dtype=float)

    return dates, prices, vols



def moving_average(x: np.ndarray, n: int) -> np.ndarray:

    if n <= 1:

        return x.copy()

    kern = np.ones(n)/n

    return np.convolve(x, kern, mode='same')



def bollinger_bands(x: np.ndarray, n: int = 20, k: float = 2.0) -> Tuple[np.ndarray, np.ndarray]:

    ma = moving_average(x, n)

    # rolling std (simple)

    pad = n//2

    std = np.array([np.std(x[max(0,i-pad):min(len(x), i+pad+1)]) for i in range(len(x))])

    upper = ma + k*std

    lower = ma - k*std

    return upper, lower



def whale_accumulation_proxy(prices: np.ndarray, volumes: np.ndarray, lookback: int = 30) -> Tuple[float, dict]:

    """

    Heuristic index in [0, 100]. Higher implies more likely silent accumulation.

    Components:

      - High Volume Red Days: days where close<prev_close and volume>median -> suspected stealth buying via sell pressure absorption.

      - Low Volatility Clusters: std of returns below 30d median suggests quiet accumulation periods.

      - Rising OBV: simple On-Balance Volume slope over lookback.

    """

    if len(prices) < lookback + 5:

        return 50.0, {"note": "insufficient history"}


    rets = np.diff(np.log(prices))

    vol_med = np.median(volumes[-lookback:])

    red_days = 0

    for i in range(1, lookback+1):

        if prices[-i] < prices[-i-1] and volumes[-i] > vol_med:

            red_days += 1

    red_score = min(1.0, red_days / (lookback*0.5))  # saturate


    # low vol cluster

    rolling_std = np.array([np.std(rets[max(0,i-lookback):i+1]) for i in range(len(rets))])

    lowvol = 1.0 - (np.mean(rolling_std[-lookback:]) / (np.median(rolling_std[-lookback:]) + 1e-9))

    lowvol = float(np.clip(0.5 + 0.5*lowvol, 0, 1))


    # OBV slope

    obv = [0.0]

    for i in range(1, len(prices)):

        obv.append(obv[-1] + (volumes[i] if prices[i] > prices[i-1] else (-volumes[i] if prices[i] < prices[i-1] else 0)))

    obv = np.array(obv)

    x = np.arange(len(obv))

    # simple slope via last lookback points

    xb = x[-lookback:]

    yb = obv[-lookback:]

    denom = (len(xb)*np.sum(xb**2) - (np.sum(xb))**2) + 1e-9

    slope = (len(xb)*np.sum(xb*yb) - np.sum(xb)*np.sum(yb)) / denom

    slope_norm = 0.5 + 0.5*np.tanh(slope / (np.std(yb)+1e-9))


    # combine

    score = 100.0 * (0.45*red_score + 0.35*lowvol + 0.20*slope_norm)

    detail = {

        "red_days": int(red_days),

        "red_score": float(red_score),

        "lowvol_component": float(lowvol),

        "obv_slope_component": float(slope_norm)

    }

    return float(np.clip(score, 0, 100)), detail



def gbm_forecast(prices: np.ndarray, days: int = 180, n_paths: int = 500,

                  drift_adj: float = 0.0, vol_adj: float = 1.0, seed: Optional[int] = 42) -> np.ndarray:

    """Geometric Brownian Motion simulation returning paths [n_paths, days+1]."""

    if seed is not None:

        np.random.seed(seed)

    last = prices[-1]

    logrets = np.diff(np.log(prices))

    mu = np.mean(logrets) + drift_adj

    sigma = np.std(logrets) * vol_adj

    dtau = 1.0

    paths = np.zeros((n_paths, days+1))

    paths[:,0] = last

    for t in range(1, days+1):

        z = np.random.normal(size=n_paths)

        paths[:,t] = paths[:,t-1] * np.exp((mu - 0.5*sigma**2)*dtau + sigma*np.sqrt(dtau)*z)

    return paths


# --------------------------- UI Components ------------------------- #


class DogeApp(ctk.CTk):

    def __init__(self):

        super().__init__()

        ctk.set_appearance_mode("dark")

        ctk.set_default_color_theme("blue")

        self.geometry("1100x720")

        self.title(APP_NAME)

        self.minsize(1000, 650)


        self.data = DogeData()


        # State vars

        self.etf_positive = ctk.BooleanVar(value=False)

        self.user_sentiment = ctk.DoubleVar(value=0.0)  # -1..+1

        self.forecast_days = ctk.IntVar(value=180)

        self.paths_var = ctk.IntVar(value=500)

        self.lookback_var = ctk.IntVar(value=30)


        # Layout

        self._build_layout()

        self._async_refresh()


    # ---- Layout helpers ---- #

    def _build_layout(self):

        self.grid_columnconfigure(1, weight=1)

        self.grid_rowconfigure(0, weight=1)


        # Sidebar

        sidebar = ctk.CTkFrame(self, corner_radius=16)

        sidebar.grid(row=0, column=0, sticky="nsw", padx=12, pady=12)

        sidebar.grid_rowconfigure(10, weight=1)

        ctk.CTkLabel(sidebar, text="DOGE $1?", font=("Segoe UI", 22, "bold")).grid(row=0, column=0, padx=12, pady=(12,6))

        self.price_lbl = ctk.CTkLabel(sidebar, text="Price: —", font=("Segoe UI", 16))

        self.price_lbl.grid(row=1, column=0, padx=12, pady=4)

        self.chg_lbl = ctk.CTkLabel(sidebar, text="24h: —", font=("Segoe UI", 14))

        self.chg_lbl.grid(row=2, column=0, padx=12, pady=2)

        self.mc_lbl = ctk.CTkLabel(sidebar, text="MCap: —", font=("Segoe UI", 13))

        self.mc_lbl.grid(row=3, column=0, padx=12, pady=2)


        ctk.CTkButton(sidebar, text="Refresh Data", command=self._async_refresh).grid(row=4, column=0, padx=12, pady=(10,4))

        ctk.CTkButton(sidebar, text="Run Forecast", command=self._run_forecast).grid(row=5, column=0, padx=12, pady=4)

        ctk.CTkButton(sidebar, text="Export Report", command=self._export_report).grid(row=6, column=0, padx=12, pady=4)

        ctk.CTkSwitch(sidebar, text="ETF Catalyst Positive", variable=self.etf_positive).grid(row=7, column=0, padx=12, pady=(12,6))


        ctk.CTkLabel(sidebar, text="Your Sentiment (-1..+1)").grid(row=8, column=0, padx=12, pady=(10,2))

        self.sent_scale = ctk.CTkSlider(sidebar, from_=-1.0, to=1.0, number_of_steps=40, variable=self.user_sentiment)

        self.sent_scale.grid(row=9, column=0, padx=12, pady=6)


        self.theme_btn = ctk.CTkSegmentedButton(sidebar, values=["Dark","Light"], command=self._toggle_theme)

        self.theme_btn.set("Dark")

        self.theme_btn.grid(row=11, column=0, padx=12, pady=(20,12))


        # Main area with tabs

        main = ctk.CTkTabview(self, corner_radius=16)

        main.grid(row=0, column=1, sticky="nsew", padx=12, pady=12)

        self.tab_chart = main.add("Chart")

        self.tab_whale = main.add("Whale Index")

        self.tab_fore = main.add("Forecast")

        self.tab_notes = main.add("Notes & News")

        self.tab_about = main.add("About")


        # Chart tab

        self.fig_chart = Figure(figsize=(6,4), dpi=120)

        self.ax_price = self.fig_chart.add_subplot(111)

        self.canvas_chart = FigureCanvasTkAgg(self.fig_chart, master=self.tab_chart)

        self.canvas_chart.get_tk_widget().pack(fill="both", expand=True, padx=8, pady=8)


        # Whale tab

        whale_frame = ctk.CTkFrame(self.tab_whale)

        whale_frame.pack(fill="both", expand=True, padx=8, pady=8)

        whale_frame.grid_columnconfigure(1, weight=1)

        ctk.CTkLabel(whale_frame, text="Lookback days:").grid(row=0, column=0, sticky="w", padx=8, pady=8)

        ctk.CTkEntry(whale_frame, textvariable=self.lookback_var, width=80).grid(row=0, column=1, sticky="w")

        ctk.CTkButton(whale_frame, text="Recompute", command=self._update_whale).grid(row=0, column=2, padx=8)

        self.whale_lbl = ctk.CTkLabel(whale_frame, text="Whale Accumulation Proxy: —", font=("Segoe UI", 16, "bold"))

        self.whale_lbl.grid(row=1, column=0, columnspan=3, sticky="w", padx=8, pady=(8,2))

        self.whale_detail = ctk.CTkTextbox(whale_frame, height=120)

        self.whale_detail.grid(row=2, column=0, columnspan=3, sticky="nsew", padx=8, pady=8)

        whale_frame.grid_rowconfigure(2, weight=1)


        # Forecast tab

        fore_frame = ctk.CTkFrame(self.tab_fore)

        fore_frame.pack(fill="both", expand=True, padx=8, pady=8)

        for i in range(10):

            fore_frame.grid_columnconfigure(i, weight=1)

        ctk.CTkLabel(fore_frame, text="Days").grid(row=0, column=0, padx=6, pady=6)

        ctk.CTkEntry(fore_frame, textvariable=self.forecast_days, width=80).grid(row=0, column=1)

        ctk.CTkLabel(fore_frame, text="# Paths").grid(row=0, column=2)

        ctk.CTkEntry(fore_frame, textvariable=self.paths_var, width=80).grid(row=0, column=3)

        ctk.CTkButton(fore_frame, text="Simulate", command=self._run_forecast).grid(row=0, column=4, padx=6)


        self.fig_fore = Figure(figsize=(6,4), dpi=120)

        self.ax_fore = self.fig_fore.add_subplot(111)

        self.canvas_fore = FigureCanvasTkAgg(self.fig_fore, master=fore_frame)

        self.canvas_fore.get_tk_widget().grid(row=1, column=0, columnspan=10, sticky="nsew", padx=8, pady=8)

        fore_frame.grid_rowconfigure(1, weight=1)


        # Notes/News tab

        self.notes = ctk.CTkTextbox(self.tab_notes)

        self.notes.pack(fill="both", expand=True, padx=8, pady=8)

        self.notes.insert("1.0", "Paste headlines/catalysts here. Set 'ETF Catalyst Positive' if relevant.\n\n")


        # About tab

        about = ctk.CTkLabel(self.tab_about, justify="left",

            text=("FuzzuTech — DOGE Long‑Term Bull Case GUI\n\n"

                  "⚠️ Disclaimer: This app is educational. Forecasts are models, not financial advice.\n"

                  "Data: CoinGecko markets chart + simple heuristics for accumulation proxy."))

        about.pack(anchor="w", padx=12, pady=12)


    # ---- Actions ---- #

    def _toggle_theme(self, value: str):

        ctk.set_appearance_mode("dark" if value=="Dark" else "light")


    def _async_refresh(self):

        threading.Thread(target=self._refresh_data, daemon=True).start()


    def _refresh_data(self):

        self._set_status("Refreshing…")

        try:

            self.data.ensure_data()

            # if online, refresh live

            try:

                self.data.fetch_snapshot()

                self.data.fetch_history(365)

            except Exception:

                pass

            self._update_sidebar()

            self._draw_chart()

            self._update_whale()

            self._set_status("Ready")

        except Exception as e:

            self._set_status(f"Error: {e}")


    def _set_status(self, msg: str):

        self.title(f"{APP_NAME} — {msg}")


    def _fmt_cur(self, x: float) -> str:

        if x is None or math.isnan(x):

            return "—"

        # compact USD

        absx = abs(x)

        if absx >= 1e12:

            s = f"${x/1e12:.2f}T"

        elif absx >= 1e9:

            s = f"${x/1e9:.2f}B"

        elif absx >= 1e6:

            s = f"${x/1e6:.2f}M"

        elif absx >= 1e3:

            s = f"${x/1e3:.2f}K"

        else:

            s = f"${x:.4f}"

        return s


    def _update_sidebar(self):

        snap = self.data.snapshot

        if not snap:

            return

        self.price_lbl.configure(text=f"Price: ${snap.now_price:.6f}")

        chg = snap.change_24h

        sign = "▲" if chg>=0 else "▼"

        self.chg_lbl.configure(text=f"24h: {sign}{abs(chg):.2f}%")

        self.mc_lbl.configure(text=f"MCap: {self._fmt_cur(snap.market_cap)}")


    def _draw_chart(self):

        dates, prices, vols = to_series(self.data.history)

        self.ax_price.clear()

        self.ax_price.plot(dates, prices, linewidth=1.6, label="Price")

        ma20 = moving_average(prices, 20)

        ma50 = moving_average(prices, 50)

        ma200 = moving_average(prices, 200)

        self.ax_price.plot(dates, ma20, linewidth=1.2, label="MA20")

        self.ax_price.plot(dates, ma50, linewidth=1.2, label="MA50")

        self.ax_price.plot(dates, ma200, linewidth=1.2, label="MA200")

        u,l = bollinger_bands(prices, 20, 2.0)

        self.ax_price.fill_between(dates, l, u, alpha=0.12, label="Bollinger 20/2")

        self.ax_price.axhline(1.0, linestyle='--', linewidth=1.0, alpha=0.7, label="$1 target")

        self.ax_price.set_title("DOGE/USD — Price with MAs & Bands")

        self.ax_price.set_xlabel("Date")

        self.ax_price.set_ylabel("USD")

        self.ax_price.legend(loc="upper left")

        self.fig_chart.tight_layout()

        self._blit(self.canvas_chart)


    def _blit(self, canvas):

        # switch backend for draw then embed; keep Agg

        canvas.draw()


    def _update_whale(self):

        _, prices, vols = to_series(self.data.history)

        lb = max(10, int(self.lookback_var.get()))

        score, detail = whale_accumulation_proxy(prices, vols, lookback=lb)

        self.whale_lbl.configure(text=f"Whale Accumulation Proxy (last {lb}d): {score:.1f}/100")

        pretty = json.dumps(detail, indent=2)

        self.whale_detail.delete("1.0", "end")

        self.whale_detail.insert("1.0", pretty)


    def _run_forecast(self):

        dates, prices, _ = to_series(self.data.history)

        days = max(30, int(self.forecast_days.get()))

        paths_n = max(100, int(self.paths_var.get()))

        drift_adj = 0.0

        # tweak drift by user sentiment and ETF toggle

        drift_adj += 0.3 * float(self.user_sentiment.get()) / 365.0

        if self.etf_positive.get():

            drift_adj += 0.2 / 365.0

        paths = gbm_forecast(prices, days=days, n_paths=paths_n, drift_adj=drift_adj)


        p5 = np.percentile(paths, 5, axis=0)

        p50 = np.percentile(paths, 50, axis=0)

        p95 = np.percentile(paths, 95, axis=0)


        self.ax_fore.clear()

        horizon = np.arange(len(p50))

        self.ax_fore.plot(horizon, p50, linewidth=1.8, label="Median")

        self.ax_fore.fill_between(horizon, p5, p95, alpha=0.18, label="90% band")

        self.ax_fore.axhline(1.0, linestyle='--', linewidth=1.0, alpha=0.7, label="$1 target")

        self.ax_fore.set_title(f"GBM Forecast — {days} days | SentAdj={float(self.user_sentiment.get()):+.2f} ETF+={self.etf_positive.get()}")

        self.ax_fore.set_xlabel("Days ahead")

        self.ax_fore.set_ylabel("USD")

        self.ax_fore.legend(loc="upper left")

        self.fig_fore.tight_layout()

        self._blit(self.canvas_fore)


    def _export_report(self):

        dates, prices, vols = to_series(self.data.history)

        lb = max(10, int(self.lookback_var.get()))

        wap, detail = whale_accumulation_proxy(prices, vols, lookback=lb)

        days = max(30, int(self.forecast_days.get()))

        paths = gbm_forecast(prices, days=days, n_paths=500,

                             drift_adj=(0.3 * float(self.user_sentiment.get()) / 365.0) + (0.2/365.0 if self.etf_positive.get() else 0.0))

        p5 = np.percentile(paths, 5, axis=0)[-1]

        p50 = np.percentile(paths, 50, axis=0)[-1]

        p95 = np.percentile(paths, 95, axis=0)[-1]


        snap = self.data.snapshot

        price_now = snap.now_price if snap else prices[-1]


        lines = []

        lines.append(f"# FuzzuTech — DOGE Long‑Term Bull Case Report\n")

        lines.append(f"Generated: {dt.datetime.utcnow().isoformat()}Z\n")

        lines.append(f"Current Price: ${price_now:.6f}\n")

        if snap:

            lines.append(f"24h Change: {snap.change_24h:+.2f}%\n")

            if snap.market_cap:

                lines.append(f"Market Cap: {self._fmt_cur(snap.market_cap)}\n")

        lines.append("\n## Whale Accumulation Proxy\n")

        lines.append(f"Score (last {lb}d): {wap:.1f}/100\n")

        lines.append("Details:\n\n")

        lines.append(json.dumps(detail, indent=2))


        lines.append("\n\n## Forecast Summary\n")

        lines.append(f"Horizon: {days} days\n")

        lines.append(f"Median: ${p50:.4f}\n")

        lines.append(f"5th pct: ${p5:.4f}\n")

        lines.append(f"95th pct: ${p95:.4f}\n")

        lines.append(f"$1 target hit in median? {'YES' if p50>=1.0 else 'NO'}\n")


        lines.append("\n## Catalysts / Notes (user-entered)\n")

        lines.append(self.notes.get("1.0","end").strip() or "—")


        os.makedirs("reports", exist_ok=True)

        fname = os.path.join("reports", f"doge_bull_case_{int(time.time())}.md")

        with open(fname, "w", encoding="utf-8") as f:

            f.write("\n".join(lines))

        self._toast(f"Exported: {fname}")


    def _toast(self, msg: str, dur_ms: int = 2000):

        toast = ctk.CTkToplevel(self)

        toast.geometry("320x60")

        toast.title("")

        toast.attributes("-topmost", True)

        toast.overrideredirect(True)

        ctk.CTkLabel(toast, text=msg, font=("Segoe UI", 12)).pack(expand=True, fill="both", padx=12, pady=12)

        self.after(dur_ms, toast.destroy)



if __name__ == "__main__":

    app = DogeApp()

    app.mainloop()

Comments

Popular posts from this blog

πŸš€ Simple Login & Registration System in Python Tkinter πŸ“±

πŸ“‘ Fuzzu Packet Sniffer – Python GUI for Real-Time IP Monitoring | Tkinter + Scapy

πŸ”₯ Advanced MP3 Music Player in Python | CustomTkinter + Pygame | Free Source Code