beast-agent 2.61.0 → 2.62.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.61.0",
4
+ "version": "2.62.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -2057,7 +2057,15 @@ class Engine {
2057
2057
  'VERİ AKIŞI (her değerlendirmede): mt5_account + mt5_positions + mt5_market çağrılarını AYNI turda PARALEL ver; gerekiyorsa mt5_history ile son işlemleri gör.\n' +
2058
2058
  'PARALEL + KOORDİNASYON: uzun araştırma/işleri run_background ile paralel finance işçisine devret (parent finance olduğu için işçi mt5 okuma araçlarını görür); koşan ajanlarla konuşmak için agent_dm (to: ajan başlığındaki anahtar kelime, örn "GOLD"; ortak karar için group: "İSİM" ile grup sohbeti kur — mesaj tüm üyelere düşer). Görevin bitince DM/grup sohbetleri otomatik KAPANIR (geçmiş panelde kalır).\n' +
2059
2059
  (symbols ? `İZLEME LİSTESİ: ${symbols}\n` : '') +
2060
- (lim.maxLot ? `LİMİTLER: max lot ${lim.maxLot}` + (lim.maxPositions ? ` · max eşzamanlı pozisyon ${lim.maxPositions}` : '') + ' — bunları aşıp araç kullanma; sistem zaten reddeder.\n' : '') +
2060
+ (lim.minLot || lim.maxLot
2061
+ ? 'LİMİTLER: ' +
2062
+ [
2063
+ lim.minLot ? `min lot ${lim.minLot}` : null,
2064
+ lim.maxLot ? `max lot ${lim.maxLot}` : null,
2065
+ lim.maxPositions ? `max eşzamanlı pozisyon ${lim.maxPositions}` : null,
2066
+ ].filter(Boolean).join(' · ') +
2067
+ ' — bunları aşıp araç kullanma; sistem zaten reddeder (lot min lot altına inemez, max lot üstüne çıkamaz).\n'
2068
+ : '') +
2061
2069
  'RİSK DİSİPLİNİ:\n' +
2062
2070
  '- SL\u2019siz pozisyon BIRAKMA: açtığın her işlemde mantıklı bir SL koy; SL\u2019siz kalan pozisyonu mt5_modify ile tamamla.\n' +
2063
2071
  '- Martingale/kademeli lot artışı YASAK; kaybı geri kovalama (revenge trade) YASAK.\n' +
@@ -31,7 +31,7 @@ const NAMES = [
31
31
  'mt5_cancel',
32
32
  ];
33
33
 
34
- let getCfg = () => ({ allowTrading: false, maxLot: 0.1, maxPositions: 3, symbols: [] });
34
+ let getCfg = () => ({ allowTrading: false, minLot: 0.01, maxLot: 0.1, maxPositions: 3, symbols: [] });
35
35
  let notify = () => {};
