beast-agent 2.13.0 → 2.15.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/engine.js +154 -3
- package/src/agent/financetools.js +307 -0
- package/src/agent/llm.js +3 -1
- package/src/agent/scripts/mt5_bridge.py +381 -0
- package/src/agent/tools.js +10 -3
- package/src/main.js +466 -0
- package/src/mt5bridge.js +215 -0
- package/src/preload.js +11 -0
- package/src/renderer/i18n.js +3 -1
- package/src/renderer/index.html +76 -1
- package/src/renderer/renderer.js +562 -4
- package/src/renderer/style.css +393 -1
package/src/mt5bridge.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast Finance — MT5 köprü yöneticisi (main süreç).
|
|
4
|
+
Python stdio köprüsünü (mt5_bridge.py) spawn eder; JSON satır istek/yanıt
|
|
5
|
+
ve event akışını yönetir. Python bulunamazsa aday listesinde sıradakini
|
|
6
|
+
dener; süreç düşerse backoff ile yeniden başlatır. */
|
|
7
|
+
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const { EventEmitter } = require('events');
|
|
12
|
+
|
|
13
|
+
const BUNDLED_SCRIPT = path.join(__dirname, 'agent', 'scripts', 'mt5_bridge.py');
|
|
14
|
+
|
|
15
|
+
/* Paketli uygulamada köprü scripti app.asar İÇİNDE kalır — harici Python
|
|
16
|
+
onu OKUYAMAZ. Her başlatmada %APPDATA%\beast\finance altına güncel haliyle
|
|
17
|
+
yazılır (Node asar içini okuyabilir) ve oradan çalıştırılır. */
|
|
18
|
+
function scriptPath() {
|
|
19
|
+
try {
|
|
20
|
+
const dest = path.join(process.env.APPDATA || process.env.TMP || process.cwd(), 'beast', 'finance', 'mt5_bridge.py');
|
|
21
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
22
|
+
fs.copyFileSync(BUNDLED_SCRIPT, dest);
|
|
23
|
+
return dest;
|
|
24
|
+
} catch {
|
|
25
|
+
return BUNDLED_SCRIPT;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class MT5Bridge extends EventEmitter {
|
|
30
|
+
constructor() {
|
|
31
|
+
super();
|
|
32
|
+
this.proc = null;
|
|
33
|
+
this._buf = '';
|
|
34
|
+
this._reqId = 1;
|
|
35
|
+
this._pending = new Map();
|
|
36
|
+
this._stopped = true;
|
|
37
|
+
this._backoff = 1500;
|
|
38
|
+
this._lastError = '';
|
|
39
|
+
this._terminal = null;
|
|
40
|
+
this._account = null;
|
|
41
|
+
this._candidates = ['python', 'py -3'];
|
|
42
|
+
this._candIdx = 0;
|
|
43
|
+
this._terminalPath = '';
|
|
44
|
+
this._everConnected = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get running() {
|
|
48
|
+
return !!this.proc;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
status() {
|
|
52
|
+
return {
|
|
53
|
+
running: this.running,
|
|
54
|
+
connected: !!this._terminal,
|
|
55
|
+
terminal: this._terminal,
|
|
56
|
+
account: this._account,
|
|
57
|
+
error: this._lastError,
|
|
58
|
+
python: this._candidates[this._candIdx] || '',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* opts: { candidates: ['python','py -3'], terminal: 'C:\\...\\terminal64.exe' } */
|
|
63
|
+
start(opts = {}) {
|
|
64
|
+
if (Array.isArray(opts.candidates) && opts.candidates.length) {
|
|
65
|
+
const next = opts.candidates.map((c) => String(c).trim()).filter(Boolean);
|
|
66
|
+
if (next.join('|') !== this._candidates.join('|')) {
|
|
67
|
+
this._candidates = next;
|
|
68
|
+
this._candIdx = 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this._terminalPath = String(opts.terminal || '').trim();
|
|
72
|
+
this._stopped = false;
|
|
73
|
+
if (this.running) return;
|
|
74
|
+
this._spawn();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_spawn() {
|
|
78
|
+
if (this._stopped) return;
|
|
79
|
+
const cand = this._candidates[this._candIdx] || 'python';
|
|
80
|
+
/* 'py -3' gibi çok kelimeli kısa komutlar bölünür; tam yol tek exe'dir */
|
|
81
|
+
const parts = /[\\/]/.test(cand) ? [cand] : cand.split(/\s+/);
|
|
82
|
+
const exe = parts[0];
|
|
83
|
+
const preArgs = parts.slice(1);
|
|
84
|
+
let proc;
|
|
85
|
+
try {
|
|
86
|
+
proc = spawn(exe, [...preArgs, scriptPath(), ...(this._terminalPath ? ['--terminal', this._terminalPath] : [])], {
|
|
87
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
88
|
+
windowsHide: true,
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
this._lastError = 'Python başlatılamadı: ' + String((e && e.message) || e);
|
|
92
|
+
this.emit('log', { level: 'error', line: this._lastError });
|
|
93
|
+
this._nextCandidateOrRetry();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.proc = proc;
|
|
97
|
+
this._buf = '';
|
|
98
|
+
proc.stdout.setEncoding('utf8');
|
|
99
|
+
proc.stdout.on('data', (chunk) => {
|
|
100
|
+
this._buf += chunk;
|
|
101
|
+
let i;
|
|
102
|
+
while ((i = this._buf.indexOf('\n')) >= 0) {
|
|
103
|
+
const line = this._buf.slice(0, i).trim();
|
|
104
|
+
this._buf = this._buf.slice(i + 1);
|
|
105
|
+
if (line) this._onLine(line);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
proc.stderr.setEncoding('utf8');
|
|
109
|
+
proc.stderr.on('data', (c) => {
|
|
110
|
+
const t = String(c).trim();
|
|
111
|
+
if (t) this.emit('log', { level: 'error', line: t.slice(0, 400) });
|
|
112
|
+
});
|
|
113
|
+
proc.on('error', (e) => {
|
|
114
|
+
this._lastError = `Python (${exe}) bulunamadı/başlatılamadı: ` + String((e && e.message) || e);
|
|
115
|
+
this.emit('log', { level: 'error', line: this._lastError });
|
|
116
|
+
try { this._failAll(this._lastError); } catch {}
|
|
117
|
+
if (this.proc === proc) this.proc = null;
|
|
118
|
+
this._nextCandidateOrRetry();
|
|
119
|
+
});
|
|
120
|
+
proc.on('exit', (code) => {
|
|
121
|
+
if (this.proc === proc) this.proc = null;
|
|
122
|
+
this._terminal = null;
|
|
123
|
+
this._account = null;
|
|
124
|
+
try { this._failAll('MT5 köprüsü kapandı (kod ' + code + ')'); } catch {}
|
|
125
|
+
this.emit('bridge', { connected: false, error: this._lastError || ('süreç kapandı (kod ' + code + ')') });
|
|
126
|
+
this._nextCandidateOrRetry();
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* aday python'ları sırayla dener; hepsi tükendiyse backoff ile yeniden dener */
|
|
131
|
+
_nextCandidateOrRetry() {
|
|
132
|
+
if (this._stopped) return;
|
|
133
|
+
if (this._candIdx < this._candidates.length - 1) {
|
|
134
|
+
this._candIdx += 1;
|
|
135
|
+
this.emit('log', { level: 'info', line: 'python adayı deneniyor: ' + this._candidates[this._candIdx] });
|
|
136
|
+
setTimeout(() => { try { this._spawn(); } catch {} }, 300);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this._candIdx = 0;
|
|
140
|
+
setTimeout(() => { try { if (!this._stopped && !this.running) this._spawn(); } catch {} }, this._backoff);
|
|
141
|
+
this._backoff = Math.min(this._backoff * 2, 15000);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
_onLine(line) {
|
|
145
|
+
let msg;
|
|
146
|
+
try { msg = JSON.parse(line); } catch {
|
|
147
|
+
this.emit('log', { level: 'info', line: line.slice(0, 240) });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (msg.event) {
|
|
151
|
+
if (msg.event === 'bridge') {
|
|
152
|
+
this._terminal = msg.terminal || null;
|
|
153
|
+
this._account = msg.account || null;
|
|
154
|
+
this._lastError = String(msg.error || '');
|
|
155
|
+
if (msg.connected) {
|
|
156
|
+
this._everConnected = true;
|
|
157
|
+
this._backoff = 1500;
|
|
158
|
+
}
|
|
159
|
+
this.emit('bridge', msg);
|
|
160
|
+
} else if (msg.event === 'log') {
|
|
161
|
+
this.emit('log', msg);
|
|
162
|
+
} else {
|
|
163
|
+
this.emit('event', msg);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (msg.id != null && this._pending.has(msg.id)) {
|
|
168
|
+
const en = this._pending.get(msg.id);
|
|
169
|
+
this._pending.delete(msg.id);
|
|
170
|
+
clearTimeout(en.timer);
|
|
171
|
+
en.resolve(msg);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
call(method, params, timeoutMs = 12000) {
|
|
176
|
+
if (!this.running) return Promise.resolve({ ok: false, error: 'MT5 köprüsü çalışmıyor' });
|
|
177
|
+
const id = this._reqId++;
|
|
178
|
+
let resolveFn = null;
|
|
179
|
+
const p = new Promise((resolve) => { resolveFn = resolve; });
|
|
180
|
+
const timer = setTimeout(() => {
|
|
181
|
+
this._pending.delete(id);
|
|
182
|
+
resolveFn({ ok: false, error: 'MT5 zaman aşımı: ' + method });
|
|
183
|
+
}, Math.max(2000, Number(timeoutMs) || 12000));
|
|
184
|
+
this._pending.set(id, { timer, resolve: resolveFn });
|
|
185
|
+
try {
|
|
186
|
+
this.proc.stdin.write(JSON.stringify({ id, method, params: params || {} }) + '\n');
|
|
187
|
+
} catch (e) {
|
|
188
|
+
clearTimeout(timer);
|
|
189
|
+
this._pending.delete(id);
|
|
190
|
+
resolveFn({ ok: false, error: 'köprü yazma hatası: ' + String((e && e.message) || e) });
|
|
191
|
+
}
|
|
192
|
+
return p;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
stop() {
|
|
196
|
+
this._stopped = true;
|
|
197
|
+
try { this._failAll('köprü kapatıldı'); } catch {}
|
|
198
|
+
if (this.proc) {
|
|
199
|
+
try { this.proc.kill(); } catch {}
|
|
200
|
+
this.proc = null;
|
|
201
|
+
}
|
|
202
|
+
this._terminal = null;
|
|
203
|
+
this._account = null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
_failAll(err) {
|
|
207
|
+
for (const [, en] of this._pending) {
|
|
208
|
+
clearTimeout(en.timer);
|
|
209
|
+
en.resolve({ ok: false, error: err });
|
|
210
|
+
}
|
|
211
|
+
this._pending.clear();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = new MT5Bridge();
|
package/src/preload.js
CHANGED
|
@@ -94,6 +94,17 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
94
94
|
studioTree: (rel) => ipcRenderer.invoke('studio:tree', rel),
|
|
95
95
|
studioPickVideo: () => ipcRenderer.invoke('studio:pickvideo'),
|
|
96
96
|
studioDelete: (rel) => ipcRenderer.invoke('studio:delete', rel),
|
|
97
|
+
|
|
98
|
+
financeState: () => ipcRenderer.invoke('finance:state'),
|
|
99
|
+
financeSnapshot: () => ipcRenderer.invoke('finance:snapshot'),
|
|
100
|
+
financeMode: (on, sessionId) => ipcRenderer.invoke('finance:mode', { on, sessionId }),
|
|
101
|
+
financeSettings: (patch) => ipcRenderer.invoke('finance:settings', patch),
|
|
102
|
+
financeTraderStart: () => ipcRenderer.invoke('finance:trader:start'),
|
|
103
|
+
financeTraderStop: () => ipcRenderer.invoke('finance:trader:stop'),
|
|
104
|
+
financeConnect: () => ipcRenderer.invoke('finance:connect'),
|
|
105
|
+
financeClose: (ticket, volume) => ipcRenderer.invoke('finance:close', { ticket, volume }),
|
|
106
|
+
financeInstall: () => ipcRenderer.invoke('finance:install'),
|
|
107
|
+
financeSymbolsList: (filter) => ipcRenderer.invoke('finance:symbols:list', filter),
|
|
97
108
|
clipRead: () => ipcRenderer.invoke('clip:read'),
|
|
98
109
|
clipWrite: (t) => ipcRenderer.invoke('clip:write', t),
|
|
99
110
|
thinkSet: (v) => ipcRenderer.invoke('think:set', v),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -540,6 +540,8 @@
|
|
|
540
540
|
bot_plugin_sub: 'Aktif plugin\u2019leri seç (değişiklik loglanır).',
|
|
541
541
|
bot_watcher_sub: 'Bu botun oturumlarına bağlı izleyiciler.',
|
|
542
542
|
bot_no_watchers: 'Bu bota ait izleyici yok.',
|
|
543
|
+
w_pane: 'İZLEYİCİLER',
|
|
544
|
+
w_empty: 'Kayıtlı izleyici yok.',
|
|
543
545
|
bot_stats_sub: 'Bu botun kullanım özeti.',
|
|
544
546
|
bot_stat_numbers: 'Numara',
|
|
545
547
|
bot_stat_sessions: 'Sohbet',
|
|
@@ -1035,6 +1037,7 @@
|
|
|
1035
1037
|
cr_ran: 'Started',
|
|
1036
1038
|
cr_toggle_pause: 'Pause',
|
|
1037
1039
|
w_empty: 'No saved watchers — add from the form above.',
|
|
1040
|
+
w_pane: 'WATCHERS',
|
|
1038
1041
|
wa_vazgec: 'Cancel',
|
|
1039
1042
|
wa_kaydet: 'save',
|
|
1040
1043
|
wa_updated: 'Updated',
|
|
@@ -1065,7 +1068,6 @@
|
|
|
1065
1068
|
cr_modal_empty: 'Kayıtlı cron görev yok — Ayarlar → Cron sekmesinden ekle.',
|
|
1066
1069
|
cr_ran: 'Çalıştırıldı',
|
|
1067
1070
|
cr_toggle_pause: 'Duraklat',
|
|
1068
|
-
w_empty: 'Kayıtlı izleyici yok — yukarıdaki formla ekle.',
|
|
1069
1071
|
wa_vazgec: 'Vazgeç',
|
|
1070
1072
|
wa_kaydet: 'kaydet',
|
|
1071
1073
|
wa_updated: 'Güncellendi',
|
package/src/renderer/index.html
CHANGED
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
<div id="brand"><span class="brand-beast">BEAST</span><span class="brand-sub">Agent</span></div>
|
|
14
14
|
<button id="newChat" title="Yeni sohbet" data-i18n="newChat">+ Yeni Sohbet</button>
|
|
15
15
|
<div id="sessList"></div>
|
|
16
|
+
<div id="watchPane">
|
|
17
|
+
<div id="watchPaneHead">
|
|
18
|
+
<span id="watchPaneTitle" data-i18n="w_pane">İZLEYİCİLER</span>
|
|
19
|
+
<span class="file-spacer"></span>
|
|
20
|
+
<button id="watchPaneOpen" title="İzleyicileri yönet">+</button>
|
|
21
|
+
</div>
|
|
22
|
+
<div id="watchPaneList"></div>
|
|
23
|
+
</div>
|
|
16
24
|
<div id="botBar">
|
|
17
25
|
<div id="botBarHead">
|
|
18
26
|
<span id="botBarTitle" data-i18n="bot_head">BOTLAR</span>
|
|
@@ -82,6 +90,7 @@
|
|
|
82
90
|
</div>
|
|
83
91
|
</div>
|
|
84
92
|
<button id="studioBtn" class="dd-btn dd-icon-btn" title="Beast Studio — video yapma/düzenleme" data-i18n-title="tipStudio"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="13" height="12" rx="2"/><path d="M15 10l7-4v12l-7-4"/></svg></button>
|
|
93
|
+
<button id="finBtn" class="dd-btn dd-icon-btn" title="Beast Finance — MT5 trade paneli (bilgisayardaki MetaTrader 5 ile çalışır)"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 15l4-5 3 3 5-7"/></svg></button>
|
|
85
94
|
<button id="sbBtn" class="dd-btn dd-icon-btn" title="Beast Sandbox — GitHub reposu indir & içinde çalış" data-i18n-title="tipSandbox"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></button>
|
|
86
95
|
<button id="railBtn" class="dd-btn dd-icon-btn" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">▤</button>
|
|
87
96
|
<button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">◎</button>
|
|
@@ -122,6 +131,57 @@
|
|
|
122
131
|
</div>
|
|
123
132
|
</div>
|
|
124
133
|
|
|
134
|
+
<section id="finPanel">
|
|
135
|
+
<div id="finHead">
|
|
136
|
+
<span id="finTitle">BEAST FINANCE</span>
|
|
137
|
+
<span id="finDot" class="fin-dot off" title="MT5 köprüsü"></span>
|
|
138
|
+
<span id="finTerm" title="">MT5</span>
|
|
139
|
+
<span class="file-spacer"></span>
|
|
140
|
+
<button id="finReconnect" title="MT5'e yeniden bağlan">⟳</button>
|
|
141
|
+
</div>
|
|
142
|
+
<div id="finBody">
|
|
143
|
+
<div class="fin-card">
|
|
144
|
+
<div class="fin-card-title">HESAP</div>
|
|
145
|
+
<div class="fin-grid">
|
|
146
|
+
<div class="fin-kv"><span>Bakiye</span><b id="finBalance">—</b></div>
|
|
147
|
+
<div class="fin-kv"><span>Özkaynak</span><b id="finEquity">—</b></div>
|
|
148
|
+
<div class="fin-kv"><span>Kâr/Zarar</span><b id="finProfit">—</b></div>
|
|
149
|
+
<div class="fin-kv"><span>Serbest Marj</span><b id="finFree">—</b></div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="fin-card">
|
|
153
|
+
<div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span></div>
|
|
154
|
+
<div class="fin-row"><select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select></div>
|
|
155
|
+
<div class="fin-row fin-mini">
|
|
156
|
+
<label>Tur (sn)</label><input id="finInterval" type="number" min="30" max="3600" step="10" />
|
|
157
|
+
<label>Max lot</label><input id="finMaxLot" type="number" min="0.01" max="100" step="0.01" />
|
|
158
|
+
</div>
|
|
159
|
+
<div class="fin-row fin-mini"><label>Semboller</label><button id="finSymBtn" class="fin-sym-select" title="MT5 sembol listesinden seç — tıkla">…</button></div>
|
|
160
|
+
<div class="fin-row">
|
|
161
|
+
<label class="fin-check"><input id="finAllow" type="checkbox" /> Otomatik işlem (ajan trade açabilir)</label>
|
|
162
|
+
<button id="finTraderBtn" class="fin-start">▶︎ Ajanı Başlat</button>
|
|
163
|
+
</div>
|
|
164
|
+
<div id="finTraderStatus">Kapalı</div>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="fin-card">
|
|
167
|
+
<div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span></div>
|
|
168
|
+
<div id="finPosList"><div class="fin-empty">Açık pozisyon yok</div></div>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="fin-card">
|
|
171
|
+
<div class="fin-card-title">
|
|
172
|
+
PİYASA
|
|
173
|
+
<span class="file-spacer"></span>
|
|
174
|
+
<button id="finSymAdd" title="Sembol ekle/çıkar — MT5 sembol listesinden seç">+</button>
|
|
175
|
+
</div>
|
|
176
|
+
<div id="finSymList"><div class="fin-empty">—</div></div>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="fin-card fin-grow">
|
|
179
|
+
<div class="fin-card-title">AKIŞ</div>
|
|
180
|
+
<div id="finLog"></div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</section>
|
|
184
|
+
|
|
125
185
|
<div id="ideRow">
|
|
126
186
|
<section id="codePane">
|
|
127
187
|
<div id="codeTabsBar">
|
|
@@ -177,7 +237,7 @@
|
|
|
177
237
|
</div>
|
|
178
238
|
<img id="bcQrImg" alt="Expo QR" draggable="false" />
|
|
179
239
|
<div id="bcQrUrl" title=""></div>
|
|
180
|
-
<div class="bq-hint">Telefonda <b>Expo Go</b>
|
|
240
|
+
<div class="bq-hint">Telefonda <b>Expo Go</b> ile okut — tünel modunda her ağdan çalışır</div>
|
|
181
241
|
</div>
|
|
182
242
|
<div id="bcInputRow">
|
|
183
243
|
<button id="bcAttach" title="Dosya / resim ekle" data-i18n-title="tipAttach">+</button>
|
|
@@ -468,6 +528,21 @@
|
|
|
468
528
|
</div>
|
|
469
529
|
</div>
|
|
470
530
|
|
|
531
|
+
<div id="finSymOverlay" class="mini-overlay" hidden>
|
|
532
|
+
<div class="mini-dialog fin-sym-dialog">
|
|
533
|
+
<div class="mini-head">
|
|
534
|
+
<span class="mini-title">MT5 SEMBOLLERİ</span>
|
|
535
|
+
<span id="finSymCount" class="fin-sym-count"></span>
|
|
536
|
+
<button class="mini-close" id="finSymClose" title="Kapat">×</button>
|
|
537
|
+
</div>
|
|
538
|
+
<div class="mini-body">
|
|
539
|
+
<input id="finSymSearch" placeholder="Sembol ara… (örn: XAU, BTC, EUR, US500)" spellcheck="false" autocomplete="off" />
|
|
540
|
+
<div id="finSymPick" class="mini-list"></div>
|
|
541
|
+
<div class="fin-sym-hint">Tıkla: izleme listesine ekle/çıkar — PİYASA kartına anında düşer</div>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
|
|
471
546
|
<div id="cronOverlay" class="mini-overlay" hidden>
|
|
472
547
|
<div class="mini-dialog">
|
|
473
548
|
<div class="mini-head">
|