beast-agent 2.63.1 → 2.63.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/bots.js +19 -1
- package/src/agent/engine.js +74 -14
- package/src/agent/financetools.js +60 -18
- package/src/agent/finrisk.js +16 -6
- package/src/agent/seeds/risk-yonetimi/SKILL.md +52 -52
- package/src/main.js +442 -22
- package/src/preload.js +1 -0
- package/src/renderer/i18n.js +2 -0
- package/src/renderer/index.html +18 -1
- package/src/renderer/renderer.js +344 -1
- package/src/renderer/style.css +115 -0
package/src/main.js
CHANGED
|
@@ -2356,6 +2356,7 @@ function botToolSet(cfg) {
|
|
|
2356
2356
|
'tool_request', /* CEPHANE: eksik aracı TOOL botuna yazdırma hakkı HER botta */
|
|
2357
2357
|
'channel_send', /* İLK MESAJ: allow listteki kişilere kendiliğinden yazma hakkı HER botta */
|
|
2358
2358
|
'agent_dm', /* BOTLAR ARASI DM: botlar birbirine iş atar/cevap verir (ör. Beast → Tool) */
|
|
2359
|
+
'skill', /* SKILL KATALOĞU: botlar SKILL.md gövdesini skill() ile okur (Tool botu: tool-yazma/mql5) */
|
|
2359
2360
|
]);
|
|
2360
2361
|
if (s.web_search) { set.add('web_search'); set.add('http_fetch'); set.add('webfetch'); set.add('deep_search'); }
|
|
2361
2362
|
if (s.browser) {
|
|
@@ -2363,7 +2364,7 @@ function botToolSet(cfg) {
|
|
|
2363
2364
|
}
|
|
2364
2365
|
if (s.email) { set.add('email_list'); set.add('email_read'); set.add('email_send'); }
|
|
2365
2366
|
if (s.run_command) {
|
|
2366
|
-
for (const t of ['run_command', 'python_run', 'read_file', 'write_file', 'list_dir', 'computer_look', 'computer_act',
|
|
2367
|
+
for (const t of ['run_command', 'python_run', 'read_file', 'write_file', 'edit_file', 'grep', 'glob', 'list_dir', 'computer_look', 'computer_act',
|
|
2367
2368
|
'git_commit', 'git_diff_review', 'git_pr_create', 'repo_map', 'repo_symbols', 'xlsx_read', 'xlsx_write', 'xlsx_edit']) set.add(t);
|
|
2368
2369
|
}
|
|
2369
2370
|
if (s.memory) { set.add('memory_write'); set.add('user_write'); set.add('memory_search'); set.add('memory_hygiene'); }
|
|
@@ -4323,6 +4324,43 @@ function detachBrowser() {
|
|
|
4323
4324
|
browser.attached = false;
|
|
4324
4325
|
}
|
|
4325
4326
|
|
|
4327
|
+
/* TARAYICI YENİDEN BAŞLATMA: takılan/kilitlenen sayfa için view + webContents
|
|
4328
|
+
tamamen kapatılır, aynı URL YENİ bir view'da açılır. persist:browser
|
|
4329
|
+
partition'ı silinmez → oturum/çerezler (Google, X vb.) korunur. */
|
|
4330
|
+
function restartBrowser() {
|
|
4331
|
+
if (!win || win.isDestroyed()) return { ok: false, error: 'pencere yok' };
|
|
4332
|
+
const wasOpen = browser.open;
|
|
4333
|
+
let url = '';
|
|
4334
|
+
try {
|
|
4335
|
+
if (browser.view && browser.view.webContents && !browser.view.webContents.isDestroyed()) {
|
|
4336
|
+
url = browser.view.webContents.getURL();
|
|
4337
|
+
}
|
|
4338
|
+
} catch {}
|
|
4339
|
+
detachBrowser();
|
|
4340
|
+
try {
|
|
4341
|
+
const oldWc = browser.view && browser.view.webContents;
|
|
4342
|
+
if (oldWc && !oldWc.isDestroyed()) {
|
|
4343
|
+
try { oldWc.close(); } catch { try { oldWc.destroy(); } catch {} }
|
|
4344
|
+
/* takılı renderer süreci close()'u geciktirebilir — güvence: kısa süre
|
|
4345
|
+
sonra hâlâ duruyorsa zorla yok et (yeni view'ı asla kilitlemez) */
|
|
4346
|
+
setTimeout(() => { try { if (!oldWc.isDestroyed()) oldWc.destroy(); } catch {} }, 1500);
|
|
4347
|
+
}
|
|
4348
|
+
} catch {}
|
|
4349
|
+
browser.view = null;
|
|
4350
|
+
browser.started = false;
|
|
4351
|
+
browser.lastDialog = null;
|
|
4352
|
+
if (wasOpen) {
|
|
4353
|
+
ensureBrowser();
|
|
4354
|
+
browser.started = true;
|
|
4355
|
+
browser.view.webContents.loadURL(url || BROWSER_START_URL).catch(() => {});
|
|
4356
|
+
layoutBrowser();
|
|
4357
|
+
const [w] = win.getContentSize();
|
|
4358
|
+
browserEmit({ open: true, width: browserShownWidth(w), url: url || BROWSER_START_URL, restarted: true });
|
|
4359
|
+
}
|
|
4360
|
+
blog('restart', 'tarayıcı yeniden başlatıldı' + (url ? ' — ' + url.slice(0, 90) : ''));
|
|
4361
|
+
return { ok: true, url };
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4326
4364
|
function setBrowserOpen(v, forceVisible) {
|
|
4327
4365
|
if (!win || win.isDestroyed()) return;
|
|
4328
4366
|
const want = !!v;
|
|
@@ -8399,6 +8437,7 @@ ipcMain.handle('browser:ctrl', (_e, action) => {
|
|
|
8399
8437
|
setBrowserOpen(false);
|
|
8400
8438
|
return { ok: true };
|
|
8401
8439
|
}
|
|
8440
|
+
if (action === 'restart') return restartBrowser();
|
|
8402
8441
|
const wc = browser.view && browser.view.webContents;
|
|
8403
8442
|
if (!wc) return { ok: false };
|
|
8404
8443
|
try {
|
|
@@ -9371,6 +9410,33 @@ function financeDir() {
|
|
|
9371
9410
|
return d;
|
|
9372
9411
|
}
|
|
9373
9412
|
|
|
9413
|
+
/* SEMBOL BAZLI LOT LİMİTLERİ: trader mt5_limits symbol ile koyar; anahtar =
|
|
9414
|
+
sembol (XAUUSD…). Genel aralık taban/tavan olarak zorlanır — sembol limiti
|
|
9415
|
+
onun dışına çıkamaz; boş alan genel aralığı kullanır. Her okuma/yazmada
|
|
9416
|
+
normalize edilir (bozuk/eski kayıtlar temizlenir). */
|
|
9417
|
+
function finNormalizeSymbolLimits(f) {
|
|
9418
|
+
const gMin = Number(f.minLot) || 0.01;
|
|
9419
|
+
const gMax = Number(f.maxLot) || 0.1;
|
|
9420
|
+
if (!f.symbolLimits || typeof f.symbolLimits !== 'object') f.symbolLimits = {};
|
|
9421
|
+
const clean = {};
|
|
9422
|
+
for (const [k, v] of Object.entries(f.symbolLimits).slice(0, 40)) {
|
|
9423
|
+
const sym = String(k || '').trim().toUpperCase();
|
|
9424
|
+
const o = v && typeof v === 'object' ? v : {};
|
|
9425
|
+
if (!sym) continue;
|
|
9426
|
+
let mn = Number(o.minLot) > 0 ? Math.round(Math.max(0.01, Math.min(100, Number(o.minLot))) * 100) / 100 : 0;
|
|
9427
|
+
let mx = Number(o.maxLot) > 0 ? Math.round(Math.max(0.01, Math.min(100, Number(o.maxLot))) * 100) / 100 : 0;
|
|
9428
|
+
if (mn && mx && mn > mx) mn = mx;
|
|
9429
|
+
if (mn) mn = Math.min(Math.max(mn, gMin), gMax);
|
|
9430
|
+
if (mx) mx = Math.max(Math.min(mx, gMax), gMin);
|
|
9431
|
+
if (mn && mx && mn > mx) mn = mx;
|
|
9432
|
+
const lim = {};
|
|
9433
|
+
if (mn) lim.minLot = mn;
|
|
9434
|
+
if (mx) lim.maxLot = mx;
|
|
9435
|
+
if (lim.minLot || lim.maxLot) clean[sym] = lim;
|
|
9436
|
+
}
|
|
9437
|
+
f.symbolLimits = clean;
|
|
9438
|
+
}
|
|
9439
|
+
|
|
9374
9440
|
function finCfg() {
|
|
9375
9441
|
if (!settings.finance || typeof settings.finance !== 'object') settings.finance = {};
|
|
9376
9442
|
const f = settings.finance;
|
|
@@ -9409,8 +9475,10 @@ function finCfg() {
|
|
|
9409
9475
|
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
9410
9476
|
if (!Number(f.minLot)) f.minLot = 0.01;
|
|
9411
9477
|
if (!Number(f.maxLot)) f.maxLot = 0.1;
|
|
9412
|
-
/* LOT ARALIĞI
|
|
9478
|
+
/* LOT ARALIĞI KİLİDİ: min lot hiçbir zaman max lotu aşamaz */
|
|
9413
9479
|
if (f.minLot > f.maxLot) f.minLot = f.maxLot;
|
|
9480
|
+
/* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı — mt5_limits symbol ile koyar) */
|
|
9481
|
+
finNormalizeSymbolLimits(f);
|
|
9414
9482
|
if (f.maxPositions == null) f.maxPositions = 3;
|
|
9415
9483
|
/* KODLA DİSİPLİN (trader kuralları): hepsi 0 = kural kapalı */
|
|
9416
9484
|
if (!Number.isFinite(Number(f.maxTradesPerDay))) f.maxTradesPerDay = 10;
|
|
@@ -11209,6 +11277,8 @@ function finLimitsSnapshot() {
|
|
|
11209
11277
|
maxLot: Number(f.maxLot) || 0.1,
|
|
11210
11278
|
maxPositions: Number(f.maxPositions) || 3,
|
|
11211
11279
|
riskPerTradePct: Number(f.riskPerTradePct) || 0,
|
|
11280
|
+
/* SEMBOL BAZLI LOT LİMİTLERİ (trader kararı) — genel aralığı daraltır */
|
|
11281
|
+
symbols: { ...(f.symbolLimits && typeof f.symbolLimits === 'object' ? f.symbolLimits : {}) },
|
|
11212
11282
|
};
|
|
11213
11283
|
}
|
|
11214
11284
|
|
|
@@ -11223,21 +11293,22 @@ function finLimitsSessionAllowed(sid) {
|
|
|
11223
11293
|
return true;
|
|
11224
11294
|
}
|
|
11225
11295
|
|
|
11226
|
-
function finSyncLimitsToSessions() {
|
|
11296
|
+
function finSyncLimitsToSessions(extra) {
|
|
11227
11297
|
const lim = finLimitsSnapshot();
|
|
11298
|
+
const apply = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions, symbolLimits: lim.symbols };
|
|
11228
11299
|
if (engine) {
|
|
11229
11300
|
try {
|
|
11230
11301
|
for (const [sid] of financeState.agents) {
|
|
11231
11302
|
const s = engine.cache.get(String(sid));
|
|
11232
|
-
if (s) s.financeLimits = {
|
|
11303
|
+
if (s) s.financeLimits = { ...apply };
|
|
11233
11304
|
}
|
|
11234
11305
|
for (const [, s] of engine.cache) {
|
|
11235
11306
|
if (!s || !s.finance || s.bgJob || !s.financeLimits) continue;
|
|
11236
|
-
s.financeLimits = {
|
|
11307
|
+
s.financeLimits = { ...apply };
|
|
11237
11308
|
}
|
|
11238
11309
|
} catch {}
|
|
11239
11310
|
}
|
|
11240
|
-
finPush('limits', { ...lim });
|
|
11311
|
+
finPush('limits', { ...lim, ...(extra && typeof extra === 'object' ? extra : {}) });
|
|
11241
11312
|
}
|
|
11242
11313
|
|
|
11243
11314
|
function finLimitsSet(patch, ctx) {
|
|
@@ -11247,6 +11318,54 @@ function finLimitsSet(patch, ctx) {
|
|
|
11247
11318
|
}
|
|
11248
11319
|
const f = finCfg();
|
|
11249
11320
|
const notes = [];
|
|
11321
|
+
/* SEMBOL BAZLI LOT LİMİTİ (symbol verilirse): trader sembole göre min/max lot
|
|
11322
|
+
koyar — genel aralık taban/tavan olarak zorlanır. reset:true (ya da yalnız
|
|
11323
|
+
symbol) → sembol limiti kaldırılır, genel aralık geçerli olur. */
|
|
11324
|
+
const sym = String(patch.symbol || '').trim().toUpperCase();
|
|
11325
|
+
if (sym) {
|
|
11326
|
+
if (patch.maxPositions !== undefined) {
|
|
11327
|
+
return { ok: false, error: 'maxPositions sembol bazlı değil — symbol vermeden genel limit olarak ayarla' };
|
|
11328
|
+
}
|
|
11329
|
+
const hasMin = patch.minLot !== undefined && patch.minLot !== null && patch.minLot !== '';
|
|
11330
|
+
const hasMax = patch.maxLot !== undefined && patch.maxLot !== null && patch.maxLot !== '';
|
|
11331
|
+
const reset = patch.reset === true || String(patch.reset || '').toLowerCase() === 'true';
|
|
11332
|
+
if (!reset && !hasMin && !hasMax) {
|
|
11333
|
+
return { ok: false, error: 'sembol güncellemesi için minLot/maxLot ver ya da reset:true' };
|
|
11334
|
+
}
|
|
11335
|
+
if (!f.symbolLimits || typeof f.symbolLimits !== 'object') f.symbolLimits = {};
|
|
11336
|
+
if (reset) {
|
|
11337
|
+
if (f.symbolLimits[sym]) {
|
|
11338
|
+
delete f.symbolLimits[sym];
|
|
11339
|
+
notes.push('sembol limiti kaldırıldı → genel aralık geçerli');
|
|
11340
|
+
}
|
|
11341
|
+
} else {
|
|
11342
|
+
const cur = { ...(f.symbolLimits[sym] || {}) };
|
|
11343
|
+
if (hasMin) {
|
|
11344
|
+
const v = Number(patch.minLot);
|
|
11345
|
+
if (!(v >= 0.01) || v > 100) return { ok: false, error: 'sembol minLot 0.01-100 arasında olmalı' };
|
|
11346
|
+
cur.minLot = Math.round(v * 100) / 100;
|
|
11347
|
+
}
|
|
11348
|
+
if (hasMax) {
|
|
11349
|
+
const v = Number(patch.maxLot);
|
|
11350
|
+
if (!(v >= 0.01) || v > 100) return { ok: false, error: 'sembol maxLot 0.01-100 arasında olmalı' };
|
|
11351
|
+
cur.maxLot = Math.round(v * 100) / 100;
|
|
11352
|
+
}
|
|
11353
|
+
/* genel aralığa kelepçe: sembol limiti geneli AŞAMAZ/ALTA İNEMEZ */
|
|
11354
|
+
if (cur.minLot !== undefined && cur.minLot < f.minLot) { cur.minLot = f.minLot; notes.push(`minLot genel tabana kelepçelendi (${f.minLot})`); }
|
|
11355
|
+
if (cur.maxLot !== undefined && cur.maxLot > f.maxLot) { cur.maxLot = f.maxLot; notes.push(`maxLot genel tavana kelepçelendi (${f.maxLot})`); }
|
|
11356
|
+
if (cur.minLot !== undefined && cur.maxLot !== undefined && cur.minLot > cur.maxLot) { cur.minLot = cur.maxLot; notes.push(`minLot maxLot'a kelepçelendi (${cur.maxLot})`); }
|
|
11357
|
+
if (cur.minLot === undefined && cur.maxLot === undefined) delete f.symbolLimits[sym];
|
|
11358
|
+
else f.symbolLimits[sym] = cur;
|
|
11359
|
+
}
|
|
11360
|
+
try { saveSettings(); } catch {}
|
|
11361
|
+
const e = f.symbolLimits[sym];
|
|
11362
|
+
const line = `⚙️ SEMBOL LOT LİMİTİ: ${sym} → ${e ? `min ${e.minLot ?? f.minLot} · max ${e.maxLot ?? f.maxLot}` : 'genel aralığa döndü'}${sid ? ' (ajan kararı)' : ''}`;
|
|
11363
|
+
financeLog('[limit] ' + line);
|
|
11364
|
+
finSyncLimitsToSessions({ symbol: sym, note: line });
|
|
11365
|
+
financeNotify(line, 'limit', true);
|
|
11366
|
+
try { finJournal({ kind: 'limits', symbol: sym, minLot: e ? e.minLot : null, maxLot: e ? e.maxLot : null, sid }); } catch {}
|
|
11367
|
+
return { ok: true, symbol: sym, limits: finLimitsSnapshot(), note: notes.length ? notes.join('; ') : undefined };
|
|
11368
|
+
}
|
|
11250
11369
|
if (patch.maxLot !== undefined) {
|
|
11251
11370
|
const v = Number(patch.maxLot);
|
|
11252
11371
|
if (!(v >= 0.01) || v > 100) return { ok: false, error: 'maxLot 0.01-100 arasında olmalı' };
|
|
@@ -11280,8 +11399,25 @@ function finLimitsSet(patch, ctx) {
|
|
|
11280
11399
|
kazanç/kayıp, net, MFE/MAE, kâr geri verme) (2) ajanın yazdığı dersler.
|
|
11281
11400
|
Trader turunda bu özet sistem promptuna gömülür — sistem kendi geçmişinden
|
|
11282
11401
|
öğrenir; her kapanış otomatik işlenir, dersleri ajan mt5_ogrenme ile yazar. */
|
|
11283
|
-
const FIN_LEARN_MAX_NOTES =
|
|
11402
|
+
const FIN_LEARN_MAX_NOTES = 60; /* sembol başına ders tavanı */
|
|
11284
11403
|
const FIN_LEARN_MAX_TRADES = 60;
|
|
11404
|
+
const FIN_LEARN_MAX_SYMBOLS = 60; /* toplam sembol tavanı — depo sınırsız büyümesin */
|
|
11405
|
+
const FIN_LEARN_MAX_NOTES_DAY = 3; /* sembol başına 24 saatte en fazla ders — ders SELİ depoyu zehirlemesin */
|
|
11406
|
+
/* DERS KALİTESİ: "günlük tutar gibi" uzun/genel metinler ders DEĞİLDİR —
|
|
11407
|
+
yalnız kısa, somut, tekrar kullanılabilir bilgi kaydedilir. */
|
|
11408
|
+
const FIN_LEARN_NOTE_MIN = 12; /* çok kısa/genel laf (ör. "iyi işlem") ders sayılmaz */
|
|
11409
|
+
const FIN_LEARN_NOTE_MAX = 200; /* tek kısa cümle tavanı (karakter) */
|
|
11410
|
+
/* SÜREKLİ UNUTMA (bellek hijyeni): eski kayıtlar otomatik düşer — depo büyüyüp
|
|
11411
|
+
zehirlenmez, yalnız GÜNCEL dersler ve son performans kalır. */
|
|
11412
|
+
const FIN_LEARN_NOTE_TTL_MS = 30 * 24 * 60 * 60 * 1000; /* 30 gün: eski ders unutulur */
|
|
11413
|
+
const FIN_LEARN_TRADE_TTL_MS = 90 * 24 * 60 * 60 * 1000; /* 90 gün: eski işlem kaydı düşer */
|
|
11414
|
+
const FIN_LEARN_SYMBOL_TTL_MS = 120 * 24 * 60 * 60 * 1000; /* 120 gün aktivite yoksa sembol kaydı silinir */
|
|
11415
|
+
/* ÖZETLEME (opencode tarzı compaction): ham dersler birikince ESKİ olanlar
|
|
11416
|
+
modele özetlettirilir → tek kalıcı özet; ham yalnız son dersler kalır. */
|
|
11417
|
+
const FIN_LEARN_COMPACT_AT = 24; /* not sayısı bunu aşınca arka planda otomatik özetle */
|
|
11418
|
+
const FIN_LEARN_KEEP_RECENT = 8; /* ham kalan son ders sayısı */
|
|
11419
|
+
const FIN_LEARN_SUMMARY_MAX = 800; /* kalıcı özet metin tavanı (karakter) */
|
|
11420
|
+
const FIN_LEARN_COMPACT_MIN = 4; /* elle özetleme için gereken en az eski ders */
|
|
11285
11421
|
|
|
11286
11422
|
function finLearnEmptyStats() {
|
|
11287
11423
|
return {
|
|
@@ -11291,6 +11427,84 @@ function finLearnEmptyStats() {
|
|
|
11291
11427
|
};
|
|
11292
11428
|
}
|
|
11293
11429
|
|
|
11430
|
+
/* Ders metni karşılaştırma anahtarı: büyük/küçük harf, noktalama ve boşluk
|
|
11431
|
+
farkları aynı ders sayılır — ajan aynı cümleyi kopyalayıp depoyu doldurmasın */
|
|
11432
|
+
function finLearnNoteKey(text) {
|
|
11433
|
+
return String(text || '')
|
|
11434
|
+
.toLowerCase()
|
|
11435
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, ' ')
|
|
11436
|
+
.replace(/\s+/g, ' ')
|
|
11437
|
+
.trim()
|
|
11438
|
+
.slice(0, 140);
|
|
11439
|
+
}
|
|
11440
|
+
|
|
11441
|
+
/* DERS KALİTE KAPISI: gerçekten öğrenilmiş, işe yarar bilgi kısa cümleyle;
|
|
11442
|
+
günlük/özet tarzı metin ya da genel laf kaydedilmez. Hata metni ya da null. */
|
|
11443
|
+
function finLearnNoteQuality(text) {
|
|
11444
|
+
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
11445
|
+
if (t.length < FIN_LEARN_NOTE_MIN) return 'ders çok kısa — tek cümlede somut ders yaz (ör. "Londra açılışı M5 EMA50 üstü momentum işledi")';
|
|
11446
|
+
if (t.length > FIN_LEARN_NOTE_MAX) return `ders çok uzun (${t.length} karakter) — en fazla ${FIN_LEARN_NOTE_MAX} karakterlik TEK kısa cümle yaz; günlük/rapor metnini ders diye kaydetme`;
|
|
11447
|
+
if (/^(bug[üu]n|g[üu]nl[üu]k|bu sabah|bu ak[şs]am|sabah|ak[şs]am)\b/i.test(t)) return 'günlük tarzı metin ders değildir — yalnız tekrar kullanılabilir somut ders yaz';
|
|
11448
|
+
/* GENEL LAFLAR: kanıtsız/tavsiye cümleleri ders değildir (somut setup/ölçüm iste) */
|
|
11449
|
+
if (/(dikkatli ol|dikkat et|sab[ıi]rl[ıi] ol|panik yapma|duygular[ıi]na kap[ıi]lma|disiplinli ol|temkinli ol)/i.test(t)) {
|
|
11450
|
+
return 'genel tavsiye ders değildir — hangi setup/koşulda ne olduğunu somut yaz (ör. sembol+periyot+seviye+sonuç)';
|
|
11451
|
+
}
|
|
11452
|
+
return null;
|
|
11453
|
+
}
|
|
11454
|
+
|
|
11455
|
+
/* sembolün son aktivitesi (kapanış / ders / eski istatistik) — tavan aşımında
|
|
11456
|
+
EN ESKİ semboller düşürülür; dokunulan (protectSym) sembol asla atılmaz */
|
|
11457
|
+
function finLearnActivity(e) {
|
|
11458
|
+
const last = (arr) => (Array.isArray(arr) && arr.length ? Number(arr[arr.length - 1].at) || 0 : 0);
|
|
11459
|
+
return Math.max(Number((e.stats || {}).lastAt) || 0, last(e.notes), last(e.trades));
|
|
11460
|
+
}
|
|
11461
|
+
|
|
11462
|
+
function finLearnPrune(learn, keep, protectSym) {
|
|
11463
|
+
const cap = Number.isFinite(Number(keep)) ? Number(keep) : FIN_LEARN_MAX_SYMBOLS;
|
|
11464
|
+
const keys = Object.keys(learn.symbols);
|
|
11465
|
+
if (keys.length <= cap) return 0;
|
|
11466
|
+
const protect = String(protectSym || '').trim().toUpperCase();
|
|
11467
|
+
const dropped = keys
|
|
11468
|
+
.sort((a, b) => {
|
|
11469
|
+
if (a === protect) return 1;
|
|
11470
|
+
if (b === protect) return -1;
|
|
11471
|
+
return finLearnActivity(learn.symbols[a]) - finLearnActivity(learn.symbols[b]);
|
|
11472
|
+
})
|
|
11473
|
+
.slice(0, keys.length - cap);
|
|
11474
|
+
for (const k of dropped) delete learn.symbols[k];
|
|
11475
|
+
return dropped.length;
|
|
11476
|
+
}
|
|
11477
|
+
|
|
11478
|
+
/* SÜREKLİ UNUTMA: TTL'i geçen ders/işlem kayıtları düşer; uzun süre hiç
|
|
11479
|
+
aktivite görmeyen sembol kaydı (istatistik dahil) silinir. Her yazma ve
|
|
11480
|
+
yüklemede çalışır → depo kendiliğinden sade kalır, birikme/zehirlenme olmaz. */
|
|
11481
|
+
function finLearnForget(learn, now) {
|
|
11482
|
+
const t = Number(now) || Date.now();
|
|
11483
|
+
const stat = { notes: 0, trades: 0, symbols: 0 };
|
|
11484
|
+
for (const [sym, e] of Object.entries(learn.symbols)) {
|
|
11485
|
+
if (!e || typeof e !== 'object') { delete learn.symbols[sym]; stat.symbols++; continue; }
|
|
11486
|
+
const notes = Array.isArray(e.notes) ? e.notes : [];
|
|
11487
|
+
e.notes = notes.filter((n) => n && t - (Number(n.at) || 0) <= FIN_LEARN_NOTE_TTL_MS);
|
|
11488
|
+
stat.notes += notes.length - e.notes.length;
|
|
11489
|
+
const trades = Array.isArray(e.trades) ? e.trades : [];
|
|
11490
|
+
e.trades = trades.filter((x) => x && t - (Number(x.at) || 0) <= FIN_LEARN_TRADE_TTL_MS);
|
|
11491
|
+
stat.trades += trades.length - e.trades.length;
|
|
11492
|
+
if (t - finLearnActivity(e) > FIN_LEARN_SYMBOL_TTL_MS) {
|
|
11493
|
+
delete learn.symbols[sym];
|
|
11494
|
+
stat.symbols += 1;
|
|
11495
|
+
}
|
|
11496
|
+
}
|
|
11497
|
+
return stat;
|
|
11498
|
+
}
|
|
11499
|
+
|
|
11500
|
+
/* TEK TEMİZLİK: önce TTL unutması, sonra sembol tavanı. protectSym (yeni
|
|
11501
|
+
dokunulan sembol) asla düşürülmez. */
|
|
11502
|
+
function finLearnTidy(learn, protectSym, now) {
|
|
11503
|
+
const forgot = finLearnForget(learn, now);
|
|
11504
|
+
const cap = finLearnPrune(learn, FIN_LEARN_MAX_SYMBOLS, protectSym);
|
|
11505
|
+
return { notes: forgot.notes, trades: forgot.trades, symbols: forgot.symbols + cap };
|
|
11506
|
+
}
|
|
11507
|
+
|
|
11294
11508
|
function finLearnLoad() {
|
|
11295
11509
|
if (financeState.learnReady) return financeState.learn;
|
|
11296
11510
|
financeState.learnReady = true;
|
|
@@ -11305,21 +11519,128 @@ function finLearnLoad() {
|
|
|
11305
11519
|
notes: Array.isArray(v.notes) ? v.notes.filter((n) => n && n.id && n.text).slice(-FIN_LEARN_MAX_NOTES) : [],
|
|
11306
11520
|
trades: Array.isArray(v.trades) ? v.trades.filter(Boolean).slice(-FIN_LEARN_MAX_TRADES) : [],
|
|
11307
11521
|
stats: v.stats && typeof v.stats === 'object' ? { ...finLearnEmptyStats(), ...v.stats } : finLearnEmptyStats(),
|
|
11522
|
+
/* KALICI ÖZET (opencode tarzı sıkıştırma): eski derslerin modele
|
|
11523
|
+
özetlettirilmiş hâli — ham dersler düşse de bilgi kalır */
|
|
11524
|
+
summary: v.summary && typeof v.summary === 'object' && String(v.summary.text || '').trim()
|
|
11525
|
+
? {
|
|
11526
|
+
text: String(v.summary.text).replace(/\s+/g, ' ').trim().slice(0, FIN_LEARN_SUMMARY_MAX),
|
|
11527
|
+
at: Number(v.summary.at) || 0,
|
|
11528
|
+
covered: Math.max(0, Math.round(Number(v.summary.covered) || 0)),
|
|
11529
|
+
}
|
|
11530
|
+
: null,
|
|
11308
11531
|
};
|
|
11309
11532
|
}
|
|
11310
11533
|
if (Number(raw.updatedAt)) learn.updatedAt = Number(raw.updatedAt);
|
|
11311
11534
|
}
|
|
11312
11535
|
} catch {}
|
|
11536
|
+
const dropped = finLearnTidy(learn); /* yükleme anında unut + tavan uygula */
|
|
11313
11537
|
financeState.learn = learn;
|
|
11538
|
+
if (dropped.notes || dropped.trades || dropped.symbols) finLearnSave(); /* disk de hemen küçülsün */
|
|
11314
11539
|
return learn;
|
|
11315
11540
|
}
|
|
11316
11541
|
|
|
11317
11542
|
function finLearnSave() {
|
|
11318
11543
|
const learn = finLearnLoad();
|
|
11544
|
+
finLearnTidy(learn); /* her yazmada sürekli unutma */
|
|
11319
11545
|
learn.updatedAt = Date.now();
|
|
11320
11546
|
try { finWriteJson(finFile('ogrenme.json'), learn); } catch {}
|
|
11321
11547
|
}
|
|
11322
11548
|
|
|
11549
|
+
/* ---------- OPENCODE TARZI ÖZETLEME (COMPACTION) ----------
|
|
11550
|
+
Ham dersler birikince ESKİ dersler modele özetlettirilir: çalışan desenler,
|
|
11551
|
+
kaçınılacak hatalar, kurallar tek KALICI ÖZET'e iner; ham olarak yalnız son
|
|
11552
|
+
dersler kalır. Böylece öğrenme hafızası hem küçük hem zehirsiz kalır —
|
|
11553
|
+
context penceresi sıkıştırmasının sembol bazlı hâli. */
|
|
11554
|
+
const FIN_LEARN_COMPACT_SYSTEM =
|
|
11555
|
+
'Sen bir trading mentorüsün. Sana bir sembolün ESKİ ders notları ve mevcut özeti verilir. ' +
|
|
11556
|
+
'Görevin: güncellenmiş KISA bir özet üretmek.\n' +
|
|
11557
|
+
'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 ' +
|
|
11559
|
+
'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. ' +
|
|
11562
|
+
'(6) Çıktı SADECE özet metni olsun; başlık/etiket/açıklama yazma.';
|
|
11563
|
+
|
|
11564
|
+
const finLearnCompacting = new Set();
|
|
11565
|
+
|
|
11566
|
+
function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
11567
|
+
const st = { ...finLearnEmptyStats(), ...(e.stats || {}) };
|
|
11568
|
+
const wr = st.trades ? Math.round((st.wins / st.trades) * 100) : 0;
|
|
11569
|
+
const lines = [];
|
|
11570
|
+
lines.push('SEMBOL: ' + sym);
|
|
11571
|
+
lines.push(
|
|
11572
|
+
`OTOMATİK İSTATİSTİK: ${st.trades} işlem · %${wr} kazanç · net ${st.net >= 0 ? '+' : ''}${st.net}` +
|
|
11573
|
+
(st.givebacks ? ` · ${st.givebacks} kez kâr geri verildi` : '') +
|
|
11574
|
+
(st.streak >= 2 ? ` · ${st.streak} ardışık kayıp` : '')
|
|
11575
|
+
);
|
|
11576
|
+
if (e.summary && e.summary.text) lines.push('MEVCUT ÖZET:\n' + String(e.summary.text).slice(0, 1400));
|
|
11577
|
+
lines.push('ESKİ DERSLER (kronolojik — en yeni EN SONDA):');
|
|
11578
|
+
for (const n of oldNotes.slice(-80)) {
|
|
11579
|
+
lines.push(`- [${String(n.kind || 'observation')}] ${String(n.text || '').slice(0, 220)}`);
|
|
11580
|
+
}
|
|
11581
|
+
lines.push('Görev: yalnız kalıcı ve güncel dersleri koruyan yeni özet metnini yaz.');
|
|
11582
|
+
return lines.join('\n');
|
|
11583
|
+
}
|
|
11584
|
+
|
|
11585
|
+
/* Bir sembolü özetle: snapshot'taki ESKİ dersleri modele özetlet, onları düş,
|
|
11586
|
+
yerine kalıcı özet yaz. Model çalışırken gelen YENİ dersler ham kalır. */
|
|
11587
|
+
async function finLearnCompactSymbol(symbol, force) {
|
|
11588
|
+
const sym = String(symbol || '').trim().toUpperCase();
|
|
11589
|
+
if (!sym) return { ok: false, error: 'symbol gerekli' };
|
|
11590
|
+
if (finLearnCompacting.has(sym)) return { ok: false, error: 'bu sembol şu an özetleniyor' };
|
|
11591
|
+
if (!engine || !engine.sel) return { ok: false, error: 'özetleme için aktif model yok' };
|
|
11592
|
+
const e = finLearnLoad().symbols[sym];
|
|
11593
|
+
if (!e) return { ok: false, error: 'sembol kaydı yok: ' + sym };
|
|
11594
|
+
const oldNotes = e.notes.slice(0, Math.max(0, e.notes.length - FIN_LEARN_KEEP_RECENT));
|
|
11595
|
+
const min = force ? FIN_LEARN_COMPACT_MIN : FIN_LEARN_COMPACT_AT - FIN_LEARN_KEEP_RECENT;
|
|
11596
|
+
if (oldNotes.length < min) {
|
|
11597
|
+
return { ok: false, error: `özetlenecek yeterli eski ders yok (${oldNotes.length}/${min})` };
|
|
11598
|
+
}
|
|
11599
|
+
finLearnCompacting.add(sym);
|
|
11600
|
+
try {
|
|
11601
|
+
const ctrl = new AbortController();
|
|
11602
|
+
const kill = setTimeout(() => ctrl.abort(), 60000);
|
|
11603
|
+
let r = null;
|
|
11604
|
+
try {
|
|
11605
|
+
r = await require('./agent/llm').chatOnce(engine.sel, {
|
|
11606
|
+
messages: [
|
|
11607
|
+
{ role: 'system', content: FIN_LEARN_COMPACT_SYSTEM },
|
|
11608
|
+
{ role: 'user', content: finLearnSummaryPrompt(sym, e, oldNotes) },
|
|
11609
|
+
],
|
|
11610
|
+
temperature: 0.2,
|
|
11611
|
+
}, { signal: ctrl.signal });
|
|
11612
|
+
} catch (err) {
|
|
11613
|
+
return { ok: false, error: 'özetleme çağrısı başarısız: ' + String((err && err.message) || err).slice(0, 120) };
|
|
11614
|
+
} finally {
|
|
11615
|
+
clearTimeout(kill);
|
|
11616
|
+
}
|
|
11617
|
+
const text = String((r && r.content) || '').replace(/\s+/g, ' ').trim().slice(0, FIN_LEARN_SUMMARY_MAX);
|
|
11618
|
+
if (!text) return { ok: false, error: 'özet üretilemedi (model yanıtı boş)' };
|
|
11619
|
+
/* yarışma koruması: yalnız snapshot'taki eski dersleri düşür */
|
|
11620
|
+
const fresh = finLearnLoad().symbols[sym];
|
|
11621
|
+
if (!fresh) return { ok: false, error: 'sembol kaydı kalmadı' };
|
|
11622
|
+
const drop = new Set(oldNotes.map((n) => String(n.id)));
|
|
11623
|
+
const before = fresh.notes.length;
|
|
11624
|
+
fresh.notes = fresh.notes.filter((n) => !drop.has(String(n.id)));
|
|
11625
|
+
const covered = before - fresh.notes.length;
|
|
11626
|
+
fresh.summary = {
|
|
11627
|
+
text,
|
|
11628
|
+
at: Date.now(),
|
|
11629
|
+
covered: (Number(fresh.summary && fresh.summary.covered) || 0) + covered,
|
|
11630
|
+
};
|
|
11631
|
+
finLearnSave();
|
|
11632
|
+
financeLog(`[öğrenme] ${sym} özetlendi: ${covered} eski ders → kalıcı özet (${text.length} karakter)`);
|
|
11633
|
+
return { ok: true, symbol: sym, covered, remaining: fresh.notes.length, summary: text };
|
|
11634
|
+
} finally {
|
|
11635
|
+
finLearnCompacting.delete(sym);
|
|
11636
|
+
}
|
|
11637
|
+
}
|
|
11638
|
+
|
|
11639
|
+
/* Arka plan tetikleyici: ajanı BEKLETMEDEN özetler (notlar birikince) */
|
|
11640
|
+
function finLearnCompactAuto(sym) {
|
|
11641
|
+
try { finLearnCompactSymbol(sym, false).catch(() => {}); } catch {}
|
|
11642
|
+
}
|
|
11643
|
+
|
|
11323
11644
|
function finLearnSym(symbol) {
|
|
11324
11645
|
const sym = String(symbol || '').trim().toUpperCase();
|
|
11325
11646
|
if (!sym) return null;
|
|
@@ -11367,6 +11688,7 @@ function finLearnRecordClose({ symbol, side, net, mfe, mae, reason }) {
|
|
|
11367
11688
|
reason: String(reason || '').slice(0, 40),
|
|
11368
11689
|
});
|
|
11369
11690
|
while (e.trades.length > FIN_LEARN_MAX_TRADES) e.trades.shift();
|
|
11691
|
+
finLearnTidy(finLearnLoad(), symbol);
|
|
11370
11692
|
finLearnSave();
|
|
11371
11693
|
} catch {}
|
|
11372
11694
|
}
|
|
@@ -11389,6 +11711,11 @@ function finBuildLearnDigest(symbols) {
|
|
|
11389
11711
|
if (st.lastAt) parts.push(`son: ${st.lastNet >= 0 ? '+' : ''}${st.lastNet}`);
|
|
11390
11712
|
L.push(`- ${sym}: ${parts.join(' · ')}`);
|
|
11391
11713
|
}
|
|
11714
|
+
/* KALICI ÖZET (compaction): eski derslerin sıkıştırılmış hâli — ham ders
|
|
11715
|
+
sayısı azalsa da birikmiş bilgi prompta girer */
|
|
11716
|
+
if (e.summary && e.summary.text) {
|
|
11717
|
+
L.push(` kalıcı özet: "${String(e.summary.text).slice(0, 500)}"`);
|
|
11718
|
+
}
|
|
11392
11719
|
if (e.notes.length) {
|
|
11393
11720
|
const last = e.notes[e.notes.length - 1];
|
|
11394
11721
|
const extra = e.notes.length > 1 ? ` (+${e.notes.length - 1} ders)` : '';
|
|
@@ -11401,19 +11728,21 @@ function finBuildLearnDigest(symbols) {
|
|
|
11401
11728
|
function finLearnApi() {
|
|
11402
11729
|
const norm = (s) => String(s || '').trim().toUpperCase();
|
|
11403
11730
|
return {
|
|
11404
|
-
list: ({ symbol } = {}) => {
|
|
11731
|
+
list: ({ symbol, limit } = {}) => {
|
|
11405
11732
|
const learn = finLearnLoad();
|
|
11406
11733
|
const sym = norm(symbol);
|
|
11734
|
+
const take = Math.max(1, Math.min(25, Math.round(Number(limit) || 8)));
|
|
11407
11735
|
if (sym) {
|
|
11408
11736
|
const e = learn.symbols[sym];
|
|
11409
|
-
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats() };
|
|
11737
|
+
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats(), summary: null };
|
|
11410
11738
|
return {
|
|
11411
11739
|
ok: true,
|
|
11412
11740
|
symbol: sym,
|
|
11413
11741
|
count: e.notes.length,
|
|
11414
|
-
notes: e.notes.slice(-
|
|
11742
|
+
notes: e.notes.slice(-take),
|
|
11415
11743
|
trades: e.trades.slice(-12),
|
|
11416
11744
|
stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
|
|
11745
|
+
summary: e.summary && e.summary.text ? e.summary : null,
|
|
11417
11746
|
};
|
|
11418
11747
|
}
|
|
11419
11748
|
const rows = Object.entries(learn.symbols)
|
|
@@ -11422,14 +11751,26 @@ function finLearnApi() {
|
|
|
11422
11751
|
notes: e.notes.length,
|
|
11423
11752
|
stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
|
|
11424
11753
|
lastNote: String(((e.notes[e.notes.length - 1] || {}).text) || ''),
|
|
11754
|
+
hasSummary: !!(e.summary && e.summary.text),
|
|
11425
11755
|
}))
|
|
11426
11756
|
.sort((a, b) => (b.stats.lastAt || 0) - (a.stats.lastAt || 0));
|
|
11427
|
-
return { ok: true, count: rows.length, symbols: rows.slice(0,
|
|
11757
|
+
return { ok: true, count: rows.length, symbols: rows.slice(0, FIN_LEARN_MAX_SYMBOLS) };
|
|
11428
11758
|
},
|
|
11429
11759
|
stats: ({ symbol } = {}) => {
|
|
11760
|
+
/* KOMPAKT OKUMA: istatistik + kalıcı özet + son 3 ders — ajan karar
|
|
11761
|
+
öncesi bunu okur (hafızanın TAMAMI değil, sadece güncel öz) */
|
|
11430
11762
|
const learn = finLearnLoad();
|
|
11431
11763
|
const sym = norm(symbol);
|
|
11432
|
-
if (sym)
|
|
11764
|
+
if (sym) {
|
|
11765
|
+
const e = learn.symbols[sym] || {};
|
|
11766
|
+
return {
|
|
11767
|
+
ok: true,
|
|
11768
|
+
symbol: sym,
|
|
11769
|
+
stats: { ...finLearnEmptyStats(), ...(e.stats || {}) },
|
|
11770
|
+
summary: e.summary && e.summary.text ? e.summary : null,
|
|
11771
|
+
notes: Array.isArray(e.notes) ? e.notes.slice(-3) : [],
|
|
11772
|
+
};
|
|
11773
|
+
}
|
|
11433
11774
|
const stats = {};
|
|
11434
11775
|
for (const [s, e] of Object.entries(learn.symbols)) stats[s] = { ...finLearnEmptyStats(), ...(e.stats || {}) };
|
|
11435
11776
|
return { ok: true, count: Object.keys(stats).length, stats };
|
|
@@ -11438,24 +11779,45 @@ function finLearnApi() {
|
|
|
11438
11779
|
const sym = norm(symbol);
|
|
11439
11780
|
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
11440
11781
|
if (!sym || !t) return { ok: false, error: 'symbol ve text gerekli' };
|
|
11782
|
+
/* KALİTE KAPISI: kısa + somut + tekrar kullanılabilir — günlük tutma */
|
|
11783
|
+
const q = finLearnNoteQuality(t);
|
|
11784
|
+
if (q) return { ok: false, error: q, symbol: sym };
|
|
11785
|
+
const learn = finLearnLoad();
|
|
11441
11786
|
const e = finLearnSym(sym);
|
|
11442
11787
|
if (!e) return { ok: false, error: 'sembol yok' };
|
|
11443
|
-
/* aynı ders tekrar yazılmasın (son
|
|
11444
|
-
|
|
11445
|
-
|
|
11788
|
+
/* aynı ders tekrar yazılmasın (son 40 kayıtta normalize metin) — kopyala/
|
|
11789
|
+
yapıştır ve noktalama oyunlarıyla depo şişirilemez */
|
|
11790
|
+
const key = finLearnNoteKey(t);
|
|
11791
|
+
if (key && e.notes.slice(-40).some((n) => finLearnNoteKey(n.text) === key)) {
|
|
11446
11792
|
return { ok: true, duplicate: true, symbol: sym, count: e.notes.length };
|
|
11447
11793
|
}
|
|
11794
|
+
/* GÜNLÜK DERS LİMİTİ: ajan aynı gün aynı sembole ders yağdıramaz —
|
|
11795
|
+
hafıza öğrenmeyle zehirlenmesin, özet son dersleri temsil etsin */
|
|
11796
|
+
const dayAgo = Date.now() - 24 * 60 * 60 * 1000;
|
|
11797
|
+
const today = e.notes.filter((n) => Number(n.at) >= dayAgo).length;
|
|
11798
|
+
if (today >= FIN_LEARN_MAX_NOTES_DAY) {
|
|
11799
|
+
return {
|
|
11800
|
+
ok: false,
|
|
11801
|
+
error: `günlük ders limiti doldu (${FIN_LEARN_MAX_NOTES_DAY}/24s) — ders kaydedilmedi`,
|
|
11802
|
+
symbol: sym,
|
|
11803
|
+
count: e.notes.length,
|
|
11804
|
+
};
|
|
11805
|
+
}
|
|
11448
11806
|
const note = {
|
|
11449
11807
|
id: 'l' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
11450
11808
|
at: Date.now(),
|
|
11451
|
-
text: t.slice(0,
|
|
11809
|
+
text: t.slice(0, FIN_LEARN_NOTE_MAX),
|
|
11452
11810
|
kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
|
|
11453
11811
|
tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
|
|
11454
11812
|
sid: String(sid || ''),
|
|
11455
11813
|
};
|
|
11456
11814
|
e.notes.push(note);
|
|
11457
11815
|
while (e.notes.length > FIN_LEARN_MAX_NOTES) e.notes.shift();
|
|
11816
|
+
finLearnTidy(learn, sym);
|
|
11458
11817
|
finLearnSave();
|
|
11818
|
+
/* OPENCODE TARZI COMPACTION: notlar birikince ESKİLERİ arka planda
|
|
11819
|
+
özetle — ajanı bekletmez, hafıza kendiliğinden sadeleşir */
|
|
11820
|
+
if (e.notes.length > FIN_LEARN_COMPACT_AT) finLearnCompactAuto(sym);
|
|
11459
11821
|
return { ok: true, id: note.id, symbol: sym, count: e.notes.length };
|
|
11460
11822
|
},
|
|
11461
11823
|
remove: ({ id, symbol } = {}) => {
|
|
@@ -11497,6 +11859,31 @@ function finLearnApi() {
|
|
|
11497
11859
|
if (removed) finLearnSave();
|
|
11498
11860
|
return { ok: true, removed };
|
|
11499
11861
|
},
|
|
11862
|
+
drop: ({ symbol } = {}) => {
|
|
11863
|
+
/* sembolün TÜM kaydını (istatistik + dersler + işlemler) bırak —
|
|
11864
|
+
artık işlem yapılmayan/eski semboller depoda yer kaplamasın */
|
|
11865
|
+
const learn = finLearnLoad();
|
|
11866
|
+
const sym = norm(symbol);
|
|
11867
|
+
if (!sym || !learn.symbols[sym]) return { ok: false, error: 'sembol yok' };
|
|
11868
|
+
delete learn.symbols[sym];
|
|
11869
|
+
finLearnSave();
|
|
11870
|
+
return { ok: true, symbol: sym };
|
|
11871
|
+
},
|
|
11872
|
+
forget: () => {
|
|
11873
|
+
/* ELLE SADELEŞTİRME: TTL'i geçen ders/işlemleri ve hareketsiz sembolleri
|
|
11874
|
+
hemen unut (normalde her yazmada otomatik çalışır) */
|
|
11875
|
+
const learn = finLearnLoad();
|
|
11876
|
+
const r = finLearnForget(learn);
|
|
11877
|
+
if (r.notes || r.trades || r.symbols) finLearnSave();
|
|
11878
|
+
return { ok: true, ...r };
|
|
11879
|
+
},
|
|
11880
|
+
compact: ({ symbol } = {}) => {
|
|
11881
|
+
/* OPENCODE TARZI ÖZETLEME (elle): eski dersleri modele özetlet,
|
|
11882
|
+
ham yalnız son dersler kalsın */
|
|
11883
|
+
const sym = norm(symbol);
|
|
11884
|
+
if (!sym) return Promise.resolve({ ok: false, error: 'compact için symbol gerekli' });
|
|
11885
|
+
return finLearnCompactSymbol(sym, true);
|
|
11886
|
+
},
|
|
11500
11887
|
};
|
|
11501
11888
|
}
|
|
11502
11889
|
|
|
@@ -11883,7 +12270,10 @@ function finApplyTraderFields(s, symbolsOverride, role, isMain) {
|
|
|
11883
12270
|
s.financeSymbols = Array.isArray(symbolsOverride) && symbolsOverride.length ? symbolsOverride : f.symbols;
|
|
11884
12271
|
s.financeStrategy = String(f.strategy || '');
|
|
11885
12272
|
s.financeShadow = !!f.shadowMode; /* shadow modda işlem araçları emir göndermez */
|
|
11886
|
-
|
|
12273
|
+
{
|
|
12274
|
+
const lim = finLimitsSnapshot();
|
|
12275
|
+
s.financeLimits = { minLot: lim.minLot, maxLot: lim.maxLot, maxPositions: lim.maxPositions, symbolLimits: lim.symbols };
|
|
12276
|
+
}
|
|
11887
12277
|
/* ZORUNLU TEK skill: rol ajanı → roleSkills[rol], ana trader → roleSkills.trader */
|
|
11888
12278
|
const skillKey = r || (s.financePlaybook ? 'trader' : '');
|
|
11889
12279
|
s.financeRoleSkills = (skillKey && f.roleSkills && f.roleSkills[skillKey]) || [];
|
|
@@ -11984,9 +12374,17 @@ function finTraderBrief(agent) {
|
|
|
11984
12374
|
return [
|
|
11985
12375
|
`Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
|
|
11986
12376
|
`Odak semboller: ${syms || '(boş — mt5_status ile terminale bak, mantıklı semboller seç)'}`,
|
|
11987
|
-
`Tur aralığı: ${f.intervalSec} sn · Lot aralığı: ${f.minLot}–${f.maxLot} (
|
|
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}`,
|
|
12378
|
+
(() => {
|
|
12379
|
+
const rows = Object.entries(f.symbolLimits || {}).map(([s, v]) => {
|
|
12380
|
+
const mn = Number(v && v.minLot) > 0 ? Number(v.minLot) : Number(f.minLot);
|
|
12381
|
+
const mx = Number(v && v.maxLot) > 0 ? Number(v.maxLot) : Number(f.maxLot);
|
|
12382
|
+
return `${s} ${mn}–${mx}`;
|
|
12383
|
+
});
|
|
12384
|
+
return rows.length ? `SEMBOL LOT LİMİTLERİN (senin kararın): ${rows.join(' · ')}` : '';
|
|
12385
|
+
})(),
|
|
11988
12386
|
agent && agent.main
|
|
11989
|
-
? `LİMİT YETKİN: volatilite/performansa göre mt5_limits {action:"set", minLot?, maxLot?, maxPositions?}
|
|
12387
|
+
? `LİMİT YETKİN: volatilite/performansa göre 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}.`
|
|
11990
12388
|
: '',
|
|
11991
12389
|
roleDef
|
|
11992
12390
|
? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
|
|
@@ -11999,7 +12397,7 @@ function finTraderBrief(agent) {
|
|
|
11999
12397
|
f.shadowMode ? 'SHADOW MOD AÇIK: emir gönderilmez — kararlarını gerekçesiyle raporla, gerçek işlem açılmaz.' : '',
|
|
12000
12398
|
'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → mt5_rates/mt5_indicators ile teknik okuma → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
|
|
12001
12399
|
'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
|
|
12002
|
-
'ÖĞRENME (
|
|
12400
|
+
'ÖĞRENME (SEÇİCİ): yalnız gerçekten öğrenilmiş, tekrar kullanılabilir içgörüyü tek KISA cümleyle mt5_ogrenme {action:"add", symbol, text, kind} ile kaydet (günde en fazla 2-3); her kapanışa ders yazma, günlük/uzun metin kaydetme; yeni karar öncesi mt5_ogrenme {action:"stats"/"list", symbol} ile geçmişi oku.',
|
|
12003
12401
|
'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.',
|
|
12004
12402
|
'⚡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.',
|
|
12005
12403
|
'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
|
|
@@ -12085,7 +12483,7 @@ async function finConsultPlan(f, agent, sid) {
|
|
|
12085
12483
|
'Güncel piyasa bağlamı gerekiyorsa web_search kullan; kurulu bir skill konuyla ilgiliyse skill aracıyla oku.';
|
|
12086
12484
|
const ctx = [
|
|
12087
12485
|
`Odak semboller: ${agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ') || '(boş — ajan kendi bulabilir)'}`,
|
|
12088
|
-
`Otomatik işlem: ${auto} · lot aralığı ${f.minLot}–${f.maxLot} · max eşzamanlı pozisyon ${f.maxPositions}`,
|
|
12486
|
+
`Otomatik işlem: ${auto} · lot aralığı ${f.minLot}–${f.maxLot} (genel; sembol bazlı limitler daraltır) · max eşzamanlı pozisyon ${f.maxPositions}`,
|
|
12089
12487
|
account
|
|
12090
12488
|
? `Hesap: bakiye ${account.balance} ${account.currency} · özkaynak ${account.equity ?? '?'} · serbest marj ${account.margin_free ?? '?'} · kaldıraç 1:${account.leverage ?? '?'}`
|
|
12091
12489
|
: 'Hesap verisi alınamadı.',
|
|
@@ -12226,7 +12624,7 @@ function finAgentRound(sid, opts) {
|
|
|
12226
12624
|
: 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
|
|
12227
12625
|
const learnTip = roleDef
|
|
12228
12626
|
? ' İlgili sembolün geçmiş derslerini (mt5_ogrenme) oku ve önerine yansıt.'
|
|
12229
|
-
: '
|
|
12627
|
+
: ' Yalnız gerçekten öğretici, tekrar kullanılabilir bir içgörü varsa TEK kısa cümleyle mt5_ogrenme add ile kaydet (her kapanışa ders yazma); karar öncesi mt5_ogrenme stats/list ile o sembolün geçmişini oku' + (agent.main ? '; volatiliteye göre mt5_limits ile genel ya da SEMBOL BAZLI (symbol) lot limitlerini güncelleyebilirsin.' : '.');
|
|
12230
12628
|
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
12231
12629
|
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.`;
|
|
12232
12630
|
const launch = (planBlock) => {
|
|
@@ -12397,6 +12795,26 @@ ipcMain.handle('finance:alerts:remove', async (_e, id) => {
|
|
|
12397
12795
|
return { ok };
|
|
12398
12796
|
});
|
|
12399
12797
|
|
|
12798
|
+
/* MT5 ÖĞRENME HAFIZASI (panel 🧠): sembol bazlı istatistik + ajan derslerini
|
|
12799
|
+
görüntüle/sil — depo tavanları (sembol/ders/işlem/günlük ders) çekirdekte uygulanır */
|
|
12800
|
+
ipcMain.handle('finance:learn', async (_e, payload) => {
|
|
12801
|
+
const p = payload && typeof payload === 'object' ? payload : {};
|
|
12802
|
+
const api = finLearnApi();
|
|
12803
|
+
const action = String(p.action || 'list');
|
|
12804
|
+
try {
|
|
12805
|
+
if (action === 'list') return api.list({ symbol: p.symbol });
|
|
12806
|
+
if (action === 'stats') return api.stats({ symbol: p.symbol });
|
|
12807
|
+
if (action === 'remove') return api.remove({ id: p.id, symbol: p.symbol });
|
|
12808
|
+
if (action === 'clear') return api.clear({ ids: p.ids, symbol: p.symbol, all: p.all });
|
|
12809
|
+
if (action === 'drop') return api.drop({ symbol: p.symbol });
|
|
12810
|
+
if (action === 'forget') return api.forget();
|
|
12811
|
+
if (action === 'compact') return await api.compact({ symbol: p.symbol });
|
|
12812
|
+
return { ok: false, error: 'bilinmeyen action' };
|
|
12813
|
+
} catch (e) {
|
|
12814
|
+
return { ok: false, error: String((e && e.message) || e).slice(0, 160) };
|
|
12815
|
+
}
|
|
12816
|
+
});
|
|
12817
|
+
|
|
12400
12818
|
ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
12401
12819
|
financeState.mode = !!(payload && payload.on);
|
|
12402
12820
|
/* SAAT DENETİMİ: pencere kapanınca ajanlar otomatik durur, açılınca geri başlar */
|
|
@@ -12512,6 +12930,8 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
12512
12930
|
if (p.minLot !== undefined) f.minLot = Math.max(0.01, Math.min(100, Number(p.minLot) || 0.01));
|
|
12513
12931
|
/* min lot max lotu aşamaz — hangisi sonra yazıldıysa diğerine kelepçelenir */
|
|
12514
12932
|
if (f.minLot > f.maxLot) f.minLot = f.maxLot;
|
|
12933
|
+
/* genel aralık değişti: sembol bazlı limitler yeni taban/tavana kelepçelenir */
|
|
12934
|
+
if (p.minLot !== undefined || p.maxLot !== undefined) finNormalizeSymbolLimits(f);
|
|
12515
12935
|
if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
|
|
12516
12936
|
/* limit değişikliği koşan oturumlara ANINDA işlenir (sonraki turu beklemez) */
|
|
12517
12937
|
if (p.minLot !== undefined || p.maxLot !== undefined || p.maxPositions !== undefined) {
|