beast-agent 2.63.7 → 2.63.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.63.7",
4
+ "version": "2.63.8",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -0,0 +1,146 @@
1
+ 'use strict';
2
+
3
+ /* ---------- TYPESAFE KARAR KALİBRASYONU (saf matematik) ----------
4
+ main süreç tskarar.json'daki kapanmış TypeSafe kararlarını buraya verir;
5
+ burada ampirik kazanç oranlarından EYLEM EŞİĞİ, RİSK ÇARPANI ve KURAL
6
+ istatistikleri hesaplanır. Saf fonksiyonlar — test edilebilir, yan etkisiz. */
7
+
8
+ const DEFAULT_TH = { action: 0.6, margin: 0.12, confirm: 0.55, exit: 0.62 };
9
+
10
+ const BANDS = [
11
+ { key: '0.50-0.60', min: 0.5, max: 0.6 },
12
+ { key: '0.60-0.70', min: 0.6, max: 0.7 },
13
+ { key: '0.70-0.80', min: 0.7, max: 0.8 },
14
+ { key: '0.80-1.00', min: 0.8, max: 1.01 },
15
+ ];
16
+
17
+ const RULES = [
18
+ { key: 'trend_uyumlu', label: 'trend uyumlu', match: (x) => x.emaAlign === 1 },
19
+ { key: 'trend_ters', label: 'trend ters', match: (x) => x.emaAlign === 0 },
20
+ { key: 'londra', label: 'Londra (07-12)', match: (x) => x.hour >= 7 && x.hour < 12 },
21
+ { key: 'newyork', label: 'New York (12-18)', match: (x) => x.hour >= 12 && x.hour < 18 },
22
+ { key: 'asya', label: 'Asya (00-07)', match: (x) => x.hour < 7 },
23
+ { key: 'p_070', label: 'p≥0.70', match: (x) => Number(x.p) >= 0.7 },
24
+ { key: 'rsi_asiri', label: 'RSI aşırı (70+/30-)', match: (x) => x.rsi != null && (x.rsi > 70 || x.rsi < 30) },
25
+ ];
26
+
27
+ function bandOf(p) {
28
+ const v = Number(p) || 0;
29
+ const b = BANDS.find((x) => v >= x.min && v < x.max);
30
+ return b ? b.key : BANDS[0].key;
31
+ }
32
+
33
+ function closedRows(decisions, symbol) {
34
+ const sym = String(symbol || '').toUpperCase();
35
+ return (Array.isArray(decisions) ? decisions : []).filter((x) => x && x.closedAt && String(x.symbol || '').toUpperCase() === sym);
36
+ }
37
+
38
+ /* KALİBRASYON: tf verilirse ve o periyotta ≥8 kapalı karar varsa periyoda
39
+ özel; yoksa sembol geneli. Eşik ancak ≥8 kapalı karar birikince oynar. */
40
+ function calibration(decisions, symbol, tf) {
41
+ let rows = closedRows(decisions, symbol);
42
+ let tfScoped = false;
43
+ if (tf) {
44
+ const tRows = rows.filter((x) => x.tf === tf);
45
+ if (tRows.length >= 8) {
46
+ rows = tRows;
47
+ tfScoped = true;
48
+ }
49
+ }
50
+ const bands = {};
51
+ let n = 0;
52
+ let wins = 0;
53
+ let net = 0;
54
+ for (const x of rows) {
55
+ const v = Number(x.net) || 0;
56
+ n += 1;
57
+ net += v;
58
+ if (v >= 0) wins += 1;
59
+ const key = bandOf(x.p);
60
+ if (!bands[key]) bands[key] = { n: 0, wins: 0, net: 0 };
61
+ bands[key].n += 1;
62
+ bands[key].net += v;
63
+ if (v >= 0) bands[key].wins += 1;
64
+ }
65
+ /* EŞİK: kazanç oranı ≥ %50 olan en düşük p bandının alt sınırı; yeterli
66
+ veri yoksa varsayılan; aralık 0.55-0.75. */
67
+ let action = DEFAULT_TH.action;
68
+ for (const band of BANDS) {
69
+ const b = bands[band.key];
70
+ if (!b || b.n < 5) continue;
71
+ if (b.wins / b.n >= 0.5) {
72
+ action = band.min;
73
+ break;
74
+ }
75
+ }
76
+ if (n < 8) action = DEFAULT_TH.action;
77
+ action = Math.max(0.55, Math.min(0.75, action));
78
+ /* RİSK ÇARPANI: üst üste kayıpta küçül, istikrarlı kazançta büyü */
79
+ const last3 = rows.slice(-3);
80
+ let riskMult = 1;
81
+ if (last3.length >= 2 && last3.every((x) => (Number(x.net) || 0) < 0)) riskMult = 0.6;
82
+ else if (n >= 8 && wins / n >= 0.6) riskMult = 1.2;
83
+ riskMult = Math.max(0.5, Math.min(1.3, riskMult));
84
+ return { n, wins, net: Math.round(net * 100) / 100, bands, action, riskMult, tfScoped };
85
+ }
86
+
87
+ /* KURAL MADENCİLİĞİ: hangi koşullarda kazanıyoruz (en az 4 örnek) */
88
+ function rules(decisions, symbol) {
89
+ const rows = closedRows(decisions, symbol);
90
+ const out = [];
91
+ for (const r of RULES) {
92
+ const set = rows.filter((x) => r.match(x));
93
+ if (set.length < 4) continue;
94
+ const wins = set.filter((x) => (Number(x.net) || 0) >= 0).length;
95
+ out.push({
96
+ key: r.key,
97
+ label: r.label,
98
+ n: set.length,
99
+ wr: wins / set.length,
100
+ net: Math.round(set.reduce((a, x) => a + (Number(x.net) || 0), 0) * 100) / 100,
101
+ });
102
+ }
103
+ out.sort((a, b) => b.wr - a.wr || b.n - a.n);
104
+ return out;
105
+ }
106
+
107
+ function rulesText(ruleRows) {
108
+ const list = Array.isArray(ruleRows) ? ruleRows : [];
109
+ if (!list.length) return '';
110
+ const good = list.filter((r) => r.wr >= 0.55).slice(0, 2).map((r) => `${r.label} %${Math.round(r.wr * 100)} (n=${r.n})`);
111
+ const bad = list.filter((r) => r.wr < 0.45).slice(-1).map((r) => `${r.label} %${Math.round(r.wr * 100)} (n=${r.n}) → kaçın`);
112
+ const parts = [];
113
+ if (good.length) parts.push('çalışan: ' + good.join(' · '));
114
+ if (bad.length) parts.push('zayıf: ' + bad.join(' · '));
115
+ return parts.join(' — ');
116
+ }
117
+
118
+ /* Digest satırı: kalibrasyon + kurallar (LLM'li ajanlar da görür) */
119
+ function calText(decisions, symbol, tf) {
120
+ const c = calibration(decisions, symbol, tf);
121
+ if (!c.n) return '';
122
+ const wr = Math.round((c.wins / c.n) * 100);
123
+ const bandTxt = Object.entries(c.bands)
124
+ .sort((a, b) => b[0].localeCompare(a[0]))
125
+ .map(([k, b]) => `${k}: %${Math.round((b.wins / b.n) * 100)} (n=${b.n})`)
126
+ .slice(0, 3)
127
+ .join(' · ');
128
+ const f = calibration(decisions, symbol, null);
129
+ const rt = rulesText(rules(decisions, symbol));
130
+ return (
131
+ `${symbol}: ${c.n} kapalı karar · %${wr} kazanç · risk ×${f.riskMult}` +
132
+ (bandTxt ? ` · band ${bandTxt}` : '') +
133
+ (rt ? ` · kural — ${rt}` : '')
134
+ );
135
+ }
136
+
137
+ /* Tur state'i için kompakt satır (TypeSafe'e giren kalibrasyon) */
138
+ function calState(decisions, symbol, tf) {
139
+ const c = calibration(decisions, symbol, tf);
140
+ if (!c.n) return `${symbol}: kalibrasyon verisi yok (henüz kapanan TypeSafe işlemi yok)`;
141
+ const wr = Math.round((c.wins / c.n) * 100);
142
+ const rt = rulesText(rules(decisions, symbol));
143
+ return `${symbol}: ${c.n} kapalı karar · %${wr} kazanç · eylem eşiği p≥${c.action.toFixed(2)} · risk ×${c.riskMult} (tf=${tf || '?'})` + (rt ? ` · ${rt}` : '');
144
+ }
145
+
146
+ module.exports = { DEFAULT_TH, BANDS, RULES, bandOf, calibration, rules, rulesText, calText, calState };
package/src/main.js CHANGED
@@ -32,6 +32,7 @@ const log = require('./agent/logger');
32
32
  const squeeze = require('./agent/squeeze');
