beast-agent 2.63.4 → 2.63.6
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 +766 -77
- package/src/preload.js +4 -0
- package/src/renderer/i18n.js +38 -0
- package/src/renderer/index.html +3 -1
- 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;
|
|
@@ -10640,6 +10690,12 @@ function finWakeAgentDm(sid, text) {
|
|
|
10640
10690
|
finTradeHoursBlockedLog('olay uyandırması');
|
|
10641
10691
|
return false;
|
|
10642
10692
|
}
|
|
10693
|
+
/* TYPESAFE-ONLY: LLM turu BAŞLATILMAZ — olay TS turunu öne çeker; prompt/
|
|
10694
|
+
pendingReports hattı (engine.send) bu modda hiç kullanılmaz */
|
|
10695
|
+
if (finTsAgentMode(agent)) {
|
|
10696
|
+
if (!agent.tsBusy) finTypeSafeKick(id, agent);
|
|
10697
|
+
return true;
|
|
10698
|
+
}
|
|
10643
10699
|
try {
|
|
10644
10700
|
const body = agent.role
|
|
10645
10701
|
? String(text || '') + "\n(ROL HATIRLATMASI: işlem AÇMA — durumu analiz et; gerekiyorsa agent_dm ile ANA TRADER'a bildir.)"
|
|
@@ -10717,11 +10773,13 @@ async function finRecordClose(ticket, st) {
|
|
|
10717
10773
|
finExcSave();
|
|
10718
10774
|
finExcFlush(true);
|
|
10719
10775
|
const kind = finwatch.closeKind(closeReason);
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10776
|
+
const tf = String((st && st.tf) || '') || '';
|
|
10777
|
+
finJournal({ kind: 'close', ticket, symbol, net: rounded, side: (st && st.side) || '', mfe, mae, reason: kind || '', timeframe: tf || '' });
|
|
10778
|
+
/* SEMBOL + PERİYOT BAZLI SÜREKLİ ÖĞRENME: her kapanış istatistiğe işlenir;
|
|
10779
|
+
zararla kapananlar yapılandırılmış hata kaydı olur (mt5_ogrenme) */
|
|
10780
|
+
finLearnRecordClose({ symbol, side: (st && st.side) || '', net: rounded, mfe, mae, reason: kind || '', timeframe: tf });
|
|
10723
10781
|
const pl = rounded == null ? '' : ` · K/Z ${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
|
|
10724
|
-
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${pl}`;
|
|
10782
|
+
const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${tf ? ' · ' + tf : ''}${pl}`;
|
|
10725
10783
|
financeLog('[watchdog] ' + line);
|
|
10726
10784
|
if (finCfg().notifyTrades !== false) financeNotify(line, 'close');
|
|
10727
10785
|
if (rounded != null) finStatsRefresh(true);
|
|
@@ -10731,10 +10789,17 @@ async function finRecordClose(ticket, st) {
|
|
|
10731
10789
|
const label = kind === 'stop' ? 'STOP OLDU' : kind === 'tp' ? 'TP OLDU' : 'STOP-OUT (teminat)';
|
|
10732
10790
|
const how = kind === 'tp' ? 'take-profit' : kind === 'stop' ? 'stop-loss' : 'stop-out';
|
|
10733
10791
|
const netTxt = rounded == null ? '?' : `${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
|
|
10792
|
+
const loss = rounded != null && rounded < 0;
|
|
10793
|
+
/* YANLIŞTAN DERS: zararla kapanışta ajan hata dersini YAZMAKLA yükümlü —
|
|
10794
|
+
ders bir sonraki turun girdisine (öğrenme digest'i) otomatik girer */
|
|
10795
|
+
const lessonLine = loss
|
|
10796
|
+
? `\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.`
|
|
10797
|
+
: `\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
10798
|
finWakeAgents(
|
|
10735
10799
|
`[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
|
|
10800
|
+
`- ${symbol || '?'}${st && st.side ? ' ' + String(st.side).toUpperCase() : ''} #${ticket}${tf ? ' · ' + tf : ''} ${how} ile kapandı · net ${netTxt}\n` +
|
|
10801
|
+
`Ş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.` +
|
|
10802
|
+
lessonLine,
|
|
10738
10803
|
{ kind, ticket, symbol }
|
|
10739
10804
|
);
|
|
10740
10805
|
}
|
|
@@ -10920,6 +10985,9 @@ async function finWatchTick() {
|
|
|
10920
10985
|
symbol: String(p.symbol || ''),
|
|
10921
10986
|
side: Number(p.type) === 0 ? 'buy' : 'sell',
|
|
10922
10987
|
entry: Number(p.price_open) || 0,
|
|
10988
|
+
/* ZAMAN DİLİMİ: ajan emir açarken yorum yazar ("Beast M15") — kapanışta
|
|
10989
|
+
öğrenme bu periyoda işlenir */
|
|
10990
|
+
tf: finLearnParseTf(p.comment),
|
|
10923
10991
|
r: 0,
|
|
10924
10992
|
be: false,
|
|
10925
10993
|
partial: false,
|
|
@@ -10930,9 +10998,12 @@ async function finWatchTick() {
|
|
|
10930
10998
|
financeState.watch.set(ticket, st);
|
|
10931
10999
|
const isBeast = Number(p.magic) === 20260908 || /beast/i.test(String(p.comment || ''));
|
|
10932
11000
|
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 });
|
|
11001
|
+
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
11002
|
}
|
|
10935
11003
|
finWatchSave();
|
|
11004
|
+
} else if (!st.tf) {
|
|
11005
|
+
const tfNow = finLearnParseTf(p.comment);
|
|
11006
|
+
if (tfNow) { st.tf = tfNow; finWatchSave(); }
|
|
10936
11007
|
}
|
|
10937
11008
|
/* KÂR KORUMA: görülen EN İYİ kâr (fiyat farkı) tick aralarında da korunur */
|
|
10938
11009
|
{
|
|
@@ -11176,8 +11247,8 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11176
11247
|
}
|
|
11177
11248
|
/* SEMBOL BAZLI ÖĞRENME kaydı: günlüğe + AKIŞ paneline düşer */
|
|
11178
11249
|
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)}`;
|
|
11250
|
+
finJournal({ kind: 'learn', sid, agent: who, symbol: d.symbol, text: d.text, learnKind: d.kind || 'observation', timeframe: d.timeframe || '' });
|
|
11251
|
+
const line = `🧠 ÖĞRENME${d.timeframe ? ' [' + d.timeframe + ']' : ''}: ${d.symbol || '?'} — ${String(d.text || '').replace(/\s+/g, ' ').slice(0, 160)}`;
|
|
11181
11252
|
financeLog('[ogrenme] ' + line);
|
|
11182
11253
|
finPush('trade', { line });
|
|
11183
11254
|
return;
|
|
@@ -11191,7 +11262,7 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11191
11262
|
return;
|
|
11192
11263
|
}
|
|
11193
11264
|
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 || '' });
|
|
11265
|
+
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
11266
|
} else if (k === 'close') {
|
|
11196
11267
|
/* KISMİ KAPATMA (kısmi TP / kısmi stop): ajan kararı; günlük + bildirim.
|
|
11197
11268
|
Kesin toplam K/Z yine watchdog tarafından yazılır (çift sayım yok). */
|
|
@@ -11211,14 +11282,14 @@ function finTradeEvent(kRaw, dataRaw, sidRaw) {
|
|
|
11211
11282
|
} else if (k === 'modify') {
|
|
11212
11283
|
finJournal({ kind: 'modify', sid, agent: who, ticket: d.ticket, sl: d.sl, tp: d.tp });
|
|
11213
11284
|
} else if (k === 'pending') {
|
|
11214
|
-
finJournal({ kind: 'pending', sid, agent: who, symbol: d.symbol, type: d.type, volume: d.volume, reason: d.reason || '' });
|
|
11285
|
+
finJournal({ kind: 'pending', sid, agent: who, symbol: d.symbol, type: d.type, volume: d.volume, timeframe: d.timeframe || '', reason: d.reason || '' });
|
|
11215
11286
|
} else if (k === 'cancel') {
|
|
11216
11287
|
finJournal({ kind: 'cancel', sid, agent: who, ticket: d.ticket });
|
|
11217
11288
|
}
|
|
11218
11289
|
let line = '';
|
|
11219
|
-
if (k === 'trade') line = `İŞLEM AÇILDI: ${d.side} ${d.volume} ${d.symbol}${who ? ' · ' + who : ''}`;
|
|
11290
|
+
if (k === 'trade') line = `İŞLEM AÇILDI: ${d.side} ${d.volume} ${d.symbol}${d.timeframe ? ' · ' + d.timeframe : ''}${who ? ' · ' + who : ''}`;
|
|
11220
11291
|
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 : ''}`;
|
|
11292
|
+
else if (k === 'pending') line = `BEKLEYEN EMİR: ${d.type} ${d.volume} ${d.symbol}${d.timeframe ? ' · ' + d.timeframe : ''}${who ? ' · ' + who : ''}`;
|
|
11222
11293
|
else if (k === 'cancel') line = `EMİR İPTAL: ticket ${d.ticket}`;
|
|
11223
11294
|
if (line) {
|
|
11224
11295
|
financeLog('[ajan] ' + line);
|
|
@@ -11254,9 +11325,9 @@ try {
|
|
|
11254
11325
|
if (!kind) return;
|
|
11255
11326
|
if (kind === 'trade') {
|
|
11256
11327
|
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);
|
|
11328
|
+
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
11329
|
} 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);
|
|
11330
|
+
else if (kind === 'pending') finTradeEvent('pending', { symbol: a.symbol, type: a.type, volume: a.volume, timeframe: a.timeframe || '', reason: a.reason || '' }, sid);
|
|
11260
11331
|
else if (kind === 'cancel') finTradeEvent('cancel', { ticket: a.ticket }, sid);
|
|
11261
11332
|
else if (kind === 'close') {
|
|
11262
11333
|
finTradeEvent('close', {
|
|
@@ -11493,15 +11564,62 @@ const FIN_LEARN_COMPACT_AT = 24; /* not sayısı bunu aşınca arka planda ot
|
|
|
11493
11564
|
const FIN_LEARN_KEEP_RECENT = 8; /* ham kalan son ders sayısı */
|
|
11494
11565
|
const FIN_LEARN_SUMMARY_MAX = 800; /* kalıcı özet metin tavanı (karakter) */
|
|
11495
11566
|
const FIN_LEARN_COMPACT_MIN = 4; /* elle özetleme için gereken en az eski ders */
|
|
11567
|
+
/* ZAMAN DİLİMİ: ajan kendi periyodunu SEÇER (bot kararı) — işlem emri yorumuna
|
|
11568
|
+
yazılır ("Beast M15"), kapanışta periyot bazlı öğrenmeye işlenir. */
|
|
11569
|
+
const FIN_LEARN_TFS = ['M1', 'M5', 'M15', 'M30', 'H1', 'H4', 'D1', 'W1', 'MN1'];
|
|
11570
|
+
const FIN_LEARN_MAX_MISTAKES = 40; /* sembol başına otomatik hata kaydı tavanı */
|
|
11571
|
+
|
|
11572
|
+
function finLearnNormTf(v) {
|
|
11573
|
+
const s = String(v || '').trim().toUpperCase();
|
|
11574
|
+
if (!s) return '';
|
|
11575
|
+
const m = s.match(/^(M|H|D|W|MN)\s*(\d+)$/);
|
|
11576
|
+
if (!m) return '';
|
|
11577
|
+
const key = m[1] === 'MN' ? 'MN1' : m[1] + m[2];
|
|
11578
|
+
return FIN_LEARN_TFS.includes(key) ? key : '';
|
|
11579
|
+
}
|
|
11580
|
+
|
|
11581
|
+
/* MT5 pozisyon yorumundan periyot okur: "Beast M15 ..." → M15 */
|
|
11582
|
+
function finLearnParseTf(comment) {
|
|
11583
|
+
const m = String(comment || '').toUpperCase().match(/\b(M1|M5|M15|M30|H1|H4|D1|W1|MN1)\b/);
|
|
11584
|
+
return m ? m[1] : '';
|
|
11585
|
+
}
|
|
11496
11586
|
|
|
11497
11587
|
function finLearnEmptyStats() {
|
|
11498
11588
|
return {
|
|
11499
11589
|
trades: 0, wins: 0, losses: 0, net: 0,
|
|
11500
11590
|
mfe: 0, mae: 0, givebacks: 0, givebackAmount: 0,
|
|
11501
11591
|
lastAt: 0, lastNet: 0, streak: 0, bestNet: 0, worstNet: 0,
|
|
11592
|
+
/* PERİYOT KIRILIMI: tf → {trades,wins,losses,net,mfe,mae,givebacks,lastAt,lastNet} */
|
|
11593
|
+
byTf: {},
|
|
11502
11594
|
};
|
|
11503
11595
|
}
|
|
11504
11596
|
|
|
11597
|
+
/* Bozuk/eski istatistik kaydını temizle: bilinen alanlar + byTf süzülür */
|
|
11598
|
+
function finLearnNormStats(v) {
|
|
11599
|
+
const raw = v && typeof v === 'object' ? v : {};
|
|
11600
|
+
const out = { ...finLearnEmptyStats(), ...raw };
|
|
11601
|
+
const src = raw.byTf && typeof raw.byTf === 'object' ? raw.byTf : {};
|
|
11602
|
+
const byTf = {};
|
|
11603
|
+
for (const [k, s] of Object.entries(src)) {
|
|
11604
|
+
const tf = finLearnNormTf(k);
|
|
11605
|
+
if (!tf || !s || typeof s !== 'object') continue;
|
|
11606
|
+
const t = {
|
|
11607
|
+
trades: Math.max(0, Math.round(Number(s.trades) || 0)),
|
|
11608
|
+
wins: Math.max(0, Math.round(Number(s.wins) || 0)),
|
|
11609
|
+
losses: Math.max(0, Math.round(Number(s.losses) || 0)),
|
|
11610
|
+
net: Math.round((Number(s.net) || 0) * 100) / 100,
|
|
11611
|
+
mfe: Math.round((Number(s.mfe) || 0) * 100) / 100,
|
|
11612
|
+
mae: Math.round((Number(s.mae) || 0) * 100) / 100,
|
|
11613
|
+
givebacks: Math.max(0, Math.round(Number(s.givebacks) || 0)),
|
|
11614
|
+
lastAt: Number(s.lastAt) || 0,
|
|
11615
|
+
lastNet: Math.round((Number(s.lastNet) || 0) * 100) / 100,
|
|
11616
|
+
};
|
|
11617
|
+
if (t.trades > 0) byTf[tf] = t;
|
|
11618
|
+
}
|
|
11619
|
+
out.byTf = byTf;
|
|
11620
|
+
return out;
|
|
11621
|
+
}
|
|
11622
|
+
|
|
11505
11623
|
/* Ders metni karşılaştırma anahtarı: büyük/küçük harf, noktalama ve boşluk
|
|
11506
11624
|
farkları aynı ders sayılır — ajan aynı cümleyi kopyalayıp depoyu doldurmasın */
|
|
11507
11625
|
function finLearnNoteKey(text) {
|
|
@@ -11555,7 +11673,7 @@ function finLearnPrune(learn, keep, protectSym) {
|
|
|
11555
11673
|
yüklemede çalışır → depo kendiliğinden sade kalır, birikme/zehirlenme olmaz. */
|
|
11556
11674
|
function finLearnForget(learn, now) {
|
|
11557
11675
|
const t = Number(now) || Date.now();
|
|
11558
|
-
const stat = { notes: 0, trades: 0, symbols: 0 };
|
|
11676
|
+
const stat = { notes: 0, trades: 0, mistakes: 0, symbols: 0 };
|
|
11559
11677
|
for (const [sym, e] of Object.entries(learn.symbols)) {
|
|
11560
11678
|
if (!e || typeof e !== 'object') { delete learn.symbols[sym]; stat.symbols++; continue; }
|
|
11561
11679
|
const notes = Array.isArray(e.notes) ? e.notes : [];
|
|
@@ -11564,6 +11682,10 @@ function finLearnForget(learn, now) {
|
|
|
11564
11682
|
const trades = Array.isArray(e.trades) ? e.trades : [];
|
|
11565
11683
|
e.trades = trades.filter((x) => x && t - (Number(x.at) || 0) <= FIN_LEARN_TRADE_TTL_MS);
|
|
11566
11684
|
stat.trades += trades.length - e.trades.length;
|
|
11685
|
+
/* HATA KAYITLARI da işlem TTL'iyle unutulur — depo şişmez */
|
|
11686
|
+
const mistakes = Array.isArray(e.mistakes) ? e.mistakes : [];
|
|
11687
|
+
e.mistakes = mistakes.filter((x) => x && t - (Number(x.at) || 0) <= FIN_LEARN_TRADE_TTL_MS).slice(-FIN_LEARN_MAX_MISTAKES);
|
|
11688
|
+
stat.mistakes += mistakes.length - e.mistakes.length;
|
|
11567
11689
|
if (t - finLearnActivity(e) > FIN_LEARN_SYMBOL_TTL_MS) {
|
|
11568
11690
|
delete learn.symbols[sym];
|
|
11569
11691
|
stat.symbols += 1;
|
|
@@ -11577,7 +11699,7 @@ function finLearnForget(learn, now) {
|
|
|
11577
11699
|
function finLearnTidy(learn, protectSym, now) {
|
|
11578
11700
|
const forgot = finLearnForget(learn, now);
|
|
11579
11701
|
const cap = finLearnPrune(learn, FIN_LEARN_MAX_SYMBOLS, protectSym);
|
|
11580
|
-
return { notes: forgot.notes, trades: forgot.trades, symbols: forgot.symbols + cap };
|
|
11702
|
+
return { notes: forgot.notes, trades: forgot.trades, mistakes: forgot.mistakes, symbols: forgot.symbols + cap };
|
|
11581
11703
|
}
|
|
11582
11704
|
|
|
11583
11705
|
function finLearnLoad() {
|
|
@@ -11593,7 +11715,8 @@ function finLearnLoad() {
|
|
|
11593
11715
|
learn.symbols[sym] = {
|
|
11594
11716
|
notes: Array.isArray(v.notes) ? v.notes.filter((n) => n && n.id && n.text).slice(-FIN_LEARN_MAX_NOTES) : [],
|
|
11595
11717
|
trades: Array.isArray(v.trades) ? v.trades.filter(Boolean).slice(-FIN_LEARN_MAX_TRADES) : [],
|
|
11596
|
-
|
|
11718
|
+
mistakes: Array.isArray(v.mistakes) ? v.mistakes.filter(Boolean).slice(-FIN_LEARN_MAX_MISTAKES) : [],
|
|
11719
|
+
stats: finLearnNormStats(v.stats),
|
|
11597
11720
|
/* KALICI ÖZET (opencode tarzı sıkıştırma): eski derslerin modele
|
|
11598
11721
|
özetlettirilmiş hâli — ham dersler düşse de bilgi kalır */
|
|
11599
11722
|
summary: v.summary && typeof v.summary === 'object' && String(v.summary.text || '').trim()
|
|
@@ -11610,7 +11733,7 @@ function finLearnLoad() {
|
|
|
11610
11733
|
} catch {}
|
|
11611
11734
|
const dropped = finLearnTidy(learn); /* yükleme anında unut + tavan uygula */
|
|
11612
11735
|
financeState.learn = learn;
|
|
11613
|
-
if (dropped.notes || dropped.trades || dropped.symbols) finLearnSave(); /* disk de hemen küçülsün */
|
|
11736
|
+
if (dropped.notes || dropped.trades || dropped.mistakes || dropped.symbols) finLearnSave(); /* disk de hemen küçülsün */
|
|
11614
11737
|
return learn;
|
|
11615
11738
|
}
|
|
11616
11739
|
|
|
@@ -11630,16 +11753,16 @@ const FIN_LEARN_COMPACT_SYSTEM =
|
|
|
11630
11753
|
'Sen bir trading mentorüsün. Sana bir sembolün ESKİ ders notları ve mevcut özeti verilir. ' +
|
|
11631
11754
|
'Görevin: güncellenmiş KISA bir özet üretmek.\n' +
|
|
11632
11755
|
'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 ' +
|
|
11756
|
+
'seans/setup/PERİYOT (M5/M15/H1…) kuralları, SL/TP davranışı. (2) Tekrarları birleştir; tarih/saat damgası, anlık ' +
|
|
11634
11757
|
'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. ' +
|
|
11758
|
+
'desteklenen kazanır. (4) Sayısal istatistikleri (işlem, kazanç %, net, kâr geri verme, periyot bazlı net) koru; ' +
|
|
11759
|
+
'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
11760
|
'(6) Çıktı SADECE özet metni olsun; başlık/etiket/açıklama yazma.';
|
|
11638
11761
|
|
|
11639
11762
|
const finLearnCompacting = new Set();
|
|
11640
11763
|
|
|
11641
11764
|
function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
11642
|
-
const st =
|
|
11765
|
+
const st = finLearnNormStats(e.stats);
|
|
11643
11766
|
const wr = st.trades ? Math.round((st.wins / st.trades) * 100) : 0;
|
|
11644
11767
|
const lines = [];
|
|
11645
11768
|
lines.push('SEMBOL: ' + sym);
|
|
@@ -11648,12 +11771,27 @@ function finLearnSummaryPrompt(sym, e, oldNotes) {
|
|
|
11648
11771
|
(st.givebacks ? ` · ${st.givebacks} kez kâr geri verildi` : '') +
|
|
11649
11772
|
(st.streak >= 2 ? ` · ${st.streak} ardışık kayıp` : '')
|
|
11650
11773
|
);
|
|
11774
|
+
const tfRows = Object.entries(st.byTf || {}).filter(([, s]) => s && Number(s.trades) > 0);
|
|
11775
|
+
if (tfRows.length) {
|
|
11776
|
+
lines.push('PERİYOT İSTATİSTİĞİ:');
|
|
11777
|
+
for (const [tf, s] of tfRows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0))) {
|
|
11778
|
+
const w = Math.round((Number(s.wins) / Number(s.trades)) * 100);
|
|
11779
|
+
lines.push(`- ${tf}: ${s.trades} işlem · %${w} kazanç · net ${Number(s.net) >= 0 ? '+' : ''}${Math.round(Number(s.net) * 100) / 100}`);
|
|
11780
|
+
}
|
|
11781
|
+
}
|
|
11782
|
+
const mistakes = Array.isArray(e.mistakes) ? e.mistakes.slice(-20) : [];
|
|
11783
|
+
if (mistakes.length) {
|
|
11784
|
+
lines.push('SON HATALAR (zararla kapananlar):');
|
|
11785
|
+
for (const m of mistakes) {
|
|
11786
|
+
lines.push(`- ${m.tf || '?'} ${m.side || ''} net ${Math.round(Number(m.net) * 100) / 100}${m.reason ? ' (' + m.reason + ')' : ''}${m.giveback ? ' [kâr geri verildi]' : ''}`);
|
|
11787
|
+
}
|
|
11788
|
+
}
|
|
11651
11789
|
if (e.summary && e.summary.text) lines.push('MEVCUT ÖZET:\n' + String(e.summary.text).slice(0, 1400));
|
|
11652
11790
|
lines.push('ESKİ DERSLER (kronolojik — en yeni EN SONDA):');
|
|
11653
11791
|
for (const n of oldNotes.slice(-80)) {
|
|
11654
|
-
lines.push(`- [${String(n.kind || 'observation')}] ${String(n.text || '').slice(0, 220)}`);
|
|
11792
|
+
lines.push(`- [${String(n.kind || 'observation')}${n.tf ? ' ' + String(n.tf) : ''}] ${String(n.text || '').slice(0, 220)}`);
|
|
11655
11793
|
}
|
|
11656
|
-
lines.push('Görev: yalnız kalıcı ve güncel dersleri koruyan yeni özet metnini yaz.');
|
|
11794
|
+
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
11795
|
return lines.join('\n');
|
|
11658
11796
|
}
|
|
11659
11797
|
|
|
@@ -11720,40 +11858,55 @@ function finLearnSym(symbol) {
|
|
|
11720
11858
|
const sym = String(symbol || '').trim().toUpperCase();
|
|
11721
11859
|
if (!sym) return null;
|
|
11722
11860
|
const learn = finLearnLoad();
|
|
11723
|
-
if (!learn.symbols[sym]) learn.symbols[sym] = { notes: [], trades: [], stats: finLearnEmptyStats() };
|
|
11861
|
+
if (!learn.symbols[sym]) learn.symbols[sym] = { notes: [], trades: [], mistakes: [], stats: finLearnEmptyStats(), summary: null };
|
|
11724
11862
|
const e = learn.symbols[sym];
|
|
11725
|
-
e.
|
|
11863
|
+
if (!Array.isArray(e.mistakes)) e.mistakes = [];
|
|
11864
|
+
e.stats = finLearnNormStats(e.stats);
|
|
11726
11865
|
return e;
|
|
11727
11866
|
}
|
|
11728
11867
|
|
|
11729
|
-
/* Kapanan her işlem otomatik öğrenme girdisi olur (sembol bazlı)
|
|
11730
|
-
|
|
11868
|
+
/* Kapanan her işlem otomatik öğrenme girdisi olur (sembol + periyot bazlı).
|
|
11869
|
+
timeframe: ajanın emir anında verdiği periyot (yoksa '' — genel istatistiğe
|
|
11870
|
+
yine işler). Zararla kapanan işlem AYRICA yapılandırılmış hata kaydı olur:
|
|
11871
|
+
hata dersleri bu kayıttan üretilir ve bir sonraki turda prompta girer. */
|
|
11872
|
+
function finLearnRecordClose({ symbol, side, net, mfe, mae, reason, timeframe }) {
|
|
11731
11873
|
try {
|
|
11732
11874
|
if (net == null) return; /* K/Z okunamadı — sahte kayıt yazma */
|
|
11733
11875
|
const n = Number(net);
|
|
11734
11876
|
if (!isFinite(n) || !symbol) return;
|
|
11735
11877
|
const e = finLearnSym(symbol);
|
|
11736
11878
|
if (!e) return;
|
|
11879
|
+
const tf = finLearnNormTf(timeframe);
|
|
11737
11880
|
const st = e.stats;
|
|
11738
11881
|
const m = Number(mfe);
|
|
11739
11882
|
const ma = Number(mae);
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11883
|
+
const bump = (s) => {
|
|
11884
|
+
s.trades += 1;
|
|
11885
|
+
if (n >= 0) s.wins += 1;
|
|
11886
|
+
else s.losses += 1;
|
|
11887
|
+
s.net = Math.round((Number(s.net) + n) * 100) / 100;
|
|
11888
|
+
s.lastAt = Date.now();
|
|
11889
|
+
s.lastNet = Math.round(n * 100) / 100;
|
|
11890
|
+
s.streak = n < 0 ? Number(s.streak || 0) + 1 : 0;
|
|
11891
|
+
if (isFinite(m)) {
|
|
11892
|
+
s.mfe = Math.round(((Number(s.mfe) || 0) + m) * 100) / 100;
|
|
11893
|
+
if (m > 0 && n < 0) {
|
|
11894
|
+
s.givebacks = Number(s.givebacks || 0) + 1;
|
|
11895
|
+
s.givebackAmount = Math.round(((Number(s.givebackAmount) || 0) + m) * 100) / 100;
|
|
11896
|
+
}
|
|
11897
|
+
}
|
|
11898
|
+
if (isFinite(ma)) s.mae = Math.round(((Number(s.mae) || 0) + ma) * 100) / 100;
|
|
11899
|
+
};
|
|
11900
|
+
/* GENEL istatistik */
|
|
11901
|
+
bump(st);
|
|
11747
11902
|
st.bestNet = Math.max(Number(st.bestNet) || 0, Math.round(n * 100) / 100);
|
|
11748
11903
|
st.worstNet = Math.min(Number(st.worstNet) || 0, Math.round(n * 100) / 100);
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
if (
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
}
|
|
11904
|
+
/* PERİYOT istatistiği: hangi zaman dilimi kazandırıyor/kaybettiriyor */
|
|
11905
|
+
if (tf) {
|
|
11906
|
+
if (!st.byTf || typeof st.byTf !== 'object') st.byTf = {};
|
|
11907
|
+
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 };
|
|
11908
|
+
bump(st.byTf[tf]);
|
|
11755
11909
|
}
|
|
11756
|
-
if (isFinite(ma)) st.mae = Math.round(((Number(st.mae) || 0) + ma) * 100) / 100;
|
|
11757
11910
|
e.trades.push({
|
|
11758
11911
|
at: Date.now(),
|
|
11759
11912
|
side: String(side || ''),
|
|
@@ -11761,14 +11914,69 @@ function finLearnRecordClose({ symbol, side, net, mfe, mae, reason }) {
|
|
|
11761
11914
|
mfe: isFinite(m) ? Math.round(m * 100) / 100 : null,
|
|
11762
11915
|
mae: isFinite(ma) ? Math.round(ma * 100) / 100 : null,
|
|
11763
11916
|
reason: String(reason || '').slice(0, 40),
|
|
11917
|
+
tf: tf || null,
|
|
11764
11918
|
});
|
|
11765
11919
|
while (e.trades.length > FIN_LEARN_MAX_TRADES) e.trades.shift();
|
|
11920
|
+
/* YANLIŞTAN DERS: zararla kapanan işlem yapılandırılmış hata kaydı olur —
|
|
11921
|
+
sonraki turlarda ajana "tekrar eden hata" olarak gösterilir */
|
|
11922
|
+
if (n < 0) {
|
|
11923
|
+
if (!Array.isArray(e.mistakes)) e.mistakes = [];
|
|
11924
|
+
e.mistakes.push({
|
|
11925
|
+
at: Date.now(),
|
|
11926
|
+
tf: tf || '',
|
|
11927
|
+
side: String(side || ''),
|
|
11928
|
+
net: Math.round(n * 100) / 100,
|
|
11929
|
+
mfe: isFinite(m) ? Math.round(m * 100) / 100 : null,
|
|
11930
|
+
mae: isFinite(ma) ? Math.round(ma * 100) / 100 : null,
|
|
11931
|
+
reason: String(reason || '').slice(0, 40),
|
|
11932
|
+
giveback: !!(isFinite(m) && m > 0),
|
|
11933
|
+
});
|
|
11934
|
+
while (e.mistakes.length > FIN_LEARN_MAX_MISTAKES) e.mistakes.shift();
|
|
11935
|
+
}
|
|
11766
11936
|
finLearnTidy(finLearnLoad(), symbol);
|
|
11767
11937
|
finLearnSave();
|
|
11768
11938
|
} catch {}
|
|
11769
11939
|
}
|
|
11770
11940
|
|
|
11771
|
-
/*
|
|
11941
|
+
/* Tur girişine gömülen öğrenme özeti: sembol + PERİYOT istatistiği, son
|
|
11942
|
+
hatalar ve dersler. Ajanın "her şeye öğrendiğiyle karar vermesi" için
|
|
11943
|
+
karar anında önüne konur; en iyi/en kötü periyot ve tekrar eden hata
|
|
11944
|
+
kalıbı açıkça işaretlenir. */
|
|
11945
|
+
function finBuildLearnTfLine(st) {
|
|
11946
|
+
const rows = Object.entries(st.byTf || {}).filter(([, s]) => s && Number(s.trades) > 0);
|
|
11947
|
+
if (!rows.length) return '';
|
|
11948
|
+
rows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0));
|
|
11949
|
+
const fmt = ([tf, s]) => {
|
|
11950
|
+
const wrT = Math.round((Number(s.wins) / Number(s.trades)) * 100);
|
|
11951
|
+
return `${tf} ${s.trades}i %${wrT} net ${Number(s.net) >= 0 ? '+' : ''}${Math.round(Number(s.net) * 100) / 100}`;
|
|
11952
|
+
};
|
|
11953
|
+
const parts = rows.map(fmt);
|
|
11954
|
+
const best = rows[0];
|
|
11955
|
+
const worst = rows.length > 1 ? rows[rows.length - 1] : null;
|
|
11956
|
+
let out = `periyot: ${parts.join(' | ')}`;
|
|
11957
|
+
if (best) out += ` · EN İYİ: ${best[0]} (net ${Number(best[1].net) >= 0 ? '+' : ''}${Math.round(Number(best[1].net) * 100) / 100})`;
|
|
11958
|
+
if (worst && Number(worst[1].net) < 0) out += ` · NEGATİF: ${worst[0]} (riski düşür/teyit ara)`;
|
|
11959
|
+
return out;
|
|
11960
|
+
}
|
|
11961
|
+
|
|
11962
|
+
/* Son hatalardan kalıp çıkar: aynı periyotta tekrarlayan kayıp sayısı vb. */
|
|
11963
|
+
function finBuildLearnMistakeLine(e) {
|
|
11964
|
+
const ms = Array.isArray(e.mistakes) ? e.mistakes : [];
|
|
11965
|
+
if (!ms.length) return '';
|
|
11966
|
+
const last = ms.slice(-6);
|
|
11967
|
+
const byTf = {};
|
|
11968
|
+
for (const m of last) if (m.tf) byTf[m.tf] = (byTf[m.tf] || 0) + 1;
|
|
11969
|
+
const tfTxt = Object.entries(byTf).map(([tf, n]) => `${tf}×${n}`).join(', ');
|
|
11970
|
+
const lastMs = last[last.length - 1];
|
|
11971
|
+
const kinds = { stop: 'SL', tp: 'TP', stopout: 'stop-out', manual: 'manuel' };
|
|
11972
|
+
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(' · ');
|
|
11973
|
+
if (tfTxt && Object.keys(byTf).length) out += ` · periyot dağılımı: ${tfTxt}`;
|
|
11974
|
+
const repeated = Object.entries(byTf).find(([, n]) => n >= 3);
|
|
11975
|
+
if (repeated) out += ` · ⚠ TEKRAR EDEN HATA: ${repeated[0]} periyodunda ${repeated[1]} kayıp — bu periyotta riski düşür ya da teyit bekle`;
|
|
11976
|
+
if (lastMs && lastMs.giveback) out += ' · KÂR GERİ VERME: kârı koru (kısmi TP/BE kullan)';
|
|
11977
|
+
return out;
|
|
11978
|
+
}
|
|
11979
|
+
|
|
11772
11980
|
function finBuildLearnDigest(symbols) {
|
|
11773
11981
|
const learn = finLearnLoad();
|
|
11774
11982
|
const prefer = Array.isArray(symbols) ? symbols.map((s) => String(s || '').trim().toUpperCase()).filter(Boolean) : [];
|
|
@@ -11777,7 +11985,7 @@ function finBuildLearnDigest(symbols) {
|
|
|
11777
11985
|
const L = [];
|
|
11778
11986
|
for (const sym of keys.slice(0, 10)) {
|
|
11779
11987
|
const e = learn.symbols[sym];
|
|
11780
|
-
const st =
|
|
11988
|
+
const st = finLearnNormStats(e.stats);
|
|
11781
11989
|
if (st.trades > 0) {
|
|
11782
11990
|
const wr = Math.round((st.wins / st.trades) * 100);
|
|
11783
11991
|
const parts = [`${st.trades} işlem`, `%${wr} kazanç`, `net ${st.net >= 0 ? '+' : ''}${st.net}`];
|
|
@@ -11786,6 +11994,13 @@ function finBuildLearnDigest(symbols) {
|
|
|
11786
11994
|
if (st.lastAt) parts.push(`son: ${st.lastNet >= 0 ? '+' : ''}${st.lastNet}`);
|
|
11787
11995
|
L.push(`- ${sym}: ${parts.join(' · ')}`);
|
|
11788
11996
|
}
|
|
11997
|
+
/* PERİYOT KIRILIMI: hangi zaman dilimi kazandırıyor — bot işlem açarken
|
|
11998
|
+
periyodu buna göre SEÇER (kendi kararı) */
|
|
11999
|
+
const tfLine = finBuildLearnTfLine(st);
|
|
12000
|
+
if (tfLine) L.push(' ' + tfLine);
|
|
12001
|
+
/* SON HATALAR: yapılan yanlışlar + tekrar eden kalıp uyarısı */
|
|
12002
|
+
const mLine = finBuildLearnMistakeLine(e);
|
|
12003
|
+
if (mLine) L.push(' ' + mLine);
|
|
11789
12004
|
/* KALICI ÖZET (compaction): eski derslerin sıkıştırılmış hâli — ham ders
|
|
11790
12005
|
sayısı azalsa da birikmiş bilgi prompta girer */
|
|
11791
12006
|
if (e.summary && e.summary.text) {
|
|
@@ -11797,62 +12012,78 @@ function finBuildLearnDigest(symbols) {
|
|
|
11797
12012
|
L.push(` ders${extra}: "${String(last.text || '').slice(0, 160)}"`);
|
|
11798
12013
|
}
|
|
11799
12014
|
}
|
|
12015
|
+
if (L.length) {
|
|
12016
|
+
L.unshift(
|
|
12017
|
+
'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.'
|
|
12018
|
+
);
|
|
12019
|
+
}
|
|
11800
12020
|
return L.join('\n');
|
|
11801
12021
|
}
|
|
11802
12022
|
|
|
11803
12023
|
function finLearnApi() {
|
|
11804
12024
|
const norm = (s) => String(s || '').trim().toUpperCase();
|
|
11805
12025
|
return {
|
|
11806
|
-
list: ({ symbol, limit } = {}) => {
|
|
12026
|
+
list: ({ symbol, timeframe, limit } = {}) => {
|
|
11807
12027
|
const learn = finLearnLoad();
|
|
11808
12028
|
const sym = norm(symbol);
|
|
12029
|
+
const tf = finLearnNormTf(timeframe);
|
|
11809
12030
|
const take = Math.max(1, Math.min(25, Math.round(Number(limit) || 8)));
|
|
11810
12031
|
if (sym) {
|
|
11811
12032
|
const e = learn.symbols[sym];
|
|
11812
|
-
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats(), summary: null };
|
|
12033
|
+
if (!e) return { ok: true, symbol: sym, count: 0, notes: [], trades: [], stats: finLearnEmptyStats(), summary: null, mistakes: [] };
|
|
12034
|
+
const notes = tf ? e.notes.filter((n) => finLearnNormTf(n.tf) === tf) : e.notes;
|
|
12035
|
+
const mistakes = tf ? (Array.isArray(e.mistakes) ? e.mistakes.filter((m) => finLearnNormTf(m.tf) === tf) : []) : (Array.isArray(e.mistakes) ? e.mistakes : []);
|
|
11813
12036
|
return {
|
|
11814
12037
|
ok: true,
|
|
11815
12038
|
symbol: sym,
|
|
11816
|
-
|
|
11817
|
-
|
|
12039
|
+
timeframe: tf || '',
|
|
12040
|
+
count: notes.length,
|
|
12041
|
+
notes: notes.slice(-take),
|
|
11818
12042
|
trades: e.trades.slice(-12),
|
|
11819
|
-
stats:
|
|
12043
|
+
stats: finLearnNormStats(e.stats),
|
|
11820
12044
|
summary: e.summary && e.summary.text ? e.summary : null,
|
|
12045
|
+
mistakes: mistakes.slice(-8),
|
|
11821
12046
|
};
|
|
11822
12047
|
}
|
|
11823
12048
|
const rows = Object.entries(learn.symbols)
|
|
11824
12049
|
.map(([s, e]) => ({
|
|
11825
12050
|
symbol: s,
|
|
11826
12051
|
notes: e.notes.length,
|
|
11827
|
-
stats:
|
|
12052
|
+
stats: finLearnNormStats(e.stats),
|
|
11828
12053
|
lastNote: String(((e.notes[e.notes.length - 1] || {}).text) || ''),
|
|
11829
12054
|
hasSummary: !!(e.summary && e.summary.text),
|
|
11830
12055
|
}))
|
|
11831
12056
|
.sort((a, b) => (b.stats.lastAt || 0) - (a.stats.lastAt || 0));
|
|
11832
12057
|
return { ok: true, count: rows.length, symbols: rows.slice(0, FIN_LEARN_MAX_SYMBOLS) };
|
|
11833
12058
|
},
|
|
11834
|
-
stats: ({ symbol } = {}) => {
|
|
11835
|
-
/* KOMPAKT OKUMA: istatistik + kalıcı özet + son 3 ders
|
|
11836
|
-
|
|
12059
|
+
stats: ({ symbol, timeframe } = {}) => {
|
|
12060
|
+
/* KOMPAKT OKUMA: istatistik + PERİYOT kırılımı + kalıcı özet + son 3 ders
|
|
12061
|
+
+ son hatalar — ajan karar öncesi bunu okur */
|
|
11837
12062
|
const learn = finLearnLoad();
|
|
11838
12063
|
const sym = norm(symbol);
|
|
12064
|
+
const tf = finLearnNormTf(timeframe);
|
|
11839
12065
|
if (sym) {
|
|
11840
12066
|
const e = learn.symbols[sym] || {};
|
|
12067
|
+
const st = finLearnNormStats(e.stats);
|
|
11841
12068
|
return {
|
|
11842
12069
|
ok: true,
|
|
11843
12070
|
symbol: sym,
|
|
11844
|
-
|
|
12071
|
+
timeframe: tf || '',
|
|
12072
|
+
stats: st,
|
|
12073
|
+
byTf: tf && st.byTf[tf] ? { [tf]: st.byTf[tf] } : st.byTf,
|
|
11845
12074
|
summary: e.summary && e.summary.text ? e.summary : null,
|
|
11846
|
-
notes: Array.isArray(e.notes) ? e.notes.slice(-3) : [],
|
|
12075
|
+
notes: Array.isArray(e.notes) ? e.notes.filter((n) => !tf || finLearnNormTf(n.tf) === tf).slice(-3) : [],
|
|
12076
|
+
mistakes: Array.isArray(e.mistakes) ? e.mistakes.filter((m) => !tf || finLearnNormTf(m.tf) === tf).slice(-5) : [],
|
|
11847
12077
|
};
|
|
11848
12078
|
}
|
|
11849
12079
|
const stats = {};
|
|
11850
|
-
for (const [s, e] of Object.entries(learn.symbols)) stats[s] =
|
|
12080
|
+
for (const [s, e] of Object.entries(learn.symbols)) stats[s] = finLearnNormStats(e.stats);
|
|
11851
12081
|
return { ok: true, count: Object.keys(stats).length, stats };
|
|
11852
12082
|
},
|
|
11853
|
-
add: ({ symbol, text, kind, tags, sid } = {}) => {
|
|
12083
|
+
add: ({ symbol, text, kind, tags, timeframe, sid } = {}) => {
|
|
11854
12084
|
const sym = norm(symbol);
|
|
11855
12085
|
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
12086
|
+
const tf = finLearnNormTf(timeframe);
|
|
11856
12087
|
if (!sym || !t) return { ok: false, error: 'symbol ve text gerekli' };
|
|
11857
12088
|
/* KALİTE KAPISI: kısa + somut + tekrar kullanılabilir — günlük tutma */
|
|
11858
12089
|
const q = finLearnNoteQuality(t);
|
|
@@ -11860,10 +12091,10 @@ function finLearnApi() {
|
|
|
11860
12091
|
const learn = finLearnLoad();
|
|
11861
12092
|
const e = finLearnSym(sym);
|
|
11862
12093
|
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 */
|
|
12094
|
+
/* aynı ders tekrar yazılmasın (son 40 kayıtta normalize metin + periyot) —
|
|
12095
|
+
kopyala/yapıştır ve noktalama oyunlarıyla depo şişirilemez */
|
|
11865
12096
|
const key = finLearnNoteKey(t);
|
|
11866
|
-
if (key && e.notes.slice(-40).some((n) => finLearnNoteKey(n.text) === key)) {
|
|
12097
|
+
if (key && e.notes.slice(-40).some((n) => finLearnNoteKey(n.text) === key && finLearnNormTf(n.tf) === tf)) {
|
|
11867
12098
|
return { ok: true, duplicate: true, symbol: sym, count: e.notes.length };
|
|
11868
12099
|
}
|
|
11869
12100
|
/* GÜNLÜK DERS LİMİTİ: ajan aynı gün aynı sembole ders yağdıramaz —
|
|
@@ -11884,6 +12115,7 @@ function finLearnApi() {
|
|
|
11884
12115
|
text: t.slice(0, FIN_LEARN_NOTE_MAX),
|
|
11885
12116
|
kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
|
|
11886
12117
|
tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
|
|
12118
|
+
tf: tf || '',
|
|
11887
12119
|
sid: String(sid || ''),
|
|
11888
12120
|
};
|
|
11889
12121
|
e.notes.push(note);
|
|
@@ -11893,7 +12125,7 @@ function finLearnApi() {
|
|
|
11893
12125
|
/* OPENCODE TARZI COMPACTION: notlar birikince ESKİLERİ arka planda
|
|
11894
12126
|
özetle — ajanı bekletmez, hafıza kendiliğinden sadeleşir */
|
|
11895
12127
|
if (e.notes.length > FIN_LEARN_COMPACT_AT) finLearnCompactAuto(sym);
|
|
11896
|
-
return { ok: true, id: note.id, symbol: sym, count: e.notes.length };
|
|
12128
|
+
return { ok: true, id: note.id, symbol: sym, timeframe: tf || '', count: e.notes.length };
|
|
11897
12129
|
},
|
|
11898
12130
|
remove: ({ id, symbol } = {}) => {
|
|
11899
12131
|
const learn = finLearnLoad();
|
|
@@ -12446,8 +12678,8 @@ function finTraderBrief(agent) {
|
|
|
12446
12678
|
const syms = agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ');
|
|
12447
12679
|
const roleDef = finRoleDef(agent && agent.role);
|
|
12448
12680
|
const who = agent && agent.main ? 'TRADER' : roleDef ? roleDef.label.toUpperCase() : 'FINANCE AJANI';
|
|
12449
|
-
const playbook = agent && agent.main && f.roleSkills &&
|
|
12450
|
-
? f.roleSkills.trader
|
|
12681
|
+
const playbook = agent && agent.main && f.roleSkills && Array.isArray(f.roleSkills.trader)
|
|
12682
|
+
? f.roleSkills.trader.filter(Boolean).join(' + ')
|
|
12451
12683
|
: '';
|
|
12452
12684
|
return [
|
|
12453
12685
|
`Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
|
|
@@ -12471,11 +12703,11 @@ function finTraderBrief(agent) {
|
|
|
12471
12703
|
? '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
12704
|
: f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
12473
12705
|
roleDef && f.strategy ? `Sahibinin strateji notu (bu çerçevede analiz et): ${f.strategy}` : '',
|
|
12474
|
-
playbook ? `PLAYBOOK (ZORUNLU skill):
|
|
12706
|
+
playbook ? `PLAYBOOK (ZORUNLU skill): ${playbook} — sistem promptunda tam metinleri verilmiştir; her turda birebir uygula.` : '',
|
|
12475
12707
|
f.shadowMode ? 'SHADOW MOD AÇIK: emir gönderilmez — kararlarını gerekçesiyle raporla, gerçek işlem açılmaz.' : '',
|
|
12476
12708
|
'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
12709
|
'Önemli kararların gerekçesini mt5_note ile günlüğe yaz (haftalık performans raporu bu notları kullanır).',
|
|
12478
|
-
'ÖĞRENME (
|
|
12710
|
+
'ÖĞ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
12711
|
'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
12712
|
'⚡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
12713
|
'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
|
|
@@ -12637,6 +12869,441 @@ function finTeamWaitTick(sid) {
|
|
|
12637
12869
|
finAgentRound(sid, { noTeamWait: true });
|
|
12638
12870
|
}
|
|
12639
12871
|
|
|
12872
|
+
/* ---------- TYPESAFE-ONLY FİNANS MODU (LLM'SİZ) ----------
|
|
12873
|
+
Rol→Skill eşleştirmesinde "typesafe-ai" seçiliyse ajan LLM HİÇ kullanmaz:
|
|
12874
|
+
tur verisi (fiyat/gösterge/pozisyon/öğrenme geçmişi/ekip yanıtları) YALNIZ
|
|
12875
|
+
TypeSafe System One'a gider; yanıtlar olasılık olarak döner, kararı KOD
|
|
12876
|
+
uygular. Yanıtlar AJAN DM ekip grubuna yazılır — tüm ajanlar birbirinin
|
|
12877
|
+
cevabını görür (son yanıtlar sonraki turların state'ine de girer) ve emirler
|
|
12878
|
+
YALNIZ market buy/sell olarak açılır (bekleyen emir YOK). */
|
|
12879
|
+
const FIN_TS_ACTION_P = 0.6; /* choice: işlem için min olasılık */
|
|
12880
|
+
const FIN_TS_MARGIN_P = 0.12; /* birinci ile ikinci seçenek arası min fark */
|
|
12881
|
+
const FIN_TS_CONFIRM_P = 0.55; /* noul teyit eşiği */
|
|
12882
|
+
const FIN_TS_EXIT_P = 0.62; /* pozisyon kapatma eşiği */
|
|
12883
|
+
const FIN_TS_MAX_SYMBOLS = 3; /* tur başına işlenecek sembol tavanı */
|
|
12884
|
+
const FIN_TS_ROLE_TF = { technic: 'M15', risk: 'H1', macro: 'H4', visual: 'M15' };
|
|
12885
|
+
const FIN_TS_ROLE_CRIT = {
|
|
12886
|
+
technic: {
|
|
12887
|
+
yukari: 'Boğa yapısı — HH/HL, yükseliş trendi, kırılım teyidi',
|
|
12888
|
+
asagi: 'Ayı yapısı — LH/LL, düşüş trendi, kırılım teyidi',
|
|
12889
|
+
yatay: 'Yatay/range — yön teyidi yok',
|
|
12890
|
+
},
|
|
12891
|
+
risk: {
|
|
12892
|
+
uygun: 'Risk limitleri uygun — pozisyon açılabilir',
|
|
12893
|
+
dikkat: 'Sınırda — küçük lot / ek teyit şart',
|
|
12894
|
+
uygundegil: 'Uygun değil — açma, mevcudu küçült',
|
|
12895
|
+
},
|
|
12896
|
+
macro: {
|
|
12897
|
+
pozitif: 'Makro akış pozitif (faiz/veri/jeopolitik destekliyor)',
|
|
12898
|
+
negatif: 'Makro akış negatif (risk iştahı bozuk)',
|
|
12899
|
+
notr: 'Makro etki nötr/belirsiz',
|
|
12900
|
+
},
|
|
12901
|
+
visual: {
|
|
12902
|
+
yukari: 'Grafik yapısı yukarı (formasyon/trend yukarı)',
|
|
12903
|
+
asagi: 'Grafik yapısı aşağı',
|
|
12904
|
+
yatay: 'Yatay/sıkışma — formasyon yok',
|
|
12905
|
+
},
|
|
12906
|
+
};
|
|
12907
|
+
|
|
12908
|
+
function finTsAgentMode(agent) {
|
|
12909
|
+
try {
|
|
12910
|
+
const f = finCfg();
|
|
12911
|
+
const key = agent && agent.main ? 'trader' : String((agent && agent.role) || '');
|
|
12912
|
+
if (!key) return false;
|
|
12913
|
+
const list = f.roleSkills && Array.isArray(f.roleSkills[key]) ? f.roleSkills[key] : [];
|
|
12914
|
+
return list.some((s) => String(s || '').toLowerCase() === 'typesafe-ai');
|
|
12915
|
+
} catch {
|
|
12916
|
+
return false;
|
|
12917
|
+
}
|
|
12918
|
+
}
|
|
12919
|
+
|
|
12920
|
+
function finTsWho(agent, extra) {
|
|
12921
|
+
const role = agent && agent.main ? 'TRADER' : ((finRoleDef(agent && agent.role) || {}).label || 'FİNANS').toUpperCase();
|
|
12922
|
+
return 'TypeSafe · ' + role + (extra ? ' · ' + extra : '');
|
|
12923
|
+
}
|
|
12924
|
+
|
|
12925
|
+
/* TypeSafe yanıtını AJAN DM ekip grubuna yaz (panelde görünür) + state için
|
|
12926
|
+
besleme tamponuna ekle (ajanlar birbirini sonraki turda görür) */
|
|
12927
|
+
function finTsPost(sid, agent, text, topic) {
|
|
12928
|
+
const body = String(text || '').trim();
|
|
12929
|
+
if (!body) return;
|
|
12930
|
+
try {
|
|
12931
|
+
if (engine && typeof engine.agentDmGroupPost === 'function') {
|
|
12932
|
+
engine.agentDmGroupPost({
|
|
12933
|
+
gid: FIN_TEAM_GID,
|
|
12934
|
+
title: FIN_TEAM_TITLE,
|
|
12935
|
+
fromSid: String(sid),
|
|
12936
|
+
fromTitle: finTsWho(agent),
|
|
12937
|
+
topic: String(topic || 'typesafe'),
|
|
12938
|
+
text: body,
|
|
12939
|
+
});
|
|
12940
|
+
}
|
|
12941
|
+
} catch {}
|
|
12942
|
+
try {
|
|
12943
|
+
financeState.tsFeed = Array.isArray(financeState.tsFeed) ? financeState.tsFeed : [];
|
|
12944
|
+
financeState.tsFeed.push({ at: Date.now(), sid: String(sid), who: finTsWho(agent, topic), text: body });
|
|
12945
|
+
while (financeState.tsFeed.length > 30) financeState.tsFeed.shift();
|
|
12946
|
+
} catch {}
|
|
12947
|
+
}
|
|
12948
|
+
|
|
12949
|
+
function finTsFeedText(limit) {
|
|
12950
|
+
const feed = Array.isArray(financeState.tsFeed) ? financeState.tsFeed.slice(-(Math.max(1, Number(limit) || 6))) : [];
|
|
12951
|
+
if (!feed.length) return '(ekip yanıtı yok)';
|
|
12952
|
+
return feed.map((x) => `- ${x.who}: ${String(x.text || '').replace(/\s+/g, ' ').slice(0, 220)}`).join('\n');
|
|
12953
|
+
}
|
|
12954
|
+
|
|
12955
|
+
/* ZAMAN DİLİMİ BOT KARARI: önce öğrenmede en iyi net getiren periyot (>=3
|
|
12956
|
+
işlem), sonra ajanın seçimi, sonra rol varsayılanı */
|
|
12957
|
+
function finTsPickTf(agent, symbol) {
|
|
12958
|
+
try {
|
|
12959
|
+
const sym = String(symbol || '').toUpperCase();
|
|
12960
|
+
const e = finLearnLoad().symbols[sym];
|
|
12961
|
+
const byTf = (e && e.stats && e.stats.byTf) || {};
|
|
12962
|
+
let best = '';
|
|
12963
|
+
let bestNet = -Infinity;
|
|
12964
|
+
for (const [tf, s] of Object.entries(byTf)) {
|
|
12965
|
+
if (!s || Number(s.trades) < 3) continue;
|
|
12966
|
+
const n = Number(s.net) || 0;
|
|
12967
|
+
if (n > bestNet) { bestNet = n; best = tf; }
|
|
12968
|
+
}
|
|
12969
|
+
if (best) return best;
|
|
12970
|
+
} catch {}
|
|
12971
|
+
const role = String((agent && agent.role) || '');
|
|
12972
|
+
return finLearnNormTf(agent && agent.tsTf) || FIN_TS_ROLE_TF[role] || 'M15';
|
|
12973
|
+
}
|
|
12974
|
+
|
|
12975
|
+
function finTsChoice(ans, id) {
|
|
12976
|
+
const a = ans && ans.answers && ans.answers[id];
|
|
12977
|
+
if (!a || a.type !== 'choice') return null;
|
|
12978
|
+
const probs = a.probabilities && typeof a.probabilities === 'object' ? a.probabilities : {};
|
|
12979
|
+
const top = String(a.choice || '');
|
|
12980
|
+
let p = Number(probs[top]);
|
|
12981
|
+
if (!isFinite(p)) { p = 0; for (const v of Object.values(probs)) p = Math.max(p, Number(v) || 0); }
|
|
12982
|
+
let second = 0;
|
|
12983
|
+
for (const [k, v] of Object.entries(probs)) {
|
|
12984
|
+
if (k === top) continue;
|
|
12985
|
+
second = Math.max(second, Number(v) || 0);
|
|
12986
|
+
}
|
|
12987
|
+
return { choice: top, p, second, conf: Number(a.confidence) || 0, probs };
|
|
12988
|
+
}
|
|
12989
|
+
|
|
12990
|
+
function finTsNoul(ans, id) {
|
|
12991
|
+
const a = ans && ans.answers && ans.answers[id];
|
|
12992
|
+
if (!a || a.type !== 'noul') return null;
|
|
12993
|
+
const n = Number(a.noul);
|
|
12994
|
+
return isFinite(n) ? n : null;
|
|
12995
|
+
}
|
|
12996
|
+
|
|
12997
|
+
function finTsScore(ans, id) {
|
|
12998
|
+
const a = ans && ans.answers && ans.answers[id];
|
|
12999
|
+
if (!a || a.type !== 'score') return null;
|
|
13000
|
+
return { score: Number(a.score) || 0, conf: Number(a.confidence) || 0, probs: a.probabilities || {} };
|
|
13001
|
+
}
|
|
13002
|
+
|
|
13003
|
+
function finTsFmtChoice(c) {
|
|
13004
|
+
if (!c) return 'yanıt yok';
|
|
13005
|
+
const probs = Object.entries(c.probs || {}).map(([k, v]) => `${k}=${(Number(v) || 0).toFixed(2)}`).join(' ');
|
|
13006
|
+
return `${c.choice} (p=${c.p.toFixed(2)}, güven=${c.conf.toFixed(2)})` + (probs ? ' [' + probs + ']' : '');
|
|
13007
|
+
}
|
|
13008
|
+
|
|
13009
|
+
function finTsIndLine(ind) {
|
|
13010
|
+
if (!ind || ind.ok !== true) return null;
|
|
13011
|
+
const g = (k) => {
|
|
13012
|
+
const row = ind.indicators && ind.indicators[k];
|
|
13013
|
+
return row && Array.isArray(row.last) ? Number(row.last[row.last.length - 1]) : null;
|
|
13014
|
+
};
|
|
13015
|
+
return {
|
|
13016
|
+
atr14: g('ATR(14)'),
|
|
13017
|
+
ema50: g('EMA(50)'),
|
|
13018
|
+
ema200: g('EMA(200)'),
|
|
13019
|
+
rsi14: g('RSI(14)'),
|
|
13020
|
+
close: Number(ind.close) || null,
|
|
13021
|
+
range20: ind.range20 || null,
|
|
13022
|
+
};
|
|
13023
|
+
}
|
|
13024
|
+
|
|
13025
|
+
function finTsSchedule(sid, agent, sec) {
|
|
13026
|
+
if (!agent || !financeState.agents.has(String(sid))) return;
|
|
13027
|
+
clearTimeout(agent.timer);
|
|
13028
|
+
const s = Math.max(15, Math.min(3600, Math.round(Number(sec) || finPaceSec())));
|
|
13029
|
+
agent.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, s * 1000);
|
|
13030
|
+
}
|
|
13031
|
+
|
|
13032
|
+
function finTypeSafeKick(sid, agent) {
|
|
13033
|
+
if (!agent) return;
|
|
13034
|
+
clearTimeout(agent.timer);
|
|
13035
|
+
agent.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, 800);
|
|
13036
|
+
}
|
|
13037
|
+
|
|
13038
|
+
/* LLM'siz TypeSafe turu: veri → TypeSafe (noul/choice/score) → kod kararı →
|
|
13039
|
+
AJAN DM yayını. Hata durumunda tur atlanır, LLM'e ASLA düşülmez. */
|
|
13040
|
+
async function finTypeSafeRound(sid, agent) {
|
|
13041
|
+
if (!agent || !engine || agent.tsBusy) return;
|
|
13042
|
+
agent.tsBusy = true;
|
|
13043
|
+
const sidS = String(sid);
|
|
13044
|
+
const isTrader = !!agent.main;
|
|
13045
|
+
const role = String(agent.role || '');
|
|
13046
|
+
const roleLabel = isTrader ? 'TRADER (ana karar verici)' : ((finRoleDef(role) || {}).label || role || 'finans');
|
|
13047
|
+
try {
|
|
13048
|
+
if (!typesafeMod.cfg().apiKey) {
|
|
13049
|
+
const now = Date.now();
|
|
13050
|
+
if (!agent.tsNoKeyAt || now - agent.tsNoKeyAt > 10 * 60 * 1000) {
|
|
13051
|
+
agent.tsNoKeyAt = now;
|
|
13052
|
+
finTsPost(sidS, agent, '⚠ TypeSafe API anahtarı yok — bu rol typesafe-ai modunda ve LLM KULLANILMIYOR; tur atlanıyor. Sahibe söyle: Ayarlar → TypeSafe sekmesinden anahtar girilmeli.', 'anahtar');
|
|
13053
|
+
}
|
|
13054
|
+
return;
|
|
13055
|
+
}
|
|
13056
|
+
if (!mt5bridge.running) {
|
|
13057
|
+
const now = Date.now();
|
|
13058
|
+
if (!agent.tsNoBridgeAt || now - agent.tsNoBridgeAt > 5 * 60 * 1000) {
|
|
13059
|
+
agent.tsNoBridgeAt = now;
|
|
13060
|
+
finTsPost(sidS, agent, '⚠ MT5 köprüsü bağlı değil — TypeSafe turu atlandı.', 'köprü');
|
|
13061
|
+
}
|
|
13062
|
+
return;
|
|
13063
|
+
}
|
|
13064
|
+
const f = finCfg();
|
|
13065
|
+
const symbols = (Array.isArray(agent.symbols) && agent.symbols.length ? agent.symbols : f.symbols || [])
|
|
13066
|
+
.map((x) => String(x || '').trim().toUpperCase()).filter(Boolean).slice(0, FIN_TS_MAX_SYMBOLS);
|
|
13067
|
+
if (!symbols.length) {
|
|
13068
|
+
const now = Date.now();
|
|
13069
|
+
if (!agent.tsNoSymAt || now - agent.tsNoSymAt > 10 * 60 * 1000) {
|
|
13070
|
+
agent.tsNoSymAt = now;
|
|
13071
|
+
finTsPost(sidS, agent, '⚠ İzleme listesi boş — TypeSafe kararı üretilemiyor. Panele sembol ekle.', 'sembol');
|
|
13072
|
+
}
|
|
13073
|
+
return;
|
|
13074
|
+
}
|
|
13075
|
+
agent.round = (Number(agent.round) || 0) + 1;
|
|
13076
|
+
if (isTrader) {
|
|
13077
|
+
financeState.traderRounds = agent.round;
|
|
13078
|
+
finPush('trader', { state: 'running', round: agent.round, mode: 'typesafe' });
|
|
13079
|
+
}
|
|
13080
|
+
/* VERİ: hesap + pozisyonlar + sembol fiyat/gösterge — hepsi KOD ile toplanır,
|
|
13081
|
+
yalnız TypeSafe'e gönderilir; hiçbir LLM çağrısı yapılmaz */
|
|
13082
|
+
const [accR, posR] = await Promise.all([
|
|
13083
|
+
financetools.handlers.mt5_account({}),
|
|
13084
|
+
financetools.handlers.mt5_positions({}),
|
|
13085
|
+
]);
|
|
13086
|
+
const account = (accR && accR.account) || {};
|
|
13087
|
+
const posList = (posR && posR.positions) || [];
|
|
13088
|
+
const market = {};
|
|
13089
|
+
for (const sym of symbols) {
|
|
13090
|
+
const tf = finTsPickTf(agent, sym);
|
|
13091
|
+
const [mktR, indR] = await Promise.all([
|
|
13092
|
+
financetools.handlers.mt5_market({ symbols: [sym] }),
|
|
13093
|
+
financetools.handlers.mt5_indicators({ symbol: sym, timeframe: tf, count: 300, indicators: ['ATR(14)', 'EMA(50)', 'EMA(200)', 'RSI(14)'] }),
|
|
13094
|
+
]);
|
|
13095
|
+
market[sym] = {
|
|
13096
|
+
row: (mktR && mktR.symbols && mktR.symbols[0]) || null,
|
|
13097
|
+
tf,
|
|
13098
|
+
ind: finTsIndLine(indR),
|
|
13099
|
+
};
|
|
13100
|
+
}
|
|
13101
|
+
const state = {
|
|
13102
|
+
rol: roleLabel,
|
|
13103
|
+
yerel_saat: new Date().getHours(),
|
|
13104
|
+
hesap: {
|
|
13105
|
+
balance: Number(account.balance) || 0,
|
|
13106
|
+
equity: Number(account.equity) || 0,
|
|
13107
|
+
margin_free: Number(account.margin_free) || 0,
|
|
13108
|
+
acik_pozisyon: posList.length,
|
|
13109
|
+
},
|
|
13110
|
+
limitler: { max_pozisyon: Number(f.maxPositions) || 3, min_lot: Number(f.minLot) || 0.01, max_lot: Number(f.maxLot) || 0.1 },
|
|
13111
|
+
pozisyonlar: posList.map((p) => ({
|
|
13112
|
+
ticket: p.ticket, sembol: p.symbol, yon: Number(p.type) === 0 ? 'buy' : 'sell',
|
|
13113
|
+
hacim: p.volume, giris: p.price_open, sl: p.sl, tp: p.tp, kar: p.profit,
|
|
13114
|
+
})),
|
|
13115
|
+
piyasa: {},
|
|
13116
|
+
ogrenme: finBuildLearnDigest(symbols),
|
|
13117
|
+
ekip_yanitlari: finTsFeedText(6),
|
|
13118
|
+
};
|
|
13119
|
+
for (const sym of symbols) {
|
|
13120
|
+
const d = market[sym];
|
|
13121
|
+
const row = d.row || {};
|
|
13122
|
+
state.piyasa[sym] = {
|
|
13123
|
+
zaman_dilimi: d.tf,
|
|
13124
|
+
bid: Number(row.bid) || null,
|
|
13125
|
+
ask: Number(row.ask) || null,
|
|
13126
|
+
spread: Number(row.spread) || null,
|
|
13127
|
+
gosterge: d.ind,
|
|
13128
|
+
};
|
|
13129
|
+
}
|
|
13130
|
+
|
|
13131
|
+
/* ---------------- TRADER: yön + teyit + kapat kararı ---------------- */
|
|
13132
|
+
if (isTrader) {
|
|
13133
|
+
const posBySym = {};
|
|
13134
|
+
for (const p of posList) {
|
|
13135
|
+
const s = String(p.symbol || '').toUpperCase();
|
|
13136
|
+
if (symbols.includes(s)) posBySym[s] = p;
|
|
13137
|
+
}
|
|
13138
|
+
const questions = {};
|
|
13139
|
+
symbols.forEach((sym, i) => {
|
|
13140
|
+
questions['yon_' + i] = {
|
|
13141
|
+
type: 'choice',
|
|
13142
|
+
instructions: `${sym} ${market[sym].tf} grafiğinde verilen fiyat, gösterge, pozisyon, öğrenme geçmişi ve ekip yanıtlarına göre kısa vadeli doğru aksiyon nedir? Emin değilsen bekle.`,
|
|
13143
|
+
criteria: {
|
|
13144
|
+
buy: 'LONG aç — yükseliş teyidi var (yapı/momentum/EMA eğimi destekliyor)',
|
|
13145
|
+
sell: 'SHORT aç — düşüş teyidi var',
|
|
13146
|
+
bekle: 'İşlem açma — teyit yok, belirsiz ya da riskli',
|
|
13147
|
+
},
|
|
13148
|
+
};
|
|
13149
|
+
questions['teyit_' + i] = {
|
|
13150
|
+
type: 'noul',
|
|
13151
|
+
instructions: `${sym} fiyat yapısı ve momentum, 'yon_' + ${i} kararını gerçekten destekliyor mu?`,
|
|
13152
|
+
criteria: { true: 'Net destekliyor', false: 'Zayıf/çelişkili' },
|
|
13153
|
+
};
|
|
13154
|
+
if (posBySym[sym]) {
|
|
13155
|
+
const pp = posBySym[sym];
|
|
13156
|
+
questions['kapat_' + i] = {
|
|
13157
|
+
type: 'noul',
|
|
13158
|
+
instructions: `${sym} açık pozisyon (${Number(pp.type) === 0 ? 'buy' : 'sell'}, kâr ${Number(pp.profit) || 0}) ŞİMDİ kapatılmalı mı? Kârı koruma, momentum dönüşü veya SL/TP yakınlığını değerlendir.`,
|
|
13159
|
+
criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
|
|
13160
|
+
};
|
|
13161
|
+
}
|
|
13162
|
+
});
|
|
13163
|
+
let ans = null;
|
|
13164
|
+
try {
|
|
13165
|
+
ans = await finTsAsk(state, questions);
|
|
13166
|
+
} catch (e) {
|
|
13167
|
+
const now = Date.now();
|
|
13168
|
+
if (!agent.tsErrAt || now - agent.tsErrAt > 2 * 60 * 1000) {
|
|
13169
|
+
agent.tsErrAt = now;
|
|
13170
|
+
finTsPost(sidS, agent, '⚠ TypeSafe çağrısı başarısız: ' + String((e && e.message) || e).slice(0, 200), 'hata');
|
|
13171
|
+
}
|
|
13172
|
+
return;
|
|
13173
|
+
}
|
|
13174
|
+
const lines = [];
|
|
13175
|
+
let opened = 0;
|
|
13176
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
13177
|
+
const sym = symbols[i];
|
|
13178
|
+
if (!financeState.agents.has(sidS)) return;
|
|
13179
|
+
const c = finTsChoice(ans, 'yon_' + i);
|
|
13180
|
+
const conf = finTsNoul(ans, 'teyit_' + i);
|
|
13181
|
+
const pos = posBySym[sym];
|
|
13182
|
+
if (pos) {
|
|
13183
|
+
const ex = finTsNoul(ans, 'kapat_' + i);
|
|
13184
|
+
if (ex != null && ex >= FIN_TS_EXIT_P) {
|
|
13185
|
+
const r = await financetools.handlers.mt5_close(
|
|
13186
|
+
{ ticket: Number(pos.ticket), reason: `TypeSafe: kapat p=${ex.toFixed(2)}` },
|
|
13187
|
+
{ sessionId: sidS }
|
|
13188
|
+
);
|
|
13189
|
+
lines.push(`- ${sym}: KAPAT (p=${ex.toFixed(2)}) ${r && r.ok ? '✓ pozisyon kapatıldı' : '✗ ' + ((r && r.error) || 'hata')}`);
|
|
13190
|
+
} else {
|
|
13191
|
+
lines.push(`- ${sym}: pozisyon açık (kapat=${ex == null ? 'yanıt yok' : ex.toFixed(2)} < ${FIN_TS_EXIT_P}) — tez sürüyor`);
|
|
13192
|
+
}
|
|
13193
|
+
continue;
|
|
13194
|
+
}
|
|
13195
|
+
if (!c || (c.choice !== 'buy' && c.choice !== 'sell')) {
|
|
13196
|
+
lines.push(`- ${sym}: ${finTsFmtChoice(c)} → BEKLE`);
|
|
13197
|
+
continue;
|
|
13198
|
+
}
|
|
13199
|
+
if (!(c.p >= FIN_TS_ACTION_P) || c.p - c.second < FIN_TS_MARGIN_P) {
|
|
13200
|
+
lines.push(`- ${sym}: ${finTsFmtChoice(c)} → eşik/fark altı, BEKLE`);
|
|
13201
|
+
continue;
|
|
13202
|
+
}
|
|
13203
|
+
if (conf == null || conf < FIN_TS_CONFIRM_P) {
|
|
13204
|
+
lines.push(`- ${sym}: ${finTsFmtChoice(c)} · teyit=${conf == null ? 'yok' : conf.toFixed(2)} < ${FIN_TS_CONFIRM_P} → BEKLE`);
|
|
13205
|
+
continue;
|
|
13206
|
+
}
|
|
13207
|
+
if (opened >= 1 || posList.length >= (Number(f.maxPositions) || 3)) {
|
|
13208
|
+
lines.push(`- ${sym}: sinyal güçlü (${c.choice} p=${c.p.toFixed(2)}) ama pozisyon sınırı dolu — bu tur açılmadı`);
|
|
13209
|
+
continue;
|
|
13210
|
+
}
|
|
13211
|
+
/* SL/TP KOD ile: ATR(14) tabanlı — tip ve seviyeler deterministik */
|
|
13212
|
+
const row = market[sym].row || {};
|
|
13213
|
+
const price = c.choice === 'buy' ? Number(row.ask) || Number(row.bid) : Number(row.bid) || Number(row.ask);
|
|
13214
|
+
if (!(price > 0)) { lines.push(`- ${sym}: fiyat okunamadı`); continue; }
|
|
13215
|
+
const atr = Number(market[sym].ind && market[sym].ind.atr14) || 0;
|
|
13216
|
+
const slDist = Math.max(atr > 0 ? atr * 1.5 : price * 0.003, price * 0.0004);
|
|
13217
|
+
const tpDist = Math.max(atr > 0 ? atr * 2.5 : price * 0.006, slDist * 1.2);
|
|
13218
|
+
const digits = Math.max(0, Math.min(8, Number(row.digits) || 2));
|
|
13219
|
+
const rnd = (v) => Number(Number(v).toFixed(digits));
|
|
13220
|
+
const sl = rnd(c.choice === 'buy' ? price - slDist : price + slDist);
|
|
13221
|
+
const tp = rnd(c.choice === 'buy' ? price + tpDist : price - tpDist);
|
|
13222
|
+
const riskPct = Number(f.riskPerTradePct) > 0 ? Number(f.riskPerTradePct) : 0.5;
|
|
13223
|
+
const r = await financetools.handlers.mt5_trade(
|
|
13224
|
+
{
|
|
13225
|
+
symbol: sym,
|
|
13226
|
+
side: c.choice,
|
|
13227
|
+
type: 'market',
|
|
13228
|
+
sl,
|
|
13229
|
+
tp,
|
|
13230
|
+
riskPct,
|
|
13231
|
+
timeframe: market[sym].tf,
|
|
13232
|
+
comment: 'TS',
|
|
13233
|
+
reason: `TypeSafe: yön ${c.choice} p=${c.p.toFixed(2)} fark=${(c.p - c.second).toFixed(2)} teyit=${conf.toFixed(2)}`,
|
|
13234
|
+
},
|
|
13235
|
+
{ sessionId: sidS }
|
|
13236
|
+
);
|
|
13237
|
+
if (r && r.ok) {
|
|
13238
|
+
opened += 1;
|
|
13239
|
+
lines.push(`- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, risk %${riskPct}) · p=${c.p.toFixed(2)} teyit=${conf.toFixed(2)} · tf=${market[sym].tf}`);
|
|
13240
|
+
} else {
|
|
13241
|
+
lines.push(`- ${sym}: ${c.choice.toUpperCase()} sinyali reddedildi — ${(r && r.error) || 'hata'}`);
|
|
13242
|
+
}
|
|
13243
|
+
}
|
|
13244
|
+
finTsPost(
|
|
13245
|
+
sidS,
|
|
13246
|
+
agent,
|
|
13247
|
+
`🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
|
|
13248
|
+
'TF KARARI: ' + symbols.map((s) => `${s}=${market[s].tf}`).join(', ') + '\n' +
|
|
13249
|
+
lines.join('\n') +
|
|
13250
|
+
(opened ? '' : '\nYeni pozisyon açılmadı (eşikler/limitler).'),
|
|
13251
|
+
symbols.join(',')
|
|
13252
|
+
);
|
|
13253
|
+
return;
|
|
13254
|
+
}
|
|
13255
|
+
|
|
13256
|
+
/* ---------------- ROL AJANI: analiz yanıtları (işlem açmaz) ---------------- */
|
|
13257
|
+
const questions = {};
|
|
13258
|
+
const crit = FIN_TS_ROLE_CRIT[role] || FIN_TS_ROLE_CRIT.technic;
|
|
13259
|
+
symbols.forEach((sym, i) => {
|
|
13260
|
+
questions['bias_' + i] = {
|
|
13261
|
+
type: 'choice',
|
|
13262
|
+
instructions: `${sym} ${market[sym].tf} için ${roleLabel} rolünde yön/eğilim değerlendirmen nedir? Verilen fiyat, gösterge, öğrenme geçmişi ve ekip yanıtlarını kullan.`,
|
|
13263
|
+
criteria: crit,
|
|
13264
|
+
};
|
|
13265
|
+
questions['guc_' + i] = {
|
|
13266
|
+
type: 'score',
|
|
13267
|
+
instructions: `${sym} için bu değerlendirmenin gücü nedir? İstatistik ve gösterge teyidi zayıfsa düşük puan ver.`,
|
|
13268
|
+
criteria: ['çok zayıf', 'zayıf', 'orta', 'güçlü', 'çok güçlü'],
|
|
13269
|
+
};
|
|
13270
|
+
questions['risk_' + i] = {
|
|
13271
|
+
type: 'noul',
|
|
13272
|
+
instructions: `${sym} için ${roleLabel} rolünde işlemi engelleyecek bir risk/uygunsuzluk var mı (volatilite, haber, limit, yapı belirsizliği)?`,
|
|
13273
|
+
criteria: { true: 'Risk var — dikkat/engel', false: 'Risk normal' },
|
|
13274
|
+
};
|
|
13275
|
+
});
|
|
13276
|
+
let ans = null;
|
|
13277
|
+
try {
|
|
13278
|
+
ans = await finTsAsk(state, questions);
|
|
13279
|
+
} catch (e) {
|
|
13280
|
+
const now = Date.now();
|
|
13281
|
+
if (!agent.tsErrAt || now - agent.tsErrAt > 2 * 60 * 1000) {
|
|
13282
|
+
agent.tsErrAt = now;
|
|
13283
|
+
finTsPost(sidS, agent, '⚠ TypeSafe çağrısı başarısız: ' + String((e && e.message) || e).slice(0, 200), 'hata');
|
|
13284
|
+
}
|
|
13285
|
+
return;
|
|
13286
|
+
}
|
|
13287
|
+
const lines = symbols.map((sym, i) => {
|
|
13288
|
+
const b = finTsChoice(ans, 'bias_' + i);
|
|
13289
|
+
const g = finTsScore(ans, 'guc_' + i);
|
|
13290
|
+
const rk = finTsNoul(ans, 'risk_' + i);
|
|
13291
|
+
return (
|
|
13292
|
+
`- ${sym} (tf=${market[sym].tf}): ${finTsFmtChoice(b)}` +
|
|
13293
|
+
(g ? ` · güç=${g.score.toFixed(2)}/4 (güven=${g.conf.toFixed(2)})` : '') +
|
|
13294
|
+
(rk == null ? '' : ` · risk=${rk.toFixed(2)}${rk >= 0.55 ? ' ⚠' : ''}`)
|
|
13295
|
+
);
|
|
13296
|
+
});
|
|
13297
|
+
finTsPost(sidS, agent, `🧠 TypeSafe analiz (LLM yok — girdi yalnız TypeSafe):\n` + lines.join('\n'), symbols.join(','));
|
|
13298
|
+
} catch (e) {
|
|
13299
|
+
try { financeLog('[typesafe] tur hatası (' + sidS + '): ' + String((e && e.message) || e)); } catch {}
|
|
13300
|
+
} finally {
|
|
13301
|
+
agent.tsBusy = false;
|
|
13302
|
+
finTsSchedule(sidS, agent, finPaceSec());
|
|
13303
|
+
if (isTrader && financeState.agents.has(sidS)) finPush('trader', { state: 'idle', round: agent.round, mode: 'typesafe' });
|
|
13304
|
+
}
|
|
13305
|
+
}
|
|
13306
|
+
|
|
12640
13307
|
/* Bir ajanın TEK turu: (opsiyonel) ana ajan planı + tur emri */
|
|
12641
13308
|
function finAgentRound(sid, opts) {
|
|
12642
13309
|
const agent = financeState.agents.get(String(sid));
|
|
@@ -12654,6 +13321,13 @@ function finAgentRound(sid, opts) {
|
|
|
12654
13321
|
/* hâlâ çalışıyor — done eventinde tekrar planlanır */
|
|
12655
13322
|
return;
|
|
12656
13323
|
}
|
|
13324
|
+
/* TYPESAFE-ONLY MODU: roleSkills'te typesafe-ai varsa LLM HİÇ kullanılmaz —
|
|
13325
|
+
tur deterministik TypeSafe hattıyla koşar (girdi yalnız TypeSafe'e gider,
|
|
13326
|
+
cevaplar AJAN DM'e düşer, emirler yalnız market buy/sell) */
|
|
13327
|
+
if (finTsAgentMode(agent)) {
|
|
13328
|
+
finTypeSafeRound(String(sid), agent).catch(() => {});
|
|
13329
|
+
return;
|
|
13330
|
+
}
|
|
12657
13331
|
const f = finCfg();
|
|
12658
13332
|
/* EKİP BEKLEMESİ SÜRÜYOR: DM uyandırması vb. turu erkene çekemez — tick açar */
|
|
12659
13333
|
if (agent.main && agent.waitingTeam && !(opts && opts.noTeamWait)) return;
|
|
@@ -12701,8 +13375,8 @@ function finAgentRound(sid, opts) {
|
|
|
12701
13375
|
? 'SHADOW MOD AÇIK: emir GÖNDERİLMEZ — kararını teziyle raporla (günlüğe yazılır).'
|
|
12702
13376
|
: 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.';
|
|
12703
13377
|
const learnTip = roleDef
|
|
12704
|
-
? ' İlgili sembolün geçmiş derslerini (mt5_ogrenme) oku
|
|
12705
|
-
: '
|
|
13378
|
+
? ' İ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).'
|
|
13379
|
+
: ' 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
13380
|
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
12707
13381
|
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
13382
|
const launch = (planBlock) => {
|
|
@@ -13061,13 +13735,13 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
13061
13735
|
if (p.analysisTeam !== undefined) f.analysisTeam = finRolesValid(p.analysisTeam);
|
|
13062
13736
|
if (p.analysisAuto !== undefined) f.analysisAuto = !!p.analysisAuto;
|
|
13063
13737
|
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) */
|
|
13738
|
+
/* ROL → SKILL eşleştirmesi güncellemesi (modal): rol başına EN FAZLA 2
|
|
13739
|
+
skill + ana trader playbook'u (trader) */
|
|
13066
13740
|
if (p.roleSkills !== undefined && p.roleSkills && typeof p.roleSkills === 'object') {
|
|
13067
13741
|
for (const id of [...FIN_ROLES.map((d) => d.id), 'trader']) {
|
|
13068
13742
|
if (p.roleSkills[id] === undefined) continue;
|
|
13069
13743
|
f.roleSkills[id] = (Array.isArray(p.roleSkills[id]) ? p.roleSkills[id] : [])
|
|
13070
|
-
.map((s) => String(s || '').trim()).filter(Boolean).slice(0,
|
|
13744
|
+
.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 2);
|
|
13071
13745
|
}
|
|
13072
13746
|
/* koşan rol ajanlarını da tazele — seçim sonraki turdan itibaren sistem
|
|
13073
13747
|
promptuna girer (ajanı durdurmaya gerek yok) */
|
|
@@ -13173,6 +13847,15 @@ async function financeTraderStart() {
|
|
|
13173
13847
|
try { engine.setSessionModel(mainSid, f.traderSel || null); } catch {}
|
|
13174
13848
|
financeState.traderOn = true;
|
|
13175
13849
|
try { engine.clearStop(); } catch {}
|
|
13850
|
+
/* TYPESAFE-ONLY: roleSkills.trader'da typesafe-ai varsa LLM'e HİÇ gidilmez */
|
|
13851
|
+
if (finTsAgentMode(mainAgent)) {
|
|
13852
|
+
financeState.lastRoundAt = Date.now();
|
|
13853
|
+
financeLog('[trader] başlatıldı (TYPESAFE-ONLY — LLM kullanılmıyor, girdi yalnız TypeSafe)');
|
|
13854
|
+
finPush('trader', { state: 'running', round: mainAgent.round, mode: 'typesafe' });
|
|
13855
|
+
finTypeSafeKick(mainSid, mainAgent);
|
|
13856
|
+
finTeamStart(f);
|
|
13857
|
+
return { ok: true, sid: mainSid };
|
|
13858
|
+
}
|
|
13176
13859
|
const ok = engine.send(mainSid, finTraderBrief(mainAgent), { userAction: true });
|
|
13177
13860
|
if (!ok) {
|
|
13178
13861
|
financeState.traderOn = false;
|
|
@@ -13205,6 +13888,12 @@ function finTeamStart(f) {
|
|
|
13205
13888
|
try {
|
|
13206
13889
|
const created = finAgentCreate([], false, roleId);
|
|
13207
13890
|
try { engine.setSessionModel(created.s.id, f.traderSel || null); } catch {}
|
|
13891
|
+
/* TYPESAFE-ONLY: bu rolün skill eşleştirmesinde typesafe-ai varsa LLM yok */
|
|
13892
|
+
if (finTsAgentMode(created.agent)) {
|
|
13893
|
+
financeLog('[ekip] ' + roleDef.label + ' başladı (TYPESAFE-ONLY — LLM kullanılmıyor)');
|
|
13894
|
+
finTypeSafeKick(String(created.s.id), created.agent);
|
|
13895
|
+
continue;
|
|
13896
|
+
}
|
|
13208
13897
|
const ok = engine.send(created.s.id, finTraderBrief(created.agent), { userAction: true });
|
|
13209
13898
|
if (!ok) {
|
|
13210
13899
|
finAgentStop(String(created.s.id), 'oturum meşgul — ekip ajanı başlatılamadı');
|