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,575 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""Beast Finance - MetaTrader 5 stdio koprusu.
|
|
4
|
-
|
|
5
|
-
Bilgisayardaki calisan MT5 terminaline MetaTrader5 python paketiyle baglanir.
|
|
6
|
-
Stdin'den JSON satirlari okur ({id, method, params}), her istegin yanitini
|
|
7
|
-
tek JSON satiri olarak stdout'a yazar ({id, ok, data|error}).
|
|
8
|
-
Baglanti olaylarini event satirlari ile bildirir ({event: 'bridge', ...}).
|
|
9
|
-
|
|
10
|
-
Gereksinim: pip install MetaTrader5 (yalnizca Windows + MT5 terminal kurulu)
|
|
11
|
-
"""
|
|
12
|
-
import sys
|
|
13
|
-
import json
|
|
14
|
-
import time
|
|
15
|
-
import os
|
|
16
|
-
import argparse
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
import MetaTrader5 as mt5
|
|
20
|
-
except Exception:
|
|
21
|
-
mt5 = None
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def out(obj):
|
|
25
|
-
try:
|
|
26
|
-
sys.stdout.write(json.dumps(obj, ensure_ascii=False, default=str) + "\n")
|
|
27
|
-
sys.stdout.flush()
|
|
28
|
-
except Exception:
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def d(obj):
|
|
33
|
-
"""namedtuple/dict/list -> sade dict agaci (JSON'a hazir)."""
|
|
34
|
-
if obj is None:
|
|
35
|
-
return None
|
|
36
|
-
if hasattr(obj, "_asdict"):
|
|
37
|
-
return {k: d(v) for k, v in obj._asdict().items()}
|
|
38
|
-
if isinstance(obj, dict):
|
|
39
|
-
return {k: d(v) for k, v in obj.items()}
|
|
40
|
-
if isinstance(obj, (list, tuple)):
|
|
41
|
-
return [d(x) for x in obj]
|
|
42
|
-
return obj
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
TERMINAL_PATH = ""
|
|
46
|
-
INIT_TRIED = False
|
|
47
|
-
ALLOW_LAUNCH = True # Beast Finance kapaliyken False: MT5 kapaliysa ACILMAZ
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def ensure_init():
|
|
51
|
-
"""Terminal baglantisi yoksa (yeniden) init dener; True/False doner.
|
|
52
|
-
ALLOW_LAUNCH False iken terminal kapaliysa BASLATMA denenmez — Beast
|
|
53
|
-
Finance acilmadikca MT5 kendiliginden acilmaz."""
|
|
54
|
-
global INIT_TRIED
|
|
55
|
-
if mt5 is None:
|
|
56
|
-
return False
|
|
57
|
-
try:
|
|
58
|
-
if mt5.terminal_info() is not None:
|
|
59
|
-
return True
|
|
60
|
-
except Exception:
|
|
61
|
-
pass
|
|
62
|
-
if not ALLOW_LAUNCH:
|
|
63
|
-
return False
|
|
64
|
-
INIT_TRIED = True
|
|
65
|
-
if TERMINAL_PATH:
|
|
66
|
-
return bool(mt5.initialize(path=TERMINAL_PATH))
|
|
67
|
-
return bool(mt5.initialize())
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def need():
|
|
71
|
-
if mt5 is None:
|
|
72
|
-
raise RuntimeError("MetaTrader5 python paketi kurulu degil (pip install MetaTrader5)")
|
|
73
|
-
if not ensure_init():
|
|
74
|
-
if not ALLOW_LAUNCH:
|
|
75
|
-
raise RuntimeError("MT5 terminali kapali — Beast Finance acilinca otomatik baslatilir")
|
|
76
|
-
last = ""
|
|
77
|
-
try:
|
|
78
|
-
last = str(mt5.last_error())
|
|
79
|
-
except Exception:
|
|
80
|
-
pass
|
|
81
|
-
raise RuntimeError("MT5 terminaline baglanilamadi (terminal acik mi?) " + last)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# ---------------- yontemler ----------------
|
|
85
|
-
|
|
86
|
-
def h_ping(p):
|
|
87
|
-
return {"pong": True, "mt5": mt5 is not None, "connected": bool(ensure_init()) if mt5 else False, "launch": ALLOW_LAUNCH}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def h_policy(p):
|
|
91
|
-
"""Otomatik baslatma politikasi: {"launch": bool}. Beast Finance
|
|
92
|
-
kapatilinca launch=False gonderilir — MT5 kapaliysa bir daha ACILMAZ;
|
|
93
|
-
terminal aciksa cagrilar calismaya devam eder (koruma surer)."""
|
|
94
|
-
global ALLOW_LAUNCH
|
|
95
|
-
ALLOW_LAUNCH = p.get("launch") is not False
|
|
96
|
-
return {"launch": ALLOW_LAUNCH}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def h_account(p):
|
|
100
|
-
need()
|
|
101
|
-
return {"account": d(mt5.account_info())}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def h_terminal(p):
|
|
105
|
-
need()
|
|
106
|
-
return {"terminal": d(mt5.terminal_info())}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def h_symbols(p):
|
|
110
|
-
need()
|
|
111
|
-
syms = p.get("symbols") or []
|
|
112
|
-
res = []
|
|
113
|
-
for s in syms:
|
|
114
|
-
s = str(s).strip().upper()
|
|
115
|
-
if not s:
|
|
116
|
-
continue
|
|
117
|
-
info = mt5.symbol_info(s)
|
|
118
|
-
if info is None:
|
|
119
|
-
# sembol Market Watch'ta yoksa secmeyi dene
|
|
120
|
-
mt5.symbol_select(s, True)
|
|
121
|
-
info = mt5.symbol_info(s)
|
|
122
|
-
if info is None:
|
|
123
|
-
res.append({"symbol": s, "missing": True})
|
|
124
|
-
continue
|
|
125
|
-
tick = mt5.symbol_info_tick(s)
|
|
126
|
-
row = d(info)
|
|
127
|
-
row.update(d(tick) or {})
|
|
128
|
-
row["symbol"] = s
|
|
129
|
-
res.append(row)
|
|
130
|
-
return {"symbols": res}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def h_positions(p):
|
|
134
|
-
need()
|
|
135
|
-
return {"positions": d(mt5.positions_get()) or []}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def h_orders(p):
|
|
139
|
-
need()
|
|
140
|
-
return {"orders": d(mt5.orders_get()) or []}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def h_deals(p):
|
|
144
|
-
need()
|
|
145
|
-
days = p.get("days")
|
|
146
|
-
try:
|
|
147
|
-
days = float(days)
|
|
148
|
-
except Exception:
|
|
149
|
-
days = 1.0
|
|
150
|
-
days = max(0.01, min(days, 90.0))
|
|
151
|
-
to = time.time()
|
|
152
|
-
frm = to - days * 86400.0
|
|
153
|
-
deals = mt5.history_deals_get(frm, to) or []
|
|
154
|
-
rows = d(deals)
|
|
155
|
-
# sadece pozisyon giris/cikislari: entry 0 giris 1 cikis (DEAL_ENTRY_OUT)
|
|
156
|
-
return {"deals": rows[-500:], "count": len(rows)}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _send_request(req):
|
|
160
|
-
res = mt5.order_send(req)
|
|
161
|
-
if res is None:
|
|
162
|
-
raise RuntimeError("order_send None dondu " + str(mt5.last_error()))
|
|
163
|
-
r = d(res)
|
|
164
|
-
# bazi brokerlar IOC desteklemez: FOK, sonra RETURN ile birer kez tekrar dene
|
|
165
|
-
if int(r.get("retcode") or 0) == 10030 and "type_filling" in req:
|
|
166
|
-
for filling in (mt5.ORDER_FILLING_FOK, mt5.ORDER_FILLING_RETURN):
|
|
167
|
-
if req["type_filling"] == filling:
|
|
168
|
-
continue
|
|
169
|
-
req2 = dict(req)
|
|
170
|
-
req2["type_filling"] = filling
|
|
171
|
-
res = mt5.order_send(req2)
|
|
172
|
-
if res is None:
|
|
173
|
-
continue
|
|
174
|
-
r = d(res)
|
|
175
|
-
if int(r.get("retcode") or 0) in (10008, 10009):
|
|
176
|
-
break
|
|
177
|
-
if int(r.get("retcode") or 0) not in (10008, 10009): # PLACED / DONE
|
|
178
|
-
raise RuntimeError("islem reddedildi retcode=%s comment=%s" % (r.get("retcode"), r.get("comment")))
|
|
179
|
-
return r
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def h_market(p):
|
|
183
|
-
need()
|
|
184
|
-
symbol = str(p.get("symbol") or "").strip().upper()
|
|
185
|
-
side = str(p.get("side") or "").lower()
|
|
186
|
-
volume = float(p.get("volume") or 0)
|
|
187
|
-
if not symbol or side not in ("buy", "sell") or volume <= 0:
|
|
188
|
-
raise RuntimeError("symbol/side/volume zorunlu")
|
|
189
|
-
mt5.symbol_select(symbol, True)
|
|
190
|
-
tick = mt5.symbol_info_tick(symbol)
|
|
191
|
-
if tick is None:
|
|
192
|
-
raise RuntimeError("tick alinamadi: " + symbol)
|
|
193
|
-
price = tick.ask if side == "buy" else tick.bid
|
|
194
|
-
order_type = mt5.ORDER_TYPE_BUY if side == "buy" else mt5.ORDER_TYPE_SELL
|
|
195
|
-
req = {
|
|
196
|
-
"action": mt5.TRADE_ACTION_DEAL,
|
|
197
|
-
"symbol": symbol,
|
|
198
|
-
"volume": volume,
|
|
199
|
-
"type": order_type,
|
|
200
|
-
"price": price,
|
|
201
|
-
"deviation": int(p.get("deviation") or 20),
|
|
202
|
-
"magic": int(p.get("magic") or 20260908),
|
|
203
|
-
"comment": str(p.get("comment") or "Beast")[:26],
|
|
204
|
-
"type_time": mt5.ORDER_TIME_GTC,
|
|
205
|
-
"type_filling": mt5.ORDER_FILLING_IOC,
|
|
206
|
-
}
|
|
207
|
-
if p.get("sl"):
|
|
208
|
-
req["sl"] = float(p.get("sl"))
|
|
209
|
-
if p.get("tp"):
|
|
210
|
-
req["tp"] = float(p.get("tp"))
|
|
211
|
-
return {"result": _send_request(req), "price": price}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def h_close(p):
|
|
215
|
-
need()
|
|
216
|
-
ticket = int(p.get("ticket") or 0)
|
|
217
|
-
if not ticket:
|
|
218
|
-
raise RuntimeError("ticket zorunlu")
|
|
219
|
-
pos = mt5.positions_get(ticket=ticket)
|
|
220
|
-
if not pos:
|
|
221
|
-
raise RuntimeError("pozisyon bulunamadi: %s" % ticket)
|
|
222
|
-
pos = pos[0]
|
|
223
|
-
volume = float(p.get("volume") or pos.volume)
|
|
224
|
-
volume = min(volume, pos.volume)
|
|
225
|
-
symbol = pos.symbol
|
|
226
|
-
mt5.symbol_select(symbol, True)
|
|
227
|
-
tick = mt5.symbol_info_tick(symbol)
|
|
228
|
-
if tick is None:
|
|
229
|
-
raise RuntimeError("tick alinamadi: " + symbol)
|
|
230
|
-
is_buy = int(pos.type) == int(mt5.POSITION_TYPE_BUY)
|
|
231
|
-
req = {
|
|
232
|
-
"action": mt5.TRADE_ACTION_DEAL,
|
|
233
|
-
"symbol": symbol,
|
|
234
|
-
"volume": volume,
|
|
235
|
-
"type": mt5.ORDER_TYPE_SELL if is_buy else mt5.ORDER_TYPE_BUY,
|
|
236
|
-
"position": ticket,
|
|
237
|
-
"price": tick.bid if is_buy else tick.ask,
|
|
238
|
-
"deviation": int(p.get("deviation") or 20),
|
|
239
|
-
"magic": int(pos.magic or 0),
|
|
240
|
-
"comment": "Beast close",
|
|
241
|
-
"type_time": mt5.ORDER_TIME_GTC,
|
|
242
|
-
"type_filling": mt5.ORDER_FILLING_IOC,
|
|
243
|
-
}
|
|
244
|
-
return {"result": _send_request(req)}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def h_modify(p):
|
|
248
|
-
need()
|
|
249
|
-
ticket = int(p.get("ticket") or 0)
|
|
250
|
-
if not ticket:
|
|
251
|
-
raise RuntimeError("ticket zorunlu")
|
|
252
|
-
pos = mt5.positions_get(ticket=ticket)
|
|
253
|
-
if not pos:
|
|
254
|
-
raise RuntimeError("pozisyon bulunamadi: %s" % ticket)
|
|
255
|
-
pos = pos[0]
|
|
256
|
-
req = {
|
|
257
|
-
"action": mt5.TRADE_ACTION_SLTP,
|
|
258
|
-
"symbol": pos.symbol,
|
|
259
|
-
"position": ticket,
|
|
260
|
-
"sl": float(p.get("sl") or 0.0),
|
|
261
|
-
"tp": float(p.get("tp") or 0.0),
|
|
262
|
-
}
|
|
263
|
-
return {"result": _send_request(req)}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def h_pending(p):
|
|
267
|
-
need()
|
|
268
|
-
symbol = str(p.get("symbol") or "").strip().upper()
|
|
269
|
-
ptype = str(p.get("type") or "").lower()
|
|
270
|
-
volume = float(p.get("volume") or 0)
|
|
271
|
-
price = float(p.get("price") or 0)
|
|
272
|
-
if not symbol or volume <= 0 or price <= 0:
|
|
273
|
-
raise RuntimeError("symbol/volume/price zorunlu")
|
|
274
|
-
tmap = {
|
|
275
|
-
"buy_limit": mt5.ORDER_TYPE_BUY_LIMIT,
|
|
276
|
-
"sell_limit": mt5.ORDER_TYPE_SELL_LIMIT,
|
|
277
|
-
"buy_stop": mt5.ORDER_TYPE_BUY_STOP,
|
|
278
|
-
"sell_stop": mt5.ORDER_TYPE_SELL_STOP,
|
|
279
|
-
}
|
|
280
|
-
if ptype not in tmap:
|
|
281
|
-
raise RuntimeError("type: buy_limit|sell_limit|buy_stop|sell_stop")
|
|
282
|
-
mt5.symbol_select(symbol, True)
|
|
283
|
-
req = {
|
|
284
|
-
"action": mt5.TRADE_ACTION_PENDING,
|
|
285
|
-
"symbol": symbol,
|
|
286
|
-
"volume": volume,
|
|
287
|
-
"type": tmap[ptype],
|
|
288
|
-
"price": price,
|
|
289
|
-
"deviation": int(p.get("deviation") or 20),
|
|
290
|
-
"magic": int(p.get("magic") or 20260908),
|
|
291
|
-
"comment": str(p.get("comment") or "Beast")[:26],
|
|
292
|
-
"type_time": mt5.ORDER_TIME_GTC,
|
|
293
|
-
"type_filling": mt5.ORDER_FILLING_IOC,
|
|
294
|
-
}
|
|
295
|
-
if p.get("sl"):
|
|
296
|
-
req["sl"] = float(p.get("sl"))
|
|
297
|
-
if p.get("tp"):
|
|
298
|
-
req["tp"] = float(p.get("tp"))
|
|
299
|
-
return {"result": _send_request(req)}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
def h_cancel(p):
|
|
303
|
-
need()
|
|
304
|
-
ticket = int(p.get("ticket") or 0)
|
|
305
|
-
if not ticket:
|
|
306
|
-
raise RuntimeError("ticket zorunlu")
|
|
307
|
-
req = {"action": mt5.TRADE_ACTION_REMOVE, "order": ticket}
|
|
308
|
-
return {"result": _send_request(req)}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
def h_rates(p):
|
|
312
|
-
"""OHLC mumlar: symbol + timeframe (M1..MN1) + count (1..1000)."""
|
|
313
|
-
need()
|
|
314
|
-
symbol = str(p.get("symbol") or "").strip().upper()
|
|
315
|
-
if not symbol:
|
|
316
|
-
raise RuntimeError("symbol zorunlu")
|
|
317
|
-
tf_name = str(p.get("timeframe") or "M15").strip().upper()
|
|
318
|
-
tf_map = {
|
|
319
|
-
"M1": mt5.TIMEFRAME_M1, "M2": mt5.TIMEFRAME_M2, "M3": mt5.TIMEFRAME_M3,
|
|
320
|
-
"M4": mt5.TIMEFRAME_M4, "M5": mt5.TIMEFRAME_M5, "M6": mt5.TIMEFRAME_M6,
|
|
321
|
-
"M10": mt5.TIMEFRAME_M10, "M12": mt5.TIMEFRAME_M12, "M15": mt5.TIMEFRAME_M15,
|
|
322
|
-
"M20": mt5.TIMEFRAME_M20, "M30": mt5.TIMEFRAME_M30,
|
|
323
|
-
"H1": mt5.TIMEFRAME_H1, "H2": mt5.TIMEFRAME_H2, "H3": mt5.TIMEFRAME_H3,
|
|
324
|
-
"H4": mt5.TIMEFRAME_H4, "H6": mt5.TIMEFRAME_H6, "H8": mt5.TIMEFRAME_H8,
|
|
325
|
-
"H12": mt5.TIMEFRAME_H12,
|
|
326
|
-
"D1": mt5.TIMEFRAME_D1, "W1": mt5.TIMEFRAME_W1, "MN1": mt5.TIMEFRAME_MN1,
|
|
327
|
-
}
|
|
328
|
-
if tf_name not in tf_map:
|
|
329
|
-
raise RuntimeError("timeframe: " + "|".join(tf_map.keys()))
|
|
330
|
-
try:
|
|
331
|
-
count = int(p.get("count") or 200)
|
|
332
|
-
except Exception:
|
|
333
|
-
count = 200
|
|
334
|
-
count = max(1, min(count, 1000))
|
|
335
|
-
mt5.symbol_select(symbol, True)
|
|
336
|
-
rates = mt5.copy_rates_from_pos(symbol, tf_map[tf_name], 0, count)
|
|
337
|
-
if rates is None:
|
|
338
|
-
raise RuntimeError("mum verisi alinamadi: %s %s %s" % (symbol, tf_name, mt5.last_error()))
|
|
339
|
-
rows = []
|
|
340
|
-
for r in rates:
|
|
341
|
-
rows.append({
|
|
342
|
-
"time": int(r["time"]),
|
|
343
|
-
"open": float(r["open"]),
|
|
344
|
-
"high": float(r["high"]),
|
|
345
|
-
"low": float(r["low"]),
|
|
346
|
-
"close": float(r["close"]),
|
|
347
|
-
"tick_volume": int(r["tick_volume"]),
|
|
348
|
-
"spread": int(r["spread"]),
|
|
349
|
-
"real_volume": int(r["real_volume"]),
|
|
350
|
-
})
|
|
351
|
-
return {"symbol": symbol, "timeframe": tf_name, "count": len(rows), "rates": rows}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
def h_margin(p):
|
|
355
|
-
"""order_calc_margin: tahmini gerekli marj (islem oncesi kalkan icin)."""
|
|
356
|
-
need()
|
|
357
|
-
symbol = str(p.get("symbol") or "").strip().upper()
|
|
358
|
-
side = str(p.get("side") or "buy").lower()
|
|
359
|
-
volume = float(p.get("volume") or 0)
|
|
360
|
-
if not symbol or volume <= 0 or side not in ("buy", "sell"):
|
|
361
|
-
raise RuntimeError("symbol/side/volume zorunlu")
|
|
362
|
-
mt5.symbol_select(symbol, True)
|
|
363
|
-
price = float(p.get("price") or 0)
|
|
364
|
-
if price <= 0:
|
|
365
|
-
tick = mt5.symbol_info_tick(symbol)
|
|
366
|
-
if tick is None:
|
|
367
|
-
raise RuntimeError("tick alinamadi: " + symbol)
|
|
368
|
-
price = tick.ask if side == "buy" else tick.bid
|
|
369
|
-
order_type = mt5.ORDER_TYPE_BUY if side == "buy" else mt5.ORDER_TYPE_SELL
|
|
370
|
-
m = mt5.order_calc_margin(order_type, symbol, volume, price)
|
|
371
|
-
return {"margin": (float(m) if m is not None else None), "price": price}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
def h_all_symbols(p):
|
|
375
|
-
"""Terminaldeki tum semboller (sembol secici icin). filter: '*XAU*' gibi."""
|
|
376
|
-
need()
|
|
377
|
-
flt = str(p.get("filter") or "").strip()
|
|
378
|
-
if flt:
|
|
379
|
-
rows = mt5.symbols_get(group="*" + flt + "*") or []
|
|
380
|
-
else:
|
|
381
|
-
rows = mt5.symbols_get() or []
|
|
382
|
-
cap = 8000
|
|
383
|
-
out = []
|
|
384
|
-
for r in rows[:cap]:
|
|
385
|
-
out.append({
|
|
386
|
-
"name": getattr(r, "name", "") or "",
|
|
387
|
-
"desc": getattr(r, "description", "") or "",
|
|
388
|
-
"visible": bool(getattr(r, "visible", False)),
|
|
389
|
-
})
|
|
390
|
-
return {"symbols": out, "count": len(rows), "truncated": len(rows) > cap}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
# ---------------- BeastFinance EA kopru dosyalari ----------------
|
|
394
|
-
|
|
395
|
-
def _files_dir():
|
|
396
|
-
"""MQL5\\Files sandbox klasoru (EA ile ortak). Terminal baglantisi sart."""
|
|
397
|
-
need()
|
|
398
|
-
dp = ""
|
|
399
|
-
try:
|
|
400
|
-
ti = mt5.terminal_info()
|
|
401
|
-
dp = getattr(ti, "data_path", "") if ti is not None else ""
|
|
402
|
-
except Exception:
|
|
403
|
-
dp = ""
|
|
404
|
-
if not dp:
|
|
405
|
-
raise RuntimeError("terminal data_path alinamadi")
|
|
406
|
-
d = os.path.join(dp, "MQL5", "Files")
|
|
407
|
-
os.makedirs(d, exist_ok=True)
|
|
408
|
-
return d
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
def _read_json_file(p):
|
|
412
|
-
"""EA dosyalari UTF-16 (FILE_UNICODE) ya da UTF-8 olabilir — BOM'a gore coz."""
|
|
413
|
-
try:
|
|
414
|
-
with open(p, "rb") as f:
|
|
415
|
-
buf = f.read()
|
|
416
|
-
if not buf:
|
|
417
|
-
return None
|
|
418
|
-
if buf[:2] in (b"\xff\xfe", b"\xfe\xff"):
|
|
419
|
-
raw = buf.decode("utf-16")
|
|
420
|
-
elif buf[:3] == b"\xef\xbb\xbf":
|
|
421
|
-
raw = buf.decode("utf-8-sig")
|
|
422
|
-
else:
|
|
423
|
-
try:
|
|
424
|
-
raw = buf.decode("utf-8")
|
|
425
|
-
except UnicodeDecodeError:
|
|
426
|
-
raw = buf.decode("utf-16")
|
|
427
|
-
if "\x00" in raw: # BOM'suz UTF-16 (ASCII icerik)
|
|
428
|
-
raw = buf.decode("utf-16")
|
|
429
|
-
raw = raw.strip()
|
|
430
|
-
return json.loads(raw) if raw else None
|
|
431
|
-
except Exception:
|
|
432
|
-
return None
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
def _write_json_file(p, obj):
|
|
436
|
-
"""EA tarafi FILE_UNICODE okudugu icin UTF-16 (BOM'lu) yaz."""
|
|
437
|
-
with open(p, "wb") as f:
|
|
438
|
-
f.write(json.dumps(obj, ensure_ascii=False).encode("utf-16"))
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
def h_ea_status(p):
|
|
442
|
-
"""Grafikteki BeastFinance EA'nin heartbeat durumu (yuklu mu + izinler)."""
|
|
443
|
-
d = _files_dir()
|
|
444
|
-
beat = _read_json_file(os.path.join(d, "beast_ea.json"))
|
|
445
|
-
ack = _read_json_file(os.path.join(d, "beast_cmd_ack.json"))
|
|
446
|
-
note = _read_json_file(os.path.join(d, "beast_note.json"))
|
|
447
|
-
if beat is None:
|
|
448
|
-
return {"installed": False, "files_dir": d}
|
|
449
|
-
return {"installed": True, "files_dir": d, "ea": beat, "last_ack": ack, "note": note}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
def h_ea_note(p):
|
|
453
|
-
"""Grafik panosu + seviye cizgileri: beast_note.json yazar (EA bir sonraki
|
|
454
|
-
timer turunda okur; screenshot/entegrasyon icin)."""
|
|
455
|
-
d = _files_dir()
|
|
456
|
-
note = {
|
|
457
|
-
"symbol": str(p.get("symbol") or ""),
|
|
458
|
-
"text": str(p.get("text") or "")[:4000],
|
|
459
|
-
"levels": [],
|
|
460
|
-
"at": int(time.time()),
|
|
461
|
-
}
|
|
462
|
-
lv = p.get("levels")
|
|
463
|
-
if isinstance(lv, list):
|
|
464
|
-
for x in lv[:20]:
|
|
465
|
-
try:
|
|
466
|
-
price = float(x.get("price"))
|
|
467
|
-
except Exception:
|
|
468
|
-
continue
|
|
469
|
-
if price > 0:
|
|
470
|
-
note["levels"].append({"price": price, "label": str(x.get("label") or "")[:40]})
|
|
471
|
-
path = os.path.join(d, "beast_note.json")
|
|
472
|
-
_write_json_file(path, note)
|
|
473
|
-
return {"ok": True, "path": path, "levels": len(note["levels"])}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
def h_ea_cmd(p):
|
|
477
|
-
"""EA'ya komut yaz (ping|chart|status|note) ve ack bekle."""
|
|
478
|
-
d = _files_dir()
|
|
479
|
-
cmd = str(p.get("cmd") or "").strip()
|
|
480
|
-
if not cmd:
|
|
481
|
-
raise RuntimeError("cmd gerekli (ping|chart|status|note)")
|
|
482
|
-
rid = str(p.get("id") or ("c" + str(int(time.time() * 1000) % 100000000)))
|
|
483
|
-
payload = {"id": rid, "cmd": cmd, "params": p.get("params") or {}}
|
|
484
|
-
ack_path = os.path.join(d, "beast_cmd_ack.json")
|
|
485
|
-
try:
|
|
486
|
-
os.remove(ack_path)
|
|
487
|
-
except Exception:
|
|
488
|
-
pass
|
|
489
|
-
with open(os.path.join(d, "beast_cmd.json"), "wb") as f:
|
|
490
|
-
f.write(json.dumps(payload, ensure_ascii=False).encode("utf-16"))
|
|
491
|
-
timeout = float(p.get("timeoutSec") or 8)
|
|
492
|
-
t0 = time.time()
|
|
493
|
-
while time.time() - t0 < max(1.0, min(timeout, 30.0)):
|
|
494
|
-
time.sleep(0.25)
|
|
495
|
-
ack = _read_json_file(ack_path)
|
|
496
|
-
if ack and str(ack.get("id") or "") == rid:
|
|
497
|
-
return {"ok": True, "ack": ack, "waited": round(time.time() - t0, 2)}
|
|
498
|
-
return {"ok": False, "error": "EA yanit vermedi (grafikte BeastFinance yuklu mu?)", "sent": payload}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
HANDLERS = {
|
|
502
|
-
"ping": h_ping,
|
|
503
|
-
"policy": h_policy,
|
|
504
|
-
"account": h_account,
|
|
505
|
-
"terminal": h_terminal,
|
|
506
|
-
"symbols": h_symbols,
|
|
507
|
-
"all_symbols": h_all_symbols,
|
|
508
|
-
"rates": h_rates,
|
|
509
|
-
"positions": h_positions,
|
|
510
|
-
"orders": h_orders,
|
|
511
|
-
"deals": h_deals,
|
|
512
|
-
"market": h_market,
|
|
513
|
-
"close": h_close,
|
|
514
|
-
"modify": h_modify,
|
|
515
|
-
"pending": h_pending,
|
|
516
|
-
"cancel": h_cancel,
|
|
517
|
-
"margin": h_margin,
|
|
518
|
-
"ea_status": h_ea_status,
|
|
519
|
-
"ea_cmd": h_ea_cmd,
|
|
520
|
-
"ea_note": h_ea_note,
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
def main():
|
|
525
|
-
global TERMINAL_PATH
|
|
526
|
-
ap = argparse.ArgumentParser()
|
|
527
|
-
ap.add_argument("--terminal", default="")
|
|
528
|
-
args, _ = ap.parse_known_args()
|
|
529
|
-
TERMINAL_PATH = args.terminal
|
|
530
|
-
|
|
531
|
-
for line in sys.stdin:
|
|
532
|
-
line = line.strip()
|
|
533
|
-
if not line:
|
|
534
|
-
continue
|
|
535
|
-
try:
|
|
536
|
-
req = json.loads(line)
|
|
537
|
-
except Exception as e:
|
|
538
|
-
out({"id": None, "ok": False, "error": "bozuk json: %s" % e})
|
|
539
|
-
continue
|
|
540
|
-
rid = req.get("id")
|
|
541
|
-
method = str(req.get("method") or "")
|
|
542
|
-
params = req.get("params") or {}
|
|
543
|
-
fn = HANDLERS.get(method)
|
|
544
|
-
if fn is None:
|
|
545
|
-
out({"id": rid, "ok": False, "error": "bilinmeyen yontem: " + method})
|
|
546
|
-
continue
|
|
547
|
-
try:
|
|
548
|
-
data = fn(params)
|
|
549
|
-
out({"id": rid, "ok": True, "data": data})
|
|
550
|
-
except Exception as e:
|
|
551
|
-
out({"id": rid, "ok": False, "error": str(e)})
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if __name__ == "__main__":
|
|
555
|
-
if mt5 is None:
|
|
556
|
-
out({"event": "bridge", "connected": False, "error": "MetaTrader5 python paketi kurulu degil (pip install MetaTrader5)"})
|
|
557
|
-
else:
|
|
558
|
-
ok = False
|
|
559
|
-
err = ""
|
|
560
|
-
try:
|
|
561
|
-
if TERMINAL_PATH:
|
|
562
|
-
ok = bool(mt5.initialize(path=TERMINAL_PATH))
|
|
563
|
-
else:
|
|
564
|
-
ok = bool(mt5.initialize())
|
|
565
|
-
if not ok:
|
|
566
|
-
err = str(mt5.last_error())
|
|
567
|
-
except Exception as e:
|
|
568
|
-
err = str(e)
|
|
569
|
-
acc = None
|
|
570
|
-
term = None
|
|
571
|
-
if ok:
|
|
572
|
-
acc = d(mt5.account_info())
|
|
573
|
-
term = d(mt5.terminal_info())
|
|
574
|
-
out({"event": "bridge", "connected": ok, "error": "" if ok else err, "account": acc, "terminal": term})
|
|
575
|
-
main()
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: haber-duygu
|
|
3
|
-
description: Haber ve piyasa duyarlılığı rehberi — ekonomik takvim riskleri (NFP/CPI/FOMC/faiz), haber öncesi-sonrası pozisyon disiplini, DXY/faiz/VIX/Fear&Greed okuma, risk-on/risk-off ve emtia bağıntıları. Makro/haber ajanı veya trader haber etkisi değerlendirirken ÖNCE bu skill'i okur.
|
|
4
|
-
version: 1.0.0
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Haber & Piyasa Duyarlılığı
|
|
8
|
-
|
|
9
|
-
## TEMEL İLKE
|
|
10
|
-
|
|
11
|
-
Haber yön tahmini için değil, **risk zamanlaması** için kullanılır. Yüksek etkili veri saatinde piyasa spreadi genişler ve yön belirsizdir; çoğu zaman en iyi işlem beklemektir.
|
|
12
|
-
|
|
13
|
-
## 1) EKONOMİK TAKVİM
|
|
14
|
-
|
|
15
|
-
- Yüksek etkili: FOMC/faiz kararları, NFP (tarım dışı istihdam), CPI/PPI, GDP, işsizlik, ECB/BOE/BOJ kararları, jeopolitik başlıklar.
|
|
16
|
-
- Kural: yüksek etkili veriden **15–30 dk önce yeni pozisyon açma**; mevcut pozisyonu gözden geçir (SL sabit, panik kapatma yok).
|
|
17
|
-
- Veri sonrası: ilk 5–15 dk fitil/spike olur; kapanış teyidi gelmeden işlem açma.
|
|
18
|
-
- Takvimi `web_search`/`deep_search` ile teyit et; tarih/saati yerel saatle yaz (sunucu saati farkını belirt).
|
|
19
|
-
|
|
20
|
-
## 2) DUYGU GÖSTERGELERİ
|
|
21
|
-
|
|
22
|
-
- **DXY**: dolar gücü — yükselirse altın ve EURUSD genelde baskılanır (ters korelasyon).
|
|
23
|
-
- **ABD 10Y tahvil faizi**: faiz yükseliyorsa risk iştahı azalabilir, altın baskılanabilir.
|
|
24
|
-
- **VIX**: yükseliyorsa risk-off; düşüyorsa risk-on.
|
|
25
|
-
- **Kripto Fear & Greed**: yalnız BTC/ETH gibi kripto pozisyonlarında; hisse/emtia için ana referans değil.
|
|
26
|
-
- Aşırı iyimserlik (greed) ve aşırı korku (fear) uçlarda dönüş riskidir; trend takipçisi için tek başına sinyal değildir.
|
|
27
|
-
|
|
28
|
-
## 3) RİSK-ON / RİSK-OFF
|
|
29
|
-
|
|
30
|
-
- Risk-on: hisse ↑, emtia ↑, dolar/altın baskılı olabilir.
|
|
31
|
-
- Risk-off: DXY/altın/tahvil ↑, hisse/kripto ↓.
|
|
32
|
-
- Makro başlık bölgeyi tersine çevirdiyse mevcut teknik planı **güncelle**, körlemesine uygulama.
|
|
33
|
-
|
|
34
|
-
## 4) HABER DOĞRULAMA
|
|
35
|
-
|
|
36
|
-
- Tek kaynağa güvenme; 2+ bağımsız kaynak teyidi ara.
|
|
37
|
-
- Manşet ile içerik çelişirse içeriği esas al; tarih/saat eski haberi taze sanma.
|
|
38
|
-
- Söylenti (rumor) ve resmi açıklamayı ayır; söylentiye işlem açma.
|
|
39
|
-
|
|
40
|
-
## 5) BAĞINTI NOTLARI
|
|
41
|
-
|
|
42
|
-
- XAUUSD ile DXY ters korele; aynı yönde iki korelasyonlu pozisyon açma (bkz. risk-yonetimi).
|
|
43
|
-
- Petrol haberleri (OPEC, arz kesintisi) enerji sembollerini ve genel risk iştahını etkiler.
|
|
44
|
-
- Emtia sembollerinde broker sembol adını kullan (ör. "GOLD", "XAUUSD" değil) — `mt5_market` ile doğrula.
|
|
45
|
-
|
|
46
|
-
## 6) RAPOR FORMATI
|
|
47
|
-
|
|
48
|
-
`Takvim: <saat> <veri> (yüksek) | duygu: risk-on/off | DXY yön | sembol etkisi: <yön/temkin> | aksiyon: bekle/azalt/normal`
|
|
49
|
-
Veri saatine 30 dk'dan az varsa bunu raporda AÇIKÇA belirt.
|