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
|
@@ -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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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:
|
|
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": "
|
|
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"
|
|
14
|
+
"required": ["symbol", "type", "volume"]
|
|
15
15
|
},
|
|
16
|
-
"
|
|
16
|
+
"defaultsVersion": 2,
|
|
17
|
+
"updatedAt": "2026-09-15T14:00:00.000Z"
|
|
17
18
|
}
|
package/src/agent/engine.js
CHANGED
|
@@ -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 (
|
|
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
|
-
'
|
|
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', '
|
|
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
|
-
'
|
|
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: {
|
|
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'
|
|
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 =
|
|
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 =
|
|
642
|
-
const pside =
|
|
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:
|
|
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) {
|
package/src/agent/finwatch.js
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
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
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
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
|
-
'[
|
|
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
|
-
|
|
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İ →
|
|
10224
|
-
SANİYESİNİ BEKLEMEDEN
|
|
10225
|
-
(Köprü kesintisinde taban sıfırlanır —
|
|
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
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
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
|
|
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,
|
|
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))
|
|
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
|
-
|
|
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
|
|
11396
|
-
|
|
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.
|
|
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
|
|