beast-agent 2.69.0 → 2.70.0
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/finwatch.js +3 -1
- package/src/main.js +162 -10
package/package.json
CHANGED
package/src/agent/finwatch.js
CHANGED
|
@@ -99,7 +99,9 @@ function plan(pos, meta, st, cfg) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/* ---- kısmi TP ---- */
|
|
102
|
-
|
|
102
|
+
/* partial = kısmi kapatma GERÇEKTEN yapıldı (Jev/kod); partialNotified =
|
|
103
|
+
yalnız bildirim gitti — ikisi de tekrar üretimi engeller */
|
|
104
|
+
if (partialR > 0 && partialPct > 0 && partialPct < 100 && r > 0 && !(st && (st.partial || st.partialNotified)) && profit >= partialR * r) {
|
|
103
105
|
const step = num(meta.volume_step, 0.01);
|
|
104
106
|
const vmin = num(meta.volume_min, 0.01);
|
|
105
107
|
const vmax = num(meta.volume_max, 100);
|
package/src/main.js
CHANGED
|
@@ -11505,10 +11505,11 @@ async function finWatchTick() {
|
|
|
11505
11505
|
finJournal({ kind: 'watchdog', action: 'sl-move', ticket, symbol: p.symbol, sl: act.sl });
|
|
11506
11506
|
}
|
|
11507
11507
|
} else if (act.kind === 'partial') {
|
|
11508
|
-
/* OTOMATİK KAPATMA YOK — kısmi
|
|
11509
|
-
gelince
|
|
11510
|
-
|
|
11511
|
-
|
|
11508
|
+
/* OTOMATİK KAPATMA YOK — kısmi kapatma kararını JEV verir: seviye
|
|
11509
|
+
gelince Jev turu TUR BEKLEMEDEN uyanır ve tur içinde karar verir.
|
|
11510
|
+
primary flag 'partial' GERÇEK kapatmaya ayrıldı; burada yalnız
|
|
11511
|
+
bildirim tekrarını engelleyen 'partialNotified' işaretlenir. */
|
|
11512
|
+
st.partialNotified = true;
|
|
11512
11513
|
const rNow = decision.r > 0
|
|
11513
11514
|
? Math.round(((Number(p.type) === 0 ? Number(p.price_current) - Number(p.price_open) : Number(p.price_open) - Number(p.price_current)) / decision.r) * 10) / 10
|
|
11514
11515
|
: 0;
|
|
@@ -13736,6 +13737,131 @@ function finTsIndLine(ind) {
|
|
|
13736
13737
|
};
|
|
13737
13738
|
}
|
|
13738
13739
|
|
|
13740
|
+
/* AÇIK POZİSYON ÖZETİ (TypeSafe state girdisi): R mesafesi, kâr R'si, görülen
|
|
13741
|
+
en iyi R ve koruma bayrakları — Jev'in pozisyon yönetimi kararları bunlara
|
|
13742
|
+
dayanır. Saf okuma; hiçbir IO/karar yok. */
|
|
13743
|
+
function finTsPosState(p) {
|
|
13744
|
+
const st = financeState.watch.get(String((p && p.ticket) || '')) || null;
|
|
13745
|
+
const entry = Number(p && p.price_open) || 0;
|
|
13746
|
+
const ref = Number(p && p.price_current) || 0;
|
|
13747
|
+
const sl = Number(p && p.sl) || 0;
|
|
13748
|
+
const isBuy = Number(p && p.type) === 0;
|
|
13749
|
+
let r = Number(st && st.r) || 0;
|
|
13750
|
+
if (!(r > 0) && sl > 0 && entry > 0) r = Math.abs(entry - sl);
|
|
13751
|
+
const profitDist = entry > 0 && ref > 0 ? (isBuy ? ref - entry : entry - ref) : 0;
|
|
13752
|
+
const bestDist = st && isFinite(Number(st.bestProfit)) ? Number(st.bestProfit) : profitDist;
|
|
13753
|
+
const round2 = (v) => Math.round(v * 100) / 100;
|
|
13754
|
+
return {
|
|
13755
|
+
ticket: p.ticket, sembol: p.symbol, yon: isBuy ? 'buy' : 'sell',
|
|
13756
|
+
hacim: p.volume, giris: p.price_open, sl: p.sl, tp: p.tp, kar: p.profit,
|
|
13757
|
+
fiyat: ref || null,
|
|
13758
|
+
r_mesafe: r > 0 ? Math.round(r * 1e6) / 1e6 : null,
|
|
13759
|
+
kz_r: r > 0 && entry > 0 && ref > 0 ? round2(profitDist / r) : null,
|
|
13760
|
+
en_iyi_r: r > 0 && entry > 0 && ref > 0 && isFinite(bestDist) ? round2(bestDist / r) : null,
|
|
13761
|
+
be: !!(st && st.be),
|
|
13762
|
+
kismi_alindi: !!(st && st.partial),
|
|
13763
|
+
};
|
|
13764
|
+
}
|
|
13765
|
+
|
|
13766
|
+
/* AÇIK POZİSYON YÖNETİMİ (JEV): tur yanıtındaki kısmi kapatma + SL/koruma
|
|
13767
|
+
kararını KOD uygular. Seviyeler deterministik (R/ATR tabanlı) hesaplanır,
|
|
13768
|
+
broker min mesafesine kırpılır ve SL ASLA geriye (risk yönüne) taşınmaz.
|
|
13769
|
+
Kısmi kapatma pozisyon başına BİR kez uygulanır (st.partial). */
|
|
13770
|
+
async function finTsManageOpen(sidS, agent, sym, pos, mkt, ans, qi, lines) {
|
|
13771
|
+
const st = financeState.watch.get(String(pos.ticket)) || null;
|
|
13772
|
+
/* kısmi kapatma pozisyon başına BİR kez: watchdog kaydı yoksa ajan üstünde izle */
|
|
13773
|
+
const partialDone = !!(st && st.partial) || !!(agent && agent.tsPartials && agent.tsPartials.has(String(pos.ticket)));
|
|
13774
|
+
const isBuy = Number(pos.type) === 0;
|
|
13775
|
+
const entry = Number(pos.price_open) || 0;
|
|
13776
|
+
const ref = Number(pos.price_current) || 0;
|
|
13777
|
+
const slNow = Number(pos.sl) || 0;
|
|
13778
|
+
const row = (mkt && mkt.row) || {};
|
|
13779
|
+
const digits = Math.max(0, Math.min(8, Number(row.digits) || 2));
|
|
13780
|
+
const rnd = (v) => Number(Number(v).toFixed(digits));
|
|
13781
|
+
const point = Number(row.point) > 0 ? Number(row.point) : Math.pow(10, -digits);
|
|
13782
|
+
const stopsPts = Number(row.trade_stops_level) || 0;
|
|
13783
|
+
const minDist = stopsPts > 0 ? (stopsPts + 1) * point : point;
|
|
13784
|
+
const atr = Number(mkt && mkt.ind && mkt.ind.atr14) || 0;
|
|
13785
|
+
let rDist = Number(st && st.r) || 0;
|
|
13786
|
+
if (!(rDist > 0) && slNow > 0 && entry > 0) rDist = Math.abs(entry - slNow);
|
|
13787
|
+
const base = rDist > 0 ? rDist : atr > 0 ? atr * 1.5 : ref > 0 ? ref * 0.003 : 0;
|
|
13788
|
+
const profitDist = entry > 0 && ref > 0 ? (isBuy ? ref - entry : entry - ref) : 0;
|
|
13789
|
+
|
|
13790
|
+
/* ---- KISMİ KAPATMA (kısmi TP / kısmi stop): karar Jev'in, uygulama kodun */
|
|
13791
|
+
const par = finTsChoice(ans, 'kismi_' + qi);
|
|
13792
|
+
const pct = par && par.p >= FIN_TS_CONFIRM_P && par.p - par.second >= FIN_TS_MARGIN_P
|
|
13793
|
+
? ({ yuzde25: 25, yuzde50: 50, yuzde75: 75 }[String(par.choice)] || 0)
|
|
13794
|
+
: 0;
|
|
13795
|
+
if (pct > 0 && !partialDone) {
|
|
13796
|
+
const kind = profitDist >= 0 ? 'partial_tp' : 'partial_sl';
|
|
13797
|
+
let r = null;
|
|
13798
|
+
try {
|
|
13799
|
+
r = await financetools.handlers.mt5_close(
|
|
13800
|
+
{
|
|
13801
|
+
ticket: Number(pos.ticket),
|
|
13802
|
+
percent: pct,
|
|
13803
|
+
kind,
|
|
13804
|
+
reason: `TypeSafe: kısmi %${pct} p=${par.p.toFixed(2)} (${kind === 'partial_tp' ? 'kâr al' : 'zarar kes'})`,
|
|
13805
|
+
},
|
|
13806
|
+
{ sessionId: sidS }
|
|
13807
|
+
);
|
|
13808
|
+
} catch (e) {
|
|
13809
|
+
r = { ok: false, error: String((e && e.message) || e) };
|
|
13810
|
+
}
|
|
13811
|
+
if (r && r.ok) {
|
|
13812
|
+
if (st) { st.partial = true; finWatchSave(); }
|
|
13813
|
+
if (agent) { agent.tsPartials = agent.tsPartials || new Set(); agent.tsPartials.add(String(pos.ticket)); }
|
|
13814
|
+
lines.push(
|
|
13815
|
+
`- ${sym} #${pos.ticket}: KISMİ %${pct} ${kind === 'partial_tp' ? 'TP ✓' : 'STOP ✓'}` +
|
|
13816
|
+
` (p=${par.p.toFixed(2)}${r.remaining != null ? ', kalan ' + r.remaining + ' lot' : ''})`
|
|
13817
|
+
);
|
|
13818
|
+
} else {
|
|
13819
|
+
lines.push(`- ${sym} #${pos.ticket}: kısmi %${pct} reddedildi — ${(r && r.error) || 'hata'}`);
|
|
13820
|
+
}
|
|
13821
|
+
} else if (pct > 0 && partialDone) {
|
|
13822
|
+
lines.push(`- ${sym} #${pos.ticket}: kısmi atlandı — daha önce alındı`);
|
|
13823
|
+
}
|
|
13824
|
+
|
|
13825
|
+
/* ---- SL / KORUMA: yalnız İYİLEŞTİRME yönünde, kârsızda mevcut SL'ye dokunma */
|
|
13826
|
+
const slc = finTsChoice(ans, 'sl_' + qi);
|
|
13827
|
+
if (!slc || slc.p < FIN_TS_CONFIRM_P || slc.p - slc.second < FIN_TS_MARGIN_P) return;
|
|
13828
|
+
const choice = String(slc.choice || '');
|
|
13829
|
+
if (choice === 'birak' || !(ref > 0)) return;
|
|
13830
|
+
if (profitDist <= 0 && slNow > 0) {
|
|
13831
|
+
lines.push(`- ${sym} #${pos.ticket}: SL kararı (${choice}) uygulanmadı — kâr yok`);
|
|
13832
|
+
return;
|
|
13833
|
+
}
|
|
13834
|
+
if (!(base > 0)) return;
|
|
13835
|
+
let desired = 0;
|
|
13836
|
+
if (choice === 'be' && rDist > 0) desired = isBuy ? entry + 0.05 * rDist : entry - 0.05 * rDist;
|
|
13837
|
+
else if (choice === 'trail') desired = isBuy ? ref - 0.5 * base : ref + 0.5 * base;
|
|
13838
|
+
else if (choice === 'sikilastir') desired = isBuy ? ref - Math.max(0.3 * base, minDist + point) : ref + Math.max(0.3 * base, minDist + point);
|
|
13839
|
+
else if (choice === 'koru' && slNow <= 0) desired = isBuy ? ref - Math.max(atr > 0 ? atr * 1.5 : base, minDist + point) : ref + Math.max(atr > 0 ? atr * 1.5 : base, minDist + point);
|
|
13840
|
+
if (!(desired > 0)) return;
|
|
13841
|
+
desired = isBuy ? Math.min(desired, ref - minDist) : Math.max(desired, ref + minDist);
|
|
13842
|
+
const improved = slNow <= 0 || (isBuy ? desired > slNow + point * 0.5 : desired < slNow - point * 0.5);
|
|
13843
|
+
if (!improved) {
|
|
13844
|
+
lines.push(`- ${sym} #${pos.ticket}: SL (${choice}) iyileştirme değil — dokunulmadı`);
|
|
13845
|
+
return;
|
|
13846
|
+
}
|
|
13847
|
+
let mod = null;
|
|
13848
|
+
try {
|
|
13849
|
+
mod = await financetools.handlers.mt5_modify(
|
|
13850
|
+
{ ticket: Number(pos.ticket), sl: rnd(desired), tp: Number(pos.tp) || 0 },
|
|
13851
|
+
{ sessionId: sidS }
|
|
13852
|
+
);
|
|
13853
|
+
} catch (e) {
|
|
13854
|
+
mod = { ok: false, error: String((e && e.message) || e) };
|
|
13855
|
+
}
|
|
13856
|
+
const label = { be: 'breakeven', trail: 'trailing', sikilastir: 'sıkılaştırma', koru: 'koruma' }[choice] || choice;
|
|
13857
|
+
if (mod && mod.ok) {
|
|
13858
|
+
if (st) { st.be = true; finWatchSave(); }
|
|
13859
|
+
lines.push(`- ${sym} #${pos.ticket}: SL ${label} → ${rnd(desired)} ✓ (p=${slc.p.toFixed(2)})`);
|
|
13860
|
+
} else {
|
|
13861
|
+
lines.push(`- ${sym} #${pos.ticket}: SL ${label} reddedildi — ${(mod && mod.error) || 'hata'}`);
|
|
13862
|
+
}
|
|
13863
|
+
}
|
|
13864
|
+
|
|
13739
13865
|
function finTsSchedule(sid, agent, sec) {
|
|
13740
13866
|
if (!agent || !financeState.agents.has(String(sid))) return;
|
|
13741
13867
|
clearTimeout(agent.timer);
|
|
@@ -13828,10 +13954,9 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13828
13954
|
acik_pozisyon: posList.length,
|
|
13829
13955
|
},
|
|
13830
13956
|
limitler: { max_pozisyon: Number(f.maxPositions) || 3, min_lot: Number(f.minLot) || 0.01, max_lot: Number(f.maxLot) || 0.1 },
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
})),
|
|
13957
|
+
/* AÇIK POZİSYONLAR: R/kâr R'si/en iyi R + koruma bayrakları — Jev'in
|
|
13958
|
+
kapatma/kısmi/SL kararları bu bağlamla verilir (finTsPosState) */
|
|
13959
|
+
pozisyonlar: posList.map((p) => finTsPosState(p)),
|
|
13835
13960
|
piyasa: {},
|
|
13836
13961
|
ogrenme: finBuildLearnDigest(symbols),
|
|
13837
13962
|
/* KALİBRASYON: öğrenilmiş eşik + risk çarpanı + kazandıran kurallar */
|
|
@@ -13850,7 +13975,7 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13850
13975
|
};
|
|
13851
13976
|
}
|
|
13852
13977
|
|
|
13853
|
-
/*
|
|
13978
|
+
/* -------- TRADER: yön + teyit + açık pozisyon yönetimi (kapat/kısmi/SL) -------- */
|
|
13854
13979
|
if (isTrader) {
|
|
13855
13980
|
const posBySym = {};
|
|
13856
13981
|
for (const p of posList) {
|
|
@@ -13875,11 +14000,36 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13875
14000
|
};
|
|
13876
14001
|
if (posBySym[sym]) {
|
|
13877
14002
|
const pp = posBySym[sym];
|
|
14003
|
+
const ps = finTsPosState(pp);
|
|
14004
|
+
const kzTxt = ps.kz_r == null ? 'R bilinmiyor' : `kâr ${ps.kz_r}R`;
|
|
14005
|
+
const bestTxt = ps.en_iyi_r == null ? '' : ` · en iyi ${ps.en_iyi_r}R`;
|
|
14006
|
+
const slTxt = Number(pp.sl) > 0 ? `SL ${pp.sl}` : 'SL YOK';
|
|
13878
14007
|
questions['kapat_' + i] = {
|
|
13879
14008
|
type: 'noul',
|
|
13880
|
-
instructions: `${sym} açık pozisyon (${
|
|
14009
|
+
instructions: `${sym} açık pozisyon (${ps.yon}, kâr ${Number(pp.profit) || 0}, ${kzTxt}${bestTxt}, ${slTxt}) ŞİMDİ kapatılmalı mı? Kârı koruma, kâr geri verme, momentum dönüşü veya SL/TP yakınlığını değerlendir.`,
|
|
13881
14010
|
criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
|
|
13882
14011
|
};
|
|
14012
|
+
questions['kismi_' + i] = {
|
|
14013
|
+
type: 'choice',
|
|
14014
|
+
instructions: `${sym} açık pozisyon (${kzTxt}${bestTxt}${ps.be ? ' · SL takipte' : ''}) için ŞİMDİ kısmi kapatma uygun mu?${ps.kismi_alindi ? ' Bu pozisyonda kısmi kapatma ZATEN yapıldı — yok seç.' : ' Kârı realize etmek/riski azaltmak için değerlendir.'}`,
|
|
14015
|
+
criteria: {
|
|
14016
|
+
yok: 'Kısmi kapatma yok — pozisyon tam kalsın',
|
|
14017
|
+
yuzde25: 'Pozisyonun %25’i kapatılsın',
|
|
14018
|
+
yuzde50: 'Pozisyonun %50’si kapatılsın',
|
|
14019
|
+
yuzde75: 'Pozisyonun %75’i kapatılsın',
|
|
14020
|
+
},
|
|
14021
|
+
};
|
|
14022
|
+
questions['sl_' + i] = {
|
|
14023
|
+
type: 'choice',
|
|
14024
|
+
instructions: `${sym} açık pozisyonu için SL/koruma aksiyonu: ${ps.yon}, giriş ${Number(pp.price_open) || '?'}, şimdi ${ps.fiyat || '?'}, ${slTxt}, ${kzTxt}. Kâr yoksa ve SL varsa 'birak'; SL yoksa 'koru' ile koruma koy.`,
|
|
14025
|
+
criteria: {
|
|
14026
|
+
birak: 'Dokunma — SL yerinde kalsın',
|
|
14027
|
+
be: 'Breakeven — SL girişe çekilsin (kârı kilitle)',
|
|
14028
|
+
trail: 'Trailing — SL fiyatın gerisine taşınsın (kârı takip et)',
|
|
14029
|
+
sikilastir: 'Sıkılaştır — SL fiyata yaklaştırılsın (kârı daha çok koru)',
|
|
14030
|
+
koru: 'Koruma koy — SL’siz pozisyona ATR tabanlı stop eklensin',
|
|
14031
|
+
},
|
|
14032
|
+
};
|
|
13883
14033
|
}
|
|
13884
14034
|
});
|
|
13885
14035
|
let ans = null;
|
|
@@ -13915,6 +14065,8 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
13915
14065
|
lines.push(`- ${sym}: KAPAT (p=${ex.toFixed(2)}) ${r && r.ok ? '✓ pozisyon kapatıldı' : '✗ ' + ((r && r.error) || 'hata')}`);
|
|
13916
14066
|
} else {
|
|
13917
14067
|
lines.push(`- ${sym}: pozisyon açık (kapat=${ex == null ? 'yanıt yok' : ex.toFixed(2)} < ${FIN_TS_EXIT_P}) — tez sürüyor`);
|
|
14068
|
+
/* JEV POZİSYON YÖNETİMİ: kısmi kapatma + SL/koruma kararları */
|
|
14069
|
+
try { await finTsManageOpen(sidS, agent, sym, pos, market[sym], ans, i, lines); } catch {}
|
|
13918
14070
|
}
|
|
13919
14071
|
continue;
|
|
13920
14072
|
}
|