beast-agent 2.73.0 → 2.75.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.75.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -802,7 +802,8 @@ const handlers = {
802
802
  }
803
803
  const pos = await bcall('positions', {}, 8000);
804
804
  const list = (pos && pos.positions) || [];
805
- const maxPos = Number(cfg.maxPositions) || 3;
805
+ /* EFEKTİF TAVAN: talimattaki "en fazla N işlem/pozisyon" ayarı ezer */
806
+ const maxPos = Number(cfg.maxPositionsNote) > 0 ? Number(cfg.maxPositionsNote) : Number(cfg.maxPositions) || 3;
806
807
  if (list.length >= maxPos) {
807
808
  return { ok: false, error: `max eşzamanlı pozisyon dolu (${list.length}/${maxPos}) — önce bir pozisyon kapat` };
808
809
  }
package/src/main.js CHANGED
@@ -10018,6 +10018,27 @@ function finCfg() {
10018
10018
  try { saveSettings(); } catch {}
10019
10019
  }
10020
10020
  if (!Number.isFinite(Number(f.maxPerCurrency))) f.maxPerCurrency = 3;
10021
+ /* TALİMATTAN ÜST SINIR: "aynı anda en fazla 10 işlem/pozisyon" → eşzamanlı
10022
+ tavan; "günde en fazla 10 işlem" → günlük işlem tavanı. Ayarı KALICI
10023
+ ezmez: not yalnız talimat metninde durdukça geçerlidir (0 = not yok). */
10024
+ f.maxPositionsNote = 0;
10025
+ f.maxTradesPerDayNote = 0;
10026
+ try {
10027
+ const txt = (String(f.strategy || '') + '\n' + String(f.posManagerNote || '')).toLowerCase();
10028
+ const re = /(en\s*fazla|en\s*[çc]ok|max(?:imum)?|maks(?:imum)?)\s*(\d{1,2})\s*(?:i[şs]lem|islem|trade|emir|pozisyon|poz)/g;
10029
+ let mm;
10030
+ while ((mm = re.exec(txt))) {
10031
+ const n = Number(mm[2]);
10032
+ if (!(n >= 1 && n <= 20)) continue;
10033
+ /* GÜNLÜK ayrımı: "günde/günlük/gün içinde ... en fazla N işlem" — cümle
10034
+ sınırı (nokta/virgül) aşılmaz; "günlük zarar %3, maks 4 pozisyon"
10035
+ ifadesindeki "günlük" günlük tavana YAZILMAZ, eşzamanlı sayılır. */
10036
+ const before = txt.slice(0, mm.index);
10037
+ const daily = /(?:g[üu]nde|g[üu]nl[üu]k|g[üu]n\s*i[çc]inde)[^.;,\n]{0,14}$/.test(before);
10038
+ if (daily) f.maxTradesPerDayNote = n;
10039
+ else if (!f.maxPositionsNote) f.maxPositionsNote = n; /* eşzamanlı tavan */
10040
+ }
10041
+ } catch {}
10021
10042
  /* SHADOW MOD: emir gönderilmez — kararlar gerekçesiyle günlüğe yazılır */
10022
10043
  if (typeof f.shadowMode !== 'boolean') f.shadowMode = false;
10023
10044
  /* POZİSYON YÖNETİCİSİ: açık pozisyonları 5 sn'de bir Jev ile yöneten ayrı
@@ -10379,12 +10400,31 @@ function finTeamDigest() {
10379
10400
  /* KODLA DİSİPLİN kancası: financetools işlem öncesi bunu çağırır. */
10380
10401
  function finDisciplineError(side, symbol, positions) {
10381
10402
  try {
10382
- return finrisk.disciplineError(finJournalTail(400), Date.now(), finCfg(), side, symbol, positions);
10403
+ const cfg = finCfg();
10404
+ /* TALİMAT: "günde en fazla N işlem" → günlük işlem tavanını ezer */
10405
+ if (Number(cfg.maxTradesPerDayNote) > 0) {
10406
+ return finrisk.disciplineError(
10407
+ finJournalTail(400),
10408
+ Date.now(),
10409
+ { ...cfg, maxTradesPerDay: Number(cfg.maxTradesPerDayNote) },
10410
+ side,
10411
+ symbol,
10412
+ positions
10413
+ );
10414
+ }
10415
+ return finrisk.disciplineError(finJournalTail(400), Date.now(), cfg, side, symbol, positions);
10383
10416
  } catch {
10384
10417
  return null;
10385
10418
  }
10386
10419
  }
