mirror of
https://github.com/Gerald-Ha/HodlEye-Crypto-Price-Tracker.git
synced 2025-06-25 01:01:46 +00:00
Compare commits
8 Commits
6b051a074e
...
1be8b97407
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1be8b97407 | ||
![]() |
62b1c11882 | ||
![]() |
42c11d2e7e | ||
![]() |
6a33a5c9ec | ||
![]() |
509956bb6f | ||
a354d7a453 | |||
aed13bfcd6 | |||
bed31f6618 |
157
README.md
157
README.md
@ -2,18 +2,16 @@
|
|||||||
|
|
||||||
A lightweight Docker-based web tool to monitor cryptocurrency prices (via Binance and OKX) with **unlimited alarms** and **unlimited crypto tracking**, outshining typical TradingView limitations. It also provides quick access to multiple RSS-based crypto news sources and a live Economic Calendar.
|
A lightweight Docker-based web tool to monitor cryptocurrency prices (via Binance and OKX) with **unlimited alarms** and **unlimited crypto tracking**, outshining typical TradingView limitations. It also provides quick access to multiple RSS-based crypto news sources and a live Economic Calendar.
|
||||||
|
|
||||||
|
<img src="https://github.com/user-attachments/assets/15c9227b-0683-4563-8b71-75f2f9ed475e" width="800" height="auto">
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
Check out the live demo here: [HodlEye Demo](https://hodleye.gerald-hasani.com/)
|
Check out the live demo here: [HodlEye Demo](https://hodleye.gerald-hasani.com/)
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [Overview](#overview)
|
1. [Overview](#overview)
|
||||||
@ -23,15 +21,18 @@ Check out the live demo here: [HodlEye Demo](https://hodleye.gerald-hasani.com/)
|
|||||||
- [Alarm Functionality](#alarm-functionality)
|
- [Alarm Functionality](#alarm-functionality)
|
||||||
- [Crypto News](#crypto-news)
|
- [Crypto News](#crypto-news)
|
||||||
- [Economic Calendar](#economic-calendar)
|
- [Economic Calendar](#economic-calendar)
|
||||||
|
- [TradingView Chart](#tradingview-chart)
|
||||||
3. [Installation & Usage](#installation--usage)
|
3. [Installation & Usage](#installation--usage)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
- [Docker Build & Run](#docker-build--run)
|
- [Docker Build & Run](#docker-build--run)
|
||||||
4. [Project Structure](#project-structure)
|
4. [Windows Notification App: HodlEye_Notify](#windows-notification-app-hodleye_notify)
|
||||||
- [Frontend (`index.html` & `magic.js`)](#frontend-indexhtml--magicjs)
|
5. [Project Structure](#project-structure)
|
||||||
|
- [Frontend (index.html & magic.js)](#frontend-indexhtml--magicjs)
|
||||||
- [News Feed Server (Node.js)](#news-feed-server-nodejs)
|
- [News Feed Server (Node.js)](#news-feed-server-nodejs)
|
||||||
5. [Important Notes / Limitations](#important-notes--limitations)
|
6. [Important Notes / Limitations](#important-notes--limitations)
|
||||||
6. [Privacy & Data Disclaimer](#privacy--data-disclaimer)
|
7. [Coming Soon](#coming-soon)
|
||||||
7. [License](#license)
|
8. [Privacy & Data Disclaimer](#privacy--data-disclaimer)
|
||||||
|
9. [License](#license)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -41,25 +42,36 @@ Check out the live demo here: [HodlEye Demo](https://hodleye.gerald-hasani.com/)
|
|||||||
|
|
||||||
- **Unlimited Alarms**: No cap on the number of alarms you can set.
|
- **Unlimited Alarms**: No cap on the number of alarms you can set.
|
||||||
- **Unlimited Crypto Tracking**: Easily add as many coins as you want.
|
- **Unlimited Crypto Tracking**: Easily add as many coins as you want.
|
||||||
- **Real-Time Price Updates (every 5 seconds)**: Uses Binance and OKX data.
|
- **Real-Time Price Updates (every 1 seconds)**: Uses Binance and OKX data.
|
||||||
- **Crypto News & Economic Calendar**: Stay updated on the latest events affecting the market.
|
- **Crypto News & Economic Calendar**: Stay updated on the latest events affecting the market.
|
||||||
|
- **TradingView Modal Integration**: Click on any crypto pair to open a TradingView modal for in-depth chart analysis.**New**
|
||||||
|
|
||||||
The tool refreshes prices every **5 seconds**, which may introduce a slight delay in alarm triggers if the price quickly touches the threshold in between intervals.
|
The tool refreshes prices every **1 seconds**, which may introduce a slight delay in alarm triggers if the price quickly touches the threshold in between intervals.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Unlimited Alarms & Tracking
|
### Unlimited Alarms & Tracking
|
||||||
|
|
||||||
- You can set **as many alarms as you like** — no daily or total limit.
|
- You can set **as many alarms as you like** — no daily or total limit.
|
||||||
- Track **any number of cryptocurrencies** in the list simultaneously.
|
- Track **any number of cryptocurrencies** in the list simultaneously.
|
||||||
|
|
||||||
|
<img src="https://github.com/user-attachments/assets/276de04d-dcf6-411a-b550-cbb5104c1579" width="auto" height="400">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Price Updates
|
### Price Updates
|
||||||
|
|
||||||
- **Binance** and **OKX** are integrated as the primary data sources.
|
- **Binance** and **OKX** are integrated as the primary data sources.
|
||||||
- By default, HodlEye tries Binance first; if that fails or is forced off, it falls back to OKX.
|
- By default, HodlEye tries Binance first; if that fails or is forced off, it falls back to OKX.
|
||||||
- It automatically fetches the current price, 24h open, 1h open, and calculates the 24h and 1h percentage changes every **5 seconds**.
|
- It automatically fetches the current price, 24h open, 1h open, and calculates the 24h and 1h percentage changes every **1 seconds**.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Alarm Functionality
|
### Alarm Functionality
|
||||||
|
|
||||||
- Set alarms for each coin (e.g., `BTC/USDT`), choosing:
|
- Set alarms for each coin (e.g., `BTC/USDT`), choosing:
|
||||||
- **Alarm Price** (threshold)
|
- **Alarm Price** (threshold)
|
||||||
- **Direction** (Rising, Falling, or Both)
|
- **Direction** (Rising, Falling, or Both)
|
||||||
@ -67,47 +79,68 @@ The tool refreshes prices every **5 seconds**, which may introduce a slight dela
|
|||||||
- When triggered, a popup and sound notification appear, with optional desktop notifications.
|
- When triggered, a popup and sound notification appear, with optional desktop notifications.
|
||||||
- **Once** alarms are marked locally in the browser (not removed from the server) so they do not trigger again unless reloaded or manually reset.
|
- **Once** alarms are marked locally in the browser (not removed from the server) so they do not trigger again unless reloaded or manually reset.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Crypto News
|
### Crypto News
|
||||||
|
|
||||||
- News from multiple RSS sources:
|
- News from multiple RSS sources:
|
||||||
- `https://crypto.news/feed/`
|
- `https://crypto.news/feed/`
|
||||||
- `https://cointelegraph.com/rss`
|
- `https://cointelegraph.com/rss`
|
||||||
- `https://thedefiant.io/api/feed`
|
- `https://thedefiant.io/api/feed`
|
||||||
- `https://newsbtc.com/feed`
|
- `https://newsbtc.com/feed`
|
||||||
- `https://news.bitcoin.com/feed` *(may be inaccessible in certain regions)*
|
- `https://news.bitcoin.com/feed` _(may be inaccessible in certain regions)_
|
||||||
- `https://bitcoinmagazine.com/feed`
|
- `https://bitcoinmagazine.com/feed`
|
||||||
- `https://cryptopanic.com/news/rss/`
|
- `https://cryptopanic.com/news/rss/`
|
||||||
- `https://decrypt.co/feed`
|
- `https://decrypt.co/feed`
|
||||||
- Quickly view and filter recent articles within the built-in News modal.
|
- Quickly view and filter recent articles within the built-in News modal.
|
||||||
|
|
||||||
|
<img src="https://github.com/user-attachments/assets/f0727b39-a075-4d50-9600-f53c803d4a1b" width="auto" height="400">
|
||||||
|
|
||||||
|
|
||||||
### Economic Calendar
|
### Economic Calendar
|
||||||
|
|
||||||
- The **Economic Calendar** button opens a modal with an [Investing.com](https://www.investing.com/) iframe, showing major economic events such as central bank decisions and market-impacting announcements.
|
- The **Economic Calendar** button opens a modal with an [Investing.com](https://www.investing.com/) iframe, showing major economic events such as central bank decisions and market-impacting announcements.
|
||||||
|
|
||||||
---
|
<img src="https://github.com/user-attachments/assets/e254301e-9aaa-48d8-84e7-6faa598ca8be" width="600" height="auto">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### TradingView Chart
|
||||||
|
- The **TradingView Chart** Crypto Box Currency click opens a modal with a Tradingview Chart Window iframe, get a better overview of the charts.
|
||||||
|
<img src="https://github.com/user-attachments/assets/53bd1553-7679-40c1-afa8-0330cd28a71b" width="600" height="auto">
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Installation & Usage
|
## Installation & Usage
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- [Docker](https://www.docker.com/) installed.
|
- [Docker](https://www.docker.com/) installed.
|
||||||
- (Optional) [Docker-Compose](https://docs.docker.com/compose/) if you want a more complex or multi-container setup.
|
- (Optional) [Docker-Compose](https://docs.docker.com/compose/) if you want a more complex or multi-container setup.
|
||||||
|
|
||||||
### Docker Build & Run
|
### Docker Build & Run
|
||||||
|
|
||||||
1. **Clone this repository**
|
1. **Clone this repository**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/YourGitHubName/HodlEye.git
|
git clone https://github.com/Gerald-Ha/HodlEye-Crypto-Price-Tracker.git
|
||||||
cd HodlEye
|
cd HodlEye
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Build the Docker image**
|
2. **Build the Docker image**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker buildx build -t hodleye-crypto-tracker .
|
docker buildx build -t hodleye-crypto-tracker .
|
||||||
```
|
```
|
||||||
*(Make sure you’re in the same directory as the Dockerfile.)*
|
|
||||||
|
_(Make sure you’re in the same directory as the Dockerfile.)_
|
||||||
|
|
||||||
3. **Run the container**
|
3. **Run the container**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d -p 3099:3099 -p 5001:5001 --name hodleye-container hodleye-crypto-tracker
|
docker run -p 3099:3099 -p 5001:5001 -v hodleye_data:/app/data --name hodleye-container hodleye-crypto-tracker
|
||||||
```
|
```
|
||||||
|
|
||||||
- Port `3099` serves the main web interface.
|
- Port `3099` serves the main web interface.
|
||||||
- Port `5001` is used by the Node.js server that fetches news RSS feeds.
|
- Port `5001` is used by the Node.js server that fetches news RSS feeds.
|
||||||
|
|
||||||
@ -116,7 +149,48 @@ The tool refreshes prices every **5 seconds**, which may introduce a slight dela
|
|||||||
- **News Feed Endpoint**: [http://localhost:5001/api/news](http://localhost:5001/api/news)
|
- **News Feed Endpoint**: [http://localhost:5001/api/news](http://localhost:5001/api/news)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Windows Notification App: HodlEye_Notify
|
||||||
|
|
||||||
|
<img src="https://github.com/user-attachments/assets/a3356708-1b3a-4fb8-9a71-d1af88f29c5f" width="auto" height="150">
|
||||||
|
<img src="https://github.com/user-attachments/assets/cb0eb2a8-45fb-4a80-bb81-56957e838153" width="auto" height="150">
|
||||||
|
|
||||||
|
If you prefer not to keep the HodlEye Crypto Price Tracker web interface open in your browser all the time, you can use a lightweight Windows application called **HodlEye_Notify**. This tool connects directly to the same endpoint as the Docker container and will display notifications on your desktop whenever an alarm is triggered.
|
||||||
|
|
||||||
|
|
||||||
|
1. **Setup**
|
||||||
|
|
||||||
|
- Enter the IP address and port of your HodlEye Docker container (for example, `http://192.168.1.112:3099/`) in the HodlEye_Notify window.
|
||||||
|
- Click **Connect** to establish a WebSocket connection.
|
||||||
|
- Once connected, you’ll see the status change to “Connected.”
|
||||||
|
|
||||||
|
2. **Autostart**
|
||||||
|
|
||||||
|
- Add HodlEye_Notify to your Windows **Startup** folder so it automatically launches when Windows starts. This way, you’ll continuously receive notifications without needing to reopen the program manually.
|
||||||
|
|
||||||
|
3. **Testing Notifications**
|
||||||
|
|
||||||
|
- From the machine running the Docker container, you can trigger a test notification using the following `curl` command:
|
||||||
|
|
||||||
|
**Ubuntu**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://192.168.1.112:3099/api/notifications \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"message\": \"⚠️ ALARM (Recurring, Both): BTC reached 92250\", \"timestamp\": \"2025-03-06T06:19:58.584Z\"}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows CMD**
|
||||||
|
```bash
|
||||||
|
|
||||||
|
curl -X POST http://192.168.1.112:3099/api/notifications -H "Content-Type: application/json" -d "{\"message\": \"⚠️ ALARM (Recurring, Both): BTC reached 92250\", \"timestamp\": \"2025-03-06T06:19:58.584Z\"}"
|
||||||
|
```
|
||||||
|
|
||||||
|
- If everything is configured correctly, you should receive a desktop notification from HodlEye_Notify indicating the alarm has triggered.
|
||||||
|
|
||||||
|
This application simplifies the process of staying informed about your alarms, letting you work on other tasks without leaving the HodlEye web interface open.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
Below is an example directory tree (based on your structure). Yours may vary slightly:
|
Below is an example directory tree (based on your structure). Yours may vary slightly:
|
||||||
@ -124,21 +198,30 @@ Below is an example directory tree (based on your structure). Yours may vary sli
|
|||||||
```
|
```
|
||||||
HodlEye-Crypto-Price-Tracker
|
HodlEye-Crypto-Price-Tracker
|
||||||
├── Dockerfile
|
├── Dockerfile
|
||||||
|
├── LICENSE.txt
|
||||||
|
├── PRIVACY.md
|
||||||
|
├── README.md
|
||||||
├── data
|
├── data
|
||||||
│ └── data.json
|
│ └── data.json
|
||||||
├── public
|
├── public
|
||||||
│ ├── font
|
│ ├── font
|
||||||
│ │ └── BreeSerif-Regular.ttf
|
│ │ └── BreeSerif-Regular.ttf
|
||||||
│ ├── images
|
│ ├── images
|
||||||
|
│ │ ├── Gitea_Logo.svg
|
||||||
│ │ ├── coffee.svg
|
│ │ ├── coffee.svg
|
||||||
│ │ └── favicon.png
|
│ │ ├── favicon.png
|
||||||
|
│ │ └── github-mark.svg
|
||||||
│ ├── index.html
|
│ ├── index.html
|
||||||
│ ├── magic.js
|
│ ├── magic.js
|
||||||
│ ├── news.js
|
│ ├── news.js
|
||||||
|
│ ├── tradingview.js
|
||||||
|
│ ├── script.js
|
||||||
|
│ ├── responsive.css
|
||||||
│ ├── sound
|
│ ├── sound
|
||||||
│ │ ├── cashing.mp3
|
│ │ ├── cashing.mp3
|
||||||
│ │ └── ping.mp3
|
│ │ └── ping.mp3
|
||||||
│ └── style.css
|
│ ├── style.css
|
||||||
|
│ └── update.js
|
||||||
├── server
|
├── server
|
||||||
│ ├── newsfeed
|
│ ├── newsfeed
|
||||||
│ │ ├── node_modules
|
│ │ ├── node_modules
|
||||||
@ -149,12 +232,15 @@ HodlEye-Crypto-Price-Tracker
|
|||||||
│ ├── package-lock.json
|
│ ├── package-lock.json
|
||||||
│ ├── package.json
|
│ ├── package.json
|
||||||
│ └── server.js
|
│ └── server.js
|
||||||
└── sound
|
├── sound
|
||||||
├── cashing.mp3
|
│ ├── cashing.mp3
|
||||||
└── ping.mp3
|
│ └── ping.mp3
|
||||||
|
└── update.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Frontend (`index.html` & `magic.js`)
|
### Frontend (`index.html` & `magic.js`)
|
||||||
|
|
||||||
- **`index.html`**
|
- **`index.html`**
|
||||||
- Main interface containing modals and buttons (Add Crypto, Edit List, Alarms, Options, etc.).
|
- Main interface containing modals and buttons (Add Crypto, Edit List, Alarms, Options, etc.).
|
||||||
- Includes buttons for:
|
- Includes buttons for:
|
||||||
@ -167,11 +253,13 @@ HodlEye-Crypto-Price-Tracker
|
|||||||
- Core logic:
|
- Core logic:
|
||||||
- Fetches cryptos (`/api/cryptos`)
|
- Fetches cryptos (`/api/cryptos`)
|
||||||
- Loads alarms (`/api/alarms`) and notifications (`/api/notifications`)
|
- Loads alarms (`/api/alarms`) and notifications (`/api/notifications`)
|
||||||
- Pulls prices from Binance/OKX every 5 seconds
|
- Pulls prices from Binance/OKX every 1 seconds
|
||||||
- Checks and triggers alarms
|
- Checks and triggers alarms
|
||||||
- Handles UI rendering (prices, alarms, notifications, drag & drop reorder)
|
- Handles UI rendering (prices, alarms, notifications, drag & drop reorder)
|
||||||
|
|
||||||
|
|
||||||
### News Feed Server (Node.js)
|
### News Feed Server (Node.js)
|
||||||
|
|
||||||
- A minimal Node.js Express server (in `server.js` or similar) which:
|
- A minimal Node.js Express server (in `server.js` or similar) which:
|
||||||
- Retrieves the listed RSS feeds and parses them via `xml2js`
|
- Retrieves the listed RSS feeds and parses them via `xml2js`
|
||||||
- Serves them in JSON format at `/api/news`
|
- Serves them in JSON format at `/api/news`
|
||||||
@ -184,20 +272,24 @@ HodlEye-Crypto-Price-Tracker
|
|||||||
```
|
```
|
||||||
- Accessible at [http://localhost:5001/api/news](http://localhost:5001/api/news).
|
- Accessible at [http://localhost:5001/api/news](http://localhost:5001/api/news).
|
||||||
|
|
||||||
*(Within Docker, it’s already bundled, so just expose `5001`.)*
|
_(Within Docker, it’s already bundled, so just expose `5001`.)_
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Important Notes / Limitations
|
## Important Notes / Limitations
|
||||||
|
|
||||||
1. **1-second polling**
|
1. **1-second polling**
|
||||||
- There’s a potential delay in alarms because price thresholds are only checked every 5 seconds. If a price briefly touches and moves away between polls, you might miss that exact trigger moment.
|
|
||||||
|
- There’s a potential delay in alarms because price thresholds are only checked every 1 seconds. If a price briefly touches and moves away between polls, you might miss that exact trigger moment.
|
||||||
|
|
||||||
2. **API availability**
|
2. **API availability**
|
||||||
|
|
||||||
- Binance/OKX may be temporarily down or might not support certain symbols.
|
- Binance/OKX may be temporarily down or might not support certain symbols.
|
||||||
- HodlEye tries Binance → fallback to OKX if needed.
|
- HodlEye tries Binance → fallback to OKX if needed.
|
||||||
|
|
||||||
3. **Unlimited Alarms (Once vs. Recurring)**
|
3. **Unlimited Alarms (Once vs. Recurring)**
|
||||||
|
|
||||||
- **Once** alarms become locally “triggered” to avoid repeated alerts but are not server-side deactivated.
|
- **Once** alarms become locally “triggered” to avoid repeated alerts but are not server-side deactivated.
|
||||||
- **Recurring** triggers repeatedly every time the threshold is crossed.
|
- **Recurring** triggers repeatedly every time the threshold is crossed.
|
||||||
|
|
||||||
@ -207,26 +299,33 @@ HodlEye-Crypto-Price-Tracker
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Coming Soon
|
## Coming Soon
|
||||||
|
|
||||||
Exciting new features and improvements are on the way! Here are some planned updates:
|
Exciting new features and improvements are on the way! Here are some planned updates:
|
||||||
|
|
||||||
- **Portfolio Management**: Track your crypto holdings in real-time with easy-to-read analytics.
|
- **Portfolio Management**: Track your crypto holdings in real-time with easy-to-read analytics.
|
||||||
- **Windows Notification App**: A lightweight Windows application to receive price alerts and notifications directly on your desktop.
|
|
||||||
- **More Integrations**: Expanding support for additional exchanges and data sources.
|
- **More Integrations**: Expanding support for additional exchanges and data sources.
|
||||||
|
- **Automatic alarm in the event of a sharp price drop of 10% within 1 hour**
|
||||||
|
- **HodlEye Notify Alarm with various sound selections and HodlEye Alarms**
|
||||||
|
|
||||||
|
|
||||||
Stay tuned for updates!
|
Stay tuned for updates!
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Privacy & Data Disclaimer
|
## Privacy & Data Disclaimer
|
||||||
|
|
||||||
- **No Data Collection by This Application**: HodlEye itself does not collect, store, or process any personal data or usage analytics.
|
- **No Data Collection by This Application**: HodlEye itself does not collect, store, or process any personal data or usage analytics.
|
||||||
- **External Services**: Certain features (e.g., news feeds, iframes) rely on third-party websites or APIs. We do not control and are not responsible for the data-collection practices or privacy policies of these external providers. Please refer to the privacy policies of those services for details.
|
- **External Services**: Certain features (e.g., news feeds, iframes) rely on third-party websites or APIs. We do not control and are not responsible for the data-collection practices or privacy policies of these external providers. Please refer to the privacy policies of those services for details.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="version-info">
|
<div class="version-info">
|
||||||
<span id="currentVersion">Version 1.0.7</span>
|
<span id="currentVersion">Version 1.0.8</span>
|
||||||
<span
|
<span
|
||||||
id="updateAvailable"
|
id="updateAvailable"
|
||||||
style="display: none; color: red; cursor: pointer"
|
style="display: none; color: red; cursor: pointer"
|
||||||
@ -288,8 +288,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="tradingViewModal" class="modal">
|
||||||
|
<div class="modal-content" id="tradingViewModalContent" style="height:700px; width:100%;">
|
||||||
|
<!--
|
||||||
|
Tradingview Place
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="magic.js"></script>
|
<script src="magic.js"></script>
|
||||||
<script src="news.js"></script>
|
<script src="news.js"></script>
|
||||||
<script src="update.js"></script>
|
<script src="update.js"></script>
|
||||||
|
<script src="tradingview.js"></script>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -17,6 +17,10 @@ let apiPreference = JSON.parse(localStorage.getItem("apiPreference")) || {};
|
|||||||
let editMode = false;
|
let editMode = false;
|
||||||
let currentApiSelectSymbol = null;
|
let currentApiSelectSymbol = null;
|
||||||
|
|
||||||
|
|
||||||
|
let exchangeUsedMap = {};
|
||||||
|
|
||||||
|
|
||||||
async function loadCryptosFromServer() {
|
async function loadCryptosFromServer() {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/api/cryptos");
|
const resp = await fetch("/api/cryptos");
|
||||||
@ -63,6 +67,7 @@ async function deleteCrypto(index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function loadAlarmsFromServer() {
|
async function loadAlarmsFromServer() {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/api/alarms");
|
const resp = await fetch("/api/alarms");
|
||||||
@ -102,6 +107,7 @@ async function deleteAlarm(alarmId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function loadNotificationsFromServer() {
|
async function loadNotificationsFromServer() {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/api/notifications");
|
const resp = await fetch("/api/notifications");
|
||||||
@ -134,6 +140,7 @@ async function clearNotifications() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
document.getElementById("alarmSound").src = "sound/" + userOptions.soundFile;
|
document.getElementById("alarmSound").src = "sound/" + userOptions.soundFile;
|
||||||
updateTheme(userOptions.darkMode);
|
updateTheme(userOptions.darkMode);
|
||||||
@ -151,6 +158,7 @@ async function init() {
|
|||||||
renderNotifications();
|
renderNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function renderCryptoGrid() {
|
function renderCryptoGrid() {
|
||||||
const grid = document.getElementById("cryptoGrid");
|
const grid = document.getElementById("cryptoGrid");
|
||||||
grid.innerHTML = "";
|
grid.innerHTML = "";
|
||||||
@ -200,6 +208,7 @@ function renderCryptoGrid() {
|
|||||||
box.appendChild(apiLabel);
|
box.appendChild(apiLabel);
|
||||||
|
|
||||||
if (editMode) {
|
if (editMode) {
|
||||||
|
|
||||||
box.draggable = true;
|
box.draggable = true;
|
||||||
box.setAttribute("data-index", index);
|
box.setAttribute("data-index", index);
|
||||||
|
|
||||||
@ -217,6 +226,20 @@ function renderCryptoGrid() {
|
|||||||
deleteCrypto(index);
|
deleteCrypto(index);
|
||||||
});
|
});
|
||||||
box.appendChild(deleteBtn);
|
box.appendChild(deleteBtn);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
box.addEventListener("click", (event) => {
|
||||||
|
|
||||||
|
if (event.target.classList.contains("api-label") || event.target.closest(".api-label")) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const usedExchange = exchangeUsedMap[symbol] || "BINANCE";
|
||||||
|
openTradingViewModal(usedExchange, symbol);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.appendChild(box);
|
grid.appendChild(box);
|
||||||
@ -260,9 +283,7 @@ function renderNotifications() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
|
||||||
EINFACHE UI-Funktionen: Modal open/close
|
|
||||||
------------------------------------------------------------------ */
|
|
||||||
function openAddCryptoModal() {
|
function openAddCryptoModal() {
|
||||||
document.getElementById("addCryptoModal").style.display = "block";
|
document.getElementById("addCryptoModal").style.display = "block";
|
||||||
document.getElementById("newCryptoSymbol").value = "";
|
document.getElementById("newCryptoSymbol").value = "";
|
||||||
@ -332,9 +353,7 @@ function closeBuyMeModal() {
|
|||||||
document.getElementById("buyMeModal").style.display = "none";
|
document.getElementById("buyMeModal").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
|
||||||
NEU: Crypto News & Economic Calendar Modals
|
|
||||||
------------------------------------------------------------------ */
|
|
||||||
function openCryptoNews() {
|
function openCryptoNews() {
|
||||||
document.getElementById("cryptoNewsModal").style.display = "block";
|
document.getElementById("cryptoNewsModal").style.display = "block";
|
||||||
}
|
}
|
||||||
@ -351,6 +370,7 @@ function closeEconomicCalendarModal() {
|
|||||||
document.getElementById("economicCalendarModal").style.display = "none";
|
document.getElementById("economicCalendarModal").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function saveOptions() {
|
function saveOptions() {
|
||||||
userOptions.soundFile = document.getElementById("soundSelect").value;
|
userOptions.soundFile = document.getElementById("soundSelect").value;
|
||||||
const alarmSound = document.getElementById("alarmSound");
|
const alarmSound = document.getElementById("alarmSound");
|
||||||
@ -396,9 +416,7 @@ function saveApiSelection() {
|
|||||||
closeApiSelectModal();
|
closeApiSelectModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
|
||||||
EDIT MODE (Drag & Drop)
|
|
||||||
------------------------------------------------------------------ */
|
|
||||||
function toggleEditMode() {
|
function toggleEditMode() {
|
||||||
editMode = !editMode;
|
editMode = !editMode;
|
||||||
document.getElementById("editButton").textContent = editMode ? "Done" : "Edit List";
|
document.getElementById("editButton").textContent = editMode ? "Done" : "Edit List";
|
||||||
@ -440,7 +458,6 @@ async function reorderCryptoList(fromIndex, toIndex) {
|
|||||||
cryptoList.splice(toIndex, 0, item);
|
cryptoList.splice(toIndex, 0, item);
|
||||||
|
|
||||||
renderCryptoGrid();
|
renderCryptoGrid();
|
||||||
|
|
||||||
await saveCryptoList();
|
await saveCryptoList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +473,7 @@ async function saveCryptoList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function fetchCryptoData(symbol, elementId) {
|
async function fetchCryptoData(symbol, elementId) {
|
||||||
const preferredApi = apiPreference[symbol] || "auto";
|
const preferredApi = apiPreference[symbol] || "auto";
|
||||||
|
|
||||||
@ -474,6 +492,7 @@ async function fetchCryptoData(symbol, elementId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (await isBinanceSupported(symbol)) {
|
if (await isBinanceSupported(symbol)) {
|
||||||
return fetchFromBinance(symbol, elementId);
|
return fetchFromBinance(symbol, elementId);
|
||||||
} else if (await isOkxSupported(symbol)) {
|
} else if (await isOkxSupported(symbol)) {
|
||||||
@ -519,7 +538,7 @@ async function fetchFromBinance(symbol, elementId) {
|
|||||||
updateCryptoBox({
|
updateCryptoBox({
|
||||||
symbol,
|
symbol,
|
||||||
elementId,
|
elementId,
|
||||||
apiUsed: "Binance",
|
apiUsed: "BINANCE",
|
||||||
dailyOpen,
|
dailyOpen,
|
||||||
hourlyOpen,
|
hourlyOpen,
|
||||||
lastPrice,
|
lastPrice,
|
||||||
@ -545,6 +564,7 @@ async function fetchFromOkx(symbol, elementId) {
|
|||||||
pct24 = ((lastPrice - dailyOpen) / dailyOpen) * 100;
|
pct24 = ((lastPrice - dailyOpen) / dailyOpen) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const cUrl = `https://www.okx.com/api/v5/market/candles?instId=${instId}&bar=1H&limit=1`;
|
const cUrl = `https://www.okx.com/api/v5/market/candles?instId=${instId}&bar=1H&limit=1`;
|
||||||
let cResp = await fetch(cUrl);
|
let cResp = await fetch(cUrl);
|
||||||
if (!cResp.ok) throw new Error("OKX 1h candle request failed");
|
if (!cResp.ok) throw new Error("OKX 1h candle request failed");
|
||||||
@ -581,7 +601,11 @@ function formatPrice(value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updateCryptoBox({symbol, elementId, apiUsed, dailyOpen, hourlyOpen, lastPrice, change24h, change1h}) {
|
function updateCryptoBox({symbol, elementId, apiUsed, dailyOpen, hourlyOpen, lastPrice, change24h, change1h}) {
|
||||||
|
|
||||||
|
exchangeUsedMap[symbol] = apiUsed;
|
||||||
|
|
||||||
const dOpenStr = formatPrice(dailyOpen);
|
const dOpenStr = formatPrice(dailyOpen);
|
||||||
const hOpenStr = formatPrice(hourlyOpen);
|
const hOpenStr = formatPrice(hourlyOpen);
|
||||||
const lastStr = formatPrice(lastPrice);
|
const lastStr = formatPrice(lastPrice);
|
||||||
@ -590,9 +614,7 @@ function updateCryptoBox({symbol, elementId, apiUsed, dailyOpen, hourlyOpen, las
|
|||||||
const pct1hStr = isNaN(change1h) ? "-" : change1h.toFixed(2) + "%";
|
const pct1hStr = isNaN(change1h) ? "-" : change1h.toFixed(2) + "%";
|
||||||
|
|
||||||
document.getElementById("daily-" + elementId).innerHTML = `Daily Price:<br>${dOpenStr} USDT<br>`;
|
document.getElementById("daily-" + elementId).innerHTML = `Daily Price:<br>${dOpenStr} USDT<br>`;
|
||||||
|
|
||||||
document.getElementById("hourly-" + elementId).innerHTML = `Price H:<br>${hOpenStr} USDT<br>`;
|
document.getElementById("hourly-" + elementId).innerHTML = `Price H:<br>${hOpenStr} USDT<br>`;
|
||||||
|
|
||||||
document.getElementById("price-" + elementId).innerHTML = `<strong>Current Price:</strong><br>${lastStr} USDT<br>`;
|
document.getElementById("price-" + elementId).innerHTML = `<strong>Current Price:</strong><br>${lastStr} USDT<br>`;
|
||||||
|
|
||||||
const c24 = document.getElementById("change24-" + elementId);
|
const c24 = document.getElementById("change24-" + elementId);
|
||||||
@ -605,10 +627,12 @@ function updateCryptoBox({symbol, elementId, apiUsed, dailyOpen, hourlyOpen, las
|
|||||||
|
|
||||||
document.getElementById("api-" + elementId).innerHTML = `API: ${apiUsed}`;
|
document.getElementById("api-" + elementId).innerHTML = `API: ${apiUsed}`;
|
||||||
|
|
||||||
|
|
||||||
checkAlarms(symbol, lastPrice);
|
checkAlarms(symbol, lastPrice);
|
||||||
lastPrices[symbol] = lastPrice;
|
lastPrices[symbol] = lastPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkAlarms(symbol, currentPrice) {
|
function checkAlarms(symbol, currentPrice) {
|
||||||
alarms.forEach((alarm) => {
|
alarms.forEach((alarm) => {
|
||||||
if (alarm.symbol !== symbol) return;
|
if (alarm.symbol !== symbol) return;
|
||||||
@ -639,6 +663,7 @@ function checkAlarms(symbol, currentPrice) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function showErrorMessage(msg) {
|
function showErrorMessage(msg) {
|
||||||
document.getElementById("errorMessage").textContent = msg;
|
document.getElementById("errorMessage").textContent = msg;
|
||||||
document.getElementById("errorOverlay").style.display = "block";
|
document.getElementById("errorOverlay").style.display = "block";
|
||||||
@ -670,6 +695,7 @@ function closeAlarmPopup() {
|
|||||||
document.getElementById("alarmOverlay").style.display = "none";
|
document.getElementById("alarmOverlay").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function copyToClipboard(address) {
|
function copyToClipboard(address) {
|
||||||
if (navigator.clipboard && window.isSecureContext) {
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -708,6 +734,10 @@ function copyToClipboard(address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", init);
|
||||||
|
|
||||||
|
|
||||||
async function isBinanceSupported(symbol) {
|
async function isBinanceSupported(symbol) {
|
||||||
const url = `https://api.binance.com/api/v3/ticker/24hr?symbol=${symbol}USDT`;
|
const url = `https://api.binance.com/api/v3/ticker/24hr?symbol=${symbol}USDT`;
|
||||||
try {
|
try {
|
||||||
|
@ -72,3 +72,13 @@
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
#tradingViewModal .modal-content {
|
||||||
|
width: 95%;
|
||||||
|
min-width: unset;
|
||||||
|
max-width: 95%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
10
public/script.js
Normal file
10
public/script.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
document.querySelectorAll('.modal').forEach(modal => {
|
||||||
|
modal.addEventListener('click', function(event) {
|
||||||
|
|
||||||
|
if (event.target === modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -628,7 +628,11 @@ body.light .alarm-item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#updateModal .modal-content {}
|
#updateModal .modal-content {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.version-info {
|
.version-info {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -651,3 +655,33 @@ body.light .alarm-item {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#tradingViewModal .modal-content {
|
||||||
|
min-width: 80%;
|
||||||
|
min-height: 80%;
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 35%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -45%);
|
||||||
|
padding: 25px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow-y: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tradingViewModal .close {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 7px;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: red;
|
||||||
|
}
|
63
public/tradingview.js
Normal file
63
public/tradingview.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
function openTradingViewModal(exchange, symbol) {
|
||||||
|
const tradingViewSymbol = `${exchange}:${symbol}USDT`;
|
||||||
|
console.log("TradingView-Symbol =", tradingViewSymbol);
|
||||||
|
|
||||||
|
const modal = document.getElementById("tradingViewModal");
|
||||||
|
const container = document.getElementById("tradingViewModalContent");
|
||||||
|
|
||||||
|
|
||||||
|
modal.style.display = "block";
|
||||||
|
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<span class="close" onclick="closeTradingViewModal()">×</span>
|
||||||
|
<div class="tradingview-widget-container" style="height:700px; width:100%;">
|
||||||
|
<div class="tradingview-widget-container__widget" style="height:calc(700px - 32px); width:100%;"></div>
|
||||||
|
<div class="tradingview-widget-copyright">
|
||||||
|
<a href="https://www.tradingview.com/" rel="noopener nofollow" target="_blank">
|
||||||
|
<span class="blue-text">Track all markets on TradingView</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.type = "text/javascript";
|
||||||
|
script.src = "https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js";
|
||||||
|
script.async = true;
|
||||||
|
|
||||||
|
script.text = JSON.stringify({
|
||||||
|
"autosize": true,
|
||||||
|
"symbol": tradingViewSymbol,
|
||||||
|
"interval": "1H",
|
||||||
|
"timezone": "Etc/UTC",
|
||||||
|
"theme": "dark",
|
||||||
|
"hide_side_toolbar": false,
|
||||||
|
"style": "1",
|
||||||
|
"locale": "en",
|
||||||
|
"allow_symbol_change": true,
|
||||||
|
"calendar": false,
|
||||||
|
"support_host": "https://www.tradingview.com"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const widgetContainer = container.querySelector(".tradingview-widget-container");
|
||||||
|
if (widgetContainer) {
|
||||||
|
widgetContainer.appendChild(script);
|
||||||
|
console.log("TradingView-Widget-Skript korrekt dem Container hinzugefügt.");
|
||||||
|
} else {
|
||||||
|
console.error("Fehler: Widget-Container nicht gefunden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function closeTradingViewModal() {
|
||||||
|
const modal = document.getElementById("tradingViewModal");
|
||||||
|
modal.style.display = "none";
|
||||||
|
|
||||||
|
|
||||||
|
const container = document.getElementById("tradingViewModalContent");
|
||||||
|
container.innerHTML = "";
|
||||||
|
console.log("TradingView-Modal geschlossen und Inhalt bereinigt.");
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
const CURRENT_VERSION = "1.0.7";
|
const CURRENT_VERSION = "1.0.8";
|
||||||
|
|
||||||
function getUpdateUrl() {
|
function getUpdateUrl() {
|
||||||
return "/api/update?t=" + new Date().getTime();
|
return "/api/update?t=" + new Date().getTime();
|
||||||
@ -9,7 +9,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
checkForUpdates();
|
checkForUpdates();
|
||||||
|
|
||||||
|
|
||||||
setInterval(checkForUpdates, 259200000);
|
setInterval(checkForUpdates, 86400000);
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkForUpdates() {
|
function checkForUpdates() {
|
||||||
|
@ -8,7 +8,7 @@ const PORT = process.env.PORT || 5001;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Metadata
|
* Metadata
|
||||||
* Version: 1.0.4
|
* Version: 1.0.6
|
||||||
* Author/Dev: Gerald Hasani
|
* Author/Dev: Gerald Hasani
|
||||||
* Name: HodlEye Crypto Price Tracker
|
* Name: HodlEye Crypto Price Tracker
|
||||||
* Email: contact@gerald-hasani.com
|
* Email: contact@gerald-hasani.com
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.7",
|
"version": "1.0.8",
|
||||||
"changelog": [
|
"changelog": [
|
||||||
"Bug fixes and improvements",
|
"Bug fixes and improvements",
|
||||||
"News Feed Error Fix"
|
"Adding Tradingview chart display loaded when you click on the respective cryptocurrencies"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user