beast-agent 2.58.2 → 2.60.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.58.2",
4
+ "version": "2.60.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -2032,9 +2032,11 @@ class Engine {
2032
2032
  modeBlock2 + '\n' +
2033
2033
  (roleBlockFull ? roleBlockFull + '\n' : '') +
2034
2034
  (teamLine || '') +
2035
- '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 (TAM ya da KISMİ kapat: percent ile kısmi TP & kısmi stop — karar ajanın), 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' +
2035
+ '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ı: kur/listele/sil — modu SEN seçersin: once/repeat), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (⚡PİYASA EMRİ: side:"buy"|"sell" ile ANLIK gir — bekleyen emir ZORUNLU DEĞİL; lot için volume yerine riskPct+sl ver, sistem hesaplar; limit/stop tipleri de kabul eder), mt5_close (TAM ya da KISMİ kapat: percent ile kısmi TP & kısmi stop — karar ajanın), mt5_modify (SL/TP), mt5_pending (BEKLEYEN emir: limit/stop + price — fiyatın seviyeye gelmesini beklemek için; anlık giriş için mt5_trade kullan), mt5_cancel (emir iptal).\n' +
2036
2036
  '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' +
2037
+ '⚡HIZLI AKSİYON (ZORUNLU ZİHNİYET): piyasa emri (market buy/sell) HER ZAMAN elinde ve bekleyen emir vermek ZORUNDA DEĞİLSİN. Fırsat anıksa / teyitli kırılım, momentum, haber veya seviye reddi anında mt5_trade {symbol, side:"buy"|"sell", sl, tp, riskPct ya da volume, reason} ile ANINDA market gir — type bile yazmana gerek yok. Bekleyen emir YALNIZ fiyatın seviyeye gelmesini beklemek gerçekten mantıklıysa kurulur (ör. geri çekilme limiti); "emir" diye her şeyi bekleyen emirle yapma. Hızlı girişte tek çağrı yeter: volume yerine riskPct+sl ver → lot otomatik hesaplanır.\n' +
2037
2038
  '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' +
2039
+ 'ALARM TEMİZLİĞİ (ZORUNLU DİSİPLİN): alarm bir BEKÇİDİR, süs değil — gereksiz alarm biriktirme. Her turda (ya da en az birkaç turda bir) mt5_alerts action:"list" ile alarmları gör; listede "mine:true" senin kurduklarındır. Tezi/geçerliliği biten alarmı SİL: seviye kırıldı ve artık anlamsız, pozisyon kapandı, plan/hedef değişti, tetiklendi ve işlevi kalmadı. Silme: tekil kayıt action:"remove" {id} · kendi gereksizlerini toplu action:"clear" (varsayılan yalnız SENİN alarmların; symbol ile daralt; tümünü temizlemek için all:true — bunu başka ajanların alarmlarını da sileceği için YALNIZ açıkça gerekliyse kullan). Akan fiyatta işe yaramayan, üst üste aynı bölgeye kurulmuş alarmları temiz tut.\n' +
2038
2040
  'KISMİ KAPATMA YETKİSİ (TÜM finance ajanlarında): açık pozisyonda kısmi kapatma — kısmi TP (kâr al) ve kısmi stop (zarar kes) — TAMAMEN SENİN KARARIN, OTOMATİK DEĞİLDİR; mt5_close {ticket, percent, kind:"partial_tp"|"partial_sl", reason}. Gerek gördüğünde kullan; kalan pozisyon SL/TP ile devam eder. Kısmi TP fırsatı bildirimi gelirse (fiyat seviyeye ulaştı) kapatıp kapatmamak sana bağlı.\n' +
2039
2041
  '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' +
2040
2042
  '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' +