10387
10420
 
10421
+ /* EFEKTİF POZİSYON TAVANI: talimattaki "en fazla N işlem/pozisyon" ayarı ezer */
10422
+ function finPosCap(f) {
10423
+ const note = Number(f && f.maxPositionsNote) > 0 ? Number(f.maxPositionsNote) : 0;
10424
+ const base = Number(f && f.maxPositions) || 3;
10425
+ return Math.max(1, Math.min(20, note > 0 ? note : base));
10426
+ }
10427
+
10388
10428
  /* ---------- MAE/MFE: pozisyonun gördüğü en iyi/en kötü seviye ---------- */
10389
10429
  function finExcLoad() {
10390
10430
  try {
@@ -10779,17 +10819,36 @@ function finEquitySample(account) {
10779
10819
  function finDailyLossCheck(point) {
10780
10820
  const cfg = finCfg();
10781
10821
  const day = finstats.dayKey(point.at);
10822
+ /* TALİMAT: "gün başı bakiye" yazıldıysa taban O'dur; "günlük zarar %X"
10823
+ yazıldıysa limit ayarın yerine talimattan gelir (kullanıcı her gün günceller). */
10824
+ let startBal = 0;
10825
+ let limitPct = Number(cfg.maxDailyLossPct) || 0;
10826
+ try {
10827
+ const ins = finParsedInstr();
10828
+ if (Number(ins.entry.startBalance) > 0) startBal = Number(ins.entry.startBalance);
10829
+ if (Number(ins.entry.dailyLossPct) > 0) limitPct = Number(ins.entry.dailyLossPct);
10830
+ } catch {}
10782
10831
  let ds = financeState.dayStart;
10783
10832
  if (!ds || ds.day !== day) {
10784
- financeState.dayStart = ds = { day, equity: Number(point.balance) > 0 ? Number(point.balance) : point.equity, warned: false, acted: false };
10833
+ financeState.dayStart = ds = {
10834
+ day,
10835
+ equity: startBal > 0 ? startBal : Number(point.balance) > 0 ? Number(point.balance) : point.equity,
10836
+ warned: false,
10837
+ acted: false,
10838
+ fromNote: startBal > 0,
10839
+ };
10785
10840
  }
10786
- if (ds.warned || !(Number(cfg.maxDailyLossPct) > 0) || !(ds.equity > 0)) return;
10841
+ if (startBal > 0) {
10842
+ ds.equity = startBal;
10843
+ ds.fromNote = true;
10844
+ }
10845
+ if (ds.warned || !(limitPct > 0) || !(ds.equity > 0)) return;
10787
10846
  const dd = ((ds.equity - point.equity) / ds.equity) * 100;
10788
- if (dd >= Number(cfg.maxDailyLossPct)) {
10847
+ if (dd >= limitPct) {
10789
10848
  ds.warned = true;
10790
10849
  const action = String(cfg.dailyLossAction || 'stop');
10791
10850
  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}`;
10851
+ const line = `⚠️ Günlük kayıp %${dd.toFixed(2)} (limit %${limitPct}${ds.fromNote ? ' · talimat gün başı bakiye' : ''})${tail}`;
10793
10852
  financeLog('[risk] ' + line);
10794
10853
  financeNotify(line, 'drawdown');
10795
10854
  finJournal({ kind: 'drawdown', pct: Math.round(dd * 100) / 100, action });
@@ -13698,6 +13757,123 @@ function finTsStrategyText() {
13698
13757
  }
13699
13758
  }
13700
13759
 
13760
+ /* ---- TALİMAT AYRIŞTIRICI ----
13761
+ Trade Ajanı + Pozisyon Yöneticisi notlarındaki SAYISAL kuralları koda çevirir:
13762
+ "risk %2" → giriş riski %2
13763
+ "lot x1.5" / "poz başı lot 1.5" / "lotu 2 kat" → lot çarpanı
13764
+ "martingale" / "martingale 1.5" → kayıp serisinde lot katlama (varsayılan ×2)
13765
+ "1R'de %50 kısmi kapat" / "%50 1R" / "kısmi %50" → otomatik kısmi close kuralı
13766
+ Saf metin ayrıştırma; uygulama ilgili turda kodla yapılır (LLM yok). */
13767
+ function finInstrNum(v) {
13768
+ const n = Number(String(v == null ? '' : v).replace(',', '.'));
13769
+ return isFinite(n) ? n : null;
13770
+ }
13771
+
13772
+ /* Para değeri: "10.000" / "10,000" → 10000; "10000,50" / "10000.50" → 10000.5 */
13773
+ function finInstrMoney(v) {
13774
+ const s = String(v == null ? '' : v).replace(/\s+/g, '');
13775
+ if (!s) return null;
13776
+ /* "5.000,50" / "5,000.50" → 5000.50 ; "10.000" → 10000 ; "10000,50" → 10000.5 */
13777
+ const m = s.match(/^(\d{1,3}(?:[.,]\d{3})+)(?:[.,](\d{1,2}))?$/);
13778
+ if (m) {
13779
+ const n = Number(m[1].replace(/[.,]/g, '') + (m[2] ? '.' + m[2] : ''));
13780
+ return isFinite(n) && n > 0 ? n : null;
13781
+ }
13782
+ const n2 = Number(s.replace(',', '.'));
13783
+ return isFinite(n2) && n2 > 0 ? n2 : null;
13784
+ }
13785
+
13786
+ function finParseInstructions(text) {
13787
+ const low = String(text || '').toLowerCase();
13788
+ const out = { riskPct: null, lotMult: null, martingale: null, partial: null, startBalance: null, dailyLossPct: null };
13789
+ if (!low.trim()) return out;
13790
+ /* "risk %2", "%2 risk", "risk yüzde 2", "riski 2 yap" → 2 (ilk sayı) */
13791
+ let m = low.match(
13792
+ /(?:risk\s*%\s*(\d+(?:[.,]\d+)?))|(?:%\s*(\d+(?:[.,]\d+)?)\s*risk)|(?:risk[^0-9]{0,14}(\d+(?:[.,]\d+)?))/
13793
+ );
13794
+ if (m) out.riskPct = finInstrNum(m[1] || m[2] || m[3]);
13795
+ if (out.riskPct != null) out.riskPct = Math.max(0.1, Math.min(10, out.riskPct));
13796
+ /* GÜN BAŞLANGIÇ BAKİYESİ (kullanıcı her gün talimata yazar): "başlangıç bakiye 10.000",
13797
+ "gün başı: 10000", "starting balance 10000" */
13798
+ 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]*)/);
13799
+ if (m) out.startBalance = finInstrMoney(m[1]);
13800
+ /* GÜNLÜK ZARAR LİMİTİ: "günlük zarar %3", "max günlük kayıp 3", "daily loss %3" */
13801
+ 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+)?)/);
13802
+ if (m) {
13803
+ const v = finInstrNum(m[1]);
13804
+ if (v != null && v > 0) out.dailyLossPct = Math.max(0.1, Math.min(50, v));
13805
+ }
13806
+ 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)/);
13807
+ if (m) out.lotMult = finInstrNum(m[1] || m[2] || m[3]);
13808
+ if (out.lotMult != null) out.lotMult = Math.max(1, Math.min(5, out.lotMult));
13809
+ if (/martingale|mart[ıi]ngale/.test(low)) {
13810
+ /* Faktör serbest: "martingale 1.2", "martingale x1.20", "martingale 1,2",
13811
+ "martingale çarpanı 1.2", "1.2x martingale", "1.2 kat martingale".
13812
+ Yalnız bilinen bağlaçlardan sonra sayı okunur — "martingale kullan, 1R..."
13813
+ gibi metinden yanlış sayı kapmaz. Varsayılan ×2 (1.01–5 arası geçerli). */
13814
+ m = low.match(/(?:mart[ıi]ngale\s*(?:x|×|çarpan[ıi]?|carpan[ıi]?|oran[ıi]?|fakt[öo]r[üu]?|kat[ıi]?)?\s*(\d+(?:[.,]\d+)?))|(?:(\d+(?:[.,]\d+)?)\s*(?:x|×|kat[ıi]?)?\s*mart[ıi]ngale)/);
13815
+ const mf = m ? finInstrNum(m[1] || m[2]) : null;
13816
+ out.martingale = Math.max(1.01, Math.min(5, mf != null && mf > 1 ? mf : 2));
13817
+ }
13818
+ /* kısmi kapatma: "1R'de %50" → atR=1, pct=50; "%50 1R" → aynı; "kısmi %50" → atR=1 */
13819
+ m = low.match(/(\d+(?:[.,]\d+)?)\s*r[^%\d]{0,28}%\s*(\d+(?:[.,]\d+)?)/);
13820
+ if (m) out.partial = { atR: finInstrNum(m[1]), pct: finInstrNum(m[2]) };
13821
+ if (!out.partial) {
13822
+ m = low.match(/%\s*(\d+(?:[.,]\d+)?)[^%\d]{0,28}?(\d+(?:[.,]\d+)?)\s*r/);
13823
+ if (m) out.partial = { atR: finInstrNum(m[2]), pct: finInstrNum(m[1]) };
13824
+ }
13825
+ if (!out.partial) {
13826
+ m = low.match(/(?:k[ıi]smi|partial)[^%\d]{0,24}%\s*(\d+(?:[.,]\d+)?)/);
13827
+ if (m) out.partial = { atR: 1, pct: finInstrNum(m[1]) };
13828
+ }
13829
+ if (out.partial) {
13830
+ const p = out.partial;
13831
+ if (!(p.atR > 0)) p.atR = 1;
13832
+ p.atR = Math.min(10, Math.round(p.atR * 100) / 100);
13833
+ if (!(p.pct >= 5 && p.pct <= 90)) out.partial = null;
13834
+ else p.pct = Math.round(p.pct);
13835
+ }
13836
+ return out;
13837
+ }
13838
+
13839
+ /* İki notu birleştir: GİRİŞ kuralları trade notundan, YÖNETİM kuralları
13840
+ pozisyon yöneticisi notundan (yoksa trade notundan) gelir. */
13841
+ function finParsedInstr() {
13842
+ const trade = finParseInstructions(finTsStrategyText());
13843
+ const mgr = finParseInstructions(String(finCfg().posManagerNote || ''));
13844
+ return {
13845
+ entry: {
13846
+ riskPct: trade.riskPct != null ? trade.riskPct : mgr.riskPct,
13847
+ lotMult: trade.lotMult != null ? trade.lotMult : mgr.lotMult,
13848
+ martingale: trade.martingale != null ? trade.martingale : mgr.martingale,
13849
+ startBalance: trade.startBalance != null ? trade.startBalance : mgr.startBalance,
13850
+ dailyLossPct: trade.dailyLossPct != null ? trade.dailyLossPct : mgr.dailyLossPct,
13851
+ },
13852
+ manage: { partial: mgr.partial || trade.partial },
13853
+ };
13854
+ }
13855
+
13856
+ /* Rapor/state için tek satır özet: kod ne uygulayacak */
13857
+ function finInstrSummary(ins) {
13858
+ try {
13859
+ const p = [];
13860
+ if (ins.entry.riskPct != null) p.push(`risk %${ins.entry.riskPct}`);
13861
+ if (ins.entry.lotMult != null) p.push(`lot ×${ins.entry.lotMult}`);
13862
+ if (ins.entry.martingale != null) p.push(`martingale ×${ins.entry.martingale}`);
13863
+ if (ins.entry.startBalance != null) p.push(`gün başı ${ins.entry.startBalance}`);
13864
+ if (ins.entry.dailyLossPct != null) p.push(`günlük zarar limiti %${ins.entry.dailyLossPct}`);
13865
+ if (ins.manage.partial) p.push(`kısmi %${ins.manage.partial.pct} @${ins.manage.partial.atR}R`);
13866
+ try {
13867
+ const f = finCfg();
13868
+ if (Number(f.maxPositionsNote) > 0) p.push(`maks ${f.maxPositionsNote} pozisyon`);
13869
+ if (Number(f.maxTradesPerDayNote) > 0) p.push(`günde maks ${f.maxTradesPerDayNote} işlem`);
13870
+ } catch {}
13871
+ return p.join(' · ');
13872
+ } catch {
13873
+ return '';
13874
+ }
13875
+ }
13876
+
13701
13877
  /* Talimattan ZAMAN DİLİMİ çıkar: "M15", "1m", "1 dk", "4 saat" → M15/M1/H4 */
13702
13878
  function finTsStrategyTf() {
13703
13879
  const s = String(finTsStrategyText() || '');
@@ -14079,6 +14255,9 @@ async function finPosManagerRound(positions, account) {
14079
14255
  pm.rounds = (Number(pm.rounds) || 0) + 1;
14080
14256
  pm.lastAt = Date.now();
14081
14257
  const f = finCfg();
14258
+ /* TALİMAT: kısmi close kuralı varsa KOD uygular (Jev'e sorulmaz) */
14259
+ const ins = finParsedInstr();
14260
+ const instrTxt = finInstrSummary(ins);
14082
14261
  const lines = [];
14083
14262
  try {
14084
14263
  /* pozisyon sembolleri: piyasa + gösterge (tur başına tek Jev çağrısı) */
@@ -14106,6 +14285,18 @@ async function finPosManagerRound(positions, account) {
14106
14285
  piyasa: {},
14107
14286
  alarmlar: (financeState.alerts || []).slice(0, 8).map((a) => ({ sembol: a.symbol, yon: a.direction, fiyat: a.price, mod: a.once ? 'tek' : 'tekrarlı' })),
14108
14287
  sahip_talimati: String(f.posManagerNote || '').slice(0, 1500) || '(yok — genel disiplin: kârı koru, zararı sınırla, SL/TP aktif yönet)',
14288
+ talimat_ayarlari: instrTxt || '(sayısal kural yok)',
14289
+ gun_durumu: (() => {
14290
+ try {
14291
+ const sb = Number(ins.entry.startBalance) || 0;
14292
+ const eqNow = Number(account && account.equity) || 0;
14293
+ if (!(sb > 0) || !(eqNow > 0)) return '';
14294
+ const pct = Math.round(((eqNow - sb) / sb) * 10000) / 100;
14295
+ return `Gün başı ${sb} → %${pct >= 0 ? '+' : ''}${pct} (${pct >= 0 ? 'KÂR' : 'ZARAR'})`;
14296
+ } catch {
14297
+ return '';
14298
+ }
14299
+ })(),
14109
14300
  ekip_yanitlari: finTsFeedText(6),
14110
14301
  };
14111
14302
  for (const sym of syms) {
@@ -14131,16 +14322,19 @@ async function finPosManagerRound(positions, account) {
14131
14322
  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
14323
  criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
14133
14324
  };
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
- };
14325
+ /* Talimatta SAYISAL kısmi kuralı varsa Jev'e sorulmaz — kodu uygular */
14326
+ if (!ins.manage.partial) {
14327
+ questions['kismi_' + i] = {
14328
+ type: 'choice',
14329
+ 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.'}`,
14330
+ criteria: {
14331
+ yok: 'Kısmi kapatma yok — pozisyon tam kalsın',
14332
+ yuzde25: 'Pozisyonun %25’i kapatılsın',
14333
+ yuzde50: 'Pozisyonun %50’si kapatılsın',
14334
+ yuzde75: 'Pozisyonun %75’i kapatılsın',
14335
+ },
14336
+ };
14337
+ }
14144
14338
  questions['sl_' + i] = {
14145
14339
  type: 'choice',
14146
14340
  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 +14367,39 @@ async function finPosManagerRound(positions, account) {
14173
14367
  }
14174
14368
  const psNow = finTsPosState(pp);
14175
14369
  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'}`);
14370
+ /* TALİMAT KISMİ KURALI (kod): "+atR'de %pct kapat" → otomatik uygula */
14371
+ const rule = ins.manage.partial;
14372
+ if (rule) {
14373
+ const stP = financeState.watch.get(String(pp.ticket)) || null;
14374
+ const done = !!(stP && stP.partial) || !!(pm.tsPartials && pm.tsPartials.has(String(pp.ticket)));
14375
+ if (!done && psNow.kz_r != null && psNow.kz_r >= rule.atR) {
14376
+ let rr = null;
14377
+ try {
14378
+ rr = await financetools.handlers.mt5_close(
14379
+ { 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)` },
14380
+ { sessionId: FIN_POSMGR_ID }
14381
+ );
14382
+ } catch (e) {
14383
+ rr = { ok: false, error: String((e && e.message) || e) };
14384
+ }
14385
+ if (rr && rr.ok) {
14386
+ if (stP) { stP.partial = true; finWatchSave(); }
14387
+ pm.tsPartials = pm.tsPartials || new Set();
14388
+ pm.tsPartials.add(String(pp.ticket));
14389
+ 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' : ''})`);
14390
+ } else {
14391
+ lines.push(`- ${sym} #${pp.ticket}: talimat kısmi reddedildi — ${(rr && rr.error) || 'hata'}`);
14392
+ }
14393
+ }
14394
+ }
14176
14395
  try { await finTsManageOpen(FIN_POSMGR_ID, pm, sym, pp, market[sym], ans, i, lines); } catch {}
14177
14396
  }
14178
14397
  finTsPost(
14179
14398
  FIN_POSMGR_ID,
14180
14399
  FIN_POSMGR_AGENT,
14181
- `🛡️ Pozisyon yöneticisi turu #${pm.rounds} (LLM yok — 5 sn):\n` + lines.join('\n'),
14400
+ `🛡️ Pozisyon yöneticisi turu #${pm.rounds} (LLM yok — 5 sn):\n` +
14401
+ (instrTxt ? 'TALİMAT AYARLARI (kod uygular): ' + instrTxt + '\n' : '') +
14402
+ lines.join('\n'),
14182
14403
  'yönetim'
14183
14404
  );
14184
14405
  } catch (e) {
@@ -14265,6 +14486,25 @@ async function finTypeSafeRound(sid, agent) {
14265
14486
  const posList = (posR && posR.positions) || [];
14266
14487
  /* BEKLEYEN EMİRLER: pozisyon slotunu paylaşır (aşırı birikmeyi önler) */
14267
14488
  const pendCount = (ordR && Array.isArray(ordR.orders) ? ordR.orders.length : 0);
14489
+ /* TALİMAT AYARLARI: sayısal kurallar koda çevrildi (risk/lot/martingale/
14490
+ gün başı bakiye/günlük zarar limiti) — state ve giriş kapısı kullanır */
14491
+ const ins = finParsedInstr();
14492
+ /* GÜN BAŞI BAKİYE → günlük K/Z ve limit durumu (hesap anlık değerinden) */
14493
+ let dayPct = null;
14494
+ let dayBlocked = '';
14495
+ try {
14496
+ const sb = Number(ins.entry.startBalance) || 0;
14497
+ const eqNow = Number(account.equity) || 0;
14498
+ const balNow = Number(account.balance) || 0;
14499
+ const refNow = eqNow > 0 ? eqNow : balNow;
14500
+ if (sb > 0 && refNow > 0) {
14501
+ dayPct = Math.round(((refNow - sb) / sb) * 10000) / 100;
14502
+ const lim = Number(ins.entry.dailyLossPct) || 0;
14503
+ if (lim > 0 && dayPct <= -lim) {
14504
+ dayBlocked = `günlük zarar limiti AŞILDI (gün başı ${sb} → ${refNow}, %${dayPct} ≤ -%${lim}) — talimat gereği yeni işlem açılmaz`;
14505
+ }
14506
+ }
14507
+ } catch {}
14268
14508
  const market = {};
14269
14509
  for (const sym of symbols) {
14270
14510
  const tf = finTsPickTf(agent, sym);
@@ -14305,6 +14545,16 @@ async function finTypeSafeRound(sid, agent) {
14305
14545
  talimat "zorunlu giriş" içeriyorsa kod eşikleri devre dışı bırakır. */
14306
14546
  sahip_talimati: finTsStrategyText() || '(yok — temel teknik okuma)',
14307
14547
  talimat_modu: finTsMandatoryEntry() ? 'ZORUNLU GİRİŞ — bu turda işlem açmak zorunlu (bekle yok); yönü sen seç' : '',
14548
+ talimat_ayarlari: finInstrSummary(ins) || '(sayısal kural yok)',
14549
+ /* GÜN BAŞI BAKİYE (talimattan): günlük K/Z — Jev zarar durumunu otomatik görür */
14550
+ gun_baslangic_bakiye: ins.entry.startBalance != null ? ins.entry.startBalance : null,
14551
+ gun_net_yuzde: dayPct,
14552
+ gun_durumu: dayBlocked
14553
+ ? dayBlocked
14554
+ : ins.entry.startBalance != null && dayPct != null
14555
+ ? `Gün başı ${ins.entry.startBalance} → şimdi %${dayPct >= 0 ? '+' : ''}${dayPct} (${dayPct >= 0 ? 'KÂR' : 'ZARAR'})` +
14556
+ (ins.entry.dailyLossPct != null ? ` · limit -%${ins.entry.dailyLossPct}` : '')
14557
+ : '',
14308
14558
  };
14309
14559
  for (const sym of symbols) {
14310
14560
  const d = market[sym];
@@ -14329,6 +14579,7 @@ async function finTypeSafeRound(sid, agent) {
14329
14579
  const talimatZorunlu = finTsMandatoryEntry();
14330
14580
  /* Yönetici açıkken açık pozisyon kararları ONA aittir (çift yönetim yok) */
14331
14581
  const posManagerOn = f.posManagerEnabled !== false;
14582
+ /* TALİMAT AYARLARI: yukarıda (state kurulmadan) ayrıştırıldı */
14332
14583
  symbols.forEach((sym, i) => {
14333
14584
  questions['yon_' + i] = {
14334
14585
  type: 'choice',
@@ -14491,8 +14742,17 @@ async function finTypeSafeRound(sid, agent) {
14491
14742
  continue;
14492
14743
  }
14493
14744
  }
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ı`);
14745
+ /* GÜN BAŞI BAKİYE LİMİTİ (talimat): aşıldıysa YENİ İŞLEM AÇILMAZ */
14746
+ if (dayBlocked) {
14747
+ lines.push(`- ${sym}: ${dayBlocked}`);
14748
+ continue;
14749
+ }
14750
+ /* ÇOKLU GİRİŞ: aynı turda birden çok sembol açılabilir — yalnız toplam
14751
+ slot (pozisyon + bekleyen emir + bu turda açılanlar) tavanı korur */
14752
+ const posCap = finPosCap(f);
14753
+ const slotsLeft = posCap - pendCount - posList.length - opened;
14754
+ if (slotsLeft <= 0) {
14755
+ lines.push(`- ${sym}: sinyal güçlü (${c.choice} p=${c.p.toFixed(2)}) ama pozisyon+bekleyen emir sınırı dolu (${posList.length + pendCount}/${posCap}) — bu tur açılmadı`);
14496
14756
  continue;
14497
14757
  }
14498
14758
  /* GİRİŞ TİPİ + RİSK PROFİLİ (JEV kararı) — seviyeleri KOD hesaplar */
@@ -14535,8 +14795,29 @@ async function finTypeSafeRound(sid, agent) {
14535
14795
  }
14536
14796
  const sl = rnd(isBuy ? entry - slDist : entry + slDist);
14537
14797
  const tp = rnd(isBuy ? entry + tpDist : entry - tpDist);
14798
+ /* RİSK + LOT: talimatta "risk %2" varsa AYNEN uygulanır; yoksa ayar ×
14799
+ kalibrasyon. Lot çarpanı ve MARTİNGALE (kayıp serisi × kat) burada. */
14538
14800
  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));
14801
+ let riskPct = ins.entry.riskPct != null
14802
+ ? ins.entry.riskPct
14803
+ : Math.max(0.1, Math.min(2, Math.round(riskBase * cal.riskMult * 100) / 100));
14804
+ let sizeMult = 1;
14805
+ let martNote = '';
14806
+ if (ins.entry.lotMult != null) sizeMult *= ins.entry.lotMult;
14807
+ if (ins.entry.martingale != null) {
14808
+ let streak = 0;
14809
+ try {
14810
+ const le = finLearnLoad().symbols[sym];
14811
+ streak = Number(le && le.stats && le.stats.streak) || 0;
14812
+ } catch {}
14813
+ const pow = Math.min(3, Math.max(0, Math.round(streak)));
14814
+ if (pow > 0) {
14815
+ sizeMult *= Math.pow(ins.entry.martingale, pow);
14816
+ martNote = ` · martingale ×${ins.entry.martingale}^${pow}`;
14817
+ }
14818
+ }
14819
+ if (sizeMult !== 1) riskPct = Math.max(0.1, Math.min(10, Math.round(riskPct * sizeMult * 100) / 100));
14820
+ const riskNote = ins.entry.riskPct != null ? ' · TALİMAT risk %' + ins.entry.riskPct : '';
14540
14821
  const emirLabel =
14541
14822
  orderType === 'market' ? 'MARKET' :
14542
14823
  isBuy && orderType === 'buy_limit' ? 'BUY LIMIT' :
@@ -14584,10 +14865,11 @@ async function finTypeSafeRound(sid, agent) {
14584
14865
  orderType,
14585
14866
  });
14586
14867
  } catch {}
14868
+ const riskTxt = `risk %${riskPct}${ins.entry.riskPct == null && cal.riskMult !== 1 ? ` ×${cal.riskMult}` : ''}${riskNote}${martNote}`;
14587
14869
  lines.push(
14588
14870
  (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})`) +
14871
+ ? `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, ${riskTxt})`
14872
+ : `- ${sym}: ⏳ ${emirLabel} emri kondu @${entry} (${profKey} profil, SL ${sl}, TP ${tp}, ${riskTxt})`) +
14591
14873
  ` · p=${c.p.toFixed(2)} teyit=${confVal.toFixed(2)} · tf=${market[sym].tf}` +
14592
14874
  (talimatZorunlu ? ' · TALİMAT: zorunlu giriş' : '') +
14593
14875
  (thAction !== FIN_TS_DEFAULT_TH.action ? ` · öğrenilmiş eşik p≥${thAction.toFixed(2)}` : '')
@@ -14600,10 +14882,12 @@ async function finTypeSafeRound(sid, agent) {
14600
14882
  }
14601
14883
  /* JEV ARAÇ İSTEĞİ (tur düzeyi): eksik araç varsa TOOL botuna asenkron yazdır */
14602
14884
  try { await finTsToolRequest(sidS, agent, ans, lines); } catch {}
14885
+ const insTxt = finInstrSummary(ins);
14603
14886
  finTsPost(
14604
14887
  sidS,
14605
14888
  agent,
14606
14889
  `🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
14890
+ (insTxt ? 'TALİMAT AYARLARI (kod uygular): ' + insTxt + '\n' : '') +
14607
14891
  'TF KARARI: ' + symbols.map((s) => `${s}=${market[s].tf}`).join(', ') + '\n' +
14608
14892
  'ÖĞRENİLMİŞ: ' +
14609
14893
  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,92 @@ 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.2</code><code>martingale x1.20</code><code>1.2 kat martingale</code>',
10938
+ '<b>Kod otomatik uygular.</b> Kayıp serisinde lot katlanır — faktör serbest (ör. 1.2). Varsayılan ×2; 1.01–5 arası geçerli, 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
+ ['<code>aynı anda en fazla 10 işlem</code><code>en fazla 10 pozisyon</code><code>günde en fazla 10 işlem</code>',
10948
+ '<b>Kod otomatik uygular.</b> Eşzamanlı tavan (pozisyon + bekleyen emir) ya da günlük işlem tavanı. Talimatta yazdığın sürece Ayarlar\'daki değeri ezer; metinden kaldırınca ayar geçerli olur.'],
10949
+ ];
10950
+ const example = posmgr
10951
+ ? `1- 1R'de %50 kısmi kapat\n2- kâr 2R'ye gelince kalanı kapat\n3- zarar -0.5R'yi geçerse kes`
10952
+ : `1- risk yüzde 2\n2- martingale kullan (x1.2)\n3- her mumda işlem açmak zorundasın\n4- aynı anda en fazla 10 işlem\n5- başlangıç bakiye 10.000\n6- günlük zarar %3`;
10953
+ const head = posmgr
10954
+ ? 'Pozisyon Yöneticisi (5 sn turu) açık pozisyonları yönetir: kapat / kısmi kapat / SL taşı.'
10955
+ : 'Trade Ajanı girişleri yönetir: yön, emir tipi, risk, lot, martingale ve günlük limitler.';
10956
+ const rows = items
10957
+ .map((it) => '<div class="fin-help-item"><div class="fin-help-code">' + it[0] + '</div><div class="fin-help-desc">' + it[1] + '</div></div>')
10958
+ .join('');
10959
+ return (
10960
+ '<div class="fin-help-head">' + head + '</div>' +
10961
+ '<div class="fin-help-sec">Neler yazabilirsin?</div>' +
10962
+ rows +
10963
+ '<div class="fin-help-sec">Nasıl yazılır?</div>' +
10964
+ '<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>' +
10965
+ '<pre class="fin-help-ex">' + example + '</pre>'
10966
+ );
10967
+ }
10968
+
10969
+ function finHelpOpen(which) {
10970
+ if (!els.finHelpOverlay) return;
10971
+ const posmgr = which === 'posmgr';
10972
+ if (els.finHelpTitle) els.finHelpTitle.textContent = posmgr ? 'POZİSYON YÖNETİCİSİ — TALİMAT KILAVUZU' : 'TRADE AJANI — TALİMAT KILAVUZU';
10973
+ if (els.finHelpBody) els.finHelpBody.innerHTML = finHelpHtml(which);
10974
+ els.finHelpOverlay.hidden = false;
10975
+ }
10976
+
10977
+ function finHelpClose() {
10978
+ if (els.finHelpOverlay) els.finHelpOverlay.hidden = true;
10979
+ }
10980
+
10889
10981
  /* POZİSYON YÖNETİCİSİ: 5 sn Jev turu — kendi talimatı + aç/kapa */
10890
10982
  if (els.finPosNote) {
10891
10983
  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