33
33
  const chansessions = require('./agent/channelsessions');
34
34
  const typesafeMod = require('./agent/typesafe');
35
+ const tslearn = require('./agent/tslearn');
35
36
  const QRCode = require('qrcode'); /* Expo Go QR (bc-expurl) — whatsapp ile aynı paket */
36
37
 
37
38
  /* Renderer'a sır gönderirken kullanılan maske; kaydederken aynen geri gelirse
@@ -10778,6 +10779,12 @@ async function finRecordClose(ticket, st) {
10778
10779
  /* SEMBOL + PERİYOT BAZLI SÜREKLİ ÖĞRENME: her kapanış istatistiğe işlenir;
10779
10780
  zararla kapananlar yapılandırılmış hata kaydı olur (mt5_ogrenme) */
10780
10781
  finLearnRecordClose({ symbol, side: (st && st.side) || '', net: rounded, mfe, mae, reason: kind || '', timeframe: tf });
10782
+ /* TYPESAFE KARAR GÜNLÜĞÜ: kapanış kararla eşleştirilir (kalibrasyon verisi)
10783
+ ve TypeSafe'e hata/desen sınıflandırması sorulup otomatik ders yazılır */
10784
+ try {
10785
+ const dec = finTsDecClose({ symbol, side: (st && st.side) || '', net: rounded, mfe, mae, reason: kind || '' });
10786
+ if (dec) finTsAutoLesson(dec).catch(() => {});
10787
+ } catch {}
10781
10788
  const pl = rounded == null ? '' : ` · K/Z ${rounded >= 0 ? '+' : ''}${rounded.toFixed(2)}`;