36
36
  /* DİSİPLİN KANCASI (main enjekte eder): kodla zorlanan kurallar — günlük işlem
37
37
  limiti, kayıp serisi molası, re-entry beklemesi, kur maruziyeti.
@@ -273,7 +273,7 @@ const definitions = NAMES.map((name) => {
273
273
  enum: ['market', 'buy_market', 'sell_market', 'buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'],
274
274
  description: 'Emir tipi. Boş/side verilirse piyasa (anlık); limit/stop verilirse bekleyen emir (price zorunlu olur).',
275
275
  },
276
- volume: { type: 'number', description: 'Lot (max lot sınırına tabi) — boşsa riskPct + sl ile OTOMATİK hesaplanır' },
276
+ volume: { type: 'number', description: 'Lot (min/max lot sınırına tabi) — boşsa riskPct + sl ile OTOMATİK hesaplanır' },
277
277
  riskPct: { type: 'number', description: 'İşlem riski % (ör. 0.5-2) — volume yerine ver: SL mesafesinden lot hesaplanır (sl zorunlu)' },
278
278
  price: { type: 'number', description: 'Limit/stop emirlerinde tetik fiyatı (piyasa emrinde gerekmez)' },
279
279
  sl: { type: 'number', description: 'Stop loss fiyatı (0 = yok) — şiddetle önerilir' },
@@ -565,7 +565,7 @@ const handlers = {
565
565
  const balance = Number(acct && acct.account && acct.account.balance) || 0;
566
566
  if (!(balance > 0)) return { ok: false, error: 'bakiye okunamadı' };
567
567
  const riskAmount = (balance * riskPct) / 100;
568
- const res = finrisk.calcRiskLot(info, entry, sl, riskAmount, cfg.maxLot);
568
+ const res = finrisk.calcRiskLot(info, entry, sl, riskAmount, cfg.maxLot, cfg.minLot);
569
569
  if (res.error) return { ok: false, error: res.error, raw: res.raw, lossPerLot: res.lossPerLot, riskAmount: Math.round(riskAmount * 100) / 100 };
570
570
  const digits = Number(info.digits) || 5;
571
571
  const dist = Math.abs(entry - sl);
@@ -584,7 +584,9 @@ const handlers = {
584
584
  atr: atr != null ? Math.round(atr * 1e6) / 1e6 : null,
585
585
  timeframe: timeframe || null,
586
586
  capped: !!res.capped,
587
+ raised: !!res.raised,
587
588
  warning: closeErr || undefined,
589
+ minLot: Number(cfg.minLot) || 0.01,
588
590
  maxLot: Number(cfg.maxLot) || 0.1,
589
591
  };
590
592
  },
@@ -623,12 +625,12 @@ const handlers = {
623
625
  const acct = await bcall('account', {}, 8000);
624
626
  const balance = Number(acct && acct.account && acct.account.balance) || 0;
625
627
  const riskAmount = (balance * riskPct) / 100;
626
- const rr = finrisk.calcRiskLot(info, price, sl, riskAmount, maxLot);
628
+ const rr = finrisk.calcRiskLot(info, price, sl, riskAmount, maxLot, cfg.minLot);
627
629
  if (rr.error) return { ok: false, error: rr.error };
628
630
  vol = rr.volume;
629
- riskInfo = { riskPct, riskAmount: Math.round(riskAmount * 100) / 100, lossPerLot: rr.lossPerLot };
631
+ riskInfo = { riskPct, riskAmount: Math.round(riskAmount * 100) / 100, lossPerLot: rr.lossPerLot, raised: !!rr.raised };
630
632
  } else {
631
- const norm = finrisk.normalizeVolume(info, wantVolume, maxLot);
633
+ const norm = finrisk.normalizeVolume(info, wantVolume, maxLot, cfg.minLot);
632
634
  if (norm.error) return { ok: false, error: norm.error };
633
635
  vol = norm.volume;
634
636
  }
@@ -745,7 +747,7 @@ const handlers = {
745
747
  if (!symbol) return { ok: false, error: 'symbol gerekli' };
746
748
  const info = await symInfoRow(symbol);
747
749
  if (!info) return { ok: false, error: 'sembol bulunamadı: ' + symbol + ' (MT5 Market Watch?)' };
748
- const norm = finrisk.normalizeVolume(info, args.volume, Number(cfg.maxLot) || 0.1);
750
+ const norm = finrisk.normalizeVolume(info, args.volume, Number(cfg.maxLot) || 0.1, cfg.minLot);
749
751
  if (norm.error) return { ok: false, error: norm.error };
750
752
  const vol = norm.volume;
751
753
  const ptype = t.type;
@@ -16,24 +16,46 @@ function stepFloor(v, step) {
16
16
  return Math.floor(v / s + 1e-9) * s;
17
17
  }
18
18
 
19
- /* info: MT5 symbol_info satırı. wanted: istenen lot. hardMax: kullanıcı limiti. */
20
- function normalizeVolume(info, wanted, hardMax) {
19
+ function stepCeil(v, step) {
20
+ const s = num(step, 0.01);
21
+ if (!(s > 0)) return v;
22
+ return Math.ceil(v / s - 1e-9) * s;
23
+ }
24
+
25
+ /* info: MT5 symbol_info satırı. wanted: istenen lot. hardMax: kullanıcı üst
26
+ limiti (max lot). hardMin: kullanıcı alt limiti (min lot) — istenen lot bu
27
+ tabanın altındaysa adıma yukarı yuvarlanır (raised). */
28
+ function normalizeVolume(info, wanted, hardMax, hardMin) {
21
29
  const vmin = num(info && info.volume_min, 0.01);
22
30
  const vmax = num(info && info.volume_max, 100);
23
31
  const step = num(info && info.volume_step, 0.01);
24
32
  let v = num(wanted, 0);
25
33
  if (!(v > 0)) return { error: 'volume gerekli (pozitif sayı)' };
26
34
  const cap = Math.min(vmax > 0 ? vmax : Infinity, num(hardMax, 0) > 0 ? num(hardMax, 0) : Infinity);
35
+ const floor = num(hardMin, 0) > 0 ? num(hardMin, 0) : 0;
27
36
  let capped = false;
37
+ let raised = false;
28
38
  if (v > cap) {
29
39
  v = stepFloor(cap, step);
30
40
  capped = true;
31
41
  }
42
+ if (v < floor - 1e-9) {
43
+ v = stepCeil(floor, step);
44
+ raised = true;
45
+ }
32
46
  v = Math.round(stepFloor(v, step) * 1e8) / 1e8;
33
47
  if (!(v > 0) || v < vmin - 1e-9) {
34
48
  return { error: `hacim broker minimumunun altında (min ${vmin} lot)` };
35
49
  }
36
- return { volume: v, capped };
50
+ if (v > cap + 1e-9) {
51
+ /* min lot tabanı broker adımıyla üst limite sığmadı — üst limitte kal */
52
+ v = Math.round(stepFloor(cap, step) * 1e8) / 1e8;
53
+ capped = true;
54
+ if (!(v > 0) || v < vmin - 1e-9) {
55
+ return { error: `hacim broker minimumunun altında (min ${vmin} lot)` };
56
+ }
57
+ }
58
+ return { volume: v, capped, raised };
37
59
  }
38
60
 
39
61
  /* 1 lot için SL mesafesi kaybı (hesap para birimi): tick değeri, yoksa kontrat */
@@ -49,11 +71,11 @@ function lossPerLot(info, entry, sl) {
49
71
  }
50
72
 
51
73
  /* riskAmount: hesap para birimi cinsinden kaybedilecek tutar */
52
- function calcRiskLot(info, entry, sl, riskAmount, hardMax) {
74
+ function calcRiskLot(info, entry, sl, riskAmount, hardMax, hardMin) {
53
75
  const lpl = lossPerLot(info, entry, sl);
54
76
  if (!(lpl > 0)) return { error: 'risk hesaplanamadı (tick değeri/kontrat bilgisi yok)' };
55
77
  const raw = num(riskAmount, 0) / lpl;
56
- const norm = normalizeVolume(info, raw, hardMax);
78
+ const norm = normalizeVolume(info, raw, hardMax, hardMin);
57
79
  if (norm.error) return { error: norm.error, raw, lossPerLot: lpl };
58
80
  return {
59
81
  volume: norm.volume,
@@ -61,6 +83,7 @@ function calcRiskLot(info, entry, sl, riskAmount, hardMax) {
61
83
  lossPerLot: Math.round(lpl * 100) / 100,
62
84
  riskAmount: Math.round(raw * lpl * 100) / 100,
63
85
  capped: !!norm.capped,
86
+ raised: !!norm.raised,
64
87
  };
65
88
  }
66
89
 
package/src/main.js CHANGED
@@ -9387,7 +9387,10 @@ function finCfg() {
9387
9387
  f.roleSkills.trader = cur.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 1);
9388
9388
  }
9389
9389
  if (!Number(f.intervalSec)) f.intervalSec = 120;
9390
+ if (!Number(f.minLot)) f.minLot = 0.01;
9390
9391
  if (!Number(f.maxLot)) f.maxLot = 0.1;
9392
+ /* LOT ARALIĞI KILİDİ: min lot hiçbir zaman max lotu aşamaz */
9393
+ if (f.minLot > f.maxLot) f.minLot = f.maxLot;
9391
9394
  if (f.maxPositions == null) f.maxPositions = 3;
9392
9395
  /* KODLA DİSİPLİN (trader kuralları): hepsi 0 = kural kapalı */
9393
9396
  if (!Number.isFinite(Number(f.maxTradesPerDay))) f.maxTradesPerDay = 10;
@@ -11403,7 +11406,7 @@ function finApplyTraderFields(s, symbolsOverride, role, isMain) {
11403
11406
  s.financeSymbols = Array.isArray(symbolsOverride) && symbolsOverride.length ? symbolsOverride : f.symbols;
11404
11407
  s.financeStrategy = String(f.strategy || '');
11405
11408
  s.financeShadow = !!f.shadowMode; /* shadow modda işlem araçları emir göndermez */
11406
- s.financeLimits = { maxLot: f.maxLot, maxPositions: f.maxPositions };
11409
+ s.financeLimits = { minLot: f.minLot, maxLot: f.maxLot, maxPositions: f.maxPositions };
11407
11410
  /* ZORUNLU TEK skill: rol ajanı → roleSkills[rol], ana trader → roleSkills.trader */
11408
11411
  const skillKey = r || (s.financePlaybook ? 'trader' : '');
11409
11412
  s.financeRoleSkills = (skillKey && f.roleSkills && f.roleSkills[skillKey]) || [];
@@ -11499,7 +11502,7 @@ function finTraderBrief(agent) {
11499
11502
  return [
11500
11503
  `Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
11501
11504
  `Odak semboller: ${syms || '(boş — mt5_status ile terminale bak, mantıklı semboller seç)'}`,
11502
- `Tur aralığı: ${f.intervalSec} sn · Max lot: ${f.maxLot} · Max eşzamanlı pozisyon: ${f.maxPositions}`,
11505
+ `Tur aralığı: ${f.intervalSec} sn · Lot aralığı: ${f.minLot}–${f.maxLot} (min lot tabanı zorlanır, max lot tavanı aşılamaz) · Max eşzamanlı pozisyon: ${f.maxPositions}`,
11503
11506
  roleDef
11504
11507
  ? `UZMANLIK: ${roleDef.desc} — raporlarını bu çerçevede yaz; İŞLEM AÇMA, yalnız analiz + net öneri üret.`
11505
11508
  : 'Otomatik işlem AÇIK (daima): 6 emir tipinin HEPSİ açık — buy_market/sell_market (anlık piyasa), buy_limit/sell_limit ve buy_stop/sell_stop (bekleyen; price zorunlu). mt5_trade ya da mt5_pending İKİSİ de tüm tipleri kabul eder; ayrıca mt5_close/mt5_modify/mt5_cancel açık (limitler sistemce zorlanır). KISMİ KAPATMA yetkisi: kısmi TP (kâr al) ve kısmi stop (zarar kes) — mt5_close {percent, kind:"partial_tp"|"partial_sl"}; otomatik DEĞİL, gerek görürsen sen kullan. ALARM: mt5_alerts ile kurarken modu SEN seç — mode:"once" tek seferlik, mode:"repeat" + cooldownMin (dk) tekrarlı. Lot için mt5_risksize hesapla ya da mt5_trade’e riskPct ver; SL/TP broker stops_level mesafesine uymalı.',
@@ -11596,7 +11599,7 @@ async function finConsultPlan(f, agent, sid) {
11596
11599
  'Güncel piyasa bağlamı gerekiyorsa web_search kullan; kurulu bir skill konuyla ilgiliyse skill aracıyla oku.';
11597
11600
  const ctx = [
11598
11601
  `Odak semboller: ${agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ') || '(boş — ajan kendi bulabilir)'}`,
11599
- `Otomatik işlem: ${auto} · max lot ${f.maxLot} · max eşzamanlı pozisyon ${f.maxPositions}`,
11602
+ `Otomatik işlem: ${auto} · lot aralığı ${f.minLot}–${f.maxLot} · max eşzamanlı pozisyon ${f.maxPositions}`,
11600
11603
  account
11601
11604
  ? `Hesap: bakiye ${account.balance} ${account.currency} · özkaynak ${account.equity ?? '?'} · serbest marj ${account.margin_free ?? '?'} · kaldıraç 1:${account.leverage ?? '?'}`
11602
11605
  : 'Hesap verisi alınamadı.',
@@ -12007,6 +12010,9 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
12007
12010
  }
12008
12011
  }
12009
12012
  if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
12013
+ if (p.minLot !== undefined) f.minLot = Math.max(0.01, Math.min(100, Number(p.minLot) || 0.01));
12014
+ /* min lot max lotu aşamaz — hangisi sonra yazıldıysa diğerine kelepçelenir */
12015
+ if (f.minLot > f.maxLot) f.minLot = f.maxLot;
12010
12016
  if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
12011
12017
  /* RİSK OTOMASYONU + BİLDİRİM ayarları */
12012
12018
  if (p.watchdog !== undefined) f.watchdog = !!p.watchdog;
@@ -169,14 +169,18 @@
169
169
  <span>Model</span>
170
170
  <select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select>
171
171
  </div>
172
- <div class="fin-grid2" title="Değişiklik anında kaydedilir — ajanı DURDURMADAN sonraki tura uygulanır">
172
+ <div class="fin-grid3" title="Değişiklik anında kaydedilir — ajanı DURDURMADAN sonraki tura uygulanır">
173
173
  <div class="fin-field">
174
174
  <span>Tur (sn)</span>
175
175
  <input id="finInterval" type="number" min="30" max="3600" step="10" />
176
176
  </div>
177
+ <div class="fin-field">
178
+ <span>Min lot</span>
179
+ <input id="finMinLot" type="number" min="0.01" max="100" step="0.01" title="Alt sınır: lot hesapları bu tabanın altına inemez — taban altındaki lotlar adıma yukarı yuvarlanır (broker minimumunun üzerinde bir taban koyar)" />
180
+ </div>
177
181
  <div class="fin-field">
178
182
  <span>Max lot</span>
179
- <input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" />
183
+ <input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" title="Üst sınır: lot hesapları bu tavanın üzerine çıkamaz (min lotun altına inemez)" />
180
184
  </div>
181
185
  </div>
182
186
  <div class="fin-field">
@@ -223,6 +223,7 @@ const els = {
223
223
  finTraderModel: $('#finTraderModel'),
224
224
  finModelRefreshBtn: $('#finModelRefreshBtn'),
225
225
  finInterval: $('#finInterval'),
226
+ finMinLot: $('#finMinLot'),
226
227
  finMaxLot: $('#finMaxLot'),
227
228
  finStrategy: $('#finStrategy'),
228
229
  finMaxTradesDay: $('#finMaxTradesDay'),
@@ -9803,6 +9804,7 @@ function finTraderInputsSet(cfg) {
9803
9804
  : [];
9804
9805
  const ae = document.activeElement;
9805
9806
  if (els.finInterval && ae !== els.finInterval) els.finInterval.value = cfg.intervalSec || 120;
9807
+ if (els.finMinLot && ae !== els.finMinLot) els.finMinLot.value = cfg.minLot || 0.01;
9806
9808
  if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
9807
9809
  if (els.finStrategy && ae !== els.finStrategy && !finStrategyDirty) els.finStrategy.value = cfg.strategy || '';
9808
9810
  if (els.finMaxTradesDay && ae !== els.finMaxTradesDay) els.finMaxTradesDay.value = Number(cfg.maxTradesPerDay) || 0;
@@ -10529,9 +10531,22 @@ if (els.finInterval) els.finInterval.addEventListener('change', () => {
10529
10531
  if (els.finMaxLot) els.finMaxLot.addEventListener('change', () => {
10530
10532
  const v = Math.max(0.01, Math.min(100, Number(els.finMaxLot.value) || 0.1));
10531
10533
  els.finMaxLot.value = v;
10534
+ /* min lot max lotu aşamaz — görünürde de kelepçele (sunucu ayrıca zorlar) */
10535
+ if (els.finMinLot && Number(els.finMinLot.value) > v) els.finMinLot.value = v;
10532
10536
  finSaveCfg({ maxLot: v });
10533
10537
  toast('Max lot: ' + v + ' — sonraki turdan itibaren geçerli');
10534
10538
  });
10539
+ if (els.finMinLot) els.finMinLot.addEventListener('change', () => {
10540
+ const maxV = Math.max(0.01, Math.min(100, Number(els.finMaxLot && els.finMaxLot.value) || 0.1));
10541
+ let v = Math.max(0.01, Math.min(100, Number(els.finMinLot.value) || 0.01));
10542
+ if (v > maxV) {
10543
+ v = maxV;
10544
+ toast('Min lot max lotu aşamaz — ' + v + ' olarak ayarlandı');
10545
+ }
10546
+ els.finMinLot.value = v;
10547
+ finSaveCfg({ minLot: v });
10548
+ toast('Min lot: ' + v + ' — lot hesapları bu tabanın altına inmez');
10549
+ });
10535
10550
  if (els.finSymBtn) els.finSymBtn.addEventListener('click', finSymPickerOpen);
10536
10551
 
10537
10552
  /* RİSK OTOMASYONU kontrolleri: değişiklik anında kaydedilir (ajan turunu beklemez) */
@@ -5447,6 +5447,8 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5447
5447
  .fin-field .fin-sym-select { width: 100%; }
5448
5448
  .fin-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
5449
5449
  .fin-grid2 .fin-field { margin-bottom: 8px; }
5450
+ .fin-grid3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
5451
+ .fin-grid3 .fin-field { margin-bottom: 8px; }
5450
5452
  .fin-hint { font-style: normal; opacity: 0.75; }
5451
5453
  /* RİSK OTOMASYONU + PERFORMANS kartları */
5452
5454
  .fin-check label { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text); cursor: pointer; }