beast-agent 2.76.5 → 2.77.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 +669 -664
- package/src/agent/customtools.js +6 -19
- package/src/agent/engine.js +55 -307
- package/src/agent/seeds/tool-yazma/SKILL.md +13 -16
- package/src/agent/skills.js +14 -35
- package/src/agent/supertonic.js +622 -0
- package/src/agent/tools.js +3 -7
- package/src/main.js +77 -6068
- package/src/preload.js +2 -17
- package/src/renderer/i18n.js +14 -8
- package/src/renderer/index.html +0 -297
- package/src/renderer/renderer.js +142 -2280
- package/src/renderer/style.css +3 -825
- package/src/agent/defaulttools/mt5_barlar2/run.js +0 -188
- package/src/agent/defaulttools/mt5_barlar2/tool.json +0 -23
- package/src/agent/defaulttools/mt5_bekleyen/run.js +0 -105
- package/src/agent/defaulttools/mt5_bekleyen/tool.json +0 -18
- package/src/agent/defaulttools/mt5_durum/run.js +0 -93
- package/src/agent/defaulttools/mt5_durum/tool.json +0 -10
- package/src/agent/defaulttools/mt5_emir/run.js +0 -98
- package/src/agent/defaulttools/mt5_emir/tool.json +0 -17
- package/src/agent/defaulttools/mt5_emir_iptal/run.js +0 -90
- package/src/agent/defaulttools/mt5_emir_iptal/tool.json +0 -12
- package/src/agent/defaulttools/mt5_fiyat/run.js +0 -87
- package/src/agent/defaulttools/mt5_fiyat/tool.json +0 -16
- package/src/agent/defaulttools/mt5_gecmis/run.js +0 -87
- package/src/agent/defaulttools/mt5_gecmis/tool.json +0 -12
- package/src/agent/defaulttools/mt5_kapat/run.js +0 -108
- package/src/agent/defaulttools/mt5_kapat/tool.json +0 -15
- package/src/agent/defaulttools/mt5_m15_m5/m15m5.py +0 -60
- package/src/agent/defaulttools/mt5_m15_m5/run.js +0 -73
- package/src/agent/defaulttools/mt5_m15_m5/tool.json +0 -10
- package/src/agent/defaulttools/mt5_pozisyon_gecmis/run.js +0 -144
- package/src/agent/defaulttools/mt5_pozisyon_gecmis/tool.json +0 -12
- package/src/agent/defaulttools/mt5_shot/run.js +0 -649
- package/src/agent/defaulttools/mt5_shot/stitch.js +0 -151
- package/src/agent/defaulttools/mt5_shot/tool.json +0 -25
- package/src/agent/defaulttools/mt5_sltp/run.js +0 -92
- package/src/agent/defaulttools/mt5_sltp/tool.json +0 -14
- package/src/agent/financetools.js +0 -1025
- package/src/agent/finindicators.js +0 -254
- package/src/agent/finrisk.js +0 -289
- package/src/agent/finstats.js +0 -239
- package/src/agent/finwatch.js +0 -302
- package/src/agent/mt5/BeastFinance.mq5 +0 -498
- package/src/agent/mt5/BeastShot.tpl +0 -0
- package/src/agent/mt5setup.js +0 -542
- package/src/agent/scripts/mt5_bridge.py +0 -575
- package/src/agent/seeds/haber-duygu/SKILL.md +0 -49
- package/src/agent/seeds/mql5/SKILL.md +0 -154
- package/src/agent/seeds/price-action/SKILL.md +0 -60
- package/src/agent/seeds/risk-yonetimi/SKILL.md +0 -52
- package/src/mt5bridge.js +0 -215
|
@@ -1,1025 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/* Beast Finance — MT5 ajan araçları.
|
|
4
|
-
Handler'lar main süreçteki mt5bridge singleton'ına bağlanır; işlem açan
|
|
5
|
-
araçlar settings.finance.allowTrading / maxLot / maxPositions ile sınırlanır.
|
|
6
|
-
main tarafı setConfig/setNotify ile ayar ve bildirim kancasını enjekte eder. */
|
|
7
|
-
|
|
8
|
-
const mt5 = require('../mt5bridge');
|
|
9
|
-
const finindicators = require('./finindicators');
|
|
10
|
-
const finstats = require('./finstats');
|
|
11
|
-
const finrisk = require('./finrisk');
|
|
12
|
-
const finwatch = require('./finwatch');
|
|
13
|
-
|
|
14
|
-
const NAMES = [
|
|
15
|
-
'mt5_status',
|
|
16
|
-
'mt5_account',
|
|
17
|
-
'mt5_market',
|
|
18
|
-
'mt5_rates',
|
|
19
|
-
'mt5_indicators',
|
|
20
|
-
'mt5_risksize',
|
|
21
|
-
'mt5_positions',
|
|
22
|
-
'mt5_orders',
|
|
23
|
-
'mt5_history',
|
|
24
|
-
'mt5_alerts',
|
|
25
|
-
'mt5_note',
|
|
26
|
-
'mt5_ogrenme',
|
|
27
|
-
'mt5_limits',
|
|
28
|
-
'mt5_ea',
|
|
29
|
-
'mt5_trade',
|
|
30
|
-
'mt5_close',
|
|
31
|
-
'mt5_modify',
|
|
32
|
-
'mt5_pending',
|
|
33
|
-
'mt5_cancel',
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
let getCfg = () => ({ allowTrading: false, minLot: 0.01, maxLot: 0.1, maxPositions: 3, symbols: [] });
|
|
37
|
-
let notify = () => {};
|
|
38
|
-
/* DİSİPLİN KANCASI (main enjekte eder): kodla zorlanan kurallar — günlük işlem
|
|
39
|
-
limiti, kayıp serisi molası, re-entry beklemesi, kur maruziyeti.
|
|
40
|
-
(side, symbol, positions) → hata metni ya da null. */
|
|
41
|
-
let discipline = () => null;
|
|
42
|
-
/* alarm deposu main süreçte yaşar (dosyaya kalıcı) — buradan enjekte edilir */
|
|
43
|
-
let alertsApi = {
|
|
44
|
-
list: () => [],
|
|
45
|
-
set: () => null,
|
|
46
|
-
remove: () => false,
|
|
47
|
-
};
|
|
48
|
-
/* LIMIT API (main enjekte eder): trader lot/pozisyon limitlerini okuyup yazar */
|
|
49
|
-
let limitsApi = {
|
|
50
|
-
get: () => ({ minLot: 0.01, maxLot: 0.1, maxPositions: 3 }),
|
|
51
|
-
set: () => ({ ok: false, error: 'limit güncelleme kullanılamıyor' }),
|
|
52
|
-
};
|
|
53
|
-
/* ÖĞRENME API (main enjekte eder): sembol bazlı öğrenme deposu */
|
|
54
|
-
let learningApi = {
|
|
55
|
-
list: () => ({ ok: false, error: 'öğrenme deposu kullanılamıyor' }),
|
|
56
|
-
add: () => ({ ok: false, error: 'öğrenme deposu kullanılamıyor' }),
|
|
57
|
-
remove: () => ({ ok: false, error: 'öğrenme deposu kullanılamıyor' }),
|
|
58
|
-
clear: () => ({ ok: false, error: 'öğrenme deposu kullanılamıyor' }),
|
|
59
|
-
stats: () => ({ ok: false, error: 'öğrenme deposu kullanılamıyor' }),
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function setConfig(fn) {
|
|
63
|
-
if (typeof fn === 'function') getCfg = fn;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function setNotify(fn) {
|
|
67
|
-
if (typeof fn === 'function') notify = fn;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function setAlerts(api) {
|
|
71
|
-
if (api && typeof api === 'object') alertsApi = api;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function setDiscipline(fn) {
|
|
75
|
-
if (typeof fn === 'function') discipline = fn;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function setLimits(api) {
|
|
79
|
-
if (api && typeof api === 'object') limitsApi = { ...limitsApi, ...api };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function setLearning(api) {
|
|
83
|
-
if (api && typeof api === 'object') learningApi = { ...learningApi, ...api };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function bcall(method, params, timeoutMs) {
|
|
87
|
-
const r = await mt5.call(method, params, timeoutMs);
|
|
88
|
-
if (!r || r.ok !== true) throw new Error((r && r.error) || 'MT5 hatası');
|
|
89
|
-
return r.data;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function symInfoRow(symbol) {
|
|
93
|
-
const data = await bcall('symbols', { symbols: [String(symbol || '').toUpperCase()] }, 10000);
|
|
94
|
-
const row = data && data.symbols && data.symbols[0];
|
|
95
|
-
return row && !row.missing ? row : null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function tickPrice(row, side) {
|
|
99
|
-
const p = side === 'buy' ? Number(row && row.ask) : Number(row && row.bid);
|
|
100
|
-
if (isFinite(p) && p > 0) return p;
|
|
101
|
-
return Number(row && row.bid) || Number(row && row.ask) || 0;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/* ZAMAN DİLİMİ: ajanın seçtiği periyot — işlem yorumuna yazılır ("Beast M15"),
|
|
105
|
-
watchdog pozisyondan okur ve kapanışta periyot bazlı öğrenmeye işler. */
|
|
106
|
-
const TF_VALID = new Set(['M1', 'M5', 'M15', 'M30', 'H1', 'H4', 'D1', 'W1', 'MN1']);
|
|
107
|
-
function normTimeframe(v) {
|
|
108
|
-
const s = String(v || '').trim().toUpperCase();
|
|
109
|
-
if (!s) return '';
|
|
110
|
-
const m = s.match(/^(M|H|D|W|MN)\s*(\d+)$/);
|
|
111
|
-
if (!m) return '';
|
|
112
|
-
const key = m[1] === 'MN' ? 'MN1' : m[1] + m[2];
|
|
113
|
-
return TF_VALID.has(key) ? key : '';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/* SEMBOL BAZLI LOT ARALIĞI (trader mt5_limits ile koyar): sembol limiti
|
|
117
|
-
varsa genel aralığın yerine geçer — yoksa genel minLot/maxLot kullanılır.
|
|
118
|
-
İki değer de broker normalizasyonuna girer (adım/min/max sembolden gelir). */
|
|
119
|
-
function lotRange(cfg, symbol) {
|
|
120
|
-
const sym = String(symbol || '').trim().toUpperCase();
|
|
121
|
-
const o = (cfg && cfg.symbolLimits && cfg.symbolLimits[sym]) || null;
|
|
122
|
-
let minLot = Number(o && o.minLot) > 0 ? Number(o.minLot) : Number(cfg && cfg.minLot) || 0.01;
|
|
123
|
-
let maxLot = Number(o && o.maxLot) > 0 ? Number(o.maxLot) : Number(cfg && cfg.maxLot) || 0.1;
|
|
124
|
-
if (minLot > maxLot) minLot = maxLot;
|
|
125
|
-
if (maxLot < minLot) maxLot = minLot;
|
|
126
|
-
return { minLot, maxLot };
|
|
127
|
-
}
|
|
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
|
-
|
|
143
|
-
/* İşlem öncesi risk katmanı: stops_level + yoğunluk + marj kalkanı.
|
|
144
|
-
Hata metni ya da null döner. positions çağıran tarafından verilir. */
|
|
145
|
-
async function preTradeCheck(cfg, side, symbol, info, price, volume, sl, tp, positions) {
|
|
146
|
-
const closeErr = finrisk.stopsTooClose(info, price, sl, tp);
|
|
147
|
-
if (closeErr) return closeErr;
|
|
148
|
-
/* SEMBOL/YÖN TAVANI: main'in efektif değeri (talimat "sembol başına N" ya da
|
|
149
|
-
"aynı anda en fazla N işlem" notu) ayarı ve toplam tavanı KELEPÇELER */
|
|
150
|
-
let maxSym = Number(cfg.maxPerSymbol) || 0;
|
|
151
|
-
let maxSide = Number(cfg.maxSameSide) || 0;
|
|
152
|
-
try {
|
|
153
|
-
const lim = limitsApi.get();
|
|
154
|
-
if (Number(lim && lim.maxPerSymbol) > 0) maxSym = Number(lim.maxPerSymbol);
|
|
155
|
-
if (Number(lim && lim.maxSameSide) > 0) maxSide = Number(lim.maxSameSide);
|
|
156
|
-
} catch {}
|
|
157
|
-
const expErr = finrisk.exposureCheck(positions || [], side, symbol, maxSym, maxSide);
|
|
158
|
-
if (expErr) return expErr;
|
|
159
|
-
/* KODLA DİSİPLİN: günlük limit / kayıp serisi molası / re-entry / kur maruziyeti */
|
|
160
|
-
let discErr = null;
|
|
161
|
-
try { discErr = discipline(side, symbol, positions || []); } catch {}
|
|
162
|
-
if (discErr) return discErr;
|
|
163
|
-
const minLevel = Number(cfg.minMarginLevel) || 0;
|
|
164
|
-
let account = null;
|
|
165
|
-
let estMargin = 0;
|
|
166
|
-
try {
|
|
167
|
-
const a = await bcall('account', {}, 8000);
|
|
168
|
-
account = a && a.account;
|
|
169
|
-
} catch {}
|
|
170
|
-
try {
|
|
171
|
-
const m = await bcall('margin', { symbol, side, volume, price }, 8000);
|
|
172
|
-
estMargin = Number(m && m.margin) || 0;
|
|
173
|
-
} catch {}
|
|
174
|
-
const mErr = finrisk.marginCheck(account, estMargin, minLevel);
|
|
175
|
-
if (mErr && mErr.error) return mErr.error;
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function noteTrade(kind, data, ctx) {
|
|
180
|
-
try { notify({ kind, data, at: Date.now(), sid: ctx && ctx.sessionId ? String(ctx.sessionId) : '' }); } catch {}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function notConnected() {
|
|
184
|
-
return { ok: false, error: 'MT5 köprüsü bağlı değil — terminal açık mı? (panelde ⟳ ile yeniden bağlan)' };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const definitions = NAMES.map((name) => {
|
|
188
|
-
const specs = {
|
|
189
|
-
mt5_status: {
|
|
190
|
-
description:
|
|
191
|
-
'MT5 (MetaTrader 5) köprü durumunu döndürür: köprü çalışıyor mu, terminale bağlı mı, terminal/hesap özeti. Her finance turunda VEYA bağlantı sorunlarında ilk çağrı.',
|
|
192
|
-
parameters: { type: 'object', properties: {} },
|
|
193
|
-
},
|
|
194
|
-
mt5_account: {
|
|
195
|
-
description:
|
|
196
|
-
'MT5 hesap özeti: bakiye, özkaynak (equity), yüzen kâr/zarar, marj, serbest marj, marj seviyesi, kaldıraç, para birimi, hesap no/sunucu.',
|
|
197
|
-
parameters: { type: 'object', properties: {} },
|
|
198
|
-
},
|
|
199
|
-
mt5_market: {
|
|
200
|
-
description:
|
|
201
|
-
'Verilen semboller için canlı fiyat + TAM sembol sözleşme bilgisi: bid/ask, spread, digit, point, swap_long/short, komisyon, kontrat büyüklüğü, min/max/step lot, stops_level, işlem modu. symbols verilmezse izleme listesi (watchlist) kullanılır. Örn: ["EURUSD","XAUUSD"].',
|
|
202
|
-
parameters: {
|
|
203
|
-
type: 'object',
|
|
204
|
-
properties: {
|
|
205
|
-
symbols: { type: 'array', items: { type: 'string' }, description: 'Sembol listesi (boşsa watchlist)' },
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
mt5_rates: {
|
|
210
|
-
description:
|
|
211
|
-
'OHLC MUM VERİSİ: mt5_rates {symbol, timeframe:"M1|M5|M15|M30|H1|H4|D1|W1|MN1", count?}. Trend/yapı/destek-direnç analizinin HAM verisi. Varsayılan M15 × 200 mum. Formasyon/price-action için İDEAL: H1/H4 yapı + M15/M5 giriş zamanlaması.',
|
|
212
|
-
parameters: {
|
|
213
|
-
type: 'object',
|
|
214
|
-
properties: {
|
|
215
|
-
symbol: { type: 'string', description: 'Örn: XAUUSD' },
|
|
216
|
-
timeframe: { type: 'string', description: 'M1|M5|M15|M30|H1|H4|D1|W1|MN1 (varsayılan M15)' },
|
|
217
|
-
count: { type: 'number', description: 'Mum sayısı 1-1000 (varsayılan 200)' },
|
|
218
|
-
},
|
|
219
|
-
required: ['symbol'],
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
mt5_indicators: {
|
|
223
|
-
description:
|
|
224
|
-
'TEKNİK GÖSTERGELER: mt5_indicators {symbol, timeframe, count?, indicators?}. indicators örn: ["EMA(50)","EMA(200)","RSI(14)","ATR(14)","MACD(12,26,9)","BB(20,2)","STOCH(14,3,3)","SMA(200)"]. Varsayılan: EMA20/EMA50/RSI14/ATR14/MACD. ATR ile SL mesafesi/lot, RSI ile aşırı alım-satım, EMA ile trend yönü oku.',
|
|
225
|
-
parameters: {
|
|
226
|
-
type: 'object',
|
|
227
|
-
properties: {
|
|
228
|
-
symbol: { type: 'string' },
|
|
229
|
-
timeframe: { type: 'string', description: 'M1|M5|M15|M30|H1|H4|D1|W1|MN1 (varsayılan H1)' },
|
|
230
|
-
count: { type: 'number', description: 'Hesaplamaya giren mum sayısı (varsayılan 300, en fazla 1000)' },
|
|
231
|
-
indicators: {
|
|
232
|
-
type: 'array',
|
|
233
|
-
items: { type: 'string' },
|
|
234
|
-
description: 'Gösterge listesi (boşsa varsayılan set)',
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
required: ['symbol'],
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
mt5_risksize: {
|
|
241
|
-
description:
|
|
242
|
-
'RİSK BAZLI LOT HESABI: mt5_risksize {symbol, sl?, entry?, side?, riskPct?, atrMult?, timeframe?}. Bakiyenin riskPct%’i kadar kayıp verecek lotu hesaplar (SL mesafesi × tick değeri). sl yoksa atrMult verilirse ATR(14) ile SL mesafesi önerir. riskPct boşsa ayardaki işlem riski (%) kullanılır. İŞLEM AÇMADAN ÖNCE çağır; çıkan lotu mt5_trade’e ver.',
|
|
243
|
-
parameters: {
|
|
244
|
-
type: 'object',
|
|
245
|
-
properties: {
|
|
246
|
-
symbol: { type: 'string' },
|
|
247
|
-
sl: { type: 'number', description: 'Stop loss fiyatı' },
|
|
248
|
-
entry: { type: 'number', description: 'Giriş fiyatı (boşsa güncel fiyat)' },
|
|
249
|
-
side: { type: 'string', enum: ['buy', 'sell'], description: 'Yön (varsayılan buy)' },
|
|
250
|
-
riskPct: { type: 'number', description: 'Hesap riski yüzdesi (boşsa ayar)' },
|
|
251
|
-
atrMult: { type: 'number', description: 'SL yoksa: SL mesafesi = atrMult × ATR(14)' },
|
|
252
|
-
timeframe: { type: 'string', description: 'ATR zaman dilimi (varsayılan H1)' },
|
|
253
|
-
},
|
|
254
|
-
required: ['symbol'],
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
mt5_positions: {
|
|
258
|
-
description: 'Açık pozisyonlar: ticket, sembol, yön, lot, açılış fiyatı, güncel fiyat, SL/TP, yüzen kâr/zarar, açılış zamanı.',
|
|
259
|
-
parameters: { type: 'object', properties: {} },
|
|
260
|
-
},
|
|
261
|
-
mt5_orders: {
|
|
262
|
-
description: 'Bekleyen emirler (pending orders): ticket, sembol, tip (limit/stop), lot, fiyat, SL/TP.',
|
|
263
|
-
parameters: { type: 'object', properties: {} },
|
|
264
|
-
},
|
|
265
|
-
mt5_history: {
|
|
266
|
-
description: 'Kapanmış işlemler (deals) özeti: son N günde kapanan pozisyonlar, kâr/zarar, komisyon, swap. days=1 → son 24 saat.',
|
|
267
|
-
parameters: {
|
|
268
|
-
type: 'object',
|
|
269
|
-
properties: { days: { type: 'number', description: 'Kaç gün geriye bakılsın (varsayılan 1, en fazla 90)' } },
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
mt5_alerts: {
|
|
273
|
-
description:
|
|
274
|
-
'FİYAT ALARMI: mt5_alerts {action:"list"|"set"|"remove"|"clear"}. set: {symbol, price, direction:"above"|"below", mode:"once"|"repeat", note?, cooldownMin?}. ALARM MODUNU KURAN AJAN (sen) SEÇERSİN — mode zorunlu: "once" tek seferlik (ilk tetiklemede kapanır) ya da "repeat" tekrarlı (alarm açık kalır, koşul sürdükçe cooldownMin dakikada bir tekrar bildirir + sahibi ajanı uyandırır; cooldownMin varsayılan 5). Örn kritik seviye: XAUUSD 3400 üstü → mode:"repeat", cooldownMin:15; tek kere haber: mode:"once". TEMİZLİK: alarm bir bekçidir, süs değil — gereksiz alarm biriktirme; her turda list ile KENDİ alarmlarını gör, işi bitenleri (tez geçersiz, pozisyon kapandı, seviye anlamsız) clear ile SİL. clear varsayılanı YALNIZ senin kurduğun alarmlar; symbol ile daralt; ids:[...] belirli alarmlar; all:true tüm finance alarmları.',
|
|
275
|
-
parameters: {
|
|
276
|
-
type: 'object',
|
|
277
|
-
properties: {
|
|
278
|
-
action: { type: 'string', enum: ['list', 'set', 'remove', 'clear'] },
|
|
279
|
-
symbol: { type: 'string', description: 'set/clear için sembol (clear: yalnız bu sembole daralt)' },
|
|
280
|
-
price: { type: 'number', description: 'set için tetik fiyatı' },
|
|
281
|
-
direction: { type: 'string', enum: ['above', 'below'], description: 'above: fiyat ≥ price; below: fiyat ≤ price' },
|
|
282
|
-
mode: {
|
|
283
|
-
type: 'string',
|
|
284
|
-
enum: ['once', 'repeat'],
|
|
285
|
-
description: "set için ZORUNLU: 'once' tek seferlik | 'repeat' tekrarlı (kararı alarmı kuran ajan verir)",
|
|
286
|
-
},
|
|
287
|
-
note: { type: 'string', description: 'Alarm notu (bildirimde görünür)' },
|
|
288
|
-
cooldownMin: { type: 'number', description: "mode:'repeat' için iki tetik arası en az dakika (varsayılan 5, 0 = her tetiklemede, max 10080)" },
|
|
289
|
-
once: { type: 'boolean', description: "mode'a alternatif kısa yol (true = tek seferlik)" },
|
|
290
|
-
id: { type: 'string', description: 'remove için alarm id' },
|
|
291
|
-
ids: { type: 'array', items: { type: 'string' }, description: 'clear için: silinecek alarm id listesi (verilirse sahiplik aranmaz)' },
|
|
292
|
-
all: { type: 'boolean', description: 'clear için: true → sahiplik filtresini kaldır, kalan TÜM finance alarmlarını sil (varsayılan: yalnız kendi kurdukların)' },
|
|
293
|
-
},
|
|
294
|
-
required: ['action'],
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
mt5_note: {
|
|
298
|
-
description:
|
|
299
|
-
'İŞLEM GÜNLÜĞÜ NOTU: mt5_note {symbol, note, ticket?, side?}. Kararın GEREKÇESİNİ kalıcı günlüğe yaz (neden girdin/çıktın, tez, iptal koşulu). Haftalık performans raporu bu notları içerir — her önemli kararda kullan.',
|
|
300
|
-
parameters: {
|
|
301
|
-
type: 'object',
|
|
302
|
-
properties: {
|
|
303
|
-
symbol: { type: 'string' },
|
|
304
|
-
note: { type: 'string', description: 'Gerekçe/tez (kısa, net)' },
|
|
305
|
-
ticket: { type: 'number', description: 'İlgili pozisyon ticket no (varsa)' },
|
|
306
|
-
side: { type: 'string', enum: ['buy', 'sell', 'wait'] },
|
|
307
|
-
},
|
|
308
|
-
required: ['symbol', 'note'],
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
mt5_limits: {
|
|
312
|
-
description:
|
|
313
|
-
'LOT / POZİSYON / RİTİM LİMİTLERİNİ oku ve GÜNCELLE — trader karar mercii: mt5_limits {action:"get"|"set", symbol?, minLot?, maxLot?, maxPositions?, intervalSec?, intervalForMin?, reset?}. get: genel min lot / max lot / max eşzamanlı pozisyon + SEMBOL BAZLI limitler + efektif tur aralığı + işlem riski %. GENEL set: minLot/maxLot/maxPositions. SEMBOL BAZLI set: symbol ver (ör. {action:"set", symbol:"XAUUSD", minLot:0.02, maxLot:0.5}) — her sembolün min/max lotunu kendi karakterine göre SEN belirle (volatilite, spread, marj); sembol limiti genel aralığın dışına çıkamaz (genel taban/tavan kelepçeler). Sembolü genele döndürmek için {action:"set", symbol:"XAUUSD", reset:true}. TUR RİTMİ: intervalSec (30-3600 sn) + intervalForMin (1-240 dk) → geçici hızlanma; süre bitince taban aralığa OTOMATİK döner (ör. {action:"set", intervalSec:45, intervalForMin:15}). intervalForMin yoksa kalıcı temel aralık değişir. Sert hareket/haber anında hızlan, sakinleşince geri çek — SÜREKLİ en düşük aralıkta kalma (API/maliyet limiti); sınırlar sistemce zorlanır. set: yalnız ana trader (ve finance sohbet copilot\'ı) kullanabilir — rol/işçi ajanları değiştiremez. Değişiklik ANINDA panelde ve sonraki turda geçerli olur. Kural: minLot maxLot\'u aşamaz.',
|
|
314
|
-
parameters: {
|
|
315
|
-
type: 'object',
|
|
316
|
-
properties: {
|
|
317
|
-
action: { type: 'string', enum: ['get', 'set'], description: 'get: mevcut limitler · set: güncelle' },
|
|
318
|
-
symbol: { type: 'string', description: 'set: sembol bazlı lot limiti (ör. XAUUSD) — genel set için boş bırak' },
|
|
319
|
-
minLot: { type: 'number', description: 'set: yeni alt sınır (0.01-100)' },
|
|
320
|
-
maxLot: { type: 'number', description: 'set: yeni üst sınır (0.01-100)' },
|
|
321
|
-
maxPositions: { type: 'number', description: 'set: eşzamanlı pozisyon tavanı (1-20) — yalnız GENEL (sembol ile verilemez)' },
|
|
322
|
-
intervalSec: { type: 'number', description: 'set: tur aralığı saniye (30-3600) — intervalForMin ile geçici hız modu, tek başına kalıcı temel aralık' },
|
|
323
|
-
intervalForMin: { type: 'number', description: 'set: hız modu süresi dakika (1-240) — süre bitince taban aralığa otomatik döner' },
|
|
324
|
-
reset: { type: 'boolean', description: 'set + symbol: true → sembol limitini kaldır, genel aralığa dön' },
|
|
325
|
-
},
|
|
326
|
-
required: ['action'],
|
|
327
|
-
},
|
|
328
|
-
},
|
|
329
|
-
mt5_ogrenme: {
|
|
330
|
-
description:
|
|
331
|
-
'SEMBOL + ZAMAN DİLİMİ BAZLI ÖĞRENME HAFIZASI — Beast Finance sürekli öğrenir: mt5_ogrenme {action:"list"|"add"|"remove"|"clear"|"stats"|"compact"|"forget", symbol?, timeframe?, text?, kind?, tags?, id?, ids?, all?, limit?}. Her sembolün KENDİ istatistiği (işlem/kazanç/kayıp, net, kâr yakalama, kâr geri verme) OTOMATİK birikir; timeframe verilirse PERİYOT kırılımı da tutulur (M5/M15/H1… hangi periyot kazandırıyor) ve yanlışlar hata kaydı olur. ZAMAN DİLİMİ SENİN KARARIN: işlemi mt5_trade/mt5_pending timeframe ile açarsan kapanış otomatik o periyoda işlenir; istatistikler periyot bazlıdır. DERS KURALLARI (SEÇİCİ): (1) Her kapanışa ders YAZMA — yalnız gerçekten öğrenilmiş, tekrar kullanılabilir, kanıtlı içgörüyü TEK KISA cümleyle kaydet (en fazla ~20 kelime / 200 karakter); ör. "XAUUSD M5 EMA50 üstü Londra açılışı momentumu 3/4 işledi". ZARAR/STOP olan her kapanışta İSE hata dersini SEN yazmalısın: kind:"mistake", text "yanlış neydi + bundan sonra kaçınma kuralı" (ör. "M5 destek kırılımında erken girdim; kapanış teyidi beklemeliydim"). YASAK: günlük/rapor tarzı uzun metin ("bugün şunu yaptım"), genel laf ("dikkatli olmalıyım"), tekrar. (2) Yeni işlem kararından ÖNCE ilgili sembolün stats (kompakt: istatistik + periyot kırılımı + kalıcı özet + son 3 ders + son hatalar) ya da list (son 8 ders; limit ile artır) çıktısını oku; aynı hatayı tekrarlama, işleyen deseni kullan, NEGATİF periyotta riski düşür. (3) Yanlış çıkan/genel geçersiz dersi remove/clear ile sil. Aynı ders tekrar yazılamaz; sembol başına 24 saatte en fazla 3 ders kabul edilir (kalite kapısı: çok kısa/uzun, günlük tarzı ve genel tavsiye metinleri reddedilir). HAFIZA KENDİNİ SADELEŞTİRİR (opencode tarzı compaction): ham dersler birikince (24+) ESKİ dersler OTOMATİK olarak modele özetlettirilir ve tek KALICI ÖZETE sıkıştırılır — ham yalnız son dersler kalır; 30 günden eski dersler, 90 günden eski işlem kayıtları ve 120 gün hareketsiz semboller otomatik unutulur. Gerekirse compact {symbol} ile hemen özetlet, forget ile süresi geçenleri temizle. action:"list" symbol verilmezse tüm sembollerin özetini döner (periyot kırılımı + kalıcı özet dahil).',
|
|
332
|
-
parameters: {
|
|
333
|
-
type: 'object',
|
|
334
|
-
properties: {
|
|
335
|
-
action: { type: 'string', enum: ['list', 'add', 'remove', 'clear', 'stats', 'compact', 'forget'] },
|
|
336
|
-
symbol: { type: 'string', description: 'Sembol (ör. XAUUSD) — öğrenme sembol bazlıdır' },
|
|
337
|
-
timeframe: { type: 'string', description: 'Zaman dilimi (M1|M5|M15|M30|H1|H4|D1|W1|MN1) — add: dersin periyodu; list/stats: yalnız o periyodu filtrele (boş = tümü)' },
|
|
338
|
-
text: { type: 'string', description: 'add: öğrenilen ders — TEK kısa cümle (en fazla ~20 kelime/200 karakter), somut ve tekrar kullanılabilir; zarar sonrası kind:"mistake" ile "yanlış + kaçınma kuralı" yaz' },
|
|
339
|
-
kind: { type: 'string', enum: ['pattern', 'mistake', 'rule', 'observation'], description: 'add: kayıt türü (zarar/stop sonrası hata dersi → mistake)' },
|
|
340
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'add: etiketler (ör. ["scalp","london"])' },
|
|
341
|
-
id: { type: 'string', description: 'remove: kayıt id' },
|
|
342
|
-
ids: { type: 'array', items: { type: 'string' }, description: 'clear: silinecek kayıt id listesi' },
|
|
343
|
-
all: { type: 'boolean', description: 'clear: true → tüm öğrenme kayıtları (dikkatli)' },
|
|
344
|
-
limit: { type: 'number', description: 'list: döndürülecek son ders sayısı (varsayılan 8, en fazla 25)' },
|
|
345
|
-
},
|
|
346
|
-
required: ['action'],
|
|
347
|
-
},
|
|
348
|
-
},
|
|
349
|
-
mt5_ea: {
|
|
350
|
-
description:
|
|
351
|
-
'BEASTFINANCE EA KÖPRÜSÜ (grafikte çalışan uzman danışman): action:"status" → EA heartbeat (yüklü mü, AutoTrading/EA izni, equity, pozisyon, son ack); "ping" → EA canlı yanıt; "chart" → aktif grafiğin sembol/periyot/fiyat/spread bilgisi; "note" → {symbol?, text, levels:[{price,label}]} grafik panosuna not + yatay seviye çizgileri yazar. NOT: yazdığın pano ve çizgiler görsel ajanın screenshot\'ında GÖRÜNÜR — GERÇEK grafik PNG\'si için varsayılan kurulu tool__mt5_shot kullan (EA "shot" komutu; görsel ajanın gözüne gelir), seviyeleri çizdikten sonra tool__mt5_shot ile görsel doğrula; grafiği agent_dm image ile ekibe paylaş.',
|
|
352
|
-
parameters: {
|
|
353
|
-
type: 'object',
|
|
354
|
-
properties: {
|
|
355
|
-
action: { type: 'string', enum: ['status', 'ping', 'chart', 'note'] },
|
|
356
|
-
symbol: { type: 'string', description: 'note: yalnız bu sembolün grafiğine uygulanır (boş = tüm grafikler)' },
|
|
357
|
-
text: { type: 'string', description: 'note: grafik panosuna yazılacak kısa analiz/plan metni' },
|
|
358
|
-
levels: {
|
|
359
|
-
type: 'array',
|
|
360
|
-
description: 'note: yatay seviye çizgileri (destek/direnç/SL/TP)',
|
|
361
|
-
items: {
|
|
362
|
-
type: 'object',
|
|
363
|
-
properties: { price: { type: 'number' }, label: { type: 'string' } },
|
|
364
|
-
required: ['price'],
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
timeoutSec: { type: 'number', description: 'ping/chart: EA yanıt bekleme süresi sn (varsayılan 8)' },
|
|
368
|
-
},
|
|
369
|
-
required: ['action'],
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
mt5_trade: {
|
|
373
|
-
description:
|
|
374
|
-
'ANLIK PİYASA EMRİ AÇAR — ⚡HIZLI AKSİYON; bekleyen emir vermek ZORUNLU DEĞİL: mt5_trade {symbol, side:"buy"|"sell", volume?|riskPct?, sl?, tp?, timeframe?, type?, comment?, reason?}. Fırsat anıksa / teyitli kırılım-momentum-haber anında market buy/sell ile HEMEN gir — side:"buy"|"sell" yeter (type gerekmez; type:"market"|"buy_market"|"sell_market" de anlıktır). BEKLEYEN emir yalnız fiyatın bir seviyeye gelmesini beklemek gerçekten mantıklıysa kurulur: type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop" + price (bu tipler otomatik mt5_pending hattına gider). ZAMAN DİLİMİ SENİN KARARIN: işlemi hangi periyodun (M5/M15/H1…) analizine dayandırdıysan timeframe ile ver — öğrenme bu periyoda yazılır; aynı sembolde en iyi istatistik gösteren periyodu tercih et, negatif periyotta riski düşür. LOT: volume ver ya da volume yerine riskPct + sl ver — sistem SL mesafesinden lotu hesaplar (tek çağrıda giriş; ayardaki işlem riski % varsayılan). Lot limiti, max pozisyon, marj ve yoğunluk sistemce zorlanır. SL vermek ŞİDDETLİ önerilir. reason: kararın tek cümlelik tezi (günlüğe yazılır).',
|
|
375
|
-
parameters: {
|
|
376
|
-
type: 'object',
|
|
377
|
-
properties: {
|
|
378
|
-
symbol: { type: 'string', description: 'Örn: EURUSD' },
|
|
379
|
-
side: { type: 'string', enum: ['buy', 'sell'], description: 'Yön — piyasa emri için tek başına yeterli (type gerekmez)' },
|
|
380
|
-
type: {
|
|
381
|
-
type: 'string',
|
|
382
|
-
enum: ['market', 'buy_market', 'sell_market', 'buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'],
|
|
383
|
-
description: 'Emir tipi. Boş/side verilirse piyasa (anlık); limit/stop verilirse bekleyen emir (price zorunlu olur).',
|
|
384
|
-
},
|
|
385
|
-
timeframe: { type: 'string', description: 'İşlemin dayandığı periyot (M1|M5|M15|M30|H1|H4|D1|W1|MN1) — SEN seçersin; öğrenme/istatistik bu periyoda yazılır (boş = belirsiz)' },
|
|
386
|
-
volume: { type: 'number', description: 'Lot (min/max lot sınırına tabi) — boşsa riskPct + sl ile OTOMATİK hesaplanır' },
|
|
387
|
-
riskPct: { type: 'number', description: 'İşlem riski % (ör. 0.5-2) — volume yerine ver: SL mesafesinden lot hesaplanır (sl zorunlu)' },
|
|
388
|
-
price: { type: 'number', description: 'Limit/stop emirlerinde tetik fiyatı (piyasa emrinde gerekmez)' },
|
|
389
|
-
sl: { type: 'number', description: 'Stop loss fiyatı (0 = yok) — şiddetle önerilir' },
|
|
390
|
-
tp: { type: 'number', description: 'Take profit fiyatı (0 = yok)' },
|
|
391
|
-
comment: { type: 'string', description: 'Kısa işlem notu' },
|
|
392
|
-
reason: { type: 'string', description: 'Kararın tezi/gerekçesi (tek cümle)' },
|
|
393
|
-
},
|
|
394
|
-
required: ['symbol'],
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
mt5_close: {
|
|
398
|
-
description:
|
|
399
|
-
'Pozisyonu kapatır — TAM ya da KISMİ: mt5_close {ticket, percent?|volume?, kind?:"partial_tp"|"partial_sl"|"close", reason?}. KISMİ KAPATMA (kâr al = kısmi TP, zarar kes = kısmi stop) tamamen SENİN KARARIN — otomatik yapılmaz; gerek gördüğünde kullan. percent: pozisyonun % kaçı kapatılsın (ör. 50), volume: lot; kalan pozisyon SL/TP ile devam eder. Kalan lot broker minimumunun altında kalırsa tamamı kapanır. Risk yönetiminin temel aracıdır.',
|
|
400
|
-
parameters: {
|
|
401
|
-
type: 'object',
|
|
402
|
-
properties: {
|
|
403
|
-
ticket: { type: 'number', description: 'Pozisyon ticket no' },
|
|
404
|
-
percent: { type: 'number', description: 'Kapatılacak yüzde (ör. 50) — kısmi TP/kısmi stop için' },
|
|
405
|
-
volume: { type: 'number', description: 'Kapatılacak lot (percent yerine; boşsa ve percent yoksa tamamı kapanır)' },
|
|
406
|
-
kind: { type: 'string', enum: ['partial_tp', 'partial_sl', 'close'], description: 'Kısmi kapatmanın nedeni: kâr al / zarar kes (günlüğe etiket olur)' },
|
|
407
|
-
reason: { type: 'string', description: 'Kararın tezi (tek cümle, günlüğe yazılır)' },
|
|
408
|
-
},
|
|
409
|
-
required: ['ticket'],
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
mt5_modify: {
|
|
413
|
-
description: 'Pozisyonun SL/TP seviyelerini günceller: mt5_modify {ticket, sl, tp}. SL’siz pozisyon bırakmamak için kullan.',
|
|
414
|
-
parameters: {
|
|
415
|
-
type: 'object',
|
|
416
|
-
properties: {
|
|
417
|
-
ticket: { type: 'number' },
|
|
418
|
-
sl: { type: 'number', description: 'Yeni SL (0 = kaldır)' },
|
|
419
|
-
tp: { type: 'number', description: 'Yeni TP (0 = kaldır)' },
|
|
420
|
-
},
|
|
421
|
-
required: ['ticket'],
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
mt5_pending: {
|
|
425
|
-
description:
|
|
426
|
-
'BEKLEYEN EMİR (limit/stop) AÇAR: mt5_pending {symbol, type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop", volume, price, sl?, tp?, timeframe?, reason?}. Fiyatın seviyeye gelmesini beklemek için kullanılır. ⚡ANLIK giriş (hızlı aksiyon) için bunu kullanma — mt5_trade {symbol, side} ile market gir (buy_market/sell_market tipleri burada da kabul edilir ve anlık emre yönlenir). ZAMAN DİLİMİ SENİN KARARIN: timeframe ile işlemin dayandığı periyodu ver — öğrenme o periyoda yazılır. Otomatik işlem anahtarına tabidir. reason: kararın tezi (günlüğe yazılır).',
|
|
427
|
-
parameters: {
|
|
428
|
-
type: 'object',
|
|
429
|
-
properties: {
|
|
430
|
-
symbol: { type: 'string' },
|
|
431
|
-
type: {
|
|
432
|
-
type: 'string',
|
|
433
|
-
enum: ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop', 'buy_market', 'sell_market'],
|
|
434
|
-
description: 'Limit/stop = bekleyen (price zorunlu); buy_market/sell_market = anlık piyasa emri',
|
|
435
|
-
},
|
|
436
|
-
timeframe: { type: 'string', description: 'İşlemin dayandığı periyot (M1|M5|M15|M30|H1|H4|D1|W1|MN1) — SEN seçersin; öğrenme bu periyoda yazılır' },
|
|
437
|
-
volume: { type: 'number' },
|
|
438
|
-
price: { type: 'number', description: 'Tetik fiyatı — limit/stop için zorunlu, market tiplerinde gerekmez' },
|
|
439
|
-
sl: { type: 'number' },
|
|
440
|
-
tp: { type: 'number' },
|
|
441
|
-
reason: { type: 'string', description: 'Kararın tezi/gerekçesi (tek cümle)' },
|
|
442
|
-
},
|
|
443
|
-
required: ['symbol', 'type', 'volume'],
|
|
444
|
-
},
|
|
445
|
-
},
|
|
446
|
-
mt5_cancel: {
|
|
447
|
-
description: 'Bekleyen emri iptal eder: mt5_cancel {ticket}.',
|
|
448
|
-
parameters: {
|
|
449
|
-
type: 'object',
|
|
450
|
-
properties: { ticket: { type: 'number' } },
|
|
451
|
-
required: ['ticket'],
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
};
|
|
455
|
-
const s = specs[name];
|
|
456
|
-
return { type: 'function', function: { name, ...s } };
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
const handlers = {
|
|
460
|
-
async mt5_status() {
|
|
461
|
-
const st = mt5.status();
|
|
462
|
-
return { ok: true, ...st };
|
|
463
|
-
},
|
|
464
|
-
async mt5_account() {
|
|
465
|
-
if (!mt5.running) return notConnected();
|
|
466
|
-
const data = await bcall('account', {}, 8000);
|
|
467
|
-
return { ok: true, ...data };
|
|
468
|
-
},
|
|
469
|
-
async mt5_market(args) {
|
|
470
|
-
if (!mt5.running) return notConnected();
|
|
471
|
-
const cfg = getCfg();
|
|
472
|
-
let syms = Array.isArray(args.symbols) ? args.symbols.map((s) => String(s).trim()).filter(Boolean) : [];
|
|
473
|
-
if (!syms.length) syms = Array.isArray(cfg.symbols) ? cfg.symbols : [];
|
|
474
|
-
const data = await bcall('symbols', { symbols: syms }, 10000);
|
|
475
|
-
return { ok: true, symbols: data && data.symbols ? data.symbols : [] };
|
|
476
|
-
},
|
|
477
|
-
async mt5_rates(args) {
|
|
478
|
-
if (!mt5.running) return notConnected();
|
|
479
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
480
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
481
|
-
const timeframe = String(args.timeframe || 'M15').trim().toUpperCase();
|
|
482
|
-
let count = Math.round(Number(args.count) || 200);
|
|
483
|
-
count = Math.max(1, Math.min(1000, count));
|
|
484
|
-
const data = await bcall('rates', { symbol, timeframe, count }, 15000);
|
|
485
|
-
const rates = (data && data.rates) || [];
|
|
486
|
-
return { ok: true, symbol, timeframe, count: rates.length, rates };
|
|
487
|
-
},
|
|
488
|
-
async mt5_indicators(args) {
|
|
489
|
-
if (!mt5.running) return notConnected();
|
|
490
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
491
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
492
|
-
const timeframe = String(args.timeframe || 'H1').trim().toUpperCase();
|
|
493
|
-
let count = Math.round(Number(args.count) || 300);
|
|
494
|
-
count = Math.max(30, Math.min(1000, count));
|
|
495
|
-
const specs = Array.isArray(args.indicators) ? args.indicators.map((s) => String(s || '').trim()).filter(Boolean) : [];
|
|
496
|
-
const data = await bcall('rates', { symbol, timeframe, count }, 15000);
|
|
497
|
-
const rates = (data && data.rates) || [];
|
|
498
|
-
if (!rates.length) return { ok: false, error: 'mum verisi yok: ' + symbol + ' ' + timeframe };
|
|
499
|
-
const summary = finindicators.compute(rates, specs);
|
|
500
|
-
return { ok: true, symbol, timeframe, ...summary };
|
|
501
|
-
},
|
|
502
|
-
async mt5_positions() {
|
|
503
|
-
if (!mt5.running) return notConnected();
|
|
504
|
-
const data = await bcall('positions', {}, 8000);
|
|
505
|
-
const list = (data && data.positions) || [];
|
|
506
|
-
return { ok: true, count: list.length, positions: list };
|
|
507
|
-
},
|
|
508
|
-
async mt5_orders() {
|
|
509
|
-
if (!mt5.running) return notConnected();
|
|
510
|
-
const data = await bcall('orders', {}, 8000);
|
|
511
|
-
const list = (data && data.orders) || [];
|
|
512
|
-
return { ok: true, count: list.length, orders: list };
|
|
513
|
-
},
|
|
514
|
-
async mt5_history(args) {
|
|
515
|
-
if (!mt5.running) return notConnected();
|
|
516
|
-
let days = Number(args.days);
|
|
517
|
-
if (!isFinite(days) || days <= 0) days = 1;
|
|
518
|
-
days = Math.min(days, 90);
|
|
519
|
-
const data = await bcall('deals', { days }, 12000);
|
|
520
|
-
const deals = (data && data.deals) || [];
|
|
521
|
-
const stats = finstats.summarizeDeals(deals);
|
|
522
|
-
return {
|
|
523
|
-
ok: true,
|
|
524
|
-
days,
|
|
525
|
-
count: deals.length,
|
|
526
|
-
netProfit: stats.netProfit,
|
|
527
|
-
wins: stats.wins,
|
|
528
|
-
losses: stats.losses,
|
|
529
|
-
winRate: stats.winRate,
|
|
530
|
-
profitFactor: stats.profitFactor,
|
|
531
|
-
bySymbol: stats.bySymbol,
|
|
532
|
-
deals: deals.slice(-60),
|
|
533
|
-
};
|
|
534
|
-
},
|
|
535
|
-
async mt5_alerts(args, ctx) {
|
|
536
|
-
const action = String(args.action || 'list').toLowerCase();
|
|
537
|
-
if (action === 'list') {
|
|
538
|
-
/* mine:true → bu oturumun (senin) kurduğu alarm — temizlik kararı için */
|
|
539
|
-
const sid = ctx && ctx.sessionId ? String(ctx.sessionId) : '';
|
|
540
|
-
const list = alertsApi.list().map((a) => ({ ...a, mine: sid ? String(a.sid || '') === sid : !a.sid }));
|
|
541
|
-
return { ok: true, count: list.length, alerts: list };
|
|
542
|
-
}
|
|
543
|
-
if (action === 'remove') {
|
|
544
|
-
const id = String(args.id || '').trim();
|
|
545
|
-
if (!id) return { ok: false, error: 'id gerekli' };
|
|
546
|
-
const removed = alertsApi.remove(id);
|
|
547
|
-
return { ok: !!removed, removed: !!removed, id };
|
|
548
|
-
}
|
|
549
|
-
if (action === 'clear') {
|
|
550
|
-
/* ALARM TEMİZLİĞİ: alarmı kuran ajan kendi gereksiz alarmlarını siler.
|
|
551
|
-
Varsayılan kapsam: bu oturumun kurdukları (ctx.sessionId); symbol ile
|
|
552
|
-
daralt; ids açıkça verilirse sahiplik aranmaz; all:true → tümü. */
|
|
553
|
-
if (typeof alertsApi.clear !== 'function') return { ok: false, error: 'toplu alarm temizliği desteklenmiyor' };
|
|
554
|
-
const ids = Array.isArray(args.ids) ? args.ids.map((x) => String(x || '').trim()).filter(Boolean) : [];
|
|
555
|
-
const all = args.all === true || String(args.all || '').toLowerCase() === 'true' || String(args.scope || '').toLowerCase() === 'all';
|
|
556
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
557
|
-
const sid = ctx && ctx.sessionId ? String(ctx.sessionId) : '';
|
|
558
|
-
const res = alertsApi.clear({ ids, sid, all, symbol }) || {};
|
|
559
|
-
const removed = Number(res.removed) || 0;
|
|
560
|
-
if (removed > 0) noteTrade('alert-clear', { removed, ids: res.ids || [], symbol, all }, ctx || {});
|
|
561
|
-
return { ok: true, removed, ids: res.ids || [] };
|
|
562
|
-
}
|
|
563
|
-
if (action === 'set') {
|
|
564
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
565
|
-
const price = Number(args.price);
|
|
566
|
-
const direction = String(args.direction || 'above').toLowerCase();
|
|
567
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
568
|
-
if (!isFinite(price) || price <= 0) return { ok: false, error: 'price gerekli (pozitif sayı)' };
|
|
569
|
-
if (direction !== 'above' && direction !== 'below') return { ok: false, error: "direction 'above' veya 'below' olmalı" };
|
|
570
|
-
/* ALARM MODUNU KURAN AJAN SEÇER: 'once' (tek seferlik) ya da
|
|
571
|
-
'repeat' (tekrarlı; koşul sürdükçe cooldownMin'de bir). Seçim
|
|
572
|
-
yapılmadan alarm kurulamaz — sistem varsayılanına bırakılmaz. */
|
|
573
|
-
const mRaw = String(args.mode || '').toLowerCase().trim();
|
|
574
|
-
let once;
|
|
575
|
-
if (mRaw === 'once' || mRaw === 'tek' || mRaw === 'tek_seferlik' || mRaw === 'tek-seferlik') once = true;
|
|
576
|
-
else if (mRaw === 'repeat' || mRaw === 'recurring' || mRaw === 'tekrarli' || mRaw === 'surekli' || mRaw === 'always') once = false;
|
|
577
|
-
else if (args.once !== undefined) once = args.once === true || String(args.once).toLowerCase() === 'true';
|
|
578
|
-
else {
|
|
579
|
-
return {
|
|
580
|
-
ok: false,
|
|
581
|
-
error:
|
|
582
|
-
"mode gerekli — alarmı kuran ajan karar verir: mode:'once' (tek seferlik, ilk tetiklemede kapanır) ya da mode:'repeat' (tekrarlı; koşul sürdükçe cooldownMin'de bir uyarır, varsayılan 5 dk)",
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
const cdRaw = Number(args.cooldownMin);
|
|
586
|
-
const cooldownMin = Number.isFinite(cdRaw) ? Math.min(Math.max(Math.round(cdRaw), 0), 10080) : undefined;
|
|
587
|
-
const alarm = alertsApi.set({
|
|
588
|
-
symbol,
|
|
589
|
-
price,
|
|
590
|
-
direction,
|
|
591
|
-
note: String(args.note || '').slice(0, 200),
|
|
592
|
-
sid: ctx && ctx.sessionId ? String(ctx.sessionId) : '',
|
|
593
|
-
...(cooldownMin === undefined ? {} : { cooldownMin }),
|
|
594
|
-
once,
|
|
595
|
-
});
|
|
596
|
-
if (!alarm) return { ok: false, error: 'alarm kurulamadı' };
|
|
597
|
-
noteTrade('alert-set', { symbol, price, direction, id: alarm.id, note: alarm.note, cooldownMin: alarm.cooldownMin, once: alarm.once }, ctx || {});
|
|
598
|
-
return { ok: true, alert: alarm };
|
|
599
|
-
}
|
|
600
|
-
return { ok: false, error: "action: list|set|remove|clear" };
|
|
601
|
-
},
|
|
602
|
-
async mt5_note(args, ctx) {
|
|
603
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
604
|
-
const note = String(args.note || '').trim();
|
|
605
|
-
if (!symbol || !note) return { ok: false, error: 'symbol ve note gerekli' };
|
|
606
|
-
noteTrade('note', {
|
|
607
|
-
symbol,
|
|
608
|
-
note: note.slice(0, 2000),
|
|
609
|
-
ticket: Number(args.ticket) || 0,
|
|
610
|
-
side: String(args.side || '').toLowerCase(),
|
|
611
|
-
}, ctx || {});
|
|
612
|
-
return { ok: true, saved: true };
|
|
613
|
-
},
|
|
614
|
-
async mt5_limits(args, ctx) {
|
|
615
|
-
const action = String(args.action || 'get').toLowerCase();
|
|
616
|
-
if (action === 'get') {
|
|
617
|
-
return { ok: true, ...limitsApi.get() };
|
|
618
|
-
}
|
|
619
|
-
if (action === 'set') {
|
|
620
|
-
const patch = {};
|
|
621
|
-
for (const k of ['minLot', 'maxLot', 'maxPositions', 'intervalSec', 'intervalForMin']) {
|
|
622
|
-
if (args[k] !== undefined && args[k] !== null && args[k] !== '') patch[k] = Number(args[k]);
|
|
623
|
-
}
|
|
624
|
-
/* SEMBOL BAZLI LOT LİMİTİ: symbol verilirse trader o sembolün min/max
|
|
625
|
-
lotunu ayarlar (reset:true → genel aralığa döndürür) */
|
|
626
|
-
const sym = String(args.symbol || '').trim().toUpperCase();
|
|
627
|
-
if (sym) patch.symbol = sym;
|
|
628
|
-
const reset = args.reset === true || String(args.reset || '').toLowerCase() === 'true';
|
|
629
|
-
if (reset) patch.reset = true;
|
|
630
|
-
if (!Object.keys(patch).length || (sym && patch.minLot === undefined && patch.maxLot === undefined && !reset)) {
|
|
631
|
-
return { ok: false, error: 'set için en az bir alan ver: minLot / maxLot / maxPositions / intervalSec (sembol bazlıda minLot/maxLot ya da reset:true)' };
|
|
632
|
-
}
|
|
633
|
-
return await limitsApi.set(patch, ctx || {});
|
|
634
|
-
}
|
|
635
|
-
return { ok: false, error: 'action: get|set' };
|
|
636
|
-
},
|
|
637
|
-
async mt5_ogrenme(args, ctx) {
|
|
638
|
-
const action = String(args.action || 'list').toLowerCase();
|
|
639
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
640
|
-
const timeframe = normTimeframe(args.timeframe);
|
|
641
|
-
if (action === 'list') {
|
|
642
|
-
/* kompakt okuma: varsayılan son 8 ders (limit ile artırılır, max 25) —
|
|
643
|
-
hafızanın tamamı ajana gönderilmez */
|
|
644
|
-
const limit = Math.round(Number(args.limit) || 0);
|
|
645
|
-
return learningApi.list({ symbol, timeframe, limit: limit > 0 ? limit : 8 });
|
|
646
|
-
}
|
|
647
|
-
if (action === 'stats') return learningApi.stats({ symbol, timeframe });
|
|
648
|
-
if (action === 'add') {
|
|
649
|
-
const text = String(args.text || '').replace(/\s+/g, ' ').trim();
|
|
650
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli — öğrenme sembol bazlıdır' };
|
|
651
|
-
if (!text) return { ok: false, error: 'text gerekli (öğrenilen ders)' };
|
|
652
|
-
const kindRaw = String(args.kind || 'observation').toLowerCase();
|
|
653
|
-
const kind = ['pattern', 'mistake', 'rule', 'observation'].includes(kindRaw) ? kindRaw : 'observation';
|
|
654
|
-
const tags = Array.isArray(args.tags) ? args.tags.map((t) => String(t || '').trim()).filter(Boolean).slice(0, 6) : [];
|
|
655
|
-
/* metin AYNEN geçer: kalite kapısı (kısa+somut) main tarafında çalışır —
|
|
656
|
-
uzun/günlük metin sessizce kırpılmaz, ajan net hata alıp kısaltır */
|
|
657
|
-
const res = learningApi.add({ symbol, text, kind, tags, timeframe, sid: (ctx && ctx.sessionId) || '' });
|
|
658
|
-
if (res && res.ok) {
|
|
659
|
-
noteTrade('learn', { symbol, text: text.slice(0, 200), kind, timeframe }, ctx || {});
|
|
660
|
-
}
|
|
661
|
-
return res;
|
|
662
|
-
}
|
|
663
|
-
if (action === 'remove') {
|
|
664
|
-
const id = String(args.id || '').trim();
|
|
665
|
-
if (!id) return { ok: false, error: 'remove için id gerekli' };
|
|
666
|
-
return learningApi.remove({ id, symbol });
|
|
667
|
-
}
|
|
668
|
-
if (action === 'clear') {
|
|
669
|
-
const ids = Array.isArray(args.ids) ? args.ids.map((x) => String(x || '').trim()).filter(Boolean) : [];
|
|
670
|
-
const all = args.all === true || String(args.all || '').toLowerCase() === 'true';
|
|
671
|
-
return learningApi.clear({ ids, symbol, all });
|
|
672
|
-
}
|
|
673
|
-
if (action === 'compact') {
|
|
674
|
-
/* OPENCODE TARZI ÖZETLEME: eski dersler modele özetlettirilir, ham yalnız
|
|
675
|
-
son dersler kalır — bellek sadeleşir (normalde otomatik de çalışır) */
|
|
676
|
-
if (!symbol) return { ok: false, error: 'compact için symbol gerekli' };
|
|
677
|
-
if (typeof learningApi.compact !== 'function') return { ok: false, error: 'özetleme kullanılamıyor' };
|
|
678
|
-
return await learningApi.compact({ symbol });
|
|
679
|
-
}
|
|
680
|
-
if (action === 'forget') {
|
|
681
|
-
if (typeof learningApi.forget !== 'function') return { ok: false, error: 'temizleme kullanılamıyor' };
|
|
682
|
-
return learningApi.forget();
|
|
683
|
-
}
|
|
684
|
-
return { ok: false, error: 'action: list|add|remove|clear|stats|compact|forget' };
|
|
685
|
-
},
|
|
686
|
-
async mt5_ea(args) {
|
|
687
|
-
if (!mt5.running) return notConnected();
|
|
688
|
-
const action = String(args.action || 'status').toLowerCase();
|
|
689
|
-
if (action === 'status') {
|
|
690
|
-
const data = await bcall('ea_status', {}, 10000);
|
|
691
|
-
return { ok: true, ...data };
|
|
692
|
-
}
|
|
693
|
-
if (action === 'note') {
|
|
694
|
-
const levels = Array.isArray(args.levels)
|
|
695
|
-
? args.levels
|
|
696
|
-
.slice(0, 20)
|
|
697
|
-
.map((x) => ({ price: Number(x && x.price) || 0, label: String((x && x.label) || '').slice(0, 40) }))
|
|
698
|
-
.filter((x) => x.price > 0)
|
|
699
|
-
: [];
|
|
700
|
-
const data = await bcall('ea_note', {
|
|
701
|
-
symbol: String(args.symbol || '').trim().toUpperCase(),
|
|
702
|
-
text: String(args.text || '').slice(0, 4000),
|
|
703
|
-
levels,
|
|
704
|
-
}, 10000);
|
|
705
|
-
return { ok: true, ...data, note: 'Grafik panosu güncellendi — computer_look screenshot\'ında görünür' };
|
|
706
|
-
}
|
|
707
|
-
if (action === 'ping' || action === 'chart') {
|
|
708
|
-
const timeoutSec = Math.max(2, Math.min(20, Number(args.timeoutSec) || 8));
|
|
709
|
-
const data = await bcall('ea_cmd', { cmd: action, timeoutSec }, (timeoutSec + 6) * 1000);
|
|
710
|
-
return { ok: true, ...data };
|
|
711
|
-
}
|
|
712
|
-
return { ok: false, error: 'action: status|ping|chart|note' };
|
|
713
|
-
},
|
|
714
|
-
async mt5_risksize(args) {
|
|
715
|
-
if (!mt5.running) return notConnected();
|
|
716
|
-
const cfg = getCfg();
|
|
717
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
718
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
719
|
-
const info = await symInfoRow(symbol);
|
|
720
|
-
if (!info) return { ok: false, error: 'sembol bulunamadı: ' + symbol + ' (MT5 Market Watch?)' };
|
|
721
|
-
const side = String(args.side || 'buy').toLowerCase() === 'sell' ? 'sell' : 'buy';
|
|
722
|
-
const entry = Number(args.entry) || tickPrice(info, side);
|
|
723
|
-
let sl = Number(args.sl) || 0;
|
|
724
|
-
let atr = null;
|
|
725
|
-
let timeframe = '';
|
|
726
|
-
if (!(sl > 0) && Number(args.atrMult) > 0) {
|
|
727
|
-
timeframe = String(args.timeframe || 'H1').trim().toUpperCase();
|
|
728
|
-
const rd = await bcall('rates', { symbol, timeframe, count: 100 }, 15000);
|
|
729
|
-
const rates = (rd && rd.rates) || [];
|
|
730
|
-
if (rates.length >= 20) {
|
|
731
|
-
const series = finindicators.atrSeries(
|
|
732
|
-
rates.map((r) => r.high),
|
|
733
|
-
rates.map((r) => r.low),
|
|
734
|
-
rates.map((r) => r.close),
|
|
735
|
-
14
|
|
736
|
-
);
|
|
737
|
-
atr = series[series.length - 1];
|
|
738
|
-
if (atr > 0) {
|
|
739
|
-
const d = atr * Number(args.atrMult);
|
|
740
|
-
sl = side === 'buy' ? entry - d : entry + d;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
if (!(sl > 0)) return { ok: false, error: 'sl ya da atrMult gerekli' };
|
|
745
|
-
const riskPct = Number(args.riskPct) > 0 ? Number(args.riskPct) : Number(cfg.riskPerTradePct) || 0;
|
|
746
|
-
if (!(riskPct > 0)) return { ok: false, error: 'riskPct gerekli (ayarlarda işlem riski %0)' };
|
|
747
|
-
const acct = await bcall('account', {}, 8000);
|
|
748
|
-
const balance = Number(acct && acct.account && acct.account.balance) || 0;
|
|
749
|
-
if (!(balance > 0)) return { ok: false, error: 'bakiye okunamadı' };
|
|
750
|
-
const riskAmount = (balance * riskPct) / 100;
|
|
751
|
-
const range = lotRange(cfg, symbol);
|
|
752
|
-
const res = finrisk.calcRiskLot(info, entry, sl, riskAmount, range.maxLot, range.minLot);
|
|
753
|
-
if (res.error) return { ok: false, error: res.error, raw: res.raw, lossPerLot: res.lossPerLot, riskAmount: Math.round(riskAmount * 100) / 100 };
|
|
754
|
-
const digits = Number(info.digits) || 5;
|
|
755
|
-
const dist = Math.abs(entry - sl);
|
|
756
|
-
const closeErr = finrisk.stopsTooClose(info, entry, sl, 0);
|
|
757
|
-
return {
|
|
758
|
-
ok: true,
|
|
759
|
-
symbol,
|
|
760
|
-
side,
|
|
761
|
-
entry: Number(entry.toFixed(digits)),
|
|
762
|
-
sl: Number(sl.toFixed(digits)),
|
|
763
|
-
volume: res.volume,
|
|
764
|
-
riskPct,
|
|
765
|
-
riskAmount: Math.round(riskAmount * 100) / 100,
|
|
766
|
-
lossPerLot: res.lossPerLot,
|
|
767
|
-
distance: Number(dist.toFixed(digits)),
|
|
768
|
-
atr: atr != null ? Math.round(atr * 1e6) / 1e6 : null,
|
|
769
|
-
timeframe: timeframe || null,
|
|
770
|
-
capped: !!res.capped,
|
|
771
|
-
raised: !!res.raised,
|
|
772
|
-
warning: closeErr || undefined,
|
|
773
|
-
minLot: range.minLot,
|
|
774
|
-
maxLot: range.maxLot,
|
|
775
|
-
};
|
|
776
|
-
},
|
|
777
|
-
async mt5_trade(args, ctx) {
|
|
778
|
-
/* TÜM EMİR TİPLERİ: piyasa (buy/sell, buy_market/sell_market) ve
|
|
779
|
-
bekleyen (buy_limit/sell_limit/buy_stop/sell_stop) — bekleyen tip
|
|
780
|
-
verilirse mt5_pending'e devredilir (tek tutarlı risk hattı). */
|
|
781
|
-
const t = finwatch.parseOrderType(args.type || args.orderType || args.side, args.side);
|
|
782
|
-
if (!t.ok) {
|
|
783
|
-
return { ok: false, error: "side 'buy'|'sell' ya da type: buy_market|sell_market|buy_limit|sell_limit|buy_stop|sell_stop" };
|
|
784
|
-
}
|
|
785
|
-
if (t.kind === 'pending') return handlers.mt5_pending({ ...args, type: t.type }, ctx);
|
|
786
|
-
const cfg = getCfg();
|
|
787
|
-
if (!cfg.allowTrading) {
|
|
788
|
-
return { ok: false, error: 'Otomatik işlem KAPALI — analiz/öneri modundasın. İşlem önerisini yaz, açma.' };
|
|
789
|
-
}
|
|
790
|
-
if (!mt5.running) return notConnected();
|
|
791
|
-
const side = t.side;
|
|
792
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
793
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
794
|
-
/* ZAMAN DİLİMİ (bot kararı): emir yorumuna yazılır — watchdog pozisyondan
|
|
795
|
-
okur, kapanışta periyot bazlı öğrenmeye işler */
|
|
796
|
-
const timeframe = normTimeframe(args.timeframe);
|
|
797
|
-
const baseComment = String(args.comment || '').trim();
|
|
798
|
-
const orderComment = (timeframe ? `Beast ${timeframe}${baseComment ? ' ' + baseComment : ''}` : baseComment || 'Beast').slice(0, 26);
|
|
799
|
-
const range = lotRange(cfg, symbol);
|
|
800
|
-
const sl = Number(args.sl) || 0;
|
|
801
|
-
const tp = Number(args.tp) || 0;
|
|
802
|
-
const info = await symInfoRow(symbol);
|
|
803
|
-
if (!info) return { ok: false, error: 'sembol bulunamadı: ' + symbol + ' (MT5 Market Watch?)' };
|
|
804
|
-
const price = tickPrice(info, side);
|
|
805
|
-
/* LOT: riskPct verilirse (ya da volume yokken ayar riski açıksa) SL
|
|
806
|
-
mesafesinden hesaplanır; aksi halde volume broker kuralına normalize edilir */
|
|
807
|
-
const explicitRisk = Number(args.riskPct) > 0 ? Number(args.riskPct) : 0;
|
|
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;
|
|
811
|
-
let vol = 0;
|
|
812
|
-
let riskInfo = null;
|
|
813
|
-
if (explicitRisk > 0 || (!(wantVolume > 0) && Number(cfg.riskPerTradePct) > 0)) {
|
|
814
|
-
if (!(sl > 0)) return { ok: false, error: '%risk ile lot için sl zorunlu — sl ver ya da volume kullan' };
|
|
815
|
-
const riskPct = explicitRisk > 0 ? explicitRisk : Number(cfg.riskPerTradePct);
|
|
816
|
-
const acct = await bcall('account', {}, 8000);
|
|
817
|
-
const balance = Number(acct && acct.account && acct.account.balance) || 0;
|
|
818
|
-
const riskAmount = (balance * riskPct) / 100;
|
|
819
|
-
const rr = finrisk.calcRiskLot(info, price, sl, riskAmount, range.maxLot, range.minLot);
|
|
820
|
-
if (rr.error) return { ok: false, error: rr.error };
|
|
821
|
-
vol = rr.volume;
|
|
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
|
-
};
|
|
831
|
-
} else {
|
|
832
|
-
const norm = finrisk.normalizeVolume(info, wantVolume, range.maxLot, range.minLot);
|
|
833
|
-
if (norm.error) return { ok: false, error: norm.error };
|
|
834
|
-
vol = norm.volume;
|
|
835
|
-
}
|
|
836
|
-
const pos = await bcall('positions', {}, 8000);
|
|
837
|
-
const list = (pos && pos.positions) || [];
|
|
838
|
-
/* EFEKTİF TAVAN: talimattaki "en fazla N işlem/pozisyon" ayarı ezer */
|
|
839
|
-
const maxPos = Number(cfg.maxPositionsNote) > 0 ? Number(cfg.maxPositionsNote) : Number(cfg.maxPositions) || 3;
|
|
840
|
-
if (list.length >= maxPos) {
|
|
841
|
-
return { ok: false, error: `max eşzamanlı pozisyon dolu (${list.length}/${maxPos}) — önce bir pozisyon kapat` };
|
|
842
|
-
}
|
|
843
|
-
/* risk katmanı: stops_level + yoğunluk/net yön + marj kalkanı + disiplin */
|
|
844
|
-
const riskErr = await preTradeCheck(cfg, side, symbol, info, price, vol, sl, tp, list);
|
|
845
|
-
if (riskErr) return { ok: false, error: riskErr };
|
|
846
|
-
/* SHADOW MOD: gerçek emir GÖNDERİLMEZ — karar teziyle günlüğe yazılır */
|
|
847
|
-
if (cfg.shadowMode) {
|
|
848
|
-
noteTrade('shadow', {
|
|
849
|
-
symbol,
|
|
850
|
-
side,
|
|
851
|
-
volume: vol,
|
|
852
|
-
sl,
|
|
853
|
-
tp,
|
|
854
|
-
timeframe,
|
|
855
|
-
reason: String(args.reason || '').slice(0, 500),
|
|
856
|
-
risk: riskInfo,
|
|
857
|
-
}, ctx);
|
|
858
|
-
return {
|
|
859
|
-
ok: true,
|
|
860
|
-
shadow: true,
|
|
861
|
-
planned: { symbol, side, volume: vol, sl, tp, timeframe: timeframe || null },
|
|
862
|
-
risk: riskInfo,
|
|
863
|
-
note: 'SHADOW MOD: emir GÖNDERİLMEDİ, karar günlüğe yazıldı. Gerçek işlem için TRADE AJANI ayarlarından shadow modu kapat.',
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
const data = await bcall('market', {
|
|
867
|
-
symbol,
|
|
868
|
-
side,
|
|
869
|
-
volume: vol,
|
|
870
|
-
sl,
|
|
871
|
-
tp,
|
|
872
|
-
deviation: 20,
|
|
873
|
-
comment: orderComment,
|
|
874
|
-
}, 20000);
|
|
875
|
-
noteTrade('trade', { symbol, side, volume: vol, sl, tp, timeframe, risk: riskInfo, comment: String(args.comment || ''), reason: String(args.reason || '').slice(0, 500), result: data && data.result }, ctx);
|
|
876
|
-
return { ok: true, opened: { symbol, side, volume: vol, sl, tp, timeframe: timeframe || null }, risk: riskInfo, result: data && data.result };
|
|
877
|
-
},
|
|
878
|
-
async mt5_close(args, ctx) {
|
|
879
|
-
if (!mt5.running) return notConnected();
|
|
880
|
-
const ticket = Number(args.ticket);
|
|
881
|
-
if (!ticket) return { ok: false, error: 'ticket gerekli' };
|
|
882
|
-
/* KISMİ KAPATMA (kısmi TP / kısmi stop): kararı AJAN verir — otomatik
|
|
883
|
-
değildir. percent: pozisyonun % kaçı; volume: lot. Kalan pozisyon
|
|
884
|
-
SL/TP ile devam eder. */
|
|
885
|
-
const percent = Math.max(0, Math.min(100, Number(args.percent) || 0));
|
|
886
|
-
let vol = Number(args.volume) || 0;
|
|
887
|
-
let pos = null;
|
|
888
|
-
try {
|
|
889
|
-
const posRes = await bcall('positions', {}, 8000);
|
|
890
|
-
pos = ((posRes && posRes.positions) || []).find((x) => Number(x.ticket) === ticket) || null;
|
|
891
|
-
} catch {}
|
|
892
|
-
if (!pos) return { ok: false, error: 'pozisyon bulunamadı: ' + ticket };
|
|
893
|
-
const pvol = Number(pos.volume) || 0;
|
|
894
|
-
if (!(pvol > 0)) return { ok: false, error: 'pozisyon hacmi okunamadı: ' + ticket };
|
|
895
|
-
let info = null;
|
|
896
|
-
try { info = await symInfoRow(String(pos.symbol || '')); } catch {}
|
|
897
|
-
if (percent > 0 && !(vol > 0)) {
|
|
898
|
-
const norm = finrisk.normalizeVolume(info || {}, (pvol * percent) / 100);
|
|
899
|
-
if (norm.error) return { ok: false, error: norm.error };
|
|
900
|
-
vol = norm.volume;
|
|
901
|
-
}
|
|
902
|
-
if (!(vol > 0)) vol = pvol; /* hacim yok → tamamı */
|
|
903
|
-
if (vol > pvol + 1e-9) return { ok: false, error: `kapatılacak lot pozisyondan büyük (${vol} > ${pvol})` };
|
|
904
|
-
/* kalan pozisyon broker minimumunun altında kalacaksa TAMAMINI kapat */
|
|
905
|
-
const vmin = Number(info && info.volume_min) || 0.01;
|
|
906
|
-
const remain = pvol - vol;
|
|
907
|
-
if (remain > 0 && remain < vmin - 1e-9) vol = pvol;
|
|
908
|
-
const isPartial = vol < pvol - 1e-9;
|
|
909
|
-
const kind = String(args.kind || args.closeKind || '').toLowerCase();
|
|
910
|
-
const data = await bcall('close', { ticket, volume: vol }, 20000);
|
|
911
|
-
noteTrade('close', {
|
|
912
|
-
ticket,
|
|
913
|
-
volume: vol,
|
|
914
|
-
percent: percent || 0,
|
|
915
|
-
partial: isPartial,
|
|
916
|
-
closeKind: /partial_sl|kismi_stop|partial_stop/.test(kind) ? 'partial_sl' : /partial_tp|kismi_tp|partial_take/.test(kind) ? 'partial_tp' : kind,
|
|
917
|
-
reason: String(args.reason || '').slice(0, 300),
|
|
918
|
-
result: data && data.result,
|
|
919
|
-
}, ctx);
|
|
920
|
-
return { ok: true, closed: { ticket, volume: vol, partial: isPartial }, remaining: Math.round(remain * 1e8) / 1e8, result: data && data.result };
|
|
921
|
-
},
|
|
922
|
-
async mt5_modify(args, ctx) {
|
|
923
|
-
if (!mt5.running) return notConnected();
|
|
924
|
-
const ticket = Number(args.ticket);
|
|
925
|
-
if (!ticket) return { ok: false, error: 'ticket gerekli' };
|
|
926
|
-
const data = await bcall('modify', {
|
|
927
|
-
ticket,
|
|
928
|
-
sl: Number(args.sl) || 0,
|
|
929
|
-
tp: Number(args.tp) || 0,
|
|
930
|
-
}, 20000);
|
|
931
|
-
noteTrade('modify', { ticket, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0 }, ctx);
|
|
932
|
-
return { ok: true, result: data && data.result };
|
|
933
|
-
},
|
|
934
|
-
async mt5_pending(args, ctx) {
|
|
935
|
-
/* 6 TİP: buy_limit/sell_limit/buy_stop/sell_stop BEKLEYEN emir;
|
|
936
|
-
buy_market/sell_market ANLIK piyasa emri — mt5_trade'e devredilir. */
|
|
937
|
-
const t = finwatch.parseOrderType(args.type || args.orderType, args.side);
|
|
938
|
-
if (!t.ok) {
|
|
939
|
-
return { ok: false, error: 'type: buy_limit|sell_limit|buy_stop|sell_stop|buy_market|sell_market' };
|
|
940
|
-
}
|
|
941
|
-
if (t.kind === 'market') return handlers.mt5_trade({ ...args, type: 'market', orderType: '', side: t.side }, ctx);
|
|
942
|
-
const cfg = getCfg();
|
|
943
|
-
if (!cfg.allowTrading) {
|
|
944
|
-
return { ok: false, error: 'Otomatik işlem KAPALI — bekleyen emir de açılamaz. Öneriyi yaz.' };
|
|
945
|
-
}
|
|
946
|
-
if (!mt5.running) return notConnected();
|
|
947
|
-
const symbol = String(args.symbol || '').trim().toUpperCase();
|
|
948
|
-
if (!symbol) return { ok: false, error: 'symbol gerekli' };
|
|
949
|
-
/* ZAMAN DİLİMİ (bot kararı): yorumla birlikte saklanır; pozisyon aktifleşip
|
|
950
|
-
kapandığında öğrenme bu periyoda yazılır */
|
|
951
|
-
const timeframe = normTimeframe(args.timeframe);
|
|
952
|
-
const baseComment = String(args.comment || '').trim();
|
|
953
|
-
const orderComment = (timeframe ? `Beast ${timeframe}${baseComment ? ' ' + baseComment : ''}` : baseComment || 'Beast').slice(0, 26);
|
|
954
|
-
const info = await symInfoRow(symbol);
|
|
955
|
-
if (!info) return { ok: false, error: 'sembol bulunamadı: ' + symbol + ' (MT5 Market Watch?)' };
|
|
956
|
-
const range = lotRange(cfg, symbol);
|
|
957
|
-
const ptype = t.type;
|
|
958
|
-
const pside = t.side;
|
|
959
|
-
const entryPrice = Number(args.price) || 0;
|
|
960
|
-
if (!(entryPrice > 0)) {
|
|
961
|
-
return { ok: false, error: 'price gerekli — limit/stop bekleyen emirleri için tetik fiyatı ver (market tipleri anlıktır, price istemez)' };
|
|
962
|
-
}
|
|
963
|
-
/* LOT: riskPct verilirse BEKLEYEN EMİR fiyatı + SL mesafesinden hesaplanır
|
|
964
|
-
(piyasa emriyle aynı risk hattı); volume verilmişse normalize edilir. */
|
|
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;
|
|
968
|
-
let vol = 0;
|
|
969
|
-
let riskInfo = null;
|
|
970
|
-
if (explicitRisk > 0 || (!(Number(args.volume) > 0) && Number(cfg.riskPerTradePct) > 0)) {
|
|
971
|
-
const slRef = Number(args.sl) || 0;
|
|
972
|
-
if (!(slRef > 0)) return { ok: false, error: '%risk ile lot için sl zorunlu — sl ver ya da volume kullan' };
|
|
973
|
-
const riskPct = explicitRisk > 0 ? explicitRisk : Number(cfg.riskPerTradePct);
|
|
974
|
-
const acct = await bcall('account', {}, 8000);
|
|
975
|
-
const balance = Number(acct && acct.account && acct.account.balance) || 0;
|
|
976
|
-
const riskAmount = (balance * riskPct) / 100;
|
|
977
|
-
const rr = finrisk.calcRiskLot(info, entryPrice, slRef, riskAmount, range.maxLot, range.minLot);
|
|
978
|
-
if (rr.error) return { ok: false, error: rr.error };
|
|
979
|
-
vol = rr.volume;
|
|
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
|
-
};
|
|
989
|
-
} else {
|
|
990
|
-
const norm = finrisk.normalizeVolume(info, args.volume, range.maxLot, range.minLot);
|
|
991
|
-
if (norm.error) return { ok: false, error: norm.error };
|
|
992
|
-
vol = norm.volume;
|
|
993
|
-
}
|
|
994
|
-
/* KODLA DİSİPLİN: bekleyen emir de işlem sayılır */
|
|
995
|
-
let discErr = null;
|
|
996
|
-
try { discErr = discipline(pside, symbol, []); } catch {}
|
|
997
|
-
if (discErr) return { ok: false, error: discErr };
|
|
998
|
-
/* SHADOW MOD: bekleyen emir de GÖNDERİLMEZ — tez günlüğe düşer */
|
|
999
|
-
if (cfg.shadowMode) {
|
|
1000
|
-
noteTrade('shadow', { symbol, type: ptype, side: pside, volume: vol, price: entryPrice, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0, timeframe, reason: String(args.reason || '').slice(0, 500), risk: riskInfo }, ctx);
|
|
1001
|
-
return { ok: true, shadow: true, planned: { symbol, type: ptype, volume: vol, price: entryPrice, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0 }, risk: riskInfo, note: 'SHADOW MOD: bekleyen emir GÖNDERİLMEDİ, karar günlüğe yazıldı.' };
|
|
1002
|
-
}
|
|
1003
|
-
const data = await bcall('pending', {
|
|
1004
|
-
symbol,
|
|
1005
|
-
type: ptype,
|
|
1006
|
-
volume: vol,
|
|
1007
|
-
price: entryPrice,
|
|
1008
|
-
sl: Number(args.sl) || 0,
|
|
1009
|
-
tp: Number(args.tp) || 0,
|
|
1010
|
-
comment: orderComment,
|
|
1011
|
-
}, 20000);
|
|
1012
|
-
noteTrade('pending', { symbol, type: ptype, volume: vol, price: entryPrice, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0, timeframe, reason: String(args.reason || '').slice(0, 500), risk: riskInfo }, ctx);
|
|
1013
|
-
return { ok: true, ordered: { symbol, type: ptype, volume: vol, price: entryPrice, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0 }, risk: riskInfo, result: data && data.result, timeframe: timeframe || null };
|
|
1014
|
-
},
|
|
1015
|
-
async mt5_cancel(args, ctx) {
|
|
1016
|
-
if (!mt5.running) return notConnected();
|
|
1017
|
-
const ticket = Number(args.ticket);
|
|
1018
|
-
if (!ticket) return { ok: false, error: 'ticket gerekli' };
|
|
1019
|
-
const data = await bcall('cancel', { ticket }, 20000);
|
|
1020
|
-
noteTrade('cancel', { ticket }, ctx);
|
|
1021
|
-
return { ok: true, result: data && data.result };
|
|
1022
|
-
},
|
|
1023
|
-
};
|
|
1024
|
-
|
|
1025
|
-
module.exports = { definitions, handlers, NAMES, setConfig, setNotify, setAlerts, setDiscipline, setLimits, setLearning };
|