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