beast-agent 2.54.0 → 2.55.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/package.json +1 -1
- package/src/agent/engine.js +4 -2
- package/src/agent/financetools.js +35 -3
- package/src/agent/finwatch.js +13 -1
- package/src/agent/watchers.js +13 -4
- package/src/main.js +71 -10
- package/src/preload.js +2 -0
- package/src/renderer/index.html +4 -0
- package/src/renderer/renderer.js +90 -5
- package/src/renderer/style.css +2 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -2009,8 +2009,9 @@ class Engine {
|
|
|
2009
2009
|
modeBlock2 + '\n' +
|
|
2010
2010
|
(roleBlockFull ? roleBlockFull + '\n' : '') +
|
|
2011
2011
|
(teamLine || '') +
|
|
2012
|
-
'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (TÜM TİPLER: buy/sell market + buy/sell limit/stop), mt5_close (kapat), mt5_modify (SL/TP), mt5_pending (TÜM TİPLER: buy_limit/sell_limit/buy_stop/sell_stop bekleyen + buy_market/sell_market anlık), mt5_cancel (emir iptal).\n' +
|
|
2012
|
+
'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_alerts (fiyat alarmı: modu SEN seçersin — once/repeat), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (TÜM TİPLER: buy/sell market + buy/sell limit/stop), mt5_close (kapat), mt5_modify (SL/TP), mt5_pending (TÜM TİPLER: buy_limit/sell_limit/buy_stop/sell_stop bekleyen + buy_market/sell_market anlık), mt5_cancel (emir iptal).\n' +
|
|
2013
2013
|
'EMİR TİPLERİ (6 tip — HEPSİ AÇIK): buy_market/sell_market = anlık piyasa emri (fiyat beklenmez), buy_limit/sell_limit = bekleyen limit (price zorunlu), buy_stop/sell_stop = bekleyen stop (price zorunlu). mt5_trade ve mt5_pending İKİSİ DE 6 tipi kabul eder; emir tipini net söyle (ör. type:"sell_limit").\n' +
|
|
2014
|
+
'ALARM KARARI SENDE: mt5_alerts ile alarm kurarken mode ZORUNLU — "once" (tek seferlik; ilk tetiklemede kapanır) ya da "repeat" (tekrarlı; alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar uyarır + seni uyandırır; cooldownMin varsayılan 5). Tekrarlı mı tek seferlik mi olacağını sen seç, varsayılana bırakma; seviyenin önemine göre cooldownMin belirle (ör. 5/15/60).\n' +
|
|
2014
2015
|
'KİŞİSEL MT5 ARAÇLARI (VARSAYILAN KURULU — tool__* tüm oturumlarda çağrılabilir): tool__mt5_shot (grafikten GERÇEK PNG + görsel ajana enjekte — grafik işlerinde BİRİNCİL; timeframes:["M1","M15"] ile çoklu periyot, symbols:["GOLD","EURUSD","BTCUSD"] ile çoklu sembol TEK karede), tool__mt5_barlar2 (OHLC mumlar, köprüsüz), tool__mt5_m15_m5 (GOLD M15+M5 hazır paket), tool__mt5_fiyat, tool__mt5_durum (hesap+pozisyon+emir tek çağrı), tool__mt5_gecmis, tool__mt5_pozisyon_gecmis, tool__mt5_sltp, tool__mt5_kapat, tool__mt5_bekleyen, tool__mt5_emir, tool__mt5_emir_iptal.\n' +
|
|
2015
2016
|
'BEASTFINANCE EA (OTOMATİK GRAFİK UZMANI — ENTEGRASYON KANALI): MT5 terminaline bağlanıldığında BeastFinance uzman danışmanı İLK GRAFİĞE OTOMATİK yüklenir, AutoTrading izni açılır (kurulum sistem tarafından yapılır; elle ekleme gerekmez). Grafik panosu + seviye çizgileri + dosya köprüsü (beast_ea.json / beast_cmd.json / beast_note.json) bu EA üzerinden yürür: entegrasyon işlerinde (status/ping/chart/note) mt5_ea kullan; ekran görüntüsü işlerinde tool__mt5_shot ("shot" komutu — gerçek PNG + ajan görseli) birincildir. Yazdığın not ve çizgiler tool__mt5_shot ve computer_look screenshot\'ında GÖRÜNÜR.\n' +
|
|
2016
2017
|
'YETKİLERİN (AÇIK — çekinmeden kullan):\n' +
|
|
@@ -6175,7 +6176,8 @@ const TOOLS = [
|
|
|
6175
6176
|
windowMin: { type: 'number', description: 'kind=logs: sliding window in minutes (default 10, max 720)' },
|
|
6176
6177
|
everyMin: { type: 'number', description: 'Check interval in minutes (default 15, min 1, max 1440)' },
|
|
6177
6178
|
everySec: { type: 'number', description: 'Check interval in SECONDS (10-8640) — overrides everyMin for fast watchers (e.g. 30 = every 30s)' },
|
|
6178
|
-
cooldownMin: { type: 'number', description: '
|
|
6179
|
+
cooldownMin: { type: 'number', description: 'İki bildirim arası en az dakika (varsayılan 60). >0 ise alarm TEKRARLIDIR: koşul sürdükçe soğuma dolduğunda yeniden bildirir (ör. 5 = 5 dakikada bir hatırlat). 0 = yalnız koşul yeniden sağlandığında (kenar tetikleme).' },
|
|
6180
|
+
once: { type: 'boolean', description: 'true = tek seferlik izleyici: ilk tetiklemede kapanır (varsayılan false = tekrarlı)' },
|
|
6179
6181
|
},
|
|
6180
6182
|
required: ['name', 'kind'],
|
|
6181
6183
|
},
|
|
@@ -200,7 +200,7 @@ const definitions = NAMES.map((name) => {
|
|
|
200
200
|
},
|
|
201
201
|
mt5_alerts: {
|
|
202
202
|
description:
|
|
203
|
-
'FİYAT ALARMI: mt5_alerts {action:"list"|"set"|"remove"}. set: {symbol, price, direction:"above"|"below", note?}.
|
|
203
|
+
'FİYAT ALARMI: mt5_alerts {action:"list"|"set"|"remove"}. set: {symbol, price, direction:"above"|"below", mode:"once"|"repeat", note?, cooldownMin?}. ALARM MODUNU KURAN AJAN (sen) SEÇERSİN — mode zorunlu: "once" tek seferlik (ilk tetiklemede kapanır) ya da "repeat" tekrarlı (alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar bildirir + sahibi ajanı uyandırır; cooldownMin varsayılan 5). Örn kritik seviye: XAUUSD 3400 üstü → mode:"repeat", cooldownMin:15; tek kere haber: mode:"once".',
|
|
204
204
|
parameters: {
|
|
205
205
|
type: 'object',
|
|
206
206
|
properties: {
|
|
@@ -208,7 +208,14 @@ const definitions = NAMES.map((name) => {
|
|
|
208
208
|
symbol: { type: 'string', description: 'set için sembol' },
|
|
209
209
|
price: { type: 'number', description: 'set için tetik fiyatı' },
|
|
210
210
|
direction: { type: 'string', enum: ['above', 'below'], description: 'above: fiyat ≥ price; below: fiyat ≤ price' },
|
|
211
|
+
mode: {
|
|
212
|
+
type: 'string',
|
|
213
|
+
enum: ['once', 'repeat'],
|
|
214
|
+
description: "set için ZORUNLU: 'once' tek seferlik | 'repeat' tekrarlı (kararı alarmı kuran ajan verir)",
|
|
215
|
+
},
|
|
211
216
|
note: { type: 'string', description: 'Alarm notu (bildirimde görünür)' },
|
|
217
|
+
cooldownMin: { type: 'number', description: "mode:'repeat' için iki tetik arası en az dakika (varsayılan 5, 0 = her tetiklemede, max 10080)" },
|
|
218
|
+
once: { type: 'boolean', description: "mode'a alternatif kısa yol (true = tek seferlik)" },
|
|
212
219
|
id: { type: 'string', description: 'remove için alarm id' },
|
|
213
220
|
},
|
|
214
221
|
required: ['action'],
|
|
@@ -425,9 +432,34 @@ const handlers = {
|
|
|
425
432
|
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
426
433
|
if (!isFinite(price) || price <= 0) return { ok: false, error: 'price gerekli (pozitif sayı)' };
|
|
427
434
|
if (direction !== 'above' && direction !== 'below') return { ok: false, error: "direction 'above' veya 'below' olmalı" };
|
|
428
|
-
|
|
435
|
+
/* ALARM MODUNU KURAN AJAN SEÇER: 'once' (tek seferlik) ya da
|
|
436
|
+
'repeat' (tekrarlı; koşul sürdükçe cooldownMin'de bir). Seçim
|
|
437
|
+
yapılmadan alarm kurulamaz — sistem varsayılanına bırakılmaz. */
|
|
438
|
+
const mRaw = String(args.mode || '').toLowerCase().trim();
|
|
439
|
+
let once;
|
|
440
|
+
if (mRaw === 'once' || mRaw === 'tek' || mRaw === 'tek_seferlik' || mRaw === 'tek-seferlik') once = true;
|
|
441
|
+
else if (mRaw === 'repeat' || mRaw === 'recurring' || mRaw === 'tekrarli' || mRaw === 'surekli' || mRaw === 'always') once = false;
|
|
442
|
+
else if (args.once !== undefined) once = args.once === true || String(args.once).toLowerCase() === 'true';
|
|
443
|
+
else {
|
|
444
|
+
return {
|
|
445
|
+
ok: false,
|
|
446
|
+
error:
|
|
447
|
+
"mode gerekli — alarmı kuran ajan karar verir: mode:'once' (tek seferlik, ilk tetiklemede kapanır) ya da mode:'repeat' (tekrarlı; koşul sürdükçe cooldownMin'de bir uyarır, varsayılan 5 dk)",
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
const cdRaw = Number(args.cooldownMin);
|
|
451
|
+
const cooldownMin = Number.isFinite(cdRaw) ? Math.min(Math.max(Math.round(cdRaw), 0), 10080) : undefined;
|
|
452
|
+
const alarm = alertsApi.set({
|
|
453
|
+
symbol,
|
|
454
|
+
price,
|
|
455
|
+
direction,
|
|
456
|
+
note: String(args.note || '').slice(0, 200),
|
|
457
|
+
sid: ctx && ctx.sessionId ? String(ctx.sessionId) : '',
|
|
458
|
+
...(cooldownMin === undefined ? {} : { cooldownMin }),
|
|
459
|
+
once,
|
|
460
|
+
});
|
|
429
461
|
if (!alarm) return { ok: false, error: 'alarm kurulamadı' };
|
|
430
|
-
noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note }, ctx || {});
|
|
462
|
+
noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note, cooldownMin: alarm.cooldownMin, once: alarm.once }, ctx || {});
|
|
431
463
|
return { ok: true, alert: alarm };
|
|
432
464
|
}
|
|
433
465
|
return { ok: false, error: "action: list|set|remove" };
|
package/src/agent/finwatch.js
CHANGED
|
@@ -147,6 +147,18 @@ function parseOrderType(raw, fallbackSide) {
|
|
|
147
147
|
return { ok: true, side, kind: 'market', type: side + '_market' };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/* ---- fiyat alarmı soğuması ----
|
|
151
|
+
Tekrarlı alarm (once=false): aynı koşul sürdükçe en fazla cooldownMin
|
|
152
|
+
dakikada bir tetiklenir. once=true alarmda soğuma uygulanmaz (ilk
|
|
153
|
+
tetiklemede kapanır). */
|
|
154
|
+
function alertCooldownActive(a, now) {
|
|
155
|
+
if (!a || a.once) return false;
|
|
156
|
+
const cd = (Number(a.cooldownMin) || 0) * 60000;
|
|
157
|
+
if (!(cd > 0)) return false;
|
|
158
|
+
const last = Number(a.lastFiredAt) || 0;
|
|
159
|
+
return last > 0 && Number(now) - last < cd;
|
|
160
|
+
}
|
|
161
|
+
|
|
150
162
|
/* ---- bekleyen emir (pending) → pozisyon eşleştirme ----
|
|
151
163
|
Watchdog turu emir listesini karşılaştırır: listeden DÜŞEN emir için aynı
|
|
152
164
|
turda YENİ açılan (ya da netting hesapta hacmi artan) eşleşen bir pozisyon
|
|
@@ -216,4 +228,4 @@ function matchPendingDelta(goneOrders, freshPositions) {
|
|
|
216
228
|
return out;
|
|
217
229
|
}
|
|
218
230
|
|
|
219
|
-
module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
|
|
231
|
+
module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, alertCooldownActive, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
|
package/src/agent/watchers.js
CHANGED
|
@@ -172,7 +172,9 @@ function normalize(input) {
|
|
|
172
172
|
const everyMin = Math.min(Math.max(Math.round(rawMin) || 15, 1), 1440);
|
|
173
173
|
everySec = everyMin * 60;
|
|
174
174
|
}
|
|
175
|
-
/* 0 geçerlidir (cooldown yok);
|
|
175
|
+
/* 0 geçerlidir (cooldown yok → yalnız kenar tetikleme); sayılamayan değerler
|
|
176
|
+
varsayılana döner. cooldownMin > 0 ise alarm TEKRARLIDIR: koşul sürdükçe
|
|
177
|
+
soğuma dolduğunda yeniden bildirir. */
|
|
176
178
|
const cdRaw = Number(i.cooldownMin ?? i.cooldown_min ?? 60);
|
|
177
179
|
const cooldownMin = Number.isFinite(cdRaw) ? Math.min(Math.max(Math.round(cdRaw), 0), 10080) : 60;
|
|
178
180
|
const prompt = String(i.prompt || '').trim().slice(0, 2000);
|
|
@@ -192,6 +194,7 @@ function normalize(input) {
|
|
|
192
194
|
everyMin: Math.max(1, Math.round(everySec / 60)), // geriye dönük uyum
|
|
193
195
|
everySec,
|
|
194
196
|
cooldownMin,
|
|
197
|
+
once: !!i.once, /* tek seferlik: ilk tetiklemede kapanır */
|
|
195
198
|
prompt,
|
|
196
199
|
sessionId: i.sessionId || null,
|
|
197
200
|
enabled: true,
|
|
@@ -223,11 +226,15 @@ function compare(op, current, target) {
|
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
/* Tek kontrol uygula — saf: tetik kararı + kaydedilecek yama.
|
|
226
|
-
|
|
227
|
-
|
|
229
|
+
TEKRARLI ALARM: cooldownMin > 0 ise koşul sürdükçe, soğuma her dolduğunda
|
|
230
|
+
YENİDEN tetiklenir (tickOnce soğuma boyunca zaten kontrol etmez).
|
|
231
|
+
cooldownMin = 0 → yalnız kenar tetikleme (koşul bozulup yeniden sağlanınca).
|
|
232
|
+
'changed' her zaman kenar tetiklemelidir (değer değişimi olaydır). */
|
|
228
233
|
function applyCheck(w, value, now) {
|
|
229
234
|
const cond = compare(w.op, value, w.value);
|
|
230
|
-
const
|
|
235
|
+
const cdMin = Number(w.cooldownMin) || 0;
|
|
236
|
+
const repeat = cdMin > 0 && w.op !== 'changed' && !!w.lastTriggeredAt;
|
|
237
|
+
const triggered = !!(cond && (w.armed || repeat));
|
|
231
238
|
const patch = {
|
|
232
239
|
lastCheckAt: new Date(now).toISOString(),
|
|
233
240
|
lastValue: value === undefined ? null : value,
|
|
@@ -371,6 +378,8 @@ async function tickOnce(deps = {}) {
|
|
|
371
378
|
w.lastError = '';
|
|
372
379
|
pushLog(w.id, r.triggered ? 'trigger' : 'ok', value);
|
|
373
380
|
if (r.triggered) {
|
|
381
|
+
/* TEK SEFERLİK: ilk tetiklemede izleyici kapanır (yeniden etkinleştirilebilir) */
|
|
382
|
+
if (w.once) w.enabled = false;
|
|
374
383
|
if (typeof hooks.onTrigger === 'function') hooks.onTrigger({ ...w }, value);
|
|
375
384
|
events.push({ id: w.id, name: w.name, value });
|
|
376
385
|
}
|
package/src/main.js
CHANGED
|
@@ -9780,7 +9780,14 @@ async function channelSendFromAgent(input) {
|
|
|
9780
9780
|
};
|
|
9781
9781
|
}
|
|
9782
9782
|
|
|
9783
|
-
/* ---- fiyat alarmları (kalıcı) ----
|
|
9783
|
+
/* ---- fiyat alarmları (kalıcı) ----
|
|
9784
|
+
TEKRARLI (varsayılan): alarm kapanmaz; koşul sürdükçe en fazla
|
|
9785
|
+
cooldownMin dakikada bir tekrar tetikler. once:true → tek tetiklemede
|
|
9786
|
+
kapanır. cooldownMin'i AJAN karar verir (mt5_alerts ile). */
|
|
9787
|
+
const FIN_ALERT_CD_DEFAULT = 5; /* dk */
|
|
9788
|
+
function finAlertCooldown(a) {
|
|
9789
|
+
return Math.min(Math.max(Math.round(Number(a && a.cooldownMin) || FIN_ALERT_CD_DEFAULT), 0), 10080);
|
|
9790
|
+
}
|
|
9784
9791
|
function finAlertsLoad() {
|
|
9785
9792
|
const raw = finReadJson(finFile('alerts.json'), []);
|
|
9786
9793
|
financeState.alerts = (Array.isArray(raw) ? raw : [])
|
|
@@ -9791,6 +9798,10 @@ function finAlertsLoad() {
|
|
|
9791
9798
|
direction: String((a && a.direction) || 'above').toLowerCase(),
|
|
9792
9799
|
note: String((a && a.note) || '').slice(0, 200),
|
|
9793
9800
|
sid: String((a && a.sid) || ''), /* alarmı kuran ajan oturumu (uyandırma hedefi) */
|
|
9801
|
+
once: !!(a && a.once), /* tek seferlik mi */
|
|
9802
|
+
cooldownMin: finAlertCooldown(a),
|
|
9803
|
+
lastFiredAt: Number(a && a.lastFiredAt) || 0,
|
|
9804
|
+
fires: Number(a && a.fires) || 0,
|
|
9794
9805
|
at: Number(a && a.at) || Date.now(),
|
|
9795
9806
|
}))
|
|
9796
9807
|
.filter((a) => a.id && a.symbol && a.price > 0);
|
|
@@ -9807,18 +9818,41 @@ function finAlertApi() {
|
|
|
9807
9818
|
const symbol = String((a && a.symbol) || '').toUpperCase();
|
|
9808
9819
|
const price = Number(a && a.price);
|
|
9809
9820
|
if (!symbol || !(price > 0)) return null;
|
|
9821
|
+
const direction = String((a && a.direction) || 'above').toLowerCase() === 'below' ? 'below' : 'above';
|
|
9822
|
+
/* AYNI ALARM ZATEN AÇIKSA yenisini EKLEME — mevcut kaydı güncelle:
|
|
9823
|
+
ajan her turda aynı seviyeyi yeniden kurup alarm/tur döngüsü
|
|
9824
|
+
(ard arda tetik + uyandırma) üretmesin. lastFiredAt KORUNUR. */
|
|
9825
|
+
const existing = financeState.alerts.find(
|
|
9826
|
+
(x) => x.symbol === symbol && x.direction === direction && Math.abs(Number(x.price) - price) < 1e-9
|
|
9827
|
+
);
|
|
9828
|
+
if (existing) {
|
|
9829
|
+
if (a.note !== undefined) existing.note = String(a.note || '').slice(0, 200);
|
|
9830
|
+
if (a.sid !== undefined) existing.sid = String(a.sid || '');
|
|
9831
|
+
if (a.cooldownMin !== undefined) existing.cooldownMin = finAlertCooldown(a);
|
|
9832
|
+
if (a.once !== undefined) existing.once = !!a.once;
|
|
9833
|
+
finAlertsSave();
|
|
9834
|
+
financeLog(`[alarm] güncellendi (zaten açıktı): ${existing.symbol} ${existing.direction === 'below' ? '≤' : '≥'} ${existing.price}`);
|
|
9835
|
+
return { ...existing, updated: true };
|
|
9836
|
+
}
|
|
9810
9837
|
const alarm = {
|
|
9811
9838
|
id: 'a' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
9812
9839
|
symbol,
|
|
9813
9840
|
price,
|
|
9814
|
-
direction
|
|
9841
|
+
direction,
|
|
9815
9842
|
note: String((a && a.note) || '').slice(0, 200),
|
|
9816
9843
|
sid: String((a && a.sid) || ''),
|
|
9844
|
+
once: !!(a && a.once),
|
|
9845
|
+
cooldownMin: finAlertCooldown(a),
|
|
9846
|
+
lastFiredAt: 0,
|
|
9847
|
+
fires: 0,
|
|
9817
9848
|
at: Date.now(),
|
|
9818
9849
|
};
|
|
9819
9850
|
financeState.alerts.push(alarm);
|
|
9820
9851
|
finAlertsSave();
|
|
9821
|
-
financeLog(
|
|
9852
|
+
financeLog(
|
|
9853
|
+
`[alarm] kuruldu: ${alarm.symbol} ${alarm.direction === 'below' ? '≤' : '≥'} ${alarm.price}` +
|
|
9854
|
+
(alarm.once ? ' (tek seferlik)' : ` (tekrarlı · ${alarm.cooldownMin} dk soğuma)`)
|
|
9855
|
+
);
|
|
9822
9856
|
return alarm;
|
|
9823
9857
|
},
|
|
9824
9858
|
remove: (id) => {
|
|
@@ -10100,23 +10134,39 @@ async function finCheckAlerts() {
|
|
|
10100
10134
|
prices[String(row.symbol)] = { bid: Number(row.bid), ask: Number(row.ask) };
|
|
10101
10135
|
}
|
|
10102
10136
|
let changed = false;
|
|
10137
|
+
const now = Date.now();
|
|
10103
10138
|
for (const a of alerts.slice()) {
|
|
10104
10139
|
const p = prices[a.symbol];
|
|
10105
10140
|
if (!p || !isFinite(p.bid)) continue;
|
|
10141
|
+
/* soğuma: tekrarlı alarm koşul sürse bile cooldown dolmadan tekrar etmez */
|
|
10142
|
+
if (finwatch.alertCooldownActive(a, now)) continue;
|
|
10106
10143
|
const hit = a.direction === 'below' ? p.bid <= a.price : p.bid >= a.price;
|
|
10107
10144
|
if (!hit) continue;
|
|
10108
10145
|
changed = true;
|
|
10109
|
-
|
|
10110
|
-
|
|
10146
|
+
const fires = (Number(a.fires) || 0) + 1;
|
|
10147
|
+
if (a.once) {
|
|
10148
|
+
/* tek seferlik: tetiklendi → kapanır */
|
|
10149
|
+
financeState.alerts = financeState.alerts.filter((x) => x.id !== a.id);
|
|
10150
|
+
} else {
|
|
10151
|
+
/* tekrarlı: alarm AÇIK kalır; cooldown sonrası yeniden hatırlatır */
|
|
10152
|
+
a.lastFiredAt = now;
|
|
10153
|
+
a.fires = fires;
|
|
10154
|
+
}
|
|
10155
|
+
const tekrar = fires > 1 ? ` (tekrar #${fires - 1})` : '';
|
|
10156
|
+
const cdTxt = a.once ? ' · tek seferlik' : ` · ${finAlertCooldown(a)} dk soğuma`;
|
|
10157
|
+
const line = `🔔 ALARM${tekrar}: ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}${cdTxt}`;
|
|
10111
10158
|
financeLog('[alarm] ' + line);
|
|
10112
10159
|
financeNotify(line, 'alert');
|
|
10113
|
-
finJournal({ kind: 'alert', symbol: a.symbol, price: a.price, direction: a.direction, note: a.note || '' });
|
|
10160
|
+
finJournal({ kind: 'alert', symbol: a.symbol, price: a.price, direction: a.direction, note: a.note || '', fires, once: !!a.once });
|
|
10114
10161
|
/* ALARM SAHİBİ AJAN: alarmı kuran koşan finance ajanı DM'den uyanır;
|
|
10115
10162
|
sahip koşmuyor/biliniyorsa koşan tüm finance ajanlarına düşer. */
|
|
10116
10163
|
const wakeTxt =
|
|
10117
|
-
`[FİNANS OLAYI — FİYAT ALARMI, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
|
|
10118
|
-
`- ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} tetiklendi (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}\n` +
|
|
10119
|
-
|
|
10164
|
+
`[FİNANS OLAYI — FİYAT ALARMI${tekrar}, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
|
|
10165
|
+
`- ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} tetiklendi (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}${cdTxt}\n` +
|
|
10166
|
+
(a.once
|
|
10167
|
+
? 'Alarm TEK SEFERLİKTİ ve kapandı. '
|
|
10168
|
+
: `Alarm TEKRARLI açık kalıyor${a.cooldownMin > 0 ? ` — koşul sürerse en erken ${a.cooldownMin} dk sonra yeniden uyarır` : ''}. `) +
|
|
10169
|
+
'Şimdi yap: alarmı kurma nedenini hatırla; fiyat seviyesini ve planını değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver.';
|
|
10120
10170
|
if (!finWakeAgentDm(a.sid, wakeTxt)) finWakeAgents(wakeTxt, { kind: 'alarm', symbol: a.symbol });
|
|
10121
10171
|
}
|
|
10122
10172
|
if (changed) finAlertsSave();
|
|
@@ -10988,7 +11038,7 @@ function finTraderBrief(agent) {
|
|
|
10988
11038
|
`Tur aralığı: ${f.intervalSec} sn · Max lot: ${f.maxLot} · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
10989
11039
|
roleDef
|
|
10990
11040
|
? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
|
|
10991
|
-
: 'Otomatik işlem AÇIK (daima): 6 emir tipinin HEPSİ açık — buy_market/sell_market (anlık piyasa), buy_limit/sell_limit ve buy_stop/sell_stop (bekleyen; price zorunlu). mt5_trade ya da mt5_pending İKİSİ de tüm tipleri kabul eder; ayrıca mt5_close/mt5_modify/mt5_cancel açık (limitler sistemce zorlanır). Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı.',
|
|
11041
|
+
: 'Otomatik işlem AÇIK (daima): 6 emir tipinin HEPSİ açık — buy_market/sell_market (anlık piyasa), buy_limit/sell_limit ve buy_stop/sell_stop (bekleyen; price zorunlu). mt5_trade ya da mt5_pending İKİSİ de tüm tipleri kabul eder; ayrıca mt5_close/mt5_modify/mt5_cancel açık (limitler sistemce zorlanır). ALARM: mt5_alerts ile kurarken modu SEN seç — mode:"once" tek seferlik, mode:"repeat" + cooldownMin (dk) tekrarlı. Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı.',
|
|
10992
11042
|
roleDef
|
|
10993
11043
|
? 'Bulgularını agent_dm ile ANA TRADER\u2019a bildir (to: "Trader" ya da ajan başlığı anahtarı); teknik/öneri çelişkisi varsa gerekçenle yaz.'
|
|
10994
11044
|
: f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
@@ -11265,6 +11315,17 @@ ipcMain.handle('finance:snapshot', async () => {
|
|
|
11265
11315
|
};
|
|
11266
11316
|
});
|
|
11267
11317
|
|
|
11318
|
+
ipcMain.handle('finance:alerts', async () => {
|
|
11319
|
+
/* İzleyiciler paneli hafif alarm listesi (hesap/pozisyon çekmez) */
|
|
11320
|
+
return { ok: true, alerts: (financeState.alerts || []).slice(0, 200) };
|
|
11321
|
+
});
|
|
11322
|
+
|
|
11323
|
+
ipcMain.handle('finance:alerts:remove', async (_e, id) => {
|
|
11324
|
+
const ok = finAlertApi().remove(String(id || ''));
|
|
11325
|
+
finPush('alert', { removed: ok, id: String(id || '') });
|
|
11326
|
+
return { ok };
|
|
11327
|
+
});
|
|
11328
|
+
|
|
11268
11329
|
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
11269
11330
|
financeState.mode = !!(payload && payload.on);
|
|
11270
11331
|
/* BEAST FINANCE = MT5 KAPISI:
|
package/src/preload.js
CHANGED
|
@@ -117,6 +117,8 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
117
117
|
|
|
118
118
|
financeState: () => ipcRenderer.invoke('finance:state'),
|
|
119
119
|
financeSnapshot: () => ipcRenderer.invoke('finance:snapshot'),
|
|
120
|
+
financeAlertsList: () => ipcRenderer.invoke('finance:alerts'),
|
|
121
|
+
financeAlertRemove: (id) => ipcRenderer.invoke('finance:alerts:remove', id),
|
|
120
122
|
financeMode: (on, sessionId) => ipcRenderer.invoke('finance:mode', { on, sessionId }),
|
|
121
123
|
financeSettings: (patch) => ipcRenderer.invoke('finance:settings', patch),
|
|
122
124
|
financeTraderStart: () => ipcRenderer.invoke('finance:trader:start'),
|
package/src/renderer/index.html
CHANGED
|
@@ -798,6 +798,10 @@
|
|
|
798
798
|
<input id="wAddWindow" class="inp" type="number" min="1" max="720" value="10" title="pencere (dk)" style="width:80px" />
|
|
799
799
|
<input id="wAddRe" class="inp" data-i18n-ph="w_add_re" placeholder="regex (opsiyonel)" />
|
|
800
800
|
</div>
|
|
801
|
+
<div class="mini-add-row">
|
|
802
|
+
<input id="wAddCd" class="inp" type="number" min="0" max="10080" value="60" title="soğuma (dk): koşul sürdükçe en fazla bu aralıkta bir bildirir — 0 = yalnız koşul yeniden sağlanınca" style="width:70px" />
|
|
803
|
+
<label style="display:flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);white-space:nowrap"><input id="wAddOnce" type="checkbox" /> tek seferlik</label>
|
|
804
|
+
</div>
|
|
801
805
|
</div>
|
|
802
806
|
<div id="watchList" class="mini-list"></div>
|
|
803
807
|
</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -6640,17 +6640,35 @@ function fmtWhen(iso) {
|
|
|
6640
6640
|
Kompakt liste: durum noktası + ad + bir sonraki tetik; satır → izleyici
|
|
6641
6641
|
modalı; ▶/× mini kontroller. Yalnız agent modunda görünür (CSS). */
|
|
6642
6642
|
let lastWatchPaneKey = '';
|
|
6643
|
+
/* FİYAT ALARMLARI (Beast Finance): İzleyiciler paneli + modalında watcher'larla
|
|
6644
|
+
BİRLİKTE listelenir — tüm alarmlar tek yerde görünür/silinir. */
|
|
6645
|
+
function finAlertMeta(a) {
|
|
6646
|
+
const dir = a.direction === 'below' ? '≤' : '≥';
|
|
6647
|
+
return {
|
|
6648
|
+
name: `🔔 ${a.symbol} ${dir} ${a.price}`,
|
|
6649
|
+
meta:
|
|
6650
|
+
(a.once ? 'tek seferlik' : `her ${Number(a.cooldownMin) || 5} dk`) +
|
|
6651
|
+
(a.fires ? ` · ${a.fires}× tetiklendi` : '') +
|
|
6652
|
+
(a.note ? ` · ${String(a.note).slice(0, 40)}` : ''),
|
|
6653
|
+
};
|
|
6654
|
+
}
|
|
6655
|
+
|
|
6643
6656
|
async function renderWatchersPane(force) {
|
|
6644
6657
|
const list = els.watchPaneList;
|
|
6645
6658
|
if (!list) return;
|
|
6646
6659
|
if (document.body.classList.contains('ide-mode') || document.body.classList.contains('studio-mode') || document.body.classList.contains('sandbox-mode')) return;
|
|
6647
6660
|
let rows = [];
|
|
6648
6661
|
try { rows = (await beast.watchersList()) || []; } catch {}
|
|
6649
|
-
|
|
6662
|
+
let alerts = [];
|
|
6663
|
+
try {
|
|
6664
|
+
const ar = await beast.financeAlertsList();
|
|
6665
|
+
alerts = (ar && ar.alerts) || [];
|
|
6666
|
+
} catch {}
|
|
6667
|
+
const key = JSON.stringify([rows, alerts]);
|
|
6650
6668
|
if (!force && key === lastWatchPaneKey) return;
|
|
6651
6669
|
lastWatchPaneKey = key;
|
|
6652
6670
|
list.innerHTML = '';
|
|
6653
|
-
if (!rows.length) {
|
|
6671
|
+
if (!rows.length && !alerts.length) {
|
|
6654
6672
|
list.innerHTML = '<div class="wp-empty">' + _t('w_empty') + '</div>';
|
|
6655
6673
|
return;
|
|
6656
6674
|
}
|
|
@@ -6698,18 +6716,52 @@ async function renderWatchersPane(force) {
|
|
|
6698
6716
|
row.addEventListener('click', () => openWatchModal());
|
|
6699
6717
|
list.appendChild(row);
|
|
6700
6718
|
}
|
|
6719
|
+
/* Beast Finance FİYAT ALARMLARI: aynı listede — silinebilir, canlı cd/fire bilgisi */
|
|
6720
|
+
for (const a of alerts) {
|
|
6721
|
+
const info = finAlertMeta(a);
|
|
6722
|
+
const row = document.createElement('div');
|
|
6723
|
+
row.className = 'wp-row alarm';
|
|
6724
|
+
row.title = info.name + (a.note ? ' — ' + a.note : '') + ' · ' + info.meta;
|
|
6725
|
+
row.innerHTML =
|
|
6726
|
+
`<span class="wp-dot"></span>` +
|
|
6727
|
+
`<div class="wp-main">` +
|
|
6728
|
+
`<div class="wp-name">${escapeHtml(info.name)}</div>` +
|
|
6729
|
+
`<div class="wp-meta">${escapeHtml(info.meta)}</div>` +
|
|
6730
|
+
`</div>`;
|
|
6731
|
+
const btns = document.createElement('div');
|
|
6732
|
+
btns.className = 'wp-btns';
|
|
6733
|
+
const d = document.createElement('button');
|
|
6734
|
+
d.className = 'wp-btn del';
|
|
6735
|
+
d.textContent = '×';
|
|
6736
|
+
d.title = _t('cr_del');
|
|
6737
|
+
d.addEventListener('click', async (e) => {
|
|
6738
|
+
e.stopPropagation();
|
|
6739
|
+
await beast.financeAlertRemove(a.id);
|
|
6740
|
+
lastWatchPaneKey = '';
|
|
6741
|
+
renderWatchersPane(true);
|
|
6742
|
+
renderWatchersModal(true);
|
|
6743
|
+
});
|
|
6744
|
+
btns.appendChild(d);
|
|
6745
|
+
row.appendChild(btns);
|
|
6746
|
+
list.appendChild(row);
|
|
6747
|
+
}
|
|
6701
6748
|
}
|
|
6702
6749
|
|
|
6703
6750
|
let lastWatchKey = '';
|
|
6704
6751
|
async function renderWatchersModal(force) {
|
|
6705
6752
|
let rows = []; try { rows = (await beast.watchersList()) || []; } catch {}
|
|
6753
|
+
let alerts = [];
|
|
6754
|
+
try {
|
|
6755
|
+
const ar = await beast.financeAlertsList();
|
|
6756
|
+
alerts = (ar && ar.alerts) || [];
|
|
6757
|
+
} catch {}
|
|
6706
6758
|
/* canlı yenilemede içerik değişmediyse DOM'a dokunma — buton tıklamaları
|
|
6707
6759
|
yeniden kurulurken yutulmasın */
|
|
6708
|
-
const key = JSON.stringify(rows);
|
|
6760
|
+
const key = JSON.stringify([rows, alerts]);
|
|
6709
6761
|
if (!force && key === lastWatchKey) return;
|
|
6710
6762
|
lastWatchKey = key;
|
|
6711
6763
|
els.watchList.innerHTML = '';
|
|
6712
|
-
if (!rows.length) {
|
|
6764
|
+
if (!rows.length && !alerts.length) {
|
|
6713
6765
|
els.watchList.innerHTML = '<div class="mini-empty">' + _t('w_empty') + '</div>';
|
|
6714
6766
|
return;
|
|
6715
6767
|
}
|
|
@@ -6724,7 +6776,7 @@ async function renderWatchersModal(force) {
|
|
|
6724
6776
|
`<span class="mr-dot"></span>` +
|
|
6725
6777
|
`<div class="mr-main">` +
|
|
6726
6778
|
`<div class="mr-name">${escapeHtml(w.name)}</div>` +
|
|
6727
|
-
`<div class="mr-meta">[${w.kind}] ${escapeHtml(String(target).slice(0, 60))} · <b>${opText(w)}</b> · her <b>${fmtEvery(w)}</b> ·
|
|
6779
|
+
`<div class="mr-meta">[${w.kind}] ${escapeHtml(String(target).slice(0, 60))} · <b>${opText(w)}</b> · her <b>${fmtEvery(w)}</b> · ${w.once ? '<b>tek seferlik</b>' : 'cd ' + w.cooldownMin + 'dk'}` +
|
|
6728
6780
|
(w.lastValue !== null && w.lastValue !== undefined ? ` · son: <b>${escapeHtml(String(w.lastValue).slice(0, 20))}</b>` : '') +
|
|
6729
6781
|
(w.lastError ? ` · <span style="color:var(--err)">${escapeHtml(w.lastError.slice(0, 40))}</span>` : '') +
|
|
6730
6782
|
`</div></div>`;
|
|
@@ -6749,6 +6801,34 @@ async function renderWatchersModal(force) {
|
|
|
6749
6801
|
row.appendChild(btns);
|
|
6750
6802
|
els.watchList.appendChild(row);
|
|
6751
6803
|
}
|
|
6804
|
+
/* Beast Finance FİYAT ALARMLARI: modalda da watcher'larla birlikte */
|
|
6805
|
+
for (const a of alerts) {
|
|
6806
|
+
const info = finAlertMeta(a);
|
|
6807
|
+
const row = document.createElement('div');
|
|
6808
|
+
row.className = 'mini-row alarm';
|
|
6809
|
+
row.innerHTML =
|
|
6810
|
+
`<span class="mr-dot"></span>` +
|
|
6811
|
+
`<div class="mr-main">` +
|
|
6812
|
+
`<div class="mr-name">${escapeHtml(info.name)}</div>` +
|
|
6813
|
+
`<div class="mr-meta">${escapeHtml(info.meta)}` +
|
|
6814
|
+
(a.note ? ` · ${escapeHtml(String(a.note).slice(0, 50))}` : '') +
|
|
6815
|
+
`</div></div>`;
|
|
6816
|
+
const btns = document.createElement('div');
|
|
6817
|
+
btns.style.cssText = 'display:flex;gap:6px;flex:none';
|
|
6818
|
+
const d = document.createElement('button');
|
|
6819
|
+
d.className = 'mr-btn del';
|
|
6820
|
+
d.textContent = '×';
|
|
6821
|
+
d.title = _t('cr_del');
|
|
6822
|
+
d.addEventListener('click', async () => {
|
|
6823
|
+
await beast.financeAlertRemove(a.id);
|
|
6824
|
+
lastWatchKey = '';
|
|
6825
|
+
renderWatchersModal(true);
|
|
6826
|
+
renderWatchersPane(true);
|
|
6827
|
+
});
|
|
6828
|
+
btns.appendChild(d);
|
|
6829
|
+
row.appendChild(btns);
|
|
6830
|
+
els.watchList.appendChild(row);
|
|
6831
|
+
}
|
|
6752
6832
|
}
|
|
6753
6833
|
|
|
6754
6834
|
/* ---------- İZLEYİCİ LOG MODALI ----------
|
|
@@ -7801,6 +7881,8 @@ async function init() {
|
|
|
7801
7881
|
name: $('#wAddName').value.trim(),
|
|
7802
7882
|
kind,
|
|
7803
7883
|
everyMin: Number($('#wAddEvery').value) || 15,
|
|
7884
|
+
cooldownMin: Math.max(0, Number($('#wAddCd') ? $('#wAddCd').value : 60) || 0),
|
|
7885
|
+
once: !!($('#wAddOnce') && $('#wAddOnce').checked),
|
|
7804
7886
|
};
|
|
7805
7887
|
if (kind === 'web') {
|
|
7806
7888
|
input.url = $('#wAddUrl').value.trim();
|
|
@@ -7823,6 +7905,8 @@ async function init() {
|
|
|
7823
7905
|
const el = $(id);
|
|
7824
7906
|
if (el) el.value = '';
|
|
7825
7907
|
}
|
|
7908
|
+
if ($('#wAddOnce')) $('#wAddOnce').checked = false;
|
|
7909
|
+
if ($('#wAddCd')) $('#wAddCd').value = '60';
|
|
7826
7910
|
lastWatchKey = '';
|
|
7827
7911
|
lastWatchPaneKey = '';
|
|
7828
7912
|
await renderWatchersModal(true);
|
|
@@ -9979,6 +10063,7 @@ function finRenderAlerts(list) {
|
|
|
9979
10063
|
row.innerHTML =
|
|
9980
10064
|
'<span class="fa-sym">' + escapeHtml(String(a.symbol || '?')) + '</span>' +
|
|
9981
10065
|
'<span class="fa-cond">' + (a.direction === 'below' ? '≤' : '≥') + ' ' + (Number(a.price) || 0) + '</span>' +
|
|
10066
|
+
'<span class="fa-note">' + (a.once ? 'tek seferlik' : `her ${Number(a.cooldownMin) || 5} dk`) + (a.fires ? ` · ${a.fires}×` : '') + '</span>' +
|
|
9982
10067
|
(a.note ? '<span class="fa-note">' + escapeHtml(String(a.note)) + '</span>' : '');
|
|
9983
10068
|
box.appendChild(row);
|
|
9984
10069
|
}
|
package/src/renderer/style.css
CHANGED
|
@@ -1504,6 +1504,8 @@ body.browser-open .mini-overlay { right: var(--bw, 480px); }
|
|
|
1504
1504
|
.mini-row.off .mr-name, .mini-row.off .mr-meta { opacity: 0.45; }
|
|
1505
1505
|
.mr-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); flex: none; }
|
|
1506
1506
|
.mini-row.off .mr-dot { background: var(--muted); }
|
|
1507
|
+
/* Beast Finance fiyat alarmları: izleyici listesinde turuncu nokta ile ayrılır */
|
|
1508
|
+
.wp-row.alarm .wp-dot, .mini-row.alarm .mr-dot { background: #e6a117; }
|
|
1507
1509
|
.mr-main { flex: 1; min-width: 0; }
|
|
1508
1510
|
.mr-name { font-weight: 700; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1509
1511
|
.mr-meta { color: var(--muted); font-size: 11px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|