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