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.
- package/package.json +1 -1
- package/src/agent/bots.js +669 -664
- package/src/agent/customtools.js +6 -19
- package/src/agent/engine.js +55 -307
- package/src/agent/seeds/tool-yazma/SKILL.md +13 -16
- package/src/agent/skills.js +14 -35
- package/src/agent/supertonic.js +622 -0
- package/src/agent/tools.js +3 -7
- package/src/main.js +77 -6068
- package/src/preload.js +2 -17
- package/src/renderer/i18n.js +14 -8
- package/src/renderer/index.html +0 -297
- package/src/renderer/renderer.js +142 -2280
- package/src/renderer/style.css +3 -825
- package/src/agent/defaulttools/mt5_barlar2/run.js +0 -188
- package/src/agent/defaulttools/mt5_barlar2/tool.json +0 -23
- package/src/agent/defaulttools/mt5_bekleyen/run.js +0 -105
- package/src/agent/defaulttools/mt5_bekleyen/tool.json +0 -18
- package/src/agent/defaulttools/mt5_durum/run.js +0 -93
- package/src/agent/defaulttools/mt5_durum/tool.json +0 -10
- package/src/agent/defaulttools/mt5_emir/run.js +0 -98
- package/src/agent/defaulttools/mt5_emir/tool.json +0 -17
- package/src/agent/defaulttools/mt5_emir_iptal/run.js +0 -90
- package/src/agent/defaulttools/mt5_emir_iptal/tool.json +0 -12
- package/src/agent/defaulttools/mt5_fiyat/run.js +0 -87
- package/src/agent/defaulttools/mt5_fiyat/tool.json +0 -16
- package/src/agent/defaulttools/mt5_gecmis/run.js +0 -87
- package/src/agent/defaulttools/mt5_gecmis/tool.json +0 -12
- package/src/agent/defaulttools/mt5_kapat/run.js +0 -108
- package/src/agent/defaulttools/mt5_kapat/tool.json +0 -15
- package/src/agent/defaulttools/mt5_m15_m5/m15m5.py +0 -60
- package/src/agent/defaulttools/mt5_m15_m5/run.js +0 -73
- package/src/agent/defaulttools/mt5_m15_m5/tool.json +0 -10
- package/src/agent/defaulttools/mt5_pozisyon_gecmis/run.js +0 -144
- package/src/agent/defaulttools/mt5_pozisyon_gecmis/tool.json +0 -12
- package/src/agent/defaulttools/mt5_shot/run.js +0 -649
- package/src/agent/defaulttools/mt5_shot/stitch.js +0 -151
- package/src/agent/defaulttools/mt5_shot/tool.json +0 -25
- package/src/agent/defaulttools/mt5_sltp/run.js +0 -92
- package/src/agent/defaulttools/mt5_sltp/tool.json +0 -14
- package/src/agent/financetools.js +0 -1025
- package/src/agent/finindicators.js +0 -254
- package/src/agent/finrisk.js +0 -289
- package/src/agent/finstats.js +0 -239
- package/src/agent/finwatch.js +0 -302
- package/src/agent/mt5/BeastFinance.mq5 +0 -498
- package/src/agent/mt5/BeastShot.tpl +0 -0
- package/src/agent/mt5setup.js +0 -542
- package/src/agent/scripts/mt5_bridge.py +0 -575
- package/src/agent/seeds/haber-duygu/SKILL.md +0 -49
- package/src/agent/seeds/mql5/SKILL.md +0 -154
- package/src/agent/seeds/price-action/SKILL.md +0 -60
- package/src/agent/seeds/risk-yonetimi/SKILL.md +0 -52
- package/src/mt5bridge.js +0 -215
package/src/renderer/renderer.js
CHANGED
|
@@ -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 (
|
|
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 === '
|
|
2963
|
-
? 'Merhaba kanka,
|
|
2964
|
-
:
|
|
2965
|
-
|
|
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 (
|
|
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).
|
|
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;
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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');
|