beast-agent 2.30.1 → 2.30.3

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.30.1",
4
+ "version": "2.30.3",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -6372,34 +6372,37 @@ Engine.prototype._agentDmSend = function (fromSid, args) {
6372
6372
  groupName verilmemişse ama iki taraf da AYNI AÇIK grubun üyesiyse mesaj
6373
6373
  YİNE GRUBA düşer — üye üyeye 1:1 thread'lere ayrı ayrı dağılmaz; ekibin
6374
6374
  tüm iç trafiği TEK grup sohbetinde canlı toplanır. */
6375
- const shared = groupName || !target ? null : this._agentDmSharedGroup(fromSid, target);
6375
+ /* TEK İŞ = TEK GRUP: taraflar AÇIK bir ekip/grup üyesiyse mesaj HER ZAMAN o
6376
+ gruba düşer — group: verilse bile yeni grup AÇILMAZ. Ayrıca group adı
6377
+ mevcut bir grubun başlığıyla eşleşiyorsa (ör. "Beast Finance EKİP" →
6378
+ team:finance) o grup kullanılır; aynı iş için ikinci grup oluşmaz. */
6379
+ const shared = target ? this._agentDmSharedGroup(fromSid, target) : null;
6376
6380
  let gid = '';
6377
- if (groupName || shared) {
6378
- let g;
6379
- if (groupName) {
6380
- /* gid Türkçe-kararsız normalize: ALTIN/Altın/altın aynı grup */
6381
- gid =
6382
- 'grp:' +
6383
- groupName
6384
- .toLowerCase()
6385
- .replace(/\u0307/g, '')
6386
- .replace(/ı/g, 'i')
6387
- .replace(/ğ/g, 'g')
6388
- .replace(/ü/g, 'u')
6389
- .replace(/ş/g, 's')
6390
- .replace(/ö/g, 'o')
6391
- .replace(/ç/g, 'c');
6392
- g = this._agentGroups.get(gid);
6393
- if (!g) {
6394
- if (!target) return { ok: false, error: 'yeni grup için to ile en az bir ajan belirt' };
6395
- g = { id: gid, title: groupName, members: [], titles: {}, createdAt: at, closed: false };
6396
- this._agentGroups.set(gid, g);
6381
+ let g = null;
6382
+ if (shared) {
6383
+ g = shared;
6384
+ gid = String(g.id || '');
6385
+ } else if (groupName) {
6386
+ gid = 'grp:' + this._agentGroupKey(groupName);
6387
+ g = this._agentGroups.get(gid) || null;
6388
+ if (!g) {
6389
+ const want = this._agentGroupKey(groupName);
6390
+ for (const [, cand] of this._agentGroups || new Map()) {
6391
+ if (!cand || cand.closed) continue;
6392
+ if (this._agentGroupKey(cand.title) === want) {
6393
+ g = cand;
6394
+ gid = String(cand.id || '');
6395
+ break;
6396
+ }
6397
6397
  }
6398
- } else {
6399
- g = shared;
6400
- gid = String(g.id || '');
6401
6398
  }
6402
- g.titles = g.titles || {};
6399
+ if (!g) {
6400
+ if (!target) return { ok: false, error: 'yeni grup için to ile en az bir ajan belirt' };
6401
+ g = { id: gid, title: groupName, members: [], titles: {}, createdAt: at, closed: false };
6402
+ this._agentGroups.set(gid, g);
6403
+ }
6404
+ }
6405
+ if (g) { g.titles = g.titles || {};
6403
6406
  const addMember = (sid, title) => {
6404
6407
  sid = String(sid);
6405
6408
  if (!g.members.includes(sid)) g.members.push(sid);
@@ -6478,6 +6481,20 @@ Engine.prototype._agentDmSharedGroup = function (a, b) {
6478
6481
  return null;
6479
6482
  };
6480
6483
 
6484
+ /* Grup adı anahtarı: Türkçe-kararsız normalize — ALTIN/Altın/altın aynı gruba
6485
+ iner; başlık eşleştirmede de kullanılır (ör. "Beast Finance EKİP"). */
6486
+ Engine.prototype._agentGroupKey = function (name) {
6487
+ return String(name || '')
6488
+ .toLowerCase()
6489
+ .replace(/\u0307/g, '')
6490
+ .replace(/ı/g, 'i')
6491
+ .replace(/ğ/g, 'g')
6492
+ .replace(/ü/g, 'u')
6493
+ .replace(/ş/g, 's')
6494
+ .replace(/ö/g, 'o')
6495
+ .replace(/ç/g, 'c');
6496
+ };
6497
+
6481
6498
  Engine.prototype._pushAgentDm = function (dm) {
6482
6499
  this._agentDms = this._agentDms || [];
6483
6500
  this._agentDms.push(dm);
@@ -6517,6 +6534,12 @@ Engine.prototype._agentTeamJoin = function (job) {
6517
6534
  g.closed = false;
6518
6535
  delete g.closedAt;
6519
6536
  g.titles = g.titles || {};
6537
+ /* durdurulmuş eski üyeler aktif kadrodan düşer — yeniden başlayan ekipte
6538
+ kapanmış ajanlar üye listesinde görünmez (geçmiş mesajlar korunur) */
6539
+ g.members = (g.members || []).filter((m) => {
6540
+ const j = this._bgJobs && this._bgJobs.get(String(m));
6541
+ return !j || j.status === 'running';
6542
+ });
6520
6543
  const add = (id, title) => {
6521
6544
  id = String(id);
6522
6545
  if (!g.members.includes(id)) g.members.push(id);
package/src/main.js CHANGED
@@ -8323,8 +8323,12 @@ function finCfg() {
8323
8323
  /* OTOMATİK İŞLEM hep açık — kullanıcı onay kutusu KALDIRILDI: trade ajanının
8324
8324
  amacı zaten işlem açmak; onay sorulmaz, limitler (max lot/pozisyon) korur */
8325
8325
  f.allowTrading = true;
8326
- if (typeof f.analysisAuto !== 'boolean') f.analysisAuto = true; /* sayıya göre otomatik atama */
8327
- if (!Number(f.analysisCount)) f.analysisCount = 2; /* varsayılan: 2 uzman ajan */
8326
+ /* ANALİZ EKİBİ ARTIK SADECE SAYI: elle rol seçimi kaldırıldı — atama daima
8327
+ otomatik (Teknik → Risk → Haber). 0 = ekip kapalı GEÇERLİ değerdir;
8328
+ yalnız eksik/bozuk değerde varsayılan 2'ye düşülür. */
8329
+ f.analysisAuto = true;
8330
+ if (!Number.isFinite(Number(f.analysisCount))) f.analysisCount = 2;
8331
+ f.analysisCount = Math.max(0, Math.min(FIN_ROLES_AUTO.length, Math.round(Number(f.analysisCount) || 0)));
8328
8332
  if (!Number(f.intervalSec)) f.intervalSec = 120;
8329
8333
  if (!Number(f.maxLot)) f.maxLot = 0.1;
8330
8334
  if (f.maxPositions == null) f.maxPositions = 3;
@@ -8605,6 +8609,9 @@ function finAgentStop(sid, reason) {
8605
8609
  engine._bgEmit();
8606
8610
  }
8607
8611
  } catch {}
8612
+ /* AJAN DM: duran ajanın birebir sohbetleri kapanır; grupta KOŞAN üye
8613
+ kalmadıysa ekip sohbeti de GEÇMİŞE düşer (aktif listede kalmaz) */
8614
+ try { if (engine && engine._agentDmClose) engine._agentDmClose(sid); } catch {}
8608
8615
  finPush('trader', { state: 'idle', rounds: financeState.traderRounds });
8609
8616
  return true;
8610
8617
  }
@@ -8767,6 +8774,7 @@ ipcMain.handle('finance:snapshot', async () => {
8767
8774
  let account = st.account || null;
8768
8775
  let positions = [];
8769
8776
  let orders = [];
8777
+ let ordersError = '';
8770
8778
  let symbols = [];
8771
8779
  if (st.running) {
8772
8780
  const [a, p, o, sy] = await Promise.all([
@@ -8778,6 +8786,7 @@ ipcMain.handle('finance:snapshot', async () => {
8778
8786
  if (a && a.ok) account = a.data && a.data.account;
8779
8787
  if (p && p.ok) positions = (p.data && p.data.positions) || [];
8780
8788
  if (o && o.ok) orders = (o.data && o.data.orders) || [];
8789
+ else if (o && o.error) ordersError = String(o.error).slice(0, 200);
8781
8790
  if (sy && sy.ok) symbols = (sy.data && sy.data.symbols) || [];
8782
8791
  }
8783
8792
  const busy = financeState.traderSid && engine ? engine.isBusy(financeState.traderSid) : false;
@@ -8787,9 +8796,11 @@ ipcMain.handle('finance:snapshot', async () => {
8787
8796
  account,
8788
8797
  positions,
8789
8798
  orders,
8799
+ ordersError,
8790
8800
  symbols,
8791
8801
  cfg: f,
8792
8802
  roles: FIN_ROLES,
8803
+ rolesAuto: FIN_ROLES_AUTO,
8793
8804
  trader: { on: financeState.traderOn, busy, rounds: financeState.traderRounds, lastAt: financeState.lastRoundAt },
8794
8805
  };
8795
8806
  });
@@ -8848,8 +8859,24 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
8848
8859
  }
8849
8860
  f.symbolHistory = hist.slice(-40);
8850
8861
  f.symbols = next;
8862
+ /* KOŞAN ajanlar DURDURULMADAN yeni listeyi alır: ana trader + analiz ekibi
8863
+ sonraki turda güncel sembollere odaklanır (sembol işçileri kendi
8864
+ sembolünde kalır — onlar tek sembole özeldir). */
8865
+ for (const [, a] of financeState.agents) {
8866
+ if (a && (a.main || a.role)) a.symbols = next.slice(0, 20);
8867
+ }
8851
8868
  }
8852
8869
  if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
8870
+ /* TUR ARALIĞI CANLI: bekleyen zamanlayıcılar yeni aralıkla yeniden kurulur —
8871
+ koşan tur bitince zaten finFlushOnDone yeni aralığı okur. */
8872
+ if (p.intervalSec !== undefined && financeState.agents) {
8873
+ const iv = Math.max(30, Math.round(Number(f.intervalSec) || 120)) * 1000;
8874
+ for (const [sid, a] of financeState.agents) {
8875
+ if (!a || !engine || engine.isBusy(sid)) continue;
8876
+ clearTimeout(a.timer);
8877
+ a.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, iv);
8878
+ }
8879
+ }
8853
8880
  if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
8854
8881
  if (p.maxPositions !== undefined) f.maxPositions = Math.max(1, Math.min(20, Math.round(Number(p.maxPositions) || 3)));
8855
8882
  /* allowTrading artık AYARLANMAZ — daima true (finCfg zorlar) */
@@ -163,15 +163,27 @@
163
163
  </div>
164
164
  <div class="fin-card fin-card-trader">
165
165
  <div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span></div>
166
- <div class="fin-row"><select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select></div>
167
- <div class="fin-row fin-mini">
168
- <label>Tur (sn)</label><input id="finInterval" type="number" min="30" max="3600" step="10" />
169
- <label>Max lot</label><input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" />
166
+ <div class="fin-field">
167
+ <span>Model</span>
168
+ <select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select>
170
169
  </div>
171
- <div class="fin-row fin-mini"><label>Semboller</label><button id="finSymBtn" class="fin-sym-select" title="MT5 sembol listesinden seç — tıkla">&#8230;</button></div>
172
- <div class="fin-row fin-roles-row">
173
- <label>Analiz ekibi</label>
174
- <div id="finRoles" class="fin-roles" title="Trader&#39;ın yanında koşacak uzman ajanlar — tüm skill + mt5 erişimli, işlem açmazlar"></div>
170
+ <div class="fin-grid2" title="Değişiklik anında kaydedilir — ajanı DURDURMADAN sonraki tura uygulanır">
171
+ <div class="fin-field">
172
+ <span>Tur (sn)</span>
173
+ <input id="finInterval" type="number" min="30" max="3600" step="10" />
174
+ </div>
175
+ <div class="fin-field">
176
+ <span>Max lot</span>
177
+ <input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" />
178
+ </div>
179
+ </div>
180
+ <div class="fin-field">
181
+ <span>Semboller</span>
182
+ <button id="finSymBtn" class="fin-sym-select" title="MT5 sembol listesinden seç — tıkla">&#8230;</button>
183
+ </div>
184
+ <div class="fin-field">
185
+ <span>Analiz ekibi <i class="fin-hint">— sadece sayı seç, rolleri sistem otomatik atar</i></span>
186
+ <div id="finRoles" class="fin-roles" title="Trader&#39;ın yanında koşacak uzman ajan SAYISI — roller otomatik atanır (Teknik → Risk → Haber); işlem açmazlar"></div>
175
187
  </div>
176
188
  <div class="fin-row fin-btn-row">
177
189
  <button id="finTraderBtn" class="fin-start">&#9654;&#xFE0E; Ajanı Başlat</button>
@@ -181,9 +193,7 @@
181
193
  <div class="fin-card">
182
194
  <div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span></div>
183
195
  <div id="finPosList"><div class="fin-empty">Açık pozisyon yok</div></div>
184
- </div>
185
- <div class="fin-card">
186
- <div class="fin-card-title">BEKLEYEN EMİRLER <span id="finOrdCount"></span></div>
196
+ <div class="fin-sect-title">BEKLEYEN EMİRLER <span id="finOrdCount"></span></div>
187
197
  <div id="finOrdList"><div class="fin-empty">Bekleyen emir yok</div></div>
188
198
  </div>
189
199
  <div class="fin-card">
@@ -8888,14 +8888,14 @@ const FIN_ORDER_TYPES = {
8888
8888
  7: { t: 'SELL STP-LMT', c: 'sell' },
8889
8889
  };
8890
8890
 
8891
- function finRenderOrders(list) {
8891
+ function finRenderOrders(list, err) {
8892
8892
  if (!els.finOrdList) return;
8893
8893
  els.finOrdList.textContent = '';
8894
8894
  if (els.finOrdCount) els.finOrdCount.textContent = list && list.length ? '(' + list.length + ')' : '';
8895
8895
  if (!list || !list.length) {
8896
8896
  const d = document.createElement('div');
8897
8897
  d.className = 'fin-empty';
8898
- d.textContent = 'Bekleyen emir yok';
8898
+ d.textContent = err ? 'Emir listesi alınamadı: ' + err : 'Bekleyen emir yok';
8899
8899
  els.finOrdList.appendChild(d);
8900
8900
  return;
8901
8901
  }
@@ -8949,17 +8949,7 @@ function finRenderSymbols(list) {
8949
8949
  row.innerHTML =
8950
8950
  '<span class="fs-name">' + (s.symbol || '?') + '</span>' +
8951
8951
  '<span class="fs-price ' + cls + '">' + bid.toFixed(digits) + '</span>' +
8952
- '<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>' +
8953
- '<button class="fs-agent" title="' + escapeHtml(s.symbol || '') + ' için AYRI finance ajanı başlat (paralel koşar)">&#9654;&#xFE0E;</button>';
8954
- const spawnBtn = row.querySelector('.fs-agent');
8955
- if (spawnBtn) {
8956
- spawnBtn.addEventListener('click', async (e) => {
8957
- e.stopPropagation();
8958
- const r = await beast.financeAgentSpawn(s.symbol).catch((err) => ({ ok: false, error: String((err && err.message) || err) }));
8959
- if (r && r.ok) toast(s.symbol + ' için ajan başladı — Paralel Ajanlar panelinde izle');
8960
- else toast((r && r.error) || 'Ajan başlatılamadı');
8961
- });
8962
- }
8952
+ '<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>';
8963
8953
  els.finSymList.appendChild(row);
8964
8954
  }
8965
8955
  }
@@ -8994,89 +8984,60 @@ function finTraderInputsSet(cfg) {
8994
8984
  if (els.finTraderModel && ae !== els.finTraderModel && cfg.traderSel) els.finTraderModel.value = cfg.traderSel;
8995
8985
  }
8996
8986
 
8997
- /* ANALİZ EKİBİ: trader yanında koşacak uzman ajan rolleri — İKİ MOD:
8998
- (1) OTOMATİK: sadece SAYI gir → gereklı ajanları sistem kendisi ata;
8999
- (2) ELLE SEÇ: rol kutucukları (Risk / Teknik / Haber-Makro) çoklu seçilir.
9000
- Katalog sunucudan (finance snapshot/state.roles) gelir, yerel yedek var. */
8987
+ /* ANALİZ EKİBİ: SADECE SAYI seçilir — rolleri sistem otomatik atar
8988
+ (FIN_ROLES_AUTO sırası: Teknik → Risk → Haber). Elle rol kutucukları
8989
+ kaldırıldı; analysisAuto sunucuda daima açık. Katalog sunucudan gelir. */
9001
8990
  let finRolesCatalog = [
9002
8991
  { id: 'risk', label: 'Risk Ajanı', desc: 'marj/kaldıraç/SL disiplini, exposure ve günlük kayıp hızı denetimi' },
9003
8992
  { id: 'technic', label: 'Teknik Analiz', desc: 'trend/yapı/destek-direnç/momentum okuma, AL-SAT-BEKLE önerileri' },
9004
8993
  { id: 'macro', label: 'Haber / Makro', desc: 'haber akışı + ekonomik takvim, DXY/emtia bağıntıları, yön eğilimi' },
9005
8994
  ];
8995
+ let finRolesAuto = ['technic', 'risk', 'macro'];
9006
8996
  let finRolesDirty = false; /* tık→kaydet arası snapshot ezmesin */
9007
8997
  let finRolesSyncTimer = null;
9008
8998
 
9009
- function finRolesSel() {
9010
- const box = els.finRoles;
9011
- return box ? [...box.querySelectorAll('input[data-role]:checked')].map((cb) => cb.dataset.role) : [];
8999
+ function finRoleLabel(id) {
9000
+ const d = (finRolesCatalog || []).find((x) => x.id === String(id || ''));
9001
+ return d ? d.label : String(id || '');
9012
9002
  }
9013
9003
 
9014
9004
  function finRenderRoles(cfg) {
9015
9005
  const box = els.finRoles;
9016
9006
  if (!box) return;
9017
9007
  if (finRolesDirty) return; /* bekleyen kullanıcı değişikliği korunur */
9018
- const auto = !!(cfg && cfg.analysisAuto);
9019
- const count = Math.max(0, Math.round(Number(cfg && cfg.analysisCount) || 0));
9020
- const sel = cfg && Array.isArray(cfg.analysisTeam) ? cfg.analysisTeam : [];
9008
+ const max = Math.max(1, (finRolesAuto || []).length || 3);
9009
+ const count = Math.max(0, Math.min(max, Math.round(Number(cfg && cfg.analysisCount) || 0)));
9021
9010
  box.innerHTML = '';
9022
-
9023
- /* OTOMATİK çipi + sayı girişi */
9024
- const autoLab = document.createElement('label');
9025
- autoLab.className = 'fin-role fin-role-auto';
9026
- autoLab.title = 'Rol seçmeden SAYI gir — gereklı ajanları sistem otomatik atar (teknik → risk → haber sırası)';
9027
- const autoCb = document.createElement('input');
9028
- autoCb.type = 'checkbox';
9029
- autoCb.checked = auto;
9030
- autoLab.appendChild(autoCb);
9031
- autoLab.appendChild(document.createTextNode('⚙ Otomatik'));
9032
- autoCb.addEventListener('change', () => {
9033
- finRolesDirty = true;
9034
- finSaveCfg({ analysisAuto: autoCb.checked });
9035
- toast(autoCb.checked ? 'Analiz ekibi OTOMATİK — sayı kadar gereklı ajan atanır' : 'Analiz ekibi ELLE SEÇİM moduna geçti');
9036
- clearTimeout(finRolesSyncTimer);
9037
- finRolesSyncTimer = setTimeout(() => { finRolesDirty = false; finSnapshot(); }, 900);
9038
- });
9039
- box.appendChild(autoLab);
9040
-
9011
+ const wrap = document.createElement('div');
9012
+ wrap.className = 'fin-team';
9041
9013
  const cnt = document.createElement('input');
9042
9014
  cnt.type = 'number';
9043
9015
  cnt.className = 'fin-role-count';
9044
9016
  cnt.min = '0';
9045
- cnt.max = String(finRolesCatalog.length);
9017
+ cnt.max = String(max);
9046
9018
  cnt.step = '1';
9047
- cnt.value = String(count || 0);
9048
- cnt.title = 'Otomatik mod: kaç uzman ajan koşacak (0 = ekip yok)';
9049
- cnt.disabled = !auto;
9019
+ cnt.value = String(count);
9020
+ cnt.title = 'Kaç uzman ajan koşacak (0 = ekip yok, en fazla ' + max + '). Roller otomatik atanır.';
9021
+ const hint = document.createElement('span');
9022
+ hint.className = 'fin-team-hint';
9023
+ const names = (n) => finRolesAuto.slice(0, n).map(finRoleLabel);
9024
+ const paint = (n) => {
9025
+ hint.textContent = n > 0 ? 'otomatik: ' + names(n).join(' · ') : 'ekip kapalı — sadece trader koşar';
9026
+ };
9050
9027
  cnt.addEventListener('change', () => {
9028
+ const n = Math.max(0, Math.min(max, Math.round(Number(cnt.value) || 0)));
9029
+ cnt.value = String(n);
9051
9030
  finRolesDirty = true;
9052
- finSaveCfg({ analysisCount: Math.max(0, Math.round(Number(cnt.value) || 0)) });
9053
- toast('Analiz ekibi: ' + (Math.round(Number(cnt.value) || 0) || 'kapalı') + ' uzman ajan (otomatik)');
9031
+ finSaveCfg({ analysisAuto: true, analysisCount: n });
9032
+ paint(n);
9033
+ toast(n ? 'Analiz ekibi: ' + n + ' ajan — ' + names(n).join(', ') : 'Analiz ekibi kapatıldı');
9054
9034
  clearTimeout(finRolesSyncTimer);
9055
9035
  finRolesSyncTimer = setTimeout(() => { finRolesDirty = false; finSnapshot(); }, 900);
9056
9036
  });
9057
- box.appendChild(cnt);
9058
-
9059
- /* rol kutucukları — otomatik moddayken pasif */
9060
- for (const r of finRolesCatalog) {
9061
- const lab = document.createElement('label');
9062
- lab.className = 'fin-role';
9063
- const cb = document.createElement('input');
9064
- cb.type = 'checkbox';
9065
- cb.dataset.role = r.id;
9066
- cb.checked = sel.includes(r.id);
9067
- cb.disabled = auto;
9068
- cb.title = r.desc + ' — tüm skill + mt5 erişimli, işlem açmaz';
9069
- lab.appendChild(cb);
9070
- lab.appendChild(document.createTextNode(r.label));
9071
- cb.addEventListener('change', () => {
9072
- finRolesDirty = true;
9073
- finSaveCfg({ analysisTeam: finRolesSel() });
9074
- toast(r.label + (cb.checked ? ' ekibe eklendi — sonraki başlatışta koşar' : ' ekipten çıkarıldı'));
9075
- clearTimeout(finRolesSyncTimer);
9076
- finRolesSyncTimer = setTimeout(() => { finRolesDirty = false; finSnapshot(); }, 900);
9077
- });
9078
- box.appendChild(lab);
9079
- }
9037
+ paint(count);
9038
+ wrap.appendChild(cnt);
9039
+ wrap.appendChild(hint);
9040
+ box.appendChild(wrap);
9080
9041
  }
9081
9042
 
9082
9043
  function finRenderTrader(trader, cfg) {
@@ -9141,9 +9102,10 @@ async function finSnapshot() {
9141
9102
  finSetDot(b.connected ? true : b.running ? 'busy' : false, b);
9142
9103
  finRenderAccount(r.account);
9143
9104
  finRenderPositions(r.positions || []);
9144
- finRenderOrders(r.orders || []);
9105
+ finRenderOrders(r.orders || [], r.ordersError || '');
9145
9106
  finRenderSymbols(r.symbols || []);
9146
9107
  if (Array.isArray(r.roles) && r.roles.length) finRolesCatalog = r.roles;
9108
+ if (Array.isArray(r.rolesAuto) && r.rolesAuto.length) finRolesAuto = r.rolesAuto;
9147
9109
  finTraderInputsSet(r.cfg);
9148
9110
  finRenderRoles(r.cfg);
9149
9111
  finRenderTrader(r.trader, r.cfg);
@@ -9238,8 +9200,18 @@ function finSaveCfg(patch) {
9238
9200
  beast.financeSettings(patch).then(() => { finWatchDirty = false; }).catch(() => { finWatchDirty = false; });
9239
9201
  }, 400);
9240
9202
  }
9241
- if (els.finInterval) els.finInterval.addEventListener('change', () => finSaveCfg({ intervalSec: Number(els.finInterval.value) || 120 }));
9242
- if (els.finMaxLot) els.finMaxLot.addEventListener('change', () => finSaveCfg({ maxLot: Number(els.finMaxLot.value) || 0.1 }));
9203
+ if (els.finInterval) els.finInterval.addEventListener('change', () => {
9204
+ const v = Math.max(30, Math.min(3600, Math.round(Number(els.finInterval.value) || 120)));
9205
+ els.finInterval.value = v;
9206
+ finSaveCfg({ intervalSec: v });
9207
+ toast('Tur aralığı: ' + v + ' sn — durdurmadan sonraki tura uygulanır');
9208
+ });
9209
+ if (els.finMaxLot) els.finMaxLot.addEventListener('change', () => {
9210
+ const v = Math.max(0.01, Math.min(100, Number(els.finMaxLot.value) || 0.1));
9211
+ els.finMaxLot.value = v;
9212
+ finSaveCfg({ maxLot: v });
9213
+ toast('Max lot: ' + v + ' — sonraki turdan itibaren geçerli');
9214
+ });
9243
9215
  if (els.finSymBtn) els.finSymBtn.addEventListener('click', finSymPickerOpen);
9244
9216
  /* OTOMATİK İŞLEM onay kutusu KALDIRILDI — trade ajanı daima işlem açabilir */
9245
9217
  if (els.finTraderModel) {
@@ -9258,9 +9230,7 @@ if (els.finTraderBtn) {
9258
9230
  else if (r && r.ok) {
9259
9231
  const cfg = (await beast.financeState().catch(() => null));
9260
9232
  const c = (cfg && cfg.cfg) || {};
9261
- const team = c.analysisAuto
9262
- ? Math.round(Number(c.analysisCount) || 0)
9263
- : finRolesSel().length;
9233
+ const team = Math.round(Number(c.analysisCount) || 0);
9264
9234
  toast(team ? 'Trade ajanı + analiz ekibi (' + team + ' uzman) başladı — turlar panelde izlenir' : 'Trade ajanı başladı — turlar panelde izlenir');
9265
9235
  }
9266
9236
  }
@@ -9286,11 +9256,18 @@ function finToggleWatch(sym) {
9286
9256
  const i = finWatch.indexOf(s);
9287
9257
  if (i >= 0) finWatch.splice(i, 1);
9288
9258
  else finWatch.push(s);
9259
+ const added = i < 0; /* true = eklendi */
9289
9260
  finSaveCfg({ symbols: [...finWatch] });
9290
9261
  finSymBtnUpdate(); /* TRADE AJANI düğmesi anında güncellensin */
9291
9262
  clearTimeout(finSymRefreshTimer);
9292
9263
  finSymRefreshTimer = setTimeout(() => finSnapshot(), 700); /* PİYASA kartı tazelensin */
9293
- return i < 0; /* true = eklendi */
9264
+ /* canlı geri bildirim: değişiklik koşan ajanı DURDURMADAN sonraki tura girer */
9265
+ toast(
9266
+ added
9267
+ ? s + ' izleme listesine eklendi — koşan ajan sonraki turda kullanır'
9268
+ : s + ' izleme listesinden çıkarıldı — koşan ajan sonraki turda görmez'
9269
+ );
9270
+ return added;
9294
9271
  }
9295
9272
 
9296
9273
  function finRenderPickRows(rows, q) {
@@ -2290,25 +2290,6 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
2290
2290
  letter-spacing: 0.4px;
2291
2291
  }
2292
2292
 
2293
- /* PİYASA satırındaki ▶ ajan butonu */
2294
- .fin-sym { position: relative; }
2295
- .fin-sym .fs-agent {
2296
- position: absolute;
2297
- right: 4px;
2298
- top: 50%;
2299
- transform: translateY(-50%);
2300
- border: none;
2301
- background: none;
2302
- color: var(--muted);
2303
- cursor: pointer;
2304
- font-size: 11px;
2305
- padding: 2px 4px;
2306
- border-radius: 4px;
2307
- visibility: hidden;
2308
- }
2309
- .fin-sym:hover .fs-agent { visibility: visible; color: var(--accent); }
2310
- .fin-sym .fs-agent:hover { background: var(--accent-dim); }
2311
-
2312
2293
  /* sembol seçicide bölüm başlıkları (geçmiş / tüm semboller) */
2313
2294
  .fin-sym-sec {
2314
2295
  font-size: 10px;
@@ -4833,6 +4814,49 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
4833
4814
  }
4834
4815
  .fin-row input:focus, .fin-row select:focus { border-color: var(--accent); outline: none; }
4835
4816
 
4817
+ /* TRADE AJANI formu — etiket üstte, kontrol altta; tur/max lot 2 kolon */
4818
+ .fin-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; margin-bottom: 8px; }
4819
+ .fin-field > span { font-size: 10px; color: var(--muted); }
4820
+ .fin-field input, .fin-field select {
4821
+ width: 100%;
4822
+ min-width: 0;
4823
+ background: var(--panel2);
4824
+ border: 1px solid var(--border);
4825
+ color: var(--text);
4826
+ font-size: 12px;
4827
+ padding: 4px 7px;
4828
+ border-radius: 6px;
4829
+ }
4830
+ .fin-field input:focus, .fin-field select:focus { border-color: var(--accent); outline: none; }
4831
+ .fin-field .fin-sym-select { width: 100%; }
4832
+ .fin-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
4833
+ .fin-grid2 .fin-field { margin-bottom: 8px; }
4834
+ .fin-hint { font-style: normal; opacity: 0.75; }
4835
+ /* ekib sayısı + otomatik atama özeti */
4836
+ .fin-team { display: flex; align-items: center; gap: 8px; min-width: 0; }
4837
+ .fin-team-hint {
4838
+ flex: 1;
4839
+ min-width: 0;
4840
+ font-size: 10.5px;
4841
+ color: var(--muted);
4842
+ white-space: nowrap;
4843
+ overflow: hidden;
4844
+ text-overflow: ellipsis;
4845
+ }
4846
+ /* POZİSYONLAR kartı içi bölüm başlığı (bekleyen emirler) */
4847
+ .fin-sect-title {
4848
+ display: flex;
4849
+ align-items: center;
4850
+ gap: 6px;
4851
+ margin: 9px 0 6px;
4852
+ padding-top: 8px;
4853
+ border-top: 1px dashed var(--border);
4854
+ font-size: 10px;
4855
+ font-weight: 800;
4856
+ letter-spacing: 1px;
4857
+ color: var(--muted);
4858
+ }
4859
+
4836
4860
  /* TRADE AJANI sembol seçme düğmesi — tıklayınca MT5 sembol seçici açılır */
4837
4861
  .fin-sym-select {
4838
4862
  flex: 1;
@@ -4887,15 +4911,15 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
4887
4911
  .fin-role input:disabled { opacity: 0.45; cursor: default; }
4888
4912
  .fin-role-auto { font-weight: 700; }
4889
4913
  /* Otomatik moddaki SAYI girişi */
4890
- .fin-role-count {
4891
- flex: none; width: 44px;
4914
+ .fin-field .fin-role-count {
4915
+ flex: none; width: 52px;
4892
4916
  background: var(--panel2); color: var(--text);
4893
4917
  border: 1px solid var(--border); border-radius: 8px;
4894
- font-size: 11px; font-weight: 700; text-align: center;
4895
- padding: 2px 3px;
4918
+ font-size: 12px; font-weight: 700; text-align: center;
4919
+ padding: 3px 3px;
4896
4920
  }
4897
- .fin-role-count:focus { border-color: var(--accent); outline: none; }
4898
- .fin-role-count:disabled { opacity: 0.45; }
4921
+ .fin-field .fin-role-count:focus { border-color: var(--accent); outline: none; }
4922
+ .fin-field .fin-role-count:disabled { opacity: 0.45; }
4899
4923
  .fin-start {
4900
4924
  flex: none;
4901
4925
  background: var(--accent);