10782
10789
  const line = `🏁 Pozisyon kapandı: ${symbol || '?'} #${ticket}${tf ? ' · ' + tf : ''}${pl}`;
10783
10790
  financeLog('[watchdog] ' + line);
@@ -12001,6 +12008,10 @@ function finBuildLearnDigest(symbols) {
12001
12008
  /* SON HATALAR: yapılan yanlışlar + tekrar eden kalıp uyarısı */
12002
12009
  const mLine = finBuildLearnMistakeLine(e);
12003
12010
  if (mLine) L.push(' ' + mLine);
12011
+ /* TYPESAFE KALİBRASYONU + KURALLAR: öğrenerek gelişme verisi — eşik/risk
12012
+ çarpanı ve hangi koşulun kazandırdığı her turda karar girdisine girer */
12013
+ const calLine = finTsCalText(sym, null);
12014
+ if (calLine) L.push(' ' + calLine);
12004
12015
  /* KALICI ÖZET (compaction): eski derslerin sıkıştırılmış hâli — ham ders
12005
12016
  sayısı azalsa da birikmiş bilgi prompta girer */
12006
12017
  if (e.summary && e.summary.text) {
@@ -12080,7 +12091,7 @@ function finLearnApi() {
12080
12091
  for (const [s, e] of Object.entries(learn.symbols)) stats[s] = finLearnNormStats(e.stats);
12081
12092
  return { ok: true, count: Object.keys(stats).length, stats };
12082
12093
  },
12083
- add: ({ symbol, text, kind, tags, timeframe, sid } = {}) => {
12094
+ add: ({ symbol, text, kind, tags, timeframe, sid, auto } = {}) => {
12084
12095
  const sym = norm(symbol);
12085
12096
  const t = String(text || '').replace(/\s+/g, ' ').trim();
12086
12097
  const tf = finLearnNormTf(timeframe);
@@ -12098,16 +12109,22 @@ function finLearnApi() {
12098
12109
  return { ok: true, duplicate: true, symbol: sym, count: e.notes.length };
12099
12110
  }
12100
12111
  /* GÜNLÜK DERS LİMİTİ: ajan aynı gün aynı sembole ders yağdıramaz —
12101
- hafıza öğrenmeyle zehirlenmesin, özet son dersleri temsil etsin */
12112
+ hafıza öğrenmeyle zehirlenmesin. OTOMATİK (TypeSafe) derslerin ayrı
12113
+ kotası vardır (3/24s): ajan ders hakkını onlar yemesin */
12102
12114
  const dayAgo = Date.now() - 24 * 60 * 60 * 1000;
12103
- const today = e.notes.filter((n) => Number(n.at) >= dayAgo).length;
12104
- if (today >= FIN_LEARN_MAX_NOTES_DAY) {
12105
- return {
12106
- ok: false,
12107
- error: `günlük ders limiti doldu (${FIN_LEARN_MAX_NOTES_DAY}/24s) — ders kaydedilmedi`,
12108
- symbol: sym,
12109
- count: e.notes.length,
12110
- };
12115
+ if (auto) {
12116
+ const autoToday = e.notes.filter((n) => n && n.auto && Number(n.at) >= dayAgo).length;
12117
+ if (autoToday >= 3) return { ok: true, skipped: true, reason: 'otomatik ders kotası doldu (3/24s)', symbol: sym };
12118
+ } else {
12119
+ const today = e.notes.filter((n) => n && !n.auto && Number(n.at) >= dayAgo).length;
12120
+ if (today >= FIN_LEARN_MAX_NOTES_DAY) {
12121
+ return {
12122
+ ok: false,
12123
+ error: `günlük ders limiti doldu (${FIN_LEARN_MAX_NOTES_DAY}/24s) — ders kaydedilmedi`,
12124
+ symbol: sym,
12125
+ count: e.notes.length,
12126
+ };
12127
+ }
12111
12128
  }
12112
12129
  const note = {
12113
12130
  id: 'l' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
@@ -12116,6 +12133,7 @@ function finLearnApi() {
12116
12133
  kind: ['pattern', 'mistake', 'rule', 'observation'].includes(String(kind || '')) ? String(kind) : 'observation',
12117
12134
  tags: Array.isArray(tags) ? tags.map((x) => String(x || '').slice(0, 24)).filter(Boolean).slice(0, 6) : [],
12118
12135
  tf: tf || '',
12136
+ auto: !!auto,
12119
12137
  sid: String(sid || ''),
12120
12138
  };
12121
12139
  e.notes.push(note);
@@ -12194,6 +12212,235 @@ function finLearnApi() {
12194
12212
  };
12195
12213
  }
12196
12214
 
12215
+ /* ---------- TYPESAFE KARAR GÜNLÜĞÜ + ÖĞRENEREK GELİŞME ----------
12216
+ Her TypeSafe kararı (p, teyit, tf, trend uyumu, RSI, saat) diske yazılır;
12217
+ pozisyon kapanınca gerçek sonuçla (net/MFE/MAE) eşleştirilir. Bu etiketli
12218
+ veriden KOD şunları öğrenir: (1) ampirik kazanç oranına göre eylem eşiği,
12219
+ (2) risk çarpanı, (3) hangi kural/koşul kazandırıyor. Kapanan her işlem
12220
+ AYRICA TypeSafe'e sorulur (LLM'siz): "hata/desen türü ne?" → otomatik ders
12221
+ yazılır. Veri: finance/tskarar.json */
12222
+ const FIN_TS_DEC_MAX = 240; /* karar kaydı tavanı (eskiler düşer) */
12223
+ const FIN_TS_DEFAULT_TH = tslearn.DEFAULT_TH; /* öğrenilmiş eşik yoksa varsayılan */
12224
+ let finTsDecReady = false;
12225
+ const finTsDecCache = { decisions: [], updatedAt: 0 };
12226
+
12227
+ function finTsDecLoad() {
12228
+ if (finTsDecReady) return finTsDecCache;
12229
+ finTsDecReady = true;
12230
+ try {
12231
+ const raw = finReadJson(finFile('tskarar.json'), null);
12232
+ if (raw && typeof raw === 'object' && Array.isArray(raw.decisions)) {
12233
+ finTsDecCache.decisions = raw.decisions.filter((d) => d && d.at && d.symbol).slice(-FIN_TS_DEC_MAX);
12234
+ finTsDecCache.updatedAt = Number(raw.updatedAt) || 0;
12235
+ }
12236
+ } catch {}
12237
+ return finTsDecCache;
12238
+ }
12239
+
12240
+ function finTsDecSave() {
12241
+ finTsDecCache.updatedAt = Date.now();
12242
+ try { finWriteJson(finFile('tskarar.json'), finTsDecCache); } catch {}
12243
+ }
12244
+
12245
+ /* Açılan her TypeSafe işlemi karar kaydı olur (özellikler + karar) */
12246
+ function finTsDecRecord(rec) {
12247
+ try {
12248
+ const d = finTsDecLoad();
12249
+ const item = {
12250
+ id: 'd' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5),
12251
+ at: Date.now(),
12252
+ sid: String((rec && rec.sid) || ''),
12253
+ role: String((rec && rec.role) || ''),
12254
+ symbol: String((rec && rec.symbol) || '').toUpperCase(),
12255
+ tf: finLearnNormTf(rec && rec.tf),
12256
+ action: String((rec && rec.action) || ''),
12257
+ p: Number(rec && rec.p) || 0,
12258
+ pSecond: Number(rec && rec.pSecond) || 0,
12259
+ confirm: Number(rec && rec.confirm) || 0,
12260
+ hour: Number(rec && rec.hour) || 0,
12261
+ emaAlign: rec && rec.emaAlign === true ? 1 : rec && rec.emaAlign === false ? 0 : null,
12262
+ rsi: Number.isFinite(Number(rec && rec.rsi)) ? Number(rec.rsi) : null,
12263
+ atrPct: Number.isFinite(Number(rec && rec.atrPct)) ? Number(rec.atrPct) : null,
12264
+ riskPct: Number(rec && rec.riskPct) || 0,
12265
+ closedAt: 0, net: null, mfe: null, mae: null, reason: '',
12266
+ };
12267
+ d.decisions.push(item);
12268
+ while (d.decisions.length > FIN_TS_DEC_MAX) d.decisions.shift();
12269
+ finTsDecSave();
12270
+ return item.id;
12271
+ } catch {
12272
+ return '';
12273
+ }
12274
+ }
12275
+
12276
+ /* Kapanışta karar kaydını sonuçla eşleştir (en yeni eşleşmeyen kayıt) */
12277
+ function finTsDecClose({ symbol, side, net, mfe, mae, reason }) {
12278
+ try {
12279
+ const sym = String(symbol || '').toUpperCase();
12280
+ const act = String(side || '').toLowerCase();
12281
+ if (!sym || !act || net == null) return null;
12282
+ const d = finTsDecLoad();
12283
+ const now = Date.now();
12284
+ let hit = null;
12285
+ for (let i = d.decisions.length - 1; i >= 0; i--) {
12286
+ const x = d.decisions[i];
12287
+ if (x.closedAt || x.symbol !== sym || x.action !== act) continue;
12288
+ if (now - Number(x.at) > 24 * 60 * 60 * 1000) continue;
12289
+ hit = x;
12290
+ break;
12291
+ }
12292
+ if (!hit) return null;
12293
+ hit.closedAt = now;
12294
+ hit.net = Math.round((Number(net) || 0) * 100) / 100;
12295
+ hit.mfe = Number.isFinite(Number(mfe)) ? Math.round(Number(mfe) * 100) / 100 : null;
12296
+ hit.mae = Number.isFinite(Number(mae)) ? Math.round(Number(mae) * 100) / 100 : null;
12297
+ hit.reason = String(reason || '').slice(0, 40);
12298
+ finTsDecSave();
12299
+ return hit;
12300
+ } catch {
12301
+ return null;
12302
+ }
12303
+ }
12304
+
12305
+ /* KALİBRASYON + KURAL (saf matematik tslearn.js'te — test edilebilir):
12306
+ bunlar diskteki karar günlüğünü okutup sonucu döndüren ince sarmalayıcılar */
12307
+ function finTsCalibration(symbol, tf) {
12308
+ return tslearn.calibration(finTsDecLoad().decisions, symbol, tf);
12309
+ }
12310
+
12311
+ function finTsRules(symbol) {
12312
+ return tslearn.rules(finTsDecLoad().decisions, symbol);
12313
+ }
12314
+
12315
+ function finTsRulesText(symbol) {
12316
+ return tslearn.rulesText(tslearn.rules(finTsDecLoad().decisions, symbol));
12317
+ }
12318
+
12319
+ function finTsCalText(symbol, tf) {
12320
+ try {
12321
+ return tslearn.calText(finTsDecLoad().decisions, symbol, tf);
12322
+ } catch {
12323
+ return '';
12324
+ }
12325
+ }
12326
+
12327
+ /* Tur state'i için kompakt kalibrasyon satırı (TypeSafe'e girecek) */
12328
+ function finTsCalState(symbol, tf) {
12329
+ try {
12330
+ return tslearn.calState(finTsDecLoad().decisions, symbol, tf);
12331
+ } catch {
12332
+ return '';
12333
+ }
12334
+ }
12335
+
12336
+ /* OTOMATİK DERS (TypeSafe, LLM'siz): kapanan işlemi sınıflandır → ders yaz.
12337
+ Aynı anda tek analiz; hata olsa bile tur döngüsünü etkilemez. */
12338
+ let finTsLessonAt = 0;
12339
+ async function finTsAutoLesson(dec) {
12340
+ try {
12341
+ if (!dec || !typesafeMod.cfg().apiKey) return;
12342
+ const now = Date.now();
12343
+ if (now - finTsLessonAt < 20000) return; /* ders seli yok: 20 sn'de bir analiz */
12344
+ finTsLessonAt = now;
12345
+ const loss = (Number(dec.net) || 0) < 0;
12346
+ const state = {
12347
+ sembol: dec.symbol,
12348
+ zaman_dilimi: dec.tf || null,
12349
+ yon: dec.action,
12350
+ net: dec.net,
12351
+ mfe: dec.mfe,
12352
+ mae: dec.mae,
12353
+ kapanis_nedeni: dec.reason || null,
12354
+ karar: {
12355
+ p: dec.p,
12356
+ teyit: dec.confirm,
12357
+ trend_uyumlu: dec.emaAlign === 1,
12358
+ rsi: dec.rsi,
12359
+ atr_yuzdesi: dec.atrPct,
12360
+ yerel_saat: dec.hour,
12361
+ },
12362
+ };
12363
+ const questions = loss
12364
+ ? {
12365
+ hata: {
12366
+ type: 'choice',
12367
+ instructions: 'Bu ZARARLA kapanan işlemde en olası hata türü hangisi?',
12368
+ criteria: {
12369
+ erken_giris: 'Teyit/kapanış gelmeden erken girildi',
12370
+ yanlis_yon: 'Yön/tez yanlıştı — trende karşı işlem',
12371
+ sl_yakin: 'SL çok yakındı; normal gürültüde stop oldu',
12372
+ sl_uzak: 'SL çok uzaktı; gereğinden büyük zarar yazdı',
12373
+ kar_geri: 'Kâr vardı, çıkış zamanlaması kötüydü (geri verildi)',
12374
+ teyitsiz: 'Momentum/teyit zayıftı, sinyal güvenilmezdi',
12375
+ haber: 'Haber/ani hareket etkisiyle ters kaldı',
12376
+ diger: 'Diğer',
12377
+ },
12378
+ },
12379
+ tekrar: {
12380
+ type: 'noul',
12381
+ instructions: 'Aynı koşullar tekrar oluşursa bu hata tekrarlanabilir mi?',
12382
+ criteria: { true: 'Tekrar riski yüksek', false: 'Tek seferlik/tesadüfi' },
12383
+ },
12384
+ }
12385
+ : {
12386
+ desen: {
12387
+ type: 'choice',
12388
+ instructions: 'Bu KAZANÇLI işlemde işe yarayan ana desen hangisi?',
12389
+ criteria: {
12390
+ trend_uyumu: 'Trend yönünde giriş (EMA/yapı uyumu)',
12391
+ seviye_reddi: 'Destek/direnç reddi',
12392
+ kirilim_retest: 'Kırılım + geri çekilme teyidi',
12393
+ momentum: 'Momentum devamı',
12394
+ haber: 'Haber/katalizör',
12395
+ diger: 'Diğer',
12396
+ },
12397
+ },
12398
+ tekrarlanabilir: {
12399
+ type: 'noul',
12400
+ instructions: 'Bu desen tekrar kullanılabilir mi (kurallaşabilir mi)?',
12401
+ criteria: { true: 'Evet, kural olabilir', false: 'Tesadüfi' },
12402
+ },
12403
+ };
12404
+ const ans = await finTsAsk(state, questions);
12405
+ const tfTxt = dec.tf ? `[${dec.tf}] ` : '';
12406
+ if (loss) {
12407
+ const c = finTsChoice(ans, 'hata');
12408
+ const t = finTsNoul(ans, 'tekrar');
12409
+ if (!c) return;
12410
+ const label = {
12411
+ erken_giris: 'erken giriş (teyit gelmeden)',
12412
+ yanlis_yon: 'yanlış yön — trende karşı',
12413
+ sl_yakin: 'SL çok yakın (gürültüde stop)',
12414
+ sl_uzak: 'SL çok uzak — zarar büyük',
12415
+ kar_geri: 'kâr geri verildi (çıkış zamanlaması)',
12416
+ teyitsiz: 'teyitsiz/zayıf momentum',
12417
+ haber: 'haber/ani hareket',
12418
+ diger: 'sınıflandırılamadı',
12419
+ }[c.choice] || c.choice;
12420
+ const text = `${tfTxt}HATA: ${label} (p=${c.p.toFixed(2)})` +
12421
+ (t != null && t >= 0.6 ? ' — tekrar riski yüksek: bu koşulda teyit artır/küçük risk al' : '') +
12422
+ ` · net ${dec.net}`;
12423
+ finLearnApi().add({ symbol: dec.symbol, text, kind: 'mistake', timeframe: dec.tf, auto: true });
12424
+ } else {
12425
+ const c = finTsChoice(ans, 'desen');
12426
+ const t = finTsNoul(ans, 'tekrarlanabilir');
12427
+ if (!c) return;
12428
+ const label = {
12429
+ trend_uyumu: 'trend uyumlu giriş',
12430
+ seviye_reddi: 'seviye reddi',
12431
+ kirilim_retest: 'kırılım + retest',
12432
+ momentum: 'momentum devamı',
12433
+ haber: 'haber/katalizör',
12434
+ diger: 'sınıflandırılamadı',
12435
+ }[c.choice] || c.choice;
12436
+ const text = `${tfTxt}DESEN: ${label} (p=${c.p.toFixed(2)})` +
12437
+ (t != null && t >= 0.6 ? ' — tekrarlanabilir, kullan' : '') +
12438
+ ` · net +${dec.net}`;
12439
+ finLearnApi().add({ symbol: dec.symbol, text, kind: 'pattern', timeframe: dec.tf, auto: true });
12440
+ }
12441
+ } catch {}
12442
+ }
12443
+
12197
12444
  /* LIMIT + ÖĞRENME kancalarını financetools'a bağla */
12198
12445
  try {
12199
12446
  financetools.setLimits({
@@ -12876,7 +13123,6 @@ function finTeamWaitTick(sid) {
12876
13123
  uygular. Yanıtlar AJAN DM ekip grubuna yazılır — tüm ajanlar birbirinin
12877
13124
  cevabını görür (son yanıtlar sonraki turların state'ine de girer) ve emirler
12878
13125
  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
13126
  const FIN_TS_MARGIN_P = 0.12; /* birinci ile ikinci seçenek arası min fark */
12881
13127
  const FIN_TS_CONFIRM_P = 0.55; /* noul teyit eşiği */
12882
13128
  const FIN_TS_EXIT_P = 0.62; /* pozisyon kapatma eşiği */
@@ -13119,6 +13365,8 @@ async function finTypeSafeRound(sid, agent) {
13119
13365
  })),
13120
13366
  piyasa: {},
13121
13367
  ogrenme: finBuildLearnDigest(symbols),
13368
+ /* KALİBRASYON: öğrenilmiş eşik + risk çarpanı + kazandıran kurallar */
13369
+ kalibrasyon: symbols.map((s) => finTsCalState(s, market[s] && market[s].tf)).filter(Boolean).join('\n') || '(kalibrasyon verisi henüz yok)',
13122
13370
  ekip_yanitlari: finTsFeedText(6),
13123
13371
  };
13124
13372
  for (const sym of symbols) {
@@ -13184,6 +13432,10 @@ async function finTypeSafeRound(sid, agent) {
13184
13432
  const c = finTsChoice(ans, 'yon_' + i);
13185
13433
  const conf = finTsNoul(ans, 'teyit_' + i);
13186
13434
  const pos = posBySym[sym];
13435
+ /* ÖĞRENİLMİŞ EŞİK + RİSK ÇARPANI: kalibrasyon verisi biriktikçe kod
13436
+ eşiği ve riski kendisi ayarlar (sistem öğrendikçe o karar verir) */
13437
+ const cal = finTsCalibration(sym, market[sym].tf);
13438
+ const thAction = cal.action;
13187
13439
  if (pos) {
13188
13440
  const ex = finTsNoul(ans, 'kapat_' + i);
13189
13441
  if (ex != null && ex >= FIN_TS_EXIT_P) {
@@ -13201,8 +13453,8 @@ async function finTypeSafeRound(sid, agent) {
13201
13453
  lines.push(`- ${sym}: ${finTsFmtChoice(c)} → BEKLE`);
13202
13454
  continue;
13203
13455
  }
13204
- if (!(c.p >= FIN_TS_ACTION_P) || c.p - c.second < FIN_TS_MARGIN_P) {
13205
- lines.push(`- ${sym}: ${finTsFmtChoice(c)} → eşik/fark altı, BEKLE`);
13456
+ if (!(c.p >= thAction) || c.p - c.second < FIN_TS_MARGIN_P) {
13457
+ lines.push(`- ${sym}: ${finTsFmtChoice(c)} → öğrenilmiş eşik p≥${thAction.toFixed(2)}/fark altı, BEKLE`);
13206
13458
  continue;
13207
13459
  }
13208
13460
  if (conf == null || conf < FIN_TS_CONFIRM_P) {
@@ -13224,7 +13476,8 @@ async function finTypeSafeRound(sid, agent) {
13224
13476
  const rnd = (v) => Number(Number(v).toFixed(digits));
13225
13477
  const sl = rnd(c.choice === 'buy' ? price - slDist : price + slDist);
13226
13478
  const tp = rnd(c.choice === 'buy' ? price + tpDist : price - tpDist);
13227
- const riskPct = Number(f.riskPerTradePct) > 0 ? Number(f.riskPerTradePct) : 0.5;
13479
+ const riskBase = Number(f.riskPerTradePct) > 0 ? Number(f.riskPerTradePct) : 0.5;
13480
+ const riskPct = Math.max(0.1, Math.min(2, Math.round(riskBase * cal.riskMult * 100) / 100));
13228
13481
  const r = await financetools.handlers.mt5_trade(
13229
13482
  {
13230
13483
  symbol: sym,
@@ -13239,9 +13492,34 @@ async function finTypeSafeRound(sid, agent) {
13239
13492
  },
13240
13493
  { sessionId: sidS }
13241
13494
  );
13242
- if (r && r.ok) {
13495
+ if (r && r.ok && !r.shadow) {
13243
13496
  opened += 1;
13244
- 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}`);
13497
+ /* KARAR KAYDI: özellikler + karar diske yazılır; kapanışta sonuçla
13498
+ eşleşir → kalibrasyon/eşik/risk öğrenmesi bu veriyle çalışır */
13499
+ try {
13500
+ const ind = market[sym].ind || {};
13501
+ finTsDecRecord({
13502
+ sid: sidS,
13503
+ role: 'trader',
13504
+ symbol: sym,
13505
+ tf: market[sym].tf,
13506
+ action: c.choice,
13507
+ p: c.p,
13508
+ pSecond: c.second,
13509
+ confirm: conf,
13510
+ hour: new Date().getHours(),
13511
+ emaAlign: ind.ema50 != null && ind.ema200 != null ? (ind.ema50 > ind.ema200) === (c.choice === 'buy') : null,
13512
+ rsi: ind.rsi14,
13513
+ atrPct: atr > 0 && price > 0 ? atr / price : null,
13514
+ riskPct,
13515
+ });
13516
+ } catch {}
13517
+ lines.push(
13518
+ `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, risk %${riskPct}${cal.riskMult !== 1 ? ` ×${cal.riskMult}` : ''}) · p=${c.p.toFixed(2)} teyit=${conf.toFixed(2)} · tf=${market[sym].tf}` +
13519
+ (thAction !== FIN_TS_DEFAULT_TH.action ? ` · öğrenilmiş eşik p≥${thAction.toFixed(2)}` : '')
13520
+ );
13521
+ } else if (r && r.ok && r.shadow) {
13522
+ lines.push(`- ${sym}: SHADOW mod — emir gönderilmedi (${c.choice} p=${c.p.toFixed(2)})`);
13245
13523
  } else {
13246
13524
  lines.push(`- ${sym}: ${c.choice.toUpperCase()} sinyali reddedildi — ${(r && r.error) || 'hata'}`);
13247
13525
  }
@@ -13251,6 +13529,14 @@ async function finTypeSafeRound(sid, agent) {
13251
13529
  agent,
13252
13530
  `🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
13253
13531
  'TF KARARI: ' + symbols.map((s) => `${s}=${market[s].tf}`).join(', ') + '\n' +
13532
+ 'ÖĞRENİLMİŞ: ' +
13533
+ symbols
13534
+ .map((s) => {
13535
+ const c = finTsCalibration(s, market[s].tf);
13536
+ return `${s} eşik p≥${c.action.toFixed(2)} risk×${c.riskMult}${c.n ? ` (%${Math.round((c.wins / c.n) * 100)}/${c.n})` : ' (veri yok)'}`;
13537
+ })
13538
+ .join(' · ') +
13539
+ '\n' +
13254
13540
  lines.join('\n') +
13255
13541
  (opened ? '' : '\nYeni pozisyon açılmadı (eşikler/limitler).'),
13256
13542
  symbols.join(',')