beast-agent 2.63.2 → 2.63.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.63.2",
4
+ "version": "2.63.3",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -2032,7 +2032,7 @@ class Engine {
2032
2032
  modeBlock2 + '\n' +
2033
2033
  (roleBlockFull ? roleBlockFull + '\n' : '') +
2034
2034
  (teamLine || '') +
2035
- 'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_alerts (fiyat alarmı: kur/listele/sil — modu SEN seçersin: once/repeat), mt5_ogrenme (SEMBOL BAZLI ÖĞRENME: işlem istatistiği + dersler — her kapanıştan sonra ders yaz, karar öncesi oku), mt5_limits (lot/pozisyon limitlerini gör/GÜNCELLE — yalnız trader), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (⚡PİYASA EMRİ: side:"buy"|"sell" ile ANLIK gir — bekleyen emir ZORUNLU DEĞİL; lot için volume yerine riskPct+sl ver, sistem hesaplar; limit/stop tipleri de kabul eder), mt5_close (TAM ya da KISMİ kapat: percent ile kısmi TP & kısmi stop — karar ajanın), mt5_modify (SL/TP), mt5_pending (BEKLEYEN emir: limit/stop + price — fiyatın seviyeye gelmesini beklemek için; anlık giriş için mt5_trade kullan), mt5_cancel (emir iptal).\n' +
2035
+ 'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_alerts (fiyat alarmı: kur/listele/sil — modu SEN seçersin: once/repeat), mt5_ogrenme (SEMBOL BAZLI ÖĞRENME: işlem istatistiği + dersler — yalnız gerçekten öğretici kısa içgörüyü yaz, karar öncesi oku), mt5_limits (lot/pozisyon limitlerini gör/GÜNCELLE — yalnız trader; symbol ile SEMBOL BAZLI min/max lot — sembolün karakterine göre karar senin), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (⚡PİYASA EMRİ: side:"buy"|"sell" ile ANLIK gir — bekleyen emir ZORUNLU DEĞİL; lot için volume yerine riskPct+sl ver, sistem hesaplar; limit/stop tipleri de kabul eder), mt5_close (TAM ya da KISMİ kapat: percent ile kısmi TP & kısmi stop — karar ajanın), mt5_modify (SL/TP), mt5_pending (BEKLEYEN emir: limit/stop + price — fiyatın seviyeye gelmesini beklemek için; anlık giriş için mt5_trade kullan), mt5_cancel (emir iptal).\n' +
2036
2036
  'EMİR TİPLERİ (6 tip — HEPSİ AÇIK): buy_market/sell_market = anlık piyasa emri (fiyat beklenmez), buy_limit/sell_limit = bekleyen limit (price zorunlu), buy_stop/sell_stop = bekleyen stop (price zorunlu). mt5_trade ve mt5_pending İKİSİ DE 6 tipi kabul eder; emir tipini net söyle (ör. type:"sell_limit").\n' +
2037
2037
  '⚡HIZLI AKSİYON (ZORUNLU ZİHNİYET): piyasa emri (market buy/sell) HER ZAMAN elinde ve bekleyen emir vermek ZORUNDA DEĞİLSİN. Fırsat anıksa / teyitli kırılım, momentum, haber veya seviye reddi anında mt5_trade {symbol, side:"buy"|"sell", sl, tp, riskPct ya da volume, reason} ile ANINDA market gir — type bile yazmana gerek yok. Bekleyen emir YALNIZ fiyatın seviyeye gelmesini beklemek gerçekten mantıklıysa kurulur (ör. geri çekilme limiti); "emir" diye her şeyi bekleyen emirle yapma. Hızlı girişte tek çağrı yeter: volume yerine riskPct+sl ver → lot otomatik hesaplanır.\n' +
2038
2038
  'ALARM KARARI SENDE: mt5_alerts ile alarm kurarken mode ZORUNLU — "once" (tek seferlik; ilk tetiklemede kapanır) ya da "repeat" (tekrarlı; alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar uyarır + seni uyandırır; cooldownMin varsayılan 5). Tekrarlı mı tek seferlik mi olacağını sen seç, varsayılana bırakma; seviyenin önemine göre cooldownMin belirle (ör. 5/15/60).\n' +
@@ -2064,7 +2064,15 @@ class Engine {
2064
2064
  lim.maxLot ? `max lot ${lim.maxLot}` : null,
2065
2065
  lim.maxPositions ? `max eşzamanlı pozisyon ${lim.maxPositions}` : null,
2066
2066
  ].filter(Boolean).join(' · ') +
2067
- ' — bunları aşıp araç kullanma; sistem zaten reddeder (lot min lot altına inemez, max lot üstüne çıkamaz).\n'
2067
+ ' (genel).' +
2068
+ (lim.symbolLimits && Object.keys(lim.symbolLimits).length
2069
+ ? ' SEMBOL BAZLI: ' +
2070
+ Object.entries(lim.symbolLimits)
2071
+ .map(([s, v]) => `${s} ${Number(v && v.minLot) > 0 ? Number(v.minLot) : lim.minLot}–${Number(v && v.maxLot) > 0 ? Number(v.maxLot) : lim.maxLot}`)
2072
+ .join(' · ') +
2073
+ ' — sembol limiti önceliklidir.'
2074
+ : '') +
2075
+ ' Bunları aşıp araç kullanma; sistem zaten reddeder (lot min lot altına inemez, max lot üstüne çıkamaz).\n'
2068
2076
  : '') +
2069
2077
  'RİSK DİSİPLİNİ:\n' +
2070
2078
  '- SL\u2019siz pozisyon BIRAKMA: açtığın her işlemde mantıklı bir SL koy; SL\u2019siz kalan pozisyonu mt5_modify ile tamamla.\n' +
