beast-agent 2.63.3 → 2.63.5
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/engine.js +35 -17
- package/src/agent/financetools.js +49 -20
- package/src/agent/seeds/typesafe-ai/SKILL.md +60 -0
- package/src/agent/tools.js +5 -2
- package/src/agent/typesafe.js +278 -0
- package/src/main.js +395 -101
- package/src/preload.js +4 -0
- package/src/renderer/i18n.js +38 -0
- package/src/renderer/index.html +2 -0
- package/src/renderer/renderer.js +169 -24
package/src/main.js
CHANGED
|
@@ -31,6 +31,7 @@ const fsguard = require('./agent/fsguard');
|
|
|
31
31
|
const log = require('./agent/logger');
|
|
32
32
|
const squeeze = require('./agent/squeeze');
|
|
33
33
|
const chansessions = require('./agent/channelsessions');
|
|
34
|
+
const typesafeMod = require('./agent/typesafe');
|
|
34
35
|
const QRCode = require('qrcode'); /* Expo Go QR (bc-expurl) — whatsapp ile aynı paket */
|
|
35
36
|
|
|
36
37
|
/* Renderer'a sır gönderirken kullanılan maske; kaydederken aynen geri gelirse
|
|
@@ -317,6 +318,13 @@ ensureBeastCode();
|
|
|
317
318
|
startHealthServer(); /* splash/boot aşamasından itibaren /health ayakta */
|
|
318
319
|
try { setSearchChain(settings.searchChain); } catch {}
|
|
319
320
|
try { setTinyfishKey(settings.tinyfishKey || null); } catch {}
|
|
321
|
+
/* TypeSafe (System One/Jev) köprüsü: ajan araçları ayarlardaki anahtarı canlı okur */
|
|
322
|
+
try {
|
|
323
|
+
typesafeMod.setConfig(() => ({
|
|
324
|
+
apiKey: settings.typesafe && settings.typesafe.apiKey,
|
|
325
|
+
model: settings.typesafe && settings.typesafe.model,
|
|
326
|
+
}));
|
|
327
|
+
} catch {}
|
|
320
328
|
let wa = null;
|
|
321
329
|
let waChats = new Map(); // jid (numara/grup) -> KENDİ oturumu: her sohbet AYRI (gizlilik)
|
|
322
330
|
let waCronSid = null; // WhatsApp CRON TABAN OTURUMU: otomatik işler; hiçbir kişiye bağlı DEĞİL
|
|
@@ -503,6 +511,12 @@ function parseSettingsText(text) {
|
|
|
503
511
|
if (!parsed.supermemory || typeof parsed.supermemory !== 'object') {
|
|
504
512
|
parsed.supermemory = { enabled: true, baseUrl: 'http://localhost:6767', apiKey: '', containerTag: 'beast' };
|
|
505
513
|
}
|
|
514
|
+
/* TypeSafe (System One/Jev) — anahtar Ayarlar → TypeSafe sekmesinden girilir;
|
|
515
|
+
boşken typesafe_decision aracı anlaşılır hata döner */
|
|
516
|
+
if (!parsed.typesafe || typeof parsed.typesafe !== 'object') {
|
|
517
|
+
parsed.typesafe = { apiKey: '', model: 'jev-latest' };
|
|
518
|
+
}
|
|
519
|
+
if (typeof parsed.typesafe.model !== 'string' || !parsed.typesafe.model.trim()) parsed.typesafe.model = 'jev-latest';
|
|
506
520
|
return parsed;
|
|
507
521
|
}
|
|
508
522
|
|
|
@@ -6754,6 +6768,7 @@ ipcMain.handle('settings:get', () => {
|
|
|
6754
6768
|
if (out.email && out.email.pass) out.email.pass = SECRET_MASK;
|
|
6755
6769
|
if (out.waTts && out.waTts.key) out.waTts.key = SECRET_MASK;
|
|
6756
6770
|
if (out.supermemory && out.supermemory.apiKey) out.supermemory.apiKey = SECRET_MASK;
|
|
6771
|
+
if (out.typesafe && out.typesafe.apiKey) out.typesafe.apiKey = SECRET_MASK;
|
|
6757
6772
|
if (out.fallout && Array.isArray(out.fallout.slots)) {
|
|
6758
6773
|
for (const s of out.fallout.slots) if (s && s.key) s.key = SECRET_MASK;
|
|
6759
6774
|
}
|
|
@@ -7132,6 +7147,41 @@ ipcMain.handle('supermemory:get', () => {
|
|
|
7132
7147
|
up: engine ? !!engine._smUp : null,
|
|
7133
7148
|
};
|
|
7134
7149
|
});
|
|
7150
|
+
|
|
7151
|
+
/* TypeSafe (System One/Jev): anahtar masked döner; typesafe_decision aracı
|
|
7152
|
+
settings'ten CANLI okur (setConfig kancası) — kaydetmek yeterli. */
|
|
7153
|
+
ipcMain.handle('typesafe:get', () => {
|
|
7154
|
+
const t = settings.typesafe || {};
|
|
7155
|
+
return { apiKey: t.apiKey ? SECRET_MASK : '', model: t.model || 'jev-latest', set: !!t.apiKey };
|
|
7156
|
+
});
|
|
7157
|
+
ipcMain.handle('typesafe:set', (_e, cfg) => {
|
|
7158
|
+
try {
|
|
7159
|
+
const incomingKey = String((cfg && cfg.apiKey) || '').trim();
|
|
7160
|
+
const prevKey = settings.typesafe && settings.typesafe.apiKey ? String(settings.typesafe.apiKey) : '';
|
|
7161
|
+
settings.typesafe = {
|
|
7162
|
+
/* maskeli anahtar geri gelirse mevcut korunur */
|
|
7163
|
+
apiKey: incomingKey === SECRET_MASK ? prevKey : incomingKey,
|
|
7164
|
+
model: String((cfg && cfg.model) || 'jev-latest').trim() || 'jev-latest',
|
|
7165
|
+
};
|
|
7166
|
+
saveSettings();
|
|
7167
|
+
return {
|
|
7168
|
+
ok: true,
|
|
7169
|
+
typesafe: { apiKey: settings.typesafe.apiKey ? SECRET_MASK : '', model: settings.typesafe.model, set: !!settings.typesafe.apiKey },
|
|
7170
|
+
};
|
|
7171
|
+
} catch (e) {
|
|
7172
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
7173
|
+
}
|
|
7174
|
+
});
|
|
7175
|
+
ipcMain.handle('typesafe:test', async (_e, payload) => {
|
|
7176
|
+
try {
|
|
7177
|
+
const typed = String((payload && payload.apiKey) || '').trim();
|
|
7178
|
+
const key = !typed || typed === SECRET_MASK ? String((settings.typesafe && settings.typesafe.apiKey) || '') : typed;
|
|
7179
|
+
const model = String((payload && payload.model) || '').trim() || String((settings.typesafe && settings.typesafe.model) || '');
|
|
7180
|
+
return await typesafeMod.probe(key, model);
|
|
7181
|
+
} catch (e) {
|
|
7182
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
7183
|
+
}
|
|
7184
|
+
});
|
|
7135
7185
|
ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
|
|
7136
7186
|
/* opencode permission reply (BC): UI kartı üçlü cevap verir —
|
|
7137
7187
|
'once' | 'always' | 'reject' (+ opsiyonel geri bildirim) */
|
|
@@ -9402,6 +9452,11 @@ const financeState = {
|
|
|
9402
9452
|
reportCheckAt: 0,
|
|
9403
9453
|
flattening: false,
|
|
9404
9454
|
breachBusy: false,
|
|
9455
|
+
/* TUR RİTMİ (trader kararı): sert hareket/haber anında geçici hızlanma.
|
|
9456
|
+
pace = { sec, until, base } — süre dolunca otomatik taban aralığa döner;
|
|
9457
|
+
kalıcı değişiklik settings.finance.intervalSec'e yazılır. */
|
|
9458
|
+
pace: { sec: 0, until: 0, base: 0 },
|
|
9459
|
+
paceTimer: null,
|
|
9405
9460
|
};
|
|
9406
9461
|
|
|
9407
9462
|
function financeDir() {
|
|
@@ -9455,9 +9510,9 @@ function finCfg() {
|
|
|
9455
9510
|
if (!Number.isFinite(Number(f.analysisCount))) f.analysisCount = 2;
|
|
9456
9511
|
f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(f.analysisCount) || 0)));
|
|
9457
9512
|
if (!f.analysisAuto) f.analysisTeam = finRolesValid(f.analysisTeam);
|
|
9458
|
-
/* ROL → SKILL eşleştirmesi: rol başına
|
|
9459
|
-
seçim yapılır.
|
|
9460
|
-
adları kurulu katalogdan gelir. */
|
|
9513
|
+
/* ROL → SKILL eşleştirmesi: rol başına EN FAZLA 2 skill — modalda çoklu
|
|
9514
|
+
seçim yapılır (ör. price-action + typesafe-ai). Eski fazla seçimlerden
|
|
9515
|
+
yalnız İLK İKİSİ korunur (göç). Skill adları kurulu katalogdan gelir. */
|
|
9461
9516
|
if (f.roleSkillsDefaultsCleared !== true) {
|
|
9462
9517
|
f.roleSkills = {};
|
|
9463
9518
|
f.roleSkillsDefaultsCleared = true;
|
|
@@ -9465,12 +9520,12 @@ function finCfg() {
|
|
|
9465
9520
|
if (!f.roleSkills || typeof f.roleSkills !== 'object') f.roleSkills = {};
|
|
9466
9521
|
for (const d of FIN_ROLES) {
|
|
9467
9522
|
const cur = Array.isArray(f.roleSkills[d.id]) ? f.roleSkills[d.id] : [];
|
|
9468
|
-
f.roleSkills[d.id] = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
9523
|
+
f.roleSkills[d.id] = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 2);
|
|
9469
9524
|
}
|
|
9470
|
-
/* TRADER PLAYBOOK: asıl karar vericinin zorunlu skill'
|
|
9525
|
+
/* TRADER PLAYBOOK: asıl karar vericinin zorunlu skill'leri (roleSkills.trader) */
|
|
9471
9526
|
{
|
|
9472
9527
|
const cur = Array.isArray(f.roleSkills.trader) ? f.roleSkills.trader : [];
|
|
9473
|
-
f.roleSkills.trader = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
9528
|
+
f.roleSkills.trader = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 2);
|
|
9474
9529
|
}
|
|
9475
9530
|
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
9476
9531
|
if (!Number(f.minLot)) f.minLot = 0.01;
|
|
@@ -10712,11 +10767,13 @@ async function finRecordClose(ticket, st) {
|
|
|
10712
10767
|
finExcSave();
|
|
10713
10768
|
finExcFlush(true);
|
|
10714
10769
|
const kind = finwatch.closeKind(closeReason);
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10770
|
+
const tf = String((st && st.tf) || '') || '';
|
|
10771
|
+
finJournal({ kind: 'close', ticket, symbol, net: rounded, side: (st && st.side) || '', mfe, mae, reason: kind || '', timeframe: tf || '' });
|
|
10772
|
+
/* SEMBOL + PERİYOT BAZLI SÜREKLİ ÖĞRENME: her kapanış istatistiğe işlenir;
|
|
10773
|
+
zararla kapananlar yapılandırılmış hata kaydı olur (mt5_ogrenme) */
|
|
10774
|
+
finLearnRecordClose({ symbol, side: (st && st.side) || '', net: rounded, mfe, mae, reason: kind || '', timeframe: tf });
|
|
10718
10775
|
const pl = rounded == null ? '' : ` · K/Z ${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
|
|
10719
|
-
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${pl}`;
|
|
10776
|
+
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${tf ? ' · ' + tf : ''}${pl}`;
|
|
10720
10777
|
financeLog('[watchdog] ' + line);
|
|
10721
10778
|
if (finCfg().notifyTrades !== false) financeNotify(line, 'close');
|
|
10722
10779
|
if (rounded != null) finStatsRefresh(true);
|
|
@@ -10726,10 +10783,17 @@ async function finRecordClose(ticket, st) {
|
|
|
10726
10783
|
const label = kind === 'stop' ? 'STOP OLDU' : kind === 'tp' ? 'TP OLDU' : 'STOP-OUT (teminat)';
|
|
10727
10784
|
const how = kind === 'tp' ? 'take-profit' : kind === 'stop' ? 'stop-loss' : 'stop-out';
|
|
10728
10785
|
const netTxt = rounded == null ? '?' : `${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
|
|
10786
|
+
const loss = rounded != null && rounded < 0;
|
|
10787
|
+
/* YANLIŞTAN DERS: zararla kapanışta ajan hata dersini YAZMAKLA yükümlü —
|
|
10788
|
+
ders bir sonraki turun girdisine (öğrenme digest'i) otomatik girer */
|
|
10789
|
+
const lessonLine = loss
|
|
10790
|
+
? `\nYANLIŞTAN DERS (ZORUNLU): mt5_ogrenme {action:"add", symbol:"${symbol || ''}", timeframe:"${tf || 'M15'}", kind:"mistake", text:"yanlış neydi + bundan sonra kaçınma kuralı"} ile hatayı yaz (tek kısa cümle: sebep + kaçınma; ör. "M5 destek kırılımında teyitsiz girdim; kapanış teyidi beklemeliydim"). Aynı hatayı tekrarlama — bu ders sonraki turların karar girdisidir.`
|
|
10791
|
+
: `\nKÂR/BAŞARI: işe yarayan desen varsa (periyot + koşul + sonuç) mt5_ogrenme {action:"add", symbol:"${symbol || ''}", timeframe:"${tf || 'M15'}", kind:"pattern", text:"..."} ile tek kısa cümle olarak kaydet — çalışan deseni tekrar kullan.`;
|
|
10729
10792
|
finWakeAgents(
|
|
10730
10793
|
`[FİNANS OLAYI — ${label}, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
|
|
10731
|
-
`- ${symbol || '?'}${st && st.side ? ' ' + String(st.side).toUpperCase() : ''} #${ticket} ${how} ile kapandı · net ${netTxt}\n` +
|
|
10732
|
-
`Şimdi yap: pozisyon kapandı — hesabı, risk durumunu (kayıp serisi/günlük limit) ve planını HEMEN değerlendir; gerekiyorsa yeni emir aç ya da BEKLE. Kısa rapor ver
|
|
10794
|
+
`- ${symbol || '?'}${st && st.side ? ' ' + String(st.side).toUpperCase() : ''} #${ticket}${tf ? ' · ' + tf : ''} ${how} ile kapandı · net ${netTxt}\n` +
|
|
10795
|
+
`Şimdi yap: pozisyon kapandı — hesabı, risk durumunu (kayıp serisi/günlük limit), periyot istatistiğini ve planını HEMEN değerlendir; gerekiyorsa yeni emir aç ya da BEKLE. Kısa rapor ver.` +
|
|
10796
|
+
lessonLine,
|
|
10733
10797
|
{ kind, ticket, symbol }
|
|
10734
10798
|
);
|
|
10735
10799
|
}
|
|
@@ -10915,6 +10979,9 @@ async function finWatchTick() {
|
|
|
10915
10979
|
symbol: String(p.symbol || ''),
|
|
10916
10980
|
side: Number(p.type) === 0 ? 'buy' : 'sell',
|
|
10917
10981
|
entry: Number(p.price_open) || 0,
|
|
10982
|
+
/* ZAMAN DİLİMİ: ajan emir açarken yorum yazar ("Beast M15") — kapanışta
|
|
10983
|
+
öğrenme bu periyoda işlenir */
|
|
10984
|
+
tf: finLearnParseTf(p.comment),
|
|
10918
10985
|
r: 0,
|
|
10919
10986
|
be: false,
|
|
10920
10987
|
partial: false,
|
|
@@ -10925,9 +10992,12 @@ async function finWatchTick() {
|
|
|
10925
10992
|
financeState.watch.set(ticket, st);
|
|
10926
10993
|
const isBeast = Number(p.magic) === 20260908 || /beast/i.test(String(p.comment || ''));
|
|
10927
10994
|
if (isBeast) {
|
|
10928
|
-
finJournal({ kind: 'open', ticket, symbol: st.symbol, side: st.side, volume: Number(p.volume) || 0, entry: st.entry, sl: Number(p.sl) || 0, tp: Number(p.tp) || 0 });
|
|
10995
|
+
finJournal({ kind: 'open', ticket, symbol: st.symbol, side: st.side, volume: Number(p.volume) || 0, entry: st.entry, sl: Number(p.sl) || 0, tp: Number(p.tp) || 0, timeframe: st.tf || '' });
|
|
10929
10996
|
}
|
|
10930
10997
|
finWatchSave();
|
|
10998
|
+
} else if (!st.tf) {
|
|
10999
|
+
const tfNow = finLearnParseTf(p.comment);
|
|
11000
|
+
if (tfNow) { st.tf = tfNow; finWatchSave(); }
|
|
10931
11001
|
}
|
|
10932
11002
|
/* KÂR KORUMA: görülen EN İYİ kâr (fiyat farkı) tick aralarında da korunur */
|
|
10933
11003
|
{
|
|
@@ -11171,8 +11241,8 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11171
11241
|
}
|
|
11172
11242
|
/* SEMBOL BAZLI ÖĞRENME kaydı: günlüğe + AKIŞ paneline düşer */
|
|
11173
11243
|
if (k === 'learn') {
|
|
11174
|
-
finJournal({ kind: 'learn', sid, agent: who, symbol: d.symbol, text: d.text, learnKind: d.kind || 'observation' });
|
|
11175
|
-
const line = `🧠 ÖĞRENME: ${d.symbol || '?'} — ${String(d.text || '').replace(/\s+/g, ' ').slice(0, 160)}`;
|
|
11244
|
+
finJournal({ kind: 'learn', sid, agent: who, symbol: d.symbol, text: d.text, learnKind: d.kind || 'observation', timeframe: d.timeframe || '' });
|
|
11245
|
+
const line = `🧠 ÖĞRENME${d.timeframe ? ' [' + d.timeframe + ']' : ''}: ${d.symbol || '?'} — ${String(d.text || '').replace(/\s+/g, ' ').slice(0, 160)}`;
|
|
11176
11246
|
financeLog('[ogrenme] ' + line);
|
|
11177
11247
|
finPush('trade', { line });
|
|
11178
11248
|
return;
|
|
@@ -11186,7 +11256,7 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11186
11256
|
return;
|
|
11187
11257
|
}
|
|
11188
11258
|
if (k === 'trade') {
|
|
11189
|
-
finJournal({ kind: 'trade', sid, agent: who, symbol: d.symbol, side: d.side, volume: d.volume, sl: d.sl, tp: d.tp, reason: d.reason || d.comment || '' });
|
|
11259
|
+
finJournal({ kind: 'trade', sid, agent: who, symbol: d.symbol, side: d.side, volume: d.volume, sl: d.sl, tp: d.tp, timeframe: d.timeframe || '', reason: d.reason || d.comment || '' });
|
|
11190
11260
|
} else if (k === 'close') {
|
|
11191
11261
|
/* KISMİ KAPATMA (kısmi TP / kısmi stop): ajan kararı; günlük + bildirim.
|
|
11192
11262
|
Kesin toplam K/Z yine watchdog tarafından yazılır (çift sayım yok). */
|
|
@@ -11206,14 +11276,14 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11206
11276
|
} else if (k === 'modify') {
|
|
11207
11277
|
finJournal({ kind: 'modify', sid, agent: who, ticket: d.ticket, sl: d.sl, tp: d.tp });
|
|
11208
11278
|
} else if (k === 'pending') {
|
|
11209
|
-
finJournal({ kind: 'pending', sid, agent: who, symbol: d.symbol, type: d.type, volume: d.volume, reason: d.reason || '' });
|
|
11279
|
+
finJournal({ kind: 'pending', sid, agent: who, symbol: d.symbol, type: d.type, volume: d.volume, timeframe: d.timeframe || '', reason: d.reason || '' });
|
|
11210
11280
|
} else if (k === 'cancel') {
|
|
11211
11281
|
finJournal({ kind: 'cancel', sid, agent: who, ticket: d.ticket });
|
|
11212
11282
|
}
|
|
11213
11283
|
let line = '';
|
|
11214
|
-
if (k === 'trade') line = `İŞLEM AÇILDI: ${d.side} ${d.volume} ${d.symbol}${who ? ' · ' + who : ''}`;
|
|
11284
|
+
if (k === 'trade') line = `İŞLEM AÇILDI: ${d.side} ${d.volume} ${d.symbol}${d.timeframe ? ' · ' + d.timeframe : ''}${who ? ' · ' + who : ''}`;
|
|
11215
11285
|
else if (k === 'modify') line = `SL/TP GÜNCELLENDİ: ticket ${d.ticket} (SL ${d.sl || 0} / TP ${d.tp || 0})${who ? ' · ' + who : ''}`;
|
|
11216
|
-
else if (k === 'pending') line = `BEKLEYEN EMİR: ${d.type} ${d.volume} ${d.symbol}${who ? ' · ' + who : ''}`;
|
|
11286
|
+
else if (k === 'pending') line = `BEKLEYEN EMİR: ${d.type} ${d.volume} ${d.symbol}${d.timeframe ? ' · ' + d.timeframe : ''}${who ? ' · ' + who : ''}`;
|
|
11217
11287
|
else if (k === 'cancel') line = `EMİR İPTAL: ticket ${d.ticket}`;
|
|
11218
11288
|
if (line) {
|
|
11219
11289
|
financeLog('[ajan] ' + line);
|
|
@@ -11249,9 +11319,9 @@ try {
|
|
|
11249
11319
|
if (!kind) return;
|
|
11250
11320
|
if (kind === 'trade') {
|
|
11251
11321
|
const side = a.side || (/^buy/i.test(String(a.type || '')) ? 'buy' : /^sell/i.test(String(a.type || '')) ? 'sell' : '');
|
|
11252
|
-
finTradeEvent('trade', { symbol: a.symbol, side, volume: a.volume, sl: a.sl, tp: a.tp, reason: a.reason || a.comment || '', result: res }, sid);
|
|
11322
|
+
finTradeEvent('trade', { symbol: a.symbol, side, volume: a.volume, sl: a.sl, tp: a.tp, timeframe: a.timeframe || '', reason: a.reason || a.comment || '', result: res }, sid);
|
|
11253
11323
|
} else if (kind === 'modify') finTradeEvent('modify', { ticket: a.ticket, sl: a.sl, tp: a.tp }, sid);
|
|
11254
|
-
else if (kind === 'pending') finTradeEvent('pending', { symbol: a.symbol, type: a.type, volume: a.volume, reason: a.reason || '' }, sid);
|
|
11324
|
+
else if (kind === 'pending') finTradeEvent('pending', { symbol: a.symbol, type: a.type, volume: a.volume, timeframe: a.timeframe || '', reason: a.reason || '' }, sid);
|
|
11255
11325
|
else if (kind === 'cancel') finTradeEvent('cancel', { ticket: a.ticket }, sid);
|
|
11256
11326
|
else if (kind === 'close') {
|
|
11257
11327
|
finTradeEvent('close', {
|
|
@@ -11270,8 +11340,51 @@ try {
|
|
|
11270
11340
|
Ana trader / finance sohbet oturumu min lot-max lot-pozisyon tavanını
|
|
11271
11341
|
güncelleyebilir; rol/işçi ajanları yalnız okur. Değişiklik ayarlara yazılır,
|
|
11272
11342
|
koşan oturumlara ANINDA işlenir ve panele duyurulur. */
|
|
11343
|
+
/* TUR RİTMİ: trader sert harekette geçici hızlanabilir — efektif aralık (sn).
|
|
11344
|
+
Süre dolunca kendiliğinden taban aralığa döner (API/maliyet limiti korunur). */
|
|
11345
|
+
function finPaceSec() {
|
|
11346
|
+
const f = finCfg();
|
|
11347
|
+
const base = Math.max(30, Math.min(3600, Math.round(Number(f.intervalSec) || 120)));
|
|
11348
|
+
const p = financeState.pace;
|
|
11349
|
+
if (p && Number(p.until) > Date.now() && Number(p.sec) >= 30) {
|
|
11350
|
+
return Math.max(30, Math.min(3600, Math.round(Number(p.sec))));
|
|
11351
|
+
}
|
|
11352
|
+
return base;
|
|
11353
|
+
}
|
|
11354
|
+
|
|
11355
|
+
/* Koşan ajan zamanlayıcılarını efektif ritme göre yeniden kur; hız modunun
|
|
11356
|
+
bitişini de zamanla — süre dolunca taban aralığa OTOMATİK dönüş. */
|
|
11357
|
+
function finRescheduleAgents() {
|
|
11358
|
+
const iv = finPaceSec() * 1000;
|
|
11359
|
+
if (financeState.paceTimer) { clearTimeout(financeState.paceTimer); financeState.paceTimer = null; }
|
|
11360
|
+
const p = financeState.pace;
|
|
11361
|
+
if (p && Number(p.until) > Date.now() && Number(p.sec) > 0) {
|
|
11362
|
+
const left = Math.max(1000, Number(p.until) - Date.now() + 1000);
|
|
11363
|
+
financeState.paceTimer = setTimeout(() => {
|
|
11364
|
+
financeState.pace = { sec: 0, until: 0, base: 0 };
|
|
11365
|
+
try { financeLog('[ritim] hız modu bitti — taban tur aralığı ' + finPaceSec() + ' sn'); } catch {}
|
|
11366
|
+
try { finRescheduleAgents(); } catch {}
|
|
11367
|
+
try { finPush('limits', { ...finLimitsSnapshot(), note: 'hız modu bitti — taban tur aralığı ' + finPaceSec() + ' sn' }); } catch {}
|
|
11368
|
+
}, left);
|
|
11369
|
+
}
|
|
11370
|
+
if (!engine || !financeState.agents) return;
|
|
11371
|
+
const hasMain = finTeamHasMain();
|
|
11372
|
+
for (const [sid, a] of financeState.agents) {
|
|
11373
|
+
if (!a) continue;
|
|
11374
|
+
if (a.role && hasMain) continue; /* cycle modu: trader turu başlatır */
|
|
11375
|
+
if (engine.isBusy(sid)) continue; /* tur bitince finFlushOnDone yeni ritmi okur */
|
|
11376
|
+
clearTimeout(a.timer);
|
|
11377
|
+
a.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, iv);
|
|
11378
|
+
}
|
|
11379
|
+
if (financeState.traderSid) {
|
|
11380
|
+
const a = financeState.agents.get(String(financeState.traderSid));
|
|
11381
|
+
finPush('trader', { state: 'idle', round: (a && a.round) || 0, nextInSec: iv / 1000 });
|
|
11382
|
+
}
|
|
11383
|
+
}
|
|
11384
|
+
|
|
11273
11385
|
function finLimitsSnapshot() {
|
|
11274
11386
|
const f = finCfg();
|
|
11387
|
+
const paceOn = Number(financeState.pace && financeState.pace.until) > Date.now();
|
|
11275
11388
|
return {
|
|
11276
11389
|
minLot: Number(f.minLot) || 0.01,
|
|
11277
11390
|
maxLot: Number(f.maxLot) || 0.1,
|
|
@@ -11279,6 +11392,10 @@ function finLimitsSnapshot() {
|
|
|
11279
11392
|
riskPerTradePct: Number(f.riskPerTradePct) || 0,
|
|
11280
11393
|
/* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı) — genel aralığı daraltır */
|
|
11281
11394
|
symbols: { ...(f.symbolLimits && typeof f.symbolLimits === 'object' ? f.symbolLimits : {}) },
|
|
11395
|
+
/* TUR RİTMİ: efektif aralık + taban + hız modu bitişi */
|
|
11396
|
+
intervalSec: finPaceSec(),
|
|
11397
|
+
intervalBase: Math.max(30, Math.min(3600, Math.round(Number(f.intervalSec) || 120))),
|
|
11398
|
+
paceUntil: paceOn ? Number(financeState.pace.until) : 0,
|
|
11282
11399
|
};
|
|
11283
11400
|
}
|
|
11284
11401
|
|
|
@@ -11295,7 +11412,7 @@ function finLimitsSessionAllowed(sid) {
|
|
|
11295
11412
|
|
|
11296
11413
|
function finSyncLimitsToSessions(extra) {
|
|
11297
11414
|
const lim = finLimitsSnapshot();
|
|
11298
|
-
const apply = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions, symbolLimits: lim.symbols };
|
|
11415
|
+
const apply = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions, symbolLimits: lim.symbols, intervalSec: lim.intervalSec, intervalBase: lim.intervalBase, paceUntil: lim.paceUntil };
|
|
11299
11416
|
if (engine) {
|
|
11300
11417
|
try {
|
|
11301
11418
|
for (const [sid] of financeState.agents) {
|
|
@@ -11323,8 +11440,8 @@ function finLimitsSet(patch, ctx) {
|
|
|
11323
11440
|
symbol) → sembol limiti kaldırılır, genel aralık geçerli olur. */
|
|
11324
11441
|
const sym = String(patch.symbol || '').trim().toUpperCase();
|
|
11325
11442
|
if (sym) {
|
|
11326
|
-
if (patch.maxPositions !== undefined) {
|
|
11327
|
-
return { ok: false, error: 'maxPositions sembol bazlı değil — symbol vermeden genel
|
|
11443
|
+
if (patch.maxPositions !== undefined || patch.intervalSec !== undefined || patch.intervalForMin !== undefined) {
|
|
11444
|
+
return { ok: false, error: 'maxPositions ve tur ritmi (intervalSec) sembol bazlı değil — symbol vermeden genel olarak ayarla' };
|
|
11328
11445
|
}
|
|
11329
11446
|
const hasMin = patch.minLot !== undefined && patch.minLot !== null && patch.minLot !== '';
|
|
11330
11447
|
const hasMax = patch.maxLot !== undefined && patch.maxLot !== null && patch.maxLot !== '';
|
|
@@ -11381,16 +11498,39 @@ function finLimitsSet(patch, ctx) {
|
|
|
11381
11498
|
if (!(v >= 1) || v > 20) return { ok: false, error: 'maxPositions 1-20 arasında olmalı' };
|
|
11382
11499
|
f.maxPositions = v;
|
|
11383
11500
|
}
|
|
11501
|
+
/* TUR RİTMİ (trader kararı): sert hareket/haber anında geçici hızlanma.
|
|
11502
|
+
intervalForMin > 0 → geçici (süre dolunca taban aralığa otomatik döner);
|
|
11503
|
+
yoksa kalıcı temel aralık güncellenir. Alt sınır 30 sn, hız modu ≤ 240 dk:
|
|
11504
|
+
API/maliyet limiti bilinçli — sürekli en düşük aralıkta kalmak YASAK. */
|
|
11505
|
+
let intervalChanged = false;
|
|
11506
|
+
if (patch.intervalSec !== undefined) {
|
|
11507
|
+
const base = Math.max(30, Math.min(3600, Math.round(Number(f.intervalSec) || 120)));
|
|
11508
|
+
const sec = Math.max(30, Math.min(3600, Math.round(Number(patch.intervalSec) || base)));
|
|
11509
|
+
const mins = Math.max(0, Math.min(240, Math.round(Number(patch.intervalForMin) || 0)));
|
|
11510
|
+
if (mins > 0 && sec < base) {
|
|
11511
|
+
financeState.pace = { sec, until: Date.now() + mins * 60 * 1000, base };
|
|
11512
|
+
notes.push(`hız modu: ${sec} sn × ${mins} dk (sonra taban ${base} sn'e döner)`);
|
|
11513
|
+
} else {
|
|
11514
|
+
f.intervalSec = sec;
|
|
11515
|
+
if (Number(financeState.pace && financeState.pace.until) > Date.now()) financeState.pace = { sec: 0, until: 0, base: 0 };
|
|
11516
|
+
notes.push(`temel tur aralığı: ${sec} sn`);
|
|
11517
|
+
}
|
|
11518
|
+
intervalChanged = true;
|
|
11519
|
+
} else if (patch.intervalForMin !== undefined) {
|
|
11520
|
+
notes.push('intervalForMin için intervalSec de verilmeli');
|
|
11521
|
+
}
|
|
11384
11522
|
if (f.minLot > f.maxLot) {
|
|
11385
11523
|
notes.push(`minLot (${f.minLot}) maxLot'tan (${f.maxLot}) büyüktü → minLot ${f.maxLot} yapıldı`);
|
|
11386
11524
|
f.minLot = f.maxLot;
|
|
11387
11525
|
}
|
|
11388
11526
|
try { saveSettings(); } catch {}
|
|
11527
|
+
if (intervalChanged) { try { finRescheduleAgents(); } catch {} }
|
|
11389
11528
|
finSyncLimitsToSessions();
|
|
11390
|
-
const
|
|
11529
|
+
const paceOn = Number(financeState.pace && financeState.pace.until) > Date.now();
|
|
11530
|
+
const line = `⚙️ LİMİTLER GÜNCELLENDİ: min lot ${f.minLot} · max lot ${f.maxLot} · max pozisyon ${f.maxPositions}${intervalChanged ? ` · tur ${finPaceSec()} sn${paceOn ? ' (hız modu)' : ''}` : ''}${sid ? ' (ajan kararı)' : ''}`;
|
|
11391
11531
|
financeLog('[limit] ' + line);
|
|
11392
11532
|
financeNotify(line, 'limit', true);
|
|
11393
|
-
try { finJournal({ kind: 'limits', minLot: f.minLot, maxLot: f.maxLot, maxPositions: f.maxPositions, sid }); } catch {}
|
|
11533
|
+
try { finJournal({ kind: 'limits', minLot: f.minLot, maxLot: f.maxLot, maxPositions: f.maxPositions, intervalSec: finPaceSec(), paceUntil: paceOn ? financeState.pace.until : 0, sid }); } catch {}
|
|
11394
11534
|
return { ok: true, limits: finLimitsSnapshot(), note: notes.length ? notes.join('; ') : undefined };
|
|
11395
11535
|
}
|
|
11396
11536
|
|
|
@@ -11418,15 +11558,62 @@ const FIN_LEARN_COMPACT_AT = 24; /* not sayısı bunu aşınca arka planda ot
|
|
|
11418
11558
|
const FIN_LEARN_KEEP_RECENT = 8; /* ham kalan son ders sayısı */
|
|
11419
11559
|
const FIN_LEARN_SUMMARY_MAX = 800; /* kalıcı özet metin tavanı (karakter) */
|
|
11420
11560
|
const FIN_LEARN_COMPACT_MIN = 4; /* elle özetleme için gereken en az eski ders */
|
|
11561
|
+
/* ZAMAN DİLİMİ: ajan kendi periyodunu SEÇER (bot kararı) — işlem emri yorumuna
|
|
11562
|
+
yazılır ("Beast M15"), kapanışta periyot bazlı öğrenmeye işlenir. */
|
|
11563
|
+
const FIN_LEARN_TFS = ['M1', 'M5', 'M15', 'M30', 'H1', 'H4', 'D1', 'W1', 'MN1'];
|
|
11564
|
+
const FIN_LEARN_MAX_MISTAKES = 40; /* sembol başına otomatik hata kaydı tavanı */
|
|
11565
|
+
|
|
11566
|
+
function finLearnNormTf(v) {
|
|
11567
|
+
const s = String(v || '').trim().toUpperCase();
|
|
11568
|
+
if (!s) return '';
|
|
11569
|
+
const m = s.match(/^(M|H|D|W|MN)\s*(\d+)$/);
|
|
11570
|
+
if (!m) return '';
|
|
11571
|
+
const key = m[1] === 'MN' ? 'MN1' : m[1] + m[2];
|
|
11572
|
+
return FIN_LEARN_TFS.includes(key) ? key : '';
|
|
11573
|
+
}
|
|
11574
|
+
|
|
11575
|
+
/* MT5 pozisyon yorumundan periyot okur: "Beast M15 ..." → M15 */
|
|
11576
|
+
function finLearnParseTf(comment) {
|
|
11577
|
+
const m = String(comment || '').toUpperCase().match(/\b(M1|M5|M15|M30|H1|H4|D1|W1|MN1)\b/);
|
|
11578
|
+
return m ? m[1] : '';
|
|
11579
|
+
}
|
|
11421
11580
|
|
|
11422
11581
|
function finLearnEmptyStats() {
|
|
11423
11582
|
return {
|
|
11424
11583
|
trades: 0, wins: 0, losses: 0, net: 0,
|
|
11425
11584
|
mfe: 0, mae: 0, givebacks: 0, givebackAmount: 0,
|
|
11426
11585
|
lastAt: 0, lastNet: 0, streak: 0, bestNet: 0, worstNet: 0,
|
|
11586
|
+
/* PERİYOT KIRILIMI: tf → {trades,wins,losses,net,mfe,mae,givebacks,lastAt,lastNet} */
|
|
11587
|
+
byTf: {},
|
|
11427
11588
|
};
|
|
11428
11589
|
}
|
|
11429
11590
|
|
|
11591
|
+
/* Bozuk/eski istatistik kaydını temizle: bilinen alanlar + byTf süzülür */
|
|
11592
|
+
function finLearnNormStats(v) {
|
|
11593
|
+
const raw = v && typeof v === 'object' ? v : {};
|
|
11594
|
+
const out = { ...finLearnEmptyStats(), ...raw };
|
|
11595
|
+
const src = raw.byTf && typeof raw.byTf === 'object' ? raw.byTf : {};
|
|
11596
|
+
const byTf = {};
|
|
11597
|
+
for (const [k, s] of Object.entries(src)) {
|
|
11598
|
+
const tf = finLearnNormTf(k);
|
|
11599
|
+
if (!tf || !s || typeof s !== 'object') continue;
|
|
11600
|
+
const t = {
|
|
11601
|
+
trades: Math.max(0, Math.round(Number(s.trades) || 0)),
|
|
11602
|
+
wins: Math.max(0, Math.round(Number(s.wins) || 0)),
|
|
11603
|
+
losses: Math.max(0, Math.round(Number(s.losses) || 0)),
|
|
11604
|
+
net: Math.round((Number(s.net) || 0) * 100) / 100,
|
|
11605
|
+
mfe: Math.round((Number(s.mfe) || 0) * 100) / 100,
|
|
11606
|
+
mae: Math.round((Number(s.mae) || 0) * 100) / 100,
|
|
11607
|
+
givebacks: Math.max(0, Math.round(Number(s.givebacks) || 0)),
|
|
11608
|
+
lastAt: Number(s.lastAt) || 0,
|
|
11609
|
+
lastNet: Math.round((Number(s.lastNet) || 0) * 100) / 100,
|
|
11610
|
+
};
|
|
11611
|
+
if (t.trades > 0) byTf[tf] = t;
|
|
11612
|
+
}
|
|
11613
|
+
out.byTf = byTf;
|
|
11614
|
+
return out;
|
|
11615
|
+
}
|
|
11616
|
+
|
|
11430
11617
|
/* Ders metni karşılaştırma anahtarı: büyük/küçük harf, noktalama ve boşluk
|
|
11431
11618
|
farkları aynı ders sayılır — ajan aynı cümleyi kopyalayıp depoyu doldurmasın */
|
|
11432
11619
|
function finLearnNoteKey(text) {
|
|
@@ -11480,7 +11667,7 @@ function finLearnPrune(learn, keep, protectSym) {
|
|
|
11480
11667
|
yüklemede çalışır → depo kendiliğinden sade kalır, birikme/zehirlenme olmaz. */
|
|
11481
11668
|
function finLearnForget(learn, now) {
|
|
11482
11669
|
const t = Number(now) || Date.now();
|
|
11483
|
-
const stat = { notes: 0, trades: 0, symbols: 0 };
|
|
11670
|
+
const stat = { notes: 0, trades: 0, mistakes: 0, symbols: 0 };
|
|
11484
11671
|
for (const [sym, e] of Object.entries(learn.symbols)) {
|
|
11485
11672
|
if (!e || typeof e !== 'object') { delete learn.symbols[sym]; stat.symbols++; continue; }
|
|
11486
11673
|
const notes = Array.isArray(e.notes) ? e.notes : [];
|
|
@@ -11489,6 +11676,10 @@ function finLearnForget(learn, now) {
|
|
|
11489
11676
|
const trades = Array.isArray(e.trades) ? e.trades : [];
|
|
11490
11677
|
e.trades = trades.filter((x) => x && t - (Number(x.at) || 0) <= FIN_LEARN_TRADE_TTL_MS);
|
|
11491
11678
|
stat.trades += trades.length - e.trades.length;
|
|
11679
|
+
/* HATA KAYITLARI da işlem TTL'iyle unutulur — depo şişmez */
|
|
11680
|
+
const mistakes = Array.isArray(e.mistakes) ? e.mistakes : [];
|
|
11681
|
+
e.mistakes = mistakes.filter((x) => x && t - (Number(x.at) || 0) <= FIN_LEARN_TRADE_TTL_MS).slice(-FIN_LEARN_MAX_MISTAKES);
|
|
11682
|
+
stat.mistakes += mistakes.length - e.mistakes.length;
|
|
11492
11683
|
if (t - finLearnActivity(e) > FIN_LEARN_SYMBOL_TTL_MS) {
|
|
11493
11684
|
delete learn.symbols[sym];
|
|
11494
11685
|
stat.symbols += 1;
|
|
@@ -11502,7 +11693,7 @@ function finLearnForget(learn, now) {
|
|
|
11502
11693
|
function finLearnTidy(learn, protectSym, now) {
|
|
11503
11694
|
const forgot = finLearnForget(learn, now);
|
|
11504
11695
|
const cap = finLearnPrune(learn, FIN_LEARN_MAX_SYMBOLS, protectSym);
|
|
11505
|
-
return { notes: forgot.notes, trades: forgot.trades, symbols: forgot.symbols + cap };
|
|
11696
|
+
return { notes: forgot.notes, trades: forgot.trades, mistakes: forgot.mistakes, symbols: forgot.symbols + cap };
|
|
11506
11697
|
}
|
|
11507
11698
|
|
|
11508
11699
|
function finLearnLoad() {
|
|
@@ -11518,7 +11709,8 @@ function finLearnLoad() {
|
|
|
11518
11709
|
learn.symbols[sym] = {
|
|
11519
11710
|
notes: Array.isArray(v.notes) ? v.notes.filter((n) => n && n.id && n.text).slice(-FIN_LEARN_MAX_NOTES) : [],
|
|
11520
11711
|
trades: Array.isArray(v.trades) ? v.trades.filter(Boolean).slice(-FIN_LEARN_MAX_TRADES) : [],
|
|
11521
|
-
|
|
11712
|
+
mistakes: Array.isArray(v.mistakes) ? v.mistakes.filter(Boolean).slice(-FIN_LEARN_MAX_MISTAKES) : [],
|
|
11713
|
+
stats: finLearnNormStats(v.stats),
|
|
11522
11714
|
/* KALICI ÖZET (opencode tarzı sıkıştırma): eski derslerin modele
|
|
11523
11715
|
özetlettirilmiş hâli — ham dersler düşse de bilgi kalır */
|
|
11524
11716
|
summary: v.summary && typeof v.summary === 'object' && String(v.summary.text || '').trim()
|
|
@@ -11535,7 +11727,7 @@ function finLearnLoad() {
|
|
|
11535
11727
|
} catch {}
|
|
11536
11728
|
const dropped = finLearnTidy(learn); /* yükleme anında unut + tavan uygula */
|
|
11537
11729
|
financeState.learn = learn;
|
|
11538
|
-
if (dropped.notes || dropped.trades || dropped.symbols) finLearnSave(); /* disk de hemen küçülsün */
|
|
11730
|
+
if (dropped.notes || dropped.trades || dropped.mistakes || dropped.symbols) finLearnSave(); /* disk de hemen küçülsün */
|
|
11539
11731
|
return learn;
|
|
11540
11732
|
}
|
|
11541
11733
|
|
|
@@ -11555,16 +11747,16 @@ const FIN_LEARN_COMPACT_SYSTEM =
|
|
|
11555
11747
|
'Sen bir trading mentorüsün. Sana bir sembolün ESKİ ders notları ve mevcut özeti verilir. ' +
|
|
11556
11748
|
'Görevin: güncellenmiş KISA bir özet üretmek.\n' +
|
|
11557
11749
|
'KURALLAR: (1) Yalnız kalıcı değeri olan bilgiyi tut: çalışan desenler, kaçınılacak hatalar, ' +
|
|
11558
|
-
'seans/setup kuralları, SL/TP davranışı. (2) Tekrarları birleştir; tarih/saat damgası, anlık ' +
|
|
11750
|
+
'seans/setup/PERİYOT (M5/M15/H1…) kuralları, SL/TP davranışı. (2) Tekrarları birleştir; tarih/saat damgası, anlık ' +
|
|
11559
11751
|
'haber, geçici durum gibi bayat bilgiyi at. (3) Çelişen bilgide EN YENİ ve istatistikle ' +
|
|
11560
|
-
'desteklenen kazanır. (4) Sayısal istatistikleri (işlem, kazanç %, net, kâr geri verme) koru
|
|
11561
|
-
'(5) En fazla ' + FIN_LEARN_SUMMARY_MAX + ' karakter; kısa maddeler ya da 2-4 akıcı cümle. ' +
|
|
11752
|
+
'desteklenen kazanır. (4) Sayısal istatistikleri (işlem, kazanç %, net, kâr geri verme, periyot bazlı net) koru; ' +
|
|
11753
|
+
'hangi periyodun kazandırdığını/kaybettirdiğini açık yaz. (5) En fazla ' + FIN_LEARN_SUMMARY_MAX + ' karakter; kısa maddeler ya da 2-4 akıcı cümle. ' +
|
|
11562
11754
|
'(6) Çıktı SADECE özet metni olsun; başlık/etiket/açıklama yazma.';
|
|
11563
11755
|
|
|
11564
11756
|
const finLearnCompacting = new Set();
|
|
11565
11757
|
|
|
11566
11758
|
function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
11567
|
-
const st =
|
|
11759
|
+
const st = finLearnNormStats(e.stats);
|
|
11568
11760
|
const wr = st.trades ? Math.round((st.wins / st.trades) * 100) : 0;
|
|
11569
11761
|
const lines = [];
|
|
11570
11762
|
lines.push('SEMBOL: ' + sym);
|
|
@@ -11573,12 +11765,27 @@ function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
|
11573
11765
|
(st.givebacks ? ` · ${st.givebacks} kez kâr geri verildi` : '') +
|
|
11574
11766
|
(st.streak >= 2 ? ` · ${st.streak} ardışık kayıp` : '')
|
|
11575
11767
|
);
|
|
11768
|
+
const tfRows = Object.entries(st.byTf || {}).filter(([, s]) => s && Number(s.trades) > 0);
|
|
11769
|
+
if (tfRows.length) {
|
|
11770
|
+
lines.push('PERİYOT İSTATİSTİĞİ:');
|
|
11771
|
+
for (const [tf, s] of tfRows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0))) {
|
|
11772
|
+
const w = Math.round((Number(s.wins) / Number(s.trades)) * 100);
|
|
11773
|
+
lines.push(`- ${tf}: ${s.trades} işlem · %${w} kazanç · net ${Number(s.net) >= 0 ? '+' : ''}${Math.round(Number(s.net) * 100) / 100}`);
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
const mistakes = Array.isArray(e.mistakes) ? e.mistakes.slice(-20) : [];
|
|
11777
|
+
if (mistakes.length) {
|
|
11778
|
+
lines.push('SON HATALAR (zararla kapananlar):');
|
|
11779
|
+
for (const m of mistakes) {
|
|
11780
|
+
lines.push(`- ${m.tf || '?'} ${m.side || ''} net ${Math.round(Number(m.net) * 100) / 100}${m.reason ? ' (' + m.reason + ')' : ''}${m.giveback ? ' [kâr geri verildi]' : ''}`);
|
|
11781
|
+
}
|
|
11782
|
+
}
|
|
11576
11783
|
if (e.summary && e.summary.text) lines.push('MEVCUT ÖZET:\n' + String(e.summary.text).slice(0, 1400));
|
|
11577
11784
|
lines.push('ESKİ DERSLER (kronolojik — en yeni EN SONDA):');
|
|
11578
11785
|
for (const n of oldNotes.slice(-80)) {
|
|
11579
|
-
lines.push(`- [${String(n.kind || 'observation')}] ${String(n.text || '').slice(0, 220)}`);
|
|
11786
|
+
lines.push(`- [${String(n.kind || 'observation')}${n.tf ? ' ' + String(n.tf) : ''}] ${String(n.text || '').slice(0, 220)}`);
|
|
11580
11787
|
}
|
|
11581
|
-
lines.push('Görev: yalnız kalıcı ve güncel dersleri koruyan yeni özet metnini yaz.');
|
|
11788
|
+
lines.push('Görev: yalnız kalıcı ve güncel dersleri (periyot bilgisi ve hata kalıplarıyla) koruyan yeni özet metnini yaz.');
|
|
11582
11789
|
return lines.join('\n');
|
|
11583
11790
|
}
|
|
11584
11791
|
|
|
@@ -11645,40 +11852,55 @@ function finLearnSym(symbol) {
|
|
|
11645
11852
|
const sym = String(symbol || '').trim().toUpperCase();
|
|
11646
11853
|
if (!sym) return null;
|
|
11647
11854
|
const learn = finLearnLoad();
|
|
11648
|
-
if (!learn.symbols[sym]) learn.symbols[sym] = { notes: [], trades: [], stats: finLearnEmptyStats() };
|
|
11855
|
+
if (!learn.symbols[sym]) learn.symbols[sym] = { notes: [], trades: [], mistakes: [], stats: finLearnEmptyStats(), summary: null };
|
|
11649
11856
|
const e = learn.symbols[sym];
|
|
11650
|
-
e.
|
|
11857
|
+
if (!Array.isArray(e.mistakes)) e.mistakes = [];
|
|
11858
|
+
e.stats = finLearnNormStats(e.stats);
|
|
11651
11859
|
return e;
|
|
11652
11860
|
}
|
|
11653
11861
|
|
|
11654
|
-
/* Kapanan her işlem otomatik öğrenme girdisi olur (sembol bazlı)
|
|
11655
|
-
|
|
11862
|
+
/* Kapanan her işlem otomatik öğrenme girdisi olur (sembol + periyot bazlı).
|
|
11863
|
+
timeframe: ajanın emir anında verdiği periyot (yoksa '' — genel istatistiğe
|
|
11864
|
+
yine işler). Zararla kapanan işlem AYRICA yapılandırılmış hata kaydı olur:
|
|
11865
|
+
hata dersleri bu kayıttan üretilir ve bir sonraki turda prompta girer. */
|
|
11866
|
+
function finLearnRecordClose({ symbol, side, net, mfe, mae, reason, timeframe }) {
|
|
11656
11867
|
try {
|
|
11657
11868
|
if (net == null) return; /* K/Z okunamadı — sahte kayıt yazma */
|
|
11658
11869
|
const n = Number(net);
|
|
11659
11870
|
if (!isFinite(n) || !symbol) return;
|
|
11660
11871
|
const e = finLearnSym(symbol);
|
|
11661
11872
|
if (!e) return;
|
|
11873
|
+
const tf = finLearnNormTf(timeframe);
|
|
11662
11874
|
const st = e.stats;
|
|
11663
11875
|
const m = Number(mfe);
|
|
11664
11876
|
const ma = Number(mae);
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
|
|
11671
|
-
|
|
11877
|
+
const bump = (s) => {
|
|
11878
|
+
s.trades += 1;
|
|
11879
|
+
if (n >= 0) s.wins += 1;
|
|
11880
|
+
else s.losses += 1;
|
|
11881
|
+
s.net = Math.round((Number(s.net) + n) * 100) / 100;
|
|
11882
|
+
s.lastAt = Date.now();
|
|
11883
|
+
s.lastNet = Math.round(n * 100) / 100;
|
|
11884
|
+
s.streak = n < 0 ? Number(s.streak || 0) + 1 : 0;
|
|
11885
|
+
if (isFinite(m)) {
|
|
11886
|
+
s.mfe = Math.round(((Number(s.mfe) || 0) + m) * 100) / 100;
|
|
11887
|
+
if (m > 0 && n < 0) {
|
|
11888
|
+
s.givebacks = Number(s.givebacks || 0) + 1;
|
|
11889
|
+
s.givebackAmount = Math.round(((Number(s.givebackAmount) || 0) + m) * 100) / 100;
|
|
11890
|
+
}
|
|
11891
|
+
}
|
|
11892
|
+
if (isFinite(ma)) s.mae = Math.round(((Number(s.mae) || 0) + ma) * 100) / 100;
|
|
11893
|
+
};
|
|
11894
|
+
/* GENEL istatistik */
|
|
11895
|
+
bump(st);
|
|
11672
11896
|
st.bestNet = Math.max(Number(st.bestNet) || 0, Math.round(n * 100) / 100);
|
|
11673
11897
|
st.worstNet = Math.min(Number(st.worstNet) || 0, Math.round(n * 100) / 100);
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
if (
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
}
|
|
11898
|
+
/* PERİYOT istatistiği: hangi zaman dilimi kazandırıyor/kaybettiriyor */
|
|
11899
|
+
if (tf) {
|
|
11900
|
+
if (!st.byTf || typeof st.byTf !== 'object') st.byTf = {};
|
|
11901
|
+
if (!st.byTf[tf]) st.byTf[tf] = { trades: 0, wins: 0, losses: 0, net: 0, mfe: 0, mae: 0, givebacks: 0, lastAt: 0, lastNet: 0 };
|
|
11902
|
+
bump(st.byTf[tf]);
|
|
11680
11903
|
}
|
|
11681
|
-
if (isFinite(ma)) st.mae = Math.round(((Number(st.mae) || 0) + ma) * 100) / 100;
|
|
11682
11904
|
e.trades.push({
|
|
11683
11905
|
at: Date.now(),
|
|
11684
11906
|
side: String(side || ''),
|
|
@@ -11686,14 +11908,69 @@ function finLearnRecordClose({ symbol, side, net, mfe, mae, reason }) {
|
|
|
11686
11908
|
mfe: isFinite(m) ? Math.round(m * 100) / 100 : null,
|
|
11687
11909
|
mae: isFinite(ma) ? Math.round(ma * 100) / 100 : null,
|
|
11688
11910
|
reason: String(reason || '').slice(0, 40),
|
|
11911
|
+
tf: tf || null,
|
|
11689
11912
|
});
|
|
11690
11913
|
while (e.trades.length > FIN_LEARN_MAX_TRADES) e.trades.shift();
|
|
11914
|
+
/* YANLIŞTAN DERS: zararla kapanan işlem yapılandırılmış hata kaydı olur —
|
|
11915
|
+
sonraki turlarda ajana "tekrar eden hata" olarak gösterilir */
|
|
11916
|
+
if (n < 0) {
|
|
11917
|
+
if (!Array.isArray(e.mistakes)) e.mistakes = [];
|
|
11918
|
+
e.mistakes.push({
|
|
11919
|
+
at: Date.now(),
|
|
11920
|
+
tf: tf || '',
|
|
11921
|
+
side: String(side || ''),
|
|
11922
|
+
net: Math.round(n * 100) / 100,
|
|
11923
|
+
mfe: isFinite(m) ? Math.round(m * 100) / 100 : null,
|
|
11924
|
+
mae: isFinite(ma) ? Math.round(ma * 100) / 100 : null,
|
|
11925
|
+
reason: String(reason || '').slice(0, 40),
|
|
11926
|
+
giveback: !!(isFinite(m) && m > 0),
|
|
11927
|
+
});
|
|
11928
|
+
while (e.mistakes.length > FIN_LEARN_MAX_MISTAKES) e.mistakes.shift();
|
|
11929
|
+
}
|
|
11691
11930
|
finLearnTidy(finLearnLoad(), symbol);
|
|
11692
11931
|
finLearnSave();
|
|
11693
11932
|
} catch {}
|
|
11694
11933
|
}
|
|
11695
11934
|
|
|
11696
|
-
/*
|
|
11935
|
+
/* Tur girişine gömülen öğrenme özeti: sembol + PERİYOT istatistiği, son
|
|
11936
|
+
hatalar ve dersler. Ajanın "her şeye öğrendiğiyle karar vermesi" için
|
|
11937
|
+
karar anında önüne konur; en iyi/en kötü periyot ve tekrar eden hata
|
|
11938
|
+
kalıbı açıkça işaretlenir. */
|
|
11939
|
+
function finBuildLearnTfLine(st) {
|
|
11940
|
+
const rows = Object.entries(st.byTf || {}).filter(([, s]) => s && Number(s.trades) > 0);
|
|
11941
|
+
if (!rows.length) return '';
|
|
11942
|
+
rows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0));
|
|
11943
|
+
const fmt = ([tf, s]) => {
|
|
11944
|
+
const wrT = Math.round((Number(s.wins) / Number(s.trades)) * 100);
|
|
11945
|
+
return `${tf} ${s.trades}i %${wrT} net ${Number(s.net) >= 0 ? '+' : ''}${Math.round(Number(s.net) * 100) / 100}`;
|
|
11946
|
+
};
|
|
11947
|
+
const parts = rows.map(fmt);
|
|
11948
|
+
const best = rows[0];
|
|
11949
|
+
const worst = rows.length > 1 ? rows[rows.length - 1] : null;
|
|
11950
|
+
let out = `periyot: ${parts.join(' | ')}`;
|
|
11951
|
+
if (best) out += ` · EN İYİ: ${best[0]} (net ${Number(best[1].net) >= 0 ? '+' : ''}${Math.round(Number(best[1].net) * 100) / 100})`;
|
|
11952
|
+
if (worst && Number(worst[1].net) < 0) out += ` · NEGATİF: ${worst[0]} (riski düşür/teyit ara)`;
|
|
11953
|
+
return out;
|
|
11954
|
+
}
|
|
11955
|
+
|
|
11956
|
+
/* Son hatalardan kalıp çıkar: aynı periyotta tekrarlayan kayıp sayısı vb. */
|
|
11957
|
+
function finBuildLearnMistakeLine(e) {
|
|
11958
|
+
const ms = Array.isArray(e.mistakes) ? e.mistakes : [];
|
|
11959
|
+
if (!ms.length) return '';
|
|
11960
|
+
const last = ms.slice(-6);
|
|
11961
|
+
const byTf = {};
|
|
11962
|
+
for (const m of last) if (m.tf) byTf[m.tf] = (byTf[m.tf] || 0) + 1;
|
|
11963
|
+
const tfTxt = Object.entries(byTf).map(([tf, n]) => `${tf}×${n}`).join(', ');
|
|
11964
|
+
const lastMs = last[last.length - 1];
|
|
11965
|
+
const kinds = { stop: 'SL', tp: 'TP', stopout: 'stop-out', manual: 'manuel' };
|
|
11966
|
+
let out = `son hatalar (${last.length}): ` + last.map((m) => `${m.tf || '?'} ${kinds[String(m.reason)] || (m.reason || 'zarar')} ${Number(m.net) >= 0 ? '+' : ''}${Math.round(Number(m.net) * 100) / 100}${m.giveback ? ' [kâr geri verildi]' : ''}`).join(' · ');
|
|
11967
|
+
if (tfTxt && Object.keys(byTf).length) out += ` · periyot dağılımı: ${tfTxt}`;
|
|
11968
|
+
const repeated = Object.entries(byTf).find(([, n]) => n >= 3);
|
|
11969
|
+
if (repeated) out += ` · ⚠ TEKRAR EDEN HATA: ${repeated[0]} periyodunda ${repeated[1]} kayıp — bu periyotta riski düşür ya da teyit bekle`;
|
|
11970
|
+
if (lastMs && lastMs.giveback) out += ' · KÂR GERİ VERME: kârı koru (kısmi TP/BE kullan)';
|
|
11971
|
+
return out;
|
|
11972
|
+
}
|
|
11973
|
+
|
|
11697
11974
|
function finBuildLearnDigest(symbols) {
|
|
11698
11975
|
const learn = finLearnLoad();
|
|
11699
11976
|
const prefer = Array.isArray(symbols) ? symbols.map((s) => String(s || '').trim().toUpperCase()).filter(Boolean) : [];
|
|
@@ -11702,7 +11979,7 @@ function finBuildLearnDigest(symbols) {
|
|
|
11702
11979
|
const L = [];
|
|
11703
11980
|
for (const sym of keys.slice(0, 10)) {
|
|
11704
11981
|
const e = learn.symbols[sym];
|
|
11705
|
-
const st =
|
|
11982
|
+
const st = finLearnNormStats(e.stats);
|
|
11706
11983
|
if (st.trades > 0) {
|
|
11707
11984
|
const wr = Math.round((st.wins / st.trades) * 100);
|
|
11708
11985
|
const parts = [`${st.trades} işlem`, `%${wr} kazanç`, `net ${st.net >= 0 ? '+' : ''}${st.net}`];
|
|
@@ -11711,6 +11988,13 @@ function finBuildLearnDigest(symbols) {
|
|
|
11711
11988
|
if (st.lastAt) parts.push(`son: ${st.lastNet >= 0 ? '+' : ''}${st.lastNet}`);
|
|
11712
11989
|
L.push(`- ${sym}: ${parts.join(' · ')}`);
|
|
11713
11990
|
}
|
|
11991
|
+
/* PERİYOT KIRILIMI: hangi zaman dilimi kazandırıyor — bot işlem açarken
|
|
11992
|
+
periyodu buna göre SEÇER (kendi kararı) */
|
|
11993
|
+
const tfLine = finBuildLearnTfLine(st);
|
|
11994
|
+
if (tfLine) L.push(' ' + tfLine);
|
|
11995
|
+
/* SON HATALAR: yapılan yanlışlar + tekrar eden kalıp uyarısı */
|
|
11996
|
+
const mLine = finBuildLearnMistakeLine(e);
|
|
11997
|
+
if (mLine) L.push(' ' + mLine);
|
|
11714
11998
|
/* KALICI ÖZET (compaction): eski derslerin sıkıştırılmış hâli — ham ders
|
|
11715
11999
|
sayısı azalsa da birikmiş bilgi prompta girer */
|
|
11716
12000
|
if (e.summary && e.summary.text) {
|
|
@@ -11722,62 +12006,78 @@ function finBuildLearnDigest(symbols) {
|
|
|
11722
12006
|
L.push(` ders${extra}: "${String(last.text || '').slice(0, 160)}"`);
|
|
11723
12007
|
}
|
|
11724
12008
|
}
|
|
12009
|
+
if (L.length) {
|
|
12010
|
+
L.unshift(
|
|
12011
|
+
'ZAMAN DİLİMİ & HATA DİSİPLİNİ: işlemi hangi periyodun analizine dayandırıyorsan mt5_trade/mt5_pending timeframe alanına YAZ (SEN seçersin; öğrenme o periyoda işlenir) — en iyi istatistikli periyodu tercih et, NEGATİF periyotta riski düşür ya da teyit bekle. Zarar/stop sonrası mt5_ogrenme {action:"add", symbol, timeframe, kind:"mistake", text:"yanlış neydi + kaçınma kuralı"} ile derdini çıkar; aynı hatayı tekrarlama.'
|
|
12012
|
+
);
|
|
12013
|
+
}
|
|
11725
12014
|
return L.join('\n');
|
|
11726
12015
|
}
|
|
11727
12016
|
|
|
11728
12017
|
function finLearnApi() {
|
|
11729
12018
|
const norm = (s) => String(s || '').trim().toUpperCase();
|
|
11730
12019
|
return {
|
|
11731
|
-
list: ({ symbol, limit } = {}) => {
|
|
12020
|
+
list: ({ symbol, timeframe, limit } = {}) => {
|
|
11732
12021
|
const learn = finLearnLoad();
|
|
11733
12022
|
const sym = norm(symbol);
|
|
12023
|
+
const tf = finLearnNormTf(timeframe);
|
|
11734
12024
|
const take = Math.max(1, Math.min(25, Math.round(Number(limit) || 8)));
|
|
11735
12025
|
if (sym) {
|
|
11736
12026
|
const e = learn.symbols[sym];
|
|
11737
|
-
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats(), summary: null };
|
|
12027
|
+
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats(), summary: null, mistakes: [] };
|
|
12028
|
+
const notes = tf ? e.notes.filter((n) => finLearnNormTf(n.tf) === tf) : e.notes;
|
|
12029
|
+
const mistakes = tf ? (Array.isArray(e.mistakes) ? e.mistakes.filter((m) => finLearnNormTf(m.tf) === tf) : []) : (Array.isArray(e.mistakes) ? e.mistakes : []);
|
|
11738
12030
|
return {
|
|
11739
12031
|
ok: true,
|
|
11740
12032
|
symbol: sym,
|
|
11741
|
-
|
|
11742
|
-
|
|
12033
|
+
timeframe: tf || '',
|
|
12034
|
+
count: notes.length,
|
|
12035
|
+
notes: notes.slice(-take),
|
|
11743
12036
|
trades: e.trades.slice(-12),
|
|
11744
|
-
stats:
|
|
12037
|
+
stats: finLearnNormStats(e.stats),
|
|
11745
12038
|
summary: e.summary && e.summary.text ? e.summary : null,
|
|
12039
|
+
mistakes: mistakes.slice(-8),
|
|
11746
12040
|
};
|
|
11747
12041
|
}
|
|
11748
12042
|
const rows = Object.entries(learn.symbols)
|
|
11749
12043
|
.map(([s, e]) => ({
|
|
11750
12044
|
symbol: s,
|
|
11751
12045
|
notes: e.notes.length,
|
|
11752
|
-
stats:
|
|
12046
|
+
stats: finLearnNormStats(e.stats),
|
|
11753
12047
|
lastNote: String(((e.notes[e.notes.length - 1] || {}).text) || ''),
|
|
11754
12048
|
hasSummary: !!(e.summary && e.summary.text),
|
|
11755
12049
|
}))
|
|
11756
12050
|
.sort((a, b) => (b.stats.lastAt || 0) - (a.stats.lastAt || 0));
|
|
11757
12051
|
return { ok: true, count: rows.length, symbols: rows.slice(0, FIN_LEARN_MAX_SYMBOLS) };
|
|
11758
12052
|
},
|
|
11759
|
-
stats: ({ symbol } = {}) => {
|
|
11760
|
-
/* KOMPAKT OKUMA: istatistik + kalıcı özet + son 3 ders
|
|
11761
|
-
|
|
12053
|
+
stats: ({ symbol, timeframe } = {}) => {
|
|
12054
|
+
/* KOMPAKT OKUMA: istatistik + PERİYOT kırılımı + kalıcı özet + son 3 ders
|
|
12055
|
+
+ son hatalar — ajan karar öncesi bunu okur */
|
|
11762
12056
|
const learn = finLearnLoad();
|
|
11763
12057
|
const sym = norm(symbol);
|
|
12058
|
+
const tf = finLearnNormTf(timeframe);
|
|
11764
12059
|
if (sym) {
|
|
11765
12060
|
const e = learn.symbols[sym] || {};
|
|
12061
|
+
const st = finLearnNormStats(e.stats);
|
|
11766
12062
|
return {
|
|
11767
12063
|
ok: true,
|
|
11768
12064
|
symbol: sym,
|
|
11769
|
-
|
|
12065
|
+
timeframe: tf || '',
|
|
12066
|
+
stats: st,
|
|
12067
|
+
byTf: tf && st.byTf[tf] ? { [tf]: st.byTf[tf] } : st.byTf,
|
|
11770
12068
|
summary: e.summary && e.summary.text ? e.summary : null,
|
|
11771
|
-
notes: Array.isArray(e.notes) ? e.notes.slice(-3) : [],
|
|
12069
|
+
notes: Array.isArray(e.notes) ? e.notes.filter((n) => !tf || finLearnNormTf(n.tf) === tf).slice(-3) : [],
|
|
12070
|
+
mistakes: Array.isArray(e.mistakes) ? e.mistakes.filter((m) => !tf || finLearnNormTf(m.tf) === tf).slice(-5) : [],
|
|
11772
12071
|
};
|
|
11773
12072
|
}
|
|
11774
12073
|
const stats = {};
|
|
11775
|
-
for (const [s, e] of Object.entries(learn.symbols)) stats[s] =
|
|
12074
|
+
for (const [s, e] of Object.entries(learn.symbols)) stats[s] = finLearnNormStats(e.stats);
|
|
11776
12075
|
return { ok: true, count: Object.keys(stats).length, stats };
|
|
11777
12076
|
},
|
|
11778
|
-
add: ({ symbol, text, kind, tags, sid } = {}) => {
|
|
12077
|
+
add: ({ symbol, text, kind, tags, timeframe, sid } = {}) => {
|
|
11779
12078
|
const sym = norm(symbol);
|
|
11780
12079
|
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
12080
|
+
const tf = finLearnNormTf(timeframe);
|
|
11781
12081
|
if (!sym || !t) return { ok: false, error: 'symbol ve text gerekli' };
|
|
11782
12082
|
/* KALİTE KAPISI: kısa + somut + tekrar kullanılabilir — günlük tutma */
|
|
11783
12083
|
const q = finLearnNoteQuality(t);
|
|
@@ -11785,10 +12085,10 @@ function finLearnApi() {
|
|
|
11785
12085
|
const learn = finLearnLoad();
|
|
11786
12086
|
const e = finLearnSym(sym);
|
|
11787
12087
|
if (!e) return { ok: false, error: 'sembol yok' };
|
|
11788
|
-
/* aynı ders tekrar yazılmasın (son 40 kayıtta normalize metin) —
|
|
11789
|
-
yapıştır ve noktalama oyunlarıyla depo şişirilemez */
|
|
12088
|
+
/* aynı ders tekrar yazılmasın (son 40 kayıtta normalize metin + periyot) —
|
|
12089
|
+
kopyala/yapıştır ve noktalama oyunlarıyla depo şişirilemez */
|
|
11790
12090
|
const key = finLearnNoteKey(t);
|
|
11791
|
-
if (key && e.notes.slice(-40).some((n) => finLearnNoteKey(n.text) === key)) {
|
|
12091
|
+
if (key && e.notes.slice(-40).some((n) => finLearnNoteKey(n.text) === key && finLearnNormTf(n.tf) === tf)) {
|
|
11792
12092
|
return { ok: true, duplicate: true, symbol: sym, count: e.notes.length };
|
|
11793
12093
|
}
|
|
11794
12094
|
/* GÜNLÜK DERS LİMİTİ: ajan aynı gün aynı sembole ders yağdıramaz —
|
|
@@ -11809,6 +12109,7 @@ function finLearnApi() {
|
|
|
11809
12109
|
text: t.slice(0, FIN_LEARN_NOTE_MAX),
|
|
11810
12110
|
kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
|
|
11811
12111
|
tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
|
|
12112
|
+
tf: tf || '',
|
|
11812
12113
|
sid: String(sid || ''),
|
|
11813
12114
|
};
|
|
11814
12115
|
e.notes.push(note);
|
|
@@ -11818,7 +12119,7 @@ function finLearnApi() {
|
|
|
11818
12119
|
/* OPENCODE TARZI COMPACTION: notlar birikince ESKİLERİ arka planda
|
|
11819
12120
|
özetle — ajanı bekletmez, hafıza kendiliğinden sadeleşir */
|
|
11820
12121
|
if (e.notes.length > FIN_LEARN_COMPACT_AT) finLearnCompactAuto(sym);
|
|
11821
|
-
return { ok: true, id: note.id, symbol: sym, count: e.notes.length };
|
|
12122
|
+
return { ok: true, id: note.id, symbol: sym, timeframe: tf || '', count: e.notes.length };
|
|
11822
12123
|
},
|
|
11823
12124
|
remove: ({ id, symbol } = {}) => {
|
|
11824
12125
|
const learn = finLearnLoad();
|
|
@@ -12272,7 +12573,10 @@ function finApplyTraderFields(s, symbolsOverride, role, isMain) {
|
|
|
12272
12573
|
s.financeShadow = !!f.shadowMode; /* shadow modda işlem araçları emir göndermez */
|
|
12273
12574
|
{
|
|
12274
12575
|
const lim = finLimitsSnapshot();
|
|
12275
|
-
s.financeLimits = {
|
|
12576
|
+
s.financeLimits = {
|
|
12577
|
+
minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions,
|
|
12578
|
+
symbolLimits: lim.symbols, intervalSec: lim.intervalSec, intervalBase: lim.intervalBase, paceUntil: lim.paceUntil,
|
|
12579
|
+
};
|
|
12276
12580
|
}
|
|
12277
12581
|
/* ZORUNLU TEK skill: rol ajanı → roleSkills[rol], ana trader → roleSkills.trader */
|
|
12278
12582
|
const skillKey = r || (s.financePlaybook ? 'trader' : '');
|
|
@@ -12351,7 +12655,7 @@ function finAgentStop(sid, reason, opts) {
|
|
|
12351
12655
|
}
|
|
12352
12656
|
/* ANA TRADER DURDU: ekip cycle'sız kaldı — kendi timer'ıyla dönmeye devam etsin */
|
|
12353
12657
|
if (agent.main) {
|
|
12354
|
-
const iv =
|
|
12658
|
+
const iv = finPaceSec() * 1000;
|
|
12355
12659
|
for (const [tsid, ta] of financeState.agents) {
|
|
12356
12660
|
if (!ta || !ta.role) continue;
|
|
12357
12661
|
if (engine && engine.isBusy(tsid)) continue;
|
|
@@ -12368,13 +12672,13 @@ function finTraderBrief(agent) {
|
|
|
12368
12672
|
const syms = agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ');
|
|
12369
12673
|
const roleDef = finRoleDef(agent && agent.role);
|
|
12370
12674
|
const who = agent && agent.main ? 'TRADER' : roleDef ? roleDef.label.toUpperCase() : 'FINANCE AJANI';
|
|
12371
|
-
const playbook = agent && agent.main && f.roleSkills &&
|
|
12372
|
-
? f.roleSkills.trader
|
|
12675
|
+
const playbook = agent && agent.main && f.roleSkills && Array.isArray(f.roleSkills.trader)
|
|
12676
|
+
? f.roleSkills.trader.filter(Boolean).join(' + ')
|
|
12373
12677
|
: '';
|
|
12374
12678
|
return [
|
|
12375
12679
|
`Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
|
|
12376
12680
|
`Odak semboller: ${syms || '(boş — mt5_status ile terminale bak, mantıklı semboller seç)'}`,
|
|
12377
|
-
`Tur aralığı: ${f.intervalSec} sn · Lot aralığı: ${f.minLot}–${f.maxLot} (genel taban/tavan; sembol bazlı limit daraltır) · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
12681
|
+
`Tur aralığı: ${finPaceSec()} sn${Number(financeState.pace && financeState.pace.until) > Date.now() ? ` (HIZ MODU — taban ${f.intervalSec} sn)` : ''} · Lot aralığı: ${f.minLot}–${f.maxLot} (genel taban/tavan; sembol bazlı limit daraltır) · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
12378
12682
|
(() => {
|
|
12379
12683
|
const rows = Object.entries(f.symbolLimits || {}).map(([s, v]) => {
|
|
12380
12684
|
const mn = Number(v && v.minLot) > 0 ? Number(v.minLot) : Number(f.minLot);
|
|
@@ -12384,20 +12688,20 @@ function finTraderBrief(agent) {
|
|
|
12384
12688
|
return rows.length ? `SEMBOL LOT LİMİTLERİN (senin kararın): ${rows.join(' · ')}` : '';
|
|
12385
12689
|
})(),
|
|
12386
12690
|
agent && agent.main
|
|
12387
|
-
? `LİMİT YETKİN:
|
|
12691
|
+
? `LİMİT+RİTİM YETKİN: mt5_limits ile SEN güncellersin (anında geçerli): genel {action:"set", minLot?, maxLot?, maxPositions?}; SEMBOL BAZLI {action:"set", symbol:"XAUUSD", minLot?, maxLot?} — her sembolün min/max lotunu kendi karakterine göre SEN belirle (volatilite, spread, marj); sembolü genele döndürmek için {action:"set", symbol:"XAUUSD", reset:true}. TUR RİTMİ: {action:"set", intervalSec:45, intervalForMin:15} → sert hareket/haber anında geçici hızlan, süre bitince taban aralığa OTOMATİK döner; sakinleşince geri çek (ör. 180-300 sn) — SÜREKLİ en düşük aralıkta kalma (API/maliyet limiti).`
|
|
12388
12692
|
: '',
|
|
12389
12693
|
roleDef
|
|
12390
12694
|
? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
|
|
12391
|
-
: 'Otomatik işlem AÇIK (daima): 6 emir tipinin HEPSİ açık — buy_market/sell_market (anlık piyasa), buy_limit/sell_limit ve buy_stop/sell_stop (bekleyen; price zorunlu). mt5_trade ya da mt5_pending İKİSİ de tüm tipleri kabul eder; ayrıca mt5_close/mt5_modify/mt5_cancel açık (limitler sistemce zorlanır). KISMİ KAPATMA yetkisi: kısmi TP (kâr al) ve kısmi stop (zarar kes) — mt5_close {percent, kind:"partial_tp"|"partial_sl"}; otomatik DEĞİL, gerek görürsen sen kullan. ALARM: mt5_alerts ile kurarken modu SEN seç — mode:"once" tek seferlik, mode:"repeat" + cooldownMin (dk) tekrarlı. Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı.',
|
|
12695
|
+
: 'Otomatik işlem AÇIK (daima): 6 emir tipinin HEPSİ açık — buy_market/sell_market (anlık piyasa), buy_limit/sell_limit ve buy_stop/sell_stop (bekleyen; price zorunlu). mt5_trade ya da mt5_pending İKİSİ de tüm tipleri kabul eder; ayrıca mt5_close/mt5_modify/mt5_cancel açık (limitler sistemce zorlanır). KISMİ KAPATMA yetkisi: kısmi TP (kâr al) ve kısmi stop (zarar kes) — mt5_close {percent, kind:"partial_tp"|"partial_sl"}; otomatik DEĞİL, gerek görürsen sen kullan. ALARM: mt5_alerts ile kurarken modu SEN seç — mode:"once" tek seferlik, mode:"repeat" + cooldownMin (dk) tekrarlı. Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı. LOT KORKUSU YOK: önemli olan lot değil SL YERİ ve risk %’sidir — yakın stoplu net setupta yüksek lot NORMALDİR (boyutu risk hesabı verir).',
|
|
12392
12696
|
roleDef
|
|
12393
12697
|
? 'Bulgularını agent_dm ile ANA TRADER\u2019a bildir (to: "Trader" ya da ajan başlığı anahtarı); teknik/öneri çelişkisi varsa gerekçenle yaz.'
|
|
12394
12698
|
: f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
12395
12699
|
roleDef && f.strategy ? `Sahibinin strateji notu (bu çerçevede analiz et): ${f.strategy}` : '',
|
|
12396
|
-
playbook ? `PLAYBOOK (ZORUNLU skill):
|
|
12700
|
+
playbook ? `PLAYBOOK (ZORUNLU skill): ${playbook} — sistem promptunda tam metinleri verilmiştir; her turda birebir uygula.` : '',
|
|
12397
12701
|
f.shadowMode ? 'SHADOW MOD AÇIK: emir gönderilmez — kararlarını gerekçesiyle raporla, gerçek işlem açılmaz.' : '',
|
|
12398
12702
|
'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → mt5_rates/mt5_indicators ile teknik okuma → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
|
|
12399
12703
|
'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
|
|
12400
|
-
'ÖĞRENME (
|
|
12704
|
+
'ÖĞRENME (ZORUNLU DÖNGÜ): işlem açarken timeframe ver (SEN seçersin: M1..MN1) — kapanış otomatik o periyoda işlenir. Zarar/stop olan her kapanışta mt5_ogrenme {action:"add", symbol, timeframe, kind:"mistake", text:"yanlış neydi + kaçınma kuralı"} ile hata dersini MUTLAKA yaz; işe yarayan deseni kind:"pattern" ile tek kısa cümle olarak kaydet (her kapanışa değil). Karar öncesi mt5_ogrenme {action:"stats"/"list", symbol} ile periyot istatistiğini ve son hataları oku; NEGATİF periyotta riski düşür, en iyi periyodu tercih et, aynı hatayı tekrarlama.',
|
|
12401
12705
|
'Risk otomasyonu main süreçte 5 sn döngüyle çalışır (+R BE, trailing, KÂR KORUMA: kâr 0.3R\'de kilitlenir, kısmi TP) — TP için RR ≥ 1.5 şartı YOK; 0.3-0.5R\'de kısmi kâr alıp kalanı korumalı trailing ile taşı (hızlı kâr toplama), SL/TP seviyelerini yine sen aktif yönet.',
|
|
12402
12706
|
'⚡HIZLI AKSİYON: onaylı fırsatta market buy/sell ile ANINDA gir (mt5_trade {symbol, side, sl, tp, riskPct}); bekleyen emir vermek ZORUNLU DEĞİL — limit/stop yalnız seviye beklemede kurulur.',
|
|
12403
12707
|
'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
|
|
@@ -12535,7 +12839,7 @@ function finTeamHasMain() {
|
|
|
12535
12839
|
|
|
12536
12840
|
/* Takılma valfi: ekip turu bu süreyi aşarsa trader eski raporla başlar */
|
|
12537
12841
|
function finTeamWaitMaxMs() {
|
|
12538
|
-
const iSec =
|
|
12842
|
+
const iSec = finPaceSec();
|
|
12539
12843
|
return Math.max(60, Math.min(300, iSec)) * 1000;
|
|
12540
12844
|
}
|
|
12541
12845
|
|
|
@@ -12623,8 +12927,8 @@ function finAgentRound(sid, opts) {
|
|
|
12623
12927
|
? 'SHADOW MOD AÇIK: emir GÖNDERİLMEZ — kararını teziyle raporla (günlüğe yazılır).'
|
|
12624
12928
|
: 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
|
|
12625
12929
|
const learnTip = roleDef
|
|
12626
|
-
? ' İlgili sembolün geçmiş derslerini (mt5_ogrenme) oku
|
|
12627
|
-
: '
|
|
12930
|
+
? ' İlgili sembolün geçmiş derslerini ve PERİYOT istatistiğini (mt5_ogrenme stats/list) oku; önerine yansıt (hangi zaman dilimi kazandırıyor).'
|
|
12931
|
+
: ' Karar öncesi mt5_ogrenme stats/list ile sembolün PERİYOT istatistiğini ve son hatalarını oku; işlem açarken timeframe ver (SEN seçersin) — NEGATİF periyotta riski düşür/teyit ara, en iyi periyodu kullan; yalnız kalıcı içgörüyü mt5_ogrenme add ile yaz, ZARAR/stop sonrası hata dersini (kind:"mistake") mutlaka çıkar' + (agent.main ? '; mt5_limits ile lot/pozisyon limitlerini ve TUR RİTMİNİ (intervalSec+intervalForMin) güncelleyebilirsin.' : '.');
|
|
12628
12932
|
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
12629
12933
|
const round = `FINANCE TUR #${agent.round}: ${focus}hesap + pozisyonlar + fiyatları çek; ${roleDef ? 'rolüne uygun analiz yap (mt5_rates/mt5_indicators ile) ve öneri ver.' : 'açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); mt5_rates/mt5_indicators ile yeni fırsatları değerlendir. ⚡Onaylı fırsatta market buy/sell ile ANINDA gir (bekleyen emir ZORUNLU DEĞİL); lot için volume yerine riskPct+sl yeter.'} ${auto}${learnTip} Önemli kararların gerekçesini mt5_note ile günlüğe yaz. Kısa rapor ver.`;
|
|
12630
12934
|
const launch = (planBlock) => {
|
|
@@ -12719,8 +13023,7 @@ function finFlushOnDone(ev) {
|
|
|
12719
13023
|
sıradaki turu trader'ın cycle'ı başlatır (rapor tazeliği). Trader yoksa
|
|
12720
13024
|
ekip kendi timer'ıyla dönmeye devam eder. */
|
|
12721
13025
|
if (agent.role && finTeamHasMain()) return;
|
|
12722
|
-
const
|
|
12723
|
-
const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
|
|
13026
|
+
const iv = finPaceSec() * 1000;
|
|
12724
13027
|
financeState.lastRoundAt = Date.now();
|
|
12725
13028
|
if (agent.main) {
|
|
12726
13029
|
finPush('trader', { state: 'idle', round: agent.round, nextInSec: iv / 1000 });
|
|
@@ -12915,17 +13218,8 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
12915
13218
|
}
|
|
12916
13219
|
if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
|
|
12917
13220
|
/* TUR ARALIĞI CANLI: bekleyen zamanlayıcılar yeni aralıkla yeniden kurulur —
|
|
12918
|
-
koşan tur bitince zaten finFlushOnDone yeni
|
|
12919
|
-
if (p.intervalSec !== undefined
|
|
12920
|
-
const iv = Math.max(30, Math.round(Number(f.intervalSec) || 120)) * 1000;
|
|
12921
|
-
const hasMain = finTeamHasMain();
|
|
12922
|
-
for (const [sid, a] of financeState.agents) {
|
|
12923
|
-
if (!a || !engine || engine.isBusy(sid)) continue;
|
|
12924
|
-
if (a.role && hasMain) continue; /* cycle modu: ekip turunu trader başlatır */
|
|
12925
|
-
clearTimeout(a.timer);
|
|
12926
|
-
a.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, iv);
|
|
12927
|
-
}
|
|
12928
|
-
}
|
|
13221
|
+
koşan tur bitince zaten finFlushOnDone yeni ritmi okur. */
|
|
13222
|
+
if (p.intervalSec !== undefined) { try { finRescheduleAgents(); } catch {} }
|
|
12929
13223
|
if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
|
|
12930
13224
|
if (p.minLot !== undefined) f.minLot = Math.max(0.01, Math.min(100, Number(p.minLot) || 0.01));
|
|
12931
13225
|
/* min lot max lotu aşamaz — hangisi sonra yazıldıysa diğerine kelepçelenir */
|
|
@@ -12993,13 +13287,13 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
12993
13287
|
if (p.analysisTeam !== undefined) f.analysisTeam = finRolesValid(p.analysisTeam);
|
|
12994
13288
|
if (p.analysisAuto !== undefined) f.analysisAuto = !!p.analysisAuto;
|
|
12995
13289
|
if (p.analysisCount !== undefined) f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(p.analysisCount) || 0)));
|
|
12996
|
-
/* ROL → SKILL eşleştirmesi güncellemesi (modal): rol başına
|
|
12997
|
-
ana trader playbook'u (trader) */
|
|
13290
|
+
/* ROL → SKILL eşleştirmesi güncellemesi (modal): rol başına EN FAZLA 2
|
|
13291
|
+
skill + ana trader playbook'u (trader) */
|
|
12998
13292
|
if (p.roleSkills !== undefined && p.roleSkills && typeof p.roleSkills === 'object') {
|
|
12999
13293
|
for (const id of [...FIN_ROLES.map((d) => d.id), 'trader']) {
|
|
13000
13294
|
if (p.roleSkills[id] === undefined) continue;
|
|
13001
13295
|
f.roleSkills[id] = (Array.isArray(p.roleSkills[id]) ? p.roleSkills[id] : [])
|
|
13002
|
-
.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
13296
|
+
.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 2);
|
|
13003
13297
|
}
|
|
13004
13298
|
/* koşan rol ajanlarını da tazele — seçim sonraki turdan itibaren sistem
|
|
13005
13299
|
promptuna girer (ajanı durdurmaya gerek yok) */
|