From 04d2f2e189fd415c7c7ab800eac914d83db25ed2 Mon Sep 17 00:00:00 2001 From: Vyacheslav Demidyuk Date: Wed, 15 Jan 2025 18:31:56 +0200 Subject: [PATCH] PyMtApi5: started implementation MtApi client MT5 for python3 --- PyMtApi5/client.py | 203 +++++++++ PyMtApi5/mt5apiclient.py | 450 +++++++++++++++++++ PyMtApi5/mt5commandtype.py | 254 +++++++++++ PyMtApi5/mt5enums.py | 855 +++++++++++++++++++++++++++++++++++++ PyMtApi5/mtrpcclient.py | 162 +++++++ 5 files changed, 1924 insertions(+) create mode 100644 PyMtApi5/client.py create mode 100644 PyMtApi5/mt5apiclient.py create mode 100644 PyMtApi5/mt5commandtype.py create mode 100644 PyMtApi5/mt5enums.py create mode 100644 PyMtApi5/mtrpcclient.py 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}";