openbroker 1.9.6 → 1.12.0
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/CHANGELOG.md +35 -0
- package/README.md +52 -0
- package/SKILL.md +57 -5
- package/bin/cli.ts +11 -0
- package/dist/auto/examples/grid.js +1 -1
- package/dist/auto/examples/mm-maker.js +4 -4
- package/dist/auto/examples/mm-spread.js +4 -4
- package/dist/core/client.d.ts +83 -32
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +214 -29
- package/dist/core/types.d.ts +22 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/utils.d.ts +29 -0
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +27 -0
- package/dist/core/ws.d.ts +5 -0
- package/dist/core/ws.d.ts.map +1 -1
- package/dist/core/ws.js +9 -7
- package/dist/guardian/cli.d.ts +2 -0
- package/dist/guardian/cli.d.ts.map +1 -0
- package/dist/guardian/cli.js +288 -0
- package/dist/guardian/engine.d.ts +107 -0
- package/dist/guardian/engine.d.ts.map +1 -0
- package/dist/guardian/engine.js +526 -0
- package/dist/guardian/rules.d.ts +35 -0
- package/dist/guardian/rules.d.ts.map +1 -0
- package/dist/guardian/rules.js +237 -0
- package/dist/guardian/telegram.d.ts +24 -0
- package/dist/guardian/telegram.d.ts.map +1 -0
- package/dist/guardian/telegram.js +125 -0
- package/dist/guardian/types.d.ts +90 -0
- package/dist/guardian/types.d.ts.map +1 -0
- package/dist/guardian/types.js +45 -0
- package/dist/lib.d.ts +10 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +6 -1
- package/dist/operations/advanced-orders.test.js +302 -13
- package/dist/operations/bracket.d.ts +35 -5
- package/dist/operations/bracket.d.ts.map +1 -1
- package/dist/operations/bracket.js +238 -64
- package/dist/operations/cancel.js +4 -1
- package/dist/operations/chase.d.ts +4 -2
- package/dist/operations/chase.d.ts.map +1 -1
- package/dist/operations/chase.js +61 -24
- package/dist/operations/scale.d.ts.map +1 -1
- package/dist/operations/scale.js +10 -1
- package/dist/operations/set-tpsl.js +69 -57
- package/dist/operations/trigger-order.js +18 -6
- package/dist/operations/twap.js +13 -1
- package/package.json +2 -2
- package/scripts/auto/examples/grid.ts +1 -1
- package/scripts/auto/examples/mm-maker.ts +4 -4
- package/scripts/auto/examples/mm-spread.ts +4 -4
- package/scripts/core/client.ts +278 -33
- package/scripts/core/types.ts +23 -0
- package/scripts/core/utils.ts +37 -0
- package/scripts/core/ws.ts +9 -6
- package/scripts/guardian/cli.ts +322 -0
- package/scripts/guardian/engine.ts +614 -0
- package/scripts/guardian/rules.ts +265 -0
- package/scripts/guardian/telegram.ts +147 -0
- package/scripts/guardian/types.ts +160 -0
- package/scripts/lib.ts +36 -0
- package/scripts/operations/advanced-orders.test.ts +347 -14
- package/scripts/operations/bracket.ts +252 -73
- package/scripts/operations/cancel.ts +4 -1
- package/scripts/operations/chase.ts +61 -26
- package/scripts/operations/scale.ts +10 -1
- package/scripts/operations/set-tpsl.ts +62 -57
- package/scripts/operations/trigger-order.ts +20 -6
- package/scripts/operations/twap.ts +14 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Guardian rule engine — pure arithmetic over in-memory target + price state,
|
|
2
|
+
// zero API cost per tick. Ported 1:1 from openbroker-copilot src/watcher/risk.ts
|
|
3
|
+
// (same thresholds, hysteresis, and cooldowns); position lifecycle events are
|
|
4
|
+
// emitted by the engine's snapshot diff, not here. Alerts are suppressed while
|
|
5
|
+
// the price feed is stale.
|
|
6
|
+
import { DEFAULT_GUARDIAN_THRESHOLDS } from './types.js';
|
|
7
|
+
const HOURS_PER_YEAR = 24 * 365;
|
|
8
|
+
export class GuardianRiskEngine {
|
|
9
|
+
prices;
|
|
10
|
+
onAlert;
|
|
11
|
+
thresholds;
|
|
12
|
+
/** address:coin:threshold → state */
|
|
13
|
+
liqState = new Map();
|
|
14
|
+
/** address → state */
|
|
15
|
+
marginState = new Map();
|
|
16
|
+
/** address:coin → alerted */
|
|
17
|
+
tpslState = new Set();
|
|
18
|
+
/** address:oid → last fired */
|
|
19
|
+
staleOrderState = new Map();
|
|
20
|
+
/** address:coin → { since, firedAt } */
|
|
21
|
+
fundingState = new Map();
|
|
22
|
+
evaluations = 0;
|
|
23
|
+
constructor(prices, onAlert, thresholds) {
|
|
24
|
+
this.prices = prices;
|
|
25
|
+
this.onAlert = onAlert;
|
|
26
|
+
this.thresholds = { ...DEFAULT_GUARDIAN_THRESHOLDS, ...thresholds };
|
|
27
|
+
}
|
|
28
|
+
getStats() {
|
|
29
|
+
return { evaluations: this.evaluations };
|
|
30
|
+
}
|
|
31
|
+
/** One evaluation pass over the given targets. */
|
|
32
|
+
tick(targets, now = Date.now()) {
|
|
33
|
+
const stale = this.prices.stale;
|
|
34
|
+
for (const target of targets) {
|
|
35
|
+
if (!target.seeded)
|
|
36
|
+
continue;
|
|
37
|
+
this.updateLiqDistance(target);
|
|
38
|
+
if (stale)
|
|
39
|
+
continue; // keep distances fresh, but never alert on stale prices
|
|
40
|
+
this.evaluations++;
|
|
41
|
+
this.evalLiqProximity(target, now);
|
|
42
|
+
this.evalMarginUsage(target, now);
|
|
43
|
+
this.evalNoTpsl(target, now);
|
|
44
|
+
this.evalStaleOrders(target, now);
|
|
45
|
+
this.evalFundingBleed(target, now);
|
|
46
|
+
this.gcClosedPositions(target);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Distance = (mark − liqPx) / mark, signed by side. */
|
|
50
|
+
liqDistance(target, coin) {
|
|
51
|
+
const pos = target.positions.get(coin);
|
|
52
|
+
if (!pos || pos.liquidationPx === null || pos.liquidationPx <= 0)
|
|
53
|
+
return null;
|
|
54
|
+
const entry = this.prices.get(coin);
|
|
55
|
+
const mark = entry?.mark ?? entry?.mid;
|
|
56
|
+
if (!mark || mark <= 0)
|
|
57
|
+
return null;
|
|
58
|
+
return pos.side === 'long'
|
|
59
|
+
? (mark - pos.liquidationPx) / mark
|
|
60
|
+
: (pos.liquidationPx - mark) / mark;
|
|
61
|
+
}
|
|
62
|
+
updateLiqDistance(target) {
|
|
63
|
+
let min = null;
|
|
64
|
+
for (const coin of target.positions.keys()) {
|
|
65
|
+
const d = this.liqDistance(target, coin);
|
|
66
|
+
if (d !== null && (min === null || d < min))
|
|
67
|
+
min = d;
|
|
68
|
+
}
|
|
69
|
+
target.minLiqDist = min;
|
|
70
|
+
}
|
|
71
|
+
evalLiqProximity(target, now) {
|
|
72
|
+
const t = this.thresholds;
|
|
73
|
+
for (const [coin, pos] of target.positions) {
|
|
74
|
+
const dist = this.liqDistance(target, coin);
|
|
75
|
+
if (dist === null)
|
|
76
|
+
continue;
|
|
77
|
+
for (const { pct, severity } of t.liqThresholds) {
|
|
78
|
+
const key = `${target.address}:${coin}:${pct}`;
|
|
79
|
+
const state = this.liqState.get(key) ?? { firedAt: 0, armed: true };
|
|
80
|
+
if (!state.armed && dist >= pct * t.liqRearmFactor) {
|
|
81
|
+
state.armed = true; // hysteresis re-arm
|
|
82
|
+
}
|
|
83
|
+
if (state.armed && dist <= pct && now - state.firedAt >= t.liqCooldownMs) {
|
|
84
|
+
state.armed = false;
|
|
85
|
+
state.firedAt = now;
|
|
86
|
+
const entry = this.prices.get(coin);
|
|
87
|
+
const mark = entry?.mark ?? entry?.mid;
|
|
88
|
+
this.onAlert({
|
|
89
|
+
time: now,
|
|
90
|
+
address: target.address,
|
|
91
|
+
rule: 'liq_proximity',
|
|
92
|
+
coin,
|
|
93
|
+
severity,
|
|
94
|
+
dedupKey: String(pct),
|
|
95
|
+
message: `Liquidation risk on ${coin}: mark $${mark} is ${(dist * 100).toFixed(2)}% from ` +
|
|
96
|
+
`liquidation price $${pos.liquidationPx} (${pos.side}, ${pos.leverage}x)`,
|
|
97
|
+
payload: { coin, distancePct: dist * 100, thresholdPct: pct * 100, mark, liquidationPx: pos.liquidationPx, side: pos.side },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
this.liqState.set(key, state);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
evalMarginUsage(target, now) {
|
|
105
|
+
const t = this.thresholds;
|
|
106
|
+
const key = target.address;
|
|
107
|
+
const state = this.marginState.get(key) ?? { firedAt: 0, armed: true };
|
|
108
|
+
if (!state.armed && target.marginUsedPct < t.marginRearmPct)
|
|
109
|
+
state.armed = true;
|
|
110
|
+
if (state.armed && target.marginUsedPct > t.marginThresholdPct && now - state.firedAt >= t.marginCooldownMs) {
|
|
111
|
+
state.armed = false;
|
|
112
|
+
state.firedAt = now;
|
|
113
|
+
this.onAlert({
|
|
114
|
+
time: now,
|
|
115
|
+
address: target.address,
|
|
116
|
+
rule: 'margin_usage',
|
|
117
|
+
coin: null,
|
|
118
|
+
severity: 'warning',
|
|
119
|
+
dedupKey: String(t.marginThresholdPct),
|
|
120
|
+
message: `Margin usage at ${target.marginUsedPct.toFixed(1)}% of equity ($${target.equity.toFixed(2)})`,
|
|
121
|
+
payload: { marginUsedPct: target.marginUsedPct, equity: target.equity, thresholdPct: t.marginThresholdPct },
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
this.marginState.set(key, state);
|
|
125
|
+
}
|
|
126
|
+
evalNoTpsl(target, now) {
|
|
127
|
+
if (target.lastOrdersAt === null || target.orders === null)
|
|
128
|
+
return; // no order data yet
|
|
129
|
+
for (const [coin, pos] of target.positions) {
|
|
130
|
+
const key = `${target.address}:${coin}`;
|
|
131
|
+
const openedAt = target.firstSeen.get(coin) ?? now;
|
|
132
|
+
const hasProtection = target.orders.some((o) => o.coin === coin && o.isTrigger && o.reduceOnly);
|
|
133
|
+
if (hasProtection) {
|
|
134
|
+
this.tpslState.delete(key);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (this.tpslState.has(key))
|
|
138
|
+
continue;
|
|
139
|
+
if (now - openedAt < this.thresholds.noTpslAfterMs)
|
|
140
|
+
continue;
|
|
141
|
+
this.tpslState.add(key);
|
|
142
|
+
this.onAlert({
|
|
143
|
+
time: now,
|
|
144
|
+
address: target.address,
|
|
145
|
+
rule: 'no_tpsl',
|
|
146
|
+
coin,
|
|
147
|
+
severity: 'info',
|
|
148
|
+
dedupKey: 'no_tpsl',
|
|
149
|
+
message: `${coin} ${pos.side} position has been open ${Math.round((now - openedAt) / 60_000)} min with no TP/SL orders`,
|
|
150
|
+
payload: { coin, side: pos.side, size: pos.szi, openMinutes: Math.round((now - openedAt) / 60_000) },
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
evalStaleOrders(target, now) {
|
|
155
|
+
if (!target.orders)
|
|
156
|
+
return;
|
|
157
|
+
const t = this.thresholds;
|
|
158
|
+
for (const order of target.orders) {
|
|
159
|
+
if (order.isTrigger)
|
|
160
|
+
continue; // resting limit orders only
|
|
161
|
+
if (now - order.timestamp < t.staleOrderAgeMs)
|
|
162
|
+
continue;
|
|
163
|
+
const entry = this.prices.get(order.coin);
|
|
164
|
+
const mid = entry?.mid ?? entry?.mark;
|
|
165
|
+
const limitPx = parseFloat(order.limitPx);
|
|
166
|
+
if (!mid || mid <= 0 || !Number.isFinite(limitPx))
|
|
167
|
+
continue;
|
|
168
|
+
const dist = Math.abs(limitPx - mid) / mid;
|
|
169
|
+
if (dist <= t.staleOrderDist)
|
|
170
|
+
continue;
|
|
171
|
+
const key = `${target.address}:${order.oid}`;
|
|
172
|
+
const lastFired = this.staleOrderState.get(key) ?? 0;
|
|
173
|
+
if (now - lastFired < t.staleOrderCooldownMs)
|
|
174
|
+
continue;
|
|
175
|
+
this.staleOrderState.set(key, now);
|
|
176
|
+
const ageH = Math.round((now - order.timestamp) / 3600_000);
|
|
177
|
+
this.onAlert({
|
|
178
|
+
time: now,
|
|
179
|
+
address: target.address,
|
|
180
|
+
rule: 'stale_order',
|
|
181
|
+
coin: order.coin,
|
|
182
|
+
severity: 'info',
|
|
183
|
+
dedupKey: String(order.oid),
|
|
184
|
+
message: `Stale ${order.side === 'B' ? 'buy' : 'sell'} order on ${order.coin}: resting ${ageH}h, ` +
|
|
185
|
+
`limit $${order.limitPx} is ${(dist * 100).toFixed(1)}% from mid $${mid}`,
|
|
186
|
+
payload: { coin: order.coin, oid: order.oid, limitPx, mid, distancePct: dist * 100, ageHours: ageH },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
evalFundingBleed(target, now) {
|
|
191
|
+
const t = this.thresholds;
|
|
192
|
+
for (const [coin, pos] of target.positions) {
|
|
193
|
+
const funding = this.prices.get(coin)?.funding;
|
|
194
|
+
const key = `${target.address}:${coin}`;
|
|
195
|
+
if (funding === null || funding === undefined)
|
|
196
|
+
continue;
|
|
197
|
+
// Longs pay when funding > 0; shorts pay when funding < 0. Funding is hourly.
|
|
198
|
+
const paying = pos.side === 'long' ? funding > 0 : funding < 0;
|
|
199
|
+
const aprPct = Math.abs(funding) * HOURS_PER_YEAR * 100;
|
|
200
|
+
if (!paying || aprPct <= t.fundingBleedAprPct) {
|
|
201
|
+
this.fundingState.delete(key);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const state = this.fundingState.get(key) ?? { since: now, firedAt: null };
|
|
205
|
+
this.fundingState.set(key, state);
|
|
206
|
+
const bleedingFor = now - state.since;
|
|
207
|
+
const cooledDown = state.firedAt === null || now - state.firedAt >= t.fundingCooldownMs;
|
|
208
|
+
if (bleedingFor >= t.fundingBleedForMs && cooledDown) {
|
|
209
|
+
state.firedAt = now;
|
|
210
|
+
this.onAlert({
|
|
211
|
+
time: now,
|
|
212
|
+
address: target.address,
|
|
213
|
+
rule: 'funding_bleed',
|
|
214
|
+
coin,
|
|
215
|
+
severity: 'warning',
|
|
216
|
+
dedupKey: 'funding_bleed',
|
|
217
|
+
message: `Funding bleed on ${coin}: paying ~${aprPct.toFixed(1)}% APR against your ${pos.side} ` +
|
|
218
|
+
`for over ${Math.round(bleedingFor / 60_000)} min`,
|
|
219
|
+
payload: { coin, side: pos.side, fundingHourly: funding, aprPct, bleedingMinutes: Math.round(bleedingFor / 60_000) },
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/** Drop per-position rule state when positions close (prevents unbounded growth). */
|
|
225
|
+
gcClosedPositions(target) {
|
|
226
|
+
for (const key of this.tpslState) {
|
|
227
|
+
const [addr, coin] = [key.slice(0, 42), key.slice(43)];
|
|
228
|
+
if (addr === target.address && coin && !target.positions.has(coin))
|
|
229
|
+
this.tpslState.delete(key);
|
|
230
|
+
}
|
|
231
|
+
for (const key of this.fundingState.keys()) {
|
|
232
|
+
const [addr, coin] = [key.slice(0, 42), key.slice(43)];
|
|
233
|
+
if (addr === target.address && coin && !target.positions.has(coin))
|
|
234
|
+
this.fundingState.delete(key);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { GuardianAlert } from './types.js';
|
|
2
|
+
export interface TelegramSettings {
|
|
3
|
+
token: string | null;
|
|
4
|
+
chatId: string | null;
|
|
5
|
+
}
|
|
6
|
+
/** Read TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID (dotenv is loaded by core/config on import). */
|
|
7
|
+
export declare function loadTelegramSettings(): TelegramSettings;
|
|
8
|
+
export declare function sendTelegramMessage(token: string, chatId: string | number, text: string): Promise<void>;
|
|
9
|
+
export declare function getTelegramBotInfo(token: string): Promise<{
|
|
10
|
+
id: number;
|
|
11
|
+
username: string;
|
|
12
|
+
}>;
|
|
13
|
+
/** Same message shape as the hosted guardian's Telegram channel. */
|
|
14
|
+
export declare function formatTelegramAlert(alert: GuardianAlert): string;
|
|
15
|
+
export declare function generateLinkCode(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Long-poll getUpdates until someone sends `/start <code>` (or the bare code)
|
|
18
|
+
* to the bot, then confirm in-chat and return the chat id. Returns null on
|
|
19
|
+
* timeout. Drains the update backlog first so old messages can't match.
|
|
20
|
+
*/
|
|
21
|
+
export declare function waitForTelegramLink(token: string, code: string, timeoutMs?: number): Promise<number | null>;
|
|
22
|
+
/** Persist a key=value into the active openbroker config file (creates it if missing). */
|
|
23
|
+
export declare function saveEnvVar(key: string, value: string): string;
|
|
24
|
+
//# sourceMappingURL=telegram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../scripts/guardian/telegram.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,8FAA8F;AAC9F,wBAAgB,oBAAoB,IAAI,gBAAgB,CAKvD;AAoBD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7G;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEjG;AAMD,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAIhE;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,SAAc,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4CxB;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiB7D"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Telegram delivery for the guardian via raw fetch — no SDK. Unlike the hosted
|
|
2
|
+
// guardian (shared bot + DB-backed link codes), the CLI uses the user's OWN bot:
|
|
3
|
+
// TELEGRAM_BOT_TOKEN comes from @BotFather, and `openbroker guardian connect`
|
|
4
|
+
// runs a one-shot getUpdates loop that captures the chat id from a
|
|
5
|
+
// `/start <code>` deep link and persists TELEGRAM_CHAT_ID to the config file.
|
|
6
|
+
//
|
|
7
|
+
// Caveat: getUpdates conflicts with any other consumer of the same bot token
|
|
8
|
+
// (including a set webhook) — use a dedicated bot for the CLI guardian.
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import crypto from 'crypto';
|
|
11
|
+
import { ensureConfigDir, getConfigPath } from '../core/config.js';
|
|
12
|
+
/** Read TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID (dotenv is loaded by core/config on import). */
|
|
13
|
+
export function loadTelegramSettings() {
|
|
14
|
+
return {
|
|
15
|
+
token: process.env.TELEGRAM_BOT_TOKEN || null,
|
|
16
|
+
chatId: process.env.TELEGRAM_CHAT_ID || null,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function api(token, method) {
|
|
20
|
+
return `https://api.telegram.org/bot${token}/${method}`;
|
|
21
|
+
}
|
|
22
|
+
async function call(token, method, body, timeoutMs = 10_000) {
|
|
23
|
+
const res = await fetch(api(token, method), {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
body: JSON.stringify(body),
|
|
27
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
28
|
+
});
|
|
29
|
+
const data = (await res.json().catch(() => null));
|
|
30
|
+
if (!res.ok || !data?.ok) {
|
|
31
|
+
throw new Error(`telegram ${method} failed: HTTP ${res.status}${data?.description ? ` — ${data.description}` : ''}`);
|
|
32
|
+
}
|
|
33
|
+
return data.result;
|
|
34
|
+
}
|
|
35
|
+
export async function sendTelegramMessage(token, chatId, text) {
|
|
36
|
+
await call(token, 'sendMessage', {
|
|
37
|
+
chat_id: chatId,
|
|
38
|
+
text,
|
|
39
|
+
parse_mode: 'HTML',
|
|
40
|
+
disable_web_page_preview: true,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export async function getTelegramBotInfo(token) {
|
|
44
|
+
return call(token, 'getMe', {});
|
|
45
|
+
}
|
|
46
|
+
function shortAddr(address) {
|
|
47
|
+
return `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
48
|
+
}
|
|
49
|
+
/** Same message shape as the hosted guardian's Telegram channel. */
|
|
50
|
+
export function formatTelegramAlert(alert) {
|
|
51
|
+
const tag = alert.severity === 'critical' ? 'CRITICAL' : alert.severity === 'warning' ? 'WARNING' : 'INFO';
|
|
52
|
+
const scope = alert.coin ? `${alert.coin} — ` : '';
|
|
53
|
+
return `<b>[${tag}]</b> ${scope}${shortAddr(alert.address)}\n${alert.message}`;
|
|
54
|
+
}
|
|
55
|
+
export function generateLinkCode() {
|
|
56
|
+
return crypto.randomBytes(4).toString('hex');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Long-poll getUpdates until someone sends `/start <code>` (or the bare code)
|
|
60
|
+
* to the bot, then confirm in-chat and return the chat id. Returns null on
|
|
61
|
+
* timeout. Drains the update backlog first so old messages can't match.
|
|
62
|
+
*/
|
|
63
|
+
export async function waitForTelegramLink(token, code, timeoutMs = 10 * 60_000) {
|
|
64
|
+
const deadline = Date.now() + timeoutMs;
|
|
65
|
+
let offset = -1; // -1 = skip backlog, start from the next incoming update
|
|
66
|
+
while (Date.now() < deadline) {
|
|
67
|
+
let updates;
|
|
68
|
+
try {
|
|
69
|
+
updates = await call(token, 'getUpdates', {
|
|
70
|
+
offset,
|
|
71
|
+
timeout: 25,
|
|
72
|
+
allowed_updates: ['message'],
|
|
73
|
+
}, 35_000);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (Date.now() >= deadline)
|
|
77
|
+
break;
|
|
78
|
+
await new Promise((r) => setTimeout(r, 3_000));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
for (const update of updates ?? []) {
|
|
82
|
+
offset = update.update_id + 1;
|
|
83
|
+
const msg = update.message;
|
|
84
|
+
const text = msg?.text?.trim();
|
|
85
|
+
if (!msg || !text)
|
|
86
|
+
continue;
|
|
87
|
+
const m = text.match(/^\/start(?:\s+(\S+))?$/);
|
|
88
|
+
const supplied = m ? m[1] : text;
|
|
89
|
+
if (supplied === code) {
|
|
90
|
+
try {
|
|
91
|
+
await sendTelegramMessage(token, msg.chat.id, 'Linked. This chat will receive OpenBroker guardian alerts (liquidation proximity, missing TP/SL, funding bleed and more).');
|
|
92
|
+
}
|
|
93
|
+
catch { /* link still succeeded */ }
|
|
94
|
+
return msg.chat.id;
|
|
95
|
+
}
|
|
96
|
+
if (m && !supplied) {
|
|
97
|
+
try {
|
|
98
|
+
await sendTelegramMessage(token, msg.chat.id, 'OpenBroker guardian bot. Run <code>openbroker guardian connect</code> in the terminal and follow the link it prints.');
|
|
99
|
+
}
|
|
100
|
+
catch { /* ignore */ }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/** Persist a key=value into the active openbroker config file (creates it if missing). */
|
|
107
|
+
export function saveEnvVar(key, value) {
|
|
108
|
+
ensureConfigDir();
|
|
109
|
+
const configPath = getConfigPath();
|
|
110
|
+
const line = `${key}=${value}`;
|
|
111
|
+
let content = '';
|
|
112
|
+
if (fs.existsSync(configPath)) {
|
|
113
|
+
content = fs.readFileSync(configPath, 'utf8');
|
|
114
|
+
}
|
|
115
|
+
const re = new RegExp(`^${key}=.*$`, 'm');
|
|
116
|
+
if (re.test(content)) {
|
|
117
|
+
content = content.replace(re, line);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
content = content.length > 0 && !content.endsWith('\n') ? `${content}\n${line}\n` : `${content}${line}\n`;
|
|
121
|
+
}
|
|
122
|
+
fs.writeFileSync(configPath, content, { mode: 0o600 });
|
|
123
|
+
process.env[key] = value;
|
|
124
|
+
return configPath;
|
|
125
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { FrontendOpenOrder } from '../core/types.js';
|
|
2
|
+
export type GuardianRuleId = 'liq_proximity' | 'no_tpsl' | 'stale_order' | 'margin_usage' | 'funding_bleed' | 'position_lifecycle';
|
|
3
|
+
export declare const GUARDIAN_RULE_IDS: GuardianRuleId[];
|
|
4
|
+
export declare const GUARDIAN_RULE_LABELS: Record<GuardianRuleId, string>;
|
|
5
|
+
export type GuardianSeverity = 'info' | 'warning' | 'critical';
|
|
6
|
+
export declare const GUARDIAN_SEVERITY_RANK: Record<GuardianSeverity, number>;
|
|
7
|
+
export interface GuardianAlert {
|
|
8
|
+
time: number;
|
|
9
|
+
address: string;
|
|
10
|
+
rule: GuardianRuleId;
|
|
11
|
+
coin: string | null;
|
|
12
|
+
severity: GuardianSeverity;
|
|
13
|
+
/** Dedup discriminator within address+rule+coin (e.g. threshold pct). */
|
|
14
|
+
dedupKey: string;
|
|
15
|
+
message: string;
|
|
16
|
+
payload: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/** Per-run alert preferences — same contract as the hosted guardian's alert_prefs. */
|
|
19
|
+
export interface GuardianPrefs {
|
|
20
|
+
/** Minimum severity to deliver. Default 'info'. */
|
|
21
|
+
minSeverity?: GuardianSeverity;
|
|
22
|
+
/** Per-rule opt-out, e.g. { position_lifecycle: false }. Default: all on. */
|
|
23
|
+
rules?: Partial<Record<GuardianRuleId, boolean>>;
|
|
24
|
+
}
|
|
25
|
+
/** Rule thresholds. Defaults mirror the hosted guardian. */
|
|
26
|
+
export interface GuardianThresholds {
|
|
27
|
+
/** Liquidation-distance tiers (fraction of mark), tightest last. */
|
|
28
|
+
liqThresholds: Array<{
|
|
29
|
+
pct: number;
|
|
30
|
+
severity: GuardianSeverity;
|
|
31
|
+
}>;
|
|
32
|
+
/** Re-arm only after moving this multiple past a fired threshold. */
|
|
33
|
+
liqRearmFactor: number;
|
|
34
|
+
liqCooldownMs: number;
|
|
35
|
+
marginThresholdPct: number;
|
|
36
|
+
marginRearmPct: number;
|
|
37
|
+
marginCooldownMs: number;
|
|
38
|
+
/** Alert when a position has no reduce-only trigger after this long. */
|
|
39
|
+
noTpslAfterMs: number;
|
|
40
|
+
staleOrderAgeMs: number;
|
|
41
|
+
/** Distance from mid (fraction) before a resting order counts as stale. */
|
|
42
|
+
staleOrderDist: number;
|
|
43
|
+
staleOrderCooldownMs: number;
|
|
44
|
+
/** Annualized funding APR (%) the position must be paying to count as bleed. */
|
|
45
|
+
fundingBleedAprPct: number;
|
|
46
|
+
/** How long the bleed must persist before alerting. */
|
|
47
|
+
fundingBleedForMs: number;
|
|
48
|
+
fundingCooldownMs: number;
|
|
49
|
+
}
|
|
50
|
+
export declare const DEFAULT_GUARDIAN_THRESHOLDS: GuardianThresholds;
|
|
51
|
+
export interface GuardianPositionSnap {
|
|
52
|
+
coin: string;
|
|
53
|
+
szi: number;
|
|
54
|
+
side: 'long' | 'short';
|
|
55
|
+
entryPx: number | null;
|
|
56
|
+
positionValue: number;
|
|
57
|
+
unrealizedPnl: number;
|
|
58
|
+
liquidationPx: number | null;
|
|
59
|
+
leverage: number;
|
|
60
|
+
marginUsed: number;
|
|
61
|
+
}
|
|
62
|
+
/** In-memory state per watched address. */
|
|
63
|
+
export interface GuardianTargetState {
|
|
64
|
+
address: string;
|
|
65
|
+
positions: Map<string, GuardianPositionSnap>;
|
|
66
|
+
equity: number;
|
|
67
|
+
marginUsedPct: number;
|
|
68
|
+
seeded: boolean;
|
|
69
|
+
lastStateAt: number | null;
|
|
70
|
+
orders: FrontendOpenOrder[] | null;
|
|
71
|
+
lastOrdersAt: number | null;
|
|
72
|
+
/** First time each open position (coin) was observed — for the no-TP/SL rule. */
|
|
73
|
+
firstSeen: Map<string, number>;
|
|
74
|
+
/** Min liquidation distance (fraction) across open positions; set by the risk engine. */
|
|
75
|
+
minLiqDist: number | null;
|
|
76
|
+
}
|
|
77
|
+
/** Live price entry maintained by the guardian's price feed. */
|
|
78
|
+
export interface GuardianPriceEntry {
|
|
79
|
+
mid: number | null;
|
|
80
|
+
mark: number | null;
|
|
81
|
+
/** Hourly funding rate (decimal, e.g. 0.0000125). */
|
|
82
|
+
funding: number | null;
|
|
83
|
+
updatedAt: number;
|
|
84
|
+
}
|
|
85
|
+
/** Narrow price view the rule engine reads — tests can pass plain fakes. */
|
|
86
|
+
export interface GuardianPriceView {
|
|
87
|
+
get(coin: string): GuardianPriceEntry | undefined;
|
|
88
|
+
readonly stale: boolean;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../scripts/guardian/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Guardian — read-only position risk monitoring for Hyperliquid addresses.
|
|
2
|
+
// CLI/library port of the hosted copilot guardian (openbroker-copilot
|
|
3
|
+
// src/watcher): same six rules, same default thresholds, same severity
|
|
4
|
+
// contract, minus the multi-tenant DB. Alerts are delivered to the console,
|
|
5
|
+
// Telegram, an OpenClaw agent hook, and/or an onAlert callback.
|
|
6
|
+
export const GUARDIAN_RULE_IDS = [
|
|
7
|
+
'liq_proximity',
|
|
8
|
+
'no_tpsl',
|
|
9
|
+
'stale_order',
|
|
10
|
+
'margin_usage',
|
|
11
|
+
'funding_bleed',
|
|
12
|
+
'position_lifecycle',
|
|
13
|
+
];
|
|
14
|
+
export const GUARDIAN_RULE_LABELS = {
|
|
15
|
+
liq_proximity: 'Liquidation proximity',
|
|
16
|
+
no_tpsl: 'No TP/SL protection',
|
|
17
|
+
stale_order: 'Stale limit orders',
|
|
18
|
+
margin_usage: 'High margin usage',
|
|
19
|
+
funding_bleed: 'Funding bleed',
|
|
20
|
+
position_lifecycle: 'Position open / close / resize',
|
|
21
|
+
};
|
|
22
|
+
export const GUARDIAN_SEVERITY_RANK = {
|
|
23
|
+
info: 0,
|
|
24
|
+
warning: 1,
|
|
25
|
+
critical: 2,
|
|
26
|
+
};
|
|
27
|
+
export const DEFAULT_GUARDIAN_THRESHOLDS = {
|
|
28
|
+
liqThresholds: [
|
|
29
|
+
{ pct: 0.10, severity: 'warning' },
|
|
30
|
+
{ pct: 0.05, severity: 'critical' },
|
|
31
|
+
{ pct: 0.02, severity: 'critical' },
|
|
32
|
+
],
|
|
33
|
+
liqRearmFactor: 1.5,
|
|
34
|
+
liqCooldownMs: 30 * 60_000,
|
|
35
|
+
marginThresholdPct: 80,
|
|
36
|
+
marginRearmPct: 72,
|
|
37
|
+
marginCooldownMs: 60 * 60_000,
|
|
38
|
+
noTpslAfterMs: 15 * 60_000,
|
|
39
|
+
staleOrderAgeMs: 12 * 3600_000,
|
|
40
|
+
staleOrderDist: 0.03,
|
|
41
|
+
staleOrderCooldownMs: 12 * 3600_000,
|
|
42
|
+
fundingBleedAprPct: 15,
|
|
43
|
+
fundingBleedForMs: 60 * 60_000,
|
|
44
|
+
fundingCooldownMs: 6 * 3600_000,
|
|
45
|
+
};
|
package/dist/lib.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { HyperliquidClient, getClient, } from './core/client.js';
|
|
|
2
2
|
export { WebSocketManager, getWebSocket, resetWebSocket, } from './core/ws.js';
|
|
3
3
|
export type { WsEventMap, WsEventType, WsEventHandler } from './core/ws.js';
|
|
4
4
|
export { loadConfig, isConfigured, getNetwork, isMainnet, ensureConfigDir, getConfigPath, GLOBAL_CONFIG_DIR, GLOBAL_ENV_PATH, OPEN_BROKER_BUILDER_ADDRESS, } from './core/config.js';
|
|
5
|
-
export { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate, parseArgs, getSlippagePrice, getTimestampMs, generateCloid, orderToWire, checkBuilderFeeApproval, } from './core/utils.js';
|
|
5
|
+
export { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate, parseArgs, getSlippagePrice, getTimestampMs, generateCloid, orderToWire, checkBuilderFeeApproval, MIN_ORDER_NOTIONAL_USD, parseOrderStatus, } from './core/utils.js';
|
|
6
|
+
export type { ParsedOrderStatus } from './core/utils.js';
|
|
6
7
|
export type * from './core/types.js';
|
|
7
8
|
export { runBracket } from './operations/bracket.js';
|
|
8
9
|
export type { BracketOptions, BracketResult } from './operations/bracket.js';
|
|
@@ -17,4 +18,12 @@ export { GuardrailViolation, CLIENT_WRITE_METHODS, canonicalMarket, validateAuto
|
|
|
17
18
|
export type { GuardrailedClientOptions } from './auto/guardrails.js';
|
|
18
19
|
export { registerAutomation, unregisterAutomation, cleanRegistry, getAutomationsToRestart, markAutomationError, } from './auto/registry.js';
|
|
19
20
|
export type * from './auto/types.js';
|
|
21
|
+
export { startGuardian, Guardian } from './guardian/engine.js';
|
|
22
|
+
export type { GuardianOptions, GuardianHandle, GuardianStats, GuardianTelegramChannel, GuardianAgentHookChannel, } from './guardian/engine.js';
|
|
23
|
+
export { GuardianRiskEngine } from './guardian/rules.js';
|
|
24
|
+
export type { GuardianAlertHandler } from './guardian/rules.js';
|
|
25
|
+
export { sendTelegramMessage, getTelegramBotInfo, formatTelegramAlert, loadTelegramSettings, waitForTelegramLink, generateLinkCode, saveEnvVar, } from './guardian/telegram.js';
|
|
26
|
+
export type { TelegramSettings } from './guardian/telegram.js';
|
|
27
|
+
export { GUARDIAN_RULE_IDS, GUARDIAN_RULE_LABELS, GUARDIAN_SEVERITY_RANK, DEFAULT_GUARDIAN_THRESHOLDS, } from './guardian/types.js';
|
|
28
|
+
export type * from './guardian/types.js';
|
|
20
29
|
//# sourceMappingURL=lib.d.ts.map
|
package/dist/lib.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../scripts/lib.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,iBAAiB,EACjB,SAAS,GACV,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,SAAS,EACT,KAAK,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,WAAW,EACX,uBAAuB,
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../scripts/lib.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,iBAAiB,EACjB,SAAS,GACV,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,UAAU,EACV,SAAS,EACT,KAAK,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,mBAAmB,iBAAiB,CAAC;AAIrC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7E,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAInF,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,mBAAmB,iBAAiB,CAAC;AAIrC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC/D,YAAY,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,GACX,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,mBAAmB,qBAAqB,CAAC"}
|
package/dist/lib.js
CHANGED
|
@@ -11,7 +11,7 @@ export { HyperliquidClient, getClient, } from './core/client.js';
|
|
|
11
11
|
// REST-polling. `userFill` / `orderUpdate` are emitted per own-order event; see WsEventMap.
|
|
12
12
|
export { WebSocketManager, getWebSocket, resetWebSocket, } from './core/ws.js';
|
|
13
13
|
export { loadConfig, isConfigured, getNetwork, isMainnet, ensureConfigDir, getConfigPath, GLOBAL_CONFIG_DIR, GLOBAL_ENV_PATH, OPEN_BROKER_BUILDER_ADDRESS, } from './core/config.js';
|
|
14
|
-
export { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate, parseArgs, getSlippagePrice, getTimestampMs, generateCloid, orderToWire, checkBuilderFeeApproval, } from './core/utils.js';
|
|
14
|
+
export { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate, parseArgs, getSlippagePrice, getTimestampMs, generateCloid, orderToWire, checkBuilderFeeApproval, MIN_ORDER_NOTIONAL_USD, parseOrderStatus, } from './core/utils.js';
|
|
15
15
|
// ── Operations (in-process callable) ────────────────────────────────
|
|
16
16
|
export { runBracket } from './operations/bracket.js';
|
|
17
17
|
export { runChase } from './operations/chase.js';
|
|
@@ -21,3 +21,8 @@ export { startAutomation, getRunningAutomations, getAutomation, getRegisteredAut
|
|
|
21
21
|
export { resolveScriptPath, resolveExamplePath, listAutomations, listExamples, loadExampleConfigs, ensureAutomationsDir, loadAutomation, } from './auto/loader.js';
|
|
22
22
|
export { GuardrailViolation, CLIENT_WRITE_METHODS, canonicalMarket, validateAutomationGuardrails, resolveAutomationGuardrails, createGuardrailedClient, } from './auto/guardrails.js';
|
|
23
23
|
export { registerAutomation, unregisterAutomation, cleanRegistry, getAutomationsToRestart, markAutomationError, } from './auto/registry.js';
|
|
24
|
+
// ── Guardian (read-only position risk monitoring) ───────────────────
|
|
25
|
+
export { startGuardian, Guardian } from './guardian/engine.js';
|
|
26
|
+
export { GuardianRiskEngine } from './guardian/rules.js';
|
|
27
|
+
export { sendTelegramMessage, getTelegramBotInfo, formatTelegramAlert, loadTelegramSettings, waitForTelegramLink, generateLinkCode, saveEnvVar, } from './guardian/telegram.js';
|
|
28
|
+
export { GUARDIAN_RULE_IDS, GUARDIAN_RULE_LABELS, GUARDIAN_SEVERITY_RANK, DEFAULT_GUARDIAN_THRESHOLDS, } from './guardian/types.js';
|