beast-agent 2.76.5 → 2.76.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.76.5",
4
+ "version": "2.76.6",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/main.js CHANGED
@@ -10926,23 +10926,42 @@ async function finStopAllFinanceAgents(reason) {
10926
10926
  return sids.length;
10927
10927
  }
10928
10928
 
10929
- /* Tüm açık pozisyonları kapatır (günlük limit 'flatten' aksiyonu). */
10930
- async function finFlattenAll() {
10929
+ /* Tüm açık pozisyonları kapatır (TOPLU KAPATMA).
10930
+ reason: 'risk' (günlük limit flatten) | 'manuel' (panel butonu) |
10931
+ 'kural' (talimat kâr hedefi — "karda tümünü kapat" gibi).
10932
+ opts.withPending: bekleyen emirler de iptal edilir (yeni pozisyon açılmasın). */
10933
+ async function finFlattenAll(reason, opts) {
10931
10934
  if (financeState.flattening) return 0;
10932
10935
  if (!mt5bridge.running) return 0;
10936
+ const why = ['risk', 'manuel', 'kural'].includes(String(reason)) ? String(reason) : 'risk';
10933
10937
  financeState.flattening = true;
10934
10938
  let closed = 0;
10935
10939
  try {
10940
+ /* ÖNCE bekleyen emirler (ops.): kapanırken yeni pozisyon aktifleşmesin */
10941
+ if (opts && opts.withPending) {
10942
+ try {
10943
+ const o = await mt5bridge.call('orders', {}, 10000);
10944
+ const orders = (o && o.ok && o.data && o.data.orders) || [];
10945
+ for (const ord of orders) {
10946
+ await mt5bridge.call('cancel', { ticket: ord.ticket }, 10000).catch(() => null);
10947
+ }
10948
+ if (orders.length) financeLog('[toplu] ' + orders.length + ' bekleyen emir iptal edildi');
10949
+ } catch {}
10950
+ }
10936
10951
  const r = await mt5bridge.call('positions', {}, 10000);
10937
10952
  const list = (r && r.ok && r.data && r.data.positions) || [];
10938
10953
  for (const p of list) {
10939
10954
  const cr = await mt5bridge.call('close', { ticket: p.ticket, volume: 0 }, 15000).catch(() => null);
10940
10955
  if (cr && cr.ok) closed++;
10941
10956
  }
10942
- const line = `🚨 Günlük limit: ${closed}/${list.length} pozisyon kapatıldı`;
10943
- financeLog('[risk] ' + line);
10944
- financeNotify(line, 'risk');
10945
- finJournal({ kind: 'risk-flatten', closed, total: list.length });
10957
+ const line = why === 'manuel'
10958
+ ? `🧯 Toplu kapatma (panel): ${closed}/${list.length} pozisyon kapatıldı`
10959
+ : why === 'kural'
10960
+ ? `🎯 Toplu kapatma (kâr hedefi): ${closed}/${list.length} pozisyon kapatıldı`
10961
+ : `🚨 Günlük limit: ${closed}/${list.length} pozisyon kapatıldı`;
10962
+ financeLog('[' + why + '] ' + line);
10963
+ financeNotify(line, why === 'risk' ? 'risk' : 'close');
10964
+ finJournal({ kind: why === 'risk' ? 'risk-flatten' : 'close-all', why, closed, total: list.length });
10946
10965
  finStatsRefresh(true).catch(() => {});
10947
10966
  } finally {
10948
10967
  financeState.flattening = false;
@@ -10950,6 +10969,34 @@ async function finFlattenAll() {
10950
10969
  return closed;
10951
10970
  }
10952
10971
 
10972
+ /* TOPLU KAPATMA KURALI (talimat): "karda tümünü kapat" / "toplam kâr %2 olunca
10973
+ hepsini kapat" / "50 dolar kârda tümünü kapat" → sepet (tüm pozisyonların
10974
+ toplam yüzen K/Z'si) hedefe ulaşınca HEPSİ kapatılır. Kural yoksa no-op. */
10975
+ async function finBasketCloseCheck(positions, account) {
10976
+ const rule = finParsedInstr().manage.closeAllProfit;
10977
+ if (!rule) return;
10978
+ if (financeState.flattening) return;
10979
+ const list = Array.isArray(positions) ? positions.filter(Boolean) : [];
10980
+ if (!list.length) return;
10981
+ const total = Math.round(list.reduce((a, p) => a + (Number(p.profit) || 0), 0) * 100) / 100;
10982
+ const bal = Number(account && account.balance) || 0;
10983
+ let hit = false;
10984
+ if (rule.money != null) hit = total >= Number(rule.money);
10985
+ else if (rule.pct != null && bal > 0) hit = total >= (bal * Number(rule.pct)) / 100;
10986
+ else hit = total > 0; /* "karda tümünü kapat" → herhangi bir net kâr */
10987
+ if (!hit) return;
10988
+ const closed = await finFlattenAll('kural', { withPending: true });
10989
+ if (closed > 0) {
10990
+ financeNotify(`🎯 Toplu kapatma kuralı: sepet +${total} → ${closed} pozisyon kapatıldı`, 'close');
10991
+ finWakeAgents(
10992
+ `[FİNANS OLAYI — TOPLU KAPATMA] Talimat kâr hedefi tetiklendi: tüm pozisyonların toplamı +${total} (hedef ` +
10993
+ (rule.money != null ? rule.money : rule.pct != null ? '%' + rule.pct : 'kâr') +
10994
+ ') → tüm pozisyonlar kapatıldı. Hesabı ve yeni planı değerlendir.',
10995
+ { kind: 'close-all', total }
10996
+ );
10997
+ }
10998
+ }
10999
+
10953
11000
  /* ---- performans istatistikleri (kalıcı) ---- */
10954
11001
  function finStatsLoad() {
10955
11002
  const s = finReadJson(finFile('stats.json'), null);
@@ -11720,6 +11767,10 @@ async function finWatchTick() {
11720
11767
  if (positions.length) {
11721
11768
  try { finPosManagerMaybe(positions, account); } catch {}
11722
11769
  }
11770
+ /* TOPLU KAPATMA KURALI: talimat kâr hedefi (sepet) dolduysa hepsini kapat */
11771
+ if (positions.length) {
11772
+ try { await finBasketCloseCheck(positions, account); } catch {}
11773
+ }
11723
11774
  }
11724
11775
  try { await finCheckAlerts(); } catch {}
11725
11776
  if (!financeState.lastStatsAt || Date.now() - financeState.lastStatsAt > 120000) {
@@ -13967,6 +14018,31 @@ function finParseInstructions(text) {
13967
14018
  ) {
13968
14019
  out.noLossClose = true;
13969
14020
  }
14021
+ /* TOPLU KAPATMA (kâr hedefi): "karda tümünü kapat", "toplam kâr %2 olunca
14022
+ hepsini kapat", "50 dolar kârda tümünü kapat" → sepet (tüm pozisyonların
14023
+ toplam yüzen K/Z'si) hedefe ulaşınca HEPSİ kapatılır (kod uygular).
14024
+ Zarar hedefli panik kapatma bilinçli olarak KAPSAM DIŞI. */
14025
+ {
14026
+ const allClose =
14027
+ /(?:t[üu]m[üu]n[üu](?:\s*pozisyonlar[ıi]?)?|hepsini|t[üu]m\s*pozisyonlar[ıi]?|toplu(?:\s*kapatma)?)[^.\n]{0,30}?kapat/.test(low);
14028
+ if (allClose && /k[âa]r/.test(low)) {
14029
+ let pct = null;
14030
+ let money = null;
14031
+ let mm = low.match(/k[âa]r[^0-9%]{0,16}%\s*(\d+(?:[.,]\d+)?)/) ||
14032
+ low.match(/toplam[^0-9%]{0,16}%\s*(\d+(?:[.,]\d+)?)[^.\n]{0,16}?k[âa]r/);
14033
+ if (mm) pct = finInstrNum(mm[1]);
14034
+ if (pct == null) {
14035
+ mm = low.match(/(\d+(?:[.,]\d+)?)\s*(?:dolar|usd|\$)[^.\n]{0,16}?k[âa]r/) ||
14036
+ low.match(/k[âa]r[^0-9]{0,16}\$?\s*(\d+(?:[.,]\d+)?)/);
14037
+ if (mm) money = finInstrNum(mm[1]);
14038
+ }
14039
+ out.closeAllProfit = {
14040
+ pct: pct != null && pct > 0 ? Math.min(50, pct) : null,
14041
+ money: pct == null && money != null && money > 0 ? money : null,
14042
+ any: pct == null && money == null,
14043
+ };
14044
+ }
14045
+ }
13970
14046
  return out;
13971
14047
  }
13972
14048
 
@@ -13987,6 +14063,8 @@ function finParsedInstr() {
13987
14063
  partial: mgr.partial || trade.partial,
13988
14064
  /* YÖNETİM KURALI: zararda kapatma yasağı iki nottan birinde varsa geçerli */
13989
14065
  noLossClose: !!(mgr.noLossClose || trade.noLossClose),
14066
+ /* TOPLU KAPATMA kâr hedefi (iki nottan biri) */
14067
+ closeAllProfit: mgr.closeAllProfit || trade.closeAllProfit || null,
13990
14068
  },
13991
14069
  };
13992
14070
  }
@@ -14002,6 +14080,10 @@ function finInstrSummary(ins) {
14002
14080
  if (ins.entry.dailyLossPct != null) p.push(`günlük zarar limiti %${ins.entry.dailyLossPct}`);
14003
14081
  if (ins.manage.partial) p.push(`kısmi %${ins.manage.partial.pct} @${ins.manage.partial.atR}R (yalnız kârda)`);
14004
14082
  if (ins.manage.noLossClose) p.push('zararda kapatma YOK');
14083
+ if (ins.manage.closeAllProfit) {
14084
+ const r = ins.manage.closeAllProfit;
14085
+ p.push(r.money != null ? `toplu kapatma +${r.money}` : r.pct != null ? `toplu kapatma +%${r.pct}` : 'kârda toplu kapatma');
14086
+ }
14005
14087
  try {
14006
14088
  const f = finCfg();
14007
14089
  if (Number(f.maxPositionsNote) > 0) p.push(`maks ${f.maxPositionsNote} pozisyon`);
@@ -14510,6 +14592,12 @@ async function finPosManagerRound(positions, account) {
14510
14592
  kapatma_kurali: ins.manage.noLossClose
14511
14593
  ? 'ZARARDA KAPATMA YOK (kod uygular): zarardaki pozisyonun kapatma kararı reddedilir — SL/TP kapatması serbest; kısmi kapatma YALNIZ kârda'
14512
14594
  : 'zararda kapatma serbest',
14595
+ /* SEPET: tüm pozisyonların toplam yüzen K/Z'si — toplu kapatma kuralı
14596
+ ve "tümünü kapat" kararı bu veriyle verilir */
14597
+ sepet_kz: Math.round(positions.reduce((a, p) => a + (Number(p.profit) || 0), 0) * 100) / 100,
14598
+ toplu_kapatma: ins.manage.closeAllProfit
14599
+ ? 'KURAL: sepet kâr hedefine ulaşınca TÜMÜ kapatılır (kod uygular; panelde "Tümünü Kapat" da var)'
14600
+ : '',
14513
14601
  talimat_ayarlari: instrTxt || '(sayısal kural yok)',
14514
14602
  gun_durumu: (() => {
14515
14603
  try {
@@ -15908,6 +15996,14 @@ ipcMain.handle('finance:close', async (_e, payload) => {
15908
15996
  return r;
15909
15997
  });
15910
15998
 
15999
+ /* TOPLU KAPATMA (panel butonu): tüm açık pozisyonlar + bekleyen emirler kapanır */
16000
+ ipcMain.handle('finance:closeAll', async () => {
16001
+ if (!mt5bridge.running) return { ok: false, error: 'MT5 köprüsü bağlı değil' };
16002
+ const closed = await finFlattenAll('manuel', { withPending: true });
16003
+ finPush('trade', { line: 'TOPLU KAPATMA (panel): ' + closed + ' pozisyon kapatıldı' });
16004
+ return { ok: true, closed };
16005
+ });
16006
+
15911
16007
  /* Bekleyen emri panelden iptal et (ajanın mt5_cancel aracıyla aynı köprü) */
15912
16008
  ipcMain.handle('finance:cancel', async (_e, payload) => {
15913
16009
  const ticket = Number(payload && payload.ticket);
package/src/preload.js CHANGED
@@ -130,6 +130,7 @@ contextBridge.exposeInMainWorld('beast', {
130
130
  financeAgentSpawn: (symbol) => ipcRenderer.invoke('finance:agent:spawn', { symbol }),
131
131
  financeConnect: () => ipcRenderer.invoke('finance:connect'),
132
132
  financeClose: (ticket, volume) => ipcRenderer.invoke('finance:close', { ticket, volume }),
133
+ financeCloseAll: () => ipcRenderer.invoke('finance:closeAll'),
133
134
  financeCancel: (ticket) => ipcRenderer.invoke('finance:cancel', { ticket }),
134
135
  financeInstall: () => ipcRenderer.invoke('finance:install'),
135
136
  financeMt5Setup: () => ipcRenderer.invoke('finance:mt5:setup'),
@@ -300,7 +300,7 @@
300
300
  </div>
301
301
  </div>
302
302
  <div class="fin-card" data-fin-card="positions">
303
- <div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span></div>
303
+ <div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span><span class="file-spacer"></span><button id="finCloseAll" title="TOPLU KAPATMA — tüm açık pozisyonları ve bekleyen emirleri kapat">Tümünü Kapat</button></div>
304
304
  <div id="finPosList"><div class="fin-empty">Açık pozisyon yok</div></div>
305
305
  <div class="fin-sect-title">BEKLEYEN EMİRLER <span id="finOrdCount"></span></div>
306
306
  <div id="finOrdList"><div class="fin-empty">Bekleyen emir yok</div></div>
@@ -258,6 +258,7 @@ const els = {
258
258
  finPosStatus: $('#finPosStatus'),
259
259
  finPosList: $('#finPosList'),
260
260
  finPosCount: $('#finPosCount'),
261
+ finCloseAll: $('#finCloseAll'),
261
262
  finOrdList: $('#finOrdList'),
262
263
  finOrdCount: $('#finOrdCount'),
263
264
  finSymList: $('#finSymList'),
@@ -9780,6 +9781,8 @@ function finRenderPositions(list) {
9780
9781
  if (!els.finPosList) return;
9781
9782
  els.finPosList.textContent = '';
9782
9783
  if (els.finPosCount) els.finPosCount.textContent = list && list.length ? '(' + list.length + ')' : '';
9784
+ /* toplu kapatma butonu: pozisyon yoksa pasif */
9785
+ if (els.finCloseAll) els.finCloseAll.disabled = !(list && list.length);
9783
9786
  if (!list || !list.length) {
9784
9787
  const d = document.createElement('div');
9785
9788
  d.className = 'fin-empty';
@@ -10956,6 +10959,8 @@ function finHelpHtml(which) {
10956
10959
  '<b>Kod otomatik uygular.</b> Zarardaki pozisyonun kapatma kararı REDDEDİLİR — SL/TP çalışmaya devam eder (stop = martingale serisinin parçası). Kârdaki/başabaştaki pozisyon normal kapatılır.'],
10957
10960
  ['<code>0.5R karda yarısını kapat</code><code>partial close ile yarısını kapat</code>',
10958
10961
  '<b>Kod otomatik uygular.</b> Kâr +0.5R\'ye ulaşınca pozisyonun %50\'si kapatılır — <b>yalnız kârdayken</b> uygulanır (zararda kısmi stop yapılmaz).'],
10962
+ ['<code>karda tümünü kapat</code><code>toplam kâr %2 olunca hepsini kapat</code><code>50 dolar kârda tümünü kapat</code>',
10963
+ '<b>Kod otomatik uygular (TOPLU KAPATMA).</b> Tüm pozisyonların toplamı hedefe ulaşınca (herhangi bir kâr / +%X / +X dolar) HEPSİ + bekleyen emirler kapatılır. Panelde POZİSYONLAR başlığındaki <b>Tümünü Kapat</b> butonuyla elle de yapılabilir.'],
10959
10964
  ];
10960
10965
  const example = posmgr
10961
10966
  ? `1- 1R'de %50 kısmi kapat\n2- kâr 2R'ye gelince kalanı kapat\n3- zarar -0.5R'yi geçerse kes`
@@ -11707,6 +11712,22 @@ if (els.finLearnClear) {
11707
11712
  }
11708
11713
 
11709
11714
  if (els.finSymAdd) els.finSymAdd.addEventListener('click', finSymPickerOpen);
11715
+ /* TOPLU KAPATMA: tüm pozisyonlar + bekleyen emirler tek onayla kapanır */
11716
+ if (els.finCloseAll) {
11717
+ els.finCloseAll.addEventListener('click', async () => {
11718
+ const ok = await uiConfirm(
11719
+ 'TÜM açık pozisyonlar ve bekleyen emirler kapatılsın mı?\n(TOPLU KAPATMA — geri alınamaz)',
11720
+ 'Tümünü Kapat',
11721
+ 'Vazgeç'
11722
+ );
11723
+ if (!ok) return;
11724
+ els.finCloseAll.disabled = true;
11725
+ const r = await beast.financeCloseAll().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
11726
+ if (r && r.ok) toast('Toplu kapatma: ' + (Number(r.closed) || 0) + ' pozisyon kapatıldı');
11727
+ else toast('Toplu kapatma başarısız: ' + ((r && r.error) || '?'));
11728
+ finSnapshot();
11729
+ });
11730
+ }
11710
11731
  if (els.finWatchClear) {
11711
11732
  els.finWatchClear.addEventListener('click', async () => {
11712
11733
  if (!finWatch.length) {