Skip to main content

Market Pulse Dashboard

Parse a CSV price series, calculate its change, and draw a compact market chart. Demo mode is deterministic; the same app includes a cached HTTP path for live data.

SPX market display with a green price, percentage change, and rising area chart

Complete app

app.star
load("cache.star", "cache")
load("encoding/csv.star", "csv")
load("http.star", "http")
load("humanize.star", "humanize")
load("math.star", "math")
load("render.star", "render")
load("schema.star", "schema")

DEMO_MODE = True
DEMO_CSV = """minute,price
0,181.2
1,182.6
2,181.9
3,184.1
4,183.5
5,185.7
6,186.3
7,185.9
"""

def parse_prices(source):
rows = csv.read_all(source, skip = 1)
return [float(row[1]) for row in rows]

def fetch_prices(symbol):
key = "market:%s" % symbol
stored = cache.get(key)
if stored != None:
return stored

response = http.get(
"https://markets.example/prices.csv?symbol=%s" % symbol,
ttl_seconds = 300,
)
if response.status_code != 200:
fail("Market request failed: %d" % response.status_code)

source = response.body()
cache.set(key, source, ttl_seconds = 300)
return source

def main(config):
symbol = config.str("symbol", "SPX")
source = DEMO_CSV if DEMO_MODE else fetch_prices(symbol)
prices = parse_prices(source)
points = [(index, value) for index, value in enumerate(prices)]
change = ((prices[-1] - prices[0]) / prices[0]) * 100
color = "#4ade80" if change >= 0 else "#fb7185"

return render.Root(
child = render.Stack(
children = [
render.Padding(
pad = (1, 2, 0, 1),
child = render.Column(
expanded = True,
main_align = "space_between",
children = [
render.Text(symbol, font = "6x10", color = "#e2e8f0"),
render.Column(
children = [
render.Text(
"$%s" % humanize.ftoa(prices[-1], 1),
color = color,
),
render.Text(
"%s%%" % humanize.ftoa(math.floor(change * 10) / 10, 1),
font = "CG-pixel-3x5-mono",
color = color,
),
],
),
],
),
),
render.Padding(
pad = (28, 6, 0, 0),
child = render.Plot(
data = points,
width = 36,
height = 26,
color = color,
fill = True,
fill_color = color + "55",
x_lim = (0, len(points) - 1),
y_lim = (180, 188),
),
),
],
),
)

def get_schema():
return schema.Schema(
version = "1",
fields = [
schema.Dropdown(
id = "symbol",
name = "Market",
desc = "Market symbol to display.",
icon = "money-bill-trend-up",
default = "SPX",
options = [
schema.Option(display = "S&P 500", value = "SPX"),
schema.Option(display = "Nasdaq", value = "IXIC"),
],
),
],
)

Download this app.star.

How it works

  1. csv.read_all() skips the header and the comprehension converts prices to floats.
  2. The first and last values produce the percentage change and its gain/loss color.
  3. humanize and math keep the two displayed numbers compact and predictable.
  4. Plot receives (x, y) pairs and explicit limits sized for the 64×32 display.
  5. fetch_prices() checks the shared cache before requesting and caching live CSV.

Connect live prices

Replace markets.example with a CSV endpoint whose second column is the price, then set DEMO_MODE to False. Include the selected symbol in the cache key so installations do not collide.