Ask the indicator assistant (streaming)
Same request as POST /v1/indicator-builder/chat, but the answer is streamed as newline-delimited JSON (application/x-ndjson) so the client can show the reply as it is written. Events: {"type":"thinking"} heartbeats while the model reasons, {"type":"delta","text":"…"} for each piece of answer text (prose and the code block, in order), and exactly one terminal event — {"type":"done","data":{message,source,pane,name}} with the same shape as the non-streaming response, or {"type":"error","code","message"}. The HTTP status is 200 once streaming has begun; rate-limit and configuration errors are returned as normal JSON errors before any bytes are sent. Prefer this endpoint: long scripts can take over a minute to generate.
curl --request POST \
--url https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"messages": [
{
"role": "user",
"content": "Build an RSI with 30/70 guides."
}
],
"script": {
"pane": "separate",
"source": "const length = input.int('Length', 14, { min: 2, max: 100 });\nfunction compute(bars, inputs, hp) {\n const rsi = hp.rsi(bars.map(b => b.c), length);\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\n}",
"name": "RSI"
},
"error": null
}
EOFimport requests
url = "https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream"
payload = {
"messages": [
{
"role": "user",
"content": "Build an RSI with 30/70 guides."
}
],
"script": {
"pane": "separate",
"source": "const length = input.int('Length', 14, { min: 2, max: 100 });
function compute(bars, inputs, hp) {
const rsi = hp.rsi(bars.map(b => b.c), length);
return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };
}",
"name": "RSI"
},
"error": None
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
messages: [{role: 'user', content: 'Build an RSI with 30/70 guides.'}],
script: {
pane: 'separate',
source: 'const length = input.int(\'Length\', 14, { min: 2, max: 100 });\nfunction compute(bars, inputs, hp) {\n const rsi = hp.rsi(bars.map(b => b.c), length);\n return { plots: [{ title: \'RSI\', values: rsi, color: \'#ab47bc\' }], range: { min: 0, max: 100 }, guides: [30, 70] };\n}',
name: 'RSI'
},
error: null
})
};
fetch('https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'messages' => [
[
'role' => 'user',
'content' => 'Build an RSI with 30/70 guides.'
]
],
'script' => [
'pane' => 'separate',
'source' => 'const length = input.int(\'Length\', 14, { min: 2, max: 100 });
function compute(bars, inputs, hp) {
const rsi = hp.rsi(bars.map(b => b.c), length);
return { plots: [{ title: \'RSI\', values: rsi, color: \'#ab47bc\' }], range: { min: 0, max: 100 }, guides: [30, 70] };
}',
'name' => 'RSI'
],
'error' => null
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Build an RSI with 30/70 guides.\"\n }\n ],\n \"script\": {\n \"pane\": \"separate\",\n \"source\": \"const length = input.int('Length', 14, { min: 2, max: 100 });\\nfunction compute(bars, inputs, hp) {\\n const rsi = hp.rsi(bars.map(b => b.c), length);\\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\\n}\",\n \"name\": \"RSI\"\n },\n \"error\": null\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Build an RSI with 30/70 guides.\"\n }\n ],\n \"script\": {\n \"pane\": \"separate\",\n \"source\": \"const length = input.int('Length', 14, { min: 2, max: 100 });\\nfunction compute(bars, inputs, hp) {\\n const rsi = hp.rsi(bars.map(b => b.c), length);\\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\\n}\",\n \"name\": \"RSI\"\n },\n \"error\": null\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Build an RSI with 30/70 guides.\"\n }\n ],\n \"script\": {\n \"pane\": \"separate\",\n \"source\": \"const length = input.int('Length', 14, { min: 2, max: 100 });\\nfunction compute(bars, inputs, hp) {\\n const rsi = hp.rsi(bars.map(b => b.c), length);\\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\\n}\",\n \"name\": \"RSI\"\n },\n \"error\": null\n}"
response = http.request(request)
puts response.read_body"<string>"{
"success": false,
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed",
"code": "VALIDATION_ERROR"
}{
"success": false,
"statusCode": 401,
"error": "Unauthorized",
"message": "Authentication required",
"code": "UNAUTHORIZED"
}{
"success": false,
"statusCode": 429,
"error": "Too Many Requests",
"message": "You have reached the indicator builder limit for this hour. Try again later.",
"code": "RATE_LIMITED"
}{
"success": false,
"statusCode": 503,
"error": "Error",
"message": "The indicator assistant is not configured on this server.",
"code": "MODEL_UNAVAILABLE"
}Body
Chat history, oldest first. The last message must be from the user. Each message is capped at 24,000 characters — enough for a full Pine Script paste.
1 - 40 elementsShow child attributes
Show child attributes
[
{
"role": "user",
"content": "Build an RSI with 30/70 guides."
}
]
What is currently in the editor. The reply replaces it wholesale when it contains code.
Show child attributes
Show child attributes
Compile or runtime error the chart reported for the current script, if any.
2000null
The sample series the builder chart is showing, so the model can reason about scale and bar count.
Show child attributes
Show child attributes
Response
NDJSON event stream (see notes)
The response is of type string.
Related topics
Ask the indicator assistantIndicator assistant availabilityHyperprop Script: build indicators (agent recipe)ChangelogGet all order books (best bid/ask)curl --request POST \
--url https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"messages": [
{
"role": "user",
"content": "Build an RSI with 30/70 guides."
}
],
"script": {
"pane": "separate",
"source": "const length = input.int('Length', 14, { min: 2, max: 100 });\nfunction compute(bars, inputs, hp) {\n const rsi = hp.rsi(bars.map(b => b.c), length);\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\n}",
"name": "RSI"
},
"error": null
}
EOFimport requests
url = "https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream"
payload = {
"messages": [
{
"role": "user",
"content": "Build an RSI with 30/70 guides."
}
],
"script": {
"pane": "separate",
"source": "const length = input.int('Length', 14, { min: 2, max: 100 });
function compute(bars, inputs, hp) {
const rsi = hp.rsi(bars.map(b => b.c), length);
return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };
}",
"name": "RSI"
},
"error": None
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
messages: [{role: 'user', content: 'Build an RSI with 30/70 guides.'}],
script: {
pane: 'separate',
source: 'const length = input.int(\'Length\', 14, { min: 2, max: 100 });\nfunction compute(bars, inputs, hp) {\n const rsi = hp.rsi(bars.map(b => b.c), length);\n return { plots: [{ title: \'RSI\', values: rsi, color: \'#ab47bc\' }], range: { min: 0, max: 100 }, guides: [30, 70] };\n}',
name: 'RSI'
},
error: null
})
};
fetch('https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'messages' => [
[
'role' => 'user',
'content' => 'Build an RSI with 30/70 guides.'
]
],
'script' => [
'pane' => 'separate',
'source' => 'const length = input.int(\'Length\', 14, { min: 2, max: 100 });
function compute(bars, inputs, hp) {
const rsi = hp.rsi(bars.map(b => b.c), length);
return { plots: [{ title: \'RSI\', values: rsi, color: \'#ab47bc\' }], range: { min: 0, max: 100 }, guides: [30, 70] };
}',
'name' => 'RSI'
],
'error' => null
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Build an RSI with 30/70 guides.\"\n }\n ],\n \"script\": {\n \"pane\": \"separate\",\n \"source\": \"const length = input.int('Length', 14, { min: 2, max: 100 });\\nfunction compute(bars, inputs, hp) {\\n const rsi = hp.rsi(bars.map(b => b.c), length);\\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\\n}\",\n \"name\": \"RSI\"\n },\n \"error\": null\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Build an RSI with 30/70 guides.\"\n }\n ],\n \"script\": {\n \"pane\": \"separate\",\n \"source\": \"const length = input.int('Length', 14, { min: 2, max: 100 });\\nfunction compute(bars, inputs, hp) {\\n const rsi = hp.rsi(bars.map(b => b.c), length);\\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\\n}\",\n \"name\": \"RSI\"\n },\n \"error\": null\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hyperprop.com/platform/v1/indicator-builder/chat/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Build an RSI with 30/70 guides.\"\n }\n ],\n \"script\": {\n \"pane\": \"separate\",\n \"source\": \"const length = input.int('Length', 14, { min: 2, max: 100 });\\nfunction compute(bars, inputs, hp) {\\n const rsi = hp.rsi(bars.map(b => b.c), length);\\n return { plots: [{ title: 'RSI', values: rsi, color: '#ab47bc' }], range: { min: 0, max: 100 }, guides: [30, 70] };\\n}\",\n \"name\": \"RSI\"\n },\n \"error\": null\n}"
response = http.request(request)
puts response.read_body"<string>"{
"success": false,
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed",
"code": "VALIDATION_ERROR"
}{
"success": false,
"statusCode": 401,
"error": "Unauthorized",
"message": "Authentication required",
"code": "UNAUTHORIZED"
}{
"success": false,
"statusCode": 429,
"error": "Too Many Requests",
"message": "You have reached the indicator builder limit for this hour. Try again later.",
"code": "RATE_LIMITED"
}{
"success": false,
"statusCode": 503,
"error": "Error",
"message": "The indicator assistant is not configured on this server.",
"code": "MODEL_UNAVAILABLE"
}