beast-agent 2.63.6 → 2.63.9
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 +307 -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 */
|
|
@@ -13000,6 +13246,11 @@ function finTsScore(ans, id) {
|
|
|
13000
13246
|
return { score: Number(a.score) || 0, conf: Number(a.confidence) || 0, probs: a.probabilities || {} };
|
|
13001
13247
|
}
|
|
13002
13248
|
|
|
13249
|
+
/* TypeSafe System One çağrısı — state + tipli sorular; tek giriş noktası */
|
|
13250
|
+
async function finTsAsk(state, questions) {
|
|
13251
|
+
return await typesafeMod.systemOne({ state, questions });
|
|
13252
|
+
}
|
|
13253
|
+
|
|
13003
13254
|
function finTsFmtChoice(c) {
|
|
13004
13255
|
if (!c) return 'yanıt yok';
|
|
13005
13256
|
const probs = Object.entries(c.probs || {}).map(([k, v]) => `${k}=${(Number(v) || 0).toFixed(2)}`).join(' ');
|
|
@@ -13114,6 +13365,8 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13114
13365
|
})),
|
|
13115
13366
|
piyasa: {},
|
|
13116
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)',
|
|
13117
13370
|
ekip_yanitlari: finTsFeedText(6),
|
|
13118
13371
|
};
|
|
13119
13372
|
for (const sym of symbols) {
|
|
@@ -13179,6 +13432,10 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13179
13432
|
const c = finTsChoice(ans, 'yon_' + i);
|
|
13180
13433
|
const conf = finTsNoul(ans, 'teyit_' + i);
|
|
13181
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;
|
|
13182
13439
|
if (pos) {
|
|
13183
13440
|
const ex = finTsNoul(ans, 'kapat_' + i);
|
|
13184
13441
|
if (ex != null && ex >= FIN_TS_EXIT_P) {
|
|
@@ -13196,8 +13453,8 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13196
13453
|
lines.push(`- ${sym}: ${finTsFmtChoice(c)} → BEKLE`);
|
|
13197
13454
|
continue;
|
|
13198
13455
|
}
|
|
13199
|
-
if (!(c.p >=
|
|
13200
|
-
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`);
|
|
13201
13458
|
continue;
|
|
13202
13459
|
}
|
|
13203
13460
|
if (conf == null || conf < FIN_TS_CONFIRM_P) {
|
|
@@ -13219,7 +13476,8 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13219
13476
|
const rnd = (v) => Number(Number(v).toFixed(digits));
|
|
13220
13477
|
const sl = rnd(c.choice === 'buy' ? price - slDist : price + slDist);
|
|
13221
13478
|
const tp = rnd(c.choice === 'buy' ? price + tpDist : price - tpDist);
|
|
13222
|
-
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));
|
|
13223
13481
|
const r = await financetools.handlers.mt5_trade(
|
|
13224
13482
|
{
|
|
13225
13483
|
symbol: sym,
|
|
@@ -13234,9 +13492,34 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13234
13492
|
},
|
|
13235
13493
|
{ sessionId: sidS }
|
|
13236
13494
|
);
|
|
13237
|
-
if (r && r.ok) {
|
|
13495
|
+
if (r && r.ok && !r.shadow) {
|
|
13238
13496
|
opened += 1;
|
|
13239
|
-
|
|
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)})`);
|
|
13240
13523
|
} else {
|
|
13241
13524
|
lines.push(`- ${sym}: ${c.choice.toUpperCase()} sinyali reddedildi — ${(r && r.error) || 'hata'}`);
|
|
13242
13525
|
}
|
|
@@ -13246,6 +13529,14 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13246
13529
|
agent,
|
|
13247
13530
|
`🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
|
|
13248
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' +
|
|
13249
13540
|
lines.join('\n') +
|
|
13250
13541
|
(opened ? '' : '\nYeni pozisyon açılmadı (eşikler/limitler).'),
|
|
13251
13542
|
symbols.join(',')
|