beast-agent 2.72.1 → 2.73.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.72.1",
4
+ "version": "2.73.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/main.js CHANGED
@@ -9904,6 +9904,9 @@ const financeState = {
9904
9904
  watchTimer: null,
9905
9905
  watchBusy: false,
9906
9906
  watchTickAt: 0,
9907
+ /* POZİSYON YÖNETİCİSİ (JEV): açık pozisyon varken 5 sn'lik watchdog turundan
9908
+ tetiklenen ayrı Jev ajanı — kapat/kısmi/SL kararlarını kendi talimatıyla verir */
9909
+ posManager: { busy: false, rounds: 0, lastAt: 0, lastErr: '', lastPosCount: 0, tsPartials: new Set() },
9907
9910
  hoursTimer: null, /* trade saatleri otomatik duraklatma denetimi (10 sn) */
9908
9911
  hoursPaused: null, /* pencere kapanınca durdurulan ajanların planı — açılınca geri gelir */
9909
9912
  alerts: [], /* fiyat alarmları (kalıcı: finance/alerts.json) */
@@ -10017,6 +10020,10 @@ function finCfg() {
10017
10020
  if (!Number.isFinite(Number(f.maxPerCurrency))) f.maxPerCurrency = 3;
10018
10021
  /* SHADOW MOD: emir gönderilmez — kararlar gerekçesiyle günlüğe yazılır */
10019
10022
  if (typeof f.shadowMode !== 'boolean') f.shadowMode = false;
10023
+ /* POZİSYON YÖNETİCİSİ: açık pozisyonları 5 sn'de bir Jev ile yöneten ayrı
10024
+ ajan — varsayılan AÇIK; kendi talimatı (posManagerNote) yalnız onu bağlar. */
10025
+ if (typeof f.posManagerEnabled !== 'boolean') f.posManagerEnabled = true;
10026
+ if (typeof f.posManagerNote !== 'string') f.posManagerNote = '';
10020
10027
  /* GÜNLÜK RUTİN: plan/review saatleri (HH:MM; boş = kapalı) — hafta içi cron */
10021
10028
  if (typeof f.planTime !== 'string') f.planTime = '';
10022
10029
  if (typeof f.reviewTime !== 'string') f.reviewTime = '';
@@ -10410,6 +10417,9 @@ function finExcFlush(force) {
10410
10417
 
10411
10418
  function finAgentInfo(sid) {
10412
10419
  try {
10420
+ if (String(sid || '') === FIN_POSMGR_ID) {
10421
+ return { main: false, role: 'posmanager', label: 'Pozisyon Yöneticisi' };
10422
+ }
10413
10423
  const a = financeState.agents.get(String(sid || ''));
10414
10424
  if (a) {
10415
10425
  const role = String(a.role || '');
@@ -11595,6 +11605,11 @@ async function finWatchTick() {
11595
11605
  try { await finRecordClose(ticket, st); } catch {}
11596
11606
  }
11597
11607
  }
11608
+ /* POZİSYON YÖNETİCİSİ (JEV): açık pozisyon varken 5 sn'lik tur aynı anda
11609
+ yönetici karar turunu tetikler (ayrı MT5 sorgusu yok; meşgulse atlar) */
11610
+ if (positionsOk && positions.length) {
11611
+ try { finPosManagerMaybe(positions, account); } catch {}
11612
+ }
11598
11613
  try { await finCheckAlerts(); } catch {}
11599
11614
  if (!financeState.lastStatsAt || Date.now() - financeState.lastStatsAt > 120000) {
11600
11615
  financeState.lastStatsAt = Date.now();
@@ -13639,6 +13654,7 @@ function finTsAgentMode(agent) {
13639
13654
  }
13640
13655
 
13641
13656
  function finTsWho(agent, extra) {
13657
+ if (agent && agent.__posmgr) return 'TypeSafe · POZİSYON YÖNETİCİSİ' + (extra ? ' · ' + extra : '');
13642
13658
  const role = agent && agent.main ? 'TRADER' : ((finRoleDef(agent && agent.role) || {}).label || 'FİNANS').toUpperCase();
13643
13659
  return 'TypeSafe · ' + role + (extra ? ' · ' + extra : '');
13644
13660
  }
@@ -14022,6 +14038,161 @@ async function finTsToolRequest(sidS, agent, ans, lines) {
14022
14038
  }
14023
14039
  }
14024
14040
 
14041
+ /* ================= POZİSYON YÖNETİCİSİ (JEV · 5 sn) =================
14042
+ Açık pozisyon varken watchdog turu (5 sn) bu turu tetikler: pozisyon başına
14043
+ kapatma / kısmi kapatma / SL-koruma kararlarını JEV verir; kodu
14044
+ finTsManageOpen uygular. Kendi talimatı (posManagerNote) birincil kuraldır —
14045
+ trade ajanının talimatından bağımsızdır. LLM kullanılmaz. */
14046
+ const FIN_POSMGR_ID = 'posmanager';
14047
+ const FIN_POSMGR_AGENT = { main: false, role: '', __posmgr: true };
14048
+
14049
+ /* Watchdog turundan çağrılır: uygunSA yönetici turunu arka planda başlatır
14050
+ (MT5 pozisyon verisi watchdog turundan gelir — ikinci sorgu yok). */
14051
+ function finPosManagerMaybe(positions, account) {
14052
+ const pm = financeState.posManager;
14053
+ if (!pm || pm.busy) return;
14054
+ let f = {};
14055
+ try { f = finCfg(); } catch {}
14056
+ if (f.posManagerEnabled === false) return;
14057
+ const list = Array.isArray(positions) ? positions.filter(Boolean) : [];
14058
+ pm.lastPosCount = list.length;
14059
+ if (!list.length) return;
14060
+ if (!mt5bridge.running) return;
14061
+ if (!typesafeMod.cfg().apiKey) {
14062
+ const now = Date.now();
14063
+ if (!pm.noKeyAt || now - pm.noKeyAt > 10 * 60 * 1000) {
14064
+ pm.noKeyAt = now;
14065
+ finTsPost(FIN_POSMGR_ID, FIN_POSMGR_AGENT, '⚠ TypeSafe anahtarı yok — pozisyon yöneticisi çalışamıyor (Ayarlar → TypeSafe).', 'anahtar');
14066
+ }
14067
+ return;
14068
+ }
14069
+ finPosManagerRound(list, account).catch((e) => {
14070
+ pm.busy = false;
14071
+ pm.lastErr = String((e && e.message) || e).slice(0, 200);
14072
+ });
14073
+ }
14074
+
14075
+ async function finPosManagerRound(positions, account) {
14076
+ const pm = financeState.posManager;
14077
+ if (!pm || pm.busy) return;
14078
+ pm.busy = true;
14079
+ pm.rounds = (Number(pm.rounds) || 0) + 1;
14080
+ pm.lastAt = Date.now();
14081
+ const f = finCfg();
14082
+ const lines = [];
14083
+ try {
14084
+ /* pozisyon sembolleri: piyasa + gösterge (tur başına tek Jev çağrısı) */
14085
+ const syms = [...new Set(positions.map((p) => String(p.symbol || '').toUpperCase()).filter(Boolean))].slice(0, 8);
14086
+ const market = {};
14087
+ const posTf = positions.map((p) => finLearnParseTf(p && p.comment)).find((t) => t) || '';
14088
+ for (const sym of syms) {
14089
+ const tf = finLearnNormTf(posTf) || finTsStrategyTf() || 'M15';
14090
+ const [mktR, indR] = await Promise.all([
14091
+ financetools.handlers.mt5_market({ symbols: [sym] }),
14092
+ financetools.handlers.mt5_indicators({ symbol: sym, timeframe: tf, count: 300, indicators: ['ATR(14)', 'EMA(50)', 'EMA(200)', 'RSI(14)'] }),
14093
+ ]);
14094
+ market[sym] = { row: (mktR && mktR.symbols && mktR.symbols[0]) || null, tf, ind: finTsIndLine(indR) };
14095
+ }
14096
+ const state = {
14097
+ rol: 'POZİSYON YÖNETİCİSİ',
14098
+ yerel_saat: new Date().getHours(),
14099
+ hesap: {
14100
+ balance: Number(account && account.balance) || 0,
14101
+ equity: Number(account && account.equity) || 0,
14102
+ margin_free: Number(account && account.margin_free) || 0,
14103
+ acik_pozisyon: positions.length,
14104
+ },
14105
+ pozisyonlar: positions.map((p) => finTsPosState(p)),
14106
+ piyasa: {},
14107
+ alarmlar: (financeState.alerts || []).slice(0, 8).map((a) => ({ sembol: a.symbol, yon: a.direction, fiyat: a.price, mod: a.once ? 'tek' : 'tekrarlı' })),
14108
+ sahip_talimati: String(f.posManagerNote || '').slice(0, 1500) || '(yok — genel disiplin: kârı koru, zararı sınırla, SL/TP aktif yönet)',
14109
+ ekip_yanitlari: finTsFeedText(6),
14110
+ };
14111
+ for (const sym of syms) {
14112
+ const d = market[sym];
14113
+ const row = d.row || {};
14114
+ state.piyasa[sym] = {
14115
+ zaman_dilimi: d.tf,
14116
+ bid: Number(row.bid) || null,
14117
+ ask: Number(row.ask) || null,
14118
+ spread: Number(row.spread) || null,
14119
+ gosterge: d.ind,
14120
+ };
14121
+ }
14122
+ const questions = {};
14123
+ positions.forEach((pp, i) => {
14124
+ const sym = String(pp.symbol || '').toUpperCase();
14125
+ const ps = finTsPosState(pp);
14126
+ const kzTxt = ps.kz_r == null ? 'R bilinmiyor' : `kâr ${ps.kz_r}R`;
14127
+ const bestTxt = ps.en_iyi_r == null ? '' : ` · en iyi ${ps.en_iyi_r}R`;
14128
+ const slTxt = Number(pp.sl) > 0 ? `SL ${pp.sl}` : 'SL YOK';
14129
+ questions['kapat_' + i] = {
14130
+ type: 'noul',
14131
+ instructions: `${sym} açık pozisyon #${pp.ticket} (${ps.yon}, kâr ${Number(pp.profit) || 0}, ${kzTxt}${bestTxt}, ${slTxt}) ŞİMDİ kapatılmalı mı? Sahip talimatını, kârı korumayı ve momentum dönüşünü değerlendir.`,
14132
+ criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
14133
+ };
14134
+ questions['kismi_' + i] = {
14135
+ type: 'choice',
14136
+ instructions: `${sym} #${pp.ticket} (${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ç.' : ' Sahip talimatını ve kâr realize etmeyi değerlendir.'}`,
14137
+ criteria: {
14138
+ yok: 'Kısmi kapatma yok — pozisyon tam kalsın',
14139
+ yuzde25: 'Pozisyonun %25’i kapatılsın',
14140
+ yuzde50: 'Pozisyonun %50’si kapatılsın',
14141
+ yuzde75: 'Pozisyonun %75’i kapatılsın',
14142
+ },
14143
+ };
14144
+ questions['sl_' + i] = {
14145
+ type: 'choice',
14146
+ instructions: `${sym} #${pp.ticket} 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.`,
14147
+ criteria: {
14148
+ birak: 'Dokunma — SL yerinde kalsın',
14149
+ be: 'Breakeven — SL girişe çekilsin (kârı kilitle)',
14150
+ trail: 'Trailing — SL fiyatın gerisine taşınsın (kârı takip et)',
14151
+ sikilastir: 'Sıkılaştır — SL fiyata yaklaştırılsın (kârı daha çok koru)',
14152
+ koru: 'Koruma koy — SL’siz pozisyona ATR tabanlı stop eklensin',
14153
+ },
14154
+ };
14155
+ });
14156
+ const ans = await finTsAsk(state, questions);
14157
+ for (let i = 0; i < positions.length; i++) {
14158
+ const pp = positions[i];
14159
+ const sym = String(pp.symbol || '').toUpperCase();
14160
+ const ex = finTsNoul(ans, 'kapat_' + i);
14161
+ if (ex != null && ex >= FIN_TS_EXIT_P) {
14162
+ let r = null;
14163
+ try {
14164
+ r = await financetools.handlers.mt5_close(
14165
+ { ticket: Number(pp.ticket), reason: `TypeSafe yönetici: kapat p=${ex.toFixed(2)}` },
14166
+ { sessionId: FIN_POSMGR_ID }
14167
+ );
14168
+ } catch (e) {
14169
+ r = { ok: false, error: String((e && e.message) || e) };
14170
+ }
14171
+ lines.push(`- ${sym} #${pp.ticket}: KAPAT (p=${ex.toFixed(2)}) ${r && r.ok ? '✓ kapatıldı' : '✗ ' + ((r && r.error) || 'hata')}`);
14172
+ continue;
14173
+ }
14174
+ const psNow = finTsPosState(pp);
14175
+ lines.push(`- ${sym} #${pp.ticket}: açık (kapat=${ex == null ? 'yanıt yok' : ex.toFixed(2)} < ${FIN_TS_EXIT_P}) · ${psNow.kz_r == null ? 'kâr ?' : 'kâr ' + psNow.kz_r + 'R'}`);
14176
+ try { await finTsManageOpen(FIN_POSMGR_ID, pm, sym, pp, market[sym], ans, i, lines); } catch {}
14177
+ }
14178
+ finTsPost(
14179
+ FIN_POSMGR_ID,
14180
+ FIN_POSMGR_AGENT,
14181
+ `🛡️ Pozisyon yöneticisi turu #${pm.rounds} (LLM yok — 5 sn):\n` + lines.join('\n'),
14182
+ 'yönetim'
14183
+ );
14184
+ } catch (e) {
14185
+ pm.lastErr = String((e && e.message) || e).slice(0, 200);
14186
+ const now = Date.now();
14187
+ if (!pm.errAt || now - pm.errAt > 2 * 60 * 1000) {
14188
+ pm.errAt = now;
14189
+ finTsPost(FIN_POSMGR_ID, FIN_POSMGR_AGENT, '⚠ Yönetici turu hatası: ' + pm.lastErr, 'hata');
14190
+ }
14191
+ } finally {
14192
+ pm.busy = false;
14193
+ }
14194
+ }
14195
+
14025
14196
  function finTsSchedule(sid, agent, sec) {
14026
14197
  if (!agent || !financeState.agents.has(String(sid))) return;
14027
14198
  clearTimeout(agent.timer);
@@ -14156,6 +14327,8 @@ async function finTypeSafeRound(sid, agent) {
14156
14327
  }
14157
14328
  const questions = {};
14158
14329
  const talimatZorunlu = finTsMandatoryEntry();
14330
+ /* Yönetici açıkken açık pozisyon kararları ONA aittir (çift yönetim yok) */
14331
+ const posManagerOn = f.posManagerEnabled !== false;
14159
14332
  symbols.forEach((sym, i) => {
14160
14333
  questions['yon_' + i] = {
14161
14334
  type: 'choice',
@@ -14211,7 +14384,7 @@ async function finTypeSafeRound(sid, agent) {
14211
14384
  asagi_atr: 'Fiyat -0.5×ATR altında alarm',
14212
14385
  },
14213
14386
  };
14214
- if (posBySym[sym]) {
14387
+ if (posBySym[sym] && !posManagerOn) {
14215
14388
  const pp = posBySym[sym];
14216
14389
  const ps = finTsPosState(pp);
14217
14390
  const kzTxt = ps.kz_r == null ? 'R bilinmiyor' : `kâr ${ps.kz_r}R`;
@@ -14283,6 +14456,11 @@ async function finTypeSafeRound(sid, agent) {
14283
14456
  const cal = finTsCalibration(sym, market[sym].tf);
14284
14457
  const thAction = cal.action;
14285
14458
  if (pos) {
14459
+ /* POZİSYON YÖNETİCİSİ açık: kapat/kısmi/SL kararları onun turunda */
14460
+ if (posManagerOn) {
14461
+ lines.push(`- ${sym}: pozisyon açık #${pos.ticket} — yönetim Pozisyon Yöneticisi'nde (5 sn Jev turu)`);
14462
+ continue;
14463
+ }
14286
14464
  const ex = finTsNoul(ans, 'kapat_' + i);
14287
14465
  if (ex != null && ex >= FIN_TS_EXIT_P) {
14288
14466
  const r = await financetools.handlers.mt5_close(
@@ -14735,6 +14913,16 @@ ipcMain.handle('finance:snapshot', async () => {
14735
14913
  equity: (financeState.equity || []).slice(-180),
14736
14914
  alerts: (financeState.alerts || []).slice(0, 50),
14737
14915
  watch: { on: !!financeState.watchTimer, managed: financeState.watch.size, lastAt: financeState.watchTickAt || 0 },
14916
+ /* POZİSYON YÖNETİCİSİ durumu (panel kartı) */
14917
+ posManager: {
14918
+ enabled: f.posManagerEnabled !== false,
14919
+ busy: !!financeState.posManager.busy,
14920
+ rounds: Number(financeState.posManager.rounds) || 0,
14921
+ lastAt: Number(financeState.posManager.lastAt) || 0,
14922
+ lastErr: String(financeState.posManager.lastErr || ''),
14923
+ positions: Number(financeState.posManager.lastPosCount) || 0,
14924
+ note: String(f.posManagerNote || ''),
14925
+ },
14738
14926
  };
14739
14927
  });
14740
14928
 
@@ -14964,6 +15152,9 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
14964
15152
  if (p.reentryCooldownMin !== undefined) f.reentryCooldownMin = Math.max(0, Math.min(1440, Math.round(Number(p.reentryCooldownMin) || 0)));
14965
15153
  if (p.maxPerCurrency !== undefined) f.maxPerCurrency = Math.max(0, Math.min(20, Math.round(Number(p.maxPerCurrency) || 0)));
14966
15154
  if (p.shadowMode !== undefined) f.shadowMode = !!p.shadowMode;
15155
+ /* POZİSYON YÖNETİCİSİ: kendi talimatı + aç/kapa */
15156
+ if (p.posManagerNote !== undefined) f.posManagerNote = String(p.posManagerNote || '').slice(0, 1500);
15157
+ if (p.posManagerEnabled !== undefined) f.posManagerEnabled = p.posManagerEnabled !== false;
14967
15158
  if (p.planTime !== undefined) f.planTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.planTime || '').trim()) ? String(p.planTime).trim() : '';
14968
15159
  if (p.reviewTime !== undefined) f.reviewTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.reviewTime || '').trim()) ? String(p.reviewTime).trim() : '';
14969
15160
  /* TRADE SAATLERİ: {on, start, end} — yerel saat; aralık dışında ajan turları
@@ -219,6 +219,19 @@
219
219
  </div>
220
220
  <div id="finTraderStatus">Kapalı</div>
221
221
  </div>
222
+ <div class="fin-card" id="finPosCard" data-fin-card="posmgr">
223
+ <div class="fin-card-title">POZİSYON YÖNETİCİSİ <span id="finPosDot" class="fin-dot off" title="Pozisyon yöneticisi durumu"></span><span id="finPosInfo" class="fin-hint"></span></div>
224
+ <div class="fin-field fin-check">
225
+ <label title="Açık pozisyon varken 5 saniyede bir JEV turu: kapat / kısmi kapat / SL taşı kararları. Trade ajanından bağımsız ayrı bir ajan — kendi talimatıyla çalışır; LLM kullanmaz.">
226
+ <input id="finPosOn" type="checkbox" /> Aktif — açık pozisyonları 5 sn'de bir Jev ile yönet
227
+ </label>
228
+ </div>
229
+ <div class="fin-field">
230
+ <span>Yönetici talimatı <i class="fin-hint">— yalnız bu ajanı bağlar</i></span>
231
+ <textarea id="finPosNote" rows="2" maxlength="1500" placeholder="örn. Kâr 1R'ye gelince %50 kısmi al ve SL'yi breakeven'a çek; 2R'de kalanı kapat; zarar -0.5R'yi geçerse kes" title="Bu talimat YALNIZ Pozisyon Yöneticisi Jev ajanına gider (5 sn turu, kapat/kısmi/SL kararları); kaydetmek için alandan çık"></textarea>
232
+ </div>
233
+ <div id="finPosStatus">Beklemede — açık pozisyon yok</div>
234
+ </div>
222
235
  <div class="fin-card" id="finRiskCard" data-fin-card="risk" hidden>
223
236
  <div class="fin-card-title">RİSK OTOMASYONU <span id="finWatchDot" class="fin-dot off" title="Koruma döngüsü"></span><span id="finWatchInfo" class="fin-hint"></span></div>
224
237
  <div class="fin-field fin-check">
@@ -244,6 +244,11 @@ const els = {
244
244
  finTraderBtn: $('#finTraderBtn'),
245
245
  finTraderDot: $('#finTraderDot'),
246
246
  finTraderStatus: $('#finTraderStatus'),
247
+ finPosDot: $('#finPosDot'),
248
+ finPosInfo: $('#finPosInfo'),
249
+ finPosOn: $('#finPosOn'),
250
+ finPosNote: $('#finPosNote'),
251
+ finPosStatus: $('#finPosStatus'),
247
252
  finPosList: $('#finPosList'),
248
253
  finPosCount: $('#finPosCount'),
249
254
  finOrdList: $('#finOrdList'),
@@ -9718,6 +9723,7 @@ let finModelsFilled = false;
9718
9723
  let finLastPrices = new Map(); /* sembol → son bid (renk için) */
9719
9724
  let finCfgCache = null; /* son snapshot cfg — rol→skill modalı bundan okur */
9720
9725
  let finStrategyDirty = false; /* textarea'da kaydedilmeyi bekleyen ajan talimatı var */
9726
+ let finPosNoteDirty = false; /* kaydedilmeyi bekleyen pozisyon yöneticisi talimatı */
9721
9727
  const FIN_COLOR_UP = 'fs-up';
9722
9728
  const FIN_COLOR_DOWN = 'fs-down';
9723
9729
 
@@ -9933,6 +9939,8 @@ function finTraderInputsSet(cfg) {
9933
9939
  if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
9934
9940
  finSymLimitsRender(cfg.symbolLimits);
9935
9941
  if (els.finStrategy && ae !== els.finStrategy && !finStrategyDirty) els.finStrategy.value = cfg.strategy || '';
9942
+ if (els.finPosNote && ae !== els.finPosNote && !finPosNoteDirty) els.finPosNote.value = cfg.posManagerNote || '';
9943
+ if (els.finPosOn && ae !== els.finPosOn) els.finPosOn.checked = cfg.posManagerEnabled !== false;
9936
9944
  if (els.finMaxTradesDay && ae !== els.finMaxTradesDay) els.finMaxTradesDay.value = Number(cfg.maxTradesPerDay) || 0;
9937
9945
  if (els.finLossStreak && ae !== els.finLossStreak) els.finLossStreak.value = Number(cfg.lossStreakLimit) || 0;
9938
9946
  if (els.finLossStreakPause && ae !== els.finLossStreakPause) els.finLossStreakPause.value = Number(cfg.lossStreakPauseMin) || 0;
@@ -10094,6 +10102,39 @@ function finRenderTrader(trader, cfg) {
10094
10102
  }
10095
10103
  }
10096
10104
 
10105
+ /* POZİSYON YÖNETİCİSİ kartı: açık pozisyon varken 5 sn Jev turu durumu */
10106
+ function finRenderPosManager(pm, cfg) {
10107
+ if (!pm) return;
10108
+ const enabled = cfg ? cfg.posManagerEnabled !== false : pm.enabled !== false;
10109
+ const pos = Number(pm.positions) || 0;
10110
+ if (els.finPosDot) {
10111
+ els.finPosDot.classList.remove('on', 'off', 'busy');
10112
+ els.finPosDot.classList.add(!enabled ? 'off' : pm.busy ? 'busy' : pos ? 'on' : 'off');
10113
+ els.finPosDot.title = !enabled
10114
+ ? 'Pozisyon yöneticisi kapalı'
10115
+ : pos
10116
+ ? 'İzliyor — ' + pos + ' açık pozisyon (5 sn Jev turu)'
10117
+ : 'Açık pozisyon yok — beklemede';
10118
+ }
10119
+ if (els.finPosInfo) els.finPosInfo.textContent = pm.rounds ? '· tur ' + pm.rounds : '';
10120
+ if (els.finPosStatus) {
10121
+ let t;
10122
+ if (!enabled) t = 'Kapalı — kutucuktan aç';
10123
+ else if (pm.busy) t = 'Tur çalışıyor…';
10124
+ else if (!pos) t = 'Beklemede — açık pozisyon yok';
10125
+ else t = 'İzliyor · ' + pos + ' pozisyon';
10126
+ if (pm.lastAt) {
10127
+ const d = new Date(pm.lastAt);
10128
+ const p = (x) => String(x).padStart(2, '0');
10129
+ t += ' · son tur ' + p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds());
10130
+ }
10131
+ if (pm.lastErr) t += ' · hata: ' + String(pm.lastErr).slice(0, 60);
10132
+ els.finPosStatus.textContent = t;
10133
+ els.finPosStatus.classList.toggle('on', enabled && !!pos);
10134
+ els.finPosStatus.classList.toggle('busy', !!pm.busy);
10135
+ }
10136
+ }
10137
+
10097
10138
  /* ---- RİSK OTOMASYONU + PERFORMANS panosu ---- */
10098
10139
  function finDayKey(ms) {
10099
10140
  const d = new Date(Number(ms) || 0);
@@ -10254,6 +10295,7 @@ async function finSnapshot() {
10254
10295
  finTraderInputsSet(r.cfg);
10255
10296
  finRenderRoles(r.cfg);
10256
10297
  finRenderTrader(r.trader, r.cfg);
10298
+ finRenderPosManager(r.posManager, r.cfg);
10257
10299
  finRenderAutomation(r.cfg, r.watch);
10258
10300
  /* GİZLİ RİSK OTOMASYONU AKTİF OLAMAZ: sunucu hâlâ açık diyorsa kapat */
10259
10301
  if (!finView.risk && r.cfg && r.cfg.watchdog !== false) finSaveCfg({ watchdog: false });
@@ -10392,8 +10434,10 @@ let finWatchDirty = false; /* picker/input değişikliği kaydedilmeyi bekliyor
10392
10434
  function finSaveCfg(patch) {
10393
10435
  if (patch && patch.symbols !== undefined) finWatchDirty = true;
10394
10436
  if (patch && patch.strategy !== undefined) finStrategyDirty = true;
10437
+ if (patch && patch.posManagerNote !== undefined) finPosNoteDirty = true;
10395
10438
  clearTimeout(finCfgTimer);
10396
10439
  const wasStrategy = !!(patch && patch.strategy !== undefined);
10440
+ const wasPosNote = !!(patch && patch.posManagerNote !== undefined);
10397
10441
  finCfgTimer = setTimeout(() => {
10398
10442
  beast.financeSettings(patch)
10399
10443
  .then(() => {
@@ -10401,6 +10445,10 @@ function finSaveCfg(patch) {
10401
10445
  finStrategyDirty = false;
10402
10446
  if (finCfgCache) finCfgCache.strategy = patch.strategy;
10403
10447
  }
10448
+ if (wasPosNote) {
10449
+ finPosNoteDirty = false;
10450
+ if (finCfgCache) finCfgCache.posManagerNote = patch.posManagerNote;
10451
+ }
10404
10452
  finWatchDirty = false;
10405
10453
  })
10406
10454
  .catch(() => { finWatchDirty = false; });
@@ -10838,6 +10886,29 @@ if (els.finStrategy) {
10838
10886
  toast(v.trim() ? 'Ajan talimatı kaydedildi — tüm ajanlar sonraki turda uygular' : 'Ajan talimatı temizlendi');
10839
10887
  });
10840
10888
  }
10889
+ /* POZİSYON YÖNETİCİSİ: 5 sn Jev turu — kendi talimatı + aç/kapa */
10890
+ if (els.finPosNote) {
10891
+ els.finPosNote.addEventListener('input', () => {
10892
+ finSaveCfg({ posManagerNote: els.finPosNote.value });
10893
+ });
10894
+ els.finPosNote.addEventListener('change', () => {
10895
+ const v = els.finPosNote.value;
10896
+ if (finCfgCache && String(finCfgCache.posManagerNote || '') === v) return;
10897
+ finSaveCfg({ posManagerNote: v });
10898
+ toast(v.trim() ? 'Yönetici talimatı kaydedildi — sonraki yönetici turunda uygular' : 'Yönetici talimatı temizlendi');
10899
+ });
10900
+ }
10901
+ if (els.finPosOn) {
10902
+ els.finPosOn.addEventListener('change', () => {
10903
+ finSaveCfg({ posManagerEnabled: els.finPosOn.checked });
10904
+ toast(
10905
+ els.finPosOn.checked
10906
+ ? 'Pozisyon yöneticisi AÇIK — açık pozisyonlar 5 sn Jev turuyla yönetilecek'
10907
+ : 'Pozisyon yöneticisi kapalı — pozisyon yönetimi trade ajanına döndü'
10908
+ );
10909
+ finSnapshot();
10910
+ });
10911
+ }
10841
10912
  /* TRADE AJANI: modelleri yeniden çek — üstteki ⟳ ile aynı mantık
10842
10913
  (models:refresh). Eksik/yeni modeller listeye girer, seçim korunur. */
10843
10914
  if (els.finModelRefreshBtn) {
@@ -5625,6 +5625,25 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5625
5625
  #finTraderStatus.on.busy { color: var(--accent); }
5626
5626
  #finTraderStatus.on.busy::before { color: #f59e0b; }
5627
5627
 
5628
+ /* POZİSYON YÖNETİCİSİ kartı — trader durum satırıyla aynı görünüm */
5629
+ #finPosStatus {
5630
+ margin-top: 8px;
5631
+ font-size: 10.5px;
5632
+ color: var(--muted);
5633
+ background: var(--panel2);
5634
+ border: 1px solid var(--border);
5635
+ border-radius: 7px;
5636
+ padding: 5px 8px;
5637
+ white-space: nowrap;
5638
+ overflow: hidden;
5639
+ text-overflow: ellipsis;
5640
+ }
5641
+ #finPosStatus::before { content: '● '; font-size: 9px; color: var(--muted); }
5642
+ #finPosStatus.on { color: var(--text); border-color: var(--accent); }
5643
+ #finPosStatus.on::before { color: #22c55e; }
5644
+ #finPosStatus.on.busy { color: var(--accent); }
5645
+ #finPosStatus.on.busy::before { color: #f59e0b; }
5646
+
5628
5647
  #finPosList, #finOrdList, #finSymList { display: flex; flex-direction: column; gap: 5px; }
5629
5648
  .fin-empty { font-size: 11px; color: var(--muted); padding: 2px 0 4px; }
5630
5649