GX-01The app MUST accept GPX files via file picker and drag-and-drop onto the page.
GX-02The app MUST parse track points (<trkpt>) with latitude, longitude, elevation (<ele>) and timestamp (<time>).
GX-03Tracks without timestamps MUST be rejected with a friendly error — no time means no speed, and speed is the whole show.
GX-04Cumulative distance MUST be computed point-to-point with the haversine formula on lat/lon (elevation ignored for distance).
GX-05A ticker symbol MUST be derived from the file: uppercase alphanumeric characters of the filename stem, truncated sensibly; if that yields nothing usable, fall back to a date-based symbol from the track's start time.
GX-09Multi-segment and multi-track GPX files MUST be concatenated in file order into one continuous track. Recording gaps simply become pauses, which are kept anyway (GX-08).
Canonical speed curve
Everything downstream — candles, hover values, moving averages — reads from one canonical speed curve. There is exactly one definition of speed in this product.
GX-06Speed at a track point MUST be computed as the slope of cumulative distance over a centered time window: distance covered between t−10s and t+10s, divided by the elapsed time of that window (clamped at track ends). This is robust to single-point GPS jitter; turns are unaffected because distance, not heading, is measured.
GX-07The chart plots speed in km/h (up = faster = rally). Hover/tooltip surfaces MUST additionally show pace in min/km.
GX-08Pauses MUST NOT be trimmed. Standing at a traffic light is a market crash and stays in the data.
Worked example. A point at t=300s: cumulative distance at t=290s is 812.0 m, at t=310s is 867.6 m. Speed = 55.6 m / 20 s = 2.78 m/s = 10.0 km/h; displayed pace = 6:00 min/km. This example MUST exist as a unit-test fixture (TS-06).