beast-agent 2.52.0 → 2.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/financetools.js +3 -3
- package/src/agent/finwatch.js +13 -1
- package/src/main.js +129 -242
package/package.json
CHANGED
|
@@ -396,7 +396,7 @@ const handlers = {
|
|
|
396
396
|
deals: deals.slice(-60),
|
|
397
397
|
};
|
|
398
398
|
},
|
|
399
|
-
async mt5_alerts(args) {
|
|
399
|
+
async mt5_alerts(args, ctx) {
|
|
400
400
|
const action = String(args.action || 'list').toLowerCase();
|
|
401
401
|
if (action === 'list') {
|
|
402
402
|
const list = alertsApi.list();
|
|
@@ -415,9 +415,9 @@ const handlers = {
|
|
|
415
415
|
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
416
416
|
if (!isFinite(price) || price <= 0) return { ok: false, error: 'price gerekli (pozitif sayı)' };
|
|
417
417
|
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) });
|
|
418
|
+
const alarm = alertsApi.set({ symbol, price, direction, note: String(args.note || '').slice(0, 200), sid: ctx && ctx.sessionId ? String(ctx.sessionId) : '' });
|
|
419
419
|
if (!alarm) return { ok: false, error: 'alarm kurulamadı' };
|
|
420
|
-
noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note }, {});
|
|
420
|
+
noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note }, ctx || {});
|
|
421
421
|
return { ok: true, alert: alarm };
|
|
422
422
|
}
|
|
423
423
|
return { ok: false, error: "action: list|set|remove" };
|
package/src/agent/finwatch.js
CHANGED
|
@@ -107,6 +107,18 @@ 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
|
+
|
|
110
122
|
/* ---- bekleyen emir (pending) → pozisyon eşleştirme ----
|
|
111
123
|
Watchdog turu emir listesini karşılaştırır: listeden DÜŞEN emir için aynı
|
|
112
124
|
turda YENİ açılan (ya da netting hesapta hacmi artan) eşleşen bir pozisyon
|
|
@@ -176,4 +188,4 @@ function matchPendingDelta(goneOrders, freshPositions) {
|
|
|
176
188
|
return out;
|
|
177
189
|
}
|
|
178
190
|
|
|
179
|
-
module.exports = { plan, roundTo, stepRound, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
|
|
191
|
+
module.exports = { plan, roundTo, stepRound, closeKind, 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;
|
|
@@ -10878,20 +10949,6 @@ function finAgentStop(sid, reason) {
|
|
|
10878
10949
|
if (!agent) return false;
|
|
10879
10950
|
clearTimeout(agent.timer);
|
|
10880
10951
|
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
10952
|
if (sid === String(financeState.traderSid || '')) financeState.traderSid = null;
|
|
10896
10953
|
try {
|
|
10897
10954
|
if (engine && engine.isBusy(sid)) {
|
|
@@ -11044,116 +11101,6 @@ async function finConsultPlan(f, agent, sid) {
|
|
|
11044
11101
|
}
|
|
11045
11102
|
}
|
|
11046
11103
|
|
|
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
11104
|
/* Bir ajanın TEK turu: (opsiyonel) ana ajan planı + tur emri */
|
|
11158
11105
|
function finAgentRound(sid) {
|
|
11159
11106
|
const agent = financeState.agents.get(String(sid));
|
|
@@ -11183,26 +11130,9 @@ function finAgentRound(sid) {
|
|
|
11183
11130
|
: 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
|
|
11184
11131
|
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
11185
11132
|
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
11133
|
const launch = (planBlock) => {
|
|
11204
11134
|
if (!financeState.agents.has(String(sid))) return;
|
|
11205
|
-
const ok = engine.send(sid,
|
|
11135
|
+
const ok = engine.send(sid, planBlock + round, { userAction: false });
|
|
11206
11136
|
if (ok) {
|
|
11207
11137
|
financeState.lastRoundAt = Date.now();
|
|
11208
11138
|
if (agent.main) finPush('trader', { state: 'running', round: agent.round });
|
|
@@ -11237,7 +11167,12 @@ function finWakeAgent(sid) {
|
|
|
11237
11167
|
const id = String(sid || '');
|
|
11238
11168
|
const agent = financeState.agents.get(id);
|
|
11239
11169
|
if (!agent || !engine) return;
|
|
11240
|
-
if (engine.isBusy(id))
|
|
11170
|
+
if (engine.isBusy(id)) {
|
|
11171
|
+
/* tur sürüyor: olay dmInbox'ta bekler — tur bitince finFlushOnDone
|
|
11172
|
+
BEKLEMEDEN yeni tur açar (interval beklenmez) */
|
|
11173
|
+
agent.wakePending = true;
|
|
11174
|
+
return;
|
|
11175
|
+
}
|
|
11241
11176
|
clearTimeout(agent.timer);
|
|
11242
11177
|
agent.timer = setTimeout(() => { try { finAgentRound(id); } catch {} }, 300);
|
|
11243
11178
|
}
|
|
@@ -11254,30 +11189,20 @@ function finFlushOnDone(ev) {
|
|
|
11254
11189
|
return;
|
|
11255
11190
|
}
|
|
11256
11191
|
clearTimeout(agent.timer);
|
|
11257
|
-
|
|
11192
|
+
/* TUR SIRASINDA GELEN OLAY/DM: tur bitti → interval BEKLENMEDEN yeni tur
|
|
11193
|
+
(dmInbox'taki olay mesajı bu turda okunur) */
|
|
11194
|
+
if (agent.wakePending) {
|
|
11195
|
+
agent.wakePending = false;
|
|
11196
|
+
financeState.lastRoundAt = Date.now();
|
|
11197
|
+
agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, 800);
|
|
11198
|
+
return;
|
|
11199
|
+
}
|
|
11258
11200
|
const f = finCfg();
|
|
11259
11201
|
const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
|
|
11260
11202
|
financeState.lastRoundAt = Date.now();
|
|
11261
11203
|
if (agent.main) {
|
|
11262
11204
|
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
11205
|
}
|
|
11280
|
-
/* SEMBOL İŞÇİSİ: döngü dışı — kendi aralığıyla koşar */
|
|
11281
11206
|
agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, iv);
|
|
11282
11207
|
}
|
|
11283
11208
|
|
|
@@ -11338,7 +11263,6 @@ ipcMain.handle('finance:snapshot', async () => {
|
|
|
11338
11263
|
});
|
|
11339
11264
|
|
|
11340
11265
|
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
11341
|
-
const wasMode = financeState.mode;
|
|
11342
11266
|
financeState.mode = !!(payload && payload.on);
|
|
11343
11267
|
/* BEAST FINANCE = MT5 KAPISI:
|
|
11344
11268
|
- mod AÇILINCA köprü + MT5 terminali OTOMATİK başlar (watchdog dahil) —
|
|
@@ -11392,23 +11316,8 @@ ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
|
11392
11316
|
}
|
|
11393
11317
|
} catch {}
|
|
11394
11318
|
}
|
|
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
|
-
}
|
|
11319
|
+
/* OTOMATİK TRADER KAPALI: finance moduna geçmek trader'ı KENDİLİĞİNDEN
|
|
11320
|
+
başlatmaz — kullanıcı TRADE AJANI kartındaki ▶ ile elle başlatır */
|
|
11412
11321
|
return { ok: true, mode: financeState.mode, needNew, resumeSid };
|
|
11413
11322
|
});
|
|
11414
11323
|
|
|
@@ -11443,23 +11352,14 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
11443
11352
|
}
|
|
11444
11353
|
if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
|
|
11445
11354
|
/* 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. */
|
|
11355
|
+
koşan tur bitince zaten finFlushOnDone yeni aralığı okur. */
|
|
11449
11356
|
if (p.intervalSec !== undefined && financeState.agents) {
|
|
11450
11357
|
const iv = Math.max(30, Math.round(Number(f.intervalSec) || 120)) * 1000;
|
|
11451
|
-
const hasTeam = finRoleSids().length > 0;
|
|
11452
11358
|
for (const [sid, a] of financeState.agents) {
|
|
11453
11359
|
if (!a || !engine || engine.isBusy(sid)) continue;
|
|
11454
|
-
if (a.main && hasTeam) continue;
|
|
11455
|
-
if (a.role && finMainAgentRef()) continue;
|
|
11456
11360
|
clearTimeout(a.timer);
|
|
11457
11361
|
a.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, iv);
|
|
11458
11362
|
}
|
|
11459
|
-
if (financeState.cycleTimer) {
|
|
11460
|
-
clearTimeout(financeState.cycleTimer);
|
|
11461
|
-
financeState.cycleTimer = setTimeout(() => { try { finTeamKickCycle(); } catch {} }, iv);
|
|
11462
|
-
}
|
|
11463
11363
|
}
|
|
11464
11364
|
if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
|
|
11465
11365
|
if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
|
|
@@ -11594,19 +11494,6 @@ async function financeTraderStart() {
|
|
|
11594
11494
|
finPush('trader', { state: 'running', round: mainAgent.round });
|
|
11595
11495
|
/* ANALİZ EKİBİ: seçili roller için ayrı sürekli ajanlar (koşmıyorsa) */
|
|
11596
11496
|
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
11497
|
return { ok: true, sid: mainSid };
|
|
11611
11498
|
}
|
|
11612
11499
|
|