beast-agent 2.72.1 → 2.74.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 +436 -10
- package/src/renderer/index.html +25 -2
- package/src/renderer/renderer.js +161 -0
- package/src/renderer/style.css +78 -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) */
|
|
@@ -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 || '');
|
|
@@ -10769,17 +10779,36 @@ function finEquitySample(account) {
|
|
|
10769
10779
|
function finDailyLossCheck(point) {
|
|
10770
10780
|
const cfg = finCfg();
|
|
10771
10781
|
const day = finstats.dayKey(point.at);
|
|
10782
|
+
/* TALİMAT: "gün başı bakiye" yazıldıysa taban O'dur; "günlük zarar %X"
|
|
10783
|
+
yazıldıysa limit ayarın yerine talimattan gelir (kullanıcı her gün günceller). */
|
|
10784
|
+
let startBal = 0;
|
|
10785
|
+
let limitPct = Number(cfg.maxDailyLossPct) || 0;
|
|
10786
|
+
try {
|
|
10787
|
+
const ins = finParsedInstr();
|
|
10788
|
+
if (Number(ins.entry.startBalance) > 0) startBal = Number(ins.entry.startBalance);
|
|
10789
|
+
if (Number(ins.entry.dailyLossPct) > 0) limitPct = Number(ins.entry.dailyLossPct);
|
|
10790
|
+
} catch {}
|
|
10772
10791
|
let ds = financeState.dayStart;
|
|
10773
10792
|
if (!ds || ds.day !== day) {
|
|
10774
|
-
financeState.dayStart = ds = {
|
|
10793
|
+
financeState.dayStart = ds = {
|
|
10794
|
+
day,
|
|
10795
|
+
equity: startBal > 0 ? startBal : Number(point.balance) > 0 ? Number(point.balance) : point.equity,
|
|
10796
|
+
warned: false,
|
|
10797
|
+
acted: false,
|
|
10798
|
+
fromNote: startBal > 0,
|
|
10799
|
+
};
|
|
10800
|
+
}
|
|
10801
|
+
if (startBal > 0) {
|
|
10802
|
+
ds.equity = startBal;
|
|
10803
|
+
ds.fromNote = true;
|
|
10775
10804
|
}
|
|
10776
|
-
if (ds.warned || !(
|
|
10805
|
+
if (ds.warned || !(limitPct > 0) || !(ds.equity > 0)) return;
|
|
10777
10806
|
const dd = ((ds.equity - point.equity) / ds.equity) * 100;
|
|
10778
|
-
if (dd >=
|
|
10807
|
+
if (dd >= limitPct) {
|
|
10779
10808
|
ds.warned = true;
|
|
10780
10809
|
const action = String(cfg.dailyLossAction || 'stop');
|
|
10781
10810
|
const tail = action === 'flatten' ? ' — ajanlar durduruldu, pozisyonlar kapatılıyor' : action === 'stop' ? ' — ajanlar durduruldu' : '';
|
|
10782
|
-
const line = `⚠️ Günlük kayıp %${dd.toFixed(2)} (limit %${
|
|
10811
|
+
const line = `⚠️ Günlük kayıp %${dd.toFixed(2)} (limit %${limitPct}${ds.fromNote ? ' · talimat gün başı bakiye' : ''})${tail}`;
|
|
10783
10812
|
financeLog('[risk] ' + line);
|
|
10784
10813
|
financeNotify(line, 'drawdown');
|
|
10785
10814
|
finJournal({ kind: 'drawdown', pct: Math.round(dd * 100) / 100, action });
|
|
@@ -11595,6 +11624,11 @@ async function finWatchTick() {
|
|
|
11595
11624
|
try { await finRecordClose(ticket, st); } catch {}
|
|
11596
11625
|
}
|
|
11597
11626
|
}
|
|
11627
|
+
/* POZİSYON YÖNETİCİSİ (JEV): açık pozisyon varken 5 sn'lik tur aynı anda
|
|
11628
|
+
yönetici karar turunu tetikler (ayrı MT5 sorgusu yok; meşgulse atlar) */
|
|
11629
|
+
if (positionsOk && positions.length) {
|
|
11630
|
+
try { finPosManagerMaybe(positions, account); } catch {}
|
|
11631
|
+
}
|
|
11598
11632
|
try { await finCheckAlerts(); } catch {}
|
|
11599
11633
|
if (!financeState.lastStatsAt || Date.now() - financeState.lastStatsAt > 120000) {
|
|
11600
11634
|
financeState.lastStatsAt = Date.now();
|
|
@@ -13639,6 +13673,7 @@ function finTsAgentMode(agent) {
|
|
|
13639
13673
|
}
|
|
13640
13674
|
|
|
13641
13675
|
function finTsWho(agent, extra) {
|
|
13676
|
+
if (agent && agent.__posmgr) return 'TypeSafe · POZİSYON YÖNETİCİSİ' + (extra ? ' · ' + extra : '');
|
|
13642
13677
|
const role = agent && agent.main ? 'TRADER' : ((finRoleDef(agent && agent.role) || {}).label || 'FİNANS').toUpperCase();
|
|
13643
13678
|
return 'TypeSafe · ' + role + (extra ? ' · ' + extra : '');
|
|
13644
13679
|
}
|
|
@@ -13682,6 +13717,115 @@ function finTsStrategyText() {
|
|
|
13682
13717
|
}
|
|
13683
13718
|
}
|
|
13684
13719
|
|
|
13720
|
+
/* ---- TALİMAT AYRIŞTIRICI ----
|
|
13721
|
+
Trade Ajanı + Pozisyon Yöneticisi notlarındaki SAYISAL kuralları koda çevirir:
|
|
13722
|
+
"risk %2" → giriş riski %2
|
|
13723
|
+
"lot x1.5" / "poz başı lot 1.5" / "lotu 2 kat" → lot çarpanı
|
|
13724
|
+
"martingale" / "martingale 1.5" → kayıp serisinde lot katlama (varsayılan ×2)
|
|
13725
|
+
"1R'de %50 kısmi kapat" / "%50 1R" / "kısmi %50" → otomatik kısmi close kuralı
|
|
13726
|
+
Saf metin ayrıştırma; uygulama ilgili turda kodla yapılır (LLM yok). */
|
|
13727
|
+
function finInstrNum(v) {
|
|
13728
|
+
const n = Number(String(v == null ? '' : v).replace(',', '.'));
|
|
13729
|
+
return isFinite(n) ? n : null;
|
|
13730
|
+
}
|
|
13731
|
+
|
|
13732
|
+
/* Para değeri: "10.000" / "10,000" → 10000; "10000,50" / "10000.50" → 10000.5 */
|
|
13733
|
+
function finInstrMoney(v) {
|
|
13734
|
+
const s = String(v == null ? '' : v).replace(/\s+/g, '');
|
|
13735
|
+
if (!s) return null;
|
|
13736
|
+
/* "5.000,50" / "5,000.50" → 5000.50 ; "10.000" → 10000 ; "10000,50" → 10000.5 */
|
|
13737
|
+
const m = s.match(/^(\d{1,3}(?:[.,]\d{3})+)(?:[.,](\d{1,2}))?$/);
|
|
13738
|
+
if (m) {
|
|
13739
|
+
const n = Number(m[1].replace(/[.,]/g, '') + (m[2] ? '.' + m[2] : ''));
|
|
13740
|
+
return isFinite(n) && n > 0 ? n : null;
|
|
13741
|
+
}
|
|
13742
|
+
const n2 = Number(s.replace(',', '.'));
|
|
13743
|
+
return isFinite(n2) && n2 > 0 ? n2 : null;
|
|
13744
|
+
}
|
|
13745
|
+
|
|
13746
|
+
function finParseInstructions(text) {
|
|
13747
|
+
const low = String(text || '').toLowerCase();
|
|
13748
|
+
const out = { riskPct: null, lotMult: null, martingale: null, partial: null, startBalance: null, dailyLossPct: null };
|
|
13749
|
+
if (!low.trim()) return out;
|
|
13750
|
+
/* "risk %2", "%2 risk", "risk yüzde 2", "riski 2 yap" → 2 (ilk sayı) */
|
|
13751
|
+
let m = low.match(
|
|
13752
|
+
/(?:risk\s*%\s*(\d+(?:[.,]\d+)?))|(?:%\s*(\d+(?:[.,]\d+)?)\s*risk)|(?:risk[^0-9]{0,14}(\d+(?:[.,]\d+)?))/
|
|
13753
|
+
);
|
|
13754
|
+
if (m) out.riskPct = finInstrNum(m[1] || m[2] || m[3]);
|
|
13755
|
+
if (out.riskPct != null) out.riskPct = Math.max(0.1, Math.min(10, out.riskPct));
|
|
13756
|
+
/* GÜN BAŞLANGIÇ BAKİYESİ (kullanıcı her gün talimata yazar): "başlangıç bakiye 10.000",
|
|
13757
|
+
"gün başı: 10000", "starting balance 10000" */
|
|
13758
|
+
m = low.match(/(?:ba[şs]lang[ıi][çc]\s*bakiye|g[üu]n\s*ba[şs][ıi](?:\s*bakiye)?|g[üu]nl[üu]k\s*ba[şs]lang[ıi][çc](?:\s*bakiye)?|starting\s*balance|start\s*balance)[^0-9]{0,14}(\d[\d.,\s]*)/);
|
|
13759
|
+
if (m) out.startBalance = finInstrMoney(m[1]);
|
|
13760
|
+
/* GÜNLÜK ZARAR LİMİTİ: "günlük zarar %3", "max günlük kayıp 3", "daily loss %3" */
|
|
13761
|
+
m = low.match(/(?:g[üu]nl[üu]k\s*(?:max(?:imum)?\s*)?(?:zarar|kay[ıi]p)|max\s*g[üu]nl[üu]k\s*kay[ıi]p|g[üu]nl[üu]k\s*limit|daily\s*(?:max\s*)?loss)[^0-9%]{0,12}%?\s*(\d+(?:[.,]\d+)?)/);
|
|
13762
|
+
if (m) {
|
|
13763
|
+
const v = finInstrNum(m[1]);
|
|
13764
|
+
if (v != null && v > 0) out.dailyLossPct = Math.max(0.1, Math.min(50, v));
|
|
13765
|
+
}
|
|
13766
|
+
m = low.match(/(?:lot\s*(?:çarpan[ıi]?|carpan[ıi]?)?\s*(?:x|×|\*)\s*(\d+(?:[.,]\d+)?))|(?:poz\s*ba[şs][ıi]\s*lot[^0-9]{0,12}(\d+(?:[.,]\d+)?))|(?:lot[^0-9]{0,12}(\d+(?:[.,]\d+)?)\s*kat)/);
|
|
13767
|
+
if (m) out.lotMult = finInstrNum(m[1] || m[2] || m[3]);
|
|
13768
|
+
if (out.lotMult != null) out.lotMult = Math.max(1, Math.min(5, out.lotMult));
|
|
13769
|
+
if (/martingale|mart[ıi]ngale/.test(low)) {
|
|
13770
|
+
/* faktör yalnız bitişik yazımdan okunur: "martingale 1.5", "martingale x2",
|
|
13771
|
+
"martingale çarpanı 1.5" — "martingale kullan, 1R..." gibi metinden sayı kapmaz */
|
|
13772
|
+
m = low.match(/mart[ıi]ngale\s*(?:x|×|çarpan[ıi]?|carpan[ıi]?)?\s*(\d+(?:[.,]\d+)?)/);
|
|
13773
|
+
out.martingale = m ? Math.max(1.1, Math.min(5, finInstrNum(m[1]) || 2)) : 2;
|
|
13774
|
+
}
|
|
13775
|
+
/* kısmi kapatma: "1R'de %50" → atR=1, pct=50; "%50 1R" → aynı; "kısmi %50" → atR=1 */
|
|
13776
|
+
m = low.match(/(\d+(?:[.,]\d+)?)\s*r[^%\d]{0,28}%\s*(\d+(?:[.,]\d+)?)/);
|
|
13777
|
+
if (m) out.partial = { atR: finInstrNum(m[1]), pct: finInstrNum(m[2]) };
|
|
13778
|
+
if (!out.partial) {
|
|
13779
|
+
m = low.match(/%\s*(\d+(?:[.,]\d+)?)[^%\d]{0,28}?(\d+(?:[.,]\d+)?)\s*r/);
|
|
13780
|
+
if (m) out.partial = { atR: finInstrNum(m[2]), pct: finInstrNum(m[1]) };
|
|
13781
|
+
}
|
|
13782
|
+
if (!out.partial) {
|
|
13783
|
+
m = low.match(/(?:k[ıi]smi|partial)[^%\d]{0,24}%\s*(\d+(?:[.,]\d+)?)/);
|
|
13784
|
+
if (m) out.partial = { atR: 1, pct: finInstrNum(m[1]) };
|
|
13785
|
+
}
|
|
13786
|
+
if (out.partial) {
|
|
13787
|
+
const p = out.partial;
|
|
13788
|
+
if (!(p.atR > 0)) p.atR = 1;
|
|
13789
|
+
p.atR = Math.min(10, Math.round(p.atR * 100) / 100);
|
|
13790
|
+
if (!(p.pct >= 5 && p.pct <= 90)) out.partial = null;
|
|
13791
|
+
else p.pct = Math.round(p.pct);
|
|
13792
|
+
}
|
|
13793
|
+
return out;
|
|
13794
|
+
}
|
|
13795
|
+
|
|
13796
|
+
/* İki notu birleştir: GİRİŞ kuralları trade notundan, YÖNETİM kuralları
|
|
13797
|
+
pozisyon yöneticisi notundan (yoksa trade notundan) gelir. */
|
|
13798
|
+
function finParsedInstr() {
|
|
13799
|
+
const trade = finParseInstructions(finTsStrategyText());
|
|
13800
|
+
const mgr = finParseInstructions(String(finCfg().posManagerNote || ''));
|
|
13801
|
+
return {
|
|
13802
|
+
entry: {
|
|
13803
|
+
riskPct: trade.riskPct != null ? trade.riskPct : mgr.riskPct,
|
|
13804
|
+
lotMult: trade.lotMult != null ? trade.lotMult : mgr.lotMult,
|
|
13805
|
+
martingale: trade.martingale != null ? trade.martingale : mgr.martingale,
|
|
13806
|
+
startBalance: trade.startBalance != null ? trade.startBalance : mgr.startBalance,
|
|
13807
|
+
dailyLossPct: trade.dailyLossPct != null ? trade.dailyLossPct : mgr.dailyLossPct,
|
|
13808
|
+
},
|
|
13809
|
+
manage: { partial: mgr.partial || trade.partial },
|
|
13810
|
+
};
|
|
13811
|
+
}
|
|
13812
|
+
|
|
13813
|
+
/* Rapor/state için tek satır özet: kod ne uygulayacak */
|
|
13814
|
+
function finInstrSummary(ins) {
|
|
13815
|
+
try {
|
|
13816
|
+
const p = [];
|
|
13817
|
+
if (ins.entry.riskPct != null) p.push(`risk %${ins.entry.riskPct}`);
|
|
13818
|
+
if (ins.entry.lotMult != null) p.push(`lot ×${ins.entry.lotMult}`);
|
|
13819
|
+
if (ins.entry.martingale != null) p.push(`martingale ×${ins.entry.martingale}`);
|
|
13820
|
+
if (ins.entry.startBalance != null) p.push(`gün başı ${ins.entry.startBalance}`);
|
|
13821
|
+
if (ins.entry.dailyLossPct != null) p.push(`günlük zarar limiti %${ins.entry.dailyLossPct}`);
|
|
13822
|
+
if (ins.manage.partial) p.push(`kısmi %${ins.manage.partial.pct} @${ins.manage.partial.atR}R`);
|
|
13823
|
+
return p.join(' · ');
|
|
13824
|
+
} catch {
|
|
13825
|
+
return '';
|
|
13826
|
+
}
|
|
13827
|
+
}
|
|
13828
|
+
|
|
13685
13829
|
/* Talimattan ZAMAN DİLİMİ çıkar: "M15", "1m", "1 dk", "4 saat" → M15/M1/H4 */
|
|
13686
13830
|
function finTsStrategyTf() {
|
|
13687
13831
|
const s = String(finTsStrategyText() || '');
|
|
@@ -14022,6 +14166,206 @@ async function finTsToolRequest(sidS, agent, ans, lines) {
|
|
|
14022
14166
|
}
|
|
14023
14167
|
}
|
|
14024
14168
|
|
|
14169
|
+
/* ================= POZİSYON YÖNETİCİSİ (JEV · 5 sn) =================
|
|
14170
|
+
Açık pozisyon varken watchdog turu (5 sn) bu turu tetikler: pozisyon başına
|
|
14171
|
+
kapatma / kısmi kapatma / SL-koruma kararlarını JEV verir; kodu
|
|
14172
|
+
finTsManageOpen uygular. Kendi talimatı (posManagerNote) birincil kuraldır —
|
|
14173
|
+
trade ajanının talimatından bağımsızdır. LLM kullanılmaz. */
|
|
14174
|
+
const FIN_POSMGR_ID = 'posmanager';
|
|
14175
|
+
const FIN_POSMGR_AGENT = { main: false, role: '', __posmgr: true };
|
|
14176
|
+
|
|
14177
|
+
/* Watchdog turundan çağrılır: uygunSA yönetici turunu arka planda başlatır
|
|
14178
|
+
(MT5 pozisyon verisi watchdog turundan gelir — ikinci sorgu yok). */
|
|
14179
|
+
function finPosManagerMaybe(positions, account) {
|
|
14180
|
+
const pm = financeState.posManager;
|
|
14181
|
+
if (!pm || pm.busy) return;
|
|
14182
|
+
let f = {};
|
|
14183
|
+
try { f = finCfg(); } catch {}
|
|
14184
|
+
if (f.posManagerEnabled === false) return;
|
|
14185
|
+
const list = Array.isArray(positions) ? positions.filter(Boolean) : [];
|
|
14186
|
+
pm.lastPosCount = list.length;
|
|
14187
|
+
if (!list.length) return;
|
|
14188
|
+
if (!mt5bridge.running) return;
|
|
14189
|
+
if (!typesafeMod.cfg().apiKey) {
|
|
14190
|
+
const now = Date.now();
|
|
14191
|
+
if (!pm.noKeyAt || now - pm.noKeyAt > 10 * 60 * 1000) {
|
|
14192
|
+
pm.noKeyAt = now;
|
|
14193
|
+
finTsPost(FIN_POSMGR_ID, FIN_POSMGR_AGENT, '⚠ TypeSafe anahtarı yok — pozisyon yöneticisi çalışamıyor (Ayarlar → TypeSafe).', 'anahtar');
|
|
14194
|
+
}
|
|
14195
|
+
return;
|
|
14196
|
+
}
|
|
14197
|
+
finPosManagerRound(list, account).catch((e) => {
|
|
14198
|
+
pm.busy = false;
|
|
14199
|
+
pm.lastErr = String((e && e.message) || e).slice(0, 200);
|
|
14200
|
+
});
|
|
14201
|
+
}
|
|
14202
|
+
|
|
14203
|
+
async function finPosManagerRound(positions, account) {
|
|
14204
|
+
const pm = financeState.posManager;
|
|
14205
|
+
if (!pm || pm.busy) return;
|
|
14206
|
+
pm.busy = true;
|
|
14207
|
+
pm.rounds = (Number(pm.rounds) || 0) + 1;
|
|
14208
|
+
pm.lastAt = Date.now();
|
|
14209
|
+
const f = finCfg();
|
|
14210
|
+
/* TALİMAT: kısmi close kuralı varsa KOD uygular (Jev'e sorulmaz) */
|
|
14211
|
+
const ins = finParsedInstr();
|
|
14212
|
+
const instrTxt = finInstrSummary(ins);
|
|
14213
|
+
const lines = [];
|
|
14214
|
+
try {
|
|
14215
|
+
/* pozisyon sembolleri: piyasa + gösterge (tur başına tek Jev çağrısı) */
|
|
14216
|
+
const syms = [...new Set(positions.map((p) => String(p.symbol || '').toUpperCase()).filter(Boolean))].slice(0, 8);
|
|
14217
|
+
const market = {};
|
|
14218
|
+
const posTf = positions.map((p) => finLearnParseTf(p && p.comment)).find((t) => t) || '';
|
|
14219
|
+
for (const sym of syms) {
|
|
14220
|
+
const tf = finLearnNormTf(posTf) || finTsStrategyTf() || 'M15';
|
|
14221
|
+
const [mktR, indR] = await Promise.all([
|
|
14222
|
+
financetools.handlers.mt5_market({ symbols: [sym] }),
|
|
14223
|
+
financetools.handlers.mt5_indicators({ symbol: sym, timeframe: tf, count: 300, indicators: ['ATR(14)', 'EMA(50)', 'EMA(200)', 'RSI(14)'] }),
|
|
14224
|
+
]);
|
|
14225
|
+
market[sym] = { row: (mktR && mktR.symbols && mktR.symbols[0]) || null, tf, ind: finTsIndLine(indR) };
|
|
14226
|
+
}
|
|
14227
|
+
const state = {
|
|
14228
|
+
rol: 'POZİSYON YÖNETİCİSİ',
|
|
14229
|
+
yerel_saat: new Date().getHours(),
|
|
14230
|
+
hesap: {
|
|
14231
|
+
balance: Number(account && account.balance) || 0,
|
|
14232
|
+
equity: Number(account && account.equity) || 0,
|
|
14233
|
+
margin_free: Number(account && account.margin_free) || 0,
|
|
14234
|
+
acik_pozisyon: positions.length,
|
|
14235
|
+
},
|
|
14236
|
+
pozisyonlar: positions.map((p) => finTsPosState(p)),
|
|
14237
|
+
piyasa: {},
|
|
14238
|
+
alarmlar: (financeState.alerts || []).slice(0, 8).map((a) => ({ sembol: a.symbol, yon: a.direction, fiyat: a.price, mod: a.once ? 'tek' : 'tekrarlı' })),
|
|
14239
|
+
sahip_talimati: String(f.posManagerNote || '').slice(0, 1500) || '(yok — genel disiplin: kârı koru, zararı sınırla, SL/TP aktif yönet)',
|
|
14240
|
+
talimat_ayarlari: instrTxt || '(sayısal kural yok)',
|
|
14241
|
+
gun_durumu: (() => {
|
|
14242
|
+
try {
|
|
14243
|
+
const sb = Number(ins.entry.startBalance) || 0;
|
|
14244
|
+
const eqNow = Number(account && account.equity) || 0;
|
|
14245
|
+
if (!(sb > 0) || !(eqNow > 0)) return '';
|
|
14246
|
+
const pct = Math.round(((eqNow - sb) / sb) * 10000) / 100;
|
|
14247
|
+
return `Gün başı ${sb} → %${pct >= 0 ? '+' : ''}${pct} (${pct >= 0 ? 'KÂR' : 'ZARAR'})`;
|
|
14248
|
+
} catch {
|
|
14249
|
+
return '';
|
|
14250
|
+
}
|
|
14251
|
+
})(),
|
|
14252
|
+
ekip_yanitlari: finTsFeedText(6),
|
|
14253
|
+
};
|
|
14254
|
+
for (const sym of syms) {
|
|
14255
|
+
const d = market[sym];
|
|
14256
|
+
const row = d.row || {};
|
|
14257
|
+
state.piyasa[sym] = {
|
|
14258
|
+
zaman_dilimi: d.tf,
|
|
14259
|
+
bid: Number(row.bid) || null,
|
|
14260
|
+
ask: Number(row.ask) || null,
|
|
14261
|
+
spread: Number(row.spread) || null,
|
|
14262
|
+
gosterge: d.ind,
|
|
14263
|
+
};
|
|
14264
|
+
}
|
|
14265
|
+
const questions = {};
|
|
14266
|
+
positions.forEach((pp, i) => {
|
|
14267
|
+
const sym = String(pp.symbol || '').toUpperCase();
|
|
14268
|
+
const ps = finTsPosState(pp);
|
|
14269
|
+
const kzTxt = ps.kz_r == null ? 'R bilinmiyor' : `kâr ${ps.kz_r}R`;
|
|
14270
|
+
const bestTxt = ps.en_iyi_r == null ? '' : ` · en iyi ${ps.en_iyi_r}R`;
|
|
14271
|
+
const slTxt = Number(pp.sl) > 0 ? `SL ${pp.sl}` : 'SL YOK';
|
|
14272
|
+
questions['kapat_' + i] = {
|
|
14273
|
+
type: 'noul',
|
|
14274
|
+
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.`,
|
|
14275
|
+
criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
|
|
14276
|
+
};
|
|
14277
|
+
/* Talimatta SAYISAL kısmi kuralı varsa Jev'e sorulmaz — kodu uygular */
|
|
14278
|
+
if (!ins.manage.partial) {
|
|
14279
|
+
questions['kismi_' + i] = {
|
|
14280
|
+
type: 'choice',
|
|
14281
|
+
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.'}`,
|
|
14282
|
+
criteria: {
|
|
14283
|
+
yok: 'Kısmi kapatma yok — pozisyon tam kalsın',
|
|
14284
|
+
yuzde25: 'Pozisyonun %25’i kapatılsın',
|
|
14285
|
+
yuzde50: 'Pozisyonun %50’si kapatılsın',
|
|
14286
|
+
yuzde75: 'Pozisyonun %75’i kapatılsın',
|
|
14287
|
+
},
|
|
14288
|
+
};
|
|
14289
|
+
}
|
|
14290
|
+
questions['sl_' + i] = {
|
|
14291
|
+
type: 'choice',
|
|
14292
|
+
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.`,
|
|
14293
|
+
criteria: {
|
|
14294
|
+
birak: 'Dokunma — SL yerinde kalsın',
|
|
14295
|
+
be: 'Breakeven — SL girişe çekilsin (kârı kilitle)',
|
|
14296
|
+
trail: 'Trailing — SL fiyatın gerisine taşınsın (kârı takip et)',
|
|
14297
|
+
sikilastir: 'Sıkılaştır — SL fiyata yaklaştırılsın (kârı daha çok koru)',
|
|
14298
|
+
koru: 'Koruma koy — SL’siz pozisyona ATR tabanlı stop eklensin',
|
|
14299
|
+
},
|
|
14300
|
+
};
|
|
14301
|
+
});
|
|
14302
|
+
const ans = await finTsAsk(state, questions);
|
|
14303
|
+
for (let i = 0; i < positions.length; i++) {
|
|
14304
|
+
const pp = positions[i];
|
|
14305
|
+
const sym = String(pp.symbol || '').toUpperCase();
|
|
14306
|
+
const ex = finTsNoul(ans, 'kapat_' + i);
|
|
14307
|
+
if (ex != null && ex >= FIN_TS_EXIT_P) {
|
|
14308
|
+
let r = null;
|
|
14309
|
+
try {
|
|
14310
|
+
r = await financetools.handlers.mt5_close(
|
|
14311
|
+
{ ticket: Number(pp.ticket), reason: `TypeSafe yönetici: kapat p=${ex.toFixed(2)}` },
|
|
14312
|
+
{ sessionId: FIN_POSMGR_ID }
|
|
14313
|
+
);
|
|
14314
|
+
} catch (e) {
|
|
14315
|
+
r = { ok: false, error: String((e && e.message) || e) };
|
|
14316
|
+
}
|
|
14317
|
+
lines.push(`- ${sym} #${pp.ticket}: KAPAT (p=${ex.toFixed(2)}) ${r && r.ok ? '✓ kapatıldı' : '✗ ' + ((r && r.error) || 'hata')}`);
|
|
14318
|
+
continue;
|
|
14319
|
+
}
|
|
14320
|
+
const psNow = finTsPosState(pp);
|
|
14321
|
+
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'}`);
|
|
14322
|
+
/* TALİMAT KISMİ KURALI (kod): "+atR'de %pct kapat" → otomatik uygula */
|
|
14323
|
+
const rule = ins.manage.partial;
|
|
14324
|
+
if (rule) {
|
|
14325
|
+
const stP = financeState.watch.get(String(pp.ticket)) || null;
|
|
14326
|
+
const done = !!(stP && stP.partial) || !!(pm.tsPartials && pm.tsPartials.has(String(pp.ticket)));
|
|
14327
|
+
if (!done && psNow.kz_r != null && psNow.kz_r >= rule.atR) {
|
|
14328
|
+
let rr = null;
|
|
14329
|
+
try {
|
|
14330
|
+
rr = await financetools.handlers.mt5_close(
|
|
14331
|
+
{ ticket: Number(pp.ticket), percent: rule.pct, kind: 'partial_tp', reason: `TypeSafe yönetici: TALİMAT kısmi %${rule.pct} @${rule.atR}R (kâr ${psNow.kz_r}R)` },
|
|
14332
|
+
{ sessionId: FIN_POSMGR_ID }
|
|
14333
|
+
);
|
|
14334
|
+
} catch (e) {
|
|
14335
|
+
rr = { ok: false, error: String((e && e.message) || e) };
|
|
14336
|
+
}
|
|
14337
|
+
if (rr && rr.ok) {
|
|
14338
|
+
if (stP) { stP.partial = true; finWatchSave(); }
|
|
14339
|
+
pm.tsPartials = pm.tsPartials || new Set();
|
|
14340
|
+
pm.tsPartials.add(String(pp.ticket));
|
|
14341
|
+
lines.push(`- ${sym} #${pp.ticket}: ✂️ TALİMAT kısmi %${rule.pct} TP ✓ (${psNow.kz_r}R ≥ ${rule.atR}R${rr.remaining != null ? ', kalan ' + rr.remaining + ' lot' : ''})`);
|
|
14342
|
+
} else {
|
|
14343
|
+
lines.push(`- ${sym} #${pp.ticket}: talimat kısmi reddedildi — ${(rr && rr.error) || 'hata'}`);
|
|
14344
|
+
}
|
|
14345
|
+
}
|
|
14346
|
+
}
|
|
14347
|
+
try { await finTsManageOpen(FIN_POSMGR_ID, pm, sym, pp, market[sym], ans, i, lines); } catch {}
|
|
14348
|
+
}
|
|
14349
|
+
finTsPost(
|
|
14350
|
+
FIN_POSMGR_ID,
|
|
14351
|
+
FIN_POSMGR_AGENT,
|
|
14352
|
+
`🛡️ Pozisyon yöneticisi turu #${pm.rounds} (LLM yok — 5 sn):\n` +
|
|
14353
|
+
(instrTxt ? 'TALİMAT AYARLARI (kod uygular): ' + instrTxt + '\n' : '') +
|
|
14354
|
+
lines.join('\n'),
|
|
14355
|
+
'yönetim'
|
|
14356
|
+
);
|
|
14357
|
+
} catch (e) {
|
|
14358
|
+
pm.lastErr = String((e && e.message) || e).slice(0, 200);
|
|
14359
|
+
const now = Date.now();
|
|
14360
|
+
if (!pm.errAt || now - pm.errAt > 2 * 60 * 1000) {
|
|
14361
|
+
pm.errAt = now;
|
|
14362
|
+
finTsPost(FIN_POSMGR_ID, FIN_POSMGR_AGENT, '⚠ Yönetici turu hatası: ' + pm.lastErr, 'hata');
|
|
14363
|
+
}
|
|
14364
|
+
} finally {
|
|
14365
|
+
pm.busy = false;
|
|
14366
|
+
}
|
|
14367
|
+
}
|
|
14368
|
+
|
|
14025
14369
|
function finTsSchedule(sid, agent, sec) {
|
|
14026
14370
|
if (!agent || !financeState.agents.has(String(sid))) return;
|
|
14027
14371
|
clearTimeout(agent.timer);
|
|
@@ -14094,6 +14438,25 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14094
14438
|
const posList = (posR && posR.positions) || [];
|
|
14095
14439
|
/* BEKLEYEN EMİRLER: pozisyon slotunu paylaşır (aşırı birikmeyi önler) */
|
|
14096
14440
|
const pendCount = (ordR && Array.isArray(ordR.orders) ? ordR.orders.length : 0);
|
|
14441
|
+
/* TALİMAT AYARLARI: sayısal kurallar koda çevrildi (risk/lot/martingale/
|
|
14442
|
+
gün başı bakiye/günlük zarar limiti) — state ve giriş kapısı kullanır */
|
|
14443
|
+
const ins = finParsedInstr();
|
|
14444
|
+
/* GÜN BAŞI BAKİYE → günlük K/Z ve limit durumu (hesap anlık değerinden) */
|
|
14445
|
+
let dayPct = null;
|
|
14446
|
+
let dayBlocked = '';
|
|
14447
|
+
try {
|
|
14448
|
+
const sb = Number(ins.entry.startBalance) || 0;
|
|
14449
|
+
const eqNow = Number(account.equity) || 0;
|
|
14450
|
+
const balNow = Number(account.balance) || 0;
|
|
14451
|
+
const refNow = eqNow > 0 ? eqNow : balNow;
|
|
14452
|
+
if (sb > 0 && refNow > 0) {
|
|
14453
|
+
dayPct = Math.round(((refNow - sb) / sb) * 10000) / 100;
|
|
14454
|
+
const lim = Number(ins.entry.dailyLossPct) || 0;
|
|
14455
|
+
if (lim > 0 && dayPct <= -lim) {
|
|
14456
|
+
dayBlocked = `günlük zarar limiti AŞILDI (gün başı ${sb} → ${refNow}, %${dayPct} ≤ -%${lim}) — talimat gereği yeni işlem açılmaz`;
|
|
14457
|
+
}
|
|
14458
|
+
}
|
|
14459
|
+
} catch {}
|
|
14097
14460
|
const market = {};
|
|
14098
14461
|
for (const sym of symbols) {
|
|
14099
14462
|
const tf = finTsPickTf(agent, sym);
|
|
@@ -14134,6 +14497,16 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14134
14497
|
talimat "zorunlu giriş" içeriyorsa kod eşikleri devre dışı bırakır. */
|
|
14135
14498
|
sahip_talimati: finTsStrategyText() || '(yok — temel teknik okuma)',
|
|
14136
14499
|
talimat_modu: finTsMandatoryEntry() ? 'ZORUNLU GİRİŞ — bu turda işlem açmak zorunlu (bekle yok); yönü sen seç' : '',
|
|
14500
|
+
talimat_ayarlari: finInstrSummary(ins) || '(sayısal kural yok)',
|
|
14501
|
+
/* GÜN BAŞI BAKİYE (talimattan): günlük K/Z — Jev zarar durumunu otomatik görür */
|
|
14502
|
+
gun_baslangic_bakiye: ins.entry.startBalance != null ? ins.entry.startBalance : null,
|
|
14503
|
+
gun_net_yuzde: dayPct,
|
|
14504
|
+
gun_durumu: dayBlocked
|
|
14505
|
+
? dayBlocked
|
|
14506
|
+
: ins.entry.startBalance != null && dayPct != null
|
|
14507
|
+
? `Gün başı ${ins.entry.startBalance} → şimdi %${dayPct >= 0 ? '+' : ''}${dayPct} (${dayPct >= 0 ? 'KÂR' : 'ZARAR'})` +
|
|
14508
|
+
(ins.entry.dailyLossPct != null ? ` · limit -%${ins.entry.dailyLossPct}` : '')
|
|
14509
|
+
: '',
|
|
14137
14510
|
};
|
|
14138
14511
|
for (const sym of symbols) {
|
|
14139
14512
|
const d = market[sym];
|
|
@@ -14156,6 +14529,9 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14156
14529
|
}
|
|
14157
14530
|
const questions = {};
|
|
14158
14531
|
const talimatZorunlu = finTsMandatoryEntry();
|
|
14532
|
+
/* Yönetici açıkken açık pozisyon kararları ONA aittir (çift yönetim yok) */
|
|
14533
|
+
const posManagerOn = f.posManagerEnabled !== false;
|
|
14534
|
+
/* TALİMAT AYARLARI: yukarıda (state kurulmadan) ayrıştırıldı */
|
|
14159
14535
|
symbols.forEach((sym, i) => {
|
|
14160
14536
|
questions['yon_' + i] = {
|
|
14161
14537
|
type: 'choice',
|
|
@@ -14211,7 +14587,7 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14211
14587
|
asagi_atr: 'Fiyat -0.5×ATR altında alarm',
|
|
14212
14588
|
},
|
|
14213
14589
|
};
|
|
14214
|
-
if (posBySym[sym]) {
|
|
14590
|
+
if (posBySym[sym] && !posManagerOn) {
|
|
14215
14591
|
const pp = posBySym[sym];
|
|
14216
14592
|
const ps = finTsPosState(pp);
|
|
14217
14593
|
const kzTxt = ps.kz_r == null ? 'R bilinmiyor' : `kâr ${ps.kz_r}R`;
|
|
@@ -14283,6 +14659,11 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14283
14659
|
const cal = finTsCalibration(sym, market[sym].tf);
|
|
14284
14660
|
const thAction = cal.action;
|
|
14285
14661
|
if (pos) {
|
|
14662
|
+
/* POZİSYON YÖNETİCİSİ açık: kapat/kısmi/SL kararları onun turunda */
|
|
14663
|
+
if (posManagerOn) {
|
|
14664
|
+
lines.push(`- ${sym}: pozisyon açık #${pos.ticket} — yönetim Pozisyon Yöneticisi'nde (5 sn Jev turu)`);
|
|
14665
|
+
continue;
|
|
14666
|
+
}
|
|
14286
14667
|
const ex = finTsNoul(ans, 'kapat_' + i);
|
|
14287
14668
|
if (ex != null && ex >= FIN_TS_EXIT_P) {
|
|
14288
14669
|
const r = await financetools.handlers.mt5_close(
|
|
@@ -14313,8 +14694,16 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14313
14694
|
continue;
|
|
14314
14695
|
}
|
|
14315
14696
|
}
|
|
14316
|
-
|
|
14317
|
-
|
|
14697
|
+
/* GÜN BAŞI BAKİYE LİMİTİ (talimat): aşıldıysa YENİ İŞLEM AÇILMAZ */
|
|
14698
|
+
if (dayBlocked) {
|
|
14699
|
+
lines.push(`- ${sym}: ${dayBlocked}`);
|
|
14700
|
+
continue;
|
|
14701
|
+
}
|
|
14702
|
+
/* ÇOKLU GİRİŞ: aynı turda birden çok sembol açılabilir — yalnız toplam
|
|
14703
|
+
slot (pozisyon + bekleyen emir + bu turda açılanlar) tavanı korur */
|
|
14704
|
+
const slotsLeft = (Number(f.maxPositions) || 3) - pendCount - posList.length - opened;
|
|
14705
|
+
if (slotsLeft <= 0) {
|
|
14706
|
+
lines.push(`- ${sym}: sinyal güçlü (${c.choice} p=${c.p.toFixed(2)}) ama pozisyon+bekleyen emir sınırı dolu (${posList.length + pendCount}/${Number(f.maxPositions) || 3}) — bu tur açılmadı`);
|
|
14318
14707
|
continue;
|
|
14319
14708
|
}
|
|
14320
14709
|
/* GİRİŞ TİPİ + RİSK PROFİLİ (JEV kararı) — seviyeleri KOD hesaplar */
|
|
@@ -14357,8 +14746,29 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14357
14746
|
}
|
|
14358
14747
|
const sl = rnd(isBuy ? entry - slDist : entry + slDist);
|
|
14359
14748
|
const tp = rnd(isBuy ? entry + tpDist : entry - tpDist);
|
|
14749
|
+
/* RİSK + LOT: talimatta "risk %2" varsa AYNEN uygulanır; yoksa ayar ×
|
|
14750
|
+
kalibrasyon. Lot çarpanı ve MARTİNGALE (kayıp serisi × kat) burada. */
|
|
14360
14751
|
const riskBase = Number(f.riskPerTradePct) > 0 ? Number(f.riskPerTradePct) : 0.5;
|
|
14361
|
-
|
|
14752
|
+
let riskPct = ins.entry.riskPct != null
|
|
14753
|
+
? ins.entry.riskPct
|
|
14754
|
+
: Math.max(0.1, Math.min(2, Math.round(riskBase * cal.riskMult * 100) / 100));
|
|
14755
|
+
let sizeMult = 1;
|
|
14756
|
+
let martNote = '';
|
|
14757
|
+
if (ins.entry.lotMult != null) sizeMult *= ins.entry.lotMult;
|
|
14758
|
+
if (ins.entry.martingale != null) {
|
|
14759
|
+
let streak = 0;
|
|
14760
|
+
try {
|
|
14761
|
+
const le = finLearnLoad().symbols[sym];
|
|
14762
|
+
streak = Number(le && le.stats && le.stats.streak) || 0;
|
|
14763
|
+
} catch {}
|
|
14764
|
+
const pow = Math.min(3, Math.max(0, Math.round(streak)));
|
|
14765
|
+
if (pow > 0) {
|
|
14766
|
+
sizeMult *= Math.pow(ins.entry.martingale, pow);
|
|
14767
|
+
martNote = ` · martingale ×${ins.entry.martingale}^${pow}`;
|
|
14768
|
+
}
|
|
14769
|
+
}
|
|
14770
|
+
if (sizeMult !== 1) riskPct = Math.max(0.1, Math.min(10, Math.round(riskPct * sizeMult * 100) / 100));
|
|
14771
|
+
const riskNote = ins.entry.riskPct != null ? ' · TALİMAT risk %' + ins.entry.riskPct : '';
|
|
14362
14772
|
const emirLabel =
|
|
14363
14773
|
orderType === 'market' ? 'MARKET' :
|
|
14364
14774
|
isBuy && orderType === 'buy_limit' ? 'BUY LIMIT' :
|
|
@@ -14406,10 +14816,11 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14406
14816
|
orderType,
|
|
14407
14817
|
});
|
|
14408
14818
|
} catch {}
|
|
14819
|
+
const riskTxt = `risk %${riskPct}${ins.entry.riskPct == null && cal.riskMult !== 1 ? ` ×${cal.riskMult}` : ''}${riskNote}${martNote}`;
|
|
14409
14820
|
lines.push(
|
|
14410
14821
|
(orderType === 'market'
|
|
14411
|
-
? `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp},
|
|
14412
|
-
: `- ${sym}: ⏳ ${emirLabel} emri kondu @${entry} (${profKey} profil, SL ${sl}, TP ${tp},
|
|
14822
|
+
? `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, ${riskTxt})`
|
|
14823
|
+
: `- ${sym}: ⏳ ${emirLabel} emri kondu @${entry} (${profKey} profil, SL ${sl}, TP ${tp}, ${riskTxt})`) +
|
|
14413
14824
|
` · p=${c.p.toFixed(2)} teyit=${confVal.toFixed(2)} · tf=${market[sym].tf}` +
|
|
14414
14825
|
(talimatZorunlu ? ' · TALİMAT: zorunlu giriş' : '') +
|
|
14415
14826
|
(thAction !== FIN_TS_DEFAULT_TH.action ? ` · öğrenilmiş eşik p≥${thAction.toFixed(2)}` : '')
|
|
@@ -14422,10 +14833,12 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14422
14833
|
}
|
|
14423
14834
|
/* JEV ARAÇ İSTEĞİ (tur düzeyi): eksik araç varsa TOOL botuna asenkron yazdır */
|
|
14424
14835
|
try { await finTsToolRequest(sidS, agent, ans, lines); } catch {}
|
|
14836
|
+
const insTxt = finInstrSummary(ins);
|
|
14425
14837
|
finTsPost(
|
|
14426
14838
|
sidS,
|
|
14427
14839
|
agent,
|
|
14428
14840
|
`🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
|
|
14841
|
+
(insTxt ? 'TALİMAT AYARLARI (kod uygular): ' + insTxt + '\n' : '') +
|
|
14429
14842
|
'TF KARARI: ' + symbols.map((s) => `${s}=${market[s].tf}`).join(', ') + '\n' +
|
|
14430
14843
|
'ÖĞRENİLMİŞ: ' +
|
|
14431
14844
|
symbols
|
|
@@ -14735,6 +15148,16 @@ ipcMain.handle('finance:snapshot', async () => {
|
|
|
14735
15148
|
equity: (financeState.equity || []).slice(-180),
|
|
14736
15149
|
alerts: (financeState.alerts || []).slice(0, 50),
|
|
14737
15150
|
watch: { on: !!financeState.watchTimer, managed: financeState.watch.size, lastAt: financeState.watchTickAt || 0 },
|
|
15151
|
+
/* POZİSYON YÖNETİCİSİ durumu (panel kartı) */
|
|
15152
|
+
posManager: {
|
|
15153
|
+
enabled: f.posManagerEnabled !== false,
|
|
15154
|
+
busy: !!financeState.posManager.busy,
|
|
15155
|
+
rounds: Number(financeState.posManager.rounds) || 0,
|
|
15156
|
+
lastAt: Number(financeState.posManager.lastAt) || 0,
|
|
15157
|
+
lastErr: String(financeState.posManager.lastErr || ''),
|
|
15158
|
+
positions: Number(financeState.posManager.lastPosCount) || 0,
|
|
15159
|
+
note: String(f.posManagerNote || ''),
|
|
15160
|
+
},
|
|
14738
15161
|
};
|
|
14739
15162
|
});
|
|
14740
15163
|
|
|
@@ -14964,6 +15387,9 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
14964
15387
|
if (p.reentryCooldownMin !== undefined) f.reentryCooldownMin = Math.max(0, Math.min(1440, Math.round(Number(p.reentryCooldownMin) || 0)));
|
|
14965
15388
|
if (p.maxPerCurrency !== undefined) f.maxPerCurrency = Math.max(0, Math.min(20, Math.round(Number(p.maxPerCurrency) || 0)));
|
|
14966
15389
|
if (p.shadowMode !== undefined) f.shadowMode = !!p.shadowMode;
|
|
15390
|
+
/* POZİSYON YÖNETİCİSİ: kendi talimatı + aç/kapa */
|
|
15391
|
+
if (p.posManagerNote !== undefined) f.posManagerNote = String(p.posManagerNote || '').slice(0, 1500);
|
|
15392
|
+
if (p.posManagerEnabled !== undefined) f.posManagerEnabled = p.posManagerEnabled !== false;
|
|
14967
15393
|
if (p.planTime !== undefined) f.planTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.planTime || '').trim()) ? String(p.planTime).trim() : '';
|
|
14968
15394
|
if (p.reviewTime !== undefined) f.reviewTime = /^([01]?\d|2[0-3]):([0-5]\d)$/.test(String(p.reviewTime || '').trim()) ? String(p.reviewTime).trim() : '';
|
|
14969
15395
|
/* TRADE SAATLERİ: {on, start, end} — yerel saat; aralık dışında ajan turları
|
package/src/renderer/index.html
CHANGED
|
@@ -193,8 +193,8 @@
|
|
|
193
193
|
<div id="finRoles" class="fin-roles" title="Trader'ın yanında koşacak uzman ajanları seç — Risk / Teknik / Haber / Görsel; seçilen her rol AYRI sürekli ajan olarak koşar (işlem açmaz)"></div>
|
|
194
194
|
</div>
|
|
195
195
|
<div class="fin-field">
|
|
196
|
-
<span>Ajan talimatı <i class="fin-hint">— 1-2 cümle; trader + tüm paralel ajanlara iletilir</i></span>
|
|
197
|
-
<textarea id="finStrategy" rows="2" maxlength="2000" placeholder="örn.
|
|
196
|
+
<span>Ajan talimatı <i class="fin-hint">— 1-2 cümle; trader + tüm paralel ajanlara iletilir</i><button id="finStrategyHelp" class="fin-help-btn" type="button" title="Neler yazabilirim? Desteklenen talimatlar ve örnekler">?</button></span>
|
|
197
|
+
<textarea id="finStrategy" rows="2" maxlength="2000" placeholder="örn. 1- risk yüzde 2 2- martingale kullan 3- her mumda işlem açmak zorundasın" title="Buraya yazdığın talimat trade ajanına ve koşan tüm paralel ajanlara (ekip + sembol işçileri) sistem promptunda öncelikli strateji notu olarak gider; kaydetmek için alandan çık"></textarea>
|
|
198
198
|
</div>
|
|
199
199
|
<div id="finAdvWrap" hidden>
|
|
200
200
|
<div class="fin-field">
|
|
@@ -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><button id="finPosHelp" class="fin-help-btn" type="button" title="Neler yazabilirim? Desteklenen talimatlar ve örnekler">?</button></span>
|
|
231
|
+
<textarea id="finPosNote" rows="2" maxlength="1500" placeholder="örn. 1- 1R'de %50 kısmi kapat 2- kâr 2R'ye gelince kalanı kapat 3- 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">
|
|
@@ -876,6 +889,16 @@
|
|
|
876
889
|
</div>
|
|
877
890
|
</div>
|
|
878
891
|
|
|
892
|
+
<div id="finHelpOverlay" class="mini-overlay" hidden>
|
|
893
|
+
<div class="mini-dialog fin-help-dialog">
|
|
894
|
+
<div class="mini-head">
|
|
895
|
+
<span class="mini-title" id="finHelpTitle">TALİMAT KILAVUZU</span>
|
|
896
|
+
<button class="mini-close" id="finHelpClose" title="Kapat">×</button>
|
|
897
|
+
</div>
|
|
898
|
+
<div class="mini-body" id="finHelpBody"></div>
|
|
899
|
+
</div>
|
|
900
|
+
</div>
|
|
901
|
+
|
|
879
902
|
<div id="finTeamOverlay" class="mini-overlay" hidden>
|
|
880
903
|
<div class="mini-dialog fin-sym-dialog">
|
|
881
904
|
<div class="mini-head">
|
package/src/renderer/renderer.js
CHANGED
|
@@ -228,6 +228,12 @@ const els = {
|
|
|
228
228
|
finMaxLot: $('#finMaxLot'),
|
|
229
229
|
finSymLimits: $('#finSymLimits'),
|
|
230
230
|
finStrategy: $('#finStrategy'),
|
|
231
|
+
finStrategyHelp: $('#finStrategyHelp'),
|
|
232
|
+
finPosHelp: $('#finPosHelp'),
|
|
233
|
+
finHelpOverlay: $('#finHelpOverlay'),
|
|
234
|
+
finHelpTitle: $('#finHelpTitle'),
|
|
235
|
+
finHelpBody: $('#finHelpBody'),
|
|
236
|
+
finHelpClose: $('#finHelpClose'),
|
|
231
237
|
finMaxTradesDay: $('#finMaxTradesDay'),
|
|
232
238
|
finLossStreak: $('#finLossStreak'),
|
|
233
239
|
finLossStreakPause: $('#finLossStreakPause'),
|
|
@@ -244,6 +250,11 @@ const els = {
|
|
|
244
250
|
finTraderBtn: $('#finTraderBtn'),
|
|
245
251
|
finTraderDot: $('#finTraderDot'),
|
|
246
252
|
finTraderStatus: $('#finTraderStatus'),
|
|
253
|
+
finPosDot: $('#finPosDot'),
|
|
254
|
+
finPosInfo: $('#finPosInfo'),
|
|
255
|
+
finPosOn: $('#finPosOn'),
|
|
256
|
+
finPosNote: $('#finPosNote'),
|
|
257
|
+
finPosStatus: $('#finPosStatus'),
|
|
247
258
|
finPosList: $('#finPosList'),
|
|
248
259
|
finPosCount: $('#finPosCount'),
|
|
249
260
|
finOrdList: $('#finOrdList'),
|
|
@@ -9718,6 +9729,7 @@ let finModelsFilled = false;
|
|
|
9718
9729
|
let finLastPrices = new Map(); /* sembol → son bid (renk için) */
|
|
9719
9730
|
let finCfgCache = null; /* son snapshot cfg — rol→skill modalı bundan okur */
|
|
9720
9731
|
let finStrategyDirty = false; /* textarea'da kaydedilmeyi bekleyen ajan talimatı var */
|
|
9732
|
+
let finPosNoteDirty = false; /* kaydedilmeyi bekleyen pozisyon yöneticisi talimatı */
|
|
9721
9733
|
const FIN_COLOR_UP = 'fs-up';
|
|
9722
9734
|
const FIN_COLOR_DOWN = 'fs-down';
|
|
9723
9735
|
|
|
@@ -9933,6 +9945,8 @@ function finTraderInputsSet(cfg) {
|
|
|
9933
9945
|
if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
|
|
9934
9946
|
finSymLimitsRender(cfg.symbolLimits);
|
|
9935
9947
|
if (els.finStrategy && ae !== els.finStrategy && !finStrategyDirty) els.finStrategy.value = cfg.strategy || '';
|
|
9948
|
+
if (els.finPosNote && ae !== els.finPosNote && !finPosNoteDirty) els.finPosNote.value = cfg.posManagerNote || '';
|
|
9949
|
+
if (els.finPosOn && ae !== els.finPosOn) els.finPosOn.checked = cfg.posManagerEnabled !== false;
|
|
9936
9950
|
if (els.finMaxTradesDay && ae !== els.finMaxTradesDay) els.finMaxTradesDay.value = Number(cfg.maxTradesPerDay) || 0;
|
|
9937
9951
|
if (els.finLossStreak && ae !== els.finLossStreak) els.finLossStreak.value = Number(cfg.lossStreakLimit) || 0;
|
|
9938
9952
|
if (els.finLossStreakPause && ae !== els.finLossStreakPause) els.finLossStreakPause.value = Number(cfg.lossStreakPauseMin) || 0;
|
|
@@ -10094,6 +10108,39 @@ function finRenderTrader(trader, cfg) {
|
|
|
10094
10108
|
}
|
|
10095
10109
|
}
|
|
10096
10110
|
|
|
10111
|
+
/* POZİSYON YÖNETİCİSİ kartı: açık pozisyon varken 5 sn Jev turu durumu */
|
|
10112
|
+
function finRenderPosManager(pm, cfg) {
|
|
10113
|
+
if (!pm) return;
|
|
10114
|
+
const enabled = cfg ? cfg.posManagerEnabled !== false : pm.enabled !== false;
|
|
10115
|
+
const pos = Number(pm.positions) || 0;
|
|
10116
|
+
if (els.finPosDot) {
|
|
10117
|
+
els.finPosDot.classList.remove('on', 'off', 'busy');
|
|
10118
|
+
els.finPosDot.classList.add(!enabled ? 'off' : pm.busy ? 'busy' : pos ? 'on' : 'off');
|
|
10119
|
+
els.finPosDot.title = !enabled
|
|
10120
|
+
? 'Pozisyon yöneticisi kapalı'
|
|
10121
|
+
: pos
|
|
10122
|
+
? 'İzliyor — ' + pos + ' açık pozisyon (5 sn Jev turu)'
|
|
10123
|
+
: 'Açık pozisyon yok — beklemede';
|
|
10124
|
+
}
|
|
10125
|
+
if (els.finPosInfo) els.finPosInfo.textContent = pm.rounds ? '· tur ' + pm.rounds : '';
|
|
10126
|
+
if (els.finPosStatus) {
|
|
10127
|
+
let t;
|
|
10128
|
+
if (!enabled) t = 'Kapalı — kutucuktan aç';
|
|
10129
|
+
else if (pm.busy) t = 'Tur çalışıyor…';
|
|
10130
|
+
else if (!pos) t = 'Beklemede — açık pozisyon yok';
|
|
10131
|
+
else t = 'İzliyor · ' + pos + ' pozisyon';
|
|
10132
|
+
if (pm.lastAt) {
|
|
10133
|
+
const d = new Date(pm.lastAt);
|
|
10134
|
+
const p = (x) => String(x).padStart(2, '0');
|
|
10135
|
+
t += ' · son tur ' + p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds());
|
|
10136
|
+
}
|
|
10137
|
+
if (pm.lastErr) t += ' · hata: ' + String(pm.lastErr).slice(0, 60);
|
|
10138
|
+
els.finPosStatus.textContent = t;
|
|
10139
|
+
els.finPosStatus.classList.toggle('on', enabled && !!pos);
|
|
10140
|
+
els.finPosStatus.classList.toggle('busy', !!pm.busy);
|
|
10141
|
+
}
|
|
10142
|
+
}
|
|
10143
|
+
|
|
10097
10144
|
/* ---- RİSK OTOMASYONU + PERFORMANS panosu ---- */
|
|
10098
10145
|
function finDayKey(ms) {
|
|
10099
10146
|
const d = new Date(Number(ms) || 0);
|
|
@@ -10254,6 +10301,7 @@ async function finSnapshot() {
|
|
|
10254
10301
|
finTraderInputsSet(r.cfg);
|
|
10255
10302
|
finRenderRoles(r.cfg);
|
|
10256
10303
|
finRenderTrader(r.trader, r.cfg);
|
|
10304
|
+
finRenderPosManager(r.posManager, r.cfg);
|
|
10257
10305
|
finRenderAutomation(r.cfg, r.watch);
|
|
10258
10306
|
/* GİZLİ RİSK OTOMASYONU AKTİF OLAMAZ: sunucu hâlâ açık diyorsa kapat */
|
|
10259
10307
|
if (!finView.risk && r.cfg && r.cfg.watchdog !== false) finSaveCfg({ watchdog: false });
|
|
@@ -10392,8 +10440,10 @@ let finWatchDirty = false; /* picker/input değişikliği kaydedilmeyi bekliyor
|
|
|
10392
10440
|
function finSaveCfg(patch) {
|
|
10393
10441
|
if (patch && patch.symbols !== undefined) finWatchDirty = true;
|
|
10394
10442
|
if (patch && patch.strategy !== undefined) finStrategyDirty = true;
|
|
10443
|
+
if (patch && patch.posManagerNote !== undefined) finPosNoteDirty = true;
|
|
10395
10444
|
clearTimeout(finCfgTimer);
|
|
10396
10445
|
const wasStrategy = !!(patch && patch.strategy !== undefined);
|
|
10446
|
+
const wasPosNote = !!(patch && patch.posManagerNote !== undefined);
|
|
10397
10447
|
finCfgTimer = setTimeout(() => {
|
|
10398
10448
|
beast.financeSettings(patch)
|
|
10399
10449
|
.then(() => {
|
|
@@ -10401,6 +10451,10 @@ function finSaveCfg(patch) {
|
|
|
10401
10451
|
finStrategyDirty = false;
|
|
10402
10452
|
if (finCfgCache) finCfgCache.strategy = patch.strategy;
|
|
10403
10453
|
}
|
|
10454
|
+
if (wasPosNote) {
|
|
10455
|
+
finPosNoteDirty = false;
|
|
10456
|
+
if (finCfgCache) finCfgCache.posManagerNote = patch.posManagerNote;
|
|
10457
|
+
}
|
|
10404
10458
|
finWatchDirty = false;
|
|
10405
10459
|
})
|
|
10406
10460
|
.catch(() => { finWatchDirty = false; });
|
|
@@ -10838,6 +10892,113 @@ if (els.finStrategy) {
|
|
|
10838
10892
|
toast(v.trim() ? 'Ajan talimatı kaydedildi — tüm ajanlar sonraki turda uygular' : 'Ajan talimatı temizlendi');
|
|
10839
10893
|
});
|
|
10840
10894
|
}
|
|
10895
|
+
/* TALİMAT KILAVUZU (?): her iki talimat alanı için desteklenen kalıplar.
|
|
10896
|
+
"kod" etiketli kalıplar AYRICALIKLI olarak kod tarafından otomatik uygulanır;
|
|
10897
|
+
serbest cümleler Jev'in tur state'ine girer ve Jev ona göre karar verir. */
|
|
10898
|
+
/* BUTONLAR: ? → kılavuz; dışına tıkla / × → kapat */
|
|
10899
|
+
if (els.finStrategyHelp) {
|
|
10900
|
+
els.finStrategyHelp.addEventListener('click', (e) => {
|
|
10901
|
+
e.preventDefault();
|
|
10902
|
+
finHelpOpen('trade');
|
|
10903
|
+
});
|
|
10904
|
+
}
|
|
10905
|
+
if (els.finPosHelp) {
|
|
10906
|
+
els.finPosHelp.addEventListener('click', (e) => {
|
|
10907
|
+
e.preventDefault();
|
|
10908
|
+
finHelpOpen('posmgr');
|
|
10909
|
+
});
|
|
10910
|
+
}
|
|
10911
|
+
if (els.finHelpClose) els.finHelpClose.addEventListener('click', finHelpClose);
|
|
10912
|
+
if (els.finHelpOverlay) {
|
|
10913
|
+
els.finHelpOverlay.addEventListener('click', (e) => {
|
|
10914
|
+
if (e.target === els.finHelpOverlay) finHelpClose();
|
|
10915
|
+
});
|
|
10916
|
+
}
|
|
10917
|
+
function finHelpHtml(which) {
|
|
10918
|
+
const posmgr = which === 'posmgr';
|
|
10919
|
+
const items = posmgr
|
|
10920
|
+
? [
|
|
10921
|
+
['<code>1R\'de %50 kısmi kapat</code><code>%50 1R</code><code>kısmi close %50</code>',
|
|
10922
|
+
'<b>Kod otomatik uygular.</b> Kâr belirtilen R\'ye gelince pozisyonun %50\'si kapatılır (tek sefer). "kısmi %50" tek başına yazılırsa 1R kabul edilir.'],
|
|
10923
|
+
['<code>1.5R\'de %25 kısmi</code><code>3R\'de %75 kapat</code>',
|
|
10924
|
+
'<b>Kod otomatik uygular.</b> R ve yüzdeyi sen seç; yüzde 5-90 arası olmalı.'],
|
|
10925
|
+
['<code>kâr 2R olunca kalanı kapat</code><code>kârı koru</code>',
|
|
10926
|
+
'<b>Jev uygular.</b> Serbest ifade; her 5 sn turunda Jev okur ve kapatma/trailing kararını verir.'],
|
|
10927
|
+
['<code>SL\'yi takip et</code><code>breakeven\'a çek</code><code>zararı -0.5R\'de kes</code>',
|
|
10928
|
+
'<b>Jev uygular.</b> SL/koruma aksiyonu olarak değerlendirilir; kod seviyeyi ATR/R ile hesaplar, SL asla geriye gitmez.'],
|
|
10929
|
+
['<code>başlangıç bakiye 10.000</code><code>gün başı: 10000</code>',
|
|
10930
|
+
'Gün başı bakiyesi — günlük K/Z % otomatik hesaplanır (raporda "Gün başı → %-2.3 ZARAR").'],
|
|
10931
|
+
]
|
|
10932
|
+
: [
|
|
10933
|
+
['<code>risk %2</code><code>%2 risk</code><code>risk yüzde 2</code>',
|
|
10934
|
+
'<b>Kod otomatik uygular.</b> Girişler %2 risk ile açılır (kalibrasyon çarpanı devre dışı, 0.1-10 arası).'],
|
|
10935
|
+
['<code>lot x1.5</code><code>poz başı lot 1.5</code><code>lotu 2 kat yap</code>',
|
|
10936
|
+
'<b>Kod otomatik uygular.</b> Tüm giriş riskine/lotuna çarpan.'],
|
|
10937
|
+
['<code>martingale</code><code>martingale 1.5</code><code>martingale x2</code>',
|
|
10938
|
+
'<b>Kod otomatik uygular.</b> Kayıp serisinde lot katlanır (varsayılan ×2; seri en çok ×3, toplam risk tavanı %10 ve max lot kilidi geçerli).'],
|
|
10939
|
+
['<code>her mumda işlem açmak zorundasın</code><code>her barda işlem</code><code>her zaman işlem</code>',
|
|
10940
|
+
'<b>ZORUNLU GİRİŞ modu.</b> "bekle" seçeneği kaldırılır, eşik/teyit kapıları atlanır, emir market olur. "zorunlu değil" yazarsan mod açılmaz.'],
|
|
10941
|
+
['<code>M1</code><code>1m</code><code>5 dk</code><code>4 saat</code>',
|
|
10942
|
+
'İşlem zaman dilimi. M1/M5 yazarsan tur ritmi hızlanır (M1 en hızlı 60 sn).'],
|
|
10943
|
+
['<code>başlangıç bakiye 10.000</code><code>gün başı: 10000</code><code>starting balance 10000</code>',
|
|
10944
|
+
'Gün başı bakiyesi — her turda günlük K/Z otomatik hesaplanır ve Jev\'e durum olarak verilir.'],
|
|
10945
|
+
['<code>günlük zarar %3</code><code>max günlük kayıp 3</code>',
|
|
10946
|
+
'<b>Kod otomatik uygular.</b> Limit aşılırsa yeni işlem açılmaz; günlük kayıp otomasyonunun limiti bu olur.'],
|
|
10947
|
+
];
|
|
10948
|
+
const example = posmgr
|
|
10949
|
+
? `1- 1R'de %50 kısmi kapat\n2- kâr 2R'ye gelince kalanı kapat\n3- zarar -0.5R'yi geçerse kes`
|
|
10950
|
+
: `1- risk yüzde 2\n2- martingale kullan (x2)\n3- her mumda işlem açmak zorundasın\n4- başlangıç bakiye 10.000\n5- günlük zarar %3`;
|
|
10951
|
+
const head = posmgr
|
|
10952
|
+
? 'Pozisyon Yöneticisi (5 sn turu) açık pozisyonları yönetir: kapat / kısmi kapat / SL taşı.'
|
|
10953
|
+
: 'Trade Ajanı girişleri yönetir: yön, emir tipi, risk, lot, martingale ve günlük limitler.';
|
|
10954
|
+
const rows = items
|
|
10955
|
+
.map((it) => '<div class="fin-help-item"><div class="fin-help-code">' + it[0] + '</div><div class="fin-help-desc">' + it[1] + '</div></div>')
|
|
10956
|
+
.join('');
|
|
10957
|
+
return (
|
|
10958
|
+
'<div class="fin-help-head">' + head + '</div>' +
|
|
10959
|
+
'<div class="fin-help-sec">Neler yazabilirsin?</div>' +
|
|
10960
|
+
rows +
|
|
10961
|
+
'<div class="fin-help-sec">Nasıl yazılır?</div>' +
|
|
10962
|
+
'<div class="fin-help-text">İstediğin gibi düz cümle yazabilirsin; birden çok kuralı <b>1- 2- 3-</b> diye numaralayarak alt alta yazman en kolayı. Kod, yukarıdaki sayısal kalıpları otomatik uygular; kalan serbest ifadeleri Jev her turda okur ve kararına katar. Talimat değişiklikleri <b>sonraki turda</b> geçerli olur.</div>' +
|
|
10963
|
+
'<pre class="fin-help-ex">' + example + '</pre>'
|
|
10964
|
+
);
|
|
10965
|
+
}
|
|
10966
|
+
|
|
10967
|
+
function finHelpOpen(which) {
|
|
10968
|
+
if (!els.finHelpOverlay) return;
|
|
10969
|
+
const posmgr = which === 'posmgr';
|
|
10970
|
+
if (els.finHelpTitle) els.finHelpTitle.textContent = posmgr ? 'POZİSYON YÖNETİCİSİ — TALİMAT KILAVUZU' : 'TRADE AJANI — TALİMAT KILAVUZU';
|
|
10971
|
+
if (els.finHelpBody) els.finHelpBody.innerHTML = finHelpHtml(which);
|
|
10972
|
+
els.finHelpOverlay.hidden = false;
|
|
10973
|
+
}
|
|
10974
|
+
|
|
10975
|
+
function finHelpClose() {
|
|
10976
|
+
if (els.finHelpOverlay) els.finHelpOverlay.hidden = true;
|
|
10977
|
+
}
|
|
10978
|
+
|
|
10979
|
+
/* POZİSYON YÖNETİCİSİ: 5 sn Jev turu — kendi talimatı + aç/kapa */
|
|
10980
|
+
if (els.finPosNote) {
|
|
10981
|
+
els.finPosNote.addEventListener('input', () => {
|
|
10982
|
+
finSaveCfg({ posManagerNote: els.finPosNote.value });
|
|
10983
|
+
});
|
|
10984
|
+
els.finPosNote.addEventListener('change', () => {
|
|
10985
|
+
const v = els.finPosNote.value;
|
|
10986
|
+
if (finCfgCache && String(finCfgCache.posManagerNote || '') === v) return;
|
|
10987
|
+
finSaveCfg({ posManagerNote: v });
|
|
10988
|
+
toast(v.trim() ? 'Yönetici talimatı kaydedildi — sonraki yönetici turunda uygular' : 'Yönetici talimatı temizlendi');
|
|
10989
|
+
});
|
|
10990
|
+
}
|
|
10991
|
+
if (els.finPosOn) {
|
|
10992
|
+
els.finPosOn.addEventListener('change', () => {
|
|
10993
|
+
finSaveCfg({ posManagerEnabled: els.finPosOn.checked });
|
|
10994
|
+
toast(
|
|
10995
|
+
els.finPosOn.checked
|
|
10996
|
+
? 'Pozisyon yöneticisi AÇIK — açık pozisyonlar 5 sn Jev turuyla yönetilecek'
|
|
10997
|
+
: 'Pozisyon yöneticisi kapalı — pozisyon yönetimi trade ajanına döndü'
|
|
10998
|
+
);
|
|
10999
|
+
finSnapshot();
|
|
11000
|
+
});
|
|
11001
|
+
}
|
|
10841
11002
|
/* TRADE AJANI: modelleri yeniden çek — üstteki ⟳ ile aynı mantık
|
|
10842
11003
|
(models:refresh). Eksik/yeni modeller listeye girer, seçim korunur. */
|
|
10843
11004
|
if (els.finModelRefreshBtn) {
|
package/src/renderer/style.css
CHANGED
|
@@ -5625,6 +5625,84 @@ 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
|
+
|
|
5647
|
+
/* TALİMAT KILAVUZU (? butonu + modal) */
|
|
5648
|
+
.fin-help-btn {
|
|
5649
|
+
width: 16px;
|
|
5650
|
+
height: 16px;
|
|
5651
|
+
margin-left: 6px;
|
|
5652
|
+
border-radius: 50%;
|
|
5653
|
+
border: 1px solid var(--border);
|
|
5654
|
+
background: var(--panel2);
|
|
5655
|
+
color: var(--muted);
|
|
5656
|
+
font-size: 10px;
|
|
5657
|
+
font-weight: 700;
|
|
5658
|
+
line-height: 1;
|
|
5659
|
+
padding: 0;
|
|
5660
|
+
cursor: pointer;
|
|
5661
|
+
vertical-align: 1px;
|
|
5662
|
+
}
|
|
5663
|
+
.fin-help-btn:hover { color: var(--text); border-color: var(--accent); }
|
|
5664
|
+
.fin-help-dialog { max-width: 620px; }
|
|
5665
|
+
#finHelpBody { max-height: 62vh; overflow: auto; }
|
|
5666
|
+
.fin-help-head { font-size: 11px; color: var(--muted); margin: 2px 0 10px; }
|
|
5667
|
+
.fin-help-sec {
|
|
5668
|
+
font-size: 10.5px;
|
|
5669
|
+
font-weight: 700;
|
|
5670
|
+
letter-spacing: 0.04em;
|
|
5671
|
+
text-transform: uppercase;
|
|
5672
|
+
color: var(--muted);
|
|
5673
|
+
margin: 12px 0 6px;
|
|
5674
|
+
}
|
|
5675
|
+
.fin-help-item {
|
|
5676
|
+
border: 1px solid var(--border);
|
|
5677
|
+
border-radius: 7px;
|
|
5678
|
+
padding: 6px 8px;
|
|
5679
|
+
margin-bottom: 6px;
|
|
5680
|
+
background: var(--panel2);
|
|
5681
|
+
}
|
|
5682
|
+
.fin-help-code { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
|
|
5683
|
+
.fin-help-code code {
|
|
5684
|
+
font-size: 10.5px;
|
|
5685
|
+
padding: 1px 5px;
|
|
5686
|
+
border-radius: 4px;
|
|
5687
|
+
background: rgba(127, 127, 127, 0.14);
|
|
5688
|
+
border: 1px solid var(--border);
|
|
5689
|
+
}
|
|
5690
|
+
.fin-help-desc { font-size: 11px; color: var(--muted); line-height: 1.5; }
|
|
5691
|
+
.fin-help-desc b { color: var(--text); }
|
|
5692
|
+
.fin-help-text { font-size: 11px; color: var(--muted); line-height: 1.55; }
|
|
5693
|
+
.fin-help-text b { color: var(--text); }
|
|
5694
|
+
.fin-help-ex {
|
|
5695
|
+
margin: 8px 0 2px;
|
|
5696
|
+
padding: 8px 10px;
|
|
5697
|
+
border-radius: 7px;
|
|
5698
|
+
border: 1px dashed var(--border);
|
|
5699
|
+
background: var(--panel2);
|
|
5700
|
+
font-size: 11px;
|
|
5701
|
+
line-height: 1.6;
|
|
5702
|
+
white-space: pre-wrap;
|
|
5703
|
+
color: var(--text);
|
|
5704
|
+
}
|
|
5705
|
+
|
|
5628
5706
|
#finPosList, #finOrdList, #finSymList { display: flex; flex-direction: column; gap: 5px; }
|
|
5629
5707
|
.fin-empty { font-size: 11px; color: var(--muted); padding: 2px 0 4px; }
|
|
5630
5708
|
|