beast-agent 2.49.1 → 2.51.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/agent/bots.js +1 -1
- package/src/agent/defaulttools/mt5_shot/run.js +120 -50
- package/src/agent/defaulttools/mt5_shot/stitch.js +32 -7
- package/src/agent/defaulttools/mt5_shot/tool.json +8 -7
- package/src/agent/engine.js +17 -4
- package/src/agent/mt5/BeastFinance.mq5 +18 -4
- package/src/main.js +18 -2
- package/src/renderer/index.html +6 -0
- package/src/renderer/renderer.js +46 -1
- package/src/renderer/style.css +35 -0
package/package.json
CHANGED
package/src/agent/bots.js
CHANGED
|
@@ -107,7 +107,7 @@ const DEFAULT_BOT_SEEDS = [
|
|
|
107
107
|
name: 'Trader',
|
|
108
108
|
icon: '📊',
|
|
109
109
|
prompt:
|
|
110
|
-
'Sen BEAST Trader botsun: disiplinli bir trading analisti. Gerçek emir göndermez, API anahtarı istemez/kullanmazsın — yalnız genel fiyat API\'lerinden okur, paper trade tutarsın. Her kararda sembol, zaman dilimi, giriş/stop/hedef seviyeleri ve risk yüzdesini açıkça belirtirsin; sinyal zayıfsa "BEKLE" dersin. İzleyici (watcher) kurarak sürekli izlersin. Grafik doğrulaması: tool__mt5_shot ile MT5 grafiğinden GERÇEK PNG al ve grafiği gör (yoksa computer_look; metin-model isen ocr_read source:"screen"/"browser"); web grafiklerinde browser_screenshot kullan. İhtiyacın olan özel grafik aracı yoksa tool_request ile TOOL botuna yazdır — tool yazımı onun işidir. Beast Finance MT5 terminaline bağlandığında BeastFinance uzman danışmanı İLK GRAFİĞE OTOMATİK kurulur (AutoTrading + grafik panosu + seviye çizgileri); grafik/entegrasyon işlerinde bu EA ve mt5_ea köprüsü kullanılır — elle ekleme varsayma.',
|
|
110
|
+
'Sen BEAST Trader botsun: disiplinli bir trading analisti. Gerçek emir göndermez, API anahtarı istemez/kullanmazsın — yalnız genel fiyat API\'lerinden okur, paper trade tutarsın. Her kararda sembol, zaman dilimi, giriş/stop/hedef seviyeleri ve risk yüzdesini açıkça belirtirsin; sinyal zayıfsa "BEKLE" dersin. İzleyici (watcher) kurarak sürekli izlersin. Grafik doğrulaması: tool__mt5_shot ile MT5 grafiğinden GERÇEK PNG al ve grafiği gör (timeframes:["M1","M15"] ile çoklu periyot, symbols:["GOLD","EURUSD"] ile çoklu sembol TEK karede; yoksa computer_look; metin-model isen ocr_read source:"screen"/"browser"); web grafiklerinde browser_screenshot kullan. İhtiyacın olan özel grafik aracı yoksa tool_request ile TOOL botuna yazdır — tool yazımı onun işidir. Beast Finance MT5 terminaline bağlandığında BeastFinance uzman danışmanı İLK GRAFİĞE OTOMATİK kurulur (AutoTrading + grafik panosu + seviye çizgileri); grafik/entegrasyon işlerinde bu EA ve mt5_ea köprüsü kullanılır — elle ekleme varsayma.',
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
id: 'coder',
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
/* mt5_shot — MT5 grafiğinden GERÇEK PNG screenshot (BeastFinance EA "shot" komutu)
|
|
3
3
|
stdin: JSON args -> stdout: SADECE JSON sonuç
|
|
4
4
|
|
|
5
|
-
Girdi : {symbol?, timeframe?, timeframes?:["M1","M15"], layout?:"h"|"v", width?, height?, file?, template?, timeoutSec?, outdir?, diag?, embed?}
|
|
6
|
-
Çıktı : {ok:true, path, symbol, timeframe, ...} | {ok:false, err, error, cmdId, filesDir, ...}
|
|
5
|
+
Girdi : {symbol?, symbols?:["GOLD","EURUSD"], timeframe?, timeframes?:["M1","M15"], layout?:"h"|"v", width?, height?, file?, template?, timeoutSec?, outdir?, diag?, embed?}
|
|
6
|
+
Çıktı : {ok:true, path, symbol, symbols, timeframe, timeframes, panels:[...], ...} | {ok:false, err, error, cmdId, filesDir, ...}
|
|
7
7
|
err kodları: no_terminal | ea_offline | cmd_write_fail | ea_no_ack | ea_error | png_yok | timeout | stitch_fail | stitch_write_fail
|
|
8
8
|
|
|
9
|
+
ÇOKLU SEMBOL: symbols:["GOLD","EURUSD","BTCUSD"] (2-4 sembol) verilirse her
|
|
10
|
+
sembolden bir kare çekilir ve TEK PNG'de birleştirilir; timeframes ile
|
|
11
|
+
birlikte verilirse sembol × periyot ızgarası kurulur (en fazla 6 kare).
|
|
12
|
+
İzleme listesinin tamamı TEK görselde ekibe gönderilebilir.
|
|
13
|
+
|
|
9
14
|
ÇOKLU PERİYOT: timeframes:["M1","M15"] (2-3 adet) verilirse her periyot ayrı
|
|
10
15
|
çekilir ve TEK PNG'de birleştirilir (layout:"h" soldan sağa — varsayılan,
|
|
11
|
-
"v" üstten alta). Panellerin
|
|
12
|
-
sonuçta panels[{timeframe, position, path}] hangi tarafın hangi
|
|
13
|
-
olduğunu söyler. tf2:"M15" kısayolu da (timeframe + tf2) kabul edilir.
|
|
16
|
+
"v" üstten alta). Panellerin SAĞ ÜSTüne "SEMBOL · PERİYOT" etiketi çizilir;
|
|
17
|
+
sonuçta panels[{symbol, timeframe, position, path}] hangi tarafın hangi
|
|
18
|
+
sembol/periyot olduğunu söyler. tf2:"M15" kısayolu da (timeframe + tf2) kabul edilir.
|
|
14
19
|
|
|
15
20
|
Notlar:
|
|
16
21
|
- beast_cmd.json'a {id, ts, cmd, params} yazılır (UTF-16 BOM; EA FILE_UNICODE okur).
|
|
@@ -42,6 +47,7 @@ function syncSleep(ms) {
|
|
|
42
47
|
|
|
43
48
|
let CTX = { cmdId: null, filesDir: null, started: Date.now() };
|
|
44
49
|
let ARGS = {};
|
|
50
|
+
let WATCHDOG = null;
|
|
45
51
|
|
|
46
52
|
function out(o) {
|
|
47
53
|
try { fs.writeSync(1, JSON.stringify(o) + '\n'); }
|
|
@@ -180,6 +186,44 @@ function normalizeTfs(a) {
|
|
|
180
186
|
return out.slice(0, 3); /* 80 sn watchdog bütçesi: en fazla 3 panel */
|
|
181
187
|
}
|
|
182
188
|
|
|
189
|
+
/* çoklu sembol listesi: ["GOLD","EURUSD"] ya da "GOLD,EURUSD";
|
|
190
|
+
symbol alanı da listeye katılır (symbol + symbols birlikte verilebilir) */
|
|
191
|
+
function normalizeSymbols(a) {
|
|
192
|
+
const out = [];
|
|
193
|
+
const add = (x) => {
|
|
194
|
+
const s = String(x == null ? '' : x).trim().toUpperCase();
|
|
195
|
+
if (s && !out.includes(s)) out.push(s);
|
|
196
|
+
};
|
|
197
|
+
add(a.symbol);
|
|
198
|
+
let s = a.symbols;
|
|
199
|
+
if (typeof s === 'string') s = s.split(/[,\s|+/]+/);
|
|
200
|
+
if (Array.isArray(s)) for (const x of s) add(x);
|
|
201
|
+
return out.slice(0, 4);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* PANEL PLANI: symbols × timeframes çarpımı → sırayla çekilecek kareler.
|
|
205
|
+
- symbols 2+ → her sembolden bir kare (timeframes da verilmişse her periyot)
|
|
206
|
+
- tek sembol + timeframes 2+ → eski çoklu periyot davranışı
|
|
207
|
+
- tek karelik işler plana girmez: [] döner (tek çekim yolu kullanılır)
|
|
208
|
+
Dönüş: [{symbol, timeframe}] — en fazla MAX_PANELS kare (watchdog bütçesi) */
|
|
209
|
+
const MAX_PANELS = 6;
|
|
210
|
+
function buildPlan(a) {
|
|
211
|
+
const A = a || ARGS;
|
|
212
|
+
const symbols = normalizeSymbols(A);
|
|
213
|
+
const tfs = normalizeTfs(A);
|
|
214
|
+
const out = [];
|
|
215
|
+
const push = (symbol, timeframe) => {
|
|
216
|
+
if (out.length < MAX_PANELS) out.push({ symbol, timeframe });
|
|
217
|
+
};
|
|
218
|
+
if (symbols.length >= 2) {
|
|
219
|
+
const per = tfs.length ? tfs : [null];
|
|
220
|
+
for (const s of symbols) for (const tf of per) push(s, tf);
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
if (tfs.length >= 2) for (const tf of tfs) push(symbols[0] || null, tf);
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
|
|
183
227
|
function sanitizeFile(name, fallback) {
|
|
184
228
|
let f = (name && String(name).trim()) ? path.basename(String(name).trim()) : fallback;
|
|
185
229
|
if (!/\.(png|gif|bmp|jpg|jpeg)$/i.test(f)) f += '.png';
|
|
@@ -275,18 +319,22 @@ function embedData(src, size) {
|
|
|
275
319
|
return dataUrl;
|
|
276
320
|
}
|
|
277
321
|
|
|
278
|
-
/* ---- ÇOKLU
|
|
279
|
-
|
|
280
|
-
"SEMBOL · PERİYOT" etiketi çizilir; soldan sağa (layout:"v" ile
|
|
281
|
-
|
|
322
|
+
/* ---- ÇOKLU KARE: plan (sembol × periyot) sırayla çekilir ve TEK PNG'de
|
|
323
|
+
birleştirilir — kilit zaten alınmış (ack çakışması yok); her panelin SAĞ
|
|
324
|
+
ÜSTüne "SEMBOL · PERİYOT" etiketi çizilir; soldan sağa (layout:"v" ile
|
|
325
|
+
üstten alta). plan: [{symbol, timeframe}] — boş alan = aktif grafik. */
|
|
326
|
+
async function runPanels(plan, filesDir, cmdFile, ackFile, beatFile, timeoutSec, diag) {
|
|
282
327
|
let compose = null;
|
|
283
328
|
try { compose = require('./stitch.js').compose; } catch (e) {}
|
|
284
329
|
if (!compose) return { err: 'stitch_fail', error: 'stitch.js bulunamadı (mt5_shot güncel kurulum gerekiyor)' };
|
|
285
330
|
|
|
286
|
-
const n =
|
|
331
|
+
const n = plan.length;
|
|
287
332
|
const baseId = 'S' + Date.now().toString(36);
|
|
288
333
|
const fileArg = ARGS.file && String(ARGS.file).trim() ? String(ARGS.file).trim() : '';
|
|
289
|
-
const
|
|
334
|
+
const uniqSym = [...new Set(plan.map((p) => p.symbol).filter(Boolean))];
|
|
335
|
+
const base = fileArg
|
|
336
|
+
? fileArg.replace(/\.[a-z0-9]+$/i, '')
|
|
337
|
+
: (uniqSym.length > 1 ? 'beast_symbols_' : 'beast_mtf_') + baseId;
|
|
290
338
|
const perAck = Math.max(5, Math.min(15, Math.floor(45 / n)));
|
|
291
339
|
const width = Number(ARGS.width) > 0 ? Math.min(10000, Math.round(Number(ARGS.width))) : 1600;
|
|
292
340
|
const height = Number(ARGS.height) > 0 ? Math.min(10000, Math.round(Number(ARGS.height))) : 900;
|
|
@@ -294,32 +342,36 @@ async function runMulti(tfList, filesDir, cmdFile, ackFile, beatFile, timeoutSec
|
|
|
294
342
|
|
|
295
343
|
const panels = [];
|
|
296
344
|
const cmdIds = [];
|
|
297
|
-
let symbol = ARGS.symbol ? String(ARGS.symbol).toUpperCase() : null;
|
|
298
345
|
for (let i = 0; i < n; i++) {
|
|
299
|
-
const
|
|
346
|
+
const spec = plan[i] || {};
|
|
347
|
+
const symArg = spec.symbol ? String(spec.symbol).toUpperCase() : null;
|
|
348
|
+
const tfArg = spec.timeframe ? String(spec.timeframe).toUpperCase() : null;
|
|
300
349
|
const id = baseId + 'p' + (i + 1);
|
|
301
|
-
const
|
|
302
|
-
|
|
350
|
+
const tag = [symArg, tfArg].filter(Boolean).join('_') || 'aktif';
|
|
351
|
+
const params = { file: sanitizeFile(base + '_' + tag, 'beast_shot_' + id), width, height };
|
|
352
|
+
if (tfArg) params.timeframe = tfArg;
|
|
353
|
+
if (symArg) params.symbol = symArg;
|
|
303
354
|
if (ARGS.template !== undefined) params.template = String(ARGS.template);
|
|
304
355
|
cmdIds.push(id);
|
|
305
356
|
const r = captureOne({ id, file: params.file, params, filesDir, cmdFile, ackFile, beatFile, timeoutSec: perAck, diag });
|
|
306
357
|
if (!r.ok) {
|
|
307
358
|
return {
|
|
308
359
|
err: r.err,
|
|
309
|
-
error: '[' +
|
|
310
|
-
extra: Object.assign({ panel:
|
|
360
|
+
error: '[' + tag + '] ' + r.error,
|
|
361
|
+
extra: Object.assign({ panel: tag, cmdId: id, requested: { symbol: symArg, timeframe: tfArg } }, r.extra || {})
|
|
311
362
|
};
|
|
312
363
|
}
|
|
313
364
|
const cp = copyOut(r.src, r.shotFile, ARGS.outdir);
|
|
314
|
-
|
|
315
|
-
|
|
365
|
+
const tfDone = (r.res && r.res.period ? tfName(r.res.period) : null) || tfArg || null;
|
|
366
|
+
const symDone = (r.res && r.res.symbol ? String(r.res.symbol).toUpperCase() : null) || symArg || null;
|
|
367
|
+
panels.push({ spec, sym: symDone, tf: tfDone, src: r.src, file: r.shotFile, size: r.size, res: r.res, pubPath: cp.pubPath, beastPath: cp.beastPath });
|
|
316
368
|
}
|
|
317
369
|
|
|
318
|
-
/* birleştir */
|
|
370
|
+
/* birleştir: etiketler çekimden SONRA gerçek sembol/periyotla kurulur */
|
|
319
371
|
let composed = null;
|
|
320
372
|
try {
|
|
321
373
|
composed = await compose(
|
|
322
|
-
panels.map((p) => ({ path: p.src, label: (
|
|
374
|
+
panels.map((p) => ({ path: p.src, label: [p.sym, p.tf].filter(Boolean).join(' · ') || 'aktif grafik' })),
|
|
323
375
|
{ layout }
|
|
324
376
|
);
|
|
325
377
|
} catch (e) { composed = { ok: false, error: String((e && e.message) || e) }; }
|
|
@@ -327,19 +379,21 @@ async function runMulti(tfList, filesDir, cmdFile, ackFile, beatFile, timeoutSec
|
|
|
327
379
|
return { err: 'stitch_fail', error: 'görüntüler birleştirilemedi: ' + ((composed && composed.error) || 'bilinmeyen') };
|
|
328
380
|
}
|
|
329
381
|
|
|
330
|
-
const outFile = sanitizeFile(fileArg,
|
|
382
|
+
const outFile = sanitizeFile(fileArg, base + '.png');
|
|
331
383
|
const pathMt5 = path.join(filesDir, outFile);
|
|
332
384
|
try { fs.writeFileSync(pathMt5, composed.buffer); }
|
|
333
385
|
catch (e) { return { err: 'stitch_write_fail', error: 'birleşik PNG yazılamadı: ' + e.message }; }
|
|
334
386
|
const size = composed.buffer.length;
|
|
335
387
|
const cp = copyOut(pathMt5, outFile, ARGS.outdir);
|
|
336
388
|
const dataUrl = embedData(pathMt5, size);
|
|
389
|
+
const symDone = [...new Set(panels.map((p) => p.sym).filter(Boolean))];
|
|
390
|
+
const tfList = [...new Set(plan.map((p) => p.timeframe).filter(Boolean))];
|
|
337
391
|
|
|
338
392
|
return {
|
|
339
393
|
ok: true,
|
|
340
394
|
payload: {
|
|
341
395
|
ok: true,
|
|
342
|
-
path: cp.pubPath || cp.beastPath || pathMt5, // send_file
|
|
396
|
+
path: cp.pubPath || cp.beastPath || pathMt5, // send_file / agent_dm image:"<path>"
|
|
343
397
|
path_mt5: pathMt5,
|
|
344
398
|
path_beast: cp.beastPath,
|
|
345
399
|
file: outFile,
|
|
@@ -348,10 +402,12 @@ async function runMulti(tfList, filesDir, cmdFile, ackFile, beatFile, timeoutSec
|
|
|
348
402
|
...(dataUrl ? { __injectImage: dataUrl } : {}),
|
|
349
403
|
cmdIds,
|
|
350
404
|
filesDir,
|
|
351
|
-
symbol:
|
|
405
|
+
symbol: symDone.length === 1 ? symDone[0] : null,
|
|
406
|
+
symbols: symDone,
|
|
352
407
|
timeframes: tfList,
|
|
353
408
|
layout,
|
|
354
409
|
panels: panels.map((p, i) => ({
|
|
410
|
+
symbol: p.sym,
|
|
355
411
|
timeframe: p.tf,
|
|
356
412
|
position: layout === 'h'
|
|
357
413
|
? (i === 0 ? 'left' : (i === n - 1 ? 'right' : 'middle'))
|
|
@@ -363,7 +419,12 @@ async function runMulti(tfList, filesDir, cmdFile, ackFile, beatFile, timeoutSec
|
|
|
363
419
|
})),
|
|
364
420
|
width: composed.width,
|
|
365
421
|
height: composed.height,
|
|
366
|
-
requested: {
|
|
422
|
+
requested: {
|
|
423
|
+
symbol: ARGS.symbol ? String(ARGS.symbol).toUpperCase() : null,
|
|
424
|
+
symbols: Array.isArray(ARGS.symbols) ? ARGS.symbols.map((s) => String(s).toUpperCase()) : (ARGS.symbols || null),
|
|
425
|
+
timeframes: tfList,
|
|
426
|
+
width, height, layout
|
|
427
|
+
},
|
|
367
428
|
heartbeat_age_sec: heartbeatAge(beatFile),
|
|
368
429
|
ms: Date.now() - CTX.started,
|
|
369
430
|
pubErr: cp.pubErr || undefined
|
|
@@ -394,10 +455,11 @@ async function run() {
|
|
|
394
455
|
const locked = acquireLock(Math.min(15000, timeoutSec * 500));
|
|
395
456
|
if (!locked) console.error('[mt5_shot] uyarı: kilit alınamadı, yine de devam ediliyor');
|
|
396
457
|
|
|
397
|
-
/* ÇOKLU
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
458
|
+
/* ÇOKLU KARE: symbols:["GOLD","EURUSD"] ve/veya timeframes:["M1","M15"]
|
|
459
|
+
→ sırayla çekilip tek PNG'de etiketli birleştirilir */
|
|
460
|
+
const plan = buildPlan();
|
|
461
|
+
if (plan.length >= 2) {
|
|
462
|
+
const r = await runPanels(plan, filesDir, cmdFile, ackFile, beatFile, timeoutSec, diag);
|
|
401
463
|
if (!r.ok) return fail(r.err, r.error, r.extra);
|
|
402
464
|
return out(r.payload);
|
|
403
465
|
}
|
|
@@ -546,24 +608,32 @@ function finish() {
|
|
|
546
608
|
.then(run)
|
|
547
609
|
.catch((e) => fail('timeout', 'beklenmeyen hata: ' + (e && e.message ? e.message : String(e))));
|
|
548
610
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
611
|
+
|
|
612
|
+
function main() {
|
|
613
|
+
try {
|
|
614
|
+
process.stdin.setEncoding('utf8');
|
|
615
|
+
process.stdin.on('data', (d) => {
|
|
616
|
+
raw += d;
|
|
617
|
+
if (silenceTimer) clearTimeout(silenceTimer);
|
|
618
|
+
// JSON tamamlandıysa EOF beklemeden başla; değilse kısa bir sessizlik penceresi daha bekle
|
|
619
|
+
const o = parseArgs(raw);
|
|
620
|
+
if (raw.trim() && Object.keys(o).length) return begin(finish);
|
|
621
|
+
silenceTimer = setTimeout(() => begin(finish), 1200);
|
|
622
|
+
});
|
|
623
|
+
process.stdin.on('end', () => begin(finish));
|
|
624
|
+
process.stdin.on('error', () => begin(finish));
|
|
625
|
+
process.stdin.on('close', () => begin(finish));
|
|
626
|
+
process.stdin.resume();
|
|
627
|
+
setTimeout(() => begin(finish), 2500); // stdin hiç açılmaz/kapanmazsa da devam et (asılmaya izin yok)
|
|
628
|
+
} catch (e) { begin(finish); }
|
|
629
|
+
|
|
630
|
+
/* watchdog: hiçbir koşulda 80 sn üzeri asılı kalma */
|
|
631
|
+
WATCHDOG = setTimeout(() => {
|
|
632
|
+
fail('timeout', 'mt5_shot ' + Math.round((Date.now() - CTX.started) / 1000) + ' sn içinde tamamlanamadı (watchdog) — ack/PNG beklemesi aşıldı');
|
|
633
|
+
}, HARD_MS);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/* saf yardımcılar test için dışa açılır; runner yalnız doğrudan çalıştırılınca başlar */
|
|
637
|
+
module.exports = { normalizeTfs, normalizeSymbols, buildPlan, sanitizeFile };
|
|
638
|
+
|
|
639
|
+
if (require.main === module) main();
|
|
@@ -97,16 +97,41 @@ async function compose(panels, opts) {
|
|
|
97
97
|
if (layout === 'h') ctx.fillRect(x - GAP, 0, GAP, H);
|
|
98
98
|
else ctx.fillRect(0, y - GAP, W, GAP);
|
|
99
99
|
}
|
|
100
|
-
/* etiket:
|
|
100
|
+
/* etiket: SAĞ ÜST köşede BÜYÜK zaman dilimi (üstünde küçük sembol) —
|
|
101
|
+
görsel ajan hangi panelin hangi periyot olduğunu bakışta okur */
|
|
101
102
|
if (p.label) {
|
|
102
103
|
try {
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
const parts = String(p.label).split('·').map((s) => s.trim()).filter(Boolean);
|
|
105
|
+
const tf = parts.length > 1 ? parts[1] : parts[0];
|
|
106
|
+
const sym = parts.length > 1 ? parts[0] : '';
|
|
107
|
+
const fsTf = Math.max(22, Math.round(cellW / 44)); /* 1600 px panel → 36 px */
|
|
108
|
+
const fsSym = Math.max(12, Math.round(fsTf * 0.45));
|
|
109
|
+
const padX = Math.round(fsTf * 0.55);
|
|
110
|
+
const padY = Math.round(fsTf * 0.42);
|
|
111
|
+
const lineGap = Math.round(fsSym * 0.45);
|
|
108
112
|
ctx.textBaseline = 'middle';
|
|
109
|
-
ctx.
|
|
113
|
+
ctx.textAlign = 'right';
|
|
114
|
+
ctx.font = pickFont(fsTf);
|
|
115
|
+
const tw = ctx.measureText(tf).width;
|
|
116
|
+
ctx.font = pickFont(fsSym);
|
|
117
|
+
const sw = sym ? ctx.measureText(sym).width : 0;
|
|
118
|
+
const boxW = Math.ceil(Math.max(tw, sw) + padX * 2);
|
|
119
|
+
const boxH = Math.ceil(padY * 2 + fsTf + (sym ? fsSym + lineGap : 0));
|
|
120
|
+
const bx = x + cellW - boxW - 10;
|
|
121
|
+
const by = y + 10;
|
|
122
|
+
ctx.fillStyle = 'rgba(0,0,0,0.80)';
|
|
123
|
+
ctx.fillRect(bx, by, boxW, boxH);
|
|
124
|
+
ctx.fillStyle = '#00e676';
|
|
125
|
+
ctx.fillRect(bx, by, boxW, 3); /* üst aksan çizgisi */
|
|
126
|
+
if (sym) {
|
|
127
|
+
ctx.font = pickFont(fsSym);
|
|
128
|
+
ctx.fillStyle = '#c9d2d9';
|
|
129
|
+
ctx.fillText(sym, bx + boxW - padX, by + padY + fsSym / 2);
|
|
130
|
+
}
|
|
131
|
+
ctx.font = pickFont(fsTf);
|
|
132
|
+
ctx.fillStyle = '#00e676';
|
|
133
|
+
ctx.fillText(tf, bx + boxW - padX, by + padY + (sym ? fsSym + lineGap : 0) + fsTf / 2);
|
|
134
|
+
ctx.textAlign = 'start';
|
|
110
135
|
} catch (e) {}
|
|
111
136
|
}
|
|
112
137
|
});
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mt5_shot",
|
|
3
|
-
"defaultsVersion":
|
|
4
|
-
"description": "MT5 grafiğinin GERÇEK ekran görüntüsünü alır ve GÖRSELİ ÇAĞIRAN AJANA GÖSTERİR: MT5\\MQL5\\Files\\beast_cmd.json üzerinden BeastFinance EA'ya 'shot' komutu yazar, EA ChartScreenShot ile PNG üretir; PNG hem tool klasörüne hem <kullanıcı>\\beast_shots altına kopyalanır (dönen path send_file'a verilebilir) hem de data URL olarak sonuca gömülür — engine bunu sonraki turda ajana görsel olarak enjekte eder (embed:false kapatır). ÇOKLU ZAMAN DİLİMİ: timeframes:[\"M1\",\"M15\"] verirsen aynı sembolün periyotları SIRAYLA çekilip TEK PNG'de birleştirilir (layout:\"h\" soldan sağa — varsayılan; \"v\" üstten alta; 2-3 periyot) ve her panelin
|
|
3
|
+
"defaultsVersion": 4,
|
|
4
|
+
"description": "MT5 grafiğinin GERÇEK ekran görüntüsünü alır ve GÖRSELİ ÇAĞIRAN AJANA GÖSTERİR: MT5\\MQL5\\Files\\beast_cmd.json üzerinden BeastFinance EA'ya 'shot' komutu yazar, EA ChartScreenShot ile PNG üretir; PNG hem tool klasörüne hem <kullanıcı>\\beast_shots altına kopyalanır (dönen path send_file'a verilebilir) hem de data URL olarak sonuca gömülür — engine bunu sonraki turda ajana görsel olarak enjekte eder (embed:false kapatır). ÇOKLU SEMBOL: symbols:[\"GOLD\",\"EURUSD\",\"BTCUSD\"] (2-4 sembol) verirsen her sembolden bir kare çekilip TEK PNG'de birleştirilir (izleme listesinin tamamı TEK görselle gönderilir); timeframes ile birlikte verilirse sembol × periyot ızgarası kurulur (en fazla 6 kare). ÇOKLU ZAMAN DİLİMİ: timeframes:[\"M1\",\"M15\"] verirsen aynı sembolün periyotları SIRAYLA çekilip TEK PNG'de birleştirilir (layout:\"h\" soldan sağa — varsayılan; \"v\" üstten alta; 2-3 periyot) ve her panelin SAĞ ÜST köşesine BÜYÜK periyot etiketi (üstünde küçük sembol) çizilir — solda/sağda hangi sembol/periyot olduğu görüntüden bakışta okunur. Aktif grafik istenen sembol/periyot ile aynıysa ondan çekilir (Beast panosu + seviye çizgileri görünür), değilse EA geçici grafik açar (ChartOpen/ChartSetSymbolPeriod + BeastShot.tpl açık tema); çoklu sembolde her sembol için sırayla. Çekim öncesi grafiğe sağ boşluk payı (chart shift %12) uygulanır — SON MUM sağ kenara yapışmaz; aktif grafikte önceki ayar çekimden sonra geri yazılır. Komut id/ts ile yazılır, ack poll edilir ve PNG MQL5\\Files'ta belirene kadar beklenir; hata halinde gövde şu kodu taşır: err = no_terminal|ea_offline|cmd_write_fail|ea_no_ack|ea_error|png_yok|timeout|stitch_fail (+ cmdId, filesDir, diagnostic, ping). Grafik analizi/raporu ve ekip paylaşımı için birincil screenshot aracıdır.",
|
|
5
5
|
"parameters": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
8
|
-
"symbol": { "type": "string", "description": "
|
|
8
|
+
"symbol": { "type": "string", "description": "Tek sembol (örn GOLD); boş = aktif grafik sembolü. Çoklu sembol için symbols kullan" },
|
|
9
|
+
"symbols": { "type": "array", "items": { "type": "string" }, "description": "ÇOKLU SEMBOL TEK GÖRÜNTÜ: örn [\"GOLD\",\"EURUSD\",\"BTCUSD\"] — her sembolden bir kare çekilip etiketli tek PNG'de birleştirilir (2-4 sembol; timeframes ile birlikte sembol × periyot ızgarası, en fazla 6 kare)" },
|
|
9
10
|
"timeframe": { "type": "string", "description": "M1|M5|M15|M30|H1|H4|D1 (boş = aktif periyot)" },
|
|
10
|
-
"timeframes": { "type": "array", "items": { "type": "string" }, "description": "ÇOKLU PERİYOT TEK GÖRÜNTÜ: örn [\"M1\",\"M15\"] — sırayla çekilip tek PNG'de birleştirilir (2-3 adet; soldan sağa)" },
|
|
11
|
+
"timeframes": { "type": "array", "items": { "type": "string" }, "description": "ÇOKLU PERİYOT TEK GÖRÜNTÜ: örn [\"M1\",\"M15\"] — sırayla çekilip tek PNG'de birleştirilir (2-3 adet; soldan sağa); symbols ile birlikte ızgara" },
|
|
11
12
|
"tf2": { "type": "string", "description": "İkinci periyot kısayolu (timeframe ile birlikte): örn timeframe M1 + tf2 M15" },
|
|
12
|
-
"layout": { "type": "string", "description": "Çoklu
|
|
13
|
+
"layout": { "type": "string", "description": "Çoklu kare yerleşimi: h = yan yana (varsayılan), v = alt alta" },
|
|
13
14
|
"width": { "type": "number", "description": "Panel başına görüntü genişliği (varsayılan 1600)" },
|
|
14
15
|
"height": { "type": "number", "description": "Panel başına görüntü yüksekliği (varsayılan 900)" },
|
|
15
|
-
"file": { "type": "string", "description": "Opsiyonel dosya adı (varsayılan beast_shot_<id>.png; çoklu
|
|
16
|
-
"timeoutSec": { "type": "number", "description": "Ack/PNG bekleme süresi sn (varsayılan 20, 5-70; çoklu
|
|
16
|
+
"file": { "type": "string", "description": "Opsiyonel dosya adı (varsayılan beast_shot_<id>.png; çoklu karede birleşik dosyanın adı)" },
|
|
17
|
+
"timeoutSec": { "type": "number", "description": "Ack/PNG bekleme süresi sn (varsayılan 20, 5-70; çoklu karede panel başına 15 sn tavan)" },
|
|
17
18
|
"outdir": { "type": "string", "description": "PNG'nin kopyalanacağı klasör (varsayılan <kullanıcı>\\beast_shots — send_file buradan gönderebilir)" },
|
|
18
19
|
"diag": { "type": "boolean", "description": "Ack gelmezse teşhis amaçlı ping at (varsayılan true)" },
|
|
19
20
|
"embed": { "type": "boolean", "description": "PNG'yi ajana görsel olarak enjekte et (varsayılan true; false = yalnız dosya yolu döner)" }
|
package/src/agent/engine.js
CHANGED
|
@@ -1970,7 +1970,7 @@ class Engine {
|
|
|
1970
1970
|
macro:
|
|
1971
1971
|
'ROL: MAKRO AJANI 🌍 — büyük resim uzmanısın, İşlem AÇMAZSIN (mt5_trade/mt5_pending/mt5_close KULLANMA). Her turda web_search ile güncel makro manşetleri + ekonomik takvim riskleri (faiz, CPI, jeopolitik); DXY/altın/petrol bağıntılarını odak sembollere çevir; sembol başına yön eğilimi + TEMKİN/BEKLE notu ver.',
|
|
1972
1972
|
visual:
|
|
1973
|
-
'ROL: GÖRSEL ANALİZ AJANI 👁 — grafik/ekran görüntüsü uzmanısın, İşlem AÇMAZSIN (mt5_trade/mt5_pending/mt5_close KULLANMA). MT5 grafiği için ÖNCE tool__mt5_shot kullan (BeastFinance EA "shot": gerçek grafik PNG + görsel SONRAKİ TURDA gözüne gelir; symbol/timeframe vererek başka sembol/periyot çekebilirsin; dönen path send_file ile gönderilebilir); gerekirse EA panosuna mt5_ea action:"note" ile plan/seviye yaz. web grafiklerinde browser_open + browser_screenshot; bilgisayar ekranı için computer_look; görsel göremeyen metin-model isen ocr_read (source:"screen"/"browser") kullan. Gördüğün yapıyı yorumla: trend, formasyon, mum yapısı, destek-direnç ve SL/TP çizgileri; sembol başına GÖRSEL TEYİT + AL/SAT/BEKLE notu üret. Önemli bulguyu GÖRSELİYLE paylaş: agent_dm image:true (son ekran görüntün) ya da image:"<mt5_shot path>" ile ANA TRADER\'a/ekibe gönder — karşı ajan görüntüyü GERÇEKTEN görür, AJAN DM panelinde açılır.',
|
|
1973
|
+
'ROL: GÖRSEL ANALİZ AJANI 👁 — grafik/ekran görüntüsü uzmanısın, İşlem AÇMAZSIN (mt5_trade/mt5_pending/mt5_close KULLANMA). MT5 grafiği için ÖNCE tool__mt5_shot kullan (BeastFinance EA "shot": gerçek grafik PNG + görsel SONRAKİ TURDA gözüne gelir; symbol/timeframe vererek başka sembol/periyot çekebilirsin; dönen path send_file ile gönderilebilir); gerekirse EA panosuna mt5_ea action:"note" ile plan/seviye yaz. ÇOKLU SEMBOL: symbols:["GOLD","EURUSD","BTCUSD"] ile izleme listesindeki tüm semboller TEK çağrıda çekilip etiketli TEK PNG\'de birleştirilir — ayrı ayrı 3 DM atmak yerine bu birleşik görseli gönder. web grafiklerinde browser_open + browser_screenshot; bilgisayar ekranı için computer_look; görsel göremeyen metin-model isen ocr_read (source:"screen"/"browser") kullan. Gördüğün yapıyı yorumla: trend, formasyon, mum yapısı, destek-direnç ve SL/TP çizgileri; sembol başına GÖRSEL TEYİT + AL/SAT/BEKLE notu üret. Önemli bulguyu GÖRSELİYLE paylaş: agent_dm image:true (son ekran görüntün) ya da image:"<mt5_shot path>" ile ANA TRADER\'a/ekibe gönder — karşı ajan görüntüyü GERÇEKTEN görür, AJAN DM panelinde açılır. NOT: image:true DAİMA oturumdaki EN SON görseli yollar — her sembolün fotoğrafını ayrı göndermen gerekirse dönen path\'leri kullan (image:"<path>").',
|
|
1974
1974
|
})[String((session && session.financeRole) || '')] || '';
|
|
1975
1975
|
/* ROL → SKILL eşleştirmesi (ayarlar modalı): rol başına TEK ve ZORUNLU
|
|
1976
1976
|
skill; ANA TRADER için ayrıca PLAYBOOK skill'i (roleSkills.trader).
|
|
@@ -2010,7 +2010,7 @@ class Engine {
|
|
|
2010
2010
|
(roleBlockFull ? roleBlockFull + '\n' : '') +
|
|
2011
2011
|
(teamLine || '') +
|
|
2012
2012
|
'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_ea (BeastFinance grafik panosu: status/ping/chart/note), mt5_trade (piyasa emri), mt5_close (kapat), mt5_modify (SL/TP), mt5_pending (bekleyen emir), mt5_cancel (emir iptal).\n' +
|
|
2013
|
-
'KİŞİSEL MT5 ARAÇLARI (VARSAYILAN KURULU — tool__* tüm oturumlarda çağrılabilir): tool__mt5_shot (grafikten GERÇEK PNG + görsel ajana enjekte — grafik işlerinde BİRİNCİL; timeframes:["M1","M15"] ile çoklu periyot TEK karede
|
|
2013
|
+
'KİŞİSEL MT5 ARAÇLARI (VARSAYILAN KURULU — tool__* tüm oturumlarda çağrılabilir): tool__mt5_shot (grafikten GERÇEK PNG + görsel ajana enjekte — grafik işlerinde BİRİNCİL; timeframes:["M1","M15"] ile çoklu periyot, symbols:["GOLD","EURUSD","BTCUSD"] ile çoklu sembol TEK karede), tool__mt5_barlar2 (OHLC mumlar, köprüsüz), tool__mt5_m15_m5 (GOLD M15+M5 hazır paket), tool__mt5_fiyat, tool__mt5_durum (hesap+pozisyon+emir tek çağrı), tool__mt5_gecmis, tool__mt5_pozisyon_gecmis, tool__mt5_sltp, tool__mt5_kapat, tool__mt5_bekleyen, tool__mt5_emir, tool__mt5_emir_iptal.\n' +
|
|
2014
2014
|
'BEASTFINANCE EA (OTOMATİK GRAFİK UZMANI — ENTEGRASYON KANALI): MT5 terminaline bağlanıldığında BeastFinance uzman danışmanı İLK GRAFİĞE OTOMATİK yüklenir, AutoTrading izni açılır (kurulum sistem tarafından yapılır; elle ekleme gerekmez). Grafik panosu + seviye çizgileri + dosya köprüsü (beast_ea.json / beast_cmd.json / beast_note.json) bu EA üzerinden yürür: entegrasyon işlerinde (status/ping/chart/note) mt5_ea kullan; ekran görüntüsü işlerinde tool__mt5_shot ("shot" komutu — gerçek PNG + ajan görseli) birincildir. Yazdığın not ve çizgiler tool__mt5_shot ve computer_look screenshot\'ında GÖRÜNÜR.\n' +
|
|
2015
2015
|
'YETKİLERİN (AÇIK — çekinmeden kullan):\n' +
|
|
2016
2016
|
'- skill: kurulu SKILL.md kataloğunu oku ve uygula — tool yazmadan ÖNCE skill("tool-yazma"), MT5 tarafı işlerden ÖNCE skill("mql5") oku ve prosedürüne birebir uy.\n' +
|
|
@@ -2018,7 +2018,7 @@ class Engine {
|
|
|
2018
2018
|
'- MQL5: MT5 tarafında script/gösterge/EA yaz (write_file), metaeditor64.exe /compile ile derle, MQL5\\Files dosya köprüsüyle veriyi Beast\'e taşı; kullanıcıya çalıştırma adımını açıkça söyle.\n' +
|
|
2019
2019
|
'- Yerleşik araçlar: run_command, python_run, read_file/write_file/edit_file, web_search/deep_search, browser_*, computer_look (ekran görüntüsü) — hepsi açık.\n' +
|
|
2020
2020
|
'GRAFİK & GÖRSEL DOĞRULAMA (karar öncesi — vazgeçilmez):\n' +
|
|
2021
|
-
'- tool__mt5_shot: MT5 grafiğinin GERÇEK PNG\'si (BeastFinance EA "shot" komutu). Görsel SONRAKİ TURDA gözüne gelir; aktif grafikte Beast panosu + mt5_ea note ile çizilen seviyeler GÖRÜNÜR. symbol/timeframe ver → EA geçici grafik açıp o sembol/periyottan çeker; dönen path send_file ile kullanıcıya gönderilebilir. Grafik analizinde İLK tercih budur. ÇOKLU ZAMAN DİLİMİ: timeframes:["M1","M15"] (2-3 periyot, layout:"v" alt alta) → periyotlar sırayla çekilip TEK PNG\'de birleştirilir
|
|
2021
|
+
'- tool__mt5_shot: MT5 grafiğinin GERÇEK PNG\'si (BeastFinance EA "shot" komutu). Görsel SONRAKİ TURDA gözüne gelir; aktif grafikte Beast panosu + mt5_ea note ile çizilen seviyeler GÖRÜNÜR. symbol/timeframe ver → EA geçici grafik açıp o sembol/periyottan çeker; dönen path send_file ile kullanıcıya gönderilebilir. Grafik analizinde İLK tercih budur. ÇOKLU SEMBOL: symbols:["GOLD","EURUSD","BTCUSD"] (2-4 sembol) → her sembolden bir kare çekilip TEK PNG\'de birleştirilir (izleme listesinin tamamı TEK görselle paylaşılır; sembol başına ayrı DM atmaya gerek yok); timeframes ile birlikte verilirse sembol × periyot ızgarası kurulur (en fazla 6 kare, her panel "SEMBOL · PERİYOT" etiketli). ÇOKLU ZAMAN DİLİMİ: timeframes:["M1","M15"] (2-3 periyot, layout:"v" alt alta) → periyotlar sırayla çekilip TEK PNG\'de birleştirilir; solda/sağda hangisi olduğunu görüntüden OKURSUN — trend uyumu/MTF teyidi için bunu kullan (gerçek kapanışlarla).\n' +
|
|
2022
2022
|
'- mt5_ea action:"note": grafiğe kısa plan metni + yatay seviye çizgileri yazar (destek/direnç/SL/TP) — sonra tool__mt5_shot ile çekip seviyelerin doğru yerde olduğunu GÖRSEL doğrula.\n' +
|
|
2023
2023
|
'- computer_look: tüm masaüstü ekranı (MT5 dışı pencereler dahil); browser_screenshot: web grafikleri (TradingView/Investing) — browser_open ile aç.\n' +
|
|
2024
2024
|
'- Görsel göremiyorsan (metin-model) ocr_read (source:"screen"/"browser") ile grafikteki fiyat/seviyeleri metne çevir.\n' +
|
|
@@ -3688,7 +3688,9 @@ class Engine {
|
|
|
3688
3688
|
/* SÜREKLİ ajan (Beast Finance): DM/rapor teslimi YENİ TUR AÇMAZ — aksi
|
|
3689
3689
|
halde ajanlar birbirine DM attıkça sonsuz tur döngüsü doğar (bağlam
|
|
3690
3690
|
şişer, ajan hiç dinlenmez). Mesaj inbox'a yazılır; bir sonraki PLANLI
|
|
3691
|
-
turda send() tarafından tek blok halinde enjekte edilir.
|
|
3691
|
+
turda send() tarafından tek blok halinde enjekte edilir.
|
|
3692
|
+
İSTİSNA: kullanıcı/chat kaynaklı DM'ler (wake) onDmQueued kancasıyla
|
|
3693
|
+
ajanı HEMEN uyandırabilir — ajan-ajan trafiği beklemede kalır. */
|
|
3692
3694
|
const continuous = !!(job && job.continuous);
|
|
3693
3695
|
const rest = [];
|
|
3694
3696
|
for (const r of this._pendingReports) {
|
|
@@ -3701,6 +3703,11 @@ class Engine {
|
|
|
3701
3703
|
...(r.image ? { image: String(r.image) } : {}),
|
|
3702
3704
|
});
|
|
3703
3705
|
if (job.dmInbox.length > 20) job.dmInbox.splice(0, job.dmInbox.length - 20);
|
|
3706
|
+
/* kullanıcı/chat DM'i: sürekli ajan boştaysa turu beklemeden uyandır
|
|
3707
|
+
(main.js finWakeAgent); ajan-ajan DM'lerinde tetiklenmez */
|
|
3708
|
+
if (r.dm && r.wake) {
|
|
3709
|
+
try { if (typeof this.onDmQueued === 'function') this.onDmQueued(job, r); } catch {}
|
|
3710
|
+
}
|
|
3704
3711
|
continue;
|
|
3705
3712
|
}
|
|
3706
3713
|
if (this.isBusy(sessionId)) { rest.push(r); continue; }
|
|
@@ -6973,6 +6980,10 @@ Engine.prototype._agentDmSend = async function (fromSid, args) {
|
|
|
6973
6980
|
`[AJAN DM (grup: "${g.title}") — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
|
|
6974
6981
|
`(Cevap yalnızca aksiyon/karar GEREKİYORSA ver — agent_dm group: "${g.title}", to: "${dm.fromTitle}", topic: "${topic}"; teşekkür/onay yazma.)`,
|
|
6975
6982
|
...(image ? { image } : {}),
|
|
6983
|
+
/* dm: AJAN DM'i; wake: gönderen koşan bir ajan DEĞİL (kullanıcı/chat) —
|
|
6984
|
+
sürekli ajan boştaysa turu beklemeden uyandırılır (ping-pong koruması) */
|
|
6985
|
+
dm: true,
|
|
6986
|
+
wake: !fromJob,
|
|
6976
6987
|
});
|
|
6977
6988
|
this.flushPendingReports(m);
|
|
6978
6989
|
}
|
|
@@ -6999,6 +7010,8 @@ Engine.prototype._agentDmSend = async function (fromSid, args) {
|
|
|
6999
7010
|
`[AJAN DM — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
|
|
7000
7011
|
`(Cevap yalnızca aksiyon/karar GEREKİYORSA ver — agent_dm to: "${dm.fromTitle}", topic: "${topic}"; teşekkür/onay yazma.)`,
|
|
7001
7012
|
...(image ? { image } : {}),
|
|
7013
|
+
dm: true,
|
|
7014
|
+
wake: !fromJob,
|
|
7002
7015
|
});
|
|
7003
7016
|
this.flushPendingReports(target);
|
|
7004
7017
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//+------------------------------------------------------------------+
|
|
2
|
-
//| BeastFinance.mq5 — Beast Finance köprü EA'sı (v1.
|
|
2
|
+
//| BeastFinance.mq5 — Beast Finance köprü EA'sı (v1.21) |
|
|
3
3
|
//| - Heartbeat: MQL5\Files\beast_ea.json (durum + izinler + equity) |
|
|
4
4
|
//| - Komut köprüsü: beast_cmd.json (Beast yazar) → beast_cmd_ack.json|
|
|
5
5
|
//| - Entegrasyon/Screenshot: grafik üstü pano (Comment) + seviye |
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//+------------------------------------------------------------------+
|
|
9
9
|
#property copyright "Beast Agent"
|
|
10
10
|
#property link "https://github.com/algokodcom/beast-agent"
|
|
11
|
-
#property version "1.
|
|
11
|
+
#property version "1.21"
|
|
12
12
|
#property description "Beast Finance köprü EA'sı — heartbeat + komut köprüsü + grafik panosu."
|
|
13
13
|
|
|
14
14
|
#include <Trade\Trade.mqh>
|
|
@@ -72,7 +72,7 @@ void WriteBeat(const string stage)
|
|
|
72
72
|
{
|
|
73
73
|
int h = FileOpen(BeatFile, FILE_WRITE|FILE_TXT|FILE_UNICODE);
|
|
74
74
|
if(h == INVALID_HANDLE) return;
|
|
75
|
-
string js = StringFormat("{\"ok\":true,\"ea\":\"BeastFinance\",\"version\":\"1.
|
|
75
|
+
string js = StringFormat("{\"ok\":true,\"ea\":\"BeastFinance\",\"version\":\"1.21\",\"tag\":\"%s\",\"stage\":\"%s\",\"time\":%d,\"server_time\":\"%s\",\"symbol\":\"%s\",\"period\":%d,\"equity\":%.2f,\"balance\":%.2f,\"terminal_trade_allowed\":%s,\"mql_trade_allowed\":%s,\"positions\":%d,\"note\":\"%s\"}",
|
|
76
76
|
JStr(InpTag), stage, (int)TimeCurrent(), TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
|
|
77
77
|
_Symbol, (int)Period(),
|
|
78
78
|
AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE),
|
|
@@ -93,7 +93,7 @@ void RefreshPanel()
|
|
|
93
93
|
Comment("");
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
|
-
string lines = "Beast Finance — BeastFinance v1.
|
|
96
|
+
string lines = "Beast Finance — BeastFinance v1.21\n";
|
|
97
97
|
lines += _Symbol + " · " + EnumToString((ENUM_TIMEFRAMES)Period()) + " · " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + "\n";
|
|
98
98
|
lines += StringFormat("Equity: %.2f · Balance: %.2f · Acik pozisyon: %d\n",
|
|
99
99
|
AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE), PositionsTotal());
|
|
@@ -351,8 +351,16 @@ void ProcessCommands()
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
/* SON MUM SAĞ KENARA YAPIŞMASIN: grafik "shift" payı (%12 sağ boşluk).
|
|
355
|
+
Aktif grafikte önceki ayar çekimden SONRA geri yazılır — kullanıcının
|
|
356
|
+
grafiği kalıcı değişmez; geçici grafik zaten kapanır. */
|
|
357
|
+
long shiftPrev = ChartGetInteger(cid, CHART_SHIFT);
|
|
358
|
+
long shiftSizePrev = ChartGetInteger(cid, CHART_SHIFT_SIZE);
|
|
359
|
+
|
|
354
360
|
if(ok)
|
|
355
361
|
{
|
|
362
|
+
ChartSetInteger(cid, CHART_SHIFT, true);
|
|
363
|
+
ChartSetInteger(cid, CHART_SHIFT_SIZE, 12);
|
|
356
364
|
ChartRedraw(cid);
|
|
357
365
|
Sleep(1200); /* mumlar/olcek otursun */
|
|
358
366
|
ChartRedraw(cid);
|
|
@@ -367,6 +375,12 @@ void ProcessCommands()
|
|
|
367
375
|
result = StringFormat("{\"file\":\"%s\",\"width\":%d,\"height\":%d,\"symbol\":\"%s\",\"period\":%d,\"temp_chart\":%s,\"template_applied\":%s,\"theme_ok\":%s,\"bg\":%d}",
|
|
368
376
|
JStr(file), w, hh, symWant, (int)tfUse, (tempChart ? "true" : "false"), (tplApplied ? "true" : "false"), (themeOk ? "true" : "false"), bgNow);
|
|
369
377
|
}
|
|
378
|
+
if(!tempChart)
|
|
379
|
+
{
|
|
380
|
+
ChartSetInteger(cid, CHART_SHIFT, shiftPrev != 0);
|
|
381
|
+
ChartSetInteger(cid, CHART_SHIFT_SIZE, shiftSizePrev);
|
|
382
|
+
ChartRedraw(cid);
|
|
383
|
+
}
|
|
370
384
|
}
|
|
371
385
|
if(tempChart) ChartClose(cid);
|
|
372
386
|
}
|
package/src/main.js
CHANGED
|
@@ -3551,10 +3551,14 @@ function reloadBackend() {
|
|
|
3551
3551
|
})();
|
|
3552
3552
|
}
|
|
3553
3553
|
},
|
|
3554
|
-
});
|
|
3554
|
+
});
|
|
3555
|
+
/* AJAN DM UYANDIRMA: kullanıcı/chat kaynaklı DM sürekli finance ajanına
|
|
3556
|
+
düşünce sıradaki planlı turu BEKLEMEZ — ajan boştaysa tur hemen başlar
|
|
3557
|
+
(ajan-ajan DM'lerinde engine kancayı çağırmaz; ping-pong korunur). */
|
|
3558
|
+
engine.onDmQueued = (job) => { try { finWakeAgent(job && job.id); } catch {} };
|
|
3555
3559
|
/* PANEL RUN köprüsü: ajanın panel_run aracı → ÇALIŞTIR panelindeki yönetilen
|
|
3556
3560
|
süreç koşucusu (sandbox:run IPC ile aynı makine). */
|
|
3557
|
-
engine.sbRunHook = sbRunStartManaged;
|
|
3561
|
+
engine.sbRunHook = sbRunStartManaged;
|
|
3558
3562
|
/* SANDBOX KÖPRÜSÜ: ana sohbetteki ajanın sandbox_repo aracı → panel ile aynı
|
|
3559
3563
|
makine (indir/kur/başlat/durdur); kullanıcı chatte repo linki bırakınca
|
|
3560
3564
|
ajan kendisi indirip kurabilir. */
|
|
@@ -10942,6 +10946,18 @@ function finAgentRound(sid) {
|
|
|
10942
10946
|
.catch(() => launch(teamPrefix));
|
|
10943
10947
|
}
|
|
10944
10948
|
|
|
10949
|
+
/* AJAN DM UYANDIRMA (engine.onDmQueued): kullanıcı/chat kaynaklı DM sürekli
|
|
10950
|
+
ajanın inbox'ına düştüğünde tur zamanlayıcısını beklemeden turu başlatır.
|
|
10951
|
+
Ajan meşgulse dokunmaz — dmInbox zaten sıradaki güvenli noktada okunur. */
|
|
10952
|
+
function finWakeAgent(sid) {
|
|
10953
|
+
const id = String(sid || '');
|
|
10954
|
+
const agent = financeState.agents.get(id);
|
|
10955
|
+
if (!agent || !engine) return;
|
|
10956
|
+
if (engine.isBusy(id)) return;
|
|
10957
|
+
clearTimeout(agent.timer);
|
|
10958
|
+
agent.timer = setTimeout(() => { try { finAgentRound(id); } catch {} }, 300);
|
|
10959
|
+
}
|
|
10960
|
+
|
|
10945
10961
|
/* Tur/durum sonları: sürekli ajan döngüsünü besle; kullanıcı iptalinde kapat */
|
|
10946
10962
|
function finFlushOnDone(ev) {
|
|
10947
10963
|
if (!ev || (ev.type !== 'done' && ev.type !== 'error')) return;
|
package/src/renderer/index.html
CHANGED
|
@@ -549,6 +549,12 @@
|
|
|
549
549
|
</div>
|
|
550
550
|
</div>
|
|
551
551
|
|
|
552
|
+
<!-- DM görselleri: tıkla → tam boy görüntüleyici (Esc / tıkla kapat) -->
|
|
553
|
+
<div id="imgOverlay" hidden>
|
|
554
|
+
<img id="imgOverlayImg" alt="görsel" />
|
|
555
|
+
<button id="imgOverlayClose" title="Kapat (Esc)">✕︎</button>
|
|
556
|
+
</div>
|
|
557
|
+
|
|
552
558
|
<div id="toolOverlay" hidden>
|
|
553
559
|
<div id="toolDialog">
|
|
554
560
|
<div id="toolDialogHead">
|
package/src/renderer/renderer.js
CHANGED
|
@@ -53,6 +53,9 @@ const els = {
|
|
|
53
53
|
dmColumnBtn: $('#dmColumnBtn'),
|
|
54
54
|
dmMaxBtn: $('#dmMaxBtn'),
|
|
55
55
|
dmBackBtn: $('#dmBackBtn'),
|
|
56
|
+
imgOverlay: $('#imgOverlay'),
|
|
57
|
+
imgOverlayImg: $('#imgOverlayImg'),
|
|
58
|
+
imgOverlayClose: $('#imgOverlayClose'),
|
|
56
59
|
watchBtn: $('#watchBtn'),
|
|
57
60
|
watchPaneList: $('#watchPaneList'),
|
|
58
61
|
watchPaneOpen: $('#watchPaneOpen'),
|
|
@@ -5688,7 +5691,8 @@ if (els.dmOverlay) {
|
|
|
5688
5691
|
els.dmOverlay.addEventListener('keydown', (e) => {
|
|
5689
5692
|
if (e.key === 'Escape') {
|
|
5690
5693
|
e.stopPropagation();
|
|
5691
|
-
if (els.
|
|
5694
|
+
if (els.imgOverlay && !els.imgOverlay.hidden) closeImgViewer(); /* önce görüntüleyici kapansın */
|
|
5695
|
+
else if (els.dmOverlay.classList.contains('dm-max')) toggleDmMax(false);
|
|
5692
5696
|
else closeDmModal();
|
|
5693
5697
|
}
|
|
5694
5698
|
});
|
|
@@ -5702,6 +5706,47 @@ if (els.dmColumnBtn) {
|
|
|
5702
5706
|
});
|
|
5703
5707
|
}
|
|
5704
5708
|
|
|
5709
|
+
/* --- DM görsel görüntüleyici: görsele tıkla → TAM BOY (tıkla/Esc kapat) --- */
|
|
5710
|
+
function openImgViewer(src) {
|
|
5711
|
+
if (!els.imgOverlay || !els.imgOverlayImg || !src) return;
|
|
5712
|
+
els.imgOverlayImg.src = String(src);
|
|
5713
|
+
els.imgOverlay.hidden = false;
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
function closeImgViewer() {
|
|
5717
|
+
if (els.imgOverlay) els.imgOverlay.hidden = true;
|
|
5718
|
+
if (els.imgOverlayImg) els.imgOverlayImg.src = '';
|
|
5719
|
+
}
|
|
5720
|
+
|
|
5721
|
+
if (els.imgOverlay) {
|
|
5722
|
+
els.imgOverlay.addEventListener('click', () => closeImgViewer());
|
|
5723
|
+
}
|
|
5724
|
+
if (els.imgOverlayClose) {
|
|
5725
|
+
els.imgOverlayClose.addEventListener('click', (e) => {
|
|
5726
|
+
e.stopPropagation();
|
|
5727
|
+
closeImgViewer();
|
|
5728
|
+
});
|
|
5729
|
+
}
|
|
5730
|
+
if (els.dmDialogList) {
|
|
5731
|
+
/* delegasyon: modal her mesajda yeniden çizilir — dinleyici tek yerde kalır */
|
|
5732
|
+
els.dmDialogList.addEventListener('click', (e) => {
|
|
5733
|
+
const img = e.target && e.target.closest ? e.target.closest('img.dmt-img') : null;
|
|
5734
|
+
if (!img || !img.src) return;
|
|
5735
|
+
e.stopPropagation();
|
|
5736
|
+
openImgViewer(img.src);
|
|
5737
|
+
});
|
|
5738
|
+
}
|
|
5739
|
+
document.addEventListener(
|
|
5740
|
+
'keydown',
|
|
5741
|
+
(e) => {
|
|
5742
|
+
if (e.key !== 'Escape' || !els.imgOverlay || els.imgOverlay.hidden) return;
|
|
5743
|
+
e.preventDefault();
|
|
5744
|
+
e.stopPropagation();
|
|
5745
|
+
closeImgViewer();
|
|
5746
|
+
},
|
|
5747
|
+
true
|
|
5748
|
+
);
|
|
5749
|
+
|
|
5705
5750
|
/* ---------------- TOOLS konsolu (kişisel araçlar) ----------------
|
|
5706
5751
|
%APPDATA%\beast\tools\<ad>\tool.json + run.js — kullanıcı kendi araçlarını
|
|
5707
5752
|
yazar; finance dahil TÜM oturumlar tool__<ad> olarak çağırır. Sol panelde
|
package/src/renderer/style.css
CHANGED
|
@@ -2331,6 +2331,7 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
|
|
|
2331
2331
|
border-radius: 6px;
|
|
2332
2332
|
margin-top: 5px;
|
|
2333
2333
|
border: 1px solid var(--border, rgba(255,255,255,0.12));
|
|
2334
|
+
cursor: zoom-in;
|
|
2334
2335
|
}
|
|
2335
2336
|
.dmt-msg .dmt-img-note {
|
|
2336
2337
|
font-size: 10px;
|
|
@@ -2338,6 +2339,40 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
|
|
|
2338
2339
|
font-style: italic;
|
|
2339
2340
|
margin-top: 3px;
|
|
2340
2341
|
}
|
|
2342
|
+
/* DM görsel görüntüleyici: tam boy, tıkla/Esc ile kapanır */
|
|
2343
|
+
#imgOverlay {
|
|
2344
|
+
position: fixed;
|
|
2345
|
+
inset: 0;
|
|
2346
|
+
z-index: 400;
|
|
2347
|
+
background: rgba(0, 0, 0, 0.88);
|
|
2348
|
+
display: flex;
|
|
2349
|
+
align-items: center;
|
|
2350
|
+
justify-content: center;
|
|
2351
|
+
cursor: zoom-out;
|
|
2352
|
+
}
|
|
2353
|
+
#imgOverlay[hidden] { display: none; }
|
|
2354
|
+
#imgOverlay img {
|
|
2355
|
+
max-width: 96vw;
|
|
2356
|
+
max-height: 94vh;
|
|
2357
|
+
object-fit: contain;
|
|
2358
|
+
border-radius: 6px;
|
|
2359
|
+
box-shadow: 0 9px 40px rgba(0, 0, 0, 0.55);
|
|
2360
|
+
background: #0b0b0b;
|
|
2361
|
+
}
|
|
2362
|
+
#imgOverlayClose {
|
|
2363
|
+
position: absolute;
|
|
2364
|
+
top: 14px;
|
|
2365
|
+
right: 18px;
|
|
2366
|
+
width: 34px;
|
|
2367
|
+
height: 34px;
|
|
2368
|
+
border: none;
|
|
2369
|
+
border-radius: 8px;
|
|
2370
|
+
background: rgba(255, 255, 255, 0.12);
|
|
2371
|
+
color: #fff;
|
|
2372
|
+
font-size: 17px;
|
|
2373
|
+
cursor: pointer;
|
|
2374
|
+
}
|
|
2375
|
+
#imgOverlayClose:hover { background: rgba(255, 255, 255, 0.26); }
|
|
2341
2376
|
/* ajan adının yanındaki KOD rozeti (oturum id) */
|
|
2342
2377
|
.dm-code {
|
|
2343
2378
|
font-size: 9px; font-weight: 700; letter-spacing: 0.5px;
|