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 +1 -1
- package/src/agent/engine.js +48 -25
- package/src/main.js +29 -2
- package/src/renderer/index.html +21 -11
- package/src/renderer/renderer.js +55 -78
- package/src/renderer/style.css +49 -25
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
.
|
|
6391
|
-
|
|
6392
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8327
|
-
|
|
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) */
|
package/src/renderer/index.html
CHANGED
|
@@ -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-
|
|
167
|
-
|
|
168
|
-
<
|
|
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-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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">…</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'ı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">▶︎ 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
|
-
|
|
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">
|
package/src/renderer/renderer.js
CHANGED
|
@@ -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)">▶︎</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İ:
|
|
8998
|
-
(
|
|
8999
|
-
|
|
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
|
|
9010
|
-
const
|
|
9011
|
-
return
|
|
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
|
|
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
|
-
|
|
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(
|
|
9017
|
+
cnt.max = String(max);
|
|
9046
9018
|
cnt.step = '1';
|
|
9047
|
-
cnt.value = String(count
|
|
9048
|
-
cnt.title = '
|
|
9049
|
-
|
|
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({
|
|
9053
|
-
|
|
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
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
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', () =>
|
|
9242
|
-
|
|
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.
|
|
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
|
-
|
|
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) {
|
package/src/renderer/style.css
CHANGED
|
@@ -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:
|
|
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:
|
|
4895
|
-
padding:
|
|
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);
|