beast-agent 2.14.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 +85 -3
- package/src/agent/financetools.js +307 -0
- package/src/agent/scripts/mt5_bridge.py +381 -0
- package/src/agent/tools.js +10 -3
- package/src/main.js +410 -28
- package/src/mt5bridge.js +215 -0
- package/src/preload.js +11 -3
- package/src/renderer/index.html +67 -0
- package/src/renderer/renderer.js +493 -38
- package/src/renderer/style.css +328 -1
- package/src/agent/android.js +0 -265
package/src/renderer/renderer.js
CHANGED
|
@@ -128,6 +128,33 @@ const els = {
|
|
|
128
128
|
stMic: $('#stMic'),
|
|
129
129
|
studioRow: $('#studioRow'),
|
|
130
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'),
|
|
131
158
|
stVideoEl: $('#stVideoEl'),
|
|
132
159
|
stVideoName: $('#stVideoName'),
|
|
133
160
|
stVideoOpen: $('#stVideoOpen'),
|
|
@@ -879,6 +906,9 @@ async function renderSessions(list) {
|
|
|
879
906
|
tgSet = new Set(await beast.tgListSessions());
|
|
880
907
|
} catch {}
|
|
881
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));
|
|
882
912
|
/* elle sıra: sessionOrder'daki id'ler önde (o sırayla), diğerleri engine sırasıyla arkada */
|
|
883
913
|
const rank = new Map(sessionOrder.map((id, i) => [id, i]));
|
|
884
914
|
const withRank = list.map((s, i) => ({ s, r: rank.has(s.id) ? rank.get(s.id) : 1e6 + i }));
|
|
@@ -977,6 +1007,8 @@ async function openSession(id) {
|
|
|
977
1007
|
els.msgs.innerHTML = '';
|
|
978
1008
|
showEmpty(s.messages.length === 0);
|
|
979
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(() => {});
|
|
980
1012
|
|
|
981
1013
|
for (let i = 0; i < s.messages.length; i++) {
|
|
982
1014
|
const m = s.messages[i];
|
|
@@ -2379,9 +2411,6 @@ async function renderInstallPane() {
|
|
|
2379
2411
|
(r.canToggle
|
|
2380
2412
|
? '<button class="btn hr-tgl" data-on="' + (r.toggleOn ? '1' : '0') + '" style="width:auto;padding:2px 12px;flex:none">' + (r.toggleOn ? 'KAPAT' : 'AÇ') + '</button>'
|
|
2381
2413
|
: '') +
|
|
2382
|
-
(r.canInstall
|
|
2383
|
-
? '<button class="btn inst-kur" style="width:auto;padding:2px 12px;flex:none">KUR</button>'
|
|
2384
|
-
: '') +
|
|
2385
2414
|
badge(r.state) +
|
|
2386
2415
|
pctLabel(r) +
|
|
2387
2416
|
'</div>'
|
|
@@ -2397,16 +2426,6 @@ async function renderInstallPane() {
|
|
|
2397
2426
|
refresh();
|
|
2398
2427
|
});
|
|
2399
2428
|
});
|
|
2400
|
-
/* Android emülatörü: KUR → arka plan SDK kurulumu (JDK + cmdline-tools + imaj) */
|
|
2401
|
-
el.querySelectorAll('.inst-kur').forEach((b) => {
|
|
2402
|
-
b.addEventListener('click', async () => {
|
|
2403
|
-
b.disabled = true;
|
|
2404
|
-
b.textContent = '…';
|
|
2405
|
-
await beast.androidInstall().catch(() => {});
|
|
2406
|
-
toast('Android SDK arka planda kuruluyor — ~1.5GB, internete göre 5-20 dk sürer');
|
|
2407
|
-
refresh();
|
|
2408
|
-
});
|
|
2409
|
-
});
|
|
2410
2429
|
};
|
|
2411
2430
|
|
|
2412
2431
|
const refresh = async () => {
|
|
@@ -6089,6 +6108,11 @@ async function init() {
|
|
|
6089
6108
|
showEmpty(true);
|
|
6090
6109
|
}
|
|
6091
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
|
+
|
|
6092
6116
|
els.newChat.addEventListener('click', async () => {
|
|
6093
6117
|
try {
|
|
6094
6118
|
const created = await beast.createSession();
|
|
@@ -7245,6 +7269,7 @@ function ideModeOn() {
|
|
|
7245
7269
|
async function setIdeMode(on) {
|
|
7246
7270
|
if (on && studioModeOn()) await setStudioMode(false); /* Studio açıkken IDE'ye geçiş — Studio kapanır */
|
|
7247
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 */
|
|
7248
7273
|
document.body.classList.toggle('ide-mode', !!on);
|
|
7249
7274
|
if (els.ideBtn) els.ideBtn.classList.toggle('on', !!on);
|
|
7250
7275
|
/* soldaki marka: chat modunda BEAST Agent, IDE modunda BEAST Code, Studio modunda BEAST Studio */
|
|
@@ -7319,6 +7344,7 @@ function studioModeOn() {
|
|
|
7319
7344
|
async function setStudioMode(on) {
|
|
7320
7345
|
if (on && ideModeOn()) await setIdeMode(false); /* IDE açıkken Studio'ya geçiş — IDE kapanır */
|
|
7321
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 */
|
|
7322
7348
|
document.body.classList.toggle('studio-mode', !!on);
|
|
7323
7349
|
if (els.studioBtn) els.studioBtn.classList.toggle('on', !!on);
|
|
7324
7350
|
/* ALT TERMINAL: yalnız Beast Code'a özgü — Studio'ya geçişte kapanır */
|
|
@@ -8049,6 +8075,450 @@ async function codeReloadIfOpen(p) {
|
|
|
8049
8075
|
|
|
8050
8076
|
$('#ideBtn').addEventListener('click', () => setIdeMode(!ideModeOn()));
|
|
8051
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
|
+
}
|
|
8052
8522
|
$('#filePick').addEventListener('click', async () => {
|
|
8053
8523
|
/* Studio modunda klasör seçimi Studio'nun KENDİ kökünü değiştirir —
|
|
8054
8524
|
IDE köküne dokunmaz (dünyalar ayrıdır) */
|
|
@@ -8156,31 +8626,15 @@ $('#filePreview').addEventListener('click', async () => {
|
|
|
8156
8626
|
toast((r && r.error) || 'preview açılamadı');
|
|
8157
8627
|
return;
|
|
8158
8628
|
}
|
|
8159
|
-
/*
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
ask =
|
|
8169
|
-
kind === 'expo'
|
|
8170
|
-
? 'Preview aç — bu klasör bir Expo/React Native projesi (app.json var). Android emülatörü başlatıldı; arka planda `npx expo start --android` çalıştır — uygulama emülatörde açılır (expo adb ile cihazı kendisi bulur). Çalışan http://localhost:PORT adresini raporda yaz.'
|
|
8171
|
-
: 'Preview aç — bu klasör bir React Native projesi. Android emülatörü hazır. Metro\u2019yu arka planda başlat (scripts.start varsa `npm start`) ve uygulamayı emülatöre kur: `npx react-native run-android`. Sunucu adresini raporda yaz.';
|
|
8172
|
-
}
|
|
8173
|
-
}
|
|
8174
|
-
if (!ask) {
|
|
8175
|
-
ask =
|
|
8176
|
-
kind === 'expo'
|
|
8177
|
-
? '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.'
|
|
8178
|
-
: kind === 'react-native'
|
|
8179
|
-
? '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.'
|
|
8180
|
-
: kind === 'web'
|
|
8181
|
-
? 'Preview aç — bu proje dev-server ister. Doğru komutu package.json scripts\'ından seç (dev/start/serve — `npm run dev` vb.) ve arka planda başlat. Sunucu kalkınca çalışan http://localhost:PORT adresini raporda yaz.'
|
|
8182
|
-
: 'Preview aç — bu projenin nasıl çalıştığını package.json / konfigürasyon dosyalarından anla, doğru dev komutunu arka planda başlat ve çalışan http://localhost:PORT adresini raporda yaz.';
|
|
8183
|
-
}
|
|
8629
|
+
/* MOBİL: tunnel (telefon QR) yolu — emülatör entegrasyonu kaldırıldı */
|
|
8630
|
+
const ask =
|
|
8631
|
+
kind === 'expo'
|
|
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.'
|
|
8633
|
+
: kind === 'react-native'
|
|
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.'
|
|
8635
|
+
: kind === 'web'
|
|
8636
|
+
? 'Preview aç — bu proje dev-server ister. Doğru komutu package.json scripts\'ından seç (dev/start/serve — `npm run dev` vb.) ve arka planda başlat. Sunucu kalkınca çalışan http://localhost:PORT adresini raporda yaz.'
|
|
8637
|
+
: 'Preview aç — bu projenin nasıl çalıştığını package.json / konfigürasyon dosyalarından anla, doğru dev komutunu arka planda başlat ve çalışan http://localhost:PORT adresini raporda yaz.';
|
|
8184
8638
|
if (els.bcInput) {
|
|
8185
8639
|
els.bcInput.value = ask;
|
|
8186
8640
|
bcInputResize();
|
|
@@ -9810,6 +10264,7 @@ async function sbClone(input) {
|
|
|
9810
10264
|
function setSandboxMode(on) {
|
|
9811
10265
|
if (on && ideModeOn()) { setIdeMode(false); }
|
|
9812
10266
|
if (on && studioModeOn()) { setStudioMode(false); }
|
|
10267
|
+
if (on && financeModeOn()) { setFinanceMode(false); }
|
|
9813
10268
|
document.body.classList.toggle('sandbox-mode', !!on);
|
|
9814
10269
|
if (els.sbBtn) els.sbBtn.classList.toggle('on', !!on);
|
|
9815
10270
|
/* marka + sol konsol başlığı: moduna göre BEAST Agent/Code/Studio/Sandbox */
|