Trade Copier & Signal Broadcaster
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
import json
@dataclass
class TradeSignal:
symbol: str
direction: str # "BUY" or "SELL"
entry_price: float
stop_loss: float
take_profit: list[float] # multiple TP levels
lot_size: float
confidence: float # 0-1
setup_type: str
timeframe: str
notes: str = ""
timestamp: str = ""
def __post_init__(self):
if not self.timestamp:
self.timestamp = datetime.utcnow().isoformat()
class SignalFormatter:
"""Format signals for different distribution channels."""
@staticmethod
def telegram_format(signal: TradeSignal) -> str:
emoji = "🟢" if signal.direction == "BUY" else "🔴"
tp_lines = "\n".join([f" TP{i+1}: {tp}" for i, tp in enumerate(signal.take_profit)])
return f"""{emoji} *{signal.direction} {signal.symbol}*
━━━━━━━━━━━━━━━
📍 Entry: `{signal.entry_price}`
🛑 SL: `{signal.stop_loss}`
{tp_lines}
📊 Lots: `{signal.lot_size}`
⏱ TF: {signal.timeframe}
🎯 Setup: {signal.setup_type}
📈 Confidence: {signal.confidence*100:.0f}%
{f'📝 {signal.notes}' if signal.notes else ''}
⏰ {signal.timestamp[:16]}"""
@staticmethod
def discord_format(signal: TradeSignal) -> dict:
return {
"embeds": [{
"title": f"{'🟢' if signal.direction == 'BUY' else '🔴'} {signal.direction} {signal.symbol}",
"color": 0x00ff00 if signal.direction == "BUY" else 0xff0000,
"fields": [
{"name": "Entry", "value": str(signal.entry_price), "inline": True},
{"name": "Stop Loss", "value": str(signal.stop_loss), "inline": True},
{"name": "Take Profit", "value": " / ".join(str(tp) for tp in signal.take_profit), "inline": True},
{"name": "Lots", "value": str(signal.lot_size), "inline": True},
{"name": "Confidence", "value": f"{signal.confidence*100:.0f}%", "inline": True},
{"name": "Setup", "value": signal.setup_type, "inline": True}],
"timestamp": signal.timestamp,
}]
}
@staticmethod
def mt5_copier_format(signal: TradeSignal) -> str:
"""Format for standard MT5 trade copier protocol."""
return (f"{signal.direction},{signal.symbol},{signal.entry_price},"
f"{signal.stop_loss},{signal.take_profit[0] if signal.take_profit else 0},"
f"{signal.lot_size}")
@staticmethod
def mql5_code(signal: TradeSignal) -> str:
"""Generate MQL5 code to execute the signal."""
sl_points = abs(signal.entry_price - signal.stop_loss)
tp_points = abs(signal.take_profit[0] - signal.entry_price) if signal.take_profit else 0
fn = "Buy" if signal.direction == "BUY" else "Sell"
return f"""#include <Trade\\Trade.mqh>
CTrade trade;
void ExecuteSignal() {{
trade.{fn}({signal.lot_size}, "{signal.symbol}", 0, {signal.stop_loss}, {signal.take_profit[0] if signal.take_profit else 0}, "{signal.setup_type}");
}}"""
@staticmethod
def webhook_payload(signal: TradeSignal) -> dict:
"""Generic webhook JSON payload."""
return {
"action": signal.direction.lower(),
"symbol": signal.symbol,
"entry": signal.entry_price,
"sl": signal.stop_loss,
"tp": signal.take_profit,
"size": signal.lot_size,
"confidence": signal.confidence,
"setup": signal.setup_type,
"tf": signal.timeframe,
"timestamp": signal.timestamp,
}
class SignalBroadcaster:
"""Broadcast signals to multiple channels simultaneously."""
@staticmethod
def broadcast(signal: TradeSignal, channels: list[str]) -> dict:
results = {}
formatter = SignalFormatter
for ch in channels:
if ch == "telegram":
results["telegram"] = formatter.telegram_format(signal)
elif ch == "discord":
results["discord"] = json.dumps(formatter.discord_format(signal))
elif ch == "mt5_copier":
results["mt5_copier"] = formatter.mt5_copier_format(signal)
elif ch == "mql5":
results["mql5"] = formatter.mql5_code(signal)
elif ch == "webhook":
results["webhook"] = json.dumps(formatter.webhook_payload(signal))
return results
1---2name: trade-copier-signal-broadcaster3description: Format and distribute trading signals to MT5 copiers, Telegram bots, Discord bots, and webhook endpoints. Use this skill whenever the user asks about "trade copier", "signal service", "broadcast signals", "Telegram trading bot", "Discord signals", "webhook alerts", "copy trading", "signal distribution", "format trade signal", "MT5 copier", "send signal to Telegram", "signal channel", or any request to distribute trading signals to external systems. Works with realtime-alert-pipeline and execution-algo-trading.4---56# Trade Copier & Signal Broadcaster78```python9from dataclasses import dataclass10from datetime import datetime11from typing import Optional12import json1314@dataclass15class TradeSignal:16 symbol: str17 direction: str # "BUY" or "SELL"18 entry_price: float19 stop_loss: float20 take_profit: list[float] # multiple TP levels21 lot_size: float22 confidence: float # 0-123 setup_type: str24 timeframe: str25 notes: str = ""26 timestamp: str = ""2728 def __post_init__(self):29 if not self.timestamp:30 self.timestamp = datetime.utcnow().isoformat()3132class SignalFormatter:33 """Format signals for different distribution channels."""3435 @staticmethod36 def telegram_format(signal: TradeSignal) -> str:37 emoji = "🟢" if signal.direction == "BUY" else "🔴"38 tp_lines = "\n".join([f" TP{i+1}: {tp}" for i, tp in enumerate(signal.take_profit)])39 return f"""{emoji} *{signal.direction} {signal.symbol}*40━━━━━━━━━━━━━━━41📍 Entry: `{signal.entry_price}`42🛑 SL: `{signal.stop_loss}`43{tp_lines}44📊 Lots: `{signal.lot_size}`45⏱ TF: {signal.timeframe}46🎯 Setup: {signal.setup_type}47📈 Confidence: {signal.confidence*100:.0f}%48{f'📝 {signal.notes}' if signal.notes else ''}49⏰ {signal.timestamp[:16]}"""5051 @staticmethod52 def discord_format(signal: TradeSignal) -> dict:53 return {54 "embeds": [{55 "title": f"{'🟢' if signal.direction == 'BUY' else '🔴'} {signal.direction} {signal.symbol}",56 "color": 0x00ff00 if signal.direction == "BUY" else 0xff0000,57 "fields": [58 {"name": "Entry", "value": str(signal.entry_price), "inline": True},59 {"name": "Stop Loss", "value": str(signal.stop_loss), "inline": True},60 {"name": "Take Profit", "value": " / ".join(str(tp) for tp in signal.take_profit), "inline": True},61 {"name": "Lots", "value": str(signal.lot_size), "inline": True},62 {"name": "Confidence", "value": f"{signal.confidence*100:.0f}%", "inline": True},63 {"name": "Setup", "value": signal.setup_type, "inline": True}],64 "timestamp": signal.timestamp,65 }]66 }6768 @staticmethod69 def mt5_copier_format(signal: TradeSignal) -> str:70 """Format for standard MT5 trade copier protocol."""71 return (f"{signal.direction},{signal.symbol},{signal.entry_price},"72 f"{signal.stop_loss},{signal.take_profit[0] if signal.take_profit else 0},"73 f"{signal.lot_size}")7475 @staticmethod76 def mql5_code(signal: TradeSignal) -> str:77 """Generate MQL5 code to execute the signal."""78 sl_points = abs(signal.entry_price - signal.stop_loss)79 tp_points = abs(signal.take_profit[0] - signal.entry_price) if signal.take_profit else 080 fn = "Buy" if signal.direction == "BUY" else "Sell"81 return f"""#include <Trade\\Trade.mqh>82CTrade trade;83void ExecuteSignal() {{84 trade.{fn}({signal.lot_size}, "{signal.symbol}", 0, {signal.stop_loss}, {signal.take_profit[0] if signal.take_profit else 0}, "{signal.setup_type}");85}}"""8687 @staticmethod88 def webhook_payload(signal: TradeSignal) -> dict:89 """Generic webhook JSON payload."""90 return {91 "action": signal.direction.lower(),92 "symbol": signal.symbol,93 "entry": signal.entry_price,94 "sl": signal.stop_loss,95 "tp": signal.take_profit,96 "size": signal.lot_size,97 "confidence": signal.confidence,98 "setup": signal.setup_type,99 "tf": signal.timeframe,100 "timestamp": signal.timestamp,101 }102103class SignalBroadcaster:104 """Broadcast signals to multiple channels simultaneously."""105106 @staticmethod107 def broadcast(signal: TradeSignal, channels: list[str]) -> dict:108 results = {}109 formatter = SignalFormatter110 for ch in channels:111 if ch == "telegram":112 results["telegram"] = formatter.telegram_format(signal)113 elif ch == "discord":114 results["discord"] = json.dumps(formatter.discord_format(signal))115 elif ch == "mt5_copier":116 results["mt5_copier"] = formatter.mt5_copier_format(signal)117 elif ch == "mql5":118 results["mql5"] = formatter.mql5_code(signal)119 elif ch == "webhook":120 results["webhook"] = json.dumps(formatter.webhook_payload(signal))121 return results122```