beast-agent 2.32.0 → 2.33.1
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/engine.js +1 -1
- package/src/agent/financetools.js +316 -45
- package/src/agent/finindicators.js +254 -0
- package/src/agent/finrisk.js +132 -0
- package/src/agent/finstats.js +229 -0
- package/src/agent/finwatch.js +110 -0
- package/src/agent/scripts/mt5_bridge.py +65 -0
- package/src/main.js +678 -8
- package/src/preload.js +1 -0
- package/src/renderer/index.html +80 -1
- package/src/renderer/renderer.js +280 -1
- package/src/renderer/style.css +52 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast Finance — risk otomasyonu KARAR katmanı (saf; köprü/IO yok).
|
|
4
|
+
Açık pozisyon + sembol meta verisi + pozisyon durumu (st) alır; uygulanacak
|
|
5
|
+
aksiyonları döndürür:
|
|
6
|
+
{kind:'modify', sl} → SL'yi taşı (BE / trailing)
|
|
7
|
+
{kind:'partial', volume} → kısmi TP kapat
|
|
8
|
+
{kind:'warnSL'} → SL'ye yaklaşma uyarısı
|
|
9
|
+
{kind:'warnNoSL'} → SL'siz pozisyon uyarısı
|
|
10
|
+
main süreç aksiyonları köprüye uygular; başarılı olanın bayrağını st'ye yazar. */
|
|
11
|
+
|
|
12
|
+
function num(v, def) {
|
|
13
|
+
const n = Number(v);
|
|
14
|
+
return isFinite(n) ? n : def;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function roundTo(v, digits) {
|
|
18
|
+
const d = Math.max(0, Math.min(8, Math.round(Number(digits) || 0)));
|
|
19
|
+
const p = Math.pow(10, d);
|
|
20
|
+
return Math.round(v * p) / p;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function stepRound(v, step) {
|
|
24
|
+
const s = num(step, 0.01);
|
|
25
|
+
if (!(s > 0)) return v;
|
|
26
|
+
const q = Math.floor(v / s + 1e-9);
|
|
27
|
+
return q * s;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* meta: { digits, point, trade_stops_level, volume_min, volume_max, volume_step } */
|
|
31
|
+
function plan(pos, meta, st, cfg) {
|
|
32
|
+
const out = { actions: [], r: num(st && st.r, 0) };
|
|
33
|
+
if (!pos || !meta) return out;
|
|
34
|
+
const isBuy = Number(pos.type) === 0;
|
|
35
|
+
const entry = num(pos.price_open, 0);
|
|
36
|
+
const ref = num(pos.price_current, 0);
|
|
37
|
+
const sl = num(pos.sl, 0);
|
|
38
|
+
const volume = num(pos.volume, 0);
|
|
39
|
+
const digits = num(meta.digits, 5);
|
|
40
|
+
const point = num(meta.point, Math.pow(10, -digits));
|
|
41
|
+
const stopsPoints = num(meta.trade_stops_level, 0);
|
|
42
|
+
const minDist = stopsPoints > 0 ? (stopsPoints + 1) * point : point;
|
|
43
|
+
if (!(entry > 0) || !(ref > 0) || !(volume > 0)) return out;
|
|
44
|
+
|
|
45
|
+
/* R (ilk risk mesafesi) yalnızca pozisyon ilk görüldüğünde SL'den hesaplanır */
|
|
46
|
+
let r = num(st && st.r, 0);
|
|
47
|
+
if (!(r > 0) && sl > 0) r = Math.abs(entry - sl);
|
|
48
|
+
out.r = r;
|
|
49
|
+
|
|
50
|
+
const beOn = num(cfg && cfg.beOnR, 1);
|
|
51
|
+
const beOff = num(cfg && cfg.beOffsetR, 0.05);
|
|
52
|
+
const trailStart = num(cfg && cfg.trailStartR, 1.5);
|
|
53
|
+
const trailDist = num(cfg && cfg.trailR, 0.5);
|
|
54
|
+
const partialR = num(cfg && cfg.partialR, 0);
|
|
55
|
+
const partialPct = num(cfg && cfg.partialPct, 50);
|
|
56
|
+
|
|
57
|
+
const profit = isBuy ? ref - entry : entry - ref;
|
|
58
|
+
|
|
59
|
+
/* ---- BE + trailing: hedef SL'yi birleştir, asla geriye taşıma ---- */
|
|
60
|
+
if (r > 0) {
|
|
61
|
+
let desired = sl > 0 ? sl : 0;
|
|
62
|
+
let want = false;
|
|
63
|
+
if (beOn > 0 && !(st && st.be) && profit >= beOn * r) {
|
|
64
|
+
const beSl = isBuy ? entry + beOff * r : entry - beOff * r;
|
|
65
|
+
if (!(desired > 0) || (isBuy ? beSl > desired : beSl < desired)) desired = beSl;
|
|
66
|
+
want = true;
|
|
67
|
+
}
|
|
68
|
+
if (trailStart > 0 && trailDist > 0 && profit >= trailStart * r) {
|
|
69
|
+
const trailSl = isBuy ? ref - trailDist * r : ref + trailDist * r;
|
|
70
|
+
if (!(desired > 0) || (isBuy ? trailSl > desired : trailSl < desired)) desired = trailSl;
|
|
71
|
+
want = true;
|
|
72
|
+
}
|
|
73
|
+
if (want && desired > 0) {
|
|
74
|
+
/* broker stop mesafesi: SL fiyata çok yakınsa geçersiz — kırp */
|
|
75
|
+
desired = isBuy ? Math.min(desired, ref - minDist) : Math.max(desired, ref + minDist);
|
|
76
|
+
const improved = isBuy ? desired > sl + point * 0.5 : (sl === 0 || desired < sl - point * 0.5);
|
|
77
|
+
if (improved && desired > 0) {
|
|
78
|
+
out.actions.push({ kind: 'modify', sl: roundTo(desired, digits) });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ---- kısmi TP ---- */
|
|
84
|
+
if (partialR > 0 && partialPct > 0 && partialPct < 100 && r > 0 && !(st && st.partial) && profit >= partialR * r) {
|
|
85
|
+
const step = num(meta.volume_step, 0.01);
|
|
86
|
+
const vmin = num(meta.volume_min, 0.01);
|
|
87
|
+
const vmax = num(meta.volume_max, 100);
|
|
88
|
+
let vol = stepRound((volume * partialPct) / 100, step);
|
|
89
|
+
vol = Math.min(vol, vmax);
|
|
90
|
+
const remain = volume - vol;
|
|
91
|
+
if (vol >= vmin && remain >= vmin - 1e-9 && vol < volume) {
|
|
92
|
+
out.actions.push({ kind: 'partial', volume: Math.round(vol * 1e8) / 1e8 });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ---- uyarılar ---- */
|
|
97
|
+
if (sl > 0) {
|
|
98
|
+
if (r > 0) {
|
|
99
|
+
const dist = isBuy ? ref - sl : sl - ref;
|
|
100
|
+
const thr = Math.max(minDist * 3, 0.2 * r);
|
|
101
|
+
if (dist > 0 && dist <= thr && !(st && st.warnSL)) out.actions.push({ kind: 'warnSL' });
|
|
102
|
+
out.rearmSL = dist > thr * 1.6;
|
|
103
|
+
}
|
|
104
|
+
} else if (!(st && st.warnNoSL)) {
|
|
105
|
+
out.actions.push({ kind: 'warnNoSL' });
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = { plan, roundTo, stepRound };
|
|
@@ -291,6 +291,69 @@ def h_cancel(p):
|
|
|
291
291
|
return {"result": _send_request(req)}
|
|
292
292
|
|
|
293
293
|
|
|
294
|
+
def h_rates(p):
|
|
295
|
+
"""OHLC mumlar: symbol + timeframe (M1..MN1) + count (1..1000)."""
|
|
296
|
+
need()
|
|
297
|
+
symbol = str(p.get("symbol") or "").strip().upper()
|
|
298
|
+
if not symbol:
|
|
299
|
+
raise RuntimeError("symbol zorunlu")
|
|
300
|
+
tf_name = str(p.get("timeframe") or "M15").strip().upper()
|
|
301
|
+
tf_map = {
|
|
302
|
+
"M1": mt5.TIMEFRAME_M1, "M2": mt5.TIMEFRAME_M2, "M3": mt5.TIMEFRAME_M3,
|
|
303
|
+
"M4": mt5.TIMEFRAME_M4, "M5": mt5.TIMEFRAME_M5, "M6": mt5.TIMEFRAME_M6,
|
|
304
|
+
"M10": mt5.TIMEFRAME_M10, "M12": mt5.TIMEFRAME_M12, "M15": mt5.TIMEFRAME_M15,
|
|
305
|
+
"M20": mt5.TIMEFRAME_M20, "M30": mt5.TIMEFRAME_M30,
|
|
306
|
+
"H1": mt5.TIMEFRAME_H1, "H2": mt5.TIMEFRAME_H2, "H3": mt5.TIMEFRAME_H3,
|
|
307
|
+
"H4": mt5.TIMEFRAME_H4, "H6": mt5.TIMEFRAME_H6, "H8": mt5.TIMEFRAME_H8,
|
|
308
|
+
"H12": mt5.TIMEFRAME_H12,
|
|
309
|
+
"D1": mt5.TIMEFRAME_D1, "W1": mt5.TIMEFRAME_W1, "MN1": mt5.TIMEFRAME_MN1,
|
|
310
|
+
}
|
|
311
|
+
if tf_name not in tf_map:
|
|
312
|
+
raise RuntimeError("timeframe: " + "|".join(tf_map.keys()))
|
|
313
|
+
try:
|
|
314
|
+
count = int(p.get("count") or 200)
|
|
315
|
+
except Exception:
|
|
316
|
+
count = 200
|
|
317
|
+
count = max(1, min(count, 1000))
|
|
318
|
+
mt5.symbol_select(symbol, True)
|
|
319
|
+
rates = mt5.copy_rates_from_pos(symbol, tf_map[tf_name], 0, count)
|
|
320
|
+
if rates is None:
|
|
321
|
+
raise RuntimeError("mum verisi alinamadi: %s %s %s" % (symbol, tf_name, mt5.last_error()))
|
|
322
|
+
rows = []
|
|
323
|
+
for r in rates:
|
|
324
|
+
rows.append({
|
|
325
|
+
"time": int(r["time"]),
|
|
326
|
+
"open": float(r["open"]),
|
|
327
|
+
"high": float(r["high"]),
|
|
328
|
+
"low": float(r["low"]),
|
|
329
|
+
"close": float(r["close"]),
|
|
330
|
+
"tick_volume": int(r["tick_volume"]),
|
|
331
|
+
"spread": int(r["spread"]),
|
|
332
|
+
"real_volume": int(r["real_volume"]),
|
|
333
|
+
})
|
|
334
|
+
return {"symbol": symbol, "timeframe": tf_name, "count": len(rows), "rates": rows}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def h_margin(p):
|
|
338
|
+
"""order_calc_margin: tahmini gerekli marj (islem oncesi kalkan icin)."""
|
|
339
|
+
need()
|
|
340
|
+
symbol = str(p.get("symbol") or "").strip().upper()
|
|
341
|
+
side = str(p.get("side") or "buy").lower()
|
|
342
|
+
volume = float(p.get("volume") or 0)
|
|
343
|
+
if not symbol or volume <= 0 or side not in ("buy", "sell"):
|
|
344
|
+
raise RuntimeError("symbol/side/volume zorunlu")
|
|
345
|
+
mt5.symbol_select(symbol, True)
|
|
346
|
+
price = float(p.get("price") or 0)
|
|
347
|
+
if price <= 0:
|
|
348
|
+
tick = mt5.symbol_info_tick(symbol)
|
|
349
|
+
if tick is None:
|
|
350
|
+
raise RuntimeError("tick alinamadi: " + symbol)
|
|
351
|
+
price = tick.ask if side == "buy" else tick.bid
|
|
352
|
+
order_type = mt5.ORDER_TYPE_BUY if side == "buy" else mt5.ORDER_TYPE_SELL
|
|
353
|
+
m = mt5.order_calc_margin(order_type, symbol, volume, price)
|
|
354
|
+
return {"margin": (float(m) if m is not None else None), "price": price}
|
|
355
|
+
|
|
356
|
+
|
|
294
357
|
def h_all_symbols(p):
|
|
295
358
|
"""Terminaldeki tum semboller (sembol secici icin). filter: '*XAU*' gibi."""
|
|
296
359
|
need()
|
|
@@ -316,6 +379,7 @@ HANDLERS = {
|
|
|
316
379
|
"terminal": h_terminal,
|
|
317
380
|
"symbols": h_symbols,
|
|
318
381
|
"all_symbols": h_all_symbols,
|
|
382
|
+
"rates": h_rates,
|
|
319
383
|
"positions": h_positions,
|
|
320
384
|
"orders": h_orders,
|
|
321
385
|
"deals": h_deals,
|
|
@@ -324,6 +388,7 @@ HANDLERS = {
|
|
|
324
388
|
"modify": h_modify,
|
|
325
389
|
"pending": h_pending,
|
|
326
390
|
"cancel": h_cancel,
|
|
391
|
+
"margin": h_margin,
|
|
327
392
|
}
|
|
328
393
|
|
|
329
394
|
|