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 +1 -1
- package/src/agent/tslearn.js +146 -0
- package/src/main.js +302 -16
package/package.json
CHANGED
|
@@ -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
|
|
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
|
-
|
|
12104
|
-
|
|
12105
|
-
return {
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
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 >=
|
|
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
|
|
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
|
-
|
|
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(',')
|