diff --git a/PyMtApi5/client.py b/PyMtApi5/client.py
new file mode 100644
index 00000000..ef7153b7
--- /dev/null
+++ b/PyMtApi5/client.py
@@ -0,0 +1,203 @@
+import logging
+import signal
+import sys
+import time
+import os
+import os.path
+import mt5enums
+from functools import partial
+from mt5apiclient import Mt5ApiClient
+from threading import Thread
+
+logger = logging.getLogger(__name__)
+
+
+def signal_handler(mtapi, _, __):
+ if mtapi.is_connected():
+ mtapi.disconnect()
+
+
+class Mt5ApiApp:
+ def __init__(self, address, port):
+ self.__address = address
+ self.__port = port
+
+ def on_disconnect(self, error_msg=None):
+ if error_msg is not None:
+ print(f"> Disconnected with error: {error_msg}")
+ else:
+ print("> Normal disconnected")
+ os.kill(os.getpid(), signal.SIGINT)
+
+ def on_quote_update(self, quote):
+ print(f"> update quote: {quote}")
+
+ def on_quote_added(self, quote):
+ print(f"> added quote: {quote}")
+
+ def on_quote_removed(self, quote):
+ print(f"> removed quote: {quote}")
+
+ def on_book_event(self, expert_handle, symbol):
+ print(f"> received book event: {expert_handle} - {symbol}")
+
+ def on_last_time_bar(self, expert_handle, instrument, rates):
+ print(
+ f"> received last time bar event: {expert_handle} - {instrument}, {rates}")
+
+ def on_trade_transaction(self, expert_handle, trade_transaction, trade_request, trade_result):
+ print(
+ f"> received trade transaction event: {expert_handle} - {trade_transaction}, {trade_request}, {trade_result}")
+
+ def process_command(self, mtapi, command):
+ pieces = command.split(' ', 1)
+ if len(pieces) != 2 or not pieces[0] or not pieces[1]:
+ print(f"! Invalid command format: {command}")
+ return
+ if pieces[0] == "AccountInfoDouble":
+ self.process_account_info_double(mtapi, pieces[1])
+ elif pieces[0] == "AccountInfoInteger":
+ self.process_account_info_integer(mtapi, pieces[1])
+ elif pieces[0] == "AccountInfoString":
+ self.process_account_info_string(mtapi, pieces[1])
+ elif pieces[0] == "SeriesInfoInteger":
+ self.process_series_info_integer(mtapi, pieces[1])
+ elif pieces[0] == "Bars":
+ self.process_bars(mtapi, pieces[1])
+ elif pieces[0] == "BarsPeriod":
+ self.process_bars_period(mtapi, pieces[1])
+ elif pieces[0] == "BarsCalculated":
+ self.process_bars_calculated(mtapi, pieces[1])
+ elif pieces[0] == "IndicatorCreate":
+ self.process_indicator_create(mtapi, pieces[1])
+ elif pieces[0] == "IndicatorRelease":
+ self.process_indicator_release(mtapi, pieces[1])
+ elif pieces[0] == "SymbolsTotal":
+ self.process_symbols_total(mtapi, pieces[1])
+ else:
+ print(f"! Unknown command: {pieces[0]}")
+
+ def process_account_info_double(self, mtapi, parameters):
+ property_id = mt5enums.ENUM_ACCOUNT_INFO_DOUBLE(int(parameters))
+ result = mtapi.account_info_double(property_id)
+ print(f"> AccountInfoDouble {property_id}: result = {result}")
+
+ def process_account_info_integer(self, mtapi, parameters):
+ property_id = mt5enums.ENUM_ACCOUNT_INFO_INTEGER(int(parameters))
+ value = mtapi.account_info_integer(property_id)
+ print(f"> AccountInfoInteger {property_id}: response = {value}")
+
+ def process_account_info_string(self, mtapi, parameters):
+ property_id = mt5enums.ENUM_ACCOUNT_INFO_STRING(int(parameters))
+ result = mtapi.account_info_string(property_id)
+ print(f"> AccountInfoString {property_id}: result = {result}")
+
+ def process_series_info_integer(self, mtpapi, parameters):
+ pieces = parameters.split(' ', 2)
+ if len(pieces) != 3 or not pieces[0] or not pieces[1] or not pieces[2]:
+ print(f"! Invalid parameters for command SeriesInfoInteger: {parameters}")
+ return
+ timeframe = mt5enums.ENUM_TIMEFRAMES(int(pieces[1]))
+ prop_id = mt5enums.ENUM_SERIES_INFO_INTEGER(int(pieces[2]))
+ result = mtpapi.series_info_integer(pieces[0], timeframe, prop_id)
+ print(f"> SeriesInfoInteger: result = {result}")
+
+ def process_bars(self, mtpapi, parameters):
+ pieces = parameters.split(' ', 1)
+ if len(pieces) != 2 or not pieces[0] or not pieces[1]:
+ print(f"! Invalid parameters for command Bars: {parameters}")
+ return
+ timeframe = mt5enums.ENUM_TIMEFRAMES(int(pieces[1]))
+ result = mtpapi.bars(pieces[0], timeframe)
+ print(f"> Bars: result = {result}")
+
+ def process_bars_period(self, mtpapi, parameters):
+ pieces = parameters.split(' ', 3)
+ if len(pieces) != 4 or not pieces[0] or not pieces[1] or not pieces[2] or not pieces[3]:
+ print(f"! Invalid parameters for command BarsPeriod: {parameters}")
+ return
+ timeframe = mt5enums.ENUM_TIMEFRAMES(int(pieces[1]))
+ start_time = int(pieces[2])
+ stop_time = int(pieces[3])
+ result = mtpapi.bars_period(pieces[0], timeframe, start_time, stop_time)
+ print(f"> Bars: result = {result}")
+
+ def process_bars_calculated(self, mtpapi, parameters):
+ if not parameters:
+ print(f"! Invalid parameters for command BarsCalculated: {parameters}")
+ return
+ indicator_handle = int(parameters)
+ result = mtpapi.bars_calculated(indicator_handle)
+ print(f"> BarsCalculated: result = {result}")
+
+ def process_indicator_create(self, mtpapi, parameters):
+ pieces = parameters.split(' ')
+ if len(pieces) != 3 or not pieces[0] or not pieces[1] or not pieces[2]:
+ print(f"! Invalid parameters for command IndicatorCreate: {parameters}")
+ return
+ timeframe = mt5enums.ENUM_TIMEFRAMES(int(pieces[1]))
+ indicator_type = mt5enums.ENUM_INDICATOR(int(pieces[2]))
+ result = mtpapi.indicator_create(pieces[0], timeframe, indicator_type)
+ print(f"> IndicatorCreate: result = {result}")
+
+ def process_indicator_release(self, mtpapi, parameters):
+ if not parameters:
+ print(f"! Invalid parameters for command IndicatorRelease: {parameters}")
+ return
+ indicator_handle = int(parameters)
+ result = mtpapi.indicator_release(indicator_handle)
+ print(f"> IndicatorRelease: response = {result}")
+
+ def process_symbols_total(self, mtpapi, parameters):
+ if not parameters or len(parameters) == 0:
+ print(f"! Invalid parameters for command SymbolsTotal: {parameters}")
+ return
+ parameters = parameters[:len(parameters) - 1]
+ selected = parameters == "True"
+ result = mtpapi.symbols_total(selected)
+ print(f"> SymbolsTotal: response = {result}")
+
+ def mtapi_command_thread(self, mtapi):
+ while mtapi.is_connected():
+ filename = "client.cmd"
+ if os.path.isfile(filename):
+ f = open("client.cmd", "r")
+ command = f.read()
+ f.close()
+ os.remove(filename)
+ self.process_command(mtapi, command)
+ time.sleep(0.5)
+
+ def run(self):
+ with Mt5ApiClient(self.__address, self.__port, self) as mtapi:
+ print(f"> Connected to {self.__address}:{self.__port}")
+ signal.signal(signal.SIGINT, partial(signal_handler, mtapi))
+ quotes = mtapi.get_quotes()
+ print(f"> quotes: {quotes}")
+ command_thread = Thread(
+ target=self.mtapi_command_thread, args=(mtapi,))
+ command_thread.start()
+ while mtapi.is_connected():
+ signal.pause()
+ command_thread.join()
+
+
+def main():
+ logging.basicConfig(filename='client.log',
+ filemode='w', level=logging.DEBUG)
+ logger.info('Started')
+
+ args_num = len(sys.argv)
+ if args_num != 3:
+ print("Incorrect arguments. For using input:\n\tclient
")
+ exit(1)
+
+ address = sys.argv[1]
+ port = int(sys.argv[2])
+
+ app = Mt5ApiApp(address, port)
+ app.run()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/PyMtApi5/mt5apiclient.py b/PyMtApi5/mt5apiclient.py
new file mode 100644
index 00000000..3e5ac0a4
--- /dev/null
+++ b/PyMtApi5/mt5apiclient.py
@@ -0,0 +1,450 @@
+import json
+import logging
+import asyncio
+from mt5enums import *
+from enum import IntEnum
+from threading import Lock, Thread
+from mtrpcclient import MtRpcClient
+from mt5commandtype import Mt5CommandType
+
+
+class Mt5EventType(IntEnum):
+ OnTradeTransaction = 1
+ OnBookEvent = 2
+ OnTick = 3
+ OnLastTimeBar = 4
+ OnLockTicks = 5
+
+
+class Mt5Quote:
+ def __init__(self, quote_json):
+ self.instrument = quote_json["Instrument"]
+ self.expert_handle = quote_json["ExpertHandle"]
+ self.bid = quote_json["Tick"]["Bid"]
+ self.ask = quote_json["Tick"]["Ask"]
+ self.volume = quote_json["Tick"]["Volume"]
+
+ def __repr__(self):
+ return f"{self.expert_handle}-{self.instrument}: Bid = {self.bid}, Ask = {self.ask}, Volume = {self.volume}"
+
+
+class MqlRates:
+ def __init__(self, mql_rates_json):
+ self.time = mql_rates_json["mt_time"]
+ self.open = mql_rates_json["open"]
+ self.high = mql_rates_json["high"]
+ self.low = mql_rates_json["low"]
+ self.close = mql_rates_json["close"]
+ self.tick_volume = mql_rates_json["tick_volume"]
+ self.spread = mql_rates_json["spread"]
+ self.real_volume = mql_rates_json["real_volume"]
+
+ def __repr__(self):
+ return f"time = {self.time}, open = {self.open}, high = {self.high}, low = {self.low}, close = {self.close}, tick_volume = {self.tick_volume}, spread = {self.spread}, real_volume = {self.real_volume}"
+
+
+class MqlTradeTransaction:
+ def __init__(self, mql_trade_transaction_json):
+ self.deal = mql_trade_transaction_json["Deal"]
+ self.order = mql_trade_transaction_json["Order"]
+ self.symbol = mql_trade_transaction_json["Symbol"]
+ self.transaction_type = mql_trade_transaction_json["Type"]
+ self.order_type = mql_trade_transaction_json["OrderType"]
+ self.order_state = mql_trade_transaction_json["OrderState"]
+ self.deal_type = mql_trade_transaction_json["DealType"]
+ self.time_type = mql_trade_transaction_json["TimeType"]
+ self.price = mql_trade_transaction_json["Price"]
+ self.price_trigger = mql_trade_transaction_json["PriceTrigger"]
+ self.price_sl = mql_trade_transaction_json["PriceSl"]
+ self.price_tp = mql_trade_transaction_json["PriceTp"]
+ self.volume = mql_trade_transaction_json["Volume"]
+ self.position = mql_trade_transaction_json["Position"]
+ self.position_by = mql_trade_transaction_json["PositionBy"]
+ self.time_expiration = mql_trade_transaction_json["MtTimeExpiration"]
+
+ def __repr__(self):
+ return (f"deal = {self.deal}, order = {self.order}, symbol = {self.symbol}, transaction_type = {self.transaction_type}, "
+ f"order_type = {self.order_type}, order_state = {self.order_state}, deal_type = {self.deal_type}, time_type = {self.time_type}, "
+ f"price = {self.price}, price_trigger = {self.price_trigger}, price_sl = {self.price_sl}, price_tp = {self.price_tp}, volume = {self.volume}, "
+ f"position = {self.position}, position_by = {self.position_by}, time_expiration = {self.time_expiration}")
+
+
+class MqlTradeRequest:
+ def __init__(self, mql_trade_request_json):
+ self.action = mql_trade_request_json["Action"]
+ self.magic = mql_trade_request_json["Magic"]
+ self.order = mql_trade_request_json["Order"]
+ self.symbol = mql_trade_request_json["Symbol"]
+ self.volume = mql_trade_request_json["Volume"]
+ self.price = mql_trade_request_json["Price"]
+ self.stop_limit = mql_trade_request_json["Stoplimit"]
+ self.sl = mql_trade_request_json["Sl"]
+ self.tp = mql_trade_request_json["Tp"]
+ self.deviation = mql_trade_request_json["Deviation"]
+ self.order_type = mql_trade_request_json["Type"]
+ self.type_filling = mql_trade_request_json["Type_filling"]
+ self.type_time = mql_trade_request_json["Type_time"]
+ self.expiration = mql_trade_request_json["MtExpiration"]
+ self.comment = mql_trade_request_json["Comment"]
+ # self.position = mql_trade_request_json["Position"]
+ # self.position_by = mql_trade_request_json["PositionBy"]
+
+ def __repr__(self):
+ return (f"action = {self.action}, magic = {self.magic}, order = {self.order}, symbol = {self.symbol}, volume = {self.volume}, "
+ f"price = {self.price}, stop_limit = {self.stop_limit}, sl = {self.sl}, tp = {self.tp}, deviation = {self.deviation}, "
+ f"order_type = {self.order_type}, type_filling = {self.type_filling}, type_time = {self.type_time}, expiration = {self.expiration}, "
+ f"comment = {self.comment}")
+
+
+class MqlTradeResult:
+ def __init__(self, mql_trade_result_json):
+ self.retcode = mql_trade_result_json["Retcode"]
+ self.deal = mql_trade_result_json["Deal"]
+ self.order = mql_trade_result_json["Order"]
+ self.volume = mql_trade_result_json["Volume"]
+ self.price = mql_trade_result_json["Price"]
+ self.bid = mql_trade_result_json["Bid"]
+ self.ask = mql_trade_result_json["Ask"]
+ self.comment = mql_trade_result_json["Comment"]
+ self.request_id = mql_trade_result_json["Request_id"]
+
+ def __repr__(self):
+ return (f"retcode = {self.retcode}, deal = {self.deal}, order = {self.order}, volume = {self.volume}, price = {self.price}, "
+ f"bid = {self.bid}, ask = {self.ask}, comment = {self.comment}, request_id = {self.request_id}")
+
+
+class Mt5ApiClient:
+ def __init__(self, address, port, callback=None):
+ self.__address = address
+ self.__port = port
+ self.__callback = callback
+ self.__logger = logging.getLogger(__name__)
+ self.__rpcclient = MtRpcClient(self)
+ self.__is_connected = False
+ self.__quotes = dict()
+ self.__experts = list()
+ self.__lock = Lock()
+
+ def __enter__(self):
+ self.connect()
+ return self
+
+ def __exit__(self, *_):
+ self.disconnect()
+
+ def connect(self):
+ self.__logger.info(f"Connecting to {self.__address}:{self.__port}")
+ url = f"ws://{self.__address}:{self.__port}"
+ self.__rpcclient.connect(url)
+ experts = self.__rpcclient.request_expert_list()
+ if experts is None:
+ self.__rpcclient.disconnect()
+ raise Exception("Failed to load expert list")
+ self.__logger.info(f"loaded exerts {self.__experts}")
+ for expert_handle in experts:
+ quote = self.__get_quote(expert_handle)
+ if quote is not None:
+ self.__experts.append(expert_handle)
+ self.__quotes[expert_handle] = quote
+ self.__logger.info(f"loaded quotes {self.__quotes}")
+ # TODO: send backtesting ready
+ self.__event_loop = asyncio.new_event_loop()
+ self.__event_thread = Thread(target=self.__event_thread_func)
+ self.__event_thread.start()
+ self.__is_connected = True
+
+ def disconnect(self):
+ self.__rpcclient.disconnect()
+ self.__event_loop.call_soon_threadsafe(self.__event_loop.stop)
+ self.__event_thread.join()
+ self.__quotes.clear()
+ self.__experts.clear()
+
+ def is_connected(self):
+ with self.__lock:
+ return self.__is_connected
+
+ def get_quotes(self):
+ with self.__lock:
+ return list(self.__quotes.values())
+
+ def is_testing(self):
+ return False
+
+ # Account Information functions
+
+ # AccountInfoDouble
+ def account_info_double(self, property_id: ENUM_ACCOUNT_INFO_DOUBLE):
+ cmd_params = {"PropertyId": property_id}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.AccountInfoDouble, cmd_params)
+
+ # AccountInfoInteger
+ def account_info_integer(self, property_id: ENUM_ACCOUNT_INFO_INTEGER):
+ cmd_params = {"PropertyId": property_id}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.AccountInfoInteger, cmd_params)
+
+ # AccountInfoString
+ def account_info_string(self, property_id: ENUM_ACCOUNT_INFO_STRING):
+ cmd_params = {"PropertyId": property_id}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.AccountInfoString, cmd_params)
+
+ # Timeseries and Indicators Access
+
+ # SeriesInfoInteger
+ def series_info_integer(self, symbol_name, timeframe: ENUM_TIMEFRAMES, prop_id: ENUM_SERIES_INFO_INTEGER):
+ if symbol_name is None:
+ symbol_name = ""
+ cmd_params = {"Symbol": symbol_name,
+ "Timeframe": timeframe, "PropId": prop_id}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.SeriesInfoInteger, cmd_params)
+
+ # Bars
+ def bars(self, symbol_name, timeframe: ENUM_TIMEFRAMES):
+ if symbol_name is None:
+ symbol_name = ""
+ cmd_params = {"Symbol": symbol_name, "Timeframe": timeframe}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.Bars, cmd_params)
+
+ # Bars (for a specified period)
+ def bars_period(self, symbol_name, timeframe: ENUM_TIMEFRAMES, start_time: int, stop_time: int):
+ if symbol_name is None:
+ symbol_name = ""
+ cmd_params = {"Symbol": symbol_name, "Timeframe": timeframe,
+ "StartTime": start_time, "StopTime": stop_time}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.Bars2, cmd_params)
+
+ # BarsCalculated
+ def bars_calculated(self, indicator_handle: int):
+ cmd_params = {"IndicatorHandle": indicator_handle}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.BarsCalculated, cmd_params)
+
+ # CopyBuffer
+ def copy_buffer(self):
+ # TODO
+ pass
+
+ # CopyRates
+ def copy_rates(self):
+ # TODO
+ pass
+
+ # CopyTime
+ def copy_time(self):
+ # TODO
+ pass
+
+ # Copy Open
+ def copy_open(self):
+ # TODO
+ pass
+
+ # Copy High
+ def copy_high(self):
+ # TODO
+ pass
+
+ # CopyLow
+ def copy_low(self):
+ # TODO
+ pass
+
+ # CopyClose
+ def copy_close(self):
+ # TODO
+ pass
+
+ # CopyTickVolume
+ def copy_tick_volume(self):
+ # TODO
+ pass
+
+ # CopyRealVolume
+ def copy_real_volume(self):
+ # TODO
+ pass
+
+ # CopySpread
+ def copy_spread(self):
+ # TODO
+ pass
+
+ # CopyTicks
+ def copy_ticks(self):
+ # TODO
+ pass
+
+ # IndicatorCreate
+ def indicator_create(self, symbol: str, period: ENUM_TIMEFRAMES, indicator_type: ENUM_INDICATOR, parameters: list = []):
+ cmd_params = {"Period": period, "IndicatorType": indicator_type}
+ if symbol is not None:
+ cmd_params["Symbol"] = symbol
+ if len(parameters) != 0:
+ cmd_params["Parameters"] = parameters
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.IndicatorCreate, cmd_params)
+
+ # IndicatorRelease
+ def indicator_release(self, indicator_handle: int):
+ cmd_params = {"IndicatorHandle": indicator_handle}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.IndicatorRelease, cmd_params)
+
+ # Market Info
+
+ # SymbolsTotal
+ def symbols_total(self, selected: bool):
+ cmd_params = {"Selected": selected}
+ return self.__send_command(
+ self.__get_default_expert(), Mt5CommandType.SymbolsTotal, cmd_params)
+
+ # Private methods
+
+ def __event_thread_func(self):
+ self.__logger.debug(f"__event_thread started")
+ asyncio.set_event_loop(self.__event_loop)
+ self.__event_loop.run_forever()
+ self.__logger.debug(f"__event_thread stopped")
+
+ def __get_quote(self, expert_handle):
+ response = self.__send_command(expert_handle, Mt5CommandType.GetQuote)
+ quote = Mt5Quote(response) if response is not None else None
+ return quote
+
+ def __get_default_expert(self):
+ with self.__lock:
+ if len(self.__experts) > 0:
+ return self.__experts[0]
+ return 0
+
+ def __send_command(self, expert_handle, command_type, payload=None):
+ payload_json = None if payload is None else json.dumps(payload)
+ response = self.__rpcclient.send_command(
+ expert_handle, command_type, payload_json)
+ if response is None:
+ self.__logger.warning("Failed to send commad. Result is None")
+ raise Exception("Failed to send commad. Result is None")
+ response_json = json.loads(response)
+ error_code = int(response_json["ErrorCode"])
+ if error_code != 0:
+ self.__logger.warning(
+ f"send_command: ErrorCode = {response.ErrorCode}. {response.ErrorMessage}")
+ raise Exception(
+ f"Failed to send command: ErrorCode = {response.ErrorCode}. {response.ErrorMessage} ")
+ return response_json["Value"]
+
+ def __process_tick_event(self, payload):
+ quote_json = json.loads(payload)
+ if quote_json is not None:
+ quote = Mt5Quote(quote_json)
+ with self.__lock:
+ self.__quotes[quote.expert_handle] = quote
+ if self.__callback is not None:
+ self.__callback.on_quote_update(quote)
+
+ def __process_event_disconnect(self, error_msg=None):
+ with self.__lock:
+ self.__is_connected = False
+ if self.__callback is not None:
+ self.__callback.on_disconnect(error_msg)
+
+ def __process_expert_added(self, expert_handle):
+ quote = self.__get_quote(expert_handle)
+ if quote is not None:
+ with self.__lock:
+ self.__quotes[expert_handle] = quote
+ self.__experts.append(expert_handle)
+ if self.__callback is not None:
+ self.__callback.on_quote_added(quote)
+
+ def __process_expert_removed(self, expert_handle):
+ quote = None
+ with self.__lock:
+ self.__experts.remove(expert_handle)
+ if expert_handle in self.__quotes:
+ quote = self.__quotes.pop(expert_handle)
+ if quote is not None and self.__callback is not None:
+ self.__callback.on_quote_removed(quote)
+
+ def __process_on_book_event(self, expert_handle, payload):
+ book_event_json = json.loads(payload)
+ if book_event_json is None:
+ self.__logger.error("Failed to parse book event json")
+ return
+ symbol = book_event_json["Symbol"]
+ if self.__callback is not None:
+ self.__callback.on_book_event(expert_handle, symbol)
+
+ def __process_on_last_time_bar(self, expert_handle, payload):
+ last_time_bar_event_json = json.loads(payload)
+ if last_time_bar_event_json is None:
+ self.__logger.error("Failed to parse last time bar event json")
+ return
+ instrument = last_time_bar_event_json["Instrument"]
+ rates = MqlRates(last_time_bar_event_json["Rates"])
+ if self.__callback is not None:
+ self.__callback.on_last_time_bar(expert_handle, instrument, rates)
+
+ def __process_on_lock_tick(self, expert_handle, payload):
+ # TODO: must be implemented
+ self.__logger.warning(
+ f"event type OnLockTicks is not supported. {expert_handle} - {payload}")
+
+ def __process_on_trade_transaction(self, expert_handle, payload):
+ trade_transaction_json = json.loads(payload)
+ trade_transaction = MqlTradeTransaction(
+ trade_transaction_json["Trans"])
+ trade_request = MqlTradeRequest(trade_transaction_json["Request"])
+ trade_result = MqlTradeResult(trade_transaction_json["Result"])
+ if self.__callback is not None:
+ self.__callback.on_trade_transaction(
+ expert_handle, trade_transaction, trade_request, trade_result)
+
+ # RPC event handlers
+
+ def mt_rpc_on_event(self, expert_handle, event_type, payload):
+ self.__logger.debug(
+ f"received event from {expert_handle}: {event_type}, {payload}")
+ mt_event_type = Mt5EventType(int(event_type))
+ if mt_event_type == Mt5EventType.OnTick:
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_tick_event, payload)
+ elif mt_event_type == Mt5EventType.OnBookEvent:
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_on_book_event, expert_handle, payload)
+ elif mt_event_type == Mt5EventType.OnLastTimeBar:
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_on_last_time_bar, expert_handle, payload)
+ elif mt_event_type == Mt5EventType.OnLockTicks:
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_on_lock_tick, expert_handle, payload)
+ elif mt_event_type == Mt5EventType.OnTradeTransaction:
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_on_trade_transaction, expert_handle, payload)
+ else:
+ self.__logger.warning(f"received unsupported event {event_type}")
+
+ def mt_rcp_on_disconnect(self):
+ self.__logger.info("normal disconnected")
+ self.__event_loop.call_soon_threadsafe(self.__process_event_disconnect)
+
+ def mt_rpc_on_connection_failed(self, error_msg=None):
+ self.__logger.info(f"connection failed: {error_msg}")
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_event_disconnect, error_msg)
+
+ def mt_rpc_on_expert_added(self, expert_handle):
+ self.__logger.info(f"expert added: {expert_handle}")
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_expert_added, expert_handle)
+
+ def mt_rpc_on_expert_removed(self, expert_handle):
+ self.__logger.info(f"expert removed: {expert_handle}")
+ self.__event_loop.call_soon_threadsafe(
+ self.__process_expert_removed, expert_handle)
diff --git a/PyMtApi5/mt5commandtype.py b/PyMtApi5/mt5commandtype.py
new file mode 100644
index 00000000..b3f05693
--- /dev/null
+++ b/PyMtApi5/mt5commandtype.py
@@ -0,0 +1,254 @@
+from enum import IntEnum
+
+class Mt5CommandType(IntEnum):
+ # NoCommand = 0
+ GetQuote = 1
+
+ #trade operations
+ OrderCalcMargin = 2
+ OrderCalcProfit = 3
+ PositionsTotal = 6
+ PositionGetSymbol = 7
+ PositionSelect = 8
+ PositionGetDouble = 9
+ PositionGetInteger = 10
+ PositionGetString = 11
+ PositionGetTicket = 4
+ OrdersTotal = 12
+ OrderGetTicket = 13
+ OrderSelect = 14
+ OrderGetDouble = 15
+ OrderGetInteger = 16
+ OrderGetString = 17
+ HistorySelect = 18
+ HistorySelectByPosition = 19
+ HistoryOrderSelect = 20
+ HistoryOrdersTotal = 21
+ HistoryOrderGetTicket = 22
+ HistoryOrderGetDouble = 23
+ HistoryOrderGetInteger = 24
+ HistoryOrderGetString = 25
+ HistoryDealSelect = 26
+ HistoryDealsTotal = 27
+ HistoryDealGetTicket = 28
+ HistoryDealGetDouble = 29
+ HistoryDealGetInteger = 30
+ HistoryDealGetString = 31
+
+ #Account Information
+ AccountInfoDouble = 32
+ AccountInfoInteger = 33
+ AccountInfoString = 34
+
+ #Access to Timeseries and Indicator Data
+ SeriesInfoInteger = 35
+ Bars = 36
+ Bars2 = 1036
+ BarsCalculated = 37
+ IndicatorCreate = 38
+ IndicatorRelease = 39
+ CopyBuffer = 40
+ CopyBuffer1 = 1040
+ CopyBuffer2 = 1140
+ CopyRates = 41
+ CopyRates1 = 1041
+ CopyRates2 = 1141
+ CopyTime = 42
+ CopyTime1 = 1042
+ CopyTime2 = 1142
+ CopyOpen = 43
+ CopyOpen1 = 1043
+ CopyOpen2 = 1143
+ CopyHigh = 44
+ CopyHigh1 = 1044
+ CopyHigh2 = 1144
+ CopyLow = 45
+ CopyLow1 = 1045
+ CopyLow2 = 1145
+ CopyClose = 46
+ CopyClose1 = 1046
+ CopyClose2 = 1146
+ CopyTickVolume = 47
+ CopyTickVolume1 = 1047
+ CopyTickVolume2 = 1147
+ CopyRealVolume = 48
+ CopyRealVolume1 = 1048
+ CopyRealVolume2 = 1148
+ CopySpread = 49
+ CopySpread1 = 1049
+ CopySpread2 = 1149
+
+ #Market Information
+ SymbolsTotal = 50
+ SymbolName = 51
+ SymbolSelect = 52
+ SymbolIsSynchronized = 53
+ SymbolInfoDouble = 54
+ SymbolInfoInteger = 55
+ SymbolInfoString = 56
+ SymbolInfoString2 = 1056
+ SymbolInfoTick = 57
+ SymbolInfoSessionQuote = 58
+ SymbolInfoSessionTrade = 59
+ MarketBookAdd = 60
+ MarketBookRelease = 61
+ MarketBookGet = 62
+ OrderCloseAll = 63
+
+ #CTrade
+ PositionClose = 64
+ PositionOpen = 65
+ PositionOpen2 = 1065
+ PositionModify = 6066
+ PositionClosePartial_bySymbol = 6067
+ PositionClosePartial_byTicket = 6068
+
+ #Backtesting
+ BacktestingReady = 66
+ IsTesting = 67
+
+ PositionSelectByTicket = 69
+
+ ObjectCreate = 70
+ ObjectName = 71
+ ObjectDelete = 72
+ ObjectsDeleteAll = 73
+ ObjectFind = 74
+ ObjectGetTimeByValue = 75
+ ObjectGetValueByTime = 76
+ ObjectMove = 77
+ ObjectsTotal = 78
+ ObjectGetDouble = 79
+ ObjectGetInteger = 80
+ ObjectGetString = 81
+ ObjectSetDouble = 82
+ ObjectSetInteger = 83
+ ObjectSetString = 84
+
+ iAC = 88
+ iAD = 89
+ iADX = 90
+ iADXWilder = 91
+ iAlligator = 92
+ iAMA = 93
+ iAO = 94
+ iATR = 95
+ iBearsPower = 96
+ iBands = 97
+ iBullsPower = 98
+ iCCI = 99
+ iChaikin = 100
+ iCustom = 101
+ iDEMA = 102
+ iDeMarker = 103
+ iEnvelopes = 104
+ iForce = 105
+ iFractals = 106
+ iFrAMA = 107
+ iGator = 108
+ iIchimoku = 109
+ iBWMFI = 110
+ iMomentum = 111
+ iMFI = 112
+ iMA = 113
+ iOsMA = 114
+ iMACD = 115
+ iOBV = 116
+ iSAR = 117
+ iRSI = 118
+ iRVI = 119
+ iStdDev = 120
+ iStochastic = 121
+ iTEMA = 122
+ iTriX = 123
+ iWPR = 124
+ iVIDyA = 125
+ iVolumes = 126
+
+ #Date and Time
+ TimeCurrent = 127
+ TimeTradeServer = 128
+ TimeLocal = 129
+ TimeGMT = 130
+
+ #Chart Operations
+ ChartId = 206
+ ChartRedraw = 207
+ ChartApplyTemplate = 236
+ ChartSaveTemplate = 237
+ ChartWindowFind = 238
+ ChartTimePriceToXY = 239
+ ChartXYToTimePrice = 240
+ ChartOpen = 241
+ ChartFirst = 242
+ ChartNext = 243
+ ChartClose = 244
+ ChartSymbol = 245
+ ChartPeriod = 246
+ ChartSetDouble = 247
+ ChartSetInteger = 248
+ ChartSetString = 249
+ ChartGetDouble = 250
+ ChartGetInteger = 251
+ ChartGetString = 252
+ ChartNavigate = 253
+ ChartIndicatorDelete = 254
+ ChartIndicatorName = 255
+ ChartIndicatorsTotal = 256
+ ChartWindowOnDropped = 257
+ ChartPriceOnDropped = 258
+ ChartTimeOnDropped = 259
+ ChartXOnDropped = 260
+ ChartYOnDropped = 261
+ ChartSetSymbolPeriod = 262
+ ChartScreenShot = 263
+ ChartIndicatorAdd = 280
+ ChartIndicatorGet = 281
+
+ # Terminal Operations
+ TerminalCompany = 68
+ TerminalName = 69
+ TerminalPath = 70
+
+ #Checkup
+ GetLastError = 132
+ TerminalInfoString = 153
+ TerminalInfoInteger = 204
+ TerminalInfoDouble = 205
+
+ #Common Functions
+ Alert = 136
+ Comment = 137
+ GetTickCount = 138
+ GetMicrosecondCount = 139
+ MessageBox = 140
+ PeriodSeconds = 141
+ PlaySound = 142
+ Print = 68
+ ResetLastError = 143
+ SendNotification = 144
+ SendMail = 145
+
+ #Global Variables
+ GlobalVariableCheck = 146
+ GlobalVariableTime = 147
+ GlobalVariableDel = 148
+ GlobalVariableGet = 149
+ GlobalVariableName = 150
+ GlobalVariableSet = 151
+ GlobalVariablesFlush = 152
+ GlobalVariableTemp = 154
+ GlobalVariableSetOnCondition = 156
+ GlobalVariablesDeleteAll = 157
+ GlobalVariablesTotal = 158
+
+ UnlockTicks = 159
+ PositionCloseAll = 160
+ TesterStop = 161
+
+ CopyTicks = 300
+ OrderSend = 301
+ OrderSendAsync = 302
+ OrderCheck = 303
+ Buy = 304
+ Sell = 305
diff --git a/PyMtApi5/mt5enums.py b/PyMtApi5/mt5enums.py
new file mode 100644
index 00000000..af3f6e8a
--- /dev/null
+++ b/PyMtApi5/mt5enums.py
@@ -0,0 +1,855 @@
+from enum import IntEnum
+
+# Chart Timeframes
+
+class ENUM_TIMEFRAMES(IntEnum):
+ PERIOD_CURRENT = 0
+ PERIOD_M1 = 1
+ PERIOD_M2 = 2
+ PERIOD_M3 = 3
+ PERIOD_M4 = 4
+ PERIOD_M5 = 5
+ PERIOD_M6 = 6
+ PERIOD_M10 = 10
+ PERIOD_M12 = 12
+ PERIOD_M15 = 15
+ PERIOD_M20 = 20
+ PERIOD_M30 = 30
+ PERIOD_H1 = 16385
+ PERIOD_H2 = 16386
+ PERIOD_H3 = 16387
+ PERIOD_H4 = 16388
+ PERIOD_H6 = 16390
+ PERIOD_H8 = 16392
+ PERIOD_H12 = 1639
+ PERIOD_D1 = 16408
+ PERIOD_W1 = 32769
+ PERIOD_MN1 = 49153
+
+
+# Charts Properties
+
+class ENUM_CHART_PROPERTY_DOUBLE(IntEnum):
+ CHART_SHIFT_SIZE = 3
+ CHART_FIXED_POSITION = 41
+ CHART_FIXED_MAX = 8
+ CHART_FIXED_MIN = 9
+ CHART_POINTS_PER_BAR = 11
+ CHART_PRICE_MIN = 108
+ CHART_PRICE_MAX = 109
+
+
+class ENUM_CHART_PROPERTY_INTEGER(IntEnum):
+ CHART_SHOW = 46
+ CHART_IS_OBJECT = 111
+ CHART_BRING_TO_TOP = 35
+ CHART_CONTEXT_MENU = 50
+ CHART_CROSSHAIR_TOOL = 49
+ CHART_MOUSE_SCROLL = 42
+ CHART_EVENT_MOUSE_WHEEL = 48
+ CHART_EVENT_MOUSE_MOVE = 40
+ CHART_EVENT_OBJECT_CREATE = 38
+ CHART_EVENT_OBJECT_DELETE = 39
+ CHART_MODE = 0
+ CHART_FOREGROUND = 1
+ CHART_SHIFT = 2
+ CHART_AUTOSCROLL = 4
+ CHART_KEYBOARD_CONTROL = 47
+ CHART_QUICK_NAVIGATION = 45
+ CHART_SCALE = 5
+ CHART_SCALEFIX = 6
+ CHART_SCALEFIX_11 = 7
+ CHART_SCALE_PT_PER_BAR = 10
+ CHART_SHOW_OHLC = 12
+ CHART_SHOW_BID_LINE = 13
+ CHART_SHOW_ASK_LINE = 14
+ CHART_SHOW_LAST_LINE = 15
+ CHART_SHOW_PERIOD_SEP = 16
+ CHART_SHOW_GRID = 17
+ CHART_SHOW_VOLUMES = 18
+ CHART_SHOW_OBJECT_DESCR = 19
+ CHART_VISIBLE_BARS = 100
+ CHART_WINDOWS_TOTAL = 101
+ CHART_WINDOW_IS_VISIBLE = 102
+ CHART_WINDOW_HANDLE = 103
+ CHART_WINDOW_YDISTANCE = 110
+ CHART_FIRST_VISIBLE_BAR = 104
+ CHART_WIDTH_IN_BARS = 105
+ CHART_WIDTH_IN_PIXELS = 106
+ CHART_HEIGHT_IN_PIXELS = 107
+ CHART_COLOR_BACKGROUND = 21
+ CHART_COLOR_FOREGROUND = 22
+ CHART_COLOR_GRID = 23
+ CHART_COLOR_VOLUME = 24
+ CHART_COLOR_CHART_UP = 25
+ CHART_COLOR_CHART_DOWN = 26
+ CHART_COLOR_CHART_LINE = 27
+ CHART_COLOR_CANDLE_BULL = 28
+ CHART_COLOR_CANDLE_BEAR = 29
+ CHART_COLOR_BID = 30
+ CHART_COLOR_ASK = 31
+ CHART_COLOR_LAST = 32
+ CHART_COLOR_STOP_LEVEL = 33
+ CHART_SHOW_TRADE_LEVELS = 34
+ CHART_DRAG_TRADE_LEVELS = 43
+ CHART_SHOW_DATE_SCALE = 36
+ CHART_SHOW_PRICE_SCALE = 37
+ CHART_SHOW_ONE_CLICK = 44
+ CHART_IS_MAXIMIZED = 115
+ CHART_IS_MINIMIZED = 116
+
+
+class ENUM_CHART_PROPERTY_STRING(IntEnum):
+ CHART_COMMENT = 20
+ CHART_EXPERT_NAME = 113
+ CHART_SCRIPT_NAME = 114
+
+
+class ENUM_CHART_POSITION(IntEnum):
+ CHART_BEGIN = 0 # Chart beginning (the oldest prices)
+ CHART_CURRENT_POS = 1 # Current position
+ CHART_END = 2 # Chart end (the latest prices)
+
+
+#Client Terminal Properties
+
+class ENUM_TERMINAL_INFO_INTEGER(IntEnum):
+ TERMINAL_BUILD = 5
+ TERMINAL_COMMUNITY_ACCOUNT = 23
+ TERMINAL_COMMUNITY_CONNECTION = 24
+ TERMINAL_CONNECTED = 6
+ TERMINAL_DLLS_ALLOWED = 7
+ TERMINAL_TRADE_ALLOWED = 8
+ TERMINAL_EMAIL_ENABLED = 9
+ TERMINAL_FTP_ENABLED = 10
+ TERMINAL_NOTIFICATIONS_ENABLED = 26
+ TERMINAL_MAXBARS = 11
+ TERMINAL_MQID = 22
+ TERMINAL_CODEPAGE = 12
+ TERMINAL_CPU_CORES = 21
+ TERMINAL_DISK_SPACE = 20
+ TERMINAL_MEMORY_PHYSICAL = 14
+ TERMINAL_MEMORY_TOTAL = 15
+ TERMINAL_MEMORY_AVAILABLE = 16
+ TERMINAL_MEMORY_USED = 17
+ TERMINAL_X64 = 18
+ TERMINAL_OPENCL_SUPPORT = 19
+ TERMINAL_SCREEN_DPI = 27
+ TERMINAL_PING_LAST = 29
+
+
+class ENUM_TERMINAL_INFO_DOUBLE(IntEnum):
+ TERMINAL_COMMUNITY_BALANCE = 25
+
+
+class ENUM_TERMINAL_INFO_STRING(IntEnum):
+ TERMINAL_LANGUAGE = 13
+ TERMINAL_COMPANY = 0
+ TERMINAL_NAME = 1
+ TERMINAL_PATH = 2
+ TERMINAL_DATA_PATH = 3
+ TERMINAL_COMMONDATA_PATH = 4
+
+
+# Symbol Properties
+
+class ENUM_SYMBOL_INFO_INTEGER(IntEnum):
+ SYMBOL_CUSTOM = 78
+ SYMBOL_BACKGROUND_COLOR = 79
+ SYMBOL_CHART_MODE = 80
+ SYMBOL_SELECT = 0
+ SYMBOL_VISIBLE = 76
+ SYMBOL_SESSION_DEALS = 56
+ SYMBOL_SESSION_BUY_ORDERS = 60
+ SYMBOL_SESSION_SELL_ORDERS = 62
+ SYMBOL_VOLUME = 10
+ SYMBOL_VOLUMEHIGH = 11
+ SYMBOL_VOLUMELOW = 12
+ SYMBOL_TIME = 15
+ SYMBOL_DIGITS = 17
+ SYMBOL_SPREAD_FLOAT = 41
+ SYMBOL_SPREAD = 18
+ SYMBOL_TICKS_BOOKDEPTH = 25
+ SYMBOL_TRADE_CALC_MODE = 29
+ SYMBOL_TRADE_MODE = 30
+ SYMBOL_START_TIME = 51
+ SYMBOL_EXPIRATION_TIME = 52
+ SYMBOL_TRADE_STOPS_LEVEL = 31
+ SYMBOL_TRADE_FREEZE_LEVEL = 32
+ SYMBOL_TRADE_EXEMODE = 33
+ SYMBOL_SWAP_MODE = 37
+ SYMBOL_SWAP_ROLLOVER3DAYS = 40
+ SYMBOL_MARGIN_HEDGED_USE_LEG = 82
+ SYMBOL_EXPIRATION_MODE = 49
+ SYMBOL_FILLING_MODE = 50
+ SYMBOL_ORDER_MODE = 71
+ SYMBOL_ORDER_GTC_MODE = 81
+ SYMBOL_ORDER_CLOSEBY = 64
+ SYMBOL_OPTION_MODE = 75
+ SYMBOL_OPTION_RIGHT = 74
+
+
+class ENUM_SYMBOL_INFO_DOUBLE(IntEnum):
+ SYMBOL_BID = 1
+ SYMBOL_BIDHIGH = 2
+ SYMBOL_BIDLOW = 3
+ SYMBOL_ASK = 4
+ SYMBOL_ASKHIGH = 5
+ SYMBOL_ASKLOW = 6
+ SYMBOL_LAST = 7
+ SYMBOL_LASTHIGH = 8
+ SYMBOL_LASTLOW = 9
+ SYMBOL_VOLUME_REAL = 10
+ SYMBOL_VOLUMEHIGH_REAL = 11
+ SYMBOL_VOLUMELOW_REAL = 12
+ SYMBOL_OPTION_STRIKE = 72
+ SYMBOL_POINT = 16
+ SYMBOL_TRADE_TICK_VALUE = 26
+ SYMBOL_TRADE_TICK_VALUE_PROFIT = 53
+ SYMBOL_TRADE_TICK_VALUE_LOSS = 54
+ SYMBOL_TRADE_TICK_SIZE = 27
+ SYMBOL_TRADE_CONTRACT_SIZE = 28
+ SYMBOL_TRADE_ACCRUED_INTEREST = 87
+ SYMBOL_TRADE_FACE_VALUE = 86
+ SYMBOL_TRADE_LIQUIDITY_RATE = 85
+ SYMBOL_VOLUME_MIN = 34
+ SYMBOL_VOLUME_MAX = 35
+ SYMBOL_VOLUME_STEP = 36
+ SYMBOL_VOLUME_LIMIT = 55
+ SYMBOL_SWAP_LONG = 38
+ SYMBOL_SWAP_SHORT = 39
+ SYMBOL_MARGIN_INITIAL = 42
+ SYMBOL_MARGIN_MAINTENANCE = 43
+ SYMBOL_MARGIN_LONG = 44 # FIXME: Undocumented!
+ SYMBOL_MARGIN_SHORT = 45 # FIXME: Undocumented!
+ SYMBOL_MARGIN_LIMIT = 46 # FIXME: Undocumented!
+ SYMBOL_MARGIN_STOP = 47 # FIXME: Undocumented!
+ SYMBOL_MARGIN_STOPLIMIT = 48 # FIXME: Undocumented!
+ SYMBOL_SESSION_VOLUME = 57
+ SYMBOL_SESSION_TURNOVER = 58
+ SYMBOL_SESSION_INTEREST = 59
+ SYMBOL_SESSION_BUY_ORDERS_VOLUME = 61
+ SYMBOL_SESSION_SELL_ORDERS_VOLUME = 63
+ SYMBOL_SESSION_OPEN = 64
+ SYMBOL_SESSION_CLOSE = 65
+ SYMBOL_SESSION_AW = 66
+ SYMBOL_SESSION_PRICE_SETTLEMENT = 67
+ SYMBOL_SESSION_PRICE_LIMIT_MIN = 68
+ SYMBOL_SESSION_PRICE_LIMIT_MAX = 69
+ SYMBOL_MARGIN_HEDGED = 77
+
+
+class ENUM_SYMBOL_INFO_STRING(IntEnum):
+ SYMBOL_BASIS = 73
+ SYMBOL_CURRENCY_BASE = 22
+ SYMBOL_CURRENCY_PROFIT = 23
+ SYMBOL_CURRENCY_MARGIN = 24
+ SYMBOL_BANK = 19
+ SYMBOL_DESCRIPTION = 20
+ SYMBOL_FORMULA = 84
+ SYMBOL_PAGE = 83
+ SYMBOL_ISIN = 70
+ SYMBOL_PATH = 21
+
+
+class ENUM_SYMBOL_CHART_MODE(IntEnum):
+ SYMBOL_CHART_MODE_BID = 0
+ SYMBOL_CHART_MODE_LAST = 1
+
+
+class ENUM_SYMBOL_ORDER_GTC_MODE(IntEnum):
+ SYMBOL_ORDERS_GTC = 0
+ SYMBOL_ORDERS_DAILY = 1
+ SYMBOL_ORDERS_DAILY_EXCLUDING_STOPS = 2
+
+
+class ENUM_SYMBOL_CALC_MODE(IntEnum):
+ SYMBOL_CALC_MODE_FOREX = 0
+ SYMBOL_CALC_MODE_FUTURES = 1
+ SYMBOL_CALC_MODE_CFD = 2
+ SYMBOL_CALC_MODE_CFDINDEX = 3
+ SYMBOL_CALC_MODE_CFDLEVERAGE = 4
+ SYMBOL_CALC_MODE_EXCH_STOCKS = 32
+ SYMBOL_CALC_MODE_EXCH_FUTURES = 33
+ SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS = 34
+ SYMBOL_CALC_MODE_SERV_COLLATERAL = 64
+
+
+class ENUM_SYMBOL_TRADE_MODE(IntEnum):
+ SYMBOL_TRADE_MODE_DISABLED = 0
+ SYMBOL_TRADE_MODE_LONGONLY = 1
+ SYMBOL_TRADE_MODE_SHORTONLY = 2
+ SYMBOL_TRADE_MODE_CLOSEONLY = 3
+ SYMBOL_TRADE_MODE_FULL = 4
+
+
+class ENUM_SYMBOL_TRADE_EXECUTION(IntEnum):
+ SYMBOL_TRADE_EXECUTION_REQUEST = 0
+ SYMBOL_TRADE_EXECUTION_INSTANT = 1
+ SYMBOL_TRADE_EXECUTION_MARKET = 2
+ SYMBOL_TRADE_EXECUTION_EXCHANGE = 3
+
+
+class ENUM_SYMBOL_SWAP_MODE(IntEnum):
+ SYMBOL_SWAP_MODE_DISABLED = 0
+ SYMBOL_SWAP_MODE_POINTS = 1
+ SYMBOL_SWAP_MODE_CURRENCY_SYMBOL = 2
+ SYMBOL_SWAP_MODE_CURRENCY_MARGIN = 3
+ SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT = 4
+ SYMBOL_SWAP_MODE_INTEREST_CURRENT = 5
+ SYMBOL_SWAP_MODE_INTEREST_OPEN = 6
+ SYMBOL_SWAP_MODE_REOPEN_CURRENT = 7
+ SYMBOL_SWAP_MODE_REOPEN_BID = 8
+
+
+class ENUM_DAY_OF_WEEK(IntEnum):
+ SUNDAY = 0
+ MONDAY = 1
+ TUESDAY = 2
+ WEDNESDAY = 3
+ THURSDAY = 4
+ FRIDAY = 5
+ SATURDAY = 6
+
+
+class ENUM_SYMBOL_OPTION_RIGHT(IntEnum):
+ SYMBOL_OPTION_RIGHT_CALL = 0
+ SYMBOL_OPTION_RIGHT_PUT = 1
+
+
+class ENUM_SYMBOL_OPTION_MODE(IntEnum):
+ SYMBOL_OPTION_MODE_EUROPEAN = 0
+ SYMBOL_OPTION_MODE_AMERICAN = 1
+
+
+# Account Properties
+
+class ENUM_ACCOUNT_INFO_INTEGER(IntEnum):
+ ACCOUNT_LOGIN = 0 # Account number
+ ACCOUNT_TRADE_MODE = 32 # Account trade mode
+ ACCOUNT_LEVERAGE = 35 # Account leverage
+ ACCOUNT_LIMIT_ORDERS = 47 # Maximum allowed number of active pending orders
+ ACCOUNT_MARGIN_SO_MODE = 44 # Mode for setting the minimal allowed margin
+ ACCOUNT_TRADE_ALLOWED = 33 # Allowed trade for the current account
+ ACCOUNT_TRADE_EXPERT = 34 # Allowed trade for an Expert Advisor
+ ACCOUNT_MARGIN_MODE = 53 # Margin calculation mode
+
+
+class ENUM_ACCOUNT_INFO_DOUBLE(IntEnum):
+ ACCOUNT_BALANCE = 37 # Account balance in the deposit currency
+ ACCOUNT_CREDIT = 38 # Account credit in the deposit currency
+ ACCOUNT_PROFIT = 39 # Current profit of an account in the deposit currency
+ ACCOUNT_EQUITY = 40 # Account equity in the deposit currency
+ ACCOUNT_MARGIN = 41 # Account margin used in the deposit currency
+ ACCOUNT_MARGIN_FREE = 42 # Free margin of an account in the deposit currency
+ ACCOUNT_MARGIN_LEVEL = 43 # Account margin level in percents
+ ACCOUNT_MARGIN_SO_CALL = 45 # Margin call level
+ ACCOUNT_MARGIN_SO_SO = 46 # Margin stop out level
+ ACCOUNT_MARGIN_INITIAL = 48 # Initial margin
+ ACCOUNT_MARGIN_MAINTENANCE = 49 # Maintenance margin
+ ACCOUNT_ASSETS = 50 # The current assets of an account
+ ACCOUNT_LIABILITIES = 51 # The current liabilities on an account
+ ACCOUNT_COMMISSION_BLOCKED = 52 # The current blocked commission amount on an account
+
+
+class ENUM_ACCOUNT_INFO_STRING(IntEnum):
+ ACCOUNT_NAME = 1 # Client name
+ ACCOUNT_SERVER = 3 # Trade server name
+ ACCOUNT_CURRENCY = 36 # Account currency
+ ACCOUNT_COMPANY = 2 # Name of a company that serves the account
+
+
+class ENUM_ACCOUNT_TRADE_MODE(IntEnum):
+ ACCOUNT_TRADE_MODE_DEMO = 0 # Demo account
+ ACCOUNT_TRADE_MODE_CONTEST = 1 # Contest account
+ ACCOUNT_TRADE_MODE_REAL = 2 # Real account
+
+
+class ENUM_ACCOUNT_STOPOUT_MODE(IntEnum):
+ ACCOUNT_STOPOUT_MODE_PERCENT = 0 # Account stop out mode in percents
+ ACCOUNT_STOPOUT_MODE_MONEY = 1 # Account stop out mode in money
+
+
+class ENUM_ACCOUNT_MARGIN_MODE(IntEnum):
+ ACCOUNT_MARGIN_MODE_RETAIL_NETTING = 0 # Used for the OTC markets to interpret positions in the "netting" mode
+ ACCOUNT_MARGIN_MODE_EXCHANGE = 1 # Used for the exchange markets
+ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING = 2 # Used for the exchange markets where individual positions are possible
+
+
+# Trade Constants:
+# History Database Properties
+
+class ENUM_SERIES_INFO_INTEGER(IntEnum):
+ SERIES_BARS_COUNT = 0 # Bars count for the symbol-period for the current moment
+ SERIES_FIRSTDATE = 1 # The very first date for the symbol-period for the current moment
+ SERIES_LASTBAR_DATE = 5 # Open time of the last bar of the symbol-period
+ SERIES_SERVER_FIRSTDATE = 2 # The very first date in the history of the symbol on the server regardless of the timeframe
+ SERIES_TERMINAL_FIRSTDATE = 3 # The very first date in the history of the symbol in the client terminal, regardless of the timeframe
+ SERIES_SYNCHRONIZED = 4 #S ymbol/period data synchronization flag for the current moment
+
+
+# Order Properties
+
+class ENUM_ORDER_PROPERTY_INTEGER(IntEnum):
+ ORDER_TICKET = 22 # Order ticket. Unique number assigned to each order
+ ORDER_TIME_SETUP = 1 # Order setup time
+ ORDER_TYPE = 4 # Order type
+ ORDER_STATE = 14 # Order state
+ ORDER_TIME_EXPIRATION = 2 # Order expiration time
+ ORDER_TIME_DONE = 3 # Order execution or cancellation time
+ ORDER_TIME_SETUP_MSC = 18 # The time of placing an order for execution in milliseconds since 01.01.1970
+ ORDER_TIME_DONE_MSC = 19 # Order execution/cancellation time in milliseconds since 01.01.1970
+ ORDER_TYPE_FILLING = 5 # Order filling type
+ ORDER_TYPE_TIME = 6 # Order lifetime
+ ORDER_MAGIC = 15 # ID of an Expert Advisor that has placed the order (designed to ensure that each Expert Advisor places its own unique number)
+ ORDER_REASON = 23 # The reason or source for placing an order
+ ORDER_POSITION_ID = 17 # Position identifier that is set to an order as soon as it is executed.
+ # Each executed order results in a deal that opens or modifies an already existing position. The identifier of exactly this position is set to the executed order at this moment.
+ ORDER_POSITION_BY_ID = 21 # Identifier of an opposite position used for closing by order ORDER_TYPE_CLOSE_BY
+
+
+class ENUM_ORDER_PROPERTY_DOUBLE(IntEnum):
+ ORDER_VOLUME_INITIAL = 7 # Order initial volume
+ ORDER_VOLUME_CURRENT = 8 # Order current volume
+ ORDER_PRICE_OPEN = 9 # Price specified in the order
+ ORDER_SL = 12 # Stop Loss value
+ ORDER_TP = 13 # Take Profit value
+ ORDER_PRICE_CURRENT = 10 # The current price of the order symbol
+ ORDER_PRICE_STOPLIMIT = 11 #The Limit order price for the StopLimit order
+
+
+class ENUM_ORDER_PROPERTY_STRING(IntEnum):
+ ORDER_SYMBOL = 0 # Symbol of the order
+ ORDER_COMMENT = 16 # Order comment
+ ORDER_EXTERNAL_ID = 20 # Order identifier in an external trading system (on the Exchange)
+
+
+class ENUM_ORDER_TYPE(IntEnum):
+ ORDER_TYPE_BUY = 0 # Market Buy order
+ ORDER_TYPE_SELL = 1 # Market Sell order
+ ORDER_TYPE_BUY_LIMIT = 2 # Buy Limit pending order
+ ORDER_TYPE_SELL_LIMIT = 3 # Sell Limit pending order
+ ORDER_TYPE_BUY_STOP = 4 # Buy Stop pending order
+ ORDER_TYPE_SELL_STOP = 5 # Sell Stop pending order
+ ORDER_TYPE_BUY_STOP_LIMIT = 6 # Upon reaching the order price, a pending Buy Limit order is places at the StopLimit price
+ ORDER_TYPE_SELL_STOP_LIMIT = 7 # Upon reaching the order price, a pending Sell Limit order is places at the StopLimit price
+ ORDER_TYPE_CLOSE_BY = 8 # Order to close a position by an opposite one
+
+
+class ENUM_ORDER_STATE(IntEnum):
+ ORDER_STATE_STARTED = 0 # Order checked, but not yet accepted by broker
+ ORDER_STATE_PLACED = 1 # Order accepted
+ ORDER_STATE_CANCELED = 2 # Order canceled by client
+ ORDER_STATE_PARTIAL = 3 # Order partially executed
+ ORDER_STATE_FILLED = 4 # Order fully executed
+ ORDER_STATE_REJECTED = 5 # Order rejected
+ ORDER_STATE_EXPIRED = 6 # Order expired
+ ORDER_STATE_REQUEST_ADD = 7 # Order is being registered (placing to the trading system)
+ ORDER_STATE_REQUEST_MODIFY = 8 # Order is being modified (changing its parameters)
+ ORDER_STATE_REQUEST_CANCEL = 9 # Order is being deleted (deleting from the trading system)
+
+
+class ENUM_ORDER_TYPE_FILLING(IntEnum):
+ ORDER_FILLING_FOK = 0
+ ORDER_FILLING_IOC = 1
+ ORDER_FILLING_RETURN = 2
+
+
+class ENUM_ORDER_TYPE_TIME(IntEnum):
+ ORDER_TIME_GTC = 0
+ ORDER_TIME_DAY = 1
+ ORDER_TIME_SPECIFIED = 2
+ ORDER_TIME_SPECIFIED_DAY = 3
+
+
+class ENUM_ORDER_REASON(IntEnum):
+ ORDER_REASON_CLIENT = 0 # The order was placed from a desktop terminal
+ ORDER_REASON_MOBILE = 1 # The order was placed from a mobile application
+ ORDER_REASON_WEB = 2 # The order was placed from a web platform
+ ORDER_REASON_EXPERT = 3 # The order was placed from an MQL5-program, i.e. by an Expert Advisor or a script
+ ORDER_REASON_SL = 4 # The order was placed as a result of Stop Loss activation
+ ORDER_REASON_TP = 5 # The order was placed as a result of Take Profit activation
+ ORDER_REASON_SO = 6 # The order was placed as a result of the Stop Out event
+
+
+# Position Properties
+
+class ENUM_POSITION_PROPERTY_INTEGER(IntEnum):
+ POSITION_TICKET = 17 # Position ticket
+ POSITION_TIME = 1 # Position open time
+ POSITION_TIME_MSC = 14 # Position opening time in milliseconds since 01.01.1970
+ POSITION_TIME_UPDATE = 15 # Position changing time in seconds since 01.01.1970
+ POSITION_TIME_UPDATE_MSC = 16 # Position changing time in milliseconds since 01.01.1970
+ POSITION_TYPE = 2 # Position type
+ POSITION_MAGIC = 12 # Position magic number
+ POSITION_IDENTIFIER = 13 # Position identifier is a unique number that is assigned to every newly opened position
+ # and doesn't change during the entire lifetime of the position. Position turnover doesn't change its identifier.
+ POSITION_REASON = 18 # The reason for opening a position
+
+
+class ENUM_POSITION_PROPERTY_DOUBLE(IntEnum):
+ POSITION_VOLUME = 3 # Position volume
+ POSITION_PRICE_OPEN = 4 # Position open price
+ POSITION_SL = 6 # Stop Loss level of opened position
+ POSITION_TP = 7 # Take Profit level of opened position
+ POSITION_PRICE_CURRENT = 5 # Current price of the position symbol
+ POSITION_SWAP = 9 # Cumulative swap
+ POSITION_PROFIT = 10 # Current profit
+
+
+class ENUM_POSITION_PROPERTY_STRING(IntEnum):
+ POSITION_SYMBOL = 0 # Symbol of the position
+ POSITION_COMMENT = 11 # Position comment
+
+
+class ENUM_POSITION_TYPE(IntEnum):
+ POSITION_TYPE_BUY = 0 # Buy
+ POSITION_TYPE_SELL = 1 # Sell
+
+
+class ENUM_POSITION_REASON(IntEnum):
+ POSITION_REASON_CLIENT = 0 # The position was opened as a result of activation of an order placed from a desktop terminal
+ POSITION_REASON_MOBILE = 1 # The position was opened as a result of activation of an order placed from a mobile application
+ POSITION_REASON_WEB = 2 # The position was opened as a result of activation of an order placed from the web platform
+ POSITION_REASON_EXPERT = 3 # The position was opened as a result of activation of an order placed from an MQL5 program
+
+
+# Deal Properties
+
+class ENUM_DEAL_PROPERTY_INTEGER(IntEnum):
+ DEAL_TICKET = 15 # Deal ticket. Unique number assigned to each deal
+ DEAL_ORDER = 1 # Deal order number
+ DEAL_TIME = 2 # Deal time
+ DEAL_TIME_MSC = 13 # The time of a deal execution in milliseconds since 01.01.1970
+ DEAL_TYPE = 3 # Deal type
+ DEAL_ENTRY = 4 # Deal entry - entry in, entry out, reverse
+ DEAL_MAGIC = 11 # Deal magic number
+ DEAL_REASON = 16 # The reason or source for deal execution
+ DEAL_POSITION_ID = 12 # Identifier of a position
+
+
+class ENUM_DEAL_PROPERTY_DOUBLE(IntEnum):
+ DEAL_VOLUME = 5 # Deal volume
+ DEAL_PRICE = 6 # Deal price
+ DEAL_COMMISSION = 7 # Deal commission
+ DEAL_SWAP = 8 # Cumulative swap on close
+ DEAL_PROFIT = 9 # Deal profit
+
+
+class ENUM_DEAL_PROPERTY_STRING(IntEnum):
+ DEAL_SYMBOL = 0 # Deal symbol
+ DEAL_COMMENT = 10 # Deal comment
+ DEAL_EXTERNAL_ID = 14 # Deal identifier in an external trading system (on the Exchange)
+
+
+class ENUM_DEAL_TYPE(IntEnum):
+ DEAL_TYPE_BUY = 0 # Buy
+ DEAL_TYPE_SELL = 1 # Sell
+ DEAL_TYPE_BALANCE = 2 # Balance
+ DEAL_TYPE_CREDIT = 3 # Credit
+ DEAL_TYPE_CHARGE = 4 # Additional charge
+ DEAL_TYPE_CORRECTION = 5 # Correction
+ DEAL_TYPE_BONUS = 6 # Bonus
+ DEAL_TYPE_COMMISSION = 7 # Additional commission
+ DEAL_TYPE_COMMISSION_DAILY = 8 # Daily commission
+ DEAL_TYPE_COMMISSION_MONTHLY = 9 # Monthly commission
+ DEAL_TYPE_COMMISSION_AGENT_DAILY = 10 # Daily agent commission
+ DEAL_TYPE_COMMISSION_AGENT_MONTHLY = 11 # Monthly agent commission
+ DEAL_TYPE_INTEREST = 12 # Interest rate
+ DEAL_TYPE_BUY_CANCELED = 13 # Canceled buy deal
+ DEAL_TYPE_SELL_CANCELED = 14 # Canceled sell deal
+ DEAL_DIVIDEND = 15 # Dividend operations
+ DEAL_DIVIDEND_FRANKED = 16 # Franked (non-taxable) dividend operations
+ DEAL_TAX = 17 # Tax charges
+
+
+class ENUM_DEAL_ENTRY(IntEnum):
+ DEAL_ENTRY_IN = 0 # Entry in
+ DEAL_ENTRY_OUT = 1 # Entry out
+ DEAL_ENTRY_INOUT = 2 # Reverse
+ DEAL_ENTRY_STATE = 255 # Close a position by an opposite one
+
+
+class ENUM_DEAL_REASON(IntEnum):
+ DEAL_REASON_CLIENT = 0 # The deal was executed as a result of activation of an order placed from a desktop terminal
+ DEAL_REASON_MOBILE = 1 # The deal was executed as a result of activation of an order placed from a mobile application
+ DEAL_REASON_WEB = 2 # The deal was executed as a result of activation of an order placed from the web platform
+ DEAL_REASON_EXPERT = 3 # The deal was executed as a result of activation of an order placed from an MQL5 program, i.e. an Expert Advisor or a script
+ DEAL_REASON_SL = 4 # The deal was executed as a result of Stop Loss activation
+ DEAL_REASON_TP = 5 # The deal was executed as a result of Take Profit activation
+ DEAL_REASON_SO = 6 # The deal was executed as a result of the Stop Out event
+ DEAL_REASON_ROLLOVER = 7 # The deal was executed due to a rollover
+ DEAL_REASON_VMARGIN = 8 # The deal was executed after charging the variation margin
+ DEAL_REASON_SPLIT = 9 # The deal was executed after the split (price reduction) of an instrument, which had an open position during split announcement
+
+
+# Trade Operation Types
+
+class ENUM_TRADE_REQUEST_ACTIONS(IntEnum):
+ TRADE_ACTION_DEAL = 1 # Place a trade order for an immediate execution with the specified parameters (market order)
+ TRADE_ACTION_PENDING = 5 # Place a trade order for the execution under specified conditions (pending order)
+ TRADE_ACTION_SLTP = 6 # Modify Stop Loss and Take Profit values of an opened position
+ TRADE_ACTION_MODIFY = 7 # Modify the parameters of the order placed previously
+ TRADE_ACTION_REMOVE = 8 # Delete the pending order placed previously
+ TRADE_ACTION_CLOSE_BY = 10 # Close a position by an opposite one
+
+
+# Trade Transaction Types
+
+class ENUM_TRADE_TRANSACTION_TYPE(IntEnum):
+ TRADE_TRANSACTION_ORDER_ADD = 0 # Adding a new open order
+ TRADE_TRANSACTION_ORDER_UPDATE = 1 # Updating an open order. The updates include not only evident changes from the client terminal
+ # or a trade server sides but also changes of an order state when setting it
+ # (for example, transition from ORDER_STATE_STARTED to ORDER_STATE_PLACED or from ORDER_STATE_PLACED to ORDER_STATE_PARTIAL, etc.).
+ TRADE_TRANSACTION_ORDER_DELETE = 2 # Removing an order from the list of the open ones. An order can be deleted from the open ones as a result of setting an appropriate request
+ # or execution (filling) and moving to the history.
+ TRADE_TRANSACTION_DEAL_ADD = 6 # Adding a deal to the history. The action is performed as a result of an order execution or performing operations with an account balance.
+ TRADE_TRANSACTION_DEAL_UPDATE = 7 # Updating a deal in the history. There may be cases when a previously executed deal is changed on a server.
+ # For example, a deal has been changed in an external trading system (exchange) where it was previously transferred by a broker.
+ TRADE_TRANSACTION_DEAL_DELETE = 8 # Deleting a deal from the history. There may be cases when a previously executed deal is deleted from a server.
+ # For example, a deal has been deleted in an external trading system (exchange) where it was previously transferred by a broker.
+ TRADE_TRANSACTION_HISTORY_ADD = 3 # Adding an order to the history as a result of execution or cancellation.
+ TRADE_TRANSACTION_HISTORY_UPDATE = 4 # Changing an order located in the orders history. This type is provided for enhancing functionality on a trade server side.
+ TRADE_TRANSACTION_HISTORY_DELETE = 5 # Deleting an order from the orders history. This type is provided for enhancing functionality on a trade server side.
+ TRADE_TRANSACTION_POSITION = 9 # Changing a position not related to a deal execution. This type of transaction shows that a position has been changed on a trade server side.
+ # Position volume, open price, Stop Loss and Take Profit levels can be changed. Data on changes are submitted in MqlTradeTransaction structure via OnTradeTransaction handler.
+ # Position change (adding, changing or closing), as a result of a deal execution, does not lead to the occurrence of TRADE_TRANSACTION_POSITION transaction.
+ TRADE_TRANSACTION_REQUEST = 10 # Notification of the fact that a trade request has been processed by a server and processing result has been received.
+ # Only type field (trade transaction type) must be analyzed for such transactions in MqlTradeTransaction structure.
+ # The second and third parameters of OnTradeTransaction (request and result) must be analyzed for additional data.
+
+
+# Trade Orders in Depth Of Market
+
+class ENUM_BOOK_TYPE(IntEnum):
+ BOOK_TYPE_SELL = 1 # Sell order (Offer)
+ BOOK_TYPE_BUY = 2 # Buy order (Bid)
+ BOOK_TYPE_SELL_MARKET = 3 # Sell order by Market
+ BOOK_TYPE_BUY_MARKET = 4 # Buy order by Market
+
+
+# Object Types
+
+class ENUM_OBJECT(IntEnum):
+ OBJ_VLINE = 0 # Vertical Line
+ OBJ_HLINE = 1 # Horizontal Line
+ OBJ_TREND = 2 # Trend Line
+ OBJ_TRENDBYANGLE = 3 # Trend Line By Angle
+ OBJ_CYCLES = 4 # Cycle Lines
+ OBJ_ARROWED_LINE = 108 # Arrowed Line
+ OBJ_CHANNEL = 5 # Equidistant Channel
+ OBJ_STDDEVCHANNEL = 6 # Standard Deviation Channel
+ OBJ_REGRESSION = 7 # Linear Regression Channel
+ OBJ_PITCHFORK = 8 # AndrewsÂ’ Pitchfork
+ OBJ_GANNLINE = 9 # Gann Line
+ OBJ_GANNFAN = 10 # Gann Fan
+ OBJ_GANNGRID = 11 # Gann Grid
+ OBJ_FIBO = 12 # Fibonacci Retracement
+ OBJ_FIBOTIMES = 13 # Fibonacci Time Zones
+ OBJ_FIBOFAN = 14 # Fibonacci Fan
+ OBJ_FIBOARC = 15 # Fibonacci Arcs
+ OBJ_FIBOCHANNEL = 16 # Fibonacci Channel
+ OBJ_EXPANSION = 17 # Fibonacci Expansion
+ OBJ_ELLIOTWAVE5 = 18 # Elliott Motive Wave
+ OBJ_ELLIOTWAVE3 = 19 # Elliott Correction Wave
+ OBJ_RECTANGLE = 20 # Rectangle
+ OBJ_TRIANGLE = 21 # Triangle
+ OBJ_ELLIPSE = 22 # Ellipse
+ OBJ_ARROW_THUMB_UP = 23 # Thumbs Up
+ OBJ_ARROW_THUMB_DOWN = 24 # Thumbs Down
+ OBJ_ARROW_UP = 25 # Arrow Up
+ OBJ_ARROW_DOWN = 26 # Arrow Down
+ OBJ_ARROW_STOP = 27 # Stop Sign
+ OBJ_ARROW_CHECK = 28 # Check Sign
+ OBJ_ARROW_LEFT_PRICE = 29 # Left Price Label
+ OBJ_ARROW_RIGHT_PRICE = 30 # Right Price Label
+ OBJ_ARROW_BUY = 31 # Buy Sign
+ OBJ_ARROW_SELL = 32 # Sell Sign
+ OBJ_ARROW = 100 # Arrow
+ OBJ_TEXT = 101 # Text
+ OBJ_LABEL = 102 # Label
+ OBJ_BUTTON = 103 # Button
+ OBJ_CHART = 104 # Chart
+ OBJ_BITMAP = 105 # Bitmap
+ OBJ_BITMAP_LABEL = 106 # Bitmap Label
+ OBJ_EDIT = 107 # Edit
+ OBJ_EVENT = 109 # The "Event" object corresponding to an event in the economic calendar
+ OBJ_RECTANGLE_LABEL = 110 # The "Rectangle label" object for creating and designing the custom graphical interface.
+
+
+# Object Properties
+
+class ENUM_OBJECT_PROPERTY_DOUBLE(IntEnum):
+ OBJPROP_PRICE = 9 # Price coordinate
+ OBJPROP_LEVELVALUE = 204 # Level value
+ OBJPROP_SCALE = 1006 # Scale (properties of Gann objects and Fibonacci Arcs)
+ OBJPROP_ANGLE = 1007 # Angle. For the objects with no angle specified, created from a program, the value is equal to EMPTY_VALUE
+ OBJPROP_DEVIATION = 1010 # Deviation for the Standard Deviation Channel
+
+
+class ENUM_OBJECT_PROPERTY_INTEGER(IntEnum):
+ OBJPROP_COLOR = 0 # Color
+ OBJPROP_STYLE = 1 # Style
+ OBJPROP_WIDTH = 2 # Line thickness
+ OBJPROP_BACK = 3 # Object in the background
+ OBJPROP_ZORDER = 207 # Priority of a graphical object for receiving events of clicking on a chart (CHARTEVENT_CLICK).
+ # The default zero value is set when creating an object; the priority can be increased if necessary.
+ # When objects are placed one atop another, only one of them with the highest priority will receive the CHARTEVENT_CLICK event.
+ OBJPROP_FILL = 1031 # Fill an object with color (for OBJ_RECTANGLE, OBJ_TRIANGLE, OBJ_ELLIPSE, OBJ_CHANNEL, OBJ_STDDEVCHANNEL, OBJ_REGRESSION)
+ OBJPROP_HIDDEN = 208 # Prohibit showing of the name of a graphical object in the list of objects from the terminal menu "Charts" - "Objects" - "List of objects".
+ # The true value allows to hide an object from the list. By default, true is set to the objects that display calendar events,
+ # trading history and to the objects created from MQL5 programs. To see such graphical objects and access their properties, click on the "All" button in the "List of objects" window.
+ OBJPROP_SELECTED = 4 # Object is selected
+ OBJPROP_READONLY = 1028 # Ability to edit text in the Edit object
+ OBJPROP_TYPE = 7 # Object type
+ OBJPROP_TIME = 8 # Time coordinate
+ OBJPROP_SELECTABLE = 10 # Object availability
+ OBJPROP_CREATETIME = 11 # Time of object creation
+ OBJPROP_LEVELS = 200 # Number of levels
+ OBJPROP_LEVELCOLOR = 201 # Color of the line-level
+ OBJPROP_LEVELSTYLE = 202 # Style of the line-level
+ OBJPROP_LEVELWIDTH = 203 # Thickness of the line-level
+ OBJPROP_ALIGN = 1036 # Horizontal text alignment in the "Edit" object (OBJ_EDIT)
+ OBJPROP_FONTSIZE = 1002 # Font size
+ OBJPROP_RAY_LEFT = 1003 # Ray goes to the left
+ OBJPROP_RAY_RIGHT = 1004 # Ray goes to the right
+ OBJPROP_RAY = 1032 # A vertical line goes through all the windows of a chart
+ OBJPROP_ELLIPSE = 1005 # Showing the full ellipse of the Fibonacci Arc object (OBJ_FIBOARC)
+ OBJPROP_ARROWCODE = 1008 # Arrow code for the Arrow object
+ OBJPROP_TIMEFRAMES = 12 # Visibility of an object at timeframes
+ OBJPROP_ANCHOR = 1011 # Location of the anchor point of a graphical object
+ OBJPROP_XDISTANCE = 1012 # The distance in pixels along the X axis from the binding corner
+ OBJPROP_YDISTANCE = 1013 # The distance in pixels along the Y axis from the binding corner
+ OBJPROP_DIRECTION = 1014 # Trend of the Gann object
+ OBJPROP_DEGREE = 1015 # Level of the Elliott Wave Marking
+ OBJPROP_DRAWLINES = 1016 # Displaying lines for marking the Elliott Wave
+ OBJPROP_STATE = 1018 # Button state (pressed / depressed)
+ OBJPROP_CHART_ID = 1030 # ID of the "Chart" object (OBJ_CHART). It allows working with the properties of this object like with a normal chart using the functions described in Chart Operations, but there some exceptions.
+ OBJPROP_XSIZE = 1019 # The object's width along the X axis in pixels. Specified for OBJ_LABEL (read only), OBJ_BUTTON, OBJ_CHART, OBJ_BITMAP, OBJ_BITMAP_LABEL, OBJ_EDIT, OBJ_RECTANGLE_LABEL objects.
+ OBJPROP_YSIZE = 1020 # The object's height along the Y axis in pixels. Specified for OBJ_LABEL (read only), OBJ_BUTTON, OBJ_CHART, OBJ_BITMAP, OBJ_BITMAP_LABEL, OBJ_EDIT, OBJ_RECTANGLE_LABEL objects.
+ OBJPROP_XOFFSET = 1033 # The X coordinate of the upper left corner of the rectangular visible area in the graphical objects "Bitmap Label" and "Bitmap" (OBJ_BITMAP_LABEL and OBJ_BITMAP).
+ # The value is set in pixels relative to the upper left corner of the original image.
+ OBJPROP_YOFFSET = 1034 # The Y coordinate of the upper left corner of the rectangular visible area in the graphical objects "Bitmap Label" and "Bitmap" (OBJ_BITMAP_LABEL and OBJ_BITMAP).
+ # The value is set in pixels relative to the upper left corner of the original image.
+ OBJPROP_PERIOD = 1022 # Timeframe for the Chart object
+ OBJPROP_DATE_SCALE = 1023 # Displaying the time scale for the Chart object
+ OBJPROP_PRICE_SCALE = 1024 # Displaying the price scale for the Chart object
+ OBJPROP_CHART_SCALE = 1027 # The scale for the Chart object
+ OBJPROP_BGCOLOR = 1025 # The background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
+ OBJPROP_CORNER = 1026 # The corner of the chart to link a graphical object
+ OBJPROP_BORDER_TYPE = 1029 # Border type for the "Rectangle label" object
+ OBJPROP_BORDER_COLOR = 1035 # Border color for the OBJ_EDIT and OBJ_BUTTON objects
+
+
+class ENUM_OBJECT_PROPERTY_STRING(IntEnum):
+ OBJPROP_NAME = 5 # Object name
+ OBJPROP_TEXT = 6 # Description of the object (the text contained in the object)
+ OBJPROP_TOOLTIP = 206 # The text of a tooltip. If the property is not set, then the tooltip generated automatically by the terminal is shown. A tooltip can be disabled by setting the "\n" (line feed) value to it
+ OBJPROP_LEVELTEXT = 205 # Level description
+ OBJPROP_FONT = 1001 # Font
+ OBJPROP_BMPFILE = 1017 # The name of BMP-file for Bitmap Label.
+ OBJPROP_SYMBOL = 1021 # Symbol for the Chart object
+
+
+class ENUM_BORDER_TYPE(IntEnum):
+ BORDER_FLAT = 0 # Flat form
+ BORDER_RAISED = 1 # Prominent form
+ BORDER_SUNKEN = 2 # Concave form
+
+
+class ENUM_ALIGN_MODE(IntEnum):
+ ALIGN_LEFT = 1 # Left alignment
+ ALIGN_CENTER = 2 # Centered (only for the Edit object)
+ ALIGN_RIGHT = 0 # Right alignment
+
+
+# Price Constants
+
+class ENUM_APPLIED_PRICE(IntEnum):
+ PRICE_CLOSE = 1 # Close price
+ PRICE_OPEN = 2 # Open price
+ PRICE_HIGH = 3 # The maximum price for the period
+ PRICE_LOW = 4 # The minimum price for the period
+ PRICE_MEDIAN = 5 # Median price, (high + low)/2
+ PRICE_TYPICAL = 6 # Typical price, (high + low + close)/3
+ PRICE_WEIGHTED = 7 # Average price, (high + low + close + close)/4
+
+
+class ENUM_APPLIED_VOLUME(IntEnum):
+ VOLUME_TICK = 0 # Tick volume
+ VOLUME_REAL = 1 # Trade volume
+
+
+class ENUM_STO_PRICE(IntEnum):
+ STO_LOWHIGH = 0 # Calculation is based on Low/High prices
+ STO_CLOSECLOSE = 1 # Calculation is based on Close/Close prices
+
+
+# Smoothing Methods
+
+class ENUM_MA_METHOD(IntEnum):
+ MODE_SMA = 0 # Simple averaging
+ MODE_EMA = 1 # Exponential averaging
+ MODE_SMMA = 2 # Smoothed averaging
+ MODE_LWMA = 3 # Linear-weighted averaging
+
+
+# Indicator constants
+
+class ENUM_INDICATOR(IntEnum):
+ IND_AC = 5 # Accelerator Oscillator
+ IND_AD = 6 # Accumulation/Distribution
+ IND_ADX = 8 # Average Directional Index
+ IND_ADXW = 9 # ADX by Welles Wilder
+ IND_ALLIGATOR = 7 # Alligator
+ IND_AMA = 40 # Adaptive Moving Average
+ IND_AO = 11 # Awesome Oscillator
+ IND_ATR = 10 # Average True Range
+ IND_BANDS = 13 # Bollinger Bands®
+ IND_BEARS = 12 # Bears Power
+ IND_BULLS = 14 # Bulls Power
+ IND_BWMFI = 22 # Market Facilitation Index
+ IND_CCI = 15 # Commodity Channel Index
+ IND_CHAIKIN = 41 # Chaikin Oscillator
+ IND_CUSTOM = 43 # Custom indicator
+ IND_DEMA = 36 # Double Exponential Moving Average
+ IND_DEMARKER = 16 # DeMarker
+ IND_ENVELOPES = 17 # Envelopes
+ IND_FORCE = 18 # Force Index
+ IND_FRACTALS = 19 # Fractals
+ IND_FRAMA = 39 # Fractal Adaptive Moving Average
+ IND_GATOR = 20 # Gator Oscillator
+ IND_ICHIMOKU = 21 # Ichimoku Kinko Hyo
+ IND_MA = 26 # Moving Average
+ IND_MACD = 23 # MACD
+ IND_MFI = 25 # Money Flow Index
+ IND_MOMENTUM = 24 # Momentum
+ IND_OBV = 28 # On Balance Volume
+ IND_OSMA = 27 # OsMA
+ IND_RSI = 30 # Relative Strength Index
+ IND_RVI = 31 # Relative Vigor Index
+ IND_SAR = 29 # Parabolic SAR
+ IND_STDDEV = 32 # Standard Deviation
+ IND_STOCHASTIC = 33 # Stochastic Oscillator
+ IND_TEMA = 37 # Triple Exponential Moving Average
+ IND_TRIX = 38 # Triple Exponential Moving Averages Oscillator
+ IND_VIDYA = 42 # Variable Index Dynamic Average
+ IND_VOLUMES = 34 # Volumes
+ IND_WPR = 35 # Williams' Percent Ranges
+
+
+class ENUM_DATATYPE(IntEnum):
+ TYPE_BOOL = 1
+ TYPE_CHAR = 2
+ TYPE_UCHAR = 3
+ TYPE_SHORT = 4
+ TYPE_USHORT = 5
+ TYPE_COLOR = 6
+ TYPE_INT = 7
+ TYPE_UINT = 8
+ TYPE_DATETIME = 9
+ TYPE_LONG = 10
+ TYPE_ULONG = 11
+ TYPE_FLOAT = 12
+ TYPE_DOUBLE = 13
+ TYPE_STRING = 14
diff --git a/PyMtApi5/mtrpcclient.py b/PyMtApi5/mtrpcclient.py
new file mode 100644
index 00000000..c11fe9f2
--- /dev/null
+++ b/PyMtApi5/mtrpcclient.py
@@ -0,0 +1,162 @@
+import websockets
+from websockets.sync.client import connect as ws_connect
+from threading import Thread, Lock, Condition
+import logging
+from enum import IntEnum
+
+class MtNotification(IntEnum):
+ ClientReady = 0
+
+class MtMessageType(IntEnum):
+ Command = 0
+ Response = 1
+ Event = 2
+ ExpertList = 3
+ ExpertAdded = 4
+ ExpertRemoved = 5
+ Notification = 6
+
+class CommandTask:
+ def __init__(self):
+ self.locker = Lock()
+ self.waiter = Condition()
+ self.response = None
+
+ def wait_response(self, time):
+ with self.waiter:
+ self.waiter.wait(time)
+ with self.locker:
+ return self.response
+
+ def set_response(self, response):
+ with self.locker:
+ self.response = response
+ with self.waiter:
+ self.waiter.notify()
+
+class MtRpcClient:
+ def __init__(self, callback=None):
+ self.__logger = logging.getLogger(__name__)
+ self.__callback = callback
+ self.__notification_tasks = dict()
+ self.__tasks = dict()
+ self.__next_command_id = 0
+ self.__lock = Lock()
+
+ def connect(self, url):
+ self.__logger.debug(f"connecting to {url}")
+ self.__ws = ws_connect(url);
+ self.__receive_thread = Thread(target = self.__receive_messages_thread)
+ self.__receive_thread.start()
+
+ def disconnect(self):
+ self.__ws.close()
+ self.__receive_thread.join()
+ self.__logger.debug("disconnected")
+
+ def request_expert_list(self):
+ task = CommandTask()
+ with self.__lock:
+ self.__notification_tasks[MtNotification.ClientReady] = task
+ self.__ws.send(self.__create_notification(MtNotification.ClientReady))
+ response = task.wait_response(10)
+ with self.__lock:
+ self.__notification_tasks.pop(MtNotification.ClientReady)
+ return response
+
+ def send_command(self, expert_handle, command_type, payload = None):
+ command_id = self.__next_command_id
+ self.__next_command_id += 1
+ task = CommandTask()
+ with self.__lock:
+ self.__tasks[command_id] = task
+ self.__ws.send(self.__create_mt_command(expert_handle, command_id, command_type, payload))
+ response = task.wait_response(10)
+ with self.__lock:
+ self.__tasks.pop(command_id)
+ return response
+
+ # Private methods
+
+ def __process_message(self, message):
+ self.__logger.debug(f"process_message: {message}")
+ pieces = message.split(';', 1)
+ if len(pieces) != 2 or not pieces[0] or not pieces[1]:
+ self.__logger.warning("process_message: Invalid message format");
+ return
+ message_type = MtMessageType(int(pieces[0]))
+ if message_type == MtMessageType.ExpertList:
+ self.__process_expert_list(pieces[1])
+ elif message_type == MtMessageType.Event:
+ self.__process_event(pieces[1])
+ elif message_type == MtMessageType.Response:
+ self.__process_response(pieces[1])
+ elif message_type == MtMessageType.ExpertAdded:
+ self.__process_expert_added(pieces[1])
+ elif message_type == MtMessageType.ExpertRemoved:
+ self.__process_expert_removed(pieces[1])
+ else:
+ self.__logger.warning(f"received unknown message type: {message_type}")
+
+ def __process_expert_list(self, payload):
+ pieces = payload.split(',')
+ experts = list()
+ for p in pieces:
+ experts.append(int(p))
+ with self.__lock:
+ task = self.__notification_tasks.get(MtNotification.ClientReady)
+ if task is not None:
+ task.set_response(experts)
+
+ def __process_event(self, payload):
+ pieces = payload.split(';', 2)
+ if len(pieces) != 3 or not pieces[0] or not pieces[1] or not pieces[2]:
+ self.__logger.warning("process_event: Invalid message format");
+ return
+ if self.__callback is not None:
+ self.__callback.mt_rpc_on_event(int(pieces[0]), int(pieces[1]), pieces[2])
+
+ def __process_response(self, payload):
+ pieces = payload.split(';', 2)
+ if len(pieces) != 3 or not pieces[0] or not pieces[1] or not pieces[2]:
+ self.__logger.warning("process_response: Invalid message format");
+ return
+ command_id = int(pieces[1])
+ with self.__lock:
+ task = self.__tasks.get(command_id)
+ if task is not None:
+ task.set_response(pieces[2])
+
+ def __process_expert_added(self, payload):
+ if self.__callback is not None:
+ self.__callback.mt_rpc_on_expert_added(int(payload))
+
+ def __process_expert_removed(self, payload):
+ if self.__callback is not None:
+ self.__callback.mt_rpc_on_expert_removed(int(payload))
+
+ def __receive_messages_thread(self):
+ self.__logger.debug("started receive_messages thread")
+ while True:
+ try:
+ message = self.__ws.recv()
+ self.__process_message(message)
+ except websockets.exceptions.ConnectionClosed:
+ self.__logger.info("Connection closed")
+ if self.__callback is not None:
+ self.__callback.mt_rcp_on_disconnect()
+ break
+ except Exception as e:
+ self.__logger.error(e)
+ if self.__callback is not None:
+ self.__callback.mt_rpc_on_connection_failed(str(e))
+ break
+ self.__logger.debug("function receive_messages finished")
+
+ def __create_notification(self, notification_type):
+ return f"{int(MtMessageType.Notification)};{notification_type}"
+
+ def __create_mt_command(self, expert_handle, command_id, command_type, payload):
+ if (payload is None):
+ return f"{MtMessageType.Command};{expert_handle};{command_id};{command_type}";
+ return f"{MtMessageType.Command};{expert_handle};{command_id};{command_type};{payload}";