Find results first
Ask your connected AI to search optimizer templates for a ticker and strategy. The tools are:
Template discovery lists the current published generation. Previously published template IDs remain readable so saved links continue to work.
The request template uses the same starting inputs as a fresh in-app optimizer for the selected strategy: one year of data, full default sweep ranges and directions, matching durations and chart timeframes, contract sizing, and disabled filters with their preset values. Holdout validation is off by default; enable it explicitly when wanted (the default split is 80% training / 20% holdout). These are starting defaults, not your saved in-app settings.
Start with a summary page, then select a candidate ID for more detail. Responses fit 48,000 bytes: five results by default, twenty maximum, up to 100 trades per page, or 200 equity/drawdown points. Follow
page.next_offset when present. Record lists are newest first; newly created or deleted records can change list offsets. Candidate/trade offsets refer to the stored, immutable result.
A run can succeed and keep no candidates. Its summary then returns an empty items list with empty_result: the reason (filters_unreachable, filters_conflict or no_trades), how many strategies were tested, and for each enabled filter the threshold against the best value any strategy achieved. Loosen the filters marked unreachable first.
Charts and interpretation
Requestsection=equity, drawdown, monthly or weekday for one candidate. The response includes data and presentation hints: chart type, axes, units and references to the returned values. Your AI/client renders it; Edgeful does not generate an image or run another simulation. A table works if your client cannot render charts.
Training and holdout are labeled separately. Read base_dataset in summaries and dataset in detail responses: templates, template favorites and results without holdout validation use full_period for their base data, including when requested with dataset=training. Template holdout is a subset of that full period; never append it again or present full-period metrics as training-only. Training-only template metrics are not computed by this API. Sampled curves can omit extremes, so use stored metrics for authoritative drawdown and performance. Monthly/weekday charts use full stored aggregates. Finite metrics are numbers. Missing legacy values are null; nonfinite values are { "value": null, "status": "unavailable" | "positive_infinity" | "negative_infinity" }. Chart y values remain numeric or null, with value_status explaining missing or nonfinite points. Template list rows additionally label a null profit factor as nonfinite (no losing trades), matching the app. None of these become zero. drawdownProfitPct is max drawdown as a percentage of net profit, matching the app’s DD/profit column; it is null when the period was not profitable. Simulations have no starting balance, so there is no drawdown percentage of an account. Retain warnings, date range, sizing and validation context when presenting results.
Start and resume work
For REST, sendAuthorization: Bearer YOUR_EDGEFUL_API_KEY. Fetch GET /v1/optimizer/options for the current input contract and runnable request_template, then submit:
POST /v1/optimizer/jobs returns HTTP 202 with a durable job ID. Poll GET /v1/optimizer/jobs/{job_id} every ten seconds. If you cannot wait, keep the ID and resume later. On success, use run_id as id with GET /v1/optimizer/results?source=run&id=....
Statuses succeeded, failed and cancelled are terminal; stop polling when poll_after_seconds is null. failure_kind=request means revise settings; infrastructure means you may retry the settings with a new ID and quota. A polling 429 does not fail the job.
origin identifies app or external jobs. Wait for an active app job to finish; cancel it only when you want it stopped, not just to free the shared slot. result_expired=true means a succeeded job’s result has been removed; payload_expired separately identifies cleanup of the job’s request/progress payload.
If the response was lost, repeat the same request ID and settings or use GET /v1/optimizer/jobs/by-request/{request_id}. An identical retry returns the original job, even after failure or cleanup. Changed settings with an accepted ID return HTTP 409 without Retry-After; use a new ID for new work. An active-job conflict returns 409 with Retry-After; keep the same ID and retry later. Rejected submissions do not bind the ID. A failed/cancelled accepted job requires a fresh ID and quota to try again.
Shared external limits
All API keys and MCP connections belonging to one member share these budgets:
Request windows start with the first request and expire after the named interval. New-job windows are rolling. Existing overall credential limits still apply. REST rejections include
Retry-After; MCP errors state the retry interval. External result lists/details share one concurrent read slot across the service. If they are busy, retry after two seconds. When capacity or admission controls are unavailable, new external work is rejected temporarily. Preserve your request ID when retrying.
External jobs have bounded search dimensions, dates, price-bar workload, queue time and runtime. Discovery returns the enforced values. Reduce the requested sweep when preflight rejects it. Accepted failures/cancellations do not refund quota.
The queue deadline is five minutes. A job can expire before execution when preceding jobs run long or a worker is unavailable; admission does not guarantee execution before that deadline.
External history retains fifty unprotected runs separately from app history. Terminal job payloads expire after thirty days; already-deleted unreferenced external results have a thirty-day purge grace. Saved/favorite references protect results. Compact retry receipts remain for the life of your account, and expired payloads are labeled explicitly.