beast-agent 2.58.2 → 2.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +2 -1
- package/src/agent/financetools.js +23 -5
- package/src/agent/finwatch.js +52 -1
- package/src/main.js +84 -2
- package/src/renderer/index.html +14 -0
- package/src/renderer/renderer.js +50 -0
- package/src/renderer/style.css +2 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -2032,9 +2032,10 @@ class Engine {
|
|
|
2032
2032
|
modeBlock2 + '\n' +
|
|
2033
2033
|
(roleBlockFull ? roleBlockFull + '\n' : '') +
|
|
2034
2034
|
(teamLine || '') +
|
|
2035
|
-
'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_alerts (fiyat alarmı: modu SEN seçersin
|
|
2035
|
+
'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_alerts (fiyat alarmı: kur/listele/sil — modu SEN seçersin: once/repeat), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (TÜM TİPLER: buy/sell market + buy/sell limit/stop), mt5_close (TAM ya da KISMİ kapat: percent ile kısmi TP & kısmi stop — karar ajanın), mt5_modify (SL/TP), mt5_pending (TÜM TİPLER: buy_limit/sell_limit/buy_stop/sell_stop bekleyen + buy_market/sell_market anlık), mt5_cancel (emir iptal).\n' +
|
|
2036
2036
|
'EMİR TİPLERİ (6 tip — HEPSİ AÇIK): buy_market/sell_market = anlık piyasa emri (fiyat beklenmez), buy_limit/sell_limit = bekleyen limit (price zorunlu), buy_stop/sell_stop = bekleyen stop (price zorunlu). mt5_trade ve mt5_pending İKİSİ DE 6 tipi kabul eder; emir tipini net söyle (ör. type:"sell_limit").\n' +
|
|
2037
2037
|
'ALARM KARARI SENDE: mt5_alerts ile alarm kurarken mode ZORUNLU — "once" (tek seferlik; ilk tetiklemede kapanır) ya da "repeat" (tekrarlı; alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar uyarır + seni uyandırır; cooldownMin varsayılan 5). Tekrarlı mı tek seferlik mi olacağını sen seç, varsayılana bırakma; seviyenin önemine göre cooldownMin belirle (ör. 5/15/60).\n' +
|
|
2038
|
+
'ALARM TEMİZLİĞİ (ZORUNLU DİSİPLİN): alarm bir BEKÇİDİR, süs değil — gereksiz alarm biriktirme. Her turda (ya da en az birkaç turda bir) mt5_alerts action:"list" ile alarmları gör; listede "mine:true" senin kurduklarındır. Tezi/geçerliliği biten alarmı SİL: seviye kırıldı ve artık anlamsız, pozisyon kapandı, plan/hedef değişti, tetiklendi ve işlevi kalmadı. Silme: tekil kayıt action:"remove" {id} · kendi gereksizlerini toplu action:"clear" (varsayılan yalnız SENİN alarmların; symbol ile daralt; tümünü temizlemek için all:true — bunu başka ajanların alarmlarını da sileceği için YALNIZ açıkça gerekliyse kullan). Akan fiyatta işe yaramayan, üst üste aynı bölgeye kurulmuş alarmları temiz tut.\n' +
|
|
2038
2039
|
'KISMİ KAPATMA YETKİSİ (TÜM finance ajanlarında): açık pozisyonda kısmi kapatma — kısmi TP (kâr al) ve kısmi stop (zarar kes) — TAMAMEN SENİN KARARIN, OTOMATİK DEĞİLDİR; mt5_close {ticket, percent, kind:"partial_tp"|"partial_sl", reason}. Gerek gördüğünde kullan; kalan pozisyon SL/TP ile devam eder. Kısmi TP fırsatı bildirimi gelirse (fiyat seviyeye ulaştı) kapatıp kapatmamak sana bağlı.\n' +
|
|
2039
2040
|
'KİŞİSEL MT5 ARAÇLARI (VARSAYILAN KURULU — tool__* tüm oturumlarda çağrılabilir): tool__mt5_shot (grafikten GERÇEK PNG + görsel ajana enjekte — grafik işlerinde BİRİNCİL; timeframes:["M1","M15"] ile çoklu periyot, symbols:["GOLD","EURUSD","BTCUSD"] ile çoklu sembol TEK karede), tool__mt5_barlar2 (OHLC mumlar, köprüsüz), tool__mt5_m15_m5 (GOLD M15+M5 hazır paket), tool__mt5_fiyat, tool__mt5_durum (hesap+pozisyon+emir tek çağrı), tool__mt5_gecmis, tool__mt5_pozisyon_gecmis, tool__mt5_sltp, tool__mt5_kapat, tool__mt5_bekleyen, tool__mt5_emir, tool__mt5_emir_iptal.\n' +
|
|
2040
2041
|
'BEASTFINANCE EA (OTOMATİK GRAFİK UZMANI — ENTEGRASYON KANALI): MT5 terminaline bağlanıldığında BeastFinance uzman danışmanı İLK GRAFİĞE OTOMATİK yüklenir, AutoTrading izni açılır (kurulum sistem tarafından yapılır; elle ekleme gerekmez). Grafik panosu + seviye çizgileri + dosya köprüsü (beast_ea.json / beast_cmd.json / beast_note.json) bu EA üzerinden yürür: entegrasyon işlerinde (status/ping/chart/note) mt5_ea kullan; ekran görüntüsü işlerinde tool__mt5_shot ("shot" komutu — gerçek PNG + ajan görseli) birincildir. Yazdığın not ve çizgiler tool__mt5_shot ve computer_look screenshot\'ında GÖRÜNÜR.\n' +
|
|
@@ -200,12 +200,12 @@ const definitions = NAMES.map((name) => {
|
|
|
200
200
|
},
|
|
201
201
|
mt5_alerts: {
|
|
202
202
|
description:
|
|
203
|
-
'FİYAT ALARMI: mt5_alerts {action:"list"|"set"|"remove"}. set: {symbol, price, direction:"above"|"below", mode:"once"|"repeat", note?, cooldownMin?}. ALARM MODUNU KURAN AJAN (sen) SEÇERSİN — mode zorunlu: "once" tek seferlik (ilk tetiklemede kapanır) ya da "repeat" tekrarlı (alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar bildirir + sahibi ajanı uyandırır; cooldownMin varsayılan 5). Örn kritik seviye: XAUUSD 3400 üstü → mode:"repeat", cooldownMin:15; tek kere haber: mode:"once".',
|
|
203
|
+
'FİYAT ALARMI: mt5_alerts {action:"list"|"set"|"remove"|"clear"}. set: {symbol, price, direction:"above"|"below", mode:"once"|"repeat", note?, cooldownMin?}. ALARM MODUNU KURAN AJAN (sen) SEÇERSİN — mode zorunlu: "once" tek seferlik (ilk tetiklemede kapanır) ya da "repeat" tekrarlı (alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar bildirir + sahibi ajanı uyandırır; cooldownMin varsayılan 5). Örn kritik seviye: XAUUSD 3400 üstü → mode:"repeat", cooldownMin:15; tek kere haber: mode:"once". TEMİZLİK: alarm bir bekçidir, süs değil — gereksiz alarm biriktirme; her turda list ile KENDİ alarmlarını gör, işi bitenleri (tez geçersiz, pozisyon kapandı, seviye anlamsız) clear ile SİL. clear varsayılanı YALNIZ senin kurduğun alarmlar; symbol ile daralt; ids:[...] belirli alarmlar; all:true tüm finance alarmları.',
|
|
204
204
|
parameters: {
|
|
205
205
|
type: 'object',
|
|
206
206
|
properties: {
|
|
207
|
-
action: { type: 'string', enum: ['list', 'set', 'remove'] },
|
|
208
|
-
symbol: { type: 'string', description: 'set için sembol' },
|
|
207
|
+
action: { type: 'string', enum: ['list', 'set', 'remove', 'clear'] },
|
|
208
|
+
symbol: { type: 'string', description: 'set/clear için sembol (clear: yalnız bu sembole daralt)' },
|
|
209
209
|
price: { type: 'number', description: 'set için tetik fiyatı' },
|
|
210
210
|
direction: { type: 'string', enum: ['above', 'below'], description: 'above: fiyat ≥ price; below: fiyat ≤ price' },
|
|
211
211
|
mode: {
|
|
@@ -217,6 +217,8 @@ const definitions = NAMES.map((name) => {
|
|
|
217
217
|
cooldownMin: { type: 'number', description: "mode:'repeat' için iki tetik arası en az dakika (varsayılan 5, 0 = her tetiklemede, max 10080)" },
|
|
218
218
|
once: { type: 'boolean', description: "mode'a alternatif kısa yol (true = tek seferlik)" },
|
|
219
219
|
id: { type: 'string', description: 'remove için alarm id' },
|
|
220
|
+
ids: { type: 'array', items: { type: 'string' }, description: 'clear için: silinecek alarm id listesi (verilirse sahiplik aranmaz)' },
|
|
221
|
+
all: { type: 'boolean', description: 'clear için: true → sahiplik filtresini kaldır, kalan TÜM finance alarmlarını sil (varsayılan: yalnız kendi kurdukların)' },
|
|
220
222
|
},
|
|
221
223
|
required: ['action'],
|
|
222
224
|
},
|
|
@@ -420,7 +422,9 @@ const handlers = {
|
|
|
420
422
|
async mt5_alerts(args, ctx) {
|
|
421
423
|
const action = String(args.action || 'list').toLowerCase();
|
|
422
424
|
if (action === 'list') {
|
|
423
|
-
|
|
425
|
+
/* mine:true → bu oturumun (senin) kurduğu alarm — temizlik kararı için */
|
|
426
|
+
const sid = ctx && ctx.sessionId ? String(ctx.sessionId) : '';
|
|
427
|
+
const list = alertsApi.list().map((a) => ({ ...a, mine: sid ? String(a.sid || '') === sid : !a.sid }));
|
|
424
428
|
return { ok: true, count: list.length, alerts: list };
|
|
425
429
|
}
|
|
426
430
|
if (action === 'remove') {
|
|
@@ -429,6 +433,20 @@ const handlers = {
|
|
|
429
433
|
const removed = alertsApi.remove(id);
|
|
430
434
|
return { ok: !!removed, removed: !!removed, id };
|
|
431
435
|
}
|
|
436
|
+
if (action === 'clear') {
|
|
437
|
+
/* ALARM TEMİZLİĞİ: alarmı kuran ajan kendi gereksiz alarmlarını siler.
|
|
438
|
+
Varsayılan kapsam: bu oturumun kurdukları (ctx.sessionId); symbol ile
|
|
439
|
+
daralt; ids açıkça verilirse sahiplik aranmaz; all:true → tümü. */
|
|
440
|
+
if (typeof alertsApi.clear !== 'function') return { ok: false, error: 'toplu alarm temizliği desteklenmiyor' };
|
|
441
|
+
const ids = Array.isArray(args.ids) ? args.ids.map((x) => String(x || '').trim()).filter(Boolean) : [];
|
|
442
|
+
const all = args.all === true || String(args.all || '').toLowerCase() === 'true' || String(args.scope || '').toLowerCase() === 'all';
|
|
443
|
+
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
444
|
+
const sid = ctx && ctx.sessionId ? String(ctx.sessionId) : '';
|
|
445
|
+
const res = alertsApi.clear({ ids, sid, all, symbol }) || {};
|
|
446
|
+
const removed = Number(res.removed) || 0;
|
|
447
|
+
if (removed > 0) noteTrade('alert-clear', { removed, ids: res.ids || [], symbol, all }, ctx || {});
|
|
448
|
+
return { ok: true, removed, ids: res.ids || [] };
|
|
449
|
+
}
|
|
432
450
|
if (action === 'set') {
|
|
433
451
|
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
434
452
|
const price = Number(args.price);
|
|
@@ -466,7 +484,7 @@ const handlers = {
|
|
|
466
484
|
noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note, cooldownMin: alarm.cooldownMin, once: alarm.once }, ctx || {});
|
|
467
485
|
return { ok: true, alert: alarm };
|
|
468
486
|
}
|
|
469
|
-
return { ok: false, error: "action: list|set|remove" };
|
|
487
|
+
return { ok: false, error: "action: list|set|remove|clear" };
|
|
470
488
|
},
|
|
471
489
|
async mt5_note(args, ctx) {
|
|
472
490
|
const symbol = String(args.symbol || '').trim().toUpperCase();
|
package/src/agent/finwatch.js
CHANGED
|
@@ -159,6 +159,57 @@ function alertCooldownActive(a, now) {
|
|
|
159
159
|
return last > 0 && Number(now) - last < cd;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/* ---- alarm temizliği (ajan kendi kurduğu gereksiz alarmı siler) ----
|
|
163
|
+
opts:
|
|
164
|
+
ids → yalnız bu id'ler (açıkça verilenler sahiplikten bağımsız silinir)
|
|
165
|
+
sid → alarmı kuran ajan oturumu; scope varsayılanı "kendi alarmlarım"
|
|
166
|
+
symbol → yalnız bu sembole daralt
|
|
167
|
+
all → sahiplik filtresini kaldır (tüm finance alarmları)
|
|
168
|
+
Dönüş: silinecek alarm id listesi (saf; IO yok). */
|
|
169
|
+
function pickAlarms(alerts, opts) {
|
|
170
|
+
const o = opts || {};
|
|
171
|
+
const ids = Array.isArray(o.ids) ? o.ids.map((x) => String(x || '')).filter(Boolean) : [];
|
|
172
|
+
const sid = String(o.sid || '');
|
|
173
|
+
const symbol = String(o.symbol || '').toUpperCase();
|
|
174
|
+
const all = !!o.all;
|
|
175
|
+
const out = [];
|
|
176
|
+
for (const a of Array.isArray(alerts) ? alerts : []) {
|
|
177
|
+
if (!a || !a.id) continue;
|
|
178
|
+
if (ids.length) {
|
|
179
|
+
if (ids.includes(String(a.id))) out.push(String(a.id));
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (symbol && String(a.symbol || '').toUpperCase() !== symbol) continue;
|
|
183
|
+
if (all) {
|
|
184
|
+
out.push(String(a.id));
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
/* sid varsa YALNIZ kendi alarmları; sid yoksa (işçi oturumu) sahipsizler */
|
|
188
|
+
const owner = String(a.sid || '');
|
|
189
|
+
if (sid ? owner === sid : !owner) out.push(String(a.id));
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* ---- trade saatleri (makinenin YEREL saati) ----
|
|
195
|
+
th: { on, start:'HH:MM', end:'HH:MM' }. on değilse 7/24 açık (true).
|
|
196
|
+
Açıkken yalnız [start,end) aralığında true döner; gece aralığı desteklenir
|
|
197
|
+
(ör. 22:00 → 06:00). start === end → 24 saat. Saf; IO yok. */
|
|
198
|
+
function parseHM(v, defMin) {
|
|
199
|
+
const m = /^([01]?\d|2[0-3]):([0-5]\d)$/.exec(String(v || '').trim());
|
|
200
|
+
if (!m) return defMin;
|
|
201
|
+
return Number(m[1]) * 60 + Number(m[2]);
|
|
202
|
+
}
|
|
203
|
+
function tradeHoursOpen(th, now) {
|
|
204
|
+
if (!th || !th.on) return true;
|
|
205
|
+
const s = parseHM(th.start, 9 * 60);
|
|
206
|
+
const e = parseHM(th.end, 22 * 60);
|
|
207
|
+
if (s === e) return true;
|
|
208
|
+
const d = now instanceof Date ? now : new Date(Number(now) || Date.now());
|
|
209
|
+
const cur = d.getHours() * 60 + d.getMinutes();
|
|
210
|
+
return s < e ? cur >= s && cur < e : cur >= s || cur < e;
|
|
211
|
+
}
|
|
212
|
+
|
|
162
213
|
/* ---- bekleyen emir (pending) → pozisyon eşleştirme ----
|
|
163
214
|
Watchdog turu emir listesini karşılaştırır: listeden DÜŞEN emir için aynı
|
|
164
215
|
turda YENİ açılan (ya da netting hesapta hacmi artan) eşleşen bir pozisyon
|
|
@@ -228,4 +279,4 @@ function matchPendingDelta(goneOrders, freshPositions) {
|
|
|
228
279
|
return out;
|
|
229
280
|
}
|
|
230
281
|
|
|
231
|
-
module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, alertCooldownActive, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
|
|
282
|
+
module.exports = { plan, roundTo, stepRound, closeKind, parseOrderType, alertCooldownActive, pickAlarms, parseHM, tradeHoursOpen, orderSide, orderTypeLabel, orderVolume, matchPendingDelta };
|
package/src/main.js
CHANGED
|
@@ -9400,6 +9400,13 @@ function finCfg() {
|
|
|
9400
9400
|
/* GÜNLÜK RUTİN: plan/review saatleri (HH:MM; boş = kapalı) — hafta içi cron */
|
|
9401
9401
|
if (typeof f.planTime !== 'string') f.planTime = '';
|
|
9402
9402
|
if (typeof f.reviewTime !== 'string') f.reviewTime = '';
|
|
9403
|
+
/* TRADE SAATLERİ: varsayılan KAPALI (7/24). Açıkken Beast Finance ve tüm
|
|
9404
|
+
ajanları YALNIZ [start,end) aralığında çalışır — makinenin YEREL saati;
|
|
9405
|
+
varsayılan aralık 09:00-22:00; gece aralığı desteklenir (22:00 → 06:00). */
|
|
9406
|
+
if (!f.tradeHours || typeof f.tradeHours !== 'object') f.tradeHours = {};
|
|
9407
|
+
if (typeof f.tradeHours.on !== 'boolean') f.tradeHours.on = false;
|
|
9408
|
+
if (!/^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(f.tradeHours.start || '').trim())) f.tradeHours.start = '09:00';
|
|
9409
|
+
if (!/^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(f.tradeHours.end || '').trim())) f.tradeHours.end = '22:00';
|
|
9403
9410
|
/* RİSK OTOMASYONU (watchdog): +R'da BE, trailing, kısmi TP — main süreç
|
|
9404
9411
|
saniyelik döngüyle uygular; ajan turunu BEKLEMEZ. 0 = ilgili kural kapalı. */
|
|
9405
9412
|
if (typeof f.watchdog !== 'boolean') f.watchdog = true;
|
|
@@ -9446,6 +9453,26 @@ function finCfg() {
|
|
|
9446
9453
|
return f;
|
|
9447
9454
|
}
|
|
9448
9455
|
|
|
9456
|
+
/* ---- TRADE SAATLERİ KAPISI (makinenin YEREL saati) ----
|
|
9457
|
+
tradeHours.on true iken ajan turları ve olay uyandırmaları yalnız
|
|
9458
|
+
[start,end) aralığında çalışır; gece aralığı desteklenir (ör. 22:00 →
|
|
9459
|
+
06:00). Kapalıyken (varsayılan) 7/24 serbesttir. Açık pozisyon koruması
|
|
9460
|
+
(watchdog: BE/trailing/kısmi TP) bu kapıdan ETKİLENMEZ — risk güvenliği
|
|
9461
|
+
her saat sürer; kapı yalnız ajan ÜRETİMİNİ (tur/karar/uyandırma) kısar.
|
|
9462
|
+
Karar katmanı saf fonksiyondur: finwatch.tradeHoursOpen (birim testli). */
|
|
9463
|
+
function finTradeHoursOpen(now) {
|
|
9464
|
+
return finwatch.tradeHoursOpen(finCfg().tradeHours, now);
|
|
9465
|
+
}
|
|
9466
|
+
/* Kapı kaynaklı ertelemeler günlüğe SEYREK düşer (10 dk'da en fazla 1 satır) */
|
|
9467
|
+
function finTradeHoursBlockedLog(what) {
|
|
9468
|
+
const now = Date.now();
|
|
9469
|
+
if (now - Number(financeState.tradeHoursLogAt || 0) < 10 * 60 * 1000) return false;
|
|
9470
|
+
financeState.tradeHoursLogAt = now;
|
|
9471
|
+
const th = finCfg().tradeHours || {};
|
|
9472
|
+
financeLog(`[saat] trade saatleri dışı (${th.start || '09:00'}-${th.end || '22:00'} yerel) — ${what} ertelendi`);
|
|
9473
|
+
return true;
|
|
9474
|
+
}
|
|
9475
|
+
|
|
9449
9476
|
/* ANALİZ EKİBİ: trader'ın yanında koşan uzman ajan rolleri — seçilen her rol
|
|
9450
9477
|
AYRI bir sürekli finance ajanı açar. Hepsi tüm SKILL'lere + mt5 okuma
|
|
9451
9478
|
araçlarına erişir (finance oturumu oldukları için), İŞLEM AÇMAZLAR.
|
|
@@ -9943,6 +9970,21 @@ function finAlertApi() {
|
|
|
9943
9970
|
}
|
|
9944
9971
|
return false;
|
|
9945
9972
|
},
|
|
9973
|
+
/* TOPLU TEMİZLİK: alarmı KURAN ajan kendi gereksiz alarmlarını siler.
|
|
9974
|
+
Varsayılan kapsam: yalnız bu oturumun (sid) kurduğu alarmlar; symbol
|
|
9975
|
+
verilirse sembole daralır; ids açıkça verilirse sahiplik aranmaz;
|
|
9976
|
+
all:true → sahiplik filtresi kalkar (tüm finance alarmları). */
|
|
9977
|
+
clear: (opts) => {
|
|
9978
|
+
const o = opts || {};
|
|
9979
|
+
const ids = finwatch.pickAlarms(financeState.alerts, o);
|
|
9980
|
+
if (!ids.length) return { removed: 0, ids: [] };
|
|
9981
|
+
const drop = new Set(ids);
|
|
9982
|
+
financeState.alerts = financeState.alerts.filter((a) => !drop.has(String(a.id)));
|
|
9983
|
+
finAlertsSave();
|
|
9984
|
+
const scope = Array.isArray(o.ids) && o.ids.length ? 'verilen id\'ler' : o.all ? 'tüm alarmlar' : 'ajanın kendi alarmları';
|
|
9985
|
+
financeLog(`[alarm] temizlendi: ${ids.length} adet (${scope}${o.symbol ? ' · ' + String(o.symbol).toUpperCase() : ''})`);
|
|
9986
|
+
return { removed: ids.length, ids };
|
|
9987
|
+
},
|
|
9946
9988
|
};
|
|
9947
9989
|
}
|
|
9948
9990
|
|
|
@@ -10367,6 +10409,12 @@ function finWakeAgentDm(sid, text) {
|
|
|
10367
10409
|
const id = String(sid || '');
|
|
10368
10410
|
const agent = financeState.agents.get(id);
|
|
10369
10411
|
if (!agent || !engine) return false;
|
|
10412
|
+
/* TRADE SAATLERİ KAPISI: aralık dışında olay ajanı UYANDIRMAZ (panel/kanal
|
|
10413
|
+
bildirimi düşmeye devam eder; tur pencere açılınca kendiliğinden başlar) */
|
|
10414
|
+
if (!finTradeHoursOpen()) {
|
|
10415
|
+
finTradeHoursBlockedLog('olay uyandırması');
|
|
10416
|
+
return false;
|
|
10417
|
+
}
|
|
10370
10418
|
try {
|
|
10371
10419
|
const body = agent.role
|
|
10372
10420
|
? String(text || '') + "\n(ROL HATIRLATMASI: işlem AÇMA — durumu analiz et; gerekiyorsa agent_dm ile ANA TRADER'a bildir.)"
|
|
@@ -10527,11 +10575,12 @@ async function finCheckAlerts() {
|
|
|
10527
10575
|
sahip koşmuyor/biliniyorsa koşan tüm finance ajanlarına düşer. */
|
|
10528
10576
|
const wakeTxt =
|
|
10529
10577
|
`[FİNANS OLAYI — FİYAT ALARMI${tekrar}, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
|
|
10530
|
-
`- ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} tetiklendi (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}${cdTxt}\n` +
|
|
10578
|
+
`- ${a.symbol} ${a.direction === 'below' ? '≤' : '≥'} ${a.price} tetiklendi (şimdi ${p.bid})${a.note ? ' — ' + a.note : ''}${cdTxt} (id: ${a.id})\n` +
|
|
10531
10579
|
(a.once
|
|
10532
10580
|
? 'Alarm TEK SEFERLİKTİ ve kapandı. '
|
|
10533
10581
|
: `Alarm TEKRARLI açık kalıyor${a.cooldownMin > 0 ? ` — koşul sürerse en erken ${a.cooldownMin} dk sonra yeniden uyarır` : ''}. `) +
|
|
10534
|
-
'Şimdi yap: alarmı kurma nedenini hatırla; fiyat seviyesini ve planını değerlendir, gerekiyorsa işlem/uyarı üret.
|
|
10582
|
+
'Şimdi yap: alarmı kurma nedenini hatırla; fiyat seviyesini ve planını değerlendir, gerekiyorsa işlem/uyarı üret. ' +
|
|
10583
|
+
'TEMİZLİK: alarmın görevi bittiyse (tez geçersiz, pozisyon kapandı, seviye anlamsızlaştı) mt5_alerts action:"remove" {id} ya da action:"clear" ile SİL — gereksiz alarm biriktirme. Kısa rapor ver.';
|
|
10535
10584
|
if (!finWakeAgentDm(a.sid, wakeTxt)) finWakeAgents(wakeTxt, { kind: 'alarm', symbol: a.symbol });
|
|
10536
10585
|
}
|
|
10537
10586
|
if (changed) finAlertsSave();
|
|
@@ -11453,6 +11502,10 @@ function finTraderBrief(agent) {
|
|
|
11453
11502
|
'finance-review' job'ları tetikler. Trader koşmuyorsa sessizce atlanır. */
|
|
11454
11503
|
function finDailyRoutine(mode) {
|
|
11455
11504
|
try {
|
|
11505
|
+
if (!finTradeHoursOpen()) {
|
|
11506
|
+
financeLog('[rutin] trade saatleri dışı — günlük ' + mode + ' atlandı');
|
|
11507
|
+
return;
|
|
11508
|
+
}
|
|
11456
11509
|
let mainSid = '';
|
|
11457
11510
|
for (const [sid, a] of financeState.agents) {
|
|
11458
11511
|
if (a && a.main) { mainSid = String(sid); break; }
|
|
@@ -11554,6 +11607,15 @@ async function finConsultPlan(f, agent, sid) {
|
|
|
11554
11607
|
function finAgentRound(sid) {
|
|
11555
11608
|
const agent = financeState.agents.get(String(sid));
|
|
11556
11609
|
if (!agent || !engine) return;
|
|
11610
|
+
/* TRADE SAATLERİ KAPISI: aralık dışında YENİ TUR BAŞLATILMAZ — ajan askıya
|
|
11611
|
+
alınır; pencere açılınca 60 sn içinde kaldığı yerden devam eder */
|
|
11612
|
+
if (!finTradeHoursOpen()) {
|
|
11613
|
+
finTradeHoursBlockedLog(`tur #${(Number(agent.round) || 0) + 1}`);
|
|
11614
|
+
clearTimeout(agent.timer);
|
|
11615
|
+
agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, 60000);
|
|
11616
|
+
if (agent.main) finPush('trader', { state: 'hours', round: agent.round, nextInSec: 60 });
|
|
11617
|
+
return;
|
|
11618
|
+
}
|
|
11557
11619
|
if (engine.isBusy(sid)) {
|
|
11558
11620
|
/* hâlâ çalışıyor — done eventinde tekrar planlanır */
|
|
11559
11621
|
return;
|
|
@@ -11616,6 +11678,12 @@ function finWakeAgent(sid) {
|
|
|
11616
11678
|
const id = String(sid || '');
|
|
11617
11679
|
const agent = financeState.agents.get(id);
|
|
11618
11680
|
if (!agent || !engine) return;
|
|
11681
|
+
/* TRADE SAATLERİ KAPISI: aralık dışında DM uyandırması tur AÇMAZ; mesaj
|
|
11682
|
+
inbox'ta bekler, pencere açılınca okunur */
|
|
11683
|
+
if (!finTradeHoursOpen()) {
|
|
11684
|
+
finTradeHoursBlockedLog('DM uyandırması');
|
|
11685
|
+
return;
|
|
11686
|
+
}
|
|
11619
11687
|
if (engine.isBusy(id)) {
|
|
11620
11688
|
/* tur sürüyor: olay dmInbox'ta bekler — tur bitince finFlushOnDone
|
|
11621
11689
|
BEKLEMEDEN yeni tur açar (interval beklenmez) */
|
|
@@ -11903,6 +11971,20 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
11903
11971
|
if (p.shadowMode !== undefined) f.shadowMode = !!p.shadowMode;
|
|
11904
11972
|
if (p.planTime !== undefined) f.planTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.planTime || '').trim()) ? String(p.planTime).trim() : '';
|
|
11905
11973
|
if (p.reviewTime !== undefined) f.reviewTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.reviewTime || '').trim()) ? String(p.reviewTime).trim() : '';
|
|
11974
|
+
/* TRADE SAATLERİ: {on, start, end} — yerel saat; aralık dışında ajan turları
|
|
11975
|
+
ve olay uyandırmaları durur (varsayılan kapalı, aralık 09:00-22:00) */
|
|
11976
|
+
if (p.tradeHours !== undefined && p.tradeHours && typeof p.tradeHours === 'object') {
|
|
11977
|
+
const th = f.tradeHours && typeof f.tradeHours === 'object' ? f.tradeHours : {};
|
|
11978
|
+
if (typeof p.tradeHours.on === 'boolean') th.on = p.tradeHours.on;
|
|
11979
|
+
for (const k of ['start', 'end']) {
|
|
11980
|
+
if (p.tradeHours[k] !== undefined) {
|
|
11981
|
+
const v = String(p.tradeHours[k] || '').trim().slice(0, 5);
|
|
11982
|
+
if (/^([01]?\d|2[0-3]):([0-5]\d)$/.test(v)) th[k] = v;
|
|
11983
|
+
}
|
|
11984
|
+
}
|
|
11985
|
+
f.tradeHours = th;
|
|
11986
|
+
if (th.on && !finTradeHoursOpen()) financeLog('[saat] trade saatleri açık — ajanlar ' + th.start + '-' + th.end + ' aralığında çalışacak (yerel saat)');
|
|
11987
|
+
}
|
|
11906
11988
|
if (p.pythonPath !== undefined) f.pythonPath = String(p.pythonPath || '').trim();
|
|
11907
11989
|
if (p.terminalPath !== undefined) f.terminalPath = String(p.terminalPath || '').trim();
|
|
11908
11990
|
if (p.traderSel !== undefined) {
|
package/src/renderer/index.html
CHANGED
|
@@ -892,6 +892,20 @@
|
|
|
892
892
|
</label>
|
|
893
893
|
<div class="fin-set-note">RİSK OTOMASYONU gizliyken koruma döngüsü (watchdog) kapatılır; tekrar gösterdiğinde ilgili kutucuktan açabilirsin.</div>
|
|
894
894
|
<div class="fin-set-sep"></div>
|
|
895
|
+
<label class="fin-set-row" title="Varsayılan KAPALI: kapalıyken ajanlar 7/24 çalışır. Açıkken Beast Finance ve tüm ajanları YALNIZ belirlenen saat aralığında çalışır — aralık dışında yeni tur başlatılmaz ve olaylar ajanı uyandırmaz (makinenin YEREL saati). Açık pozisyon koruması (BE/trailing/kısmi TP) her saat çalışmaya devam eder.">
|
|
896
|
+
<input id="finTradeHoursOn" type="checkbox" />
|
|
897
|
+
<span class="fsr-main"><b>TRADE SAATLERİ</b><i>Kapalıyken 7/24 · açıkken ajanlar yalnız bu aralıkta</i></span>
|
|
898
|
+
</label>
|
|
899
|
+
<div id="finTradeHoursWrap" class="fin-set-sub" hidden>
|
|
900
|
+
<span class="fin-set-lbl">BAŞLANGIÇ / BİTİŞ (YEREL SAAT)</span>
|
|
901
|
+
<div class="fin-th-row">
|
|
902
|
+
<input id="finTradeHoursStart" type="time" step="60" value="09:00" />
|
|
903
|
+
<span class="fin-set-lbl">→</span>
|
|
904
|
+
<input id="finTradeHoursEnd" type="time" step="60" value="22:00" />
|
|
905
|
+
</div>
|
|
906
|
+
<div class="fin-set-note" style="margin:0">Aralık dışında ajan turları askıya alınır, olay alarmları ajanı uyandırmaz; pencere açılıp kapanınca kaldığı yerden devam eder. Gece aralığı da verilebilir (ör. 22:00 → 06:00).</div>
|
|
907
|
+
</div>
|
|
908
|
+
<div class="fin-set-sep"></div>
|
|
895
909
|
<label class="fin-set-row" title="Açıkken botu eklediğin Telegram grubuna yazılan ilk mesaj o grubu AJAN DM aynası yapar. Her AJAN DM grubu Telegram'da AYRI BAŞLIK (topic) olarak canlı akar; başlığa yazdıkların ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır. Grup/Konu seçmene gerek yok.">
|
|
896
910
|
<input id="finDmTgOn" type="checkbox" />
|
|
897
911
|
<span class="fsr-main"><b>AJAN DM ↔ TELEGRAM</b><i>Her ajan grubu Telegram'da ayrı başlık — canlı</i></span>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -208,6 +208,10 @@ const els = {
|
|
|
208
208
|
finDmTgState: $('#finDmTgState'),
|
|
209
209
|
finDmTgUnbind: $('#finDmTgUnbind'),
|
|
210
210
|
finDmTgHint: $('#finDmTgHint'),
|
|
211
|
+
finTradeHoursOn: $('#finTradeHoursOn'),
|
|
212
|
+
finTradeHoursWrap: $('#finTradeHoursWrap'),
|
|
213
|
+
finTradeHoursStart: $('#finTradeHoursStart'),
|
|
214
|
+
finTradeHoursEnd: $('#finTradeHoursEnd'),
|
|
211
215
|
finRiskCard: $('#finRiskCard'),
|
|
212
216
|
finNotifyCard: $('#finNotifyCard'),
|
|
213
217
|
finPerfCard: $('#finPerfCard'),
|
|
@@ -9997,6 +10001,7 @@ function finRenderAutomation(cfg, watch) {
|
|
|
9997
10001
|
if (els.finWatchInfo) {
|
|
9998
10002
|
els.finWatchInfo.textContent = watch && watch.on ? '· izlenen ' + (watch.managed || 0) : '';
|
|
9999
10003
|
}
|
|
10004
|
+
finTradeHoursPaint();
|
|
10000
10005
|
}
|
|
10001
10006
|
|
|
10002
10007
|
function finSparkline(points) {
|
|
@@ -10158,6 +10163,7 @@ function finOnEvent(ev) {
|
|
|
10158
10163
|
} else if (ev.fn === 'trader') {
|
|
10159
10164
|
if (ev.state === 'running') finLogLine('[trader] tur #' + (ev.round || 0) + ' başladı');
|
|
10160
10165
|
else if (ev.state === 'idle') finLogLine('[trader] tur #' + (ev.round || 0) + ' bitti — sıradaki tur ~' + (ev.nextInSec || '?') + ' sn');
|
|
10166
|
+
else if (ev.state === 'hours') finLogLine('[trader] trade saatleri dışı — ajan askıda (kontrol ~' + (ev.nextInSec || 60) + ' sn)');
|
|
10161
10167
|
else if (ev.state === 'stopped') finLogLine('[trader] durduruldu');
|
|
10162
10168
|
} else if (ev.fn === 'install') {
|
|
10163
10169
|
finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
|
|
@@ -10319,11 +10325,37 @@ function finDmTgPaint() {
|
|
|
10319
10325
|
function finSettingsOpen() {
|
|
10320
10326
|
if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = false;
|
|
10321
10327
|
finDmTgPaint();
|
|
10328
|
+
finTradeHoursPaint();
|
|
10322
10329
|
}
|
|
10323
10330
|
function finSettingsCloseModal() {
|
|
10324
10331
|
if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = true;
|
|
10325
10332
|
}
|
|
10326
10333
|
|
|
10334
|
+
/* ---------- TRADE SAATLERİ (görünüm ayarları modalı) ----------
|
|
10335
|
+
Ayarlar sunucuda (settings.finance.tradeHours) yaşar: on + start/end.
|
|
10336
|
+
Kapalıyken ajanlar 7/24; açıkken yalnız [start,end) aralığında çalışır
|
|
10337
|
+
(makinenin yerel saati). Açık pozisyon koruması her saat sürer. */
|
|
10338
|
+
function finTradeHoursPaint() {
|
|
10339
|
+
if (!els.finTradeHoursOn) return;
|
|
10340
|
+
const th = (finCfgCache && finCfgCache.tradeHours) || {};
|
|
10341
|
+
const on = th.on === true;
|
|
10342
|
+
els.finTradeHoursOn.checked = on;
|
|
10343
|
+
const row = els.finTradeHoursOn.closest('.fin-set-row');
|
|
10344
|
+
if (row) row.classList.toggle('on', on);
|
|
10345
|
+
if (els.finTradeHoursWrap) els.finTradeHoursWrap.hidden = !on;
|
|
10346
|
+
const ae = document.activeElement;
|
|
10347
|
+
if (els.finTradeHoursStart && ae !== els.finTradeHoursStart) els.finTradeHoursStart.value = th.start || '09:00';
|
|
10348
|
+
if (els.finTradeHoursEnd && ae !== els.finTradeHoursEnd) els.finTradeHoursEnd.value = th.end || '22:00';
|
|
10349
|
+
}
|
|
10350
|
+
function finTradeHoursSave() {
|
|
10351
|
+
const on = !!(els.finTradeHoursOn && els.finTradeHoursOn.checked);
|
|
10352
|
+
const start = String((els.finTradeHoursStart && els.finTradeHoursStart.value) || '09:00').slice(0, 5) || '09:00';
|
|
10353
|
+
const end = String((els.finTradeHoursEnd && els.finTradeHoursEnd.value) || '22:00').slice(0, 5) || '22:00';
|
|
10354
|
+
if (finCfgCache) finCfgCache.tradeHours = { on, start, end };
|
|
10355
|
+
finSaveCfg({ tradeHours: { on, start, end } });
|
|
10356
|
+
return { on, start, end };
|
|
10357
|
+
}
|
|
10358
|
+
|
|
10327
10359
|
function finViewToggle(key, on) {
|
|
10328
10360
|
finView[key] = !!on;
|
|
10329
10361
|
finViewApply();
|
|
@@ -10373,6 +10405,24 @@ if (els.finDmTgUnbind) {
|
|
|
10373
10405
|
toast('Telegram grubu koparıldı — yeni grupta /start yazınca bağlanır');
|
|
10374
10406
|
});
|
|
10375
10407
|
}
|
|
10408
|
+
/* TRADE SAATLERİ kontrolleri — anında kaydedilir (yerel saat) */
|
|
10409
|
+
if (els.finTradeHoursOn) {
|
|
10410
|
+
els.finTradeHoursOn.addEventListener('change', () => {
|
|
10411
|
+
const { on, start, end } = finTradeHoursSave();
|
|
10412
|
+
finTradeHoursPaint();
|
|
10413
|
+
toast(on
|
|
10414
|
+
? 'Trade saatleri AÇIK — Beast Finance ve ajanları yalnız ' + start + '–' + end + ' arasında çalışır (yerel saat)'
|
|
10415
|
+
: 'Trade saatleri kapalı — ajanlar 7/24 serbest');
|
|
10416
|
+
});
|
|
10417
|
+
}
|
|
10418
|
+
for (const el of [els.finTradeHoursStart, els.finTradeHoursEnd]) {
|
|
10419
|
+
if (!el) continue;
|
|
10420
|
+
el.addEventListener('change', () => {
|
|
10421
|
+
const { on, start, end } = finTradeHoursSave();
|
|
10422
|
+
if (on) toast('Trade saatleri: ' + start + '–' + end + ' (yerel saat)');
|
|
10423
|
+
});
|
|
10424
|
+
}
|
|
10425
|
+
finTradeHoursPaint();
|
|
10376
10426
|
finViewApply();
|
|
10377
10427
|
|
|
10378
10428
|
/* ---------- BÖLÜM SIRALAMA (sürükle-bırak) ----------
|
package/src/renderer/style.css
CHANGED
|
@@ -5882,6 +5882,8 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
|
|
|
5882
5882
|
}
|
|
5883
5883
|
.fin-set-sub[hidden] { display: none; }
|
|
5884
5884
|
.fin-set-lbl { font-size: 10.5px; color: var(--muted); letter-spacing: 0.5px; }
|
|
5885
|
+
.fin-th-row { display: flex; align-items: center; gap: 8px; }
|
|
5886
|
+
.fin-th-row input[type="time"] { flex: 1; min-width: 0; }
|
|
5885
5887
|
.fin-dm-tg-state { font-size: 11.5px; color: var(--text); line-height: 1.4; word-break: break-word; }
|
|
5886
5888
|
.fin-set-btn {
|
|
5887
5889
|
align-self: flex-start;
|