@@ -200,12 +200,12 @@ 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", 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".',
203
+ 'FİYAT ALARMI: mt5_alerts {action:"list"|"set"|"remove"|"clear"}. 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". TEMİZLİK: alarm bir bekçidir, süs değil — gereksiz alarm biriktirme; her turda list ile KENDİ alarmlarını gör, işi bitenleri (tez geçersiz, pozisyon kapandı, seviye anlamsız) clear ile SİL. clear varsayılanı YALNIZ senin kurduğun alarmlar; symbol ile daralt; ids:[...] belirli alarmlar; all:true tüm finance alarmları.',
204
204
  parameters: {
205
205
  type: 'object',
206
206
  properties: {
207
- action: { type: 'string', enum: ['list', 'set', 'remove'] },
208
- symbol: { type: 'string', description: 'set için sembol' },
207
+ action: { type: 'string', enum: ['list', 'set', 'remove', 'clear'] },
208
+ symbol: { type: 'string', description: 'set/clear için sembol (clear: yalnız bu sembole daralt)' },
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
211
  mode: {
@@ -217,6 +217,8 @@ const definitions = NAMES.map((name) => {
217
217
  cooldownMin: { type: 'number', description: "mode:'repeat' için iki tetik arası en az dakika (varsayılan 5, 0 = her tetiklemede, max 10080)" },
218
218
  once: { type: 'boolean', description: "mode'a alternatif kısa yol (true = tek seferlik)" },
219
219
  id: { type: 'string', description: 'remove için alarm id' },
220
+ ids: { type: 'array', items: { type: 'string' }, description: 'clear için: silinecek alarm id listesi (verilirse sahiplik aranmaz)' },
221
+ all: { type: 'boolean', description: 'clear için: true → sahiplik filtresini kaldır, kalan TÜM finance alarmlarını sil (varsayılan: yalnız kendi kurdukların)' },
220
222
  },
221
223
  required: ['action'],
222
224
  },
@@ -260,25 +262,26 @@ const definitions = NAMES.map((name) => {
260
262
  },
261
263
  mt5_trade: {
262
264
  description:
263
- 'EMİR AÇAR — TÜM TİPLER: mt5_trade {symbol, side:"buy"|"sell", volume, type?, sl?, tp?, comment?, reason?}. type: "buy"|"sell" (piyasa/anlık) ya da "buy_market"|"sell_market"|"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop" — limit/stop tipleri otomatik bekleyen emre (mt5_pending) yönlenir ve price gerekir. Lot limiti ve max pozisyon sayısı sistem tarafından zorlanır. Otomatik işlem anahtarı kapalıysa reddedilir. SL/TP vermek ŞİDDETLİ önerilir. reason: kararın tek cümlelik tezi (günlüğe yazılır, performans değerlendirmesinde kullanılır).',
265
+ 'ANLIK PİYASA EMRİ AÇAR — ⚡HIZLI AKSİYON; bekleyen emir vermek ZORUNLU DEĞİL: mt5_trade {symbol, side:"buy"|"sell", volume?|riskPct?, sl?, tp?, type?, comment?, reason?}. Fırsat anıksa / teyitli kırılım-momentum-haber anında market buy/sell ile HEMEN gir — side:"buy"|"sell" yeter (type gerekmez; type:"market"|"buy_market"|"sell_market" de anlıktır). BEKLEYEN emir yalnız fiyatın bir seviyeye gelmesini beklemek gerçekten mantıklıysa kurulur: type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop" + price (bu tipler otomatik mt5_pending hattına gider). LOT: volume ver ya da volume yerine riskPct + sl ver — sistem SL mesafesinden lotu hesaplar (tek çağrıda giriş; ayardaki işlem riski % varsayılan). Lot limiti, max pozisyon, marj ve yoğunluk sistemce zorlanır. SL vermek ŞİDDETLİ önerilir. reason: kararın tek cümlelik tezi (günlüğe yazılır).',
264
266
  parameters: {
265
267
  type: 'object',
266
268
  properties: {
267
269
  symbol: { type: 'string', description: 'Örn: EURUSD' },
268
- side: { type: 'string', enum: ['buy', 'sell'], description: 'Yön (type ile birlikte ya da tek başına: piyasa emri)' },
270
+ side: { type: 'string', enum: ['buy', 'sell'], description: 'Yön — piyasa emri için tek başına yeterli (type gerekmez)' },
269
271
  type: {
270
272
  type: 'string',
271
273
  enum: ['market', 'buy_market', 'sell_market', 'buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'],
272
- description: 'Emir tipi. Boş/side verilirse piyasa emri; limit/stop verilirse bekleyen emir (price zorunlu olur).',
274
+ description: 'Emir tipi. Boş/side verilirse piyasa (anlık); limit/stop verilirse bekleyen emir (price zorunlu olur).',
273
275
  },
274
- volume: { type: 'number', description: 'Lot (max lot sınırına tabi)' },
276
+ volume: { type: 'number', description: 'Lot (max lot sınırına tabi) — boşsa riskPct + sl ile OTOMATİK hesaplanır' },
277
+ riskPct: { type: 'number', description: 'İşlem riski % (ör. 0.5-2) — volume yerine ver: SL mesafesinden lot hesaplanır (sl zorunlu)' },
275
278
  price: { type: 'number', description: 'Limit/stop emirlerinde tetik fiyatı (piyasa emrinde gerekmez)' },
276
- sl: { type: 'number', description: 'Stop loss fiyatı (0 = yok)' },
279
+ sl: { type: 'number', description: 'Stop loss fiyatı (0 = yok) — şiddetle önerilir' },
277
280
  tp: { type: 'number', description: 'Take profit fiyatı (0 = yok)' },
278
281
  comment: { type: 'string', description: 'Kısa işlem notu' },
279
282
  reason: { type: 'string', description: 'Kararın tezi/gerekçesi (tek cümle)' },
280
283
  },
281
- required: ['symbol', 'volume'],
284
+ required: ['symbol'],
282
285
  },
283
286
  },
284
287
  mt5_close: {
@@ -310,7 +313,7 @@ const definitions = NAMES.map((name) => {
310
313
  },
311
314
  mt5_pending: {
312
315
  description:
313
- 'EMİR AÇAR (6 tip): mt5_pending {symbol, type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop"|"buy_market"|"sell_market", volume, price?, sl?, tp?, reason?}. Limit/stop = bekleyen emir (price zorunlu); buy_market/sell_market = ANLIK piyasa emri. Otomatik işlem anahtarına tabidir. reason: kararın tezi (günlüğe yazılır).',
316
+ 'BEKLEYEN EMİR (limit/stop) AÇAR: mt5_pending {symbol, type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop", volume, price, sl?, tp?, reason?}. Fiyatın seviyeye gelmesini beklemek için kullanılır. ⚡ANLIK giriş (hızlı aksiyon) için bunu kullanma — mt5_trade {symbol, side} ile market gir (buy_market/sell_market tipleri burada da kabul edilir ve anlık emre yönlenir). Otomatik işlem anahtarına tabidir. reason: kararın tezi (günlüğe yazılır).',
314
317
  parameters: {
315
318
  type: 'object',
316
319
  properties: {
@@ -318,6 +321,7 @@ const definitions = NAMES.map((name) => {
318
321
  type: {
319
322
  type: 'string',
320
323
  enum: ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop', 'buy_market', 'sell_market'],
324
+ description: 'Limit/stop = bekleyen (price zorunlu); buy_market/sell_market = anlık piyasa emri',
321
325
  },
322
326
  volume: { type: 'number' },
323
327
  price: { type: 'number', description: 'Tetik fiyatı — limit/stop için zorunlu, market tiplerinde gerekmez' },
@@ -420,7 +424,9 @@ const handlers = {
420
424
  async mt5_alerts(args, ctx) {
421
425
  const action = String(args.action || 'list').toLowerCase();
422
426
  if (action === 'list') {
423
- const list = alertsApi.list();
427
+ /* mine:true → bu oturumun (senin) kurduğu alarm — temizlik kararı için */
428
+ const sid = ctx && ctx.sessionId ? String(ctx.sessionId) : '';
429
+ const list = alertsApi.list().map((a) => ({ ...a, mine: sid ? String(a.sid || '') === sid : !a.sid }));
424
430
  return { ok: true, count: list.length, alerts: list };
425
431
  }
426
432
  if (action === 'remove') {
@@ -429,6 +435,20 @@ const handlers = {
429
435
  const removed = alertsApi.remove(id);
430
436
  return { ok: !!removed, removed: !!removed, id };
431
437
  }
438
+ if (action === 'clear') {
439
+ /* ALARM TEMİZLİĞİ: alarmı kuran ajan kendi gereksiz alarmlarını siler.
440
+ Varsayılan kapsam: bu oturumun kurdukları (ctx.sessionId); symbol ile
441
+ daralt; ids açıkça verilirse sahiplik aranmaz; all:true → tümü. */
442
+ if (typeof alertsApi.clear !== 'function') return { ok: false, error: 'toplu alarm temizliği desteklenmiyor' };
443
+ const ids = Array.isArray(args.ids) ? args.ids.map((x) => String(x || '').trim()).filter(Boolean) : [];
444
+ const all = args.all === true || String(args.all || '').toLowerCase() === 'true' || String(args.scope || '').toLowerCase() === 'all';
445
+ const symbol = String(args.symbol || '').trim().toUpperCase();
446
+ const sid = ctx && ctx.sessionId ? String(ctx.sessionId) : '';
447
+ const res = alertsApi.clear({ ids, sid, all, symbol }) || {};
448
+ const removed = Number(res.removed) || 0;
449
+ if (removed > 0) noteTrade('alert-clear', { removed, ids: res.ids || [], symbol, all }, ctx || {});
450
+ return { ok: true, removed, ids: res.ids || [] };
451
+ }
432
452
  if (action === 'set') {
433
453
  const symbol = String(args.symbol || '').trim().toUpperCase();
434
454
  const price = Number(args.price);
@@ -466,7 +486,7 @@ const handlers = {
466
486
  noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note, cooldownMin: alarm.cooldownMin, once: alarm.once }, ctx || {});
467
487
  return { ok: true, alert: alarm };
468
488
  }
469
- return { ok: false, error: "action: list|set|remove" };
489
+ return { ok: false, error: "action: list|set|remove|clear" };
470
490
  },
471
491
  async mt5_note(args, ctx) {
472
492
  const symbol = String(args.symbol || '').trim().toUpperCase();
@@ -159,6 +159,57 @@ function alertCooldownActive(a, now) {
159
159
  return last > 0 && Number(now) - last < cd;
160
160
  }
161
161
 
162
+ /* ---- alarm temizliği (ajan kendi kurduğu gereksiz alarmı siler) ----
163
+ opts:
164
+ ids → yalnız bu id'ler (açıkça verilenler sahiplikten bağımsız silinir)
165
+ sid → alarmı kuran ajan oturumu; scope varsayılanı "kendi alarmlarım"
166
+ symbol → yalnız bu sembole daralt
167
+ all → sahiplik filtresini kaldır (tüm finance alarmları)
168
+ Dönüş: silinecek alarm id listesi (saf; IO yok). */
169
+ function pickAlarms(alerts, opts) {
170
+ const o = opts || {};
171
+ const ids = Array.isArray(o.ids) ? o.ids.map((x) => String(x || '')).filter(Boolean) : [];
172
+ const sid = String(o.sid || '');
173
+ const symbol = String(o.symbol || '').toUpperCase();
174
+ const all = !!o.all;
175
+ const out = [];
176
+ for (const a of Array.isArray(alerts) ? alerts : []) {
177
+ if (!a || !a.id) continue;
178
+ if (ids.length) {
179
+ if (ids.includes(String(a.id))) out.push(String(a.id));
180
+ continue;
181
+ }
182
+ if (symbol && String(a.symbol || '').toUpperCase() !== symbol) continue;
183
+ if (all) {
184
+ out.push(String(a.id));
185
+ continue;
186
+ }
187
+ /* sid varsa YALNIZ kendi alarmları; sid yoksa (işçi oturumu) sahipsizler */
188
+ const owner = String(a.sid || '');
189
+ if (sid ? owner === sid : !owner) out.push(String(a.id));
190
+ }
191
+ return out;
192
+ }
193
+
194
+ /* ---- trade saatleri (makinenin YEREL saati) ----
195
+ th: { on, start:'HH:MM', end:'HH:MM' }. on değilse 7/24 açık (true).
196
+ Açıkken yalnız [start,end) aralığında true döner; gece aralığı desteklenir
197
+ (ör. 22:00 → 06:00). start === end → 24 saat. Saf; IO yok. */
198
+ function parseHM(v, defMin) {
199
+ const m = /^([01]?\d|2[0-3]):([0-5]\d)$/.exec(String(v || '').trim());
200
+ if (!m) return defMin;
201
+ return Number(m[1]) * 60 + Number(m[2]);
202
+ }
203
+ function tradeHoursOpen(th, now) {
204
+ if (!th || !th.on) return true;
205
+ const s = parseHM(th.start, 9 * 60);
206
+ const e = parseHM(th.end, 22 * 60);
207
+ if (s === e) return true;
208
+ const d = now instanceof Date ? now : new Date(Number(now) || Date.now());
209
+ const cur = d.getHours() * 60 + d.getMinutes();
210
+ return s < e ? cur >= s && cur < e : cur >= s || cur < e;
211
+ }
212
+
162
213
  /* ---- bekleyen emir (pending) → pozisyon eşleştirme ----
163
214
  Watchdog turu emir listesini karşılaştırır: listeden DÜŞEN emir için aynı
164
215
  turda YENİ açılan (ya da netting hesapta hacmi artan) eşleşen bir pozisyon
@@ -228,4 +279,4 @@ function matchPendingDelta(goneOrders, freshPositions) {
228
279
  return out;
229
280
  }
230
281
 
231
- module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, alertCooldownActive, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
282
+ module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, alertCooldownActive, pickAlarms, parseHM, tradeHoursOpen, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
package/src/main.js CHANGED
@@ -9400,6 +9400,13 @@ function finCfg() {
9400
9400
  /* GÜNLÜK RUTİN: plan/review saatleri (HH:MM; boş = kapalı) — hafta içi cron */
9401
9401
  if (typeof f.planTime !== 'string') f.planTime = '';
9402
9402
  if (typeof f.reviewTime !== 'string') f.reviewTime = '';
9403
+ /* TRADE SAATLERİ: varsayılan KAPALI (7/24). Açıkken Beast Finance ve tüm
9404
+ ajanları YALNIZ [start,end) aralığında çalışır — makinenin YEREL saati;
9405
+ varsayılan aralık 09:00-22:00; gece aralığı desteklenir (22:00 → 06:00). */
9406
+ if (!f.tradeHours || typeof f.tradeHours !== 'object') f.tradeHours = {};
9407
+ if (typeof f.tradeHours.on !== 'boolean') f.tradeHours.on = false;
9408
+ if (!/^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(f.tradeHours.start || '').trim())) f.tradeHours.start = '09:00';
9409
+ if (!/^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(f.tradeHours.end || '').trim())) f.tradeHours.end = '22:00';
9403
9410
  /* RİSK OTOMASYONU (watchdog): +R'da BE, trailing, kısmi TP — main süreç
9404
9411
  saniyelik döngüyle uygular; ajan turunu BEKLEMEZ. 0 = ilgili kural kapalı. */
9405
9412
  if (typeof f.watchdog !== 'boolean') f.watchdog = true;
@@ -9446,6 +9453,26 @@ function finCfg() {
9446
9453
  return f;
9447
9454
  }
9448
9455
 
9456
+ /* ---- TRADE SAATLERİ KAPISI (makinenin YEREL saati) ----
9457
+ tradeHours.on true iken ajan turları ve olay uyandırmaları yalnız
9458
+ [start,end) aralığında çalışır; gece aralığı desteklenir (ör. 22:00 →
9459
+ 06:00). Kapalıyken (varsayılan) 7/24 serbesttir. Açık pozisyon koruması
9460
+ (watchdog: BE/trailing/kısmi TP) bu kapıdan ETKİLENMEZ — risk güvenliği
9461
+ her saat sürer; kapı yalnız ajan ÜRETİMİNİ (tur/karar/uyandırma) kısar.
9462
+ Karar katmanı saf fonksiyondur: finwatch.tradeHoursOpen (birim testli). */
9463
+ function finTradeHoursOpen(now) {
9464
+ return finwatch.tradeHoursOpen(finCfg().tradeHours, now);
9465
+ }
9466
+ /* Kapı kaynaklı ertelemeler günlüğe SEYREK düşer (10 dk'da en fazla 1 satır) */
9467
+ function finTradeHoursBlockedLog(what) {
9468
+ const now = Date.now();
9469
+ if (now - Number(financeState.tradeHoursLogAt || 0) < 10 * 60 * 1000) return false;
9470
+ financeState.tradeHoursLogAt = now;
9471
+ const th = finCfg().tradeHours || {};
9472
+ financeLog(`[saat] trade saatleri dışı (${th.start || '09:00'}-${th.end || '22:00'} yerel) — ${what} ertelendi`);
9473
+ return true;
9474
+ }
9475
+
9449
9476
  /* ANALİZ EKİBİ: trader'ın yanında koşan uzman ajan rolleri — seçilen her rol
9450
9477
  AYRI bir sürekli finance ajanı açar. Hepsi tüm SKILL'lere + mt5 okuma
9451
9478
  araçlarına erişir (finance oturumu oldukları için), İŞLEM AÇMAZLAR.
@@ -9943,6 +9970,21 @@ function finAlertApi() {
9943
9970
  }
9944
9971
  return false;
9945
9972
  },
9973
+ /* TOPLU TEMİZLİK: alarmı KURAN ajan kendi gereksiz alarmlarını siler.
9974
+ Varsayılan kapsam: yalnız bu oturumun (sid) kurduğu alarmlar; symbol
9975
+ verilirse sembole daralır; ids açıkça verilirse sahiplik aranmaz;
9976
+ all:true → sahiplik filtresi kalkar (tüm finance alarmları). */
9977
+ clear: (opts) => {
9978
+ const o = opts || {};
9979
+ const ids = finwatch.pickAlarms(financeState.alerts, o);
9980
+ if (!ids.length) return { removed: 0, ids: [] };
9981
+ const drop = new Set(ids);
9982
+ financeState.alerts = financeState.alerts.filter((a) => !drop.has(String(a.id)));
9983
+ finAlertsSave();
9984
+ const scope = Array.isArray(o.ids) && o.ids.length ? 'verilen id\'ler' : o.all ? 'tüm alarmlar' : 'ajanın kendi alarmları';
9985
+ financeLog(`[alarm] temizlendi: ${ids.length} adet (${scope}${o.symbol ? ' · ' + String(o.symbol).toUpperCase() : ''})`);
9986
+ return { removed: ids.length, ids };
9987
+ },
9946
9988
  };
9947
9989
  }
9948
9990
 
@@ -10367,6 +10409,12 @@ function finWakeAgentDm(sid, text) {
10367
10409
  const id = String(sid || '');
10368
10410
  const agent = financeState.agents.get(id);
10369
10411
  if (!agent || !engine) return false;
10412
+ /* TRADE SAATLERİ KAPISI: aralık dışında olay ajanı UYANDIRMAZ (panel/kanal
10413
+ bildirimi düşmeye devam eder; tur pencere açılınca kendiliğinden başlar) */
10414
+ if (!finTradeHoursOpen()) {
10415
+ finTradeHoursBlockedLog('olay uyandırması');
10416
+ return false;
10417
+ }
10370
10418
  try {
10371
10419
  const body = agent.role
10372
10420
  ? String(text || '') + "\n(ROL HATIRLATMASI: işlem AÇMA — durumu analiz et; gerekiyorsa agent_dm ile ANA TRADER'a bildir.)"
@@ -10527,11 +10575,12 @@ async function finCheckAlerts() {
10527
10575
  sahip koşmuyor/biliniyorsa koşan tüm finance ajanlarına düşer. */
10528
10576
  const wakeTxt =
10529
10577
  `[FİNANS OLAYI — FİYAT ALARMI${tekrar}, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
10530
- `- ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} tetiklendi (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}${cdTxt}\n` +
10578
+ `- ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} tetiklendi (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}${cdTxt} (id: ${a.id})\n` +
10531
10579
  (a.once
10532
10580
  ? 'Alarm TEK SEFERLİKTİ ve kapandı. '
10533
10581
  : `Alarm TEKRARLI açık kalıyor${a.cooldownMin > 0 ? ` — koşul sürerse en erken ${a.cooldownMin} dk sonra yeniden uyarır` : ''}. `) +
10534
- 'Şimdi yap: alarmı kurma nedenini hatırla; fiyat seviyesini ve planını değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver.';
10582
+ 'Şimdi yap: alarmı kurma nedenini hatırla; fiyat seviyesini ve planını değerlendir, gerekiyorsa işlem/uyarı üret. ' +
10583
+ 'TEMİZLİK: alarmın görevi bittiyse (tez geçersiz, pozisyon kapandı, seviye anlamsızlaştı) mt5_alerts action:"remove" {id} ya da action:"clear" ile SİL — gereksiz alarm biriktirme. Kısa rapor ver.';
10535
10584
  if (!finWakeAgentDm(a.sid, wakeTxt)) finWakeAgents(wakeTxt, { kind: 'alarm', symbol: a.symbol });
10536
10585
  }
10537
10586
  if (changed) finAlertsSave();
@@ -11444,6 +11493,7 @@ function finTraderBrief(agent) {
11444
11493
  'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → mt5_rates/mt5_indicators ile teknik okuma → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
11445
11494
  'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
11446
11495
  'Risk otomasyonu main süreçte 5 sn döngüyle çalışır (+R BE, trailing, kısmi TP) — sen yine de SL/TP seviyelerini aktif yönet.',
11496
+ '⚡HIZLI AKSİYON: onaylı fırsatta market buy/sell ile ANINDA gir (mt5_trade {symbol, side, sl, tp, riskPct}); bekleyen emir vermek ZORUNLU DEĞİL — limit/stop yalnız seviye beklemede kurulur.',
11447
11497
  'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
11448
11498
  agent && agent.main && !roleDef ? `İŞLEM GEÇMİŞİN:\n${finBuildDigest()}` : '',
11449
11499
  ].filter(Boolean).join('\n');
@@ -11453,6 +11503,10 @@ function finTraderBrief(agent) {
11453
11503
  'finance-review' job'ları tetikler. Trader koşmuyorsa sessizce atlanır. */
11454
11504
  function finDailyRoutine(mode) {
11455
11505
  try {
11506
+ if (!finTradeHoursOpen()) {
11507
+ financeLog('[rutin] trade saatleri dışı — günlük ' + mode + ' atlandı');
11508
+ return;
11509
+ }
11456
11510
  let mainSid = '';
11457
11511
  for (const [sid, a] of financeState.agents) {
11458
11512
  if (a && a.main) { mainSid = String(sid); break; }
@@ -11554,6 +11608,15 @@ async function finConsultPlan(f, agent, sid) {
11554
11608
  function finAgentRound(sid) {
11555
11609
  const agent = financeState.agents.get(String(sid));
11556
11610
  if (!agent || !engine) return;
11611
+ /* TRADE SAATLERİ KAPISI: aralık dışında YENİ TUR BAŞLATILMAZ — ajan askıya
11612
+ alınır; pencere açılınca 60 sn içinde kaldığı yerden devam eder */
11613
+ if (!finTradeHoursOpen()) {
11614
+ finTradeHoursBlockedLog(`tur #${(Number(agent.round) || 0) + 1}`);
11615
+ clearTimeout(agent.timer);
11616
+ agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, 60000);
11617
+ if (agent.main) finPush('trader', { state: 'hours', round: agent.round, nextInSec: 60 });
11618
+ return;
11619
+ }
11557
11620
  if (engine.isBusy(sid)) {
11558
11621
  /* hâlâ çalışıyor — done eventinde tekrar planlanır */
11559
11622
  return;
@@ -11578,7 +11641,7 @@ function finAgentRound(sid) {
11578
11641
  ? 'SHADOW MOD AÇIK: emir GÖNDERİLMEZ — kararını teziyle raporla (günlüğe yazılır).'
11579
11642
  : 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
11580
11643
  const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
11581
- const round = `FINANCE TUR #${agent.round}: ${focus}hesap + pozisyonlar + fiyatları çek; ${roleDef ? 'rolüne uygun analiz yap (mt5_rates/mt5_indicators ile) ve öneri ver.' : 'açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); mt5_rates/mt5_indicators ile yeni fırsatları değerlendir.'} ${auto} Önemli kararların gerekçesini mt5_note ile günlüğe yaz. Kısa rapor ver.`;
11644
+ const round = `FINANCE TUR #${agent.round}: ${focus}hesap + pozisyonlar + fiyatları çek; ${roleDef ? 'rolüne uygun analiz yap (mt5_rates/mt5_indicators ile) ve öneri ver.' : 'açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); mt5_rates/mt5_indicators ile yeni fırsatları değerlendir. ⚡Onaylı fırsatta market buy/sell ile ANINDA gir (bekleyen emir ZORUNLU DEĞİL); lot için volume yerine riskPct+sl yeter.'} ${auto} Önemli kararların gerekçesini mt5_note ile günlüğe yaz. Kısa rapor ver.`;
11582
11645
  const launch = (planBlock) => {
11583
11646
  if (!financeState.agents.has(String(sid))) return;
11584
11647
  const ok = engine.send(sid, planBlock + round, { userAction: false });
@@ -11616,6 +11679,12 @@ function finWakeAgent(sid) {
11616
11679
  const id = String(sid || '');
11617
11680
  const agent = financeState.agents.get(id);
11618
11681
  if (!agent || !engine) return;
11682
+ /* TRADE SAATLERİ KAPISI: aralık dışında DM uyandırması tur AÇMAZ; mesaj
11683
+ inbox'ta bekler, pencere açılınca okunur */
11684
+ if (!finTradeHoursOpen()) {
11685
+ finTradeHoursBlockedLog('DM uyandırması');
11686
+ return;
11687
+ }
11619
11688
  if (engine.isBusy(id)) {
11620
11689
  /* tur sürüyor: olay dmInbox'ta bekler — tur bitince finFlushOnDone
11621
11690
  BEKLEMEDEN yeni tur açar (interval beklenmez) */
@@ -11903,6 +11972,20 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
11903
11972
  if (p.shadowMode !== undefined) f.shadowMode = !!p.shadowMode;
11904
11973
  if (p.planTime !== undefined) f.planTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.planTime || '').trim()) ? String(p.planTime).trim() : '';
11905
11974
  if (p.reviewTime !== undefined) f.reviewTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.reviewTime || '').trim()) ? String(p.reviewTime).trim() : '';
11975
+ /* TRADE SAATLERİ: {on, start, end} — yerel saat; aralık dışında ajan turları
11976
+ ve olay uyandırmaları durur (varsayılan kapalı, aralık 09:00-22:00) */
11977
+ if (p.tradeHours !== undefined && p.tradeHours && typeof p.tradeHours === 'object') {
11978
+ const th = f.tradeHours && typeof f.tradeHours === 'object' ? f.tradeHours : {};
11979
+ if (typeof p.tradeHours.on === 'boolean') th.on = p.tradeHours.on;
11980
+ for (const k of ['start', 'end']) {
11981
+ if (p.tradeHours[k] !== undefined) {
11982
+ const v = String(p.tradeHours[k] || '').trim().slice(0, 5);
11983
+ if (/^([01]?\d|2[0-3]):([0-5]\d)$/.test(v)) th[k] = v;
11984
+ }
11985
+ }
11986
+ f.tradeHours = th;
11987
+ if (th.on && !finTradeHoursOpen()) financeLog('[saat] trade saatleri açık — ajanlar ' + th.start + '-' + th.end + ' aralığında çalışacak (yerel saat)');
11988
+ }
11906
11989
  if (p.pythonPath !== undefined) f.pythonPath = String(p.pythonPath || '').trim();
11907
11990
  if (p.terminalPath !== undefined) f.terminalPath = String(p.terminalPath || '').trim();
11908
11991
  if (p.traderSel !== undefined) {
@@ -892,6 +892,20 @@
892
892
  </label>
893
893
  <div class="fin-set-note">RİSK OTOMASYONU gizliyken koruma döngüsü (watchdog) kapatılır; tekrar gösterdiğinde ilgili kutucuktan açabilirsin.</div>
894
894
  <div class="fin-set-sep"></div>
895
+ <label class="fin-set-row" title="Varsayılan KAPALI: kapalıyken ajanlar 7/24 çalışır. Açıkken Beast Finance ve tüm ajanları YALNIZ belirlenen saat aralığında çalışır — aralık dışında yeni tur başlatılmaz ve olaylar ajanı uyandırmaz (makinenin YEREL saati). Açık pozisyon koruması (BE/trailing/kısmi TP) her saat çalışmaya devam eder.">
896
+ <input id="finTradeHoursOn" type="checkbox" />
897
+ <span class="fsr-main"><b>TRADE SAATLERİ</b><i>Kapalıyken 7/24 · açıkken ajanlar yalnız bu aralıkta</i></span>
898
+ </label>
899
+ <div id="finTradeHoursWrap" class="fin-set-sub" hidden>
900
+ <span class="fin-set-lbl">BAŞLANGIÇ / BİTİŞ (YEREL SAAT)</span>
901
+ <div class="fin-th-row">
902
+ <input id="finTradeHoursStart" type="time" step="60" value="09:00" />
903
+ <span class="fin-set-lbl">→</span>
904
+ <input id="finTradeHoursEnd" type="time" step="60" value="22:00" />
905
+ </div>
906
+ <div class="fin-set-note" style="margin:0">Aralık dışında ajan turları askıya alınır, olay alarmları ajanı uyandırmaz; pencere açılıp kapanınca kaldığı yerden devam eder. Gece aralığı da verilebilir (ör. 22:00 → 06:00).</div>
907
+ </div>
908
+ <div class="fin-set-sep"></div>
895
909
  <label class="fin-set-row" title="Açıkken botu eklediğin Telegram grubuna yazılan ilk mesaj o grubu AJAN DM aynası yapar. Her AJAN DM grubu Telegram'da AYRI BAŞLIK (topic) olarak canlı akar; başlığa yazdıkların ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır. Grup/Konu seçmene gerek yok.">
896
910
  <input id="finDmTgOn" type="checkbox" />
897
911
  <span class="fsr-main"><b>AJAN DM ↔ TELEGRAM</b><i>Her ajan grubu Telegram'da ayrı başlık — canlı</i></span>
@@ -208,6 +208,10 @@ const els = {
208
208
  finDmTgState: $('#finDmTgState'),
209
209
  finDmTgUnbind: $('#finDmTgUnbind'),
210
210
  finDmTgHint: $('#finDmTgHint'),
211
+ finTradeHoursOn: $('#finTradeHoursOn'),
212
+ finTradeHoursWrap: $('#finTradeHoursWrap'),
213
+ finTradeHoursStart: $('#finTradeHoursStart'),
214
+ finTradeHoursEnd: $('#finTradeHoursEnd'),
211
215
  finRiskCard: $('#finRiskCard'),
212
216
  finNotifyCard: $('#finNotifyCard'),
213
217
  finPerfCard: $('#finPerfCard'),
@@ -9997,6 +10001,7 @@ function finRenderAutomation(cfg, watch) {
9997
10001
  if (els.finWatchInfo) {
9998
10002
  els.finWatchInfo.textContent = watch && watch.on ? '· izlenen ' + (watch.managed || 0) : '';
9999
10003
  }
10004
+ finTradeHoursPaint();
10000
10005
  }
10001
10006
 
10002
10007
  function finSparkline(points) {
@@ -10158,6 +10163,7 @@ function finOnEvent(ev) {
10158
10163
  } else if (ev.fn === 'trader') {
10159
10164
  if (ev.state === 'running') finLogLine('[trader] tur #' + (ev.round || 0) + ' başladı');
10160
10165
  else if (ev.state === 'idle') finLogLine('[trader] tur #' + (ev.round || 0) + ' bitti — sıradaki tur ~' + (ev.nextInSec || '?') + ' sn');
10166
+ else if (ev.state === 'hours') finLogLine('[trader] trade saatleri dışı — ajan askıda (kontrol ~' + (ev.nextInSec || 60) + ' sn)');
10161
10167
  else if (ev.state === 'stopped') finLogLine('[trader] durduruldu');
10162
10168
  } else if (ev.fn === 'install') {
10163
10169
  finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
@@ -10319,11 +10325,37 @@ function finDmTgPaint() {
10319
10325
  function finSettingsOpen() {
10320
10326
  if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = false;
10321
10327
  finDmTgPaint();
10328
+ finTradeHoursPaint();
10322
10329
  }
10323
10330
  function finSettingsCloseModal() {
10324
10331
  if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = true;
10325
10332
  }
10326
10333
 
10334
+ /* ---------- TRADE SAATLERİ (görünüm ayarları modalı) ----------
10335
+ Ayarlar sunucuda (settings.finance.tradeHours) yaşar: on + start/end.
10336
+ Kapalıyken ajanlar 7/24; açıkken yalnız [start,end) aralığında çalışır
10337
+ (makinenin yerel saati). Açık pozisyon koruması her saat sürer. */
10338
+ function finTradeHoursPaint() {
10339
+ if (!els.finTradeHoursOn) return;
10340
+ const th = (finCfgCache && finCfgCache.tradeHours) || {};
10341
+ const on = th.on === true;
10342
+ els.finTradeHoursOn.checked = on;
10343
+ const row = els.finTradeHoursOn.closest('.fin-set-row');
10344
+ if (row) row.classList.toggle('on', on);
10345
+ if (els.finTradeHoursWrap) els.finTradeHoursWrap.hidden = !on;
10346
+ const ae = document.activeElement;
10347
+ if (els.finTradeHoursStart && ae !== els.finTradeHoursStart) els.finTradeHoursStart.value = th.start || '09:00';
10348
+ if (els.finTradeHoursEnd && ae !== els.finTradeHoursEnd) els.finTradeHoursEnd.value = th.end || '22:00';
10349
+ }
10350
+ function finTradeHoursSave() {
10351
+ const on = !!(els.finTradeHoursOn && els.finTradeHoursOn.checked);
10352
+ const start = String((els.finTradeHoursStart && els.finTradeHoursStart.value) || '09:00').slice(0, 5) || '09:00';
10353
+ const end = String((els.finTradeHoursEnd && els.finTradeHoursEnd.value) || '22:00').slice(0, 5) || '22:00';
10354
+ if (finCfgCache) finCfgCache.tradeHours = { on, start, end };
10355
+ finSaveCfg({ tradeHours: { on, start, end } });
10356
+ return { on, start, end };
10357
+ }
10358
+
10327
10359
  function finViewToggle(key, on) {
10328
10360
  finView[key] = !!on;
10329
10361
  finViewApply();
@@ -10373,6 +10405,24 @@ if (els.finDmTgUnbind) {
10373
10405
  toast('Telegram grubu koparıldı — yeni grupta /start yazınca bağlanır');
10374
10406
  });
10375
10407
  }
10408
+ /* TRADE SAATLERİ kontrolleri — anında kaydedilir (yerel saat) */
10409
+ if (els.finTradeHoursOn) {
10410
+ els.finTradeHoursOn.addEventListener('change', () => {
10411
+ const { on, start, end } = finTradeHoursSave();
10412
+ finTradeHoursPaint();
10413
+ toast(on
10414
+ ? 'Trade saatleri AÇIK — Beast Finance ve ajanları yalnız ' + start + '–' + end + ' arasında çalışır (yerel saat)'
10415
+ : 'Trade saatleri kapalı — ajanlar 7/24 serbest');
10416
+ });
10417
+ }
10418
+ for (const el of [els.finTradeHoursStart, els.finTradeHoursEnd]) {
10419
+ if (!el) continue;
10420
+ el.addEventListener('change', () => {
10421
+ const { on, start, end } = finTradeHoursSave();
10422
+ if (on) toast('Trade saatleri: ' + start + '–' + end + ' (yerel saat)');
10423
+ });
10424
+ }
10425
+ finTradeHoursPaint();
10376
10426
  finViewApply();
10377
10427
 
10378
10428
  /* ---------- BÖLÜM SIRALAMA (sürükle-bırak) ----------
@@ -5882,6 +5882,8 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5882
5882
  }
5883
5883
  .fin-set-sub[hidden] { display: none; }
5884
5884
  .fin-set-lbl { font-size: 10.5px; color: var(--muted); letter-spacing: 0.5px; }
5885
+ .fin-th-row { display: flex; align-items: center; gap: 8px; }
5886
+ .fin-th-row input[type="time"] { flex: 1; min-width: 0; }
5885
5887
  .fin-dm-tg-state { font-size: 11.5px; color: var(--text); line-height: 1.4; word-break: break-word; }
5886
5888
  .fin-set-btn {
5887
5889
  align-self: flex-start;