beast-agent 2.73.0 → 2.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.73.0",
4
+ "version": "2.74.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/main.js CHANGED
@@ -10779,17 +10779,36 @@ function finEquitySample(account) {
10779
10779
  function finDailyLossCheck(point) {
10780
10780
  const cfg = finCfg();
10781
10781
  const day = finstats.dayKey(point.at);
10782
+ /* TALİMAT: "gün başı bakiye" yazıldıysa taban O'dur; "günlük zarar %X"
10783
+ yazıldıysa limit ayarın yerine talimattan gelir (kullanıcı her gün günceller). */
10784
+ let startBal = 0;
10785
+ let limitPct = Number(cfg.maxDailyLossPct) || 0;
10786
+ try {
10787
+ const ins = finParsedInstr();
10788
+ if (Number(ins.entry.startBalance) > 0) startBal = Number(ins.entry.startBalance);
10789
+ if (Number(ins.entry.dailyLossPct) > 0) limitPct = Number(ins.entry.dailyLossPct);
10790
+ } catch {}
10782
10791
  let ds = financeState.dayStart;
10783
10792
  if (!ds || ds.day !== day) {
10784
- financeState.dayStart = ds = { day, equity: Number(point.balance) > 0 ? Number(point.balance) : point.equity, warned: false, acted: false };
10793
+ financeState.dayStart = ds = {
10794
+ day,
10795
+ equity: startBal > 0 ? startBal : Number(point.balance) > 0 ? Number(point.balance) : point.equity,
10796
+ warned: false,
10797
+ acted: false,
10798
+ fromNote: startBal > 0,
10799
+ };
10785
10800
  }
10786
- if (ds.warned || !(Number(cfg.maxDailyLossPct) > 0) || !(ds.equity > 0)) return;
10801
+ if (startBal > 0) {
10802
+ ds.equity = startBal;
10803
+ ds.fromNote = true;
10804
+ }
10805
+ if (ds.warned || !(limitPct > 0) || !(ds.equity > 0)) return;
10787
10806
  const dd = ((ds.equity - point.equity) / ds.equity) * 100;
10788
- if (dd >= Number(cfg.maxDailyLossPct)) {
10807
+ if (dd >= limitPct) {
10789
10808
  ds.warned = true;
10790
10809
  const action = String(cfg.dailyLossAction || 'stop');
10791
10810
  const tail = action === 'flatten' ? ' — ajanlar durduruldu, pozisyonlar kapatılıyor' : action === 'stop' ? ' — ajanlar durduruldu' : '';
10792
- const line = `⚠️ Günlük kayıp %${dd.toFixed(2)} (limit %${cfg.maxDailyLossPct})${tail}`;
10811
+ const line = `⚠️ Günlük kayıp %${dd.toFixed(2)} (limit %${limitPct}${ds.fromNote ? ' · talimat gün başı bakiye' : ''})${tail}`;
10793
10812
  financeLog('[risk] ' + line);
10794
10813
  financeNotify(line, 'drawdown');
10795
10814
  finJournal({ kind: 'drawdown', pct: Math.round(dd * 100) / 100, action });
@@ -13698,6 +13717,115 @@ function finTsStrategyText() {
13698
13717
  }
13699
13718
  }
13700
13719
 
13720
+ /* ---- TALİMAT AYRIŞTIRICI ----
13721
+ Trade Ajanı + Pozisyon Yöneticisi notlarındaki SAYISAL kuralları koda çevirir:
13722
+ "risk %2" → giriş riski %2
13723
+ "lot x1.5" / "poz başı lot 1.5" / "lotu 2 kat" → lot çarpanı
13724
+ "martingale" / "martingale 1.5" → kayıp serisinde lot katlama (varsayılan ×2)
13725
+ "1R'de %50 kısmi kapat" / "%50 1R" / "kısmi %50" → otomatik kısmi close kuralı
13726
+ Saf metin ayrıştırma; uygulama ilgili turda kodla yapılır (LLM yok). */
13727
+ function finInstrNum(v) {
13728
+ const n = Number(String(v == null ? '' : v).replace(',', '.'));
13729
+ return isFinite(n) ? n : null;
13730
+ }
13731
+
13732
+ /* Para değeri: "10.000" / "10,000" → 10000; "10000,50" / "10000.50" → 10000.5 */
13733
+ function finInstrMoney(v) {
13734
+ const s = String(v == null ? '' : v).replace(/\s+/g, '');
13735
+ if (!s) return null;
13736
+ /* "5.000,50" / "5,000.50" → 5000.50 ; "10.000" → 10000 ; "10000,50" → 10000.5 */
13737
+ const m = s.match(/^(\d{1,3}(?:[.,]\d{3})+)(?:[.,](\d{1,2}))?$/);
13738
+ if (m) {
13739
+ const n = Number(m[1].replace(/[.,]/g, '') + (m[2] ? '.' + m[2] : ''));
13740
+ return isFinite(n) && n > 0 ? n : null;
13741
+ }
13742
+ const n2 = Number(s.replace(',', '.'));
13743
+ return isFinite(n2) && n2 > 0 ? n2 : null;
13744
+ }
13745
+
13746
+ function finParseInstructions(text) {
13747
+ const low = String(text || '').toLowerCase();
13748
+ const out = { riskPct: null, lotMult: null, martingale: null, partial: null, startBalance: null, dailyLossPct: null };
13749
+ if (!low.trim()) return out;
13750
+ /* "risk %2", "%2 risk", "risk yüzde 2", "riski 2 yap" → 2 (ilk sayı) */
13751
+ let m = low.match(
13752
+ /(?:risk\s*%\s*(\d+(?:[.,]\d+)?))|(?:%\s*(\d+(?:[.,]\d+)?)\s*risk)|(?:risk[^0-9]{0,14}(\d+(?:[.,]\d+)?))/
13753
+ );
13754
+ if (m) out.riskPct = finInstrNum(m[1] || m[2] || m[3]);
13755
+ if (out.riskPct != null) out.riskPct = Math.max(0.1, Math.min(10, out.riskPct));
13756
+ /* GÜN BAŞLANGIÇ BAKİYESİ (kullanıcı her gün talimata yazar): "başlangıç bakiye 10.000",
13757
+ "gün başı: 10000", "starting balance 10000" */
13758
+ m = low.match(/(?:ba[şs]lang[ıi][çc]\s*bakiye|g[üu]n\s*ba[şs][ıi](?:\s*bakiye)?|g[üu]nl[üu]k\s*ba[şs]lang[ıi][çc](?:\s*bakiye)?|starting\s*balance|start\s*balance)[^0-9]{0,14}(\d[\d.,\s]*)/);
13759
+ if (m) out.startBalance = finInstrMoney(m[1]);
13760
+ /* GÜNLÜK ZARAR LİMİTİ: "günlük zarar %3", "max günlük kayıp 3", "daily loss %3" */
13761
+ m = low.match(/(?:g[üu]nl[üu]k\s*(?:max(?:imum)?\s*)?(?:zarar|kay[ıi]p)|max\s*g[üu]nl[üu]k\s*kay[ıi]p|g[üu]nl[üu]k\s*limit|daily\s*(?:max\s*)?loss)[^0-9%]{0,12}%?\s*(\d+(?:[.,]\d+)?)/);
13762
+ if (m) {
13763
+ const v = finInstrNum(m[1]);
13764
+ if (v != null && v > 0) out.dailyLossPct = Math.max(0.1, Math.min(50, v));
13765
+ }
13766
+ m = low.match(/(?:lot\s*(?:çarpan[ıi]?|carpan[ıi]?)?\s*(?:x|×|\*)\s*(\d+(?:[.,]\d+)?))|(?:poz\s*ba[şs][ıi]\s*lot[^0-9]{0,12}(\d+(?:[.,]\d+)?))|(?:lot[^0-9]{0,12}(\d+(?:[.,]\d+)?)\s*kat)/);
13767
+ if (m) out.lotMult = finInstrNum(m[1] || m[2] || m[3]);
13768
+ if (out.lotMult != null) out.lotMult = Math.max(1, Math.min(5, out.lotMult));
13769
+ if (/martingale|mart[ıi]ngale/.test(low)) {
13770
+ /* faktör yalnız bitişik yazımdan okunur: "martingale 1.5", "martingale x2",
13771
+ "martingale çarpanı 1.5" — "martingale kullan, 1R..." gibi metinden sayı kapmaz */
13772
+ m = low.match(/mart[ıi]ngale\s*(?:x|×|çarpan[ıi]?|carpan[ıi]?)?\s*(\d+(?:[.,]\d+)?)/);
13773
+ out.martingale = m ? Math.max(1.1, Math.min(5, finInstrNum(m[1]) || 2)) : 2;
13774
+ }
13775
+ /* kısmi kapatma: "1R'de %50" → atR=1, pct=50; "%50 1R" → aynı; "kısmi %50" → atR=1 */
13776
+ m = low.match(/(\d+(?:[.,]\d+)?)\s*r[^%\d]{0,28}%\s*(\d+(?:[.,]\d+)?)/);
13777
+ if (m) out.partial = { atR: finInstrNum(m[1]), pct: finInstrNum(m[2]) };
13778
+ if (!out.partial) {
13779
+ m = low.match(/%\s*(\d+(?:[.,]\d+)?)[^%\d]{0,28}?(\d+(?:[.,]\d+)?)\s*r/);
13780
+ if (m) out.partial = { atR: finInstrNum(m[2]), pct: finInstrNum(m[1]) };
13781
+ }
13782
+ if (!out.partial) {
13783
+ m = low.match(/(?:k[ıi]smi|partial)[^%\d]{0,24}%\s*(\d+(?:[.,]\d+)?)/);
13784
+ if (m) out.partial = { atR: 1, pct: finInstrNum(m[1]) };
13785
+ }
13786
+ if (out.partial) {
13787
+ const p = out.partial;
13788
+ if (!(p.atR > 0)) p.atR = 1;
13789
+ p.atR = Math.min(10, Math.round(p.atR * 100) / 100);
13790
+ if (!(p.pct >= 5 && p.pct <= 90)) out.partial = null;
13791
+ else p.pct = Math.round(p.pct);
13792
+ }
13793
+ return out;
13794
+ }
13795
+
13796
+ /* İki notu birleştir: GİRİŞ kuralları trade notundan, YÖNETİM kuralları
13797
+ pozisyon yöneticisi notundan (yoksa trade notundan) gelir. */
13798
+ function finParsedInstr() {
13799
+ const trade = finParseInstructions(finTsStrategyText());
13800
+ const mgr = finParseInstructions(String(finCfg().posManagerNote || ''));
13801
+ return {
13802
+ entry: {
13803
+ riskPct: trade.riskPct != null ? trade.riskPct : mgr.riskPct,
13804
+ lotMult: trade.lotMult != null ? trade.lotMult : mgr.lotMult,
13805
+ martingale: trade.martingale != null ? trade.martingale : mgr.martingale,
13806
+ startBalance: trade.startBalance != null ? trade.startBalance : mgr.startBalance,
13807
+ dailyLossPct: trade.dailyLossPct != null ? trade.dailyLossPct : mgr.dailyLossPct,
13808
+ },
13809
+ manage: { partial: mgr.partial || trade.partial },
13810
+ };
13811
+ }
13812
+
13813
+ /* Rapor/state için tek satır özet: kod ne uygulayacak */
13814
+ function finInstrSummary(ins) {
13815
+ try {
13816
+ const p = [];
13817
+ if (ins.entry.riskPct != null) p.push(`risk %${ins.entry.riskPct}`);
13818
+ if (ins.entry.lotMult != null) p.push(`lot ×${ins.entry.lotMult}`);
13819
+ if (ins.entry.martingale != null) p.push(`martingale ×${ins.entry.martingale}`);
13820
+ if (ins.entry.startBalance != null) p.push(`gün başı ${ins.entry.startBalance}`);
13821
+ if (ins.entry.dailyLossPct != null) p.push(`günlük zarar limiti %${ins.entry.dailyLossPct}`);
13822
+ if (ins.manage.partial) p.push(`kısmi %${ins.manage.partial.pct} @${ins.manage.partial.atR}R`);
13823
+ return p.join(' · ');
13824
+ } catch {
13825
+ return '';
13826
+ }
13827
+ }
13828
+
13701
13829
  /* Talimattan ZAMAN DİLİMİ çıkar: "M15", "1m", "1 dk", "4 saat" → M15/M1/H4 */
13702
13830
  function finTsStrategyTf() {
13703
13831
  const s = String(finTsStrategyText() || '');
@@ -14079,6 +14207,9 @@ async function finPosManagerRound(positions, account) {
14079
14207
  pm.rounds = (Number(pm.rounds) || 0) + 1;
14080
14208
  pm.lastAt = Date.now();
14081
14209
  const f = finCfg();
14210
+ /* TALİMAT: kısmi close kuralı varsa KOD uygular (Jev'e sorulmaz) */
14211
+ const ins = finParsedInstr();
14212
+ const instrTxt = finInstrSummary(ins);
14082
14213
  const lines = [];
14083
14214
  try {
14084
14215
  /* pozisyon sembolleri: piyasa + gösterge (tur başına tek Jev çağrısı) */
@@ -14106,6 +14237,18 @@ async function finPosManagerRound(positions, account) {
14106
14237
  piyasa: {},
14107
14238
  alarmlar: (financeState.alerts || []).slice(0, 8).map((a) => ({ sembol: a.symbol, yon: a.direction, fiyat: a.price, mod: a.once ? 'tek' : 'tekrarlı' })),
14108
14239
  sahip_talimati: String(f.posManagerNote || '').slice(0, 1500) || '(yok — genel disiplin: kârı koru, zararı sınırla, SL/TP aktif yönet)',
14240
+ talimat_ayarlari: instrTxt || '(sayısal kural yok)',
14241
+ gun_durumu: (() => {
14242
+ try {
14243
+ const sb = Number(ins.entry.startBalance) || 0;
14244
+ const eqNow = Number(account && account.equity) || 0;
14245
+ if (!(sb > 0) || !(eqNow > 0)) return '';
14246
+ const pct = Math.round(((eqNow - sb) / sb) * 10000) / 100;
14247
+ return `Gün başı ${sb} → %${pct >= 0 ? '+' : ''}${pct} (${pct >= 0 ? 'KÂR' : 'ZARAR'})`;
14248
+ } catch {
14249
+ return '';
14250
+ }
14251
+ })(),
14109
14252
  ekip_yanitlari: finTsFeedText(6),
14110
14253
  };
14111
14254
  for (const sym of syms) {
@@ -14131,16 +14274,19 @@ async function finPosManagerRound(positions, account) {
14131
14274
  instructions: `${sym} açık pozisyon #${pp.ticket} (${ps.yon}, kâr ${Number(pp.profit) || 0}, ${kzTxt}${bestTxt}, ${slTxt}) ŞİMDİ kapatılmalı mı? Sahip talimatını, kârı korumayı ve momentum dönüşünü değerlendir.`,
14132
14275
  criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
14133
14276
  };
14134
- questions['kismi_' + i] = {
14135
- type: 'choice',
14136
- instructions: `${sym} #${pp.ticket} (${kzTxt}${bestTxt}${ps.be ? ' · SL takipte' : ''}) için ŞİMDİ kısmi kapatma uygun mu?${ps.kismi_alindi ? ' Bu pozisyonda kısmi kapatma ZATEN yapıldı — yok seç.' : ' Sahip talimatını ve kâr realize etmeyi değerlendir.'}`,
14137
- criteria: {
14138
- yok: 'Kısmi kapatma yok — pozisyon tam kalsın',
14139
- yuzde25: 'Pozisyonun %25’i kapatılsın',
14140
- yuzde50: 'Pozisyonun %50’si kapatılsın',
14141
- yuzde75: 'Pozisyonun %75’i kapatılsın',
14142
- },
14143
- };
14277
+ /* Talimatta SAYISAL kısmi kuralı varsa Jev'e sorulmaz — kodu uygular */
14278
+ if (!ins.manage.partial) {
14279
+ questions['kismi_' + i] = {
14280
+ type: 'choice',
14281
+ instructions: `${sym} #${pp.ticket} (${kzTxt}${bestTxt}${ps.be ? ' · SL takipte' : ''}) için ŞİMDİ kısmi kapatma uygun mu?${ps.kismi_alindi ? ' Bu pozisyonda kısmi kapatma ZATEN yapıldı — yok seç.' : ' Sahip talimatını ve kâr realize etmeyi değerlendir.'}`,
14282
+ criteria: {
14283
+ yok: 'Kısmi kapatma yok — pozisyon tam kalsın',
14284
+ yuzde25: 'Pozisyonun %25’i kapatılsın',
14285
+ yuzde50: 'Pozisyonun %50’si kapatılsın',
14286
+ yuzde75: 'Pozisyonun %75’i kapatılsın',
14287
+ },
14288
+ };
14289
+ }
14144
14290
  questions['sl_' + i] = {
14145
14291
  type: 'choice',
14146
14292
  instructions: `${sym} #${pp.ticket} için SL/koruma aksiyonu: ${ps.yon}, giriş ${Number(pp.price_open) || '?'}, şimdi ${ps.fiyat || '?'}, ${slTxt}, ${kzTxt}. Kâr yoksa ve SL varsa 'birak'; SL yoksa 'koru' ile koruma koy.`,
@@ -14173,12 +14319,39 @@ async function finPosManagerRound(positions, account) {
14173
14319
  }
14174
14320
  const psNow = finTsPosState(pp);
14175
14321
  lines.push(`- ${sym} #${pp.ticket}: açık (kapat=${ex == null ? 'yanıt yok' : ex.toFixed(2)} < ${FIN_TS_EXIT_P}) · ${psNow.kz_r == null ? 'kâr ?' : 'kâr ' + psNow.kz_r + 'R'}`);
14322
+ /* TALİMAT KISMİ KURALI (kod): "+atR'de %pct kapat" → otomatik uygula */
14323
+ const rule = ins.manage.partial;
14324
+ if (rule) {
14325
+ const stP = financeState.watch.get(String(pp.ticket)) || null;
14326
+ const done = !!(stP && stP.partial) || !!(pm.tsPartials && pm.tsPartials.has(String(pp.ticket)));
14327
+ if (!done && psNow.kz_r != null && psNow.kz_r >= rule.atR) {
14328
+ let rr = null;
14329
+ try {
14330
+ rr = await financetools.handlers.mt5_close(
14331
+ { ticket: Number(pp.ticket), percent: rule.pct, kind: 'partial_tp', reason: `TypeSafe yönetici: TALİMAT kısmi %${rule.pct} @${rule.atR}R (kâr ${psNow.kz_r}R)` },
14332
+ { sessionId: FIN_POSMGR_ID }
14333
+ );
14334
+ } catch (e) {
14335
+ rr = { ok: false, error: String((e && e.message) || e) };
14336
+ }
14337
+ if (rr && rr.ok) {
14338
+ if (stP) { stP.partial = true; finWatchSave(); }
14339
+ pm.tsPartials = pm.tsPartials || new Set();
14340
+ pm.tsPartials.add(String(pp.ticket));
14341
+ lines.push(`- ${sym} #${pp.ticket}: ✂️ TALİMAT kısmi %${rule.pct} TP ✓ (${psNow.kz_r}R ≥ ${rule.atR}R${rr.remaining != null ? ', kalan ' + rr.remaining + ' lot' : ''})`);
14342
+ } else {
14343
+ lines.push(`- ${sym} #${pp.ticket}: talimat kısmi reddedildi — ${(rr && rr.error) || 'hata'}`);
14344
+ }
14345
+ }
14346
+ }
14176
14347
  try { await finTsManageOpen(FIN_POSMGR_ID, pm, sym, pp, market[sym], ans, i, lines); } catch {}
14177
14348
  }
14178
14349
  finTsPost(
14179
14350
  FIN_POSMGR_ID,
14180
14351
  FIN_POSMGR_AGENT,
14181
- `🛡️ Pozisyon yöneticisi turu #${pm.rounds} (LLM yok — 5 sn):\n` + lines.join('\n'),
14352
+ `🛡️ Pozisyon yöneticisi turu #${pm.rounds} (LLM yok — 5 sn):\n` +
14353
+ (instrTxt ? 'TALİMAT AYARLARI (kod uygular): ' + instrTxt + '\n' : '') +
14354
+ lines.join('\n'),
14182
14355
  'yönetim'
14183
14356
  );
14184
14357
  } catch (e) {
@@ -14265,6 +14438,25 @@ async function finTypeSafeRound(sid, agent) {
14265
14438
  const posList = (posR && posR.positions) || [];
14266
14439
  /* BEKLEYEN EMİRLER: pozisyon slotunu paylaşır (aşırı birikmeyi önler) */
14267
14440
  const pendCount = (ordR && Array.isArray(ordR.orders) ? ordR.orders.length : 0);
14441
+ /* TALİMAT AYARLARI: sayısal kurallar koda çevrildi (risk/lot/martingale/
14442
+ gün başı bakiye/günlük zarar limiti) — state ve giriş kapısı kullanır */
14443
+ const ins = finParsedInstr();
14444
+ /* GÜN BAŞI BAKİYE → günlük K/Z ve limit durumu (hesap anlık değerinden) */
14445
+ let dayPct = null;
14446
+ let dayBlocked = '';
14447
+ try {
14448
+ const sb = Number(ins.entry.startBalance) || 0;
14449
+ const eqNow = Number(account.equity) || 0;
14450
+ const balNow = Number(account.balance) || 0;
14451
+ const refNow = eqNow > 0 ? eqNow : balNow;
14452
+ if (sb > 0 && refNow > 0) {
14453
+ dayPct = Math.round(((refNow - sb) / sb) * 10000) / 100;
14454
+ const lim = Number(ins.entry.dailyLossPct) || 0;
14455
+ if (lim > 0 && dayPct <= -lim) {
14456
+ dayBlocked = `günlük zarar limiti AŞILDI (gün başı ${sb} → ${refNow}, %${dayPct} ≤ -%${lim}) — talimat gereği yeni işlem açılmaz`;
14457
+ }
14458
+ }
14459
+ } catch {}
14268
14460
  const market = {};
14269
14461
  for (const sym of symbols) {
14270
14462
  const tf = finTsPickTf(agent, sym);
@@ -14305,6 +14497,16 @@ async function finTypeSafeRound(sid, agent) {
14305
14497
  talimat "zorunlu giriş" içeriyorsa kod eşikleri devre dışı bırakır. */
14306
14498
  sahip_talimati: finTsStrategyText() || '(yok — temel teknik okuma)',
14307
14499
  talimat_modu: finTsMandatoryEntry() ? 'ZORUNLU GİRİŞ — bu turda işlem açmak zorunlu (bekle yok); yönü sen seç' : '',
14500
+ talimat_ayarlari: finInstrSummary(ins) || '(sayısal kural yok)',
14501
+ /* GÜN BAŞI BAKİYE (talimattan): günlük K/Z — Jev zarar durumunu otomatik görür */
14502
+ gun_baslangic_bakiye: ins.entry.startBalance != null ? ins.entry.startBalance : null,
14503
+ gun_net_yuzde: dayPct,
14504
+ gun_durumu: dayBlocked
14505
+ ? dayBlocked
14506
+ : ins.entry.startBalance != null && dayPct != null
14507
+ ? `Gün başı ${ins.entry.startBalance} → şimdi %${dayPct >= 0 ? '+' : ''}${dayPct} (${dayPct >= 0 ? 'KÂR' : 'ZARAR'})` +
14508
+ (ins.entry.dailyLossPct != null ? ` · limit -%${ins.entry.dailyLossPct}` : '')
14509
+ : '',
14308
14510
  };
14309
14511
  for (const sym of symbols) {
14310
14512
  const d = market[sym];
@@ -14329,6 +14531,7 @@ async function finTypeSafeRound(sid, agent) {
14329
14531
  const talimatZorunlu = finTsMandatoryEntry();
14330
14532
  /* Yönetici açıkken açık pozisyon kararları ONA aittir (çift yönetim yok) */
14331
14533
  const posManagerOn = f.posManagerEnabled !== false;
14534
+ /* TALİMAT AYARLARI: yukarıda (state kurulmadan) ayrıştırıldı */
14332
14535
  symbols.forEach((sym, i) => {
14333
14536
  questions['yon_' + i] = {
14334
14537
  type: 'choice',
@@ -14491,8 +14694,16 @@ async function finTypeSafeRound(sid, agent) {
14491
14694
  continue;
14492
14695
  }
14493
14696
  }
14494
- if (opened >= 1 || posList.length + pendCount >= (Number(f.maxPositions) || 3)) {
14495
- lines.push(`- ${sym}: sinyal güçlü (${c.choice} p=${c.p.toFixed(2)}) ama pozisyon+bekleyen emir sınırı dolu — bu tur açılmadı`);
14697
+ /* GÜN BAŞI BAKİYE LİMİTİ (talimat): aşıldıysa YENİ İŞLEM AÇILMAZ */
14698
+ if (dayBlocked) {
14699
+ lines.push(`- ${sym}: ${dayBlocked}`);
14700
+ continue;
14701
+ }
14702
+ /* ÇOKLU GİRİŞ: aynı turda birden çok sembol açılabilir — yalnız toplam
14703
+ slot (pozisyon + bekleyen emir + bu turda açılanlar) tavanı korur */
14704
+ const slotsLeft = (Number(f.maxPositions) || 3) - pendCount - posList.length - opened;
14705
+ if (slotsLeft <= 0) {
14706
+ lines.push(`- ${sym}: sinyal güçlü (${c.choice} p=${c.p.toFixed(2)}) ama pozisyon+bekleyen emir sınırı dolu (${posList.length + pendCount}/${Number(f.maxPositions) || 3}) — bu tur açılmadı`);
14496
14707
  continue;
14497
14708
  }
14498
14709
  /* GİRİŞ TİPİ + RİSK PROFİLİ (JEV kararı) — seviyeleri KOD hesaplar */
@@ -14535,8 +14746,29 @@ async function finTypeSafeRound(sid, agent) {
14535
14746
  }
14536
14747
  const sl = rnd(isBuy ? entry - slDist : entry + slDist);
14537
14748
  const tp = rnd(isBuy ? entry + tpDist : entry - tpDist);
14749
+ /* RİSK + LOT: talimatta "risk %2" varsa AYNEN uygulanır; yoksa ayar ×
14750
+ kalibrasyon. Lot çarpanı ve MARTİNGALE (kayıp serisi × kat) burada. */
14538
14751
  const riskBase = Number(f.riskPerTradePct) > 0 ? Number(f.riskPerTradePct) : 0.5;
14539
- const riskPct = Math.max(0.1, Math.min(2, Math.round(riskBase * cal.riskMult * 100) / 100));
14752
+ let riskPct = ins.entry.riskPct != null
14753
+ ? ins.entry.riskPct
14754
+ : Math.max(0.1, Math.min(2, Math.round(riskBase * cal.riskMult * 100) / 100));
14755
+ let sizeMult = 1;
14756
+ let martNote = '';
14757
+ if (ins.entry.lotMult != null) sizeMult *= ins.entry.lotMult;
14758
+ if (ins.entry.martingale != null) {
14759
+ let streak = 0;
14760
+ try {
14761
+ const le = finLearnLoad().symbols[sym];
14762
+ streak = Number(le && le.stats && le.stats.streak) || 0;
14763
+ } catch {}
14764
+ const pow = Math.min(3, Math.max(0, Math.round(streak)));
14765
+ if (pow > 0) {
14766
+ sizeMult *= Math.pow(ins.entry.martingale, pow);
14767
+ martNote = ` · martingale ×${ins.entry.martingale}^${pow}`;
14768
+ }
14769
+ }
14770
+ if (sizeMult !== 1) riskPct = Math.max(0.1, Math.min(10, Math.round(riskPct * sizeMult * 100) / 100));
14771
+ const riskNote = ins.entry.riskPct != null ? ' · TALİMAT risk %' + ins.entry.riskPct : '';
14540
14772
  const emirLabel =
14541
14773
  orderType === 'market' ? 'MARKET' :
14542
14774
  isBuy && orderType === 'buy_limit' ? 'BUY LIMIT' :
@@ -14584,10 +14816,11 @@ async function finTypeSafeRound(sid, agent) {
14584
14816
  orderType,
14585
14817
  });
14586
14818
  } catch {}
14819
+ const riskTxt = `risk %${riskPct}${ins.entry.riskPct == null && cal.riskMult !== 1 ? ` ×${cal.riskMult}` : ''}${riskNote}${martNote}`;
14587
14820
  lines.push(
14588
14821
  (orderType === 'market'
14589
- ? `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, risk %${riskPct}${cal.riskMult !== 1 ? ` ×${cal.riskMult}` : ''})`
14590
- : `- ${sym}: ⏳ ${emirLabel} emri kondu @${entry} (${profKey} profil, SL ${sl}, TP ${tp}, risk %${riskPct})`) +
14822
+ ? `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, ${riskTxt})`
14823
+ : `- ${sym}: ⏳ ${emirLabel} emri kondu @${entry} (${profKey} profil, SL ${sl}, TP ${tp}, ${riskTxt})`) +
14591
14824
  ` · p=${c.p.toFixed(2)} teyit=${confVal.toFixed(2)} · tf=${market[sym].tf}` +
14592
14825
  (talimatZorunlu ? ' · TALİMAT: zorunlu giriş' : '') +
14593
14826
  (thAction !== FIN_TS_DEFAULT_TH.action ? ` · öğrenilmiş eşik p≥${thAction.toFixed(2)}` : '')
@@ -14600,10 +14833,12 @@ async function finTypeSafeRound(sid, agent) {
14600
14833
  }
14601
14834
  /* JEV ARAÇ İSTEĞİ (tur düzeyi): eksik araç varsa TOOL botuna asenkron yazdır */
14602
14835
  try { await finTsToolRequest(sidS, agent, ans, lines); } catch {}
14836
+ const insTxt = finInstrSummary(ins);
14603
14837
  finTsPost(
14604
14838
  sidS,
14605
14839
  agent,
14606
14840
  `🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
14841
+ (insTxt ? 'TALİMAT AYARLARI (kod uygular): ' + insTxt + '\n' : '') +
14607
14842
  'TF KARARI: ' + symbols.map((s) => `${s}=${market[s].tf}`).join(', ') + '\n' +
14608
14843
  'ÖĞRENİLMİŞ: ' +
14609
14844
  symbols
@@ -193,8 +193,8 @@
193
193
  <div id="finRoles" class="fin-roles" title="Trader&#39;ın yanında koşacak uzman ajanları seç — Risk / Teknik / Haber / Görsel; seçilen her rol AYRI sürekli ajan olarak koşar (işlem açmaz)"></div>
194
194
  </div>
195
195
  <div class="fin-field">
196
- <span>Ajan talimatı <i class="fin-hint">— 1-2 cümle; trader + tüm paralel ajanlara iletilir</i></span>
197
- <textarea id="finStrategy" rows="2" maxlength="2000" placeholder="örn. Her türde scalp yap — M1/M5 hızlı gir-çık, günlük kâr hedefine ulaşınca dur" title="Buraya yazdığın talimat trade ajanına ve koşan tüm paralel ajanlara (ekip + sembol işçileri) sistem promptunda öncelikli strateji notu olarak gider; kaydetmek için alandan çık"></textarea>
196
+ <span>Ajan talimatı <i class="fin-hint">— 1-2 cümle; trader + tüm paralel ajanlara iletilir</i><button id="finStrategyHelp" class="fin-help-btn" type="button" title="Neler yazabilirim? Desteklenen talimatlar ve örnekler">?</button></span>
197
+ <textarea id="finStrategy" rows="2" maxlength="2000" placeholder="örn. 1- risk yüzde 2 2- martingale kullan 3- her mumda işlem açmak zorundasın" title="Buraya yazdığın talimat trade ajanına ve koşan tüm paralel ajanlara (ekip + sembol işçileri) sistem promptunda öncelikli strateji notu olarak gider; kaydetmek için alandan çık"></textarea>
198
198
  </div>
199
199
  <div id="finAdvWrap" hidden>
200
200
  <div class="fin-field">
@@ -227,8 +227,8 @@
227
227
  </label>
228
228
  </div>
229
229
  <div class="fin-field">
230
- <span>Yönetici talimatı <i class="fin-hint">— yalnız bu ajanı bağlar</i></span>
231
- <textarea id="finPosNote" rows="2" maxlength="1500" placeholder="örn. Kâr 1R'ye gelince %50 kısmi al ve SL'yi breakeven'a çek; 2R'de kalanı kapat; zarar -0.5R'yi geçerse kes" title="Bu talimat YALNIZ Pozisyon Yöneticisi Jev ajanına gider (5 sn turu, kapat/kısmi/SL kararları); kaydetmek için alandan çık"></textarea>
230
+ <span>Yönetici talimatı <i class="fin-hint">— yalnız bu ajanı bağlar</i><button id="finPosHelp" class="fin-help-btn" type="button" title="Neler yazabilirim? Desteklenen talimatlar ve örnekler">?</button></span>
231
+ <textarea id="finPosNote" rows="2" maxlength="1500" placeholder="örn. 1- 1R'de %50 kısmi kapat 2- kâr 2R'ye gelince kalanı kapat 3- zarar -0.5R'yi geçerse kes" title="Bu talimat YALNIZ Pozisyon Yöneticisi Jev ajanına gider (5 sn turu, kapat/kısmi/SL kararları); kaydetmek için alandan çık"></textarea>
232
232
  </div>
233
233
  <div id="finPosStatus">Beklemede — açık pozisyon yok</div>
234
234
  </div>
@@ -889,6 +889,16 @@
889
889
  </div>
890
890
  </div>
891
891
 
892
+ <div id="finHelpOverlay" class="mini-overlay" hidden>
893
+ <div class="mini-dialog fin-help-dialog">
894
+ <div class="mini-head">
895
+ <span class="mini-title" id="finHelpTitle">TALİMAT KILAVUZU</span>
896
+ <button class="mini-close" id="finHelpClose" title="Kapat">&#x00D7;</button>
897
+ </div>
898
+ <div class="mini-body" id="finHelpBody"></div>
899
+ </div>
900
+ </div>
901
+
892
902
  <div id="finTeamOverlay" class="mini-overlay" hidden>
893
903
  <div class="mini-dialog fin-sym-dialog">
894
904
  <div class="mini-head">
@@ -228,6 +228,12 @@ const els = {
228
228
  finMaxLot: $('#finMaxLot'),
229
229
  finSymLimits: $('#finSymLimits'),
230
230
  finStrategy: $('#finStrategy'),
231
+ finStrategyHelp: $('#finStrategyHelp'),
232
+ finPosHelp: $('#finPosHelp'),
233
+ finHelpOverlay: $('#finHelpOverlay'),
234
+ finHelpTitle: $('#finHelpTitle'),
235
+ finHelpBody: $('#finHelpBody'),
236
+ finHelpClose: $('#finHelpClose'),
231
237
  finMaxTradesDay: $('#finMaxTradesDay'),
232
238
  finLossStreak: $('#finLossStreak'),
233
239
  finLossStreakPause: $('#finLossStreakPause'),
@@ -10886,6 +10892,90 @@ if (els.finStrategy) {
10886
10892
  toast(v.trim() ? 'Ajan talimatı kaydedildi — tüm ajanlar sonraki turda uygular' : 'Ajan talimatı temizlendi');
10887
10893
  });
10888
10894
  }
10895
+ /* TALİMAT KILAVUZU (?): her iki talimat alanı için desteklenen kalıplar.
10896
+ "kod" etiketli kalıplar AYRICALIKLI olarak kod tarafından otomatik uygulanır;
10897
+ serbest cümleler Jev'in tur state'ine girer ve Jev ona göre karar verir. */
10898
+ /* BUTONLAR: ? → kılavuz; dışına tıkla / × → kapat */
10899
+ if (els.finStrategyHelp) {
10900
+ els.finStrategyHelp.addEventListener('click', (e) => {
10901
+ e.preventDefault();
10902
+ finHelpOpen('trade');
10903
+ });
10904
+ }
10905
+ if (els.finPosHelp) {
10906
+ els.finPosHelp.addEventListener('click', (e) => {
10907
+ e.preventDefault();
10908
+ finHelpOpen('posmgr');
10909
+ });
10910
+ }
10911
+ if (els.finHelpClose) els.finHelpClose.addEventListener('click', finHelpClose);
10912
+ if (els.finHelpOverlay) {
10913
+ els.finHelpOverlay.addEventListener('click', (e) => {
10914
+ if (e.target === els.finHelpOverlay) finHelpClose();
10915
+ });
10916
+ }
10917
+ function finHelpHtml(which) {
10918
+ const posmgr = which === 'posmgr';
10919
+ const items = posmgr
10920
+ ? [
10921
+ ['<code>1R\'de %50 kısmi kapat</code><code>%50 1R</code><code>kısmi close %50</code>',
10922
+ '<b>Kod otomatik uygular.</b> Kâr belirtilen R\'ye gelince pozisyonun %50\'si kapatılır (tek sefer). "kısmi %50" tek başına yazılırsa 1R kabul edilir.'],
10923
+ ['<code>1.5R\'de %25 kısmi</code><code>3R\'de %75 kapat</code>',
10924
+ '<b>Kod otomatik uygular.</b> R ve yüzdeyi sen seç; yüzde 5-90 arası olmalı.'],
10925
+ ['<code>kâr 2R olunca kalanı kapat</code><code>kârı koru</code>',
10926
+ '<b>Jev uygular.</b> Serbest ifade; her 5 sn turunda Jev okur ve kapatma/trailing kararını verir.'],
10927
+ ['<code>SL\'yi takip et</code><code>breakeven\'a çek</code><code>zararı -0.5R\'de kes</code>',
10928
+ '<b>Jev uygular.</b> SL/koruma aksiyonu olarak değerlendirilir; kod seviyeyi ATR/R ile hesaplar, SL asla geriye gitmez.'],
10929
+ ['<code>başlangıç bakiye 10.000</code><code>gün başı: 10000</code>',
10930
+ 'Gün başı bakiyesi — günlük K/Z % otomatik hesaplanır (raporda "Gün başı → %-2.3 ZARAR").'],
10931
+ ]
10932
+ : [
10933
+ ['<code>risk %2</code><code>%2 risk</code><code>risk yüzde 2</code>',
10934
+ '<b>Kod otomatik uygular.</b> Girişler %2 risk ile açılır (kalibrasyon çarpanı devre dışı, 0.1-10 arası).'],
10935
+ ['<code>lot x1.5</code><code>poz başı lot 1.5</code><code>lotu 2 kat yap</code>',
10936
+ '<b>Kod otomatik uygular.</b> Tüm giriş riskine/lotuna çarpan.'],
10937
+ ['<code>martingale</code><code>martingale 1.5</code><code>martingale x2</code>',
10938
+ '<b>Kod otomatik uygular.</b> Kayıp serisinde lot katlanır (varsayılan ×2; seri en çok ×3, toplam risk tavanı %10 ve max lot kilidi geçerli).'],
10939
+ ['<code>her mumda işlem açmak zorundasın</code><code>her barda işlem</code><code>her zaman işlem</code>',
10940
+ '<b>ZORUNLU GİRİŞ modu.</b> "bekle" seçeneği kaldırılır, eşik/teyit kapıları atlanır, emir market olur. "zorunlu değil" yazarsan mod açılmaz.'],
10941
+ ['<code>M1</code><code>1m</code><code>5 dk</code><code>4 saat</code>',
10942
+ 'İşlem zaman dilimi. M1/M5 yazarsan tur ritmi hızlanır (M1 en hızlı 60 sn).'],
10943
+ ['<code>başlangıç bakiye 10.000</code><code>gün başı: 10000</code><code>starting balance 10000</code>',
10944
+ 'Gün başı bakiyesi — her turda günlük K/Z otomatik hesaplanır ve Jev\'e durum olarak verilir.'],
10945
+ ['<code>günlük zarar %3</code><code>max günlük kayıp 3</code>',
10946
+ '<b>Kod otomatik uygular.</b> Limit aşılırsa yeni işlem açılmaz; günlük kayıp otomasyonunun limiti bu olur.'],
10947
+ ];
10948
+ const example = posmgr
10949
+ ? `1- 1R'de %50 kısmi kapat\n2- kâr 2R'ye gelince kalanı kapat\n3- zarar -0.5R'yi geçerse kes`
10950
+ : `1- risk yüzde 2\n2- martingale kullan (x2)\n3- her mumda işlem açmak zorundasın\n4- başlangıç bakiye 10.000\n5- günlük zarar %3`;
10951
+ const head = posmgr
10952
+ ? 'Pozisyon Yöneticisi (5 sn turu) açık pozisyonları yönetir: kapat / kısmi kapat / SL taşı.'
10953
+ : 'Trade Ajanı girişleri yönetir: yön, emir tipi, risk, lot, martingale ve günlük limitler.';
10954
+ const rows = items
10955
+ .map((it) => '<div class="fin-help-item"><div class="fin-help-code">' + it[0] + '</div><div class="fin-help-desc">' + it[1] + '</div></div>')
10956
+ .join('');
10957
+ return (
10958
+ '<div class="fin-help-head">' + head + '</div>' +
10959
+ '<div class="fin-help-sec">Neler yazabilirsin?</div>' +
10960
+ rows +
10961
+ '<div class="fin-help-sec">Nasıl yazılır?</div>' +
10962
+ '<div class="fin-help-text">İstediğin gibi düz cümle yazabilirsin; birden çok kuralı <b>1- 2- 3-</b> diye numaralayarak alt alta yazman en kolayı. Kod, yukarıdaki sayısal kalıpları otomatik uygular; kalan serbest ifadeleri Jev her turda okur ve kararına katar. Talimat değişiklikleri <b>sonraki turda</b> geçerli olur.</div>' +
10963
+ '<pre class="fin-help-ex">' + example + '</pre>'
10964
+ );
10965
+ }
10966
+
10967
+ function finHelpOpen(which) {
10968
+ if (!els.finHelpOverlay) return;
10969
+ const posmgr = which === 'posmgr';
10970
+ if (els.finHelpTitle) els.finHelpTitle.textContent = posmgr ? 'POZİSYON YÖNETİCİSİ — TALİMAT KILAVUZU' : 'TRADE AJANI — TALİMAT KILAVUZU';
10971
+ if (els.finHelpBody) els.finHelpBody.innerHTML = finHelpHtml(which);
10972
+ els.finHelpOverlay.hidden = false;
10973
+ }
10974
+
10975
+ function finHelpClose() {
10976
+ if (els.finHelpOverlay) els.finHelpOverlay.hidden = true;
10977
+ }
10978
+
10889
10979
  /* POZİSYON YÖNETİCİSİ: 5 sn Jev turu — kendi talimatı + aç/kapa */
10890
10980
  if (els.finPosNote) {
10891
10981
  els.finPosNote.addEventListener('input', () => {
@@ -5644,6 +5644,65 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5644
5644
  #finPosStatus.on.busy { color: var(--accent); }
5645
5645
  #finPosStatus.on.busy::before { color: #f59e0b; }
5646
5646
 
5647
+ /* TALİMAT KILAVUZU (? butonu + modal) */
5648
+ .fin-help-btn {
5649
+ width: 16px;
5650
+ height: 16px;
5651
+ margin-left: 6px;
5652
+ border-radius: 50%;
5653
+ border: 1px solid var(--border);
5654
+ background: var(--panel2);
5655
+ color: var(--muted);
5656
+ font-size: 10px;
5657
+ font-weight: 700;
5658
+ line-height: 1;
5659
+ padding: 0;
5660
+ cursor: pointer;
5661
+ vertical-align: 1px;
5662
+ }
5663
+ .fin-help-btn:hover { color: var(--text); border-color: var(--accent); }
5664
+ .fin-help-dialog { max-width: 620px; }
5665
+ #finHelpBody { max-height: 62vh; overflow: auto; }
5666
+ .fin-help-head { font-size: 11px; color: var(--muted); margin: 2px 0 10px; }
5667
+ .fin-help-sec {
5668
+ font-size: 10.5px;
5669
+ font-weight: 700;
5670
+ letter-spacing: 0.04em;
5671
+ text-transform: uppercase;
5672
+ color: var(--muted);
5673
+ margin: 12px 0 6px;
5674
+ }
5675
+ .fin-help-item {
5676
+ border: 1px solid var(--border);
5677
+ border-radius: 7px;
5678
+ padding: 6px 8px;
5679
+ margin-bottom: 6px;
5680
+ background: var(--panel2);
5681
+ }
5682
+ .fin-help-code { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
5683
+ .fin-help-code code {
5684
+ font-size: 10.5px;
5685
+ padding: 1px 5px;
5686
+ border-radius: 4px;
5687
+ background: rgba(127, 127, 127, 0.14);
5688
+ border: 1px solid var(--border);
5689
+ }
5690
+ .fin-help-desc { font-size: 11px; color: var(--muted); line-height: 1.5; }
5691
+ .fin-help-desc b { color: var(--text); }
5692
+ .fin-help-text { font-size: 11px; color: var(--muted); line-height: 1.55; }
5693
+ .fin-help-text b { color: var(--text); }
5694
+ .fin-help-ex {
5695
+ margin: 8px 0 2px;
5696
+ padding: 8px 10px;
5697
+ border-radius: 7px;
5698
+ border: 1px dashed var(--border);
5699
+ background: var(--panel2);
5700
+ font-size: 11px;
5701
+ line-height: 1.6;
5702
+ white-space: pre-wrap;
5703
+ color: var(--text);
5704
+ }
5705
+
5647
5706
  #finPosList, #finOrdList, #finSymList { display: flex; flex-direction: column; gap: 5px; }
5648
5707
  .fin-empty { font-size: 11px; color: var(--muted); padding: 2px 0 4px; }
5649
5708