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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.69.0",
4
+ "version": "2.70.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -99,7 +99,9 @@ function plan(pos, meta, st, cfg) {
99
99
  }
100
100
 
101
101
  /* ---- kısmi TP ---- */
102
- if (partialR > 0 && partialPct > 0 && partialPct < 100 && r > 0 && !(st && st.partial) && profit >= partialR * r) {
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 TP/stop kararını AJAN verir: seviye
11509
- gelince ajanlar TUR BEKLEMEDEN uyandırılır; uygun görürse
11510
- mt5_close {percent, kind:"partial_tp"} ile kısmi kâr alır. */
11511
- st.partial = true;
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
- pozisyonlar: posList.map((p) => ({
13832
- ticket: p.ticket, sembol: p.symbol, yon: Number(p.type) === 0 ? 'buy' : 'sell',
13833
- hacim: p.volume, giris: p.price_open, sl: p.sl, tp: p.tp, kar: p.profit,
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
- /* ---------------- TRADER: yön + teyit + kapat kararı ---------------- */
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 (${Number(pp.type) === 0 ? 'buy' : 'sell'}, kâr ${Number(pp.profit) || 0}) ŞİMDİ kapatılmalı mı? Kârı koruma, momentum dönüşü veya SL/TP yakınlığını değerlendir.`,
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
  }