beast-agent 2.13.0 → 2.15.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 +154 -3
- package/src/agent/financetools.js +307 -0
- package/src/agent/llm.js +3 -1
- package/src/agent/scripts/mt5_bridge.py +381 -0
- package/src/agent/tools.js +10 -3
- package/src/main.js +466 -0
- package/src/mt5bridge.js +215 -0
- package/src/preload.js +11 -0
- package/src/renderer/i18n.js +3 -1
- package/src/renderer/index.html +76 -1
- package/src/renderer/renderer.js +562 -4
- package/src/renderer/style.css +393 -1
package/src/renderer/renderer.js
CHANGED
|
@@ -43,6 +43,8 @@ const els = {
|
|
|
43
43
|
railBtn: $('#railBtn'),
|
|
44
44
|
railClear: $('#railClear'),
|
|
45
45
|
watchBtn: $('#watchBtn'),
|
|
46
|
+
watchPaneList: $('#watchPaneList'),
|
|
47
|
+
watchPaneOpen: $('#watchPaneOpen'),
|
|
46
48
|
cronBtn: $('#cronBtn'),
|
|
47
49
|
watchOverlay: $('#watchOverlay'),
|
|
48
50
|
cronOverlay: $('#cronOverlay'),
|
|
@@ -126,6 +128,33 @@ const els = {
|
|
|
126
128
|
stMic: $('#stMic'),
|
|
127
129
|
studioRow: $('#studioRow'),
|
|
128
130
|
stSplit: $('#stSplit'),
|
|
131
|
+
finBtn: $('#finBtn'),
|
|
132
|
+
finPanel: $('#finPanel'),
|
|
133
|
+
finDot: $('#finDot'),
|
|
134
|
+
finTerm: $('#finTerm'),
|
|
135
|
+
finReconnect: $('#finReconnect'),
|
|
136
|
+
finBalance: $('#finBalance'),
|
|
137
|
+
finEquity: $('#finEquity'),
|
|
138
|
+
finProfit: $('#finProfit'),
|
|
139
|
+
finFree: $('#finFree'),
|
|
140
|
+
finTraderModel: $('#finTraderModel'),
|
|
141
|
+
finInterval: $('#finInterval'),
|
|
142
|
+
finMaxLot: $('#finMaxLot'),
|
|
143
|
+
finSymBtn: $('#finSymBtn'),
|
|
144
|
+
finAllow: $('#finAllow'),
|
|
145
|
+
finTraderBtn: $('#finTraderBtn'),
|
|
146
|
+
finTraderDot: $('#finTraderDot'),
|
|
147
|
+
finTraderStatus: $('#finTraderStatus'),
|
|
148
|
+
finPosList: $('#finPosList'),
|
|
149
|
+
finPosCount: $('#finPosCount'),
|
|
150
|
+
finSymList: $('#finSymList'),
|
|
151
|
+
finLog: $('#finLog'),
|
|
152
|
+
finSymAdd: $('#finSymAdd'),
|
|
153
|
+
finSymOverlay: $('#finSymOverlay'),
|
|
154
|
+
finSymClose: $('#finSymClose'),
|
|
155
|
+
finSymSearch: $('#finSymSearch'),
|
|
156
|
+
finSymPick: $('#finSymPick'),
|
|
157
|
+
finSymCount: $('#finSymCount'),
|
|
129
158
|
stVideoEl: $('#stVideoEl'),
|
|
130
159
|
stVideoName: $('#stVideoName'),
|
|
131
160
|
stVideoOpen: $('#stVideoOpen'),
|
|
@@ -877,6 +906,9 @@ async function renderSessions(list) {
|
|
|
877
906
|
tgSet = new Set(await beast.tgListSessions());
|
|
878
907
|
} catch {}
|
|
879
908
|
els.sessList.innerHTML = '';
|
|
909
|
+
/* BEAST FINANCE izolasyonu: finance modunda YALNIZ finance sohbetleri,
|
|
910
|
+
normal modda finance olmayanlar listelenir */
|
|
911
|
+
list = (list || []).filter((s) => (financeModeOn() ? !!s.finance : !s.finance));
|
|
880
912
|
/* elle sıra: sessionOrder'daki id'ler önde (o sırayla), diğerleri engine sırasıyla arkada */
|
|
881
913
|
const rank = new Map(sessionOrder.map((id, i) => [id, i]));
|
|
882
914
|
const withRank = list.map((s, i) => ({ s, r: rank.has(s.id) ? rank.get(s.id) : 1e6 + i }));
|
|
@@ -955,8 +987,19 @@ let dragSid = null;
|
|
|
955
987
|
async function refreshSessions() {
|
|
956
988
|
await renderSessions(await beast.listSessions());
|
|
957
989
|
renderBotCards(); // bot kartlarındaki numara/sayı etiketleri de tazelensin
|
|
990
|
+
renderWatchersPane().catch(() => {}); // kenar izleyici listesi de tazelensin
|
|
958
991
|
}
|
|
959
992
|
|
|
993
|
+
/* İzleyici kenar pane canlı tazeleme: agent modunda 8 sn'de bir sessiz çekim;
|
|
994
|
+
başka moddaysa zamanlayıcı pas geçer (IPC maliyeti yok) */
|
|
995
|
+
setInterval(() => {
|
|
996
|
+
try {
|
|
997
|
+
if (document.body.classList.contains('ide-mode') || document.body.classList.contains('studio-mode') || document.body.classList.contains('sandbox-mode')) return;
|
|
998
|
+
if (document.hidden) return;
|
|
999
|
+
renderWatchersPane().catch(() => {});
|
|
1000
|
+
} catch {}
|
|
1001
|
+
}, 8000);
|
|
1002
|
+
|
|
960
1003
|
async function openSession(id) {
|
|
961
1004
|
activeId = id;
|
|
962
1005
|
streamEl = null;
|
|
@@ -964,6 +1007,8 @@ async function openSession(id) {
|
|
|
964
1007
|
els.msgs.innerHTML = '';
|
|
965
1008
|
showEmpty(s.messages.length === 0);
|
|
966
1009
|
renderTodos(s.todos || []);
|
|
1010
|
+
/* finance modu açıkken oturum değişimi: yeni oturumu da finance bayrağıyla işaretle */
|
|
1011
|
+
if (financeModeOn()) beast.financeMode(true, id).catch(() => {});
|
|
967
1012
|
|
|
968
1013
|
for (let i = 0; i < s.messages.length; i++) {
|
|
969
1014
|
const m = s.messages[i];
|
|
@@ -2381,7 +2426,7 @@ async function renderInstallPane() {
|
|
|
2381
2426
|
refresh();
|
|
2382
2427
|
});
|
|
2383
2428
|
});
|
|
2384
|
-
};
|
|
2429
|
+
};
|
|
2385
2430
|
|
|
2386
2431
|
const refresh = async () => {
|
|
2387
2432
|
if (pane.hidden) return;
|
|
@@ -5495,10 +5540,68 @@ function fmtWhen(iso) {
|
|
|
5495
5540
|
return d.toLocaleDateString([], { day: '2-digit', month: '2-digit' }) + ' ' + t;
|
|
5496
5541
|
}
|
|
5497
5542
|
|
|
5498
|
-
|
|
5499
|
-
|
|
5543
|
+
/* ---------- İZLEYİCİLER kenar pane (sohbet geçmişinin alt yarısı) ----------
|
|
5544
|
+
Kompakt liste: durum noktası + ad + bir sonraki tetik; satır → izleyici
|
|
5545
|
+
modalı; ▶/× mini kontroller. Yalnız agent modunda görünür (CSS). */
|
|
5546
|
+
let lastWatchPaneKey = '';
|
|
5547
|
+
async function renderWatchersPane(force) {
|
|
5548
|
+
const list = els.watchPaneList;
|
|
5549
|
+
if (!list) return;
|
|
5550
|
+
if (document.body.classList.contains('ide-mode') || document.body.classList.contains('studio-mode') || document.body.classList.contains('sandbox-mode')) return;
|
|
5500
5551
|
let rows = [];
|
|
5501
5552
|
try { rows = (await beast.watchersList()) || []; } catch {}
|
|
5553
|
+
const key = JSON.stringify(rows);
|
|
5554
|
+
if (!force && key === lastWatchPaneKey) return;
|
|
5555
|
+
lastWatchPaneKey = key;
|
|
5556
|
+
list.innerHTML = '';
|
|
5557
|
+
if (!rows.length) {
|
|
5558
|
+
list.innerHTML = '<div class="wp-empty">' + _t('w_empty') + '</div>';
|
|
5559
|
+
return;
|
|
5560
|
+
}
|
|
5561
|
+
for (const w of rows) {
|
|
5562
|
+
const row = document.createElement('div');
|
|
5563
|
+
row.className = 'wp-row' + (w.enabled ? '' : ' off');
|
|
5564
|
+
row.title = (w.kind === 'battery' ? 'pil yüzdesi' : w.kind === 'logs' ? 'log izleyici' : (w.path || w.re || w.url)) + ' · ' + opText(w) + ' · her ' + fmtEvery(w);
|
|
5565
|
+
row.innerHTML =
|
|
5566
|
+
`<span class="wp-dot"></span>` +
|
|
5567
|
+
`<div class="wp-main">` +
|
|
5568
|
+
`<div class="wp-name">${escapeHtml(w.name)}</div>` +
|
|
5569
|
+
`<div class="wp-meta">${opText(w)} · her ${escapeHtml(String(fmtEvery(w)))} · ${escapeHtml(fmtWhen(w.lastCheckAt))}` +
|
|
5570
|
+
(w.lastValue !== null && w.lastValue !== undefined ? ` · <b>${escapeHtml(String(w.lastValue).slice(0, 14))}</b>` : '') +
|
|
5571
|
+
`</div></div>`;
|
|
5572
|
+
const btns = document.createElement('div');
|
|
5573
|
+
btns.className = 'wp-btns';
|
|
5574
|
+
const t = document.createElement('button');
|
|
5575
|
+
t.className = 'wp-btn';
|
|
5576
|
+
t.textContent = w.enabled ? '❚❚' : '▶';
|
|
5577
|
+
t.title = w.enabled ? _t('cr_toggle_pause') : _t('cr_start');
|
|
5578
|
+
t.addEventListener('click', async (e) => {
|
|
5579
|
+
e.stopPropagation();
|
|
5580
|
+
await beast.watchersToggle(w.id);
|
|
5581
|
+
renderWatchersPane(true);
|
|
5582
|
+
renderWatchersModal(true);
|
|
5583
|
+
});
|
|
5584
|
+
const d = document.createElement('button');
|
|
5585
|
+
d.className = 'wp-btn del';
|
|
5586
|
+
d.textContent = '×';
|
|
5587
|
+
d.title = _t('cr_del');
|
|
5588
|
+
d.addEventListener('click', async (e) => {
|
|
5589
|
+
e.stopPropagation();
|
|
5590
|
+
await beast.watchersRemove(w.id);
|
|
5591
|
+
lastWatchPaneKey = '';
|
|
5592
|
+
renderWatchersPane(true);
|
|
5593
|
+
renderWatchersModal(true);
|
|
5594
|
+
});
|
|
5595
|
+
btns.append(t, d);
|
|
5596
|
+
row.appendChild(btns);
|
|
5597
|
+
row.addEventListener('click', () => openWatchModal());
|
|
5598
|
+
list.appendChild(row);
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
|
|
5602
|
+
let lastWatchKey = '';
|
|
5603
|
+
async function renderWatchersModal(force) {
|
|
5604
|
+
let rows = []; try { rows = (await beast.watchersList()) || []; } catch {}
|
|
5502
5605
|
/* canlı yenilemede içerik değişmediyse DOM'a dokunma — buton tıklamaları
|
|
5503
5606
|
yeniden kurulurken yutulmasın */
|
|
5504
5607
|
const key = JSON.stringify(rows);
|
|
@@ -5623,6 +5726,7 @@ function closeCronModal() {
|
|
|
5623
5726
|
async function openWatchModal() {
|
|
5624
5727
|
toggleMini(els.watchOverlay, false);
|
|
5625
5728
|
await renderWatchersModal(true); /* her basışta TAZE çekim */
|
|
5729
|
+
lastWatchPaneKey = ''; /* pane de bir sonraki tazelemede yeniden kurulur */
|
|
5626
5730
|
startMiniRefresh();
|
|
5627
5731
|
}
|
|
5628
5732
|
|
|
@@ -6004,6 +6108,11 @@ async function init() {
|
|
|
6004
6108
|
showEmpty(true);
|
|
6005
6109
|
}
|
|
6006
6110
|
|
|
6111
|
+
/* BEAST FINANCE: kapanışta mod açıktıysa geri yükle */
|
|
6112
|
+
try {
|
|
6113
|
+
if (localStorage.getItem('beast.financeMode') === '1') await setFinanceMode(true);
|
|
6114
|
+
} catch {}
|
|
6115
|
+
|
|
6007
6116
|
els.newChat.addEventListener('click', async () => {
|
|
6008
6117
|
try {
|
|
6009
6118
|
const created = await beast.createSession();
|
|
@@ -6337,6 +6446,7 @@ async function init() {
|
|
|
6337
6446
|
|
|
6338
6447
|
/* cron modülü — olay bağlama */
|
|
6339
6448
|
if (els.watchBtn) els.watchBtn.addEventListener('click', openWatchModal);
|
|
6449
|
+
if (els.watchPaneOpen) els.watchPaneOpen.addEventListener('click', openWatchModal);
|
|
6340
6450
|
if (els.cronBtn) els.cronBtn.addEventListener('click', openCronModal);
|
|
6341
6451
|
if (els.watchClose) els.watchClose.addEventListener('click', closeWatchModal);
|
|
6342
6452
|
if (els.cronClose) els.cronClose.addEventListener('click', closeCronModal);
|
|
@@ -7159,6 +7269,7 @@ function ideModeOn() {
|
|
|
7159
7269
|
async function setIdeMode(on) {
|
|
7160
7270
|
if (on && studioModeOn()) await setStudioMode(false); /* Studio açıkken IDE'ye geçiş — Studio kapanır */
|
|
7161
7271
|
if (on && sbModeOn()) setSandboxMode(false); /* Sandbox açıkken IDE'ye geçiş — Sandbox kapanır */
|
|
7272
|
+
if (on && financeModeOn()) setFinanceMode(false); /* Finance açıkken IDE'ye geçiş — Finance kapanır */
|
|
7162
7273
|
document.body.classList.toggle('ide-mode', !!on);
|
|
7163
7274
|
if (els.ideBtn) els.ideBtn.classList.toggle('on', !!on);
|
|
7164
7275
|
/* soldaki marka: chat modunda BEAST Agent, IDE modunda BEAST Code, Studio modunda BEAST Studio */
|
|
@@ -7233,6 +7344,7 @@ function studioModeOn() {
|
|
|
7233
7344
|
async function setStudioMode(on) {
|
|
7234
7345
|
if (on && ideModeOn()) await setIdeMode(false); /* IDE açıkken Studio'ya geçiş — IDE kapanır */
|
|
7235
7346
|
if (on && sbModeOn()) setSandboxMode(false); /* Sandbox açıkken Studio'ya geçiş — Sandbox kapanır */
|
|
7347
|
+
if (on && financeModeOn()) setFinanceMode(false); /* Finance açıkken Studio'ya geçiş — Finance kapanır */
|
|
7236
7348
|
document.body.classList.toggle('studio-mode', !!on);
|
|
7237
7349
|
if (els.studioBtn) els.studioBtn.classList.toggle('on', !!on);
|
|
7238
7350
|
/* ALT TERMINAL: yalnız Beast Code'a özgü — Studio'ya geçişte kapanır */
|
|
@@ -7963,6 +8075,450 @@ async function codeReloadIfOpen(p) {
|
|
|
7963
8075
|
|
|
7964
8076
|
$('#ideBtn').addEventListener('click', () => setIdeMode(!ideModeOn()));
|
|
7965
8077
|
if (els.studioBtn) els.studioBtn.addEventListener('click', () => setStudioMode(!studioModeOn()));
|
|
8078
|
+
|
|
8079
|
+
/* ================= BEAST FINANCE MODU =================
|
|
8080
|
+
Sol sidebar (sohbet geçmişi + izleyiciler) ve chat AYNI kalır; sağda MT5
|
|
8081
|
+
işlem paneli: hesap, pozisyonlar, piyasa, trade ajanı kontrolleri, akış. */
|
|
8082
|
+
|
|
8083
|
+
function financeModeOn() {
|
|
8084
|
+
return document.body.classList.contains('finance-mode');
|
|
8085
|
+
}
|
|
8086
|
+
|
|
8087
|
+
let finPollTimer = null;
|
|
8088
|
+
let finModelsFilled = false;
|
|
8089
|
+
let finLastPrices = new Map(); /* sembol → son bid (renk için) */
|
|
8090
|
+
const FIN_COLOR_UP = 'fs-up';
|
|
8091
|
+
const FIN_COLOR_DOWN = 'fs-down';
|
|
8092
|
+
|
|
8093
|
+
function fmtMoney(v, cur) {
|
|
8094
|
+
const n = Number(v);
|
|
8095
|
+
if (!isFinite(n)) return '—';
|
|
8096
|
+
const s = n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
8097
|
+
return cur ? s + ' ' + cur : s;
|
|
8098
|
+
}
|
|
8099
|
+
|
|
8100
|
+
function finSetDot(state, bridge) {
|
|
8101
|
+
if (!els.finDot) return;
|
|
8102
|
+
els.finDot.classList.remove('on', 'off', 'busy');
|
|
8103
|
+
if (state === true) els.finDot.classList.add('on');
|
|
8104
|
+
else if (state === 'busy') els.finDot.classList.add('busy');
|
|
8105
|
+
else els.finDot.classList.add('off');
|
|
8106
|
+
if (els.finTerm) {
|
|
8107
|
+
let label = 'MT5';
|
|
8108
|
+
if (bridge) {
|
|
8109
|
+
if (bridge.terminal) label = (bridge.terminal.name || 'MT5') + (bridge.terminal.company ? ' · ' + bridge.terminal.company : '');
|
|
8110
|
+
else if (bridge.error) label = bridge.error.length > 60 ? bridge.error.slice(0, 60) + '…' : bridge.error;
|
|
8111
|
+
else if (bridge.running) label = 'MT5 terminali bekleniyor…';
|
|
8112
|
+
else label = 'Köprü kapalı';
|
|
8113
|
+
}
|
|
8114
|
+
els.finTerm.textContent = label;
|
|
8115
|
+
els.finTerm.title = bridge && bridge.error ? bridge.error : label;
|
|
8116
|
+
}
|
|
8117
|
+
}
|
|
8118
|
+
|
|
8119
|
+
function finKvSet(el, text, cls) {
|
|
8120
|
+
if (!el) return;
|
|
8121
|
+
el.textContent = text;
|
|
8122
|
+
el.classList.remove('pos', 'neg');
|
|
8123
|
+
if (cls) el.classList.add(cls);
|
|
8124
|
+
}
|
|
8125
|
+
|
|
8126
|
+
function finRenderAccount(acc) {
|
|
8127
|
+
const cur = acc && acc.currency ? acc.currency : '';
|
|
8128
|
+
finKvSet(els.finBalance, acc ? fmtMoney(acc.balance, cur) : '—');
|
|
8129
|
+
finKvSet(els.finEquity, acc ? fmtMoney(acc.equity, cur) : '—');
|
|
8130
|
+
const p = acc ? Number(acc.profit) || 0 : NaN;
|
|
8131
|
+
finKvSet(els.finProfit, isFinite(p) ? (p > 0 ? '+' : '') + fmtMoney(p, cur) : '—', isFinite(p) && p !== 0 ? (p > 0 ? 'pos' : 'neg') : null);
|
|
8132
|
+
finKvSet(els.finFree, acc ? fmtMoney(acc.margin_free, cur) : '—');
|
|
8133
|
+
}
|
|
8134
|
+
|
|
8135
|
+
function finRenderPositions(list) {
|
|
8136
|
+
if (!els.finPosList) return;
|
|
8137
|
+
els.finPosList.textContent = '';
|
|
8138
|
+
if (els.finPosCount) els.finPosCount.textContent = list && list.length ? '(' + list.length + ')' : '';
|
|
8139
|
+
if (!list || !list.length) {
|
|
8140
|
+
const d = document.createElement('div');
|
|
8141
|
+
d.className = 'fin-empty';
|
|
8142
|
+
d.textContent = 'Açık pozisyon yok';
|
|
8143
|
+
els.finPosList.appendChild(d);
|
|
8144
|
+
return;
|
|
8145
|
+
}
|
|
8146
|
+
for (const p of list) {
|
|
8147
|
+
const row = document.createElement('div');
|
|
8148
|
+
row.className = 'fin-pos';
|
|
8149
|
+
const isBuy = String(p.type) === '0' || Number(p.type) === 0;
|
|
8150
|
+
const profit = Number(p.profit) || 0;
|
|
8151
|
+
row.innerHTML =
|
|
8152
|
+
'<span class="fp-sym" title="ticket ' + p.ticket + '">' + (p.symbol || '?') + '</span>' +
|
|
8153
|
+
'<span class="fp-dir ' + (isBuy ? 'buy">BUY' : 'sell">SELL') + '</span>' +
|
|
8154
|
+
'<span class="fp-meta">' + (Number(p.volume) || 0) + ' lot · ' + (Number(p.price_open) || 0) + ' → ' + (Number(p.price_current) || 0) +
|
|
8155
|
+
(p.sl ? ' · SL ' + p.sl : '') + (p.tp ? ' · TP ' + p.tp : '') + '</span>' +
|
|
8156
|
+
'<span class="fp-pl ' + (profit >= 0 ? 'pos' : 'neg') + '">' + (profit >= 0 ? '+' : '') + profit.toFixed(2) + '</span>';
|
|
8157
|
+
const btn = document.createElement('button');
|
|
8158
|
+
btn.className = 'fp-close';
|
|
8159
|
+
btn.title = 'Pozisyonu kapat';
|
|
8160
|
+
btn.textContent = '✕';
|
|
8161
|
+
btn.addEventListener('click', async () => {
|
|
8162
|
+
const ok = await uiConfirm((p.symbol || '') + ' pozisyonu kapatılsın mı? (ticket ' + p.ticket + ')', 'Kapat');
|
|
8163
|
+
if (!ok) return;
|
|
8164
|
+
const r = await beast.financeClose(p.ticket).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
8165
|
+
if (r && r.ok) toast('Pozisyon kapatıldı');
|
|
8166
|
+
else toast('Kapatılamadı: ' + ((r && r.error) || '?'));
|
|
8167
|
+
finSnapshot();
|
|
8168
|
+
});
|
|
8169
|
+
row.appendChild(btn);
|
|
8170
|
+
els.finPosList.appendChild(row);
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
8173
|
+
|
|
8174
|
+
function finRenderSymbols(list) {
|
|
8175
|
+
if (!els.finSymList) return;
|
|
8176
|
+
els.finSymList.textContent = '';
|
|
8177
|
+
if (!list || !list.length) {
|
|
8178
|
+
const d = document.createElement('div');
|
|
8179
|
+
d.className = 'fin-empty';
|
|
8180
|
+
d.textContent = 'Sembol verisi yok';
|
|
8181
|
+
els.finSymList.appendChild(d);
|
|
8182
|
+
return;
|
|
8183
|
+
}
|
|
8184
|
+
for (const s of list) {
|
|
8185
|
+
if (s && s.missing) continue;
|
|
8186
|
+
const row = document.createElement('div');
|
|
8187
|
+
row.className = 'fin-sym';
|
|
8188
|
+
const bid = Number(s.bid) || 0;
|
|
8189
|
+
const prev = finLastPrices.get(s.symbol);
|
|
8190
|
+
let cls = '';
|
|
8191
|
+
if (prev != null && bid !== prev) cls = bid > prev ? FIN_COLOR_UP : FIN_COLOR_DOWN;
|
|
8192
|
+
if (bid) finLastPrices.set(s.symbol, bid);
|
|
8193
|
+
const digits = s.digits != null ? Number(s.digits) : 5;
|
|
8194
|
+
row.innerHTML =
|
|
8195
|
+
'<span class="fs-name">' + (s.symbol || '?') + '</span>' +
|
|
8196
|
+
'<span class="fs-price ' + cls + '">' + bid.toFixed(digits) + '</span>' +
|
|
8197
|
+
'<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>';
|
|
8198
|
+
els.finSymList.appendChild(row);
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
|
|
8202
|
+
function finSymBtnLabel(list) {
|
|
8203
|
+
const l = Array.isArray(list) ? list : [];
|
|
8204
|
+
if (!l.length) return 'Seç…';
|
|
8205
|
+
const head = l.slice(0, 3).join(', ');
|
|
8206
|
+
return l.length > 3 ? head + ' +' + (l.length - 3) : head;
|
|
8207
|
+
}
|
|
8208
|
+
|
|
8209
|
+
function finSymBtnUpdate() {
|
|
8210
|
+
if (els.finSymBtn) {
|
|
8211
|
+
els.finSymBtn.textContent = finSymBtnLabel(finWatch);
|
|
8212
|
+
els.finSymBtn.title = finWatch.length
|
|
8213
|
+
? 'İzleme listesi: ' + finWatch.join(', ') + ' — değiştirmek için tıkla'
|
|
8214
|
+
: 'MT5 sembol listesinden seç — tıkla';
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
|
|
8218
|
+
function finTraderInputsSet(cfg) {
|
|
8219
|
+
if (!cfg) return;
|
|
8220
|
+
if (!finWatchDirty) finWatchSet(cfg.symbols);
|
|
8221
|
+
const ae = document.activeElement;
|
|
8222
|
+
if (els.finInterval && ae !== els.finInterval) els.finInterval.value = cfg.intervalSec || 120;
|
|
8223
|
+
if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
|
|
8224
|
+
finSymBtnUpdate();
|
|
8225
|
+
if (els.finAllow && ae !== els.finAllow) els.finAllow.checked = cfg.allowTrading === true;
|
|
8226
|
+
if (els.finTraderModel && ae !== els.finTraderModel && cfg.traderSel) els.finTraderModel.value = cfg.traderSel;
|
|
8227
|
+
}
|
|
8228
|
+
|
|
8229
|
+
function finRenderTrader(trader, cfg) {
|
|
8230
|
+
if (!els.finTraderBtn) return;
|
|
8231
|
+
const on = !!(trader && trader.on);
|
|
8232
|
+
els.finTraderBtn.classList.toggle('stop', on);
|
|
8233
|
+
els.finTraderBtn.innerHTML = on ? '■ Durdur' : '▶ Ajanı Başlat';
|
|
8234
|
+
if (els.finTraderDot) {
|
|
8235
|
+
els.finTraderDot.classList.remove('on', 'off', 'busy');
|
|
8236
|
+
if (on) els.finTraderDot.classList.add(trader.busy ? 'busy' : 'on');
|
|
8237
|
+
else els.finTraderDot.classList.add('off');
|
|
8238
|
+
}
|
|
8239
|
+
if (els.finTraderStatus) {
|
|
8240
|
+
let t = '';
|
|
8241
|
+
if (trader && trader.lastAt) {
|
|
8242
|
+
const d = new Date(trader.lastAt);
|
|
8243
|
+
t = ' · son tur ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
|
|
8244
|
+
}
|
|
8245
|
+
const mode = cfg && cfg.allowTrading ? 'OTOMATİK' : 'analiz modu';
|
|
8246
|
+
els.finTraderStatus.textContent = on
|
|
8247
|
+
? (trader.busy ? 'Tur çalışıyor…' : 'Beklemede — sıradaki tur ' + (cfg && cfg.intervalSec ? cfg.intervalSec + ' sn' : '')) + ' · tur #' + (trader.rounds || 0) + ' · ' + mode + t
|
|
8248
|
+
: 'Kapalı · ' + mode;
|
|
8249
|
+
}
|
|
8250
|
+
}
|
|
8251
|
+
|
|
8252
|
+
async function finFillModels() {
|
|
8253
|
+
if (!els.finTraderModel) return;
|
|
8254
|
+
let models = [];
|
|
8255
|
+
try {
|
|
8256
|
+
const st = await beast.getState();
|
|
8257
|
+
models = (st && st.models) || [];
|
|
8258
|
+
} catch {}
|
|
8259
|
+
els.finTraderModel.textContent = '';
|
|
8260
|
+
const optAuto = document.createElement('option');
|
|
8261
|
+
optAuto.value = '';
|
|
8262
|
+
optAuto.textContent = 'Genel aktif model';
|
|
8263
|
+
els.finTraderModel.appendChild(optAuto);
|
|
8264
|
+
for (const m of models) {
|
|
8265
|
+
const o = document.createElement('option');
|
|
8266
|
+
o.value = m.sel;
|
|
8267
|
+
o.textContent = (m.providerName || m.sel) + ' · ' + m.model;
|
|
8268
|
+
els.finTraderModel.appendChild(o);
|
|
8269
|
+
}
|
|
8270
|
+
finModelsFilled = true;
|
|
8271
|
+
}
|
|
8272
|
+
|
|
8273
|
+
async function finSnapshot() {
|
|
8274
|
+
if (!financeModeOn()) return;
|
|
8275
|
+
let r = null;
|
|
8276
|
+
try { r = await beast.financeSnapshot(); } catch {}
|
|
8277
|
+
if (!r || !r.ok) {
|
|
8278
|
+
finSetDot(false);
|
|
8279
|
+
return;
|
|
8280
|
+
}
|
|
8281
|
+
const b = r.bridge || {};
|
|
8282
|
+
finSetDot(b.connected ? true : b.running ? 'busy' : false, b);
|
|
8283
|
+
finRenderAccount(r.account);
|
|
8284
|
+
finRenderPositions(r.positions || []);
|
|
8285
|
+
finRenderSymbols(r.symbols || []);
|
|
8286
|
+
finTraderInputsSet(r.cfg);
|
|
8287
|
+
finRenderTrader(r.trader, r.cfg);
|
|
8288
|
+
}
|
|
8289
|
+
|
|
8290
|
+
function finLogLine(line) {
|
|
8291
|
+
if (!els.finLog) return;
|
|
8292
|
+
const d = document.createElement('div');
|
|
8293
|
+
d.className = 'fin-log-new';
|
|
8294
|
+
const now = new Date();
|
|
8295
|
+
d.textContent = String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0') + ' ' + line;
|
|
8296
|
+
els.finLog.insertBefore(d, els.finLog.firstChild);
|
|
8297
|
+
for (const c of els.finLog.children) c.classList.remove('fin-log-new');
|
|
8298
|
+
d.classList.add('fin-log-new');
|
|
8299
|
+
while (els.finLog.children.length > 200) els.finLog.removeChild(els.finLog.lastChild);
|
|
8300
|
+
}
|
|
8301
|
+
|
|
8302
|
+
function finOnEvent(ev) {
|
|
8303
|
+
if (!ev || ev.type !== 'finance') return;
|
|
8304
|
+
if (ev.fn === 'log' && ev.line) finLogLine(ev.line);
|
|
8305
|
+
else if (ev.fn === 'trade' && ev.line) {
|
|
8306
|
+
finLogLine(ev.line);
|
|
8307
|
+
toast(ev.line);
|
|
8308
|
+
finSnapshot();
|
|
8309
|
+
} else if (ev.fn === 'bridge') {
|
|
8310
|
+
finSetDot(ev.connected ? true : 'busy', { error: ev.error });
|
|
8311
|
+
} else if (ev.fn === 'trader') {
|
|
8312
|
+
if (ev.state === 'running') finLogLine('[trader] tur #' + (ev.round || 0) + ' başladı');
|
|
8313
|
+
else if (ev.state === 'idle') finLogLine('[trader] tur #' + (ev.round || 0) + ' bitti — sıradaki tur ~' + (ev.nextInSec || '?') + ' sn');
|
|
8314
|
+
else if (ev.state === 'stopped') finLogLine('[trader] durduruldu');
|
|
8315
|
+
} else if (ev.fn === 'install') {
|
|
8316
|
+
finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
beast.onEvent(finOnEvent);
|
|
8320
|
+
|
|
8321
|
+
async function setFinanceMode(on) {
|
|
8322
|
+
if (on && ideModeOn()) await setIdeMode(false);
|
|
8323
|
+
if (on && studioModeOn()) await setStudioMode(false);
|
|
8324
|
+
if (on && sbModeOn()) setSandboxMode(false);
|
|
8325
|
+
document.body.classList.toggle('finance-mode', !!on);
|
|
8326
|
+
if (els.finBtn) els.finBtn.classList.toggle('on', !!on);
|
|
8327
|
+
const brandSub = document.querySelector('#brand .brand-sub');
|
|
8328
|
+
if (brandSub) brandSub.textContent = on ? 'Finance' : 'Agent';
|
|
8329
|
+
try { localStorage.setItem('beast.financeMode', on ? '1' : '0'); } catch {}
|
|
8330
|
+
beast.financeMode(!!on, activeId).catch(() => {});
|
|
8331
|
+
/* sohbet geçmişi listesi değişir: finance modunda yalnız finance sohbetleri */
|
|
8332
|
+
try {
|
|
8333
|
+
await refreshSessions();
|
|
8334
|
+
const rows = [...els.sessList.querySelectorAll('.sess')];
|
|
8335
|
+
const activeInList = activeId && rows.some((r) => r.dataset.sid === activeId);
|
|
8336
|
+
if (!activeInList && rows.length && rows[0].dataset.sid) {
|
|
8337
|
+
await openSession(rows[0].dataset.sid); /* en üstteki uygun sohbeti aç */
|
|
8338
|
+
}
|
|
8339
|
+
} catch {}
|
|
8340
|
+
if (on) {
|
|
8341
|
+
if (!finModelsFilled) await finFillModels();
|
|
8342
|
+
finSnapshot();
|
|
8343
|
+
if (!finPollTimer) finPollTimer = setInterval(finSnapshot, 3000);
|
|
8344
|
+
} else if (finPollTimer) {
|
|
8345
|
+
clearInterval(finPollTimer);
|
|
8346
|
+
finPollTimer = null;
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
|
|
8350
|
+
if (els.finBtn) {
|
|
8351
|
+
els.finBtn.addEventListener('click', () => setFinanceMode(!financeModeOn()).catch(() => {}));
|
|
8352
|
+
}
|
|
8353
|
+
if (els.finReconnect) {
|
|
8354
|
+
els.finReconnect.addEventListener('click', async () => {
|
|
8355
|
+
toast('MT5 köprüsü yeniden başlatılıyor…');
|
|
8356
|
+
await beast.financeConnect().catch(() => {});
|
|
8357
|
+
finSnapshot();
|
|
8358
|
+
});
|
|
8359
|
+
}
|
|
8360
|
+
|
|
8361
|
+
/* trader kontrolleri: ayarlar anında kaydedilir (debounce) */
|
|
8362
|
+
let finCfgTimer = null;
|
|
8363
|
+
let finWatchDirty = false; /* picker/input değişikliği kaydedilmeyi bekliyor */
|
|
8364
|
+
function finSaveCfg(patch) {
|
|
8365
|
+
if (patch && patch.symbols !== undefined) finWatchDirty = true;
|
|
8366
|
+
clearTimeout(finCfgTimer);
|
|
8367
|
+
finCfgTimer = setTimeout(() => {
|
|
8368
|
+
beast.financeSettings(patch).then(() => { finWatchDirty = false; }).catch(() => { finWatchDirty = false; });
|
|
8369
|
+
}, 400);
|
|
8370
|
+
}
|
|
8371
|
+
if (els.finInterval) els.finInterval.addEventListener('change', () => finSaveCfg({ intervalSec: Number(els.finInterval.value) || 120 }));
|
|
8372
|
+
if (els.finMaxLot) els.finMaxLot.addEventListener('change', () => finSaveCfg({ maxLot: Number(els.finMaxLot.value) || 0.1 }));
|
|
8373
|
+
if (els.finSymBtn) els.finSymBtn.addEventListener('click', finSymPickerOpen);
|
|
8374
|
+
if (els.finAllow) {
|
|
8375
|
+
els.finAllow.addEventListener('change', async () => {
|
|
8376
|
+
if (els.finAllow.checked) {
|
|
8377
|
+
const ok = await uiConfirm(
|
|
8378
|
+
'OTOMATİK İŞLEM açılıyor: trade ajanı GERÇEK hesapta emir açabilir/kapatabilir (max lot + pozisyon limitleriyle). Onaylıyor musun?',
|
|
8379
|
+
'Aç',
|
|
8380
|
+
'Vazgeç'
|
|
8381
|
+
);
|
|
8382
|
+
if (!ok) {
|
|
8383
|
+
els.finAllow.checked = false;
|
|
8384
|
+
return;
|
|
8385
|
+
}
|
|
8386
|
+
}
|
|
8387
|
+
finSaveCfg({ allowTrading: els.finAllow.checked });
|
|
8388
|
+
});
|
|
8389
|
+
}
|
|
8390
|
+
if (els.finTraderModel) {
|
|
8391
|
+
els.finTraderModel.addEventListener('change', () => {
|
|
8392
|
+
finSaveCfg({ traderSel: els.finTraderModel.value });
|
|
8393
|
+
toast('Trade ajanı modeli: ' + (els.finTraderModel.value || 'genel aktif model'));
|
|
8394
|
+
});
|
|
8395
|
+
}
|
|
8396
|
+
if (els.finTraderBtn) {
|
|
8397
|
+
els.finTraderBtn.addEventListener('click', async () => {
|
|
8398
|
+
if (els.finTraderBtn.classList.contains('stop')) {
|
|
8399
|
+
await beast.financeTraderStop().catch(() => {});
|
|
8400
|
+
} else {
|
|
8401
|
+
const r = await beast.financeTraderStart().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
|
|
8402
|
+
if (r && !r.ok) toast(r.error || 'Trader başlatılamadı');
|
|
8403
|
+
else if (r && r.ok) toast('Trade ajanı başladı — turlar panelde izlenir');
|
|
8404
|
+
}
|
|
8405
|
+
finSnapshot();
|
|
8406
|
+
});
|
|
8407
|
+
}
|
|
8408
|
+
|
|
8409
|
+
/* ---------- PİYASA sembol seçici ----------
|
|
8410
|
+
MT5 terminalindeki tüm semboller listelenir; tıkla → izleme listesine
|
|
8411
|
+
ekle/çıkar. PİYASA kartı yalnız seçili sembolleri gösterir. */
|
|
8412
|
+
|
|
8413
|
+
let finWatch = []; /* anlık izleme listesi (snapshot cfg'den senkron) */
|
|
8414
|
+
let finSymSearchTimer = null;
|
|
8415
|
+
let finSymRefreshTimer = null;
|
|
8416
|
+
|
|
8417
|
+
function finWatchSet(list) {
|
|
8418
|
+
finWatch = (Array.isArray(list) ? list : []).map((s) => String(s).trim().toUpperCase()).filter(Boolean);
|
|
8419
|
+
}
|
|
8420
|
+
|
|
8421
|
+
function finToggleWatch(sym) {
|
|
8422
|
+
const s = String(sym || '').toUpperCase();
|
|
8423
|
+
const i = finWatch.indexOf(s);
|
|
8424
|
+
if (i >= 0) finWatch.splice(i, 1);
|
|
8425
|
+
else finWatch.push(s);
|
|
8426
|
+
finSaveCfg({ symbols: [...finWatch] });
|
|
8427
|
+
finSymBtnUpdate(); /* TRADE AJANI düğmesi anında güncellensin */
|
|
8428
|
+
clearTimeout(finSymRefreshTimer);
|
|
8429
|
+
finSymRefreshTimer = setTimeout(() => finSnapshot(), 700); /* PİYASA kartı tazelensin */
|
|
8430
|
+
return i < 0; /* true = eklendi */
|
|
8431
|
+
}
|
|
8432
|
+
|
|
8433
|
+
function finRenderPickRows(rows, q) {
|
|
8434
|
+
if (!els.finSymPick) return;
|
|
8435
|
+
els.finSymPick.textContent = '';
|
|
8436
|
+
const term = String(q || '').trim().toUpperCase();
|
|
8437
|
+
const matches = rows.filter((r) => {
|
|
8438
|
+
if (!term) return true;
|
|
8439
|
+
return (String(r.name || '').toUpperCase().includes(term) || String(r.desc || '').toUpperCase().includes(term));
|
|
8440
|
+
});
|
|
8441
|
+
if (els.finSymCount) els.finSymCount.textContent = matches.length ? matches.length + ' sembol' : '';
|
|
8442
|
+
if (!matches.length) {
|
|
8443
|
+
const d = document.createElement('div');
|
|
8444
|
+
d.className = 'fin-empty';
|
|
8445
|
+
d.textContent = term ? 'Eşleşme yok' : 'Sembol listesi boş';
|
|
8446
|
+
els.finSymPick.appendChild(d);
|
|
8447
|
+
return;
|
|
8448
|
+
}
|
|
8449
|
+
const cap = 300;
|
|
8450
|
+
for (const r of matches.slice(0, cap)) {
|
|
8451
|
+
const row = document.createElement('div');
|
|
8452
|
+
row.className = 'fin-sym-row';
|
|
8453
|
+
const on = finWatch.includes(String(r.name || '').toUpperCase());
|
|
8454
|
+
row.innerHTML =
|
|
8455
|
+
'<span class="fsr-check">' + (on ? '✓' : '') + '</span>' +
|
|
8456
|
+
'<span class="fsr-name">' + escapeHtml(r.name || '?') + '</span>' +
|
|
8457
|
+
'<span class="fsr-desc">' + escapeHtml((r.desc || '').slice(0, 60)) + '</span>';
|
|
8458
|
+
if (on) row.classList.add('on');
|
|
8459
|
+
row.addEventListener('click', () => {
|
|
8460
|
+
const added = finToggleWatch(r.name);
|
|
8461
|
+
row.classList.toggle('on', added);
|
|
8462
|
+
row.querySelector('.fsr-check').textContent = added ? '✓' : '';
|
|
8463
|
+
});
|
|
8464
|
+
els.finSymPick.appendChild(row);
|
|
8465
|
+
}
|
|
8466
|
+
if (matches.length > cap) {
|
|
8467
|
+
const d = document.createElement('div');
|
|
8468
|
+
d.className = 'fin-empty';
|
|
8469
|
+
d.textContent = '…' + (matches.length - cap) + ' sembol daha — daraltmak için ara';
|
|
8470
|
+
els.finSymPick.appendChild(d);
|
|
8471
|
+
}
|
|
8472
|
+
}
|
|
8473
|
+
|
|
8474
|
+
async function finSymLoad(q) {
|
|
8475
|
+
if (!els.finSymPick) return;
|
|
8476
|
+
els.finSymPick.textContent = '';
|
|
8477
|
+
const d = document.createElement('div');
|
|
8478
|
+
d.className = 'fin-empty';
|
|
8479
|
+
d.textContent = 'MT5 sembolleri çekiliyor…';
|
|
8480
|
+
els.finSymPick.appendChild(d);
|
|
8481
|
+
const r = await beast.financeSymbolsList(q).catch(() => ({ ok: false, error: 'köprü yok' }));
|
|
8482
|
+
if (!r || !r.ok) {
|
|
8483
|
+
els.finSymPick.textContent = '';
|
|
8484
|
+
const e = document.createElement('div');
|
|
8485
|
+
e.className = 'fin-empty';
|
|
8486
|
+
e.textContent = (r && r.error) || 'MT5 sembolleri alınamadı';
|
|
8487
|
+
els.finSymPick.appendChild(e);
|
|
8488
|
+
if (els.finSymCount) els.finSymCount.textContent = '';
|
|
8489
|
+
return;
|
|
8490
|
+
}
|
|
8491
|
+
finRenderPickRows((r.data && r.data.symbols) || [], q);
|
|
8492
|
+
}
|
|
8493
|
+
|
|
8494
|
+
function finSymPickerOpen() {
|
|
8495
|
+
if (!els.finSymOverlay) return;
|
|
8496
|
+
els.finSymOverlay.hidden = false;
|
|
8497
|
+
if (els.finSymSearch) els.finSymSearch.value = '';
|
|
8498
|
+
finSymLoad('');
|
|
8499
|
+
if (els.finSymSearch) els.finSymSearch.focus();
|
|
8500
|
+
}
|
|
8501
|
+
|
|
8502
|
+
function finSymPickerClose() {
|
|
8503
|
+
if (els.finSymOverlay) els.finSymOverlay.hidden = true;
|
|
8504
|
+
}
|
|
8505
|
+
|
|
8506
|
+
if (els.finSymAdd) els.finSymAdd.addEventListener('click', finSymPickerOpen);
|
|
8507
|
+
if (els.finSymClose) els.finSymClose.addEventListener('click', finSymPickerClose);
|
|
8508
|
+
if (els.finSymOverlay) {
|
|
8509
|
+
els.finSymOverlay.addEventListener('click', (e) => {
|
|
8510
|
+
if (e.target === els.finSymOverlay) finSymPickerClose();
|
|
8511
|
+
});
|
|
8512
|
+
els.finSymOverlay.addEventListener('keydown', (e) => {
|
|
8513
|
+
if (e.key === 'Escape') { e.stopPropagation(); finSymPickerClose(); }
|
|
8514
|
+
});
|
|
8515
|
+
}
|
|
8516
|
+
if (els.finSymSearch) {
|
|
8517
|
+
els.finSymSearch.addEventListener('input', () => {
|
|
8518
|
+
clearTimeout(finSymSearchTimer);
|
|
8519
|
+
finSymSearchTimer = setTimeout(() => finSymLoad(els.finSymSearch.value.trim()), 350);
|
|
8520
|
+
});
|
|
8521
|
+
}
|
|
7966
8522
|
$('#filePick').addEventListener('click', async () => {
|
|
7967
8523
|
/* Studio modunda klasör seçimi Studio'nun KENDİ kökünü değiştirir —
|
|
7968
8524
|
IDE köküne dokunmaz (dünyalar ayrıdır) */
|
|
@@ -8070,9 +8626,10 @@ $('#filePreview').addEventListener('click', async () => {
|
|
|
8070
8626
|
toast((r && r.error) || 'preview açılamadı');
|
|
8071
8627
|
return;
|
|
8072
8628
|
}
|
|
8629
|
+
/* MOBİL: tunnel (telefon QR) yolu — emülatör entegrasyonu kaldırıldı */
|
|
8073
8630
|
const ask =
|
|
8074
8631
|
kind === 'expo'
|
|
8075
|
-
? 'Preview aç — bu klasör bir Expo/React Native projesi (app.json var).
|
|
8632
|
+
? 'Preview aç — bu klasör bir Expo/React Native projesi (app.json var). Arka planda `npx expo start --tunnel` çalıştır (tunnel: telefon her ağdan bağlanabilir; web önizleme için `--web` de ekle). Sunucu kalkınca çalışan http://localhost:PORT adresini raporda yaz.'
|
|
8076
8633
|
: kind === 'react-native'
|
|
8077
8634
|
? 'Preview aç — bu klasör bir React Native projesi. Metro sunucusunu arka planda başlat: package.json scripts.start varsa `npm start`, yoksa `npx react-native start`. Sunucu kalkınca çalışan http://localhost:PORT adresini raporda yaz.'
|
|
8078
8635
|
: kind === 'web'
|
|
@@ -9707,6 +10264,7 @@ async function sbClone(input) {
|
|
|
9707
10264
|
function setSandboxMode(on) {
|
|
9708
10265
|
if (on && ideModeOn()) { setIdeMode(false); }
|
|
9709
10266
|
if (on && studioModeOn()) { setStudioMode(false); }
|
|
10267
|
+
if (on && financeModeOn()) { setFinanceMode(false); }
|
|
9710
10268
|
document.body.classList.toggle('sandbox-mode', !!on);
|
|
9711
10269
|
if (els.sbBtn) els.sbBtn.classList.toggle('on', !!on);
|
|
9712
10270
|
/* marka + sol konsol başlığı: moduna göre BEAST Agent/Code/Studio/Sandbox */
|