beast-agent 2.76.6 → 2.77.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.
Files changed (54) hide show
  1. package/package.json +1 -1
  2. package/src/agent/bots.js +669 -664
  3. package/src/agent/customtools.js +6 -19
  4. package/src/agent/engine.js +55 -307
  5. package/src/agent/seeds/tool-yazma/SKILL.md +13 -16
  6. package/src/agent/skills.js +14 -35
  7. package/src/agent/supertonic.js +622 -0
  8. package/src/agent/tools.js +3 -7
  9. package/src/main.js +77 -6164
  10. package/src/preload.js +2 -18
  11. package/src/renderer/i18n.js +14 -8
  12. package/src/renderer/index.html +0 -297
  13. package/src/renderer/renderer.js +142 -2301
  14. package/src/renderer/style.css +3 -825
  15. package/src/agent/defaulttools/mt5_barlar2/run.js +0 -188
  16. package/src/agent/defaulttools/mt5_barlar2/tool.json +0 -23
  17. package/src/agent/defaulttools/mt5_bekleyen/run.js +0 -105
  18. package/src/agent/defaulttools/mt5_bekleyen/tool.json +0 -18
  19. package/src/agent/defaulttools/mt5_durum/run.js +0 -93
  20. package/src/agent/defaulttools/mt5_durum/tool.json +0 -10
  21. package/src/agent/defaulttools/mt5_emir/run.js +0 -98
  22. package/src/agent/defaulttools/mt5_emir/tool.json +0 -17
  23. package/src/agent/defaulttools/mt5_emir_iptal/run.js +0 -90
  24. package/src/agent/defaulttools/mt5_emir_iptal/tool.json +0 -12
  25. package/src/agent/defaulttools/mt5_fiyat/run.js +0 -87
  26. package/src/agent/defaulttools/mt5_fiyat/tool.json +0 -16
  27. package/src/agent/defaulttools/mt5_gecmis/run.js +0 -87
  28. package/src/agent/defaulttools/mt5_gecmis/tool.json +0 -12
  29. package/src/agent/defaulttools/mt5_kapat/run.js +0 -108
  30. package/src/agent/defaulttools/mt5_kapat/tool.json +0 -15
  31. package/src/agent/defaulttools/mt5_m15_m5/m15m5.py +0 -60
  32. package/src/agent/defaulttools/mt5_m15_m5/run.js +0 -73
  33. package/src/agent/defaulttools/mt5_m15_m5/tool.json +0 -10
  34. package/src/agent/defaulttools/mt5_pozisyon_gecmis/run.js +0 -144
  35. package/src/agent/defaulttools/mt5_pozisyon_gecmis/tool.json +0 -12
  36. package/src/agent/defaulttools/mt5_shot/run.js +0 -649
  37. package/src/agent/defaulttools/mt5_shot/stitch.js +0 -151
  38. package/src/agent/defaulttools/mt5_shot/tool.json +0 -25
  39. package/src/agent/defaulttools/mt5_sltp/run.js +0 -92
  40. package/src/agent/defaulttools/mt5_sltp/tool.json +0 -14
  41. package/src/agent/financetools.js +0 -1025
  42. package/src/agent/finindicators.js +0 -254
  43. package/src/agent/finrisk.js +0 -289
  44. package/src/agent/finstats.js +0 -239
  45. package/src/agent/finwatch.js +0 -302
  46. package/src/agent/mt5/BeastFinance.mq5 +0 -498
  47. package/src/agent/mt5/BeastShot.tpl +0 -0
  48. package/src/agent/mt5setup.js +0 -542
  49. package/src/agent/scripts/mt5_bridge.py +0 -575
  50. package/src/agent/seeds/haber-duygu/SKILL.md +0 -49
  51. package/src/agent/seeds/mql5/SKILL.md +0 -154
  52. package/src/agent/seeds/price-action/SKILL.md +0 -60
  53. package/src/agent/seeds/risk-yonetimi/SKILL.md +0 -52
  54. package/src/mt5bridge.js +0 -215
@@ -191,127 +191,6 @@ const els = {
191
191
  stMic: $('#stMic'),
192
192
  studioRow: $('#studioRow'),
193
193
  stSplit: $('#stSplit'),
194
- finBtn: $('#finBtn'),
195
- finPanel: $('#finPanel'),
196
- finBody: $('#finBody'),
197
- finDot: $('#finDot'),
198
- finTerm: $('#finTerm'),
199
- finReconnect: $('#finReconnect'),
200
- finSettings: $('#finSettings'),
201
- finSettingsOverlay: $('#finSettingsOverlay'),
202
- finSettingsClose: $('#finSettingsClose'),
203
- finShowRisk: $('#finShowRisk'),
204
- finShowNotify: $('#finShowNotify'),
205
- finShowPerf: $('#finShowPerf'),
206
- finShowFlow: $('#finShowFlow'),
207
- finDmTgOn: $('#finDmTgOn'),
208
- finDmTgWrap: $('#finDmTgWrap'),
209
- finDmTgState: $('#finDmTgState'),
210
- finDmTgUnbind: $('#finDmTgUnbind'),
211
- finDmTgHint: $('#finDmTgHint'),
212
- finTradeHoursOn: $('#finTradeHoursOn'),
213
- finTradeHoursWrap: $('#finTradeHoursWrap'),
214
- finTradeHoursStart: $('#finTradeHoursStart'),
215
- finTradeHoursEnd: $('#finTradeHoursEnd'),
216
- finRiskCard: $('#finRiskCard'),
217
- finNotifyCard: $('#finNotifyCard'),
218
- finPerfCard: $('#finPerfCard'),
219
- finFlowCard: $('#finFlowCard'),
220
- finBalance: $('#finBalance'),
221
- finEquity: $('#finEquityValue'),
222
- finProfit: $('#finProfit'),
223
- finFree: $('#finFree'),
224
- finTraderModel: $('#finTraderModel'),
225
- finModelRefreshBtn: $('#finModelRefreshBtn'),
226
- finInterval: $('#finInterval'),
227
- finMinLot: $('#finMinLot'),
228
- finMaxLot: $('#finMaxLot'),
229
- finSymLimits: $('#finSymLimits'),
230
- finStrategy: $('#finStrategy'),
231
- finStrategyHelp: $('#finStrategyHelp'),
232
- finPosHelp: $('#finPosHelp'),
233
- finHelpOverlay: $('#finHelpOverlay'),
234
- finHelpTitle: $('#finHelpTitle'),
235
- finHelpBody: $('#finHelpBody'),
236
- finHelpClose: $('#finHelpClose'),
237
- finMaxTradesDay: $('#finMaxTradesDay'),
238
- finLossStreak: $('#finLossStreak'),
239
- finLossStreakPause: $('#finLossStreakPause'),
240
- finReentry: $('#finReentry'),
241
- finMaxPerCurrency: $('#finMaxPerCurrency'),
242
- finPlanTime: $('#finPlanTime'),
243
- finReviewTime: $('#finReviewTime'),
244
- finShadow: $('#finShadow'),
245
- finAdvWrap: $('#finAdvWrap'),
246
- finAdvToggle: $('#finAdvToggle'),
247
- finSymBtn: $('#finSymBtn'),
248
- finRoles: $('#finRoles'),
249
- finWatchClear: $('#finWatchClear'),
250
- finTraderBtn: $('#finTraderBtn'),
251
- finTraderDot: $('#finTraderDot'),
252
- finTraderStatus: $('#finTraderStatus'),
253
- finPosDot: $('#finPosDot'),
254
- finPosInfo: $('#finPosInfo'),
255
- finPosOn: $('#finPosOn'),
256
- finPosSec: $('#finPosSec'),
257
- finPosNote: $('#finPosNote'),
258
- finPosStatus: $('#finPosStatus'),
259
- finPosList: $('#finPosList'),
260
- finPosCount: $('#finPosCount'),
261
- finCloseAll: $('#finCloseAll'),
262
- finOrdList: $('#finOrdList'),
263
- finOrdCount: $('#finOrdCount'),
264
- finSymList: $('#finSymList'),
265
- finLog: $('#finLog'),
266
- finSymAdd: $('#finSymAdd'),
267
- finSymOverlay: $('#finSymOverlay'),
268
- finSymClose: $('#finSymClose'),
269
- finTeamOverlay: $('#finTeamOverlay'),
270
- finTeamClose: $('#finTeamClose'),
271
- finTeamPick: $('#finTeamPick'),
272
- finTeamCount: $('#finTeamCount'),
273
- finSetupBtn: $('#finSetupBtn'),
274
- finSkillsBtn: $('#finSkillsBtn'),
275
- finSkillsOverlay: $('#finSkillsOverlay'),
276
- finSkillsClose: $('#finSkillsClose'),
277
- finSkillsList: $('#finSkillsList'),
278
- finLearnBtn: $('#finLearnBtn'),
279
- finLearnOverlay: $('#finLearnOverlay'),
280
- finLearnClose: $('#finLearnClose'),
281
- finLearnList: $('#finLearnList'),
282
- finLearnCount: $('#finLearnCount'),
283
- finLearnClear: $('#finLearnClear'),
284
- finSymSearch: $('#finSymSearch'),
285
- finSymPick: $('#finSymPick'),
286
- finSymCount: $('#finSymCount'),
287
- finWatchdogCb: $('#finWatchdog'),
288
- finBeOnR: $('#finBeOnR'),
289
- finTrailStartR: $('#finTrailStartR'),
290
- finTrailR: $('#finTrailR'),
291
- finProtectStartR: $('#finProtectStartR'),
292
- finProtectDistR: $('#finProtectDistR'),
293
- finPartialR: $('#finPartialR'),
294
- finPartialPct: $('#finPartialPct'),
295
- finMaxDailyLoss: $('#finMaxDailyLoss'),
296
- finRiskPct: $('#finRiskPct'),
297
- finMaxPerSymbol: $('#finMaxPerSymbol'),
298
- finMaxSameSide: $('#finMaxSameSide'),
299
- finMinMarginLevel: $('#finMinMarginLevel'),
300
- finDailyLossAction: $('#finDailyLossAction'),
301
- finNotifyTarget: $('#finNotifyTarget'),
302
- finNotifyTradeOnly: $('#finNotifyTradeOnly'),
303
- finWatchDot: $('#finWatchDot'),
304
- finWatchInfo: $('#finWatchInfo'),
305
- finAlertList: $('#finAlertList'),
306
- finReportBtn: $('#finReportBtn'),
307
- finStatsInfo: $('#finStatsInfo'),
308
- finStatToday: $('#finStatToday'),
309
- finStatWeek: $('#finStatWeek'),
310
- finStatWin: $('#finStatWin'),
311
- finStatPf: $('#finStatPf'),
312
- finStatDd: $('#finStatDd'),
313
- finStatTrades: $('#finStatTrades'),
314
- finEquityBox: $('#finEquityChart'),
315
194
  stVideoEl: $('#stVideoEl'),
316
195
  stVideoName: $('#stVideoName'),
317
196
  stVideoOpen: $('#stVideoOpen'),
@@ -1082,21 +961,15 @@ async function renderSessions(list) {
1082
961
  }
1083
962
  } catch {}
1084
963
  els.sessList.innerHTML = '';
1085
- /* BEAST FINANCE izolasyonu: finance modunda YALNIZ finance sohbetleri,
1086
- normal modda finance olmayanlar listelenir */
1087
- const finMode = financeModeOn();
1088
- list = (list || []).filter((s) => (finMode ? !!s.finance : !s.finance));
1089
964
  /* elle sıra: sessionOrder'daki id'ler önde (o sırayla), diğerleri engine sırasıyla arkada */
1090
965
  const rank = new Map(sessionOrder.map((id, i) => [id, i]));
1091
966
  const withRank = list.map((s, i) => ({ s, r: rank.has(s.id) ? rank.get(s.id) : 1e6 + i }));
1092
967
  withRank.sort((a, b) => a.r - b.r);
