Reuse the selected feed
Market Pulse provides one demand-driven price/candle feed that multiple application views can share. It supplies quotes, bounded history and reusable chart/panel code. It does not provide trading authority, an order executor or an independently verified financial oracle.
Select uk.capp.sidecar.market-pulse and configure its provider. The shell package exposes window.capp.market.pulse in its owning document, with capp:market-pulse-ready and capp:market-pulse-error lifecycle events. No required Sidecar peers are needed. Selection supplies the feed/factory; mount your own view or panel rather than assuming it creates a complete market application.
In a same-document app, that owner can be window. In a split shell/app host, use the selected instance supplied by the trusted owning host; this package does not declare a generic Market Pulse app-agent proxy. A missing object in a child window is not evidence that no feed was selected. Do not search arbitrary remote parent frames or silently create a second provider.
A selected-but-unavailable or disabled provider must remain unavailable/disabled. Application defaults are not a recovery path around that choice. Independent ES-module creation is a deliberate standalone ownership decision, described below.
First successful integration
- Choose the provider and instrument IDs. Jupiter IDs are Solana mint addresses; an HTTP/manual adapter uses its declared IDs.
- Configure a permitted client credential only when the provider requires and allows one. Do not put a backend-only secret in a native binary or public web bundle.
- Build/install the selection, or explicitly construct a standalone module instance in an application you own. Confirm the actual configuration and provider mode.
- Acquire demand for the view, subscribe to snapshots and inspect
quote().availableandquote().stale, not juststatus().ready. - Release demand and remove listeners when the view leaves. Test paused, missing-credential, stale and rate-limited states before treating the display as live.
status().ready means the instance is not destroyed. It can be ready while paused, without a mark, awaiting a provider or in an authentication error. status().active describes scheduled demand, not a fresh quote; manual mode does not run the HTTP loop.
A lifecycle-safe price view
Supply the trusted owner to this helper. The default suits a same-document app. It does not construct, resume, reconfigure or destroy a shared feed. Its display-only timer rechecks quote age without making a provider request, so a stopped stream does not leave a stale price labelled live indefinitely.
<p>Market: <output id="market-price" aria-live="polite">Waiting for Market Pulse…</output></p>function bindMarketPrice(output, owner = window) {
if (!output) throw new Error('Market output is missing');
let feed = null;
let release = () => {};
let unsubscribe = () => {};
let timer = null;
let disposed = false;
function detach() {
if (timer !== null) clearInterval(timer);
timer = null;
unsubscribe();
unsubscribe = () => {};
release();
release = () => {};
feed = null;
}
function draw() {
if (disposed || !feed) return;
let text;
try {
const state = feed.status();
const instrument = state.symbols?.[0];
const quote = instrument ? feed.quote({ id: instrument.id }) : null;
if (!state.ready) text = 'Selected feed is closed.';
else if (state.phase === 'paused') text = 'Feed paused.';
else if (!quote?.available) text = 'No current quote.';
else if (quote.stale) text = 'Quote is stale; waiting for a fresh observation.';
else text = `${instrument.label}: $${Number(quote.price).toFixed(2)} USD`;
} catch { text = 'Quote unavailable; check the selected provider.'; }
if (output.textContent !== text) output.textContent = text;
}
function attach() {
if (disposed) return;
const next = owner.capp?.market?.pulse;
if (next === feed && feed) { draw(); return; }
detach();
if (!next || ['status', 'quote', 'acquire', 'subscribe'].some(key => typeof next[key] !== 'function')) {
output.textContent = 'Selected Market Pulse is unavailable.';
return;
}
feed = next;
try {
release = feed.acquire('public-price-view');
unsubscribe = feed.subscribe(draw);
timer = setInterval(draw, 1000);
} catch { detach(); output.textContent = 'Market view could not attach.'; }
}
function failed() { detach(); if (!disposed) output.textContent = 'Selected Market Pulse failed.'; }
owner.addEventListener('capp:market-pulse-ready', attach);
owner.addEventListener('capp:market-pulse-error', failed);
attach();
return () => {
disposed = true;
owner.removeEventListener('capp:market-pulse-ready', attach);
owner.removeEventListener('capp:market-pulse-error', failed);
detach();
};
}
const disposeMarketPrice = bindMarketPrice(document.querySelector('#market-price'));Call disposeMarketPrice() on view teardown. A real product may also show provider phase, last observation time and quality. Do not replace stale/missing data with an invented price or use this display helper as transaction validation.
Feed API
| Method | Input and result | Ownership |
|---|---|---|
status() | Redacted lifecycle, cadence, deadlines, configured instruments and errors | Read-only |
quote({id}) | Available/stale flags, price, age, source/observation time, optional block and quality | Re-read when evaluating freshness |
candles({id, intervalMs, limit}) | OHLCV history and interval status; absent volume is null | Configured instrument and supported interval required |
configuration() / configure(patch) | Redacted settings / validated persistent update | Only the configuration owner should update shared policy |
exportConfig() / importConfig(value) | Versioned settings interchange; export omits credential values | A configuration change, not market history export |
subscribe(callback) | Immediate and later snapshots; returns unsubscribe | Each consumer removes its subscription |
acquire(label) | Retain demand; returns release | Each view releases its own demand |
refresh() | Request an update within cadence/cooldown limits | Not a rate-limit bypass |
pause() / resume() / setHidden(bool) | Feed-wide demand and visibility controls | Not a child view's substitute for release |
ingest(packet) | Atomic validated push packet in manual mode | Manual provider owner |
destroy() | Abort/dispose the instance | Final provider owner only |
Subscribing observes state; acquiring retains the polling demand. Multiple consumers of the same instance share the request budget. Separate instances have separate loops/budgets. Repeated refresh calls do not grant a larger upstream rate tier.
Provider modes and data quality
| Mode | Supplies | Important limit |
|---|---|---|
jupiter | Current USD price observations sampled into candles | Not a historical trade feed; no volume inferred from samples |
http-candles | Your configured HTTPS OHLCV adapter | You own its service, data quality, availability and permitted credentials |
manual | App-supplied capp.market-feed.v1 packets | No hosted broadcaster or WebSocket reconnect service is included |
HTTP adapters receive a GET with ids and intervalMs. adapterToken, when configured, is sent as a Bearer header to that adapter; the Jupiter key is not forwarded there. The configured adapter URL must be HTTPS without embedded credentials, query or fragment, and must support the requesting origin/headers. Redirects are refused.
Times are Unix milliseconds. Candle starts align to their interval. Prices are USD; supplied volume is traded base-asset units, never inferred from samples. A later provider bar replaces the matching bar instead of adding its volume twice. Supply a current quote for a live mark; bars alone populate history. Runtime validation adds time-ordering/alignment and price-range checks beyond JSON shape.
The packet bounds are 50 instruments, 1,000 bars per instrument and 1 MB per HTTP response. Cadence respects configured budget/headroom and cooldowns. A 429 enters backoff; access rejection pauses requests until the owner fixes access and resumes. Do not force retry loops around either state.
Remembered device state can include configured client credentials. Exported configuration omits their values, but that is not encryption of local persistence. Persisted history does not automatically restore a fresh current mark after reload.
Deliberately standalone modules and charts
The package source includes core.mjs, chart.mjs and panel.mjs. In a deliberately standalone application, construct MarketPulse once and pass that instance to every panel/consumer. There is no automatic npm installation assumed by this page.
For a selected shell package, window.CAppMarketPulse.create(config, env) and .mount(container, feed, options) are owner-document helpers. Prefer mounting a panel against the already selected feed. A panel starts collapsed and acquires/releases its own demand as it opens/closes; its destroy() removes that panel, not every consumer's shared provider.
The following helper only constructs a manual instance when explicitly called. It is for a local fixture or an application-owned adapter, never an implicit fallback around a selected package:
function createManualMarket(MarketPulse) {
const feed = new MarketPulse({
providerMode: 'manual',
symbols: [{ id: 'DEMO-USD', label: 'Demo / USD' }],
remember: false
});
const time = Date.now();
feed.ingest({
protocol: 'capp.market-feed.v1',
instruments: [{ id: 'DEMO-USD', quote: { price: 12.5, time } }]
});
return feed; // The caller owns final feed.destroy(). This is synthetic data.
}When using ES modules, pass the class imported from your explicitly vendored core.mjs. Supply a project/app-scoped storage key when intentionally persisting a standalone instance; without a storage adapter, history stays in memory. Test packet errors and cleanup without contacting a live price provider first.
Agent integration is separate
The package declares market.status, market.quote and market.candles. A declaration is not registration in every shared broker. Solana Lab has a separate bounded lab.market.* adapter; its availability does not certify generic Agent integration.
Market Pulse never chooses a wallet, authorises an operation, signs or submits a trade. Use separate wallet, transaction and independent verification capabilities for those effects.
Troubleshooting
| Symptom | Check and next step |
|---|---|
| API missing in the app frame | Check the trusted owner and actual selected instance. There is no universal Market app-agent proxy. |
| Instance ready but no quote | Inspect provider phase, demand, configured instrument and available/stale. |
| Configured manual/disabled provider replaced by live defaults | Fix the application's ownership/fallback integration; do not accept a different provider as recovery. |
| Duplicate requests | Share one instance; release consumers and destroy only an independently owned provider. |
| Old price remains visible during outage | Re-read quote age independently of snapshot notifications, as in the example. |
| History exists but mark is missing after restart | Expected until a fresh current observation arrives. |
| Adapter packet rejected | Validate the schema plus interval alignment, timestamps, instrument IDs and OHLC range. |
Related data contracts
These schemas describe payload shape, not evidence of approval, provider truth or successful execution. Match the version in your payload.
Configuration reference
These are the selected package's configuration fields. String credential values and installation-specific defaults are never published here. A field's presence does not bypass its runtime policy.
| Field | Type | Meaning and constraints |
|---|---|---|
providerMode | select | Price source. Choices: jupiter, http-candles, manual |
apiKey | text | Optional distributed client key. Used only for api.jup.ag.. Sensitive: do not embed backend-only credentials in distributed apps |
candleMs | number | Candle interval (ms). Bounds: 1000 to 3600000 |
chartType | select | Chart style. Choices: candles, ohlc, line, area |
enabled | checkbox | Enable price requests |
remember | checkbox | Remember on this device |
pollMs | number | Requested polling interval (ms). Bounds: 1000 to 300000 |
rateMode | select | Request profile. Choices: conservative, custom |
budgetPerMinute | number | Applies 20% headroom. Other devices and apps may share your provider quota.. Bounds: 1 to 6000 |
staleAfterMs | number | Stale after (ms). Bounds: 15000 to 600000 |
adapterUrl | text | Absolute HTTPS URL supporting capp.market-feed.v1. |
adapterToken | text | OHLCV provider token. Sensitive: do not embed backend-only credentials in distributed apps |
Agent interfaces
These names are declared by the package for host/Agent integration, not JavaScript methods to invoke directly. The host must register a tool, grant its scope and enforce its approval policy. A manifest declaration alone does not prove availability in every Agent catalogue. Prefer the application API above when integrating your own app.
| Tool | Declared risk | Approval requirement |
|---|---|---|
market.status | low | mode-policy |
market.quote | low | mode-policy |
market.candles | low | mode-policy |