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 +1 -1
- package/src/main.js +102 -6
- package/src/preload.js +1 -0
- package/src/renderer/index.html +1 -1
- package/src/renderer/renderer.js +21 -0
package/package.json
CHANGED
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 (
|
|
10930
|
-
|
|
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 =
|
|
10943
|
-
|
|
10944
|
-
|
|
10945
|
-
|
|
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'),
|
package/src/renderer/index.html
CHANGED
|
@@ -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>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -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) {
|