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 +1 -1
- package/src/main.js +201 -2
- package/src/renderer/index.html +14 -1
- package/src/renderer/renderer.js +71 -0
- package/src/renderer/style.css +19 -0
package/package.json
CHANGED
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
|
-
|
|
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ı
|
package/src/renderer/index.html
CHANGED
|
@@ -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">
|
package/src/renderer/renderer.js
CHANGED
|
@@ -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) {
|
package/src/renderer/style.css
CHANGED
|
@@ -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
|
|