beast-agent 2.62.1 → 2.63.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/defaulttools/mt5_shot/run.js +31 -23
- package/src/agent/defaulttools/mt5_shot/tool.json +1 -1
- package/src/agent/engine.js +163 -64
- package/src/agent/financetools.js +103 -1
- package/src/agent/finwatch.js +19 -1
- package/src/agent/mt5/BeastFinance.mq5 +80 -23
- package/src/agent/seeds/price-action/SKILL.md +2 -1
- package/src/agent/seeds/risk-yonetimi/SKILL.md +2 -0
- package/src/main.js +378 -5
- package/src/renderer/index.html +3 -1
- package/src/renderer/renderer.js +48 -11
package/src/main.js
CHANGED
|
@@ -2355,6 +2355,7 @@ function botToolSet(cfg) {
|
|
|
2355
2355
|
'watcher_add', 'watcher_list', 'watcher_remove',
|
|
2356
2356
|
'tool_request', /* CEPHANE: eksik aracı TOOL botuna yazdırma hakkı HER botta */
|
|
2357
2357
|
'channel_send', /* İLK MESAJ: allow listteki kişilere kendiliğinden yazma hakkı HER botta */
|
|
2358
|
+
'agent_dm', /* BOTLAR ARASI DM: botlar birbirine iş atar/cevap verir (ör. Beast → Tool) */
|
|
2358
2359
|
]);
|
|
2359
2360
|
if (s.web_search) { set.add('web_search'); set.add('http_fetch'); set.add('webfetch'); set.add('deep_search'); }
|
|
2360
2361
|
if (s.browser) {
|
|
@@ -5577,6 +5578,10 @@ ipcMain.handle('sessions:create', () => {
|
|
|
5577
5578
|
try {
|
|
5578
5579
|
engine.markFinance(v.id, false);
|
|
5579
5580
|
v.finance = true;
|
|
5581
|
+
/* finance sohbeti TAM araç setiyle koşar (trader botu dahil):
|
|
5582
|
+
bot skill kısıtı finance araçlarını (mt5_*) düşürmesin */
|
|
5583
|
+
engine.setSessionTools(v.id, null);
|
|
5584
|
+
engine.setSessionPerm(v.id, 'all');
|
|
5580
5585
|
} catch {}
|
|
5581
5586
|
}
|
|
5582
5587
|
return v;
|
|
@@ -5638,7 +5643,17 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
5638
5643
|
const fsid = String(sessionId);
|
|
5639
5644
|
let fsess = engine.cache.get(fsid);
|
|
5640
5645
|
if (!fsess) { try { fsess = engine._load(fsid); } catch {} }
|
|
5641
|
-
|
|
5646
|
+
/* botIdsiz sohbetler + Trader botu (finance'ın kendi botu) finance'a girer;
|
|
5647
|
+
diğer müşteri botlarının kanalları finance izolasyonuna karışmaz */
|
|
5648
|
+
const finBot = !fsess || !fsess.botId || fsess.botId === 'trader';
|
|
5649
|
+
if (fsess && finBot && !fsess.bgJob && financeState.mode) {
|
|
5650
|
+
/* Trader botu finance sohbetinde tam araç setiyle koşar (mt5_* açık) */
|
|
5651
|
+
if (fsess.botId === 'trader') {
|
|
5652
|
+
try {
|
|
5653
|
+
engine.setSessionTools(fsid, null);
|
|
5654
|
+
engine.setSessionPerm(fsid, 'all');
|
|
5655
|
+
} catch {}
|
|
5656
|
+
}
|
|
5642
5657
|
if (!fsess.finance) {
|
|
5643
5658
|
engine.markFinance(fsid, false);
|
|
5644
5659
|
finApplyTraderFields(fsess);
|
|
@@ -9337,6 +9352,8 @@ const financeState = {
|
|
|
9337
9352
|
hoursTimer: null, /* trade saatleri otomatik duraklatma denetimi (10 sn) */
|
|
9338
9353
|
hoursPaused: null, /* pencere kapanınca durdurulan ajanların planı — açılınca geri gelir */
|
|
9339
9354
|
alerts: [], /* fiyat alarmları (kalıcı: finance/alerts.json) */
|
|
9355
|
+
learn: null, /* sembol bazlı öğrenme deposu (kalıcı: finance/ogrenme.json) */
|
|
9356
|
+
learnReady: false, /* depo diskten yüklendi mi (tembel yükleme) */
|
|
9340
9357
|
stats: null, /* son performans özeti (kalıcı: finance/stats.json) */
|
|
9341
9358
|
statsAt: 0,
|
|
9342
9359
|
lastStatsAt: 0,
|
|
@@ -9420,6 +9437,11 @@ function finCfg() {
|
|
|
9420
9437
|
if (!Number.isFinite(Number(f.beOffsetR))) f.beOffsetR = 0.05;
|
|
9421
9438
|
if (!Number.isFinite(Number(f.trailStartR))) f.trailStartR = 1.5;
|
|
9422
9439
|
if (!Number.isFinite(Number(f.trailR))) f.trailR = 0.5;
|
|
9440
|
+
/* KÂR KORUMA (erken trailing): kâr bu R'ye ulaşınca SL, görülen en iyi
|
|
9441
|
+
kârın protectDistR gerisine kilitlenir — 0.3R'de başlar, kâr eksiye
|
|
9442
|
+
dönmez (0 = kapalı) */
|
|
9443
|
+
if (!Number.isFinite(Number(f.protectStartR))) f.protectStartR = 0.3;
|
|
9444
|
+
if (!Number.isFinite(Number(f.protectDistR))) f.protectDistR = 0.15;
|
|
9423
9445
|
if (!Number.isFinite(Number(f.partialR))) f.partialR = 0;
|
|
9424
9446
|
if (!Number.isFinite(Number(f.partialPct))) f.partialPct = 50;
|
|
9425
9447
|
/* BİLDİRİM: işlem/koruma/alarm olayları bağlı kanallara (TG/WA/Discord)
|
|
@@ -10623,6 +10645,8 @@ async function finRecordClose(ticket, st) {
|
|
|
10623
10645
|
finExcFlush(true);
|
|
10624
10646
|
const kind = finwatch.closeKind(closeReason);
|
|
10625
10647
|
finJournal({ kind: 'close', ticket, symbol, net: rounded, side: (st && st.side) || '', mfe, mae, reason: kind || '' });
|
|
10648
|
+
/* SEMBOL BAZLI SÜREKLİ ÖĞRENME: her kapanış istatistiğe işlenir (mt5_ogrenme) */
|
|
10649
|
+
finLearnRecordClose({ symbol, side: (st && st.side) || '', net: rounded, mfe, mae, reason: kind || '' });
|
|
10626
10650
|
const pl = rounded == null ? '' : ` · K/Z ${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
|
|
10627
10651
|
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${pl}`;
|
|
10628
10652
|
financeLog('[watchdog] ' + line);
|
|
@@ -10837,6 +10861,11 @@ async function finWatchTick() {
|
|
|
10837
10861
|
}
|
|
10838
10862
|
finWatchSave();
|
|
10839
10863
|
}
|
|
10864
|
+
/* KÂR KORUMA: görülen EN İYİ kâr (fiyat farkı) tick aralarında da korunur */
|
|
10865
|
+
{
|
|
10866
|
+
const dist = st.side === 'buy' ? (Number(p.price_current) || 0) - st.entry : st.entry - (Number(p.price_current) || 0);
|
|
10867
|
+
if (isFinite(dist) && !(Number(st.bestProfit) >= dist)) st.bestProfit = dist;
|
|
10868
|
+
}
|
|
10840
10869
|
if (cfg.watchdog === false) continue;
|
|
10841
10870
|
let meta = null;
|
|
10842
10871
|
try { meta = await finSymbolMetaGet(p.symbol); } catch {}
|
|
@@ -11072,6 +11101,14 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11072
11101
|
finJournal({ kind: 'note', sid, agent: who, symbol: d.symbol, note: d.note, ticket: d.ticket || 0, side: d.side || '' });
|
|
11073
11102
|
return;
|
|
11074
11103
|
}
|
|
11104
|
+
/* SEMBOL BAZLI ÖĞRENME kaydı: günlüğe + AKIŞ paneline düşer */
|
|
11105
|
+
if (k === 'learn') {
|
|
11106
|
+
finJournal({ kind: 'learn', sid, agent: who, symbol: d.symbol, text: d.text, learnKind: d.kind || 'observation' });
|
|
11107
|
+
const line = `🧠 ÖĞRENME: ${d.symbol || '?'} — ${String(d.text || '').replace(/\s+/g, ' ').slice(0, 160)}`;
|
|
11108
|
+
financeLog('[ogrenme] ' + line);
|
|
11109
|
+
finPush('trade', { line });
|
|
11110
|
+
return;
|
|
11111
|
+
}
|
|
11075
11112
|
/* SHADOW: gerçek emir yok — karar teziyle günlüğe yazılır (test/ölçüm) */
|
|
11076
11113
|
if (k === 'shadow') {
|
|
11077
11114
|
finJournal({ kind: 'shadow', sid, agent: who, symbol: d.symbol, side: d.side || '', volume: d.volume, sl: d.sl || 0, tp: d.tp || 0, price: d.price || 0, type: d.type || '', reason: d.reason || '' });
|
|
@@ -11161,6 +11198,317 @@ try {
|
|
|
11161
11198
|
});
|
|
11162
11199
|
} catch {}
|
|
11163
11200
|
|
|
11201
|
+
/* ---------- LIMIT API (mt5_limits) ----------
|
|
11202
|
+
Ana trader / finance sohbet oturumu min lot-max lot-pozisyon tavanını
|
|
11203
|
+
güncelleyebilir; rol/işçi ajanları yalnız okur. Değişiklik ayarlara yazılır,
|
|
11204
|
+
koşan oturumlara ANINDA işlenir ve panele duyurulur. */
|
|
11205
|
+
function finLimitsSnapshot() {
|
|
11206
|
+
const f = finCfg();
|
|
11207
|
+
return {
|
|
11208
|
+
minLot: Number(f.minLot) || 0.01,
|
|
11209
|
+
maxLot: Number(f.maxLot) || 0.1,
|
|
11210
|
+
maxPositions: Number(f.maxPositions) || 3,
|
|
11211
|
+
riskPerTradePct: Number(f.riskPerTradePct) || 0,
|
|
11212
|
+
};
|
|
11213
|
+
}
|
|
11214
|
+
|
|
11215
|
+
function finLimitsSessionAllowed(sid) {
|
|
11216
|
+
const id = String(sid || '');
|
|
11217
|
+
if (!id || !engine) return false;
|
|
11218
|
+
let s = null;
|
|
11219
|
+
try { s = engine.cache.get(id) || engine._load(id); } catch {}
|
|
11220
|
+
if (!s || !s.finance) return false;
|
|
11221
|
+
if (s.financeRole) return false; /* analiz ekibi rol ajanı — limit değiştiremez */
|
|
11222
|
+
if (s.bgJob) return false; /* arka plan işçisi — limit değiştiremez */
|
|
11223
|
+
return true;
|
|
11224
|
+
}
|
|
11225
|
+
|
|
11226
|
+
function finSyncLimitsToSessions() {
|
|
11227
|
+
const lim = finLimitsSnapshot();
|
|
11228
|
+
if (engine) {
|
|
11229
|
+
try {
|
|
11230
|
+
for (const [sid] of financeState.agents) {
|
|
11231
|
+
const s = engine.cache.get(String(sid));
|
|
11232
|
+
if (s) s.financeLimits = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions };
|
|
11233
|
+
}
|
|
11234
|
+
for (const [, s] of engine.cache) {
|
|
11235
|
+
if (!s || !s.finance || s.bgJob || !s.financeLimits) continue;
|
|
11236
|
+
s.financeLimits = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions };
|
|
11237
|
+
}
|
|
11238
|
+
} catch {}
|
|
11239
|
+
}
|
|
11240
|
+
finPush('limits', { ...lim });
|
|
11241
|
+
}
|
|
11242
|
+
|
|
11243
|
+
function finLimitsSet(patch, ctx) {
|
|
11244
|
+
const sid = String((ctx && ctx.sessionId) || '');
|
|
11245
|
+
if (!finLimitsSessionAllowed(sid)) {
|
|
11246
|
+
return { ok: false, error: 'limit değiştirme yetkisi yalnız ana trader / finance sohbet oturumunda — rol ve işçi ajanları limit değiştiremez' };
|
|
11247
|
+
}
|
|
11248
|
+
const f = finCfg();
|
|
11249
|
+
const notes = [];
|
|
11250
|
+
if (patch.maxLot !== undefined) {
|
|
11251
|
+
const v = Number(patch.maxLot);
|
|
11252
|
+
if (!(v >= 0.01) || v > 100) return { ok: false, error: 'maxLot 0.01-100 arasında olmalı' };
|
|
11253
|
+
f.maxLot = Math.round(v * 100) / 100;
|
|
11254
|
+
}
|
|
11255
|
+
if (patch.minLot !== undefined) {
|
|
11256
|
+
const v = Number(patch.minLot);
|
|
11257
|
+
if (!(v >= 0.01) || v > 100) return { ok: false, error: 'minLot 0.01-100 arasında olmalı' };
|
|
11258
|
+
f.minLot = Math.round(v * 100) / 100;
|
|
11259
|
+
}
|
|
11260
|
+
if (patch.maxPositions !== undefined) {
|
|
11261
|
+
const v = Math.round(Number(patch.maxPositions));
|
|
11262
|
+
if (!(v >= 1) || v > 20) return { ok: false, error: 'maxPositions 1-20 arasında olmalı' };
|
|
11263
|
+
f.maxPositions = v;
|
|
11264
|
+
}
|
|
11265
|
+
if (f.minLot > f.maxLot) {
|
|
11266
|
+
notes.push(`minLot (${f.minLot}) maxLot'tan (${f.maxLot}) büyüktü → minLot ${f.maxLot} yapıldı`);
|
|
11267
|
+
f.minLot = f.maxLot;
|
|
11268
|
+
}
|
|
11269
|
+
try { saveSettings(); } catch {}
|
|
11270
|
+
finSyncLimitsToSessions();
|
|
11271
|
+
const line = `⚙️ LİMİTLER GÜNCELLENDİ: min lot ${f.minLot} · max lot ${f.maxLot} · max pozisyon ${f.maxPositions}${sid ? ' (ajan kararı)' : ''}`;
|
|
11272
|
+
financeLog('[limit] ' + line);
|
|
11273
|
+
financeNotify(line, 'limit', true);
|
|
11274
|
+
try { finJournal({ kind: 'limits', minLot: f.minLot, maxLot: f.maxLot, maxPositions: f.maxPositions, sid }); } catch {}
|
|
11275
|
+
return { ok: true, limits: finLimitsSnapshot(), note: notes.length ? notes.join('; ') : undefined };
|
|
11276
|
+
}
|
|
11277
|
+
|
|
11278
|
+
/* ---------- MT5 ÖĞRENME (mt5_ogrenme) — SEMBOL BAZLI SÜREKLİ ÖĞRENME ----------
|
|
11279
|
+
finance/ogrenme.json: her sembol için (1) OTOMATİK istatistik (işlem sayısı,
|
|
11280
|
+
kazanç/kayıp, net, MFE/MAE, kâr geri verme) (2) ajanın yazdığı dersler.
|
|
11281
|
+
Trader turunda bu özet sistem promptuna gömülür — sistem kendi geçmişinden
|
|
11282
|
+
öğrenir; her kapanış otomatik işlenir, dersleri ajan mt5_ogrenme ile yazar. */
|
|
11283
|
+
const FIN_LEARN_MAX_NOTES = 120;
|
|
11284
|
+
const FIN_LEARN_MAX_TRADES = 60;
|
|
11285
|
+
|
|
11286
|
+
function finLearnEmptyStats() {
|
|
11287
|
+
return {
|
|
11288
|
+
trades: 0, wins: 0, losses: 0, net: 0,
|
|
11289
|
+
mfe: 0, mae: 0, givebacks: 0, givebackAmount: 0,
|
|
11290
|
+
lastAt: 0, lastNet: 0, streak: 0, bestNet: 0, worstNet: 0,
|
|
11291
|
+
};
|
|
11292
|
+
}
|
|
11293
|
+
|
|
11294
|
+
function finLearnLoad() {
|
|
11295
|
+
if (financeState.learnReady) return financeState.learn;
|
|
11296
|
+
financeState.learnReady = true;
|
|
11297
|
+
const learn = { symbols: {}, updatedAt: 0 };
|
|
11298
|
+
try {
|
|
11299
|
+
const raw = finReadJson(finFile('ogrenme.json'), null);
|
|
11300
|
+
if (raw && typeof raw === 'object' && raw.symbols && typeof raw.symbols === 'object') {
|
|
11301
|
+
for (const [k, v] of Object.entries(raw.symbols)) {
|
|
11302
|
+
const sym = String(k || '').trim().toUpperCase();
|
|
11303
|
+
if (!sym || !v || typeof v !== 'object') continue;
|
|
11304
|
+
learn.symbols[sym] = {
|
|
11305
|
+
notes: Array.isArray(v.notes) ? v.notes.filter((n) => n && n.id && n.text).slice(-FIN_LEARN_MAX_NOTES) : [],
|
|
11306
|
+
trades: Array.isArray(v.trades) ? v.trades.filter(Boolean).slice(-FIN_LEARN_MAX_TRADES) : [],
|
|
11307
|
+
stats: v.stats && typeof v.stats === 'object' ? { ...finLearnEmptyStats(), ...v.stats } : finLearnEmptyStats(),
|
|
11308
|
+
};
|
|
11309
|
+
}
|
|
11310
|
+
if (Number(raw.updatedAt)) learn.updatedAt = Number(raw.updatedAt);
|
|
11311
|
+
}
|
|
11312
|
+
} catch {}
|
|
11313
|
+
financeState.learn = learn;
|
|
11314
|
+
return learn;
|
|
11315
|
+
}
|
|
11316
|
+
|
|
11317
|
+
function finLearnSave() {
|
|
11318
|
+
const learn = finLearnLoad();
|
|
11319
|
+
learn.updatedAt = Date.now();
|
|
11320
|
+
try { finWriteJson(finFile('ogrenme.json'), learn); } catch {}
|
|
11321
|
+
}
|
|
11322
|
+
|
|
11323
|
+
function finLearnSym(symbol) {
|
|
11324
|
+
const sym = String(symbol || '').trim().toUpperCase();
|
|
11325
|
+
if (!sym) return null;
|
|
11326
|
+
const learn = finLearnLoad();
|
|
11327
|
+
if (!learn.symbols[sym]) learn.symbols[sym] = { notes: [], trades: [], stats: finLearnEmptyStats() };
|
|
11328
|
+
const e = learn.symbols[sym];
|
|
11329
|
+
e.stats = { ...finLearnEmptyStats(), ...(e.stats || {}) };
|
|
11330
|
+
return e;
|
|
11331
|
+
}
|
|
11332
|
+
|
|
11333
|
+
/* Kapanan her işlem otomatik öğrenme girdisi olur (sembol bazlı) */
|
|
11334
|
+
function finLearnRecordClose({ symbol, side, net, mfe, mae, reason }) {
|
|
11335
|
+
try {
|
|
11336
|
+
if (net == null) return; /* K/Z okunamadı — sahte kayıt yazma */
|
|
11337
|
+
const n = Number(net);
|
|
11338
|
+
if (!isFinite(n) || !symbol) return;
|
|
11339
|
+
const e = finLearnSym(symbol);
|
|
11340
|
+
if (!e) return;
|
|
11341
|
+
const st = e.stats;
|
|
11342
|
+
const m = Number(mfe);
|
|
11343
|
+
const ma = Number(mae);
|
|
11344
|
+
st.trades += 1;
|
|
11345
|
+
if (n >= 0) st.wins += 1;
|
|
11346
|
+
else st.losses += 1;
|
|
11347
|
+
st.net = Math.round((Number(st.net) + n) * 100) / 100;
|
|
11348
|
+
st.lastAt = Date.now();
|
|
11349
|
+
st.lastNet = Math.round(n * 100) / 100;
|
|
11350
|
+
st.streak = n < 0 ? Number(st.streak || 0) + 1 : 0;
|
|
11351
|
+
st.bestNet = Math.max(Number(st.bestNet) || 0, Math.round(n * 100) / 100);
|
|
11352
|
+
st.worstNet = Math.min(Number(st.worstNet) || 0, Math.round(n * 100) / 100);
|
|
11353
|
+
if (isFinite(m)) {
|
|
11354
|
+
st.mfe = Math.round(((Number(st.mfe) || 0) + m) * 100) / 100;
|
|
11355
|
+
if (m > 0 && n < 0) {
|
|
11356
|
+
st.givebacks += 1;
|
|
11357
|
+
st.givebackAmount = Math.round(((Number(st.givebackAmount) || 0) + m) * 100) / 100;
|
|
11358
|
+
}
|
|
11359
|
+
}
|
|
11360
|
+
if (isFinite(ma)) st.mae = Math.round(((Number(st.mae) || 0) + ma) * 100) / 100;
|
|
11361
|
+
e.trades.push({
|
|
11362
|
+
at: Date.now(),
|
|
11363
|
+
side: String(side || ''),
|
|
11364
|
+
net: Math.round(n * 100) / 100,
|
|
11365
|
+
mfe: isFinite(m) ? Math.round(m * 100) / 100 : null,
|
|
11366
|
+
mae: isFinite(ma) ? Math.round(ma * 100) / 100 : null,
|
|
11367
|
+
reason: String(reason || '').slice(0, 40),
|
|
11368
|
+
});
|
|
11369
|
+
while (e.trades.length > FIN_LEARN_MAX_TRADES) e.trades.shift();
|
|
11370
|
+
finLearnSave();
|
|
11371
|
+
} catch {}
|
|
11372
|
+
}
|
|
11373
|
+
|
|
11374
|
+
/* Trader promptuna gömülen sembol bazlı öğrenme özeti */
|
|
11375
|
+
function finBuildLearnDigest(symbols) {
|
|
11376
|
+
const learn = finLearnLoad();
|
|
11377
|
+
const prefer = Array.isArray(symbols) ? symbols.map((s) => String(s || '').trim().toUpperCase()).filter(Boolean) : [];
|
|
11378
|
+
const keys = [...new Set([...prefer, ...Object.keys(learn.symbols)])].filter((k) => learn.symbols[k]);
|
|
11379
|
+
if (!keys.length) return '';
|
|
11380
|
+
const L = [];
|
|
11381
|
+
for (const sym of keys.slice(0, 10)) {
|
|
11382
|
+
const e = learn.symbols[sym];
|
|
11383
|
+
const st = { ...finLearnEmptyStats(), ...(e.stats || {}) };
|
|
11384
|
+
if (st.trades > 0) {
|
|
11385
|
+
const wr = Math.round((st.wins / st.trades) * 100);
|
|
11386
|
+
const parts = [`${st.trades} işlem`, `%${wr} kazanç`, `net ${st.net >= 0 ? '+' : ''}${st.net}`];
|
|
11387
|
+
if (st.streak >= 2) parts.push(`${st.streak} ardışık kayıp`);
|
|
11388
|
+
if (st.givebacks > 0) parts.push(`${st.givebacks} kez kâr geri verildi (tepe kâr ${Math.round(st.givebackAmount * 100) / 100} eksiye döndü)`);
|
|
11389
|
+
if (st.lastAt) parts.push(`son: ${st.lastNet >= 0 ? '+' : ''}${st.lastNet}`);
|
|
11390
|
+
L.push(`- ${sym}: ${parts.join(' · ')}`);
|
|
11391
|
+
}
|
|
11392
|
+
if (e.notes.length) {
|
|
11393
|
+
const last = e.notes[e.notes.length - 1];
|
|
11394
|
+
const extra = e.notes.length > 1 ? ` (+${e.notes.length - 1} ders)` : '';
|
|
11395
|
+
L.push(` ders${extra}: "${String(last.text || '').slice(0, 160)}"`);
|
|
11396
|
+
}
|
|
11397
|
+
}
|
|
11398
|
+
return L.join('\n');
|
|
11399
|
+
}
|
|
11400
|
+
|
|
11401
|
+
function finLearnApi() {
|
|
11402
|
+
const norm = (s) => String(s || '').trim().toUpperCase();
|
|
11403
|
+
return {
|
|
11404
|
+
list: ({ symbol } = {}) => {
|
|
11405
|
+
const learn = finLearnLoad();
|
|
11406
|
+
const sym = norm(symbol);
|
|
11407
|
+
if (sym) {
|
|
11408
|
+
const e = learn.symbols[sym];
|
|
11409
|
+
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats() };
|
|
11410
|
+
return {
|
|
11411
|
+
ok: true,
|
|
11412
|
+
symbol: sym,
|
|
11413
|
+
count: e.notes.length,
|
|
11414
|
+
notes: e.notes.slice(-25),
|
|
11415
|
+
trades: e.trades.slice(-12),
|
|
11416
|
+
stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
|
|
11417
|
+
};
|
|
11418
|
+
}
|
|
11419
|
+
const rows = Object.entries(learn.symbols)
|
|
11420
|
+
.map(([s, e]) => ({
|
|
11421
|
+
symbol: s,
|
|
11422
|
+
notes: e.notes.length,
|
|
11423
|
+
stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
|
|
11424
|
+
lastNote: String(((e.notes[e.notes.length - 1] || {}).text) || ''),
|
|
11425
|
+
}))
|
|
11426
|
+
.sort((a, b) => (b.stats.lastAt || 0) - (a.stats.lastAt || 0));
|
|
11427
|
+
return { ok: true, count: rows.length, symbols: rows.slice(0, 40) };
|
|
11428
|
+
},
|
|
11429
|
+
stats: ({ symbol } = {}) => {
|
|
11430
|
+
const learn = finLearnLoad();
|
|
11431
|
+
const sym = norm(symbol);
|
|
11432
|
+
if (sym) return { ok: true, symbol: sym, stats: { ...finLearnEmptyStats(), ...((learn.symbols[sym] || {}).stats || {}) } };
|
|
11433
|
+
const stats = {};
|
|
11434
|
+
for (const [s, e] of Object.entries(learn.symbols)) stats[s] = { ...finLearnEmptyStats(), ...(e.stats || {}) };
|
|
11435
|
+
return { ok: true, count: Object.keys(stats).length, stats };
|
|
11436
|
+
},
|
|
11437
|
+
add: ({ symbol, text, kind, tags, sid } = {}) => {
|
|
11438
|
+
const sym = norm(symbol);
|
|
11439
|
+
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
11440
|
+
if (!sym || !t) return { ok: false, error: 'symbol ve text gerekli' };
|
|
11441
|
+
const e = finLearnSym(sym);
|
|
11442
|
+
if (!e) return { ok: false, error: 'sembol yok' };
|
|
11443
|
+
/* aynı ders tekrar yazılmasın (son 20 kayıtta aynı metin) */
|
|
11444
|
+
const key = t.toLowerCase().slice(0, 120);
|
|
11445
|
+
if (e.notes.slice(-20).some((n) => String(n.text || '').toLowerCase().slice(0, 120) === key)) {
|
|
11446
|
+
return { ok: true, duplicate: true, symbol: sym, count: e.notes.length };
|
|
11447
|
+
}
|
|
11448
|
+
const note = {
|
|
11449
|
+
id: 'l' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
11450
|
+
at: Date.now(),
|
|
11451
|
+
text: t.slice(0, 600),
|
|
11452
|
+
kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
|
|
11453
|
+
tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
|
|
11454
|
+
sid: String(sid || ''),
|
|
11455
|
+
};
|
|
11456
|
+
e.notes.push(note);
|
|
11457
|
+
while (e.notes.length > FIN_LEARN_MAX_NOTES) e.notes.shift();
|
|
11458
|
+
finLearnSave();
|
|
11459
|
+
return { ok: true, id: note.id, symbol: sym, count: e.notes.length };
|
|
11460
|
+
},
|
|
11461
|
+
remove: ({ id, symbol } = {}) => {
|
|
11462
|
+
const learn = finLearnLoad();
|
|
11463
|
+
const sym = norm(symbol);
|
|
11464
|
+
const key = String(id || '');
|
|
11465
|
+
if (!key) return { ok: false, error: 'id gerekli' };
|
|
11466
|
+
let removed = 0;
|
|
11467
|
+
const targets = sym ? [sym] : Object.keys(learn.symbols);
|
|
11468
|
+
for (const s of targets) {
|
|
11469
|
+
const e = learn.symbols[s];
|
|
11470
|
+
if (!e) continue;
|
|
11471
|
+
const before = e.notes.length;
|
|
11472
|
+
e.notes = e.notes.filter((n) => String(n.id) !== key);
|
|
11473
|
+
removed += before - e.notes.length;
|
|
11474
|
+
}
|
|
11475
|
+
if (removed) finLearnSave();
|
|
11476
|
+
return { ok: true, removed, id: key };
|
|
11477
|
+
},
|
|
11478
|
+
clear: ({ ids = [], symbol, all } = {}) => {
|
|
11479
|
+
const learn = finLearnLoad();
|
|
11480
|
+
const sym = norm(symbol);
|
|
11481
|
+
const list = Array.isArray(ids) ? ids.map((x) => String(x || '').trim()).filter(Boolean) : [];
|
|
11482
|
+
let removed = 0;
|
|
11483
|
+
const wipe = (e) => {
|
|
11484
|
+
if (!e) return;
|
|
11485
|
+
if (list.length) {
|
|
11486
|
+
const before = e.notes.length;
|
|
11487
|
+
e.notes = e.notes.filter((n) => !list.includes(String(n.id)));
|
|
11488
|
+
removed += before - e.notes.length;
|
|
11489
|
+
} else {
|
|
11490
|
+
removed += e.notes.length;
|
|
11491
|
+
e.notes = [];
|
|
11492
|
+
}
|
|
11493
|
+
};
|
|
11494
|
+
if (all) for (const e of Object.values(learn.symbols)) wipe(e);
|
|
11495
|
+
else if (sym) wipe(learn.symbols[sym]);
|
|
11496
|
+
else return { ok: false, error: 'clear için symbol ya da all:true ver (ids de verilebilir)' };
|
|
11497
|
+
if (removed) finLearnSave();
|
|
11498
|
+
return { ok: true, removed };
|
|
11499
|
+
},
|
|
11500
|
+
};
|
|
11501
|
+
}
|
|
11502
|
+
|
|
11503
|
+
/* LIMIT + ÖĞRENME kancalarını financetools'a bağla */
|
|
11504
|
+
try {
|
|
11505
|
+
financetools.setLimits({
|
|
11506
|
+
get: () => finLimitsSnapshot(),
|
|
11507
|
+
set: (patch, ctx) => finLimitsSet(patch, ctx),
|
|
11508
|
+
});
|
|
11509
|
+
financetools.setLearning(finLearnApi());
|
|
11510
|
+
} catch {}
|
|
11511
|
+
|
|
11164
11512
|
/* KİŞİSEL TOOLLAR: çalışma kaydı renderer'a (TOOLS konsolu buradan beslenir) */
|
|
11165
11513
|
try {
|
|
11166
11514
|
customtools.setNotify((entry) => {
|
|
@@ -11539,6 +11887,8 @@ function finApplyTraderFields(s, symbolsOverride, role, isMain) {
|
|
|
11539
11887
|
/* ZORUNLU TEK skill: rol ajanı → roleSkills[rol], ana trader → roleSkills.trader */
|
|
11540
11888
|
const skillKey = r || (s.financePlaybook ? 'trader' : '');
|
|
11541
11889
|
s.financeRoleSkills = (skillKey && f.roleSkills && f.roleSkills[skillKey]) || [];
|
|
11890
|
+
/* SEMBOL BAZLI ÖĞRENME ÖZETİ: tur promptuna gömülür (kendi geçmişinden öğren) */
|
|
11891
|
+
try { s.financeLearn = finBuildLearnDigest(s.financeSymbols); } catch { s.financeLearn = ''; }
|
|
11542
11892
|
engine.cache.set(String(s.id), s);
|
|
11543
11893
|
}
|
|
11544
11894
|
|
|
@@ -11635,6 +11985,9 @@ function finTraderBrief(agent) {
|
|
|
11635
11985
|
`Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
|
|
11636
11986
|
`Odak semboller: ${syms || '(boş — mt5_status ile terminale bak, mantıklı semboller seç)'}`,
|
|
11637
11987
|
`Tur aralığı: ${f.intervalSec} sn · Lot aralığı: ${f.minLot}–${f.maxLot} (min lot tabanı zorlanır, max lot tavanı aşılamaz) · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
11988
|
+
agent && agent.main
|
|
11989
|
+
? `LİMİT YETKİN: volatilite/performansa göre mt5_limits {action:"set", minLot?, maxLot?, maxPositions?} ile limitleri SEN güncelleyebilirsin (değişiklik anında geçerli).`
|
|
11990
|
+
: '',
|
|
11638
11991
|
roleDef
|
|
11639
11992
|
? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
|
|
11640
11993
|
: 'Otomatik işlem AÇIK (daima): 6 emir tipinin HEPSİ açık — buy_market/sell_market (anlık piyasa), buy_limit/sell_limit ve buy_stop/sell_stop (bekleyen; price zorunlu). mt5_trade ya da mt5_pending İKİSİ de tüm tipleri kabul eder; ayrıca mt5_close/mt5_modify/mt5_cancel açık (limitler sistemce zorlanır). KISMİ KAPATMA yetkisi: kısmi TP (kâr al) ve kısmi stop (zarar kes) — mt5_close {percent, kind:"partial_tp"|"partial_sl"}; otomatik DEĞİL, gerek görürsen sen kullan. ALARM: mt5_alerts ile kurarken modu SEN seç — mode:"once" tek seferlik, mode:"repeat" + cooldownMin (dk) tekrarlı. Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı.',
|
|
@@ -11646,7 +11999,8 @@ function finTraderBrief(agent) {
|
|
|
11646
11999
|
f.shadowMode ? 'SHADOW MOD AÇIK: emir gönderilmez — kararlarını gerekçesiyle raporla, gerçek işlem açılmaz.' : '',
|
|
11647
12000
|
'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → mt5_rates/mt5_indicators ile teknik okuma → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
|
|
11648
12001
|
'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
|
|
11649
|
-
'
|
|
12002
|
+
'ÖĞRENME (ZORUNLU DÖNGÜ): her kapanıştan sonra aynı turda mt5_ogrenme {action:"add", symbol, text, kind} ile dersi kaydet; yeni karar öncesi mt5_ogrenme {action:"stats"/"list", symbol} ile o sembolün geçmişini oku.',
|
|
12003
|
+
'Risk otomasyonu main süreçte 5 sn döngüyle çalışır (+R BE, trailing, KÂR KORUMA: kâr 0.3R\'de kilitlenir, kısmi TP) — TP için RR ≥ 1.5 şartı YOK; 0.3-0.5R\'de kısmi kâr alıp kalanı korumalı trailing ile taşı (hızlı kâr toplama), SL/TP seviyelerini yine sen aktif yönet.',
|
|
11650
12004
|
'⚡HIZLI AKSİYON: onaylı fırsatta market buy/sell ile ANINDA gir (mt5_trade {symbol, side, sl, tp, riskPct}); bekleyen emir vermek ZORUNLU DEĞİL — limit/stop yalnız seviye beklemede kurulur.',
|
|
11651
12005
|
'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
|
|
11652
12006
|
agent && agent.main && !roleDef ? `İŞLEM GEÇMİŞİN:\n${finBuildDigest()}` : '',
|
|
@@ -11870,8 +12224,11 @@ function finAgentRound(sid, opts) {
|
|
|
11870
12224
|
: shadow
|
|
11871
12225
|
? 'SHADOW MOD AÇIK: emir GÖNDERİLMEZ — kararını teziyle raporla (günlüğe yazılır).'
|
|
11872
12226
|
: 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
|
|
12227
|
+
const learnTip = roleDef
|
|
12228
|
+
? ' İlgili sembolün geçmiş derslerini (mt5_ogrenme) oku ve önerine yansıt.'
|
|
12229
|
+
: ' Kapanan her işlemden sonra mt5_ogrenme add ile sembol dersini yaz; karar öncesi mt5_ogrenme stats/list ile o sembolün geçmişini oku' + (agent.main ? '; volatiliteye göre mt5_limits ile lot/pozisyon limitlerini güncelleyebilirsin.' : '.');
|
|
11873
12230
|
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
11874
|
-
const round = `FINANCE TUR #${agent.round}: ${focus}hesap + pozisyonlar + fiyatları çek; ${roleDef ? 'rolüne uygun analiz yap (mt5_rates/mt5_indicators ile) ve öneri ver.' : 'açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); mt5_rates/mt5_indicators ile yeni fırsatları değerlendir. ⚡Onaylı fırsatta market buy/sell ile ANINDA gir (bekleyen emir ZORUNLU DEĞİL); lot için volume yerine riskPct+sl yeter.'} ${auto} Önemli kararların gerekçesini mt5_note ile günlüğe yaz. Kısa rapor ver.`;
|
|
12231
|
+
const round = `FINANCE TUR #${agent.round}: ${focus}hesap + pozisyonlar + fiyatları çek; ${roleDef ? 'rolüne uygun analiz yap (mt5_rates/mt5_indicators ile) ve öneri ver.' : 'açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); mt5_rates/mt5_indicators ile yeni fırsatları değerlendir. ⚡Onaylı fırsatta market buy/sell ile ANINDA gir (bekleyen emir ZORUNLU DEĞİL); lot için volume yerine riskPct+sl yeter.'} ${auto}${learnTip} Önemli kararların gerekçesini mt5_note ile günlüğe yaz. Kısa rapor ver.`;
|
|
11875
12232
|
const launch = (planBlock) => {
|
|
11876
12233
|
if (!financeState.agents.has(String(sid))) return;
|
|
11877
12234
|
const ok = engine.send(sid, planBlock + round, { userAction: false });
|
|
@@ -12065,7 +12422,9 @@ ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
|
12065
12422
|
try {
|
|
12066
12423
|
let s = engine.cache.get(sid);
|
|
12067
12424
|
if (!s) { try { s = engine._load(sid); } catch {} }
|
|
12068
|
-
|
|
12425
|
+
/* 'trader' botu finance'ın kendi botudur (mod açılınca UI o bota döner) —
|
|
12426
|
+
onun sohbeti de finance olarak işlenir; diğer müşteri botları karışmaz */
|
|
12427
|
+
if (s && !s.bgJob && (!s.botId || s.botId === 'beast' || s.botId === 'trader')) {
|
|
12069
12428
|
if (financeState.mode) {
|
|
12070
12429
|
if (s.finance) {
|
|
12071
12430
|
/* zaten finance oturumu — aynen sürdür */
|
|
@@ -12089,7 +12448,13 @@ ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
|
12089
12448
|
sohbet oturumu önerilir; hiç yoksa yeni oturum açılır. */
|
|
12090
12449
|
if (financeState.mode && needNew && engine) {
|
|
12091
12450
|
try {
|
|
12092
|
-
const
|
|
12451
|
+
const list = engine.listSessions() || [];
|
|
12452
|
+
/* önce AKTİF BOTUN finance sohbeti (mod açılınca bot Trader'a döner),
|
|
12453
|
+
yoksa en son finance sohbeti */
|
|
12454
|
+
const actBot = String(settings.activeBotId || '');
|
|
12455
|
+
const prev =
|
|
12456
|
+
(actBot && list.find((v) => v && v.finance && !v.isBg && v.id && String(v.botId || '') === actBot)) ||
|
|
12457
|
+
list.find((v) => v && v.finance && !v.isBg && v.id);
|
|
12093
12458
|
if (prev) {
|
|
12094
12459
|
resumeSid = String(prev.id);
|
|
12095
12460
|
needNew = false;
|
|
@@ -12148,12 +12513,20 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
12148
12513
|
/* min lot max lotu aşamaz — hangisi sonra yazıldıysa diğerine kelepçelenir */
|
|
12149
12514
|
if (f.minLot > f.maxLot) f.minLot = f.maxLot;
|
|
12150
12515
|
if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
|
|
12516
|
+
/* limit değişikliği koşan oturumlara ANINDA işlenir (sonraki turu beklemez) */
|
|
12517
|
+
if (p.minLot !== undefined || p.maxLot !== undefined || p.maxPositions !== undefined) {
|
|
12518
|
+
try { finSyncLimitsToSessions(); } catch {}
|
|
12519
|
+
}
|
|
12151
12520
|
/* RİSK OTOMASYONU + BİLDİRİM ayarları */
|
|
12152
12521
|
if (p.watchdog !== undefined) f.watchdog = !!p.watchdog;
|
|
12153
12522
|
if (p.beOnR !== undefined) f.beOnR = Math.max(0, Math.min(10, Number(p.beOnR) || 0));
|
|
12154
12523
|
if (p.beOffsetR !== undefined) f.beOffsetR = Math.max(0, Math.min(1, Number(p.beOffsetR) || 0));
|
|
12155
12524
|
if (p.trailStartR !== undefined) f.trailStartR = Math.max(0, Math.min(10, Number(p.trailStartR) || 0));
|
|
12156
12525
|
if (p.trailR !== undefined) f.trailR = Math.max(0, Math.min(5, Number(p.trailR) || 0));
|
|
12526
|
+
/* KÂR KORUMA: kâr protectStartR'ye ulaşınca SL, en iyi kârın protectDistR
|
|
12527
|
+
gerisine kilitlenir (0 = kapalı) */
|
|
12528
|
+
if (p.protectStartR !== undefined) f.protectStartR = Math.max(0, Math.min(10, Number(p.protectStartR) || 0));
|
|
12529
|
+
if (p.protectDistR !== undefined) f.protectDistR = Math.max(0, Math.min(5, Number(p.protectDistR) || 0));
|
|
12157
12530
|
if (p.partialR !== undefined) f.partialR = Math.max(0, Math.min(10, Number(p.partialR) || 0));
|
|
12158
12531
|
if (p.partialPct !== undefined) f.partialPct = Math.max(0, Math.min(90, Number(p.partialPct) || 0));
|
|
12159
12532
|
if (p.notifyTarget !== undefined) {
|
package/src/renderer/index.html
CHANGED
|
@@ -222,13 +222,15 @@
|
|
|
222
222
|
<div class="fin-card-title">RİSK OTOMASYONU <span id="finWatchDot" class="fin-dot off" title="Koruma döngüsü"></span><span id="finWatchInfo" class="fin-hint"></span></div>
|
|
223
223
|
<div class="fin-field fin-check">
|
|
224
224
|
<label title="5 saniyelik döngü: +R'da SL'yi başabaşa çeker, trailing uygular, kısmi TP alır — ajan turunu beklemez">
|
|
225
|
-
<input id="finWatchdog" type="checkbox" /> Aktif — BE + trailing + kısmi TP
|
|
225
|
+
<input id="finWatchdog" type="checkbox" /> Aktif — BE + trailing + kâr koruma + kısmi TP
|
|
226
226
|
</label>
|
|
227
227
|
</div>
|
|
228
228
|
<div class="fin-grid2">
|
|
229
229
|
<div class="fin-field"><span>BE (R)</span><input id="finBeOnR" type="number" min="0" max="10" step="0.1" title="Kâr kaç R'ye ulaşınca SL başabaşa çekilsin (0 = kapalı)" /></div>
|
|
230
230
|
<div class="fin-field"><span>Trailing başlangıç (R)</span><input id="finTrailStartR" type="number" min="0" max="10" step="0.1" title="Kaç R kârdan sonra trailing başlasın (0 = kapalı)" /></div>
|
|
231
231
|
<div class="fin-field"><span>Trailing mesafe (R)</span><input id="finTrailR" type="number" min="0" max="5" step="0.1" title="SL fiyattan kaç R geriden gelsin" /></div>
|
|
232
|
+
<div class="fin-field"><span>Kâr koruma başlangıç (R)</span><input id="finProtectStartR" type="number" min="0" max="10" step="0.1" title="Kâr bu R'ye ulaşınca SL kilitlenir: görülen EN İYİ kârın bir kısmı garantiye alınır — kâr eksiye dönmez (0 = kapalı, önerilen 0.3)" /></div>
|
|
233
|
+
<div class="fin-field"><span>Kâr koruma payı (R)</span><input id="finProtectDistR" type="number" min="0" max="5" step="0.05" title="Kilitlenen SL, görülen en iyi kârın kaç R gerisinde kalsın (ör. 0.15: +0.5R görüldüyse SL +0.35R'ye kilitlenir)" /></div>
|
|
232
234
|
<div class="fin-field"><span>Kısmi TP (R)</span><input id="finPartialR" type="number" min="0" max="10" step="0.1" title="Kaç R kârda ajana kısmi TP fırsatı bildirilsin (0 = kapalı) — kapatma kararını AJAN verir, otomatik kapatılmaz" /></div>
|
|
233
235
|
<div class="fin-field"><span>Kısmi TP (%)</span><input id="finPartialPct" type="number" min="0" max="90" step="5" title="Fırsat bildiriminde ajana önerilecek kısmi kapatma yüzdesi (kapatma kararı ajanın)" /></div>
|
|
234
236
|
<div class="fin-field"><span>Günlük kayıp uyarı (%)</span><input id="finMaxDailyLoss" type="number" min="0" max="50" step="0.5" title="Gün içi düşüş bu yüzdeyi geçince aksiyon uygulanır (0 = kapalı)" /></div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -267,6 +267,8 @@ const els = {
|
|
|
267
267
|
finBeOnR: $('#finBeOnR'),
|
|
268
268
|
finTrailStartR: $('#finTrailStartR'),
|
|
269
269
|
finTrailR: $('#finTrailR'),
|
|
270
|
+
finProtectStartR: $('#finProtectStartR'),
|
|
271
|
+
finProtectDistR: $('#finProtectDistR'),
|
|
270
272
|
finPartialR: $('#finPartialR'),
|
|
271
273
|
finPartialPct: $('#finPartialPct'),
|
|
272
274
|
finMaxDailyLoss: $('#finMaxDailyLoss'),
|
|
@@ -1061,16 +1063,19 @@ async function renderSessions(list) {
|
|
|
1061
1063
|
els.sessList.innerHTML = '';
|
|
1062
1064
|
/* BEAST FINANCE izolasyonu: finance modunda YALNIZ finance sohbetleri,
|
|
1063
1065
|
normal modda finance olmayanlar listelenir */
|
|
1064
|
-
|
|
1066
|
+
const finMode = financeModeOn();
|
|
1067
|
+
list = (list || []).filter((s) => (finMode ? !!s.finance : !s.finance));
|
|
1065
1068
|
/* elle sıra: sessionOrder'daki id'ler önde (o sırayla), diğerleri engine sırasıyla arkada */
|
|
1066
1069
|
const rank = new Map(sessionOrder.map((id, i) => [id, i]));
|
|
1067
1070
|
const withRank = list.map((s, i) => ({ s, r: rank.has(s.id) ? rank.get(s.id) : 1e6 + i }));
|
|
1068
1071
|
withRank.sort((a, b) => a.r - b.r);
|
|
1069
1072
|
/* aktif botun oturumları — botlar arası geçişte liste de o bota göre değişir.
|
|
1070
1073
|
KATI KURAL: her oturum yalnız BAĞLI OLDUĞU botun listesinde görünür;
|
|
1071
|
-
bot kaydı olmayan (eski) oturumlar Beast (varsayılan sahip) altında görünür.
|
|
1074
|
+
bot kaydı olmayan (eski) oturumlar Beast (varsayılan sahip) altında görünür.
|
|
1075
|
+
İSTİSNA — finance modu: mod Trader botuna döner; TÜM finance sohbetleri
|
|
1076
|
+
(eski Beast finance'ları dahil) listede kalır, kaybolmaz. */
|
|
1072
1077
|
for (const { s } of withRank) {
|
|
1073
|
-
if ((s.botId || 'beast') !== activeBotId) continue;
|
|
1078
|
+
if (!finMode && (s.botId || 'beast') !== activeBotId) continue;
|
|
1074
1079
|
const row = document.createElement('div');
|
|
1075
1080
|
row.className = 'sess' + (s.id === activeId ? ' active' : '');
|
|
1076
1081
|
row.dataset.sid = s.id; /* en üstteki sohbeti otomatik aktif etmek için */
|
|
@@ -4607,7 +4612,7 @@ function updateBotChip() {
|
|
|
4607
4612
|
/* BOTLAR ARASI GEÇİŞ: kart tıklaması SADECE aktif botu değiştirir —
|
|
4608
4613
|
yönetim sayfası (Ayarlar/Memory/Log/Watcher/İstatistik) bot satırındaki ⚙
|
|
4609
4614
|
butonundan veya üstteki bot rozetinden (botChip) açılır. */
|
|
4610
|
-
async function switchBot(id) {
|
|
4615
|
+
async function switchBot(id, opts) {
|
|
4611
4616
|
const b = botsCache.find((x) => x.id === id);
|
|
4612
4617
|
if (!b) return;
|
|
4613
4618
|
if (id === activeBotId) return;
|
|
@@ -4625,15 +4630,19 @@ async function switchBot(id) {
|
|
|
4625
4630
|
}
|
|
4626
4631
|
} catch {}
|
|
4627
4632
|
try { localStorage.setItem('beast.activeBot', id); } catch {}
|
|
4628
|
-
/*
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4633
|
+
/* noOpen: oturum açma çağırana bırakılır (finance modu kendi finance
|
|
4634
|
+
sohbetini açar/sürdürür — bot geçişi sohbeti değiştirmez) */
|
|
4635
|
+
if (!(opts && opts.noOpen)) {
|
|
4636
|
+
/* o botun en son oturumuna geç; hiç yoksa o bot için yeni sohbet aç */
|
|
4637
|
+
try {
|
|
4638
|
+
const list = (await beast.listSessions()).filter((s) => (s.botId || 'beast') === id);
|
|
4639
|
+
if (list.length) await openSession(list[0].id);
|
|
4640
|
+
else { const v = await beast.createSession(); await openSession(v.id); }
|
|
4641
|
+
} catch {}
|
|
4642
|
+
}
|
|
4634
4643
|
/* picker, aktif botun kendi modelini göstersin */
|
|
4635
4644
|
applyState();
|
|
4636
|
-
toast(b.name + (b.admin ? '' : ' — ' + _t('bot_switched')));
|
|
4645
|
+
if (!(opts && opts.silent)) toast(b.name + (b.admin ? '' : ' — ' + _t('bot_switched')));
|
|
4637
4646
|
}
|
|
4638
4647
|
|
|
4639
4648
|
function botOverlaySetOpen(open) {
|
|
@@ -9985,6 +9994,8 @@ function finRenderAutomation(cfg, watch) {
|
|
|
9985
9994
|
setNum(els.finBeOnR, cfg.beOnR != null ? cfg.beOnR : 1);
|
|
9986
9995
|
setNum(els.finTrailStartR, cfg.trailStartR != null ? cfg.trailStartR : 1.5);
|
|
9987
9996
|
setNum(els.finTrailR, cfg.trailR != null ? cfg.trailR : 0.5);
|
|
9997
|
+
setNum(els.finProtectStartR, cfg.protectStartR != null ? cfg.protectStartR : 0.3);
|
|
9998
|
+
setNum(els.finProtectDistR, cfg.protectDistR != null ? cfg.protectDistR : 0.15);
|
|
9988
9999
|
setNum(els.finPartialR, cfg.partialR != null ? cfg.partialR : 0);
|
|
9989
10000
|
setNum(els.finPartialPct, cfg.partialPct != null ? cfg.partialPct : 50);
|
|
9990
10001
|
setNum(els.finMaxDailyLoss, cfg.maxDailyLossPct != null ? cfg.maxDailyLossPct : 3);
|
|
@@ -10170,6 +10181,20 @@ function finOnEvent(ev) {
|
|
|
10170
10181
|
else if (ev.state === 'hours-stop') finLogLine('[trader] trade saatleri kapandı — ' + (ev.count || 0) + ' finance ajanı otomatik durduruldu; pencere açılınca geri başlar');
|
|
10171
10182
|
else if (ev.state === 'hours-resume') finLogLine('[trader] trade saatleri açıldı — duraklatılan ajanlar geri başlatıldı');
|
|
10172
10183
|
else if (ev.state === 'stopped') finLogLine('[trader] durduruldu');
|
|
10184
|
+
} else if (ev.fn === 'limits') {
|
|
10185
|
+
/* trader mt5_limits ile limitleri değiştirdi — panel alanları ANINDA tazelenir */
|
|
10186
|
+
if (finCfgCache) {
|
|
10187
|
+
if (ev.minLot !== undefined) finCfgCache.minLot = ev.minLot;
|
|
10188
|
+
if (ev.maxLot !== undefined) finCfgCache.maxLot = ev.maxLot;
|
|
10189
|
+
if (ev.maxPositions !== undefined) finCfgCache.maxPositions = ev.maxPositions;
|
|
10190
|
+
finTraderInputsSet(finCfgCache);
|
|
10191
|
+
} else {
|
|
10192
|
+
const ae2 = document.activeElement;
|
|
10193
|
+
if (els.finMinLot && ae2 !== els.finMinLot && ev.minLot !== undefined) els.finMinLot.value = ev.minLot;
|
|
10194
|
+
if (els.finMaxLot && ae2 !== els.finMaxLot && ev.maxLot !== undefined) els.finMaxLot.value = ev.maxLot;
|
|
10195
|
+
}
|
|
10196
|
+
finLogLine('[limit] ' + (ev.note || ('min lot ' + ev.minLot + ' · max lot ' + ev.maxLot + ' · max pozisyon ' + ev.maxPositions)));
|
|
10197
|
+
toast('Trader limitleri güncelledi: min ' + ev.minLot + ' / max ' + ev.maxLot);
|
|
10173
10198
|
} else if (ev.fn === 'install') {
|
|
10174
10199
|
finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
|
|
10175
10200
|
}
|
|
@@ -10186,6 +10211,16 @@ async function setFinanceMode(on) {
|
|
|
10186
10211
|
const brandSub = document.querySelector('#brand .brand-sub');
|
|
10187
10212
|
if (brandSub) brandSub.textContent = on ? 'Finance' : 'Agent';
|
|
10188
10213
|
try { localStorage.setItem('beast.financeMode', on ? '1' : '0'); } catch {}
|
|
10214
|
+
/* BEAST TRADER MODU: finance moduna geçilince aktif bot OTOMATİK Trader
|
|
10215
|
+
botuna döner — oturum açma finance akışına bırakılır (noOpen) */
|
|
10216
|
+
if (on) {
|
|
10217
|
+
try {
|
|
10218
|
+
if (!botsCache.length) await refreshBots();
|
|
10219
|
+
if (activeBotId !== 'trader' && botsCache.some((b) => b.id === 'trader')) {
|
|
10220
|
+
await switchBot('trader', { noOpen: true });
|
|
10221
|
+
}
|
|
10222
|
+
} catch {}
|
|
10223
|
+
}
|
|
10189
10224
|
const fm = await beast.financeMode(!!on, activeId).catch(() => ({}));
|
|
10190
10225
|
/* finance modu açıldı ama aktif oturum NORMAL (örn. WhatsApp sohbeti) ise
|
|
10191
10226
|
ASLA o oturumu finance'e çevirme — önce EN SON finance sohbetinden devam
|
|
@@ -10571,6 +10606,8 @@ const finAutoNum = (el, key, min, max, step) => {
|
|
|
10571
10606
|
finAutoNum(els.finBeOnR, 'beOnR', 0, 10);
|
|
10572
10607
|
finAutoNum(els.finTrailStartR, 'trailStartR', 0, 10);
|
|
10573
10608
|
finAutoNum(els.finTrailR, 'trailR', 0, 5);
|
|
10609
|
+
finAutoNum(els.finProtectStartR, 'protectStartR', 0, 10);
|
|
10610
|
+
finAutoNum(els.finProtectDistR, 'protectDistR', 0, 5);
|
|
10574
10611
|
finAutoNum(els.finPartialR, 'partialR', 0, 10);
|
|
10575
10612
|
finAutoNum(els.finPartialPct, 'partialPct', 0, 90, 5);
|
|
10576
10613
|
finAutoNum(els.finMaxDailyLoss, 'maxDailyLossPct', 0, 50);
|