A Kindle-compatible stock-price display page served by a Python/Flask server running in Docker.
Designed for the Amazon Kindle 3 (Keyboard) browser, which renders basic HTML with no JavaScript. The page auto-refreshes using an HTML <meta http-equiv="refresh"> tag. Stock data is sourced from the Finnhub API.
- Main ticker displayed in large type (3× the font size of secondary tickers)
- Up to 7 secondary tickers in a table below: symbol, price, $ change, % change
- Market-hours aware – the Finnhub API is called only on weekdays within the configured trading window (Eastern Time); cached prices are served outside those hours
- In-memory cache – API is called at most once per
REFRESH_INTERVALseconds - Last-refresh timestamp and market-closed indicator at the bottom of every page
| Tool | Purpose |
|---|---|
| Docker Desktop | Build and run the container |
| Finnhub API key | Free tier is sufficient (60 req/min) |
| Kindle 3 (or any browser) | Display the page |
git clone https://github.com/<your-username>/<your-repo>.git
cd <your-repo>All configuration lives in the environment: block of docker-compose.yml. Open it and fill in your values:
environment:
FINNHUB_API_KEY: "your_actual_key_here" # ← required
MAIN_TICKER: "SPY"
TICKERS: "SPY,AAPL,MSFT,GOOGL,AMZN,META,TSLA,NVDA"
REFRESH_INTERVAL: "300"
MARKET_OPEN: "09:30"
MARKET_CLOSE: "16:00"
PORT: "5001"
⚠️ API key security: If your GitHub repository is public, do not commit your realFINNHUB_API_KEYindocker-compose.yml. Use a private repo, or pass the key as a shell environment variable:FINNHUB_API_KEY=your_key docker compose up --build -dand set
FINNHUB_API_KEY: "${FINNHUB_API_KEY}"in the compose file.
docker compose up --build -dThe server starts on port 5001. Open http://localhost:5001 in any browser to verify.
To stop:
docker compose downTo view logs:
docker compose logs -fAll variables are set in the environment: section of docker-compose.yml.
| Variable | Default | Description |
|---|---|---|
FINNHUB_API_KEY |
(required) | Your Finnhub API key |
MAIN_TICKER |
SPY |
Primary ticker shown large at the top |
TICKERS |
SPY,AAPL,... |
Comma-separated list of all tickers. MAIN_TICKER is removed from the secondary list automatically. Only the first 7 non-main tickers are shown. |
REFRESH_INTERVAL |
300 |
Seconds between browser refresh and API poll |
MARKET_OPEN |
09:30 |
Market open time, Eastern Time, 24h format |
MARKET_CLOSE |
16:00 |
Market close time, Eastern Time, 24h format |
PORT |
5001 |
Port the server listens on (must match ports: in docker-compose.yml) |
Market holidays: The server checks only weekday + time. U.S. market holidays (e.g., Thanksgiving, Christmas) are not automatically excluded. On those days the API will return zeros; cached data from the previous session will be served if available.
Requires Python 3.11+.
python -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate
pip install -r requirements.txtSet the required variables in your shell before running:
# Windows PowerShell
$env:FINNHUB_API_KEY="your_key"; $env:MAIN_TICKER="SPY"; python app.py
# macOS / Linux
FINNHUB_API_KEY=your_key MAIN_TICKER=SPY python app.pyThe server starts at http://localhost:5001.
- Make sure the Kindle and the machine running the server are on the same Wi-Fi network.
- Find the server's local IP address:
- Windows:
ipconfig→ look for IPv4 Address (e.g.,192.168.1.42) - macOS/Linux:
ifconfigorip addr
- Windows:
- On the Kindle, open the browser and navigate to:
http://192.168.1.42:5001 - The page will auto-refresh every
REFRESH_INTERVALseconds.
Tip: If you run the Docker container on a Raspberry Pi or a NAS on your local network, the Kindle can display stock data continuously with no PC required.
By default the Kindle goes to screensaver after a few minutes, interrupting the display. Use the hidden ~ds (disable screensaver) command to prevent this.
- Go to the Home screen.
- Using the keyboard, type
~ds— it will appear in the search bar. - Press Enter (select "Search everywhere" or similar if prompted).
- The screen may flicker briefly. The Kindle will no longer enter screensaver mode.
- Resets on reboot. The setting is lost whenever the Kindle restarts or the battery dies completely. You must re-enter
~dseach time. - Battery drain. With the screen always on and Wi-Fi active for refreshing, battery drains faster than normal. Keep the Kindle plugged in while using it as a display.
- Older firmware only. This command works on Kindle Gen 3 (Kindle Keyboard) and similarly aged devices. It does not work on newer Kindles.
- Magnetic cases. If
~dsseems to have no effect, check whether you are using a magnetic cover — the magnet can continuously trigger the sleep sensor and override the setting.
Some firmware versions require Debug Mode to be active before the screensaver command is recognised.
- On the Home screen, type
;debugOninto the search bar and press Enter. - Type
~disableScreensaverand press Enter. - Optionally, type
;debugOffand press Enter to exit debug mode.
Set the Kindle's browser bookmark to http://<your-server-ip>:5001 so it opens directly to the weather page after each reboot.
# Inside the project directory
git init
git add .
git commit -m "Initial commit: Kindle stock ticker"
# Create a new repo on GitHub (via the website or gh CLI):
gh repo create <repo-name> --public --source=. --remote=origin --push
# OR manually:
git remote add origin https://github.com/<your-username>/<repo-name>.git
git branch -M main
git push -u origin maingit add .
git commit -m "Your message"
git push
⚠️ Before pushing: if the repo is public, make sureFINNHUB_API_KEYindocker-compose.ymlstill contains only the placeholder"your_api_key_here", not your real key. Your tickers, port, and timing settings are fine to commit.
.
├── app.py # Flask server – cache, Finnhub client, route
├── templates/
│ └── index.html # Kindle-compatible HTML template (no JS)
├── screenshots/
│ └── kindle-stock-ticker.jpg # Gallery photo
├── requirements.txt # Python dependencies
├── Dockerfile # Single-stage Python 3.11-slim image
├── docker-compose.yml # Compose service definition + ALL configuration
├── .dockerignore # Files excluded from the Docker build context
└── .gitignore # Files excluded from git
- Single Gunicorn worker is intentional. The quote cache lives in Python memory; multiple workers would each maintain their own cache and make redundant API calls.
- No JavaScript on the page. All refresh logic uses the HTML
<meta http-equiv="refresh">tag, which the Kindle 3 browser supports. - Cache behaviour:
- On first request (even outside market hours) → API is called once so prices are immediately visible.
- Subsequent requests within
REFRESH_INTERVAL→ cached data returned, no API call. - Outside market hours after first load → stale cache served, API never called.
- During market hours, cache expired → API called and cache updated.
