beast-agent 2.52.0 → 2.54.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.52.0",
4
+ "version": "2.54.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -78,20 +78,25 @@ function call(requests) {
78
78
  const type = String(args.type || '').trim().toLowerCase();
79
79
  const volume = Number(args.volume || 0);
80
80
  const price = Number(args.price || 0);
81
- const izin = ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'];
81
+ /* 6 TİP: 4 bekleyen (limit/stop) + 2 ANLIK piyasa (buy_market/sell_market) */
82
+ const izin = ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop', 'buy_market', 'sell_market'];
82
83
  if (!symbol) return console.log(JSON.stringify({ ok: false, error: 'symbol zorunlu' }));
83
- if (!izin.includes(type)) return console.log(JSON.stringify({ ok: false, error: 'type: buy_limit|sell_limit|buy_stop|sell_stop' }));
84
+ if (!izin.includes(type)) return console.log(JSON.stringify({ ok: false, error: 'type: buy_limit|sell_limit|buy_stop|sell_stop|buy_market|sell_market' }));
84
85
  if (!(volume > 0)) return console.log(JSON.stringify({ ok: false, error: 'volume (lot) zorunlu ve 0 dan buyuk' }));
85
- if (!(price > 0)) return console.log(JSON.stringify({ ok: false, error: 'price zorunlu ve 0 dan buyuk' }));
86
- const params = { symbol: symbol, type: type, volume: volume, price: price };
86
+ const isMarket = type === 'buy_market' || type === 'sell_market';
87
+ if (!isMarket && !(price > 0)) return console.log(JSON.stringify({ ok: false, error: 'price limit/stop emri icin zorunlu ve 0 dan buyuk' }));
88
+ const method = isMarket ? 'market' : 'pending';
89
+ const params = isMarket
90
+ ? { symbol: symbol, side: type.startsWith('buy') ? 'buy' : 'sell', volume: volume }
91
+ : { symbol: symbol, type: type, volume: volume, price: price };
87
92
  if (args.sl) params.sl = Number(args.sl);
88
93
  if (args.tp) params.tp = Number(args.tp);
89
- const r = await call([{ method: 'pending', params: params }]);
94
+ const r = await call([{ method: method, params: params }]);
90
95
  if (r.ok && r.data && r.data.result) {
91
96
  const res = r.data.result;
92
97
  console.log(JSON.stringify({ ok: true, ticket: res.order, retcode: res.retcode, comment: res.comment, detay: r.data }));
93
98
  } else {
94
- console.log(JSON.stringify({ ok: false, error: r.error || 'bekleyen emir reddedildi' }));
99
+ console.log(JSON.stringify({ ok: false, error: r.error || (isMarket ? 'piyasa emri reddedildi' : 'bekleyen emir reddedildi') }));
95
100
  }
96
101
  })();
97
102
 
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "mt5_bekleyen",
3
- "description": "BEKLEYEN EMİR koyar: symbol, type (buy_limit|sell_limit|buy_stop|sell_stop), volume, price zorunlu; sl/tp opsiyonel. Başarıda emir ticket'ı döner.",
3
+ "description": "MT5 EMİR açar (6 tip): buy_limit, sell_limit, buy_stop, sell_stop (bekleyen; price zorunlu) ve buy_market, sell_market (ANLIK piyasa emri; price gerekmez). symbol, type, volume zorunlu; sl/tp opsiyonel. Başarıda emir ticket'ı döner.",
4
4
  "parameters": {
5
5
  "type": "object",
6
6
  "properties": {
7
7
  "symbol": { "type": "string", "description": "Broker sembol adi: GOLD veya BITCOIN (XAUUSD degil)" },
8
- "type": { "type": "string", "description": "buy_limit | sell_limit | buy_stop | sell_stop" },
8
+ "type": { "type": "string", "description": "buy_limit | sell_limit | buy_stop | sell_stop | buy_market | sell_market" },
9
9
  "volume": { "type": "number", "description": "Lot (0.01-0.1)" },
10
- "price": { "type": "number", "description": "Tetiklenme/limit fiyati" },
10
+ "price": { "type": "number", "description": "Tetiklenme/limit fiyati (limit/stop icin zorunlu; market tiplerinde yok sayilir)" },
11
11
  "sl": { "type": "number", "description": "Stop loss (opsiyonel)" },
12
12
  "tp": { "type": "number", "description": "Take profit (opsiyonel)" }
13
13
  },
14
- "required": ["symbol", "type", "volume", "price"]
14
+ "required": ["symbol", "type", "volume"]
15
15
  },
16
- "updatedAt": "2026-09-09T18:50:00.000Z"
16
+ "defaultsVersion": 2,
17
+ "updatedAt": "2026-09-15T14:00:00.000Z"
17
18
  }
@@ -2009,7 +2009,8 @@ 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 (piyasa emri), mt5_close (kapat), mt5_modify (SL/TP), mt5_pending (bekleyen emir), 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_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
+ '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' +
2013
2014
  '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' +
2014
2015
  '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' +
2015
2016
  'YETKİLERİN (AÇIK — çekinmeden kullan):\n' +
@@ -9,6 +9,7 @@ const mt5 = require('../mt5bridge');
9
9
  const finindicators = require('./finindicators');
10
10
  const finstats = require('./finstats');
11
11
  const finrisk = require('./finrisk');
12
+ const finwatch = require('./finwatch');
12
13
 
