beast-agent 2.72.0 → 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.0",
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) */
@@ -10005,10 +10008,22 @@ function finCfg() {
10005
10008
  if (!Number.isFinite(Number(f.maxTradesPerDay))) f.maxTradesPerDay = 10;
10006
10009
  if (!Number.isFinite(Number(f.lossStreakLimit))) f.lossStreakLimit = 2;
10007
10010
  if (!Number.isFinite(Number(f.lossStreakPauseMin))) f.lossStreakPauseMin = 30;
10008
- if (!Number.isFinite(Number(f.reentryCooldownMin))) f.reentryCooldownMin = 15;
10011
+ /* RE-ENTRY BEKLEME: kural VARSAYILAN KAPALI (0) — isteyen Gelişmiş
10012
+ ayarlardan açar. Eski sürümün zorla koyduğu 15 dk değeri BİR KEZ
10013
+ sıfırlanır; kullanıcı bilerek başka değer girdiyse dokunulmaz. */
10014
+ if (!Number.isFinite(Number(f.reentryCooldownMin))) f.reentryCooldownMin = 0;
10015
+ if (f.reentryDefaultCleared !== true) {
10016
+ f.reentryDefaultCleared = true;
10017
+ if (Number(f.reentryCooldownMin) === 15) f.reentryCooldownMin = 0;
10018
+ try { saveSettings(); } catch {}
10019
+ }
10009
10020
  if (!Number.isFinite(Number(f.maxPerCurrency))) f.maxPerCurrency = 3;
10010
10021
  /* SHADOW MOD: emir gönderilmez — kararlar gerekçesiyle günlüğe yazılır */
10011
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 = '';
10012
10027
  /* GÜNLÜK RUTİN: plan/review saatleri (HH:MM; boş = kapalı) — hafta içi cron */
10013
10028
  if (typeof f.planTime !== 'string') f.planTime = '';
10014
10029
  if (typeof f.reviewTime !== 'string') f.reviewTime = '';
@@ -10402,6 +10417,9 @@ function finExcFlush(force) {
10402
10417
 
10403
10418
  function finAgentInfo(sid) {
10404
10419
  try {
10420
+ if (String(sid || '') === FIN_POSMGR_ID) {
10421
+ return { main: false, role: 'posmanager', label: 'Pozisyon Yöneticisi' };
10422
+ }
10405
10423
  const a = financeState.agents.get(String(sid || ''));
10406
10424
  if (a) {
10407
10425
  const role = String(a.role || '');
@@ -11587,6 +11605,11 @@ async function finWatchTick() {
11587
11605
  try { await finRecordClose(ticket, st); } catch {}
11588
11606
  }
11589
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
+ }
11590
11613
  try { await finCheckAlerts(); } catch {}
11591
11614
  if (!financeState.lastStatsAt || Date.now() - financeState.lastStatsAt > 120000) {
11592
11615
  financeState.lastStatsAt = Date.now();
@@ -13631,6 +13654,7 @@ function finTsAgentMode(agent) {
13631
13654
  }
13632
13655
 
13633
13656
  function finTsWho(agent, extra) {
13657
+ if (agent && agent.__posmgr) return 'TypeSafe · POZİSYON YÖNETİCİSİ' + (extra ? ' · ' + extra : '');
13634
13658
  const role = agent && agent.main ? 'TRADER' : ((finRoleDef(agent && agent.role) || {}).label || 'FİNANS').toUpperCase();
13635
13659
  return 'TypeSafe · ' + role + (extra ? ' · ' + extra : '');
13636
13660
  }
@@ -14014,6 +14038,161 @@ async function finTsToolRequest(sidS, agent, ans, lines) {
14014
14038
  }
14015
14039
  }
14016
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
+
14017
14196
  function finTsSchedule(sid, agent, sec) {
14018
14197
  if (!agent || !financeState.agents.has(String(sid))) return;
14019
14198
  clearTimeout(agent.timer);
@@ -14148,6 +14327,8 @@ async function finTypeSafeRound(sid, agent) {
14148
14327
  }
14149
14328
  const questions = {};
14150
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;
14151
14332
  symbols.forEach((sym, i) => {
14152
14333
  questions['yon_' + i] = {
14153
14334
  type: 'choice',
@@ -14203,7 +14384,7 @@ async function finTypeSafeRound(sid, agent) {
14203
14384
  asagi_atr: 'Fiyat -0.5×ATR altında alarm',
14204
14385
  },
14205
14386
  };
14206
- if (posBySym[sym]) {
14387
+ if (posBySym[sym] && !posManagerOn) {
14207
14388
  const pp = posBySym[sym];
14208
14389
  const ps = finTsPosState(pp);
14209
14390
  const kzTxt = ps.kz_r == null ? 'R bilinmiyor' : `kâr ${ps.kz_r}R`;
@@ -14275,6 +14456,11 @@ async function finTypeSafeRound(sid, agent) {
14275
14456
  const cal = finTsCalibration(sym, market[sym].tf);
14276
14457
  const thAction = cal.action;
14277
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
+ }
14278
14464
  const ex = finTsNoul(ans, 'kapat_' + i);
14279
14465
  if (ex != null && ex >= FIN_TS_EXIT_P) {
14280
14466
  const r = await financetools.handlers.mt5_close(
@@ -14727,6 +14913,16 @@ ipcMain.handle('finance:snapshot', async () => {
14727
14913
  equity: (financeState.equity || []).slice(-180),
14728
14914
  alerts: (financeState.alerts || []).slice(0, 50),
14729
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
+ },
14730
14926
  };
14731
14927
  });
14732
14928
 
@@ -14956,6 +15152,9 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
14956
15152
  if (p.reentryCooldownMin !== undefined) f.reentryCooldownMin = Math.max(0, Math.min(1440, Math.round(Number(p.reentryCooldownMin) || 0)));
14957
15153
  if (p.maxPerCurrency !== undefined) f.maxPerCurrency = Math.max(0, Math.min(20, Math.round(Number(p.maxPerCurrency) || 0)));
14958
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;
14959
15158
  if (p.planTime !== undefined) f.planTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.planTime || '').trim()) ? String(p.planTime).trim() : '';
14960
15159
  if (p.reviewTime !== undefined) f.reviewTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.reviewTime || '').trim()) ? String(p.reviewTime).trim() : '';
14961
15160
  /* TRADE SAATLERİ: {on, start, end} — yerel saat; aralık dışında ajan turları
@@ -203,7 +203,7 @@
203
203
  <div class="fin-field"><span>Günlük max işlem</span><input id="finMaxTradesDay" type="number" min="0" max="50" step="1" title="Bir günde en fazla kaç işlem açılabilir (0 = sınırsız)" /></div>
204
204
  <div class="fin-field"><span>Kayıp serisi limiti</span><input id="finLossStreak" type="number" min="0" max="10" step="1" title="Üst üste bu kadar kayıptan sonra yeni işlem kilidi (0 = kapalı)" /></div>
205
205
  <div class="fin-field"><span>Seri molası (dk)</span><input id="finLossStreakPause" type="number" min="0" max="1440" step="5" title="Kayıp serisi limitine ulaşınca kaç dakika yeni işlem açılmaz" /></div>
206
- <div class="fin-field"><span>Re-entry bekleme (dk)</span><input id="finReentry" type="number" min="0" max="1440" step="5" title="Aynı sembolda bir işlem kapandıktan sonra yeni giriş için beklenecek süre (0 = kapalı)" /></div>
206
+ <div class="fin-field"><span>Re-entry bekleme (dk)</span><input id="finReentry" type="number" min="0" max="1440" step="5" title="Aynı sembolda bir işlem kapandıktan sonra yeni giriş için beklenecek süre — VARSAYILAN KAPALI (0 = kapalı)" /></div>
207
207
  <div class="fin-field"><span>Kur başına max</span><input id="finMaxPerCurrency" type="number" min="0" max="20" step="1" title="Yönlü kur maruziyeti (korelasyon) limiti — örn. 3: aynı para biriminde en fazla ±3 net pozisyon (0 = kapalı)" /></div>
208
208
  <div class="fin-field"><span>Plan / Review (saat)</span><div class="fin-time-row"><input id="finPlanTime" type="time" title="Hafta içi günlük plan saati (boş = kapalı)" /><input id="finReviewTime" type="time" title="Hafta içi günlük review saati (boş = kapalı)" /></div></div>
209
209
  </div>
@@ -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