package server import ( "database/sql" "encoding/json" "net/http" "git.rouggy.com/rouggy/stockradar/internal/scanner" ) func (s *Server) handleGetSignals(w http.ResponseWriter, r *http.Request) { onlyEtoro := r.URL.Query().Get("etoro") == "1" query := ` SELECT sig.ticker, COALESCE(inst.name, sig.ticker), sig.price, sig.change_pct, sig.rsi14, sig.macd, sig.macd_signal, sig.macd_hist, sig.sma20, sig.sma50, sig.volume, sig.avg_volume20, COALESCE(sig.market_cap, 0), COALESCE(sig.short_ratio, 0), COALESCE(sig.week52_high, 0), COALESCE(sig.week52_low, 0), COALESCE(sig.pct_from_high, 0), COALESCE(sig.insider_value_30d, 0), COALESCE(sig.score, 0), COALESCE(sig.on_etoro, 0), COALESCE(sig.alert,''), sig.computed_at FROM signals sig LEFT JOIN instruments inst ON inst.ticker = sig.ticker` if onlyEtoro { query += ` WHERE sig.on_etoro = 1` } query += ` ORDER BY sig.score DESC, CASE WHEN sig.alert != '' THEN 0 ELSE 1 END` rows, err := s.db.Query(query) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() signals := []scanner.Signal{} for rows.Next() { var sig scanner.Signal var onEtoro int if err := rows.Scan( &sig.Ticker, &sig.Name, &sig.Price, &sig.ChangePct, &sig.RSI14, &sig.MACD, &sig.MACDSignal, &sig.MACDHist, &sig.SMA20, &sig.SMA50, &sig.Volume, &sig.AvgVolume20, &sig.MarketCap, &sig.ShortRatio, &sig.Week52High, &sig.Week52Low, &sig.PctFromHigh, &sig.InsiderValue30d, &sig.Score, &onEtoro, &sig.Alert, &sig.ComputedAt, ); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } sig.OnEtoro = onEtoro == 1 signals = append(signals, sig) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(signals) } func (s *Server) handleTriggerScan(w http.ResponseWriter, r *http.Request) { go func() { s.scanner.Scan() }() w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"status":"scanning"}`)) } func (s *Server) handleGetPrices(w http.ResponseWriter, r *http.Request) { ticker := r.URL.Query().Get("ticker") if ticker == "" { http.Error(w, "ticker required", http.StatusBadRequest) return } rows, err := s.db.Query(` SELECT date, open, high, low, close, volume FROM prices WHERE ticker = ? ORDER BY date ASC LIMIT 90 `, ticker) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() type bar struct { Date string `json:"date"` Open float64 `json:"open"` High float64 `json:"high"` Low float64 `json:"low"` Close float64 `json:"close"` Volume int64 `json:"volume"` } bars := []bar{} for rows.Next() { var b bar var vol sql.NullInt64 if err := rows.Scan(&b.Date, &b.Open, &b.High, &b.Low, &b.Close, &vol); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if vol.Valid { b.Volume = vol.Int64 } bars = append(bars, b) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(bars) }