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/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
- if (fsess && !fsess.botId && !fsess.bgJob && financeState.mode) {
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
- 'Risk otomasyonu main süreçte 5 sn döngüyle çalışır (+R BE, trailing, kısmi TP) — sen yine de SL/TP seviyelerini aktif yönet.',
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
- if (s && !s.bgJob && (!s.botId || s.botId === 'beast')) {
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 prev = (engine.listSessions() || []).find((v) => v && v.finance && !v.isBg && v.id);
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) {
@@ -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>
@@ -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
- list = (list || []).filter((s) => (financeModeOn() ? !!s.finance : !s.finance));
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
- /* o botun en son oturumuna geç; hiç yoksa o bot için yeni sohbet aç */
4629
- try {
4630
- const list = (await beast.listSessions()).filter((s) => (s.botId || 'beast') === id);
4631
- if (list.length) await openSession(list[0].id);
4632
- else { const v = await beast.createSession(); await openSession(v.id); }
4633
- } catch {}
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);