FTSAAIMT5C 0.1.0__tar.gz
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.
- ftsaaimt5c-0.1.0/FTSAAIMT5C.egg-info/PKG-INFO +23 -0
- ftsaaimt5c-0.1.0/FTSAAIMT5C.egg-info/SOURCES.txt +12 -0
- ftsaaimt5c-0.1.0/FTSAAIMT5C.egg-info/dependency_links.txt +1 -0
- ftsaaimt5c-0.1.0/FTSAAIMT5C.egg-info/entry_points.txt +3 -0
- ftsaaimt5c-0.1.0/FTSAAIMT5C.egg-info/requires.txt +5 -0
- ftsaaimt5c-0.1.0/FTSAAIMT5C.egg-info/top_level.txt +1 -0
- ftsaaimt5c-0.1.0/PKG-INFO +23 -0
- ftsaaimt5c-0.1.0/README.md +9 -0
- ftsaaimt5c-0.1.0/ftsaaipkg/__init__.py +0 -0
- ftsaaimt5c-0.1.0/ftsaaipkg/bridge.py +84 -0
- ftsaaimt5c-0.1.0/ftsaaipkg/cli.py +158 -0
- ftsaaimt5c-0.1.0/ftsaaipkg/exit_cli.py +16 -0
- ftsaaimt5c-0.1.0/setup.cfg +4 -0
- ftsaaimt5c-0.1.0/setup.py +35 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FTSAAIMT5C
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FTSA AI MT5 Connector CLI
|
|
5
|
+
Author: Kelvin Mburu
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
10
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
11
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: pymongo>=4.3.3
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: MetaTrader5>=5.0.5640
|
|
17
|
+
Requires-Dist: click>=8.0.0
|
|
18
|
+
Requires-Dist: requests>=2.32.5
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
FTSAAIMT5C.egg-info/PKG-INFO
|
|
4
|
+
FTSAAIMT5C.egg-info/SOURCES.txt
|
|
5
|
+
FTSAAIMT5C.egg-info/dependency_links.txt
|
|
6
|
+
FTSAAIMT5C.egg-info/entry_points.txt
|
|
7
|
+
FTSAAIMT5C.egg-info/requires.txt
|
|
8
|
+
FTSAAIMT5C.egg-info/top_level.txt
|
|
9
|
+
ftsaaipkg/__init__.py
|
|
10
|
+
ftsaaipkg/bridge.py
|
|
11
|
+
ftsaaipkg/cli.py
|
|
12
|
+
ftsaaipkg/exit_cli.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ftsaaipkg
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FTSAAIMT5C
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FTSA AI MT5 Connector CLI
|
|
5
|
+
Author: Kelvin Mburu
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
10
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
11
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: pymongo>=4.3.3
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: MetaTrader5>=5.0.5640
|
|
17
|
+
Requires-Dist: click>=8.0.0
|
|
18
|
+
Requires-Dist: requests>=2.32.5
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# ftsaaipkg/bridge.py
|
|
2
|
+
|
|
3
|
+
import MetaTrader5 as mt5
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def connect_mt5(login: str, password: str, server: str, platform: str = "MT5") -> bool:
|
|
8
|
+
"""
|
|
9
|
+
Connects to MT5 using the provided credentials.
|
|
10
|
+
Returns True if connection is successful, else False.
|
|
11
|
+
"""
|
|
12
|
+
try:
|
|
13
|
+
login_int = int(login)
|
|
14
|
+
except ValueError:
|
|
15
|
+
print(f"Invalid login number: {login}")
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
# If already initialized, shut it down cleanly
|
|
19
|
+
if mt5.initialize():
|
|
20
|
+
mt5.shutdown()
|
|
21
|
+
|
|
22
|
+
# Attempt to initialize MT5
|
|
23
|
+
if not mt5.initialize(login=login_int, password=password, server=server):
|
|
24
|
+
error_code, error_descr = mt5.last_error()
|
|
25
|
+
print(f"MT5 initialize failed, error code = {error_code}, description = {error_descr}")
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def disconnect_mt5():
|
|
32
|
+
"""
|
|
33
|
+
Shuts down MT5 terminal connection if initialized.
|
|
34
|
+
"""
|
|
35
|
+
if mt5.initialize():
|
|
36
|
+
mt5.shutdown()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# š„ NEW: Account Summary Fetcher
|
|
40
|
+
def get_account_summary() -> dict:
|
|
41
|
+
"""
|
|
42
|
+
Returns account summary data:
|
|
43
|
+
balance, equity, margin, free margin
|
|
44
|
+
"""
|
|
45
|
+
info = mt5.account_info()
|
|
46
|
+
|
|
47
|
+
if info is None:
|
|
48
|
+
return {}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
"balance": info.balance,
|
|
52
|
+
"equity": info.equity,
|
|
53
|
+
"margin": info.margin,
|
|
54
|
+
"freeMargin": info.margin_free
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# š„ NEW: Open Trades Fetcher
|
|
59
|
+
def get_open_trades() -> list:
|
|
60
|
+
"""
|
|
61
|
+
Returns list of currently open positions
|
|
62
|
+
"""
|
|
63
|
+
positions = mt5.positions_get()
|
|
64
|
+
|
|
65
|
+
if positions is None:
|
|
66
|
+
return []
|
|
67
|
+
|
|
68
|
+
trades = []
|
|
69
|
+
|
|
70
|
+
for p in positions:
|
|
71
|
+
trades.append({
|
|
72
|
+
"symbol": p.symbol,
|
|
73
|
+
"ticket": p.ticket,
|
|
74
|
+
"time": datetime.utcfromtimestamp(p.time).isoformat() + "Z",
|
|
75
|
+
"type": "buy" if p.type == 0 else "sell",
|
|
76
|
+
"volume": p.volume,
|
|
77
|
+
"open_price": p.price_open,
|
|
78
|
+
"current_price": p.price_current,
|
|
79
|
+
"sl": p.sl,
|
|
80
|
+
"tp": p.tp,
|
|
81
|
+
"profit": p.profit
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
return trades
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# ftsaaipkg/cli.py
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
import requests
|
|
7
|
+
from pymongo import MongoClient
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from .bridge import connect_mt5, get_account_summary, get_open_trades
|
|
10
|
+
|
|
11
|
+
# Load environment variables
|
|
12
|
+
load_dotenv()
|
|
13
|
+
|
|
14
|
+
# Load Mongo URI from .env
|
|
15
|
+
MONGO_URI = os.getenv("MONGO_URI")
|
|
16
|
+
if not MONGO_URI:
|
|
17
|
+
print("MONGO_URI not set in .env. Exiting...")
|
|
18
|
+
sys.exit(1)
|
|
19
|
+
|
|
20
|
+
DB_NAME = "ftsa_ai"
|
|
21
|
+
|
|
22
|
+
BACKEND_URL = "https://ftsa-ai-backend.onrender.com/api/ftsaaicli/mt5trades"
|
|
23
|
+
|
|
24
|
+
# File to track live streaming status
|
|
25
|
+
RUN_FILE = os.path.join(os.path.expanduser("~"), "ftsaai_running.txt")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_user_by_email(email: str, client: MongoClient):
|
|
29
|
+
db = client[DB_NAME]
|
|
30
|
+
return db.users.find_one({"email": email.lower()})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_mt_accounts(user_id: str, client: MongoClient):
|
|
34
|
+
db = client[DB_NAME]
|
|
35
|
+
return list(db.mtaccounts.find({"userId": user_id}))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_prop_accounts(user_id: str, client: MongoClient):
|
|
39
|
+
db = client[DB_NAME]
|
|
40
|
+
return list(db.propaccounts.find({"userId": user_id}))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def run_cli():
|
|
44
|
+
print("=== FTSA AI MT5 Connector ===")
|
|
45
|
+
email = input("Enter your email: ").strip()
|
|
46
|
+
|
|
47
|
+
client = MongoClient(MONGO_URI)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
# Create running flag
|
|
51
|
+
with open(RUN_FILE, "w") as f:
|
|
52
|
+
f.write("running")
|
|
53
|
+
|
|
54
|
+
user = get_user_by_email(email, client)
|
|
55
|
+
if not user:
|
|
56
|
+
print(f"User with email '{email}' not found.")
|
|
57
|
+
print("Existing users in the database:")
|
|
58
|
+
for u in client[DB_NAME].users.find({}, {"email": 1}):
|
|
59
|
+
print(" -", u["email"])
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
|
|
62
|
+
user_id = str(user["_id"])
|
|
63
|
+
print(f"Found user: {user.get('firstName', 'Unknown')} (ID: {user_id})")
|
|
64
|
+
|
|
65
|
+
mt_accounts = get_mt_accounts(user_id, client)
|
|
66
|
+
prop_accounts = get_prop_accounts(user_id, client)
|
|
67
|
+
|
|
68
|
+
# Prefer MT account first, fallback to Prop
|
|
69
|
+
account = mt_accounts[0] if mt_accounts else (
|
|
70
|
+
prop_accounts[0] if prop_accounts else None
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if not account:
|
|
74
|
+
print("No MT or Prop accounts found for this user. Exiting...")
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
|
|
77
|
+
broker = account.get("broker", "Unknown")
|
|
78
|
+
login = account.get("login")
|
|
79
|
+
password = account.get("password")
|
|
80
|
+
server = account.get("server")
|
|
81
|
+
|
|
82
|
+
if not all([login, password, server]):
|
|
83
|
+
print("Account credentials are incomplete. Cannot connect to MT5.")
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
|
|
86
|
+
print(f"Using account from broker: {broker}")
|
|
87
|
+
print(f"Connecting to MT5 account {login} on {server}...")
|
|
88
|
+
|
|
89
|
+
# CONNECT TO MT5
|
|
90
|
+
if connect_mt5(login, password, server):
|
|
91
|
+
print("MT5 connection established successfully!")
|
|
92
|
+
print("Starting live trade streaming...\n")
|
|
93
|
+
else:
|
|
94
|
+
print("Failed to connect to MT5.")
|
|
95
|
+
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
# š„ LIVE STREAM LOOP
|
|
98
|
+
last_trades = None # track last trades to send only if changed
|
|
99
|
+
first_sync_done = False # to print success message only once
|
|
100
|
+
spinner_chars = ['|', '/', '-', '\\'] # spinner animation
|
|
101
|
+
spinner_index = 0
|
|
102
|
+
|
|
103
|
+
while True:
|
|
104
|
+
try:
|
|
105
|
+
# Stop if exit command deleted the flag
|
|
106
|
+
if not os.path.exists(RUN_FILE):
|
|
107
|
+
print("\nš Stop signal received. Exiting live streaming...")
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
summary = get_account_summary()
|
|
111
|
+
trades = get_open_trades()
|
|
112
|
+
|
|
113
|
+
# Only send if trades have changed
|
|
114
|
+
if trades != last_trades:
|
|
115
|
+
payload = {
|
|
116
|
+
"userId": user_id,
|
|
117
|
+
"broker": broker,
|
|
118
|
+
"login": int(login),
|
|
119
|
+
"summary": summary,
|
|
120
|
+
"trades": trades
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
response = requests.post(BACKEND_URL, json=payload, timeout=10)
|
|
125
|
+
if response.status_code == 200 and not first_sync_done:
|
|
126
|
+
print("ā
Trades synced successfully")
|
|
127
|
+
first_sync_done = True
|
|
128
|
+
elif response.status_code != 200:
|
|
129
|
+
print(f"ā Sync failed ({response.status_code})")
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"Error sending trades to backend: {e}")
|
|
132
|
+
|
|
133
|
+
last_trades = trades
|
|
134
|
+
|
|
135
|
+
# Animate spinner for live streaming
|
|
136
|
+
spinner_char = spinner_chars[spinner_index]
|
|
137
|
+
spinner_index = (spinner_index + 1) % len(spinner_chars)
|
|
138
|
+
print(f"\rš¢šā” Live trades ongoing {spinner_char} ", end='', flush=True)
|
|
139
|
+
|
|
140
|
+
# Poll every 0.3 seconds for near real-time updates
|
|
141
|
+
time.sleep(0.3)
|
|
142
|
+
|
|
143
|
+
except KeyboardInterrupt:
|
|
144
|
+
print("\nStopping live streaming...")
|
|
145
|
+
break
|
|
146
|
+
except Exception as e:
|
|
147
|
+
print(f"\nError during streaming: {e}")
|
|
148
|
+
time.sleep(0.3)
|
|
149
|
+
|
|
150
|
+
finally:
|
|
151
|
+
# Remove running flag on exit
|
|
152
|
+
if os.path.exists(RUN_FILE):
|
|
153
|
+
os.remove(RUN_FILE)
|
|
154
|
+
client.close()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
run_cli()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# ftsaaipkg/exit_cli.py
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
"""
|
|
7
|
+
Stops the live streaming of FTSA AI MT5 Connector
|
|
8
|
+
by deleting the running flag file.
|
|
9
|
+
"""
|
|
10
|
+
RUN_FILE = os.path.join(os.path.expanduser("~"), "ftsaai_running.txt")
|
|
11
|
+
|
|
12
|
+
if os.path.exists(RUN_FILE):
|
|
13
|
+
os.remove(RUN_FILE)
|
|
14
|
+
print("ā
Live streaming stopped successfully.")
|
|
15
|
+
else:
|
|
16
|
+
print("ā ļø No live streaming process found or already stopped.")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# setup.py
|
|
2
|
+
|
|
3
|
+
from setuptools import setup, find_packages
|
|
4
|
+
|
|
5
|
+
setup(
|
|
6
|
+
name="FTSAAIMT5C", # updated name for global install
|
|
7
|
+
version="0.1.0",
|
|
8
|
+
packages=find_packages(),
|
|
9
|
+
install_requires=[
|
|
10
|
+
"pymongo>=4.3.3",
|
|
11
|
+
"python-dotenv>=1.0.0",
|
|
12
|
+
"MetaTrader5>=5.0.5640",
|
|
13
|
+
"click>=8.0.0",
|
|
14
|
+
"requests>=2.32.5"
|
|
15
|
+
],
|
|
16
|
+
entry_points={
|
|
17
|
+
"console_scripts": [
|
|
18
|
+
"ftsaai-run = ftsaaipkg.cli:run_cli", # start live streaming
|
|
19
|
+
"exit-ftsaai = ftsaaipkg.exit_cli:main" # stop live streaming
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
python_requires='>=3.10',
|
|
23
|
+
author="Kelvin Mburu",
|
|
24
|
+
description="FTSA AI MT5 Connector CLI",
|
|
25
|
+
include_package_data=True,
|
|
26
|
+
classifiers=[
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.10",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Operating System :: Microsoft :: Windows :: Windows 10",
|
|
31
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
32
|
+
"Topic :: Office/Business :: Financial",
|
|
33
|
+
"Development Status :: 4 - Beta"
|
|
34
|
+
],
|
|
35
|
+
)
|