beast-agent 2.76.3 → 2.76.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/financetools.js +36 -2
- package/src/main.js +104 -28
- package/src/renderer/renderer.js +5 -1
package/package.json
CHANGED
|
@@ -126,6 +126,20 @@ function lotRange(cfg, symbol) {
|
|
|
126
126
|
return { minLot, maxLot };
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/* LOT ÇARPANI (martingale/kademe): risk lotu hesaplandıktan SONRA hacim
|
|
130
|
+
çarpılır ve broker adımına normalize edilir; maxLot ile kırpılır.
|
|
131
|
+
1. işlem 0.01 lot ise martingale kademesi 0.012'ye çıkar (adıma yuvarlanır)
|
|
132
|
+
— kâra geçince çarpan kalkar, taban lota dönülür. */
|
|
133
|
+
function scaleVolume(info, vol, mult, minLot, maxLot) {
|
|
134
|
+
const m = Number(mult);
|
|
135
|
+
if (!(m > 0) || m === 1) return vol;
|
|
136
|
+
const lo = Number(minLot) > 0 ? Number(minLot) : 0.01;
|
|
137
|
+
const hi = Number(maxLot) > 0 ? Number(maxLot) : vol * m;
|
|
138
|
+
const want = Math.max(lo, Math.min(hi, vol * m));
|
|
139
|
+
const n = finrisk.normalizeVolume(info, want, maxLot, minLot);
|
|
140
|
+
return !n.error && Number(n.volume) > 0 ? Number(n.volume) : vol;
|
|
141
|
+
}
|
|
142
|
+
|
|
129
143
|
/* İşlem öncesi risk katmanı: stops_level + yoğunluk + marj kalkanı.
|
|
130
144
|
Hata metni ya da null döner. positions çağıran tarafından verilir. */
|
|
131
145
|
async function preTradeCheck(cfg, side, symbol, info, price, volume, sl, tp, positions) {
|
|
@@ -792,6 +806,8 @@ const handlers = {
|
|
|
792
806
|
mesafesinden hesaplanır; aksi halde volume broker kuralına normalize edilir */
|
|
793
807
|
const explicitRisk = Number(args.riskPct) > 0 ? Number(args.riskPct) : 0;
|
|
794
808
|
const wantVolume = Number(args.volume);
|
|
809
|
+
/* MARTİNGALE/KADEME ÇARPANI: risk lotu hesaplandıktan sonra hacme uygulanır */
|
|
810
|
+
const sizeMult = Number(args.sizeMult) > 0 ? Math.min(20, Number(args.sizeMult)) : 1;
|
|
795
811
|
let vol = 0;
|
|
796
812
|
let riskInfo = null;
|
|
797
813
|
if (explicitRisk > 0 || (!(wantVolume > 0) && Number(cfg.riskPerTradePct) > 0)) {
|
|
@@ -803,7 +819,15 @@ const handlers = {
|
|
|
803
819
|
const rr = finrisk.calcRiskLot(info, price, sl, riskAmount, range.maxLot, range.minLot);
|
|
804
820
|
if (rr.error) return { ok: false, error: rr.error };
|
|
805
821
|
vol = rr.volume;
|
|
806
|
-
|
|
822
|
+
if (sizeMult !== 1) vol = scaleVolume(info, vol, sizeMult, range.minLot, range.maxLot);
|
|
823
|
+
const perLot = Number(rr.lossPerLot) || 0;
|
|
824
|
+
riskInfo = {
|
|
825
|
+
riskPct,
|
|
826
|
+
riskAmount: Math.round((perLot > 0 ? perLot * vol : riskAmount) * 100) / 100,
|
|
827
|
+
lossPerLot: rr.lossPerLot,
|
|
828
|
+
raised: !!rr.raised,
|
|
829
|
+
sizeMult,
|
|
830
|
+
};
|
|
807
831
|
} else {
|
|
808
832
|
const norm = finrisk.normalizeVolume(info, wantVolume, range.maxLot, range.minLot);
|
|
809
833
|
if (norm.error) return { ok: false, error: norm.error };
|
|
@@ -939,6 +963,8 @@ const handlers = {
|
|
|
939
963
|
/* LOT: riskPct verilirse BEKLEYEN EMİR fiyatı + SL mesafesinden hesaplanır
|
|
940
964
|
(piyasa emriyle aynı risk hattı); volume verilmişse normalize edilir. */
|
|
941
965
|
const explicitRisk = Number(args.riskPct) > 0 ? Number(args.riskPct) : 0;
|
|
966
|
+
/* MARTİNGALE/KADEME ÇARPANI: risk lotu hesaplandıktan sonra hacme uygulanır */
|
|
967
|
+
const sizeMult = Number(args.sizeMult) > 0 ? Math.min(20, Number(args.sizeMult)) : 1;
|
|
942
968
|
let vol = 0;
|
|
943
969
|
let riskInfo = null;
|
|
944
970
|
if (explicitRisk > 0 || (!(Number(args.volume) > 0) && Number(cfg.riskPerTradePct) > 0)) {
|
|
@@ -951,7 +977,15 @@ const handlers = {
|
|
|
951
977
|
const rr = finrisk.calcRiskLot(info, entryPrice, slRef, riskAmount, range.maxLot, range.minLot);
|
|
952
978
|
if (rr.error) return { ok: false, error: rr.error };
|
|
953
979
|
vol = rr.volume;
|
|
954
|
-
|
|
980
|
+
if (sizeMult !== 1) vol = scaleVolume(info, vol, sizeMult, range.minLot, range.maxLot);
|
|
981
|
+
const perLot = Number(rr.lossPerLot) || 0;
|
|
982
|
+
riskInfo = {
|
|
983
|
+
riskPct,
|
|
984
|
+
riskAmount: Math.round((perLot > 0 ? perLot * vol : riskAmount) * 100) / 100,
|
|
985
|
+
lossPerLot: rr.lossPerLot,
|
|
986
|
+
raised: !!rr.raised,
|
|
987
|
+
sizeMult,
|
|
988
|
+
};
|
|
955
989
|
} else {
|
|
956
990
|
const norm = finrisk.normalizeVolume(info, args.volume, range.maxLot, range.minLot);
|
|
957
991
|
if (norm.error) return { ok: false, error: norm.error };
|
package/src/main.js
CHANGED
|
@@ -10006,7 +10006,16 @@ function finCfg() {
|
|
|
10006
10006
|
finNormalizeSymbolLimits(f);
|
|
10007
10007
|
if (f.maxPositions == null) f.maxPositions = 3;
|
|
10008
10008
|
/* KODLA DİSİPLİN (trader kuralları): hepsi 0 = kural kapalı */
|
|
10009
|
-
|
|
10009
|
+
/* GÜNLÜK İŞLEM LİMİTİ KALDIRILDI (varsayılan KAPALI/0 = sınırsız): eski
|
|
10010
|
+
sürümün zorla koyduğu 10 değeri BİR KEZ sıfırlanır; kullanıcı bilerek
|
|
10011
|
+
başka değer girdiyse dokunulmaz. Talimatta "günde en fazla N işlem"
|
|
10012
|
+
yazılıysa not yine geçerlidir (bilinçli opt-in). */
|
|
10013
|
+
if (!Number.isFinite(Number(f.maxTradesPerDay))) f.maxTradesPerDay = 0;
|
|
10014
|
+
if (f.maxTradesPerDayDefaultCleared !== true) {
|
|
10015
|
+
f.maxTradesPerDayDefaultCleared = true;
|
|
10016
|
+
if (Number(f.maxTradesPerDay) === 10) f.maxTradesPerDay = 0;
|
|
10017
|
+
try { saveSettings(); } catch {}
|
|
10018
|
+
}
|
|
10010
10019
|
if (!Number.isFinite(Number(f.lossStreakLimit))) f.lossStreakLimit = 2;
|
|
10011
10020
|
if (!Number.isFinite(Number(f.lossStreakPauseMin))) f.lossStreakPauseMin = 30;
|
|
10012
10021
|
/* RE-ENTRY BEKLEME: kural VARSAYILAN KAPALI (0) — isteyen Gelişmiş
|
|
@@ -11333,19 +11342,28 @@ async function finRecordClose(ticket, st) {
|
|
|
11333
11342
|
let net = null;
|
|
11334
11343
|
let symbol = (st && st.symbol) || '';
|
|
11335
11344
|
let closeReason = null; /* kapanışı yapan son deal'in nedeni (4=SL, 5=TP, 6=stop-out) */
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
if (
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
+
/* DEAL GECİKMESİ: kapanıştan hemen sonra deal listesi boş dönebilir — kısa
|
|
11346
|
+
gecikmeyle 1 kez daha dene. K/Z kaydı kaçarsa martingale kayıp serisi,
|
|
11347
|
+
öğrenme istatistiği ve kalibrasyon boş kalır (martingale çalışmaz). */
|
|
11348
|
+
for (let attempt = 0; attempt < 2 && net == null; attempt++) {
|
|
11349
|
+
if (attempt > 0) await new Promise((res) => setTimeout(res, 2000));
|
|
11350
|
+
try {
|
|
11351
|
+
const r = await mt5bridge.call('deals', { days: 3 }, 12000);
|
|
11352
|
+
const deals = (r && r.ok && r.data && r.data.deals) || [];
|
|
11353
|
+
const mine = deals.filter((d) => String(d.position_id) === String(ticket));
|
|
11354
|
+
if (mine.length) {
|
|
11355
|
+
net = 0;
|
|
11356
|
+
for (const d of mine) {
|
|
11357
|
+
net += (Number(d.profit) || 0) + (Number(d.swap) || 0) + (Number(d.commission) || 0);
|
|
11358
|
+
if (Number(d.entry) === 1) closeReason = Number(d.reason);
|
|
11359
|
+
}
|
|
11360
|
+
symbol = String(mine[0].symbol || symbol);
|
|
11345
11361
|
}
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11362
|
+
} catch {}
|
|
11363
|
+
}
|
|
11364
|
+
if (net == null) {
|
|
11365
|
+
try { financeLog(`[watchdog] kapanış K/Z okunamadı (ticket ${ticket}, ${symbol || '?'}) — deal kaydı yok`); } catch {}
|
|
11366
|
+
}
|
|
11349
11367
|
const rounded = net == null ? null : Math.round(net * 100) / 100;
|
|
11350
11368
|
/* MAE/MFE: pozisyonun gördüğü en iyi/en kötü seviyeler kapanışa iliştirilir */
|
|
11351
11369
|
const ex = financeState.excursions.get(String(ticket)) || null;
|
|
@@ -13868,7 +13886,7 @@ function finInstrMoney(v) {
|
|
|
13868
13886
|
|
|
13869
13887
|
function finParseInstructions(text) {
|
|
13870
13888
|
const low = String(text || '').toLowerCase();
|
|
13871
|
-
const out = { riskPct: null, lotMult: null, martingale: null, partial: null, startBalance: null, dailyLossPct: null };
|
|
13889
|
+
const out = { riskPct: null, lotMult: null, martingale: null, partial: null, startBalance: null, dailyLossPct: null, noLossClose: false };
|
|
13872
13890
|
if (!low.trim()) return out;
|
|
13873
13891
|
/* "risk %2", "%2 risk", "risk yüzde 2", "riski 2 yap" → 2 (ilk sayı) */
|
|
13874
13892
|
let m = low.match(
|
|
@@ -13898,7 +13916,9 @@ function finParseInstructions(text) {
|
|
|
13898
13916
|
sayılar faktör sayılmaz (varsayılan ×2 kalır). Geçerli aralık 1.01-5. */
|
|
13899
13917
|
const pats = [
|
|
13900
13918
|
/mart[ıi]ngale\s*(?:x|×|çarpan[ıi]?|carpan[ıi]?|oran[ıi]?|fakt[öo]r[üu]?|kat[ıi]?)?\s*(\d+(?:[.,]\d+)?)(?!\s*r\b)/,
|
|
13901
|
-
|
|
13919
|
+
/* "1.2x martingale" — çarpan kelimesi (x/kat) ZORUNLU: yoksa "risk %2
|
|
13920
|
+
martingale" cümlesindeki 2 yanlışlıkla faktör sanılırdı */
|
|
13921
|
+
/(\d+(?:[.,]\d+)?)\s*(?:x|×|kat[ıi]?)\s*mart[ıi]ngale/,
|
|
13902
13922
|
/mart[ıi]ngale[^0-9%\n]{0,24}?(\d+(?:[.,]\d+)?)\s*(?:x|×|kat[ıi]?)/,
|
|
13903
13923
|
/mart[ıi]ngale[^0-9%\n]{0,24}?(\d+[.,]\d+)(?!\s*r\b)/,
|
|
13904
13924
|
];
|
|
@@ -13924,6 +13944,16 @@ function finParseInstructions(text) {
|
|
|
13924
13944
|
m = low.match(/(?:k[ıi]smi|partial)[^%\d]{0,24}%\s*(\d+(?:[.,]\d+)?)/);
|
|
13925
13945
|
if (m) out.partial = { atR: 1, pct: finInstrNum(m[1]) };
|
|
13926
13946
|
}
|
|
13947
|
+
if (!out.partial) {
|
|
13948
|
+
/* "0.5R'de yarısını kapat", "0.5R karda partial close ile yarısını kapat" */
|
|
13949
|
+
m = low.match(/(\d+(?:[.,]\d+)?)\s*r[^.\n]{0,40}?(?:yar[ıi]s[ıi](?:n[ıi])?|half)/);
|
|
13950
|
+
if (m) out.partial = { atR: finInstrNum(m[1]), pct: 50 };
|
|
13951
|
+
}
|
|
13952
|
+
if (!out.partial) {
|
|
13953
|
+
/* R yazılmamış: "partial close ile yarısını kapat" → 1R varsayılan */
|
|
13954
|
+
m = low.match(/(?:k[ıi]smi|partial|yar[ıi]s[ıi](?:n[ıi])?)[^.\n]{0,30}?(?:yar[ıi]s[ıi](?:n[ıi])?|%\s*50)/);
|
|
13955
|
+
if (m) out.partial = { atR: 1, pct: 50 };
|
|
13956
|
+
}
|
|
13927
13957
|
if (out.partial) {
|
|
13928
13958
|
const p = out.partial;
|
|
13929
13959
|
if (!(p.atR > 0)) p.atR = 1;
|
|
@@ -13931,6 +13961,18 @@ function finParseInstructions(text) {
|
|
|
13931
13961
|
if (!(p.pct >= 5 && p.pct <= 90)) out.partial = null;
|
|
13932
13962
|
else p.pct = Math.round(p.pct);
|
|
13933
13963
|
}
|
|
13964
|
+
/* ZARARDA KAPATMA YASAĞI (KOD uygular): "zararda işlem kapatma",
|
|
13965
|
+
"zararına kapatma", "sadece karda kapat", "kârda değilse kapatma" →
|
|
13966
|
+
kapatma kararı zarardayken REDDEDİLİR (SL/TP hariç — stop çalışır). */
|
|
13967
|
+
if (
|
|
13968
|
+
/(?:zarar(?:da|ına|dayken|day[ıi]m)?|eksi(?:de|ye))[^.\n]{0,24}?kapatma/.test(low) ||
|
|
13969
|
+
/kapatma[^.\n]{0,24}?(?:zarar|eksi)/.test(low) ||
|
|
13970
|
+
/(?:sadece|yaln[ıi]z)[^.\n]{0,24}?k[âa]r(?:da|daysa|dayken)?\b/.test(low) ||
|
|
13971
|
+
/k[âa]rda de[ğg]il(?:se)?[^.\n]{0,20}?kapatma/.test(low) ||
|
|
13972
|
+
/k[âa]r olmadan[^.\n]{0,16}?kapatma/.test(low)
|
|
13973
|
+
) {
|
|
13974
|
+
out.noLossClose = true;
|
|
13975
|
+
}
|
|
13934
13976
|
return out;
|
|
13935
13977
|
}
|
|
13936
13978
|
|
|
@@ -13947,7 +13989,11 @@ function finParsedInstr() {
|
|
|
13947
13989
|
startBalance: trade.startBalance != null ? trade.startBalance : mgr.startBalance,
|
|
13948
13990
|
dailyLossPct: trade.dailyLossPct != null ? trade.dailyLossPct : mgr.dailyLossPct,
|
|
13949
13991
|
},
|
|
13950
|
-
manage: {
|
|
13992
|
+
manage: {
|
|
13993
|
+
partial: mgr.partial || trade.partial,
|
|
13994
|
+
/* YÖNETİM KURALI: zararda kapatma yasağı iki nottan birinde varsa geçerli */
|
|
13995
|
+
noLossClose: !!(mgr.noLossClose || trade.noLossClose),
|
|
13996
|
+
},
|
|
13951
13997
|
};
|
|
13952
13998
|
}
|
|
13953
13999
|
|
|
@@ -13960,7 +14006,8 @@ function finInstrSummary(ins) {
|
|
|
13960
14006
|
if (ins.entry.martingale != null) p.push(`martingale ×${ins.entry.martingale}`);
|
|
13961
14007
|
if (ins.entry.startBalance != null) p.push(`gün başı ${ins.entry.startBalance}`);
|
|
13962
14008
|
if (ins.entry.dailyLossPct != null) p.push(`günlük zarar limiti %${ins.entry.dailyLossPct}`);
|
|
13963
|
-
if (ins.manage.partial) p.push(`kısmi %${ins.manage.partial.pct} @${ins.manage.partial.atR}R`);
|
|
14009
|
+
if (ins.manage.partial) p.push(`kısmi %${ins.manage.partial.pct} @${ins.manage.partial.atR}R (yalnız kârda)`);
|
|
14010
|
+
if (ins.manage.noLossClose) p.push('zararda kapatma YOK');
|
|
13964
14011
|
try {
|
|
13965
14012
|
const f = finCfg();
|
|
13966
14013
|
if (Number(f.maxPositionsNote) > 0) p.push(`maks ${f.maxPositionsNote} pozisyon`);
|
|
@@ -14151,8 +14198,10 @@ async function finTsManageOpen(sidS, agent, sym, pos, mkt, ans, qi, lines) {
|
|
|
14151
14198
|
const pct = par && par.p >= FIN_TS_CONFIRM_P && par.p - par.second >= FIN_TS_MARGIN_P
|
|
14152
14199
|
? ({ yuzde25: 25, yuzde50: 50, yuzde75: 75 }[String(par.choice)] || 0)
|
|
14153
14200
|
: 0;
|
|
14154
|
-
if (pct > 0 && !partialDone) {
|
|
14155
|
-
|
|
14201
|
+
if (pct > 0 && profitDist > 0 && !partialDone) {
|
|
14202
|
+
/* YALNIZ KÂRDA: sahip kuralı "kısmi kapatma sadece kardaysa" — zararda
|
|
14203
|
+
kısmi kapatma (partial_sl) UYGULANMAZ; zarar kesme kararı SL'ye aittir. */
|
|
14204
|
+
const kind = 'partial_tp';
|
|
14156
14205
|
let r = null;
|
|
14157
14206
|
try {
|
|
14158
14207
|
r = await financetools.handlers.mt5_close(
|
|
@@ -14160,7 +14209,7 @@ async function finTsManageOpen(sidS, agent, sym, pos, mkt, ans, qi, lines) {
|
|
|
14160
14209
|
ticket: Number(pos.ticket),
|
|
14161
14210
|
percent: pct,
|
|
14162
14211
|
kind,
|
|
14163
|
-
reason: `TypeSafe: kısmi %${pct} p=${par.p.toFixed(2)} (
|
|
14212
|
+
reason: `TypeSafe: kısmi %${pct} p=${par.p.toFixed(2)} (kâr al)`,
|
|
14164
14213
|
},
|
|
14165
14214
|
{ sessionId: sidS }
|
|
14166
14215
|
);
|
|
@@ -14171,12 +14220,14 @@ async function finTsManageOpen(sidS, agent, sym, pos, mkt, ans, qi, lines) {
|
|
|
14171
14220
|
if (st) { st.partial = true; finWatchSave(); }
|
|
14172
14221
|
if (agent) { agent.tsPartials = agent.tsPartials || new Set(); agent.tsPartials.add(String(pos.ticket)); }
|
|
14173
14222
|
lines.push(
|
|
14174
|
-
`- ${sym} #${pos.ticket}: KISMİ %${pct}
|
|
14223
|
+
`- ${sym} #${pos.ticket}: KISMİ %${pct} TP ✓` +
|
|
14175
14224
|
` (p=${par.p.toFixed(2)}${r.remaining != null ? ', kalan ' + r.remaining + ' lot' : ''})`
|
|
14176
14225
|
);
|
|
14177
14226
|
} else {
|
|
14178
14227
|
lines.push(`- ${sym} #${pos.ticket}: kısmi %${pct} reddedildi — ${(r && r.error) || 'hata'}`);
|
|
14179
14228
|
}
|
|
14229
|
+
} else if (pct > 0 && profitDist <= 0) {
|
|
14230
|
+
lines.push(`- ${sym} #${pos.ticket}: kısmi atlandı — kârda değil (talimat: sadece kârda)`);
|
|
14180
14231
|
} else if (pct > 0 && partialDone) {
|
|
14181
14232
|
lines.push(`- ${sym} #${pos.ticket}: kısmi atlandı — daha önce alındı`);
|
|
14182
14233
|
}
|
|
@@ -14459,6 +14510,12 @@ async function finPosManagerRound(positions, account) {
|
|
|
14459
14510
|
piyasa: {},
|
|
14460
14511
|
alarmlar: (financeState.alerts || []).slice(0, 8).map((a) => ({ sembol: a.symbol, yon: a.direction, fiyat: a.price, mod: a.once ? 'tek' : 'tekrarlı' })),
|
|
14461
14512
|
sahip_talimati: String(f.posManagerNote || '').slice(0, 1500) || '(yok — genel disiplin: kârı koru, zararı sınırla, SL/TP aktif yönet)',
|
|
14513
|
+
/* TRADE TALİMATI da yöneticiye görünür: iki kartın kuralları çelişmesin */
|
|
14514
|
+
trader_talimati: finTsStrategyText() ? String(finTsStrategyText()).slice(0, 1000) : '',
|
|
14515
|
+
/* KOD KURALI ÖZETİ: Jev bunları ihlal eden seçim yapmasın */
|
|
14516
|
+
kapatma_kurali: ins.manage.noLossClose
|
|
14517
|
+
? 'ZARARDA KAPATMA YOK (kod uygular): zarardaki pozisyonun kapatma kararı reddedilir — SL/TP kapatması serbest; kısmi kapatma YALNIZ kârda'
|
|
14518
|
+
: 'zararda kapatma serbest',
|
|
14462
14519
|
talimat_ayarlari: instrTxt || '(sayısal kural yok)',
|
|
14463
14520
|
gun_durumu: (() => {
|
|
14464
14521
|
try {
|
|
@@ -14526,7 +14583,16 @@ async function finPosManagerRound(positions, account) {
|
|
|
14526
14583
|
const pp = positions[i];
|
|
14527
14584
|
const sym = String(pp.symbol || '').toUpperCase();
|
|
14528
14585
|
const ex = finTsNoul(ans, 'kapat_' + i);
|
|
14529
|
-
|
|
14586
|
+
const psNow = finTsPosState(pp);
|
|
14587
|
+
/* ZARARDA KAPATMA YASAĞI (talimat — KOD uygular): zarardaki pozisyonun
|
|
14588
|
+
kapatma kararı REDDEDİLİR. SL/TP kapatması bundan etkilenmez. */
|
|
14589
|
+
const inLoss = Number(pp.profit) < 0 || (psNow.kz_r != null && psNow.kz_r < 0);
|
|
14590
|
+
if (ex != null && ex >= FIN_TS_EXIT_P && ins.manage.noLossClose && inLoss) {
|
|
14591
|
+
lines.push(
|
|
14592
|
+
`- ${sym} #${pp.ticket}: KAPATILMADI — talimat "zararda kapatma" ` +
|
|
14593
|
+
`(K/Z ${Math.round((Number(pp.profit) || 0) * 100) / 100}${psNow.kz_r != null ? ' · ' + psNow.kz_r + 'R' : ''})`
|
|
14594
|
+
);
|
|
14595
|
+
} else if (ex != null && ex >= FIN_TS_EXIT_P) {
|
|
14530
14596
|
let r = null;
|
|
14531
14597
|
try {
|
|
14532
14598
|
r = await financetools.handlers.mt5_close(
|
|
@@ -14538,9 +14604,9 @@ async function finPosManagerRound(positions, account) {
|
|
|
14538
14604
|
}
|
|
14539
14605
|
lines.push(`- ${sym} #${pp.ticket}: KAPAT (p=${ex.toFixed(2)}) ${r && r.ok ? '✓ kapatıldı' : '✗ ' + ((r && r.error) || 'hata')}`);
|
|
14540
14606
|
continue;
|
|
14607
|
+
} else {
|
|
14608
|
+
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'}`);
|
|
14541
14609
|
}
|
|
14542
|
-
const psNow = finTsPosState(pp);
|
|
14543
|
-
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'}`);
|
|
14544
14610
|
/* TALİMAT KISMİ KURALI (kod): "+atR'de %pct kapat" → otomatik uygula */
|
|
14545
14611
|
const rule = ins.manage.partial;
|
|
14546
14612
|
if (rule) {
|
|
@@ -14889,7 +14955,13 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14889
14955
|
lines.push(`- ${sym}: pozisyon açık #${pos.ticket} — yönetim Pozisyon Yöneticisi'nde (${finPosMgrSec()} sn Jev turu)`);
|
|
14890
14956
|
} else {
|
|
14891
14957
|
const ex = finTsNoul(ans, 'kapat_' + i);
|
|
14892
|
-
|
|
14958
|
+
const psT = finTsPosState(pos);
|
|
14959
|
+
const inLossT = Number(pos.profit) < 0 || (psT.kz_r != null && psT.kz_r < 0);
|
|
14960
|
+
if (ex != null && ex >= FIN_TS_EXIT_P && ins.manage.noLossClose && inLossT) {
|
|
14961
|
+
lines.push(`- ${sym}: kapat REDDEDİLDİ — talimat "zararda kapatma" (K/Z ${Math.round((Number(pos.profit) || 0) * 100) / 100})`);
|
|
14962
|
+
/* yönetim kararları (kısmi/SL) yine değerlendirilir */
|
|
14963
|
+
try { await finTsManageOpen(sidS, agent, sym, pos, market[sym], ans, i, lines); } catch {}
|
|
14964
|
+
} else if (ex != null && ex >= FIN_TS_EXIT_P) {
|
|
14893
14965
|
const r = await financetools.handlers.mt5_close(
|
|
14894
14966
|
{ ticket: Number(pos.ticket), reason: `TypeSafe: kapat p=${ex.toFixed(2)}` },
|
|
14895
14967
|
{ sessionId: sidS }
|
|
@@ -14999,7 +15071,10 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
14999
15071
|
martNote = ` · martingale ×${ins.entry.martingale}^${pow} (${pow}. ardışık kayıp sonrası)`;
|
|
15000
15072
|
}
|
|
15001
15073
|
}
|
|
15002
|
-
|
|
15074
|
+
/* MARTİNGALE/LOT ÇARPANI: riskPct TABAN kalır; çarpan LOTA uygulanır
|
|
15075
|
+
(araç: risk lotu × sizeMult, broker adımı + maxLot kelepçesi).
|
|
15076
|
+
Gösterim ve karar kaydı için efektif risk yüzdesi hesaplanır. */
|
|
15077
|
+
const effRiskPct = Math.max(0.1, Math.min(30, Math.round(riskPct * sizeMult * 100) / 100));
|
|
15003
15078
|
const riskNote = ins.entry.riskPct != null ? ' · TALİMAT risk %' + ins.entry.riskPct : '';
|
|
15004
15079
|
const emirLabel =
|
|
15005
15080
|
orderType === 'market' ? 'MARKET' :
|
|
@@ -15015,6 +15090,7 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
15015
15090
|
sl,
|
|
15016
15091
|
tp,
|
|
15017
15092
|
riskPct,
|
|
15093
|
+
sizeMult,
|
|
15018
15094
|
timeframe: market[sym].tf,
|
|
15019
15095
|
comment: 'TS',
|
|
15020
15096
|
reason:
|
|
@@ -15051,11 +15127,11 @@ async function finTypeSafeRound(sid, agent) {
|
|
|
15051
15127
|
emaAlign: ind.ema50 != null && ind.ema200 != null ? (ind.ema50 > ind.ema200) === (c.choice === 'buy') : null,
|
|
15052
15128
|
rsi: ind.rsi14,
|
|
15053
15129
|
atrPct: atr > 0 && entryRef > 0 ? atr / entryRef : null,
|
|
15054
|
-
riskPct,
|
|
15130
|
+
riskPct: effRiskPct,
|
|
15055
15131
|
orderType,
|
|
15056
15132
|
});
|
|
15057
15133
|
} catch {}
|
|
15058
|
-
const riskTxt = `risk %${
|
|
15134
|
+
const riskTxt = `risk %${effRiskPct}${ins.entry.riskPct == null && cal.riskMult !== 1 ? ` ×${cal.riskMult}` : ''}${riskNote}${martNote}`;
|
|
15059
15135
|
lines.push(
|
|
15060
15136
|
(orderType === 'market'
|
|
15061
15137
|
? `- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, ${riskTxt})`
|
package/src/renderer/renderer.js
CHANGED
|
@@ -10949,9 +10949,13 @@ function finHelpHtml(which) {
|
|
|
10949
10949
|
['<code>günlük zarar %3</code><code>max günlük kayıp 3</code>',
|
|
10950
10950
|
'<b>Kod otomatik uygular.</b> Limit aşılırsa yeni işlem açılmaz; günlük kayıp otomasyonunun limiti bu olur.'],
|
|
10951
10951
|
['<code>aynı anda en fazla 10 işlem</code><code>en fazla 10 pozisyon</code><code>günde en fazla 10 işlem</code>',
|
|
10952
|
-
'<b>Kod otomatik uygular.</b>
|
|
10952
|
+
'<b>Kod otomatik uygular.</b> "aynı anda/en fazla pozisyon" = eşzamanlı tavan (pozisyon + bekleyen emir). <b>Günlük işlem limiti varsayılan olarak KAPALI (sınırsız)</b>; yalnız "günde en fazla N işlem" yazarsan o gün için devreye girer. Ayarlar\'daki değer 0 = sınırsız.'],
|
|
10953
10953
|
['<code>sembol başına 5 işlem</code><code>aynı sembolde en fazla 3 pozisyon</code>',
|
|
10954
10954
|
'<b>Kod otomatik uygular.</b> Aynı sembolde çoklu işlem tavanı (toplam tavanı aşamaz). Toplam tavan yazarsan ("aynı anda en fazla 10 işlem") aynı sembolde de o tavana kadar işlem açılabilir; yazmazsan Ayarlar → maks/pay değeri geçerlidir.'],
|
|
10955
|
+
['<code>zararda işlem kapatma</code><code>sadece karda kapat</code><code>kârda değilse kapatma</code>',
|
|
10956
|
+
'<b>Kod otomatik uygular.</b> Zarardaki pozisyonun kapatma kararı REDDEDİLİR — SL/TP çalışmaya devam eder (stop = martingale serisinin parçası). Kârdaki/başabaştaki pozisyon normal kapatılır.'],
|
|
10957
|
+
['<code>0.5R karda yarısını kapat</code><code>partial close ile yarısını kapat</code>',
|
|
10958
|
+
'<b>Kod otomatik uygular.</b> Kâr +0.5R\'ye ulaşınca pozisyonun %50\'si kapatılır — <b>yalnız kârdayken</b> uygulanır (zararda kısmi stop yapılmaz).'],
|
|
10955
10959
|
];
|
|
10956
10960
|
const example = posmgr
|
|
10957
10961
|
? `1- 1R'de %50 kısmi kapat\n2- kâr 2R'ye gelince kalanı kapat\n3- zarar -0.5R'yi geçerse kes`
|