13
14
  const NAMES = [
14
15
  'mt5_status',
@@ -252,19 +253,25 @@ const definitions = NAMES.map((name) => {
252
253
  },
253
254
  mt5_trade: {
254
255
  description:
255
- 'PİYASA EMRİ AÇAR: mt5_trade {symbol, side:"buy"|"sell", volume, sl?, tp?, comment?, reason?}. Lot limiti ve max pozisyon sayısı sistem tarafından zorlanır. Otomatik işlem anahtarı kapalıysa reddedilir. SL/TP vermek ŞIDDETLİ önerilir. reason: kararın tek cümlelik tezi (günlüğe yazılır, performans değerlendirmesinde kullanılır).',
256
+ '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).',
256
257
  parameters: {
257
258
  type: 'object',
258
259
  properties: {
259
260
  symbol: { type: 'string', description: 'Örn: EURUSD' },
260
- side: { type: 'string', enum: ['buy', 'sell'] },
261
+ side: { type: 'string', enum: ['buy', 'sell'], description: 'Yön (type ile birlikte ya da tek başına: piyasa emri)' },
262
+ type: {
263
+ type: 'string',
264
+ enum: ['market', 'buy_market', 'sell_market', 'buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'],
265
+ description: 'Emir tipi. Boş/side verilirse piyasa emri; limit/stop verilirse bekleyen emir (price zorunlu olur).',
266
+ },
261
267
  volume: { type: 'number', description: 'Lot (max lot sınırına tabi)' },
268
+ price: { type: 'number', description: 'Limit/stop emirlerinde tetik fiyatı (piyasa emrinde gerekmez)' },
262
269
  sl: { type: 'number', description: 'Stop loss fiyatı (0 = yok)' },
263
270
  tp: { type: 'number', description: 'Take profit fiyatı (0 = yok)' },
264
271
  comment: { type: 'string', description: 'Kısa işlem notu' },
265
272
  reason: { type: 'string', description: 'Kararın tezi/gerekçesi (tek cümle)' },
266
273
  },
267
- required: ['symbol', 'side', 'volume'],
274
+ required: ['symbol', 'volume'],
268
275
  },
269
276
  },
270
277
  mt5_close: {
@@ -292,19 +299,22 @@ const definitions = NAMES.map((name) => {
292
299
  },
293
300
  mt5_pending: {
294
301
  description:
295
- 'BEKLEYEN EMİR koyar: mt5_pending {symbol, type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop", volume, price, sl?, tp?, reason?}. Otomatik işlem anahtarına tabidir. reason: kararın tezi (günlüğe yazılır).',
302
+ '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).',
296
303
  parameters: {
297
304
  type: 'object',
298
305
  properties: {
299
306
  symbol: { type: 'string' },
300
- type: { type: 'string', enum: ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'] },
307
+ type: {
308
+ type: 'string',
309
+ enum: ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop', 'buy_market', 'sell_market'],
310
+ },
301
311
  volume: { type: 'number' },
302
- price: { type: 'number' },
312
+ price: { type: 'number', description: 'Tetik fiyatı — limit/stop için zorunlu, market tiplerinde gerekmez' },
303
313
  sl: { type: 'number' },
304
314
  tp: { type: 'number' },
305
315
  reason: { type: 'string', description: 'Kararın tezi/gerekçesi (tek cümle)' },
306
316
  },
307
- required: ['symbol', 'type', 'volume', 'price'],
317
+ required: ['symbol', 'type', 'volume'],
308
318
  },
309
319
  },
310
320
  mt5_cancel: {
@@ -396,7 +406,7 @@ const handlers = {
396
406
  deals: deals.slice(-60),
397
407
  };
398
408
  },
399
- async mt5_alerts(args) {
409
+ async mt5_alerts(args, ctx) {
400
410
  const action = String(args.action || 'list').toLowerCase();
401
411
  if (action === 'list') {
402
412
  const list = alertsApi.list();
@@ -415,9 +425,9 @@ const handlers = {
415
425
  if (!symbol) return { ok: false, error: 'symbol gerekli' };
416
426
  if (!isFinite(price) || price <= 0) return { ok: false, error: 'price gerekli (pozitif sayı)' };
417
427
  if (direction !== 'above' && direction !== 'below') return { ok: false, error: "direction 'above' veya 'below' olmalı" };
418
- const alarm = alertsApi.set({ symbol, price, direction, note: String(args.note || '').slice(0, 200) });
428
+ const alarm = alertsApi.set({ symbol, price, direction, note: String(args.note || '').slice(0, 200), sid: ctx && ctx.sessionId ? String(ctx.sessionId) : '' });
419
429
  if (!alarm) return { ok: false, error: 'alarm kurulamadı' };
420
- noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note }, {});
430
+ noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note }, ctx || {});
421
431
  return { ok: true, alert: alarm };
422
432
  }
423
433
  return { ok: false, error: "action: list|set|remove" };
@@ -523,13 +533,20 @@ const handlers = {
523
533
  };
524
534
  },
525
535
  async mt5_trade(args, ctx) {
536
+ /* TÜM EMİR TİPLERİ: piyasa (buy/sell, buy_market/sell_market) ve
537
+ bekleyen (buy_limit/sell_limit/buy_stop/sell_stop) — bekleyen tip
538
+ verilirse mt5_pending'e devredilir (tek tutarlı risk hattı). */
539
+ const t = finwatch.parseOrderType(args.type || args.orderType || args.side, args.side);
540
+ if (!t.ok) {
541
+ return { ok: false, error: "side 'buy'|'sell' ya da type: buy_market|sell_market|buy_limit|sell_limit|buy_stop|sell_stop" };
542
+ }
543
+ if (t.kind === 'pending') return handlers.mt5_pending({ ...args, type: t.type }, ctx);
526
544
  const cfg = getCfg();
527
545
  if (!cfg.allowTrading) {
528
546
  return { ok: false, error: 'Otomatik işlem KAPALI — analiz/öneri modundasın. İşlem önerisini yaz, açma.' };
529
547
  }
530
548
  if (!mt5.running) return notConnected();
531
- const side = String(args.side || '').toLowerCase();
532
- if (side !== 'buy' && side !== 'sell') return { ok: false, error: "side 'buy' veya 'sell' olmalı" };
549
+ const side = t.side;
533
550
  const symbol = String(args.symbol || '').trim().toUpperCase();
534
551
  if (!symbol) return { ok: false, error: 'symbol gerekli' };
535
552
  const maxLot = Number(cfg.maxLot) || 0.1;
@@ -626,6 +643,13 @@ const handlers = {
626
643
  return { ok: true, result: data && data.result };
627
644
  },
628
645
  async mt5_pending(args, ctx) {
646
+ /* 6 TİP: buy_limit/sell_limit/buy_stop/sell_stop BEKLEYEN emir;
647
+ buy_market/sell_market ANLIK piyasa emri — mt5_trade'e devredilir. */
648
+ const t = finwatch.parseOrderType(args.type || args.orderType, args.side);
649
+ if (!t.ok) {
650
+ return { ok: false, error: 'type: buy_limit|sell_limit|buy_stop|sell_stop|buy_market|sell_market' };
651
+ }
652
+ if (t.kind === 'market') return handlers.mt5_trade({ ...args, type: 'market', orderType: '', side: t.side }, ctx);
629
653
  const cfg = getCfg();
630
654
  if (!cfg.allowTrading) {
631
655
  return { ok: false, error: 'Otomatik işlem KAPALI — bekleyen emir de açılamaz. Öneriyi yaz.' };
@@ -638,8 +662,11 @@ const handlers = {
638
662
  const norm = finrisk.normalizeVolume(info, args.volume, Number(cfg.maxLot) || 0.1);
639
663
  if (norm.error) return { ok: false, error: norm.error };
640
664
  const vol = norm.volume;
641
- const ptype = String(args.type || '').toLowerCase();
642
- const pside = ptype.startsWith('buy') ? 'buy' : ptype.startsWith('sell') ? 'sell' : '';
665
+ const ptype = t.type;
666
+ const pside = t.side;
667
+ if (!(Number(args.price) > 0)) {
668
+ return { ok: false, error: 'price gerekli — limit/stop bekleyen emirleri için tetik fiyatı ver (market tipleri anlıktır, price istemez)' };
669
+ }
643
670
  /* KODLA DİSİPLİN: bekleyen emir de işlem sayılır */
644
671
  let discErr = null;
645
672
  try { discErr = discipline(pside, symbol, []); } catch {}
@@ -658,7 +685,7 @@ const handlers = {
658
685
  tp: Number(args.tp) || 0,
659
686
  comment: String(args.comment || 'Beast').slice(0, 26),
660
687
  }, 20000);
661
- noteTrade('pending', { symbol, type: args.type, volume: vol, reason: String(args.reason || '').slice(0, 500) }, ctx);
688
+ noteTrade('pending', { symbol, type: ptype, volume: vol, reason: String(args.reason || '').slice(0, 500) }, ctx);
662
689
  return { ok: true, result: data && data.result };
663
690
  },
664
691
  async mt5_cancel(args, ctx) {
@@ -107,6 +107,46 @@ function plan(pos, meta, st, cfg) {
107
107
  return out;
108
108
  }
109
109
 
110
+ /* ---- kapanış nedeni (MT5 deal reason) ----
111
+ İşlem SL/TP ile mi kapandı? Saf sınıflandırma: 4 = stop-loss,
112
+ 5 = take-profit, 6 = stop-out (teminat). Diğerleri (manuel/EA/broker)
113
+ BOŞ döner — bu olaylarda ajan uyandırılmaz. */
114
+ function closeKind(reason) {
115
+ const n = Number(reason);
116
+ if (n === 4) return 'stop';
117
+ if (n === 5) return 'tp';
118
+ if (n === 6) return 'stopout';
119
+ return '';
120
+ }
121
+
122
+ /* ---- emir tipi çözümleme (6 tip) ----
123
+ "buy"/"sell" → piyasa (market); buy_market/sell_market → piyasa;
124
+ buy_limit/sell_limit/buy_stop/sell_stop → bekleyen (pending).
125
+ Esnek yazım: "sell limit", "limit_buy", "MARKET BUY", "anlık" aynı sonuca iner.
126
+ fallbackSide: tip yön taşımıyorsa (ör. yalnız "market"/"limit") kullanılır.
127
+ Dönüş: { ok, side:'buy'|'sell', kind:'market'|'pending', type:'buy_market'|... } */
128
+ function parseOrderType(raw, fallbackSide) {
129
+ const s = String(raw || '').toLowerCase().replace(/[\s-]+/g, '_');
130
+ const fb = String(fallbackSide || '').toLowerCase().trim();
131
+ const side = /(^|_)sell(_|$)/.test(s)
132
+ ? 'sell'
133
+ : /(^|_)buy(_|$)/.test(s)
134
+ ? 'buy'
135
+ : fb === 'sell'
136
+ ? 'sell'
137
+ : fb === 'buy'
138
+ ? 'buy'
139
+ : '';
140
+ if (!side) return { ok: false };
141
+ const hasLimit = /limit/.test(s);
142
+ const hasStop = /stop/.test(s);
143
+ const hasMarket = /market|instant|piyasa|anl[ıi]k/.test(s);
144
+ if ((hasLimit || hasStop) && !hasMarket) {
145
+ return { ok: true, side, kind: 'pending', type: side + '_' + (hasLimit ? 'limit' : 'stop') };
146
+ }
147
+ return { ok: true, side, kind: 'market', type: side + '_market' };
148
+ }
149
+
110
150
  /* ---- bekleyen emir (pending) → pozisyon eşleştirme ----
111
151
  Watchdog turu emir listesini karşılaştırır: listeden DÜŞEN emir için aynı
112
152
  turda YENİ açılan (ya da netting hesapta hacmi artan) eşleşen bir pozisyon
@@ -176,4 +216,4 @@ function matchPendingDelta(goneOrders, freshPositions) {
176
216
  return out;
177
217
  }
178
218
 
179
- module.exports = { plan, roundTo, stepRound, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
219
+ module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
package/src/main.js CHANGED
@@ -596,6 +596,16 @@ function startEventBus() {
596
596
  notify: (sub, text) => {
597
597
  try {
598
598
  waLog(`olay bildirimi → sid=${sub.sessionId} (${sub.type})`);
599
+ /* FINANCE AJANI: olay aboneliği alarmı AJAN DM'i olarak düşer ve
600
+ boştaki ajanı tur beklemeden uyandırır; diğer oturumlar aynı. */
601
+ if (financeState.agents.has(String(sub.sessionId))) {
602
+ finWakeAgentDm(
603
+ sub.sessionId,
604
+ `[FİNANS OLAYI — OLAY ABONELİĞİ (${sub.type})]\n${String(text || '')}\n` +
605
+ `Şimdi yap: aboneliği kurma nedenini hatırla; durumu değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver.`
606
+ );
607
+ return;
608
+ }
599
609
  engine.send(sub.sessionId, { text });
600
610
  } catch {}
601
611
  },
@@ -7673,6 +7683,17 @@ function watcherFire(w, value) {
7673
7683
  ? 'izlenen değer değişti'
7674
7684
  : `kural sağlandı (son değer ${value}, koşul ${w.op} ${w.value ?? ''})`;
7675
7685
  toastNotify(`İzleyici: ${w.name}`, target, 'watchers');
7686
+ /* FINANCE AJANI: izleyici alarmı AJAN DM'i olarak düşer ve boştaki ajanı
7687
+ tur beklemeden uyandırır (main.js finWakeAgentDm). Diğer oturumlarda
7688
+ eski davranış: mesaj doğrudan sohbete iner. */
7689
+ if (financeState.agents.has(String(sid))) {
7690
+ finWakeAgentDm(
7691
+ sid,
7692
+ `[FİNANS OLAYI — İZLEYİCİ ALARMI: ${w.name}]\n- ${target}\n` +
7693
+ `Şimdi yap: izleyiciyi kurma nedenini hatırla; durumu değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver.`
7694
+ );
7695
+ return;
7696
+ }
7676
7697
  engine.send(sid, {
7677
7698
  text:
7678
7699
  `[IZLEYICI: ${w.name}] ${target}. ` +
@@ -9237,19 +9258,11 @@ const financeState = {
9237
9258
  pendingOrders: new Map(), /* ticket -> bekleyen emir (son başarılı okuma) */
9238
9259
  pendingReady: false, /* emir tabanı kuruldu mu (ilk tur sessiz; köprü kopunca sıfırlanır) */
9239
9260
  posVolume: new Map(), /* ticket -> hacim (netting hesapta artış = yeni dolum) */
9240
- pendingWarnings: [], /* tur saniyesi beklenmeden gönderilemeyen uyarılar sıradaki tura */
9241
9261
  excDirty: false,
9242
9262
  excSavedAt: 0,
9243
9263
  watchTimer: null,
9244
9264
  watchBusy: false,
9245
9265
  watchTickAt: 0,
9246
- /* TUR DÖNGÜSÜ (ekip → trader): rol ajanlarının turları bitince asıl trader
9247
- tetiklenir; trader turu bitince interval sonra yeni ekip turu başlar */
9248
- teamPending: new Set(), /* bu döngüde rapor beklenen rol ajanları */
9249
- cyclePhase: 'idle', /* idle: sıradaki ekip turu bekleniyor · team: raporlar toplanıyor · trader: trader turda */
9250
- cycleTimer: null, /* trader turundan sonra ekip turlarını başlatan zamanlayıcı */
9251
- cycleGuardTimer: null, /* ekip raporları takılırsa trader'ı zorlayan emniyet zamanlayıcısı */
9252
- traderWaitTimer: null, /* ekip bitince trader'ı tetikleyen kısa gecikme */
9253
9266
  alerts: [], /* fiyat alarmları (kalıcı: finance/alerts.json) */
9254
9267
  stats: null, /* son performans özeti (kalıcı: finance/stats.json) */
9255
9268
  statsAt: 0,
@@ -9777,6 +9790,7 @@ function finAlertsLoad() {
9777
9790
  price: Number(a && a.price) || 0,
9778
9791
  direction: String((a && a.direction) || 'above').toLowerCase(),
9779
9792
  note: String((a && a.note) || '').slice(0, 200),
9793
+ sid: String((a && a.sid) || ''), /* alarmı kuran ajan oturumu (uyandırma hedefi) */
9780
9794
  at: Number(a && a.at) || Date.now(),
9781
9795
  }))
9782
9796
  .filter((a) => a.id && a.symbol && a.price > 0);
@@ -9799,6 +9813,7 @@ function finAlertApi() {
9799
9813
  price,
9800
9814
  direction: String((a && a.direction) || 'above').toLowerCase() === 'below' ? 'below' : 'above',
9801
9815
  note: String((a && a.note) || '').slice(0, 200),
9816
+ sid: String((a && a.sid) || ''),
9802
9817
  at: Date.now(),
9803
9818
  };
9804
9819
  financeState.alerts.push(alarm);
@@ -9952,17 +9967,72 @@ async function finStatsRefresh(force) {
9952
9967
  }
9953
9968
  }
9954
9969
 
9970
+ /* ---- FİNANS OLAY → AJAN DM UYANDIRMA ----
9971
+ Stop/TP/bekleyen emir aktivasyonu/fiyat alarmı gibi olaylar koşan finance
9972
+ ajanlarına AJAN DM'i olarak düşer ve BOŞTAKİ ajanı TUR SANİYESİNİ
9973
+ BEKLEMEDEN uyandırır: dm+wake kaydı engine._pendingReports'a girer;
9974
+ flushPendingReports bunu continuous ajanın dmInbox'ına yazar ve
9975
+ onDmQueued → finWakeAgent turu hemen başlatır. Ajan meşgulse mesaj
9976
+ KAYBOLMAZ — tur biter bitmez (interval beklenmeden) yeni turda okunur
9977
+ (finWakeAgent.wakePending → finFlushOnDone).
9978
+ Olay ayrıca ekip DM grubuna sistem postu düşer (panelde canlı görünür). */
9979
+ function finWakeAgentDm(sid, text) {
9980
+ const id = String(sid || '');
9981
+ const agent = financeState.agents.get(id);
9982
+ if (!agent || !engine) return false;
9983
+ try {
9984
+ const body = agent.role
9985
+ ? String(text || '') + "\n(ROL HATIRLATMASI: işlem AÇMA — durumu analiz et; gerekiyorsa agent_dm ile ANA TRADER'a bildir.)"
9986
+ : String(text || '');
9987
+ (engine._pendingReports = engine._pendingReports || []).push({
9988
+ parentId: id,
9989
+ text: body,
9990
+ dm: true,
9991
+ wake: true,
9992
+ });
9993
+ engine.flushPendingReports(id);
9994
+ return true;
9995
+ } catch {
9996
+ return false;
9997
+ }
9998
+ }
9999
+
10000
+ /* Tüm koşan finance ajanlarını olayla uyandır. Dönüş: uyandırılan ajan sayısı. */
10001
+ function finWakeAgents(text, meta) {
10002
+ const body = String(text || '').trim();
10003
+ if (!body || !engine) return 0;
10004
+ if (!financeState.agents.size) {
10005
+ financeLog('[olay] koşan finance ajanı yok — uyarı yalnız günlük/bildirimde' + (meta && meta.kind ? ' (' + meta.kind + ')' : ''));
10006
+ return 0;
10007
+ }
10008
+ /* Panel görünürlüğü: olay ekip DM grubuna sistem postu olarak düşer */
10009
+ try {
10010
+ if (typeof engine._agentTeamPost === 'function') {
10011
+ engine._agentTeamPost('team:finance', 'finance', 'Beast Finance', body);
10012
+ }
10013
+ } catch {}
10014
+ let sent = 0;
10015
+ for (const [sid] of financeState.agents) {
10016
+ if (finWakeAgentDm(sid, body)) sent += 1;
10017
+ }
10018
+ return sent;
10019
+ }
10020
+
9955
10021
  /* ---- işlem günlüğü (görülen/kapanan pozisyonlar) ---- */
9956
10022
  async function finRecordClose(ticket, st) {
9957
10023
  let net = null;
9958
10024
  let symbol = (st && st.symbol) || '';
10025
+ let closeReason = null; /* kapanışı yapan son deal'in nedeni (4=SL, 5=TP, 6=stop-out) */
9959
10026
  try {
9960
10027
  const r = await mt5bridge.call('deals', { days: 3 }, 12000);
9961
10028
  const deals = (r && r.ok && r.data && r.data.deals) || [];
9962
10029
  const mine = deals.filter((d) => String(d.position_id) === String(ticket));
9963
10030
  if (mine.length) {
9964
10031
  net = 0;
9965
- for (const d of mine) net += (Number(d.profit) || 0) + (Number(d.swap) || 0) + (Number(d.commission) || 0);
10032
+ for (const d of mine) {
10033
+ net += (Number(d.profit) || 0) + (Number(d.swap) || 0) + (Number(d.commission) || 0);
10034
+ if (Number(d.entry) === 1) closeReason = Number(d.reason);
10035
+ }
9966
10036
  symbol = String(mine[0].symbol || symbol);
9967
10037
  }
9968
10038
  } catch {}
@@ -9974,12 +10044,26 @@ async function finRecordClose(ticket, st) {
9974
10044
  financeState.excursions.delete(String(ticket));
9975
10045
  finExcSave();
9976
10046
  finExcFlush(true);
9977
- finJournal({ kind: 'close', ticket, symbol, net: rounded, side: (st && st.side) || '', mfe, mae });
10047
+ const kind = finwatch.closeKind(closeReason);
10048
+ finJournal({ kind: 'close', ticket, symbol, net: rounded, side: (st && st.side) || '', mfe, mae, reason: kind || '' });
9978
10049
  const pl = rounded == null ? '' : ` · K/Z ${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
9979
10050
  const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${pl}`;
9980
10051
  financeLog('[watchdog] ' + line);
9981
10052
  if (finCfg().notifyTrades !== false) financeNotify(line, 'close');
9982
10053
  if (rounded != null) finStatsRefresh(true);
10054
+ /* STOP / TP / STOP-OUT: koşan tüm finance ajanları TUR BEKLEMEDEN uyanır
10055
+ (ajan DM kutusu + boştaysa anında tur) — manuel kapanışta uyandırma yok. */
10056
+ if (kind) {
10057
+ const label = kind === 'stop' ? 'STOP OLDU' : kind === 'tp' ? 'TP OLDU' : 'STOP-OUT (teminat)';
10058
+ const how = kind === 'tp' ? 'take-profit' : kind === 'stop' ? 'stop-loss' : 'stop-out';
10059
+ const netTxt = rounded == null ? '?' : `${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
10060
+ finWakeAgents(
10061
+ `[FİNANS OLAYI — ${label}, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
10062
+ `- ${symbol || '?'}${st && st.side ? ' ' + String(st.side).toUpperCase() : ''} #${ticket} ${how} ile kapandı · net ${netTxt}\n` +
10063
+ `Şimdi yap: pozisyon kapandı — hesabı, risk durumunu (kayıp serisi/günlük limit) ve planını HEMEN değerlendir; gerekiyorsa yeni emir aç ya da BEKLE. Kısa rapor ver.`,
10064
+ { kind, ticket, symbol }
10065
+ );
10066
+ }
9983
10067
  }
9984
10068
 
9985
10069
  /* ---- risk otomasyonu (watchdog) ---- */
@@ -10027,21 +10111,26 @@ async function finCheckAlerts() {
10027
10111
  financeLog('[alarm] ' + line);
10028
10112
  financeNotify(line, 'alert');
10029
10113
  finJournal({ kind: 'alert', symbol: a.symbol, price: a.price, direction: a.direction, note: a.note || '' });
10114
+ /* ALARM SAHİBİ AJAN: alarmı kuran koşan finance ajanı DM'den uyanır;
10115
+ sahip koşmuyor/biliniyorsa koşan tüm finance ajanlarına düşer. */
10116
+ 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
+ `Şimdi yap: alarmı kurma nedenini hatırla; fiyat seviyesini ve planını değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver.`;
10120
+ if (!finWakeAgentDm(a.sid, wakeTxt)) finWakeAgents(wakeTxt, { kind: 'alarm', symbol: a.symbol });
10030
10121
  }
10031
10122
  if (changed) finAlertsSave();
10032
10123
  }
10033
10124
 
10034
10125
  /* ---- BEKLEYEN EMİR HABERLERİ ----
10035
- Aktifleşme uyarısı: günlük + panel/kanal bildirimi + koşan TÜM finance
10036
- ajanlarına TUR SANİYESİ BEKLEMEDEN mesaj. Oturum koşuyorsa engine steer
10037
- tamponuna girer (koşan turun güvenli noktasında okunur), boştaysa anında
10038
- yeni tur başlatır; sonraki turlar normal aralıkla devam eder. Gönderim
10039
- başarısızsa (durdurma kilidi/meşguliyet) uyarı sıradaki tur mesajına eklenir. */
10126
+ Aktifleşme: günlük + panel/kanal bildirimi + koşan TÜM finance ajanlarına
10127
+ AJAN DM'i — boştaki ajan tur saniyesini beklemeden uyanır, meşgul olan
10128
+ mesajı sıradaki güvenli noktada okur (finWakeAgents). İptal/süre doldu:
10129
+ yalnız günlük + bildirim (uyandırma yok). */
10040
10130
  function finNotifyPendingActivated(list) {
10041
10131
  const items = (Array.isArray(list) ? list : []).filter((x) => x && x.order && x.position);
10042
10132
  if (!items.length) return;
10043
10133
  const f = finCfg();
10044
- const heads = [];
10045
10134
  const detail = [];
10046
10135
  for (const { order: o, position: p } of items) {
10047
10136
  const symbol = String(o.symbol || p.symbol || '?');
@@ -10049,7 +10138,6 @@ function finNotifyPendingActivated(list) {
10049
10138
  const line =
10050
10139
  `⚡ BEKLEYEN EMİR AKTİFLEŞTİ: ${symbol} ${finwatch.orderTypeLabel(o)} ${finwatch.orderVolume(o)}` +
10051
10140
  ` → pozisyon #${String(p.ticket)} @ ${Number(p.price_open) || '?'}`;
10052
- heads.push(line);
10053
10141
  detail.push(
10054
10142
  `- ${symbol} · ${side === 'buy' ? 'ALIŞ' : 'SATIŞ'} · emir #${String(o.ticket || '?')}` +
10055
10143
  ` (${finwatch.orderTypeLabel(o)} @ ${Number(o.price_open) || '?'}) → pozisyon #${String(p.ticket)}` +
@@ -10074,30 +10162,12 @@ function finNotifyPendingActivated(list) {
10074
10162
  }
10075
10163
  }
10076
10164
  const body = [
10077
- '[ACİL UYARI — BEKLEYEN EMİR AKTİFLEŞTİ, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]',
10165
+ '[FİNANS OLAYI — BEKLEYEN EMİR AKTİFLEŞTİ, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]',
10078
10166
  ...detail,
10079
10167
  'Şimdi yap: yeni pozisyonu HEMEN değerlendir — SL/TP yerinde mi, korumasız mı, planına uygun mu; gerekiyorsa SL/TP güncelle ya da kapat. Kısa rapor ver.',
10080
10168
  'NOT: Bu bir ara uyarıdır; tur düzenin bu turdan sonra normal aralığıyla devam eder (tur sayacı/aralık değişmez).',
10081
10169
  ].join('\n');
10082
- let sent = 0;
10083
- for (const [sid, a] of financeState.agents) {
10084
- if (!a || !engine) continue;
10085
- const text = a.role
10086
- ? body + '\n(ROL HATIRLATMASI: işlem AÇMA — pozisyonu analiz et; gerekiyorsa agent_dm ile ANA TRADER\u2019a bildir.)'
10087
- : body;
10088
- try {
10089
- if (engine.send(String(sid), text, { userAction: false })) {
10090
- sent += 1;
10091
- } else {
10092
- financeState.pendingWarnings.push({ sid: String(sid), text, at: Date.now() });
10093
- if (financeState.pendingWarnings.length > 20) {
10094
- financeState.pendingWarnings.splice(0, financeState.pendingWarnings.length - 20);
10095
- }
10096
- }
10097
- } catch {}
10098
- }
10099
- if (!financeState.agents.size) financeLog('[bekleyen] koşan finance ajanı yok — uyarı yalnız günlük/bildirimde');
10100
- else if (!sent) financeLog('[bekleyen] uyarı hemen gönderilemedi — sıradaki turlara iliştirildi');
10170
+ finWakeAgents(body, { kind: 'pending-active' });
10101
10171
  }
10102
10172
 
10103
10173
  /* Aktifleşmeden listeden düşen emir (iptal/süre doldu): günlük + bildirim */
@@ -10220,9 +10290,10 @@ async function finWatchTick() {
10220
10290
  }
10221
10291
  /* ---- BEKLEYEN EMİR AKTİFLİĞİ ----
10222
10292
  Emir listesi her tur karşılaştırılır; listeden düşen emir için aynı anda
10223
- yeni/eşleşen bir pozisyon oluştuysa emir AKTİFLEŞTİ → ajanlar TUR
10224
- SANİYESİNİ BEKLEMEDEN uyarılır; tur döngüsü sonra normal aralıkla sürer.
10225
- (Köprü kesintisinde taban sıfırlanır — yanlış "aktifleşti" uyarısı yok.) */
10293
+ yeni/eşleşen bir pozisyon oluştuysa emir AKTİFLEŞTİ → koşan finance
10294
+ ajanları TUR SANİYESİNİ BEKLEMEDEN ajan DM'iyle uyandırılır; tur düzeni
10295
+ sonra normal aralıkla sürer. (Köprü kesintisinde taban sıfırlanır —
10296
+ yanlış "aktifleşti" uyarısı yok.) */
10226
10297
  try {
10227
10298
  if (!(ordersOk && positionsOk)) {
10228
10299
  financeState.pendingReady = false;
@@ -10445,17 +10516,20 @@ try {
10445
10516
  girmez) — başarılı emir/SL-TP/bekleyen/iptal olayları aynı hatta düşer */
10446
10517
  customtools.setTradeHook((id, args, res, sid) => {
10447
10518
  if (!res || res.ok !== true) return;
10519
+ const a = args || {};
10520
+ /* mt5_bekleyen 6 tip kabul eder: market tipleri ANLIK işlem sayılır */
10448
10521
  const kind = {
10449
10522
  mt5_emir: 'trade',
10450
10523
  mt5_sltp: 'modify',
10451
- mt5_bekleyen: 'pending',
10524
+ mt5_bekleyen: /_market$/i.test(String(a.type || '')) ? 'trade' : 'pending',
10452
10525
  mt5_emir_iptal: 'cancel',
10453
10526
  mt5_kapat: 'close',
10454
10527
  }[String(id || '')];
10455
10528
  if (!kind) return;
10456
- const a = args || {};
10457
- if (kind === 'trade') finTradeEvent('trade', { symbol: a.symbol, side: a.side, volume: a.volume, sl: a.sl, tp: a.tp, reason: a.reason || a.comment || '', result: res }, sid);
10458
- else if (kind === 'modify') finTradeEvent('modify', { ticket: a.ticket, sl: a.sl, tp: a.tp }, sid);
10529
+ if (kind === 'trade') {
10530
+ const side = a.side || (/^buy/i.test(String(a.type || '')) ? 'buy' : /^sell/i.test(String(a.type || '')) ? 'sell' : '');
10531
+ finTradeEvent('trade', { symbol: a.symbol, side, volume: a.volume, sl: a.sl, tp: a.tp, reason: a.reason || a.comment || '', result: res }, sid);
10532
+ } else if (kind === 'modify') finTradeEvent('modify', { ticket: a.ticket, sl: a.sl, tp: a.tp }, sid);
10459
10533
  else if (kind === 'pending') finTradeEvent('pending', { symbol: a.symbol, type: a.type, volume: a.volume, reason: a.reason || '' }, sid);
10460
10534
  else if (kind === 'cancel') finTradeEvent('cancel', { ticket: a.ticket }, sid);
10461
10535
  else if (kind === 'close') finTradeEvent('close', { ticket: a.ticket, volume: a.volume || 'all' }, sid);
@@ -10878,20 +10952,6 @@ function finAgentStop(sid, reason) {
10878
10952
  if (!agent) return false;
10879
10953
  clearTimeout(agent.timer);
10880
10954
  financeState.agents.delete(sid);
10881
- /* TUR DÖNGÜSÜ temizliği: trader gidince döngü tamamen durur; ekip ajanı
10882
- gidince raporu bekleyen döngü kalanlarla tamamlanır (bekleme boşa düşmez) */
10883
- try {
10884
- if (agent.main) {
10885
- clearTimeout(financeState.cycleTimer); financeState.cycleTimer = null;
10886
- clearTimeout(financeState.cycleGuardTimer); financeState.cycleGuardTimer = null;
10887
- clearTimeout(financeState.traderWaitTimer); financeState.traderWaitTimer = null;
10888
- financeState.teamPending = new Set();
10889
- financeState.cyclePhase = 'idle';
10890
- } else if (agent.role) {
10891
- financeState.teamPending.delete(sid);
10892
- finMaybeTraderAfterTeam(false);
10893
- }
10894
- } catch {}
10895
10955
  if (sid === String(financeState.traderSid || '')) financeState.traderSid = null;
10896
10956
  try {
10897
10957
  if (engine && engine.isBusy(sid)) {
@@ -10928,7 +10988,7 @@ function finTraderBrief(agent) {
10928
10988
  `Tur aralığı: ${f.intervalSec} sn · Max lot: ${f.maxLot} · Max eşzamanlı pozisyon: ${f.maxPositions}`,
10929
10989
  roleDef
10930
10990
  ? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
10931
- : 'Otomatik işlem AÇIK (daima): mt5_trade/mt5_close/mt5_modify/mt5_pending kullanabilirsin (limitler sistemce zorlanır). Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı.',
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ı.',
10932
10992
  roleDef
10933
10993
  ? '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.'
10934
10994
  : f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
@@ -11044,116 +11104,6 @@ async function finConsultPlan(f, agent, sid) {
11044
11104
  }
11045
11105
  }
11046
11106
 
11047
- /* ---- TUR DÖNGÜSÜ: EKİP TURU → ASIL TRADER ----
11048
- Ekip (rol) ajanları sırayla değil AYNI ANDA turlar; HEPSİ bitince asıl
11049
- trader'ın turu tetiklenir — böylece teknik/görsel/haber raporları TAZEYKEN
11050
- karar verir (finTeamDigest aynı turda enjekte edilir). Trader turu bitince
11051
- `intervalSec` sonra yeni ekip turu başlar. Ekip yoksa trader kendi
11052
- aralığıyla koşar (eski davranış). Sembol işçileri döngüye girmez. */
11053
-
11054
- function finRoleSids() {
11055
- const out = [];
11056
- for (const [sid, a] of financeState.agents) if (a && a.role) out.push(String(sid));
11057
- return out;
11058
- }
11059
-
11060
- function finMainAgentRef() {
11061
- for (const [sid, a] of financeState.agents) if (a && a.main) return { sid: String(sid), agent: a };
11062
- return null;
11063
- }
11064
-
11065
- /* Ekip turları için EMNİYET: raporlar takılır/döngü koparsa trader zorlanır */
11066
- function finArmCycleGuard() {
11067
- clearTimeout(financeState.cycleGuardTimer);
11068
- const iv = Math.max(30, Number(finCfg().intervalSec) || 120) * 1000;
11069
- const guard = Math.max(10 * 60 * 1000, iv * 5);
11070
- financeState.cycleGuardTimer = setTimeout(() => {
11071
- financeState.cycleGuardTimer = null;
11072
- if (financeState.cyclePhase !== 'team') return;
11073
- financeLog('[tur] ekip raporları zaman aşımı — trader turu zorlanıyor');
11074
- financeState.teamPending = new Set();
11075
- financeState.cyclePhase = 'idle';
11076
- finMaybeTraderAfterTeam(true);
11077
- }, guard);
11078
- }
11079
-
11080
- /* Ekip turlarını başlat (yeni döngü): rol ajanları hafif gecikmeyle sıraya girer */
11081
- function finTeamKickCycle() {
11082
- clearTimeout(financeState.cycleTimer);
11083
- financeState.cycleTimer = null;
11084
- clearTimeout(financeState.traderWaitTimer);
11085
- financeState.traderWaitTimer = null;
11086
- const roles = finRoleSids();
11087
- if (!roles.length) {
11088
- financeState.cyclePhase = 'idle';
11089
- return;
11090
- }
11091
- financeState.teamPending = new Set(roles);
11092
- financeState.cyclePhase = 'team';
11093
- finArmCycleGuard();
11094
- roles.forEach((sid, i) => {
11095
- const a = financeState.agents.get(sid);
11096
- if (!a) return;
11097
- clearTimeout(a.timer);
11098
- a.timer = setTimeout(() => {
11099
- a.timer = null;
11100
- try { finAgentRound(sid); } catch {}
11101
- }, 400 + i * 1200);
11102
- });
11103
- financeLog('[tur] ekip turları başladı (' + roles.length + ' ajan) — bitince trader tetiklenecek');
11104
- }
11105
-
11106
- /* Ekip ajanı turu bitti: HEPSİ bittiyse asıl trader'ı tetikle.
11107
- force=true (emniyet): bekleyen raporlar boş sayılır. */
11108
- function finMaybeTraderAfterTeam(force) {
11109
- if (!force && financeState.cyclePhase !== 'team') return;
11110
- const p = financeState.teamPending || new Set();
11111
- financeState.teamPending = p;
11112
- for (const s of [...p]) if (!financeState.agents.has(s)) p.delete(s);
11113
- if (!force && p.size) return;
11114
- financeState.teamPending = new Set();
11115
- const main = finMainAgentRef();
11116
- if (!main || !engine) {
11117
- financeState.cyclePhase = 'idle';
11118
- return;
11119
- }
11120
- financeState.cyclePhase = 'trader';
11121
- clearTimeout(financeState.cycleGuardTimer);
11122
- financeState.cycleGuardTimer = null;
11123
- clearTimeout(financeState.traderWaitTimer);
11124
- const fire = () => {
11125
- financeState.traderWaitTimer = null;
11126
- if (!financeState.agents.has(main.sid)) return;
11127
- if (engine.isBusy(main.sid)) {
11128
- /* trader başka turda (DM uyandırması vb.) — kısa süre sonra yeniden dene */
11129
- financeState.traderWaitTimer = setTimeout(fire, 5000);
11130
- return;
11131
- }
11132
- financeLog('[tur] ekip raporları tamam — trader turu tetiklendi');
11133
- finAgentRound(main.sid);
11134
- };
11135
- financeState.traderWaitTimer = setTimeout(fire, 1200);
11136
- }
11137
-
11138
- /* Trader turu bitti → interval sonra yeni ekip turu (ekip varsa) */
11139
- function finCycleAfterTrader() {
11140
- const f = finCfg();
11141
- const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
11142
- clearTimeout(financeState.cycleTimer);
11143
- financeState.cycleTimer = null;
11144
- clearTimeout(financeState.traderWaitTimer);
11145
- financeState.traderWaitTimer = null;
11146
- clearTimeout(financeState.cycleGuardTimer);
11147
- financeState.cycleGuardTimer = null;
11148
- financeState.teamPending = new Set();
11149
- financeState.cyclePhase = 'idle';
11150
- if (!finRoleSids().length) return;
11151
- financeState.cycleTimer = setTimeout(() => {
11152
- financeState.cycleTimer = null;
11153
- try { finTeamKickCycle(); } catch {}
11154
- }, iv);
11155
- }
11156
-
11157
11107
  /* Bir ajanın TEK turu: (opsiyonel) ana ajan planı + tur emri */
11158
11108
  function finAgentRound(sid) {
11159
11109
  const agent = financeState.agents.get(String(sid));
@@ -11183,26 +11133,9 @@ function finAgentRound(sid) {
11183
11133
  : 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
11184
11134
  const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
11185
11135
  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.`;
11186
- /* TUR SANİYESİNİ BEKLEMEDEN gönderilemeyen uyarılar (bekleyen emir
11187
- aktivasyonu): sıradaki tur mesajına en başta iliştirilir. */
11188
- let warnPrefix = '';
11189
- try {
11190
- const q = financeState.pendingWarnings || [];
11191
- if (q.length) {
11192
- const mine = q.filter((w) => w && String(w.sid) === String(sid));
11193
- if (mine.length) {
11194
- financeState.pendingWarnings = q.filter((w) => mine.indexOf(w) === -1);
11195
- warnPrefix = mine.map((w) => String(w.text || '')).join('\n\n') + '\n\n';
11196
- } else {
11197
- /* yaşlı kayıtları buda (duran/silinmiş ajanlar için birikmesin) */
11198
- const now = Date.now();
11199
- financeState.pendingWarnings = q.filter((w) => w && now - Number(w.at || 0) < 15 * 60000);
11200
- }
11201
- }
11202
- } catch {}
11203
11136
  const launch = (planBlock) => {
11204
11137
  if (!financeState.agents.has(String(sid))) return;
11205
- const ok = engine.send(sid, warnPrefix + planBlock + round, { userAction: false });
11138
+ const ok = engine.send(sid, planBlock + round, { userAction: false });
11206
11139
  if (ok) {
11207
11140
  financeState.lastRoundAt = Date.now();
11208
11141
  if (agent.main) finPush('trader', { state: 'running', round: agent.round });
@@ -11237,7 +11170,12 @@ function finWakeAgent(sid) {
11237
11170
  const id = String(sid || '');
11238
11171
  const agent = financeState.agents.get(id);
11239
11172
  if (!agent || !engine) return;
11240
- if (engine.isBusy(id)) return;
11173
+ if (engine.isBusy(id)) {
11174
+ /* tur sürüyor: olay dmInbox'ta bekler — tur bitince finFlushOnDone
11175
+ BEKLEMEDEN yeni tur açar (interval beklenmez) */
11176
+ agent.wakePending = true;
11177
+ return;
11178
+ }
11241
11179
  clearTimeout(agent.timer);
11242
11180
  agent.timer = setTimeout(() => { try { finAgentRound(id); } catch {} }, 300);
11243
11181
  }
@@ -11254,30 +11192,20 @@ function finFlushOnDone(ev) {
11254
11192
  return;
11255
11193
  }
11256
11194
  clearTimeout(agent.timer);
11257
- agent.timer = null;
11195
+ /* TUR SIRASINDA GELEN OLAY/DM: tur bitti → interval BEKLENMEDEN yeni tur
11196
+ (dmInbox'taki olay mesajı bu turda okunur) */
11197
+ if (agent.wakePending) {
11198
+ agent.wakePending = false;
11199
+ financeState.lastRoundAt = Date.now();
11200
+ agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, 800);
11201
+ return;
11202
+ }
11258
11203
  const f = finCfg();
11259
11204
  const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
11260
11205
  financeState.lastRoundAt = Date.now();
11261
11206
  if (agent.main) {
11262
11207
  finPush('trader', { state: 'idle', round: agent.round, nextInSec: iv / 1000 });
11263
- /* TUR DÖNGÜSÜ: ekip varsa trader KENDİ KENDİNE zamanlanmaz — interval
11264
- sonra ekip turları başlar, hepsi bitince trader yeniden tetiklenir.
11265
- (Ekip turu hâlâ sürüyorsa veya trader tetiği bekliyorsa dokunma.) */
11266
- if (finRoleSids().length) {
11267
- if (financeState.cyclePhase !== 'team' && !financeState.traderWaitTimer) finCycleAfterTrader();
11268
- } else {
11269
- agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, iv);
11270
- }
11271
- return;
11272
- }
11273
- if (agent.role) {
11274
- /* EKİP AJANI turu bitti → raporlar tamamlanınca asıl trader tetiklenir.
11275
- Trader koşmuyorsa (nadir) rol ajanı kendi aralığıyla devam eder. */
11276
- finMaybeTraderAfterTeam(false);
11277
- if (!finMainAgentRef()) agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, iv);
11278
- return;
11279
11208
  }
11280
- /* SEMBOL İŞÇİSİ: döngü dışı — kendi aralığıyla koşar */
11281
11209
  agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, iv);
11282
11210
  }
11283
11211
 
@@ -11338,7 +11266,6 @@ ipcMain.handle('finance:snapshot', async () => {
11338
11266
  });
11339
11267
 
11340
11268
  ipcMain.handle('finance:mode', async (_e, payload) => {
11341
- const wasMode = financeState.mode;
11342
11269
  financeState.mode = !!(payload && payload.on);
11343
11270
  /* BEAST FINANCE = MT5 KAPISI:
11344
11271
  - mod AÇILINCA köprü + MT5 terminali OTOMATİK başlar (watchdog dahil) —
@@ -11392,23 +11319,8 @@ ipcMain.handle('finance:mode', async (_e, payload) => {
11392
11319
  }
11393
11320
  } catch {}
11394
11321
  }
11395
- /* TRADER OTOMATİK BAŞLAT: Beast Finance moduna GEÇİLİNCE (kapalıyken
11396
- açılınca) TRADE AJANI kendiliğinden koşar — ▶ Ajanı Başlat beklenmez;
11397
- analiz ekibi de aynı akışta başlar. Zaten koşuyorsa sessizce onaylanır.
11398
- /stop kilidi açıkken kendiliğinden başlatılmaz (kilit yalnız açık bir
11399
- başlat eylemiyle — /start ya da ▶ — açılır). */
11400
- if (financeState.mode && !wasMode && engine && !engine._stopped) {
11401
- financeTraderStart()
11402
- .then((r) => {
11403
- if (r && r.ok) {
11404
- if (!r.already) financeLog('[trader] otomatik başlatıldı (finance modu açıldı)');
11405
- } else if (r && r.error) {
11406
- financeLog('[trader] otomatik başlatılamadı: ' + r.error);
11407
- try { financeNotify('⚠️ Trader otomatik başlatılamadı: ' + r.error, 'risk'); } catch {}
11408
- }
11409
- })
11410
- .catch(() => {});
11411
- }
11322
+ /* OTOMATİK TRADER KAPALI: finance moduna geçmek trader'ı KENDİLİĞİNDEN
11323
+ başlatmaz — kullanıcı TRADE AJANI kartındaki ▶ ile elle başlatır */
11412
11324
  return { ok: true, mode: financeState.mode, needNew, resumeSid };
11413
11325
  });
11414
11326
 
@@ -11443,23 +11355,14 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
11443
11355
  }
11444
11356
  if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
11445
11357
  /* TUR ARALIĞI CANLI: bekleyen zamanlayıcılar yeni aralıkla yeniden kurulur —
11446
- koşan tur bitince zaten finFlushOnDone yeni aralığı okur. TUR DÖNGÜSÜ
11447
- yönetimindeki trader/rol ajanları kendi zamanlayıcısını KULLANMAZ —
11448
- döngü zamanlayıcısı aşağıda yeni aralıkla kurulur. */
11358
+ koşan tur bitince zaten finFlushOnDone yeni aralığı okur. */
11449
11359
  if (p.intervalSec !== undefined && financeState.agents) {
11450
11360
  const iv = Math.max(30, Math.round(Number(f.intervalSec) || 120)) * 1000;
11451
- const hasTeam = finRoleSids().length > 0;
11452
11361
  for (const [sid, a] of financeState.agents) {
11453
11362
  if (!a || !engine || engine.isBusy(sid)) continue;
11454
- if (a.main && hasTeam) continue;
11455
- if (a.role && finMainAgentRef()) continue;
11456
11363
  clearTimeout(a.timer);
11457
11364
  a.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, iv);
11458
11365
  }
11459
- if (financeState.cycleTimer) {
11460
- clearTimeout(financeState.cycleTimer);
11461
- financeState.cycleTimer = setTimeout(() => { try { finTeamKickCycle(); } catch {} }, iv);
11462
- }
11463
11366
  }
11464
11367
  if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
11465
11368
  if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
@@ -11594,19 +11497,6 @@ async function financeTraderStart() {
11594
11497
  finPush('trader', { state: 'running', round: mainAgent.round });
11595
11498
  /* ANALİZ EKİBİ: seçili roller için ayrı sürekli ajanlar (koşmıyorsa) */
11596
11499
  finTeamStart(f);
11597
- /* TUR DÖNGÜSÜ başlangıcı: ilk ekip raporları bitince trader karar turuna
11598
- geçer; trader brief turu bitince de yeni ekip turu interval ile başlar */
11599
- try {
11600
- const roles = finRoleSids();
11601
- if (roles.length) {
11602
- financeState.teamPending = new Set(roles);
11603
- financeState.cyclePhase = 'team';
11604
- finArmCycleGuard();
11605
- } else {
11606
- financeState.teamPending = new Set();
11607
- financeState.cyclePhase = 'idle';
11608
- }
11609
- } catch {}
11610
11500
  return { ok: true, sid: mainSid };
11611
11501
  }
11612
11502