@@ -2074,7 +2082,7 @@ class Engine {
2074
2082
  '- Emin olmadığında İŞLEM YOK — "BEKLE: <sebep>" yaz. Sık işlem tek başına iyi işlem değil; ama onaylı fırsatta HIZLI gir, kârı hızlı topla.\n' +
2075
2083
  '- Hesap kaldıracı ve serbest marja göre pozisyon boyutunu düşük tut; tek işlemde serbest marjın büyük kısmını riske atma. Sık işlem için lotu değil, TUR SIKLIĞINI ve kısmi kâr alımını kullan.\n' +
2076
2084
  (session && session.financeLearn ? `SEMBOL BAZLI ÖĞRENME HAFIZAN (otomatik istatistik + kendi derslerin — kararlarında kullan):\n${session.financeLearn}\n` : '') +
2077
- 'ÖĞRENME DÖNGÜSÜ (ZORUNLU): her kapanıştan (özellikle stop/TP sonrası) hemen sonra mt5_ogrenme {action:"add", symbol, text, kind} ile o sembolün dersini yaz (ne işe yaradı/yaramadı, hata mı, kural mı); yeni işlem kararından önce mt5_ogrenme {action:"stats", symbol} ya da list ile o sembolün geçmişini oku (kalıcı özet + son dersler) — aynı hatayı tekrarlama, işleyen deseni kullan. Hafıza kendini sadeleştirir: eski dersler otomatik özetlenir ve süresi geçenler unutulur; gerekirse mt5_ogrenme {action:"compact", symbol} ile hemen özetlet, geçersiz dersi remove/clear ile sil.\n' +
2085
+ 'ÖĞRENME DÖNGÜSÜ (SEÇİCİ — ders çıkarmak zorunlu DEĞİL): yalnız GERÇEKTEN öğrenilmiş, tekrar kullanılabilir, kanıtlı bir içgörü varsa tek KISA cümleyle kaydet: mt5_ogrenme {action:"add", symbol, text, kind}. İyi ders: "XAUUSD M5 EMA50 üstü Londra açılışı momentumu 3/4 işledi" gibi somut, sebep-sonuç içeren, en fazla ~20 kelimelik cümle. YASAK: her kapanışa ders yazmak, günlük/rapor tarzı uzun metin ("bugün şunu yaptım"), genel laf ("dikkatli olmalıyım"), tekrar. Emin değilsen HİÇ ders yazma — hafızayı çöple doldurma; günde en fazla 2-3 ders. Yeni karar öncesi mt5_ogrenme {action:"stats", symbol} ya da list ile o sembolün geçmişini oku (kalıcı özet + son dersler) — aynı hatayı tekrarlama, işleyen deseni kullan. Hafıza kendini sadeleştirir: eski dersler otomatik özetlenir ve süresi geçenler unutulur; gerekirse mt5_ogrenme {action:"compact", symbol} ile hemen özetlet, geçersiz dersi remove/clear ile sil.\n' +
2078
2086
  (session && session.financeStrategy ? `SAHİBİNİN STRATEJİ NOTU (önceliklidir):\n${session.financeStrategy}\n` : '') +
2079
2087
  (session && session.financeShadow ? 'SHADOW MOD AKTİF: mt5_trade/mt5_pending emir GÖNDERMEZ — kararını teziyle raporla; gerçek işlem açılmaz (karar günlüğe yazılır).\n' : '') +
2080
2088
  (session && session.financeDigest ? `İŞLEM PERFORMANS GEÇMİŞİN (kendi kayıtların — kararlarında ders çıkar):\n${session.financeDigest}\n` : '') +
@@ -101,6 +101,19 @@ function tickPrice(row, side) {
101
101
  return Number(row && row.bid) || Number(row && row.ask) || 0;
102
102
  }
103
103
 
104
+ /* SEMBOL BAZLI LOT ARALIĞI (trader mt5_limits ile koyar): sembol limiti
105
+ varsa genel aralığın yerine geçer — yoksa genel minLot/maxLot kullanılır.
106
+ İki değer de broker normalizasyonuna girer (adım/min/max sembolden gelir). */
107
+ function lotRange(cfg, symbol) {
108
+ const sym = String(symbol || '').trim().toUpperCase();
109
+ const o = (cfg && cfg.symbolLimits && cfg.symbolLimits[sym]) || null;
110
+ let minLot = Number(o && o.minLot) > 0 ? Number(o.minLot) : Number(cfg && cfg.minLot) || 0.01;
111
+ let maxLot = Number(o && o.maxLot) > 0 ? Number(o.maxLot) : Number(cfg && cfg.maxLot) || 0.1;
112
+ if (minLot > maxLot) minLot = maxLot;
113
+ if (maxLot < minLot) maxLot = minLot;
114
+ return { minLot, maxLot };
115
+ }
116
+
104
117
  /* İşlem öncesi risk katmanı: stops_level + yoğunluk + marj kalkanı.
105
118
  Hata metni ya da null döner. positions çağıran tarafından verilir. */
106
119
  async function preTradeCheck(cfg, side, symbol, info, price, volume, sl, tp, positions) {
@@ -262,32 +275,35 @@ const definitions = NAMES.map((name) => {
262
275
  },
263
276
  mt5_limits: {
264
277
  description:
265
- 'LOT / POZİSYON LİMİTLERİNİ oku ve GÜNCELLE — trader karar mercii: mt5_limits {action:"get"|"set", minLot?, maxLot?, maxPositions?}. get: yürürlükteki min lot / max lot / max eşzamanlı pozisyon ve işlem riski %. set: yalnız ana trader (ve finance sohbet copilot\'ı) kullanabilir — rol/işçi ajanları değiştiremez. Volatilite rejimine göre limitleri sen ayarla (ör. sakin piyasada max lot artır, haber anında düşür); değişiklik ANINDA panelde ve sonraki turda geçerli olur. Kural: minLot maxLot\'u aşamaz.',
278
+ 'LOT / POZİSYON LİMİTLERİNİ oku ve GÜNCELLE — trader karar mercii: mt5_limits {action:"get"|"set", symbol?, minLot?, maxLot?, maxPositions?, reset?}. get: genel min lot / max lot / max eşzamanlı pozisyon + SEMBOL BAZLI limitler + işlem riski %. GENEL set: minLot/maxLot/maxPositions. SEMBOL BAZLI set: symbol ver (ör. {action:"set", symbol:"XAUUSD", minLot:0.02, maxLot:0.5}) — her sembolün min/max lotunu kendi karakterine göre SEN belirle (volatilite, spread, marj); sembol limiti genel aralığın dışına çıkamaz (genel taban/tavan kelepçeler). Sembolü genele döndürmek için {action:"set", symbol:"XAUUSD", reset:true}. set: yalnız ana trader (ve finance sohbet copilot\'ı) kullanabilir — rol/işçi ajanları değiştiremez. Değişiklik ANINDA panelde ve sonraki turda geçerli olur. Kural: minLot maxLot\'u aşamaz.',
266
279
  parameters: {
267
280
  type: 'object',
268
281
  properties: {
269
282
  action: { type: 'string', enum: ['get', 'set'], description: 'get: mevcut limitler · set: güncelle' },
283
+ symbol: { type: 'string', description: 'set: sembol bazlı lot limiti (ör. XAUUSD) — genel set için boş bırak' },
270
284
  minLot: { type: 'number', description: 'set: yeni alt sınır (0.01-100)' },
271
285
  maxLot: { type: 'number', description: 'set: yeni üst sınır (0.01-100)' },
272
- maxPositions: { type: 'number', description: 'set: eşzamanlı pozisyon tavanı (1-20)' },
286
+ maxPositions: { type: 'number', description: 'set: eşzamanlı pozisyon tavanı (1-20) — yalnız GENEL (sembol ile verilemez)' },
287
+ reset: { type: 'boolean', description: 'set + symbol: true → sembol limitini kaldır, genel aralığa dön' },
273
288
  },
274
289
  required: ['action'],
275
290
  },
276
291
  },
277
292
  mt5_ogrenme: {
278
293
  description:
279
- 'SEMBOL BAZLI ÖĞRENME HAFIZASI — Beast Finance sürekli öğrenir: mt5_ogrenme {action:"list"|"add"|"remove"|"clear"|"stats"|"compact"|"forget", symbol?, text?, kind?, tags?, id?, ids?, all?}. Her sembolün KENDİ istatistiği (işlem/kazanç/kayıp, net, kâr yakalama, kâr geri verme) OTOMATİK birikir; ayrıca işlemlerden çıkardığın DERSLERİ sen kaydedersin. KURALLAR: (1) Her kapanıştan sonra (özellikle stop/tp sonrası) sembol için tek cümle ders yaz: hangi setup işe yaradı/yaramadı, saat/seans, SL yeri, hata mı hata yok mu — kind:"pattern"|"mistake"|"rule"|"observation". (2) Yeni işlem kararından ÖNCE ilgili sembolün list/stats çıktısını oku; aynı hatayı tekrarlama, işleyen deseni kullan. (3) Yanlış çıkan/genel geçersiz dersi remove/clear ile sil. Aynı ders tekrar yazılamaz ve sembol başına 24 saatte en fazla 12 ders kaydedilir. HAFIZA KENDİNİ SADELEŞTİRİR (opencode tarzı compaction): ham dersler birikince (24+) ESKİ dersler OTOMATİK olarak modele özetlettirilir ve tek KALICI ÖZETE sıkıştırılır — ham yalnız son dersler kalır; 30 günden eski dersler, 90 günden eski işlem kayıtları ve 120 gün hareketsiz semboller otomatik unutulur. Gerekirse compact {symbol} ile hemen özetlet, forget ile süresi geçenleri temizle. action:"list" symbol verilmezse tüm sembollerin özetini döner (kalıcı özet dahil). Ör: {action:"add", symbol:"XAUUSD", text:"Londra açılışında M5 EMA50 üstü momentum girişleri iyi çalışıyor", kind:"pattern"}.',
294
+ 'SEMBOL BAZLI ÖĞRENME HAFIZASI — Beast Finance sürekli öğrenir: mt5_ogrenme {action:"list"|"add"|"remove"|"clear"|"stats"|"compact"|"forget", symbol?, text?, kind?, tags?, id?, ids?, all?, limit?}. Her sembolün KENDİ istatistiği (işlem/kazanç/kayıp, net, kâr yakalama, kâr geri verme) OTOMATİK birikir; ayrıca gerçekten öğrendiğin dersleri sen kaydedersin. DERS KURALLARI (SEÇİCİ): (1) Her kapanışa ders YAZMA — yalnız gerçekten öğrenilmiş, tekrar kullanılabilir, kanıtlı içgörüyü TEK KISA cümleyle kaydet (en fazla ~20 kelime / 200 karakter); ör. "XAUUSD M5 EMA50 üstü Londra açılışı momentumu 3/4 işledi". YASAK: günlük/rapor tarzı uzun metin ("bugün şunu yaptım"), genel laf ("dikkatli olmalıyım"), her turda ders, tekrar. Emin değilsen HİÇ kaydetme. (2) Yeni işlem kararından ÖNCE ilgili sembolün stats (kompakt: istatistik + kalıcı özet + son 3 ders) ya da list (son 8 ders; limit ile artır) çıktısını oku; aynı hatayı tekrarlama, işleyen deseni kullan. (3) Yanlış çıkan/genel geçersiz dersi remove/clear ile sil. Aynı ders tekrar yazılamaz; sembol başına 24 saatte en fazla 3 ders kabul edilir (kalite kapısı: çok kısa/uzun, günlük tarzı ve genel tavsiye metinleri reddedilir). HAFIZA KENDİNİ SADELEŞTİRİR (opencode tarzı compaction): ham dersler birikince (24+) ESKİ dersler OTOMATİK olarak modele özetlettirilir ve tek KALICI ÖZETE sıkıştırılır — ham yalnız son dersler kalır; 30 günden eski dersler, 90 günden eski işlem kayıtları ve 120 gün hareketsiz semboller otomatik unutulur. Gerekirse compact {symbol} ile hemen özetlet, forget ile süresi geçenleri temizle. action:"list" symbol verilmezse tüm sembollerin özetini döner (kalıcı özet dahil).',
280
295
  parameters: {
281
296
  type: 'object',
282
297
  properties: {
283
298
  action: { type: 'string', enum: ['list', 'add', 'remove', 'clear', 'stats', 'compact', 'forget'] },
284
299
  symbol: { type: 'string', description: 'Sembol (ör. XAUUSD) — öğrenme sembol bazlıdır' },
285
- text: { type: 'string', description: 'add: öğrenilen ders/desen (kısa, net, tek cümle)' },
300
+ text: { type: 'string', description: 'add: öğrenilen ders — TEK kısa cümle (en fazla ~20 kelime/200 karakter), somut ve tekrar kullanılabilir; günlük/uzun metin kaydetme' },
286
301
  kind: { type: 'string', enum: ['pattern', 'mistake', 'rule', 'observation'], description: 'add: kayıt türü (varsayılan observation)' },
287
302
  tags: { type: 'array', items: { type: 'string' }, description: 'add: etiketler (ör. ["scalp","london"])' },
288
303
  id: { type: 'string', description: 'remove: kayıt id' },
289
304
  ids: { type: 'array', items: { type: 'string' }, description: 'clear: silinecek kayıt id listesi' },
290
305
  all: { type: 'boolean', description: 'clear: true → tüm öğrenme kayıtları (dikkatli)' },
306
+ limit: { type: 'number', description: 'list: döndürülecek son ders sayısı (varsayılan 8, en fazla 25)' },
291
307
  },
292
308
  required: ['action'],
293
309
  },
@@ -565,8 +581,14 @@ const handlers = {
565
581
  for (const k of ['minLot', 'maxLot', 'maxPositions']) {
566
582
  if (args[k] !== undefined && args[k] !== null && args[k] !== '') patch[k] = Number(args[k]);
567
583
  }
568
- if (!Object.keys(patch).length) {
569
- return { ok: false, error: 'set için en az bir alan ver: minLot / maxLot / maxPositions' };
584
+ /* SEMBOL BAZLI LOT LİMİTİ: symbol verilirse trader o sembolün min/max
585
+ lotunu ayarlar (reset:true → genel aralığa döndürür) */
586
+ const sym = String(args.symbol || '').trim().toUpperCase();
587
+ if (sym) patch.symbol = sym;
588
+ const reset = args.reset === true || String(args.reset || '').toLowerCase() === 'true';
589
+ if (reset) patch.reset = true;
590
+ if (!Object.keys(patch).length || (sym && patch.minLot === undefined && patch.maxLot === undefined && !reset)) {
591
+ return { ok: false, error: 'set için en az bir alan ver: minLot / maxLot / maxPositions (sembol bazlıda minLot/maxLot ya da reset:true)' };
570
592
  }
571
593
  return await limitsApi.set(patch, ctx || {});
572
594
  }
@@ -575,7 +597,12 @@ const handlers = {
575
597
  async mt5_ogrenme(args, ctx) {
576
598
  const action = String(args.action || 'list').toLowerCase();
577
599
  const symbol = String(args.symbol || '').trim().toUpperCase();
578
- if (action === 'list') return learningApi.list({ symbol });
600
+ if (action === 'list') {
601
+ /* kompakt okuma: varsayılan son 8 ders (limit ile artırılır, max 25) —
602
+ hafızanın tamamı ajana gönderilmez */
603
+ const limit = Math.round(Number(args.limit) || 0);
604
+ return learningApi.list({ symbol, limit: limit > 0 ? limit : 8 });
605
+ }
579
606
  if (action === 'stats') return learningApi.stats({ symbol });
580
607
  if (action === 'add') {
581
608
  const text = String(args.text || '').replace(/\s+/g, ' ').trim();
@@ -584,9 +611,11 @@ const handlers = {
584
611
  const kindRaw = String(args.kind || 'observation').toLowerCase();
585
612
  const kind = ['pattern', 'mistake', 'rule', 'observation'].includes(kindRaw) ? kindRaw : 'observation';
586
613
  const tags = Array.isArray(args.tags) ? args.tags.map((t) => String(t || '').trim()).filter(Boolean).slice(0, 6) : [];
587
- const res = learningApi.add({ symbol, text: text.slice(0, 600), kind, tags, sid: (ctx && ctx.sessionId) || '' });
614
+ /* metin AYNEN geçer: kalite kapısı (kısa+somut) main tarafında çalışır —
615
+ uzun/günlük metin sessizce kırpılmaz, ajan net hata alıp kısaltır */
616
+ const res = learningApi.add({ symbol, text, kind, tags, sid: (ctx && ctx.sessionId) || '' });
588
617
  if (res && res.ok) {
589
- noteTrade('learn', { symbol, text: text.slice(0, 300), kind }, ctx || {});
618
+ noteTrade('learn', { symbol, text: text.slice(0, 200), kind }, ctx || {});
590
619
  }
591
620
  return res;
592
621
  }
@@ -678,7 +707,8 @@ const handlers = {
678
707
  const balance = Number(acct && acct.account && acct.account.balance) || 0;
679
708
  if (!(balance > 0)) return { ok: false, error: 'bakiye okunamadı' };
680
709
  const riskAmount = (balance * riskPct) / 100;
681
- const res = finrisk.calcRiskLot(info, entry, sl, riskAmount, cfg.maxLot, cfg.minLot);
710
+ const range = lotRange(cfg, symbol);
711
+ const res = finrisk.calcRiskLot(info, entry, sl, riskAmount, range.maxLot, range.minLot);
682
712
  if (res.error) return { ok: false, error: res.error, raw: res.raw, lossPerLot: res.lossPerLot, riskAmount: Math.round(riskAmount * 100) / 100 };
683
713
  const digits = Number(info.digits) || 5;
684
714
  const dist = Math.abs(entry - sl);
@@ -699,8 +729,8 @@ const handlers = {
699
729
  capped: !!res.capped,
700
730
  raised: !!res.raised,
701
731
  warning: closeErr || undefined,
702
- minLot: Number(cfg.minLot) || 0.01,
703
- maxLot: Number(cfg.maxLot) || 0.1,
732
+ minLot: range.minLot,
733
+ maxLot: range.maxLot,
704
734
  };
705
735
  },
706
736
  async mt5_trade(args, ctx) {
@@ -720,7 +750,7 @@ const handlers = {
720
750
  const side = t.side;
721
751
  const symbol = String(args.symbol || '').trim().toUpperCase();
722
752
  if (!symbol) return { ok: false, error: 'symbol gerekli' };
723
- const maxLot = Number(cfg.maxLot) || 0.1;
753
+ const range = lotRange(cfg, symbol);
724
754
  const sl = Number(args.sl) || 0;
725
755
  const tp = Number(args.tp) || 0;
726
756
  const info = await symInfoRow(symbol);
@@ -738,12 +768,12 @@ const handlers = {
738
768
  const acct = await bcall('account', {}, 8000);
739
769
  const balance = Number(acct && acct.account && acct.account.balance) || 0;
740
770
  const riskAmount = (balance * riskPct) / 100;
741
- const rr = finrisk.calcRiskLot(info, price, sl, riskAmount, maxLot, cfg.minLot);
771
+ const rr = finrisk.calcRiskLot(info, price, sl, riskAmount, range.maxLot, range.minLot);
742
772
  if (rr.error) return { ok: false, error: rr.error };
743
773
  vol = rr.volume;
744
774
  riskInfo = { riskPct, riskAmount: Math.round(riskAmount * 100) / 100, lossPerLot: rr.lossPerLot, raised: !!rr.raised };
745
775
  } else {
746
- const norm = finrisk.normalizeVolume(info, wantVolume, maxLot, cfg.minLot);
776
+ const norm = finrisk.normalizeVolume(info, wantVolume, range.maxLot, range.minLot);
747
777
  if (norm.error) return { ok: false, error: norm.error };
748
778
  vol = norm.volume;
749
779
  }
@@ -860,7 +890,8 @@ const handlers = {
860
890
  if (!symbol) return { ok: false, error: 'symbol gerekli' };
861
891
  const info = await symInfoRow(symbol);
862
892
  if (!info) return { ok: false, error: 'sembol bulunamadı: ' + symbol + ' (MT5 Market Watch?)' };
863
- const norm = finrisk.normalizeVolume(info, args.volume, Number(cfg.maxLot) || 0.1, cfg.minLot);
893
+ const range = lotRange(cfg, symbol);
894
+ const norm = finrisk.normalizeVolume(info, args.volume, range.maxLot, range.minLot);
864
895
  if (norm.error) return { ok: false, error: norm.error };
865
896
  const vol = norm.volume;
866
897
  const ptype = t.type;
@@ -24,7 +24,9 @@ function stepCeil(v, step) {
24
24
 
25
25
  /* info: MT5 symbol_info satırı. wanted: istenen lot. hardMax: kullanıcı üst
26
26
  limiti (max lot). hardMin: kullanıcı alt limiti (min lot) — istenen lot bu
27
- tabanın altındaysa adıma yukarı yuvarlanır (raised). */
27
+ tabanın altındaysa adıma yukarı yuvarlanır (raised). Taban/tavan broker
28
+ adımına OTURMUYORSA (ör. min 0.15–max 0.19, adım 0.1) sessizce limit dışına
29
+ düşmek yerine net hata döner — aralığı mt5_limits ile düzelt. */
28
30
  function normalizeVolume(info, wanted, hardMax, hardMin) {
29
31
  const vmin = num(info && info.volume_min, 0.01);
30
32
  const vmax = num(info && info.volume_max, 100);
@@ -33,24 +35,32 @@ function normalizeVolume(info, wanted, hardMax, hardMin) {
33
35
  if (!(v > 0)) return { error: 'volume gerekli (pozitif sayı)' };
34
36
  const cap = Math.min(vmax > 0 ? vmax : Infinity, num(hardMax, 0) > 0 ? num(hardMax, 0) : Infinity);
35
37
  const floor = num(hardMin, 0) > 0 ? num(hardMin, 0) : 0;
38
+ const align = (x) => Math.round(x * 1e8) / 1e8;
39
+ const mismatch = () => ({
40
+ error: `lot aralığı broker adımıyla uyuşmuyor (min ${floor}, max ${cap === Infinity ? '∞' : cap}, adım ${step}) — mt5_limits ile aralığı düzelt`,
41
+ });
42
+ if (floor > cap + 1e-9) return mismatch();
36
43
  let capped = false;
37
44
  let raised = false;
38
45
  if (v > cap) {
39
- v = stepFloor(cap, step);
46
+ v = align(stepFloor(cap, step));
40
47
  capped = true;
48
+ if (floor > 0 && v < floor - 1e-9) return mismatch();
41
49
  }
42
50
  if (v < floor - 1e-9) {
43
- v = stepCeil(floor, step);
51
+ v = align(stepCeil(floor, step));
44
52
  raised = true;
53
+ if (v > cap + 1e-9) return mismatch();
45
54
  }
46
- v = Math.round(stepFloor(v, step) * 1e8) / 1e8;
55
+ v = align(stepFloor(v, step));
56
+ if (floor > 0 && v < floor - 1e-9) { v = align(stepCeil(floor, step)); raised = true; }
47
57
  if (!(v > 0) || v < vmin - 1e-9) {
48
58
  return { error: `hacim broker minimumunun altında (min ${vmin} lot)` };
49
59
  }
50
60
  if (v > cap + 1e-9) {
51
- /* min lot tabanı broker adımıyla üst limite sığmadı — üst limitte kal */
52
- v = Math.round(stepFloor(cap, step) * 1e8) / 1e8;
61
+ v = align(stepFloor(cap, step));
53
62
  capped = true;
63
+ if (floor > 0 && v < floor - 1e-9) return mismatch();
54
64
  if (!(v > 0) || v < vmin - 1e-9) {
55
65
  return { error: `hacim broker minimumunun altında (min ${vmin} lot)` };
56
66
  }
@@ -1,52 +1,52 @@
1
- ---
2
- name: risk-yonetimi
3
- description: Pozisyon boyutlandırma ve risk disiplini rehberi — hesap yüzdesiyle lot hesabı, marj/kaldıraç denetimi, max eşzamanlı pozisyon, korelasyon/tek yön birikme, günlük kayıp limiti, SL/BE/trailing kuralları. Risk ajanı veya trader her turda ve her işlemden önce bu skill'e göre kontrol yapar.
4
- version: 1.0.0
5
- ---
6
-
7
- # Risk Yönetimi
8
-
9
- ## TEMEL KURAL
10
-
11
- Önce kaybı sınırla, kârı piyasa versin. İşlem başına risk sabittir; lot, SL mesafesine göre hesaplanır.
12
-
13
- ## 1) POZİSYON BOYUTU
14
-
15
- - İşlem başına risk: hesabın **%0.5–1'i** (agresif değilse %0.25–0.5).
16
- - Lot hesabı: `lot = (bakiye × risk%) / (SL_mesafesi × sembolün pip değeri)`.
17
- - SL mesafesi ATR/yapıdan gelir; önce SL belirle, sonra lotu küçült — tersini yapma.
18
- - Broker limitleri: panel `max lot` ve `max eşzamanlı pozisyon` ayarlarına uy; sistem reddederse zorlama.
19
-
20
- ## 2) MARJ / KALDIRAÇ
21
-
22
- - Serbest marjın tamamını tek işleme bağlama; toplam açık risk serbest marjın küçük bir kısmı olsun.
23
- - Kaldıraç yüksekse lotu düşür; kaldıraç = risk değil, imkândır.
24
- - Marj seviyesi düşerken yeni pozisyon AÇMA; önce riski azalt.
25
-
26
- ## 3) PORTFÖY DENETİMİ (her turda)
27
-
28
- - Açık pozisyonların toplam riskini hesapla (SL'lere göre kayıp potansiyeli).
29
- - **Tek yön birikme**: aynı yönde üst üste pozisyon = tek işlem gibi davranır; toplam riski tek işlem limiti gibi say.
30
- - **Korelasyon**: XAUUSD/EURUSD ile DXY ters koreledir; aynı yönde iki korelasyonlu pozisyon riski katlar.
31
- - SL'siz pozisyon varsa ilk iş: SL tamamla veya kapat.
32
- - Hedefine ulaşan/kâra geçen pozisyonda BE veya kısmi kâr; trailing ATR bazlı.
33
- - **Kâr koruma:** +0.3R'de sistem SL'yi en iyi kârın 0.15R gerisine kilitler; +0.3-0.5R'de kısmi TP (ör. %50) ile kârı cebe al, kalanı trailing'e bırak. TP için R:R ≥ 1.5 şartı YOK — hızlı kâr toplama (scalp) serbest; ama kârı kale verme.
34
- - **Sembol bazlı öğrenme:** her kapanıştan sonra mt5_ogrenme add ile dersi yaz (ne işe yaradı/yaramadı); yeni işlem öncesi o sembolün stats/list geçmişini oku — aynı hatayı tekrarlama.
35
-
36
- ## 4) KAYIP LİMİTLERİ
37
-
38
- - Günlük kayıp limiti belirle (ör. hesabın %2–3'ü). Limit dolduysa **o gün yeni işlem yok**; raporla.
39
- - Üst üste 2-3 kayıptan sonra lotu artırma (revenge trade YASAK); aksine küçült veya dur.
40
- - Drawdown artıyorsa tur sıklığını/agresifliği azalt, sadece A+ setuplara gir.
41
-
42
- ## 5) YASAKLAR
43
-
44
- - Martingale / kademeli lot artışı.
45
- - SL taşımayı kaybı büyütecek yönde yapmak (SL genişletme).
46
- - Marjı zorlayan "son işlem" kumarı.
47
- - Aynı anda korelasyonlu 3+ pozisyonla tek yön yığmak.
48
-
49
- ## 6) RAPOR FORMATI
50
-
51
- `Risk: toplam açık R%X | marj kullanımı %Y | en riskli pozisyon: ... | öneri: SL/BE/küçült/kapat`
52
- Sınır aşımı varsa öncelikli ve net yaz.
1
+ ---
2
+ name: risk-yonetimi
3
+ description: Pozisyon boyutlandırma ve risk disiplini rehberi — hesap yüzdesiyle lot hesabı, marj/kaldıraç denetimi, max eşzamanlı pozisyon, korelasyon/tek yön birikme, günlük kayıp limiti, SL/BE/trailing kuralları. Risk ajanı veya trader her turda ve her işlemden önce bu skill'e göre kontrol yapar.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Risk Yönetimi
8
+
9
+ ## TEMEL KURAL
10
+
11
+ Önce kaybı sınırla, kârı piyasa versin. İşlem başına risk sabittir; lot, SL mesafesine göre hesaplanır.
12
+
13
+ ## 1) POZİSYON BOYUTU
14
+
15
+ - İşlem başına risk: hesabın **%0.5–1'i** (agresif değilse %0.25–0.5).
16
+ - Lot hesabı: `lot = (bakiye × risk%) / (SL_mesafesi × sembolün pip değeri)`.
17
+ - SL mesafesi ATR/yapıdan gelir; önce SL belirle, sonra lotu küçült — tersini yapma.
18
+ - Broker limitleri: panel `max lot` ve `max eşzamanlı pozisyon` ayarlarına uy; sistem reddederse zorlama.
19
+
20
+ ## 2) MARJ / KALDIRAÇ
21
+
22
+ - Serbest marjın tamamını tek işleme bağlama; toplam açık risk serbest marjın küçük bir kısmı olsun.
23
+ - Kaldıraç yüksekse lotu düşür; kaldıraç = risk değil, imkândır.
24
+ - Marj seviyesi düşerken yeni pozisyon AÇMA; önce riski azalt.
25
+
26
+ ## 3) PORTFÖY DENETİMİ (her turda)
27
+
28
+ - Açık pozisyonların toplam riskini hesapla (SL'lere göre kayıp potansiyeli).
29
+ - **Tek yön birikme**: aynı yönde üst üste pozisyon = tek işlem gibi davranır; toplam riski tek işlem limiti gibi say.
30
+ - **Korelasyon**: XAUUSD/EURUSD ile DXY ters koreledir; aynı yönde iki korelasyonlu pozisyon riski katlar.
31
+ - SL'siz pozisyon varsa ilk iş: SL tamamla veya kapat.
32
+ - Hedefine ulaşan/kâra geçen pozisyonda BE veya kısmi kâr; trailing ATR bazlı.
33
+ - **Kâr koruma:** +0.3R'de sistem SL'yi en iyi kârın 0.15R gerisine kilitler; +0.3-0.5R'de kısmi TP (ör. %50) ile kârı cebe al, kalanı trailing'e bırak. TP için R:R ≥ 1.5 şartı YOK — hızlı kâr toplama (scalp) serbest; ama kârı kale verme.
34
+ - **Sembol bazlı öğrenme (seçici):** her kapanışa ders YAZMA — yalnız gerçekten öğretici, tekrar kullanılabilir içgörüyü TEK kısa cümleyle (≈20 kelime) mt5_ogrenme add ile kaydet; yeni işlem öncesi o sembolün stats/list geçmişini oku — aynı hatayı tekrarlama.
35
+
36
+ ## 4) KAYIP LİMİTLERİ
37
+
38
+ - Günlük kayıp limiti belirle (ör. hesabın %2–3'ü). Limit dolduysa **o gün yeni işlem yok**; raporla.
39
+ - Üst üste 2-3 kayıptan sonra lotu artırma (revenge trade YASAK); aksine küçült veya dur.
40
+ - Drawdown artıyorsa tur sıklığını/agresifliği azalt, sadece A+ setuplara gir.
41
+
42
+ ## 5) YASAKLAR
43
+
44
+ - Martingale / kademeli lot artışı.
45
+ - SL taşımayı kaybı büyütecek yönde yapmak (SL genişletme).
46
+ - Marjı zorlayan "son işlem" kumarı.
47
+ - Aynı anda korelasyonlu 3+ pozisyonla tek yön yığmak.
48
+
49
+ ## 6) RAPOR FORMATI
50
+
51
+ `Risk: toplam açık R%X | marj kullanımı %Y | en riskli pozisyon: ... | öneri: SL/BE/küçült/kapat`
52
+ Sınır aşımı varsa öncelikli ve net yaz.
package/src/main.js CHANGED
@@ -9410,6 +9410,33 @@ function financeDir() {
9410
9410
  return d;
9411
9411
  }
9412
9412
 
9413
+ /* SEMBOL BAZLI LOT LİMİTLERİ: trader mt5_limits symbol ile koyar; anahtar =
9414
+ sembol (XAUUSD…). Genel aralık taban/tavan olarak zorlanır — sembol limiti
9415
+ onun dışına çıkamaz; boş alan genel aralığı kullanır. Her okuma/yazmada
9416
+ normalize edilir (bozuk/eski kayıtlar temizlenir). */
9417
+ function finNormalizeSymbolLimits(f) {
9418
+ const gMin = Number(f.minLot) || 0.01;
9419
+ const gMax = Number(f.maxLot) || 0.1;
9420
+ if (!f.symbolLimits || typeof f.symbolLimits !== 'object') f.symbolLimits = {};
9421
+ const clean = {};
9422
+ for (const [k, v] of Object.entries(f.symbolLimits).slice(0, 40)) {
9423
+ const sym = String(k || '').trim().toUpperCase();
9424
+ const o = v && typeof v === 'object' ? v : {};
9425
+ if (!sym) continue;
9426
+ let mn = Number(o.minLot) > 0 ? Math.round(Math.max(0.01, Math.min(100, Number(o.minLot))) * 100) / 100 : 0;
9427
+ let mx = Number(o.maxLot) > 0 ? Math.round(Math.max(0.01, Math.min(100, Number(o.maxLot))) * 100) / 100 : 0;
9428
+ if (mn && mx && mn > mx) mn = mx;
9429
+ if (mn) mn = Math.min(Math.max(mn, gMin), gMax);
9430
+ if (mx) mx = Math.max(Math.min(mx, gMax), gMin);
9431
+ if (mn && mx && mn > mx) mn = mx;
9432
+ const lim = {};
9433
+ if (mn) lim.minLot = mn;
9434
+ if (mx) lim.maxLot = mx;
9435
+ if (lim.minLot || lim.maxLot) clean[sym] = lim;
9436
+ }
9437
+ f.symbolLimits = clean;
9438
+ }
9439
+
9413
9440
  function finCfg() {
9414
9441
  if (!settings.finance || typeof settings.finance !== 'object') settings.finance = {};
9415
9442
  const f = settings.finance;
@@ -9448,8 +9475,10 @@ function finCfg() {
9448
9475
  if (!Number(f.intervalSec)) f.intervalSec = 120;
9449
9476
  if (!Number(f.minLot)) f.minLot = 0.01;
9450
9477
  if (!Number(f.maxLot)) f.maxLot = 0.1;
9451
- /* LOT ARALIĞI KILİDİ: min lot hiçbir zaman max lotu aşamaz */
9478
+ /* LOT ARALIĞI KİLİDİ: min lot hiçbir zaman max lotu aşamaz */
9452
9479
  if (f.minLot > f.maxLot) f.minLot = f.maxLot;
9480
+ /* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı — mt5_limits symbol ile koyar) */
9481
+ finNormalizeSymbolLimits(f);
9453
9482
  if (f.maxPositions == null) f.maxPositions = 3;
9454
9483
  /* KODLA DİSİPLİN (trader kuralları): hepsi 0 = kural kapalı */
9455
9484
  if (!Number.isFinite(Number(f.maxTradesPerDay))) f.maxTradesPerDay = 10;
@@ -11248,6 +11277,8 @@ function finLimitsSnapshot() {
11248
11277
  maxLot: Number(f.maxLot) || 0.1,
11249
11278
  maxPositions: Number(f.maxPositions) || 3,
11250
11279
  riskPerTradePct: Number(f.riskPerTradePct) || 0,
11280
+ /* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı) — genel aralığı daraltır */
11281
+ symbols: { ...(f.symbolLimits && typeof f.symbolLimits === 'object' ? f.symbolLimits : {}) },
11251
11282
  };
11252
11283
  }
11253
11284
 
@@ -11262,21 +11293,22 @@ function finLimitsSessionAllowed(sid) {
11262
11293
  return true;
11263
11294
  }
11264
11295
 
11265
- function finSyncLimitsToSessions() {
11296
+ function finSyncLimitsToSessions(extra) {
11266
11297
  const lim = finLimitsSnapshot();
11298
+ const apply = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions, symbolLimits: lim.symbols };
11267
11299
  if (engine) {
11268
11300
  try {
11269
11301
  for (const [sid] of financeState.agents) {
11270
11302
  const s = engine.cache.get(String(sid));
11271
- if (s) s.financeLimits = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions };
11303
+ if (s) s.financeLimits = { ...apply };
11272
11304
  }
11273
11305
  for (const [, s] of engine.cache) {
11274
11306
  if (!s || !s.finance || s.bgJob || !s.financeLimits) continue;
11275
- s.financeLimits = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions };
11307
+ s.financeLimits = { ...apply };
11276
11308
  }
11277
11309
  } catch {}
11278
11310
  }
11279
- finPush('limits', { ...lim });
11311
+ finPush('limits', { ...lim, ...(extra && typeof extra === 'object' ? extra : {}) });
11280
11312
  }
11281
11313
 
11282
11314
  function finLimitsSet(patch, ctx) {
@@ -11286,6 +11318,54 @@ function finLimitsSet(patch, ctx) {
11286
11318
  }
11287
11319
  const f = finCfg();
11288
11320
  const notes = [];
11321
+ /* SEMBOL BAZLI LOT LİMİTİ (symbol verilirse): trader sembole göre min/max lot
11322
+ koyar — genel aralık taban/tavan olarak zorlanır. reset:true (ya da yalnız
11323
+ symbol) → sembol limiti kaldırılır, genel aralık geçerli olur. */
11324
+ const sym = String(patch.symbol || '').trim().toUpperCase();
11325
+ if (sym) {
11326
+ if (patch.maxPositions !== undefined) {
11327
+ return { ok: false, error: 'maxPositions sembol bazlı değil — symbol vermeden genel limit olarak ayarla' };
11328
+ }
11329
+ const hasMin = patch.minLot !== undefined && patch.minLot !== null && patch.minLot !== '';
11330
+ const hasMax = patch.maxLot !== undefined && patch.maxLot !== null && patch.maxLot !== '';
11331
+ const reset = patch.reset === true || String(patch.reset || '').toLowerCase() === 'true';
11332
+ if (!reset && !hasMin && !hasMax) {
11333
+ return { ok: false, error: 'sembol güncellemesi için minLot/maxLot ver ya da reset:true' };
11334
+ }
11335
+ if (!f.symbolLimits || typeof f.symbolLimits !== 'object') f.symbolLimits = {};
11336
+ if (reset) {
11337
+ if (f.symbolLimits[sym]) {
11338
+ delete f.symbolLimits[sym];
11339
+ notes.push('sembol limiti kaldırıldı → genel aralık geçerli');
11340
+ }
11341
+ } else {
11342
+ const cur = { ...(f.symbolLimits[sym] || {}) };
11343
+ if (hasMin) {
11344
+ const v = Number(patch.minLot);
11345
+ if (!(v >= 0.01) || v > 100) return { ok: false, error: 'sembol minLot 0.01-100 arasında olmalı' };
11346
+ cur.minLot = Math.round(v * 100) / 100;
11347
+ }
11348
+ if (hasMax) {
11349
+ const v = Number(patch.maxLot);
11350
+ if (!(v >= 0.01) || v > 100) return { ok: false, error: 'sembol maxLot 0.01-100 arasında olmalı' };
11351
+ cur.maxLot = Math.round(v * 100) / 100;
11352
+ }
11353
+ /* genel aralığa kelepçe: sembol limiti geneli AŞAMAZ/ALTA İNEMEZ */
11354
+ if (cur.minLot !== undefined && cur.minLot < f.minLot) { cur.minLot = f.minLot; notes.push(`minLot genel tabana kelepçelendi (${f.minLot})`); }
11355
+ if (cur.maxLot !== undefined && cur.maxLot > f.maxLot) { cur.maxLot = f.maxLot; notes.push(`maxLot genel tavana kelepçelendi (${f.maxLot})`); }
11356
+ if (cur.minLot !== undefined && cur.maxLot !== undefined && cur.minLot > cur.maxLot) { cur.minLot = cur.maxLot; notes.push(`minLot maxLot'a kelepçelendi (${cur.maxLot})`); }
11357
+ if (cur.minLot === undefined && cur.maxLot === undefined) delete f.symbolLimits[sym];
11358
+ else f.symbolLimits[sym] = cur;
11359
+ }
11360
+ try { saveSettings(); } catch {}
11361
+ const e = f.symbolLimits[sym];
11362
+ const line = `⚙️ SEMBOL LOT LİMİTİ: ${sym} → ${e ? `min ${e.minLot ?? f.minLot} · max ${e.maxLot ?? f.maxLot}` : 'genel aralığa döndü'}${sid ? ' (ajan kararı)' : ''}`;
11363
+ financeLog('[limit] ' + line);
11364
+ finSyncLimitsToSessions({ symbol: sym, note: line });
11365
+ financeNotify(line, 'limit', true);
11366
+ try { finJournal({ kind: 'limits', symbol: sym, minLot: e ? e.minLot : null, maxLot: e ? e.maxLot : null, sid }); } catch {}
11367
+ return { ok: true, symbol: sym, limits: finLimitsSnapshot(), note: notes.length ? notes.join('; ') : undefined };
11368
+ }
11289
11369
  if (patch.maxLot !== undefined) {
11290
11370
  const v = Number(patch.maxLot);
11291
11371
  if (!(v >= 0.01) || v > 100) return { ok: false, error: 'maxLot 0.01-100 arasında olmalı' };
@@ -11322,7 +11402,11 @@ function finLimitsSet(patch, ctx) {
11322
11402
  const FIN_LEARN_MAX_NOTES = 60; /* sembol başına ders tavanı */
11323
11403
  const FIN_LEARN_MAX_TRADES = 60;
11324
11404
  const FIN_LEARN_MAX_SYMBOLS = 60; /* toplam sembol tavanı — depo sınırsız büyümesin */
11325
- const FIN_LEARN_MAX_NOTES_DAY = 12; /* sembol başına 24 saatte en fazla ders — ajan seli depoyu zehirlemesin */
11405
+ const FIN_LEARN_MAX_NOTES_DAY = 3; /* sembol başına 24 saatte en fazla ders — ders SELİ depoyu zehirlemesin */
11406
+ /* DERS KALİTESİ: "günlük tutar gibi" uzun/genel metinler ders DEĞİLDİR —
11407
+ yalnız kısa, somut, tekrar kullanılabilir bilgi kaydedilir. */
11408
+ const FIN_LEARN_NOTE_MIN = 12; /* çok kısa/genel laf (ör. "iyi işlem") ders sayılmaz */
11409
+ const FIN_LEARN_NOTE_MAX = 200; /* tek kısa cümle tavanı (karakter) */
11326
11410
  /* SÜREKLİ UNUTMA (bellek hijyeni): eski kayıtlar otomatik düşer — depo büyüyüp
11327
11411
  zehirlenmez, yalnız GÜNCEL dersler ve son performans kalır. */
11328
11412
  const FIN_LEARN_NOTE_TTL_MS = 30 * 24 * 60 * 60 * 1000; /* 30 gün: eski ders unutulur */
@@ -11354,6 +11438,20 @@ function finLearnNoteKey(text) {
11354
11438
  .slice(0, 140);
11355
11439
  }
11356
11440
 
11441
+ /* DERS KALİTE KAPISI: gerçekten öğrenilmiş, işe yarar bilgi kısa cümleyle;
11442
+ günlük/özet tarzı metin ya da genel laf kaydedilmez. Hata metni ya da null. */
11443
+ function finLearnNoteQuality(text) {
11444
+ const t = String(text || '').replace(/\s+/g, ' ').trim();
11445
+ if (t.length < FIN_LEARN_NOTE_MIN) return 'ders çok kısa — tek cümlede somut ders yaz (ör. "Londra açılışı M5 EMA50 üstü momentum işledi")';
11446
+ if (t.length > FIN_LEARN_NOTE_MAX) return `ders çok uzun (${t.length} karakter) — en fazla ${FIN_LEARN_NOTE_MAX} karakterlik TEK kısa cümle yaz; günlük/rapor metnini ders diye kaydetme`;
11447
+ if (/^(bug[üu]n|g[üu]nl[üu]k|bu sabah|bu ak[şs]am|sabah|ak[şs]am)\b/i.test(t)) return 'günlük tarzı metin ders değildir — yalnız tekrar kullanılabilir somut ders yaz';
11448
+ /* GENEL LAFLAR: kanıtsız/tavsiye cümleleri ders değildir (somut setup/ölçüm iste) */
11449
+ if (/(dikkatli ol|dikkat et|sab[ıi]rl[ıi] ol|panik yapma|duygular[ıi]na kap[ıi]lma|disiplinli ol|temkinli ol)/i.test(t)) {
11450
+ return 'genel tavsiye ders değildir — hangi setup/koşulda ne olduğunu somut yaz (ör. sembol+periyot+seviye+sonuç)';
11451
+ }
11452
+ return null;
11453
+ }
11454
+
11357
11455
  /* sembolün son aktivitesi (kapanış / ders / eski istatistik) — tavan aşımında
11358
11456
  EN ESKİ semboller düşürülür; dokunulan (protectSym) sembol asla atılmaz */
11359
11457
  function finLearnActivity(e) {
@@ -11630,9 +11728,10 @@ function finBuildLearnDigest(symbols) {
11630
11728
  function finLearnApi() {
11631
11729
  const norm = (s) => String(s || '').trim().toUpperCase();
11632
11730
  return {
11633
- list: ({ symbol } = {}) => {
11731
+ list: ({ symbol, limit } = {}) => {
11634
11732
  const learn = finLearnLoad();
11635
11733
  const sym = norm(symbol);
11734
+ const take = Math.max(1, Math.min(25, Math.round(Number(limit) || 8)));
11636
11735
  if (sym) {
11637
11736
  const e = learn.symbols[sym];
11638
11737
  if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats(), summary: null };
@@ -11640,7 +11739,7 @@ function finLearnApi() {
11640
11739
  ok: true,
11641
11740
  symbol: sym,
11642
11741
  count: e.notes.length,
11643
- notes: e.notes.slice(-25),
11742
+ notes: e.notes.slice(-take),
11644
11743
  trades: e.trades.slice(-12),
11645
11744
  stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
11646
11745
  summary: e.summary && e.summary.text ? e.summary : null,
@@ -11658,9 +11757,20 @@ function finLearnApi() {
11658
11757
  return { ok: true, count: rows.length, symbols: rows.slice(0, FIN_LEARN_MAX_SYMBOLS) };
11659
11758
  },
11660
11759
  stats: ({ symbol } = {}) => {
11760
+ /* KOMPAKT OKUMA: istatistik + kalıcı özet + son 3 ders — ajan karar
11761
+ öncesi bunu okur (hafızanın TAMAMI değil, sadece güncel öz) */
11661
11762
  const learn = finLearnLoad();
11662
11763
  const sym = norm(symbol);
11663
- if (sym) return { ok: true, symbol: sym, stats: { ...finLearnEmptyStats(), ...((learn.symbols[sym] || {}).stats || {}) } };
11764
+ if (sym) {
11765
+ const e = learn.symbols[sym] || {};
11766
+ return {
11767
+ ok: true,
11768
+ symbol: sym,
11769
+ stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
11770
+ summary: e.summary && e.summary.text ? e.summary : null,
11771
+ notes: Array.isArray(e.notes) ? e.notes.slice(-3) : [],
11772
+ };
11773
+ }
11664
11774
  const stats = {};
11665
11775
  for (const [s, e] of Object.entries(learn.symbols)) stats[s] = { ...finLearnEmptyStats(), ...(e.stats || {}) };
11666
11776
  return { ok: true, count: Object.keys(stats).length, stats };
@@ -11669,6 +11779,9 @@ function finLearnApi() {
11669
11779
  const sym = norm(symbol);
11670
11780
  const t = String(text || '').replace(/\s+/g, ' ').trim();
11671
11781
  if (!sym || !t) return { ok: false, error: 'symbol ve text gerekli' };
11782
+ /* KALİTE KAPISI: kısa + somut + tekrar kullanılabilir — günlük tutma */
11783
+ const q = finLearnNoteQuality(t);
11784
+ if (q) return { ok: false, error: q, symbol: sym };
11672
11785
  const learn = finLearnLoad();
11673
11786
  const e = finLearnSym(sym);
11674
11787
  if (!e) return { ok: false, error: 'sembol yok' };
@@ -11693,7 +11806,7 @@ function finLearnApi() {
11693
11806
  const note = {
11694
11807
  id: 'l' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
11695
11808
  at: Date.now(),
11696
- text: t.slice(0, 600),
11809
+ text: t.slice(0, FIN_LEARN_NOTE_MAX),
11697
11810
  kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
11698
11811
  tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
11699
11812
  sid: String(sid || ''),
@@ -12157,7 +12270,10 @@ function finApplyTraderFields(s, symbolsOverride, role, isMain) {
12157
12270
  s.financeSymbols = Array.isArray(symbolsOverride) && symbolsOverride.length ? symbolsOverride : f.symbols;
12158
12271
  s.financeStrategy = String(f.strategy || '');
12159
12272
  s.financeShadow = !!f.shadowMode; /* shadow modda işlem araçları emir göndermez */
12160
- s.financeLimits = { minLot: f.minLot, maxLot: f.maxLot, maxPositions: f.maxPositions };
12273
+ {
12274
+ const lim = finLimitsSnapshot();
12275
+ s.financeLimits = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions, symbolLimits: lim.symbols };
12276
+ }
12161
12277
  /* ZORUNLU TEK skill: rol ajanı → roleSkills[rol], ana trader → roleSkills.trader */
12162
12278
  const skillKey = r || (s.financePlaybook ? 'trader' : '');
12163
12279
  s.financeRoleSkills = (skillKey && f.roleSkills && f.roleSkills[skillKey]) || [];
@@ -12258,9 +12374,17 @@ function finTraderBrief(agent) {
12258
12374
  return [
12259
12375
  `Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
12260
12376
  `Odak semboller: ${syms || '(boş — mt5_status ile terminale bak, mantıklı semboller seç)'}`,
12261
- `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}`,
12377
+ `Tur aralığı: ${f.intervalSec} sn · Lot aralığı: ${f.minLot}–${f.maxLot} (genel taban/tavan; sembol bazlı limit daraltır) · Max eşzamanlı pozisyon: ${f.maxPositions}`,
12378
+ (() => {
12379
+ const rows = Object.entries(f.symbolLimits || {}).map(([s, v]) => {
12380
+ const mn = Number(v && v.minLot) > 0 ? Number(v.minLot) : Number(f.minLot);
12381
+ const mx = Number(v && v.maxLot) > 0 ? Number(v.maxLot) : Number(f.maxLot);
12382
+ return `${s} ${mn}–${mx}`;
12383
+ });
12384
+ return rows.length ? `SEMBOL LOT LİMİTLERİN (senin kararın): ${rows.join(' · ')}` : '';
12385
+ })(),
12262
12386
  agent && agent.main
12263
- ? `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).`
12387
+ ? `LİMİT YETKİN: volatilite/performansa göre mt5_limits ile SEN güncellersin (anında geçerli): genel {action:"set", minLot?, maxLot?, maxPositions?}; SEMBOL BAZLI {action:"set", symbol:"XAUUSD", minLot?, maxLot?} — her sembolün min/max lotunu kendi karakterine göre SEN belirle (volatilite, spread, marj); sembolü genele döndürmek için {action:"set", symbol:"XAUUSD", reset:true}.`
12264
12388
  : '',
12265
12389
  roleDef
12266
12390
  ? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
@@ -12273,7 +12397,7 @@ function finTraderBrief(agent) {
12273
12397
  f.shadowMode ? 'SHADOW MOD AÇIK: emir gönderilmez — kararlarını gerekçesiyle raporla, gerçek işlem açılmaz.' : '',
12274
12398
  'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → mt5_rates/mt5_indicators ile teknik okuma → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
12275
12399
  'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
12276
- 'ÖĞ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.',
12400
+ 'ÖĞRENME (SEÇİCİ): yalnız gerçekten öğrenilmiş, tekrar kullanılabilir içgörüyü tek KISA cümleyle mt5_ogrenme {action:"add", symbol, text, kind} ile kaydet (günde en fazla 2-3); her kapanışa ders yazma, günlük/uzun metin kaydetme; yeni karar öncesi mt5_ogrenme {action:"stats"/"list", symbol} ile geçmişi oku.',
12277
12401
  '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.',
12278
12402
  '⚡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.',
12279
12403
  'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
@@ -12359,7 +12483,7 @@ async function finConsultPlan(f, agent, sid) {
12359
12483
  'Güncel piyasa bağlamı gerekiyorsa web_search kullan; kurulu bir skill konuyla ilgiliyse skill aracıyla oku.';
12360
12484
  const ctx = [
12361
12485
  `Odak semboller: ${agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ') || '(boş — ajan kendi bulabilir)'}`,
12362
- `Otomatik işlem: ${auto} · lot aralığı ${f.minLot}–${f.maxLot} · max eşzamanlı pozisyon ${f.maxPositions}`,
12486
+ `Otomatik işlem: ${auto} · lot aralığı ${f.minLot}–${f.maxLot} (genel; sembol bazlı limitler daraltır) · max eşzamanlı pozisyon ${f.maxPositions}`,
12363
12487
  account
12364
12488
  ? `Hesap: bakiye ${account.balance} ${account.currency} · özkaynak ${account.equity ?? '?'} · serbest marj ${account.margin_free ?? '?'} · kaldıraç 1:${account.leverage ?? '?'}`
12365
12489
  : 'Hesap verisi alınamadı.',
@@ -12500,7 +12624,7 @@ function finAgentRound(sid, opts) {
12500
12624
  : 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
12501
12625
  const learnTip = roleDef
12502
12626
  ? ' İlgili sembolün geçmiş derslerini (mt5_ogrenme) oku ve önerine yansıt.'
12503
- : ' 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.' : '.');
12627
+ : ' Yalnız gerçekten öğretici, tekrar kullanılabilir bir içgörü varsa TEK kısa cümleyle mt5_ogrenme add ile kaydet (her kapanışa ders yazma); karar öncesi mt5_ogrenme stats/list ile o sembolün geçmişini oku' + (agent.main ? '; volatiliteye göre mt5_limits ile genel ya da SEMBOL BAZLI (symbol) lot limitlerini güncelleyebilirsin.' : '.');
12504
12628
  const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
12505
12629
  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.`;
12506
12630
  const launch = (planBlock) => {
@@ -12806,6 +12930,8 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
12806
12930
  if (p.minLot !== undefined) f.minLot = Math.max(0.01, Math.min(100, Number(p.minLot) || 0.01));
12807
12931
  /* min lot max lotu aşamaz — hangisi sonra yazıldıysa diğerine kelepçelenir */
12808
12932
  if (f.minLot > f.maxLot) f.minLot = f.maxLot;
12933
+ /* genel aralık değişti: sembol bazlı limitler yeni taban/tavana kelepçelenir */
12934
+ if (p.minLot !== undefined || p.maxLot !== undefined) finNormalizeSymbolLimits(f);
12809
12935
  if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
12810
12936
  /* limit değişikliği koşan oturumlara ANINDA işlenir (sonraki turu beklemez) */
12811
12937
  if (p.minLot !== undefined || p.maxLot !== undefined || p.maxPositions !== undefined) {
@@ -164,7 +164,7 @@
164
164
  </div>
165
165
  </div>
166
166
  <div class="fin-card fin-card-trader" data-fin-card="trader">
167
- <div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span><span class="file-spacer"></span><button id="finSetupBtn" title="MT5 kurulumunu uygula — BeastFinance.mq5 derle + AutoTrading izni + grafiğe ekle">&#x1F6E0;</button><button id="finModelRefreshBtn" title="Modelleri yeniden çek — eksik/güncel modelleri tazeler">&#x27F3;</button><button id="finLearnBtn" title="MT5 öğrenme hafızası — neler öğrendi: sembol bazlı dersler + otomatik istatistik (görüntüle/sil)">&#x1F9E0;</button><button id="finSkillsBtn" title="Rol → skill eşleştirmesi — rol başına tek skill (çöp ikonuyla kaldırılır; boş = ajan kendi seçer)">&#9881;</button><button id="finAdvToggle" type="button" aria-expanded="false" title="Gelişmiş — disiplin kuralları + shadow mod"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M4 6h9"/><circle cx="16.5" cy="6" r="2.5"/><path d="M20 6h0.01"/><path d="M4 12h3"/><circle cx="10.5" cy="12" r="2.5"/><path d="M15 12h5"/><path d="M4 18h11"/><circle cx="18.5" cy="18" r="2.5"/></svg></button></div>
167
+ <div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span><span class="file-spacer"></span><button id="finSetupBtn" title="MT5 kurulumunu uygula — BeastFinance.mq5 derle + AutoTrading izni + grafiğe ekle">&#x1F6E0;</button><button id="finModelRefreshBtn" title="Modelleri yeniden çek — eksik/güncel modelleri tazeler">&#x27F3;</button><button id="finLearnBtn" title="MT5 öğrenme hafızası — neler öğrendi: sembol bazlı dersler + otomatik istatistik (görüntüle/sil)"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/><path d="M17.599 6.5a3 3 0 0 0 .399-1.375"/><path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"/><path d="M3.477 10.896a4 4 0 0 1 .585-.396"/><path d="M19.938 10.5a4 4 0 0 1 .585.396"/><path d="M6 18a4 4 0 0 1-1.967-.516"/><path d="M19.967 17.484A4 4 0 0 1 18 18"/></svg></button><button id="finSkillsBtn" title="Rol → skill eşleştirmesi — rol başına tek skill (çöp ikonuyla kaldırılır; boş = ajan kendi seçer)">&#9881;</button><button id="finAdvToggle" type="button" aria-expanded="false" title="Gelişmiş — disiplin kuralları + shadow mod"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M4 6h9"/><circle cx="16.5" cy="6" r="2.5"/><path d="M20 6h0.01"/><path d="M4 12h3"/><circle cx="10.5" cy="12" r="2.5"/><path d="M15 12h5"/><path d="M4 18h11"/><circle cx="18.5" cy="18" r="2.5"/></svg></button></div>
168
168
  <div class="fin-field">
169
169
  <span>Model</span>
170
170
  <select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select>
@@ -183,6 +183,7 @@
183
183
  <input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" title="Üst sınır: lot hesapları bu tavanın üzerine çıkamaz (min lotun altına inemez)" />
184
184
  </div>
185
185
  </div>
186
+ <div id="finSymLimits" class="fin-hint" hidden title="Trader'ın mt5_limits ile sembole özel koyduğu lot aralıkları — sembol limiti genel aralığı aşamaz; trader sembolü sıfırlarsa genel aralık geçerli olur"></div>
186
187
  <div class="fin-field">
187
188
  <span>Semboller</span>
188
189
  <button id="finSymBtn" class="fin-sym-select" title="MT5 sembol listesinden seç — tıkla">&#8230;</button>
@@ -868,7 +869,7 @@
868
869
  </div>
869
870
  <div class="mini-body">
870
871
  <div id="finLearnList" class="mini-list"></div>
871
- <div class="fin-sym-hint">Trade ajanının çekirdekteki sembol bazlı hafızası: her işlem kapanışı otomatik istatistiğe işlenir, ajan dersleri mt5_ogrenme ile yazar. Sembol satırına tıkla → kalıcı özeti, dersleri ve son işlemleri gör; ÖZET ile eski dersleri modele sıkıştırt (24+ ders birikince otomatik), tek dersi çöp ikonuyla sil, sembol kaydını tümden bırak. Hafıza sürekli sadeleşir: 30 günden eski dersler, 90 günden eski işlem kayıtları ve 120 gün hareketsiz semboller otomatik unutulur.</div>
872
+ <div class="fin-sym-hint">Trade ajanının çekirdekteki sembol bazlı hafızası: her işlem kapanışı otomatik istatistiğe işlenir; ajan yalnız gerçekten öğretici, kısa bir içgörü olunca ders yazar (her kapanışa ders YAZMAZ — günlük en fazla 3, tek kısa cümle). Sembol satırına tıkla → kalıcı özeti, dersleri ve son işlemleri gör; ÖZET ile eski dersleri modele sıkıştırt (24+ ders birikince otomatik), tek dersi çöp ikonuyla sil, sembol kaydını tümden bırak. Hafıza sürekli sadeleşir: 30 günden eski dersler, 90 günden eski işlem kayıtları ve 120 gün hareketsiz semboller otomatik unutulur.</div>
872
873
  </div>
873
874
  </div>
874
875
  </div>
@@ -226,6 +226,7 @@ const els = {
226
226
  finInterval: $('#finInterval'),
227
227
  finMinLot: $('#finMinLot'),
228
228
  finMaxLot: $('#finMaxLot'),
229
+ finSymLimits: $('#finSymLimits'),
229
230
  finStrategy: $('#finStrategy'),
230
231
  finMaxTradesDay: $('#finMaxTradesDay'),
231
232
  finLossStreak: $('#finLossStreak'),
@@ -9821,6 +9822,30 @@ function finSymBtnUpdate() {
9821
9822
  }
9822
9823
  }
9823
9824
 
9825
+ /* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı — mt5_limits symbol ile):
9826
+ genel min/max'tan sapmalar tek satırda gösterilir; trader sıfırlarsa satır kaybolur */
9827
+ function finSymLimitsRender(map) {
9828
+ const box = els.finSymLimits;
9829
+ if (!box) return;
9830
+ const rows = Object.entries(map && typeof map === 'object' ? map : {})
9831
+ .map(([sym, v]) => {
9832
+ const o = v && typeof v === 'object' ? v : {};
9833
+ const min = Number(o.minLot) > 0 ? Number(o.minLot) : 0;
9834
+ const max = Number(o.maxLot) > 0 ? Number(o.maxLot) : 0;
9835
+ if (!min && !max) return '';
9836
+ if (min && max) return sym + ' ' + min + '–' + max;
9837
+ return sym + (min ? ' ≥' + min : ' ≤' + max);
9838
+ })
9839
+ .filter(Boolean);
9840
+ if (!rows.length) {
9841
+ box.hidden = true;
9842
+ box.textContent = '';
9843
+ return;
9844
+ }
9845
+ box.hidden = false;
9846
+ box.textContent = 'Sembol lot limitleri (trader): ' + rows.join(' · ');
9847
+ }
9848
+
9824
9849
  function finTraderInputsSet(cfg) {
9825
9850
  if (!cfg) return;
9826
9851
  if (!finWatchDirty) finWatchSet(cfg.symbols);
@@ -9832,6 +9857,7 @@ function finTraderInputsSet(cfg) {
9832
9857
  if (els.finInterval && ae !== els.finInterval) els.finInterval.value = cfg.intervalSec || 120;
9833
9858
  if (els.finMinLot && ae !== els.finMinLot) els.finMinLot.value = cfg.minLot || 0.01;
9834
9859
  if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
9860
+ finSymLimitsRender(cfg.symbolLimits);
9835
9861
  if (els.finStrategy && ae !== els.finStrategy && !finStrategyDirty) els.finStrategy.value = cfg.strategy || '';
9836
9862
  if (els.finMaxTradesDay && ae !== els.finMaxTradesDay) els.finMaxTradesDay.value = Number(cfg.maxTradesPerDay) || 0;
9837
9863
  if (els.finLossStreak && ae !== els.finLossStreak) els.finLossStreak.value = Number(cfg.lossStreakLimit) || 0;
@@ -10204,14 +10230,17 @@ function finOnEvent(ev) {
10204
10230
  if (ev.minLot !== undefined) finCfgCache.minLot = ev.minLot;
10205
10231
  if (ev.maxLot !== undefined) finCfgCache.maxLot = ev.maxLot;
10206
10232
  if (ev.maxPositions !== undefined) finCfgCache.maxPositions = ev.maxPositions;
10233
+ if (ev.symbols !== undefined) finCfgCache.symbolLimits = ev.symbols;
10207
10234
  finTraderInputsSet(finCfgCache);
10208
10235
  } else {
10209
10236
  const ae2 = document.activeElement;
10210
10237
  if (els.finMinLot && ae2 !== els.finMinLot && ev.minLot !== undefined) els.finMinLot.value = ev.minLot;
10211
10238
  if (els.finMaxLot && ae2 !== els.finMaxLot && ev.maxLot !== undefined) els.finMaxLot.value = ev.maxLot;
10239
+ if (ev.symbols !== undefined) finSymLimitsRender(ev.symbols);
10212
10240
  }
10213
10241
  finLogLine('[limit] ' + (ev.note || ('min lot ' + ev.minLot + ' · max lot ' + ev.maxLot + ' · max pozisyon ' + ev.maxPositions)));
10214
- toast('Trader limitleri güncelledi: min ' + ev.minLot + ' / max ' + ev.maxLot);
10242
+ if (ev.symbol) toast('Sembol lot limiti güncellendi: ' + ev.symbol);
10243
+ else toast('Trader limitleri güncelledi: min ' + ev.minLot + ' / max ' + ev.maxLot);
10215
10244
  } else if (ev.fn === 'install') {
10216
10245
  finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
10217
10246
  }
@@ -11051,7 +11080,7 @@ if (els.finSkillsOverlay) {
11051
11080
  Çekirdekteki (main process) sembol bazlı öğrenme deposunu gösterir:
11052
11081
  otomatik istatistik + ajanın mt5_ogrenme ile yazdığı dersler. Tek ders
11053
11082
  silinebilir, sembol kaydı tümden bırakılabilir; depo tavanları
11054
- (60 sembol / 120 ders / 60 işlem / günlük 12 ders) çekirdekte uygulanır. */
11083
+ (60 sembol / 60 ders / 60 işlem / günlük 3 ders; ders = kısa+somut) çekirdekte uygulanır. */
11055
11084
  const FIN_LEARN_TRASH_SVG =
11056
11085
  '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>';
11057
11086
 
@@ -11211,7 +11240,7 @@ function finLearnSymEl(row) {
11211
11240
  body.hidden = false;
11212
11241
  finLearnOpenSym = sym;
11213
11242
  body.innerHTML = '<div class="fin-empty">yükleniyor…</div>';
11214
- const d = await finLearnCall({ action: 'list', symbol: sym });
11243
+ const d = await finLearnCall({ action: 'list', symbol: sym, limit: 25 });
11215
11244
  if (!els.finLearnOverlay || els.finLearnOverlay.hidden) { busy = false; return; }
11216
11245
  finLearnDetail(body, sym, d);
11217
11246
  loaded = true;
@@ -11266,7 +11295,7 @@ function finLearnDetail(body, sym, d) {
11266
11295
  if (!notes.length) {
11267
11296
  const e = document.createElement('div');
11268
11297
  e.className = 'fin-empty';
11269
- e.textContent = 'Ders yok — ajan her kapanıştan sonra mt5_ogrenme ile buraya yazar.';
11298
+ e.textContent = 'Ders yok — ajan yalnız gerçekten öğretici, kısa bir içgörü olunca mt5_ogrenme ile buraya yazar (her kapanışa ders yazmaz).';
11270
11299
  body.appendChild(e);
11271
11300
  } else {
11272
11301
  for (const n of notes.slice().reverse()) {