1093
968
  /* aktif botun oturumları — botlar arası geçişte liste de o bota göre değişir.
1094
969
  KATI KURAL: her oturum yalnız BAĞLI OLDUĞU botun listesinde görünür;
1095
- bot kaydı olmayan (eski) oturumlar Beast (varsayılan sahip) altında görünür.
1096
- İSTİSNA — finance modu: mod Trader botuna döner; TÜM finance sohbetleri
1097
- (eski Beast finance'ları dahil) listede kalır, kaybolmaz. */
970
+ bot kaydı olmayan (eski) oturumlar Beast (varsayılan sahip) altında görünür. */
1098
971
  for (const { s } of withRank) {
1099
- if (!finMode && (s.botId || 'beast') !== activeBotId) continue;
972
+ if ((s.botId || 'beast') !== activeBotId) continue;
1100
973
  const row = document.createElement('div');
1101
974
  row.className = 'sess' + (s.id === activeId ? ' active' : '');
1102
975
  row.dataset.sid = s.id; /* en üstteki sohbeti otomatik aktif etmek için */
@@ -1191,8 +1064,6 @@ async function openSession(id) {
1191
1064
  els.msgs.innerHTML = '';
1192
1065
  showEmpty(s.messages.length === 0);
1193
1066
  renderTodos(s.todos || []);
1194
- /* finance modu açıkken oturum değişimi: yeni oturumu da finance bayrağıyla işaretle */
1195
- if (financeModeOn()) beast.financeMode(true, id).catch(() => {});
1196
1067
 
1197
1068
  for (let i = 0; i < s.messages.length; i++) {
1198
1069
  const m = s.messages[i];
@@ -1695,9 +1566,7 @@ async function renderWebSearchPane() {
1695
1566
  });
1696
1567
  }
1697
1568
 
1698
- /* TypeSafe sekmesi (System One / Jev): API anahtarı + model + bağlantı testi.
1699
- Beast Finance ajanları JEV-ONLY çalışır (anahtar zorunlu); diğer ajanlar
1700
- anahtar kayıtlıysa typesafe_decision aracıyla tipli kararlar alır. */
1569
+ /* TypeSafe sekmesi (System One / Jev): API anahtarı + model + bağlantı testi. */
1701
1570
  async function renderTypesafePane() {
1702
1571
  const pane = $('#tab-typesafe');
1703
1572
  if (!pane) return;
@@ -2718,6 +2587,53 @@ async function renderTtsPane() {
2718
2587
  ['en_US-lessac-medium', 'Lessac — English (male)'],
2719
2588
  ['en_US-amy-medium', 'Amy — English (female)'],
2720
2589
  ];
2590
+ /* Supertonic 3: 10 hazır ses (M1-M5 erkek, F1-F5 kadın) + 31 dil */
2591
+ const SUPERTONIC_VOICES = [
2592
+ ['M1', 'M1 — Erkek 1'],
2593
+ ['M2', 'M2 — Erkek 2'],
2594
+ ['M3', 'M3 — Erkek 3'],
2595
+ ['M4', 'M4 — Erkek 4'],
2596
+ ['M5', 'M5 — Erkek 5'],
2597
+ ['F1', 'F1 — Kadın 1'],
2598
+ ['F2', 'F2 — Kadın 2'],
2599
+ ['F3', 'F3 — Kadın 3'],
2600
+ ['F4', 'F4 — Kadın 4'],
2601
+ ['F5', 'F5 — Kadın 5'],
2602
+ ];
2603
+ const SUPERTONIC_LANGS = [
2604
+ ['ar', 'العربية'],
2605
+ ['bg', 'Български'],
2606
+ ['cs', 'Čeština'],
2607
+ ['da', 'Dansk'],
2608
+ ['de', 'Deutsch'],
2609
+ ['el', 'Ελληνικά'],
2610
+ ['en', 'English'],
2611
+ ['es', 'Español'],
2612
+ ['et', 'Eesti'],
2613
+ ['fi', 'Suomi'],
2614
+ ['fr', 'Français'],
2615
+ ['hi', 'हिन्दी'],
2616
+ ['hr', 'Hrvatski'],
2617
+ ['hu', 'Magyar'],
2618
+ ['id', 'Bahasa Indonesia'],
2619
+ ['it', 'Italiano'],
2620
+ ['ja', '日本語'],
2621
+ ['ko', '한국어'],
2622
+ ['lt', 'Lietuvių'],
2623
+ ['lv', 'Latviešu'],
2624
+ ['nl', 'Nederlands'],
2625
+ ['pl', 'Polski'],
2626
+ ['pt', 'Português'],
2627
+ ['ro', 'Română'],
2628
+ ['ru', 'Русский'],
2629
+ ['sk', 'Slovenčina'],
2630
+ ['sl', 'Slovenščina'],
2631
+ ['sv', 'Svenska'],
2632
+ ['tr', 'Türkçe'],
2633
+ ['uk', 'Українська'],
2634
+ ['vi', 'Tiếng Việt'],
2635
+ ['na', 'Otomatik (dil bilinmiyor)'],
2636
+ ];
2721
2637
  const seg = (name, items) => '<div class="voice-seg" id="' + name + '">' +
2722
2638
  items.map(([v, n]) => `<label><input type="radio" name="${name}" value="${v}"/><span>${n}</span></label>`).join('') +
2723
2639
  '</div>';
@@ -2746,7 +2662,7 @@ async function renderTtsPane() {
2746
2662
  </div>
2747
2663
  <label class="lock-row" style="margin-left:auto"><input type="checkbox" id="ttsOn" /><span>${_t('tts_active')}</span></label>
2748
2664
  </div>
2749
- ${seg('ttsEngine', [['edge', 'Edge'], ['piper', 'Piper'], ['openai', 'OpenAI']])}
2665
+ ${seg('ttsEngine', [['edge', 'Edge'], ['piper', 'Piper'], ['supertonic', 'Supertonic'], ['openai', 'OpenAI']])}
2750
2666
  <div class="voice-row">
2751
2667
  <select id="ttsVoiceSel" class="inp"></select>
2752
2668
  </div>
@@ -2760,6 +2676,19 @@ async function renderTtsPane() {
2760
2676
  <div class="voice-slider"><span>${_t('tts_piper_silence')}</span><input id="piperSil" type="range" min="0" max="1" step="0.05" value="0.2"/><b id="piperSilVal">0.20s</b></div>
2761
2677
  <div class="voice-slider"><span>${_t('tts_piper_noise')}</span><input id="piperNoise" type="range" min="0" max="1.2" step="0.05" value="0.667"/><b id="piperNoiseVal">0.67</b></div>
2762
2678
  </div>
2679
+ <div id="stxWrap" style="display:none">
2680
+ <div class="voice-row" style="margin-top:10px">
2681
+ <select id="stxLang" class="inp" style="max-width:240px;flex:none"></select>
2682
+ </div>
2683
+ <div class="voice-status" id="stxStatusRow">
2684
+ <span id="stxDot" class="voice-dot"></span><span id="stxStatusTxt"></span>
2685
+ <button id="stxDlBtn" class="btn ghost" style="margin-left:auto">${_t('tts_supertonic_download')}</button>
2686
+ </div>
2687
+ <div class="voice-sliders" id="stxAdvWrap">
2688
+ <div class="voice-slider"><span>${_t('tts_supertonic_speed')}</span><input id="stxSpeed" type="range" min="0.7" max="2" step="0.05" value="1.05"/><b id="stxSpeedVal">1.05x</b></div>
2689
+ <div class="voice-slider"><span>${_t('tts_supertonic_quality')}</span><input id="stxSteps" type="range" min="5" max="12" step="1" value="8"/><b id="stxStepsVal">8</b></div>
2690
+ </div>
2691
+ </div>
2763
2692
  <div id="ttsOpenaiWrap" style="display:none">
2764
2693
  <div class="voice-row" style="margin-top:10px">
2765
2694
  <input id="ttsUrl" class="inp" placeholder="https://api.openai.com/v1" autocomplete="off" />
@@ -2805,13 +2734,15 @@ async function renderTtsPane() {
2805
2734
  /* ---------- ses listeleri (motor başına) ---------- */
2806
2735
  let edgeVoice = 'tr-TR-AhmetNeural';
2807
2736
  let piperVoice = 'tr_TR-fahrettin-medium';
2737
+ let stxVoice = 'M1';
2738
+ let stxLang = 'tr';
2808
2739
  const fillVoices = () => {
2809
2740
  const eng = ttsEng();
2810
2741
  const sel = $('#ttsVoiceSel');
2811
2742
  if (!sel) return;
2812
- const list = eng === 'piper' ? PIPER_VOICES : EDGE_VOICES;
2743
+ const list = eng === 'piper' ? PIPER_VOICES : eng === 'supertonic' ? SUPERTONIC_VOICES : EDGE_VOICES;
2813
2744
  sel.innerHTML = list.map(([v, n]) => `<option value="${v}">${n}</option>`).join('');
2814
- sel.value = eng === 'piper' ? piperVoice : edgeVoice;
2745
+ sel.value = eng === 'piper' ? piperVoice : eng === 'supertonic' ? stxVoice : edgeVoice;
2815
2746
  sel.style.display = eng === 'openai' ? 'none' : '';
2816
2747
  };
2817
2748
  const captureVoice = () => {
@@ -2819,6 +2750,7 @@ async function renderTtsPane() {
2819
2750
  const sel = $('#ttsVoiceSel');
2820
2751
  if (!sel) return;
2821
2752
  if (eng === 'piper') piperVoice = sel.value || piperVoice;
2753
+ else if (eng === 'supertonic') stxVoice = sel.value || stxVoice;
2822
2754
  else if (eng === 'edge') edgeVoice = sel.value || edgeVoice;
2823
2755
  };
2824
2756
 
@@ -2827,6 +2759,8 @@ async function renderTtsPane() {
2827
2759
  const isPiper = eng === 'piper';
2828
2760
  $('#piperStatusRow').style.display = isPiper ? '' : 'none';
2829
2761
  $('#piperAdvWrap').style.display = isPiper ? '' : 'none';
2762
+ const stxWrap = $('#stxWrap');
2763
+ if (stxWrap) stxWrap.style.display = eng === 'supertonic' ? '' : 'none';
2830
2764
  $('#ttsOpenaiWrap').style.display = eng === 'openai' ? '' : 'none';
2831
2765
  };
2832
2766
 
@@ -2838,6 +2772,13 @@ async function renderTtsPane() {
2838
2772
  if (nv) nv.textContent = Number($('#piperNoise').value).toFixed(2);
2839
2773
  };
2840
2774
 
2775
+ /* Supertonic ince ayar etiketleri (hız/kalite adımı) */
2776
+ const syncStxLabels = () => {
2777
+ const sv = $('#stxSpeedVal'), qv = $('#stxStepsVal');
2778
+ if (sv) sv.textContent = Number($('#stxSpeed').value).toFixed(2) + 'x';
2779
+ if (qv) qv.textContent = String(Math.round(Number($('#stxSteps').value)));
2780
+ };
2781
+
2841
2782
  /* ---------- durum satırları ---------- */
2842
2783
  const refreshPiperStatus = async () => {
2843
2784
  try {
@@ -2850,6 +2791,17 @@ async function renderTtsPane() {
2850
2791
  return st;
2851
2792
  } catch { return null; }
2852
2793
  };
2794
+ const refreshSupertonicStatus = async () => {
2795
+ try {
2796
+ const st = await beast.supertonicStatus(stxVoice);
2797
+ const txt = $('#stxStatusTxt'), dot = $('#stxDot'), btn = $('#stxDlBtn');
2798
+ if (!st) return null;
2799
+ if (txt) txt.textContent = st.installed ? 'Hazır — yerel/offline (Supertonic 3)' : st.installing ? 'İndiriliyor…' : 'İndirilmedi (~400MB)';
2800
+ if (dot) dot.className = 'voice-dot' + (st.installed ? ' ok' : st.installing ? ' warn' : '');
2801
+ if (btn) btn.disabled = !!st.installed;
2802
+ return st;
2803
+ } catch { return null; }
2804
+ };
2853
2805
  const refreshSttStatus = async () => {
2854
2806
  try {
2855
2807
  stt = await beast.sttStatus();
@@ -2871,21 +2823,34 @@ async function renderTtsPane() {
2871
2823
  $('#ttsOn').checked = !!tts.enabled;
2872
2824
  edgeVoice = tts.edgeVoice || edgeVoice;
2873
2825
  piperVoice = tts.piperVoice || piperVoice;
2826
+ stxVoice = tts.supertonicVoice || stxVoice;
2827
+ stxLang = tts.supertonicLang || stxLang;
2874
2828
  $('#piperSpeed').value = Number.isFinite(Number(tts.piperSpeed)) ? tts.piperSpeed : 1;
2875
2829
  $('#piperSil').value = Number.isFinite(Number(tts.piperSilence)) ? tts.piperSilence : 0.2;
2876
2830
  $('#piperNoise').value = Number.isFinite(Number(tts.piperNoise)) ? tts.piperNoise : 0.667;
2831
+ $('#stxSpeed').value = Number.isFinite(Number(tts.supertonicSpeed)) ? tts.supertonicSpeed : 1.05;
2832
+ $('#stxSteps').value = Number.isFinite(Number(tts.supertonicSteps)) ? tts.supertonicSteps : 8;
2877
2833
  $('#ttsChatAuto').checked = !!tts.chatAutoSpeak;
2878
2834
  $('#ttsUrl').value = tts.baseUrl || '';
2879
2835
  $('#ttsKey').value = tts.key || '';
2880
2836
  $('#ttsModel').value = tts.model || 'tts-1';
2881
2837
  $('#ttsVoice').value = tts.voice || 'alloy';
2882
- setSeg('ttsEngine', ['edge', 'piper', 'openai'].includes(tts.engine) ? tts.engine : 'edge');
2838
+ setSeg('ttsEngine', ['edge', 'piper', 'supertonic', 'openai'].includes(tts.engine) ? tts.engine : 'edge');
2883
2839
  } catch {}
2884
2840
  setSeg('sttEngine', (stt && stt.pref) || '');
2841
+ const stxLangSel = $('#stxLang');
2842
+ if (stxLangSel) {
2843
+ stxLangSel.innerHTML = SUPERTONIC_LANGS.map(([v, n]) => `<option value="${v}">${n}</option>`).join('');
2844
+ stxLangSel.value = SUPERTONIC_LANGS.some(([v]) => v === stxLang) ? stxLang : 'tr';
2845
+ stxLang = stxLangSel.value;
2846
+ stxLangSel.addEventListener('change', () => { stxLang = stxLangSel.value; });
2847
+ }
2885
2848
  syncPiperLabels();
2849
+ syncStxLabels();
2886
2850
  fillVoices();
2887
2851
  syncEngineUi();
2888
2852
  await refreshPiperStatus();
2853
+ await refreshSupertonicStatus();
2889
2854
  await refreshSttStatus();
2890
2855
 
2891
2856
  /* ---------- olaylar ---------- */
@@ -2893,6 +2858,10 @@ async function renderTtsPane() {
2893
2858
  const el = $('#' + id);
2894
2859
  if (el) el.addEventListener('input', syncPiperLabels);
2895
2860
  });
2861
+ ['stxSpeed', 'stxSteps'].forEach((id) => {
2862
+ const el = $('#' + id);
2863
+ if (el) el.addEventListener('input', syncStxLabels);
2864
+ });
2896
2865
  document.querySelectorAll('input[name="ttsEngine"]').forEach((r) => {
2897
2866
  r.addEventListener('change', () => {
2898
2867
  captureVoice();
@@ -2900,6 +2869,7 @@ async function renderTtsPane() {
2900
2869
  fillVoices();
2901
2870
  syncEngineUi();
2902
2871
  refreshPiperStatus();
2872
+ refreshSupertonicStatus();
2903
2873
  });
2904
2874
  });
2905
2875
  document.querySelectorAll('input[name="sttEngine"]').forEach((r) => {
@@ -2923,6 +2893,15 @@ async function renderTtsPane() {
2923
2893
  await refreshPiperStatus();
2924
2894
  toast(r && r.ok ? 'Piper hazır ✓ — artık çevrimdışı seslendirme yapılır' : 'Piper indirilemedi: ' + ((r && r.error) || '?'));
2925
2895
  });
2896
+ $('#stxDlBtn').addEventListener('click', async () => {
2897
+ captureVoice();
2898
+ toast('Supertonic indiriliyor… (ilk seferde ~400MB)');
2899
+ const poll = setInterval(refreshSupertonicStatus, 4000);
2900
+ const r = await beast.supertonicInstall(stxVoice).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
2901
+ clearInterval(poll);
2902
+ await refreshSupertonicStatus();
2903
+ toast(r && r.ok ? 'Supertonic hazır ✓ — artık çevrimdışı seslendirme yapılır' : 'Supertonic indirilemedi: ' + ((r && r.error) || '?'));
2904
+ });
2926
2905
  $('#sttDlBtn').addEventListener('click', async () => {
2927
2906
  const s0 = await refreshSttStatus();
2928
2907
  if (s0 && s0.state === 'cloud') { toast('Bulut motoru seçili — indirme gerekmez'); return; }
@@ -2943,6 +2922,10 @@ async function renderTtsPane() {
2943
2922
  piperSpeed: Number($('#piperSpeed').value),
2944
2923
  piperSilence: Number($('#piperSil').value),
2945
2924
  piperNoise: Number($('#piperNoise').value),
2925
+ supertonicVoice: stxVoice,
2926
+ supertonicLang: stxLang,
2927
+ supertonicSpeed: Number($('#stxSpeed').value),
2928
+ supertonicSteps: Number($('#stxSteps').value),
2946
2929
  chatAutoSpeak: $('#ttsChatAuto').checked,
2947
2930
  baseUrl: $('#ttsUrl').value.trim(),
2948
2931
  key: $('#ttsKey').value.trim(),
@@ -2960,10 +2943,16 @@ async function renderTtsPane() {
2960
2943
  const eng = ttsEng();
2961
2944
  const line = eng === 'piper'
2962
2945
  ? 'Merhaba kanka, Piper yerel TTS testi. Ben Beast.'
2963
- : eng === 'openai'
2964
- ? 'Merhaba kanka, OpenAI TTS testi. Ben Beast.'
2965
- : 'Merhaba kanka, Edge TTS testi. Ben Beast.';
2966
- toast(eng === 'piper' ? 'TTS test ediliyor… (Piper ilk seferde indiriyorsa uzun sürebilir)' : 'TTS test ediliyor…');
2946
+ : eng === 'supertonic'
2947
+ ? 'Merhaba kanka, Supertonic yerel TTS testi. Ben Beast.'
2948
+ : eng === 'openai'
2949
+ ? 'Merhaba kanka, OpenAI TTS testi. Ben Beast.'
2950
+ : 'Merhaba kanka, Edge TTS testi. Ben Beast.';
2951
+ toast(eng === 'piper'
2952
+ ? 'TTS test ediliyor… (Piper ilk seferde indiriyorsa uzun sürebilir)'
2953
+ : eng === 'supertonic'
2954
+ ? 'TTS test ediliyor… (Supertonic ilk seferde ~400MB indiriyorsa uzun sürebilir)'
2955
+ : 'TTS test ediliyor…');
2967
2956
  const r = await beast.ttsSpeak(line).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
2968
2957
  if (!(r && r.ok)) { toast('TTS test HATA: ' + ((r && r.error) || '?')); return; }
2969
2958
  try {
@@ -4724,8 +4713,7 @@ async function switchBot(id, opts) {
4724
4713
  }
4725
4714
  } catch {}
4726
4715
  try { localStorage.setItem('beast.activeBot', id); } catch {}
4727
- /* noOpen: oturum açma çağırana bırakılır (finance modu kendi finance
4728
- sohbetini açar/sürdürür — bot geçişi sohbeti değiştirmez) */
4716
+ /* noOpen: oturum açma çağırana bırakılır (bot geçişi sohbeti değiştirmez) */
4729
4717
  if (!(opts && opts.noOpen)) {
4730
4718
  /* o botun en son oturumuna geç; hiç yoksa o bot için yeni sohbet aç */
4731
4719
  try {
@@ -5280,9 +5268,7 @@ async function refreshAgentsPane() {
5280
5268
  kullanıcı çalışırken elle kapatırsa ezilmez; dahili tarayıcı ve IDE modu
5281
5269
  açıkken karışılmaz — orada railBtn ile elle açılır)
5282
5270
  - Tüm işler bitince otomatik KAPANIR (yalnız açılışı biz yaptıysak;
5283
- kullanıcı elle açtıysa açık kalır). BEAST FINANCE istisnası: trade ajanı
5284
- durdurulunca konsol KAPANMAZ — duran ajanların raporları/geçmişi görünür
5285
- kalsın; kullanıcı isterse railBtn ile kendisi kapatır. */
5271
+ kullanıcı elle açtıysa açık kalır). */
5286
5272
  function maybeAutoOpenRail() {
5287
5273
  const hasRunning = agentState.jobs.some((j) => j.status === 'running');
5288
5274
  const hidden = document.body.classList.contains('rail-hidden');
@@ -5292,8 +5278,7 @@ function maybeAutoOpenRail() {
5292
5278
  railPrefBeforeBrowser = true;
5293
5279
  }
5294
5280
  if (!hasRunning && agentState.autoOpened && !hidden &&
5295
- !railManualOpen && !document.body.classList.contains('browser-open') &&
5296
- !financeModeOn()) {
5281
+ !railManualOpen && !document.body.classList.contains('browser-open')) {
5297
5282
  toggleRail(true);
5298
5283
  railManualOpen = false;
5299
5284
  }
@@ -5862,7 +5847,7 @@ document.addEventListener(
5862
5847
 
5863
5848
  /* ---------------- TOOLS konsolu (kişisel araçlar) ----------------
5864
5849
  %APPDATA%\beast\tools\<ad>\tool.json + run.js — kullanıcı kendi araçlarını
5865
- yazar; finance dahil TÜM oturumlar tool__<ad> olarak çağırır. Sol panelde
5850
+ yazar; tüm oturumlar tool__<ad> olarak çağırır. Sol panelde
5866
5851
  buradan görüntülenir, yazılır, test edilir, silinir. */
5867
5852
 
5868
5853
  const toolUi = {
@@ -5879,7 +5864,7 @@ const TOOL_EXAMPLE_CODE =
5879
5864
  "try { raw = fs.readFileSync(0, 'utf8'); } catch {}\n" +
5880
5865
  "let args = {};\n" +
5881
5866
  "try { args = JSON.parse(raw || '{}'); } catch {}\n" +
5882
- "// Buraya kendi işini yaz (MT5 köprüsü, özel API, hesap, sinyal…)\n" +
5867
+ "// Buraya kendi işini yaz (özel API, hesap, veri işleme…)\n" +
5883
5868
  "console.log(JSON.stringify({ ok: true, echo: args }));\n";
5884
5869
 
5885
5870
  let toolRenderTimer = null;
@@ -5899,7 +5884,7 @@ async function renderToolBox() {
5899
5884
  if (!toolUi.tools.length) {
5900
5885
  const d = document.createElement('div');
5901
5886
  d.className = 'tool-empty';
5902
- d.textContent = 'Tool yok — + ile kendi aracını yaz (Finance dahil tüm oturumlarda çalışır)';
5887
+ d.textContent = 'Tool yok — + ile kendi aracını yaz (tüm oturumlarda çalışır)';
5903
5888
  list.appendChild(d);
5904
5889
  return;
5905
5890
  }
@@ -5994,7 +5979,7 @@ if (els.toolSaveBtn) {
5994
5979
  .catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
5995
5980
  if (r && r.ok) {
5996
5981
  closeToolDialog();
5997
- toast('Tool kaydedildi — ajanlar tool__' + r.id + ' olarak çağırabilir (Finance dahil)');
5982
+ toast('Tool kaydedildi — ajanlar tool__' + r.id + ' olarak çağırabilir');
5998
5983
  renderToolBox();
5999
5984
  } else {
6000
5985
  toolErr((r && r.error) || 'Kaydedilemedi');
@@ -6753,18 +6738,6 @@ function fmtWhen(iso) {
6753
6738
  Kompakt liste: durum noktası + ad + bir sonraki tetik; satır → izleyici
6754
6739
  modalı; ▶/× mini kontroller. Yalnız agent modunda görünür (CSS). */
6755
6740
  let lastWatchPaneKey = '';
6756
- /* FİYAT ALARMLARI (Beast Finance): İzleyiciler paneli + modalında watcher'larla
6757
- BİRLİKTE listelenir — tüm alarmlar tek yerde görünür/silinir. */
6758
- function finAlertMeta(a) {
6759
- const dir = a.direction === 'below' ? '≤' : '≥';
6760
- return {
6761
- name: `🔔 ${a.symbol} ${dir} ${a.price}`,
6762
- meta:
6763
- (a.once ? 'tek seferlik' : `her ${Number(a.cooldownMin) || 5} dk`) +
6764
- (a.fires ? ` · ${a.fires}× tetiklendi` : '') +
6765
- (a.note ? ` · ${String(a.note).slice(0, 40)}` : ''),
6766
- };
6767
- }
6768
6741
 
6769
6742
  async function renderWatchersPane(force) {
6770
6743
  const list = els.watchPaneList;
@@ -6772,16 +6745,11 @@ async function renderWatchersPane(force) {
6772
6745
  if (document.body.classList.contains('ide-mode') || document.body.classList.contains('studio-mode') || document.body.classList.contains('sandbox-mode')) return;
6773
6746
  let rows = [];
6774
6747
  try { rows = (await beast.watchersList()) || []; } catch {}
6775
- let alerts = [];
6776
- try {
6777
- const ar = await beast.financeAlertsList();
6778
- alerts = (ar && ar.alerts) || [];
6779
- } catch {}
6780
- const key = JSON.stringify([rows, alerts]);
6748
+ const key = JSON.stringify(rows);
6781
6749
  if (!force && key === lastWatchPaneKey) return;
6782
6750
  lastWatchPaneKey = key;
6783
6751
  list.innerHTML = '';
6784
- if (!rows.length && !alerts.length) {
6752
+ if (!rows.length) {
6785
6753
  list.innerHTML = '<div class="wp-empty">' + _t('w_empty') + '</div>';
6786
6754
  return;
6787
6755
  }
@@ -6829,52 +6797,18 @@ async function renderWatchersPane(force) {
6829
6797
  row.addEventListener('click', () => openWatchModal());
6830
6798
  list.appendChild(row);
6831
6799
  }
6832
- /* Beast Finance FİYAT ALARMLARI: aynı listede — silinebilir, canlı cd/fire bilgisi */
6833
- for (const a of alerts) {
6834
- const info = finAlertMeta(a);
6835
- const row = document.createElement('div');
6836
- row.className = 'wp-row alarm';
6837
- row.title = info.name + (a.note ? ' — ' + a.note : '') + ' · ' + info.meta;
6838
- row.innerHTML =
6839
- `<span class="wp-dot"></span>` +
6840
- `<div class="wp-main">` +
6841
- `<div class="wp-name">${escapeHtml(info.name)}</div>` +
6842
- `<div class="wp-meta">${escapeHtml(info.meta)}</div>` +
6843
- `</div>`;
6844
- const btns = document.createElement('div');
6845
- btns.className = 'wp-btns';
6846
- const d = document.createElement('button');
6847
- d.className = 'wp-btn del';
6848
- d.textContent = '×';
6849
- d.title = _t('cr_del');
6850
- d.addEventListener('click', async (e) => {
6851
- e.stopPropagation();
6852
- await beast.financeAlertRemove(a.id);
6853
- lastWatchPaneKey = '';
6854
- renderWatchersPane(true);
6855
- renderWatchersModal(true);
6856
- });
6857
- btns.appendChild(d);
6858
- row.appendChild(btns);
6859
- list.appendChild(row);
6860
- }
6861
6800
  }
6862
6801
 
6863
6802
  let lastWatchKey = '';
6864
6803
  async function renderWatchersModal(force) {
6865
6804
  let rows = []; try { rows = (await beast.watchersList()) || []; } catch {}
6866
- let alerts = [];
6867
- try {
6868
- const ar = await beast.financeAlertsList();
6869
- alerts = (ar && ar.alerts) || [];
6870
- } catch {}
6871
6805
  /* canlı yenilemede içerik değişmediyse DOM'a dokunma — buton tıklamaları
6872
6806
  yeniden kurulurken yutulmasın */
6873
- const key = JSON.stringify([rows, alerts]);
6807
+ const key = JSON.stringify(rows);
6874
6808
  if (!force && key === lastWatchKey) return;
6875
6809
  lastWatchKey = key;
6876
6810
  els.watchList.innerHTML = '';
6877
- if (!rows.length && !alerts.length) {
6811
+ if (!rows.length) {
6878
6812
  els.watchList.innerHTML = '<div class="mini-empty">' + _t('w_empty') + '</div>';
6879
6813
  return;
6880
6814
  }
@@ -6914,34 +6848,6 @@ async function renderWatchersModal(force) {
6914
6848
  row.appendChild(btns);
6915
6849
  els.watchList.appendChild(row);
6916
6850
  }
6917
- /* Beast Finance FİYAT ALARMLARI: modalda da watcher'larla birlikte */
6918
- for (const a of alerts) {
6919
- const info = finAlertMeta(a);
6920
- const row = document.createElement('div');
6921
- row.className = 'mini-row alarm';
6922
- row.innerHTML =
6923
- `<span class="mr-dot"></span>` +
6924
- `<div class="mr-main">` +
6925
- `<div class="mr-name">${escapeHtml(info.name)}</div>` +
6926
- `<div class="mr-meta">${escapeHtml(info.meta)}` +
6927
- (a.note ? ` · ${escapeHtml(String(a.note).slice(0, 50))}` : '') +
6928
- `</div></div>`;
6929
- const btns = document.createElement('div');
6930
- btns.style.cssText = 'display:flex;gap:6px;flex:none';
6931
- const d = document.createElement('button');
6932
- d.className = 'mr-btn del';
6933
- d.textContent = '×';
6934
- d.title = _t('cr_del');
6935
- d.addEventListener('click', async () => {
6936
- await beast.financeAlertRemove(a.id);
6937
- lastWatchKey = '';
6938
- renderWatchersModal(true);
6939
- renderWatchersPane(true);
6940
- });
6941
- btns.appendChild(d);
6942
- row.appendChild(btns);
6943
- els.watchList.appendChild(row);
6944
- }
6945
6851
  }
6946
6852
 
6947
6853
  /* ---------- İZLEYİCİ LOG MODALI ----------
@@ -7597,11 +7503,6 @@ async function init() {
7597
7503
  showEmpty(true);
7598
7504
  }
7599
7505
 
7600
- /* BEAST FINANCE: kapanışta mod açıktıysa geri yükle */
7601
- try {
7602
- if (localStorage.getItem('beast.financeMode') === '1') await setFinanceMode(true);
7603
- } catch {}
7604
-
7605
7506
  els.newChat.addEventListener('click', async () => {
7606
7507
  try {
7607
7508
  const created = await beast.createSession();
@@ -7737,8 +7638,6 @@ async function init() {
7737
7638
  try {
7738
7639
  state = await beast.refreshModels();
7739
7640
  applyState();
7740
- /* finance modu açıksa TRADE AJANI model listesini de tazele */
7741
- if (financeModeOn()) await finFillModels();
7742
7641
  toast(_t('mr_done'));
7743
7642
  } catch {
7744
7643
  toast(_t('ws_fail_toast'));
@@ -8909,7 +8808,6 @@ function ideModeOn() {
8909
8808
  async function setIdeMode(on) {
8910
8809
  if (on && studioModeOn()) await setStudioMode(false); /* Studio açıkken IDE'ye geçiş — Studio kapanır */
8911
8810
  if (on && sbModeOn()) setSandboxMode(false); /* Sandbox açıkken IDE'ye geçiş — Sandbox kapanır */
8912
- if (on && financeModeOn()) setFinanceMode(false); /* Finance açıkken IDE'ye geçiş — Finance kapanır */
8913
8811
  if (on && mcpModeOn()) setMcpMode(false); /* MCP açıkken IDE'ye geçiş — MCP kapanır */
8914
8812
  document.body.classList.toggle('ide-mode', !!on);
8915
8813
  if (els.ideBtn) els.ideBtn.classList.toggle('on', !!on);
@@ -8985,7 +8883,6 @@ function studioModeOn() {
8985
8883
  async function setStudioMode(on) {
8986
8884
  if (on && ideModeOn()) await setIdeMode(false); /* IDE açıkken Studio'ya geçiş — IDE kapanır */
8987
8885
  if (on && sbModeOn()) setSandboxMode(false); /* Sandbox açıkken Studio'ya geçiş — Sandbox kapanır */
8988
- if (on && financeModeOn()) setFinanceMode(false); /* Finance açıkken Studio'ya geçiş — Finance kapanır */
8989
8886
  if (on && mcpModeOn()) setMcpMode(false); /* MCP açıkken Studio'ya geçiş — MCP kapanır */
8990
8887
  document.body.classList.toggle('studio-mode', !!on);
8991
8888
  if (els.studioBtn) els.studioBtn.classList.toggle('on', !!on);
@@ -9718,2060 +9615,6 @@ async function codeReloadIfOpen(p) {
9718
9615
  $('#ideBtn').addEventListener('click', () => setIdeMode(!ideModeOn()));
9719
9616
  if (els.studioBtn) els.studioBtn.addEventListener('click', () => setStudioMode(!studioModeOn()));
9720
9617
 
9721
- /* ================= BEAST FINANCE MODU =================
9722
- Sol sidebar (sohbet geçmişi + izleyiciler) ve chat AYNI kalır; sağda MT5
9723
- işlem paneli: hesap, pozisyonlar, piyasa, trade ajanı kontrolleri, akış. */
9724
-
9725
- function financeModeOn() {
9726
- return document.body.classList.contains('finance-mode');
9727
- }
9728
-
9729
- let finPollTimer = null;
9730
- let finModelsFilled = false;
9731
- let finLastPrices = new Map(); /* sembol → son bid (renk için) */
9732
- let finCfgCache = null; /* son snapshot cfg — rol→skill modalı bundan okur */
9733
- let finStrategyDirty = false; /* textarea'da kaydedilmeyi bekleyen ajan talimatı var */
9734
- let finPosNoteDirty = false; /* kaydedilmeyi bekleyen pozisyon yöneticisi talimatı */
9735
- const FIN_COLOR_UP = 'fs-up';
9736
- const FIN_COLOR_DOWN = 'fs-down';
9737
-
9738
- function fmtMoney(v, cur) {
9739
- const n = Number(v);
9740
- if (!isFinite(n)) return '—';
9741
- const s = n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
9742
- return cur ? s + ' ' + cur : s;
9743
- }
9744
-
9745
- function finSetDot(state, bridge) {
9746
- if (!els.finDot) return;
9747
- els.finDot.classList.remove('on', 'off', 'busy');
9748
- if (state === true) els.finDot.classList.add('on');
9749
- else if (state === 'busy') els.finDot.classList.add('busy');
9750
- else els.finDot.classList.add('off');
9751
- if (els.finTerm) {
9752
- let label = 'MT5';
9753
- if (bridge) {
9754
- if (bridge.terminal) label = (bridge.terminal.name || 'MT5') + (bridge.terminal.company ? ' · ' + bridge.terminal.company : '');
9755
- else if (bridge.error) label = bridge.error.length > 60 ? bridge.error.slice(0, 60) + '…' : bridge.error;
9756
- else if (bridge.running) label = 'MT5 terminali bekleniyor…';
9757
- else label = 'Köprü kapalı';
9758
- }
9759
- els.finTerm.textContent = label;
9760
- els.finTerm.title = bridge && bridge.error ? bridge.error : label;
9761
- }
9762
- }
9763
-
9764
- function finKvSet(el, text, cls) {
9765
- if (!el) return;
9766
- el.textContent = text;
9767
- el.classList.remove('pos', 'neg');
9768
- if (cls) el.classList.add(cls);
9769
- }
9770
-
9771
- function finRenderAccount(acc) {
9772
- const cur = acc && acc.currency ? acc.currency : '';
9773
- finKvSet(els.finBalance, acc ? fmtMoney(acc.balance, cur) : '—');
9774
- finKvSet(els.finEquity, acc ? fmtMoney(acc.equity, cur) : '—');
9775
- const p = acc ? Number(acc.profit) || 0 : NaN;
9776
- finKvSet(els.finProfit, isFinite(p) ? (p > 0 ? '+' : '') + fmtMoney(p, cur) : '—', isFinite(p) && p !== 0 ? (p > 0 ? 'pos' : 'neg') : null);
9777
- finKvSet(els.finFree, acc ? fmtMoney(acc.margin_free, cur) : '—');
9778
- }
9779
-
9780
- function finRenderPositions(list) {
9781
- if (!els.finPosList) return;
9782
- els.finPosList.textContent = '';
9783
- if (els.finPosCount) els.finPosCount.textContent = list && list.length ? '(' + list.length + ')' : '';
9784
- /* toplu kapatma butonu: pozisyon yoksa pasif */
9785
- if (els.finCloseAll) els.finCloseAll.disabled = !(list && list.length);
9786
- if (!list || !list.length) {
9787
- const d = document.createElement('div');
9788
- d.className = 'fin-empty';
9789
- d.textContent = 'Açık pozisyon yok';
9790
- els.finPosList.appendChild(d);
9791
- return;
9792
- }
9793
- for (const p of list) {
9794
- const row = document.createElement('div');
9795
- row.className = 'fin-pos';
9796
- const isBuy = String(p.type) === '0' || Number(p.type) === 0;
9797
- const profit = Number(p.profit) || 0;
9798
- row.innerHTML =
9799
- '<span class="fp-sym" title="ticket ' + p.ticket + '">' + (p.symbol || '?') + '</span>' +
9800
- '<span class="fp-dir ' + (isBuy ? 'buy">BUY' : 'sell">SELL') + '</span>' +
9801
- '<span class="fp-meta">' + (Number(p.volume) || 0) + ' lot · ' + (Number(p.price_open) || 0) + ' → ' + (Number(p.price_current) || 0) +
9802
- (p.sl ? ' · SL ' + p.sl : '') + (p.tp ? ' · TP ' + p.tp : '') + '</span>' +
9803
- '<span class="fp-pl ' + (profit >= 0 ? 'pos' : 'neg') + '">' + (profit >= 0 ? '+' : '') + profit.toFixed(2) + '</span>';
9804
- const btn = document.createElement('button');
9805
- btn.className = 'fp-close';
9806
- btn.title = 'Pozisyonu kapat';
9807
- btn.textContent = '✕';
9808
- btn.addEventListener('click', async () => {
9809
- const ok = await uiConfirm((p.symbol || '') + ' pozisyonu kapatılsın mı? (ticket ' + p.ticket + ')', 'Kapat');
9810
- if (!ok) return;
9811
- const r = await beast.financeClose(p.ticket).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
9812
- if (r && r.ok) toast('Pozisyon kapatıldı');
9813
- else toast('Kapatılamadı: ' + ((r && r.error) || '?'));
9814
- finSnapshot();
9815
- });
9816
- row.appendChild(btn);
9817
- els.finPosList.appendChild(row);
9818
- }
9819
- }
9820
-
9821
- /* MT5 bekleyen emir tipi → etiket + renk sınıfı (0..7) */
9822
- const FIN_ORDER_TYPES = {
9823
- 0: { t: 'BUY', c: 'buy' },
9824
- 1: { t: 'SELL', c: 'sell' },
9825
- 2: { t: 'BUY LIMIT', c: 'buy' },
9826
- 3: { t: 'SELL LIMIT', c: 'sell' },
9827
- 4: { t: 'BUY STOP', c: 'buy' },
9828
- 5: { t: 'SELL STOP', c: 'sell' },
9829
- 6: { t: 'BUY STP-LMT', c: 'buy' },
9830
- 7: { t: 'SELL STP-LMT', c: 'sell' },
9831
- };
9832
-
9833
- function finRenderOrders(list, err) {
9834
- if (!els.finOrdList) return;
9835
- els.finOrdList.textContent = '';
9836
- if (els.finOrdCount) els.finOrdCount.textContent = list && list.length ? '(' + list.length + ')' : '';
9837
- if (!list || !list.length) {
9838
- const d = document.createElement('div');
9839
- d.className = 'fin-empty';
9840
- d.textContent = err ? 'Emir listesi alınamadı: ' + err : 'Bekleyen emir yok';
9841
- els.finOrdList.appendChild(d);
9842
- return;
9843
- }
9844
- for (const o of list) {
9845
- const row = document.createElement('div');
9846
- row.className = 'fin-pos fin-ord';
9847
- const ot = FIN_ORDER_TYPES[Number(o.type)] || { t: String(o.type ?? '?'), c: '' };
9848
- const vol = Number(o.volume_current) || Number(o.volume_initial) || 0;
9849
- row.innerHTML =
9850
- '<span class="fp-sym" title="ticket ' + o.ticket + '">' + escapeHtml(String(o.symbol || '?')) + '</span>' +
9851
- '<span class="fp-dir ' + ot.c + '">' + ot.t + '</span>' +
9852
- '<span class="fp-meta">' + vol + ' lot @ ' + (Number(o.price_open) || 0) +
9853
- (o.sl ? ' · SL ' + o.sl : '') + (o.tp ? ' · TP ' + o.tp : '') + '</span>';
9854
- const btn = document.createElement('button');
9855
- btn.className = 'fp-close';
9856
- btn.title = 'Bekleyen emri iptal et';
9857
- btn.textContent = '✕';
9858
- btn.addEventListener('click', async () => {
9859
- const ok = await uiConfirm((o.symbol || '') + ' bekleyen emri iptal edilsin mi? (ticket ' + o.ticket + ')', 'İptal Et');
9860
- if (!ok) return;
9861
- const r = await beast.financeCancel(o.ticket).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
9862
- if (r && r.ok) toast('Emir iptal edildi');
9863
- else toast('İptal edilemedi: ' + ((r && r.error) || '?'));
9864
- finSnapshot();
9865
- });
9866
- row.appendChild(btn);
9867
- els.finOrdList.appendChild(row);
9868
- }
9869
- }
9870
-
9871
- function finRenderSymbols(list) {
9872
- if (!els.finSymList) return;
9873
- els.finSymList.textContent = '';
9874
- if (!list || !list.length) {
9875
- const d = document.createElement('div');
9876
- d.className = 'fin-empty';
9877
- d.textContent = 'Sembol verisi yok';
9878
- els.finSymList.appendChild(d);
9879
- return;
9880
- }
9881
- for (const s of list) {
9882
- if (s && s.missing) continue;
9883
- const row = document.createElement('div');
9884
- row.className = 'fin-sym';
9885
- const bid = Number(s.bid) || 0;
9886
- const prev = finLastPrices.get(s.symbol);
9887
- let cls = '';
9888
- if (prev != null && bid !== prev) cls = bid > prev ? FIN_COLOR_UP : FIN_COLOR_DOWN;
9889
- if (bid) finLastPrices.set(s.symbol, bid);
9890
- const digits = s.digits != null ? Number(s.digits) : 5;
9891
- row.innerHTML =
9892
- '<span class="fs-name">' + (s.symbol || '?') + '</span>' +
9893
- '<span class="fs-price ' + cls + '">' + bid.toFixed(digits) + '</span>' +
9894
- '<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>';
9895
- els.finSymList.appendChild(row);
9896
- }
9897
- }
9898
-
9899
- function finSymBtnLabel(list) {
9900
- const l = Array.isArray(list) ? list : [];
9901
- if (!l.length) return 'Seç…';
9902
- const head = l.slice(0, 3).join(', ');
9903
- return l.length > 3 ? head + ' +' + (l.length - 3) : head;
9904
- }
9905
-
9906
- function finSymBtnUpdate() {
9907
- if (els.finSymBtn) {
9908
- els.finSymBtn.textContent = finSymBtnLabel(finWatch);
9909
- els.finSymBtn.title = finWatch.length
9910
- ? 'İzleme listesi: ' + finWatch.join(', ') + ' — değiştirmek için tıkla'
9911
- : 'MT5 sembol listesinden seç — tıkla';
9912
- }
9913
- }
9914
-
9915
- /* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı — mt5_limits symbol ile):
9916
- genel min/max'tan sapmalar tek satırda gösterilir; trader sıfırlarsa satır kaybolur */
9917
- function finSymLimitsRender(map) {
9918
- const box = els.finSymLimits;
9919
- if (!box) return;
9920
- const rows = Object.entries(map && typeof map === 'object' ? map : {})
9921
- .map(([sym, v]) => {
9922
- const o = v && typeof v === 'object' ? v : {};
9923
- const min = Number(o.minLot) > 0 ? Number(o.minLot) : 0;
9924
- const max = Number(o.maxLot) > 0 ? Number(o.maxLot) : 0;
9925
- if (!min && !max) return '';
9926
- if (min && max) return sym + ' ' + min + '–' + max;
9927
- return sym + (min ? ' ≥' + min : ' ≤' + max);
9928
- })
9929
- .filter(Boolean);
9930
- if (!rows.length) {
9931
- box.hidden = true;
9932
- box.textContent = '';
9933
- return;
9934
- }
9935
- box.hidden = false;
9936
- box.textContent = 'Sembol lot limitleri (trader): ' + rows.join(' · ');
9937
- }
9938
-
9939
- function finTraderInputsSet(cfg) {
9940
- if (!cfg) return;
9941
- if (!finWatchDirty) finWatchSet(cfg.symbols);
9942
- /* sembol geçmişi (silinenler dahil) — seçicide "SON SEMBOLLER" olarak çıkar */
9943
- finSymHist = Array.isArray(cfg.symbolHistory)
9944
- ? cfg.symbolHistory.map((s) => String(s).trim().toUpperCase()).filter(Boolean).slice(0, 40)
9945
- : [];
9946
- const ae = document.activeElement;
9947
- if (els.finInterval && ae !== els.finInterval) els.finInterval.value = cfg.intervalSec || 120;
9948
- if (els.finMinLot && ae !== els.finMinLot) els.finMinLot.value = cfg.minLot || 0.01;
9949
- if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
9950
- finSymLimitsRender(cfg.symbolLimits);
9951
- if (els.finStrategy && ae !== els.finStrategy && !finStrategyDirty) els.finStrategy.value = cfg.strategy || '';
9952
- if (els.finPosNote && ae !== els.finPosNote && !finPosNoteDirty) els.finPosNote.value = cfg.posManagerNote || '';
9953
- if (els.finPosOn && ae !== els.finPosOn) els.finPosOn.checked = cfg.posManagerEnabled !== false;
9954
- if (els.finPosSec && ae !== els.finPosSec) els.finPosSec.value = Number(cfg.posManagerSec) || 5;
9955
- if (els.finMaxTradesDay && ae !== els.finMaxTradesDay) els.finMaxTradesDay.value = Number(cfg.maxTradesPerDay) || 0;
9956
- if (els.finLossStreak && ae !== els.finLossStreak) els.finLossStreak.value = Number(cfg.lossStreakLimit) || 0;
9957
- if (els.finLossStreakPause && ae !== els.finLossStreakPause) els.finLossStreakPause.value = Number(cfg.lossStreakPauseMin) || 0;
9958
- if (els.finReentry && ae !== els.finReentry) els.finReentry.value = Number(cfg.reentryCooldownMin) || 0;
9959
- if (els.finMaxPerCurrency && ae !== els.finMaxPerCurrency) els.finMaxPerCurrency.value = Number(cfg.maxPerCurrency) || 0;
9960
- if (els.finPlanTime && ae !== els.finPlanTime) els.finPlanTime.value = String(cfg.planTime || '');
9961
- if (els.finReviewTime && ae !== els.finReviewTime) els.finReviewTime.value = String(cfg.reviewTime || '');
9962
- if (els.finShadow && ae !== els.finShadow) els.finShadow.checked = !!cfg.shadowMode;
9963
- finSymBtnUpdate();
9964
- if (els.finTraderModel && ae !== els.finTraderModel && cfg.traderSel) els.finTraderModel.value = cfg.traderSel;
9965
- }
9966
-
9967
- /* ANALİZ EKİBİ: tek satır çoklu seçim picker'ı — hangi uzman ajanların
9968
- koşacağını seçersin (Risk / Teknik / Haber / Görsel); sayaç rozeti seçili
9969
- ajan sayısını gösterir. Katalog sunucudan gelir (finance:state roles). */
9970
- /* Ekip düğmesi ikonu: emoji yerine çizgi SVG (beyin) — tema rengiyle uyumlu */
9971
- const FIN_TEAM_SVG =
9972
- '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px">' +
9973
- '<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"/>' +
9974
- '<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"/>' +
9975
- '<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/>' +
9976
- '<path d="M17.599 6.5a3 3 0 0 0 .399-1.375"/>' +
9977
- '<path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"/>' +
9978
- '<path d="M3.477 10.896a4 4 0 0 1 .585-.396"/>' +
9979
- '<path d="M19.938 10.5a4 4 0 0 1 .585.396"/>' +
9980
- '<path d="M6 18a4 4 0 0 1-1.967-.516"/>' +
9981
- '<path d="M19.967 17.484A4 4 0 0 1 18 18"/></svg>';
9982
- function finTeamBtnPaint(btn, n) {
9983
- if (btn) btn.innerHTML = FIN_TEAM_SVG + '<span>Ekip: ' + n + ' ajan</span>';
9984
- }
9985
- let finRolesCatalog = [
9986
- { id: 'risk', label: 'Risk Ajanı', desc: 'marj/kaldıraç/SL disiplini, exposure ve günlük kayıp hızı denetimi' },
9987
- { id: 'technic', label: 'Teknik Analiz', desc: 'trend/yapı/destek-direnç/momentum okuma, AL-SAT-BEKLE önerileri' },
9988
- { id: 'macro', label: 'Haber / Makro', desc: 'haber akışı + ekonomik takvim, DXY/emtia bağıntıları, yön eğilimi' },
9989
- { id: 'visual', label: 'Görsel Ajan', desc: 'MT5 grafiği/ekran görüntüsü — trend, formasyon, seviye ve mum yapısını görsel doğrulama' },
9990
- ];
9991
- let finRolesAuto = ['technic', 'risk', 'macro', 'visual'];
9992
- let finRolesDirty = false; /* tık→kaydet arası snapshot ezmesin */
9993
- let finRolesSyncTimer = null;
9994
-
9995
- function finRoleLabel(id) {
9996
- const d = (finRolesCatalog || []).find((x) => x.id === String(id || ''));
9997
- return d ? d.label : String(id || '');
9998
- }
9999
-
10000
- /* Etkin ekip listesi: oto modda ilk N rol, elle modda seçilen roller */
10001
- function finTeamSelectedIds(cfg) {
10002
- const c = cfg || finCfgCache || {};
10003
- if (c.analysisAuto !== false) {
10004
- const n = Math.max(0, Math.min((finRolesAuto || []).length || 4, Math.round(Number(c.analysisCount) || 0)));
10005
- return (finRolesAuto || []).slice(0, n);
10006
- }
10007
- return (Array.isArray(c.analysisTeam) ? c.analysisTeam : []).filter((id) =>
10008
- (finRolesCatalog || []).some((d) => d.id === id)
10009
- );
10010
- }
10011
-
10012
- /* tek satır: buton (seçili sayı) + seçili rol adları özeti */
10013
- function finRenderRoles(cfg) {
10014
- const box = els.finRoles;
10015
- if (!box) return;
10016
- if (finRolesDirty) return; /* bekleyen kullanıcı değişikliği korunur */
10017
- const selected = finTeamSelectedIds(cfg);
10018
- box.innerHTML = '';
10019
- const wrap = document.createElement('div');
10020
- wrap.className = 'fin-team';
10021
- const btn = document.createElement('button');
10022
- btn.type = 'button';
10023
- btn.className = 'fin-sym-select fin-team-btn';
10024
- finTeamBtnPaint(btn, selected.length);
10025
- btn.title = 'Uzman ajanları çoktan seç — seçilen her rol AYRI sürekli ajan olarak koşar (işlem açmaz)';
10026
- const hint = document.createElement('span');
10027
- hint.className = 'fin-team-hint';
10028
- hint.textContent = selected.length
10029
- ? ((cfg && cfg.analysisAuto !== false) ? 'oto: ' : '') + selected.map(finRoleLabel).join(', ')
10030
- : 'ekip kapalı — sadece trader koşar';
10031
- btn.addEventListener('click', finTeamPickerOpen);
10032
- wrap.appendChild(btn);
10033
- wrap.appendChild(hint);
10034
- box.appendChild(wrap);
10035
- }
10036
-
10037
- /* picker modalı: çoklu checkbox listesi — her değişiklik ANINDA kaydedilir */
10038
- function finTeamPickerPaint() {
10039
- const pick = els.finTeamPick;
10040
- if (!pick) return;
10041
- const sel = new Set(finTeamSelectedIds());
10042
- if (els.finTeamCount) els.finTeamCount.textContent = sel.size ? sel.size + ' seçili' : 'kapalı';
10043
- pick.innerHTML = '';
10044
- for (const d of finRolesCatalog || []) {
10045
- const row = document.createElement('label');
10046
- row.className = 'fin-team-item';
10047
- row.title = d.desc || '';
10048
- const cb = document.createElement('input');
10049
- cb.type = 'checkbox';
10050
- cb.checked = sel.has(d.id);
10051
- cb.dataset.role = d.id;
10052
- cb.addEventListener('change', () => {
10053
- const ids = [...pick.querySelectorAll('input[data-role]')].filter((x) => x.checked).map((x) => x.dataset.role);
10054
- finRolesDirty = true;
10055
- finCfgCache = finCfgCache || {};
10056
- finCfgCache.analysisAuto = false;
10057
- finCfgCache.analysisTeam = ids;
10058
- finSaveCfg({ analysisAuto: false, analysisTeam: ids });
10059
- if (els.finTeamCount) els.finTeamCount.textContent = ids.length ? ids.length + ' seçili' : 'kapalı';
10060
- const btn = els.finRoles && els.finRoles.querySelector('.fin-team-btn');
10061
- const hint = els.finRoles && els.finRoles.querySelector('.fin-team-hint');
10062
- finTeamBtnPaint(btn, ids.length);
10063
- if (hint) hint.textContent = ids.length ? ids.map(finRoleLabel).join(', ') : 'ekip kapalı — sadece trader koşar';
10064
- toast(ids.length ? 'Analiz ekibi: ' + ids.length + ' ajan — ' + ids.map(finRoleLabel).join(', ') : 'Analiz ekibi kapatıldı');
10065
- clearTimeout(finRolesSyncTimer);
10066
- finRolesSyncTimer = setTimeout(() => { finRolesDirty = false; finSnapshot(); }, 900);
10067
- });
10068
- const txt = document.createElement('span');
10069
- txt.className = 'fin-team-txt';
10070
- txt.innerHTML = '<b>' + escapeHtml(d.label) + '</b><i>' + escapeHtml(d.desc || '') + '</i>';
10071
- row.appendChild(cb);
10072
- row.appendChild(txt);
10073
- pick.appendChild(row);
10074
- }
10075
- }
10076
-
10077
- function finTeamPickerOpen() {
10078
- if (!els.finTeamOverlay) return;
10079
- finTeamPickerPaint();
10080
- els.finTeamOverlay.hidden = false;
10081
- }
10082
-
10083
- function finTeamPickerClose() {
10084
- if (els.finTeamOverlay) els.finTeamOverlay.hidden = true;
10085
- }
10086
-
10087
- function finRenderTrader(trader, cfg) {
10088
- if (!els.finTraderBtn) return;
10089
- const on = !!(trader && trader.on);
10090
- els.finTraderBtn.classList.toggle('stop', on);
10091
- els.finTraderBtn.innerHTML = on ? '■ Durdur' : '▶ Ajanı Başlat';
10092
- if (els.finTraderDot) {
10093
- els.finTraderDot.classList.remove('on', 'off', 'busy');
10094
- if (on) els.finTraderDot.classList.add(trader.busy ? 'busy' : 'on');
10095
- else els.finTraderDot.classList.add('off');
10096
- }
10097
- if (els.finTraderStatus) {
10098
- els.finTraderStatus.classList.toggle('on', on);
10099
- els.finTraderStatus.classList.toggle('busy', !!(on && trader && trader.busy));
10100
- let t = '';
10101
- if (trader && trader.lastAt) {
10102
- const d = new Date(trader.lastAt);
10103
- t = ' · son tur ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
10104
- }
10105
- /* ekip bilgisi: otomatik modda sayı, elle seçimde seçili rol sayısı */
10106
- const team = cfg && cfg.analysisAuto
10107
- ? Math.round(Number(cfg.analysisCount) || 0)
10108
- : (cfg && Array.isArray(cfg.analysisTeam) ? cfg.analysisTeam.length : 0);
10109
- const teamTxt = team ? ' · ekip ' + team + ' ajan' + (cfg && cfg.analysisAuto ? ' (oto)' : '') : '';
10110
- els.finTraderStatus.textContent = on
10111
- ? (trader.busy ? 'Tur çalışıyor…' : 'Beklemede — sıradaki tur ' + (cfg && cfg.intervalSec ? cfg.intervalSec + ' sn' : '')) + ' · tur #' + (trader.rounds || 0) + ' · OTONOM' + teamTxt + t
10112
- : 'Kapalı · OTONOM' + teamTxt;
10113
- }
10114
- }
10115
-
10116
- /* POZİSYON YÖNETİCİSİ kartı: açık pozisyon varken 5 sn Jev turu durumu */
10117
- function finRenderPosManager(pm, cfg) {
10118
- if (!pm) return;
10119
- const enabled = cfg ? cfg.posManagerEnabled !== false : pm.enabled !== false;
10120
- const pos = Number(pm.positions) || 0;
10121
- const sec = Number(pm.sec || (cfg && cfg.posManagerSec)) || 5;
10122
- if (els.finPosDot) {
10123
- els.finPosDot.classList.remove('on', 'off', 'busy');
10124
- els.finPosDot.classList.add(!enabled ? 'off' : pm.busy ? 'busy' : pos ? 'on' : 'off');
10125
- els.finPosDot.title = !enabled
10126
- ? 'Pozisyon yöneticisi kapalı'
10127
- : pos
10128
- ? 'İzliyor — ' + pos + ' açık pozisyon (' + sec + ' sn Jev turu)'
10129
- : 'Açık pozisyon yok — beklemede';
10130
- }
10131
- if (els.finPosInfo) els.finPosInfo.textContent = pm.rounds ? '· tur ' + pm.rounds : '';
10132
- if (els.finPosStatus) {
10133
- let t;
10134
- if (!enabled) t = 'Kapalı — kutucuktan aç';
10135
- else if (pm.busy) t = 'Tur çalışıyor…';
10136
- else if (!pos) t = 'Beklemede — açık pozisyon yok (' + sec + ' sn yoklama)';
10137
- else t = 'İzliyor · ' + pos + ' pozisyon · ' + sec + ' sn';
10138
- if (pm.lastAt) {
10139
- const d = new Date(pm.lastAt);
10140
- const p = (x) => String(x).padStart(2, '0');
10141
- t += ' · son tur ' + p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds());
10142
- }
10143
- if (pm.lastErr) t += ' · hata: ' + String(pm.lastErr).slice(0, 60);
10144
- els.finPosStatus.textContent = t;
10145
- els.finPosStatus.classList.toggle('on', enabled && !!pos);
10146
- els.finPosStatus.classList.toggle('busy', !!pm.busy);
10147
- }
10148
- }
10149
-
10150
- /* ---- RİSK OTOMASYONU + PERFORMANS panosu ---- */
10151
- function finDayKey(ms) {
10152
- const d = new Date(Number(ms) || 0);
10153
- const p = (x) => String(x).padStart(2, '0');
10154
- return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate());
10155
- }
10156
-
10157
- function finRenderAutomation(cfg, watch) {
10158
- if (!cfg) return;
10159
- const ae = document.activeElement;
10160
- if (els.finWatchdogCb) els.finWatchdogCb.checked = finView.risk && cfg.watchdog !== false;
10161
- const setNum = (el, v) => {
10162
- if (el && ae !== el) el.value = v;
10163
- };
10164
- setNum(els.finBeOnR, cfg.beOnR != null ? cfg.beOnR : 1);
10165
- setNum(els.finTrailStartR, cfg.trailStartR != null ? cfg.trailStartR : 1.5);
10166
- setNum(els.finTrailR, cfg.trailR != null ? cfg.trailR : 0.5);
10167
- setNum(els.finProtectStartR, cfg.protectStartR != null ? cfg.protectStartR : 0.3);
10168
- setNum(els.finProtectDistR, cfg.protectDistR != null ? cfg.protectDistR : 0.15);
10169
- setNum(els.finPartialR, cfg.partialR != null ? cfg.partialR : 0);
10170
- setNum(els.finPartialPct, cfg.partialPct != null ? cfg.partialPct : 50);
10171
- setNum(els.finMaxDailyLoss, cfg.maxDailyLossPct != null ? cfg.maxDailyLossPct : 3);
10172
- setNum(els.finRiskPct, cfg.riskPerTradePct != null ? cfg.riskPerTradePct : 1);
10173
- setNum(els.finMaxPerSymbol, cfg.maxPerSymbol != null ? cfg.maxPerSymbol : 2);
10174
- setNum(els.finMaxSameSide, cfg.maxSameSide != null ? cfg.maxSameSide : 3);
10175
- setNum(els.finMinMarginLevel, cfg.minMarginLevel != null ? cfg.minMarginLevel : 150);
10176
- if (els.finDailyLossAction && ae !== els.finDailyLossAction) els.finDailyLossAction.value = cfg.dailyLossAction || 'stop';
10177
- if (els.finNotifyTarget && ae !== els.finNotifyTarget) els.finNotifyTarget.value = cfg.notifyTarget || 'auto';
10178
- if (els.finNotifyTradeOnly && ae !== els.finNotifyTradeOnly) els.finNotifyTradeOnly.checked = cfg.notifyTradeOnly !== false;
10179
- if (els.finWatchDot) {
10180
- els.finWatchDot.classList.remove('on', 'off', 'busy');
10181
- els.finWatchDot.classList.add(watch && watch.on ? 'on' : 'off');
10182
- els.finWatchDot.title = watch && watch.on ? 'Koruma döngüsü çalışıyor (5 sn)' : 'Koruma döngüsü kapalı';
10183
- }
10184
- if (els.finWatchInfo) {
10185
- els.finWatchInfo.textContent = watch && watch.on ? '· izlenen ' + (watch.managed || 0) : '';
10186
- }
10187
- finTradeHoursPaint();
10188
- }
10189
-
10190
- function finSparkline(points) {
10191
- const list = (Array.isArray(points) ? points : []).filter((p) => p && isFinite(Number(p.equity)));
10192
- if (list.length < 2) return '<div class="fin-empty">Equity verisi toplanıyor…</div>';
10193
- const w = 100;
10194
- const h = 28;
10195
- const vals = list.map((p) => Number(p.equity));
10196
- let min = Math.min(...vals);
10197
- let max = Math.max(...vals);
10198
- if (max - min < 1e-9) max = min + 1;
10199
- const step = w / (list.length - 1);
10200
- const pts = vals
10201
- .map((v, i) => (i * step).toFixed(2) + ',' + (h - 2 - ((v - min) / (max - min)) * (h - 4)).toFixed(2))
10202
- .join(' ');
10203
- const up = vals[vals.length - 1] >= vals[0];
10204
- return '<svg viewBox="0 0 ' + w + ' ' + h + '" preserveAspectRatio="none" class="' + (up ? 'up' : 'down') + '">' +
10205
- '<polyline points="' + pts + '" fill="none" stroke="currentColor" stroke-width="1.4" vector-effect="non-scaling-stroke" /></svg>';
10206
- }
10207
-
10208
- function finRenderStats(stats, equity, cur) {
10209
- const s = stats || null;
10210
- if (els.finEquityBox) {
10211
- els.finEquityBox.innerHTML = finSparkline(equity);
10212
- }
10213
- if (els.finStatsInfo) {
10214
- els.finStatsInfo.textContent = s && s.at
10215
- ? '· son 90 gün · ' + new Date(s.at).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })
10216
- : '';
10217
- }
10218
- if (!s) {
10219
- finKvSet(els.finStatToday, '—');
10220
- finKvSet(els.finStatWeek, '—');
10221
- finKvSet(els.finStatWin, '—');
10222
- finKvSet(els.finStatPf, '—');
10223
- finKvSet(els.finStatDd, '—');
10224
- finKvSet(els.finStatTrades, '—');
10225
- return;
10226
- }
10227
- const byDay = s.byDay && typeof s.byDay === 'object' ? s.byDay : {};
10228
- const today = Number(byDay[finDayKey(Date.now())]) || 0;
10229
- const week = Object.entries(byDay)
10230
- .filter(([d]) => Date.now() - new Date(d + 'T00:00:00').getTime() < 7 * 86400000)
10231
- .reduce((a, [, v]) => a + (Number(v) || 0), 0);
10232
- const fmt = (n) => (n > 0 ? '+' : '') + fmtMoney(n, cur);
10233
- finKvSet(els.finStatToday, fmt(today), today !== 0 ? (today > 0 ? 'pos' : 'neg') : null);
10234
- finKvSet(els.finStatWeek, fmt(week), week !== 0 ? (week > 0 ? 'pos' : 'neg') : null);
10235
- finKvSet(els.finStatWin, '%' + (Number(s.winRate) || 0) + ' (' + (Number(s.wins) || 0) + '/' + (Number(s.losses) || 0) + ')');
10236
- finKvSet(els.finStatPf, s.profitFactor == null ? '∞' : String(s.profitFactor));
10237
- const dd = s.drawdown || {};
10238
- finKvSet(els.finStatDd, '%' + (Number(dd.maxPct) || 0), (Number(dd.maxPct) || 0) > 0 ? 'neg' : null);
10239
- finKvSet(els.finStatTrades, String(Number(s.trades) || 0));
10240
- }
10241
-
10242
- function finRenderAlerts(list) {
10243
- const box = els.finAlertList;
10244
- if (!box) return;
10245
- box.textContent = '';
10246
- if (!list || !list.length) {
10247
- const d = document.createElement('div');
10248
- d.className = 'fin-empty';
10249
- d.textContent = 'Aktif fiyat alarmı yok';
10250
- box.appendChild(d);
10251
- return;
10252
- }
10253
- for (const a of list) {
10254
- const row = document.createElement('div');
10255
- row.className = 'fin-alert';
10256
- row.innerHTML =
10257
- '<span class="fa-sym">' + escapeHtml(String(a.symbol || '?')) + '</span>' +
10258
- '<span class="fa-cond">' + (a.direction === 'below' ? '≤' : '≥') + ' ' + (Number(a.price) || 0) + '</span>' +
10259
- '<span class="fa-note">' + (a.once ? 'tek seferlik' : `her ${Number(a.cooldownMin) || 5} dk`) + (a.fires ? ` · ${a.fires}×` : '') + '</span>' +
10260
- (a.note ? '<span class="fa-note">' + escapeHtml(String(a.note)) + '</span>' : '');
10261
- box.appendChild(row);
10262
- }
10263
- }
10264
-
10265
- async function finFillModels() {
10266
- if (!els.finTraderModel) return;
10267
- /* mevcut seçim korunur — yenilemede model kaybolursa "Genel aktif model"e düşer */
10268
- const cur = els.finTraderModel.value || (finCfgCache && finCfgCache.traderSel) || '';
10269
- let models = [];
10270
- try {
10271
- const st = await beast.getState();
10272
- models = (st && st.models) || [];
10273
- } catch {}
10274
- els.finTraderModel.textContent = '';
10275
- const optAuto = document.createElement('option');
10276
- optAuto.value = '';
10277
- optAuto.textContent = 'Genel aktif model';
10278
- if (!cur) optAuto.selected = true;
10279
- els.finTraderModel.appendChild(optAuto);
10280
- for (const m of models) {
10281
- const o = document.createElement('option');
10282
- o.value = m.sel;
10283
- o.textContent = (m.providerName || m.sel) + ' · ' + m.model;
10284
- if (m.sel === cur) o.selected = true;
10285
- els.finTraderModel.appendChild(o);
10286
- }
10287
- finModelsFilled = true;
10288
- }
10289
-
10290
- async function finSnapshot() {
10291
- if (!financeModeOn()) return;
10292
- let r = null;
10293
- try { r = await beast.financeSnapshot(); } catch {}
10294
- if (!r || !r.ok) {
10295
- finSetDot(false);
10296
- return;
10297
- }
10298
- const b = r.bridge || {};
10299
- finCfgCache = r.cfg || null;
10300
- finSetDot(b.connected ? true : b.running ? 'busy' : false, b);
10301
- finRenderAccount(r.account);
10302
- finRenderPositions(r.positions || []);
10303
- finRenderOrders(r.orders || [], r.ordersError || '');
10304
- finRenderSymbols(r.symbols || []);
10305
- if (Array.isArray(r.roles) && r.roles.length) finRolesCatalog = r.roles;
10306
- if (Array.isArray(r.rolesAuto) && r.rolesAuto.length) finRolesAuto = r.rolesAuto;
10307
- finTraderInputsSet(r.cfg);
10308
- finRenderRoles(r.cfg);
10309
- finRenderTrader(r.trader, r.cfg);
10310
- finRenderPosManager(r.posManager, r.cfg);
10311
- finRenderAutomation(r.cfg, r.watch);
10312
- /* GİZLİ RİSK OTOMASYONU AKTİF OLAMAZ: sunucu hâlâ açık diyorsa kapat */
10313
- if (!finView.risk && r.cfg && r.cfg.watchdog !== false) finSaveCfg({ watchdog: false });
10314
- finRenderStats(r.stats, r.equity, r.account && r.account.currency ? r.account.currency : '');
10315
- finRenderAlerts(r.alerts);
10316
- /* görünüm ayarları açıkken AJAN DM ↔ TELEGRAM durumu cfg ile senkron kalsın */
10317
- if (els.finSettingsOverlay && !els.finSettingsOverlay.hidden) {
10318
- finDmTgPaint();
10319
- }
10320
- }
10321
-
10322
- function finLogLine(line) {
10323
- if (!els.finLog) return;
10324
- const d = document.createElement('div');
10325
- d.className = 'fin-log-new';
10326
- const now = new Date();
10327
- d.textContent = String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0') + ' ' + line;
10328
- els.finLog.insertBefore(d, els.finLog.firstChild);
10329
- for (const c of els.finLog.children) c.classList.remove('fin-log-new');
10330
- d.classList.add('fin-log-new');
10331
- while (els.finLog.children.length > 200) els.finLog.removeChild(els.finLog.lastChild);
10332
- }
10333
-
10334
- function finOnEvent(ev) {
10335
- if (!ev || ev.type !== 'finance') return;
10336
- if (ev.fn === 'log' && ev.line) finLogLine(ev.line);
10337
- else if (ev.fn === 'notify' && ev.line) {
10338
- finLogLine(ev.line);
10339
- toast(ev.line);
10340
- finSnapshot();
10341
- } else if (ev.fn === 'trade' && ev.line) {
10342
- finLogLine(ev.line);
10343
- toast(ev.line);
10344
- finSnapshot();
10345
- } else if (ev.fn === 'bridge') {
10346
- finSetDot(ev.connected ? true : 'busy', { error: ev.error });
10347
- } else if (ev.fn === 'trader') {
10348
- if (ev.state === 'running') finLogLine('[trader] tur #' + (ev.round || 0) + ' başladı');
10349
- else if (ev.state === 'team') finLogLine('[trader] ekip raporları bekleniyor — ' + (ev.waiting || 0) + ' ajan turunu bitirsin (max ' + (ev.maxSec || '?') + ' sn)');
10350
- else if (ev.state === 'idle') finLogLine('[trader] tur #' + (ev.round || 0) + ' bitti — sıradaki tur ~' + (ev.nextInSec || '?') + ' sn');
10351
- else if (ev.state === 'hours') finLogLine('[trader] trade saatleri dışı — ajan askıda (kontrol ~' + (ev.nextInSec || 60) + ' sn)');
10352
- else if (ev.state === 'hours-stop') finLogLine('[trader] trade saatleri kapandı — ' + (ev.count || 0) + ' finance ajanı otomatik durduruldu; pencere açılınca geri başlar');
10353
- else if (ev.state === 'hours-resume') finLogLine('[trader] trade saatleri açıldı — duraklatılan ajanlar geri başlatıldı');
10354
- else if (ev.state === 'stopped') finLogLine('[trader] durduruldu');
10355
- } else if (ev.fn === 'limits') {
10356
- /* trader mt5_limits ile limitleri değiştirdi — panel alanları ANINDA tazelenir */
10357
- if (finCfgCache) {
10358
- if (ev.minLot !== undefined) finCfgCache.minLot = ev.minLot;
10359
- if (ev.maxLot !== undefined) finCfgCache.maxLot = ev.maxLot;
10360
- if (ev.maxPositions !== undefined) finCfgCache.maxPositions = ev.maxPositions;
10361
- if (ev.symbols !== undefined) finCfgCache.symbolLimits = ev.symbols;
10362
- if (ev.intervalBase !== undefined) finCfgCache.intervalSec = ev.intervalBase;
10363
- finTraderInputsSet(finCfgCache);
10364
- } else {
10365
- const ae2 = document.activeElement;
10366
- if (els.finMinLot && ae2 !== els.finMinLot && ev.minLot !== undefined) els.finMinLot.value = ev.minLot;
10367
- if (els.finMaxLot && ae2 !== els.finMaxLot && ev.maxLot !== undefined) els.finMaxLot.value = ev.maxLot;
10368
- if (els.finInterval && ae2 !== els.finInterval && ev.intervalBase !== undefined) els.finInterval.value = ev.intervalBase;
10369
- if (ev.symbols !== undefined) finSymLimitsRender(ev.symbols);
10370
- }
10371
- finLogLine('[limit] ' + (ev.note || ('min lot ' + ev.minLot + ' · max lot ' + ev.maxLot + ' · max pozisyon ' + ev.maxPositions)));
10372
- if (ev.symbol) toast('Sembol lot limiti güncellendi: ' + ev.symbol);
10373
- else if (ev.paceUntil) toast('Trader hız modu: tur ' + ev.intervalSec + ' sn — süre bitince tabana döner');
10374
- else toast('Trader limitleri güncelledi: min ' + ev.minLot + ' / max ' + ev.maxLot + (ev.intervalSec ? ' · tur ' + ev.intervalSec + ' sn' : ''));
10375
- } else if (ev.fn === 'install') {
10376
- finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
10377
- }
10378
- }
10379
- beast.onEvent(finOnEvent);
10380
-
10381
- async function setFinanceMode(on) {
10382
- if (on && ideModeOn()) await setIdeMode(false);
10383
- if (on && studioModeOn()) await setStudioMode(false);
10384
- if (on && sbModeOn()) setSandboxMode(false);
10385
- if (on && mcpModeOn()) setMcpMode(false); /* MCP açıkken Finance'a geçiş — MCP kapanır */
10386
- document.body.classList.toggle('finance-mode', !!on);
10387
- if (els.finBtn) els.finBtn.classList.toggle('on', !!on);
10388
- const brandSub = document.querySelector('#brand .brand-sub');
10389
- if (brandSub) brandSub.textContent = on ? 'Finance' : 'Agent';
10390
- try { localStorage.setItem('beast.financeMode', on ? '1' : '0'); } catch {}
10391
- /* BEAST TRADER MODU: finance moduna geçilince aktif bot OTOMATİK Trader
10392
- botuna döner — oturum açma finance akışına bırakılır (noOpen) */
10393
- if (on) {
10394
- try {
10395
- if (!botsCache.length) await refreshBots();
10396
- if (activeBotId !== 'trader' && botsCache.some((b) => b.id === 'trader')) {
10397
- await switchBot('trader', { noOpen: true });
10398
- }
10399
- } catch {}
10400
- }
10401
- const fm = await beast.financeMode(!!on, activeId).catch(() => ({}));
10402
- /* finance modu açıldı ama aktif oturum NORMAL (örn. WhatsApp sohbeti) ise
10403
- ASLA o oturumu finance'e çevirme — önce EN SON finance sohbetinden devam
10404
- et (resumeSid); hiç finance sohbeti yoksa kendine ÖZEL yeni oturum aç */
10405
- if (on && fm && fm.resumeSid) {
10406
- try { await openSession(fm.resumeSid); } catch {}
10407
- } else if (on && fm && fm.needNew) {
10408
- try {
10409
- const created = await beast.createSession();
10410
- if (created && created.id) await openSession(created.id);
10411
- } catch {}
10412
- }
10413
- /* sohbet geçmişi listesi değişir: finance modunda yalnız finance sohbetleri */
10414
- try {
10415
- await refreshSessions();
10416
- const rows = [...els.sessList.querySelectorAll('.sess')];
10417
- const activeInList = activeId && rows.some((r) => r.dataset.sid === activeId);
10418
- if (!activeInList && rows.length && rows[0].dataset.sid) {
10419
- await openSession(rows[0].dataset.sid); /* en üstteki uygun sohbeti aç */
10420
- }
10421
- } catch {}
10422
- if (on) {
10423
- if (!finModelsFilled) await finFillModels();
10424
- finSnapshot();
10425
- if (!finPollTimer) finPollTimer = setInterval(finSnapshot, 3000);
10426
- } else if (finPollTimer) {
10427
- clearInterval(finPollTimer);
10428
- finPollTimer = null;
10429
- }
10430
- }
10431
-
10432
- if (els.finBtn) {
10433
- els.finBtn.addEventListener('click', () => setFinanceMode(!financeModeOn()).catch(() => {}));
10434
- }
10435
- if (els.finReconnect) {
10436
- els.finReconnect.addEventListener('click', async () => {
10437
- toast('MT5 köprüsü yeniden başlatılıyor…');
10438
- await beast.financeConnect().catch(() => {});
10439
- finSnapshot();
10440
- });
10441
- }
10442
-
10443
- /* trader kontrolleri: ayarlar anında kaydedilir (debounce) */
10444
- let finCfgTimer = null;
10445
- let finWatchDirty = false; /* picker/input değişikliği kaydedilmeyi bekliyor */
10446
- function finSaveCfg(patch) {
10447
- if (patch && patch.symbols !== undefined) finWatchDirty = true;
10448
- if (patch && patch.strategy !== undefined) finStrategyDirty = true;
10449
- if (patch && patch.posManagerNote !== undefined) finPosNoteDirty = true;
10450
- clearTimeout(finCfgTimer);
10451
- const wasStrategy = !!(patch && patch.strategy !== undefined);
10452
- const wasPosNote = !!(patch && patch.posManagerNote !== undefined);
10453
- finCfgTimer = setTimeout(() => {
10454
- beast.financeSettings(patch)
10455
- .then(() => {
10456
- if (wasStrategy) {
10457
- finStrategyDirty = false;
10458
- if (finCfgCache) finCfgCache.strategy = patch.strategy;
10459
- }
10460
- if (wasPosNote) {
10461
- finPosNoteDirty = false;
10462
- if (finCfgCache) finCfgCache.posManagerNote = patch.posManagerNote;
10463
- }
10464
- finWatchDirty = false;
10465
- })
10466
- .catch(() => { finWatchDirty = false; });
10467
- }, 400);
10468
- }
10469
-
10470
- /* Ajan talimatı: Ajanı Başlat'tan önce bekleyen değişiklik hemen yazılır ki
10471
- ilk tur brief'i talimatı görsün (debounce beklemeden). */
10472
- function finStrategyFlush() {
10473
- if (!els.finStrategy || !finStrategyDirty) return Promise.resolve();
10474
- const v = els.finStrategy.value;
10475
- finStrategyDirty = false;
10476
- return beast.financeSettings({ strategy: v })
10477
- .then(() => { if (finCfgCache) finCfgCache.strategy = v; })
10478
- .catch(() => { finStrategyDirty = true; });
10479
- }
10480
-
10481
- /* ---------- GÖRÜNÜM AYARLARI (isteğe bağlı bölümler) ----------
10482
- RİSK OTOMASYONU + BİLDİRİM + PERFORMANS + AKIŞ varsayılan GİZLİ; açmak
10483
- isteğe bağlı. Seçim localStorage'da hatırlanır. Risk otomasyonu gizliyken
10484
- watchdog AKTİF OLMAZ — kart yeniden gösterilince kutucuktan açılabilir. */
10485
- const finViewKey = 'beast.financeView';
10486
- const finView = { risk: false, notify: false, perf: false, flow: false };
10487
- try {
10488
- const v = JSON.parse(localStorage.getItem(finViewKey) || 'null');
10489
- if (v && typeof v === 'object') {
10490
- finView.risk = v.risk === true;
10491
- finView.notify = v.notify === true;
10492
- finView.perf = v.perf === true;
10493
- finView.flow = v.flow === true;
10494
- }
10495
- } catch {}
10496
-
10497
- function finViewPaint() {
10498
- if (els.finRiskCard) els.finRiskCard.hidden = !finView.risk;
10499
- if (els.finNotifyCard) els.finNotifyCard.hidden = !finView.notify;
10500
- if (els.finPerfCard) els.finPerfCard.hidden = !finView.perf;
10501
- if (els.finFlowCard) els.finFlowCard.hidden = !finView.flow;
10502
- if (els.finShowRisk) els.finShowRisk.checked = finView.risk;
10503
- if (els.finShowNotify) els.finShowNotify.checked = finView.notify;
10504
- if (els.finShowPerf) els.finShowPerf.checked = finView.perf;
10505
- if (els.finShowFlow) els.finShowFlow.checked = finView.flow;
10506
- const row = (cb) => cb && cb.closest('.fin-set-row');
10507
- const r1 = row(els.finShowRisk); if (r1) r1.classList.toggle('on', finView.risk);
10508
- const r2 = row(els.finShowNotify); if (r2) r2.classList.toggle('on', finView.notify);
10509
- const r3 = row(els.finShowPerf); if (r3) r3.classList.toggle('on', finView.perf);
10510
- const r4 = row(els.finShowFlow); if (r4) r4.classList.toggle('on', finView.flow);
10511
- }
10512
-
10513
- function finViewApply() {
10514
- finViewPaint();
10515
- /* gizli risk otomasyonu aktif kalmaz */
10516
- if (!finView.risk && els.finWatchdogCb) els.finWatchdogCb.checked = false;
10517
- try { localStorage.setItem(finViewKey, JSON.stringify(finView)); } catch {}
10518
- if (!finView.risk) finSaveCfg({ watchdog: false });
10519
- }
10520
-
10521
- /* ---------- AJAN DM ↔ TELEGRAM (görünüm ayarları) ----------
10522
- Grup SEÇİLMEZ: köprü açıkken botu eklediğin gruba yazılan ilk mesaj o grubu
10523
- otomatik bağlar (sunucu tarafı: finDmTgBind). Bağlantıyı koparınca yeni
10524
- grupta ilk mesaj yeniden bağlar. */
10525
- function finDmTgPaint() {
10526
- if (!els.finDmTgOn) return;
10527
- const cfg = finCfgCache || {};
10528
- const on = cfg.dmTelegram === true;
10529
- els.finDmTgOn.checked = on;
10530
- const row = els.finDmTgOn.closest('.fin-set-row');
10531
- if (row) row.classList.toggle('on', on);
10532
- if (els.finDmTgWrap) els.finDmTgWrap.hidden = !on;
10533
- const chat = String(cfg.dmTelegramChat || '');
10534
- if (els.finDmTgState) {
10535
- els.finDmTgState.textContent = chat
10536
- ? '✅ Bağlı: ' + String(cfg.dmTelegramTitle || chat) + ' (' + chat + ')'
10537
- : 'Bağlı grup yok — botu gruba ekle ve gruba /start yaz; o grup otomatik bağlanır.';
10538
- }
10539
- if (els.finDmTgUnbind) els.finDmTgUnbind.hidden = !chat;
10540
- if (els.finDmTgHint) {
10541
- els.finDmTgHint.textContent =
10542
- 'Her AJAN DM grubu (Beast Finance EKİP, sembol ekipleri vb.) Telegram\u2019da AYRI BAŞLIK (topic) olarak canlı akar. ' +
10543
- 'Telegram grubunda Konular (Topics) açık ve bot YÖNETİCİ (Konuları Yönet) olmalı — yönetici değilse normal mesajlarını bot göremez (BotFather → /setprivacy → Disable da olur); Konular kapalıysa mesajlar tek akışa grup etiketiyle düşer. ' +
10544
- 'Bir başlığa yazdığın mesaj ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır.';
10545
- }
10546
- }
10547
-
10548
- function finSettingsOpen() {
10549
- if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = false;
10550
- finDmTgPaint();
10551
- finTradeHoursPaint();
10552
- }
10553
- function finSettingsCloseModal() {
10554
- if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = true;
10555
- }
10556
-
10557
- /* ---------- TRADE SAATLERİ (görünüm ayarları modalı) ----------
10558
- Ayarlar sunucuda (settings.finance.tradeHours) yaşar: on + start/end.
10559
- Kapalıyken ajanlar 7/24; açıkken yalnız [start,end) aralığında çalışır
10560
- (makinenin yerel saati). Açık pozisyon koruması her saat sürer. */
10561
- function finTradeHoursPaint() {
10562
- if (!els.finTradeHoursOn) return;
10563
- const th = (finCfgCache && finCfgCache.tradeHours) || {};
10564
- const on = th.on === true;
10565
- els.finTradeHoursOn.checked = on;
10566
- const row = els.finTradeHoursOn.closest('.fin-set-row');
10567
- if (row) row.classList.toggle('on', on);
10568
- if (els.finTradeHoursWrap) els.finTradeHoursWrap.hidden = !on;
10569
- const ae = document.activeElement;
10570
- if (els.finTradeHoursStart && ae !== els.finTradeHoursStart) els.finTradeHoursStart.value = th.start || '09:00';
10571
- if (els.finTradeHoursEnd && ae !== els.finTradeHoursEnd) els.finTradeHoursEnd.value = th.end || '22:00';
10572
- }
10573
- function finTradeHoursSave() {
10574
- const on = !!(els.finTradeHoursOn && els.finTradeHoursOn.checked);
10575
- const start = String((els.finTradeHoursStart && els.finTradeHoursStart.value) || '09:00').slice(0, 5) || '09:00';
10576
- const end = String((els.finTradeHoursEnd && els.finTradeHoursEnd.value) || '22:00').slice(0, 5) || '22:00';
10577
- if (finCfgCache) finCfgCache.tradeHours = { on, start, end };
10578
- finSaveCfg({ tradeHours: { on, start, end } });
10579
- return { on, start, end };
10580
- }
10581
-
10582
- function finViewToggle(key, on) {
10583
- finView[key] = !!on;
10584
- finViewApply();
10585
- if (key === 'risk') {
10586
- toast(on
10587
- ? 'RİSK OTOMASYONU gösteriliyor — aktif etmek için karttaki kutucuğu işaretle'
10588
- : 'RİSK OTOMASYONU gizlendi ve kapatıldı');
10589
- } else {
10590
- const label = key === 'notify' ? 'BİLDİRİM' : key === 'perf' ? 'PERFORMANS' : 'AKIŞ';
10591
- toast(label + (on ? ' bölümü gösteriliyor' : ' bölümü gizlendi'));
10592
- }
10593
- if (financeModeOn()) finSnapshot();
10594
- }
10595
-
10596
- if (els.finSettings) els.finSettings.addEventListener('click', finSettingsOpen);
10597
- if (els.finSettingsClose) els.finSettingsClose.addEventListener('click', finSettingsCloseModal);
10598
- if (els.finSettingsOverlay) {
10599
- els.finSettingsOverlay.addEventListener('click', (e) => {
10600
- if (e.target === els.finSettingsOverlay) finSettingsCloseModal();
10601
- });
10602
- els.finSettingsOverlay.addEventListener('keydown', (e) => {
10603
- if (e.key === 'Escape') { e.stopPropagation(); finSettingsCloseModal(); }
10604
- });
10605
- }
10606
- if (els.finShowRisk) els.finShowRisk.addEventListener('change', () => finViewToggle('risk', els.finShowRisk.checked));
10607
- if (els.finShowNotify) els.finShowNotify.addEventListener('change', () => finViewToggle('notify', els.finShowNotify.checked));
10608
- if (els.finShowPerf) els.finShowPerf.addEventListener('change', () => finViewToggle('perf', els.finShowPerf.checked));
10609
- if (els.finShowFlow) els.finShowFlow.addEventListener('change', () => finViewToggle('flow', els.finShowFlow.checked));
10610
- /* AJAN DM ↔ TELEGRAM köprüsü kontrolleri (grup otomatik bağlanır) */
10611
- if (els.finDmTgOn) {
10612
- els.finDmTgOn.addEventListener('change', () => {
10613
- const on = els.finDmTgOn.checked;
10614
- if (finCfgCache) finCfgCache.dmTelegram = on;
10615
- finSaveCfg({ dmTelegram: on });
10616
- finDmTgPaint();
10617
- toast(on ? 'AJAN DM ↔ Telegram açıldı — gruba /start yazınca otomatik bağlanır' : 'AJAN DM ↔ Telegram kapatıldı');
10618
- });
10619
- }
10620
- if (els.finDmTgUnbind) {
10621
- els.finDmTgUnbind.addEventListener('click', () => {
10622
- if (finCfgCache) {
10623
- finCfgCache.dmTelegramChat = '';
10624
- finCfgCache.dmTelegramTitle = '';
10625
- }
10626
- finSaveCfg({ dmTelegramChat: '', dmTelegramTitle: '' });
10627
- finDmTgPaint();
10628
- toast('Telegram grubu koparıldı — yeni grupta /start yazınca bağlanır');
10629
- });
10630
- }
10631
- /* TRADE SAATLERİ kontrolleri — anında kaydedilir (yerel saat) */
10632
- if (els.finTradeHoursOn) {
10633
- els.finTradeHoursOn.addEventListener('change', () => {
10634
- const { on, start, end } = finTradeHoursSave();
10635
- finTradeHoursPaint();
10636
- toast(on
10637
- ? 'Trade saatleri AÇIK — Beast Finance ve ajanları yalnız ' + start + '–' + end + ' arasında çalışır (yerel saat)'
10638
- : 'Trade saatleri kapalı — ajanlar 7/24 serbest');
10639
- });
10640
- }
10641
- for (const el of [els.finTradeHoursStart, els.finTradeHoursEnd]) {
10642
- if (!el) continue;
10643
- el.addEventListener('change', () => {
10644
- const { on, start, end } = finTradeHoursSave();
10645
- if (on) toast('Trade saatleri: ' + start + '–' + end + ' (yerel saat)');
10646
- });
10647
- }
10648
- finTradeHoursPaint();
10649
- finViewApply();
10650
-
10651
- /* ---------- BÖLÜM SIRALAMA (sürükle-bırak) ----------
10652
- Kartların sol üstündeki tutamak (⠿) ile bölümlerin yerini değiştir;
10653
- sıra localStorage'a yazılır — panel kapatılıp açılsa da korunur. */
10654
- const finOrderKey = 'beast.financeOrder';
10655
- let finDragCard = null;
10656
-
10657
- function finOrderLoad() {
10658
- try {
10659
- const v = JSON.parse(localStorage.getItem(finOrderKey) || 'null');
10660
- return Array.isArray(v) ? v.map(String).filter(Boolean) : [];
10661
- } catch { return []; }
10662
- }
10663
-
10664
- function finOrderApply() {
10665
- const body = els.finBody;
10666
- if (!body) return;
10667
- const cards = [...body.querySelectorAll(':scope > .fin-card')];
10668
- const byKey = new Map();
10669
- for (const c of cards) if (c.dataset.finCard) byKey.set(c.dataset.finCard, c);
10670
- for (const key of finOrderLoad()) {
10671
- const c = byKey.get(key);
10672
- if (c) { body.appendChild(c); byKey.delete(key); }
10673
- }
10674
- for (const c of byKey.values()) body.appendChild(c); /* yeni kartlar sona */
10675
- }
10676
-
10677
- function finOrderSave() {
10678
- const body = els.finBody;
10679
- if (!body) return;
10680
- const keys = [...body.querySelectorAll(':scope > .fin-card')]
10681
- .map((c) => c.dataset.finCard)
10682
- .filter(Boolean);
10683
- try { localStorage.setItem(finOrderKey, JSON.stringify(keys)); } catch {}
10684
- }
10685
-
10686
- function finCardsInit() {
10687
- const body = els.finBody;
10688
- if (!body) return;
10689
- finOrderApply();
10690
- const cards = [...body.querySelectorAll(':scope > .fin-card')];
10691
- for (const card of cards) {
10692
- const title = card.querySelector('.fin-card-title');
10693
- if (!title) continue;
10694
- const grip = document.createElement('span');
10695
- grip.className = 'fin-grip';
10696
- grip.title = 'Sürükle-bırak: bölümün yerini değiştir';
10697
- grip.textContent = '⠿';
10698
- title.prepend(grip);
10699
- grip.addEventListener('mousedown', () => { card.draggable = true; });
10700
- card.addEventListener('dragstart', (e) => {
10701
- finDragCard = card;
10702
- card.classList.add('fin-dragging');
10703
- try {
10704
- e.dataTransfer.effectAllowed = 'move';
10705
- e.dataTransfer.setData('text/plain', card.dataset.finCard || '');
10706
- } catch {}
10707
- });
10708
- card.addEventListener('dragend', () => {
10709
- finDragCard = null;
10710
- card.draggable = false;
10711
- card.classList.remove('fin-dragging');
10712
- body.querySelectorAll('.fin-drop-top, .fin-drop-bottom')
10713
- .forEach((x) => x.classList.remove('fin-drop-top', 'fin-drop-bottom'));
10714
- finOrderSave();
10715
- });
10716
- card.addEventListener('dragover', (e) => {
10717
- if (!finDragCard || finDragCard === card) return;
10718
- e.preventDefault();
10719
- try { e.dataTransfer.dropEffect = 'move'; } catch {}
10720
- const r = card.getBoundingClientRect();
10721
- const after = e.clientY > r.top + r.height / 2;
10722
- body.querySelectorAll('.fin-drop-top, .fin-drop-bottom')
10723
- .forEach((x) => x.classList.remove('fin-drop-top', 'fin-drop-bottom'));
10724
- card.classList.add(after ? 'fin-drop-bottom' : 'fin-drop-top');
10725
- });
10726
- card.addEventListener('dragleave', () => card.classList.remove('fin-drop-top', 'fin-drop-bottom'));
10727
- card.addEventListener('drop', (e) => {
10728
- e.preventDefault();
10729
- if (!finDragCard || finDragCard === card) return;
10730
- const r = card.getBoundingClientRect();
10731
- const after = e.clientY > r.top + r.height / 2;
10732
- if (after) card.after(finDragCard);
10733
- else card.before(finDragCard);
10734
- card.classList.remove('fin-drop-top', 'fin-drop-bottom');
10735
- finOrderSave();
10736
- });
10737
- }
10738
- document.addEventListener('mouseup', () => {
10739
- if (finDragCard) return;
10740
- body.querySelectorAll('.fin-card[draggable="true"]').forEach((c) => { c.draggable = false; });
10741
- });
10742
- }
10743
- finCardsInit();
10744
-
10745
- if (els.finInterval) els.finInterval.addEventListener('change', () => {
10746
- const v = Math.max(30, Math.min(3600, Math.round(Number(els.finInterval.value) || 120)));
10747
- els.finInterval.value = v;
10748
- finSaveCfg({ intervalSec: v });
10749
- toast('Tur aralığı: ' + v + ' sn — durdurmadan sonraki tura uygulanır');
10750
- });
10751
- if (els.finMaxLot) els.finMaxLot.addEventListener('change', () => {
10752
- const v = Math.max(0.01, Math.min(100, Number(els.finMaxLot.value) || 0.1));
10753
- els.finMaxLot.value = v;
10754
- /* min lot max lotu aşamaz — görünürde de kelepçele (sunucu ayrıca zorlar) */
10755
- if (els.finMinLot && Number(els.finMinLot.value) > v) els.finMinLot.value = v;
10756
- finSaveCfg({ maxLot: v });
10757
- toast('Max lot: ' + v + ' — sonraki turdan itibaren geçerli');
10758
- });
10759
- if (els.finMinLot) els.finMinLot.addEventListener('change', () => {
10760
- const maxV = Math.max(0.01, Math.min(100, Number(els.finMaxLot && els.finMaxLot.value) || 0.1));
10761
- let v = Math.max(0.01, Math.min(100, Number(els.finMinLot.value) || 0.01));
10762
- if (v > maxV) {
10763
- v = maxV;
10764
- toast('Min lot max lotu aşamaz — ' + v + ' olarak ayarlandı');
10765
- }
10766
- els.finMinLot.value = v;
10767
- finSaveCfg({ minLot: v });
10768
- toast('Min lot: ' + v + ' — lot hesapları bu tabanın altına inmez');
10769
- });
10770
- if (els.finSymBtn) els.finSymBtn.addEventListener('click', finSymPickerOpen);
10771
-
10772
- /* RİSK OTOMASYONU kontrolleri: değişiklik anında kaydedilir (ajan turunu beklemez) */
10773
- if (els.finWatchdogCb) {
10774
- els.finWatchdogCb.addEventListener('change', () => {
10775
- finSaveCfg({ watchdog: els.finWatchdogCb.checked });
10776
- toast('Risk otomasyonu ' + (els.finWatchdogCb.checked ? 'açık — BE/trailing/kısmi TP 5 sn döngüyle uygulanır' : 'kapalı'));
10777
- });
10778
- }
10779
- const finAutoNum = (el, key, min, max, step) => {
10780
- if (!el) return;
10781
- el.addEventListener('change', () => {
10782
- let v = Number(el.value);
10783
- if (!isFinite(v)) v = min;
10784
- v = Math.max(min, Math.min(max, step ? Math.round(v / step) * step : v));
10785
- el.value = v;
10786
- finSaveCfg({ [key]: v });
10787
- });
10788
- };
10789
- finAutoNum(els.finBeOnR, 'beOnR', 0, 10);
10790
- finAutoNum(els.finTrailStartR, 'trailStartR', 0, 10);
10791
- finAutoNum(els.finTrailR, 'trailR', 0, 5);
10792
- finAutoNum(els.finProtectStartR, 'protectStartR', 0, 10);
10793
- finAutoNum(els.finProtectDistR, 'protectDistR', 0, 5);
10794
- finAutoNum(els.finPartialR, 'partialR', 0, 10);
10795
- finAutoNum(els.finPartialPct, 'partialPct', 0, 90, 5);
10796
- finAutoNum(els.finMaxDailyLoss, 'maxDailyLossPct', 0, 50);
10797
- finAutoNum(els.finRiskPct, 'riskPerTradePct', 0, 20, 0.1);
10798
- finAutoNum(els.finMaxPerSymbol, 'maxPerSymbol', 0, 20, 1);
10799
- finAutoNum(els.finMaxSameSide, 'maxSameSide', 0, 20, 1);
10800
- finAutoNum(els.finMinMarginLevel, 'minMarginLevel', 0, 1000, 10);
10801
- /* KODLA DİSİPLİN + shadow + günlük plan/review — anında kaydedilir */
10802
- finAutoNum(els.finMaxTradesDay, 'maxTradesPerDay', 0, 50, 1);
10803
- finAutoNum(els.finLossStreak, 'lossStreakLimit', 0, 10, 1);
10804
- finAutoNum(els.finLossStreakPause, 'lossStreakPauseMin', 0, 1440, 5);
10805
- finAutoNum(els.finReentry, 'reentryCooldownMin', 0, 1440, 5);
10806
- finAutoNum(els.finPosSec, 'posManagerSec', 1, 300, 1);
10807
- finAutoNum(els.finMaxPerCurrency, 'maxPerCurrency', 0, 20, 1);
10808
- const finTimeSave = (el, key) => {
10809
- if (!el) return;
10810
- el.addEventListener('change', () => {
10811
- finSaveCfg({ [key]: String(el.value || '').slice(0, 5) });
10812
- toast(key === 'planTime'
10813
- ? (el.value ? 'Günlük plan saati: ' + el.value + ' (hafta içi)' : 'Günlük plan kapatıldı')
10814
- : (el.value ? 'Günlük review saati: ' + el.value + ' (hafta içi)' : 'Günlük review kapatıldı'));
10815
- });
10816
- };
10817
- finTimeSave(els.finPlanTime, 'planTime');
10818
- finTimeSave(els.finReviewTime, 'reviewTime');
10819
- if (els.finShadow) {
10820
- els.finShadow.addEventListener('change', () => {
10821
- finSaveCfg({ shadowMode: els.finShadow.checked });
10822
- toast(els.finShadow.checked
10823
- ? 'SHADOW MOD açık — emir gönderilmez, kararlar günlüğe yazılır'
10824
- : 'Shadow mod kapalı — gerçek emirler aktif');
10825
- });
10826
- }
10827
- /* GELİŞMİŞ SEÇENEKLER: disiplin + shadow bloğu varsayılan KAPALI; durum hatırlanır */
10828
- const finAdvKey = 'beast.financeAdvanced';
10829
- function finAdvPaint() {
10830
- const on = (() => { try { return localStorage.getItem(finAdvKey) === '1'; } catch { return false; } })();
10831
- if (els.finAdvWrap) els.finAdvWrap.hidden = !on;
10832
- if (els.finAdvToggle) {
10833
- els.finAdvToggle.classList.toggle('on', on);
10834
- els.finAdvToggle.setAttribute('aria-expanded', on ? 'true' : 'false');
10835
- els.finAdvToggle.title = on
10836
- ? 'Gelişmiş açık — disiplin kuralları + shadow mod (kapat)'
10837
- : 'Gelişmiş — disiplin kuralları + shadow mod';
10838
- }
10839
- }
10840
- if (els.finAdvToggle) {
10841
- els.finAdvToggle.addEventListener('click', () => {
10842
- const on = (() => { try { return localStorage.getItem(finAdvKey) === '1'; } catch { return false; } })();
10843
- try { localStorage.setItem(finAdvKey, on ? '0' : '1'); } catch {}
10844
- finAdvPaint();
10845
- });
10846
- }
10847
- finAdvPaint();
10848
- if (els.finDailyLossAction) {
10849
- els.finDailyLossAction.addEventListener('change', () => {
10850
- finSaveCfg({ dailyLossAction: els.finDailyLossAction.value });
10851
- const t = els.finDailyLossAction.options[els.finDailyLossAction.selectedIndex].textContent;
10852
- toast('Günlük limit aksiyonu: ' + t);
10853
- });
10854
- }
10855
- if (els.finNotifyTarget) {
10856
- els.finNotifyTarget.addEventListener('change', () => {
10857
- finSaveCfg({ notifyTarget: els.finNotifyTarget.value });
10858
- toast('Bildirim kanalı: ' + els.finNotifyTarget.options[els.finNotifyTarget.selectedIndex].textContent);
10859
- });
10860
- }
10861
- if (els.finNotifyTradeOnly) {
10862
- els.finNotifyTradeOnly.addEventListener('change', () => {
10863
- finSaveCfg({ notifyTradeOnly: els.finNotifyTradeOnly.checked });
10864
- toast(els.finNotifyTradeOnly.checked
10865
- ? 'Kanala yalnız emir/işlem olayları gidecek — alarm/koruma bildirimleri panelde kalır'
10866
- : 'Tüm finance bildirimleri (alarm/koruma dahil) kanala gidecek');
10867
- });
10868
- }
10869
- if (els.finReportBtn) {
10870
- els.finReportBtn.addEventListener('click', async () => {
10871
- els.finReportBtn.disabled = true;
10872
- els.finReportBtn.textContent = '⏳ Rapor…';
10873
- const r = await beast.financeReport().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
10874
- els.finReportBtn.disabled = false;
10875
- els.finReportBtn.textContent = '📄 Rapor';
10876
- if (r && r.ok) toast('Rapor hazır: ' + String(r.path || '').split(/[\\/]/).pop());
10877
- else toast('Rapor üretilemedi: ' + ((r && r.error) || '?'));
10878
- finSnapshot();
10879
- });
10880
- }
10881
-
10882
- /* OTOMATİK İŞLEM onay kutusu KALDIRILDI — trade ajanı daima işlem açabilir */
10883
- if (els.finTraderModel) {
10884
- els.finTraderModel.addEventListener('change', () => {
10885
- finSaveCfg({ traderSel: els.finTraderModel.value });
10886
- toast('Trade ajanı modeli: ' + (els.finTraderModel.value || 'genel aktif model'));
10887
- });
10888
- }
10889
- /* AJAN TALİMATI: trader + ekip + sembol işçileri sistem promptunda
10890
- "öncelikli strateji notu" olarak okur; yazarken (debounce) kaydedilir */
10891
- if (els.finStrategy) {
10892
- els.finStrategy.addEventListener('input', () => {
10893
- finSaveCfg({ strategy: els.finStrategy.value });
10894
- });
10895
- els.finStrategy.addEventListener('change', () => {
10896
- const v = els.finStrategy.value;
10897
- if (finCfgCache && String(finCfgCache.strategy || '') === v) return;
10898
- finSaveCfg({ strategy: v });
10899
- toast(v.trim() ? 'Ajan talimatı kaydedildi — tüm ajanlar sonraki turda uygular' : 'Ajan talimatı temizlendi');
10900
- });
10901
- }
10902
- /* TALİMAT KILAVUZU (?): her iki talimat alanı için desteklenen kalıplar.
10903
- "kod" etiketli kalıplar AYRICALIKLI olarak kod tarafından otomatik uygulanır;
10904
- serbest cümleler Jev'in tur state'ine girer ve Jev ona göre karar verir. */
10905
- /* BUTONLAR: ? → kılavuz; dışına tıkla / × → kapat */
10906
- if (els.finStrategyHelp) {
10907
- els.finStrategyHelp.addEventListener('click', (e) => {
10908
- e.preventDefault();
10909
- finHelpOpen('trade');
10910
- });
10911
- }
10912
- if (els.finPosHelp) {
10913
- els.finPosHelp.addEventListener('click', (e) => {
10914
- e.preventDefault();
10915
- finHelpOpen('posmgr');
10916
- });
10917
- }
10918
- if (els.finHelpClose) els.finHelpClose.addEventListener('click', finHelpClose);
10919
- if (els.finHelpOverlay) {
10920
- els.finHelpOverlay.addEventListener('click', (e) => {
10921
- if (e.target === els.finHelpOverlay) finHelpClose();
10922
- });
10923
- }
10924
- function finHelpHtml(which) {
10925
- const posmgr = which === 'posmgr';
10926
- const items = posmgr
10927
- ? [
10928
- ['<code>1R\'de %50 kısmi kapat</code><code>%50 1R</code><code>kısmi close %50</code>',
10929
- '<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.'],
10930
- ['<code>1.5R\'de %25 kısmi</code><code>3R\'de %75 kapat</code>',
10931
- '<b>Kod otomatik uygular.</b> R ve yüzdeyi sen seç; yüzde 5-90 arası olmalı.'],
10932
- ['<code>kâr 2R olunca kalanı kapat</code><code>kârı koru</code>',
10933
- '<b>Jev uygular.</b> Serbest ifade; her 5 sn turunda Jev okur ve kapatma/trailing kararını verir.'],
10934
- ['<code>SL\'yi takip et</code><code>breakeven\'a çek</code><code>zararı -0.5R\'de kes</code>',
10935
- '<b>Jev uygular.</b> SL/koruma aksiyonu olarak değerlendirilir; kod seviyeyi ATR/R ile hesaplar, SL asla geriye gitmez.'],
10936
- ['<code>başlangıç bakiye 10.000</code><code>gün başı: 10000</code>',
10937
- 'Gün başı bakiyesi — günlük K/Z % otomatik hesaplanır (raporda "Gün başı → %-2.3 ZARAR").'],
10938
- ]
10939
- : [
10940
- ['<code>risk %2</code><code>%2 risk</code><code>risk yüzde 2</code>',
10941
- '<b>Kod otomatik uygular.</b> Girişler %2 risk ile açılır (kalibrasyon çarpanı devre dışı, 0.1-10 arası).'],
10942
- ['<code>lot x1.5</code><code>poz başı lot 1.5</code><code>lotu 2 kat yap</code>',
10943
- '<b>Kod otomatik uygular.</b> Tüm giriş riskine/lotuna çarpan.'],
10944
- ['<code>martingale</code><code>martingale 1.2</code><code>martingale x1.20</code><code>1.2 kat martingale</code>',
10945
- '<b>Kod otomatik uygular.</b> Kayıp serisi KAPANAN işlemlerden sayılır (aç+kapa = 1 işlem): 1. işlem stop → 2. işlem ×1.2, o da stop → ×1.44… Kâra geçen ilk işlemde seri sıfırlanır ve taban lota dönülü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.'],
10946
- ['<code>her mumda işlem açmak zorundasın</code><code>her barda işlem</code><code>her zaman işlem</code>',
10947
- '<b>ZORUNLU GİRİŞ modu (isteğe bağlı).</b> Bu ifadeyi <b>yazmazsan girişler isteğe bağlıdır</b>: yalnız eşik + teyit geçen sinyalde işlem açılır. Yazarsan "bekle" kaldırılır, eşik/teyit atlanır, emir market olur. Teyit cümleleri ("her mum kapanışını bekle") modu açmaz; "zorunlu değil" yazarsan kapanır.'],
10948
- ['<code>M1</code><code>1m</code><code>M15</code><code>5 dk</code><code>4 saat</code>',
10949
- 'İşlem zaman dilimi. Yazdığın TF (ör. <b>M15</b>) tüm girişlerde birincil kuraldır: analiz, bildirim ve pozisyon yorumu o periyodu kullanır — talimatta M1 yazmadıkça M1\'de işlem açılmaz. M1/M5 yazarsan tur ritmi hızlanır (M1 en hızlı 60 sn).'],
10950
- ['<code>başlangıç bakiye 10.000</code><code>gün başı: 10000</code><code>starting balance 10000</code>',
10951
- 'Gün başı bakiyesi — her turda günlük K/Z otomatik hesaplanır ve Jev\'e durum olarak verilir.'],
10952
- ['<code>günlük zarar %3</code><code>max günlük kayıp 3</code>',
10953
- '<b>Kod otomatik uygular.</b> Limit aşılırsa yeni işlem açılmaz; günlük kayıp otomasyonunun limiti bu olur.'],
10954
- ['<code>aynı anda en fazla 10 işlem</code><code>en fazla 10 pozisyon</code><code>günde en fazla 10 işlem</code>',
10955
- '<b>Kod otomatik uygular.</b> "aynı anda/en fazla pozisyon" = eşzamanlı tavan (pozisyon + bekleyen emir). <b>Günlük işlem limiti kaldırıldı (varsayılan sınırsız)</b>; yalnız "günde en fazla N işlem" yazarsan o gün için devreye girer — Ayarlar\'daki günlük limit alanı artık kullanılmıyor.'],
10956
- ['<code>sembol başına 5 işlem</code><code>aynı sembolde en fazla 3 pozisyon</code>',
10957
- '<b>Kod otomatik uygular.</b> Aynı sembolde çoklu işlem tavanı (toplam tavanı aşamaz). Toplam tavan yazarsan ("aynı anda en fazla 10 işlem") aynı sembolde de o tavana kadar işlem açılabilir; yazmazsan Ayarlar → maks/pay değeri geçerlidir.'],
10958
- ['<code>zararda işlem kapatma</code><code>sadece karda kapat</code><code>kârda değilse kapatma</code>',
10959
- '<b>Kod otomatik uygular.</b> Zarardaki pozisyonun kapatma kararı REDDEDİLİR — SL/TP çalışmaya devam eder (stop = martingale serisinin parçası). Kârdaki/başabaştaki pozisyon normal kapatılır.'],
10960
- ['<code>0.5R karda yarısını kapat</code><code>partial close ile yarısını kapat</code>',
10961
- '<b>Kod otomatik uygular.</b> Kâr +0.5R\'ye ulaşınca pozisyonun %50\'si kapatılır — <b>yalnız kârdayken</b> uygulanır (zararda kısmi stop yapılmaz).'],
10962
- ['<code>karda tümünü kapat</code><code>toplam kâr %2 olunca hepsini kapat</code><code>50 dolar kârda tümünü kapat</code>',
10963
- '<b>Kod otomatik uygular (TOPLU KAPATMA).</b> Tüm pozisyonların toplamı hedefe ulaşınca (herhangi bir kâr / +%X / +X dolar) HEPSİ + bekleyen emirler kapatılır. Panelde POZİSYONLAR başlığındaki <b>Tümünü Kapat</b> butonuyla elle de yapılabilir.'],
10964
- ];
10965
- const example = posmgr
10966
- ? `1- 1R'de %50 kısmi kapat\n2- kâr 2R'ye gelince kalanı kapat\n3- zarar -0.5R'yi geçerse kes`
10967
- : `1- zaman dilimi M15\n2- risk yüzde 2\n3- martingale kullan (x1.2)\n4- sadece uygun sinyalde işlem aç (isteğe bağlı)\n5- aynı anda en fazla 10 işlem\n6- başlangıç bakiye 10.000\n7- günlük zarar %3`;
10968
- const head = posmgr
10969
- ? 'Pozisyon Yöneticisi (ayarlı sıklık — varsayılan 5 sn) açık pozisyonları yönetir: kapat / kısmi kapat / SL taşı. Trade Ajanı pozisyon açıkken de tur atmaya devam eder ve yeni girişleri değerlendirir.'
10970
- : 'Trade Ajanı girişleri yönetir: yön, emir tipi, risk, lot, martingale ve günlük limitler.';
10971
- const rows = items
10972
- .map((it) => '<div class="fin-help-item"><div class="fin-help-code">' + it[0] + '</div><div class="fin-help-desc">' + it[1] + '</div></div>')
10973
- .join('');
10974
- return (
10975
- '<div class="fin-help-head">' + head + '</div>' +
10976
- '<div class="fin-help-sec">Neler yazabilirsin?</div>' +
10977
- rows +
10978
- '<div class="fin-help-sec">Nasıl yazılır?</div>' +
10979
- '<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>' +
10980
- '<pre class="fin-help-ex">' + example + '</pre>'
10981
- );
10982
- }
10983
-
10984
- function finHelpOpen(which) {
10985
- if (!els.finHelpOverlay) return;
10986
- const posmgr = which === 'posmgr';
10987
- if (els.finHelpTitle) els.finHelpTitle.textContent = posmgr ? 'POZİSYON YÖNETİCİSİ — TALİMAT KILAVUZU' : 'TRADE AJANI — TALİMAT KILAVUZU';
10988
- if (els.finHelpBody) els.finHelpBody.innerHTML = finHelpHtml(which);
10989
- els.finHelpOverlay.hidden = false;
10990
- }
10991
-
10992
- function finHelpClose() {
10993
- if (els.finHelpOverlay) els.finHelpOverlay.hidden = true;
10994
- }
10995
-
10996
- /* POZİSYON YÖNETİCİSİ: 5 sn Jev turu — kendi talimatı + aç/kapa */
10997
- if (els.finPosNote) {
10998
- els.finPosNote.addEventListener('input', () => {
10999
- finSaveCfg({ posManagerNote: els.finPosNote.value });
11000
- });
11001
- els.finPosNote.addEventListener('change', () => {
11002
- const v = els.finPosNote.value;
11003
- if (finCfgCache && String(finCfgCache.posManagerNote || '') === v) return;
11004
- finSaveCfg({ posManagerNote: v });
11005
- toast(v.trim() ? 'Yönetici talimatı kaydedildi — sonraki yönetici turunda uygular' : 'Yönetici talimatı temizlendi');
11006
- });
11007
- }
11008
- if (els.finPosOn) {
11009
- els.finPosOn.addEventListener('change', () => {
11010
- finSaveCfg({ posManagerEnabled: els.finPosOn.checked });
11011
- toast(
11012
- els.finPosOn.checked
11013
- ? 'Pozisyon yöneticisi AÇIK — açık pozisyonlar 5 sn Jev turuyla yönetilecek'
11014
- : 'Pozisyon yöneticisi kapalı — pozisyon yönetimi trade ajanına döndü'
11015
- );
11016
- finSnapshot();
11017
- });
11018
- }
11019
- /* TRADE AJANI: modelleri yeniden çek — üstteki ⟳ ile aynı mantık
11020
- (models:refresh). Eksik/yeni modeller listeye girer, seçim korunur. */
11021
- if (els.finModelRefreshBtn) {
11022
- els.finModelRefreshBtn.addEventListener('click', async () => {
11023
- els.finModelRefreshBtn.classList.add('spin');
11024
- try {
11025
- state = await beast.refreshModels();
11026
- applyState();
11027
- await finFillModels();
11028
- toast(_t('mr_done'));
11029
- } catch {
11030
- toast(_t('ws_fail_toast'));
11031
- } finally {
11032
- els.finModelRefreshBtn.classList.remove('spin');
11033
- }
11034
- });
11035
- }
11036
- if (els.finSetupBtn) {
11037
- els.finSetupBtn.addEventListener('click', async () => {
11038
- els.finSetupBtn.disabled = true;
11039
- const r = await beast.financeMt5Setup().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
11040
- els.finSetupBtn.disabled = false;
11041
- if (r && r.ok) {
11042
- toast(r.changed
11043
- ? 'MT5 kurulumu uygulandı' + (r.restartRequired ? ' — MT5 yeniden başlatılınca etkin' : '')
11044
- : 'MT5 kurulumu zaten güncel');
11045
- } else {
11046
- toast('MT5 kurulumu: ' + ((r && r.error) || 'başarısız'));
11047
- }
11048
- });
11049
- }
11050
- if (els.finTraderBtn) {
11051
- els.finTraderBtn.addEventListener('click', async () => {
11052
- if (els.finTraderBtn.classList.contains('stop')) {
11053
- await beast.financeTraderStop().catch(() => {});
11054
- } else {
11055
- await finStrategyFlush(); /* yazılan talimat ilk turdan önce kaydedilsin */
11056
- const r = await beast.financeTraderStart().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
11057
- if (r && !r.ok) toast(r.error || 'Trader başlatılamadı');
11058
- else if (r && r.ok) {
11059
- const cfg = (await beast.financeState().catch(() => null));
11060
- const c = (cfg && cfg.cfg) || {};
11061
- const team = c.analysisAuto === false
11062
- ? (Array.isArray(c.analysisTeam) ? c.analysisTeam.length : 0)
11063
- : Math.round(Number(c.analysisCount) || 0);
11064
- toast(team ? 'Trade ajanı + analiz ekibi (' + team + ' uzman) başladı — turlar panelde izlenir' : 'Trade ajanı başladı — turlar panelde izlenir');
11065
- /* ajanlar arası konuşma için AJAN DM konsolu da açılır */
11066
- try { toggleDmRail(false); } catch {}
11067
- }
11068
- }
11069
- finSnapshot();
11070
- });
11071
- }
11072
-
11073
- /* ---------- PİYASA sembol seçici ----------
11074
- MT5 terminalindeki tüm semboller listelenir; tıkla → izleme listesine
11075
- ekle/çıkar. PİYASA kartı yalnız seçili sembolleri gösterir. */
11076
-
11077
- let finWatch = []; /* anlık izleme listesi (snapshot cfg'den senkron) */
11078
- let finSymHist = []; /* sembol geçmişi — silinenler dahil, seçicide tek tıkla geri ekleme */
11079
- let finSymSearchTimer = null;
11080
- let finSymRefreshTimer = null;
11081
-
11082
- function finWatchSet(list) {
11083
- finWatch = (Array.isArray(list) ? list : []).map((s) => String(s).trim().toUpperCase()).filter(Boolean);
11084
- }
11085
-
11086
- function finToggleWatch(sym) {
11087
- const s = String(sym || '').toUpperCase();
11088
- const i = finWatch.indexOf(s);
11089
- if (i >= 0) finWatch.splice(i, 1);
11090
- else finWatch.push(s);
11091
- const added = i < 0; /* true = eklendi */
11092
- finSaveCfg({ symbols: [...finWatch] });
11093
- finSymBtnUpdate(); /* TRADE AJANI düğmesi anında güncellensin */
11094
- clearTimeout(finSymRefreshTimer);
11095
- finSymRefreshTimer = setTimeout(() => finSnapshot(), 700); /* PİYASA kartı tazelensin */
11096
- /* canlı geri bildirim: değişiklik koşan ajanı DURDURMADAN sonraki tura girer */
11097
- toast(
11098
- added
11099
- ? s + ' izleme listesine eklendi — koşan ajan sonraki turda kullanır'
11100
- : s + ' izleme listesinden çıkarıldı — koşan ajan sonraki turda görmez'
11101
- );
11102
- return added;
11103
- }
11104
-
11105
- function finRenderPickRows(rows, q) {
11106
- if (!els.finSymPick) return;
11107
- els.finSymPick.textContent = '';
11108
- const term = String(q || '').trim().toUpperCase();
11109
- /* arama boşken ÜSTTE geçmiş bölümü: silinen semboller TEK TIKLA geri eklenir */
11110
- if (!term && finSymHist.length) {
11111
- const sec = document.createElement('div');
11112
- sec.className = 'fin-sym-sec';
11113
- sec.textContent = 'SON SEMBOLLER (sildiklerin dahil — tek tıkla geri ekle)';
11114
- els.finSymPick.appendChild(sec);
11115
- for (const name of finSymHist) {
11116
- const row = document.createElement('div');
11117
- row.className = 'fin-sym-row';
11118
- const on = finWatch.includes(name);
11119
- row.innerHTML =
11120
- '<span class="fsr-check">' + (on ? '✓' : '') + '</span>' +
11121
- '<span class="fsr-name">' + escapeHtml(name) + '</span>' +
11122
- '<span class="fsr-desc">' + (on ? 'izlemede' : 'silinmiş — eklemek için tıkla') + '</span>';
11123
- if (on) row.classList.add('on');
11124
- row.addEventListener('click', () => {
11125
- const added = finToggleWatch(name);
11126
- row.classList.toggle('on', added);
11127
- row.querySelector('.fsr-check').textContent = added ? '✓' : '';
11128
- row.querySelector('.fsr-desc').textContent = added ? 'izlemede' : 'silinmiş — eklemek için tıkla';
11129
- });
11130
- els.finSymPick.appendChild(row);
11131
- }
11132
- const sep = document.createElement('div');
11133
- sep.className = 'fin-sym-sec';
11134
- sep.textContent = 'TÜM MT5 SEMBOLLERİ';
11135
- els.finSymPick.appendChild(sep);
11136
- }
11137
- const matches = rows.filter((r) => {
11138
- if (!term) return true;
11139
- return (String(r.name || '').toUpperCase().includes(term) || String(r.desc || '').toUpperCase().includes(term));
11140
- });
11141
- if (els.finSymCount) els.finSymCount.textContent = matches.length ? matches.length + ' sembol' : '';
11142
- if (!matches.length) {
11143
- if (!term && finSymHist.length) return; /* geçmiş zaten gösterildi */
11144
- const d = document.createElement('div');
11145
- d.className = 'fin-empty';
11146
- d.textContent = term ? 'Eşleşme yok' : 'Sembol listesi boş';
11147
- els.finSymPick.appendChild(d);
11148
- return;
11149
- }
11150
- const cap = 300;
11151
- for (const r of matches.slice(0, cap)) {
11152
- const row = document.createElement('div');
11153
- row.className = 'fin-sym-row';
11154
- const on = finWatch.includes(String(r.name || '').toUpperCase());
11155
- row.innerHTML =
11156
- '<span class="fsr-check">' + (on ? '✓' : '') + '</span>' +
11157
- '<span class="fsr-name">' + escapeHtml(r.name || '?') + '</span>' +
11158
- '<span class="fsr-desc">' + escapeHtml((r.desc || '').slice(0, 60)) + '</span>';
11159
- if (on) row.classList.add('on');
11160
- row.addEventListener('click', () => {
11161
- const added = finToggleWatch(r.name);
11162
- row.classList.toggle('on', added);
11163
- row.querySelector('.fsr-check').textContent = added ? '✓' : '';
11164
- });
11165
- els.finSymPick.appendChild(row);
11166
- }
11167
- if (matches.length > cap) {
11168
- const d = document.createElement('div');
11169
- d.className = 'fin-empty';
11170
- d.textContent = '…' + (matches.length - cap) + ' sembol daha — daraltmak için ara';
11171
- els.finSymPick.appendChild(d);
11172
- }
11173
- }
11174
-
11175
- async function finSymLoad(q) {
11176
- if (!els.finSymPick) return;
11177
- els.finSymPick.textContent = '';
11178
- const d = document.createElement('div');
11179
- d.className = 'fin-empty';
11180
- d.textContent = 'MT5 sembolleri çekiliyor…';
11181
- els.finSymPick.appendChild(d);
11182
- const r = await beast.financeSymbolsList(q).catch(() => ({ ok: false, error: 'köprü yok' }));
11183
- if (!r || !r.ok) {
11184
- els.finSymPick.textContent = '';
11185
- const e = document.createElement('div');
11186
- e.className = 'fin-empty';
11187
- e.textContent = (r && r.error) || 'MT5 sembolleri alınamadı';
11188
- els.finSymPick.appendChild(e);
11189
- if (els.finSymCount) els.finSymCount.textContent = '';
11190
- return;
11191
- }
11192
- finRenderPickRows((r.data && r.data.symbols) || [], q);
11193
- }
11194
-
11195
- function finSymPickerOpen() {
11196
- if (!els.finSymOverlay) return;
11197
- els.finSymOverlay.hidden = false;
11198
- if (els.finSymSearch) els.finSymSearch.value = '';
11199
- finSymLoad('');
11200
- if (els.finSymSearch) els.finSymSearch.focus();
11201
- }
11202
-
11203
- function finSymPickerClose() {
11204
- if (els.finSymOverlay) els.finSymOverlay.hidden = true;
11205
- }
11206
-
11207
- /* ---------- ROL → SKILL eşleştirme modalı ----------
11208
- Roller sunucudan (snapshot.roles), skill listesi KURULU katalogdan gelir.
11209
- Rol başına EN FAZLA 2 skill seçilir ve finance ayarına (roleSkills) yazılır.
11210
- NOT: Finance AJANLARI artık JEV-ONLY (LLM yok) — bu seçim yalnız finance
11211
- SOHBET yardımcısının (LLM) playbook'unu etkiler. */
11212
- let finSkillNames = [];
11213
-
11214
- async function finSkillsOpen() {
11215
- if (!els.finSkillsOverlay) return;
11216
- els.finSkillsOverlay.hidden = false;
11217
- if (els.finSkillsList) els.finSkillsList.innerHTML = '<div class="fin-empty">Skill kataloğu yükleniyor…</div>';
11218
- let list = [];
11219
- try { list = (await beast.listSkills()) || []; } catch {}
11220
- finSkillNames = (Array.isArray(list) ? list : [])
11221
- .map((s) => ({ name: String((s && s.name) || ''), description: String((s && s.description) || '') }))
11222
- .filter((s) => s.name);
11223
- try { await finSnapshot(); } catch {}
11224
- finRenderSkills();
11225
- }
11226
-
11227
- function finRenderSkills() {
11228
- const box = els.finSkillsList;
11229
- if (!box) return;
11230
- const cfg = finCfgCache || {};
11231
- const map = cfg.roleSkills && typeof cfg.roleSkills === 'object' ? cfg.roleSkills : {};
11232
- /* ANA KARAR VERİCİ (trader) da bir satır: playbook skill'leri (en fazla 2) */
11233
- const roles = [
11234
- { id: 'trader', label: 'TRADER (Ana Karar Verici)', desc: 'playbook: hangi setup\'lar serbest, giriş/çıkış/iptal kuralları, seans ve risk disiplini — asıl karar vericinin skill\'i (boş = ajan kendi seçer)' },
11235
- ].concat(Array.isArray(finRolesCatalog) ? finRolesCatalog : []);
11236
- box.innerHTML = '';
11237
- if (!roles.length) {
11238
- box.innerHTML = '<div class="fin-empty">Rol kataloğu yok</div>';
11239
- return;
11240
- }
11241
- if (!finSkillNames.length) {
11242
- box.innerHTML = '<div class="fin-empty">Kurulu skill bulunamadı — %APPDATA%\\beast\\skills klasörüne SKILL.md ekle</div>';
11243
- return;
11244
- }
11245
- for (const r of roles) {
11246
- const row = document.createElement('div');
11247
- row.className = 'fin-skill-role';
11248
- const head = document.createElement('div');
11249
- head.className = 'fin-skill-role-head';
11250
- head.innerHTML =
11251
- '<b>' + escapeHtml(String(r.label || r.id)) + '</b>' +
11252
- '<span>' + escapeHtml(String(r.desc || '').slice(0, 110)) + '</span>';
11253
- const cur = Array.isArray(map[r.id]) ? map[r.id].map((x) => String(x || '').trim()).filter(Boolean).slice(0, 2) : [];
11254
- /* ÇÖP: rolün TÜM skill atamasını kaldır — boş bırakmak da geçerli bir
11255
- seçim (o zaman ajan skill'i göreve göre SKILLS kataloğundan kendi seçer) */
11256
- const clearBtn = document.createElement('button');
11257
- clearBtn.type = 'button';
11258
- clearBtn.className = 'fin-skill-clear';
11259
- clearBtn.title = cur.length
11260
- ? 'Skill atamalarını kaldır (' + cur.join(', ') + ') — boş kalırsa ajan göreve göre kendi seçer'
11261
- : 'Atama yok — boş bırakılabilir';
11262
- clearBtn.innerHTML =
11263
- '<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>';
11264
- clearBtn.addEventListener('click', (e) => {
11265
- e.preventDefault();
11266
- e.stopPropagation();
11267
- const was = cur.join(', ');
11268
- finCfgCache = finCfgCache || {};
11269
- finCfgCache.roleSkills = finCfgCache.roleSkills || {};
11270
- finCfgCache.roleSkills[r.id] = [];
11271
- finSaveCfg({ roleSkills: { [r.id]: [] } });
11272
- toast(was
11273
- ? (r.label || r.id) + ' → ' + was + ' ataması kaldırıldı — ajan göreve göre kendi seçer'
11274
- : (r.label || r.id) + ' → atama zaten boş');
11275
- finRenderSkills();
11276
- });
11277
- head.appendChild(clearBtn);
11278
- if (!cur.length) {
11279
- const warn = document.createElement('span');
11280
- warn.className = 'fin-skill-req';
11281
- warn.textContent = 'atama yok — ajan göreve göre kendi seçer';
11282
- head.appendChild(warn);
11283
- } else if (cur.length > 1) {
11284
- const cnt = document.createElement('span');
11285
- cnt.className = 'fin-skill-req';
11286
- cnt.textContent = cur.length + '/2 skill';
11287
- head.appendChild(cnt);
11288
- }
11289
- row.appendChild(head);
11290
- const chips = document.createElement('div');
11291
- chips.className = 'fin-skill-chips';
11292
- for (const s of finSkillNames) {
11293
- const on = cur.includes(s.name);
11294
- const chip = document.createElement('label');
11295
- chip.className = 'fin-skill-chip' + (on ? ' on' : '');
11296
- chip.title = s.description || s.name;
11297
- const cb = document.createElement('input');
11298
- cb.type = 'checkbox';
11299
- cb.checked = on;
11300
- /* EN FAZLA 2 seçim: yeni skill işaretlenir, sınır aşılırsa uyarır;
11301
- kaldırmak için işaret kaldırılır ya da çöp ikonu kullanılır */
11302
- cb.addEventListener('change', () => {
11303
- finCfgCache = finCfgCache || {};
11304
- finCfgCache.roleSkills = finCfgCache.roleSkills || {};
11305
- const curNow = (Array.isArray(finCfgCache.roleSkills[r.id]) ? finCfgCache.roleSkills[r.id] : []).map((x) => String(x || '').trim()).filter(Boolean);
11306
- let next;
11307
- if (cb.checked) {
11308
- if (curNow.length >= 2) {
11309
- cb.checked = false;
11310
- toast((r.label || r.id) + ' → en fazla 2 skill; önce birini kaldır');
11311
- return;
11312
- }
11313
- next = [...curNow, s.name];
11314
- } else {
11315
- next = curNow.filter((x) => x !== s.name);
11316
- }
11317
- next = next.slice(0, 2);
11318
- /* yerel önbelleğe anında yaz — 3 sn'lik snapshot eski değeri geri getirmesin */
11319
- finCfgCache.roleSkills[r.id] = next;
11320
- chip.classList.toggle('on', next.includes(s.name));
11321
- const w = head.querySelector('.fin-skill-req');
11322
- if (w) w.remove();
11323
- if (!next.length) {
11324
- const warn = document.createElement('span');
11325
- warn.className = 'fin-skill-req';
11326
- warn.textContent = 'atama yok — ajan göreve göre kendi seçer';
11327
- head.appendChild(warn);
11328
- } else if (next.length > 1) {
11329
- const cnt = document.createElement('span');
11330
- cnt.className = 'fin-skill-req';
11331
- cnt.textContent = next.length + '/2 skill';
11332
- head.appendChild(cnt);
11333
- }
11334
- finSaveCfg({ roleSkills: { [r.id]: next } });
11335
- toast((r.label || r.id) + ' → ' + (next.length ? next.join(' + ') : 'atama kaldırıldı') + ' (' + next.length + '/2)');
11336
- });
11337
- chip.appendChild(cb);
11338
- chip.appendChild(document.createTextNode(s.name));
11339
- chips.appendChild(chip);
11340
- }
11341
- row.appendChild(chips);
11342
- box.appendChild(row);
11343
- }
11344
- }
11345
-
11346
- function finSkillsCloseModal() {
11347
- if (els.finSkillsOverlay) els.finSkillsOverlay.hidden = true;
11348
- }
11349
- if (els.finSkillsBtn) els.finSkillsBtn.addEventListener('click', () => { finSkillsOpen(); });
11350
- if (els.finSkillsClose) els.finSkillsClose.addEventListener('click', finSkillsCloseModal);
11351
- if (els.finSkillsOverlay) {
11352
- els.finSkillsOverlay.addEventListener('click', (e) => {
11353
- if (e.target === els.finSkillsOverlay) finSkillsCloseModal();
11354
- });
11355
- els.finSkillsOverlay.addEventListener('keydown', (e) => {
11356
- if (e.key === 'Escape') { e.stopPropagation(); finSkillsCloseModal(); }
11357
- });
11358
- }
11359
-
11360
- /* ---------- MT5 ÖĞRENME HAFIZASI modalı (TRADE AJANI 🧠) ----------
11361
- Çekirdekteki (main process) sembol bazlı öğrenme deposunu gösterir:
11362
- otomatik istatistik + ajanın mt5_ogrenme ile yazdığı dersler. Tek ders
11363
- silinebilir, sembol kaydı tümden bırakılabilir; depo tavanları
11364
- (60 sembol / 60 ders / 60 işlem / günlük 3 ders; ders = kısa+somut) çekirdekte uygulanır. */
11365
- const FIN_LEARN_TRASH_SVG =
11366
- '<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>';
11367
-
11368
- let finLearnRows = [];
11369
- let finLearnOpenSym = '';
11370
- let finLearnBusy = false;
11371
-
11372
- function finLearnCall(payload) {
11373
- return beast.financeLearn(payload).catch(() => ({ ok: false }));
11374
- }
11375
-
11376
- function finLearnSign(v) {
11377
- const n = Math.round((Number(v) || 0) * 100) / 100;
11378
- return (n >= 0 ? '+' : '') + n;
11379
- }
11380
-
11381
- function finLearnTime(ts) {
11382
- const t = Number(ts) || 0;
11383
- if (!t) return '';
11384
- const d = new Date(t);
11385
- const p = (x) => String(x).padStart(2, '0');
11386
- return p(d.getDate()) + '.' + p(d.getMonth() + 1) + ' ' + p(d.getHours()) + ':' + p(d.getMinutes());
11387
- }
11388
-
11389
- function finLearnKindLabel(kind) {
11390
- return ({ pattern: 'desen', mistake: 'hata', rule: 'kural', observation: 'gözlem' })[String(kind || '')] || 'gözlem';
11391
- }
11392
-
11393
- function finLearnStatsText(st) {
11394
- const trades = Number(st && st.trades) || 0;
11395
- if (!trades) return '';
11396
- const parts = [
11397
- trades + ' işlem',
11398
- '%' + Math.round(((Number(st.wins) || 0) / trades) * 100) + ' kazanç',
11399
- 'net ' + finLearnSign(st.net),
11400
- ];
11401
- if (Number(st.streak) >= 2) parts.push(Number(st.streak) + ' ardışık kayıp');
11402
- if (Number(st.givebacks) > 0) parts.push(Number(st.givebacks) + ' kez kâr geri verildi');
11403
- if (st.lastAt) parts.push('son ' + finLearnSign(st.lastNet));
11404
- /* EN İYİ PERİYOT: bot zaman dilimini buna göre seçer */
11405
- const tfRows = Object.entries((st && st.byTf) || {}).filter(([, s]) => s && Number(s.trades) > 0);
11406
- if (tfRows.length) {
11407
- tfRows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0));
11408
- parts.push('en iyi ' + tfRows[0][0]);
11409
- }
11410
- return parts.join(' · ');
11411
- }
11412
-
11413
- async function finLearnOpen() {
11414
- if (!els.finLearnOverlay) return;
11415
- finLearnOpenSym = '';
11416
- els.finLearnOverlay.hidden = false;
11417
- await finLearnRefresh();
11418
- }
11419
-
11420
- function finLearnCloseModal() {
11421
- if (els.finLearnOverlay) els.finLearnOverlay.hidden = true;
11422
- }
11423
-
11424
- async function finLearnRefresh() {
11425
- const box = els.finLearnList;
11426
- if (!box || finLearnBusy) return;
11427
- finLearnBusy = true;
11428
- box.innerHTML = '<div class="fin-empty">Öğrenme kayıtları yükleniyor…</div>';
11429
- const r = await finLearnCall({ action: 'list' });
11430
- finLearnBusy = false;
11431
- finLearnRows = (r && Array.isArray(r.symbols)) ? r.symbols : [];
11432
- finLearnRender();
11433
- }
11434
-
11435
- function finLearnRender() {
11436
- const box = els.finLearnList;
11437
- if (!box) return;
11438
- box.textContent = '';
11439
- if (els.finLearnCount) els.finLearnCount.textContent = finLearnRows.length ? finLearnRows.length + ' sembol' : '';
11440
- if (!finLearnRows.length) {
11441
- box.innerHTML = '<div class="fin-empty">Henüz öğrenilmiş bir şey yok — işlem kapandıkça istatistik, ajan ders yazdıkça kayıt burada birikir.</div>';
11442
- return;
11443
- }
11444
- for (const row of finLearnRows) box.appendChild(finLearnSymEl(row));
11445
- /* silme sonrası yeniden çizimde açık sembol detayı açık kalsın */
11446
- if (finLearnOpenSym) {
11447
- const el = [...box.children].find((c) => c.dataset && c.dataset.sym === finLearnOpenSym);
11448
- if (el && el._finToggle) el._finToggle();
11449
- }
11450
- }
11451
-
11452
- function finLearnSymEl(row) {
11453
- const sym = String(row.symbol || '');
11454
- const el = document.createElement('div');
11455
- el.className = 'fin-learn-sym';
11456
- el.dataset.sym = sym;
11457
- const head = document.createElement('div');
11458
- head.className = 'fin-learn-sym-head';
11459
- const name = document.createElement('b');
11460
- name.textContent = sym;
11461
- const stats = document.createElement('span');
11462
- stats.className = 'fin-learn-sym-stats';
11463
- stats.textContent = finLearnStatsText(row.stats) || 'henüz kapanan işlem yok';
11464
- const noteCount = document.createElement('span');
11465
- noteCount.className = 'fin-learn-sym-notes';
11466
- noteCount.textContent = (Number(row.notes) || 0) + ' ders' + (row.hasSummary ? ' · özet' : '');
11467
- /* ÖZETLE: Beast Finance JEV-ONLY olduğu için LLM özetleme kapalı —
11468
- düğme gizli (dersler tavan + TTL ile kendiliğinden sadeleşir) */
11469
- const sum = document.createElement('button');
11470
- sum.hidden = true;
11471
- sum.type = 'button';
11472
- sum.className = 'fin-learn-sum-btn';
11473
- sum.title = 'Eski dersleri modele özetlet — hafıza sadeleşir (24+ ders birikince otomatik de çalışır)';
11474
- sum.textContent = 'ÖZET';
11475
- sum.addEventListener('click', async (e) => {
11476
- e.stopPropagation();
11477
- sum.disabled = true;
11478
- sum.textContent = '…';
11479
- const r = await finLearnCall({ action: 'compact', symbol: sym });
11480
- sum.disabled = false;
11481
- sum.textContent = 'ÖZET';
11482
- if (r && r.ok) {
11483
- toast(sym + ' özetlendi: ' + (Number(r.covered) || 0) + ' eski ders kalıcı özete sıkıştı');
11484
- await finLearnRefresh();
11485
- } else {
11486
- toast('Özetlenemedi: ' + ((r && r.error) || '?'));
11487
- }
11488
- });
11489
- const drop = document.createElement('button');
11490
- drop.type = 'button';
11491
- drop.className = 'fin-learn-drop';
11492
- drop.title = sym + ' kaydını TÜMDEN sil (dersler + otomatik istatistik)';
11493
- drop.innerHTML = FIN_LEARN_TRASH_SVG;
11494
- drop.addEventListener('click', async (e) => {
11495
- e.stopPropagation();
11496
- const ok = await uiConfirm(sym + ' için TÜM öğrenme kaydı (dersler + istatistik) silinsin mi?', 'Sil', 'Vazgeç');
11497
- if (!ok) return;
11498
- await finLearnCall({ action: 'drop', symbol: sym });
11499
- if (finLearnOpenSym === sym) finLearnOpenSym = '';
11500
- toast(sym + ' öğrenme kaydı silindi');
11501
- await finLearnRefresh();
11502
- });
11503
- head.appendChild(name);
11504
- head.appendChild(stats);
11505
- head.appendChild(noteCount);
11506
- head.appendChild(sum);
11507
- head.appendChild(drop);
11508
- el.appendChild(head);
11509
-
11510
- const body = document.createElement('div');
11511
- body.className = 'fin-learn-body';
11512
- body.hidden = true;
11513
- el.appendChild(body);
11514
- let loaded = false;
11515
- let busy = false;
11516
- el._finToggle = async () => {
11517
- if (busy) return;
11518
- if (loaded && !body.hidden) {
11519
- body.hidden = true;
11520
- if (finLearnOpenSym === sym) finLearnOpenSym = '';
11521
- return;
11522
- }
11523
- if (loaded) {
11524
- body.hidden = false;
11525
- finLearnOpenSym = sym;
11526
- return;
11527
- }
11528
- busy = true;
11529
- body.hidden = false;
11530
- finLearnOpenSym = sym;
11531
- body.innerHTML = '<div class="fin-empty">yükleniyor…</div>';
11532
- const d = await finLearnCall({ action: 'list', symbol: sym, limit: 25 });
11533
- if (!els.finLearnOverlay || els.finLearnOverlay.hidden) { busy = false; return; }
11534
- finLearnDetail(body, sym, d);
11535
- loaded = true;
11536
- busy = false;
11537
- };
11538
- head.addEventListener('click', () => { el._finToggle(); });
11539
- return el;
11540
- }
11541
-
11542
- function finLearnDetail(body, sym, d) {
11543
- body.textContent = '';
11544
- const st = (d && d.stats) || {};
11545
- const info = document.createElement('div');
11546
- info.className = 'fin-learn-detail-stats';
11547
- const trades = Number(st.trades) || 0;
11548
- const bits = [];
11549
- if (trades) {
11550
- bits.push(`${trades} işlem · ${Number(st.wins) || 0} kazanç · ${Number(st.losses) || 0} kayıp`);
11551
- bits.push(`net ${finLearnSign(st.net)} · en iyi ${finLearnSign(st.bestNet)} · en kötü ${finLearnSign(st.worstNet)}`);
11552
- bits.push(`MFE toplam ${finLearnSign(st.mfe)} · MAE toplam ${finLearnSign(st.mae)}`);
11553
- if (Number(st.givebacks) > 0) bits.push(`${Number(st.givebacks)} kez kâr geri verildi (${finLearnSign(st.givebackAmount)})`);
11554
- } else {
11555
- bits.push('Bu sembolde henüz kapanan işlem yok — istatistik ilk kapanışta oluşur.');
11556
- }
11557
- info.textContent = bits.join(' — ');
11558
- body.appendChild(info);
11559
-
11560
- /* PERİYOT KIRILIMI: hangi zaman dilimi kazandırıyor — bot işlem periyodunu
11561
- (timeframe) buna göre SEÇER; negatif periyotta risk düşürülür */
11562
- const tfRows = Object.entries(st.byTf || {}).filter(([, s]) => s && Number(s.trades) > 0);
11563
- if (tfRows.length) {
11564
- const tfHead = document.createElement('div');
11565
- tfHead.className = 'fin-learn-sec';
11566
- tfHead.textContent = 'ZAMAN DİLİMLERİ (işlem periyodunu bot seçer — en iyi net üstte)';
11567
- body.appendChild(tfHead);
11568
- tfRows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0));
11569
- for (const [tf, s] of tfRows) {
11570
- const tr = document.createElement('div');
11571
- tr.className = 'fin-learn-trade ' + (Number(s.net) < 0 ? 'loss' : 'win');
11572
- const w = Math.round((Number(s.wins) / Number(s.trades)) * 100);
11573
- tr.textContent = `${tf} · ${s.trades} işlem · %${w} kazanç · net ${finLearnSign(s.net)}` +
11574
- (Number(s.givebacks) > 0 ? ` · ${Number(s.givebacks)} kâr geri verme` : '') +
11575
- (Number(s.net) < 0 ? ' · NEGATİF: riski düşür/teyit ara' : '');
11576
- body.appendChild(tr);
11577
- }
11578
- }
11579
-
11580
- /* YAPILAN YANLIŞLAR: zararla kapananlar otomatik kaydedilir — ajan bunlardan
11581
- hata dersi (kind:"mistake") çıkarır; tekrar eden kalıp burada görünür */
11582
- const mistakes = (d && Array.isArray(d.mistakes)) ? d.mistakes : [];
11583
- if (mistakes.length) {
11584
- const mHead = document.createElement('div');
11585
- mHead.className = 'fin-learn-sec';
11586
- mHead.textContent = 'YAPILAN YANLIŞLAR (' + mistakes.length + ' — ders çıkar, tekrarlama)';
11587
- body.appendChild(mHead);
11588
- for (const m of mistakes.slice().reverse()) {
11589
- const tr = document.createElement('div');
11590
- tr.className = 'fin-learn-trade loss';
11591
- const side = String(m.side || '').toLowerCase();
11592
- const sideTxt = side === 'buy' ? 'AL' : side === 'sell' ? 'SAT' : (side || '?');
11593
- tr.textContent = `${finLearnTime(m.at)} · ${m.tf || 'periyot?'} · ${sideTxt} · ${finLearnSign(m.net)}` +
11594
- (m.reason ? ` · ${String(m.reason)}` : '') +
11595
- (m.giveback ? ' · kâr geri verildi' : '') +
11596
- (m.mae != null ? ` · MAE ${finLearnSign(m.mae)}` : '');
11597
- body.appendChild(tr);
11598
- }
11599
- }
11600
-
11601
- /* KALICI ÖZET (opencode tarzı compaction): eski derslerin modele
11602
- özetlettirilmiş hâli — ham dersler silinse de bilgi burada kalır */
11603
- const summ = d && d.summary && d.summary.text ? d.summary : null;
11604
- if (summ) {
11605
- const sHead = document.createElement('div');
11606
- sHead.className = 'fin-learn-sec';
11607
- sHead.textContent = 'KALICI ÖZET (opencode tarzı sıkıştırma' +
11608
- (Number(summ.covered) ? ' · ' + Number(summ.covered) + ' eski ders' : '') + ')';
11609
- body.appendChild(sHead);
11610
- const sBox = document.createElement('div');
11611
- sBox.className = 'fin-learn-summary';
11612
- sBox.textContent = String(summ.text || '');
11613
- body.appendChild(sBox);
11614
- const sAt = document.createElement('div');
11615
- sAt.className = 'fin-learn-summary-at';
11616
- sAt.textContent = 'güncelleme: ' + finLearnTime(summ.at);
11617
- body.appendChild(sAt);
11618
- }
11619
-
11620
- const notes = (d && Array.isArray(d.notes)) ? d.notes : [];
11621
- const nHead = document.createElement('div');
11622
- nHead.className = 'fin-learn-sec';
11623
- nHead.textContent = 'DERSLER (' + notes.length + ')';
11624
- body.appendChild(nHead);
11625
- if (!notes.length) {
11626
- const e = document.createElement('div');
11627
- e.className = 'fin-empty';
11628
- 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).';
11629
- body.appendChild(e);
11630
- } else {
11631
- for (const n of notes.slice().reverse()) {
11632
- const rowEl = document.createElement('div');
11633
- rowEl.className = 'fin-learn-note';
11634
- const kind = document.createElement('span');
11635
- kind.className = 'fin-learn-kind k-' + String(n.kind || 'observation');
11636
- kind.textContent = finLearnKindLabel(n.kind);
11637
- let tfEl = null;
11638
- if (n.tf) {
11639
- tfEl = document.createElement('span');
11640
- tfEl.className = 'fin-learn-kind';
11641
- tfEl.textContent = String(n.tf);
11642
- tfEl.title = 'Bu dersin zaman dilimi';
11643
- }
11644
- const txt = document.createElement('span');
11645
- txt.className = 'fin-learn-note-text';
11646
- txt.textContent = String(n.text || '');
11647
- txt.title = txt.textContent;
11648
- const at = document.createElement('span');
11649
- at.className = 'fin-learn-note-at';
11650
- at.textContent = finLearnTime(n.at);
11651
- const del = document.createElement('button');
11652
- del.type = 'button';
11653
- del.className = 'fin-learn-note-del';
11654
- del.title = 'Bu dersi sil — yanlış/geçersiz ders hafızayı zehirlemesin';
11655
- del.innerHTML = FIN_LEARN_TRASH_SVG;
11656
- del.addEventListener('click', async () => {
11657
- const r = await finLearnCall({ action: 'remove', id: n.id, symbol: sym });
11658
- if (r && r.ok) {
11659
- toast('Ders silindi');
11660
- await finLearnRefresh();
11661
- } else {
11662
- toast('Ders silinemedi');
11663
- }
11664
- });
11665
- rowEl.appendChild(kind);
11666
- if (tfEl) rowEl.appendChild(tfEl);
11667
- rowEl.appendChild(txt);
11668
- rowEl.appendChild(at);
11669
- rowEl.appendChild(del);
11670
- body.appendChild(rowEl);
11671
- }
11672
- }
11673
-
11674
- const tradeRows = (d && Array.isArray(d.trades)) ? d.trades : [];
11675
- if (tradeRows.length) {
11676
- const tHead = document.createElement('div');
11677
- tHead.className = 'fin-learn-sec';
11678
- tHead.textContent = 'SON İŞLEMLER (otomatik)';
11679
- body.appendChild(tHead);
11680
- for (const t of tradeRows.slice().reverse()) {
11681
- const tr = document.createElement('div');
11682
- tr.className = 'fin-learn-trade ' + (Number(t.net) < 0 ? 'loss' : 'win');
11683
- const side = String(t.side || '').toLowerCase();
11684
- const sideTxt = side === 'buy' ? 'AL' : side === 'sell' ? 'SAT' : (side || '?');
11685
- tr.textContent = `${finLearnTime(t.at)} · ${t.tf ? t.tf + ' · ' : ''}${sideTxt} · ${finLearnSign(t.net)}` +
11686
- (t.mfe != null ? ` · MFE ${finLearnSign(t.mfe)}` : '') +
11687
- (t.mae != null ? ` · MAE ${finLearnSign(t.mae)}` : '') +
11688
- (t.reason ? ` · ${String(t.reason)}` : '');
11689
- body.appendChild(tr);
11690
- }
11691
- }
11692
- }
11693
-
11694
- if (els.finLearnBtn) els.finLearnBtn.addEventListener('click', () => { finLearnOpen(); });
11695
- if (els.finLearnClose) els.finLearnClose.addEventListener('click', finLearnCloseModal);
11696
- if (els.finLearnOverlay) {
11697
- els.finLearnOverlay.addEventListener('click', (e) => {
11698
- if (e.target === els.finLearnOverlay) finLearnCloseModal();
11699
- });
11700
- els.finLearnOverlay.addEventListener('keydown', (e) => {
11701
- if (e.key === 'Escape') { e.stopPropagation(); finLearnCloseModal(); }
11702
- });
11703
- }
11704
- if (els.finLearnClear) {
11705
- els.finLearnClear.addEventListener('click', async () => {
11706
- const ok = await uiConfirm('TÜM sembollerin öğrenme DERSLERİ silinsin mi? Otomatik istatistikler korunur; işlem geri alınamaz.', 'Tümünü sil', 'Vazgeç');
11707
- if (!ok) return;
11708
- const r = await finLearnCall({ action: 'clear', all: true });
11709
- toast(r && r.ok ? ('Dersler temizlendi' + (r.removed ? ' (' + r.removed + ' kayıt)' : '')) : 'Dersler temizlenemedi');
11710
- await finLearnRefresh();
11711
- });
11712
- }
11713
-
11714
- if (els.finSymAdd) els.finSymAdd.addEventListener('click', finSymPickerOpen);
11715
- /* TOPLU KAPATMA: tüm pozisyonlar + bekleyen emirler tek onayla kapanır */
11716
- if (els.finCloseAll) {
11717
- els.finCloseAll.addEventListener('click', async () => {
11718
- const ok = await uiConfirm(
11719
- 'TÜM açık pozisyonlar ve bekleyen emirler kapatılsın mı?\n(TOPLU KAPATMA — geri alınamaz)',
11720
- 'Tümünü Kapat',
11721
- 'Vazgeç'
11722
- );
11723
- if (!ok) return;
11724
- els.finCloseAll.disabled = true;
11725
- const r = await beast.financeCloseAll().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
11726
- if (r && r.ok) toast('Toplu kapatma: ' + (Number(r.closed) || 0) + ' pozisyon kapatıldı');
11727
- else toast('Toplu kapatma başarısız: ' + ((r && r.error) || '?'));
11728
- finSnapshot();
11729
- });
11730
- }
11731
- if (els.finWatchClear) {
11732
- els.finWatchClear.addEventListener('click', async () => {
11733
- if (!finWatch.length) {
11734
- toast('İzleme listesi zaten boş');
11735
- return;
11736
- }
11737
- const ok = await uiConfirm(
11738
- 'İzleme listesindeki TÜM semboller silinsin mi? (' + finWatch.join(', ') + ')\nSilinenler geçmişte kalır — sembol seçicide TEK TIKLA geri ekleyebilirsin.',
11739
- 'Tümünü Sil',
11740
- 'Vazgeç'
11741
- );
11742
- if (!ok) return;
11743
- finWatchSet([]);
11744
- finSaveCfg({ symbols: [] });
11745
- finSymBtnUpdate();
11746
- clearTimeout(finSymRefreshTimer);
11747
- finSymRefreshTimer = setTimeout(() => finSnapshot(), 700);
11748
- toast('İzleme listesi temizlendi — semboller geçmişte duruyor');
11749
- });
11750
- }
11751
- if (els.finSymClose) els.finSymClose.addEventListener('click', finSymPickerClose);
11752
- if (els.finSymOverlay) {
11753
- els.finSymOverlay.addEventListener('click', (e) => {
11754
- if (e.target === els.finSymOverlay) finSymPickerClose();
11755
- });
11756
- els.finSymOverlay.addEventListener('keydown', (e) => {
11757
- if (e.key === 'Escape') { e.stopPropagation(); finSymPickerClose(); }
11758
- });
11759
- }
11760
- if (els.finTeamClose) els.finTeamClose.addEventListener('click', finTeamPickerClose);
11761
- if (els.finTeamOverlay) {
11762
- els.finTeamOverlay.addEventListener('click', (e) => {
11763
- if (e.target === els.finTeamOverlay) finTeamPickerClose();
11764
- });
11765
- els.finTeamOverlay.addEventListener('keydown', (e) => {
11766
- if (e.key === 'Escape') { e.stopPropagation(); finTeamPickerClose(); }
11767
- });
11768
- }
11769
- if (els.finSymSearch) {
11770
- els.finSymSearch.addEventListener('input', () => {
11771
- clearTimeout(finSymSearchTimer);
11772
- finSymSearchTimer = setTimeout(() => finSymLoad(els.finSymSearch.value.trim()), 350);
11773
- });
11774
- }
11775
9618
  $('#filePick').addEventListener('click', async () => {
11776
9619
  /* Studio modunda klasör seçimi Studio'nun KENDİ kökünü değiştirir —
11777
9620
  IDE köküne dokunmaz (dünyalar ayrıdır) */
@@ -13553,7 +11396,6 @@ async function sbClone(input) {
13553
11396
  function setSandboxMode(on) {
13554
11397
  if (on && ideModeOn()) { setIdeMode(false); }
13555
11398
  if (on && studioModeOn()) { setStudioMode(false); }
13556
- if (on && financeModeOn()) { setFinanceMode(false); }
13557
11399
  if (on && mcpModeOn()) setMcpMode(false); /* MCP açıkken Sandbox'a geçiş — MCP kapanır */
13558
11400
  document.body.classList.toggle('sandbox-mode', !!on);
13559
11401
  if (els.sbBtn) els.sbBtn.classList.toggle('on', !!on);
@@ -13591,7 +11433,6 @@ async function setMcpMode(on) {
13591
11433
  if (on && ideModeOn()) await setIdeMode(false);
13592
11434
  if (on && studioModeOn()) await setStudioMode(false);
13593
11435
  if (on && sbModeOn()) setSandboxMode(false);
13594
- if (on && financeModeOn()) setFinanceMode(false);
13595
11436
  document.body.classList.toggle('mcp-mode', !!on);
13596
11437
  if (els.mcpBtn) els.mcpBtn.classList.toggle('on', !!on);
13597
11438
  const brandSub = document.querySelector('#brand .brand-sub');