Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stock Alerts

Next.js React TypeScript PostgreSQL Vercel

Stock Alerts is a study project for monitoring a personal list of assets traded on the Brazilian market. It was built to explore the Next.js App Router, feature-oriented application architecture, scheduled background work, external data providers, and email delivery.

The application stores daily market data, calculates exponential moving averages, records technical buy signals, and displays the results in an authenticated dashboard. It is not intended to provide investment advice.

Index

Screenshots

Monitoring

The main screen shows the latest stored price, current EMA-based suggestion, market-data date, and monitoring status for each asset.

Monitoring screen

Asset details

Each monitored asset has a detail page with its latest price, EMA 6/13/42 values, a 60-session EMA chart, and an OHLC candlestick chart.

PETR4 detail screen

The account email was replaced with email@example.com while capturing these screenshots. The remaining data comes from the running production application.

Features

  • Google authentication with an optional exact-email allowlist.
  • Profile-owned watchlists with symbol validation through brapi.dev.
  • Asset names, logos, notes, and per-asset pause/resume controls.
  • Daily OHLC price snapshots and EMA 6, 13, and 42 calculations.
  • Current technical classification: buy, hold, sell, or insufficient data.
  • Buy-signal history based on EMA crossovers.
  • Per-profile email preference and Resend signal digests.
  • Protected Vercel Cron routes for market checks and missing-logo refreshes.
  • Persisted job history, checkpoints, and email delivery attempts.
  • Unit, component, server-action, and Playwright browser tests.

The SUPER role

The project includes a small hidden maintainer flow:

  1. Sign in normally.
  2. Click the email address in the dashboard header five times within two seconds.
  3. Enter the value configured in ROLE_ACCESS_PASSWORD.

The profile is promoted to SUPER and the Execuções section becomes available. Server-side authorization also protects the route and retry action.

The execution screen shows recent job results, duration, processed assets, generated signals, email outcomes, and provider errors. A SUPER profile can retry the latest failed alert-check run.

SUPER execution history

ROLE_ACCESS_PASSWORD is a shared promotion secret. Use a long, unique value and restrict ALLOWED_EMAILS in production. The promoted role is stored on the application profile.

How the monitoring job works

The scheduled alert check:

  1. Finds enabled profile/asset pairs.
  2. Fetches each distinct symbol from brapi.dev once per run.
  3. Stores new price history and recalculates EMA snapshots.
  4. Detects buy-signal crossovers for each profile.
  5. Sends at most one eligible signal digest per profile and market date.
  6. Records checkpoints, delivery results, and the overall job summary.

Price refreshes request data through the current São Paulo calendar date so the dashboard can store the freshest daily snapshot exposed by the provider. Alert evaluation excludes that date until the next calendar day, preventing an in-progress daily candle from generating a signal. The email date comes from the latest completed snapshot returned for each asset rather than assuming the provider has yesterday's data; delayed snapshots can therefore generate one deduplicated digest when they become available.

The current dashboard suggestion is based on EMA alignment:

Alignment Suggestion
EMA 6 > EMA 13 > EMA 42 Buy
EMA 6 < EMA 13 < EMA 42 Sell
Any other complete alignment Hold
One or more averages unavailable Insufficient data

Recorded buy signals use two crossover rules:

  • EMA 6 crosses above EMA 42.
  • EMA 6 crosses above EMA 13 while EMA 6 is above EMA 42.

Hosting

The repository is configured for Vercel and PostgreSQL. Another Node.js host can run the application, but it must provide equivalent scheduled HTTPS requests for the two cron routes.

Required dependencies

Local software

Dependency Requirement
Git Current stable version
Node.js 20.9 or newer
pnpm 11.8.0
PostgreSQL client tools Recommended for local database management

Enable the package-manager version declared by the repository:

corepack enable
corepack prepare pnpm@11.8.0 --activate

If Corepack is unavailable in the installed Node.js distribution, install it first with npm install --global corepack.

External services

Service Use
PostgreSQL Application data, Auth.js sessions, and job history
Google Cloud OAuth 2.0 web client
brapi.dev B3 asset metadata and historical prices
Resend Signal digest delivery
Vercel Hosting and scheduled route invocation

The current email-provider validation requires a sender on the exact fellcor.com domain. A fork using another domain must update src/features/alerts/infrastructure/email-delivery-provider-factory.ts.

Setup

1. Clone and install

git clone https://github.com/LeFelps/stock-alerts.git
cd stock-alerts
pnpm install --frozen-lockfile

2. Create a PostgreSQL database

For a locally running PostgreSQL server:

createdb stock_alerts

For Vercel, provision a managed PostgreSQL database through the Storage Marketplace or another provider. Use a pooled connection string for serverless deployments when the provider offers one.

Use separate databases for production and preview deployments. The prebuild script runs pnpm db:migrate, so each build migrates the database configured for that environment.

3. Create Google OAuth credentials

In the Google Auth Platform:

  1. Create or select a project.
  2. Configure the OAuth consent screen.
  3. Create an OAuth 2.0 client with type Web application.
  4. Add the exact callback URLs that will be used:
http://localhost:3000/api/auth/callback/google
https://<your-production-domain>/api/auth/callback/google

Save the client ID and secret as AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET.

4. Configure brapi.dev and Resend

Create a token in the brapi dashboard. Some test symbols work without authentication, but normal production coverage requires BRAPI_API_TOKEN.

For email delivery:

  1. Add fellcor.com to the Resend Domains dashboard.
  2. Publish the supplied SPF and DKIM records.
  3. Wait for the domain status to become Verified.
  4. Create a project-specific API key with Sending access.

5. Configure environment variables

