@hive-org/cli 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/agents.js +46 -0
- package/dist/create/generate.js +2 -2
- package/dist/index.js +7 -7
- package/dist/list/ListApp.js +1 -1
- package/dist/presets.js +11 -0
- package/dist/start/StartApp.js +189 -0
- package/package.json +1 -1
- package/templates/analysis.ts +1 -1
- package/templates/hooks/useAgent.ts +1 -0
- package/templates/prompt.ts +9 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
CLI for bootstrapping Hive AI Agents. Walk through an interactive wizard to create a fully scaffolded trading agent with its own personality, prediction strategy, and terminal UI.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx @hive-org/cli create
|
|
6
|
+
npx @hive-org/cli@latest create
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## Commands
|
|
@@ -23,10 +23,10 @@ Launches an interactive wizard that walks you through 8 steps:
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
# Interactive — prompts for everything
|
|
26
|
-
npx @hive-org/cli create
|
|
26
|
+
npx @hive-org/cli@latest create
|
|
27
27
|
|
|
28
28
|
# Skip the name prompt
|
|
29
|
-
npx @hive-org/cli create alpha-trader
|
|
29
|
+
npx @hive-org/cli@latest create alpha-trader
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
### `@hive-org/cli list`
|
|
@@ -34,7 +34,7 @@ npx @hive-org/cli create alpha-trader
|
|
|
34
34
|
Lists all agents in `~/.hive/agents/` with provider and creation date.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npx @hive-org/cli list
|
|
37
|
+
npx @hive-org/cli@latest list
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
---
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
export async function scanAgents() {
|
|
5
|
+
const agentsDir = path.join(os.homedir(), '.hive', 'agents');
|
|
6
|
+
const exists = await fs.pathExists(agentsDir);
|
|
7
|
+
if (!exists) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
11
|
+
const agents = [];
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
if (!entry.isDirectory()) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const soulPath = path.join(agentsDir, entry.name, 'SOUL.md');
|
|
17
|
+
const soulExists = await fs.pathExists(soulPath);
|
|
18
|
+
if (!soulExists) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
let provider = 'unknown';
|
|
22
|
+
const envPath = path.join(agentsDir, entry.name, '.env');
|
|
23
|
+
const envExists = await fs.pathExists(envPath);
|
|
24
|
+
if (envExists) {
|
|
25
|
+
const envContent = await fs.readFile(envPath, 'utf-8');
|
|
26
|
+
if (envContent.includes('OPENAI_API_KEY')) {
|
|
27
|
+
provider = 'OpenAI';
|
|
28
|
+
}
|
|
29
|
+
else if (envContent.includes('ANTHROPIC_API_KEY')) {
|
|
30
|
+
provider = 'Anthropic';
|
|
31
|
+
}
|
|
32
|
+
else if (envContent.includes('GOOGLE_GENERATIVE_AI_API_KEY')) {
|
|
33
|
+
provider = 'Google';
|
|
34
|
+
}
|
|
35
|
+
else if (envContent.includes('XAI_API_KEY')) {
|
|
36
|
+
provider = 'xAI';
|
|
37
|
+
}
|
|
38
|
+
else if (envContent.includes('OPENROUTER_API_KEY')) {
|
|
39
|
+
provider = 'OpenRouter';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const stat = await fs.stat(soulPath);
|
|
43
|
+
agents.push({ name: entry.name, provider, created: stat.birthtime });
|
|
44
|
+
}
|
|
45
|
+
return agents;
|
|
46
|
+
}
|
package/dist/create/generate.js
CHANGED
|
@@ -23,7 +23,7 @@ export async function scaffoldProject(projectName, provider, apiKey, soulContent
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
if (await fs.pathExists(projectDir)) {
|
|
26
|
-
callbacks.onError(`Directory ${normalizedProjectDir} already exists. Run "npx @hive-org/cli create" again with a different name.`);
|
|
26
|
+
callbacks.onError(`Directory ${normalizedProjectDir} already exists. Run "npx @hive-org/cli@latest create" again with a different name.`);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
// 1. Create directory
|
|
@@ -129,7 +129,7 @@ ${provider.envVar}="${apiKey}"
|
|
|
129
129
|
};
|
|
130
130
|
packageJson.dependencies = {
|
|
131
131
|
...packageJson.dependencies,
|
|
132
|
-
'@hive-org/sdk': '
|
|
132
|
+
'@hive-org/sdk': 'latest',
|
|
133
133
|
dotenv: '^16.0.0',
|
|
134
134
|
ai: '^6.0.71',
|
|
135
135
|
chalk: '^5.4.1',
|
package/dist/index.js
CHANGED
|
@@ -10,15 +10,15 @@ const pkg = require('../package.json');
|
|
|
10
10
|
const HELP_TEXT = `@hive-org/cli v${pkg.version}
|
|
11
11
|
|
|
12
12
|
Usage:
|
|
13
|
-
npx @hive-org/cli create [agent-name] Scaffold a new Hive agent
|
|
14
|
-
npx @hive-org/cli list List existing agents
|
|
15
|
-
npx @hive-org/cli --help Show this help message
|
|
16
|
-
npx @hive-org/cli --version Print version
|
|
13
|
+
npx @hive-org/cli@latest create [agent-name] Scaffold a new Hive agent
|
|
14
|
+
npx @hive-org/cli@latest list List existing agents
|
|
15
|
+
npx @hive-org/cli@latest --help Show this help message
|
|
16
|
+
npx @hive-org/cli@latest --version Print version
|
|
17
17
|
|
|
18
18
|
Examples:
|
|
19
|
-
npx @hive-org/cli create alpha Creates ~/.hive/agents/alpha/
|
|
20
|
-
npx @hive-org/cli create Interactive setup
|
|
21
|
-
npx @hive-org/cli list Show all agents`;
|
|
19
|
+
npx @hive-org/cli@latest create alpha Creates ~/.hive/agents/alpha/
|
|
20
|
+
npx @hive-org/cli@latest create Interactive setup
|
|
21
|
+
npx @hive-org/cli@latest list Show all agents`;
|
|
22
22
|
const command = process.argv[2];
|
|
23
23
|
const arg = process.argv[3];
|
|
24
24
|
if (command === '--version' || command === '-v') {
|
package/dist/list/ListApp.js
CHANGED
|
@@ -75,7 +75,7 @@ export function ListApp() {
|
|
|
75
75
|
return (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: colors.gray, children: "Scanning agents..." }) }));
|
|
76
76
|
}
|
|
77
77
|
if (agents.length === 0) {
|
|
78
|
-
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: colors.honey, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.white, bold: true, children: "No agents found" })] }), _jsxs(Text, { color: colors.gray, children: ["Create one with: ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli create" })] })] }));
|
|
78
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: colors.honey, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.white, bold: true, children: "No agents found" })] }), _jsxs(Text, { color: colors.gray, children: ["Create one with: ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli@latest create" })] })] }));
|
|
79
79
|
}
|
|
80
80
|
const nameWidth = Math.max(6, ...agents.map((a) => a.name.length)) + 2;
|
|
81
81
|
const providerWidth = Math.max(10, ...agents.map((a) => a.provider.length)) + 2;
|
package/dist/presets.js
CHANGED
|
@@ -339,6 +339,17 @@ ${preset.philosophy}
|
|
|
339
339
|
- Directional bias: ${preset.predictionProfile.directional_bias}
|
|
340
340
|
- Participation: ${preset.predictionProfile.participation}
|
|
341
341
|
|
|
342
|
+
## Conviction Magnitude
|
|
343
|
+
|
|
344
|
+
Your conviction number is a predicted % price change. Use the full range for your style:
|
|
345
|
+
|
|
346
|
+
- conservative: ±0.5% to ±3% (only exceed on extreme signals)
|
|
347
|
+
- moderate: ±1% to ±6%
|
|
348
|
+
- bold: ±3% to ±12%
|
|
349
|
+
- degen: ±5% to ±25% (go big or skip)
|
|
350
|
+
|
|
351
|
+
You are "${preset.predictionProfile.conviction_style}" — stay within that range but VARY your numbers. Never default to the same value repeatedly. A 0.8% call is just as valid as a 5.3% call if the signal warrants it.
|
|
352
|
+
|
|
342
353
|
## Sector Focus
|
|
343
354
|
|
|
344
355
|
- Primary: ${preset.sectorPrimary}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
3
|
+
import { Box, Text, useApp, useInput } from 'ink';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { colors, symbols, border } from '../theme.js';
|
|
8
|
+
import { scanAgents } from '../agents.js';
|
|
9
|
+
const STATUS_COLORS = {
|
|
10
|
+
spawning: colors.honey,
|
|
11
|
+
running: colors.green,
|
|
12
|
+
exited: colors.grayDim,
|
|
13
|
+
errored: colors.red,
|
|
14
|
+
};
|
|
15
|
+
const STATUS_SYMBOLS = {
|
|
16
|
+
spawning: symbols.diamondOpen,
|
|
17
|
+
running: symbols.dot,
|
|
18
|
+
exited: '\u25CB', // ○
|
|
19
|
+
errored: symbols.cross,
|
|
20
|
+
};
|
|
21
|
+
const MAX_RECENT_LINES = 10;
|
|
22
|
+
const FORCE_KILL_TIMEOUT_MS = 10_000;
|
|
23
|
+
export function StartApp() {
|
|
24
|
+
const { exit } = useApp();
|
|
25
|
+
const [agents, setAgents] = useState([]);
|
|
26
|
+
const [recentLines, setRecentLines] = useState([]);
|
|
27
|
+
const [phase, setPhase] = useState('scanning');
|
|
28
|
+
const [error, setError] = useState(null);
|
|
29
|
+
const childrenRef = useRef(new Map());
|
|
30
|
+
const shuttingDownRef = useRef(false);
|
|
31
|
+
const lineIdRef = useRef(0);
|
|
32
|
+
const updateAgent = useCallback((name, update) => {
|
|
33
|
+
setAgents((prev) => prev.map((a) => (a.name === name ? { ...a, ...update } : a)));
|
|
34
|
+
}, []);
|
|
35
|
+
const pushRecentLine = useCallback((name, text) => {
|
|
36
|
+
const id = lineIdRef.current++;
|
|
37
|
+
setRecentLines((prev) => {
|
|
38
|
+
const next = [...prev, { id, name, text }];
|
|
39
|
+
if (next.length > MAX_RECENT_LINES) {
|
|
40
|
+
return next.slice(next.length - MAX_RECENT_LINES);
|
|
41
|
+
}
|
|
42
|
+
return next;
|
|
43
|
+
});
|
|
44
|
+
}, []);
|
|
45
|
+
const shutdown = useCallback(async () => {
|
|
46
|
+
if (shuttingDownRef.current) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
shuttingDownRef.current = true;
|
|
50
|
+
setPhase('stopping');
|
|
51
|
+
const children = Array.from(childrenRef.current.values());
|
|
52
|
+
// Register exit listeners BEFORE sending signals to avoid race condition
|
|
53
|
+
const waitForAll = children.map((child) => new Promise((resolve) => {
|
|
54
|
+
if (child.exitCode !== null) {
|
|
55
|
+
resolve();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
child.on('exit', () => resolve());
|
|
59
|
+
}));
|
|
60
|
+
for (const child of children) {
|
|
61
|
+
child.kill('SIGTERM');
|
|
62
|
+
}
|
|
63
|
+
const forceKillTimer = setTimeout(() => {
|
|
64
|
+
for (const child of Array.from(childrenRef.current.values())) {
|
|
65
|
+
child.kill('SIGKILL');
|
|
66
|
+
}
|
|
67
|
+
}, FORCE_KILL_TIMEOUT_MS);
|
|
68
|
+
await Promise.all(waitForAll);
|
|
69
|
+
clearTimeout(forceKillTimer);
|
|
70
|
+
setPhase('done');
|
|
71
|
+
exit();
|
|
72
|
+
}, [exit]);
|
|
73
|
+
useInput((_input, key) => {
|
|
74
|
+
if (key.ctrl && _input === 'c') {
|
|
75
|
+
void shutdown();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const handleStreamData = useCallback((agentName, bufferRef) => {
|
|
79
|
+
return (chunk) => {
|
|
80
|
+
bufferRef.current += chunk.toString();
|
|
81
|
+
const lines = bufferRef.current.split('\n');
|
|
82
|
+
bufferRef.current = lines.pop() ?? '';
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (trimmed.length === 0) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
updateAgent(agentName, { status: 'running', lastLine: trimmed });
|
|
89
|
+
pushRecentLine(agentName, trimmed);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}, [updateAgent, pushRecentLine]);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
const run = async () => {
|
|
95
|
+
try {
|
|
96
|
+
const discovered = await scanAgents();
|
|
97
|
+
if (discovered.length === 0) {
|
|
98
|
+
setError('No agents found in ~/.hive/agents/');
|
|
99
|
+
setPhase('done');
|
|
100
|
+
exit();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const agentsDir = path.join(os.homedir(), '.hive', 'agents');
|
|
104
|
+
const initial = discovered.map((a) => ({
|
|
105
|
+
name: a.name,
|
|
106
|
+
status: 'spawning',
|
|
107
|
+
exitCode: null,
|
|
108
|
+
lastLine: '',
|
|
109
|
+
}));
|
|
110
|
+
setAgents(initial);
|
|
111
|
+
setPhase('running');
|
|
112
|
+
for (const agent of discovered) {
|
|
113
|
+
const agentDir = path.join(agentsDir, agent.name);
|
|
114
|
+
const child = spawn('npm', ['start'], {
|
|
115
|
+
cwd: agentDir,
|
|
116
|
+
stdio: 'pipe',
|
|
117
|
+
});
|
|
118
|
+
childrenRef.current.set(agent.name, child);
|
|
119
|
+
const stdoutBuffer = { current: '' };
|
|
120
|
+
const stderrBuffer = { current: '' };
|
|
121
|
+
child.stdout?.on('data', handleStreamData(agent.name, stdoutBuffer));
|
|
122
|
+
child.stderr?.on('data', handleStreamData(agent.name, stderrBuffer));
|
|
123
|
+
child.on('error', (err) => {
|
|
124
|
+
updateAgent(agent.name, { status: 'errored', lastLine: err.message });
|
|
125
|
+
childrenRef.current.delete(agent.name);
|
|
126
|
+
});
|
|
127
|
+
child.on('exit', (code) => {
|
|
128
|
+
// Flush remaining buffered output
|
|
129
|
+
const remainingStdout = stdoutBuffer.current.trim();
|
|
130
|
+
if (remainingStdout.length > 0) {
|
|
131
|
+
pushRecentLine(agent.name, remainingStdout);
|
|
132
|
+
}
|
|
133
|
+
const remainingStderr = stderrBuffer.current.trim();
|
|
134
|
+
if (remainingStderr.length > 0) {
|
|
135
|
+
pushRecentLine(agent.name, remainingStderr);
|
|
136
|
+
}
|
|
137
|
+
const exitCode = code ?? 1;
|
|
138
|
+
const status = exitCode === 0 ? 'exited' : 'errored';
|
|
139
|
+
updateAgent(agent.name, { status, exitCode });
|
|
140
|
+
childrenRef.current.delete(agent.name);
|
|
141
|
+
if (childrenRef.current.size === 0 && !shuttingDownRef.current) {
|
|
142
|
+
setPhase('done');
|
|
143
|
+
exit();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
150
|
+
setError(`Failed to scan agents: ${message}`);
|
|
151
|
+
setPhase('done');
|
|
152
|
+
exit();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
void run();
|
|
156
|
+
return () => {
|
|
157
|
+
void shutdown();
|
|
158
|
+
};
|
|
159
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
160
|
+
}, []);
|
|
161
|
+
if (error) {
|
|
162
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: colors.honey, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.red, children: error })] }), _jsxs(Text, { color: colors.gray, children: ["Create agents with: ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli@latest create" })] })] }));
|
|
163
|
+
}
|
|
164
|
+
if (phase === 'scanning') {
|
|
165
|
+
return (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: colors.gray, children: "Scanning agents..." }) }));
|
|
166
|
+
}
|
|
167
|
+
const runningCount = agents.filter((a) => a.status === 'running').length;
|
|
168
|
+
const spawningCount = agents.filter((a) => a.status === 'spawning').length;
|
|
169
|
+
const nameWidth = Math.max(16, ...agents.map((a) => a.name.length + 2));
|
|
170
|
+
const statusLabel = phase === 'stopping'
|
|
171
|
+
? 'shutting down...'
|
|
172
|
+
: `${runningCount} running${spawningCount > 0 ? `, ${spawningCount} starting` : ''}`;
|
|
173
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: colors.honey, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.white, bold: true, children: "Hive Swarm" }), _jsxs(Text, { color: colors.gray, children: [" ", border.horizontal, " ", statusLabel] })] }), agents.map((agent) => {
|
|
174
|
+
const statusColor = STATUS_COLORS[agent.status];
|
|
175
|
+
const statusSymbol = STATUS_SYMBOLS[agent.status];
|
|
176
|
+
const statusText = agent.status === 'exited' || agent.status === 'errored'
|
|
177
|
+
? `${agent.status} (${agent.exitCode})`
|
|
178
|
+
: agent.status;
|
|
179
|
+
const truncatedLine = agent.lastLine.length > 60
|
|
180
|
+
? agent.lastLine.slice(0, 57) + '...'
|
|
181
|
+
: agent.lastLine;
|
|
182
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: statusColor, children: ` ${statusSymbol} ` }), _jsx(Text, { color: colors.white, children: agent.name.padEnd(nameWidth) }), _jsx(Text, { color: statusColor, children: statusText.padEnd(12) }), _jsx(Text, { color: colors.grayDim, children: truncatedLine })] }, agent.name));
|
|
183
|
+
}), recentLines.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.grayDim, children: [border.horizontal.repeat(3), " Recent Activity ", border.horizontal.repeat(3)] }) }), recentLines.map((line) => {
|
|
184
|
+
const truncatedText = line.text.length > 60
|
|
185
|
+
? line.text.slice(0, 57) + '...'
|
|
186
|
+
: line.text;
|
|
187
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [' ', line.name.padEnd(nameWidth)] }), _jsxs(Text, { color: colors.grayDim, children: [border.vertical, " "] }), _jsx(Text, { color: colors.gray, children: truncatedText })] }, line.id));
|
|
188
|
+
})] })), phase === 'stopping' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.honey, children: "Sending SIGTERM to all agents..." }) }))] }));
|
|
189
|
+
}
|
package/package.json
CHANGED
package/templates/analysis.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const predictionSchema = z.object({
|
|
|
29
29
|
.number()
|
|
30
30
|
.nullable()
|
|
31
31
|
.describe(
|
|
32
|
-
'Predicted percent price change over the next 3 hours, up to one decimal place
|
|
32
|
+
'Predicted percent price change over the next 3 hours, up to one decimal place. Use the FULL range based on signal strength: tiny signals ±0.1-1.0, moderate ±1.5-5.0, strong ±5.0-12.0, extreme ±12.0-25.0. Negative for bearish. 0 if neutral. null if skipping. VARY your predictions — do NOT default to the same number repeatedly.',
|
|
33
33
|
),
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -135,6 +135,7 @@ export function useAgent(): UseAgentState & UseAgentActions {
|
|
|
135
135
|
const agent = new HiveAgent(baseUrl, {
|
|
136
136
|
name: config.name,
|
|
137
137
|
avatarUrl: config.avatarUrl,
|
|
138
|
+
bio: config.bio ?? undefined,
|
|
138
139
|
predictionProfile: config.predictionProfile,
|
|
139
140
|
pollIntervalMs: 5000,
|
|
140
141
|
pollLimit: 20,
|
package/templates/prompt.ts
CHANGED
|
@@ -154,7 +154,15 @@ ${threadText}
|
|
|
154
154
|
"""
|
|
155
155
|
|
|
156
156
|
Give your take in character and a conviction number.
|
|
157
|
-
Conviction: predicted % price change from the snapshot price ($${priceOnFetch}) over the 3 hours following signal time (${timestamp}), up to one decimal. Positive = up, negative = down. 0 = neutral
|
|
157
|
+
Conviction: predicted % price change from the snapshot price ($${priceOnFetch}) over the 3 hours following signal time (${timestamp}), up to one decimal. Positive = up, negative = down. 0 = neutral.
|
|
158
|
+
|
|
159
|
+
Conviction calibration — match signal strength to magnitude:
|
|
160
|
+
- Routine ecosystem update, minor partnership → ±0.5 to ±2.0
|
|
161
|
+
- Notable catalyst, solid metrics, growing momentum → ±2.0 to ±6.0
|
|
162
|
+
- Major protocol upgrade, big institutional entry, trend reversal → ±6.0 to ±12.0
|
|
163
|
+
- Black swan, regulatory bombshell, massive exploit → ±12.0 to ±25.0
|
|
164
|
+
|
|
165
|
+
IMPORTANT: Vary your conviction numbers. Do NOT reuse the same number across signals. Each signal has different strength — your conviction should reflect that.`;
|
|
158
166
|
|
|
159
167
|
return prompt;
|
|
160
168
|
}
|