beast-agent 2.63.4 → 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 +33 -16
- package/src/agent/financetools.js +44 -17
- 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 +303 -77
- 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 +165 -23
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) */
|
|
@@ -9460,9 +9510,9 @@ function finCfg() {
|
|
|
9460
9510
|
if (!Number.isFinite(Number(f.analysisCount))) f.analysisCount = 2;
|
|
9461
9511
|
f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(f.analysisCount) || 0)));
|
|
9462
9512
|
if (!f.analysisAuto) f.analysisTeam = finRolesValid(f.analysisTeam);
|
|
9463
|
-
/* ROL → SKILL eşleştirmesi: rol başına
|
|
9464
|
-
seçim yapılır.
|
|
9465
|
-
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. */
|
|
9466
9516
|
if (f.roleSkillsDefaultsCleared !== true) {
|
|
9467
9517
|
f.roleSkills = {};
|
|
9468
9518
|
f.roleSkillsDefaultsCleared = true;
|
|
@@ -9470,12 +9520,12 @@ function finCfg() {
|
|
|
9470
9520
|
if (!f.roleSkills || typeof f.roleSkills !== 'object') f.roleSkills = {};
|
|
9471
9521
|
for (const d of FIN_ROLES) {
|
|
9472
9522
|
const cur = Array.isArray(f.roleSkills[d.id]) ? f.roleSkills[d.id] : [];
|
|
9473
|
-
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);
|
|
9474
9524
|
}
|
|
9475
|
-
/* TRADER PLAYBOOK: asıl karar vericinin zorunlu skill'
|
|
9525
|
+
/* TRADER PLAYBOOK: asıl karar vericinin zorunlu skill'leri (roleSkills.trader) */
|
|
9476
9526
|
{
|
|
9477
9527
|
const cur = Array.isArray(f.roleSkills.trader) ? f.roleSkills.trader : [];
|
|
9478
|
-
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);
|
|
9479
9529
|
}
|
|
9480
9530
|
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
9481
9531
|
if (!Number(f.minLot)) f.minLot = 0.01;
|
|
@@ -10717,11 +10767,13 @@ async function finRecordClose(ticket, st) {
|
|
|
10717
10767
|
finExcSave();
|
|
10718
10768
|
finExcFlush(true);
|
|
10719
10769
|
const kind = finwatch.closeKind(closeReason);
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
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 });
|
|
10723
10775
|
const pl = rounded == null ? '' : ` · K/Z ${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
|
|
10724
|
-
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${pl}`;
|
|
10776
|
+
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${tf ? ' · ' + tf : ''}${pl}`;
|
|
10725
10777
|
financeLog('[watchdog] ' + line);
|
|
10726
10778
|
if (finCfg().notifyTrades !== false) financeNotify(line, 'close');
|
|
10727
10779
|
if (rounded != null) finStatsRefresh(true);
|
|
@@ -10731,10 +10783,17 @@ async function finRecordClose(ticket, st) {
|
|
|
10731
10783
|
const label = kind === 'stop' ? 'STOP OLDU' : kind === 'tp' ? 'TP OLDU' : 'STOP-OUT (teminat)';
|
|
10732
10784
|
const how = kind === 'tp' ? 'take-profit' : kind === 'stop' ? 'stop-loss' : 'stop-out';
|
|
10733
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.`;
|
|
10734
10792
|
finWakeAgents(
|
|
10735
10793
|
`[FİNANS OLAYI — ${label}, TUR SANİYESİ BEKLENMEDEN İLETİLDİ]\n` +
|
|
10736
|
-
`- ${symbol || '?'}${st && st.side ? ' ' + String(st.side).toUpperCase() : ''} #${ticket} ${how} ile kapandı · net ${netTxt}\n` +
|
|
10737
|
-
`Ş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,
|
|
10738
10797
|
{ kind, ticket, symbol }
|
|
10739
10798
|
);
|
|
10740
10799
|
}
|
|
@@ -10920,6 +10979,9 @@ async function finWatchTick() {
|
|
|
10920
10979
|
symbol: String(p.symbol || ''),
|
|
10921
10980
|
side: Number(p.type) === 0 ? 'buy' : 'sell',
|
|
10922
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),
|
|
10923
10985
|
r: 0,
|
|
10924
10986
|
be: false,
|
|
10925
10987
|
partial: false,
|
|
@@ -10930,9 +10992,12 @@ async function finWatchTick() {
|
|
|
10930
10992
|
financeState.watch.set(ticket, st);
|
|
10931
10993
|
const isBeast = Number(p.magic) === 20260908 || /beast/i.test(String(p.comment || ''));
|
|
10932
10994
|
if (isBeast) {
|
|
10933
|
-
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 || '' });
|
|
10934
10996
|
}
|
|
10935
10997
|
finWatchSave();
|
|
10998
|
+
} else if (!st.tf) {
|
|
10999
|
+
const tfNow = finLearnParseTf(p.comment);
|
|
11000
|
+
if (tfNow) { st.tf = tfNow; finWatchSave(); }
|
|
10936
11001
|
}
|
|
10937
11002
|
/* KÂR KORUMA: görülen EN İYİ kâr (fiyat farkı) tick aralarında da korunur */
|
|
10938
11003
|
{
|
|
@@ -11176,8 +11241,8 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11176
11241
|
}
|
|
11177
11242
|
/* SEMBOL BAZLI ÖĞRENME kaydı: günlüğe + AKIŞ paneline düşer */
|
|
11178
11243
|
if (k === 'learn') {
|
|
11179
|
-
finJournal({ kind: 'learn', sid, agent: who, symbol: d.symbol, text: d.text, learnKind: d.kind || 'observation' });
|
|
11180
|
-
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)}`;
|
|
11181
11246
|
financeLog('[ogrenme] ' + line);
|
|
11182
11247
|
finPush('trade', { line });
|
|
11183
11248
|
return;
|
|
@@ -11191,7 +11256,7 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11191
11256
|
return;
|
|
11192
11257
|
}
|
|
11193
11258
|
if (k === 'trade') {
|
|
11194
|
-
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 || '' });
|
|
11195
11260
|
} else if (k === 'close') {
|
|
11196
11261
|
/* KISMİ KAPATMA (kısmi TP / kısmi stop): ajan kararı; günlük + bildirim.
|
|
11197
11262
|
Kesin toplam K/Z yine watchdog tarafından yazılır (çift sayım yok). */
|
|
@@ -11211,14 +11276,14 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11211
11276
|
} else if (k === 'modify') {
|
|
11212
11277
|
finJournal({ kind: 'modify', sid, agent: who, ticket: d.ticket, sl: d.sl, tp: d.tp });
|
|
11213
11278
|
} else if (k === 'pending') {
|
|
11214
|
-
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 || '' });
|
|
11215
11280
|
} else if (k === 'cancel') {
|
|
11216
11281
|
finJournal({ kind: 'cancel', sid, agent: who, ticket: d.ticket });
|
|
11217
11282
|
}
|
|
11218
11283
|
let line = '';
|
|
11219
|
-
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 : ''}`;
|
|
11220
11285
|
else if (k === 'modify') line = `SL/TP GÜNCELLENDİ: ticket ${d.ticket} (SL ${d.sl || 0} / TP ${d.tp || 0})${who ? ' · ' + who : ''}`;
|
|
11221
|
-
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 : ''}`;
|
|
11222
11287
|
else if (k === 'cancel') line = `EMİR İPTAL: ticket ${d.ticket}`;
|
|
11223
11288
|
if (line) {
|
|
11224
11289
|
financeLog('[ajan] ' + line);
|
|
@@ -11254,9 +11319,9 @@ try {
|
|
|
11254
11319
|
if (!kind) return;
|
|
11255
11320
|
if (kind === 'trade') {
|
|
11256
11321
|
const side = a.side || (/^buy/i.test(String(a.type || '')) ? 'buy' : /^sell/i.test(String(a.type || '')) ? 'sell' : '');
|
|
11257
|
-
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);
|
|
11258
11323
|
} else if (kind === 'modify') finTradeEvent('modify', { ticket: a.ticket, sl: a.sl, tp: a.tp }, sid);
|
|
11259
|
-
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);
|
|
11260
11325
|
else if (kind === 'cancel') finTradeEvent('cancel', { ticket: a.ticket }, sid);
|
|
11261
11326
|
else if (kind === 'close') {
|
|
11262
11327
|
finTradeEvent('close', {
|
|
@@ -11493,15 +11558,62 @@ const FIN_LEARN_COMPACT_AT = 24; /* not sayısı bunu aşınca arka planda ot
|
|
|
11493
11558
|
const FIN_LEARN_KEEP_RECENT = 8; /* ham kalan son ders sayısı */
|
|
11494
11559
|
const FIN_LEARN_SUMMARY_MAX = 800; /* kalıcı özet metin tavanı (karakter) */
|
|
11495
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
|
+
}
|
|
11496
11580
|
|
|
11497
11581
|
function finLearnEmptyStats() {
|
|
11498
11582
|
return {
|
|
11499
11583
|
trades: 0, wins: 0, losses: 0, net: 0,
|
|
11500
11584
|
mfe: 0, mae: 0, givebacks: 0, givebackAmount: 0,
|
|
11501
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: {},
|
|
11502
11588
|
};
|
|
11503
11589
|
}
|
|
11504
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
|
+
|
|
11505
11617
|
/* Ders metni karşılaştırma anahtarı: büyük/küçük harf, noktalama ve boşluk
|
|
11506
11618
|
farkları aynı ders sayılır — ajan aynı cümleyi kopyalayıp depoyu doldurmasın */
|
|
11507
11619
|
function finLearnNoteKey(text) {
|
|
@@ -11555,7 +11667,7 @@ function finLearnPrune(learn, keep, protectSym) {
|
|
|
11555
11667
|
yüklemede çalışır → depo kendiliğinden sade kalır, birikme/zehirlenme olmaz. */
|
|
11556
11668
|
function finLearnForget(learn, now) {
|
|
11557
11669
|
const t = Number(now) || Date.now();
|
|
11558
|
-
const stat = { notes: 0, trades: 0, symbols: 0 };
|
|
11670
|
+
const stat = { notes: 0, trades: 0, mistakes: 0, symbols: 0 };
|
|
11559
11671
|
for (const [sym, e] of Object.entries(learn.symbols)) {
|
|
11560
11672
|
if (!e || typeof e !== 'object') { delete learn.symbols[sym]; stat.symbols++; continue; }
|
|
11561
11673
|
const notes = Array.isArray(e.notes) ? e.notes : [];
|
|
@@ -11564,6 +11676,10 @@ function finLearnForget(learn, now) {
|
|
|
11564
11676
|
const trades = Array.isArray(e.trades) ? e.trades : [];
|
|
11565
11677
|
e.trades = trades.filter((x) => x && t - (Number(x.at) || 0) <= FIN_LEARN_TRADE_TTL_MS);
|
|
11566
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;
|
|
11567
11683
|
if (t - finLearnActivity(e) > FIN_LEARN_SYMBOL_TTL_MS) {
|
|
11568
11684
|
delete learn.symbols[sym];
|
|
11569
11685
|
stat.symbols += 1;
|
|
@@ -11577,7 +11693,7 @@ function finLearnForget(learn, now) {
|
|
|
11577
11693
|
function finLearnTidy(learn, protectSym, now) {
|
|
11578
11694
|
const forgot = finLearnForget(learn, now);
|
|
11579
11695
|
const cap = finLearnPrune(learn, FIN_LEARN_MAX_SYMBOLS, protectSym);
|
|
11580
|
-
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 };
|
|
11581
11697
|
}
|
|
11582
11698
|
|
|
11583
11699
|
function finLearnLoad() {
|
|
@@ -11593,7 +11709,8 @@ function finLearnLoad() {
|
|
|
11593
11709
|
learn.symbols[sym] = {
|
|
11594
11710
|
notes: Array.isArray(v.notes) ? v.notes.filter((n) => n && n.id && n.text).slice(-FIN_LEARN_MAX_NOTES) : [],
|
|
11595
11711
|
trades: Array.isArray(v.trades) ? v.trades.filter(Boolean).slice(-FIN_LEARN_MAX_TRADES) : [],
|
|
11596
|
-
|
|
11712
|
+
mistakes: Array.isArray(v.mistakes) ? v.mistakes.filter(Boolean).slice(-FIN_LEARN_MAX_MISTAKES) : [],
|
|
11713
|
+
stats: finLearnNormStats(v.stats),
|
|
11597
11714
|
/* KALICI ÖZET (opencode tarzı sıkıştırma): eski derslerin modele
|
|
11598
11715
|
özetlettirilmiş hâli — ham dersler düşse de bilgi kalır */
|
|
11599
11716
|
summary: v.summary && typeof v.summary === 'object' && String(v.summary.text || '').trim()
|
|
@@ -11610,7 +11727,7 @@ function finLearnLoad() {
|
|
|
11610
11727
|
} catch {}
|
|
11611
11728
|
const dropped = finLearnTidy(learn); /* yükleme anında unut + tavan uygula */
|
|
11612
11729
|
financeState.learn = learn;
|
|
11613
|
-
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 */
|
|
11614
11731
|
return learn;
|
|
11615
11732
|
}
|
|
11616
11733
|
|
|
@@ -11630,16 +11747,16 @@ const FIN_LEARN_COMPACT_SYSTEM =
|
|
|
11630
11747
|
'Sen bir trading mentorüsün. Sana bir sembolün ESKİ ders notları ve mevcut özeti verilir. ' +
|
|
11631
11748
|
'Görevin: güncellenmiş KISA bir özet üretmek.\n' +
|
|
11632
11749
|
'KURALLAR: (1) Yalnız kalıcı değeri olan bilgiyi tut: çalışan desenler, kaçınılacak hatalar, ' +
|
|
11633
|
-
'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 ' +
|
|
11634
11751
|
'haber, geçici durum gibi bayat bilgiyi at. (3) Çelişen bilgide EN YENİ ve istatistikle ' +
|
|
11635
|
-
'desteklenen kazanır. (4) Sayısal istatistikleri (işlem, kazanç %, net, kâr geri verme) koru
|
|
11636
|
-
'(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. ' +
|
|
11637
11754
|
'(6) Çıktı SADECE özet metni olsun; başlık/etiket/açıklama yazma.';
|
|
11638
11755
|
|
|
11639
11756
|
const finLearnCompacting = new Set();
|
|
11640
11757
|
|
|
11641
11758
|
function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
11642
|
-
const st =
|
|
11759
|
+
const st = finLearnNormStats(e.stats);
|
|
11643
11760
|
const wr = st.trades ? Math.round((st.wins / st.trades) * 100) : 0;
|
|
11644
11761
|
const lines = [];
|
|
11645
11762
|
lines.push('SEMBOL: ' + sym);
|
|
@@ -11648,12 +11765,27 @@ function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
|
11648
11765
|
(st.givebacks ? ` · ${st.givebacks} kez kâr geri verildi` : '') +
|
|
11649
11766
|
(st.streak >= 2 ? ` · ${st.streak} ardışık kayıp` : '')
|
|
11650
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
|
+
}
|
|
11651
11783
|
if (e.summary && e.summary.text) lines.push('MEVCUT ÖZET:\n' + String(e.summary.text).slice(0, 1400));
|
|
11652
11784
|
lines.push('ESKİ DERSLER (kronolojik — en yeni EN SONDA):');
|
|
11653
11785
|
for (const n of oldNotes.slice(-80)) {
|
|
11654
|
-
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)}`);
|
|
11655
11787
|
}
|
|
11656
|
-
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.');
|
|
11657
11789
|
return lines.join('\n');
|
|
11658
11790
|
}
|
|
11659
11791
|
|
|
@@ -11720,40 +11852,55 @@ function finLearnSym(symbol) {
|
|
|
11720
11852
|
const sym = String(symbol || '').trim().toUpperCase();
|
|
11721
11853
|
if (!sym) return null;
|
|
11722
11854
|
const learn = finLearnLoad();
|
|
11723
|
-
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 };
|
|
11724
11856
|
const e = learn.symbols[sym];
|
|
11725
|
-
e.
|
|
11857
|
+
if (!Array.isArray(e.mistakes)) e.mistakes = [];
|
|
11858
|
+
e.stats = finLearnNormStats(e.stats);
|
|
11726
11859
|
return e;
|
|
11727
11860
|
}
|
|
11728
11861
|
|
|
11729
|
-
/* Kapanan her işlem otomatik öğrenme girdisi olur (sembol bazlı)
|
|
11730
|
-
|
|
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 }) {
|
|
11731
11867
|
try {
|
|
11732
11868
|
if (net == null) return; /* K/Z okunamadı — sahte kayıt yazma */
|
|
11733
11869
|
const n = Number(net);
|
|
11734
11870
|
if (!isFinite(n) || !symbol) return;
|
|
11735
11871
|
const e = finLearnSym(symbol);
|
|
11736
11872
|
if (!e) return;
|
|
11873
|
+
const tf = finLearnNormTf(timeframe);
|
|
11737
11874
|
const st = e.stats;
|
|
11738
11875
|
const m = Number(mfe);
|
|
11739
11876
|
const ma = Number(mae);
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
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);
|
|
11747
11896
|
st.bestNet = Math.max(Number(st.bestNet) || 0, Math.round(n * 100) / 100);
|
|
11748
11897
|
st.worstNet = Math.min(Number(st.worstNet) || 0, Math.round(n * 100) / 100);
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
if (
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
}
|
|
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]);
|
|
11755
11903
|
}
|
|
11756
|
-
if (isFinite(ma)) st.mae = Math.round(((Number(st.mae) || 0) + ma) * 100) / 100;
|
|
11757
11904
|
e.trades.push({
|
|
11758
11905
|
at: Date.now(),
|
|
11759
11906
|
side: String(side || ''),
|
|
@@ -11761,14 +11908,69 @@ function finLearnRecordClose({ symbol, side, net, mfe, mae, reason }) {
|
|
|
11761
11908
|
mfe: isFinite(m) ? Math.round(m * 100) / 100 : null,
|
|
11762
11909
|
mae: isFinite(ma) ? Math.round(ma * 100) / 100 : null,
|
|
11763
11910
|
reason: String(reason || '').slice(0, 40),
|
|
11911
|
+
tf: tf || null,
|
|
11764
11912
|
});
|
|
11765
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
|
+
}
|
|
11766
11930
|
finLearnTidy(finLearnLoad(), symbol);
|
|
11767
11931
|
finLearnSave();
|
|
11768
11932
|
} catch {}
|
|
11769
11933
|
}
|
|
11770
11934
|
|
|
11771
|
-
/*
|
|
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
|
+
|
|
11772
11974
|
function finBuildLearnDigest(symbols) {
|
|
11773
11975
|
const learn = finLearnLoad();
|
|
11774
11976
|
const prefer = Array.isArray(symbols) ? symbols.map((s) => String(s || '').trim().toUpperCase()).filter(Boolean) : [];
|
|
@@ -11777,7 +11979,7 @@ function finBuildLearnDigest(symbols) {
|
|
|
11777
11979
|
const L = [];
|
|
11778
11980
|
for (const sym of keys.slice(0, 10)) {
|
|
11779
11981
|
const e = learn.symbols[sym];
|
|
11780
|
-
const st =
|
|
11982
|
+
const st = finLearnNormStats(e.stats);
|
|
11781
11983
|
if (st.trades > 0) {
|
|
11782
11984
|
const wr = Math.round((st.wins / st.trades) * 100);
|
|
11783
11985
|
const parts = [`${st.trades} işlem`, `%${wr} kazanç`, `net ${st.net >= 0 ? '+' : ''}${st.net}`];
|
|
@@ -11786,6 +11988,13 @@ function finBuildLearnDigest(symbols) {
|
|
|
11786
11988
|
if (st.lastAt) parts.push(`son: ${st.lastNet >= 0 ? '+' : ''}${st.lastNet}`);
|
|
11787
11989
|
L.push(`- ${sym}: ${parts.join(' · ')}`);
|
|
11788
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);
|
|
11789
11998
|
/* KALICI ÖZET (compaction): eski derslerin sıkıştırılmış hâli — ham ders
|
|
11790
11999
|
sayısı azalsa da birikmiş bilgi prompta girer */
|
|
11791
12000
|
if (e.summary && e.summary.text) {
|
|
@@ -11797,62 +12006,78 @@ function finBuildLearnDigest(symbols) {
|
|
|
11797
12006
|
L.push(` ders${extra}: "${String(last.text || '').slice(0, 160)}"`);
|
|
11798
12007
|
}
|
|
11799
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
|
+
}
|
|
11800
12014
|
return L.join('\n');
|
|
11801
12015
|
}
|
|
11802
12016
|
|
|
11803
12017
|
function finLearnApi() {
|
|
11804
12018
|
const norm = (s) => String(s || '').trim().toUpperCase();
|
|
11805
12019
|
return {
|
|
11806
|
-
list: ({ symbol, limit } = {}) => {
|
|
12020
|
+
list: ({ symbol, timeframe, limit } = {}) => {
|
|
11807
12021
|
const learn = finLearnLoad();
|
|
11808
12022
|
const sym = norm(symbol);
|
|
12023
|
+
const tf = finLearnNormTf(timeframe);
|
|
11809
12024
|
const take = Math.max(1, Math.min(25, Math.round(Number(limit) || 8)));
|
|
11810
12025
|
if (sym) {
|
|
11811
12026
|
const e = learn.symbols[sym];
|
|
11812
|
-
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 : []);
|
|
11813
12030
|
return {
|
|
11814
12031
|
ok: true,
|
|
11815
12032
|
symbol: sym,
|
|
11816
|
-
|
|
11817
|
-
|
|
12033
|
+
timeframe: tf || '',
|
|
12034
|
+
count: notes.length,
|
|
12035
|
+
notes: notes.slice(-take),
|
|
11818
12036
|
trades: e.trades.slice(-12),
|
|
11819
|
-
stats:
|
|
12037
|
+
stats: finLearnNormStats(e.stats),
|
|
11820
12038
|
summary: e.summary && e.summary.text ? e.summary : null,
|
|
12039
|
+
mistakes: mistakes.slice(-8),
|
|
11821
12040
|
};
|
|
11822
12041
|
}
|
|
11823
12042
|
const rows = Object.entries(learn.symbols)
|
|
11824
12043
|
.map(([s, e]) => ({
|
|
11825
12044
|
symbol: s,
|
|
11826
12045
|
notes: e.notes.length,
|
|
11827
|
-
stats:
|
|
12046
|
+
stats: finLearnNormStats(e.stats),
|
|
11828
12047
|
lastNote: String(((e.notes[e.notes.length - 1] || {}).text) || ''),
|
|
11829
12048
|
hasSummary: !!(e.summary && e.summary.text),
|
|
11830
12049
|
}))
|
|
11831
12050
|
.sort((a, b) => (b.stats.lastAt || 0) - (a.stats.lastAt || 0));
|
|
11832
12051
|
return { ok: true, count: rows.length, symbols: rows.slice(0, FIN_LEARN_MAX_SYMBOLS) };
|
|
11833
12052
|
},
|
|
11834
|
-
stats: ({ symbol } = {}) => {
|
|
11835
|
-
/* KOMPAKT OKUMA: istatistik + kalıcı özet + son 3 ders
|
|
11836
|
-
|
|
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 */
|
|
11837
12056
|
const learn = finLearnLoad();
|
|
11838
12057
|
const sym = norm(symbol);
|
|
12058
|
+
const tf = finLearnNormTf(timeframe);
|
|
11839
12059
|
if (sym) {
|
|
11840
12060
|
const e = learn.symbols[sym] || {};
|
|
12061
|
+
const st = finLearnNormStats(e.stats);
|
|
11841
12062
|
return {
|
|
11842
12063
|
ok: true,
|
|
11843
12064
|
symbol: sym,
|
|
11844
|
-
|
|
12065
|
+
timeframe: tf || '',
|
|
12066
|
+
stats: st,
|
|
12067
|
+
byTf: tf && st.byTf[tf] ? { [tf]: st.byTf[tf] } : st.byTf,
|
|
11845
12068
|
summary: e.summary && e.summary.text ? e.summary : null,
|
|
11846
|
-
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) : [],
|
|
11847
12071
|
};
|
|
11848
12072
|
}
|
|
11849
12073
|
const stats = {};
|
|
11850
|
-
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);
|
|
11851
12075
|
return { ok: true, count: Object.keys(stats).length, stats };
|
|
11852
12076
|
},
|
|
11853
|
-
add: ({ symbol, text, kind, tags, sid } = {}) => {
|
|
12077
|
+
add: ({ symbol, text, kind, tags, timeframe, sid } = {}) => {
|
|
11854
12078
|
const sym = norm(symbol);
|
|
11855
12079
|
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
12080
|
+
const tf = finLearnNormTf(timeframe);
|
|
11856
12081
|
if (!sym || !t) return { ok: false, error: 'symbol ve text gerekli' };
|
|
11857
12082
|
/* KALİTE KAPISI: kısa + somut + tekrar kullanılabilir — günlük tutma */
|
|
11858
12083
|
const q = finLearnNoteQuality(t);
|
|
@@ -11860,10 +12085,10 @@ function finLearnApi() {
|
|
|
11860
12085
|
const learn = finLearnLoad();
|
|
11861
12086
|
const e = finLearnSym(sym);
|
|
11862
12087
|
if (!e) return { ok: false, error: 'sembol yok' };
|
|
11863
|
-
/* aynı ders tekrar yazılmasın (son 40 kayıtta normalize metin) —
|
|
11864
|
-
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 */
|
|
11865
12090
|
const key = finLearnNoteKey(t);
|
|
11866
|
-
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)) {
|
|
11867
12092
|
return { ok: true, duplicate: true, symbol: sym, count: e.notes.length };
|
|
11868
12093
|
}
|
|
11869
12094
|
/* GÜNLÜK DERS LİMİTİ: ajan aynı gün aynı sembole ders yağdıramaz —
|
|
@@ -11884,6 +12109,7 @@ function finLearnApi() {
|
|
|
11884
12109
|
text: t.slice(0, FIN_LEARN_NOTE_MAX),
|
|
11885
12110
|
kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
|
|
11886
12111
|
tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
|
|
12112
|
+
tf: tf || '',
|
|
11887
12113
|
sid: String(sid || ''),
|
|
11888
12114
|
};
|
|
11889
12115
|
e.notes.push(note);
|
|
@@ -11893,7 +12119,7 @@ function finLearnApi() {
|
|
|
11893
12119
|
/* OPENCODE TARZI COMPACTION: notlar birikince ESKİLERİ arka planda
|
|
11894
12120
|
özetle — ajanı bekletmez, hafıza kendiliğinden sadeleşir */
|
|
11895
12121
|
if (e.notes.length > FIN_LEARN_COMPACT_AT) finLearnCompactAuto(sym);
|
|
11896
|
-
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 };
|
|
11897
12123
|
},
|
|
11898
12124
|
remove: ({ id, symbol } = {}) => {
|
|
11899
12125
|
const learn = finLearnLoad();
|
|
@@ -12446,8 +12672,8 @@ function finTraderBrief(agent) {
|
|
|
12446
12672
|
const syms = agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ');
|
|
12447
12673
|
const roleDef = finRoleDef(agent && agent.role);
|
|
12448
12674
|
const who = agent && agent.main ? 'TRADER' : roleDef ? roleDef.label.toUpperCase() : 'FINANCE AJANI';
|
|
12449
|
-
const playbook = agent && agent.main && f.roleSkills &&
|
|
12450
|
-
? f.roleSkills.trader
|
|
12675
|
+
const playbook = agent && agent.main && f.roleSkills && Array.isArray(f.roleSkills.trader)
|
|
12676
|
+
? f.roleSkills.trader.filter(Boolean).join(' + ')
|
|
12451
12677
|
: '';
|
|
12452
12678
|
return [
|
|
12453
12679
|
`Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
|
|
@@ -12471,11 +12697,11 @@ function finTraderBrief(agent) {
|
|
|
12471
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.'
|
|
12472
12698
|
: f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
12473
12699
|
roleDef && f.strategy ? `Sahibinin strateji notu (bu çerçevede analiz et): ${f.strategy}` : '',
|
|
12474
|
-
playbook ? `PLAYBOOK (ZORUNLU skill):
|
|
12700
|
+
playbook ? `PLAYBOOK (ZORUNLU skill): ${playbook} — sistem promptunda tam metinleri verilmiştir; her turda birebir uygula.` : '',
|
|
12475
12701
|
f.shadowMode ? 'SHADOW MOD AÇIK: emir gönderilmez — kararlarını gerekçesiyle raporla, gerçek işlem açılmaz.' : '',
|
|
12476
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.',
|
|
12477
12703
|
'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
|
|
12478
|
-
'ÖĞ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.',
|
|
12479
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.',
|
|
12480
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.',
|
|
12481
12707
|
'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
|
|
@@ -12701,8 +12927,8 @@ function finAgentRound(sid, opts) {
|
|
|
12701
12927
|
? 'SHADOW MOD AÇIK: emir GÖNDERİLMEZ — kararını teziyle raporla (günlüğe yazılır).'
|
|
12702
12928
|
: 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
|
|
12703
12929
|
const learnTip = roleDef
|
|
12704
|
-
? ' İlgili sembolün geçmiş derslerini (mt5_ogrenme) oku
|
|
12705
|
-
: '
|
|
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.' : '.');
|
|
12706
12932
|
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
12707
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.`;
|
|
12708
12934
|
const launch = (planBlock) => {
|
|
@@ -13061,13 +13287,13 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
13061
13287
|
if (p.analysisTeam !== undefined) f.analysisTeam = finRolesValid(p.analysisTeam);
|
|
13062
13288
|
if (p.analysisAuto !== undefined) f.analysisAuto = !!p.analysisAuto;
|
|
13063
13289
|
if (p.analysisCount !== undefined) f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(p.analysisCount) || 0)));
|
|
13064
|
-
/* ROL → SKILL eşleştirmesi güncellemesi (modal): rol başına
|
|
13065
|
-
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) */
|
|
13066
13292
|
if (p.roleSkills !== undefined && p.roleSkills && typeof p.roleSkills === 'object') {
|
|
13067
13293
|
for (const id of [...FIN_ROLES.map((d) => d.id), 'trader']) {
|
|
13068
13294
|
if (p.roleSkills[id] === undefined) continue;
|
|
13069
13295
|
f.roleSkills[id] = (Array.isArray(p.roleSkills[id]) ? p.roleSkills[id] : [])
|
|
13070
|
-
.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
13296
|
+
.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 2);
|
|
13071
13297
|
}
|
|
13072
13298
|
/* koşan rol ajanlarını da tazele — seçim sonraki turdan itibaren sistem
|
|
13073
13299
|
promptuna girer (ajanı durdurmaya gerek yok) */
|