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/main.js
CHANGED
|
@@ -26,7 +26,6 @@ const watchers = require('./agent/watchers');
|
|
|
26
26
|
const usageMod = require('./agent/usage');
|
|
27
27
|
const bus = require('./agent/bus');
|
|
28
28
|
const computeruse = require('./agent/computeruse');
|
|
29
|
-
const android = require('./agent/android');
|
|
30
29
|
const log = require('./agent/logger');
|
|
31
30
|
const headroom = require('./agent/headroom');
|
|
32
31
|
const QRCode = require('qrcode'); /* Expo Go QR (bc-expurl) — whatsapp ile aynı paket */
|
|
@@ -3107,6 +3106,7 @@ function reloadBackend() {
|
|
|
3107
3106
|
bcFlushOnDone(ev); /* Beast Code kuyruğunu iş bitiminde boşalt */
|
|
3108
3107
|
stFlushOnDone(ev); /* Beast Studio kuyruğunu iş bitiminde boşalt */
|
|
3109
3108
|
sbFlushOnDone(ev); /* Sandbox kuyruğunu iş bitiminde boşalt */
|
|
3109
|
+
finFlushOnDone(ev); /* Finance trader döngüsü: iş bitince sıradaki turu planla */
|
|
3110
3110
|
/* BC canlı önizleme: ajan bir dev server başlattıysa adresi yakala —
|
|
3111
3111
|
preview butonu ve otomatik açılış DAİMA bu sunucuyu öncelikli kullanır */
|
|
3112
3112
|
if (ev.type === 'bc-preview' && /^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d{2,5})?/i.test(String(ev.url || ''))) {
|
|
@@ -4904,6 +4904,13 @@ ipcMain.handle('sessions:create', () => {
|
|
|
4904
4904
|
engine.setSessionTools(v.id, null);
|
|
4905
4905
|
}
|
|
4906
4906
|
engine.setSessionModel(v.id, b && !b.admin ? (b.model || null) : null);
|
|
4907
|
+
/* Beast Finance modu açıkken açılan sohbet finance izolasyonuna girer */
|
|
4908
|
+
if (financeState.mode) {
|
|
4909
|
+
try {
|
|
4910
|
+
engine.markFinance(v.id, false);
|
|
4911
|
+
v.finance = true;
|
|
4912
|
+
} catch {}
|
|
4913
|
+
}
|
|
4907
4914
|
return v;
|
|
4908
4915
|
});
|
|
4909
4916
|
ipcMain.handle('sessions:open', (_e, id) => engine.openSession(id));
|
|
@@ -4932,6 +4939,25 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
4932
4939
|
}
|
|
4933
4940
|
}
|
|
4934
4941
|
} catch {}
|
|
4942
|
+
/* BEAST FINANCE: finance modu açıkken bu sohbet finance listesine girer ve
|
|
4943
|
+
mt5_* araçlarını görür (botlar hariç); mod kapalıysa etiket kaldırılır */
|
|
4944
|
+
try {
|
|
4945
|
+
if (sessionId) {
|
|
4946
|
+
const fsid = String(sessionId);
|
|
4947
|
+
let fsess = engine.cache.get(fsid);
|
|
4948
|
+
if (!fsess) { try { fsess = engine._load(fsid); } catch {} }
|
|
4949
|
+
if (fsess && !fsess.botId && !fsess.bgJob) {
|
|
4950
|
+
if (financeState.mode) {
|
|
4951
|
+
if (!fsess.finance) engine.markFinance(fsid, false);
|
|
4952
|
+
finApplyTraderFields(fsess);
|
|
4953
|
+
fsess.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
4954
|
+
engine.cache.set(fsid, fsess);
|
|
4955
|
+
} else if (fsess.finance) {
|
|
4956
|
+
engine.unmarkFinance(fsid); /* normal modda yazınca normal sohbete döner */
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
} catch {}
|
|
4935
4961
|
if (t === '/stop' || t === '/start') {
|
|
4936
4962
|
handleGlobalStopStart(sessionId, t);
|
|
4937
4963
|
return true;
|
|
@@ -6310,29 +6336,6 @@ ipcMain.handle('install:status', async () => {
|
|
|
6310
6336
|
});
|
|
6311
6337
|
}
|
|
6312
6338
|
|
|
6313
|
-
/* 10) Android Emülatörü — opsiyonel; varsa Beast Code mobil uygulamaları emülatörde açar */
|
|
6314
|
-
{
|
|
6315
|
-
const st = android.probe();
|
|
6316
|
-
let state = 'optional';
|
|
6317
|
-
if (st.sdk) state = 'ok';
|
|
6318
|
-
else if (st.installing) state = 'loading';
|
|
6319
|
-
if (st.failed) state = 'failed';
|
|
6320
|
-
rows.push({
|
|
6321
|
-
id: 'android',
|
|
6322
|
-
name: 'Android Emülatörü — mobil uygulamalar emülatörde çalışır',
|
|
6323
|
-
state,
|
|
6324
|
-
canInstall: true,
|
|
6325
|
-
detail: st.installing
|
|
6326
|
-
? (st.stage || 'arka planda kuruluyor — JDK + SDK + sistem imajı (~1.5GB, internete göre 5-20 dk)')
|
|
6327
|
-
: st.failed
|
|
6328
|
-
? (st.error || 'kurulum başarısız')
|
|
6329
|
-
: st.sdk
|
|
6330
|
-
? 'hazır — SDK: ' + st.sdkPath
|
|
6331
|
-
: 'kurulu değil — KUR ile arka planda kurulur (istekge bağlı)',
|
|
6332
|
-
...(st.installing ? pctFields('android') : {}),
|
|
6333
|
-
});
|
|
6334
|
-
}
|
|
6335
|
-
|
|
6336
6339
|
return rows;
|
|
6337
6340
|
});
|
|
6338
6341
|
|
|
@@ -6349,10 +6352,7 @@ ipcMain.handle('headroom:toggle', async (_e, on) => {
|
|
|
6349
6352
|
ipcMain.handle('headroom:status', () => headroom.probe());
|
|
6350
6353
|
ipcMain.handle('headroom:stats', () => headroom.stats());
|
|
6351
6354
|
|
|
6352
|
-
/* ANDROID EMÜLATÖR
|
|
6353
|
-
ipcMain.handle('android:status', async () => ({ ok: true, ...(await android.probe()), avds: await android.listAvds() }));
|
|
6354
|
-
ipcMain.handle('android:install', () => android.autoInstall());
|
|
6355
|
-
ipcMain.handle('android:startAvd', async (_e, name) => android.startAvd(name));
|
|
6355
|
+
/* ANDROID EMÜLATÖR: kaldırıldı — mobil önizleme tunnel (telefon QR) yoluyla yapılır */
|
|
6356
6356
|
|
|
6357
6357
|
/* embedding modelini şimdi indir (mem0 arama yolu ısıtılır) */
|
|
6358
6358
|
ipcMain.handle('embed:prefetch', () => {
|
|
@@ -7987,6 +7987,388 @@ function studioWatchStop() {
|
|
|
7987
7987
|
if (studioWatchTimer) { clearTimeout(studioWatchTimer); studioWatchTimer = null; }
|
|
7988
7988
|
}
|
|
7989
7989
|
|
|
7990
|
+
/* ---------------- Beast Finance (MT5) ----------------
|
|
7991
|
+
Studio/Sandbox ile AYRI dünya: solda sohbet geçmişi + izleyiciler AYNI kalır,
|
|
7992
|
+
ortada chat, sağda MT5 işlem paneli. İki ajan: (1) chat copilot'ı — aktif
|
|
7993
|
+
sohbet finance bayrağıyla mt5_* araçlarını görür; (2) TRADER — gizli engine
|
|
7994
|
+
oturumu, tur tur piyasa tarayıp (farklı API/model seçilebilir) işlem kovalar. */
|
|
7995
|
+
const mt5bridge = require('./mt5bridge');
|
|
7996
|
+
const financetools = require('./agent/financetools');
|
|
7997
|
+
|
|
7998
|
+
const financeState = {
|
|
7999
|
+
mode: false, /* chat oturumları finance bayrağı alıyor mu */
|
|
8000
|
+
traderSid: null,
|
|
8001
|
+
traderOn: false,
|
|
8002
|
+
traderTimer: null,
|
|
8003
|
+
traderRounds: 0,
|
|
8004
|
+
lastRoundAt: 0,
|
|
8005
|
+
installing: false,
|
|
8006
|
+
installTried: false,
|
|
8007
|
+
};
|
|
8008
|
+
|
|
8009
|
+
function financeDir() {
|
|
8010
|
+
const d = path.join(APP_DIR, 'finance');
|
|
8011
|
+
try { fs.mkdirSync(d, { recursive: true }); } catch {}
|
|
8012
|
+
return d;
|
|
8013
|
+
}
|
|
8014
|
+
|
|
8015
|
+
function finCfg() {
|
|
8016
|
+
if (!settings.finance || typeof settings.finance !== 'object') settings.finance = {};
|
|
8017
|
+
const f = settings.finance;
|
|
8018
|
+
if (!Array.isArray(f.symbols) || !f.symbols.length) f.symbols = ['EURUSD', 'XAUUSD', 'GBPUSD', 'BTCUSD'];
|
|
8019
|
+
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
8020
|
+
if (!Number(f.maxLot)) f.maxLot = 0.1;
|
|
8021
|
+
if (f.maxPositions == null) f.maxPositions = 3;
|
|
8022
|
+
if (typeof f.allowTrading !== 'boolean') f.allowTrading = false;
|
|
8023
|
+
return f;
|
|
8024
|
+
}
|
|
8025
|
+
|
|
8026
|
+
function financeLog(line) {
|
|
8027
|
+
try {
|
|
8028
|
+
fs.appendFileSync(path.join(financeDir(), 'finance.log'), `[${new Date().toISOString()}] ${line}\n`);
|
|
8029
|
+
} catch {}
|
|
8030
|
+
finPush('log', { line });
|
|
8031
|
+
}
|
|
8032
|
+
|
|
8033
|
+
function finPush(fn, data) {
|
|
8034
|
+
try {
|
|
8035
|
+
if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'finance', fn, ...data });
|
|
8036
|
+
} catch {}
|
|
8037
|
+
}
|
|
8038
|
+
|
|
8039
|
+
/* financetools ayar + bildirim kancası */
|
|
8040
|
+
try {
|
|
8041
|
+
financetools.setConfig(() => finCfg());
|
|
8042
|
+
financetools.setNotify((entry) => {
|
|
8043
|
+
const k = String((entry && entry.kind) || '');
|
|
8044
|
+
const d = entry && entry.data ? entry.data : {};
|
|
8045
|
+
let line = '';
|
|
8046
|
+
if (k === 'trade') line = `İŞLEM AÇILDI: ${d.side} ${d.volume} ${d.symbol}`;
|
|
8047
|
+
else if (k === 'close') line = `POZİSYON KAPANDI: ticket ${d.ticket}${d.volume ? ' (' + d.volume + ' lot)' : ''}`;
|
|
8048
|
+
else if (k === 'modify') line = `SL/TP GÜNCELLENDİ: ticket ${d.ticket} (SL ${d.sl || 0} / TP ${d.tp || 0})`;
|
|
8049
|
+
else if (k === 'pending') line = `BEKLEYEN EMİR: ${d.type} ${d.volume} ${d.symbol}`;
|
|
8050
|
+
else if (k === 'cancel') line = `EMİR İPTAL: ticket ${d.ticket}`;
|
|
8051
|
+
if (line) {
|
|
8052
|
+
financeLog('[ajan] ' + line);
|
|
8053
|
+
finPush('trade', { line });
|
|
8054
|
+
}
|
|
8055
|
+
});
|
|
8056
|
+
} catch {}
|
|
8057
|
+
|
|
8058
|
+
function financeEnsureBridge() {
|
|
8059
|
+
const f = finCfg();
|
|
8060
|
+
const candidates = [String(f.pythonPath || '').trim(), 'python', 'py -3'].filter(Boolean);
|
|
8061
|
+
/* Beast gömülü Python runtime kuruluysa en başa ekle (makinede Python olmasa da köprü çalışır) */
|
|
8062
|
+
try {
|
|
8063
|
+
const t = require('./agent/tools');
|
|
8064
|
+
const emb = t.embeddedPythonExe();
|
|
8065
|
+
if (emb && fs.existsSync(emb) && !candidates.includes(emb)) candidates.unshift(emb);
|
|
8066
|
+
} catch {}
|
|
8067
|
+
mt5bridge.start({
|
|
8068
|
+
candidates,
|
|
8069
|
+
terminal: String(f.terminalPath || '').trim(),
|
|
8070
|
+
});
|
|
8071
|
+
return mt5bridge.status();
|
|
8072
|
+
}
|
|
8073
|
+
|
|
8074
|
+
/* MetaTrader5 paketi yoksa bir kez pip ile kurmayı dene */
|
|
8075
|
+
function financeTryInstall() {
|
|
8076
|
+
if (financeState.installing || (financeState.installTried && finCfg().autoInstall === false)) return;
|
|
8077
|
+
financeState.installing = true;
|
|
8078
|
+
financeState.installTried = true;
|
|
8079
|
+
financeLog('[MT5] MetaTrader5 python paketi kuruluyor…');
|
|
8080
|
+
const py = mt5bridge.status().python || 'python';
|
|
8081
|
+
/* 'py -3' gibi çok kelimeli kısa komutlar bölünür; tam yol tek exe'dir */
|
|
8082
|
+
const parts = /[\\/]/.test(py) ? [py] : py.split(/\s+/);
|
|
8083
|
+
const p = spawn(parts[0], [...parts.slice(1), '-m', 'pip', 'install', '--user', 'MetaTrader5'], { windowsHide: true });
|
|
8084
|
+
let tail = '';
|
|
8085
|
+
p.stdout.on('data', (c) => { tail = (tail + c).slice(-800); });
|
|
8086
|
+
p.stderr.on('data', (c) => { tail = (tail + c).slice(-800); });
|
|
8087
|
+
p.on('error', (e) => {
|
|
8088
|
+
financeState.installing = false;
|
|
8089
|
+
financeLog('[MT5] pip başlatılamadı: ' + String((e && e.message) || e));
|
|
8090
|
+
});
|
|
8091
|
+
p.on('exit', (code) => {
|
|
8092
|
+
financeState.installing = false;
|
|
8093
|
+
if (code === 0) {
|
|
8094
|
+
financeLog('[MT5] MetaTrader5 paketi kuruldu — köprü yeniden başlatılıyor');
|
|
8095
|
+
mt5bridge.stop();
|
|
8096
|
+
financeEnsureBridge();
|
|
8097
|
+
} else {
|
|
8098
|
+
financeLog('[MT5] kurulum başarısız (kod ' + code + ') ' + tail.slice(-300));
|
|
8099
|
+
}
|
|
8100
|
+
finPush('install', { installing: false, code });
|
|
8101
|
+
});
|
|
8102
|
+
}
|
|
8103
|
+
|
|
8104
|
+
mt5bridge.on('bridge', (m) => {
|
|
8105
|
+
if (m && m.connected) {
|
|
8106
|
+
financeLog('[MT5] terminal bağlı: ' + ((m.terminal && (m.terminal.name + ' @ ' + m.terminal.company)) || 'MT5'));
|
|
8107
|
+
if (m.account) financeLog(`[MT5] hesap ${m.account.login} · bakiye ${m.account.balance} ${m.account.currency}`);
|
|
8108
|
+
} else if (m && m.error) {
|
|
8109
|
+
financeLog('[MT5] bağlantı yok — ' + m.error);
|
|
8110
|
+
if (/paket/i.test(m.error)) financeTryInstall();
|
|
8111
|
+
}
|
|
8112
|
+
finPush('bridge', { connected: !!(m && m.connected), error: (m && m.error) || '' });
|
|
8113
|
+
});
|
|
8114
|
+
mt5bridge.on('log', (m) => {
|
|
8115
|
+
const line = String((m && m.line) || '');
|
|
8116
|
+
if (!line) return;
|
|
8117
|
+
financeLog('[mt5] ' + line.slice(0, 300));
|
|
8118
|
+
});
|
|
8119
|
+
|
|
8120
|
+
/* ---------- TRADER ajanı (gizli engine oturumu + tur döngüsü) ---------- */
|
|
8121
|
+
|
|
8122
|
+
function finTraderSession() {
|
|
8123
|
+
let sid = financeState.traderSid;
|
|
8124
|
+
if (sid) {
|
|
8125
|
+
try {
|
|
8126
|
+
const s = engine.cache.get(sid);
|
|
8127
|
+
if (s) return s;
|
|
8128
|
+
} catch {}
|
|
8129
|
+
financeState.traderSid = null;
|
|
8130
|
+
}
|
|
8131
|
+
const s = engine._load(engine.createSession().id);
|
|
8132
|
+
s.messages = s.messages || [];
|
|
8133
|
+
s.bgTitle = 'Beast Finance · Trader'; /* _view.isBg → sohbet geçmişinde gizli */
|
|
8134
|
+
try {
|
|
8135
|
+
fs.appendFileSync(
|
|
8136
|
+
engine._file(s.id),
|
|
8137
|
+
JSON.stringify({ t: 'meta2', bgOf: '', title: 'Beast Finance · Trader', at: new Date().toISOString() }) + '\n'
|
|
8138
|
+
);
|
|
8139
|
+
} catch {}
|
|
8140
|
+
engine.cache.set(s.id, s);
|
|
8141
|
+
engine.markFinance(s.id, true); /* kalıcı finance etiketi (trader rolüyle) */
|
|
8142
|
+
s.workspace = financeDir(); /* trader kendi klasöründe çalışır */
|
|
8143
|
+
financeState.traderSid = s.id;
|
|
8144
|
+
return s;
|
|
8145
|
+
}
|
|
8146
|
+
|
|
8147
|
+
function finApplyTraderFields(s) {
|
|
8148
|
+
const f = finCfg();
|
|
8149
|
+
s.finance = true;
|
|
8150
|
+
s.financeTrader = true;
|
|
8151
|
+
s.financeAuto = f.allowTrading === true;
|
|
8152
|
+
s.financeSymbols = f.symbols;
|
|
8153
|
+
s.financeStrategy = String(f.strategy || '');
|
|
8154
|
+
s.financeLimits = { maxLot: f.maxLot, maxPositions: f.maxPositions };
|
|
8155
|
+
engine.cache.set(String(s.id), s);
|
|
8156
|
+
}
|
|
8157
|
+
|
|
8158
|
+
function finTraderBrief() {
|
|
8159
|
+
const f = finCfg();
|
|
8160
|
+
return [
|
|
8161
|
+
'Beast Finance TRADER başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.',
|
|
8162
|
+
`İzleme listesi: ${(f.symbols || []).join(', ')}`,
|
|
8163
|
+
`Tur aralığı: ${f.intervalSec} sn · Max lot: ${f.maxLot} · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
8164
|
+
f.allowTrading
|
|
8165
|
+
? 'Otomatik işlem AÇIK: mt5_trade/mt5_close/mt5_modify/mt5_pending kullanabilirsin (limitler sistemce zorlanır).'
|
|
8166
|
+
: 'Otomatik işlem KAPALI: SADECE analiz + net işlem önerileri yaz (sembol, yön, giriş, SL, TP, sebep); işlem açma.',
|
|
8167
|
+
f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
8168
|
+
'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
|
|
8169
|
+
].join('\n');
|
|
8170
|
+
}
|
|
8171
|
+
|
|
8172
|
+
function finTraderRound() {
|
|
8173
|
+
if (!financeState.traderOn) return;
|
|
8174
|
+
if (!engine) return;
|
|
8175
|
+
const s = finTraderSession();
|
|
8176
|
+
finApplyTraderFields(s);
|
|
8177
|
+
if (engine.isBusy(s.id)) {
|
|
8178
|
+
/* hâlâ çalışıyor — done eventinde tekrar planlanır */
|
|
8179
|
+
return;
|
|
8180
|
+
}
|
|
8181
|
+
financeState.traderRounds += 1;
|
|
8182
|
+
const f = finCfg();
|
|
8183
|
+
const auto = f.allowTrading
|
|
8184
|
+
? 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.'
|
|
8185
|
+
: 'Otomatik işlem KAPALI — sadece analiz + öneri.';
|
|
8186
|
+
const ok = engine.send(
|
|
8187
|
+
s.id,
|
|
8188
|
+
`FINANCE TUR #${financeState.traderRounds}: hesap + pozisyonlar + izleme listesi fiyatlarını çek; açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); stratejine göre yeni fırsatları değerlendir. ${auto} Kısa rapor ver.`,
|
|
8189
|
+
{ userAction: false }
|
|
8190
|
+
);
|
|
8191
|
+
if (ok) {
|
|
8192
|
+
financeState.lastRoundAt = Date.now();
|
|
8193
|
+
finPush('trader', { state: 'running', round: financeState.traderRounds });
|
|
8194
|
+
} else {
|
|
8195
|
+
/* stop kapısı/yoğunluk — yarım dakika sonra sessizce tekrar dene */
|
|
8196
|
+
clearTimeout(financeState.traderTimer);
|
|
8197
|
+
financeState.traderTimer = setTimeout(() => { try { finTraderRound(); } catch {} }, 30000);
|
|
8198
|
+
}
|
|
8199
|
+
}
|
|
8200
|
+
|
|
8201
|
+
function finFlushOnDone(ev) {
|
|
8202
|
+
if (!ev || (ev.type !== 'done' && ev.type !== 'error')) return;
|
|
8203
|
+
if (String(ev.sessionId || '') !== String(financeState.traderSid || '')) return;
|
|
8204
|
+
if (!financeState.traderOn) return;
|
|
8205
|
+
clearTimeout(financeState.traderTimer);
|
|
8206
|
+
const f = finCfg();
|
|
8207
|
+
const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
|
|
8208
|
+
financeState.lastRoundAt = Date.now();
|
|
8209
|
+
finPush('trader', { state: 'idle', round: financeState.traderRounds, nextInSec: iv / 1000 });
|
|
8210
|
+
financeState.traderTimer = setTimeout(() => { try { finTraderRound(); } catch {} }, iv);
|
|
8211
|
+
}
|
|
8212
|
+
|
|
8213
|
+
ipcMain.handle('finance:state', async () => {
|
|
8214
|
+
const f = finCfg();
|
|
8215
|
+
const busy = financeState.traderSid && engine ? engine.isBusy(financeState.traderSid) : false;
|
|
8216
|
+
return {
|
|
8217
|
+
ok: true,
|
|
8218
|
+
mode: financeState.mode,
|
|
8219
|
+
cfg: f,
|
|
8220
|
+
bridge: mt5bridge.status(),
|
|
8221
|
+
trader: { on: financeState.traderOn, busy, rounds: financeState.traderRounds, lastAt: financeState.lastRoundAt, sid: financeState.traderSid },
|
|
8222
|
+
models: engine ? engine.publicState().models : [],
|
|
8223
|
+
};
|
|
8224
|
+
});
|
|
8225
|
+
|
|
8226
|
+
ipcMain.handle('finance:snapshot', async () => {
|
|
8227
|
+
const f = finCfg();
|
|
8228
|
+
const st = financeEnsureBridge();
|
|
8229
|
+
let account = st.account || null;
|
|
8230
|
+
let positions = [];
|
|
8231
|
+
let symbols = [];
|
|
8232
|
+
if (st.running) {
|
|
8233
|
+
const [a, p, sy] = await Promise.all([
|
|
8234
|
+
mt5bridge.call('account', {}, 8000).catch(() => null),
|
|
8235
|
+
mt5bridge.call('positions', {}, 8000).catch(() => null),
|
|
8236
|
+
mt5bridge.call('symbols', { symbols: f.symbols || [] }, 10000).catch(() => null),
|
|
8237
|
+
]);
|
|
8238
|
+
if (a && a.ok) account = a.data && a.data.account;
|
|
8239
|
+
if (p && p.ok) positions = (p.data && p.data.positions) || [];
|
|
8240
|
+
if (sy && sy.ok) symbols = (sy.data && sy.data.symbols) || [];
|
|
8241
|
+
}
|
|
8242
|
+
const busy = financeState.traderSid && engine ? engine.isBusy(financeState.traderSid) : false;
|
|
8243
|
+
return {
|
|
8244
|
+
ok: true,
|
|
8245
|
+
bridge: { running: st.running, connected: st.connected, error: st.error, terminal: st.terminal, python: st.python },
|
|
8246
|
+
account,
|
|
8247
|
+
positions,
|
|
8248
|
+
symbols,
|
|
8249
|
+
cfg: f,
|
|
8250
|
+
trader: { on: financeState.traderOn, busy, rounds: financeState.traderRounds, lastAt: financeState.lastRoundAt },
|
|
8251
|
+
};
|
|
8252
|
+
});
|
|
8253
|
+
|
|
8254
|
+
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
8255
|
+
financeState.mode = !!(payload && payload.on);
|
|
8256
|
+
const sid = String((payload && payload.sessionId) || '');
|
|
8257
|
+
if (sid && engine) {
|
|
8258
|
+
try {
|
|
8259
|
+
let s = engine.cache.get(sid);
|
|
8260
|
+
if (!s) { try { s = engine._load(sid); } catch {} }
|
|
8261
|
+
if (s && !s.botId && !s.bgJob) {
|
|
8262
|
+
if (financeState.mode) {
|
|
8263
|
+
engine.markFinance(sid, false); /* kalıcı etiket: finance geçmişine girer */
|
|
8264
|
+
finApplyTraderFields(s);
|
|
8265
|
+
s.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
8266
|
+
} else {
|
|
8267
|
+
engine.unmarkFinance(sid); /* normal sohbete döner */
|
|
8268
|
+
}
|
|
8269
|
+
engine.cache.set(sid, s);
|
|
8270
|
+
}
|
|
8271
|
+
} catch {}
|
|
8272
|
+
}
|
|
8273
|
+
return { ok: true, mode: financeState.mode };
|
|
8274
|
+
});
|
|
8275
|
+
|
|
8276
|
+
/* MT5 sembol seçici: terminaldeki tüm semboller (isteğe bağlı *filter*) */
|
|
8277
|
+
ipcMain.handle('finance:symbols:list', async (_e, filter) => {
|
|
8278
|
+
if (!mt5bridge.running) return { ok: false, error: 'MT5 köprüsü bağlı değil — terminal açık mı?' };
|
|
8279
|
+
return await mt5bridge.call('all_symbols', { filter: String(filter || '').trim() }, 20000);
|
|
8280
|
+
});
|
|
8281
|
+
|
|
8282
|
+
ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
8283
|
+
const f = finCfg();
|
|
8284
|
+
const p = patch || {};
|
|
8285
|
+
if (p.symbols !== undefined) {
|
|
8286
|
+
const arr = Array.isArray(p.symbols) ? p.symbols : String(p.symbols).split(/[,\s]+/);
|
|
8287
|
+
f.symbols = arr.map((s) => String(s).trim().toUpperCase()).filter(Boolean).slice(0, 20);
|
|
8288
|
+
}
|
|
8289
|
+
if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
|
|
8290
|
+
if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
|
|
8291
|
+
if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
|
|
8292
|
+
if (p.allowTrading !== undefined) f.allowTrading = !!p.allowTrading;
|
|
8293
|
+
if (p.strategy !== undefined) f.strategy = String(p.strategy || '').slice(0, 2000);
|
|
8294
|
+
if (p.pythonPath !== undefined) f.pythonPath = String(p.pythonPath || '').trim();
|
|
8295
|
+
if (p.terminalPath !== undefined) f.terminalPath = String(p.terminalPath || '').trim();
|
|
8296
|
+
if (p.traderSel !== undefined) {
|
|
8297
|
+
f.traderSel = String(p.traderSel || '').trim();
|
|
8298
|
+
if (financeState.traderSid && engine) {
|
|
8299
|
+
try { engine.setSessionModel(financeState.traderSid, f.traderSel || null); } catch {}
|
|
8300
|
+
}
|
|
8301
|
+
}
|
|
8302
|
+
saveSettings();
|
|
8303
|
+
/* trader oturumu alanlarını tazele (oturum yoksa oluşturma — start'ta kurulur) */
|
|
8304
|
+
if (financeState.traderSid && engine) {
|
|
8305
|
+
try {
|
|
8306
|
+
const ts = engine.cache.get(financeState.traderSid);
|
|
8307
|
+
if (ts) finApplyTraderFields(ts);
|
|
8308
|
+
} catch {}
|
|
8309
|
+
}
|
|
8310
|
+
return { ok: true, cfg: f };
|
|
8311
|
+
});
|
|
8312
|
+
|
|
8313
|
+
ipcMain.handle('finance:trader:start', async () => {
|
|
8314
|
+
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8315
|
+
const f = finCfg();
|
|
8316
|
+
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
8317
|
+
const s = finTraderSession();
|
|
8318
|
+
finApplyTraderFields(s);
|
|
8319
|
+
try { engine.setSessionModel(s.id, f.traderSel || null); } catch {}
|
|
8320
|
+
financeState.traderOn = true;
|
|
8321
|
+
financeState.traderRounds = 0;
|
|
8322
|
+
try { engine.clearStop(); } catch {}
|
|
8323
|
+
const ok = engine.send(s.id, finTraderBrief(), { userAction: true });
|
|
8324
|
+
if (!ok) {
|
|
8325
|
+
financeState.traderOn = false;
|
|
8326
|
+
return { ok: false, error: 'trader oturumu meşgul — birkaç saniye sonra tekrar dene' };
|
|
8327
|
+
}
|
|
8328
|
+
financeState.lastRoundAt = Date.now();
|
|
8329
|
+
financeLog('[trader] başlatıldı (model: ' + (f.traderSel || 'genel aktif model') + ')');
|
|
8330
|
+
finPush('trader', { state: 'running', round: 0 });
|
|
8331
|
+
return { ok: true, sid: s.id };
|
|
8332
|
+
});
|
|
8333
|
+
|
|
8334
|
+
ipcMain.handle('finance:trader:stop', async () => {
|
|
8335
|
+
financeState.traderOn = false;
|
|
8336
|
+
clearTimeout(financeState.traderTimer);
|
|
8337
|
+
financeState.traderTimer = null;
|
|
8338
|
+
let interrupted = false;
|
|
8339
|
+
if (financeState.traderSid && engine && engine.isBusy(financeState.traderSid)) {
|
|
8340
|
+
try { interrupted = engine.interrupt(financeState.traderSid, 'kullanıcı Beast Finance trader ajanını durdurdu'); } catch {}
|
|
8341
|
+
}
|
|
8342
|
+
financeLog('[trader] durduruldu');
|
|
8343
|
+
finPush('trader', { state: 'stopped' });
|
|
8344
|
+
return { ok: true, interrupted };
|
|
8345
|
+
});
|
|
8346
|
+
|
|
8347
|
+
ipcMain.handle('finance:connect', async () => {
|
|
8348
|
+
mt5bridge.stop();
|
|
8349
|
+
const st = financeEnsureBridge();
|
|
8350
|
+
return { ok: true, bridge: st };
|
|
8351
|
+
});
|
|
8352
|
+
|
|
8353
|
+
ipcMain.handle('finance:close', async (_e, payload) => {
|
|
8354
|
+
const ticket = Number(payload && payload.ticket);
|
|
8355
|
+
if (!ticket) return { ok: false, error: 'ticket gerekli' };
|
|
8356
|
+
if (!mt5bridge.running) return { ok: false, error: 'MT5 köprüsü bağlı değil' };
|
|
8357
|
+
const r = await mt5bridge.call('close', { ticket, volume: Number(payload && payload.volume) || 0 }, 20000);
|
|
8358
|
+
if (r && r.ok) {
|
|
8359
|
+
financeLog('[panel] pozisyon kapatıldı: ticket ' + ticket);
|
|
8360
|
+
finPush('trade', { line: 'POZİSYON KAPANDI (panel): ticket ' + ticket });
|
|
8361
|
+
}
|
|
8362
|
+
return r;
|
|
8363
|
+
});
|
|
8364
|
+
|
|
8365
|
+
ipcMain.handle('finance:install', async () => {
|
|
8366
|
+
financeState.installTried = false;
|
|
8367
|
+
financeTryInstall();
|
|
8368
|
+
return { ok: true, installing: true };
|
|
8369
|
+
});
|
|
8370
|
+
|
|
8371
|
+
|
|
7990
8372
|
/* düşünme (reasoning) seviyesi */
|
|
7991
8373
|
ipcMain.handle('think:set', (_e, v) => {
|
|
7992
8374
|
setThinkLevel(v);
|