cp .env.example .env.local

Generate separate secrets for Auth.js, cron requests, and role promotion:

openssl rand -base64 32

Complete .env.local:

AUTH_SECRET=<random-auth-secret>
AUTH_GOOGLE_ID=<google-client-id>
AUTH_GOOGLE_SECRET=<google-client-secret>

DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/stock_alerts

# Optional. Blank allows any Google account with an email address.
ALLOWED_EMAILS=you@example.com

ROLE_ACCESS_PASSWORD=<random-role-password>

MARKET_DATA_PROVIDER=brapi
BRAPI_API_TOKEN=<brapi-token>

EMAIL_PROVIDER=resend
RESEND_API_KEY=<resend-key-starting-with-re_>
ALERT_EMAIL_FROM="Stock Alerts <noreply.stock-alerts@fellcor.com>"
APP_BASE_URL=http://localhost:3000

CRON_SECRET=<random-cron-secret>
Variable Requirement
AUTH_SECRET Required by Auth.js
AUTH_GOOGLE_ID Required Google OAuth client ID
AUTH_GOOGLE_SECRET Required Google OAuth client secret
DATABASE_URL Required PostgreSQL connection string
ALLOWED_EMAILS Optional comma/newline-separated allowlist
ROLE_ACCESS_PASSWORD Required to unlock the SUPER role
MARKET_DATA_PROVIDER Optional; defaults to brapi
BRAPI_API_TOKEN Required for normal production coverage
EMAIL_PROVIDER Optional; defaults to resend
RESEND_API_KEY Required for email delivery and must start with re_
ALERT_EMAIL_FROM Required and currently restricted to fellcor.com
APP_BASE_URL Optional on Vercel; base URL for links in alert emails
CRON_SECRET Required by both scheduled routes

Environment files are ignored by Git except for .env.example. None of these variables should use a NEXT_PUBLIC_ prefix.

6. Migrate and run locally

pnpm db:migrate
pnpm dev

Open http://localhost:3000 and sign in with Google.

The alert-check route can be invoked manually with the cron secret:

curl --fail-with-body \
  --header "Authorization: Bearer $CRON_SECRET" \
  http://localhost:3000/api/cron/check-alerts

This performs real provider requests and database writes.

7. Run the checks

pnpm lint
pnpm format
pnpm test
pnpm build

pnpm build applies migrations first and requires a reachable DATABASE_URL.

For the browser suite:

pnpm exec playwright install
pnpm test:e2e

8. Deploy to Vercel

  1. Import the Git repository into a new Vercel project.
  2. Connect the production PostgreSQL database and expose its connection string as DATABASE_URL.
  3. Add the environment variables above to the Production environment.
  4. If previews are enabled, add their variables separately and use an isolated preview database.
  5. Deploy the project.
  6. Add the final production callback URL to the Google OAuth client.
  7. Confirm both jobs under Project → Settings → Cron Jobs.
  8. Sign in, add an asset, and invoke the alert-check route once to populate initial history.
  9. Unlock SUPER and inspect the resulting execution.

Environment-variable changes apply only to new Vercel deployments, so redeploy after editing them.

Scheduled routes

vercel.json registers:

Schedule Route Purpose
30 10 * * * /api/cron/refresh-asset-logos Refresh missing or generic logos
0 11 * * 2-6 /api/cron/check-alerts Update prices, indicators, signals, and emails

Vercel evaluates both schedules in UTC. The alert check runs Tuesday through Saturday at 11:00 UTC, currently 08:00 in São Paulo. Vercel sends Authorization: Bearer <CRON_SECRET> automatically.

Architecture

The App Router composes the feature modules, while scheduled routes reuse the same application services used by the protected maintainer actions.

flowchart LR
  Browser[Authenticated browser] --> Auth[Google OAuth / Auth.js]
  Auth --> UI[Next.js App Router]
  UI --> Profiles[(Profiles and watchlists)]

  Cron[Vercel Cron] --> Routes[Protected cron routes]
  Routes --> Job[Alert-check job]
  Job --> Brapi[brapi.dev]
  Brapi --> Prices[(Price snapshots)]
  Prices --> Indicators[EMA 6 / 13 / 42]
  Indicators --> Signals[(Profile signals)]
  Signals --> Digest[Daily signal digest]
  Digest --> Resend[Resend]

  Super[SUPER profile] --> Runs[Job history and retry]
  Runs --> Job
Loading

Project structure

src/
├── app/                    # App Router pages, layouts, actions, and routes
├── components/ui/          # Shared UI primitives
├── db/                     # Drizzle client and PostgreSQL schema
├── features/
│   ├── alerts/             # Email digest delivery
│   ├── indicators/         # EMA calculations and persistence
│   ├── jobs/               # Scheduled orchestration and execution history
│   ├── market-data/        # brapi adapter and market UI
│   ├── profiles/           # Product profiles and preferences
│   ├── role-access/        # SUPER promotion flow
│   ├── signals/            # Signal detection and history
│   └── watchlist/          # Watchlist and asset catalog behavior
└── lib/                    # Shared helpers and authentication policies

See docs/architecture.md and docs/adr/ for the architectural rules and accepted decisions.

Scripts

Command Description
pnpm dev Start the development server
pnpm build Migrate and build for production
pnpm start Start the production build
pnpm lint Run ESLint
pnpm format Check formatting
pnpm test Run Vitest once
pnpm test:e2e Run Playwright tests
pnpm db:generate Generate a Drizzle migration
pnpm db:migrate Apply checked-in migrations
pnpm db:push Push the schema directly

About

Authenticated B3 stock alert dashboard with scheduled market checks and email digests

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages