commit 027f9fd8de383e9e120845190cef1e5e41e2ef06 Author: Ichinga Samuel Date: Wed Oct 11 09:49:06 2023 +0100 add docs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..572c97b --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so +# Distribution / packaging +.Python +env/ +venv/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints + +# Ide environment +*.idea/ + +.pypirc + +.vscode/ + +# config file +aiomql.json \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6a127cb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Ichinga Samuel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b223962 --- /dev/null +++ b/README.md @@ -0,0 +1,3606 @@ +# aiomql +![GitHub](https://img.shields.io/github/license/ichinga-samuel/aiomql?style=plastic) +![GitHub issues](https://img.shields.io/github/issues/ichinga-samuel/aiomql?style=plastic) +![PyPI](https://img.shields.io/pypi/v/aiomql) + + +## Installation +```bash +pip install aiomql +``` + +## Key Features +- Asynchronous Python Library For MetaTrader 5 +- Build bots for trading in different financial markets using a bot factory +- Use threadpool executors to run multiple strategies on multiple instruments concurrently +- Record and keep track of trades and strategies in csv files. +- Utility classes for using the MetaTrader 5 Library +- Sample Pre-Built strategies + +## Simple Usage as an asynchronous MetaTrader5 Libray +```python +import asyncio + +# import the class +from aiomql import MetaTrader, Account, TimeFrame, OrderType + + +async def main(): + # Assuming your login details are already defined in the aiomql.json somewhere in your project directory. + acc = Account() + + # if this is unsuccessful the program exits + await acc.sign_in() + + # print all available symbols + print(acc.symbols) + + +asyncio.run(main()) +``` +## As a Bot Building FrameWork using a Sample Strategy +```python +from aiomql import Bot +from aiomql import ForexSymbol +from aiomql import FingerTrap + +# Create a bot instance +bot = Bot() + +# Choose a Symbol to trade +symbol = ForexSymbol(name='EURUSD') + +# Create a strategy +ft_eur_usd = FingerTrap(symbol=symbol) + +# Add strategy to Bot +bot.add_strategy(ft_eur_usd) + +# run the bot +bot.execute() +``` +## API Documentation + + +# aiomql + + + +# aiomql.account + + + +## Account Objects + +```python +class Account(AccountInfo) +``` + +A class for managing a trading account. A singleton class. +A subclass of AccountInfo. All AccountInfo attributes are available in this class. + +**Attributes**: + +- `connected` _bool_ - Status of connection to MetaTrader 5 Terminal +- `symbols` _set[SymbolInfo]_ - A set of available symbols for the financial market. + + +**Notes**: + + Other Account properties are defined in the AccountInfo class. + + + +#### refresh + +```python +async def refresh() +``` + +Refreshes the account instance with the latest account details from the MetaTrader 5 terminal + + + +#### account\_info + +```python +@property +def account_info() -> dict +``` + +Get account login, server and password details. If the login attribute of the account instance returns +a falsy value, the config instance is used to get the account details. + +**Returns**: + +- `dict` - A dict of login, server and password details + + +**Notes**: + + This method will only look for config details in the config instance if the login attribute of the + account Instance returns a falsy value + + + +#### sign\_in + +```python +async def sign_in() -> bool +``` + +Connect to a trading account. + +**Returns**: + +- `bool` - True if login was successful else False + + + +#### has\_symbol + +```python +def has_symbol(symbol: str | Type[SymbolInfo]) +``` + +Checks to see if a symbol is available for a trading account + +**Arguments**: + + symbol (str | SymbolInfo): + + +**Returns**: + +- `bool` - True if symbol is present otherwise False + + + +#### symbols\_get + +```python +async def symbols_get() -> set[SymbolInfo] +``` + +Get all financial instruments from the MetaTrader 5 terminal available for the current account. + +**Returns**: + +- `set[Symbol]` - A set of available symbols. + + + +# aiomql.bot\_builder + + + +## Bot Objects + +```python +class Bot() +``` + +The bot builder class. +This class is used to build a bot by adding strategies and running them in the executor. + +**Attributes**: + +- `account` _Account_ - Account Object. +- `executor` - The default thread executor. +- `symbols` _set[Symbols]_ - A set of symbols for the trading session + + + +#### initialize + +```python +async def initialize() +``` + +Prepares the bot by signing in to the trading account and initializing the symbols for the trading session. + +**Raises**: + + SystemExit if sign in was not successful + + + +#### execute + +```python +def execute() +``` + +Execute the bot. + + + +#### start + +```python +async def start() +``` + +Starts the bot by calling the initialize method and running the strategies in the executor. + + + +#### add\_strategy + +```python +def add_strategy(strategy: Strategy) +``` + +Add a strategy to the executor. An added strategy will only run if it's symbol was successfully initialized. + +**Arguments**: + +- `strategy` _Strategy_ - A Strategy instance to run on bot + + +**Notes**: + + Make sure the symbol has been added to the market + + + +#### add\_strategies + +```python +def add_strategies(strategies: Iterable[Strategy]) +``` + +Add multiple strategies at the same time + +**Arguments**: + +- `strategies` - A list of strategies + + + +#### add\_strategy\_all + +```python +def add_strategy_all(*, strategy: Type[Strategy], params: dict | None = None) +``` + +Use this to run a single strategy on all available instruments in the market using the default parameters +i.e one set of parameters for all trading symbols + +**Arguments**: + +- `strategy` _Strategy_ - Strategy class +- `params` _dict_ - A dictionary of parameters for the strategy + + + +#### init\_symbols + +```python +async def init_symbols() +``` + +Initialize the symbols for the current trading session. This method is called internally by the bot. + + + +#### init\_symbol + +```python +async def init_symbol(symbol: Symbol) -> Symbol +``` + +Initialize a symbol before the beginning of a trading sessions. +Removes it from the list of symbols if it was not successfully initialized or not available +for the current market. + +**Arguments**: + +- `symbol` _Symbol_ - Symbol object to be initialized + + +**Returns**: + +- `symbol` _Symbol_ - if successfully initialized + + + +# aiomql.candle + +Candle and Candles classes for handling bars from the MetaTrader 5 terminal. + + + +## Candle Objects + +```python +class Candle() +``` + +A class representing bars from the MetaTrader 5 terminal as a customized class analogous to Japanese Candlesticks. +You can subclass this class for added customization. + +**Attributes**: + +- `time` _int_ - Period start time. +- `open` _int_ - Open price +- `high` _float_ - The highest price of the period +- `low` _float_ - The lowest price of the period +- `close` _float_ - Close price +- `tick_volume` _float_ - Tick volume +- `real_volume` _float_ - Trade volume +- `spread` _float_ - Spread +- `Index` _int_ - Custom attribute representing the position of the candle in a sequence. + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Create a Candle object from keyword arguments. + +**Arguments**: + +- `**kwargs` - Candle attributes and values as keyword arguments. + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set keyword arguments as instance attributes + +**Arguments**: + +- `**kwargs` - Instance attributes and values as keyword arguments + + + +#### mid + +```python +@property +def mid() -> float +``` + +The median of open and close + +**Returns**: + +- `float` - The median of open and close + + + +#### is\_bullish + +```python +def is_bullish() -> bool +``` + +A simple check to see if the candle is bullish. + +**Returns**: + +- `bool` - True or False + + + +#### is\_bearish + +```python +def is_bearish() -> bool +``` + +A simple check to see if the candle is bearish. + +**Returns**: + +- `bool` - True or False + + + +## Candles Objects + +```python +class Candles(Generic[_Candle]) +``` + +An iterable container class of Candle objects in chronological order. + +**Attributes**: + +- `Index` _Series['int']_ - A pandas Series of the indexes of all candles in the object. +- `time` _Series['int']_ - A pandas Series of the time of all candles in the object. +- `open` _Series[float]_ - A pandas Series of the opening price of all candles in the object. +- `high` _Series[float]_ - A pandas Series of the high price of all candles in the object. +- `low` _Series[float]_ - A pandas Series of the low price of all candles in the object. +- `close` _Series[float]_ - A pandas Series of the closing price of all candles in the object. +- `tick_volume` _Series[float]_ - A pandas Series of the tick volume of all candles in the object. +- `real_volume` _Series[float]_ - A pandas Series of the real volume of all candles in the object. +- `spread` _Series[float]_ - A pandas Series of the spread of all candles in the object. +- `timeframe` _TimeFrame_ - The timeframe of the candles in the object. +- `Candle` _Type[Candle]_ - The Candle class for representing the candles in the object. + + properties: +- `data` _DataFrame_ - A pandas DataFrame of all candles in the object. + + +**Notes**: + + The candle class can be customized by subclassing the Candle class and passing the subclass as the candle keyword argument. + Or defining it on the class body as a class attribute. + + + +#### \_\_init\_\_ + +```python +def __init__(*, + data: DataFrame | _Candles | Iterable, + flip=False, + candle_class: Type[_Candle] = None) +``` + +A container class of Candle objects in chronological order. + +**Arguments**: + +- `data` _DataFrame|Candles|Iterable_ - A pandas dataframe, a Candles object or any suitable iterable + + +**Arguments**: + +- `flip` _bool_ - Reverse the chronological order of the candles to the oldest first. Defaults to False. +- `candle_class` - A subclass of Candle to use as the candle class. Defaults to Candle. + + + +#### ta + +```python +@property +def ta() +``` + +Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + +**Returns**: + +- `pandas_ta` - The pandas_ta library + + + +#### ta\_lib + +```python +@property +def ta_lib() +``` + +Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + +**Returns**: + +- `ta` - The ta library + + + +#### data + +```python +@property +def data() -> DataFrame +``` + +The original data passed to the class as a pandas DataFrame + + + +#### rename + +```python +def rename(inplace=True, **kwargs) -> _Candles | None +``` + +Rename columns of the candles class. + +**Arguments**: + +- `inplace` _bool_ - Rename the columns inplace or return a new instance of the class with the renamed columns +- `**kwargs` - The new names of the columns + + +**Returns**: + +- `Candles` - A new instance of the class with the renamed columns if inplace is False. +- `None` - If inplace is True + + + +# aiomql.core.base + + + +## Base Objects + +```python +class Base() +``` + +A base class for all data model classes in the aiomql package. +This class provides a set of common methods and attributes for all data model classes. +For the data model classes attributes are annotated on the class body and are set as object attributes when the +class is instantiated. + +**Arguments**: + +- `**kwargs` - Object attributes and values as keyword arguments. Only added if they are annotated on the class body. + + Class Attributes: +- `mt5` _MetaTrader_ - An instance of the MetaTrader class +- `config` _Config_ - An instance of the Config class +- `Meta` _Type[Meta]_ - The Meta class for configuration of the data model class + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set keyword arguments as object attributes + +**Arguments**: + +- `**kwargs` - Object attributes and values as keyword arguments + + +**Raises**: + +- `AttributeError` - When assigning an attribute that does not belong to the class or any parent class + + +**Notes**: + + Only sets attributes that have been annotated on the class body. + + + +#### annotations + +```python +@property +@cache +def annotations() -> dict +``` + +Class annotations from all ancestor classes and the current class. + +**Returns**: + +- `dict` - A dictionary of class annotations + + + +#### get\_dict + +```python +def get_dict(exclude: set = None, include: set = None) -> dict +``` + +Returns class attributes as a dict, with the ability to filter + +**Arguments**: + +- `exclude` - A set of attributes to be excluded +- `include` - Specific attributes to be returned + + +**Returns**: + +- `dict` - A dictionary of specified class attributes + + +**Notes**: + + You can only set either of include or exclude. If you set both, include will take precedence + + + +#### class\_vars + +```python +@property +@cache +def class_vars() +``` + +Annotated class attributes + +**Returns**: + +- `dict` - A dictionary of available class attributes in all ancestor classes and the current class. + + + +#### dict + +```python +@property +def dict() -> dict +``` + +All instance and class attributes as a dictionary, except those excluded in the Meta class. + +**Returns**: + +- `dict` - A dictionary of instance and class attributes + + + +## Meta Objects + +```python +class Meta() +``` + +A class for defining class attributes to be excluded or included in the dict property + +**Attributes**: + +- `exclude` _set_ - A set of attributes to be excluded +- `include` _set_ - Specific attributes to be returned. Include supercedes exclude. + + + +#### filter + +```python +@classmethod +@property +def filter(cls) -> set +``` + +Combine the exclude and include attributes to return a set of attributes to be excluded. + +**Returns**: + +- `set` - A set of attributes to be excluded + + + +# aiomql.core.config + + + +## Config Objects + +```python +class Config() +``` + +A class for handling configuration settings for the aiomql package. + +**Arguments**: + +- `**kwargs` - Configuration settings as keyword arguments. + Variables set this way supersede those set in the config file. + + +**Attributes**: + +- `record_trades` _bool_ - Whether to keep record of trades or not. +- `filename` _str_ - Name of the config file +- `records_dir` _str_ - Path to the directory where trade records are saved +- `win_percentage` _float_ - Percentage of achieved target profit in a trade to be considered a win +- `login` _int_ - Trading account number +- `password` _str_ - Trading account password +- `server` _str_ - Broker server +- `path` _str_ - Path to terminal file +- `timeout` _int_ - Timeout for terminal connection + + +**Notes**: + + By default, the config class looks for a file named aiomql.json. + You can change this by passing the filename keyword argument to the constructor. + By passing reload=True to the load_config method, you can reload and search again for the config file. + + + +#### account\_info + +```python +def account_info() -> dict['login', 'password', 'server'] +``` + +Returns Account login details as found in the config object if available + +**Returns**: + +- `dict` - A dictionary of login details + + + +# aiomql.core.constants + + + +## TradeAction Objects + +```python +class TradeAction(Repr, IntEnum) +``` + +TRADE_REQUEST_ACTION Enum. + +**Attributes**: + +- `DEAL` _int_ - Delete the pending order placed previously Place a trade order for an immediate execution with the + specified parameters (market order). +- `PENDING` _int_ - Delete the pending order placed previously +- `SLTP` _int_ - Modify Stop Loss and Take Profit values of an opened position +- `MODIFY` _int_ - Modify the parameters of the order placed previously +- `REMOVE` _int_ - Delete the pending order placed previously +- `CLOSE_BY` _int_ - Close a position by an opposite one + + + +## OrderFilling Objects + +```python +class OrderFilling(Repr, IntEnum) +``` + +ORDER_TYPE_FILLING Enum. + +**Attributes**: + +- `FOK` _int_ - This execution policy means that an order can be executed only in the specified volume. + If the necessary amount of a financial instrument is currently unavailable in the market, the order will + not be executed. The desired volume can be made up of several available offers. + +- `IOC` _int_ - An agreement to execute a deal at the maximum volume available in the market within the volume + specified in the order. If the request cannot be filled completely, an order with the available volume will + be executed, and the remaining volume will be canceled. + +- `RETURN` _int_ - This policy is used only for market (ORDER_TYPE_BUY and ORDER_TYPE_SELL), limit and stop limit + orders (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT,ORDER_TYPE_BUY_STOP_LIMIT and + ORDER_TYPE_SELL_STOP_LIMIT) and only for the symbols with Market or Exchange execution modes. If filled + partially, a market or limit order with the remaining volume is not canceled, and is processed further. + During activation of the ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT orders, an appropriate + limit order ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT with the ORDER_FILLING_RETURN type is created. + + + +## OrderTime Objects + +```python +class OrderTime(Repr, IntEnum) +``` + +ORDER_TIME Enum. + +**Attributes**: + +- `GTC` _int_ - Good till cancel order +- `DAY` _int_ - Good till current trade day order +- `SPECIFIED` _int_ - The order is active until the specified date +- `SPECIFIED_DAY` _int_ - The order is active until 23:59:59 of the specified day. If this time appears to be out of + a trading session, the expiration is processed at the nearest trading time. + + + +## OrderType Objects + +```python +class OrderType(Repr, IntEnum) +``` + +ORDER_TYPE Enum. + +**Attributes**: + +- `BUY` _int_ - Market buy order +- `SELL` _int_ - Market sell order +- `BUY_LIMIT` _int_ - Buy Limit pending order +- `SELL_LIMIT` _int_ - Sell Limit pending order +- `BUY_STOP` _int_ - Buy Stop pending order +- `SELL_STOP` _int_ - Sell Stop pending order +- `BUY_STOP_LIMIT` _int_ - Upon reaching the order price, Buy Limit pending order is placed at StopLimit price +- `SELL_STOP_LIMIT` _int_ - Upon reaching the order price, Sell Limit pending order is placed at StopLimit price +- `CLOSE_BY` _int_ - Order for closing a position by an opposite one + + Properties: +- `opposite` _int_ - Gets the opposite of an order type + + + +#### opposite + +```python +@property +def opposite() +``` + +Gets the opposite of an order type for closing an open position + +**Returns**: + +- `int` - integer value of opposite order type + + + +## BookType Objects + +```python +class BookType(Repr, IntEnum) +``` + +BOOK_TYPE Enum. + +**Attributes**: + +- `SELL` _int_ - Sell order (Offer) +- `BUY` _int_ - Buy order (Bid) +- `SELL_MARKET` _int_ - Sell order by Market +- `BUY_MARKET` _int_ - Buy order by Market + + + +## TimeFrame Objects + +```python +class TimeFrame(Repr, IntEnum) +``` + +TIMEFRAME Enum. + +**Attributes**: + +- `M1` _int_ - One Minute +- `M2` _int_ - Two Minutes +- `M3` _int_ - Three Minutes +- `M4` _int_ - Four Minutes +- `M5` _int_ - Five Minutes +- `M6` _int_ - Six Minutes +- `M10` _int_ - Ten Minutes +- `M15` _int_ - Fifteen Minutes +- `M20` _int_ - Twenty Minutes +- `M30` _int_ - Thirty Minutes +- `H1` _int_ - One Hour +- `H2` _int_ - Two Hours +- `H3` _int_ - Three Hours +- `H4` _int_ - Four Hours +- `H6` _int_ - Six Hours +- `H8` _int_ - Eight Hours +- `D1` _int_ - One Day +- `W1` _int_ - One Week +- `MN1` _int_ - One Month + + Properties: +- `time` - return the value of the timeframe object in seconds. Used as a property + + +**Methods**: + +- `get` - get a timeframe object from a time value in seconds + + + +#### time + +```python +@property +def time() +``` + +The number of seconds in a TIMEFRAME + +**Returns**: + +- `int` - The number of seconds in a TIMEFRAME + + +**Examples**: + + >>> t = TimeFrame.H1 + >>> print(t.time) + 3600 + + + +## CopyTicks Objects + +```python +class CopyTicks(Repr, IntEnum) +``` + +COPY_TICKS Enum. This defines the types of ticks that can be requested using the copy_ticks_from() and +copy_ticks_range() functions. + +**Attributes**: + +- `ALL` _int_ - All ticks +- `INFO` _int_ - Ticks containing Bid and/or Ask price changes +- `TRADE` _int_ - Ticks containing Last and/or Volume price changes + + + +## PositionType Objects + +```python +class PositionType(Repr, IntEnum) +``` + +POSITION_TYPE Enum. Direction of an open position (buy or sell) + +**Attributes**: + +- `BUY` _int_ - Buy +- `SELL` _int_ - Sell + + + +## PositionReason Objects + +```python +class PositionReason(Repr, IntEnum) +``` + +POSITION_REASON Enum. The reason for opening a position is contained in the POSITION_REASON Enum + +**Attributes**: + +- `CLIENT` _int_ - The position was opened as a result of activation of an order placed from a desktop terminal +- `MOBILE` _int_ - The position was opened as a result of activation of an order placed from a mobile application +- `WEB` _int_ - The position was opened as a result of activation of an order placed from the web platform +- `EXPERT` _int_ - The position was opened as a result of activation of an order placed from an MQL5 program, + i.e. an Expert Advisor or a script + + + +## DealType Objects + +```python +class DealType(Repr, IntEnum) +``` + +DEAL_TYPE enum. Each deal is characterized by a type, allowed values are enumerated in this enum + +**Attributes**: + +- `BUY` _int_ - Buy +- `SELL` _int_ - Sell +- `BALANCE` _int_ - Balance +- `CREDIT` _int_ - Credit +- `CHARGE` _int_ - Additional Charge +- `CORRECTION` _int_ - Correction +- `BONUS` _int_ - Bonus +- `COMMISSION` _int_ - Additional Commission +- `COMMISSION_DAILY` _int_ - Daily Commission +- `COMMISSION_MONTHLY` _int_ - Monthly Commission +- `COMMISSION_AGENT_DAILY` _int_ - Daily Agent Commission +- `COMMISSION_AGENT_MONTHLY` _int_ - Monthly Agent Commission +- `INTEREST` _int_ - Interest Rate +- `DEAL_DIVIDEND` _int_ - Dividend Operations +- `DEAL_DIVIDEND_FRANKED` _int_ - Franked (non-taxable) dividend operations +- `DEAL_TAX` _int_ - Tax Charges + +- `BUY_CANCELED` _int_ - Canceled buy deal. There can be a situation when a previously executed buy deal is canceled. + In this case, the type of the previously executed deal (DEAL_TYPE_BUY) is changed to DEAL_TYPE_BUY_CANCELED, + and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated + balance operation + +- `SELL_CANCELED` _int_ - Canceled sell deal. There can be a situation when a previously executed sell deal is + canceled. In this case, the type of the previously executed deal (DEAL_TYPE_SELL) is changed to + DEAL_TYPE_SELL_CANCELED, and its profit/loss is zeroized. Previously obtained profit/loss is + charged/withdrawn using a separated balance operation. + + + +## DealEntry Objects + +```python +class DealEntry(Repr, IntEnum) +``` + +DEAL_ENTRY Enum. Deals differ not only in their types set in DEAL_TYPE enum, but also in the way they change +positions. This can be a simple position opening, or accumulation of a previously opened position (market entering), +position closing by an opposite deal of a corresponding volume (market exiting), or position reversing, if the +opposite-direction deal covers the volume of the previously opened position. + +**Attributes**: + +- `IN` _int_ - Entry In +- `OUT` _int_ - Entry Out +- `INOUT` _int_ - Reverse +- `OUT_BY` _int_ - Close a position by an opposite one + + + +## DealReason Objects + +```python +class DealReason(Repr, IntEnum) +``` + +DEAL_REASON Enum. The reason for deal execution is contained in the DEAL_REASON property. A deal can be executed +as a result of triggering of an order placed from a mobile application or an MQL5 program, as well as as a result +of the StopOut event, variation margin calculation, etc. + +**Attributes**: + +- `CLIENT` _int_ - The deal was executed as a result of activation of an order placed from a desktop terminal +- `MOBILE` _int_ - The deal was executed as a result of activation of an order placed from a desktop terminal +- `WEB` _int_ - The deal was executed as a result of activation of an order placed from the web platform +- `EXPERT` _int_ - 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 +- `SL` _int_ - The deal was executed as a result of Stop Loss activation +- `TP` _int_ - The deal was executed as a result of Take Profit activation +- `SO` _int_ - The deal was executed as a result of the Stop Out event +- `ROLLOVER` _int_ - The deal was executed due to a rollover +- `VMARGIN` _int_ - The deal was executed after charging the variation margin +- `SPLIT` _int_ - The deal was executed after the split (price reduction) of an instrument, which had an open + position during split announcement + + + +## OrderReason Objects + +```python +class OrderReason(Repr, IntEnum) +``` + +ORDER_REASON Enum. + +**Attributes**: + +- `CLIENT` _int_ - The order was placed from a desktop terminal +- `MOBILE` _int_ - The order was placed from a mobile application +- `WEB` _int_ - The order was placed from a web platform +- `EXPERT` _int_ - The order was placed from an MQL5-program, i.e. by an Expert Advisor or a script +- `SL` _int_ - The order was placed as a result of Stop Loss activation +- `TP` _int_ - The order was placed as a result of Take Profit activation +- `SO` _int_ - The order was placed as a result of the Stop Out event + + + +## SymbolChartMode Objects + +```python +class SymbolChartMode(Repr, IntEnum) +``` + +SYMBOL_CHART_MODE Enum. A symbol price chart can be based on Bid or Last prices. The price selected for symbol +charts also affects the generation and display of bars in the terminal. +Possible values of the SYMBOL_CHART_MODE property are described in this enum + +**Attributes**: + +- `BID` _int_ - Bars are based on Bid prices +- `LAST` _int_ - Bars are based on last prices + + + +## SymbolCalcMode Objects + +```python +class SymbolCalcMode(Repr, IntEnum) +``` + +SYMBOL_CALC_MODE Enum. The SYMBOL_CALC_MODE enumeration is used for obtaining information about how the margin +requirements for a symbol are calculated. + +**Attributes**: + +- `FOREX` _int_ - Forex mode - calculation of profit and margin for Forex +- `FOREX_NO_LEVERAGE` _int_ - Forex No Leverage mode � calculation of profit and margin for Forex symbols without + taking into account the leverage +- `FUTURES` _int_ - Futures mode - calculation of margin and profit for futures +- `CFD` _int_ - CFD mode - calculation of margin and profit for CFD +- `CFDINDEX` _int_ - CFD index mode - calculation of margin and profit for CFD by indexes +- `CFDLEVERAGE` _int_ - CFD Leverage mode - calculation of margin and profit for CFD at leverage trading +- `EXCH_STOCKS` _int_ - Calculation of margin and profit for trading securities on a stock exchange +- `EXCH_FUTURES` _int_ - Calculation of margin and profit for trading futures contracts on a stock exchange +- `EXCH_OPTIONS` _int_ - value is 34 +- `EXCH_OPTIONS_MARGIN` _int_ - value is 36 +- `EXCH_BONDS` _int_ - Exchange Bonds mode � calculation of margin and profit for trading bonds on a stock exchange +- `STOCKS_MOEX` _int_ - Exchange MOEX Stocks mode �calculation of margin and profit for trading securities on MOEX +- `EXCH_BONDS_MOEX` _int_ - Exchange MOEX Bonds mode � calculation of margin and profit for trading bonds on MOEX + +- `SERV_COLLATERAL` _int_ - Collateral mode - a symbol is used as a non-tradable asset on a trading account. + The market value of an open position is calculated based on the volume, current market price, contract size + and liquidity ratio. The value is included into Assets, which are added to Equity. Open positions of such + symbols increase the Free Margin amount and are used as additional margin (collateral) for open positions + + + +## SymbolTradeMode Objects + +```python +class SymbolTradeMode(Repr, IntEnum) +``` + +SYMBOL_TRADE_MODE Enum. There are several symbol trading modes. Information about trading modes of a certain +symbol is reflected in the values this enumeration + +**Attributes**: + +- `DISABLED` _int_ - Trade is disabled for the symbol +- `LONGONLY` _int_ - Allowed only long positions +- `SHORTONLY` _int_ - Allowed only short positions +- `CLOSEONLY` _int_ - Allowed only position close operations +- `FULL` _int_ - No trade restrictions + + + +## SymbolTradeExecution Objects + +```python +class SymbolTradeExecution(Repr, IntEnum) +``` + +SYMBOL_TRADE_EXECUTION Enum. The modes, or execution policies, define the rules for cases when the price has +changed or the requested volume cannot be completely fulfilled at the moment. + +**Attributes**: + +- `REQUEST` _int_ - Executing a market order at the price previously received from the broker. Prices for a certain + market order are requested from the broker before the order is sent. Upon receiving the prices, order + execution at the given price can be either confirmed or rejected. + +- `INSTANT` _int_ - Executing a market order at the specified price immediately. When sending a trade request to be + executed, the platform automatically adds the current prices to the order. + - If the broker accepts the price, the order is executed. + - If the broker does not accept the requested price, a "Requote" is sent � the broker returns prices, + at which this order can be executed. + +- `MARKET` _int_ - A broker makes a decision about the order execution price without any additional discussion with the trader. + Sending the order in such a mode means advance consent to its execution at this price. + +- `EXCHANGE` _int_ - Trade operations are executed at the prices of the current market offers. + + + +## SymbolSwapMode Objects + +```python +class SymbolSwapMode(Repr, IntEnum) +``` + +SYMBOL_SWAP_MODE Enum. Methods of swap calculation at position transfer are specified in enumeration +ENUM_SYMBOL_SWAP_MODE. The method of swap calculation determines the units of measure of the SYMBOL_SWAP_LONG and +SYMBOL_SWAP_SHORT parameters. For example, if swaps are charged in the client deposit currency, then the values of +those parameters are specified as an amount of money in the client deposit currency. + +**Attributes**: + +- `DISABLED` _int_ - Swaps disabled (no swaps) +- `POINTS` _int_ - Swaps are charged in points +- `CURRENCY_SYMBOL` _int_ - Swaps are charged in money in base currency of the symbol +- `CURRENCY_MARGIN` _int_ - Swaps are charged in money in margin currency of the symbol +- `CURRENCY_DEPOSIT` _int_ - Swaps are charged in money, in client deposit currency + +- `INTEREST_CURRENT` _int_ - Swaps are charged as the specified annual interest from the instrument price at + calculation of swap (standard bank year is 360 days) + +- `INTEREST_OPEN` _int_ - Swaps are charged as the specified annual interest from the open price of position + (standard bank year is 360 days) + +- `REOPEN_CURRENT` _int_ - Swaps are charged by reopening positions. At the end of a trading day the position is + closed. Next day it is reopened by the close price +/- specified number of points + (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + +- `REOPEN_BID` _int_ - Swaps are charged by reopening positions. At the end of a trading day the position is closed. + Next day it is reopened by the current Bid price +/- specified number of + points (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + + + +## DayOfWeek Objects + +```python +class DayOfWeek(Repr, IntEnum) +``` + +DAY_OF_WEEK Enum. + +**Attributes**: + +- `SUNDAY` _int_ - Sunday +- `MONDAY` _int_ - Monday +- `TUESDAY` _int_ - Tuesday +- `WEDNESDAY` _int_ - Wednesday +- `THURSDAY` _int_ - Thursday +- `FRIDAY` _int_ - Friday +- `SATURDAY` _int_ - Saturday + + + +## SymbolOrderGTCMode Objects + +```python +class SymbolOrderGTCMode(Repr, IntEnum) +``` + +SYMBOL_ORDER_GTC_MODE Enum. If the SYMBOL_EXPIRATION_MODE property is set to SYMBOL_EXPIRATION_GTC +(good till canceled), the expiration of pending orders, as well as of +Stop Loss/Take Profit orders should be additionally set using the ENUM_SYMBOL_ORDER_GTC_MODE enumeration. + +**Attributes**: + +- `GTC` _int_ - Pending orders and Stop Loss/Take Profit levels are valid for an unlimited period + until theirConstants, Enumerations and explicit cancellation + +- `DAILY` _int_ - Orders are valid during one trading day. At the end of the day, all Stop Loss and + Take Profit levels, as well as pending orders are deleted. + +- `DAILY_NO_STOPS` _int_ - When a trade day changes, only pending orders are deleted, + while Stop Loss and Take Profit levels are preserved + + + +## SymbolOptionRight Objects + +```python +class SymbolOptionRight(Repr, IntEnum) +``` + +SYMBOL_OPTION_RIGHT Enum. An option is a contract, which gives the right, but not the obligation, +to buy or sell an underlying asset (goods, stocks, futures, etc.) at a specified price on or before a specific date. +The following enumerations describe option properties, including the option type and the right arising from it. + +**Attributes**: + +- `CALL` _int_ - A call option gives you the right to buy an asset at a specified price. +- `PUT` _int_ - A put option gives you the right to sell an asset at a specified price. + + + +## SymbolOptionMode Objects + +```python +class SymbolOptionMode(Repr, IntEnum) +``` + +SYMBOL_OPTION_MODE Enum. + +**Attributes**: + +- `EUROPEAN` _int_ - European option may only be exercised on a specified date (expiration, execution date, delivery date) +- `AMERICAN` _int_ - American option may be exercised on any trading day or before expiry. The period within which + a buyer can exercise the option is specified for it. + + + +## AccountTradeMode Objects + +```python +class AccountTradeMode(Repr, IntEnum) +``` + +ACCOUNT_TRADE_MODE Enum. There are several types of accounts that can be opened on a trade server. +The type of account on which an MQL5 program is running can be found out using +the ENUM_ACCOUNT_TRADE_MODE enumeration. + +**Attributes**: + +- `DEMO` - Demo account +- `CONTEST` - Contest account +- `REAL` - Real Account + + + +## TickFlag Objects + +```python +class TickFlag(Repr, IntFlag) +``` + +TICK_FLAG Enum. TICK_FLAG defines possible flags for ticks. These flags are used to describe ticks obtained by the +copy_ticks_from() and copy_ticks_range() functions. + +**Attributes**: + +- `BID` _int_ - Bid price changed +- `ASK` _int_ - Ask price changed +- `LAST` _int_ - Last price changed +- `VOLUME` _int_ - Volume changed +- `BUY` _int_ - last Buy price changed +- `SELL` _int_ - last Sell price changed + + + +## TradeRetcode Objects + +```python +class TradeRetcode(Repr, IntEnum) +``` + +TRADE_RETCODE Enum. Return codes for order send/check operations + +**Attributes**: + +- `REQUOTE` _int_ - Requote +- `REJECT` _int_ - Request rejected +- `CANCEL` _int_ - Request canceled by trader +- `PLACED` _int_ - Order placed +- `DONE` _int_ - Request completed +- `DONE_PARTIAL` _int_ - Only part of the request was completed +- `ERROR` _int_ - Request processing error +- `TIMEOUT` _int_ - Request canceled by timeout +- `INVALID` _int_ - Invalid request +- `INVALID_VOLUME` _int_ - Invalid volume in the request +- `INVALID_PRICE` _int_ - Invalid price in the request +- `INVALID_STOPS` _int_ - Invalid stops in the request +- `TRADE_DISABLED` _int_ - Trade is disabled +- `MARKET_CLOSED` _int_ - Market is closed +- `NO_MONEY` _int_ - There is not enough money to complete the request +- `PRICE_CHANGED` _int_ - Prices changed +- `PRICE_OFF` _int_ - There are no quotes to process the request +- `INVALID_EXPIRATION` _int_ - Invalid order expiration date in the request +- `ORDER_CHANGED` _int_ - Order state changed +- `TOO_MANY_REQUESTS` _int_ - Too frequent requests +- `NO_CHANGES` _int_ - No changes in request +- `SERVER_DISABLES_AT` _int_ - Autotrading disabled by server +- `CLIENT_DISABLES_AT` _int_ - Autotrading disabled by client terminal +- `LOCKED` _int_ - Request locked for processing +- `FROZEN` _int_ - Order or position frozen +- `INVALID_FILL` _int_ - Invalid order filling type +- `CONNECTION` _int_ - No connection with the trade server +- `ONLY_REAL` _int_ - Operation is allowed only for live accounts +- `LIMIT_ORDERS` _int_ - The number of pending orders has reached the limit +- `LIMIT_VOLUME` _int_ - The volume of orders and positions for the symbol has reached the limit +- `INVALID_ORDER` _int_ - Incorrect or prohibited order type +- `POSITION_CLOSED` _int_ - Position with the specified POSITION_IDENTIFIER has already been closed +- `INVALID_CLOSE_VOLUME` _int_ - A close volume exceeds the current position volume + +- `CLOSE_ORDER_EXIST` _int_ - A close order already exists for a specified position. This may happen when working in + the hedging system: + � when attempting to close a position with an opposite one, while close orders for the position already exist + � when attempting to fully or partially close a position if the total volume of the already present close + orders and the newly placed one exceeds the current position volume + +- `LIMIT_POSITIONS` _int_ - The number of open positions simultaneously present on an account can be limited by the + server settings.After a limit is reached, the server returns the TRADE_RETCODE_LIMIT_POSITIONS error when + attempting to place an order. The limitation operates differently depending on the position accounting type: + � Netting � number of open positions is considered. When a limit is reached, the platform does not let + placing new orders whose execution may increase the number of open positions. In fact, the platform + allows placing orders only for the symbols that already have open positions. + The current pending orders are not considered since their execution may lead to changes in the current + positions but it cannot increase their number. + + � Hedging � pending orders are considered together with open positions, since a pending order activation + always leads to opening a new position. When a limit is reached, the platform does not allow placing + both new market orders for opening positions and pending orders. + +- `REJECT_CANCEL` _int_ - The pending order activation request is rejected, the order is canceled. +- `LONG_ONLY` _int_ - The request is rejected, because the "Only long positions are allowed" rule is set for the + symbol (POSITION_TYPE_BUY) +- `SHORT_ONLY` _int_ - The request is rejected, because the "Only short positions are allowed" rule is set for the + symbol (POSITION_TYPE_SELL) +- `CLOSE_ONLY` _int_ - The request is rejected, because the "Only position closing is allowed" rule is set for the + symbol +- `FIFO_CLOSE` _int_ - The request is rejected, because "Position closing is allowed only by FIFO rule" flag is set + for the trading account (ACCOUNT_FIFO_CLOSE=true) + + + +## AccountStopOutMode Objects + +```python +class AccountStopOutMode(Repr, IntEnum) +``` + +ACCOUNT_STOPOUT_MODE Enum. + +**Attributes**: + +- `PERCENT` _int_ - Account stop out mode in percents +- `MONEY` _int_ - Account stop out mode in money + + + +## AccountMarginMode Objects + +```python +class AccountMarginMode(Repr, IntEnum) +``` + +ACCOUNT_MARGIN_MODE Enum. + +**Attributes**: + +- `RETAIL_NETTING` _int_ - Used for the OTC markets to interpret positions in the "netting" + mode (only one position can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE). + +- `EXCHANGE` _int_ - Used for the exchange markets. Margin is calculated based on the discounts specified in + symbol settings. Discounts are set by the broker, but not less than the values set by the exchange. + +- `HEDGING` _int_ - Used for the exchange markets where individual positions are possible + (hedging, multiple positions can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE) taking into account the hedged margin (SYMBOL_MARGIN_HEDGED). + + + +# aiomql.core.errors + + + +## Error Objects + +```python +class Error() +``` + +Error class for handling errors from MetaTrader 5. + + + +# aiomql.core.exceptions + +Exceptions for the aiomql package. + + + +## LoginError Objects + +```python +class LoginError(Exception) +``` + +Raised when an error occurs when logging in. + + + +## VolumeError Objects + +```python +class VolumeError(Exception) +``` + +Raised when a volume is not valid or out of range for a symbol. + + + +## SymbolError Objects + +```python +class SymbolError(Exception) +``` + +Raised when a symbol is not provided where required or not available in the Market Watch. + + + +## OrderError Objects + +```python +class OrderError(Exception) +``` + +Raised when an error occurs when working with the order class. + + + +# aiomql.core.meta\_trader + + + +## MetaTrader Objects + +```python +class MetaTrader(metaclass=BaseMeta) +``` + + + +#### \_\_aenter\_\_ + +```python +async def __aenter__() -> 'MetaTrader' +``` + +Async context manager entry point. +Initializes the connection to the MetaTrader terminal. + +**Returns**: + +- `MetaTrader` - An instance of the MetaTrader class. + + + +#### \_\_aexit\_\_ + +```python +async def __aexit__(exc_type, exc_val, exc_tb) +``` + +Async context manager exit point. Closes the connection to the MetaTrader terminal. + + + +#### login + +```python +async def login(login: int, + password: str, + server: str, + timeout: int = 60000) -> bool +``` + +Connects to the MetaTrader terminal using the specified login, password and server. + +**Arguments**: + +- `login` _int_ - The trading account number. +- `password` _str_ - The trading account password. +- `server` _str_ - The trading server name. +- `timeout` _int_ - The timeout for the connection in seconds. + + +**Returns**: + +- `bool` - True if successful, False otherwise. + + + +#### initialize + +```python +async def initialize(path: str = "", + login: int = 0, + password: str = "", + server: str = "", + timeout: int | None = None, + portable=False) -> bool +``` + +Initializes the connection to the MetaTrader terminal. All parameters are optional. + +**Arguments**: + +- `path` _str_ - The path to the MetaTrader terminal executable. +- `login` _int_ - The trading account number. +- `password` _str_ - The trading account password. +- `server` _str_ - The trading server name. +- `timeout` _int_ - The timeout for the connection in seconds. +- `portable` _bool_ - If True, the terminal will be launched in portable mode. + + +**Returns**: + +- `bool` - True if successful, False otherwise. + + + +#### shutdown + +```python +async def shutdown() -> None +``` + +Closes the connection to the MetaTrader terminal. + +**Returns**: + +- `None` - None + + + +#### version + +```python +async def version() -> tuple[int, int, str] | None +``` + + + + + +#### account\_info + +```python +async def account_info() -> AccountInfo | None +``` + + + + + +#### orders\_get + +```python +async def orders_get(group: str = "", + ticket: int = 0, + symbol: str = "") -> tuple[TradeOrder] | None +``` + +Get active orders with the ability to filter by symbol or ticket. There are three call options. +Call without parameters. Return active orders on all symbols + +**Arguments**: + +- `symbol` _str_ - Symbol name. Optional named parameter. If a symbol is specified, the ticket parameter is ignored. + +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function + returns only active orders meeting a specified criteria for a symbol name. + +- `ticket` _int_ - Order ticket (ORDER_TICKET). Optional named parameter. + + +**Returns**: + +- `list[TradeOrder]` - A list of active trade orders as TradeOrder objects + + + +# aiomql.core.models + + + +## AccountInfo Objects + +```python +class AccountInfo(Base) +``` + +Account Information Class. + +**Attributes**: + +- `login` - int +- `password` - str +- `server` - str +- `trade_mode` - AccountTradeMode +- `balance` - float +- `leverage` - float +- `profit` - float +- `point` - float +- `amount` - float = 0 +- `equity` - float +- `credit` - float +- `margin` - float +- `margin_level` - float +- `margin_free` - float +- `margin_mode` - AccountMarginMode +- `margin_so_mode` - AccountStopoutMode +- `margin_so_call` - float +- `margin_so_so` - float +- `margin_initial` - float +- `margin_maintenance` - float +- `fifo_close` - bool +- `limit_orders` - float +- `currency` - str = "USD" +- `trade_allowed` - bool = True +- `trade_expert` - bool = True +- `currency_digits` - int +- `assets` - float +- `liabilities` - float +- `commission_blocked` - float +- `name` - str +- `company` - str + + + +## TerminalInfo Objects + +```python +class TerminalInfo(Base) +``` + +Terminal information class. Holds information about the terminal. + +**Attributes**: + +- `community_account` - bool +- `community_connection` - bool +- `connected` - bool +- `dlls_allowed` - bool +- `trade_allowed` - bool +- `tradeapi_disabled` - bool +- `email_enabled` - bool +- `ftp_enabled` - bool +- `notifications_enabled` - bool +- `mqid` - bool +- `build` - int +- `maxbars` - int +- `codepage` - int +- `ping_last` - int +- `community_balance` - float +- `retransmission` - float +- `company` - str +- `name` - str +- `language` - str +- `path` - str +- `data_path` - str +- `commondata_path` - str + + + +## SymbolInfo Objects + +```python +class SymbolInfo(Base) +``` + +Symbol Information Class. Symbols are financial instruments available for trading in the MetaTrader 5 terminal. + +**Attributes**: + +- `name` - str +- `custom` - bool +- `chart_mode` - SymbolChartMode +- `select` - bool +- `visible` - bool +- `session_deals` - int +- `session_buy_orders` - int +- `session_sell_orders` - int +- `volume` - float +- `volumehigh` - float +- `volumelow` - float +- `time` - int +- `digits` - int +- `spread` - float +- `spread_float` - bool +- `ticks_bookdepth` - int +- `trade_calc_mode` - SymbolCalcMode +- `trade_mode` - SymbolTradeMode +- `start_time` - int +- `expiration_time` - int +- `trade_stops_level` - int +- `trade_freeze_level` - int +- `trade_exemode` - SymbolTradeExecution +- `swap_mode` - SymbolSwapMode +- `swap_rollover3days` - DayOfWeek +- `margin_hedged_use_leg` - bool +- `expiration_mode` - int +- `filling_mode` - int +- `order_mode` - int +- `order_gtc_mode` - SymbolOrderGTCMode +- `option_mode` - SymbolOptionMode +- `option_right` - SymbolOptionRight +- `bid` - float +- `bidhigh` - float +- `bidlow` - float +- `ask` - float +- `askhigh` - float +- `asklow` - float +- `last` - float +- `lasthigh` - float +- `lastlow` - float +- `volume_real` - float +- `volumehigh_real` - float +- `volumelow_real` - float +- `option_strike` - float +- `point` - float +- `trade_tick_value` - float +- `trade_tick_value_profit` - float +- `trade_tick_value_loss` - float +- `trade_tick_size` - float +- `trade_contract_size` - float +- `trade_accrued_interest` - float +- `trade_face_value` - float +- `trade_liquidity_rate` - float +- `volume_min` - float +- `volume_max` - float +- `volume_step` - float +- `volume_limit` - float +- `swap_long` - float +- `swap_short` - float +- `margin_initial` - float +- `margin_maintenance` - float +- `session_volume` - float +- `session_turnover` - float +- `session_interest` - float +- `session_buy_orders_volume` - float +- `session_sell_orders_volume` - float +- `session_open` - float +- `session_close` - float +- `session_aw` - float +- `session_price_settlement` - float +- `session_price_limit_min` - float +- `session_price_limit_max` - float +- `margin_hedged` - float +- `price_change` - float +- `price_volatility` - float +- `price_theoretical` - float +- `price_greeks_delta` - float +- `price_greeks_theta` - float +- `price_greeks_gamma` - float +- `price_greeks_vega` - float +- `price_greeks_rho` - float +- `price_greeks_omega` - float +- `price_sensitivity` - float +- `basis` - str +- `category` - str +- `currency_base` - str +- `currency_profit` - str +- `currency_margin` - Any +- `bank` - str +- `description` - str +- `exchange` - str +- `formula` - Any +- `isin` - Any +- `name` - str +- `page` - str +- `path` - str + + + +## BookInfo Objects + +```python +class BookInfo(Base) +``` + +Book Information Class. + +**Attributes**: + +- `type` - BookType +- `price` - float +- `volume` - float +- `volume_dbl` - float + + + +## TradeOrder Objects + +```python +class TradeOrder(Base) +``` + +Trade Order Class. + +**Attributes**: + +- `ticket` - int +- `time_setup` - int +- `time_setup_msc` - int +- `time_expiration` - int +- `time_done` - int +- `time_done_msc` - int +- `type` - OrderType +- `type_time` - OrderTime +- `type_filling` - OrderFilling +- `state` - int +- `magic` - int +- `position_id` - int +- `position_by_id` - int +- `reason` - OrderReason +- `volume_current` - float +- `volume_initial` - float +- `price_open` - float +- `sl` - float +- `tp` - float +- `price_current` - float +- `price_stoplimit` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +## TradeRequest Objects + +```python +class TradeRequest(Base) +``` + +Trade Request Class. + +**Attributes**: + +- `action` - TradeAction +- `type` - OrderType +- `order` - int +- `symbol` - str +- `volume` - float +- `sl` - float +- `tp` - float +- `price` - float +- `deviation` - float +- `stop_limit` - float +- `type_time` - OrderTime +- `type_filling` - OrderFilling +- `expiration` - int +- `position` - int +- `position_by` - int +- `comment` - str +- `magic` - int +- `deviation` - int +- `comment` - str + + + +## OrderCheckResult Objects + +```python +class OrderCheckResult(Base) +``` + +Order Check Result + +**Attributes**: + +- `retcode` - int +- `balance` - float +- `equity` - float +- `profit` - float +- `margin` - float +- `margin_free` - float +- `margin_level` - float +- `comment` - str +- `request` - TradeRequest + + + +## OrderSendResult Objects + +```python +class OrderSendResult(Base) +``` + +Order Send Result + +**Attributes**: + +- `retcode` - int +- `deal` - int +- `order` - int +- `volume` - float +- `price` - float +- `bid` - float +- `ask` - float +- `comment` - str +- `request` - TradeRequest +- `request_id` - int +- `retcode_external` - int +- `profit` - float + + + +## TradePosition Objects + +```python +class TradePosition(Base) +``` + +Trade Position + +**Attributes**: + +- `ticket` - int +- `time` - int +- `time_msc` - int +- `time_update` - int +- `time_update_msc` - int +- `type` - OrderType +- `magic` - float +- `identifier` - int +- `reason` - PositionReason +- `volume` - float +- `price_open` - float +- `sl` - float +- `tp` - float +- `price_current` - float +- `swap` - float +- `profit` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +## TradeDeal Objects + +```python +class TradeDeal(Base) +``` + +Trade Deal + +**Attributes**: + +- `ticket` - int +- `order` - int +- `time` - int +- `time_msc` - int +- `type` - DealType +- `entry` - DealEntry +- `magic` - int +- `position_id` - int +- `reason` - DealReason +- `volume` - float +- `price` - float +- `commission` - float +- `swap` - float +- `profit` - float +- `fee` - float +- `sl` - float +- `tp` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +# aiomql.core + + + +# aiomql.executor + + + +## Executor Objects + +```python +class Executor() +``` + +Executor class for running multiple strategies on multiple symbols concurrently. + +**Attributes**: + +- `executor` _ThreadPoolExecutor_ - The executor object. +- `workers` _list_ - List of strategies. + + + +#### add\_workers + +```python +def add_workers(strategies: Sequence[type(Strategy)]) +``` + +Add multiple strategies at once + +**Arguments**: + +- `strategies` _Sequence[Strategy]_ - A sequence of strategies. + + + +#### remove\_workers + +```python +def remove_workers(*symbols: Sequence[Symbol]) +``` + +Removes any worker running on a symbol not successfully initialized. + +**Arguments**: + +- `*symbols` - Successfully initialized symbols. + + + +#### add\_worker + +```python +def add_worker(strategy: type(Strategy)) +``` + +Add a strategy instance to the list of workers + +**Arguments**: + +- `strategy` _Strategy_ - A strategy object + + + +#### run + +```python +@staticmethod +def run(strategy: type(Strategy)) +``` + +Wraps the coroutine trade method of each strategy with 'asyncio.run'. + +**Arguments**: + +- `strategy` _Strategy_ - A strategy object + + + +#### execute + +```python +async def execute(workers: int = 0) +``` + +Run the strategies with a threadpool executor. + +**Arguments**: + +- `workers` - Number of workers to use in executor pool. Defaults to zero which uses all workers. + + +**Notes**: + + No matter the number specified, the executor will always use a minimum of 5 workers. + + + +# aiomql.history + + + +## History Objects + +```python +class History() +``` + +The history class handles completed trade deals and trade orders in the trading history of an account. + +**Attributes**: + +- `deals` _list[TradeDeal]_ - Iterable of trade deals +- `orders` _list[TradeOrder]_ - Iterable of trade orders +- `total_deals` - Total number of deals +- `total_orders` _int_ - Total number orders +- `group` _str_ - Filter for selecting history by symbols. +- `ticket` _int_ - Filter for selecting history by ticket number +- `position` _int_ - Filter for selecting history deals by position +- `initialized` _bool_ - check if initial request has been sent to the terminal to get history. +- `mt5` _MetaTrader_ - MetaTrader instance +- `config` _Config_ - Config instance + + + +#### \_\_init\_\_ + +```python +def __init__(*, + date_from: datetime | float = 0, + date_to: datetime | float = 0, + group: str = "", + ticket: int = 0, + position: int = 0) +``` + +**Arguments**: + +- `date_from` _datetime, float_ - Date the orders are requested from. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" + +- `date_to` _datetime, float_ - Date up to which the orders are requested. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" + +- `group` _str_ - Filter for selecting history by symbols. +- `ticket` _int_ - Filter for selecting history by ticket number +- `position` _int_ - Filter for selecting history deals by position + + + +#### init + +```python +async def init(deals=True, orders=True) -> bool +``` + +Get history deals and orders + +**Arguments**: + +- `deals` _bool_ - If true get history deals during initial request to terminal +- `orders` _bool_ - If true get history orders during initial request to terminal + + +**Returns**: + +- `bool` - True if all requests were successful else False + + + +#### get\_deals + +```python +async def get_deals() -> list[TradeDeal] +``` + +Get deals from trading history using the parameters set in the constructor. + +**Returns**: + +- `list[TradeDeal]` - A list of trade deals + + + +#### deals\_total + +```python +async def deals_total() -> int +``` + +Get total number of deals within the specified period in the constructor. + +**Returns**: + +- `int` - Total number of Deals + + + +#### get\_orders + +```python +async def get_orders() -> list[TradeOrder] +``` + +Get orders from trading history using the parameters set in the constructor. + +**Returns**: + +- `list[TradeOrder]` - A list of trade orders + + + +#### orders\_total + +```python +async def orders_total() -> int +``` + +Get total number of orders within the specified period in the constructor. + +**Returns**: + +- `int` - Total number of orders + + + +# aiomql.lib.strategies.finger\_trap + + + +## Entry Objects + +```python +@dataclass +class Entry() +``` + +Entry class for FingerTrap strategy.Will be used to store entry conditions and other entry related data. + +**Attributes**: + +- `bearish` _bool_ - True if the market is bearish +- `bullish` _bool_ - True if the market is bullish +- `ranging` _bool_ - True if the market is ranging +- `snooze` _float_ - Time to wait before checking for entry conditions +- `trend` _str_ - The current trend of the market +- `last_candle` _Candle_ - The last candle of the market +- `new` _bool_ - True if the last candle is new +- `order_type` _OrderType_ - The type of order to place +- `pips` _int_ - The number of pips to place the order from the current price + + + +# aiomql.lib.strategies + + + +# aiomql.lib.symbols.forex\_symbol + + + +## ForexSymbol Objects + +```python +class ForexSymbol(Symbol) +``` + +Subclass of Symbol for Forex Symbols. Handles the conversion of currency and the computation of stop loss, +take profit and volume. + + + +#### pip + +```python +@property +def pip() +``` + +Returns the pip value of the symbol. This is ten times the point value for forex symbols. + +**Returns**: + +- `float` - The pip value of the symbol. + + + +#### compute\_volume + +```python +async def compute_volume(*, amount: float, pips: float) -> float +``` + +Compute volume given an amount to risk and target pips. Round the computed volume to the nearest step. + +**Arguments**: + +- `amount` _float_ - Amount to risk. Given in terms of the account currency. +- `pips` _float_ - Target pips. + + +**Returns**: + +- `float` - volume + + +**Raises**: + +- `VolumeError` - If the computed volume is less than the minimum volume or greater than the maximum volume. + + + +# aiomql.lib.symbols + + + +# aiomql.lib.traders.simple\_deal\_trader + + + +## DealTrader Objects + +```python +class DealTrader(Trader) +``` + +A base class for placing trades based on the number of pips to target + + + +#### create\_order + +```python +async def create_order(*, order_type: OrderType, pips: float) +``` + +Using the number of target pips it determines the lot size, stop loss and take profit for the order, +and updates the order object with the values. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `pips` _float_ - Target pips + + + +# aiomql.lib.traders + + + +# aiomql.lib + + + +# aiomql.order + +Order Class + + + +## Order Objects + +```python +class Order(TradeRequest) +``` + +Trade order related functions and properties. Subclass of TradeRequest. + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Initialize the order object with keyword arguments, symbol must be provided. +Provide default values for action, type_time and type_filling if not provided. + +**Arguments**: + +- `**kwargs` - Keyword arguments must match the attributes of TradeRequest as well as the attributes of + Order class as specified in the annotations in the class definition. + + Default Values: +- `action` _TradeAction.DEAL_ - Trade action +- `type_time` _OrderTime.DAY_ - Order time +- `type_filling` _OrderFilling.FOK_ - Order filling + + +**Raises**: + +- `SymbolError` - If symbol is not provided + + + +#### orders\_total + +```python +async def orders_total() +``` + +Get the number of active orders. + +**Returns**: + +- `(int)` - total number of active orders + + + +#### orders + +```python +async def orders() -> tuple[TradeOrder] +``` + +Get the list of active orders for the current symbol. + +**Returns**: + +- `tuple[TradeOrder]` - A Tuple of active trade orders as TradeOrder objects + + + +#### check + +```python +async def check() -> OrderCheckResult +``` + +Check funds sufficiency for performing a required trading operation and the possibility to execute it at + +**Returns**: + +- `OrderCheckResult` - An OrderCheckResult object + + +**Raises**: + +- `OrderError` - If not successful + + + +#### send + +```python +async def send() -> OrderSendResult +``` + +Send a request to perform a trading operation from the terminal to the trade server. + +**Returns**: + +- `OrderSendResult` - An OrderSendResult object + + +**Raises**: + +- `OrderError` - If not successful + + + +#### calc\_margin + +```python +async def calc_margin() -> float +``` + +Return the required margin in the account currency to perform a specified trading operation. + +**Returns**: + +- `float` - Returns float value if successful + + +**Raises**: + +- `OrderError` - If not successful + + + +#### calc\_profit + +```python +async def calc_profit() -> float +``` + +Return profit in the account currency for a specified trading operation. + +**Returns**: + +- `float` - Returns float value if successful + + +**Raises**: + +- `OrderError` - If not successful + + + +# aiomql.positions + +Handle Open positions. + + + +## Positions Objects + +```python +class Positions() +``` + +Get Open Positions. + +**Attributes**: + +- `symbol` _str_ - Financial instrument name. +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. + If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. +- `ticket` _int_ - Position ticket. +- `mt5` _MetaTrader_ - MetaTrader instance. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: str = "", group: str = "", ticket: int = 0) +``` + +Get Open Positions. + +**Arguments**: + +- `symbol` _str_ - Financial instrument name. +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. If the group + is specified, the function returns only positions meeting a specified criteria for a symbol name. +- `ticket` _int_ - Position ticket + + + +#### positions\_total + +```python +async def positions_total() -> int +``` + +Get the number of open positions. + +**Returns**: + +- `int` - Return total number of open positions + + + +#### positions\_get + +```python +async def positions_get() +``` + +Get open positions with the ability to filter by symbol or ticket. + +**Returns**: + +- `list[TradePosition]` - A list of open trade positions + + + +#### close\_all + +```python +async def close_all() -> int +``` + +Close all open positions for the trading account. + +**Returns**: + +- `int` - Return number of positions closed. + + + +# aiomql.ram + +Risk Assessment and Management + + + +## RAM Objects + +```python +class RAM() +``` + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Risk Assessment and Management. All provided keyword arguments are set as attributes. + +**Arguments**: + +- `kwargs` _Dict_ - Keyword arguments. + + Defaults: +- `risk_to_reward` _float_ - Risk to reward ratio 1 +- `risk` _float_ - Percentage of account balance to risk per trade 0.01 # 1% +- `amount` _float_ - Amount to risk per trade in terms of base currency 10 +- `pips` _float_ - Target pips 10 +- `volume` _float_ - Volume to trade 0.05 + + + +#### get\_amount + +```python +async def get_amount() -> float +``` + +Calculate the amount to risk per trade. + +**Returns**: + +- `float` - Amount to risk per trade + + + +# aiomql.records + +This module contains the Records class, which is used to read and update trade records from csv files. + + + +## Records Objects + +```python +class Records() +``` + +This utility class read trade records from csv files, and update them based on their closing positions. + +**Attributes**: + +- `config` - Config object +- `records_dir(Path)` - Path to directory containing record of placed trades, If not given takes the default + from the config + + + +#### \_\_init\_\_ + +```python +def __init__(records_dir: Path = '') +``` + +Initialize the Records class. + +**Arguments**: + +- `records_dir` _Path_ - Path to directory containing record of placed trades. + + + +#### get\_records + +```python +async def get_records() +``` + +Get trade records from records_dir folder + +**Yields**: + +- `files` - Trade record files + + + +#### read\_update + +```python +async def read_update(file: Path) +``` + +Read and update trade records + +**Arguments**: + +- `file` - Trade record file + + + +#### update\_rows + +```python +async def update_rows(rows: list[dict]) -> list[dict] +``` + +Update the rows of entered trades in the csv file with the actual profit. + +**Arguments**: + +- `rows` - A list of dictionaries from the dictionary writer object of the csv file. + + +**Returns**: + +- `list[dict]` - A list of dictionaries with the actual profit and win status. + + + +#### update\_records + +```python +async def update_records() +``` + +Update trade records in the records_dir folder. + + + +#### update\_record + +```python +async def update_record(file: Path | str) +``` + +Update a single trade record file. + + + +# aiomql.result + + + +## Result Objects + +```python +class Result() +``` + +A base class for handling trade results and strategy parameters for record keeping and reference purpose. +The data property must be implemented in the subclass + +**Attributes**: + +- `config` _Config_ - The configuration object +- `name` - Any desired name for the result file object + + + +#### \_\_init\_\_ + +```python +def __init__(result: OrderSendResult, parameters: dict = None, name: str = '') +``` + +Prepare result data + +**Arguments**: + + result: + parameters: + name: + + + +#### to\_csv + +```python +async def to_csv() +``` + +Record trade results and associated parameters as a csv file + + + +#### save\_csv + +```python +async def save_csv() +``` + +Save trade results and associated parameters as a csv file in a separate thread + + + +# aiomql.strategy + +The base class for creating strategies. + + + +## Strategy Objects + +```python +class Strategy(ABC) +``` + +The base class for creating strategies. + +**Attributes**: + +- `symbol` _Symbol_ - The Financial Instrument as a Symbol Object +- `parameters` _Dict_ - A dictionary of parameters for the strategy. + + Class Attributes: +- `name` _str_ - A name for the strategy. +- `account` _Account_ - Account instance. +- `mt5` _MetaTrader_ - MetaTrader instance. +- `config` _Config_ - Config instance. + + +**Notes**: + + Define the name of a strategy as a class attribute. If not provided, the class name will be used as the name. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: Symbol, params: dict = None) +``` + +Initiate the parameters dict and add name and symbol fields. +Use class name as strategy name if name is not provided + +**Arguments**: + +- `symbol` _Symbol_ - The Financial instrument +- `params` _Dict_ - Trading strategy parameters + + + +#### sleep + +```python +@staticmethod +async def sleep(secs: float) +``` + +Sleep for the needed amount of seconds in between requests to the terminal. +computes the accurate amount of time needed to sleep ensuring that the next request is made at the start of +a new bar and making cooperative multitasking possible. + +**Arguments**: + +- `secs` _float_ - The time in seconds. Usually the timeframe you are trading on. + + + +#### trade + +```python +@abstractmethod +async def trade() +``` + +Place trades using this method. This is the main method of the strategy. +It will be called by the strategy runner. + + + +# aiomql.symbol + +Symbol class for handling a financial instrument. + + + +## Symbol Objects + +```python +class Symbol(SymbolInfo) +``` + +Main class for handling a financial instrument. A subclass of SymbolInfo and Base it has attributes and methods +for working with a financial instrument. + +**Attributes**: + +- `tick` _Tick_ - Price tick object for instrument +- `account` - An instance of the current trading account + + +**Notes**: + + Full properties are on the SymbolInfo Object. + Make sure Symbol is always initialized with a name argument + + + +#### info\_tick + +```python +async def info_tick(*, name: str = "") -> Tick +``` + +Get the current price tick of a financial instrument. + +**Arguments**: + +- `name` - if name is supplied get price tick of that financial instrument + + +**Returns**: + +- `Tick` - Return a Tick Object + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### symbol\_select + +```python +async def symbol_select(*, enable: bool = True) -> bool +``` + +Select a symbol in the MarketWatch window or remove a symbol from the window. +Update the select property + +**Arguments**: + +- `enable` _bool_ - Switch. Optional unnamed parameter. If 'false', a symbol should be removed from + the MarketWatch window. + + +**Returns**: + +- `bool` - True if successful, otherwise � False. + + + +#### info + +```python +async def info() -> SymbolInfo +``` + +Get data on the specified financial instrument and update the symbol object properties + +**Returns**: + +- `(SymbolInfo)` - SymbolInfo if successful + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### init + +```python +async def init() -> bool +``` + +Initialized the symbol by pulling properties from the terminal + +**Returns**: + +- `bool` - Returns True if symbol info was successful initialized + + + +#### book\_add + +```python +async def book_add() -> bool +``` + +Subscribes the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. +If the symbol is not in the list of instruments for the market, This method will return False + +**Returns**: + +- `bool` - True if successful, otherwise � False. + + + +#### book\_get + +```python +async def book_get() -> tuple[BookInfo] +``` + +Returns a tuple of BookInfo featuring Market Depth entries for the specified symbol. + +**Returns**: + +- `tuple[BookInfo]` - Returns the Market Depth contents as a tuples of BookInfo Objects + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### book\_release + +```python +async def book_release() -> bool +``` + +Cancels subscription of the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. + +**Returns**: + +- `bool` - True if successful, otherwise � False. + + + +#### compute\_volume + +```python +async def compute_volume(*, amount: float, pips: float) -> float +``` + +Computes the volume of a trade based on the amount and the number of pips to target + +**Arguments**: + +- `amount` _float_ - Amount to risk in the trade +- `pips` _float_ - Number of pips to target + + +**Returns**: + +- `float` - Returns the volume of the trade + + + +#### currency\_conversion + +```python +async def currency_conversion(*, amount: float, base: str, + quote: str) -> float +``` + +Convert from one currency to the other. + +**Arguments**: + +- `amount` - amount to convert given in terms of the quote currency +- `base` - The base currency of the pair +- `quote` - The quote currency of the pair + + +**Returns**: + +- `float` - Amount in terms of the base currency or None if it failed to convert + + +**Raises**: + +- `ValueError` - If conversion is impossible + + + +#### copy\_rates\_from + +```python +async def copy_rates_from(*, timeframe: TimeFrame, date_from: datetime | int, + count: int) -> Candles +``` + +Get bars from the MetaTrader 5 terminal starting from the specified date. + +**Arguments**: + +- `timeframe` _TimeFrame_ - Timeframe the bars are requested for. Set by a value from the TimeFrame enumeration. Required unnamed parameter. + +- `date_from` _datetime | int_ - Date of opening of the first bar from the requested sample. Set by the 'datetime' object or as a number + of seconds elapsed since 1970.01.01. Required unnamed parameter. + +- `count` _int_ - Number of bars to receive. Required unnamed parameter. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_rates\_from\_pos + +```python +async def copy_rates_from_pos(*, + timeframe: TimeFrame, + count: int = 500, + start_position: int = 0) -> Candles +``` + +Get bars from the MetaTrader 5 terminal starting from the specified index. + +**Arguments**: + +- `timeframe` _TimeFrame_ - TimeFrame value from TimeFrame Enum. Required keyword only parameter + +- `count` _int_ - Number of bars to return. Keyword argument defaults to 500 + +- `start_position` _int_ - Initial index of the bar the data are requested from. The numbering of bars goes from + present to past. Thus, the zero bar means the current one. Keyword argument defaults to 0. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_rates\_range + +```python +async def copy_rates_range(*, timeframe: TimeFrame, date_from: datetime | int, + date_to: datetime | int) -> Candles +``` + +Get bars in the specified date range from the MetaTrader 5 terminal. + +**Arguments**: + +- `timeframe` _TimeFrame_ - Timeframe for the bars using the TimeFrame enumeration. Required unnamed parameter. + +- `date_from` _datetime | int_ - Date the bars are requested from. Set by the 'datetime' object or as a number of seconds + elapsed since 1970.01.01. Bars with the open time >= date_from are returned. Required unnamed parameter. + +- `date_to` _datetime | int_ - Date, up to which the bars are requested. Set by the 'datetime' object or as a number of + seconds elapsed since 1970.01.01. Bars with the open time <= date_to are returned. Required unnamed parameter. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_ticks\_from + +```python +async def copy_ticks_from(*, + date_from: datetime | int, + count: int = 100, + flags: CopyTicks = CopyTicks.ALL) -> Ticks +``` + +Get ticks from the MetaTrader 5 terminal starting from the specified date. + +Args: date_from (datetime | int): Date the ticks are requested from. Set by the 'datetime' object or as a +number of seconds elapsed since 1970.01.01. + +count (int): Number of requested ticks. Defaults to 100 + +flags (CopyTicks): A flag to define the type of the requested ticks from CopyTicks enum. INFO is the default + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of ticks ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_ticks\_range + +```python +async def copy_ticks_range(*, + date_from: datetime | int, + date_to: datetime | int, + flags: CopyTicks = CopyTicks.ALL) -> Ticks +``` + +Get ticks for the specified date range from the MetaTrader 5 terminal. + +**Arguments**: + +- `date_from` - Date the bars are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars with + the open time >= date_from are returned. Required unnamed parameter. + +- `date_to` - Date, up to which the bars are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars + with the open time <= date_to are returned. Required unnamed parameter. + + flags (CopyTicks): + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of ticks ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned. + + + +# aiomql.terminal + +Terminal related functions and properties + + + +## Terminal Objects + +```python +class Terminal(TerminalInfo) +``` + +Terminal Class. Get information about the MetaTrader 5 terminal. The class is a subclass of the TerminalInfo +class. It inherits all the attributes and methods of the TerminalInfo class and adds some useful methods. + +**Notes**: + + Other attributes are defined in the TerminalInfo Class + + + +#### initialize + +```python +async def initialize() -> bool +``` + +Establish a connection with the MetaTrader 5 terminal. There are three call options. Call without parameters. +The terminal for connection is found automatically. Call specifying the path to the MetaTrader 5 terminal we +want to connect to. word path as a keyword argument Call specifying the trading account path and parameters +i.e login, password, server, as keyword arguments, path can be omitted. + +**Returns**: + +- `bool` - True if successful else False + + + +#### version + +```python +async def version() +``` + +Get the MetaTrader 5 terminal version. This method returns the terminal version, build and release date as +a tuple of three values + +**Returns**: + +- `Version` - version of tuple as Version object + + +**Raises**: + +- `ValueError` - If the terminal version cannot be obtained + + + +#### info + +```python +async def info() +``` + +Get the connected MetaTrader 5 client terminal status and settings. gets terminal info in the form of a +named tuple structure (namedtuple). Return None in case of an error. The info on the error can be +obtained using last_error(). + +**Returns**: + +- `Terminal` - Terminal status and settings as a terminal object. + + + +#### symbols\_total + +```python +async def symbols_total() -> int +``` + +Get the number of all financial instruments in the MetaTrader 5 terminal. + +**Returns**: + +- `int` - Total number of available symbols + + + +# aiomql.ticks + +Module for working with price ticks. + + + +## Tick Objects + +```python +class Tick() +``` + +Price Tick of a Financial Instrument. + +**Attributes**: + +- `time` _int_ - Time of the last prices update for the symbol +- `bid` _float_ - Current Bid price +- `ask` _float_ - Current Ask price +- `last` _float_ - Price of the last deal (Last) +- `volume` _float_ - Volume for the current Last price +- `time_msc` _int_ - Time of the last prices update for the symbol in milliseconds +- `flags` _TickFlag_ - Tick flags +- `volume_real` _float_ - Volume for the current Last price +- `Index` _int_ - Custom attribute representing the position of the tick in a sequence. + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set attributes from keyword arguments + + + +## Ticks Objects + +```python +class Ticks() +``` + +Container data class for price ticks. Arrange in chronological order. +Supports iteration, slicing and assignment + +**Arguments**: + +- `data` _DataFrame | tuple[tuple]_ - Dataframe of price ticks or a tuple of tuples + + +**Arguments**: + +- `flip` _bool_ - If flip is True reverse data chronological order. + + +**Attributes**: + +- `data` - Dataframe Object holding the ticks + + + +#### \_\_init\_\_ + +```python +def __init__(*, data: DataFrame | Iterable, flip=False) +``` + +Initialize the Ticks class. Creates a DataFrame of price ticks from the data argument. + +**Arguments**: + +- `data` _DataFrame | Iterable_ - Dataframe of price ticks or any iterable object that can be converted to a + pandas DataFrame +- `flip` _bool_ - If flip is True reverse data chronological order. + + + +#### ta + +```python +@property +def ta() +``` + +Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + +**Returns**: + +- `pandas_ta` - The pandas_ta library + + + +#### ta\_lib + +```python +@property +def ta_lib() +``` + +Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + +**Returns**: + +- `ta` - The ta library + + + +#### data + +```python +@property +def data() -> DataFrame +``` + +DataFrame of price ticks arranged in chronological order. + + + +#### rename + +```python +def rename(inplace=True, **kwargs) -> _Ticks | None +``` + +Rename columns of the candle class. + +**Arguments**: + +- `inplace` _bool_ - Rename the columns inplace or return a new instance of the class with the renamed columns +- `**kwargs` - The new names of the columns + + +**Returns**: + +- `Ticks` - A new instance of the class with the renamed columns if inplace is False. +- `None` - If inplace is True + + + +# aiomql.trader + +Trader class module. Handles the creation of an order and the placing of trades + + + +## Trader Objects + +```python +class Trader() +``` + +Base class for creating a Trader object. Handles the creation of an order and the placing of trades + +**Attributes**: + +- `symbol` _Symbol_ - Financial instrument class Symbol class or any subclass of it. +- `ram` _RAM_ - RAM instance +- `order` _Order_ - Trade order + + Class Attributes: +- `name` _str_ - A name for the strategy. +- `account` _Account_ - Account instance. +- `mt5` _MetaTrader_ - MetaTrader instance. +- `config` _Config_ - Config instance. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: Symbol, ram: RAM = None) +``` + +Initializes the order object and RAM instance + +**Arguments**: + +- `symbol` _Symbol_ - Financial instrument +- `ram` _RAM_ - Risk Assessment and Management instance + + + +#### create\_order + +```python +async def create_order(*, order_type: OrderType, **kwargs) +``` + +Complete the order object with the required values. +The default trader object uses the values specified in the default RAM instance to determine the take profit, +stop loss, volume, and number of pips to target. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `kwargs` - keyword arguments as required for the specific trader + + + +#### set\_order\_limits + +```python +async def set_order_limits(pips: float) +``` + +Sets the stop loss and take profit for the order. +This method uses pips as defined for forex instruments. + +**Arguments**: + +- `pips` - Target pips + + + +#### place\_trade + +```python +async def place_trade(order_type: OrderType, params: dict = None, **kwargs) +``` + +Places a trade based on the order_type. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `params` - parameters to be saved with the trade +- `kwargs` - keyword arguments as required for the specific trader + + + +# aiomql.utils + +Utility functions for aiomql. + + + +#### dict\_to\_string + +```python +def dict_to_string(data: dict, multi=False) -> str +``` + +Convert a dict to a string. Use for logging. + +**Arguments**: + +- `data` _dict_ - The dict to convert. +- `multi` _bool, optional_ - If True, each key-value pair will be on a new line. Defaults to False. + + +**Returns**: + +- `str` - The string representation of the dict. + diff --git a/docs/account.md b/docs/account.md new file mode 100644 index 0000000..4ce6aee --- /dev/null +++ b/docs/account.md @@ -0,0 +1,101 @@ +## Account + +```python +class Account(AccountInfo) +``` +Singleton class for managing a trading account. A subclass of [AccountInfo](#accountinfo). All AccountInfo attributes are available in this class. + +**Attributes** + +|Name|Type|Description|Default| +|---|---|---|---| +|**connected**|**bool**|Status of connection to MetaTrader 5 Terminal|False| +|symbols|set[SymbolInfo]|A set of available symbols for the financial market.|set()| + +**Notes**\ +Other Account properties are defined in the AccountInfo class. + +### refresh +```python +async def refresh() +``` +Refreshes the account instance with the latest data from the MetaTrader 5 terminal + +### account_info +```python +@property +def account_info() -> dict +``` +Get account login, server and password details. If the login attribute of the account instance returns +a falsy value, the config instance is used to get the account details. + +**Returns** + +|Type|Description| +|---|---| +|**dict**|A dict of login, server and password details| + +**Notes**\ +This method will only look for config details in the config instance if the login attribute of the account Instance returns a falsy value + +#### __aenter__ +```python +async def __aenter__() -> 'Account' +``` +Connect to a trading account and return the account instance. +Async context manager for the Account class. + +**Returns** + +|Type|Description| +|---|---| +|**Account**|An instance of the Account class| + +**Raises** + +|Exception|Description| +|---|---| +|**LoginError**|If login fails| + +#### sign_in +```python +async def sign_in() -> bool +``` +Connect to a trading account. + +**Returns** + +|Type|Description| +|---|---| +|**bool**|True if login was successful else False| + +#### has_symbol + +```python +def has_symbol(symbol: str | Type[SymbolInfo]) +``` +Checks to see if a symbol is available for a trading account\ +**Parameters** + +|Name|Type|Description| +|---|---|---| +|**symbol**|**str** or **SymbolInfo**|A symbol name or SymbolInfo instance| + +**Returns** + +|Type|Description| +|---|---| +|**bool**|True if symbol is available else False| + +#### symbols_get +```python +async def symbols_get() -> set[SymbolInfo] +``` +Get all financial instruments from the MetaTrader 5 terminal available for the current account. + +**Returns** + +|Type|Description| +|---|---| +|**set[SymbolInfo]**|A set of SymbolInfo instances| + diff --git a/docs/bot_builder.md b/docs/bot_builder.md new file mode 100644 index 0000000..8eaf1d1 --- /dev/null +++ b/docs/bot_builder.md @@ -0,0 +1,107 @@ +## Bot Builder + +```python +class Bot() +``` +The bot class. Create a bot instance to run your strategies. + +**Attributes** + +|Name|Type|Description|Default| +|---|---|---|---| +|**account**|**Account**|Account Object.|None| +|**executor**|**ThreadPoolExecutor**|The default thread executor.|None| +|**symbols**|**set[Symbols]**|A set of symbols for the trading session|set()| + + +### initialize +```python +async def initialize() +``` +Prepares the bot by signing in to the trading account and initializing the symbols for the trading session. + +*Raises* + +|Exception|Description| +|---|---| +|**SystemExit**|If sign in was not successful| + +SystemExit if sign in was not successful + +#### execute +```python +def execute() +``` +Execute the bot. + +#### start +```python +async def start() +``` +Starts the bot by calling the initialize method and running the strategies in the executor. + +#### add_strategy + +```python +def add_strategy(strategy: Strategy) +``` +Add a strategy to the executor. An added strategy will only run if it's symbol was successfully initialized. + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|**strategy**|**Strategy**|A Strategy instance to run on bot| + +#### add_strategies +```python +def add_strategies(strategies: Iterable[Strategy]) +``` +Add multiple strategies at the same time + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|**strategies**|**Iterable[Strategy]**|An iterable of Strategy instances| + +#### add_strategy_all +```python +def add_strategy_all(*, strategy: Type[Strategy], params: dict | None = None) +``` +Use this to run a single strategy on all available instruments in the market using the default parameters +i.e one set of parameters for all trading symbols + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|**strategy**|**Type[Strategy]**|A Strategy class| +|**params**|**dict** or **None**|A dictionary of parameters for the strategy| + +#### init_symbols +```python +async def init_symbols() +``` +Initialize the symbols for the current trading session. This method is called internally by the bot. + +#### init_symbol +```python +async def init_symbol(symbol: Symbol) -> Symbol +``` +Initialize a symbol before the beginning of a trading session. +Removes it from the list of symbols if it was not successfully initialized or not available +for the account. + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|**symbol**|**Symbol**|A Symbol instance| + + +*returns* + +|Type|Description| +|---|---| +|**Symbol**|A Symbol instance| diff --git a/docs/candle.md b/docs/candle.md new file mode 100644 index 0000000..4cef2a2 --- /dev/null +++ b/docs/candle.md @@ -0,0 +1,181 @@ +## Candle + +Candle and Candles classes for handling bars from the MetaTrader 5 terminal. + + +```python +class Candle +``` +A class representing bars from the MetaTrader 5 terminal as a customized class analogous to Japanese Candlesticks. +You can subclass this class for added customization. + +**Attributes** + +|Name|Type|Description| +|---|---|---| +|**time**|**int**|Period start time| +|**open**|**int**|Open price| +|**high**|**float**|The highest price of the period| +|**low**|**float**|The lowest price of the period| +|**close**|**float**|Close price| +|**tick_volume**|**float**|Tick volume| +|**real_volume**|**float**|Trade volume| +|**spread**|**float**|Spread| +|**Index**|**int**|Custom attribute representing the position of the candle in a sequence.| + +### \_\_init\_\_ +```python +def __init__(**kwargs) +``` +Create a Candle object from keyword arguments. Kwargs are set as instance attributes. + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|**kwargs**|**Any**|Candle attributes and values as keyword arguments.| + +### set\_attributes +```python +def set_attributes(**kwargs) +``` +Set keyword arguments as instance attributes + +### mid +```python +@property +def mid() -> float +``` +The median of open and close + +**returns** + +|Type|Description| +|---|---| +|**float**|The median of open and close| + +### is_bullish + +```python +def is_bullish() -> bool +``` +A simple check to see if the candle is bullish. + +**returns** + +|Type|Description| +|---|---| +|**bool**|True or False| + +### is_bearish +```python +def is_bearish() -> bool +``` +A simple check to see if the candle is bearish. + +**returns** + +|Type|Description| +|---|---| +|bool|True or False| + + +## Candles + +```python +class Candles(Generic[_Candle]) +``` +An iterable container class of Candle objects in chronological order. A wrapper around Pandas DataFrame object. + +**Attributes** + +|Name|Type|Description| +|---|---|---| +|**data**|**DataFrame**|A pandas DataFrame of all candles in the object.| +|**Index**|**Series['int']**|A pandas Series of the indexes of all candles in the object| +|**time**|**Series['int']**|A pandas Series of the time of all candles in the object| +|**open**|**Series[float]**|A pandas Series of the opening price of all candles in the object| +|**high**|**Series[float]**|A pandas Series of the high price of all candles in the object| +|**low**|**Series[float]**|A pandas Series of the low price of all candles in the object| +|**close**|**Series[float]**|A pandas Series of the closing price of all candles in the object| +|**tick_volume**|**Series[float]**|A pandas Series of the tick volume of all candles in the object| +|**real_volume**|**Series[float]**|A pandas Series of the real volume of all candles in the object| +|**spread**|**Series[float]**|A pandas Series of the spread of all candles in the object| +|**timeframe**|**TimeFrame**|The timeframe of the candles in the object| +|**Candle**|**Type[Candle]**|The Candle class for representing the candles in the object.| +|**data**|**DataFrame**|A pandas DataFrame of all candles in the object.| + +**Notes**: When subclassing this class make sure to Candle attribute is set to your desired candle class. + +#### \_\_init\_\_ + +```python +def __init__(*, + data: DataFrame | _Candles | Iterable, + flip=False, + candle_class: Type[_Candle] = None) +``` +A container class of Candle objects in chronological order. + +**Arguments**: + +|Name|Type|Description|Default| +|---|---|---|---| +|**data**|**DataFrame** or **Candles** or **Iterable**|A pandas dataframe, a Candles object or any suitable iterable| +|**flip**|**bool**|Reverse the chronological order of the candles to the oldest first.|False| +|**candle_class**|**Type[Candle]**|A subclass of Candle to use as the candle class.|Candle| + + +#### ta + +```python +@property +def ta() +``` +Access to the pandas_ta library for performing technical analysis on the underlying data attribute. Use this as you would use the pandas_ta library. + +**returns**: + +|Type|Description| +|---|---| +|**pandas_ta**|The pandas_ta library| + +#### ta\_lib +```python +@property +def ta_lib() +``` +Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. Use this for +functions that require pandas Series as input. + +**returns**: + +|Type|Description| +|---|---| +|ta|The ta library| + +#### data +```python +@property +def data() -> DataFrame +``` +A pandas DataFrame of all candles in the object. + +#### rename +```python +def rename(inplace=True, **kwargs) -> _Candles | None +``` +Rename columns of the candles class. + +**Arguments**: + +| Name | Type |Description|Default| +|---------|----------|---|---| +| inplace | **bool** |Rename the columns inplace or return a new instance of the class with the renamed columns|True| +| **kwargs** | **str** |The new names of the columns|| + +**returns**: + +|Type|Description| +|---|---| +|**Candles**|A new instance of the class with the renamed columns if inplace is False.| diff --git a/docs/core/base.md b/docs/core/base.md new file mode 100644 index 0000000..3084c0d --- /dev/null +++ b/docs/core/base.md @@ -0,0 +1,164 @@ +# Table of Contents + +* [aiomql.core.base](#aiomql.core.base) + * [Base](#aiomql.core.base.Base) + * [set\_attributes](#aiomql.core.base.Base.set_attributes) + * [annotations](#aiomql.core.base.Base.annotations) + * [get\_dict](#aiomql.core.base.Base.get_dict) + * [class\_vars](#aiomql.core.base.Base.class_vars) + * [dict](#aiomql.core.base.Base.dict) + * [Meta](#aiomql.core.base.Base.Meta) + + + +# aiomql.core.base + + + +## Base Objects + +```python +class Base() +``` + +A base class for all data model classes in the aiomql package. +This class provides a set of common methods and attributes for all data model classes. +For the data model classes attributes are annotated on the class body and are set as object attributes when the +class is instantiated. + +**Arguments**: + +- `**kwargs` - Object attributes and values as keyword arguments. Only added if they are annotated on the class body. + + Class Attributes: +- `mt5` _MetaTrader_ - An instance of the MetaTrader class +- `config` _Config_ - An instance of the Config class +- `Meta` _Type[Meta]_ - The Meta class for configuration of the data model class + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set keyword arguments as object attributes + +**Arguments**: + +- `**kwargs` - Object attributes and values as keyword arguments + + +**Raises**: + +- `AttributeError` - When assigning an attribute that does not belong to the class or any parent class + + +**Notes**: + + Only sets attributes that have been annotated on the class body. + + + +#### annotations + +```python +@property +@cache +def annotations() -> dict +``` + +Class annotations from all ancestor classes and the current class. + +**Returns**: + +- `dict` - A dictionary of class annotations + + + +#### get\_dict + +```python +def get_dict(exclude: set = None, include: set = None) -> dict +``` + +Returns class attributes as a dict, with the ability to filter + +**Arguments**: + +- `exclude` - A set of attributes to be excluded +- `include` - Specific attributes to be returned + + +**Returns**: + +- `dict` - A dictionary of specified class attributes + + +**Notes**: + + You can only set either of include or exclude. If you set both, include will take precedence + + + +#### class\_vars + +```python +@property +@cache +def class_vars() +``` + +Annotated class attributes + +**Returns**: + +- `dict` - A dictionary of available class attributes in all ancestor classes and the current class. + + + +#### dict + +```python +@property +def dict() -> dict +``` + +All instance and class attributes as a dictionary, except those excluded in the Meta class. + +**Returns**: + +- `dict` - A dictionary of instance and class attributes + + + +## Meta Objects + +```python +class Meta() +``` + +A class for defining class attributes to be excluded or included in the dict property + +**Attributes**: + +- `exclude` _set_ - A set of attributes to be excluded +- `include` _set_ - Specific attributes to be returned. Include supercedes exclude. + + + +#### filter + +```python +@classmethod +@property +def filter(cls) -> set +``` + +Combine the exclude and include attributes to return a set of attributes to be excluded. + +**Returns**: + +- `set` - A set of attributes to be excluded + diff --git a/docs/core/config.md b/docs/core/config.md new file mode 100644 index 0000000..f604684 --- /dev/null +++ b/docs/core/config.md @@ -0,0 +1,59 @@ +# Table of Contents + +* [aiomql.core.config](#aiomql.core.config) + * [Config](#aiomql.core.config.Config) + * [account\_info](#aiomql.core.config.Config.account_info) + + + +# aiomql.core.config + + + +## Config Objects + +```python +class Config() +``` + +A class for handling configuration settings for the aiomql package. + +**Arguments**: + +- `**kwargs` - Configuration settings as keyword arguments. + Variables set this way supersede those set in the config file. + + +**Attributes**: + +- `record_trades` _bool_ - Whether to keep record of trades or not. +- `filename` _str_ - Name of the config file +- `records_dir` _str_ - Path to the directory where trade records are saved +- `win_percentage` _float_ - Percentage of achieved target profit in a trade to be considered a win +- `login` _int_ - Trading account number +- `password` _str_ - Trading account password +- `server` _str_ - Broker server +- `path` _str_ - Path to terminal file +- `timeout` _int_ - Timeout for terminal connection + + +**Notes**: + + By default, the config class looks for a file named aiomql.json. + You can change this by passing the filename keyword argument to the constructor. + By passing reload=True to the load_config method, you can reload and search again for the config file. + + + +#### account\_info + +```python +def account_info() -> dict['login', 'password', 'server'] +``` + +Returns Account login details as found in the config object if available + +**Returns**: + +- `dict` - A dictionary of login details + diff --git a/docs/core/constants.md b/docs/core/constants.md new file mode 100644 index 0000000..0815826 --- /dev/null +++ b/docs/core/constants.md @@ -0,0 +1,736 @@ +# Table of Contents + +* [aiomql.core.constants](#aiomql.core.constants) + * [TradeAction](#aiomql.core.constants.TradeAction) + * [OrderFilling](#aiomql.core.constants.OrderFilling) + * [OrderTime](#aiomql.core.constants.OrderTime) + * [OrderType](#aiomql.core.constants.OrderType) + * [opposite](#aiomql.core.constants.OrderType.opposite) + * [BookType](#aiomql.core.constants.BookType) + * [TimeFrame](#aiomql.core.constants.TimeFrame) + * [time](#aiomql.core.constants.TimeFrame.time) + * [CopyTicks](#aiomql.core.constants.CopyTicks) + * [PositionType](#aiomql.core.constants.PositionType) + * [PositionReason](#aiomql.core.constants.PositionReason) + * [DealType](#aiomql.core.constants.DealType) + * [DealEntry](#aiomql.core.constants.DealEntry) + * [DealReason](#aiomql.core.constants.DealReason) + * [OrderReason](#aiomql.core.constants.OrderReason) + * [SymbolChartMode](#aiomql.core.constants.SymbolChartMode) + * [SymbolCalcMode](#aiomql.core.constants.SymbolCalcMode) + * [SymbolTradeMode](#aiomql.core.constants.SymbolTradeMode) + * [SymbolTradeExecution](#aiomql.core.constants.SymbolTradeExecution) + * [SymbolSwapMode](#aiomql.core.constants.SymbolSwapMode) + * [DayOfWeek](#aiomql.core.constants.DayOfWeek) + * [SymbolOrderGTCMode](#aiomql.core.constants.SymbolOrderGTCMode) + * [SymbolOptionRight](#aiomql.core.constants.SymbolOptionRight) + * [SymbolOptionMode](#aiomql.core.constants.SymbolOptionMode) + * [AccountTradeMode](#aiomql.core.constants.AccountTradeMode) + * [TickFlag](#aiomql.core.constants.TickFlag) + * [TradeRetcode](#aiomql.core.constants.TradeRetcode) + * [AccountStopOutMode](#aiomql.core.constants.AccountStopOutMode) + * [AccountMarginMode](#aiomql.core.constants.AccountMarginMode) + + + +# aiomql.core.constants + + + +## TradeAction Objects + +```python +class TradeAction(Repr, IntEnum) +``` + +TRADE_REQUEST_ACTION Enum. + +**Attributes**: + +- `DEAL` _int_ - Delete the pending order placed previously Place a trade order for an immediate execution with the + specified parameters (market order). +- `PENDING` _int_ - Delete the pending order placed previously +- `SLTP` _int_ - Modify Stop Loss and Take Profit values of an opened position +- `MODIFY` _int_ - Modify the parameters of the order placed previously +- `REMOVE` _int_ - Delete the pending order placed previously +- `CLOSE_BY` _int_ - Close a position by an opposite one + + + +## OrderFilling Objects + +```python +class OrderFilling(Repr, IntEnum) +``` + +ORDER_TYPE_FILLING Enum. + +**Attributes**: + +- `FOK` _int_ - This execution policy means that an order can be executed only in the specified volume. + If the necessary amount of a financial instrument is currently unavailable in the market, the order will + not be executed. The desired volume can be made up of several available offers. + +- `IOC` _int_ - An agreement to execute a deal at the maximum volume available in the market within the volume + specified in the order. If the request cannot be filled completely, an order with the available volume will + be executed, and the remaining volume will be canceled. + +- `RETURN` _int_ - This policy is used only for market (ORDER_TYPE_BUY and ORDER_TYPE_SELL), limit and stop limit + orders (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT,ORDER_TYPE_BUY_STOP_LIMIT and + ORDER_TYPE_SELL_STOP_LIMIT) and only for the symbols with Market or Exchange execution modes. If filled + partially, a market or limit order with the remaining volume is not canceled, and is processed further. + During activation of the ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT orders, an appropriate + limit order ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT with the ORDER_FILLING_RETURN type is created. + + + +## OrderTime Objects + +```python +class OrderTime(Repr, IntEnum) +``` + +ORDER_TIME Enum. + +**Attributes**: + +- `GTC` _int_ - Good till cancel order +- `DAY` _int_ - Good till current trade day order +- `SPECIFIED` _int_ - The order is active until the specified date +- `SPECIFIED_DAY` _int_ - The order is active until 23:59:59 of the specified day. If this time appears to be out of + a trading session, the expiration is processed at the nearest trading time. + + + +## OrderType Objects + +```python +class OrderType(Repr, IntEnum) +``` + +ORDER_TYPE Enum. + +**Attributes**: + +- `BUY` _int_ - Market buy order +- `SELL` _int_ - Market sell order +- `BUY_LIMIT` _int_ - Buy Limit pending order +- `SELL_LIMIT` _int_ - Sell Limit pending order +- `BUY_STOP` _int_ - Buy Stop pending order +- `SELL_STOP` _int_ - Sell Stop pending order +- `BUY_STOP_LIMIT` _int_ - Upon reaching the order price, Buy Limit pending order is placed at StopLimit price +- `SELL_STOP_LIMIT` _int_ - Upon reaching the order price, Sell Limit pending order is placed at StopLimit price +- `CLOSE_BY` _int_ - Order for closing a position by an opposite one + + Properties: +- `opposite` _int_ - Gets the opposite of an order type + + + +#### opposite + +```python +@property +def opposite() +``` + +Gets the opposite of an order type for closing an open position + +**Returns**: + +- `int` - integer value of opposite order type + + + +## BookType Objects + +```python +class BookType(Repr, IntEnum) +``` + +BOOK_TYPE Enum. + +**Attributes**: + +- `SELL` _int_ - Sell order (Offer) +- `BUY` _int_ - Buy order (Bid) +- `SELL_MARKET` _int_ - Sell order by Market +- `BUY_MARKET` _int_ - Buy order by Market + + + +## TimeFrame Objects + +```python +class TimeFrame(Repr, IntEnum) +``` + +TIMEFRAME Enum. + +**Attributes**: + +- `M1` _int_ - One Minute +- `M2` _int_ - Two Minutes +- `M3` _int_ - Three Minutes +- `M4` _int_ - Four Minutes +- `M5` _int_ - Five Minutes +- `M6` _int_ - Six Minutes +- `M10` _int_ - Ten Minutes +- `M15` _int_ - Fifteen Minutes +- `M20` _int_ - Twenty Minutes +- `M30` _int_ - Thirty Minutes +- `H1` _int_ - One Hour +- `H2` _int_ - Two Hours +- `H3` _int_ - Three Hours +- `H4` _int_ - Four Hours +- `H6` _int_ - Six Hours +- `H8` _int_ - Eight Hours +- `D1` _int_ - One Day +- `W1` _int_ - One Week +- `MN1` _int_ - One Month + + Properties: +- `time` - return the value of the timeframe object in seconds. Used as a property + + +**Methods**: + +- `get` - get a timeframe object from a time value in seconds + + + +#### time + +```python +@property +def time() +``` + +The number of seconds in a TIMEFRAME + +**Returns**: + +- `int` - The number of seconds in a TIMEFRAME + + +**Examples**: + + >>> t = TimeFrame.H1 + >>> print(t.time) + 3600 + + + +## CopyTicks Objects + +```python +class CopyTicks(Repr, IntEnum) +``` + +COPY_TICKS Enum. This defines the types of ticks that can be requested using the copy_ticks_from() and +copy_ticks_range() functions. + +**Attributes**: + +- `ALL` _int_ - All ticks +- `INFO` _int_ - Ticks containing Bid and/or Ask price changes +- `TRADE` _int_ - Ticks containing Last and/or Volume price changes + + + +## PositionType Objects + +```python +class PositionType(Repr, IntEnum) +``` + +POSITION_TYPE Enum. Direction of an open position (buy or sell) + +**Attributes**: + +- `BUY` _int_ - Buy +- `SELL` _int_ - Sell + + + +## PositionReason Objects + +```python +class PositionReason(Repr, IntEnum) +``` + +POSITION_REASON Enum. The reason for opening a position is contained in the POSITION_REASON Enum + +**Attributes**: + +- `CLIENT` _int_ - The position was opened as a result of activation of an order placed from a desktop terminal +- `MOBILE` _int_ - The position was opened as a result of activation of an order placed from a mobile application +- `WEB` _int_ - The position was opened as a result of activation of an order placed from the web platform +- `EXPERT` _int_ - The position was opened as a result of activation of an order placed from an MQL5 program, + i.e. an Expert Advisor or a script + + + +## DealType Objects + +```python +class DealType(Repr, IntEnum) +``` + +DEAL_TYPE enum. Each deal is characterized by a type, allowed values are enumerated in this enum + +**Attributes**: + +- `BUY` _int_ - Buy +- `SELL` _int_ - Sell +- `BALANCE` _int_ - Balance +- `CREDIT` _int_ - Credit +- `CHARGE` _int_ - Additional Charge +- `CORRECTION` _int_ - Correction +- `BONUS` _int_ - Bonus +- `COMMISSION` _int_ - Additional Commission +- `COMMISSION_DAILY` _int_ - Daily Commission +- `COMMISSION_MONTHLY` _int_ - Monthly Commission +- `COMMISSION_AGENT_DAILY` _int_ - Daily Agent Commission +- `COMMISSION_AGENT_MONTHLY` _int_ - Monthly Agent Commission +- `INTEREST` _int_ - Interest Rate +- `DEAL_DIVIDEND` _int_ - Dividend Operations +- `DEAL_DIVIDEND_FRANKED` _int_ - Franked (non-taxable) dividend operations +- `DEAL_TAX` _int_ - Tax Charges + +- `BUY_CANCELED` _int_ - Canceled buy deal. There can be a situation when a previously executed buy deal is canceled. + In this case, the type of the previously executed deal (DEAL_TYPE_BUY) is changed to DEAL_TYPE_BUY_CANCELED, + and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated + balance operation + +- `SELL_CANCELED` _int_ - Canceled sell deal. There can be a situation when a previously executed sell deal is + canceled. In this case, the type of the previously executed deal (DEAL_TYPE_SELL) is changed to + DEAL_TYPE_SELL_CANCELED, and its profit/loss is zeroized. Previously obtained profit/loss is + charged/withdrawn using a separated balance operation. + + + +## DealEntry Objects + +```python +class DealEntry(Repr, IntEnum) +``` + +DEAL_ENTRY Enum. Deals differ not only in their types set in DEAL_TYPE enum, but also in the way they change +positions. This can be a simple position opening, or accumulation of a previously opened position (market entering), +position closing by an opposite deal of a corresponding volume (market exiting), or position reversing, if the +opposite-direction deal covers the volume of the previously opened position. + +**Attributes**: + +- `IN` _int_ - Entry In +- `OUT` _int_ - Entry Out +- `INOUT` _int_ - Reverse +- `OUT_BY` _int_ - Close a position by an opposite one + + + +## DealReason Objects + +```python +class DealReason(Repr, IntEnum) +``` + +DEAL_REASON Enum. The reason for deal execution is contained in the DEAL_REASON property. A deal can be executed +as a result of triggering of an order placed from a mobile application or an MQL5 program, as well as as a result +of the StopOut event, variation margin calculation, etc. + +**Attributes**: + +- `CLIENT` _int_ - The deal was executed as a result of activation of an order placed from a desktop terminal +- `MOBILE` _int_ - The deal was executed as a result of activation of an order placed from a desktop terminal +- `WEB` _int_ - The deal was executed as a result of activation of an order placed from the web platform +- `EXPERT` _int_ - 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 +- `SL` _int_ - The deal was executed as a result of Stop Loss activation +- `TP` _int_ - The deal was executed as a result of Take Profit activation +- `SO` _int_ - The deal was executed as a result of the Stop Out event +- `ROLLOVER` _int_ - The deal was executed due to a rollover +- `VMARGIN` _int_ - The deal was executed after charging the variation margin +- `SPLIT` _int_ - The deal was executed after the split (price reduction) of an instrument, which had an open + position during split announcement + + + +## OrderReason Objects + +```python +class OrderReason(Repr, IntEnum) +``` + +ORDER_REASON Enum. + +**Attributes**: + +- `CLIENT` _int_ - The order was placed from a desktop terminal +- `MOBILE` _int_ - The order was placed from a mobile application +- `WEB` _int_ - The order was placed from a web platform +- `EXPERT` _int_ - The order was placed from an MQL5-program, i.e. by an Expert Advisor or a script +- `SL` _int_ - The order was placed as a result of Stop Loss activation +- `TP` _int_ - The order was placed as a result of Take Profit activation +- `SO` _int_ - The order was placed as a result of the Stop Out event + + + +## SymbolChartMode Objects + +```python +class SymbolChartMode(Repr, IntEnum) +``` + +SYMBOL_CHART_MODE Enum. A symbol price chart can be based on Bid or Last prices. The price selected for symbol +charts also affects the generation and display of bars in the terminal. +Possible values of the SYMBOL_CHART_MODE property are described in this enum + +**Attributes**: + +- `BID` _int_ - Bars are based on Bid prices +- `LAST` _int_ - Bars are based on last prices + + + +## SymbolCalcMode Objects + +```python +class SymbolCalcMode(Repr, IntEnum) +``` + +SYMBOL_CALC_MODE Enum. The SYMBOL_CALC_MODE enumeration is used for obtaining information about how the margin +requirements for a symbol are calculated. + +**Attributes**: + +- `FOREX` _int_ - Forex mode - calculation of profit and margin for Forex +- `FOREX_NO_LEVERAGE` _int_ - Forex No Leverage mode – calculation of profit and margin for Forex symbols without + taking into account the leverage +- `FUTURES` _int_ - Futures mode - calculation of margin and profit for futures +- `CFD` _int_ - CFD mode - calculation of margin and profit for CFD +- `CFDINDEX` _int_ - CFD index mode - calculation of margin and profit for CFD by indexes +- `CFDLEVERAGE` _int_ - CFD Leverage mode - calculation of margin and profit for CFD at leverage trading +- `EXCH_STOCKS` _int_ - Calculation of margin and profit for trading securities on a stock exchange +- `EXCH_FUTURES` _int_ - Calculation of margin and profit for trading futures contracts on a stock exchange +- `EXCH_OPTIONS` _int_ - value is 34 +- `EXCH_OPTIONS_MARGIN` _int_ - value is 36 +- `EXCH_BONDS` _int_ - Exchange Bonds mode – calculation of margin and profit for trading bonds on a stock exchange +- `STOCKS_MOEX` _int_ - Exchange MOEX Stocks mode –calculation of margin and profit for trading securities on MOEX +- `EXCH_BONDS_MOEX` _int_ - Exchange MOEX Bonds mode – calculation of margin and profit for trading bonds on MOEX + +- `SERV_COLLATERAL` _int_ - Collateral mode - a symbol is used as a non-tradable asset on a trading account. + The market value of an open position is calculated based on the volume, current market price, contract size + and liquidity ratio. The value is included into Assets, which are added to Equity. Open positions of such + symbols increase the Free Margin amount and are used as additional margin (collateral) for open positions + + + +## SymbolTradeMode Objects + +```python +class SymbolTradeMode(Repr, IntEnum) +``` + +SYMBOL_TRADE_MODE Enum. There are several symbol trading modes. Information about trading modes of a certain +symbol is reflected in the values this enumeration + +**Attributes**: + +- `DISABLED` _int_ - Trade is disabled for the symbol +- `LONGONLY` _int_ - Allowed only long positions +- `SHORTONLY` _int_ - Allowed only short positions +- `CLOSEONLY` _int_ - Allowed only position close operations +- `FULL` _int_ - No trade restrictions + + + +## SymbolTradeExecution Objects + +```python +class SymbolTradeExecution(Repr, IntEnum) +``` + +SYMBOL_TRADE_EXECUTION Enum. The modes, or execution policies, define the rules for cases when the price has +changed or the requested volume cannot be completely fulfilled at the moment. + +**Attributes**: + +- `REQUEST` _int_ - Executing a market order at the price previously received from the broker. Prices for a certain + market order are requested from the broker before the order is sent. Upon receiving the prices, order + execution at the given price can be either confirmed or rejected. + +- `INSTANT` _int_ - Executing a market order at the specified price immediately. When sending a trade request to be + executed, the platform automatically adds the current prices to the order. + - If the broker accepts the price, the order is executed. + - If the broker does not accept the requested price, a "Requote" is sent — the broker returns prices, + at which this order can be executed. + +- `MARKET` _int_ - A broker makes a decision about the order execution price without any additional discussion with the trader. + Sending the order in such a mode means advance consent to its execution at this price. + +- `EXCHANGE` _int_ - Trade operations are executed at the prices of the current market offers. + + + +## SymbolSwapMode Objects + +```python +class SymbolSwapMode(Repr, IntEnum) +``` + +SYMBOL_SWAP_MODE Enum. Methods of swap calculation at position transfer are specified in enumeration +ENUM_SYMBOL_SWAP_MODE. The method of swap calculation determines the units of measure of the SYMBOL_SWAP_LONG and +SYMBOL_SWAP_SHORT parameters. For example, if swaps are charged in the client deposit currency, then the values of +those parameters are specified as an amount of money in the client deposit currency. + +**Attributes**: + +- `DISABLED` _int_ - Swaps disabled (no swaps) +- `POINTS` _int_ - Swaps are charged in points +- `CURRENCY_SYMBOL` _int_ - Swaps are charged in money in base currency of the symbol +- `CURRENCY_MARGIN` _int_ - Swaps are charged in money in margin currency of the symbol +- `CURRENCY_DEPOSIT` _int_ - Swaps are charged in money, in client deposit currency + +- `INTEREST_CURRENT` _int_ - Swaps are charged as the specified annual interest from the instrument price at + calculation of swap (standard bank year is 360 days) + +- `INTEREST_OPEN` _int_ - Swaps are charged as the specified annual interest from the open price of position + (standard bank year is 360 days) + +- `REOPEN_CURRENT` _int_ - Swaps are charged by reopening positions. At the end of a trading day the position is + closed. Next day it is reopened by the close price +/- specified number of points + (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + +- `REOPEN_BID` _int_ - Swaps are charged by reopening positions. At the end of a trading day the position is closed. + Next day it is reopened by the current Bid price +/- specified number of + points (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + + + +## DayOfWeek Objects + +```python +class DayOfWeek(Repr, IntEnum) +``` + +DAY_OF_WEEK Enum. + +**Attributes**: + +- `SUNDAY` _int_ - Sunday +- `MONDAY` _int_ - Monday +- `TUESDAY` _int_ - Tuesday +- `WEDNESDAY` _int_ - Wednesday +- `THURSDAY` _int_ - Thursday +- `FRIDAY` _int_ - Friday +- `SATURDAY` _int_ - Saturday + + + +## SymbolOrderGTCMode Objects + +```python +class SymbolOrderGTCMode(Repr, IntEnum) +``` + +SYMBOL_ORDER_GTC_MODE Enum. If the SYMBOL_EXPIRATION_MODE property is set to SYMBOL_EXPIRATION_GTC +(good till canceled), the expiration of pending orders, as well as of +Stop Loss/Take Profit orders should be additionally set using the ENUM_SYMBOL_ORDER_GTC_MODE enumeration. + +**Attributes**: + +- `GTC` _int_ - Pending orders and Stop Loss/Take Profit levels are valid for an unlimited period + until theirConstants, Enumerations and explicit cancellation + +- `DAILY` _int_ - Orders are valid during one trading day. At the end of the day, all Stop Loss and + Take Profit levels, as well as pending orders are deleted. + +- `DAILY_NO_STOPS` _int_ - When a trade day changes, only pending orders are deleted, + while Stop Loss and Take Profit levels are preserved + + + +## SymbolOptionRight Objects + +```python +class SymbolOptionRight(Repr, IntEnum) +``` + +SYMBOL_OPTION_RIGHT Enum. An option is a contract, which gives the right, but not the obligation, +to buy or sell an underlying asset (goods, stocks, futures, etc.) at a specified price on or before a specific date. +The following enumerations describe option properties, including the option type and the right arising from it. + +**Attributes**: + +- `CALL` _int_ - A call option gives you the right to buy an asset at a specified price. +- `PUT` _int_ - A put option gives you the right to sell an asset at a specified price. + + + +## SymbolOptionMode Objects + +```python +class SymbolOptionMode(Repr, IntEnum) +``` + +SYMBOL_OPTION_MODE Enum. + +**Attributes**: + +- `EUROPEAN` _int_ - European option may only be exercised on a specified date (expiration, execution date, delivery date) +- `AMERICAN` _int_ - American option may be exercised on any trading day or before expiry. The period within which + a buyer can exercise the option is specified for it. + + + +## AccountTradeMode Objects + +```python +class AccountTradeMode(Repr, IntEnum) +``` + +ACCOUNT_TRADE_MODE Enum. There are several types of accounts that can be opened on a trade server. +The type of account on which an MQL5 program is running can be found out using +the ENUM_ACCOUNT_TRADE_MODE enumeration. + +**Attributes**: + +- `DEMO` - Demo account +- `CONTEST` - Contest account +- `REAL` - Real Account + + + +## TickFlag Objects + +```python +class TickFlag(Repr, IntFlag) +``` + +TICK_FLAG Enum. TICK_FLAG defines possible flags for ticks. These flags are used to describe ticks obtained by the +copy_ticks_from() and copy_ticks_range() functions. + +**Attributes**: + +- `BID` _int_ - Bid price changed +- `ASK` _int_ - Ask price changed +- `LAST` _int_ - Last price changed +- `VOLUME` _int_ - Volume changed +- `BUY` _int_ - last Buy price changed +- `SELL` _int_ - last Sell price changed + + + +## TradeRetcode Objects + +```python +class TradeRetcode(Repr, IntEnum) +``` + +TRADE_RETCODE Enum. Return codes for order send/check operations + +**Attributes**: + +- `REQUOTE` _int_ - Requote +- `REJECT` _int_ - Request rejected +- `CANCEL` _int_ - Request canceled by trader +- `PLACED` _int_ - Order placed +- `DONE` _int_ - Request completed +- `DONE_PARTIAL` _int_ - Only part of the request was completed +- `ERROR` _int_ - Request processing error +- `TIMEOUT` _int_ - Request canceled by timeout +- `INVALID` _int_ - Invalid request +- `INVALID_VOLUME` _int_ - Invalid volume in the request +- `INVALID_PRICE` _int_ - Invalid price in the request +- `INVALID_STOPS` _int_ - Invalid stops in the request +- `TRADE_DISABLED` _int_ - Trade is disabled +- `MARKET_CLOSED` _int_ - Market is closed +- `NO_MONEY` _int_ - There is not enough money to complete the request +- `PRICE_CHANGED` _int_ - Prices changed +- `PRICE_OFF` _int_ - There are no quotes to process the request +- `INVALID_EXPIRATION` _int_ - Invalid order expiration date in the request +- `ORDER_CHANGED` _int_ - Order state changed +- `TOO_MANY_REQUESTS` _int_ - Too frequent requests +- `NO_CHANGES` _int_ - No changes in request +- `SERVER_DISABLES_AT` _int_ - Autotrading disabled by server +- `CLIENT_DISABLES_AT` _int_ - Autotrading disabled by client terminal +- `LOCKED` _int_ - Request locked for processing +- `FROZEN` _int_ - Order or position frozen +- `INVALID_FILL` _int_ - Invalid order filling type +- `CONNECTION` _int_ - No connection with the trade server +- `ONLY_REAL` _int_ - Operation is allowed only for live accounts +- `LIMIT_ORDERS` _int_ - The number of pending orders has reached the limit +- `LIMIT_VOLUME` _int_ - The volume of orders and positions for the symbol has reached the limit +- `INVALID_ORDER` _int_ - Incorrect or prohibited order type +- `POSITION_CLOSED` _int_ - Position with the specified POSITION_IDENTIFIER has already been closed +- `INVALID_CLOSE_VOLUME` _int_ - A close volume exceeds the current position volume + +- `CLOSE_ORDER_EXIST` _int_ - A close order already exists for a specified position. This may happen when working in + the hedging system: + · when attempting to close a position with an opposite one, while close orders for the position already exist + · when attempting to fully or partially close a position if the total volume of the already present close + orders and the newly placed one exceeds the current position volume + +- `LIMIT_POSITIONS` _int_ - The number of open positions simultaneously present on an account can be limited by the + server settings.After a limit is reached, the server returns the TRADE_RETCODE_LIMIT_POSITIONS error when + attempting to place an order. The limitation operates differently depending on the position accounting type: + · Netting — number of open positions is considered. When a limit is reached, the platform does not let + placing new orders whose execution may increase the number of open positions. In fact, the platform + allows placing orders only for the symbols that already have open positions. + The current pending orders are not considered since their execution may lead to changes in the current + positions but it cannot increase their number. + + · Hedging — pending orders are considered together with open positions, since a pending order activation + always leads to opening a new position. When a limit is reached, the platform does not allow placing + both new market orders for opening positions and pending orders. + +- `REJECT_CANCEL` _int_ - The pending order activation request is rejected, the order is canceled. +- `LONG_ONLY` _int_ - The request is rejected, because the "Only long positions are allowed" rule is set for the + symbol (POSITION_TYPE_BUY) +- `SHORT_ONLY` _int_ - The request is rejected, because the "Only short positions are allowed" rule is set for the + symbol (POSITION_TYPE_SELL) +- `CLOSE_ONLY` _int_ - The request is rejected, because the "Only position closing is allowed" rule is set for the + symbol +- `FIFO_CLOSE` _int_ - The request is rejected, because "Position closing is allowed only by FIFO rule" flag is set + for the trading account (ACCOUNT_FIFO_CLOSE=true) + + + +## AccountStopOutMode Objects + +```python +class AccountStopOutMode(Repr, IntEnum) +``` + +ACCOUNT_STOPOUT_MODE Enum. + +**Attributes**: + +- `PERCENT` _int_ - Account stop out mode in percents +- `MONEY` _int_ - Account stop out mode in money + + + +## AccountMarginMode Objects + +```python +class AccountMarginMode(Repr, IntEnum) +``` + +ACCOUNT_MARGIN_MODE Enum. + +**Attributes**: + +- `RETAIL_NETTING` _int_ - Used for the OTC markets to interpret positions in the "netting" + mode (only one position can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE). + +- `EXCHANGE` _int_ - Used for the exchange markets. Margin is calculated based on the discounts specified in + symbol settings. Discounts are set by the broker, but not less than the values set by the exchange. + +- `HEDGING` _int_ - Used for the exchange markets where individual positions are possible + (hedging, multiple positions can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE) taking into account the hedged margin (SYMBOL_MARGIN_HEDGED). + diff --git a/docs/core/errors.md b/docs/core/errors.md new file mode 100644 index 0000000..f6a86c0 --- /dev/null +++ b/docs/core/errors.md @@ -0,0 +1,19 @@ +# Table of Contents + +* [aiomql.core.errors](#aiomql.core.errors) + * [Error](#aiomql.core.errors.Error) + + + +# aiomql.core.errors + + + +## Error Objects + +```python +class Error() +``` + +Error class for handling errors from MetaTrader 5. + diff --git a/docs/core/exceptions.md b/docs/core/exceptions.md new file mode 100644 index 0000000..46470e2 --- /dev/null +++ b/docs/core/exceptions.md @@ -0,0 +1,54 @@ +# Table of Contents + +* [aiomql.core.exceptions](#aiomql.core.exceptions) + * [LoginError](#aiomql.core.exceptions.LoginError) + * [VolumeError](#aiomql.core.exceptions.VolumeError) + * [SymbolError](#aiomql.core.exceptions.SymbolError) + * [OrderError](#aiomql.core.exceptions.OrderError) + + + +# aiomql.core.exceptions + +Exceptions for the aiomql package. + + + +## LoginError Objects + +```python +class LoginError(Exception) +``` + +Raised when an error occurs when logging in. + + + +## VolumeError Objects + +```python +class VolumeError(Exception) +``` + +Raised when a volume is not valid or out of range for a symbol. + + + +## SymbolError Objects + +```python +class SymbolError(Exception) +``` + +Raised when a symbol is not provided where required or not available in the Market Watch. + + + +## OrderError Objects + +```python +class OrderError(Exception) +``` + +Raised when an error occurs when working with the order class. + diff --git a/docs/core/meta_trader.md b/docs/core/meta_trader.md new file mode 100644 index 0000000..fcdbe53 --- /dev/null +++ b/docs/core/meta_trader.md @@ -0,0 +1,165 @@ +# Table of Contents + +* [aiomql.core.meta\_trader](#aiomql.core.meta_trader) + * [MetaTrader](#aiomql.core.meta_trader.MetaTrader) + * [\_\_aenter\_\_](#aiomql.core.meta_trader.MetaTrader.__aenter__) + * [\_\_aexit\_\_](#aiomql.core.meta_trader.MetaTrader.__aexit__) + * [login](#aiomql.core.meta_trader.MetaTrader.login) + * [initialize](#aiomql.core.meta_trader.MetaTrader.initialize) + * [shutdown](#aiomql.core.meta_trader.MetaTrader.shutdown) + * [version](#aiomql.core.meta_trader.MetaTrader.version) + * [account\_info](#aiomql.core.meta_trader.MetaTrader.account_info) + * [orders\_get](#aiomql.core.meta_trader.MetaTrader.orders_get) + + + +# aiomql.core.meta\_trader + + + +## MetaTrader Objects + +```python +class MetaTrader(metaclass=BaseMeta) +``` + + + +#### \_\_aenter\_\_ + +```python +async def __aenter__() -> 'MetaTrader' +``` + +Async context manager entry point. +Initializes the connection to the MetaTrader terminal. + +**Returns**: + +- `MetaTrader` - An instance of the MetaTrader class. + + + +#### \_\_aexit\_\_ + +```python +async def __aexit__(exc_type, exc_val, exc_tb) +``` + +Async context manager exit point. Closes the connection to the MetaTrader terminal. + + + +#### login + +```python +async def login(login: int, + password: str, + server: str, + timeout: int = 60000) -> bool +``` + +Connects to the MetaTrader terminal using the specified login, password and server. + +**Arguments**: + +- `login` _int_ - The trading account number. +- `password` _str_ - The trading account password. +- `server` _str_ - The trading server name. +- `timeout` _int_ - The timeout for the connection in seconds. + + +**Returns**: + +- `bool` - True if successful, False otherwise. + + + +#### initialize + +```python +async def initialize(path: str = "", + login: int = 0, + password: str = "", + server: str = "", + timeout: int | None = None, + portable=False) -> bool +``` + +Initializes the connection to the MetaTrader terminal. All parameters are optional. + +**Arguments**: + +- `path` _str_ - The path to the MetaTrader terminal executable. +- `login` _int_ - The trading account number. +- `password` _str_ - The trading account password. +- `server` _str_ - The trading server name. +- `timeout` _int_ - The timeout for the connection in seconds. +- `portable` _bool_ - If True, the terminal will be launched in portable mode. + + +**Returns**: + +- `bool` - True if successful, False otherwise. + + + +#### shutdown + +```python +async def shutdown() -> None +``` + +Closes the connection to the MetaTrader terminal. + +**Returns**: + +- `None` - None + + + +#### version + +```python +async def version() -> tuple[int, int, str] | None +``` + + + + + +#### account\_info + +```python +async def account_info() -> AccountInfo | None +``` + + + + + +#### orders\_get + +```python +async def orders_get(group: str = "", + ticket: int = 0, + symbol: str = "") -> tuple[TradeOrder] | None +``` + +Get active orders with the ability to filter by symbol or ticket. There are three call options. +Call without parameters. Return active orders on all symbols + +**Arguments**: + +- `symbol` _str_ - Symbol name. Optional named parameter. If a symbol is specified, the ticket parameter is ignored. + +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function + returns only active orders meeting a specified criteria for a symbol name. + +- `ticket` _int_ - Order ticket (ORDER_TICKET). Optional named parameter. + + +**Returns**: + +- `list[TradeOrder]` - A list of active trade orders as TradeOrder objects + diff --git a/docs/core/model.md b/docs/core/model.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/core/models.md b/docs/core/models.md new file mode 100644 index 0000000..d6ba5e0 --- /dev/null +++ b/docs/core/models.md @@ -0,0 +1,405 @@ +# Table of Contents + +* [aiomql.core.models](#aiomql.core.models) + * [AccountInfo](#aiomql.core.models.AccountInfo) + * [TerminalInfo](#aiomql.core.models.TerminalInfo) + * [SymbolInfo](#aiomql.core.models.SymbolInfo) + * [BookInfo](#aiomql.core.models.BookInfo) + * [TradeOrder](#aiomql.core.models.TradeOrder) + * [TradeRequest](#aiomql.core.models.TradeRequest) + * [OrderCheckResult](#aiomql.core.models.OrderCheckResult) + * [OrderSendResult](#aiomql.core.models.OrderSendResult) + * [TradePosition](#aiomql.core.models.TradePosition) + * [TradeDeal](#aiomql.core.models.TradeDeal) + + + +# aiomql.core.models + + + +## AccountInfo Objects + +```python +class AccountInfo(Base) +``` + +Account Information Class. + +**Attributes**: + +- `login` - int +- `password` - str +- `server` - str +- `trade_mode` - AccountTradeMode +- `balance` - float +- `leverage` - float +- `profit` - float +- `point` - float +- `amount` - float = 0 +- `equity` - float +- `credit` - float +- `margin` - float +- `margin_level` - float +- `margin_free` - float +- `margin_mode` - AccountMarginMode +- `margin_so_mode` - AccountStopoutMode +- `margin_so_call` - float +- `margin_so_so` - float +- `margin_initial` - float +- `margin_maintenance` - float +- `fifo_close` - bool +- `limit_orders` - float +- `currency` - str = "USD" +- `trade_allowed` - bool = True +- `trade_expert` - bool = True +- `currency_digits` - int +- `assets` - float +- `liabilities` - float +- `commission_blocked` - float +- `name` - str +- `company` - str + + + +## TerminalInfo Objects + +```python +class TerminalInfo(Base) +``` + +Terminal information class. Holds information about the terminal. + +**Attributes**: + +- `community_account` - bool +- `community_connection` - bool +- `connected` - bool +- `dlls_allowed` - bool +- `trade_allowed` - bool +- `tradeapi_disabled` - bool +- `email_enabled` - bool +- `ftp_enabled` - bool +- `notifications_enabled` - bool +- `mqid` - bool +- `build` - int +- `maxbars` - int +- `codepage` - int +- `ping_last` - int +- `community_balance` - float +- `retransmission` - float +- `company` - str +- `name` - str +- `language` - str +- `path` - str +- `data_path` - str +- `commondata_path` - str + + + +## SymbolInfo Objects + +```python +class SymbolInfo(Base) +``` + +Symbol Information Class. Symbols are financial instruments available for trading in the MetaTrader 5 terminal. + +**Attributes**: + +- `name` - str +- `custom` - bool +- `chart_mode` - SymbolChartMode +- `select` - bool +- `visible` - bool +- `session_deals` - int +- `session_buy_orders` - int +- `session_sell_orders` - int +- `volume` - float +- `volumehigh` - float +- `volumelow` - float +- `time` - int +- `digits` - int +- `spread` - float +- `spread_float` - bool +- `ticks_bookdepth` - int +- `trade_calc_mode` - SymbolCalcMode +- `trade_mode` - SymbolTradeMode +- `start_time` - int +- `expiration_time` - int +- `trade_stops_level` - int +- `trade_freeze_level` - int +- `trade_exemode` - SymbolTradeExecution +- `swap_mode` - SymbolSwapMode +- `swap_rollover3days` - DayOfWeek +- `margin_hedged_use_leg` - bool +- `expiration_mode` - int +- `filling_mode` - int +- `order_mode` - int +- `order_gtc_mode` - SymbolOrderGTCMode +- `option_mode` - SymbolOptionMode +- `option_right` - SymbolOptionRight +- `bid` - float +- `bidhigh` - float +- `bidlow` - float +- `ask` - float +- `askhigh` - float +- `asklow` - float +- `last` - float +- `lasthigh` - float +- `lastlow` - float +- `volume_real` - float +- `volumehigh_real` - float +- `volumelow_real` - float +- `option_strike` - float +- `point` - float +- `trade_tick_value` - float +- `trade_tick_value_profit` - float +- `trade_tick_value_loss` - float +- `trade_tick_size` - float +- `trade_contract_size` - float +- `trade_accrued_interest` - float +- `trade_face_value` - float +- `trade_liquidity_rate` - float +- `volume_min` - float +- `volume_max` - float +- `volume_step` - float +- `volume_limit` - float +- `swap_long` - float +- `swap_short` - float +- `margin_initial` - float +- `margin_maintenance` - float +- `session_volume` - float +- `session_turnover` - float +- `session_interest` - float +- `session_buy_orders_volume` - float +- `session_sell_orders_volume` - float +- `session_open` - float +- `session_close` - float +- `session_aw` - float +- `session_price_settlement` - float +- `session_price_limit_min` - float +- `session_price_limit_max` - float +- `margin_hedged` - float +- `price_change` - float +- `price_volatility` - float +- `price_theoretical` - float +- `price_greeks_delta` - float +- `price_greeks_theta` - float +- `price_greeks_gamma` - float +- `price_greeks_vega` - float +- `price_greeks_rho` - float +- `price_greeks_omega` - float +- `price_sensitivity` - float +- `basis` - str +- `category` - str +- `currency_base` - str +- `currency_profit` - str +- `currency_margin` - Any +- `bank` - str +- `description` - str +- `exchange` - str +- `formula` - Any +- `isin` - Any +- `name` - str +- `page` - str +- `path` - str + + + +## BookInfo Objects + +```python +class BookInfo(Base) +``` + +Book Information Class. + +**Attributes**: + +- `type` - BookType +- `price` - float +- `volume` - float +- `volume_dbl` - float + + + +## TradeOrder Objects + +```python +class TradeOrder(Base) +``` + +Trade Order Class. + +**Attributes**: + +- `ticket` - int +- `time_setup` - int +- `time_setup_msc` - int +- `time_expiration` - int +- `time_done` - int +- `time_done_msc` - int +- `type` - OrderType +- `type_time` - OrderTime +- `type_filling` - OrderFilling +- `state` - int +- `magic` - int +- `position_id` - int +- `position_by_id` - int +- `reason` - OrderReason +- `volume_current` - float +- `volume_initial` - float +- `price_open` - float +- `sl` - float +- `tp` - float +- `price_current` - float +- `price_stoplimit` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +## TradeRequest Objects + +```python +class TradeRequest(Base) +``` + +Trade Request Class. + +**Attributes**: + +- `action` - TradeAction +- `type` - OrderType +- `order` - int +- `symbol` - str +- `volume` - float +- `sl` - float +- `tp` - float +- `price` - float +- `deviation` - float +- `stop_limit` - float +- `type_time` - OrderTime +- `type_filling` - OrderFilling +- `expiration` - int +- `position` - int +- `position_by` - int +- `comment` - str +- `magic` - int +- `deviation` - int +- `comment` - str + + + +## OrderCheckResult Objects + +```python +class OrderCheckResult(Base) +``` + +Order Check Result + +**Attributes**: + +- `retcode` - int +- `balance` - float +- `equity` - float +- `profit` - float +- `margin` - float +- `margin_free` - float +- `margin_level` - float +- `comment` - str +- `request` - TradeRequest + + + +## OrderSendResult Objects + +```python +class OrderSendResult(Base) +``` + +Order Send Result + +**Attributes**: + +- `retcode` - int +- `deal` - int +- `order` - int +- `volume` - float +- `price` - float +- `bid` - float +- `ask` - float +- `comment` - str +- `request` - TradeRequest +- `request_id` - int +- `retcode_external` - int +- `profit` - float + + + +## TradePosition Objects + +```python +class TradePosition(Base) +``` + +Trade Position + +**Attributes**: + +- `ticket` - int +- `time` - int +- `time_msc` - int +- `time_update` - int +- `time_update_msc` - int +- `type` - OrderType +- `magic` - float +- `identifier` - int +- `reason` - PositionReason +- `volume` - float +- `price_open` - float +- `sl` - float +- `tp` - float +- `price_current` - float +- `swap` - float +- `profit` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +## TradeDeal Objects + +```python +class TradeDeal(Base) +``` + +Trade Deal + +**Attributes**: + +- `ticket` - int +- `order` - int +- `time` - int +- `time_msc` - int +- `type` - DealType +- `entry` - DealEntry +- `magic` - int +- `position_id` - int +- `reason` - DealReason +- `volume` - float +- `price` - float +- `commission` - float +- `swap` - float +- `profit` - float +- `fee` - float +- `sl` - float +- `tp` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + diff --git a/docs/executor.md b/docs/executor.md new file mode 100644 index 0000000..ef8995f --- /dev/null +++ b/docs/executor.md @@ -0,0 +1,93 @@ +## Executor + + +```python +class Executor +``` +Executor class for running multiple strategies on multiple symbols concurrently. +**Attributes**: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**executor**|**ThreadPoolExecutor** | The default thread executor. |None| +|**workers**|**list** | List of strategies. |[]| +|**coros**|**dict** | Dictionary of coroutines and keyword arguments | {} | +|**funcs**|**dict** | Dictionary of functions and keyword arguments | {} | + +#### add\_workers +```python +def add_workers(strategies: Sequence[type(Strategy)]) +``` +Add multiple strategies at once + +*Arguments*: + +|Name|Type|Description| +|---|---|---| +|**strategies**|**Sequence[type(Strategy)]**|A sequence of strategies.| + +#### remove\_workers +```python +def remove_workers(*symbols: Sequence[Symbol]) +``` +Removes any worker running on a symbol not successfully initialized. + +*Arguments*: + +|Name|Type|Description| +|---|---|---| +|**symbols**|**Sequence[Symbol]**|A sequence of symbols.| + +#### add\_worker +```python +def add_worker(strategy: type(Strategy)) +``` +Add a strategy instance to the list of workers + +*Arguments*: + +|Name|Type|Description| +|---|---|---| +|**strategy**|**type(Strategy)**|A strategy instance.| + + +#### run +```python +@staticmethod +def run(func: Callable|Coroutine, kwargs: dict) +``` +Wrap the input coroutine function with 'asyncio.run' so that it can be executed in a threadpool executor. + +*Arguments* + +| Name | Type |Description| +|------------|------------|---| +| **func** | **Callable |Coroutine**|A coroutine function.| +| **kwargs** | **Dict** |Keyword arguments to pass to the function.| + +#### trade +```python +def trade(strategy: Strategy) +``` +Wrap the input coroutine function trade method of each strategy with 'asyncio.run'. + +*Arguments*: + +|Name|Type|Description| +|---|---|---| +|**strategy**|**Strategy**|A strategy instance.| + +#### execute +```python +async def execute(workers: int = 0) +``` +Run the strategies with a threadpool executor. + +*Arguments*: + +|Name|Type|Description| +|---|---|---| +|**workers**|**int**|Number of workers to use in executor pool. Defaults to zero which uses all workers.| + +**Notes**: No matter the number specified, the executor will always use a minimum of 5 workers. + diff --git a/docs/history.md b/docs/history.md new file mode 100644 index 0000000..12cbb82 --- /dev/null +++ b/docs/history.md @@ -0,0 +1,110 @@ +## History + + +```python +class History +``` +The history class handles completed trade deals and trade orders in the trading history of an account. + +**Attributes**: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**deals**|**list[TradeDeal]** | Iterable of trade deals | [] | +|**orders**|**list[TradeOrder]** | Iterable of trade orders | [] | +|**total_deals**|**int** | Total number of deals | 0 | +|**total_orders**|**int** | Total number orders | 0 | +|**group**|**str** | Filter for selecting history by symbols. | "" | +|**ticket**|**int** | Filter for selecting history by ticket number | 0 | +|**position**|**int** | Filter for selecting history deals by position | 0 | +|**initialized**|**bool** | check if initial request has been sent to the terminal to get history. | False | +|**mt5**|**MetaTrader** | MetaTrader instance | None | +|**config**|**Config** | Config instance | None | + + +#### \_\_init\_\_ +```python +def __init__(*, + date_from: datetime | float = 0, + date_to: datetime | float = 0, + group: str = "", + ticket: int = 0, + position: int = 0) +``` +*Arguments*: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**date_from**|**datetime, float** | Date the deals are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' | 0 | +|**date_to**|**datetime, float** | Date up to which the deals are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" | 0 | +|**group**|**str** | Filter for selecting history by symbols. | "" | +|**ticket**|**int** | Filter for selecting history by ticket number | 0 | +|**position**|**int** | Filter for selecting history deals by position | 0 | + +#### init +```python +async def init(deals=True, orders=True) -> bool +``` +Get history deals and orders + +*Arguments*: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**deals**|**bool** | If true get history deals during initial request to terminal | True | +|**orders**|**bool** | If true get history orders during initial request to terminal | True | + +*returns*: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**bool**|**bool** | True if all requests were successful else False | False | +- `bool` - True if all requests were successful else False + +#### get_deals +```python +async def get_deals() -> list[TradeDeal] +``` +Get deals from trading history using the parameters set in the constructor. + +*returns*: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**deals**|**list[TradeDeal]** | A list of trade deals | [] | + +#### deals_total +```python +async def deals_total() -> int +``` +Get total number of deals within the specified period in the constructor. + +*returns*: + +|Name| Type | Description | Default | +|---|---------------------|------------------------------------------------|----| +|**total_deals**|**int** | Total number of deals | 0 | + +#### get_orders +```python +async def get_orders() -> list[TradeOrder] +``` +Get orders from trading history using the parameters set in the constructor. +*returns*: + +|Name|Type|Description|Default| +|---|---|---|---| +|**orders**|**list[TradeOrder]**|A list of trade orders|[]| + +#### orders_total +```python +async def orders_total() -> int +``` +Get total number of orders within the specified period in the constructor. + +*returns*: + +|Name| Type | Description | Default | +|---|---------------------|--------------------|----| +|**total_orders**|**int** | Total number orders| 0 | + diff --git a/docs/main.md b/docs/main.md new file mode 100644 index 0000000..be020eb --- /dev/null +++ b/docs/main.md @@ -0,0 +1,3842 @@ +# Table of Contents + +* [aiomql](#aiomql) +* [aiomql.account](#aiomql.account) + * [Account](#aiomql.account.Account) + * [refresh](#aiomql.account.Account.refresh) + * [account\_info](#aiomql.account.Account.account_info) + * [\_\_aenter\_\_](#aiomql.account.Account.__aenter__) + * [sign\_in](#aiomql.account.Account.sign_in) + * [has\_symbol](#aiomql.account.Account.has_symbol) + * [symbols\_get](#aiomql.account.Account.symbols_get) +* [aiomql.bot\_builder](#aiomql.bot_builder) + * [Bot](#aiomql.bot_builder.Bot) + * [initialize](#aiomql.bot_builder.Bot.initialize) + * [execute](#aiomql.bot_builder.Bot.execute) + * [start](#aiomql.bot_builder.Bot.start) + * [add\_strategy](#aiomql.bot_builder.Bot.add_strategy) + * [add\_strategies](#aiomql.bot_builder.Bot.add_strategies) + * [add\_strategy\_all](#aiomql.bot_builder.Bot.add_strategy_all) + * [init\_symbols](#aiomql.bot_builder.Bot.init_symbols) + * [init\_symbol](#aiomql.bot_builder.Bot.init_symbol) +* [aiomql.candle](#aiomql.candle) + * [Candle](#aiomql.candle.Candle) + * [\_\_init\_\_](#aiomql.candle.Candle.__init__) + * [set\_attributes](#aiomql.candle.Candle.set_attributes) + * [mid](#aiomql.candle.Candle.mid) + * [is\_bullish](#aiomql.candle.Candle.is_bullish) + * [is\_bearish](#aiomql.candle.Candle.is_bearish) + * [Candles](#aiomql.candle.Candles) + * [\_\_init\_\_](#aiomql.candle.Candles.__init__) + * [ta](#aiomql.candle.Candles.ta) + * [ta\_lib](#aiomql.candle.Candles.ta_lib) + * [data](#aiomql.candle.Candles.data) + * [rename](#aiomql.candle.Candles.rename) +* [aiomql.core.base](#aiomql.core.base) + * [Base](#aiomql.core.base.Base) + * [set\_attributes](#aiomql.core.base.Base.set_attributes) + * [annotations](#aiomql.core.base.Base.annotations) + * [get\_dict](#aiomql.core.base.Base.get_dict) + * [class\_vars](#aiomql.core.base.Base.class_vars) + * [dict](#aiomql.core.base.Base.dict) + * [Meta](#aiomql.core.base.Base.Meta) +* [aiomql.core.config](#aiomql.core.config) + * [Config](#aiomql.core.config.Config) + * [account\_info](#aiomql.core.config.Config.account_info) +* [aiomql.core.constants](#aiomql.core.constants) + * [TradeAction](#aiomql.core.constants.TradeAction) + * [OrderFilling](#aiomql.core.constants.OrderFilling) + * [OrderTime](#aiomql.core.constants.OrderTime) + * [OrderType](#aiomql.core.constants.OrderType) + * [opposite](#aiomql.core.constants.OrderType.opposite) + * [BookType](#aiomql.core.constants.BookType) + * [TimeFrame](#aiomql.core.constants.TimeFrame) + * [time](#aiomql.core.constants.TimeFrame.time) + * [CopyTicks](#aiomql.core.constants.CopyTicks) + * [PositionType](#aiomql.core.constants.PositionType) + * [PositionReason](#aiomql.core.constants.PositionReason) + * [DealType](#aiomql.core.constants.DealType) + * [DealEntry](#aiomql.core.constants.DealEntry) + * [DealReason](#aiomql.core.constants.DealReason) + * [OrderReason](#aiomql.core.constants.OrderReason) + * [SymbolChartMode](#aiomql.core.constants.SymbolChartMode) + * [SymbolCalcMode](#aiomql.core.constants.SymbolCalcMode) + * [SymbolTradeMode](#aiomql.core.constants.SymbolTradeMode) + * [SymbolTradeExecution](#aiomql.core.constants.SymbolTradeExecution) + * [SymbolSwapMode](#aiomql.core.constants.SymbolSwapMode) + * [DayOfWeek](#aiomql.core.constants.DayOfWeek) + * [SymbolOrderGTCMode](#aiomql.core.constants.SymbolOrderGTCMode) + * [SymbolOptionRight](#aiomql.core.constants.SymbolOptionRight) + * [SymbolOptionMode](#aiomql.core.constants.SymbolOptionMode) + * [AccountTradeMode](#aiomql.core.constants.AccountTradeMode) + * [TickFlag](#aiomql.core.constants.TickFlag) + * [TradeRetcode](#aiomql.core.constants.TradeRetcode) + * [AccountStopOutMode](#aiomql.core.constants.AccountStopOutMode) + * [AccountMarginMode](#aiomql.core.constants.AccountMarginMode) +* [aiomql.core.errors](#aiomql.core.errors) + * [Error](#aiomql.core.errors.Error) +* [aiomql.core.exceptions](#aiomql.core.exceptions) + * [LoginError](#aiomql.core.exceptions.LoginError) + * [VolumeError](#aiomql.core.exceptions.VolumeError) + * [SymbolError](#aiomql.core.exceptions.SymbolError) + * [OrderError](#aiomql.core.exceptions.OrderError) +* [aiomql.core.meta\_trader](#aiomql.core.meta_trader) + * [MetaTrader](#aiomql.core.meta_trader.MetaTrader) + * [\_\_aenter\_\_](#aiomql.core.meta_trader.MetaTrader.__aenter__) + * [\_\_aexit\_\_](#aiomql.core.meta_trader.MetaTrader.__aexit__) + * [login](#aiomql.core.meta_trader.MetaTrader.login) + * [initialize](#aiomql.core.meta_trader.MetaTrader.initialize) + * [shutdown](#aiomql.core.meta_trader.MetaTrader.shutdown) + * [version](#aiomql.core.meta_trader.MetaTrader.version) + * [account\_info](#aiomql.core.meta_trader.MetaTrader.account_info) + * [orders\_get](#aiomql.core.meta_trader.MetaTrader.orders_get) +* [aiomql.core.models](#aiomql.core.models) + * [AccountInfo](#aiomql.core.models.AccountInfo) + * [TerminalInfo](#aiomql.core.models.TerminalInfo) + * [SymbolInfo](#aiomql.core.models.SymbolInfo) + * [BookInfo](#aiomql.core.models.BookInfo) + * [TradeOrder](#aiomql.core.models.TradeOrder) + * [TradeRequest](#aiomql.core.models.TradeRequest) + * [OrderCheckResult](#aiomql.core.models.OrderCheckResult) + * [OrderSendResult](#aiomql.core.models.OrderSendResult) + * [TradePosition](#aiomql.core.models.TradePosition) + * [TradeDeal](#aiomql.core.models.TradeDeal) +* [aiomql.core](#aiomql.core) +* [aiomql.executor](#aiomql.executor) + * [Executor](#aiomql.executor.Executor) + * [add\_workers](#aiomql.executor.Executor.add_workers) + * [remove\_workers](#aiomql.executor.Executor.remove_workers) + * [add\_worker](#aiomql.executor.Executor.add_worker) + * [run](#aiomql.executor.Executor.run) + * [execute](#aiomql.executor.Executor.execute) +* [aiomql.history](#aiomql.history) + * [History](#aiomql.history.History) + * [\_\_init\_\_](#aiomql.history.History.__init__) + * [init](#aiomql.history.History.init) + * [get\_deals](#aiomql.history.History.get_deals) + * [deals\_total](#aiomql.history.History.deals_total) + * [get\_orders](#aiomql.history.History.get_orders) + * [orders\_total](#aiomql.history.History.orders_total) +* [aiomql.lib.strategies.finger\_trap](#aiomql.lib.strategies.finger_trap) + * [Entry](#aiomql.lib.strategies.finger_trap.Entry) +* [aiomql.lib.strategies](#aiomql.lib.strategies) +* [aiomql.lib.symbols.forex\_symbol](#aiomql.lib.symbols.forex_symbol) + * [ForexSymbol](#aiomql.lib.symbols.forex_symbol.ForexSymbol) + * [pip](#aiomql.lib.symbols.forex_symbol.ForexSymbol.pip) + * [compute\_volume](#aiomql.lib.symbols.forex_symbol.ForexSymbol.compute_volume) +* [aiomql.lib.symbols](#aiomql.lib.symbols) +* [aiomql.lib.traders.simple\_deal\_trader](#aiomql.lib.traders.simple_deal_trader) + * [DealTrader](#aiomql.lib.traders.simple_deal_trader.DealTrader) + * [create\_order](#aiomql.lib.traders.simple_deal_trader.DealTrader.create_order) +* [aiomql.lib.traders](#aiomql.lib.traders) +* [aiomql.lib](#aiomql.lib) +* [aiomql.order](#aiomql.order) + * [Order](#aiomql.order.Order) + * [\_\_init\_\_](#aiomql.order.Order.__init__) + * [orders\_total](#aiomql.order.Order.orders_total) + * [orders](#aiomql.order.Order.orders) + * [check](#aiomql.order.Order.check) + * [send](#aiomql.order.Order.send) + * [calc\_margin](#aiomql.order.Order.calc_margin) + * [calc\_profit](#aiomql.order.Order.calc_profit) +* [aiomql.positions](#aiomql.positions) + * [Positions](#aiomql.positions.Positions) + * [\_\_init\_\_](#aiomql.positions.Positions.__init__) + * [positions\_total](#aiomql.positions.Positions.positions_total) + * [positions\_get](#aiomql.positions.Positions.positions_get) + * [close\_all](#aiomql.positions.Positions.close_all) +* [aiomql.ram](#aiomql.ram) + * [RAM](#aiomql.ram.RAM) + * [\_\_init\_\_](#aiomql.ram.RAM.__init__) + * [get\_amount](#aiomql.ram.RAM.get_amount) + * [get\_volume](#aiomql.ram.RAM.get_volume) +* [aiomql.records](#aiomql.records) + * [Records](#aiomql.records.Records) + * [\_\_init\_\_](#aiomql.records.Records.__init__) + * [get\_records](#aiomql.records.Records.get_records) + * [read\_update](#aiomql.records.Records.read_update) + * [update\_rows](#aiomql.records.Records.update_rows) + * [update\_records](#aiomql.records.Records.update_records) + * [update\_record](#aiomql.records.Records.update_record) +* [aiomql.result](#aiomql.result) + * [Result](#aiomql.result.Result) + * [\_\_init\_\_](#aiomql.result.Result.__init__) + * [to\_csv](#aiomql.result.Result.to_csv) + * [save\_csv](#aiomql.result.Result.save_csv) +* [aiomql.strategy](#aiomql.strategy) + * [Strategy](#aiomql.strategy.Strategy) + * [\_\_init\_\_](#aiomql.strategy.Strategy.__init__) + * [sleep](#aiomql.strategy.Strategy.sleep) + * [trade](#aiomql.strategy.Strategy.trade) +* [aiomql.symbol](#aiomql.symbol) + * [Symbol](#aiomql.symbol.Symbol) + * [pip](#aiomql.symbol.Symbol.pip) + * [info\_tick](#aiomql.symbol.Symbol.info_tick) + * [symbol\_select](#aiomql.symbol.Symbol.symbol_select) + * [info](#aiomql.symbol.Symbol.info) + * [init](#aiomql.symbol.Symbol.init) + * [book\_add](#aiomql.symbol.Symbol.book_add) + * [book\_get](#aiomql.symbol.Symbol.book_get) + * [book\_release](#aiomql.symbol.Symbol.book_release) + * [compute\_volume](#aiomql.symbol.Symbol.compute_volume) + * [currency\_conversion](#aiomql.symbol.Symbol.currency_conversion) + * [copy\_rates\_from](#aiomql.symbol.Symbol.copy_rates_from) + * [copy\_rates\_from\_pos](#aiomql.symbol.Symbol.copy_rates_from_pos) + * [copy\_rates\_range](#aiomql.symbol.Symbol.copy_rates_range) + * [copy\_ticks\_from](#aiomql.symbol.Symbol.copy_ticks_from) + * [copy\_ticks\_range](#aiomql.symbol.Symbol.copy_ticks_range) +* [aiomql.terminal](#aiomql.terminal) + * [Terminal](#aiomql.terminal.Terminal) + * [initialize](#aiomql.terminal.Terminal.initialize) + * [version](#aiomql.terminal.Terminal.version) + * [info](#aiomql.terminal.Terminal.info) + * [symbols\_total](#aiomql.terminal.Terminal.symbols_total) +* [aiomql.ticks](#aiomql.ticks) + * [Tick](#aiomql.ticks.Tick) + * [set\_attributes](#aiomql.ticks.Tick.set_attributes) + * [Ticks](#aiomql.ticks.Ticks) + * [\_\_init\_\_](#aiomql.ticks.Ticks.__init__) + * [ta](#aiomql.ticks.Ticks.ta) + * [ta\_lib](#aiomql.ticks.Ticks.ta_lib) + * [data](#aiomql.ticks.Ticks.data) + * [rename](#aiomql.ticks.Ticks.rename) +* [aiomql.trader](#aiomql.trader) + * [Trader](#aiomql.trader.Trader) + * [\_\_init\_\_](#aiomql.trader.Trader.__init__) + * [create\_order](#aiomql.trader.Trader.create_order) + * [set\_order\_limits](#aiomql.trader.Trader.set_order_limits) + * [place\_trade](#aiomql.trader.Trader.place_trade) +* [aiomql.utils](#aiomql.utils) + * [dict\_to\_string](#aiomql.utils.dict_to_string) + + + +# aiomql + + + +# aiomql.account + + + +## Account Objects + +```python +class Account(AccountInfo) +``` + +A class for managing a trading account. A singleton class. +A subclass of AccountInfo. All AccountInfo attributes are available in this class. + +**Attributes**: + +- `connected` _bool_ - Status of connection to MetaTrader 5 Terminal +- `symbols` _set[SymbolInfo]_ - A set of available symbols for the financial market. + + +**Notes**: + + Other Account properties are defined in the AccountInfo class. + + + +#### refresh + +```python +async def refresh() +``` + +Refreshes the account instance with the latest account details from the MetaTrader 5 terminal + + + +#### account\_info + +```python +@property +def account_info() -> dict +``` + +Get account login, server and password details. If the login attribute of the account instance returns +a falsy value, the config instance is used to get the account details. + +**Returns**: + +- `dict` - A dict of login, server and password details + + +**Notes**: + + This method will only look for config details in the config instance if the login attribute of the + account Instance returns a falsy value + + + +#### \_\_aenter\_\_ + +```python +async def __aenter__() -> 'Account' +``` + +Connect to a trading account and return the account instance. +Async context manager for the Account class. + +**Returns**: + +- `Account` - An instance of the Account class + + +**Raises**: + +- `LoginError` - If login fails + + + +#### sign\_in + +```python +async def sign_in() -> bool +``` + +Connect to a trading account. + +**Returns**: + +- `bool` - True if login was successful else False + + + +#### has\_symbol + +```python +def has_symbol(symbol: str | Type[SymbolInfo]) +``` + +Checks to see if a symbol is available for a trading account + +**Arguments**: + + symbol (str | SymbolInfo): + + +**Returns**: + +- `bool` - True if symbol is present otherwise False + + + +#### symbols\_get + +```python +async def symbols_get() -> set[SymbolInfo] +``` + +Get all financial instruments from the MetaTrader 5 terminal available for the current account. + +**Returns**: + +- `set[Symbol]` - A set of available symbols. + + + +# aiomql.bot\_builder + + + +## Bot Objects + +```python +class Bot() +``` + +The bot class. Create a bot instance to run your strategies. + +**Attributes**: + +- `account` _Account_ - Account Object. +- `executor` - The default thread executor. +- `symbols` _set[Symbols]_ - A set of symbols for the trading session + + + +#### initialize + +```python +async def initialize() +``` + +Prepares the bot by signing in to the trading account and initializing the symbols for the trading session. + +**Raises**: + + SystemExit if sign in was not successful + + + +#### execute + +```python +def execute() +``` + +Execute the bot. + + + +#### start + +```python +async def start() +``` + +Starts the bot by calling the initialize method and running the strategies in the executor. + + + +#### add\_strategy + +```python +def add_strategy(strategy: Strategy) +``` + +Add a strategy to the executor. An added strategy will only run if it's symbol was successfully initialized. + +**Arguments**: + +- `strategy` _Strategy_ - A Strategy instance to run on bot + + +**Notes**: + + Make sure the symbol has been added to the market + + + +#### add\_strategies + +```python +def add_strategies(strategies: Iterable[Strategy]) +``` + +Add multiple strategies at the same time + +**Arguments**: + +- `strategies` - A list of strategies + + + +#### add\_strategy\_all + +```python +def add_strategy_all(*, strategy: Type[Strategy], params: dict | None = None) +``` + +Use this to run a single strategy on all available instruments in the market using the default parameters +i.e one set of parameters for all trading symbols + +**Arguments**: + +- `strategy` _Strategy_ - Strategy class +- `params` _dict_ - A dictionary of parameters for the strategy + + + +#### init\_symbols + +```python +async def init_symbols() +``` + +Initialize the symbols for the current trading session. This method is called internally by the bot. + + + +#### init\_symbol + +```python +async def init_symbol(symbol: Symbol) -> Symbol +``` + +Initialize a symbol before the beginning of a trading sessions. +Removes it from the list of symbols if it was not successfully initialized or not available +for the current market. + +**Arguments**: + +- `symbol` _Symbol_ - Symbol object to be initialized + + +**Returns**: + +- `Symbol` - if successfully initialized + + + +# aiomql.candle + +Candle and Candles classes for handling bars from the MetaTrader 5 terminal. + + + +## Candle Objects + +```python +class Candle() +``` + +A class representing bars from the MetaTrader 5 terminal as a customized class analogous to Japanese Candlesticks. +You can subclass this class for added customization. + +**Attributes**: + +- `time` _int_ - Period start time. +- `open` _int_ - Open price +- `high` _float_ - The highest price of the period +- `low` _float_ - The lowest price of the period +- `close` _float_ - Close price +- `tick_volume` _float_ - Tick volume +- `real_volume` _float_ - Trade volume +- `spread` _float_ - Spread +- `Index` _int_ - Custom attribute representing the position of the candle in a sequence. + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Create a Candle object from keyword arguments. + +**Arguments**: + +- `**kwargs` - Candle attributes and values as keyword arguments. + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set keyword arguments as instance attributes + +**Arguments**: + +- `**kwargs` - Instance attributes and values as keyword arguments + + + +#### mid + +```python +@property +def mid() -> float +``` + +The median of open and close + +**Returns**: + +- `float` - The median of open and close + + + +#### is\_bullish + +```python +def is_bullish() -> bool +``` + +A simple check to see if the candle is bullish. + +**Returns**: + +- `bool` - True or False + + + +#### is\_bearish + +```python +def is_bearish() -> bool +``` + +A simple check to see if the candle is bearish. + +**Returns**: + +- `bool` - True or False + + + +## Candles Objects + +```python +class Candles(Generic[_Candle]) +``` + +An iterable container class of Candle objects in chronological order. + +**Attributes**: + +- `Index` _Series['int']_ - A pandas Series of the indexes of all candles in the object. +- `time` _Series['int']_ - A pandas Series of the time of all candles in the object. +- `open` _Series[float]_ - A pandas Series of the opening price of all candles in the object. +- `high` _Series[float]_ - A pandas Series of the high price of all candles in the object. +- `low` _Series[float]_ - A pandas Series of the low price of all candles in the object. +- `close` _Series[float]_ - A pandas Series of the closing price of all candles in the object. +- `tick_volume` _Series[float]_ - A pandas Series of the tick volume of all candles in the object. +- `real_volume` _Series[float]_ - A pandas Series of the real volume of all candles in the object. +- `spread` _Series[float]_ - A pandas Series of the spread of all candles in the object. +- `timeframe` _TimeFrame_ - The timeframe of the candles in the object. +- `Candle` _Type[Candle]_ - The Candle class for representing the candles in the object. + + properties: +- `data` _DataFrame_ - A pandas DataFrame of all candles in the object. + + +**Notes**: + + The candle class can be customized by subclassing the Candle class and passing the subclass as the candle keyword argument. + Or defining it on the class body as a class attribute. + + + +#### \_\_init\_\_ + +```python +def __init__(*, + data: DataFrame | _Candles | Iterable, + flip=False, + candle_class: Type[_Candle] = None) +``` + +A container class of Candle objects in chronological order. + +**Arguments**: + +- `data` _DataFrame|Candles|Iterable_ - A pandas dataframe, a Candles object or any suitable iterable + + +**Arguments**: + +- `flip` _bool_ - Reverse the chronological order of the candles to the oldest first. Defaults to False. +- `candle_class` - A subclass of Candle to use as the candle class. Defaults to Candle. + + + +#### ta + +```python +@property +def ta() +``` + +Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + +**Returns**: + +- `pandas_ta` - The pandas_ta library + + + +#### ta\_lib + +```python +@property +def ta_lib() +``` + +Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + +**Returns**: + +- `ta` - The ta library + + + +#### data + +```python +@property +def data() -> DataFrame +``` + +The original data passed to the class as a pandas DataFrame + + + +#### rename + +```python +def rename(inplace=True, **kwargs) -> _Candles | None +``` + +Rename columns of the candles class. + +**Arguments**: + +- `inplace` _bool_ - Rename the columns inplace or return a new instance of the class with the renamed columns +- `**kwargs` - The new names of the columns + + +**Returns**: + +- `Candles` - A new instance of the class with the renamed columns if inplace is False. +- `None` - If inplace is True + + + +# aiomql.core.base + + + +## Base Objects + +```python +class Base() +``` + +A base class for all data model classes in the aiomql package. +This class provides a set of common methods and attributes for all data model classes. +For the data model classes attributes are annotated on the class body and are set as object attributes when the +class is instantiated. + +**Arguments**: + +- `**kwargs` - Object attributes and values as keyword arguments. Only added if they are annotated on the class body. + + Class Attributes: +- `mt5` _MetaTrader_ - An instance of the MetaTrader class +- `config` _Config_ - An instance of the Config class +- `Meta` _Type[Meta]_ - The Meta class for configuration of the data model class + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set keyword arguments as object attributes + +**Arguments**: + +- `**kwargs` - Object attributes and values as keyword arguments + + +**Raises**: + +- `AttributeError` - When assigning an attribute that does not belong to the class or any parent class + + +**Notes**: + + Only sets attributes that have been annotated on the class body. + + + +#### annotations + +```python +@property +@cache +def annotations() -> dict +``` + +Class annotations from all ancestor classes and the current class. + +**Returns**: + +- `dict` - A dictionary of class annotations + + + +#### get\_dict + +```python +def get_dict(exclude: set = None, include: set = None) -> dict +``` + +Returns class attributes as a dict, with the ability to filter + +**Arguments**: + +- `exclude` - A set of attributes to be excluded +- `include` - Specific attributes to be returned + + +**Returns**: + +- `dict` - A dictionary of specified class attributes + + +**Notes**: + + You can only set either of include or exclude. If you set both, include will take precedence + + + +#### class\_vars + +```python +@property +@cache +def class_vars() +``` + +Annotated class attributes + +**Returns**: + +- `dict` - A dictionary of available class attributes in all ancestor classes and the current class. + + + +#### dict + +```python +@property +def dict() -> dict +``` + +All instance and class attributes as a dictionary, except those excluded in the Meta class. + +**Returns**: + +- `dict` - A dictionary of instance and class attributes + + + +## Meta Objects + +```python +class Meta() +``` + +A class for defining class attributes to be excluded or included in the dict property + +**Attributes**: + +- `exclude` _set_ - A set of attributes to be excluded +- `include` _set_ - Specific attributes to be returned. Include supercedes exclude. + + + +#### filter + +```python +@classmethod +@property +def filter(cls) -> set +``` + +Combine the exclude and include attributes to return a set of attributes to be excluded. + +**Returns**: + +- `set` - A set of attributes to be excluded + + + +# aiomql.core.config + + + +## Config Objects + +```python +class Config() +``` + +A class for handling configuration settings for the aiomql package. + +**Arguments**: + +- `**kwargs` - Configuration settings as keyword arguments. + Variables set this way supersede those set in the config file. + + +**Attributes**: + +- `record_trades` _bool_ - Whether to keep record of trades or not. +- `filename` _str_ - Name of the config file +- `records_dir` _str_ - Path to the directory where trade records are saved +- `win_percentage` _float_ - Percentage of achieved target profit in a trade to be considered a win +- `login` _int_ - Trading account number +- `password` _str_ - Trading account password +- `server` _str_ - Broker server +- `path` _str_ - Path to terminal file +- `timeout` _int_ - Timeout for terminal connection + + +**Notes**: + + By default, the config class looks for a file named aiomql.json. + You can change this by passing the filename keyword argument to the constructor. + By passing reload=True to the load_config method, you can reload and search again for the config file. + + + +#### account\_info + +```python +def account_info() -> dict['login', 'password', 'server'] +``` + +Returns Account login details as found in the config object if available + +**Returns**: + +- `dict` - A dictionary of login details + + + +# aiomql.core.constants + + + +## TradeAction Objects + +```python +class TradeAction(Repr, IntEnum) +``` + +TRADE_REQUEST_ACTION Enum. + +**Attributes**: + +- `DEAL` _int_ - Delete the pending order placed previously Place a trade order for an immediate execution with the + specified parameters (market order). +- `PENDING` _int_ - Delete the pending order placed previously +- `SLTP` _int_ - Modify Stop Loss and Take Profit values of an opened position +- `MODIFY` _int_ - Modify the parameters of the order placed previously +- `REMOVE` _int_ - Delete the pending order placed previously +- `CLOSE_BY` _int_ - Close a position by an opposite one + + + +## OrderFilling Objects + +```python +class OrderFilling(Repr, IntEnum) +``` + +ORDER_TYPE_FILLING Enum. + +**Attributes**: + +- `FOK` _int_ - This execution policy means that an order can be executed only in the specified volume. + If the necessary amount of a financial instrument is currently unavailable in the market, the order will + not be executed. The desired volume can be made up of several available offers. + +- `IOC` _int_ - An agreement to execute a deal at the maximum volume available in the market within the volume + specified in the order. If the request cannot be filled completely, an order with the available volume will + be executed, and the remaining volume will be canceled. + +- `RETURN` _int_ - This policy is used only for market (ORDER_TYPE_BUY and ORDER_TYPE_SELL), limit and stop limit + orders (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT,ORDER_TYPE_BUY_STOP_LIMIT and + ORDER_TYPE_SELL_STOP_LIMIT) and only for the symbols with Market or Exchange execution modes. If filled + partially, a market or limit order with the remaining volume is not canceled, and is processed further. + During activation of the ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT orders, an appropriate + limit order ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT with the ORDER_FILLING_RETURN type is created. + + + +## OrderTime Objects + +```python +class OrderTime(Repr, IntEnum) +``` + +ORDER_TIME Enum. + +**Attributes**: + +- `GTC` _int_ - Good till cancel order +- `DAY` _int_ - Good till current trade day order +- `SPECIFIED` _int_ - The order is active until the specified date +- `SPECIFIED_DAY` _int_ - The order is active until 23:59:59 of the specified day. If this time appears to be out of + a trading session, the expiration is processed at the nearest trading time. + + + +## OrderType Objects + +```python +class OrderType(Repr, IntEnum) +``` + +ORDER_TYPE Enum. + +**Attributes**: + +- `BUY` _int_ - Market buy order +- `SELL` _int_ - Market sell order +- `BUY_LIMIT` _int_ - Buy Limit pending order +- `SELL_LIMIT` _int_ - Sell Limit pending order +- `BUY_STOP` _int_ - Buy Stop pending order +- `SELL_STOP` _int_ - Sell Stop pending order +- `BUY_STOP_LIMIT` _int_ - Upon reaching the order price, Buy Limit pending order is placed at StopLimit price +- `SELL_STOP_LIMIT` _int_ - Upon reaching the order price, Sell Limit pending order is placed at StopLimit price +- `CLOSE_BY` _int_ - Order for closing a position by an opposite one + + Properties: +- `opposite` _int_ - Gets the opposite of an order type + + + +#### opposite + +```python +@property +def opposite() +``` + +Gets the opposite of an order type for closing an open position + +**Returns**: + +- `int` - integer value of opposite order type + + + +## BookType Objects + +```python +class BookType(Repr, IntEnum) +``` + +BOOK_TYPE Enum. + +**Attributes**: + +- `SELL` _int_ - Sell order (Offer) +- `BUY` _int_ - Buy order (Bid) +- `SELL_MARKET` _int_ - Sell order by Market +- `BUY_MARKET` _int_ - Buy order by Market + + + +## TimeFrame Objects + +```python +class TimeFrame(Repr, IntEnum) +``` + +TIMEFRAME Enum. + +**Attributes**: + +- `M1` _int_ - One Minute +- `M2` _int_ - Two Minutes +- `M3` _int_ - Three Minutes +- `M4` _int_ - Four Minutes +- `M5` _int_ - Five Minutes +- `M6` _int_ - Six Minutes +- `M10` _int_ - Ten Minutes +- `M15` _int_ - Fifteen Minutes +- `M20` _int_ - Twenty Minutes +- `M30` _int_ - Thirty Minutes +- `H1` _int_ - One Hour +- `H2` _int_ - Two Hours +- `H3` _int_ - Three Hours +- `H4` _int_ - Four Hours +- `H6` _int_ - Six Hours +- `H8` _int_ - Eight Hours +- `D1` _int_ - One Day +- `W1` _int_ - One Week +- `MN1` _int_ - One Month + + Properties: +- `time` - return the value of the timeframe object in seconds. Used as a property + + +**Methods**: + +- `get` - get a timeframe object from a time value in seconds + + + +#### time + +```python +@property +def time() +``` + +The number of seconds in a TIMEFRAME + +**Returns**: + +- `int` - The number of seconds in a TIMEFRAME + + +**Examples**: + + >>> t = TimeFrame.H1 + >>> print(t.time) + 3600 + + + +## CopyTicks Objects + +```python +class CopyTicks(Repr, IntEnum) +``` + +COPY_TICKS Enum. This defines the types of ticks that can be requested using the copy_ticks_from() and +copy_ticks_range() functions. + +**Attributes**: + +- `ALL` _int_ - All ticks +- `INFO` _int_ - Ticks containing Bid and/or Ask price changes +- `TRADE` _int_ - Ticks containing Last and/or Volume price changes + + + +## PositionType Objects + +```python +class PositionType(Repr, IntEnum) +``` + +POSITION_TYPE Enum. Direction of an open position (buy or sell) + +**Attributes**: + +- `BUY` _int_ - Buy +- `SELL` _int_ - Sell + + + +## PositionReason Objects + +```python +class PositionReason(Repr, IntEnum) +``` + +POSITION_REASON Enum. The reason for opening a position is contained in the POSITION_REASON Enum + +**Attributes**: + +- `CLIENT` _int_ - The position was opened as a result of activation of an order placed from a desktop terminal +- `MOBILE` _int_ - The position was opened as a result of activation of an order placed from a mobile application +- `WEB` _int_ - The position was opened as a result of activation of an order placed from the web platform +- `EXPERT` _int_ - The position was opened as a result of activation of an order placed from an MQL5 program, + i.e. an Expert Advisor or a script + + + +## DealType Objects + +```python +class DealType(Repr, IntEnum) +``` + +DEAL_TYPE enum. Each deal is characterized by a type, allowed values are enumerated in this enum + +**Attributes**: + +- `BUY` _int_ - Buy +- `SELL` _int_ - Sell +- `BALANCE` _int_ - Balance +- `CREDIT` _int_ - Credit +- `CHARGE` _int_ - Additional Charge +- `CORRECTION` _int_ - Correction +- `BONUS` _int_ - Bonus +- `COMMISSION` _int_ - Additional Commission +- `COMMISSION_DAILY` _int_ - Daily Commission +- `COMMISSION_MONTHLY` _int_ - Monthly Commission +- `COMMISSION_AGENT_DAILY` _int_ - Daily Agent Commission +- `COMMISSION_AGENT_MONTHLY` _int_ - Monthly Agent Commission +- `INTEREST` _int_ - Interest Rate +- `DEAL_DIVIDEND` _int_ - Dividend Operations +- `DEAL_DIVIDEND_FRANKED` _int_ - Franked (non-taxable) dividend operations +- `DEAL_TAX` _int_ - Tax Charges + +- `BUY_CANCELED` _int_ - Canceled buy deal. There can be a situation when a previously executed buy deal is canceled. + In this case, the type of the previously executed deal (DEAL_TYPE_BUY) is changed to DEAL_TYPE_BUY_CANCELED, + and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated + balance operation + +- `SELL_CANCELED` _int_ - Canceled sell deal. There can be a situation when a previously executed sell deal is + canceled. In this case, the type of the previously executed deal (DEAL_TYPE_SELL) is changed to + DEAL_TYPE_SELL_CANCELED, and its profit/loss is zeroized. Previously obtained profit/loss is + charged/withdrawn using a separated balance operation. + + + +## DealEntry Objects + +```python +class DealEntry(Repr, IntEnum) +``` + +DEAL_ENTRY Enum. Deals differ not only in their types set in DEAL_TYPE enum, but also in the way they change +positions. This can be a simple position opening, or accumulation of a previously opened position (market entering), +position closing by an opposite deal of a corresponding volume (market exiting), or position reversing, if the +opposite-direction deal covers the volume of the previously opened position. + +**Attributes**: + +- `IN` _int_ - Entry In +- `OUT` _int_ - Entry Out +- `INOUT` _int_ - Reverse +- `OUT_BY` _int_ - Close a position by an opposite one + + + +## DealReason Objects + +```python +class DealReason(Repr, IntEnum) +``` + +DEAL_REASON Enum. The reason for deal execution is contained in the DEAL_REASON property. A deal can be executed +as a result of triggering of an order placed from a mobile application or an MQL5 program, as well as as a result +of the StopOut event, variation margin calculation, etc. + +**Attributes**: + +- `CLIENT` _int_ - The deal was executed as a result of activation of an order placed from a desktop terminal +- `MOBILE` _int_ - The deal was executed as a result of activation of an order placed from a desktop terminal +- `WEB` _int_ - The deal was executed as a result of activation of an order placed from the web platform +- `EXPERT` _int_ - 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 +- `SL` _int_ - The deal was executed as a result of Stop Loss activation +- `TP` _int_ - The deal was executed as a result of Take Profit activation +- `SO` _int_ - The deal was executed as a result of the Stop Out event +- `ROLLOVER` _int_ - The deal was executed due to a rollover +- `VMARGIN` _int_ - The deal was executed after charging the variation margin +- `SPLIT` _int_ - The deal was executed after the split (price reduction) of an instrument, which had an open + position during split announcement + + + +## OrderReason Objects + +```python +class OrderReason(Repr, IntEnum) +``` + +ORDER_REASON Enum. + +**Attributes**: + +- `CLIENT` _int_ - The order was placed from a desktop terminal +- `MOBILE` _int_ - The order was placed from a mobile application +- `WEB` _int_ - The order was placed from a web platform +- `EXPERT` _int_ - The order was placed from an MQL5-program, i.e. by an Expert Advisor or a script +- `SL` _int_ - The order was placed as a result of Stop Loss activation +- `TP` _int_ - The order was placed as a result of Take Profit activation +- `SO` _int_ - The order was placed as a result of the Stop Out event + + + +## SymbolChartMode Objects + +```python +class SymbolChartMode(Repr, IntEnum) +``` + +SYMBOL_CHART_MODE Enum. A symbol price chart can be based on Bid or Last prices. The price selected for symbol +charts also affects the generation and display of bars in the terminal. +Possible values of the SYMBOL_CHART_MODE property are described in this enum + +**Attributes**: + +- `BID` _int_ - Bars are based on Bid prices +- `LAST` _int_ - Bars are based on last prices + + + +## SymbolCalcMode Objects + +```python +class SymbolCalcMode(Repr, IntEnum) +``` + +SYMBOL_CALC_MODE Enum. The SYMBOL_CALC_MODE enumeration is used for obtaining information about how the margin +requirements for a symbol are calculated. + +**Attributes**: + +- `FOREX` _int_ - Forex mode - calculation of profit and margin for Forex +- `FOREX_NO_LEVERAGE` _int_ - Forex No Leverage mode – calculation of profit and margin for Forex symbols without + taking into account the leverage +- `FUTURES` _int_ - Futures mode - calculation of margin and profit for futures +- `CFD` _int_ - CFD mode - calculation of margin and profit for CFD +- `CFDINDEX` _int_ - CFD index mode - calculation of margin and profit for CFD by indexes +- `CFDLEVERAGE` _int_ - CFD Leverage mode - calculation of margin and profit for CFD at leverage trading +- `EXCH_STOCKS` _int_ - Calculation of margin and profit for trading securities on a stock exchange +- `EXCH_FUTURES` _int_ - Calculation of margin and profit for trading futures contracts on a stock exchange +- `EXCH_OPTIONS` _int_ - value is 34 +- `EXCH_OPTIONS_MARGIN` _int_ - value is 36 +- `EXCH_BONDS` _int_ - Exchange Bonds mode – calculation of margin and profit for trading bonds on a stock exchange +- `STOCKS_MOEX` _int_ - Exchange MOEX Stocks mode –calculation of margin and profit for trading securities on MOEX +- `EXCH_BONDS_MOEX` _int_ - Exchange MOEX Bonds mode – calculation of margin and profit for trading bonds on MOEX + +- `SERV_COLLATERAL` _int_ - Collateral mode - a symbol is used as a non-tradable asset on a trading account. + The market value of an open position is calculated based on the volume, current market price, contract size + and liquidity ratio. The value is included into Assets, which are added to Equity. Open positions of such + symbols increase the Free Margin amount and are used as additional margin (collateral) for open positions + + + +## SymbolTradeMode Objects + +```python +class SymbolTradeMode(Repr, IntEnum) +``` + +SYMBOL_TRADE_MODE Enum. There are several symbol trading modes. Information about trading modes of a certain +symbol is reflected in the values this enumeration + +**Attributes**: + +- `DISABLED` _int_ - Trade is disabled for the symbol +- `LONGONLY` _int_ - Allowed only long positions +- `SHORTONLY` _int_ - Allowed only short positions +- `CLOSEONLY` _int_ - Allowed only position close operations +- `FULL` _int_ - No trade restrictions + + + +## SymbolTradeExecution Objects + +```python +class SymbolTradeExecution(Repr, IntEnum) +``` + +SYMBOL_TRADE_EXECUTION Enum. The modes, or execution policies, define the rules for cases when the price has +changed or the requested volume cannot be completely fulfilled at the moment. + +**Attributes**: + +- `REQUEST` _int_ - Executing a market order at the price previously received from the broker. Prices for a certain + market order are requested from the broker before the order is sent. Upon receiving the prices, order + execution at the given price can be either confirmed or rejected. + +- `INSTANT` _int_ - Executing a market order at the specified price immediately. When sending a trade request to be + executed, the platform automatically adds the current prices to the order. + - If the broker accepts the price, the order is executed. + - If the broker does not accept the requested price, a "Requote" is sent — the broker returns prices, + at which this order can be executed. + +- `MARKET` _int_ - A broker makes a decision about the order execution price without any additional discussion with the trader. + Sending the order in such a mode means advance consent to its execution at this price. + +- `EXCHANGE` _int_ - Trade operations are executed at the prices of the current market offers. + + + +## SymbolSwapMode Objects + +```python +class SymbolSwapMode(Repr, IntEnum) +``` + +SYMBOL_SWAP_MODE Enum. Methods of swap calculation at position transfer are specified in enumeration +ENUM_SYMBOL_SWAP_MODE. The method of swap calculation determines the units of measure of the SYMBOL_SWAP_LONG and +SYMBOL_SWAP_SHORT parameters. For example, if swaps are charged in the client deposit currency, then the values of +those parameters are specified as an amount of money in the client deposit currency. + +**Attributes**: + +- `DISABLED` _int_ - Swaps disabled (no swaps) +- `POINTS` _int_ - Swaps are charged in points +- `CURRENCY_SYMBOL` _int_ - Swaps are charged in money in base currency of the symbol +- `CURRENCY_MARGIN` _int_ - Swaps are charged in money in margin currency of the symbol +- `CURRENCY_DEPOSIT` _int_ - Swaps are charged in money, in client deposit currency + +- `INTEREST_CURRENT` _int_ - Swaps are charged as the specified annual interest from the instrument price at + calculation of swap (standard bank year is 360 days) + +- `INTEREST_OPEN` _int_ - Swaps are charged as the specified annual interest from the open price of position + (standard bank year is 360 days) + +- `REOPEN_CURRENT` _int_ - Swaps are charged by reopening positions. At the end of a trading day the position is + closed. Next day it is reopened by the close price +/- specified number of points + (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + +- `REOPEN_BID` _int_ - Swaps are charged by reopening positions. At the end of a trading day the position is closed. + Next day it is reopened by the current Bid price +/- specified number of + points (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + + + +## DayOfWeek Objects + +```python +class DayOfWeek(Repr, IntEnum) +``` + +DAY_OF_WEEK Enum. + +**Attributes**: + +- `SUNDAY` _int_ - Sunday +- `MONDAY` _int_ - Monday +- `TUESDAY` _int_ - Tuesday +- `WEDNESDAY` _int_ - Wednesday +- `THURSDAY` _int_ - Thursday +- `FRIDAY` _int_ - Friday +- `SATURDAY` _int_ - Saturday + + + +## SymbolOrderGTCMode Objects + +```python +class SymbolOrderGTCMode(Repr, IntEnum) +``` + +SYMBOL_ORDER_GTC_MODE Enum. If the SYMBOL_EXPIRATION_MODE property is set to SYMBOL_EXPIRATION_GTC +(good till canceled), the expiration of pending orders, as well as of +Stop Loss/Take Profit orders should be additionally set using the ENUM_SYMBOL_ORDER_GTC_MODE enumeration. + +**Attributes**: + +- `GTC` _int_ - Pending orders and Stop Loss/Take Profit levels are valid for an unlimited period + until theirConstants, Enumerations and explicit cancellation + +- `DAILY` _int_ - Orders are valid during one trading day. At the end of the day, all Stop Loss and + Take Profit levels, as well as pending orders are deleted. + +- `DAILY_NO_STOPS` _int_ - When a trade day changes, only pending orders are deleted, + while Stop Loss and Take Profit levels are preserved + + + +## SymbolOptionRight Objects + +```python +class SymbolOptionRight(Repr, IntEnum) +``` + +SYMBOL_OPTION_RIGHT Enum. An option is a contract, which gives the right, but not the obligation, +to buy or sell an underlying asset (goods, stocks, futures, etc.) at a specified price on or before a specific date. +The following enumerations describe option properties, including the option type and the right arising from it. + +**Attributes**: + +- `CALL` _int_ - A call option gives you the right to buy an asset at a specified price. +- `PUT` _int_ - A put option gives you the right to sell an asset at a specified price. + + + +## SymbolOptionMode Objects + +```python +class SymbolOptionMode(Repr, IntEnum) +``` + +SYMBOL_OPTION_MODE Enum. + +**Attributes**: + +- `EUROPEAN` _int_ - European option may only be exercised on a specified date (expiration, execution date, delivery date) +- `AMERICAN` _int_ - American option may be exercised on any trading day or before expiry. The period within which + a buyer can exercise the option is specified for it. + + + +## AccountTradeMode Objects + +```python +class AccountTradeMode(Repr, IntEnum) +``` + +ACCOUNT_TRADE_MODE Enum. There are several types of accounts that can be opened on a trade server. +The type of account on which an MQL5 program is running can be found out using +the ENUM_ACCOUNT_TRADE_MODE enumeration. + +**Attributes**: + +- `DEMO` - Demo account +- `CONTEST` - Contest account +- `REAL` - Real Account + + + +## TickFlag Objects + +```python +class TickFlag(Repr, IntFlag) +``` + +TICK_FLAG Enum. TICK_FLAG defines possible flags for ticks. These flags are used to describe ticks obtained by the +copy_ticks_from() and copy_ticks_range() functions. + +**Attributes**: + +- `BID` _int_ - Bid price changed +- `ASK` _int_ - Ask price changed +- `LAST` _int_ - Last price changed +- `VOLUME` _int_ - Volume changed +- `BUY` _int_ - last Buy price changed +- `SELL` _int_ - last Sell price changed + + + +## TradeRetcode Objects + +```python +class TradeRetcode(Repr, IntEnum) +``` + +TRADE_RETCODE Enum. Return codes for order send/check operations + +**Attributes**: + +- `REQUOTE` _int_ - Requote +- `REJECT` _int_ - Request rejected +- `CANCEL` _int_ - Request canceled by trader +- `PLACED` _int_ - Order placed +- `DONE` _int_ - Request completed +- `DONE_PARTIAL` _int_ - Only part of the request was completed +- `ERROR` _int_ - Request processing error +- `TIMEOUT` _int_ - Request canceled by timeout +- `INVALID` _int_ - Invalid request +- `INVALID_VOLUME` _int_ - Invalid volume in the request +- `INVALID_PRICE` _int_ - Invalid price in the request +- `INVALID_STOPS` _int_ - Invalid stops in the request +- `TRADE_DISABLED` _int_ - Trade is disabled +- `MARKET_CLOSED` _int_ - Market is closed +- `NO_MONEY` _int_ - There is not enough money to complete the request +- `PRICE_CHANGED` _int_ - Prices changed +- `PRICE_OFF` _int_ - There are no quotes to process the request +- `INVALID_EXPIRATION` _int_ - Invalid order expiration date in the request +- `ORDER_CHANGED` _int_ - Order state changed +- `TOO_MANY_REQUESTS` _int_ - Too frequent requests +- `NO_CHANGES` _int_ - No changes in request +- `SERVER_DISABLES_AT` _int_ - Autotrading disabled by server +- `CLIENT_DISABLES_AT` _int_ - Autotrading disabled by client terminal +- `LOCKED` _int_ - Request locked for processing +- `FROZEN` _int_ - Order or position frozen +- `INVALID_FILL` _int_ - Invalid order filling type +- `CONNECTION` _int_ - No connection with the trade server +- `ONLY_REAL` _int_ - Operation is allowed only for live accounts +- `LIMIT_ORDERS` _int_ - The number of pending orders has reached the limit +- `LIMIT_VOLUME` _int_ - The volume of orders and positions for the symbol has reached the limit +- `INVALID_ORDER` _int_ - Incorrect or prohibited order type +- `POSITION_CLOSED` _int_ - Position with the specified POSITION_IDENTIFIER has already been closed +- `INVALID_CLOSE_VOLUME` _int_ - A close volume exceeds the current position volume + +- `CLOSE_ORDER_EXIST` _int_ - A close order already exists for a specified position. This may happen when working in + the hedging system: + · when attempting to close a position with an opposite one, while close orders for the position already exist + · when attempting to fully or partially close a position if the total volume of the already present close + orders and the newly placed one exceeds the current position volume + +- `LIMIT_POSITIONS` _int_ - The number of open positions simultaneously present on an account can be limited by the + server settings.After a limit is reached, the server returns the TRADE_RETCODE_LIMIT_POSITIONS error when + attempting to place an order. The limitation operates differently depending on the position accounting type: + · Netting — number of open positions is considered. When a limit is reached, the platform does not let + placing new orders whose execution may increase the number of open positions. In fact, the platform + allows placing orders only for the symbols that already have open positions. + The current pending orders are not considered since their execution may lead to changes in the current + positions but it cannot increase their number. + + · Hedging — pending orders are considered together with open positions, since a pending order activation + always leads to opening a new position. When a limit is reached, the platform does not allow placing + both new market orders for opening positions and pending orders. + +- `REJECT_CANCEL` _int_ - The pending order activation request is rejected, the order is canceled. +- `LONG_ONLY` _int_ - The request is rejected, because the "Only long positions are allowed" rule is set for the + symbol (POSITION_TYPE_BUY) +- `SHORT_ONLY` _int_ - The request is rejected, because the "Only short positions are allowed" rule is set for the + symbol (POSITION_TYPE_SELL) +- `CLOSE_ONLY` _int_ - The request is rejected, because the "Only position closing is allowed" rule is set for the + symbol +- `FIFO_CLOSE` _int_ - The request is rejected, because "Position closing is allowed only by FIFO rule" flag is set + for the trading account (ACCOUNT_FIFO_CLOSE=true) + + + +## AccountStopOutMode Objects + +```python +class AccountStopOutMode(Repr, IntEnum) +``` + +ACCOUNT_STOPOUT_MODE Enum. + +**Attributes**: + +- `PERCENT` _int_ - Account stop out mode in percents +- `MONEY` _int_ - Account stop out mode in money + + + +## AccountMarginMode Objects + +```python +class AccountMarginMode(Repr, IntEnum) +``` + +ACCOUNT_MARGIN_MODE Enum. + +**Attributes**: + +- `RETAIL_NETTING` _int_ - Used for the OTC markets to interpret positions in the "netting" + mode (only one position can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE). + +- `EXCHANGE` _int_ - Used for the exchange markets. Margin is calculated based on the discounts specified in + symbol settings. Discounts are set by the broker, but not less than the values set by the exchange. + +- `HEDGING` _int_ - Used for the exchange markets where individual positions are possible + (hedging, multiple positions can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE) taking into account the hedged margin (SYMBOL_MARGIN_HEDGED). + + + +# aiomql.core.errors + + + +## Error Objects + +```python +class Error() +``` + +Error class for handling errors from MetaTrader 5. + + + +# aiomql.core.exceptions + +Exceptions for the aiomql package. + + + +## LoginError Objects + +```python +class LoginError(Exception) +``` + +Raised when an error occurs when logging in. + + + +## VolumeError Objects + +```python +class VolumeError(Exception) +``` + +Raised when a volume is not valid or out of range for a symbol. + + + +## SymbolError Objects + +```python +class SymbolError(Exception) +``` + +Raised when a symbol is not provided where required or not available in the Market Watch. + + + +## OrderError Objects + +```python +class OrderError(Exception) +``` + +Raised when an error occurs when working with the order class. + + + +# aiomql.core.meta\_trader + + + +## MetaTrader Objects + +```python +class MetaTrader(metaclass=BaseMeta) +``` + + + +#### \_\_aenter\_\_ + +```python +async def __aenter__() -> 'MetaTrader' +``` + +Async context manager entry point. +Initializes the connection to the MetaTrader terminal. + +**Returns**: + +- `MetaTrader` - An instance of the MetaTrader class. + + + +#### \_\_aexit\_\_ + +```python +async def __aexit__(exc_type, exc_val, exc_tb) +``` + +Async context manager exit point. Closes the connection to the MetaTrader terminal. + + + +#### login + +```python +async def login(login: int, + password: str, + server: str, + timeout: int = 60000) -> bool +``` + +Connects to the MetaTrader terminal using the specified login, password and server. + +**Arguments**: + +- `login` _int_ - The trading account number. +- `password` _str_ - The trading account password. +- `server` _str_ - The trading server name. +- `timeout` _int_ - The timeout for the connection in seconds. + + +**Returns**: + +- `bool` - True if successful, False otherwise. + + + +#### initialize + +```python +async def initialize(path: str = "", + login: int = 0, + password: str = "", + server: str = "", + timeout: int | None = None, + portable=False) -> bool +``` + +Initializes the connection to the MetaTrader terminal. All parameters are optional. + +**Arguments**: + +- `path` _str_ - The path to the MetaTrader terminal executable. +- `login` _int_ - The trading account number. +- `password` _str_ - The trading account password. +- `server` _str_ - The trading server name. +- `timeout` _int_ - The timeout for the connection in seconds. +- `portable` _bool_ - If True, the terminal will be launched in portable mode. + + +**Returns**: + +- `bool` - True if successful, False otherwise. + + + +#### shutdown + +```python +async def shutdown() -> None +``` + +Closes the connection to the MetaTrader terminal. + +**Returns**: + +- `None` - None + + + +#### version + +```python +async def version() -> tuple[int, int, str] | None +``` + + + + + +#### account\_info + +```python +async def account_info() -> AccountInfo | None +``` + + + + + +#### orders\_get + +```python +async def orders_get(group: str = "", + ticket: int = 0, + symbol: str = "") -> tuple[TradeOrder] | None +``` + +Get active orders with the ability to filter by symbol or ticket. There are three call options. +Call without parameters. Return active orders on all symbols + +**Arguments**: + +- `symbol` _str_ - Symbol name. Optional named parameter. If a symbol is specified, the ticket parameter is ignored. + +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function + returns only active orders meeting a specified criteria for a symbol name. + +- `ticket` _int_ - Order ticket (ORDER_TICKET). Optional named parameter. + + +**Returns**: + +- `list[TradeOrder]` - A list of active trade orders as TradeOrder objects + + + +# aiomql.core.models + + + +## AccountInfo Objects + +```python +class AccountInfo(Base) +``` + +Account Information Class. + +**Attributes**: + +- `login` - int +- `password` - str +- `server` - str +- `trade_mode` - AccountTradeMode +- `balance` - float +- `leverage` - float +- `profit` - float +- `point` - float +- `amount` - float = 0 +- `equity` - float +- `credit` - float +- `margin` - float +- `margin_level` - float +- `margin_free` - float +- `margin_mode` - AccountMarginMode +- `margin_so_mode` - AccountStopoutMode +- `margin_so_call` - float +- `margin_so_so` - float +- `margin_initial` - float +- `margin_maintenance` - float +- `fifo_close` - bool +- `limit_orders` - float +- `currency` - str = "USD" +- `trade_allowed` - bool = True +- `trade_expert` - bool = True +- `currency_digits` - int +- `assets` - float +- `liabilities` - float +- `commission_blocked` - float +- `name` - str +- `company` - str + + + +## TerminalInfo Objects + +```python +class TerminalInfo(Base) +``` + +Terminal information class. Holds information about the terminal. + +**Attributes**: + +- `community_account` - bool +- `community_connection` - bool +- `connected` - bool +- `dlls_allowed` - bool +- `trade_allowed` - bool +- `tradeapi_disabled` - bool +- `email_enabled` - bool +- `ftp_enabled` - bool +- `notifications_enabled` - bool +- `mqid` - bool +- `build` - int +- `maxbars` - int +- `codepage` - int +- `ping_last` - int +- `community_balance` - float +- `retransmission` - float +- `company` - str +- `name` - str +- `language` - str +- `path` - str +- `data_path` - str +- `commondata_path` - str + + + +## SymbolInfo Objects + +```python +class SymbolInfo(Base) +``` + +Symbol Information Class. Symbols are financial instruments available for trading in the MetaTrader 5 terminal. + +**Attributes**: + +- `name` - str +- `custom` - bool +- `chart_mode` - SymbolChartMode +- `select` - bool +- `visible` - bool +- `session_deals` - int +- `session_buy_orders` - int +- `session_sell_orders` - int +- `volume` - float +- `volumehigh` - float +- `volumelow` - float +- `time` - int +- `digits` - int +- `spread` - float +- `spread_float` - bool +- `ticks_bookdepth` - int +- `trade_calc_mode` - SymbolCalcMode +- `trade_mode` - SymbolTradeMode +- `start_time` - int +- `expiration_time` - int +- `trade_stops_level` - int +- `trade_freeze_level` - int +- `trade_exemode` - SymbolTradeExecution +- `swap_mode` - SymbolSwapMode +- `swap_rollover3days` - DayOfWeek +- `margin_hedged_use_leg` - bool +- `expiration_mode` - int +- `filling_mode` - int +- `order_mode` - int +- `order_gtc_mode` - SymbolOrderGTCMode +- `option_mode` - SymbolOptionMode +- `option_right` - SymbolOptionRight +- `bid` - float +- `bidhigh` - float +- `bidlow` - float +- `ask` - float +- `askhigh` - float +- `asklow` - float +- `last` - float +- `lasthigh` - float +- `lastlow` - float +- `volume_real` - float +- `volumehigh_real` - float +- `volumelow_real` - float +- `option_strike` - float +- `point` - float +- `trade_tick_value` - float +- `trade_tick_value_profit` - float +- `trade_tick_value_loss` - float +- `trade_tick_size` - float +- `trade_contract_size` - float +- `trade_accrued_interest` - float +- `trade_face_value` - float +- `trade_liquidity_rate` - float +- `volume_min` - float +- `volume_max` - float +- `volume_step` - float +- `volume_limit` - float +- `swap_long` - float +- `swap_short` - float +- `margin_initial` - float +- `margin_maintenance` - float +- `session_volume` - float +- `session_turnover` - float +- `session_interest` - float +- `session_buy_orders_volume` - float +- `session_sell_orders_volume` - float +- `session_open` - float +- `session_close` - float +- `session_aw` - float +- `session_price_settlement` - float +- `session_price_limit_min` - float +- `session_price_limit_max` - float +- `margin_hedged` - float +- `price_change` - float +- `price_volatility` - float +- `price_theoretical` - float +- `price_greeks_delta` - float +- `price_greeks_theta` - float +- `price_greeks_gamma` - float +- `price_greeks_vega` - float +- `price_greeks_rho` - float +- `price_greeks_omega` - float +- `price_sensitivity` - float +- `basis` - str +- `category` - str +- `currency_base` - str +- `currency_profit` - str +- `currency_margin` - Any +- `bank` - str +- `description` - str +- `exchange` - str +- `formula` - Any +- `isin` - Any +- `name` - str +- `page` - str +- `path` - str + + + +## BookInfo Objects + +```python +class BookInfo(Base) +``` + +Book Information Class. + +**Attributes**: + +- `type` - BookType +- `price` - float +- `volume` - float +- `volume_dbl` - float + + + +## TradeOrder Objects + +```python +class TradeOrder(Base) +``` + +Trade Order Class. + +**Attributes**: + +- `ticket` - int +- `time_setup` - int +- `time_setup_msc` - int +- `time_expiration` - int +- `time_done` - int +- `time_done_msc` - int +- `type` - OrderType +- `type_time` - OrderTime +- `type_filling` - OrderFilling +- `state` - int +- `magic` - int +- `position_id` - int +- `position_by_id` - int +- `reason` - OrderReason +- `volume_current` - float +- `volume_initial` - float +- `price_open` - float +- `sl` - float +- `tp` - float +- `price_current` - float +- `price_stoplimit` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +## TradeRequest Objects + +```python +class TradeRequest(Base) +``` + +Trade Request Class. + +**Attributes**: + +- `action` - TradeAction +- `type` - OrderType +- `order` - int +- `symbol` - str +- `volume` - float +- `sl` - float +- `tp` - float +- `price` - float +- `deviation` - float +- `stop_limit` - float +- `type_time` - OrderTime +- `type_filling` - OrderFilling +- `expiration` - int +- `position` - int +- `position_by` - int +- `comment` - str +- `magic` - int +- `deviation` - int +- `comment` - str + + + +## OrderCheckResult Objects + +```python +class OrderCheckResult(Base) +``` + +Order Check Result + +**Attributes**: + +- `retcode` - int +- `balance` - float +- `equity` - float +- `profit` - float +- `margin` - float +- `margin_free` - float +- `margin_level` - float +- `comment` - str +- `request` - TradeRequest + + + +## OrderSendResult Objects + +```python +class OrderSendResult(Base) +``` + +Order Send Result + +**Attributes**: + +- `retcode` - int +- `deal` - int +- `order` - int +- `volume` - float +- `price` - float +- `bid` - float +- `ask` - float +- `comment` - str +- `request` - TradeRequest +- `request_id` - int +- `retcode_external` - int +- `profit` - float + + + +## TradePosition Objects + +```python +class TradePosition(Base) +``` + +Trade Position + +**Attributes**: + +- `ticket` - int +- `time` - int +- `time_msc` - int +- `time_update` - int +- `time_update_msc` - int +- `type` - OrderType +- `magic` - float +- `identifier` - int +- `reason` - PositionReason +- `volume` - float +- `price_open` - float +- `sl` - float +- `tp` - float +- `price_current` - float +- `swap` - float +- `profit` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +## TradeDeal Objects + +```python +class TradeDeal(Base) +``` + +Trade Deal + +**Attributes**: + +- `ticket` - int +- `order` - int +- `time` - int +- `time_msc` - int +- `type` - DealType +- `entry` - DealEntry +- `magic` - int +- `position_id` - int +- `reason` - DealReason +- `volume` - float +- `price` - float +- `commission` - float +- `swap` - float +- `profit` - float +- `fee` - float +- `sl` - float +- `tp` - float +- `symbol` - str +- `comment` - str +- `external_id` - str + + + +# aiomql.core + + + +# aiomql.executor + + + +## Executor Objects + +```python +class Executor() +``` + +Executor class for running multiple strategies on multiple symbols concurrently. + +**Attributes**: + +- `executor` _ThreadPoolExecutor_ - The executor object. +- `workers` _list_ - List of strategies. + + + +#### add\_workers + +```python +def add_workers(strategies: Sequence[type(Strategy)]) +``` + +Add multiple strategies at once + +**Arguments**: + +- `strategies` _Sequence[Strategy]_ - A sequence of strategies. + + + +#### remove\_workers + +```python +def remove_workers(*symbols: Sequence[Symbol]) +``` + +Removes any worker running on a symbol not successfully initialized. + +**Arguments**: + +- `*symbols` - Successfully initialized symbols. + + + +#### add\_worker + +```python +def add_worker(strategy: type(Strategy)) +``` + +Add a strategy instance to the list of workers + +**Arguments**: + +- `strategy` _Strategy_ - A strategy object + + + +#### run + +```python +@staticmethod +def run(strategy: type(Strategy)) +``` + +Wraps the coroutine trade method of each strategy with 'asyncio.run'. + +**Arguments**: + +- `strategy` _Strategy_ - A strategy object + + + +#### execute + +```python +async def execute(workers: int = 0) +``` + +Run the strategies with a threadpool executor. + +**Arguments**: + +- `workers` - Number of workers to use in executor pool. Defaults to zero which uses all workers. + + +**Notes**: + + No matter the number specified, the executor will always use a minimum of 5 workers. + + + +# aiomql.history + + + +## History Objects + +```python +class History() +``` + +The history class handles completed trade deals and trade orders in the trading history of an account. + +**Attributes**: + +- `deals` _list[TradeDeal]_ - Iterable of trade deals +- `orders` _list[TradeOrder]_ - Iterable of trade orders +- `total_deals` - Total number of deals +- `total_orders` _int_ - Total number orders +- `group` _str_ - Filter for selecting history by symbols. +- `ticket` _int_ - Filter for selecting history by ticket number +- `position` _int_ - Filter for selecting history deals by position +- `initialized` _bool_ - check if initial request has been sent to the terminal to get history. +- `mt5` _MetaTrader_ - MetaTrader instance +- `config` _Config_ - Config instance + + + +#### \_\_init\_\_ + +```python +def __init__(*, + date_from: datetime | float = 0, + date_to: datetime | float = 0, + group: str = "", + ticket: int = 0, + position: int = 0) +``` + +**Arguments**: + +- `date_from` _datetime, float_ - Date the orders are requested from. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' + +- `date_to` _datetime, float_ - Date up to which the orders are requested. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" + +- `group` _str_ - Filter for selecting history by symbols. +- `ticket` _int_ - Filter for selecting history by ticket number +- `position` _int_ - Filter for selecting history deals by position + + + +#### init + +```python +async def init(deals=True, orders=True) -> bool +``` + +Get history deals and orders + +**Arguments**: + +- `deals` _bool_ - If true get history deals during initial request to terminal +- `orders` _bool_ - If true get history orders during initial request to terminal + + +**Returns**: + +- `bool` - True if all requests were successful else False + + + +#### get\_deals + +```python +async def get_deals() -> list[TradeDeal] +``` + +Get deals from trading history using the parameters set in the constructor. + +**Returns**: + +- `list[TradeDeal]` - A list of trade deals + + + +#### deals\_total + +```python +async def deals_total() -> int +``` + +Get total number of deals within the specified period in the constructor. + +**Returns**: + +- `int` - Total number of Deals + + + +#### get\_orders + +```python +async def get_orders() -> list[TradeOrder] +``` + +Get orders from trading history using the parameters set in the constructor. + +**Returns**: + +- `list[TradeOrder]` - A list of trade orders + + + +#### orders\_total + +```python +async def orders_total() -> int +``` + +Get total number of orders within the specified period in the constructor. + +**Returns**: + +- `int` - Total number of orders + + + +# aiomql.lib.strategies.finger\_trap + + + +## Entry Objects + +```python +@dataclass +class Entry() +``` + +Entry class for FingerTrap strategy.Will be used to store entry conditions and other entry related data. + +**Attributes**: + +- `bearish` _bool_ - True if the market is bearish +- `bullish` _bool_ - True if the market is bullish +- `ranging` _bool_ - True if the market is ranging +- `snooze` _float_ - Time to wait before checking for entry conditions +- `trend` _str_ - The current trend of the market +- `last_candle` _Candle_ - The last candle of the market +- `new` _bool_ - True if the last candle is new +- `order_type` _OrderType_ - The type of order to place +- `pips` _int_ - The number of pips to place the order from the current price + + + +# aiomql.lib.strategies + + + +# aiomql.lib.symbols.forex\_symbol + + + +## ForexSymbol Objects + +```python +class ForexSymbol(Symbol) +``` + +Subclass of Symbol for Forex Symbols. Handles the conversion of currency and the computation of stop loss, +take profit and volume. + + + +#### pip + +```python +@property +def pip() +``` + +Returns the pip value of the symbol. This is ten times the point value for forex symbols. + +**Returns**: + +- `float` - The pip value of the symbol. + + + +#### compute\_volume + +```python +async def compute_volume(*, + amount: float, + pips: float, + use_minimum: bool = True) -> float +``` + +Compute volume given an amount to risk and target pips. Round the computed volume to the nearest step. + +**Arguments**: + +- `amount` _float_ - Amount to risk. Given in terms of the account currency. +- `pips` _float_ - Target pips. + + +**Arguments**: + +- `use_minimum` _bool_ - If True, the minimum volume is returned if the computed volume is less than the minimum volume. + + +**Returns**: + +- `float` - volume + + +**Raises**: + +- `VolumeError` - If the computed volume is less than the minimum volume or greater than the maximum volume. + + + +# aiomql.lib.symbols + + + +# aiomql.lib.traders.simple\_deal\_trader + + + +## DealTrader Objects + +```python +class DealTrader(Trader) +``` + +A base class for placing trades based on the number of pips to target + + + +#### create\_order + +```python +async def create_order(*, order_type: OrderType, pips: float = 0) +``` + +Using the number of target pips it determines the lot size, stop loss and take profit for the order, +and updates the order object with the values. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `pips` _float_ - Target pips + + + +# aiomql.lib.traders + + + +# aiomql.lib + + + +# aiomql.order + +Order Class + + + +## Order Objects + +```python +class Order(TradeRequest) +``` + +Trade order related functions and properties. Subclass of TradeRequest. + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Initialize the order object with keyword arguments, symbol must be provided. +Provide default values for action, type_time and type_filling if not provided. + +**Arguments**: + +- `**kwargs` - Keyword arguments must match the attributes of TradeRequest as well as the attributes of + Order class as specified in the annotations in the class definition. + + Default Values: +- `action` _TradeAction.DEAL_ - Trade action +- `type_time` _OrderTime.DAY_ - Order time +- `type_filling` _OrderFilling.FOK_ - Order filling + + +**Raises**: + +- `SymbolError` - If symbol is not provided + + + +#### orders\_total + +```python +async def orders_total() +``` + +Get the number of active orders. + +**Returns**: + +- `(int)` - total number of active orders + + + +#### orders + +```python +async def orders() -> tuple[TradeOrder] +``` + +Get the list of active orders for the current symbol. + +**Returns**: + +- `tuple[TradeOrder]` - A Tuple of active trade orders as TradeOrder objects + + + +#### check + +```python +async def check() -> OrderCheckResult +``` + +Check funds sufficiency for performing a required trading operation and the possibility to execute it at + +**Returns**: + +- `OrderCheckResult` - An OrderCheckResult object + + +**Raises**: + +- `OrderError` - If not successful + + + +#### send + +```python +async def send() -> OrderSendResult +``` + +Send a request to perform a trading operation from the terminal to the trade server. + +**Returns**: + +- `OrderSendResult` - An OrderSendResult object + + +**Raises**: + +- `OrderError` - If not successful + + + +#### calc\_margin + +```python +async def calc_margin() -> float +``` + +Return the required margin in the account currency to perform a specified trading operation. + +**Returns**: + +- `float` - Returns float value if successful + + +**Raises**: + +- `OrderError` - If not successful + + + +#### calc\_profit + +```python +async def calc_profit() -> float +``` + +Return profit in the account currency for a specified trading operation. + +**Returns**: + +- `float` - Returns float value if successful + + +**Raises**: + +- `OrderError` - If not successful + + + +# aiomql.positions + +Handle Open positions. + + + +## Positions Objects + +```python +class Positions() +``` + +Get Open Positions. + +**Attributes**: + +- `symbol` _str_ - Financial instrument name. +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. + If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. +- `ticket` _int_ - Position ticket. +- `mt5` _MetaTrader_ - MetaTrader instance. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: str = "", group: str = "", ticket: int = 0) +``` + +Get Open Positions. + +**Arguments**: + +- `symbol` _str_ - Financial instrument name. +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. If the group + is specified, the function returns only positions meeting a specified criteria for a symbol name. +- `ticket` _int_ - Position ticket + + + +#### positions\_total + +```python +async def positions_total() -> int +``` + +Get the number of open positions. + +**Returns**: + +- `int` - Return total number of open positions + + + +#### positions\_get + +```python +async def positions_get() +``` + +Get open positions with the ability to filter by symbol or ticket. + +**Returns**: + +- `list[TradePosition]` - A list of open trade positions + + + +#### close\_all + +```python +async def close_all() -> int +``` + +Close all open positions for the trading account. + +**Returns**: + +- `int` - Return number of positions closed. + + + +# aiomql.ram + +Risk Assessment and Management + + + +## RAM Objects + +```python +class RAM() +``` + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Risk Assessment and Management. All provided keyword arguments are set as attributes. + +**Arguments**: + +- `kwargs` _Dict_ - Keyword arguments. + + Defaults: +- `risk_to_reward` _float_ - Risk to reward ratio 1 +- `risk` _float_ - Percentage of account balance to risk per trade 0.01 # 1% +- `amount` _float_ - Amount to risk per trade in terms of account currency 0 +- `pips` _float_ - Target pips 0 +- `volume` _float_ - Volume to trade 0 + + + +#### get\_amount + +```python +async def get_amount(risk: float = 0) -> float +``` + +Calculate the amount to risk per trade as a percentage of free margin. + +**Arguments**: + +- `risk` _float_ - Percentage of account balance to risk per trade. Defaults to zero. + + +**Returns**: + +- `float` - Amount to risk per trade + + + +#### get\_volume + +```python +async def get_volume(*, + symbol: Symbol, + pips: float = 0, + amount: float = 0) -> float +``` + +Calculate the volume to trade. if pips is not provided, the pips attribute is used. +If the amount attribute or amount argument is zero, the amount is calculated using the get_amount method based on the risk. + +**Arguments**: + +- `symbol` _Symbol_ - Financial instrument + + +**Arguments**: + +- `pips` _float_ - Target pips. Defaults to zero. +- `amount` _float_ - Amount to risk per trade. Defaults to zero. + + +**Returns**: + +- `float` - Volume to trade + + + +# aiomql.records + +This module contains the Records class, which is used to read and update trade records from csv files. + + + +## Records Objects + +```python +class Records() +``` + +This utility class read trade records from csv files, and update them based on their closing positions. + +**Attributes**: + +- `config` - Config object +- `records_dir(Path)` - Path to directory containing record of placed trades, If not given takes the default + from the config + + + +#### \_\_init\_\_ + +```python +def __init__(records_dir: Path = '') +``` + +Initialize the Records class. + +**Arguments**: + +- `records_dir` _Path_ - Path to directory containing record of placed trades. + + + +#### get\_records + +```python +async def get_records() +``` + +Get trade records from records_dir folder + +**Yields**: + +- `files` - Trade record files + + + +#### read\_update + +```python +async def read_update(file: Path) +``` + +Read and update trade records + +**Arguments**: + +- `file` - Trade record file + + + +#### update\_rows + +```python +async def update_rows(rows: list[dict]) -> list[dict] +``` + +Update the rows of entered trades in the csv file with the actual profit. + +**Arguments**: + +- `rows` - A list of dictionaries from the dictionary writer object of the csv file. + + +**Returns**: + +- `list[dict]` - A list of dictionaries with the actual profit and win status. + + + +#### update\_records + +```python +async def update_records() +``` + +Update trade records in the records_dir folder. + + + +#### update\_record + +```python +async def update_record(file: Path | str) +``` + +Update a single trade record file. + + + +# aiomql.result + + + +## Result Objects + +```python +class Result() +``` + +A base class for handling trade results and strategy parameters for record keeping and reference purpose. +The data property must be implemented in the subclass + +**Attributes**: + +- `config` _Config_ - The configuration object +- `name` - Any desired name for the result file object + + + +#### \_\_init\_\_ + +```python +def __init__(result: OrderSendResult, parameters: dict = None, name: str = '') +``` + +Prepare result data + +**Arguments**: + + result: + parameters: + name: + + + +#### to\_csv + +```python +async def to_csv() +``` + +Record trade results and associated parameters as a csv file + + + +#### save\_csv + +```python +async def save_csv() +``` + +Save trade results and associated parameters as a csv file in a separate thread + + + +# aiomql.strategy + +The base class for creating strategies. + + + +## Strategy Objects + +```python +class Strategy(ABC) +``` + +The base class for creating strategies. + +**Attributes**: + +- `symbol` _Symbol_ - The Financial Instrument as a Symbol Object +- `parameters` _Dict_ - A dictionary of parameters for the strategy. + + Class Attributes: +- `name` _str_ - A name for the strategy. +- `account` _Account_ - Account instance. +- `mt5` _MetaTrader_ - MetaTrader instance. +- `config` _Config_ - Config instance. + + +**Notes**: + + Define the name of a strategy as a class attribute. If not provided, the class name will be used as the name. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: Symbol, params: dict = None) +``` + +Initiate the parameters dict and add name and symbol fields. +Use class name as strategy name if name is not provided + +**Arguments**: + +- `symbol` _Symbol_ - The Financial instrument +- `params` _Dict_ - Trading strategy parameters + + + +#### sleep + +```python +@staticmethod +async def sleep(secs: float) +``` + +Sleep for the needed amount of seconds in between requests to the terminal. +computes the accurate amount of time needed to sleep ensuring that the next request is made at the start of +a new bar and making cooperative multitasking possible. + +**Arguments**: + +- `secs` _float_ - The time in seconds. Usually the timeframe you are trading on. + + + +#### trade + +```python +@abstractmethod +async def trade() +``` + +Place trades using this method. This is the main method of the strategy. +It will be called by the strategy runner. + + + +# aiomql.symbol + +Symbol class for handling a financial instrument. + + + +## Symbol Objects + +```python +class Symbol(SymbolInfo) +``` + +Main class for handling a financial instrument. A subclass of SymbolInfo and Base it has attributes and methods +for working with a financial instrument. + +**Attributes**: + +- `tick` _Tick_ - Price tick object for instrument +- `account` - An instance of the current trading account + + +**Notes**: + + Full properties are on the SymbolInfo Object. + Make sure Symbol is always initialized with a name argument + + + +#### pip + +```python +@property +def pip() +``` + +Returns the pip value of the symbol. This is ten times the point value for forex symbols. + +**Returns**: + +- `float` - The pip value of the symbol. + + + +#### info\_tick + +```python +async def info_tick(*, name: str = "") -> Tick +``` + +Get the current price tick of a financial instrument. + +**Arguments**: + +- `name` - if name is supplied get price tick of that financial instrument + + +**Returns**: + +- `Tick` - Return a Tick Object + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### symbol\_select + +```python +async def symbol_select(*, enable: bool = True) -> bool +``` + +Select a symbol in the MarketWatch window or remove a symbol from the window. +Update the select property + +**Arguments**: + +- `enable` _bool_ - Switch. Optional unnamed parameter. If 'false', a symbol should be removed from + the MarketWatch window. + + +**Returns**: + +- `bool` - True if successful, otherwise – False. + + + +#### info + +```python +async def info() -> SymbolInfo +``` + +Get data on the specified financial instrument and update the symbol object properties + +**Returns**: + +- `(SymbolInfo)` - SymbolInfo if successful + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### init + +```python +async def init() -> bool +``` + +Initialized the symbol by pulling properties from the terminal + +**Returns**: + +- `bool` - Returns True if symbol info was successful initialized + + + +#### book\_add + +```python +async def book_add() -> bool +``` + +Subscribes the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. +If the symbol is not in the list of instruments for the market, This method will return False + +**Returns**: + +- `bool` - True if successful, otherwise – False. + + + +#### book\_get + +```python +async def book_get() -> tuple[BookInfo] +``` + +Returns a tuple of BookInfo featuring Market Depth entries for the specified symbol. + +**Returns**: + +- `tuple[BookInfo]` - Returns the Market Depth contents as a tuples of BookInfo Objects + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### book\_release + +```python +async def book_release() -> bool +``` + +Cancels subscription of the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. + +**Returns**: + +- `bool` - True if successful, otherwise – False. + + + +#### compute\_volume + +```python +async def compute_volume(*, + amount: float, + pips: float, + use_minimum: bool = True) -> float +``` + +Computes the volume of a trade based on the amount and the number of pips to target. +This is a dummy method that returns the minimum volume of the symbol. It is meant to be overridden by a subclass +Checkout Forex Symbol implementation in srciomql\lib\ForexSymbol.py + +**Arguments**: + +- `amount` _float_ - Amount to risk in the trade +- `pips` _float_ - Number of pips to target + + +**Arguments**: + +- `use_minimum` _bool_ - If True, the minimum volume is returned if the computed volume is less than the minimum volume. + + +**Returns**: + +- `float` - Returns the volume of the trade + + + +#### currency\_conversion + +```python +async def currency_conversion(*, amount: float, base: str, + quote: str) -> float +``` + +Convert from one currency to the other. + +**Arguments**: + +- `amount` - amount to convert given in terms of the quote currency +- `base` - The base currency of the pair +- `quote` - The quote currency of the pair + + +**Returns**: + +- `float` - Amount in terms of the base currency or None if it failed to convert + + +**Raises**: + +- `ValueError` - If conversion is impossible + + + +#### copy\_rates\_from + +```python +async def copy_rates_from(*, + timeframe: TimeFrame, + date_from: datetime | int, + count: int = 500) -> Candles +``` + +Get bars from the MetaTrader 5 terminal starting from the specified date. + +**Arguments**: + +- `timeframe` _TimeFrame_ - Timeframe the bars are requested for. Set by a value from the TimeFrame enumeration. Required unnamed parameter. + +- `date_from` _datetime | int_ - Date of opening of the first bar from the requested sample. Set by the 'datetime' object or as a number + of seconds elapsed since 1970.01.01. Required unnamed parameter. + +- `count` _int_ - Number of bars to receive. Required unnamed parameter. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_rates\_from\_pos + +```python +async def copy_rates_from_pos(*, + timeframe: TimeFrame, + count: int = 500, + start_position: int = 0) -> Candles +``` + +Get bars from the MetaTrader 5 terminal starting from the specified index. + +**Arguments**: + +- `timeframe` _TimeFrame_ - TimeFrame value from TimeFrame Enum. Required keyword only parameter + +- `count` _int_ - Number of bars to return. Keyword argument defaults to 500 + +- `start_position` _int_ - Initial index of the bar the data are requested from. The numbering of bars goes from + present to past. Thus, the zero bar means the current one. Keyword argument defaults to 0. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_rates\_range + +```python +async def copy_rates_range(*, timeframe: TimeFrame, date_from: datetime | int, + date_to: datetime | int) -> Candles +``` + +Get bars in the specified date range from the MetaTrader 5 terminal. + +**Arguments**: + +- `timeframe` _TimeFrame_ - Timeframe for the bars using the TimeFrame enumeration. Required unnamed parameter. + +- `date_from` _datetime | int_ - Date the bars are requested from. Set by the 'datetime' object or as a number of seconds + elapsed since 1970.01.01. Bars with the open time >= date_from are returned. Required unnamed parameter. + +- `date_to` _datetime | int_ - Date, up to which the bars are requested. Set by the 'datetime' object or as a number of + seconds elapsed since 1970.01.01. Bars with the open time <= date_to are returned. Required unnamed parameter. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_ticks\_from + +```python +async def copy_ticks_from(*, + date_from: datetime | int, + count: int = 100, + flags: CopyTicks = CopyTicks.ALL) -> Ticks +``` + +Get ticks from the MetaTrader 5 terminal starting from the specified date. + +Args: date_from (datetime | int): Date the ticks are requested from. Set by the 'datetime' object or as a +number of seconds elapsed since 1970.01.01. + +count (int): Number of requested ticks. Defaults to 100 + +flags (CopyTicks): A flag to define the type of the requested ticks from CopyTicks enum. INFO is the default + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of ticks ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_ticks\_range + +```python +async def copy_ticks_range(*, + date_from: datetime | int, + date_to: datetime | int, + flags: CopyTicks = CopyTicks.ALL) -> Ticks +``` + +Get ticks for the specified date range from the MetaTrader 5 terminal. + +**Arguments**: + +- `date_from` - Date the bars are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars with + the open time >= date_from are returned. Required unnamed parameter. + +- `date_to` - Date, up to which the bars are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars + with the open time <= date_to are returned. Required unnamed parameter. + + flags (CopyTicks): + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of ticks ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned. + + + +# aiomql.terminal + +Terminal related functions and properties + + + +## Terminal Objects + +```python +class Terminal(TerminalInfo) +``` + +Terminal Class. Get information about the MetaTrader 5 terminal. The class is a subclass of the TerminalInfo +class. It inherits all the attributes and methods of the TerminalInfo class and adds some useful methods. + +**Notes**: + + Other attributes are defined in the TerminalInfo Class + + + +#### initialize + +```python +async def initialize() -> bool +``` + +Establish a connection with the MetaTrader 5 terminal. There are three call options. Call without parameters. +The terminal for connection is found automatically. Call specifying the path to the MetaTrader 5 terminal we +want to connect to. word path as a keyword argument Call specifying the trading account path and parameters +i.e login, password, server, as keyword arguments, path can be omitted. + +**Returns**: + +- `bool` - True if successful else False + + + +#### version + +```python +async def version() +``` + +Get the MetaTrader 5 terminal version. This method returns the terminal version, build and release date as +a tuple of three values + +**Returns**: + +- `Version` - version of tuple as Version object + + +**Raises**: + +- `ValueError` - If the terminal version cannot be obtained + + + +#### info + +```python +async def info() +``` + +Get the connected MetaTrader 5 client terminal status and settings. gets terminal info in the form of a +named tuple structure (namedtuple). Return None in case of an error. The info on the error can be +obtained using last_error(). + +**Returns**: + +- `Terminal` - Terminal status and settings as a terminal object. + + + +#### symbols\_total + +```python +async def symbols_total() -> int +``` + +Get the number of all financial instruments in the MetaTrader 5 terminal. + +**Returns**: + +- `int` - Total number of available symbols + + + +# aiomql.ticks + +Module for working with price ticks. + + + +## Tick Objects + +```python +class Tick() +``` + +Price Tick of a Financial Instrument. + +**Attributes**: + +- `time` _int_ - Time of the last prices update for the symbol +- `bid` _float_ - Current Bid price +- `ask` _float_ - Current Ask price +- `last` _float_ - Price of the last deal (Last) +- `volume` _float_ - Volume for the current Last price +- `time_msc` _int_ - Time of the last prices update for the symbol in milliseconds +- `flags` _TickFlag_ - Tick flags +- `volume_real` _float_ - Volume for the current Last price +- `Index` _int_ - Custom attribute representing the position of the tick in a sequence. + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set attributes from keyword arguments + + + +## Ticks Objects + +```python +class Ticks() +``` + +Container data class for price ticks. Arrange in chronological order. +Supports iteration, slicing and assignment + +**Arguments**: + +- `data` _DataFrame | tuple[tuple]_ - Dataframe of price ticks or a tuple of tuples + + +**Arguments**: + +- `flip` _bool_ - If flip is True reverse data chronological order. + + +**Attributes**: + +- `data` - Dataframe Object holding the ticks + + + +#### \_\_init\_\_ + +```python +def __init__(*, data: DataFrame | Iterable, flip=False) +``` + +Initialize the Ticks class. Creates a DataFrame of price ticks from the data argument. + +**Arguments**: + +- `data` _DataFrame | Iterable_ - Dataframe of price ticks or any iterable object that can be converted to a + pandas DataFrame +- `flip` _bool_ - If flip is True reverse data chronological order. + + + +#### ta + +```python +@property +def ta() +``` + +Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + +**Returns**: + +- `pandas_ta` - The pandas_ta library + + + +#### ta\_lib + +```python +@property +def ta_lib() +``` + +Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + +**Returns**: + +- `ta` - The ta library + + + +#### data + +```python +@property +def data() -> DataFrame +``` + +DataFrame of price ticks arranged in chronological order. + + + +#### rename + +```python +def rename(inplace=True, **kwargs) -> _Ticks | None +``` + +Rename columns of the candle class. + +**Arguments**: + +- `inplace` _bool_ - Rename the columns inplace or return a new instance of the class with the renamed columns +- `**kwargs` - The new names of the columns + + +**Returns**: + +- `Ticks` - A new instance of the class with the renamed columns if inplace is False. +- `None` - If inplace is True + + + +# aiomql.trader + +Trader class module. Handles the creation of an order and the placing of trades + + + +## Trader Objects + +```python +class Trader() +``` + +Base class for creating a Trader object. Handles the creation of an order and the placing of trades + +**Attributes**: + +- `symbol` _Symbol_ - Financial instrument class Symbol class or any subclass of it. +- `ram` _RAM_ - RAM instance +- `order` _Order_ - Trade order + + Class Attributes: +- `name` _str_ - A name for the strategy. +- `account` _Account_ - Account instance. +- `mt5` _MetaTrader_ - MetaTrader instance. +- `config` _Config_ - Config instance. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: Symbol, ram: RAM = None) +``` + +Initializes the order object and RAM instance + +**Arguments**: + +- `symbol` _Symbol_ - Financial instrument +- `ram` _RAM_ - Risk Assessment and Management instance + + + +#### create\_order + +```python +async def create_order(*, order_type: OrderType, **kwargs) +``` + +Complete the order object with the required values. Creates a simple order. +Uses the ram instance to set the volume. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `kwargs` - keyword arguments as required for the specific trader + + + +#### set\_order\_limits + +```python +async def set_order_limits(pips: float) +``` + +Sets the stop loss and take profit for the order. +This method uses pips as defined for forex instruments. + +**Arguments**: + +- `pips` - Target pips + + + +#### place\_trade + +```python +async def place_trade(order_type: OrderType, params: dict = None, **kwargs) +``` + +Places a trade based on the order_type. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `params` - parameters to be saved with the trade +- `kwargs` - keyword arguments as required for the specific trader + + + +# aiomql.utils + +Utility functions for aiomql. + + + +#### dict\_to\_string + +```python +def dict_to_string(data: dict, multi=False) -> str +``` + +Convert a dict to a string. Use for logging. + +**Arguments**: + +- `data` _dict_ - The dict to convert. +- `multi` _bool, optional_ - If True, each key-value pair will be on a new line. Defaults to False. + + +**Returns**: + +- `str` - The string representation of the dict. + diff --git a/docs/order.md b/docs/order.md new file mode 100644 index 0000000..d296c7f --- /dev/null +++ b/docs/order.md @@ -0,0 +1,135 @@ +## Order + + +```python +class Order(TradeRequest) +``` +Trade order related functions and properties. Subclass of [TradeRequest](#traderequest). + +#### \_\_init\_\_ +```python +def __init__(**kwargs) +``` +Initialize the order object with keyword arguments, symbol must be provided. +Provides default values for action, type_time and type_filling if not provided. + +*Arguments*: + +|Name|Type|Description|Default| +|---|---|---|---| +|**kwargs**|**kwargs**|Keyword arguments must match the attributes of TradeRequest as well as the attributes of Order class as specified in the annotations in the class definition.|None| + + +*Default Arguments*: + +|Name|Type|Description|Default| +|---|---|---|---| +|**action**|**TradeAction**|Trade action|TradeAction.DEAL| +|**type_time**|**OrderTime**|Order time|OrderTime.DAY| +|**type_filling**|**OrderFilling**|Order filling|OrderFilling.FOK| + +*Raises*: + +|Exception|Description| +|---|---| +|**SymbolError**|If symbol is not provided| + +#### orders_total +```python +async def orders_total() +``` +Get the number of active orders. + +*returns*: + +|Type|Description| +|---|---| +|**int**|total number of active orders| + +#### orders +```python +async def orders() -> tuple[TradeOrder] +``` +Get the list of active orders for the current symbol. + +*Returns*: + +|Type|Description| +|---|---| +|**tuple[TradeOrder]**|A Tuple of active trade orders as TradeOrder objects| + +#### check +```python +async def check() -> OrderCheckResult +``` +Check funds sufficiency for performing a required trading operation and the possibility to execute it at + +*returns*: + +|Type|Description| +|---|---| +|**OrderCheckResult**|An OrderCheckResult object| + +*raises*: + +|Exception|Description| +|---|---| +|**OrderError**|If not successful| +- `OrderError` - If not successful + +#### send + +```python +async def send() -> OrderSendResult +``` +Send a request to perform a trading operation from the terminal to the trade server. + +*returns*: + +|Type|Description| +|---|---| +|**OrderSendResult**|An OrderSendResult object| + +*raises*: + +|Exception|Description| +|---|---| +|**OrderError**|If not successful| + +#### calc_margin +```python +async def calc_margin() -> float +``` +Return the required margin in the account currency to perform a specified trading operation. + +*returns*: + +|Type|Description| +|---|---| +|**float**|Returns float value if successful| + +*raises*: + +|Exception|Description| +|---|---| +|**OrderError**|If not successful| + + +#### calc_profit +```python +async def calc_profit() -> float +``` +Return profit in the account currency for a specified trading operation. + +*returns*: + +|Type|Description| +|---|---| +|**float**|Returns float value if successful| + +*raises*: + +|Exception|Description| +|---|---| +|**OrderError**|If not successful| + diff --git a/docs/positions.md b/docs/positions.md new file mode 100644 index 0000000..dcbd868 --- /dev/null +++ b/docs/positions.md @@ -0,0 +1,72 @@ +## Positions + +```python +class Positions +``` +Get and handle Open positions. + +**Attributes**: + +|Name|Type|Description|Default| +|---|---|---|---| +|**symbol**|**str**|Financial instrument name.|""| +|**group**|**str**|The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function returns only positions meeting a specified criteria for a symbol name.|""| +|**ticket**|**int**|Position ticket.|0| +|**mt5**|**MetaTrader**|MetaTrader instance.|None| + +- `symbol` _str_ - Financial instrument name. +- `group` _str_ - The filter for arranging a group of necessary symbols. Optional named parameter. + If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. +- `ticket` _int_ - Position ticket. +- `mt5` _MetaTrader_ - MetaTrader instance. + + #### \_\_init\_\_ + +```python +def __init__(*, symbol: str = "", group: str = "", ticket: int = 0) +``` +Get Open Positions. + +|Name|Type|Description|Default| +|---|---|---|---| +|**symbol**|**str**|Financial instrument name.|""| +|**group**|**str**|The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function returns only positions meeting a specified criteria for a symbol name.|""| +|**ticket**|**int**|Position ticket.|0| + +#### positions_total +```python +async def positions_total() -> int +``` +Get the number of open positions. + +**Returns**: + +|Type|Description| +|---|---| +|**int**|Return total number of open positions| + +#### positions_get + +```python +async def positions_get() +``` +Get open positions with the ability to filter by symbol or ticket. + +**Returns**: + +|Type|Description| +|---|---| +|**list[TradePosition]**|A list of open trade positions| + + + + +#### close_all +```python +async def close_all() -> int +``` +Close all open positions for the trading account. + +**Returns**: +- `int` - Return number of positions closed. + diff --git a/docs/ram.md b/docs/ram.md new file mode 100644 index 0000000..682feaf --- /dev/null +++ b/docs/ram.md @@ -0,0 +1,83 @@ + + +# aiomql.ram + +Risk Assessment and Management + + + +## RAM Objects + +```python +class RAM() +``` + + + +#### \_\_init\_\_ + +```python +def __init__(**kwargs) +``` + +Risk Assessment and Management. All provided keyword arguments are set as attributes. + +**Arguments**: + +- `kwargs` _Dict_ - Keyword arguments. + + Defaults: +- `risk_to_reward` _float_ - Risk to reward ratio 1 +- `risk` _float_ - Percentage of account balance to risk per trade 0.01 # 1% +- `amount` _float_ - Amount to risk per trade in terms of account currency 0 +- `pips` _float_ - Target pips 0 +- `volume` _float_ - Volume to trade 0 + + + +#### get\_amount + +```python +async def get_amount(risk: float = 0) -> float +``` + +Calculate the amount to risk per trade as a percentage of free margin. + +**Arguments**: + +- `risk` _float_ - Percentage of account balance to risk per trade. Defaults to zero. + + +**Returns**: + +- `float` - Amount to risk per trade + + + +#### get\_volume + +```python +async def get_volume(*, + symbol: Symbol, + pips: float = 0, + amount: float = 0) -> float +``` + +Calculate the volume to trade. if pips is not provided, the pips attribute is used. +If the amount attribute or amount argument is zero, the amount is calculated using the get_amount method based on the risk. + +**Arguments**: + +- `symbol` _Symbol_ - Financial instrument + + +**Arguments**: + +- `pips` _float_ - Target pips. Defaults to zero. +- `amount` _float_ - Amount to risk per trade. Defaults to zero. + + +**Returns**: + +- `float` - Volume to trade + diff --git a/docs/records.md b/docs/records.md new file mode 100644 index 0000000..3ac429b --- /dev/null +++ b/docs/records.md @@ -0,0 +1,103 @@ + + +# aiomql.records + +This module contains the Records class, which is used to read and update trade records from csv files. + + + +## Records Objects + +```python +class Records() +``` + +This utility class read trade records from csv files, and update them based on their closing positions. + +**Attributes**: + +- `config` - Config object +- `records_dir(Path)` - Path to directory containing record of placed trades, If not given takes the default + from the config + + + +#### \_\_init\_\_ + +```python +def __init__(records_dir: Path = '') +``` + +Initialize the Records class. + +**Arguments**: + +- `records_dir` _Path_ - Path to directory containing record of placed trades. + + + +#### get\_records + +```python +async def get_records() +``` + +Get trade records from records_dir folder + +**Yields**: + +- `files` - Trade record files + + + +#### read\_update + +```python +async def read_update(file: Path) +``` + +Read and update trade records + +**Arguments**: + +- `file` - Trade record file + + + +#### update\_rows + +```python +async def update_rows(rows: list[dict]) -> list[dict] +``` + +Update the rows of entered trades in the csv file with the actual profit. + +**Arguments**: + +- `rows` - A list of dictionaries from the dictionary writer object of the csv file. + + +**Returns**: + +- `list[dict]` - A list of dictionaries with the actual profit and win status. + + + +#### update\_records + +```python +async def update_records() +``` + +Update trade records in the records_dir folder. + + + +#### update\_record + +```python +async def update_record(file: Path | str) +``` + +Update a single trade record file. + diff --git a/docs/result.md b/docs/result.md new file mode 100644 index 0000000..700ffb7 --- /dev/null +++ b/docs/result.md @@ -0,0 +1,56 @@ + + +# aiomql.result + + + +## Result Objects + +```python +class Result() +``` + +A base class for handling trade results and strategy parameters for record keeping and reference purpose. +The data property must be implemented in the subclass + +**Attributes**: + +- `config` _Config_ - The configuration object +- `name` - Any desired name for the result file object + + + +#### \_\_init\_\_ + +```python +def __init__(result: OrderSendResult, parameters: dict = None, name: str = '') +``` + +Prepare result data + +**Arguments**: + + result: + parameters: + name: + + + +#### to\_csv + +```python +async def to_csv() +``` + +Record trade results and associated parameters as a csv file + + + +#### save\_csv + +```python +async def save_csv() +``` + +Save trade results and associated parameters as a csv file in a separate thread + diff --git a/docs/strategy.md b/docs/strategy.md new file mode 100644 index 0000000..8e9571e --- /dev/null +++ b/docs/strategy.md @@ -0,0 +1,77 @@ + + +# aiomql.strategy + +The base class for creating strategies. + + + +## Strategy Objects + +```python +class Strategy(ABC) +``` + +The base class for creating strategies. + +**Attributes**: + +- `symbol` _Symbol_ - The Financial Instrument as a Symbol Object +- `parameters` _Dict_ - A dictionary of parameters for the strategy. + + Class Attributes: +- `name` _str_ - A name for the strategy. +- `account` _Account_ - Account instance. +- `mt5` _MetaTrader_ - MetaTrader instance. +- `config` _Config_ - Config instance. + + +**Notes**: + + Define the name of a strategy as a class attribute. If not provided, the class name will be used as the name. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: Symbol, params: dict = None) +``` + +Initiate the parameters dict and add name and symbol fields. +Use class name as strategy name if name is not provided + +**Arguments**: + +- `symbol` _Symbol_ - The Financial instrument +- `params` _Dict_ - Trading strategy parameters + + + +#### sleep + +```python +@staticmethod +async def sleep(secs: float) +``` + +Sleep for the needed amount of seconds in between requests to the terminal. +computes the accurate amount of time needed to sleep ensuring that the next request is made at the start of +a new bar and making cooperative multitasking possible. + +**Arguments**: + +- `secs` _float_ - The time in seconds. Usually the timeframe you are trading on. + + + +#### trade + +```python +@abstractmethod +async def trade() +``` + +Place trades using this method. This is the main method of the strategy. +It will be called by the strategy runner. + diff --git a/docs/symbol.md b/docs/symbol.md new file mode 100644 index 0000000..2b3465e --- /dev/null +++ b/docs/symbol.md @@ -0,0 +1,383 @@ + + +# aiomql.symbol + +Symbol class for handling a financial instrument. + + + +## Symbol Objects + +```python +class Symbol(SymbolInfo) +``` + +Main class for handling a financial instrument. A subclass of SymbolInfo and Base it has attributes and methods +for working with a financial instrument. + +**Attributes**: + +- `tick` _Tick_ - Price tick object for instrument +- `account` - An instance of the current trading account + + +**Notes**: + + Full properties are on the SymbolInfo Object. + Make sure Symbol is always initialized with a name argument + + + +#### pip + +```python +@property +def pip() +``` + +Returns the pip value of the symbol. This is ten times the point value for forex symbols. + +**Returns**: + +- `float` - The pip value of the symbol. + + + +#### info\_tick + +```python +async def info_tick(*, name: str = "") -> Tick +``` + +Get the current price tick of a financial instrument. + +**Arguments**: + +- `name` - if name is supplied get price tick of that financial instrument + + +**Returns**: + +- `Tick` - Return a Tick Object + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### symbol\_select + +```python +async def symbol_select(*, enable: bool = True) -> bool +``` + +Select a symbol in the MarketWatch window or remove a symbol from the window. +Update the select property + +**Arguments**: + +- `enable` _bool_ - Switch. Optional unnamed parameter. If 'false', a symbol should be removed from + the MarketWatch window. + + +**Returns**: + +- `bool` - True if successful, otherwise – False. + + + +#### info + +```python +async def info() -> SymbolInfo +``` + +Get data on the specified financial instrument and update the symbol object properties + +**Returns**: + +- `(SymbolInfo)` - SymbolInfo if successful + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### init + +```python +async def init() -> bool +``` + +Initialized the symbol by pulling properties from the terminal + +**Returns**: + +- `bool` - Returns True if symbol info was successful initialized + + + +#### book\_add + +```python +async def book_add() -> bool +``` + +Subscribes the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. +If the symbol is not in the list of instruments for the market, This method will return False + +**Returns**: + +- `bool` - True if successful, otherwise – False. + + + +#### book\_get + +```python +async def book_get() -> tuple[BookInfo] +``` + +Returns a tuple of BookInfo featuring Market Depth entries for the specified symbol. + +**Returns**: + +- `tuple[BookInfo]` - Returns the Market Depth contents as a tuples of BookInfo Objects + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### book\_release + +```python +async def book_release() -> bool +``` + +Cancels subscription of the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. + +**Returns**: + +- `bool` - True if successful, otherwise – False. + + + +#### compute\_volume + +```python +async def compute_volume(*, + amount: float, + pips: float, + use_minimum: bool = True) -> float +``` + +Computes the volume of a trade based on the amount and the number of pips to target. +This is a dummy method that returns the minimum volume of the symbol. It is meant to be overridden by a subclass +Checkout Forex Symbol implementation in srciomql\lib\ForexSymbol.py + +**Arguments**: + +- `amount` _float_ - Amount to risk in the trade +- `pips` _float_ - Number of pips to target + + +**Arguments**: + +- `use_minimum` _bool_ - If True, the minimum volume is returned if the computed volume is less than the minimum volume. + + +**Returns**: + +- `float` - Returns the volume of the trade + + + +#### currency\_conversion + +```python +async def currency_conversion(*, amount: float, base: str, + quote: str) -> float +``` + +Convert from one currency to the other. + +**Arguments**: + +- `amount` - amount to convert given in terms of the quote currency +- `base` - The base currency of the pair +- `quote` - The quote currency of the pair + + +**Returns**: + +- `float` - Amount in terms of the base currency or None if it failed to convert + + +**Raises**: + +- `ValueError` - If conversion is impossible + + + +#### copy\_rates\_from + +```python +async def copy_rates_from(*, + timeframe: TimeFrame, + date_from: datetime | int, + count: int = 500) -> Candles +``` + +Get bars from the MetaTrader 5 terminal starting from the specified date. + +**Arguments**: + +- `timeframe` _TimeFrame_ - Timeframe the bars are requested for. Set by a value from the TimeFrame enumeration. Required unnamed parameter. + +- `date_from` _datetime | int_ - Date of opening of the first bar from the requested sample. Set by the 'datetime' object or as a number + of seconds elapsed since 1970.01.01. Required unnamed parameter. + +- `count` _int_ - Number of bars to receive. Required unnamed parameter. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_rates\_from\_pos + +```python +async def copy_rates_from_pos(*, + timeframe: TimeFrame, + count: int = 500, + start_position: int = 0) -> Candles +``` + +Get bars from the MetaTrader 5 terminal starting from the specified index. + +**Arguments**: + +- `timeframe` _TimeFrame_ - TimeFrame value from TimeFrame Enum. Required keyword only parameter + +- `count` _int_ - Number of bars to return. Keyword argument defaults to 500 + +- `start_position` _int_ - Initial index of the bar the data are requested from. The numbering of bars goes from + present to past. Thus, the zero bar means the current one. Keyword argument defaults to 0. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_rates\_range + +```python +async def copy_rates_range(*, timeframe: TimeFrame, date_from: datetime | int, + date_to: datetime | int) -> Candles +``` + +Get bars in the specified date range from the MetaTrader 5 terminal. + +**Arguments**: + +- `timeframe` _TimeFrame_ - Timeframe for the bars using the TimeFrame enumeration. Required unnamed parameter. + +- `date_from` _datetime | int_ - Date the bars are requested from. Set by the 'datetime' object or as a number of seconds + elapsed since 1970.01.01. Bars with the open time >= date_from are returned. Required unnamed parameter. + +- `date_to` _datetime | int_ - Date, up to which the bars are requested. Set by the 'datetime' object or as a number of + seconds elapsed since 1970.01.01. Bars with the open time <= date_to are returned. Required unnamed parameter. + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of rates ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_ticks\_from + +```python +async def copy_ticks_from(*, + date_from: datetime | int, + count: int = 100, + flags: CopyTicks = CopyTicks.ALL) -> Ticks +``` + +Get ticks from the MetaTrader 5 terminal starting from the specified date. + +Args: date_from (datetime | int): Date the ticks are requested from. Set by the 'datetime' object or as a +number of seconds elapsed since 1970.01.01. + +count (int): Number of requested ticks. Defaults to 100 + +flags (CopyTicks): A flag to define the type of the requested ticks from CopyTicks enum. INFO is the default + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of ticks ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned + + + +#### copy\_ticks\_range + +```python +async def copy_ticks_range(*, + date_from: datetime | int, + date_to: datetime | int, + flags: CopyTicks = CopyTicks.ALL) -> Ticks +``` + +Get ticks for the specified date range from the MetaTrader 5 terminal. + +**Arguments**: + +- `date_from` - Date the bars are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars with + the open time >= date_from are returned. Required unnamed parameter. + +- `date_to` - Date, up to which the bars are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars + with the open time <= date_to are returned. Required unnamed parameter. + + flags (CopyTicks): + + +**Returns**: + +- `Candles` - Returns a Candles object as a collection of ticks ordered chronologically. + + +**Raises**: + +- `ValueError` - If request was unsuccessful and None was returned. + diff --git a/docs/terminal.md b/docs/terminal.md new file mode 100644 index 0000000..2d0535c --- /dev/null +++ b/docs/terminal.md @@ -0,0 +1,88 @@ + + +# aiomql.terminal + +Terminal related functions and properties + + + +## Terminal Objects + +```python +class Terminal(TerminalInfo) +``` + +Terminal Class. Get information about the MetaTrader 5 terminal. The class is a subclass of the TerminalInfo +class. It inherits all the attributes and methods of the TerminalInfo class and adds some useful methods. + +**Notes**: + + Other attributes are defined in the TerminalInfo Class + + + +#### initialize + +```python +async def initialize() -> bool +``` + +Establish a connection with the MetaTrader 5 terminal. There are three call options. Call without parameters. +The terminal for connection is found automatically. Call specifying the path to the MetaTrader 5 terminal we +want to connect to. word path as a keyword argument Call specifying the trading account path and parameters +i.e login, password, server, as keyword arguments, path can be omitted. + +**Returns**: + +- `bool` - True if successful else False + + + +#### version + +```python +async def version() +``` + +Get the MetaTrader 5 terminal version. This method returns the terminal version, build and release date as +a tuple of three values + +**Returns**: + +- `Version` - version of tuple as Version object + + +**Raises**: + +- `ValueError` - If the terminal version cannot be obtained + + + +#### info + +```python +async def info() +``` + +Get the connected MetaTrader 5 client terminal status and settings. gets terminal info in the form of a +named tuple structure (namedtuple). Return None in case of an error. The info on the error can be +obtained using last_error(). + +**Returns**: + +- `Terminal` - Terminal status and settings as a terminal object. + + + +#### symbols\_total + +```python +async def symbols_total() -> int +``` + +Get the number of all financial instruments in the MetaTrader 5 terminal. + +**Returns**: + +- `int` - Total number of available symbols + diff --git a/docs/ticks.md b/docs/ticks.md new file mode 100644 index 0000000..57ebc77 --- /dev/null +++ b/docs/ticks.md @@ -0,0 +1,141 @@ + + +# aiomql.ticks + +Module for working with price ticks. + + + +## Tick Objects + +```python +class Tick() +``` + +Price Tick of a Financial Instrument. + +**Attributes**: + +- `time` _int_ - Time of the last prices update for the symbol +- `bid` _float_ - Current Bid price +- `ask` _float_ - Current Ask price +- `last` _float_ - Price of the last deal (Last) +- `volume` _float_ - Volume for the current Last price +- `time_msc` _int_ - Time of the last prices update for the symbol in milliseconds +- `flags` _TickFlag_ - Tick flags +- `volume_real` _float_ - Volume for the current Last price +- `Index` _int_ - Custom attribute representing the position of the tick in a sequence. + + + +#### set\_attributes + +```python +def set_attributes(**kwargs) +``` + +Set attributes from keyword arguments + + + +## Ticks Objects + +```python +class Ticks() +``` + +Container data class for price ticks. Arrange in chronological order. +Supports iteration, slicing and assignment + +**Arguments**: + +- `data` _DataFrame | tuple[tuple]_ - Dataframe of price ticks or a tuple of tuples + + +**Arguments**: + +- `flip` _bool_ - If flip is True reverse data chronological order. + + +**Attributes**: + +- `data` - Dataframe Object holding the ticks + + + +#### \_\_init\_\_ + +```python +def __init__(*, data: DataFrame | Iterable, flip=False) +``` + +Initialize the Ticks class. Creates a DataFrame of price ticks from the data argument. + +**Arguments**: + +- `data` _DataFrame | Iterable_ - Dataframe of price ticks or any iterable object that can be converted to a + pandas DataFrame +- `flip` _bool_ - If flip is True reverse data chronological order. + + + +#### ta + +```python +@property +def ta() +``` + +Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + +**Returns**: + +- `pandas_ta` - The pandas_ta library + + + +#### ta\_lib + +```python +@property +def ta_lib() +``` + +Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + +**Returns**: + +- `ta` - The ta library + + + +#### data + +```python +@property +def data() -> DataFrame +``` + +DataFrame of price ticks arranged in chronological order. + + + +#### rename + +```python +def rename(inplace=True, **kwargs) -> _Ticks | None +``` + +Rename columns of the candle class. + +**Arguments**: + +- `inplace` _bool_ - Rename the columns inplace or return a new instance of the class with the renamed columns +- `**kwargs` - The new names of the columns + + +**Returns**: + +- `Ticks` - A new instance of the class with the renamed columns if inplace is False. +- `None` - If inplace is True + diff --git a/docs/trader.md b/docs/trader.md new file mode 100644 index 0000000..937ab79 --- /dev/null +++ b/docs/trader.md @@ -0,0 +1,90 @@ + + +# aiomql.trader + +Trader class module. Handles the creation of an order and the placing of trades + + + +## Trader Objects + +```python +class Trader() +``` + +Base class for creating a Trader object. Handles the creation of an order and the placing of trades + +**Attributes**: + +- `symbol` _Symbol_ - Financial instrument class Symbol class or any subclass of it. +- `ram` _RAM_ - RAM instance +- `order` _Order_ - Trade order + + Class Attributes: +- `name` _str_ - A name for the strategy. +- `account` _Account_ - Account instance. +- `mt5` _MetaTrader_ - MetaTrader instance. +- `config` _Config_ - Config instance. + + + +#### \_\_init\_\_ + +```python +def __init__(*, symbol: Symbol, ram: RAM = None) +``` + +Initializes the order object and RAM instance + +**Arguments**: + +- `symbol` _Symbol_ - Financial instrument +- `ram` _RAM_ - Risk Assessment and Management instance + + + +#### create\_order + +```python +async def create_order(*, order_type: OrderType, **kwargs) +``` + +Complete the order object with the required values. Creates a simple order. +Uses the ram instance to set the volume. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `kwargs` - keyword arguments as required for the specific trader + + + +#### set\_order\_limits + +```python +async def set_order_limits(pips: float) +``` + +Sets the stop loss and take profit for the order. +This method uses pips as defined for forex instruments. + +**Arguments**: + +- `pips` - Target pips + + + +#### place\_trade + +```python +async def place_trade(order_type: OrderType, params: dict = None, **kwargs) +``` + +Places a trade based on the order_type. + +**Arguments**: + +- `order_type` _OrderType_ - Type of order +- `params` - parameters to be saved with the trade +- `kwargs` - keyword arguments as required for the specific trader + diff --git a/example.py b/example.py new file mode 100644 index 0000000..017b72b --- /dev/null +++ b/example.py @@ -0,0 +1,39 @@ +import asyncio + +from aiomql.lib import FingerTrap +from aiomql import Bot, Account, ForexSymbol, Records, RAM, DealTrader + + +def build_bot(): + # Either initialize an account here with your login details here or set them in the aiomql.json file. + # acc = Account(login=1111111111, password='*******', server='Deriv-Demo') + bot = Bot() + + # Prebuilt strategy from the library. + # Disclaimer: These strategy is only for demonstration purposes. + + ram = RAM(amount=50) + st1 = FingerTrap(symbol=ForexSymbol(name='Volatility 10 (1s) Index')) + # st1 = FingerTrap(symbol=ForexSymbol(name='Volatility 50 (1s) Index')) + # st.trader.ram = ram + st1.trader.ram = ram + + # st1 = FingerTrap(symbol=ForexSymbol(name='GBPUSD'), params={'trend_candles_count': 500}) + # st3 = FingerTrap(symbol=ForexSymbol(name='AUDUSD')) + # st4 = FingerTrap(symbol=ForexSymbol(name='USDCAD')) + # st5 = FingerTrap(symbol=ForexSymbol(name='USDJPY')) + # st6 = FingerTrap(symbol=ForexSymbol(name='EURGBP')) + # bot.add_strategies([st, st1, st3, st4, st5, st6]) + # bot.add_strategy(st) + bot.add_strategy(st1) + bot.execute() + + +build_bot() + +async def main(): + async with Account(): + res = Records() + await res.update_records() + +# asyncio.run(main()) diff --git a/examples/candles.py b/examples/candles.py new file mode 100644 index 0000000..86480ed --- /dev/null +++ b/examples/candles.py @@ -0,0 +1,44 @@ +import asyncio +from aiomql import Symbol, TimeFrame, Account + + +async def main(): + async with Account(): + # create a symbol + sym = Symbol(name="AUDUSD") + + # Get EURUSD price bars for the past 48 hours + candles = await sym.copy_rates_from_pos(timeframe=TimeFrame.H1, count=48, start_position=0) + print(len(candles)) # 48 + + # get the latest candle by accessing the last one. + last = candles[-1] # A Candle object + print(type(last)) + print(last.time) + + # get the last five hours + last_five = candles[-5:] # A Candles object. + print(type(last_five)) + print(last_five) + + close = candles['close'] # close price of all the candles as a pandas series + print(type(close)) + print(close) + + # compute ema using pandas ta + candles.ta.ema(length=34, append=True, fillna=0) + # rename the column to ema + candles.rename(EMA_34='ema') + + # use talib to compute crossover. This returns a series object that is not part of the candles object. + closeXema = candles.ta_lib.cross(candles.close, candles.ema) + # add to the candles + candles['closeXema'] = closeXema + print(candles) + + # iterate over the first 5 candles + for candle in candles[:5]: + print(candle.open, candle.Index) + + +asyncio.run(main()) \ No newline at end of file diff --git a/examples/order.py b/examples/order.py new file mode 100644 index 0000000..02644f2 --- /dev/null +++ b/examples/order.py @@ -0,0 +1,37 @@ +import asyncio + +from aiomql import Account, OrderType, TradeAction, Order, ForexSymbol + + +async def main(): + async with Account(): + + # create a symbol + sym = ForexSymbol(name="EURUSD") + + # Confirm the symbol is available for this account and initialize with default values. + res = await sym.init() + + # I want to place a market buy order, risk only 2usd, and target 10 pips in this trade. + # The ForexSymbol object has a compute_volume method that can be used to compute the volume + # given a target pips and amount. + volume = await sym.compute_volume(amount=2, pips=10) + + # a risk to reward ratio of 1:2 + # get the price tick of the symbol + tick = await sym.info_tick() + sl = tick.ask - (10 * sym.pip) + tp = tick.ask + (20 * sym.pip) + # create order + order = Order(symbol=sym.name, type=OrderType.BUY, volume=volume, action=TradeAction.DEAL, + price=tick.ask, sl=sl, tp=tp) + # check order. returns an OrderCheckResult object + chk = await order.check() + print(chk) + + # send order returns an OrderSendResult object + res = await order.send() + print(res) + + +asyncio.run(main()) diff --git a/examples/positions_history.py b/examples/positions_history.py new file mode 100644 index 0000000..b65bc26 --- /dev/null +++ b/examples/positions_history.py @@ -0,0 +1,59 @@ +import asyncio +from datetime import datetime +from aiomql import ForexSymbol, Account, Positions, History, Trader, OrderType, RAM + + +async def main(): + # Account details are in the aiomql.json file + async with Account(): + + # get start time using local timezone + tz = datetime.now().astimezone().tzinfo + start = datetime.now(tz=tz) + + # create two symbols and initialize them + sym1 = ForexSymbol(name="EURUSD") + sym2 = ForexSymbol(name="GBPUSD") + await sym1.init() + await sym2.init() + + # Risk Assets Management instance + # fix the amount to be risked at 2 USD. USD is the account currency. + ram = RAM(amount=2) + + # Create two traders instance + trd = Trader(symbol=sym1, ram=ram) + trd2 = Trader(symbol=sym2, ram=ram) + + # Place Trades + await trd.place_trade(order_type=OrderType.SELL) + await trd2.place_trade(order_type=OrderType.BUY) + + # Create a Positions object + pos = Positions(group='*USD*') + + # get the number of open positions + total = await pos.positions_total() + print(f'{total} Open positions') # 2 + + # close all open positions + await pos.close_all() + end = datetime.now(tz=tz) + + # get the number of open positions + total = await pos.positions_total() + print(f'{total} Open positions') # 0 + + # get historical trades + his = History(date_from=start, date_to=end) + + # get the number of deals + total_deals = await his.deals_total() + print(f'{total_deals} Deals') + + # get the number of order + orders = await his.orders_total() + print(f'{orders} orders') + + +asyncio.run(main()) diff --git a/examples/symbol.py b/examples/symbol.py new file mode 100644 index 0000000..52f1750 --- /dev/null +++ b/examples/symbol.py @@ -0,0 +1,37 @@ +import asyncio +from datetime import datetime +from aiomql import ForexSymbol, Symbol, TimeFrame, Account + +async def main(): + async with Account(): + sym = ForexSymbol(name="EURUSD") + res = await sym.init() + if not res: + print('Symbol not available') + return + + # get the last 1000 rates. + # data is returned as a Candles object + candles = await sym.copy_rates_from_pos(timeframe=TimeFrame.H1, count=1000, start_position=0) + print(len(candles)) # 1000 + + # get candles of the last 24 hours + today = datetime.now() + yesterday = today.replace(day=today.day - 1) + rates = await sym.copy_rates_range(timeframe=TimeFrame.H1, date_from=yesterday, date_to=today) + print(len(rates)) # 24 + + # get price ticks for the last 24 hours + # data is returned as a Ticks object + ticks = await sym.copy_ticks_range(date_from=yesterday, date_to=today) + print(len(ticks)) # ?? + + # get the current price tick + tick = await sym.info_tick() + # ask and bid price + ask, bid = tick.ask, tick.bid + print(ask, bid) + + + +asyncio.run(main()) \ No newline at end of file diff --git a/examples/sythetic.py b/examples/sythetic.py new file mode 100644 index 0000000..79b1c5b --- /dev/null +++ b/examples/sythetic.py @@ -0,0 +1,17 @@ +from aiomql import ForexSymbol, Order, Trader, Account +import asyncio + + +async def main(): + async with Account(): + les = ForexSymbol(name='Volatility 50 (1s) Index') + # t = ForexSymbol(name='EURUSD') + await les.init() + # await t.init() + await les.info_tick() + # await t.info_tick() + vol = await les.compute_volume(amount=50, pips=10) + print(les.tick.ask, les.tick.bid, les.volume_min, vol, les.point, les.digits, les.volume_max) + print(les.tick.ask + les.point*100) + +asyncio.run(main()) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5d79eae --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" + +[project] +name = "aiomql" +version = "3.0.4" +readme = "README.md" +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] +keywords = ['MetaTrader5', 'Asynchronous', 'Algorithmic Trading', 'Trading Bot'] +dependencies = ["MetaTrader5>=5.0.37", "pandas>=1.5.0", "pandas-ta>=0.3.14b0"] +authors = [{name = "Ichinga Samuel", email = "ichingasamuel@gmail.com"}] +description = "Asynchronous MetaTrader5 library and Bot Building Framework" + +[project.urls] +"Homepage" = "https://github.com/Ichinga-Samuel/aiomql" +"Bug Tracker" = "https://github.com/Ichinga-Samuel/aiomql/issues" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/scrap.py b/scrap.py new file mode 100644 index 0000000..4098d39 --- /dev/null +++ b/scrap.py @@ -0,0 +1,47 @@ +from concurrent.futures import ThreadPoolExecutor +import asyncio +import random +import time + +se = set() +def fun(arg): + while True: + time.sleep(10) + print('sleep') + # print('sleeping') + # await asyncio.sleep(random.randint(1, 10)) + # print('wake up') + + +def nuf(): + while True: + # time.sleep(5) + print('awake') + + +def main(f): + asyncio.run(f()) + + +async def run(): + # loop = asyncio.get_running_loop() + with ThreadPoolExecutor(max_workers=10) as exe: + exe.submit(fun, 10) + exe.submit(nuf) + # r.cancel() + # print(r.done()) + # return r.result() + + + +asyncio.run(run()) + +# ars = (1, 3, 4) +# +# def check(f, args): +# print(*args) +# +# +# b = {check: (3, ars)} + +# [k(v[0], v[1]) for k,v in b.items()] \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6068493 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup + +setup() diff --git a/src/aiomql/__init__.py b/src/aiomql/__init__.py new file mode 100644 index 0000000..3eb045d --- /dev/null +++ b/src/aiomql/__init__.py @@ -0,0 +1,23 @@ +from .account import Account +from .ram import RAM +from .symbol import Symbol +from .strategy import Strategy +from .bot_builder import Bot +from .result import Result +from .records import Records +from .candle import Candle, Candles +from .positions import Positions +from .executor import Executor +from .order import Order +from .ticks import Tick, Ticks +from .history import History +from .trader import Trader +from .terminal import Terminal +from .sessions import Session, Sessions + +from .core.config import Config +from .core.constants import * +from .core.meta_trader import MetaTrader +from .core.models import * +from .core.exceptions import * +from .lib import * diff --git a/src/aiomql/account.py b/src/aiomql/account.py new file mode 100644 index 0000000..153954a --- /dev/null +++ b/src/aiomql/account.py @@ -0,0 +1,109 @@ +from logging import getLogger +from typing import Type + +from .core.models import AccountInfo, SymbolInfo +from .core.exceptions import LoginError + +logger = getLogger(__name__) + + +class Account(AccountInfo): + """A class for managing a trading account. A singleton class. + A subclass of AccountInfo. All AccountInfo attributes are available in this class. + + Attributes: + connected (bool): Status of connection to MetaTrader 5 Terminal + symbols (set[SymbolInfo]): A set of available symbols for the financial market. + + Notes: + Other Account properties are defined in the AccountInfo class. + """ + connected: bool + symbols = set() + + def __new__(cls, *args, **kwargs): + if not hasattr(cls, '_instance'): + cls._instance = super().__new__(cls) + return cls._instance + + async def refresh(self): + """ + Refreshes the account instance with the latest account details from the MetaTrader 5 terminal + """ + account_info = await self.mt5.account_info() + acc = account_info._asdict() + self.set_attributes(**acc) + + @property + def account_info(self) -> dict: + """Get account login, server and password details. If the login attribute of the account instance returns + a falsy value, the config instance is used to get the account details. + + Returns: + dict: A dict of login, server and password details + + Note: + This method will only look for config details in the config instance if the login attribute of the + account Instance returns a falsy value + """ + acc_info = self.get_dict(include={'login', 'server', 'password'}) + return acc_info if acc_info['login'] else self.config.account_info() + + async def __aenter__(self) -> 'Account': + """Connect to a trading account and return the account instance. + Async context manager for the Account class. + + Returns: + Account: An instance of the Account class + + Raises: + LoginError: If login fails + """ + res = await self.sign_in() + if not res: + raise LoginError('Login failed') + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.mt5.shutdown() + self.connected = False + + async def sign_in(self) -> bool: + """Connect to a trading account. + + Returns: + bool: True if login was successful else False + """ + await self.mt5.initialize(**self.account_info) + self.connected = await self.mt5.login(**self.account_info) + if self.connected: + await self.refresh() + self.symbols = await self.symbols_get() + return self.connected + await self.mt5.shutdown() + return False + + def has_symbol(self, symbol: str | Type[SymbolInfo]): + """Checks to see if a symbol is available for a trading account + + Args: + symbol (str | SymbolInfo): + + Returns: + bool: True if symbol is present otherwise False + """ + try: + symbol = SymbolInfo(name=str(symbol)) if not isinstance(symbol, SymbolInfo) else symbol + return symbol in self.symbols + except Exception as err: + logger.warning(f'Error: {err}; {symbol} not available in this market') + return False + + async def symbols_get(self) -> set[SymbolInfo]: + """Get all financial instruments from the MetaTrader 5 terminal available for the current account. + + Returns: + set[Symbol]: A set of available symbols. + """ + syms = await self.mt5.symbols_get() + return {SymbolInfo(name=sym.name) for sym in syms} diff --git a/src/aiomql/bot_builder.py b/src/aiomql/bot_builder.py new file mode 100644 index 0000000..fa7c004 --- /dev/null +++ b/src/aiomql/bot_builder.py @@ -0,0 +1,116 @@ +import asyncio +from typing import Type, Iterable, TypeVar +import logging + +from .executor import Executor +from .account import Account +from .symbol import Symbol as _Symbol +from .strategy import Strategy as _Strategy +# from + +logger = logging.getLogger(__name__) +Strategy = TypeVar('Strategy', bound=_Strategy) +Symbol = TypeVar('Symbol', bound=_Symbol) + + +class Bot: + """The bot class. Create a bot instance to run your strategies. + + Attributes: + account (Account): Account Object. + executor: The default thread executor. + symbols (set[Symbols]): A set of symbols for the trading session + """ + account: Account = Account() + + def __init__(self): + self.symbols = set() + self.executor = Executor(bot=self) + + async def initialize(self): + """Prepares the bot by signing in to the trading account and initializing the symbols for the trading session. + + Raises: + SystemExit if sign in was not successful + """ + init = await self.account.sign_in() + logger.info("Login Successful") + if not init: + logger.warning('Unable to sign in to MetaTrder 5 Terminal') + raise SystemExit + await self.init_symbols() + self.executor.remove_workers() + + def add_func(self, func, kwargs): + self.executor.add_func(func, kwargs) + + def add_coro(self, coro, **kwargs): + self.executor.add_coro(coro, kwargs) + + def execute(self): + """Execute the bot. + """ + asyncio.run(self.start()) + + async def start(self): + """Starts the bot by calling the initialize method and running the strategies in the executor. + """ + await self.initialize() + await self.executor.execute() + + def add_strategy(self, strategy: Strategy): + """Add a strategy to the executor. An added strategy will only run if it's symbol was successfully initialized. + + Args: + strategy (Strategy): A Strategy instance to run on bot + + Notes: + Make sure the symbol has been added to the market + """ + self.symbols.add(strategy.symbol) + self.executor.add_worker(strategy) + + def add_strategies(self, strategies: Iterable[Strategy]): + """Add multiple strategies at the same time + + Args: + strategies: A list of strategies + """ + [self.add_strategy(strategy) for strategy in strategies] + + def add_strategy_all(self, *, strategy: Type[Strategy], params: dict | None = None): + """Use this to run a single strategy on all available instruments in the market using the default parameters + i.e one set of parameters for all trading symbols + + Keyword Args: + strategy (Strategy): Strategy class + params (dict): A dictionary of parameters for the strategy + """ + [self.add_strategy(strategy(symbol=symbol, params=params)) for symbol in self.symbols] + + async def init_symbols(self): + """Initialize the symbols for the current trading session. This method is called internally by the bot. + """ + syms = [self.init_symbol(symbol) for symbol in self.symbols] + await asyncio.gather(*syms, return_exceptions=True) + + async def init_symbol(self, symbol: Symbol) -> Symbol: + """Initialize a symbol before the beginning of a trading sessions. + Removes it from the list of symbols if it was not successfully initialized or not available + for the account. + + Args: + symbol (Symbol): Symbol object to be initialized + + Returns: + Symbol: if successfully initialized + """ + if self.account.has_symbol(symbol): + init = await symbol.init() + if init: + return symbol + self.symbols.discard(symbol) + logger.warning(f'Unable to initialize symbol {symbol}') + + self.symbols.remove(symbol) + logger.warning(f'{symbol} not a available for this market') diff --git a/src/aiomql/candle.py b/src/aiomql/candle.py new file mode 100644 index 0000000..84014ba --- /dev/null +++ b/src/aiomql/candle.py @@ -0,0 +1,234 @@ +"""Candle and Candles classes for handling bars from the MetaTrader 5 terminal.""" + +from typing import Type, TypeVar, Generic, Iterable +from logging import getLogger +import reprlib + +from pandas import DataFrame, Series +import pandas_ta as ta + +from .core.constants import TimeFrame + +logger = getLogger(__name__) + +class Candle: + """A class representing bars from the MetaTrader 5 terminal as a customized class analogous to Japanese Candlesticks. + You can subclass this class for added customization. + + Attributes: + time (int): Period start time. + open (int): Open price + high (float): The highest price of the period + low (float): The lowest price of the period + close (float): Close price + tick_volume (float): Tick volume + real_volume (float): Trade volume + spread (float): Spread + Index (int): Custom attribute representing the position of the candle in a sequence. + """ + time: float + high: float + low: float + close: float + real_volume: float + spread: float + open: float + tick_volume: float + Index: int + + def __init__(self, **kwargs): + """Create a Candle object from keyword arguments. + + Keyword Args: + **kwargs: Candle attributes and values as keyword arguments. + """ + self.time = kwargs.pop('time', 0) + self.Index = kwargs.pop('Index', 0) + self.set_attributes(**kwargs) + def __repr__(self): + keys = reprlib.repr(', '.join('%s=%s' % (i, j) for i, j in self.__dict__.items()))[1:-1] + return '%(class)s(%(args)s)' % {'class': self.__class__.__name__, 'args': keys} + + def __eq__(self, other: 'Candle'): + return self.time == other.time + + def __hash__(self): + return hash(self.time) + + def __lt__(self, other: 'Candle'): + return self.time < other.time + + def __gt__(self, other: 'Candle'): + return self.time > other.time + + def set_attributes(self, **kwargs): + """Set keyword arguments as instance attributes + + Keyword Args: + **kwargs: Instance attributes and values as keyword arguments + """ + [setattr(self, i, j) for i, j in kwargs.items()] + + @property + def mid(self) -> float: + """The median of open and close + + Returns: + float: The median of open and close + """ + return (self.open + self.close) / 2 + + def is_bullish(self) -> bool: + """ A simple check to see if the candle is bullish. + + Returns: + bool: True or False + """ + return self.close >= self.open + + def is_bearish(self) -> bool: + """A simple check to see if the candle is bearish. + + Returns: + bool: True or False + """ + return self.open > self.close + +_Candle = TypeVar('_Candle', bound=Candle) +_Candles = TypeVar('_Candles', bound='Candles') + + +class Candles(Generic[_Candle]): + """An iterable container class of Candle objects in chronological order. + + Attributes: + Index (Series['int']): A pandas Series of the indexes of all candles in the object. + time (Series['int']): A pandas Series of the time of all candles in the object. + open (Series[float]): A pandas Series of the opening price of all candles in the object. + high (Series[float]): A pandas Series of the high price of all candles in the object. + low (Series[float]): A pandas Series of the low price of all candles in the object. + close (Series[float]): A pandas Series of the closing price of all candles in the object. + tick_volume (Series[float]): A pandas Series of the tick volume of all candles in the object. + real_volume (Series[float]): A pandas Series of the real volume of all candles in the object. + spread (Series[float]): A pandas Series of the spread of all candles in the object. + timeframe (TimeFrame): The timeframe of the candles in the object. + Candle (Type[Candle]): The Candle class for representing the candles in the object. + + properties: + data (DataFrame): A pandas DataFrame of all candles in the object. + + Notes: + The candle class can be customized by subclassing the Candle class and passing the subclass as the candle keyword argument. + Or defining it on the class body as a class attribute. + """ + Index: Series + time: Series + open: Series + high: Series + low: Series + close: Series + tick_volume: Series + real_volume: Series + spread: Series + Candle: Type[Candle] + timeframe: TimeFrame + + def __init__(self, *, data: DataFrame | _Candles | Iterable, flip=False, candle_class: Type[_Candle] = None): + """A container class of Candle objects in chronological order. + + Args: + data (DataFrame|Candles|Iterable): A pandas dataframe, a Candles object or any suitable iterable + + Keyword Args: + flip (bool): Reverse the chronological order of the candles to the oldest first. Defaults to False. + candle_class: A subclass of Candle to use as the candle class. Defaults to Candle. + """ + if isinstance(data, DataFrame): + data = data + elif isinstance(data, type(self)): + data = DataFrame(data.data) + elif isinstance(data, Iterable): + data = DataFrame(data) + else: + raise ValueError(f'Cannot create DataFrame from object of {type(data)}') + + self._data = data.iloc[::-1] if flip else data + self.Candle = candle_class or Candle + + def __repr__(self): + return self._data.__repr__() + + def __len__(self): + return self._data.shape[0] + + def __contains__(self, item: _Candle): + return item.time == self[item.Index].time + + def __getitem__(self, index) -> _Candle | _Candles: + if isinstance(index, slice): + cls = self.__class__ + data = self._data.iloc[index] + data.reset_index(drop=True, inplace=True) + return cls(data=data) + + if isinstance(index, str): + return self._data[index] + + item = self._data.iloc[index] + return self.Candle(Index=index, **item) + + def __setitem__(self, index, value: Series): + if isinstance(value, Series): + self._data[index] = value + return + raise TypeError(f'Expected Series got {type(value)}') + + def __getattr__(self, item): + if item in list(self._data.columns.values): + return self._data[item] + raise AttributeError(f'Attribute {item} not defined on class {self.__class__.__name__}') + + def __iter__(self): + return (self.Candle(**row._asdict()) for row in self._data.itertuples()) + + @property + def timeframe(self): + tf = self.time[1] - self.time[0] + return TimeFrame.get(abs(tf)) + + @property + def ta(self): + """Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + + Returns: + pandas_ta: The pandas_ta library + """ + return self._data.ta + + @property + def ta_lib(self): + """Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + + Returns: + ta: The ta library + """ + return ta + + @property + def data(self) -> DataFrame: + """The original data passed to the class as a pandas DataFrame""" + return self._data + + def rename(self, inplace=True, **kwargs) -> _Candles | None : + """Rename columns of the candles class. + + Keyword Args: + inplace (bool): Rename the columns inplace or return a new instance of the class with the renamed columns + **kwargs: The new names of the columns + + Returns: + Candles: A new instance of the class with the renamed columns if inplace is False. + None: If inplace is True + """ + res = self._data.rename(columns=kwargs, inplace=inplace) + return res if inplace else self.__class__(data=res) diff --git a/src/aiomql/core/__init__.py b/src/aiomql/core/__init__.py new file mode 100644 index 0000000..4e57e54 --- /dev/null +++ b/src/aiomql/core/__init__.py @@ -0,0 +1,7 @@ +from .meta_trader import MetaTrader +from .config import Config +from .models import * +from .constants import * +from .base import Base +from .errors import Error +from .exceptions import * \ No newline at end of file diff --git a/src/aiomql/core/base.py b/src/aiomql/core/base.py new file mode 100644 index 0000000..325cb0c --- /dev/null +++ b/src/aiomql/core/base.py @@ -0,0 +1,137 @@ +from functools import cache +import reprlib +from logging import getLogger + +from .config import Config +from .meta_trader import MetaTrader + +logger = getLogger(__name__) + + +class Base: + """A base class for all data model classes in the aiomql package. + This class provides a set of common methods and attributes for all data model classes. + For the data model classes attributes are annotated on the class body and are set as object attributes when the + class is instantiated. + + Keyword Args: + **kwargs: Object attributes and values as keyword arguments. Only added if they are annotated on the class body. + + Class Attributes: + mt5 (MetaTrader): An instance of the MetaTrader class + config (Config): An instance of the Config class + Meta (Type[Meta]): The Meta class for configuration of the data model class + """ + mt5: MetaTrader = MetaTrader() + config = Config() + + def __init__(self, **kwargs): + self.set_attributes(**kwargs) + + def __repr__(self): + keys = reprlib.repr(', '.join('%s=%s' % (i, j) for i, j in self.__dict__.items()))[1:-1] + return '%(class)s(%(args)s)' % {'class': self.__class__.__name__, 'args': keys} + + def set_attributes(self, **kwargs): + """Set keyword arguments as object attributes + + Keyword Args: + **kwargs: Object attributes and values as keyword arguments + + Raises: + AttributeError: When assigning an attribute that does not belong to the class or any parent class + + Notes: + Only sets attributes that have been annotated on the class body. + """ + for i, j in kwargs.items(): + try: + setattr(self, i, self.annotations[i](j)) + except KeyError: + logger.warning(f"Attribute {i} does not belong to class {self.__class__.__name__}") + continue + + except ValueError: + logger.warning(f'Cannot covert object of type {type(j)} to type {self.annotations[i]}') + continue + + except Exception as exe: + logger.warning(f'Did not set attribute {i} on class {self.__class__.__name__} due to {exe}') + continue + + @property + @cache + def annotations(self) -> dict: + """Class annotations from all ancestor classes and the current class. + + Returns: + dict: A dictionary of class annotations + """ + annots = {} + for base in self.__class__.__mro__[-3::-1]: + annots |= getattr(base, '__annotations__', {}) + return annots + + def get_dict(self, exclude: set = None, include: set = None) -> dict: + """Returns class attributes as a dict, with the ability to filter + + Keyword Args: + exclude: A set of attributes to be excluded + include: Specific attributes to be returned + + Returns: + dict: A dictionary of specified class attributes + + Notes: + You can only set either of include or exclude. If you set both, include will take precedence + """ + exclude, include = exclude or set(), include or set() + filter_ = include or set(self.dict.keys()).difference(exclude) + return {key: value for key, value in self.dict.items() if key in filter_} + + @property + @cache + def class_vars(self): + """Annotated class attributes + + Returns: + dict: A dictionary of available class attributes in all ancestor classes and the current class. + """ + clss = self.__class__.__mro__[-3::-1] + cls_dict = {} + for cls in clss: + cls_dict |= cls.__dict__ + return {key: value for key, value in cls_dict.items() if key in self.annotations} + + @property + def dict(self) -> dict: + """All instance and class attributes as a dictionary, except those excluded in the Meta class. + + Returns: + dict: A dictionary of instance and class attributes + """ + try: + return {key: value for key, value in (self.class_vars | self.__dict__).items() if key not in self.Meta.filter} + except Exception as err: + logger.warning(err) + + class Meta: + """A class for defining class attributes to be excluded or included in the dict property + + Attributes: + exclude (set): A set of attributes to be excluded + include (set): Specific attributes to be returned. Include supercedes exclude. + """ + exclude = {'mt5', "Config"} + include = set() + + @classmethod + @property + def filter(cls) -> set: + """Combine the exclude and include attributes to return a set of attributes to be excluded. + + Returns: + set: A set of attributes to be excluded + """ + return cls.exclude.difference(cls.include) + \ No newline at end of file diff --git a/src/aiomql/core/config.py b/src/aiomql/core/config.py new file mode 100644 index 0000000..bc519e6 --- /dev/null +++ b/src/aiomql/core/config.py @@ -0,0 +1,110 @@ +import os +from pathlib import Path +from sys import _getframe +from typing import Iterator +import json +from logging import getLogger + +logger = getLogger(__name__) + + +class Config: + """A class for handling configuration settings for the aiomql package. + + Keyword Args: + **kwargs: Configuration settings as keyword arguments. + Variables set this way supersede those set in the config file. + + Attributes: + record_trades (bool): Whether to keep record of trades or not. + filename (str): Name of the config file + records_dir (str): Path to the directory where trade records are saved + win_percentage (float): Percentage of achieved target profit in a trade to be considered a win + login (int): Trading account number + password (str): Trading account password + server (str): Broker server + path (str): Path to terminal file + timeout (int): Timeout for terminal connection + + Notes: + By default, the config class looks for a file named aiomql.json. + You can change this by passing the filename keyword argument to the constructor. + By passing reload=True to the load_config method, you can reload and search again for the config file. + """ + login: int = 0 + password: str = '' + server: str = '' + path: str = '' + timeout: int = 60000 + record_trades: bool = True + filename: str = 'aiomql.json' + win_percentage: float = 0.85 + records_dir = Path.home() / 'Documents' / 'Aiomql' / 'Trade Records' if record_trades else None + _load = 1 + + def __new__(cls, *args, **kwargs): + if not hasattr(cls, '_instance'): + cls._instance = super().__new__(cls) + return cls._instance + + def __init__(self, **kwargs): + self.load_config(reload=False) + [setattr(self, key, value) for key, value in kwargs] + + + @staticmethod + def walk_to_root(path: str) -> Iterator[str]: + + if not os.path.exists(path): + raise IOError('Starting path not found') + + if os.path.isfile(path): + path = os.path.dirname(path) + + last_dir = None + current_dir = os.path.abspath(path) + while last_dir != current_dir: + yield current_dir + parent_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir)) + last_dir, current_dir = current_dir, parent_dir + + def find_config(self): + current_file = __file__ + frame = _getframe() + while frame.f_code.co_filename == current_file: + if frame.f_back is None: + return None + frame = frame.f_back + frame_filename = frame.f_code.co_filename + path = os.path.dirname(os.path.abspath(frame_filename)) + + for dirname in self.walk_to_root(path): + check_path = os.path.join(dirname, self.filename) + if os.path.isfile(check_path): + return check_path + return None + + def load_config(self, file: str = None, reload: bool = True): + if reload: + self._load = 1 + if self._load != 1: + return + + self._load = 0 + data = {} + if (file := (file or self.find_config())) is None: + logger.warning('No Config File Found') + else: + fh = open(file, mode='r') + data = json.load(fh) + fh.close() + [setattr(self, key, value) for key, value in data.items()] + self.records_dir.mkdir(parents=True, exist_ok=True) if self.records_dir else ... + + def account_info(self) -> dict['login', 'password', 'server']: + """Returns Account login details as found in the config object if available + + Returns: + dict: A dictionary of login details + """ + return {'login': self.login, 'password': self.password, 'server': self.server} diff --git a/src/aiomql/core/constants.py b/src/aiomql/core/constants.py new file mode 100644 index 0000000..e54a78a --- /dev/null +++ b/src/aiomql/core/constants.py @@ -0,0 +1,786 @@ +from enum import IntEnum, IntFlag + +import MetaTrader5 as mt5 + +""" +MetaTrader5 constants as IntEnum types with Python style class names and nice string representation + +Examples: + >>> from aiomql import OrderFilling + >>> fok = OrderFilling.FOK + >>> print(fok) + "ORDER_FILLING_FOK" +""" + + +class Repr: + __enum_name__ = "" + + def __str__(self): + return f"{self.__enum_name__}_{self.name}" + + +class TradeAction(Repr, IntEnum): + """TRADE_REQUEST_ACTION Enum. + + Attributes: + DEAL (int): Delete the pending order placed previously Place a trade order for an immediate execution with the + specified parameters (market order). + PENDING (int): Delete the pending order placed previously + SLTP (int): Modify Stop Loss and Take Profit values of an opened position + MODIFY (int): Modify the parameters of the order placed previously + REMOVE (int): Delete the pending order placed previously + CLOSE_BY (int): Close a position by an opposite one + """ + __enum_name__ = "TRADE_ACTION" + DEAL = mt5.TRADE_ACTION_DEAL + PENDING = mt5.TRADE_ACTION_PENDING + SLTP = mt5.TRADE_ACTION_SLTP + MODIFY = mt5.TRADE_ACTION_MODIFY + REMOVE = mt5.TRADE_ACTION_MODIFY + CLOSE_BY = mt5.TRADE_ACTION_CLOSE_BY + + +class OrderFilling(Repr, IntEnum): + """ORDER_TYPE_FILLING Enum. + + Attributes: + FOK (int): This execution policy means that an order can be executed only in the specified volume. + If the necessary amount of a financial instrument is currently unavailable in the market, the order will + not be executed. The desired volume can be made up of several available offers. + + IOC (int): An agreement to execute a deal at the maximum volume available in the market within the volume + specified in the order. If the request cannot be filled completely, an order with the available volume will + be executed, and the remaining volume will be canceled. + + RETURN (int): This policy is used only for market (ORDER_TYPE_BUY and ORDER_TYPE_SELL), limit and stop limit + orders (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT,ORDER_TYPE_BUY_STOP_LIMIT and + ORDER_TYPE_SELL_STOP_LIMIT) and only for the symbols with Market or Exchange execution modes. If filled + partially, a market or limit order with the remaining volume is not canceled, and is processed further. + During activation of the ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT orders, an appropriate + limit order ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT with the ORDER_FILLING_RETURN type is created. + """ + __enum_name__ = "ORDER_FILLING" + FOK = mt5.ORDER_FILLING_FOK + IOC = mt5.ORDER_FILLING_IOC + RETURN = mt5.ORDER_FILLING_RETURN + + +class OrderTime(Repr, IntEnum): + """ORDER_TIME Enum. + + Attributes: + GTC (int): Good till cancel order + DAY (int): Good till current trade day order + SPECIFIED (int): The order is active until the specified date + SPECIFIED_DAY (int): The order is active until 23:59:59 of the specified day. If this time appears to be out of + a trading session, the expiration is processed at the nearest trading time. + """ + __enum_name__ = "ORDER_TIME" + GTC = mt5.ORDER_TIME_GTC + DAY = mt5.ORDER_TIME_DAY + SPECIFIED = mt5.ORDER_TIME_SPECIFIED + SPECIFIED_DAY = mt5.ORDER_TIME_SPECIFIED_DAY + + +class OrderType(Repr, IntEnum): + """ORDER_TYPE Enum. + + Attributes: + BUY (int): Market buy order + SELL (int): Market sell order + BUY_LIMIT (int): Buy Limit pending order + SELL_LIMIT (int): Sell Limit pending order + BUY_STOP (int): Buy Stop pending order + SELL_STOP (int): Sell Stop pending order + BUY_STOP_LIMIT (int): Upon reaching the order price, Buy Limit pending order is placed at StopLimit price + SELL_STOP_LIMIT (int): Upon reaching the order price, Sell Limit pending order is placed at StopLimit price + CLOSE_BY (int): Order for closing a position by an opposite one + + Properties: + opposite (int): Gets the opposite of an order type + """ + __enum_name__ = "ORDER_TYPE" + BUY = mt5.ORDER_TYPE_BUY + SELL = mt5.ORDER_TYPE_SELL + BUY_LIMIT = mt5.ORDER_TYPE_BUY_LIMIT + SELL_LIMIT = mt5.ORDER_TYPE_SELL_LIMIT + BUY_STOP = mt5.ORDER_TYPE_BUY_STOP + SELL_STOP = mt5.ORDER_TYPE_SELL_STOP + BUY_STOP_LIMIT = mt5.ORDER_TYPE_BUY_STOP_LIMIT + SELL_STOP_LIMIT = mt5.ORDER_TYPE_SELL_STOP_LIMIT + CLOSE_BY = mt5.ORDER_TYPE_CLOSE_BY + + @property + def opposite(self): + """Gets the opposite of an order type for closing an open position + + Returns: + int: integer value of opposite order type + """ + return {0: 1, 1: 0, 2: 3, 3: 2, 4: 5, 5: 4, 6: 7, 7: 6, 8: 8}[self] + + +class BookType(Repr, IntEnum): + """BOOK_TYPE Enum. + + Attributes: + SELL (int): Sell order (Offer) + BUY (int): Buy order (Bid) + SELL_MARKET (int): Sell order by Market + BUY_MARKET (int): Buy order by Market + """ + __enum_name__ = "BOOK_TYPE" + SELL = mt5.BOOK_TYPE_SELL + BUY = mt5.BOOK_TYPE_BUY + SELL_MARKET = mt5.BOOK_TYPE_SELL_MARKET + BUY_MARKET = mt5.BOOK_TYPE_BUY_MARKET + + +class TimeFrame(Repr, IntEnum): + """TIMEFRAME Enum. + + Attributes: + M1 (int): One Minute + M2 (int): Two Minutes + M3 (int): Three Minutes + M4 (int): Four Minutes + M5 (int): Five Minutes + M6 (int): Six Minutes + M10 (int): Ten Minutes + M15 (int): Fifteen Minutes + M20 (int): Twenty Minutes + M30 (int): Thirty Minutes + H1 (int): One Hour + H2 (int): Two Hours + H3 (int): Three Hours + H4 (int): Four Hours + H6 (int): Six Hours + H8 (int): Eight Hours + D1 (int): One Day + W1 (int): One Week + MN1 (int): One Month + + Properties: + time: return the value of the timeframe object in seconds. Used as a property + + Methods: + get: get a timeframe object from a time value in seconds + """ + __enum_name__ = "TIMEFRAME" + + def __str__(self): + return self.name + + M1 = mt5.TIMEFRAME_M1 + M2 = mt5.TIMEFRAME_M2 + M3 = mt5.TIMEFRAME_M3 + M4 = mt5.TIMEFRAME_M4 + M5 = mt5.TIMEFRAME_M5 + M6 = mt5.TIMEFRAME_M6 + M10 = mt5.TIMEFRAME_M10 + M15 = mt5.TIMEFRAME_M15 + M20 = mt5.TIMEFRAME_M20 + M30 = mt5.TIMEFRAME_M30 + H1 = mt5.TIMEFRAME_H1 + H2 = mt5.TIMEFRAME_H2 + H3 = mt5.TIMEFRAME_H3 + H4 = mt5.TIMEFRAME_H4 + H6 = mt5.TIMEFRAME_H6 + H8 = mt5.TIMEFRAME_H8 + H12 = mt5.TIMEFRAME_H12 + D1 = mt5.TIMEFRAME_D1 + W1 = mt5.TIMEFRAME_W1 + MN1 = mt5.TIMEFRAME_MN1 + + @property + def time(self): + """The number of seconds in a TIMEFRAME + + Returns: + int: The number of seconds in a TIMEFRAME + + Examples: + >>> t = TimeFrame.H1 + >>> print(t.time) + 3600 + """ + times = {1: 60, 2: 120, 3: 180, 4: 240, 5: 300, 6: 360, 10: 600, 15: 900, 20: 1200, 30: 1800, 16385: 3600, + 16386: 7200, 16387: 10800, 16388: 14400, 16390: 21600, 16392: 28800, 16396: 43200, 16408: 86400, + 32769: 604800, 49153: 2592000} + return times[self] + + @classmethod + def get(cls, time: int): + times = {60: 1, 120: 2, 180: 3, 240: 4, 300: 5, 360: 6, 600: 10, 900: 15, 1200: 20, 1800: 30, 3600: 16385, + 7200: 16386, 10800: 16387, 14400: 16388, 21600: 16390, 28800: 16392, 43200: 16396, 86400: 16408, + 604800: 32769, 2592000: 49153} + return TimeFrame(times[int(time)]) + + +class CopyTicks(Repr, IntEnum): + """COPY_TICKS Enum. This defines the types of ticks that can be requested using the copy_ticks_from() and + copy_ticks_range() functions. + + Attributes: + ALL (int): All ticks + INFO (int): Ticks containing Bid and/or Ask price changes + TRADE (int): Ticks containing Last and/or Volume price changes + """ + __enum_name__ = "COPY_TICKS" + ALL = mt5.COPY_TICKS_ALL + INFO = mt5.COPY_TICKS_INFO + TRADE = mt5.COPY_TICKS_TRADE + + +class PositionType(Repr, IntEnum): + """POSITION_TYPE Enum. Direction of an open position (buy or sell) + + Attributes: + BUY (int): Buy + SELL (int): Sell + """ + __enum_name__ = "POSITION_TYPE" + BUY = mt5.POSITION_TYPE_BUY + SELL = mt5.POSITION_TYPE_SELL + + +class PositionReason(Repr, IntEnum): + """POSITION_REASON Enum. The reason for opening a position is contained in the POSITION_REASON Enum + + Attributes: + CLIENT (int): The position was opened as a result of activation of an order placed from a desktop terminal + MOBILE (int): The position was opened as a result of activation of an order placed from a mobile application + WEB (int): The position was opened as a result of activation of an order placed from the web platform + EXPERT (int): The position was opened as a result of activation of an order placed from an MQL5 program, + i.e. an Expert Advisor or a script + """ + __enum_name__ = "POSITION_REASON" + CLIENT = mt5.POSITION_REASON_CLIENT + MOBILE = mt5.POSITION_REASON_MOBILE + WEB = mt5.POSITION_REASON_WEB + EXPERT = mt5.POSITION_REASON_EXPERT + + +class DealType(Repr, IntEnum): + """DEAL_TYPE enum. Each deal is characterized by a type, allowed values are enumerated in this enum + + Attributes: + BUY (int): Buy + SELL (int): Sell + BALANCE (int): Balance + CREDIT (int): Credit + CHARGE (int): Additional Charge + CORRECTION (int): Correction + BONUS (int): Bonus + COMMISSION (int): Additional Commission + COMMISSION_DAILY (int): Daily Commission + COMMISSION_MONTHLY (int): Monthly Commission + COMMISSION_AGENT_DAILY (int): Daily Agent Commission + COMMISSION_AGENT_MONTHLY (int): Monthly Agent Commission + INTEREST (int): Interest Rate + DEAL_DIVIDEND (int): Dividend Operations + DEAL_DIVIDEND_FRANKED (int): Franked (non-taxable) dividend operations + DEAL_TAX (int): Tax Charges + + BUY_CANCELED (int): Canceled buy deal. There can be a situation when a previously executed buy deal is canceled. + In this case, the type of the previously executed deal (DEAL_TYPE_BUY) is changed to DEAL_TYPE_BUY_CANCELED, + and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated + balance operation + + SELL_CANCELED (int): Canceled sell deal. There can be a situation when a previously executed sell deal is + canceled. In this case, the type of the previously executed deal (DEAL_TYPE_SELL) is changed to + DEAL_TYPE_SELL_CANCELED, and its profit/loss is zeroized. Previously obtained profit/loss is + charged/withdrawn using a separated balance operation. + """ + __enum_name__ = "DEAL_TYPE" + BUY = mt5.DEAL_TYPE_BUY + SELL = mt5.DEAL_TYPE_SELL + BALANCE = mt5.DEAL_TYPE_BALANCE + CREDIT = mt5.DEAL_TYPE_CREDIT + CHARGE = mt5.DEAL_TYPE_CHARGE + CORRECTION = mt5.DEAL_TYPE_CORRECTION + BONUS = mt5.DEAL_TYPE_BONUS + COMMISSION = mt5.DEAL_TYPE_COMMISSION + COMMISSION_DAILY = mt5.DEAL_TYPE_COMMISSION_DAILY + COMMISSION_MONTHLY = mt5.DEAL_TYPE_COMMISSION_MONTHLY + COMMISSION_AGENT_DAILY = mt5.DEAL_TYPE_COMMISSION_AGENT_DAILY + COMMISSION_AGENT_MONTHLY = mt5.DEAL_TYPE_COMMISSION_AGENT_MONTHLY + INTEREST = mt5.DEAL_TYPE_INTEREST + BUY_CANCELED = mt5.DEAL_TYPE_BUY_CANCELED + SELL_CANCELED = mt5.DEAL_TYPE_SELL_CANCELED + DEAL_DIVIDEND = mt5.DEAL_DIVIDEND + DEAL_DIVIDEND_FRANKED = mt5.DEAL_DIVIDEND_FRANKED + DEAL_TAX = mt5.DEAL_TAX + + def __str__(self): + if self.name in ('DEAL_DIVIDEND', 'DEAL_DIVIDEND_FRANKED', 'DEAL_TAX'): + return self.name + return super().__str__() + + +class DealEntry(Repr, IntEnum): + """DEAL_ENTRY Enum. Deals differ not only in their types set in DEAL_TYPE enum, but also in the way they change + positions. This can be a simple position opening, or accumulation of a previously opened position (market entering), + position closing by an opposite deal of a corresponding volume (market exiting), or position reversing, if the + opposite-direction deal covers the volume of the previously opened position. + + Attributes: + IN (int): Entry In + OUT (int): Entry Out + INOUT (int): Reverse + OUT_BY (int): Close a position by an opposite one + """ + __enum_name__ = "DEAL_ENTRY" + IN = mt5.DEAL_ENTRY_IN + OUT = mt5.DEAL_ENTRY_OUT + INOUT = mt5.DEAL_ENTRY_INOUT + OUT_BY = mt5.DEAL_ENTRY_OUT_BY + + +class DealReason(Repr, IntEnum): + """DEAL_REASON Enum. The reason for deal execution is contained in the DEAL_REASON property. A deal can be executed + as a result of triggering of an order placed from a mobile application or an MQL5 program, as well as as a result + of the StopOut event, variation margin calculation, etc. + + Attributes: + CLIENT (int): The deal was executed as a result of activation of an order placed from a desktop terminal + MOBILE (int): The deal was executed as a result of activation of an order placed from a desktop terminal + WEB (int): The deal was executed as a result of activation of an order placed from the web platform + EXPERT (int): 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 + SL (int): The deal was executed as a result of Stop Loss activation + TP (int): The deal was executed as a result of Take Profit activation + SO (int): The deal was executed as a result of the Stop Out event + ROLLOVER (int): The deal was executed due to a rollover + VMARGIN (int): The deal was executed after charging the variation margin + SPLIT (int): The deal was executed after the split (price reduction) of an instrument, which had an open + position during split announcement + """ + __enum_name__ = "DEAL_REASON" + CLIENT = mt5.DEAL_REASON_CLIENT + MOBILE = mt5.DEAL_REASON_MOBILE + WEB = mt5.DEAL_REASON_WEB + EXPERT = mt5.DEAL_REASON_EXPERT + SL = mt5.DEAL_REASON_SL + TP = mt5.DEAL_REASON_TP + SO = mt5.DEAL_REASON_SO + ROLLOVER = mt5.DEAL_REASON_ROLLOVER + VMARGIN = mt5.DEAL_REASON_VMARGIN + SPLIT = mt5.DEAL_REASON_SPLIT + + +class OrderReason(Repr, IntEnum): + """ORDER_REASON Enum. + + Attributes: + CLIENT (int): The order was placed from a desktop terminal + MOBILE (int): The order was placed from a mobile application + WEB (int): The order was placed from a web platform + EXPERT (int): The order was placed from an MQL5-program, i.e. by an Expert Advisor or a script + SL (int): The order was placed as a result of Stop Loss activation + TP (int): The order was placed as a result of Take Profit activation + SO (int): The order was placed as a result of the Stop Out event + """ + __enum_name__ = "ORDER_REASON" + CLIENT = mt5.ORDER_REASON_CLIENT + MOBILE = mt5.ORDER_REASON_MOBILE + WEB = mt5.ORDER_REASON_WEB + EXPERT = mt5.ORDER_REASON_EXPERT + SL = mt5.ORDER_REASON_SL + TP = mt5.ORDER_REASON_TP + SO = mt5.ORDER_REASON_SO + + +class SymbolChartMode(Repr, IntEnum): + """SYMBOL_CHART_MODE Enum. A symbol price chart can be based on Bid or Last prices. The price selected for symbol + charts also affects the generation and display of bars in the terminal. + Possible values of the SYMBOL_CHART_MODE property are described in this enum + + Attributes: + BID (int): Bars are based on Bid prices + LAST (int): Bars are based on last prices + """ + __enum_name__ = "SYMBOL_CHART_MODE" + BID = mt5.SYMBOL_CHART_MODE_BID + LAST = mt5.SYMBOL_CHART_MODE_LAST + + +class SymbolCalcMode(Repr, IntEnum): + """SYMBOL_CALC_MODE Enum. The SYMBOL_CALC_MODE enumeration is used for obtaining information about how the margin + requirements for a symbol are calculated. + + Attributes: + FOREX (int): Forex mode - calculation of profit and margin for Forex + FOREX_NO_LEVERAGE (int): Forex No Leverage mode – calculation of profit and margin for Forex symbols without + taking into account the leverage + FUTURES (int): Futures mode - calculation of margin and profit for futures + CFD (int): CFD mode - calculation of margin and profit for CFD + CFDINDEX (int): CFD index mode - calculation of margin and profit for CFD by indexes + CFDLEVERAGE (int): CFD Leverage mode - calculation of margin and profit for CFD at leverage trading + EXCH_STOCKS (int): Calculation of margin and profit for trading securities on a stock exchange + EXCH_FUTURES (int): Calculation of margin and profit for trading futures contracts on a stock exchange + EXCH_OPTIONS (int): value is 34 + EXCH_OPTIONS_MARGIN (int): value is 36 + EXCH_BONDS (int): Exchange Bonds mode – calculation of margin and profit for trading bonds on a stock exchange + STOCKS_MOEX (int): Exchange MOEX Stocks mode –calculation of margin and profit for trading securities on MOEX + EXCH_BONDS_MOEX (int): Exchange MOEX Bonds mode – calculation of margin and profit for trading bonds on MOEX + + SERV_COLLATERAL (int): Collateral mode - a symbol is used as a non-tradable asset on a trading account. + The market value of an open position is calculated based on the volume, current market price, contract size + and liquidity ratio. The value is included into Assets, which are added to Equity. Open positions of such + symbols increase the Free Margin amount and are used as additional margin (collateral) for open positions + """ + __enum_name__ = "SYMBOL_CALC_MODE" + FOREX = mt5.SYMBOL_CALC_MODE_FOREX + FOREX_NO_LEVERAGE = mt5.SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE + FUTURES = mt5.SYMBOL_CALC_MODE_FUTURES + CFD = mt5.SYMBOL_CALC_MODE_CFD + CFDINDEX = mt5.SYMBOL_CALC_MODE_CFDINDEX + CFDLEVERAGE = mt5.SYMBOL_CALC_MODE_CFDLEVERAGE + EXCH_STOCKS = mt5.SYMBOL_CALC_MODE_EXCH_STOCKS + EXCH_FUTURES = mt5.SYMBOL_CALC_MODE_EXCH_FUTURES + EXCH_OPTIONS = mt5.SYMBOL_CALC_MODE_EXCH_OPTIONS + EXCH_OPTIONS_MARGIN = mt5.SYMBOL_CALC_MODE_EXCH_OPTIONS_MARGIN + EXCH_BONDS = mt5.SYMBOL_CALC_MODE_EXCH_BONDS + EXCH_STOCKS_MOEX = mt5.SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX + EXCH_BONDS_MOEX = mt5.SYMBOL_CALC_MODE_EXCH_BONDS_MOEX + SERV_COLLATERAL = mt5.SYMBOL_CALC_MODE_SERV_COLLATERAL + + +class SymbolTradeMode(Repr, IntEnum): + """SYMBOL_TRADE_MODE Enum. There are several symbol trading modes. Information about trading modes of a certain + symbol is reflected in the values this enumeration + + Attributes: + DISABLED (int): Trade is disabled for the symbol + LONGONLY (int): Allowed only long positions + SHORTONLY (int): Allowed only short positions + CLOSEONLY (int): Allowed only position close operations + FULL (int): No trade restrictions + """ + + __enum_name__ = "SYMBOL_TRADE_MODE" + DISABLED = mt5.SYMBOL_TRADE_MODE_DISABLED + LONGONLY = mt5.SYMBOL_TRADE_MODE_LONGONLY + SHORTONLY = mt5.SYMBOL_TRADE_MODE_SHORTONLY + CLOSEONLY = mt5.SYMBOL_TRADE_MODE_CLOSEONLY + FULL = mt5.SYMBOL_TRADE_MODE_FULL + + +class SymbolTradeExecution(Repr, IntEnum): + """SYMBOL_TRADE_EXECUTION Enum. The modes, or execution policies, define the rules for cases when the price has + changed or the requested volume cannot be completely fulfilled at the moment. + + Attributes: + REQUEST (int): Executing a market order at the price previously received from the broker. Prices for a certain + market order are requested from the broker before the order is sent. Upon receiving the prices, order + execution at the given price can be either confirmed or rejected. + + INSTANT (int): Executing a market order at the specified price immediately. When sending a trade request to be + executed, the platform automatically adds the current prices to the order. + - If the broker accepts the price, the order is executed. + - If the broker does not accept the requested price, a "Requote" is sent — the broker returns prices, + at which this order can be executed. + + MARKET (int): A broker makes a decision about the order execution price without any additional discussion with the trader. + Sending the order in such a mode means advance consent to its execution at this price. + + EXCHANGE (int): Trade operations are executed at the prices of the current market offers. + """ + + __enum_name__ = "SYMBOL_TRADE_EXECUTION" + REQUEST = mt5.SYMBOL_TRADE_EXECUTION_REQUEST + INSTANT = mt5.SYMBOL_TRADE_EXECUTION_INSTANT + MARKET = mt5.SYMBOL_TRADE_EXECUTION_MARKET + EXCHANGE = mt5.SYMBOL_TRADE_EXECUTION_EXCHANGE + + +class SymbolSwapMode(Repr, IntEnum): + """SYMBOL_SWAP_MODE Enum. Methods of swap calculation at position transfer are specified in enumeration + ENUM_SYMBOL_SWAP_MODE. The method of swap calculation determines the units of measure of the SYMBOL_SWAP_LONG and + SYMBOL_SWAP_SHORT parameters. For example, if swaps are charged in the client deposit currency, then the values of + those parameters are specified as an amount of money in the client deposit currency. + + Attributes: + DISABLED (int): Swaps disabled (no swaps) + POINTS (int): Swaps are charged in points + CURRENCY_SYMBOL (int): Swaps are charged in money in base currency of the symbol + CURRENCY_MARGIN (int): Swaps are charged in money in margin currency of the symbol + CURRENCY_DEPOSIT (int): Swaps are charged in money, in client deposit currency + + INTEREST_CURRENT (int): Swaps are charged as the specified annual interest from the instrument price at + calculation of swap (standard bank year is 360 days) + + INTEREST_OPEN (int): Swaps are charged as the specified annual interest from the open price of position + (standard bank year is 360 days) + + REOPEN_CURRENT (int): Swaps are charged by reopening positions. At the end of a trading day the position is + closed. Next day it is reopened by the close price +/- specified number of points + (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + + REOPEN_BID (int): Swaps are charged by reopening positions. At the end of a trading day the position is closed. + Next day it is reopened by the current Bid price +/- specified number of + points (parameters SYMBOL_SWAP_LONG and SYMBOL_SWAP_SHORT) + """ + __enum_name__ = "SYMBOL_SWAP_MODE" + DISABLED = mt5.SYMBOL_SWAP_MODE_DISABLED + POINTS = mt5.SYMBOL_SWAP_MODE_POINTS + CURRENCY_SYMBOL = mt5.SYMBOL_SWAP_MODE_CURRENCY_SYMBOL + CURRENCY_MARGIN = mt5.SYMBOL_SWAP_MODE_CURRENCY_MARGIN + CURRENCY_DEPOSIT = mt5.SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT + INTEREST_CURRENT = mt5.SYMBOL_SWAP_MODE_INTEREST_CURRENT + INTEREST_OPEN = mt5.SYMBOL_SWAP_MODE_INTEREST_OPEN + REOPEN_CURRENT = mt5.SYMBOL_SWAP_MODE_REOPEN_CURRENT + REOPEN_BID = mt5.SYMBOL_SWAP_MODE_REOPEN_BID + + +class DayOfWeek(Repr, IntEnum): + """DAY_OF_WEEK Enum. + + Attributes: + SUNDAY (int): Sunday + MONDAY (int): Monday + TUESDAY (int): Tuesday + WEDNESDAY (int): Wednesday + THURSDAY (int): Thursday + FRIDAY (int): Friday + SATURDAY (int): Saturday + """ + __enum__name__ = "DAY_OF_WEEK" + SUNDAY = mt5.DAY_OF_WEEK_SUNDAY + MONDAY = mt5.DAY_OF_WEEK_MONDAY + TUESDAY = mt5.DAY_OF_WEEK_TUESDAY + WEDNESDAY = mt5.DAY_OF_WEEK_WEDNESDAY + THURSDAY = mt5.DAY_OF_WEEK_THURSDAY + FRIDAY = mt5.DAY_OF_WEEK_FRIDAY + SATURDAY = mt5.DAY_OF_WEEK_SATURDAY + + +class SymbolOrderGTCMode(Repr, IntEnum): + """SYMBOL_ORDER_GTC_MODE Enum. If the SYMBOL_EXPIRATION_MODE property is set to SYMBOL_EXPIRATION_GTC + (good till canceled), the expiration of pending orders, as well as of + Stop Loss/Take Profit orders should be additionally set using the ENUM_SYMBOL_ORDER_GTC_MODE enumeration. + + Attributes: + GTC (int): Pending orders and Stop Loss/Take Profit levels are valid for an unlimited period + until theirConstants, Enumerations and explicit cancellation + + DAILY (int): Orders are valid during one trading day. At the end of the day, all Stop Loss and + Take Profit levels, as well as pending orders are deleted. + + DAILY_NO_STOPS (int): When a trade day changes, only pending orders are deleted, + while Stop Loss and Take Profit levels are preserved + """ + __enum_name__ = "SYMBOL_ORDERS" + GTC = mt5.SYMBOL_ORDERS_GTC + DAILY = mt5.SYMBOL_ORDERS_DAILY + DAILY_NO_STOPS = mt5.SYMBOL_ORDERS_DAILY_NO_STOPS + + +class SymbolOptionRight(Repr, IntEnum): + """SYMBOL_OPTION_RIGHT Enum. An option is a contract, which gives the right, but not the obligation, + to buy or sell an underlying asset (goods, stocks, futures, etc.) at a specified price on or before a specific date. + The following enumerations describe option properties, including the option type and the right arising from it. + + Attributes: + CALL (int): A call option gives you the right to buy an asset at a specified price. + PUT (int): A put option gives you the right to sell an asset at a specified price. + """ + __enum_name__ = "SYMBOL_OPTION_RIGHT" + CALL = mt5.SYMBOL_OPTION_RIGHT_CALL + PUT = mt5.SYMBOL_OPTION_RIGHT_PUT + + +class SymbolOptionMode(Repr, IntEnum): + """SYMBOL_OPTION_MODE Enum. + + Attributes: + EUROPEAN (int): European option may only be exercised on a specified date (expiration, execution date, delivery date) + AMERICAN (int): American option may be exercised on any trading day or before expiry. The period within which + a buyer can exercise the option is specified for it. + """ + __enum_name__ = "SYMBOL_OPTION_MODE" + EUROPEAN = mt5.SYMBOL_OPTION_MODE_EUROPEAN + AMERICAN = mt5.SYMBOL_OPTION_MODE_AMERICAN + + +class AccountTradeMode(Repr, IntEnum): + """ACCOUNT_TRADE_MODE Enum. There are several types of accounts that can be opened on a trade server. + The type of account on which an MQL5 program is running can be found out using + the ENUM_ACCOUNT_TRADE_MODE enumeration. + + Attributes: + DEMO: Demo account + CONTEST: Contest account + REAL: Real Account + """ + __enum_name__ = "ACCOUNT_TRADE_MODE" + DEMO = mt5.ACCOUNT_TRADE_MODE_DEMO + CONTEST = mt5.ACCOUNT_TRADE_MODE_CONTEST + REAL = mt5.ACCOUNT_TRADE_MODE_REAL + + +class TickFlag(Repr, IntFlag): + """TICK_FLAG Enum. TICK_FLAG defines possible flags for ticks. These flags are used to describe ticks obtained by the + copy_ticks_from() and copy_ticks_range() functions. + + Attributes: + BID (int): Bid price changed + ASK (int): Ask price changed + LAST (int): Last price changed + VOLUME (int): Volume changed + BUY (int): last Buy price changed + SELL (int): last Sell price changed + """ + __enum_name__ = "TICK_FLAG" + BID = mt5.TICK_FLAG_BID + ASK = mt5.TICK_FLAG_ASK + LAST = mt5.TICK_FLAG_LAST + VOLUME = mt5.TICK_FLAG_VOLUME + BUY = mt5.TICK_FLAG_BUY + SELL = mt5.TICK_FLAG_SELL + + +class TradeRetcode(Repr, IntEnum): + """TRADE_RETCODE Enum. Return codes for order send/check operations + + Attributes: + REQUOTE (int): Requote + REJECT (int): Request rejected + CANCEL (int): Request canceled by trader + PLACED (int): Order placed + DONE (int): Request completed + DONE_PARTIAL (int): Only part of the request was completed + ERROR (int): Request processing error + TIMEOUT (int): Request canceled by timeout + INVALID (int): Invalid request + INVALID_VOLUME (int): Invalid volume in the request + INVALID_PRICE (int): Invalid price in the request + INVALID_STOPS (int): Invalid stops in the request + TRADE_DISABLED (int): Trade is disabled + MARKET_CLOSED (int): Market is closed + NO_MONEY (int): There is not enough money to complete the request + PRICE_CHANGED (int): Prices changed + PRICE_OFF (int): There are no quotes to process the request + INVALID_EXPIRATION (int): Invalid order expiration date in the request + ORDER_CHANGED (int): Order state changed + TOO_MANY_REQUESTS (int): Too frequent requests + NO_CHANGES (int): No changes in request + SERVER_DISABLES_AT (int): Autotrading disabled by server + CLIENT_DISABLES_AT (int): Autotrading disabled by client terminal + LOCKED (int): Request locked for processing + FROZEN (int): Order or position frozen + INVALID_FILL (int): Invalid order filling type + CONNECTION (int): No connection with the trade server + ONLY_REAL (int): Operation is allowed only for live accounts + LIMIT_ORDERS (int): The number of pending orders has reached the limit + LIMIT_VOLUME (int): The volume of orders and positions for the symbol has reached the limit + INVALID_ORDER (int): Incorrect or prohibited order type + POSITION_CLOSED (int): Position with the specified POSITION_IDENTIFIER has already been closed + INVALID_CLOSE_VOLUME (int): A close volume exceeds the current position volume + + CLOSE_ORDER_EXIST (int): A close order already exists for a specified position. This may happen when working in + the hedging system: + · when attempting to close a position with an opposite one, while close orders for the position already exist + · when attempting to fully or partially close a position if the total volume of the already present close + orders and the newly placed one exceeds the current position volume + + LIMIT_POSITIONS (int): The number of open positions simultaneously present on an account can be limited by the + server settings.After a limit is reached, the server returns the TRADE_RETCODE_LIMIT_POSITIONS error when + attempting to place an order. The limitation operates differently depending on the position accounting type: + · Netting — number of open positions is considered. When a limit is reached, the platform does not let + placing new orders whose execution may increase the number of open positions. In fact, the platform + allows placing orders only for the symbols that already have open positions. + The current pending orders are not considered since their execution may lead to changes in the current + positions but it cannot increase their number. + + · Hedging — pending orders are considered together with open positions, since a pending order activation + always leads to opening a new position. When a limit is reached, the platform does not allow placing + both new market orders for opening positions and pending orders. + + REJECT_CANCEL (int): The pending order activation request is rejected, the order is canceled. + LONG_ONLY (int): The request is rejected, because the "Only long positions are allowed" rule is set for the + symbol (POSITION_TYPE_BUY) + SHORT_ONLY (int): The request is rejected, because the "Only short positions are allowed" rule is set for the + symbol (POSITION_TYPE_SELL) + CLOSE_ONLY (int): The request is rejected, because the "Only position closing is allowed" rule is set for the + symbol + FIFO_CLOSE (int): The request is rejected, because "Position closing is allowed only by FIFO rule" flag is set + for the trading account (ACCOUNT_FIFO_CLOSE=true) + """ + __enum_name__ = "TRADE_RETCODE" + REQUOTE = mt5.TRADE_RETCODE_REQUOTE + REJECT = mt5.TRADE_RETCODE_REJECT + CANCEL = mt5.TRADE_RETCODE_CANCEL + PLACED = mt5.TRADE_RETCODE_PLACED + DONE = mt5.TRADE_RETCODE_DONE + DONE_PARTIAL = mt5.TRADE_RETCODE_DONE_PARTIAL + ERROR = mt5.TRADE_RETCODE_ERROR + TIMEOUT = mt5.TRADE_RETCODE_TIMEOUT + INVALID = mt5.TRADE_RETCODE_INVALID + INVALID_VOLUME = mt5.TRADE_RETCODE_INVALID_VOLUME + INVALID_PRICE = mt5.TRADE_RETCODE_INVALID_PRICE + INVALID_STOPS = mt5.TRADE_RETCODE_INVALID_STOPS + TRADE_DISABLED = mt5.TRADE_RETCODE_TRADE_DISABLED + MARKET_CLOSED = mt5.TRADE_RETCODE_MARKET_CLOSED + NO_MONEY = mt5.TRADE_RETCODE_NO_MONEY + PRICE_CHANGED = mt5.TRADE_RETCODE_PRICE_CHANGED + PRICE_OFF = mt5.TRADE_RETCODE_PRICE_OFF + INVALID_EXPIRATION = mt5.TRADE_RETCODE_INVALID_EXPIRATION + ORDER_CHANGED = mt5.TRADE_RETCODE_ORDER_CHANGED + TOO_MANY_REQUESTS = mt5.TRADE_RETCODE_TOO_MANY_REQUESTS + NO_CHANGES = mt5.TRADE_RETCODE_NO_CHANGES + SERVER_DISABLES_AT = mt5.TRADE_RETCODE_SERVER_DISABLES_AT + CLIENT_DISABLES_AT = mt5.TRADE_RETCODE_CLIENT_DISABLES_AT + LOCKED = mt5.TRADE_RETCODE_LOCKED + FROZEN = mt5.TRADE_RETCODE_FROZEN + INVALID_FILL = mt5.TRADE_RETCODE_INVALID_FILL + CONNECTION = mt5.TRADE_RETCODE_CONNECTION + ONLY_REAL = mt5.TRADE_RETCODE_ONLY_REAL + LIMIT_ORDERS = mt5.TRADE_RETCODE_LIMIT_ORDERS + LIMIT_VOLUME = mt5.TRADE_RETCODE_LIMIT_VOLUME + INVALID_ORDER = mt5.TRADE_RETCODE_INVALID_ORDER + POSITION_CLOSED = mt5.TRADE_RETCODE_POSITION_CLOSED + INVALID_CLOSE_VOLUME = mt5.TRADE_RETCODE_INVALID_CLOSE_VOLUME + CLOSE_ORDER_EXIST = mt5.TRADE_RETCODE_CLOSE_ORDER_EXIST + LIMIT_POSITIONS = mt5.TRADE_RETCODE_LIMIT_POSITIONS + REJECT_CANCEL = mt5.TRADE_RETCODE_REJECT_CANCEL + LONG_ONLY = mt5.TRADE_RETCODE_LONG_ONLY + SHORT_ONLY = mt5.TRADE_RETCODE_SHORT_ONLY + CLOSE_ONLY = mt5.TRADE_RETCODE_CLOSE_ONLY + FIFO_CLOSE = mt5.TRADE_RETCODE_FIFO_CLOSE + + +class AccountStopOutMode(Repr, IntEnum): + """ACCOUNT_STOPOUT_MODE Enum. + + Attributes: + PERCENT (int): Account stop out mode in percents + MONEY (int): Account stop out mode in money + """ + + __enum_name__ = "ACCOUNT_STOPOUT_MODE" + PERCENT = mt5.ACCOUNT_STOPOUT_MODE_PERCENT + MONEY = mt5.ACCOUNT_STOPOUT_MODE_MONEY + + +class AccountMarginMode(Repr, IntEnum): + """ACCOUNT_MARGIN_MODE Enum. + + Attributes: + RETAIL_NETTING (int): Used for the OTC markets to interpret positions in the "netting" + mode (only one position can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE). + + EXCHANGE (int): Used for the exchange markets. Margin is calculated based on the discounts specified in + symbol settings. Discounts are set by the broker, but not less than the values set by the exchange. + + HEDGING (int): Used for the exchange markets where individual positions are possible + (hedging, multiple positions can exist for one symbol). The margin is calculated based on the symbol + type (SYMBOL_TRADE_CALC_MODE) taking into account the hedged margin (SYMBOL_MARGIN_HEDGED). + """ + __enum_name__ = "ACCOUNT_MARGIN_MODE" + RETAIL_NETTING = mt5.ACCOUNT_MARGIN_MODE_RETAIL_NETTING + EXCHANGE = mt5.ACCOUNT_MARGIN_MODE_EXCHANGE + RETAIL_HEDGING = mt5.ACCOUNT_MARGIN_MODE_RETAIL_HEDGING diff --git a/src/aiomql/core/errors.py b/src/aiomql/core/errors.py new file mode 100644 index 0000000..8854476 --- /dev/null +++ b/src/aiomql/core/errors.py @@ -0,0 +1,30 @@ +class Error: + """Error class for handling errors from MetaTrader 5.""" + descriptions = { + # common errors + 1: 'Successful', + -1: 'generic fail', + -2: 'invalid arguments/parameters', + -3: 'no memory condition', + -4: 'no history', + -5: 'invalid version', + -6: 'authorization failed', + -7: 'unsupported method', + -8: 'auto-trading disabled', + # internal errors + -10000: 'internal IPC general error', + -10001: 'internal IPC send failed', + -10002: 'internal IPC recv failed', + -10003: 'internal IPC initialization fail', + -10004: 'internal IPC no ipc', + -10005: 'internal timeout', + } + def __init__(self, code: int, description: str = ''): + self.code = code + self.description = description or self.descriptions.get(code, 'Unknown Error') + + def __repr__(self): + return f""" + Error Code: {self.code} + Error Description: {self.description} + """ \ No newline at end of file diff --git a/src/aiomql/core/exceptions.py b/src/aiomql/core/exceptions.py new file mode 100644 index 0000000..cffbd2a --- /dev/null +++ b/src/aiomql/core/exceptions.py @@ -0,0 +1,19 @@ +"""Exceptions for the aiomql package.""" + +__all__ = ['LoginError', 'VolumeError', 'SymbolError', 'OrderError'] + +class LoginError(Exception): + """Raised when an error occurs when logging in.""" + pass + +class VolumeError(Exception): + """Raised when a volume is not valid or out of range for a symbol.""" + pass + + +class SymbolError(Exception): + """Raised when a symbol is not provided where required or not available in the Market Watch.""" + + +class OrderError(Exception): + """Raised when an error occurs when working with the order class.""" diff --git a/src/aiomql/core/meta_trader.py b/src/aiomql/core/meta_trader.py new file mode 100644 index 0000000..ebfc9d1 --- /dev/null +++ b/src/aiomql/core/meta_trader.py @@ -0,0 +1,355 @@ +from datetime import datetime +import asyncio +from logging import getLogger +from typing import Callable + +import MetaTrader5 + +from MetaTrader5 import BookInfo, SymbolInfo, AccountInfo, Tick, TerminalInfo, TradeOrder, TradeDeal,\ + TradePosition, OrderSendResult, OrderCheckResult + +from .constants import TimeFrame, CopyTicks, OrderType +from .errors import Error +from .config import Config + +logger = getLogger() + + +class BaseMeta(type): + def __new__(mcs, cls_name, bases, cls_dict): + defaults = MetaTrader5.__dict__ + defaults = {f'_{key}': value for key, value in defaults.items() if not key.startswith('_')} + cls_dict |= defaults + return super().__new__(mcs, cls_name, bases, cls_dict) + + +class MetaTrader(metaclass=BaseMeta): + _account_info: Callable + _copy_rates_from: Callable + _copy_rates_from_pos: Callable + _copy_rates_range: Callable + _copy_ticks_from: Callable + _copy_ticks_range: Callable + _history_deals_get: Callable + _history_deals_total: Callable + _history_orders_get: Callable + _history_orders_total: Callable + _initialize: Callable + _last_error: Callable + _login: Callable + _market_book_add: Callable + _market_book_get: Callable + _market_book_release: Callable + _order_calc_margin: Callable + _order_calc_profit: Callable + _order_check: Callable + _order_send: Callable + _orders_get: Callable + _orders_total: Callable + _positions_get: Callable + _positions_total: Callable + _shutdown: Callable + _symbol_info: Callable + _symbol_info_tick: Callable + _symbol_select: Callable + _symbols_get: Callable + _symbols_total: Callable + _terminal_info: Callable + _version: Callable + + async def __aenter__(self) -> 'MetaTrader': + """ + Async context manager entry point. + Initializes the connection to the MetaTrader terminal. + + Returns: + MetaTrader: An instance of the MetaTrader class. + """ + await self.initialize(**Config().account_info()) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """ + Async context manager exit point. Closes the connection to the MetaTrader terminal. + """ + await self.shutdown() + + async def login(self, login: int, password: str, server: str, timeout: int = 60000) -> bool: + """ + Connects to the MetaTrader terminal using the specified login, password and server. + + Args: + login (int): The trading account number. + password (str): The trading account password. + server (str): The trading server name. + timeout (int): The timeout for the connection in seconds. + + Returns: + bool: True if successful, False otherwise. + """ + return await asyncio.to_thread(self._login, login, password=password, server=server, timeout=timeout) + + async def initialize(self, path: str = "", login: int = 0, password: str = "", server: str = "", + timeout: int | None = None, portable=False) -> bool: + """ + Initializes the connection to the MetaTrader terminal. All parameters are optional. + + Keyword Args: + path (str): The path to the MetaTrader terminal executable. + login (int): The trading account number. + password (str): The trading account password. + server (str): The trading server name. + timeout (int): The timeout for the connection in seconds. + portable (bool): If True, the terminal will be launched in portable mode. + + Returns: + bool: True if successful, False otherwise. + """ + args = (path,) if path else () + kwargs = {key: value for key, value in (('login', login), ('password', password), ('server', server), + ('timeout', timeout), ('portable', portable)) if value} + return await asyncio.to_thread(self._initialize, *args, **kwargs) + + async def shutdown(self) -> None: + """ + Closes the connection to the MetaTrader terminal. + + Returns: + None: None + """ + return await asyncio.to_thread(self._shutdown) + + async def last_error(self) -> tuple[int, str]: + return await asyncio.to_thread(self._last_error) + + async def version(self) -> tuple[int, int, str] | None: + """""" + res = await asyncio.to_thread(self._version) + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining version information.{Error(*err)}') + return res + + async def account_info(self) -> AccountInfo | None: + """""" + res = await asyncio.to_thread(self._account_info) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining account information.{Error(*err)}') + + return res + + async def terminal_info(self) -> TerminalInfo | None: + res = await asyncio.to_thread(self._terminal_info) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining terminal information.{Error(*err)}') + return res + + return res + + async def symbols_total(self) -> int: + return await asyncio.to_thread(self._symbols_total) + + async def symbols_get(self, group: str = "") -> tuple[SymbolInfo] | None: + kwargs = {'group': group} if group else {} + res = await asyncio.to_thread(self._symbols_get, **kwargs) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining symbols.{Error(*err)}') + return res + + return res + + async def symbol_info(self, symbol: str) -> SymbolInfo | None: + res = await asyncio.to_thread(self._symbol_info, symbol) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining information for {symbol}.{Error(*err)}') + return res + + return res + + async def symbol_info_tick(self, symbol: str) -> Tick | None: + res = await asyncio.to_thread(self._symbol_info_tick, symbol) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining tick for {symbol}.{Error(*err)}') + return res + + return res + + async def symbol_select(self, symbol: str, enable: bool) -> bool: + return await asyncio.to_thread(self._symbol_select, symbol, enable) + + async def market_book_add(self, symbol: str) -> bool: + return await asyncio.to_thread(self._market_book_add, symbol) + + async def market_book_get(self, symbol: str) -> tuple[BookInfo] | None: + res = await asyncio.to_thread(self._market_book_get, symbol) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining market depth content for {symbol}.{Error(*err)}') + return res + + return res + + async def market_book_release(self, symbol: str) -> bool: + return await asyncio.to_thread(self._market_book_release, symbol) + + async def copy_rates_from(self, symbol: str, timeframe: TimeFrame, date_from: datetime | int, count: int): + res = await asyncio.to_thread(self._copy_rates_from, symbol, timeframe, date_from, count) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining rates for {symbol}.{Error(*err)}') + return res + + return res + + async def copy_rates_from_pos(self, symbol: str, timeframe: TimeFrame, start_pos: int, count: int): + res = await asyncio.to_thread(self._copy_rates_from_pos, symbol, timeframe, start_pos, count) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining rates for {symbol}.{Error(*err)}') + return res + + return res + + async def copy_rates_range(self, symbol: str, timeframe: TimeFrame, date_from: datetime | int, + date_to: datetime | int): + res = await asyncio.to_thread(self._copy_rates_range, symbol, timeframe, date_from, date_to) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining rates for {symbol}.{Error(*err)}') + return res + + return res + + async def copy_ticks_from(self, symbol: str, date_from: datetime | int, count: int, flags: CopyTicks): + res = await asyncio.to_thread(self._copy_ticks_from, symbol, date_from, count, flags) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining ticks for {symbol}.{Error(*err)}') + return res + + return res + + async def copy_ticks_range(self, symbol: str, date_from: datetime | int, date_to: datetime | int, flags: CopyTicks): + res = await asyncio.to_thread(self._copy_ticks_range, symbol, date_from, date_to, flags) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining ticks for {symbol}.{Error(*err)}') + return res + + return res + + async def orders_total(self) -> int: + return await asyncio.to_thread(self._orders_total) + + async def orders_get(self, group: str = "", ticket: int = 0, symbol: str = "") -> tuple[TradeOrder] | None: + """Get active orders with the ability to filter by symbol or ticket. There are three call options. + Call without parameters. Return active orders on all symbols + + Keyword Args: + symbol (str): Symbol name. Optional named parameter. If a symbol is specified, the ticket parameter is ignored. + + group (str): The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function + returns only active orders meeting a specified criteria for a symbol name. + + ticket (int): Order ticket (ORDER_TICKET). Optional named parameter. + + Returns: + list[TradeOrder]: A list of active trade orders as TradeOrder objects + """ + kwargs = {key: value for key, value in (('group', group), ('ticket', ticket), ('symbol', symbol)) if value} + res = await asyncio.to_thread(self._orders_get, **kwargs) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining orders.{Error(*err)}') + return res + + return res + + async def order_calc_margin(self, action: OrderType, symbol: str, volume: float, price: float) -> float | None: + res = await asyncio.to_thread(self._order_calc_margin, action, symbol, volume, price) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in calculating margin.{Error(*err)}') + return res + + return res + + async def order_calc_profit(self, action: OrderType, symbol: str, volume: float, price_open: float, + price_close: float) -> float | None: + res = await asyncio.to_thread(self._order_calc_profit, action, symbol, volume, price_open, price_close) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in calculating profit.{Error(*err)}') + return res + + return res + + async def order_check(self, request: dict) -> OrderCheckResult: + return await asyncio.to_thread(self._order_check, request) + + async def order_send(self, request: dict) -> OrderSendResult: + return await asyncio.to_thread(self._order_send, request) + + async def positions_total(self) -> int: + return await asyncio.to_thread(self._positions_total) + + async def positions_get(self, group: str = "", ticket: int = 0, symbol: str = "") -> tuple[TradePosition] | None: + kwargs = {key: value for key, value in (('group', group), ('ticket', ticket), ('symbol', symbol)) if value} + res = await asyncio.to_thread(self._positions_get, **kwargs) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in obtaining open positions.{Error(*err)}') + return res + + return res + + async def history_orders_total(self, date_from: datetime | int, date_to: datetime | int) -> int: + return await asyncio.to_thread(self._history_orders_total, date_from, date_to) + + async def history_orders_get(self, date_from: datetime | int = None, date_to: datetime | int = None, group: str = '', + ticket: int = 0, position: int = 0) -> tuple[TradeOrder] | None: + kwargs = {key: value for key, value in (('date_from', date_from), ('date_to', date_to), ('group', group), + ('ticket', ticket), ('position', position)) if value} + res = await asyncio.to_thread(self._history_orders_get, **kwargs) + + if res is None: + err = await self.last_error() + logger.warning(f'Error in getting orders.{Error(*err)}') + return res + + return res + + async def history_deals_total(self, date_from: datetime | int, date_to: datetime | int) -> int: + return await asyncio.to_thread(self._history_deals_total, date_from, date_to) + + async def history_deals_get(self, date_from: datetime | int = None, date_to: datetime | int = None, group: str = '', + ticket: int = 0, position: int = 0) -> tuple[TradeDeal] | None: + kwargs = {key: value for key, value in (('date_from', date_from), ('date_to', date_to), ('group', group), + ('ticket', ticket), ('position', position)) if value} + res = await asyncio.to_thread(self._history_deals_get, **kwargs) + if res is None: + err = await self.last_error() + logger.warning(f'Error in getting deals.{Error(*err)}') + return res + + return res diff --git a/src/aiomql/core/models.py b/src/aiomql/core/models.py new file mode 100644 index 0000000..9dbb043 --- /dev/null +++ b/src/aiomql/core/models.py @@ -0,0 +1,613 @@ +import MetaTrader5 as mt5 + +from .constants import BookType, TradeAction, OrderType, OrderTime, OrderFilling, PositionReason, DealType, DealEntry,\ +DealReason, SymbolChartMode, SymbolTradeMode, SymbolCalcMode, SymbolOptionMode, SymbolOrderGTCMode, SymbolOptionRight,\ +SymbolTradeExecution, SymbolSwapMode, DayOfWeek, AccountTradeMode, AccountStopOutMode, AccountMarginMode, OrderReason + +from .base import Base + +""" +This module contains data models used in this library. +They are used as base classes to other classes having the same properties but with more methods. +""" + + +class AccountInfo(Base): + """Account Information Class. + + Attributes: + login: int + password: str + server: str + trade_mode: AccountTradeMode + balance: float + leverage: float + profit: float + point: float + amount: float = 0 + equity: float + credit: float + margin: float + margin_level: float + margin_free: float + margin_mode: AccountMarginMode + margin_so_mode: AccountStopoutMode + margin_so_call: float + margin_so_so: float + margin_initial: float + margin_maintenance: float + fifo_close: bool + limit_orders: float + currency: str = "USD" + trade_allowed: bool = True + trade_expert: bool = True + currency_digits: int + assets: float + liabilities: float + commission_blocked: float + name: str + company: str + """ + login: int = 0 + password: str = '' + server: str = '' + trade_mode: AccountTradeMode + balance: float + leverage: float + profit: float + point: float + amount: float = 0 + equity: float + credit: float + margin: float + margin_level: float + margin_free: float + margin_mode: AccountMarginMode + margin_so_mode: AccountStopOutMode + margin_so_call: float + margin_so_so: float + margin_initial: float + margin_maintenance: float + fifo_close: bool + limit_orders: float + currency: str = "USD" + trade_allowed: bool = True + trade_expert: bool = True + currency_digits: int + assets: float + liabilities: float + commission_blocked: float + name: str + company: str + + +class TerminalInfo(Base): + """Terminal information class. Holds information about the terminal. + + Attributes: + community_account: bool + community_connection: bool + connected: bool + dlls_allowed: bool + trade_allowed: bool + tradeapi_disabled: bool + email_enabled: bool + ftp_enabled: bool + notifications_enabled: bool + mqid: bool + build: int + maxbars: int + codepage: int + ping_last: int + community_balance: float + retransmission: float + company: str + name: str + language: str + path: str + data_path: str + commondata_path: str + """ + community_account: bool + community_connection: bool + connected: bool + dlls_allowed: bool + trade_allowed: bool + tradeapi_disabled: bool + email_enabled: bool + ftp_enabled: bool + notifications_enabled: bool + mqid: bool + build: int + maxbars: int + codepage: int + ping_last: int + community_balance: float + retransmission: float + company: str + name: str + language: str + path: str + data_path: str + commondata_path: str + + +class SymbolInfo(Base): + """Symbol Information Class. Symbols are financial instruments available for trading in the MetaTrader 5 terminal. + + Attributes: + name: str + custom: bool + chart_mode: SymbolChartMode + select: bool + visible: bool + session_deals: int + session_buy_orders: int + session_sell_orders: int + volume: float + volumehigh: float + volumelow: float + time: int + digits: int + spread: float + spread_float: bool + ticks_bookdepth: int + trade_calc_mode: SymbolCalcMode + trade_mode: SymbolTradeMode + start_time: int + expiration_time: int + trade_stops_level: int + trade_freeze_level: int + trade_exemode: SymbolTradeExecution + swap_mode: SymbolSwapMode + swap_rollover3days: DayOfWeek + margin_hedged_use_leg: bool + expiration_mode: int + filling_mode: int + order_mode: int + order_gtc_mode: SymbolOrderGTCMode + option_mode: SymbolOptionMode + option_right: SymbolOptionRight + bid: float + bidhigh: float + bidlow: float + ask: float + askhigh: float + asklow: float + last: float + lasthigh: float + lastlow: float + volume_real: float + volumehigh_real: float + volumelow_real: float + option_strike: float + point: float + trade_tick_value: float + trade_tick_value_profit: float + trade_tick_value_loss: float + trade_tick_size: float + trade_contract_size: float + trade_accrued_interest: float + trade_face_value: float + trade_liquidity_rate: float + volume_min: float + volume_max: float + volume_step: float + volume_limit: float + swap_long: float + swap_short: float + margin_initial: float + margin_maintenance: float + session_volume: float + session_turnover: float + session_interest: float + session_buy_orders_volume: float + session_sell_orders_volume: float + session_open: float + session_close: float + session_aw: float + session_price_settlement: float + session_price_limit_min: float + session_price_limit_max: float + margin_hedged: float + price_change: float + price_volatility: float + price_theoretical: float + price_greeks_delta: float + price_greeks_theta: float + price_greeks_gamma: float + price_greeks_vega: float + price_greeks_rho: float + price_greeks_omega: float + price_sensitivity: float + basis: str + category: str + currency_base: str + currency_profit: str + currency_margin: Any + bank: str + description: str + exchange: str + formula: Any + isin: Any + name: str + page: str + path: str + """ + custom: bool + chart_mode: SymbolChartMode + select: bool + visible: bool + session_deals: int + session_buy_orders: int + session_sell_orders: int + volume: float + volumehigh: float + volumelow: float + time: int + digits: int + spread: float + spread_float: bool + ticks_bookdepth: int + trade_calc_mode: SymbolCalcMode + trade_mode: SymbolTradeMode + start_time: int + expiration_time: int + trade_stops_level: int + trade_freeze_level: int + trade_exemode: SymbolTradeExecution + swap_mode: SymbolSwapMode + swap_rollover3days: DayOfWeek + margin_hedged_use_leg: bool + expiration_mode: int + filling_mode: int + order_mode: int + order_gtc_mode: SymbolOrderGTCMode + option_mode: SymbolOptionMode + option_right: SymbolOptionRight + bid: float + bidhigh: float + bidlow: float + ask: float + askhigh: float + asklow: float + last: float + lasthigh: float + lastlow: float + volume_real: float + volumehigh_real: float + volumelow_real: float + option_strike: float + point: float + trade_tick_value: float + trade_tick_value_profit: float + trade_tick_value_loss: float + trade_tick_size: float + trade_contract_size: float + trade_accrued_interest: float + trade_face_value: float + trade_liquidity_rate: float + volume_min: float + volume_max: float + volume_step: float + volume_limit: float + swap_long: float + swap_short: float + margin_initial: float + margin_maintenance: float + session_volume: float + session_turnover: float + session_interest: float + session_buy_orders_volume: float + session_sell_orders_volume: float + session_open: float + session_close: float + session_aw: float + session_price_settlement: float + session_price_limit_min: float + session_price_limit_max: float + margin_hedged: float + price_change: float + price_volatility: float + price_theoretical: float + price_greeks_delta: float + price_greeks_theta: float + price_greeks_gamma: float + price_greeks_vega: float + price_greeks_rho: float + price_greeks_omega: float + price_sensitivity: float + basis: str + category: str + currency_base: str + currency_profit: str + currency_margin: str + bank: str + description: str + exchange: str + formula: str + isin: str + name: str + page: str + path: str + + def __init__(self, **kwargs): + if 'name' not in kwargs: + raise AttributeError('Symbol Object Must be initialized with a name') + self.name = kwargs.pop('name') + super().__init__(**kwargs) + + def __repr__(self): + return self.name + + def __str__(self): + return self.name + + def __eq__(self, other: "SymbolInfo"): + return self.name == other.name + + def __hash__(self): + return hash(self.name) + + +class BookInfo(Base): + """Book Information Class. + + Attributes: + type: BookType + price: float + volume: float + volume_dbl: float + """ + type: BookType + price: float + volume: float + volume_dbl: float + + +class TradeOrder(Base): + """Trade Order Class. + + Attributes: + ticket: int + time_setup: int + time_setup_msc: int + time_expiration: int + time_done: int + time_done_msc: int + type: OrderType + type_time: OrderTime + type_filling: OrderFilling + state: int + magic: int + position_id: int + position_by_id: int + reason: OrderReason + volume_current: float + volume_initial: float + price_open: float + sl: float + tp: float + price_current: float + price_stoplimit: float + symbol: str + comment: str + external_id: str + """ + ticket: int + time_setup: int + time_setup_msc: int + time_expiration: int + time_done: int + time_done_msc: int + type: OrderType + type_time: OrderTime + type_filling: OrderFilling + state: int + magic: int + position_id: int + position_by_id: int + reason: OrderReason + volume_current: float + volume_initial: float + price_open: float + sl: float + tp: float + price_current: float + price_stoplimit: float + symbol: str + comment: str + external_id: str + + +class TradeRequest(Base): + """Trade Request Class. + + Attributes: + action: TradeAction + type: OrderType + order: int + symbol: str + volume: float + sl: float + tp: float + price: float + deviation: float + stop_limit: float + type_time: OrderTime + type_filling: OrderFilling + expiration: int + position: int + position_by: int + comment: str + magic: int + deviation: int + comment: str + """ + action: TradeAction + type: OrderType + order: int + symbol: str + volume: float + sl: float + tp: float + price: float + deviation: float + stop_limit: float + type_time: OrderTime + type_filling: OrderFilling + expiration: int + position: int + position_by: int + comment: str + magic: int + deviation: int + comment: str + + +class OrderCheckResult(Base): + """Order Check Result + + Attributes: + retcode: int + balance: float + equity: float + profit: float + margin: float + margin_free: float + margin_level: float + comment: str + request: TradeRequest + """ + retcode: int + balance: float + equity: float + profit: float + margin: float + margin_free: float + margin_level: float + comment: str + request: mt5.TradeRequest + + +class OrderSendResult(Base): + """Order Send Result + + Attributes: + retcode: int + deal: int + order: int + volume: float + price: float + bid: float + ask: float + comment: str + request: TradeRequest + request_id: int + retcode_external: int + profit: float + """ + retcode: int + deal: int + order: int + volume: float + price: float + bid: float + ask: float + comment: str + request: mt5.TradeRequest + request_id: int + retcode_external: int + profit: float + + +class TradePosition(Base): + """Trade Position + + Attributes: + ticket: int + time: int + time_msc: int + time_update: int + time_update_msc: int + type: OrderType + magic: float + identifier: int + reason: PositionReason + volume: float + price_open: float + sl: float + tp: float + price_current: float + swap: float + profit: float + symbol: str + comment: str + external_id: str + """ + ticket: int + time: int + time_msc: int + time_update: int + time_update_msc: int + type: OrderType + magic: float + identifier: int + reason: PositionReason + volume: float + price_open: float + sl: float + tp: float + price_current: float + swap: float + profit: float + symbol: str + comment: str + external_id: str + + +class TradeDeal(Base): + """Trade Deal + + Attributes: + ticket: int + order: int + time: int + time_msc: int + type: DealType + entry: DealEntry + magic: int + position_id: int + reason: DealReason + volume: float + price: float + commission: float + swap: float + profit: float + fee: float + sl: float + tp: float + symbol: str + comment: str + external_id: str + """ + ticket: int + order: int + time: int + time_msc: int + type: DealType + entry: DealEntry + magic: int + position_id: int + reason: DealReason + volume: float + price: float + commission: float + swap: float + profit: float + fee: float + sl: float + tp: float + symbol: str + comment: str + external_id: str diff --git a/src/aiomql/executor.py b/src/aiomql/executor.py new file mode 100644 index 0000000..a5c57d4 --- /dev/null +++ b/src/aiomql/executor.py @@ -0,0 +1,87 @@ +import asyncio +from concurrent.futures import ThreadPoolExecutor +from typing import Sequence, Coroutine, Callable + +from .strategy import Strategy + + +class Executor: + """Executor class for running multiple strategies on multiple symbols concurrently. + + Attributes: + executor (ThreadPoolExecutor): The executor object. + workers (list): List of strategies. + coros (dict[Coroutine, dict]): A dictionary of coroutines to run in the executor + funcs (dict[Callable, dict]): A dictionary of functions to run in the executor + + """ + + def __init__(self, bot=None): + self.executor = ThreadPoolExecutor + self.workers: list[type(Strategy)] = [] + self.coros: dict[Coroutine|Callable: dict] = {} + self.funcs: dict[Callable: dict] = {} + self.bot = bot + + def add_func(self, func, kwargs): + self.funcs[func] = kwargs | {'bot': self.bot} + + def add_coro(self, coro, kwargs): + self.coros[coro] = kwargs | {'bot': self.bot} + + def add_workers(self, strategies: Sequence[type(Strategy)]): + """Add multiple strategies at once + + Args: + strategies (Sequence[Strategy]): A sequence of strategies. + """ + self.workers.extend(strategies) + + def remove_workers(self): + """Removes any worker running on a symbol not successfully initialized. + """ + self.workers = [worker for worker in self.workers if worker.symbol in self.bot.symbols] + + def add_worker(self, strategy: type(Strategy)): + """Add a strategy instance to the list of workers + + Args: + strategy (Strategy): A strategy object + """ + self.workers.append(strategy) + + @staticmethod + def trade(strategy: type(Strategy)): + """Wraps the coroutine trade method of each strategy with 'asyncio.run'. + + Args: + strategy (Strategy): A strategy object + """ + asyncio.run(strategy.trade()) + + def run(self, func, kwargs: dict): + """ + Run a coroutine function + + Args: + func: The coroutine. A variadic function. + kwargs: A dictionary of keyword arguments for the function + """ + asyncio.run(func(**kwargs)) + + async def execute(self, workers: int = 0): + """Run the strategies with a threadpool executor. + + Args: + workers: Number of workers to use in executor pool. Defaults to zero which uses all workers. + + Notes: + No matter the number specified, the executor will always use a minimum of 5 workers. + """ + workers = workers or sum([len(self.workers), len(self.funcs), len(self.coros)]) + workers = max(workers, 5) + loop = asyncio.get_running_loop() + with self.executor(max_workers=workers) as executor: + [loop.run_in_executor(executor, self.trade, worker) for worker in self.workers] + [loop.run_in_executor(executor, self.run, coro, kwargs) for coro, kwargs in self.coros.items()] + [loop.run_in_executor(executor, func, kwargs) for func, kwargs in self.funcs.items()] diff --git a/src/aiomql/history.py b/src/aiomql/history.py new file mode 100644 index 0000000..cafea13 --- /dev/null +++ b/src/aiomql/history.py @@ -0,0 +1,119 @@ +import asyncio +from datetime import datetime +from logging import getLogger + +from .core.config import Config +from .core.meta_trader import MetaTrader +from .core.models import TradeDeal, TradeOrder + +logger = getLogger(__name__) + + +class History: + """The history class handles completed trade deals and trade orders in the trading history of an account. + + Attributes: + deals (list[TradeDeal]): Iterable of trade deals + orders (list[TradeOrder]): Iterable of trade orders + total_deals: Total number of deals + total_orders (int): Total number orders + group (str): Filter for selecting history by symbols. + ticket (int): Filter for selecting history by ticket number + position (int): Filter for selecting history deals by position + initialized (bool): check if initial request has been sent to the terminal to get history. + mt5 (MetaTrader): MetaTrader instance + config (Config): Config instance + """ + mt5: MetaTrader = MetaTrader() + config: Config = Config() + + def __init__(self, *, date_from: datetime | float = None, date_to: datetime | float = None, + group: str = "", ticket: int = 0, position: int = 0): + """ + Args: + date_from (datetime, float): Date the orders are requested from. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' + + date_to (datetime, float): Date up to which the orders are requested. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" + + group (str): Filter for selecting history by symbols. + ticket (int): Filter for selecting history by ticket number + position (int): Filter for selecting history deals by position + """ + self.date_from = date_from + self.date_to = date_to + self.group = group + self.ticket = ticket + self.position = position + self.deals: list[TradeDeal] = [] + self.orders: list[TradeOrder] = [] + self.total_deals: int = 0 + self.total_orders: int = 0 + self.initialized = False + + async def init(self, deals=True, orders=True) -> bool: + """Get history deals and orders + + Keyword Args: + deals (bool): If true get history deals during initial request to terminal + orders (bool): If true get history orders during initial request to terminal + + Returns: + bool: True if all requests were successful else False + """ + tasks = [] + tasks.append(self.get_deals()) if deals else ... + tasks.append(self.get_orders()) if orders else ... + res = await asyncio.gather(*tasks) + self.initialized = all(res) + return self.initialized + + async def get_deals(self) -> list[TradeDeal]: + """Get deals from trading history using the parameters set in the constructor. + + Returns: + list[TradeDeal]: A list of trade deals + """ + deals = await self.mt5.history_deals_get(date_from=self.date_from, date_to=self.date_to, position=self.position, + group=self.group, ticket=self.ticket) + if deals is not None: + self.deals = [TradeDeal(**deal._asdict()) for deal in deals] if deals else [] + self.total_deals = len(self.deals) + return self.deals + + return self.deals + + async def deals_total(self) -> int: + """Get total number of deals within the specified period in the constructor. + + Returns: + int: Total number of Deals + """ + self.total_deals = await self.mt5.history_deals_total(self.date_from, self.date_to) + return self.total_deals + + async def get_orders(self) -> list[TradeOrder]: + """Get orders from trading history using the parameters set in the constructor. + + Returns: + list[TradeOrder]: A list of trade orders + """ + + orders = await self.mt5.history_orders_get(date_from=self.date_from, date_to=self.date_to, group=self.group, + position=self.position, ticket=self.ticket) + if orders is None: + return self.orders + + self.orders = [TradeOrder(**order._asdict()) for order in orders] + self.total_orders = len(self.orders) + return self.orders + + async def orders_total(self) -> int: + """Get total number of orders within the specified period in the constructor. + + Returns: + int: Total number of orders + """ + self.total_orders = await self.mt5.history_orders_total(self.date_from, self.date_to) + return self.total_orders diff --git a/src/aiomql/order.py b/src/aiomql/order.py new file mode 100644 index 0000000..27e4358 --- /dev/null +++ b/src/aiomql/order.py @@ -0,0 +1,112 @@ +"""Order Class""" +from logging import getLogger + +from .core.models import TradeRequest, OrderSendResult, OrderCheckResult, TradeOrder +from .core.constants import TradeAction, OrderTime, OrderFilling +from .core.exceptions import SymbolError, OrderError +from .symbol import Symbol +logger = getLogger(__name__) + + +class Order(TradeRequest): + """Trade order related functions and properties. Subclass of TradeRequest.""" + + def __init__(self, **kwargs): + """Initialize the order object with keyword arguments, symbol must be provided. + Provide default values for action, type_time and type_filling if not provided. + + Args: + **kwargs: Keyword arguments must match the attributes of TradeRequest as well as the attributes of + Order class as specified in the annotations in the class definition. + + Default Values: + action (TradeAction.DEAL): Trade action + type_time (OrderTime.DAY): Order time + type_filling (OrderFilling.FOK): Order filling + + Raises: + SymbolError: If symbol is not provided + """ + if 'symbol' not in kwargs: + raise SymbolError('symbol is required') + sym = kwargs.pop('symbol') + self.symbol = sym.name if isinstance(sym, Symbol) else sym + self.action = kwargs.pop('action', TradeAction.DEAL) + self.type_time = kwargs.pop('type_time', OrderTime.DAY) + self.type_filling = kwargs.pop('type_filling', OrderFilling.FOK) + super().__init__(**kwargs) + + async def orders_total(self): + """Get the number of active orders. + + Returns: + (int): total number of active orders + """ + return await self.mt5.orders_total() + + async def orders(self) -> tuple[TradeOrder]: + """Get the list of active orders for the current symbol. + + Returns: + tuple[TradeOrder]: A Tuple of active trade orders as TradeOrder objects + """ + orders = await self.mt5.orders_get(symbol=self.symbol) + orders = (TradeOrder(**order._asdict()) for order in orders) + return tuple(orders) + + async def check(self) -> OrderCheckResult: + """Check funds sufficiency for performing a required trading operation and the possibility to execute it at + + Returns: + OrderCheckResult: An OrderCheckResult object + + Raises: + OrderError: If not successful + """ + res = await self.mt5.order_check(self.dict) + if res is None: + raise OrderError(f'Failed to check order {self.symbol} {self.type} {self.volume} {self.price} {res}') + return OrderCheckResult(**res._asdict()) + + async def send(self) -> OrderSendResult: + """Send a request to perform a trading operation from the terminal to the trade server. + + Returns: + OrderSendResult: An OrderSendResult object + + Raises: + OrderError: If not successful + """ + res = await self.mt5.order_send(self.dict) + if res is None: + raise OrderError(f'Failed to send order {self.symbol} {self.type} {self.volume} {self.price} {res}') + return OrderSendResult(**res._asdict()) + + async def calc_margin(self) -> float: + """Return the required margin in the account currency to perform a specified trading operation. + + Returns: + float: Returns float value if successful + + Raises: + OrderError: If not successful + """ + res = await self.mt5.order_calc_margin(self.type, self.symbol, self.volume, self.price) + if res is None: + raise OrderError(f'Failed to calculate margin for {self.symbol} {self.type} {self.volume} {self.price} {res}') + return res + + async def calc_profit(self) -> float: + """Return profit in the account currency for a specified trading operation. + + Returns: + float: Returns float value if successful + + Raises: + OrderError: If not successful + """ + res = await self.mt5.order_calc_profit(self.type, self.symbol, self.volume, self.price, self.tp) + if res is None: + raise OrderError( + f'Failed to calculate profit for {self.symbol} {self.type} {self.volume} {self.price} {self.tp}') + return res diff --git a/src/aiomql/positions.py b/src/aiomql/positions.py new file mode 100644 index 0000000..34f8d6f --- /dev/null +++ b/src/aiomql/positions.py @@ -0,0 +1,82 @@ +"""Handle Open positions.""" +import asyncio +from logging import getLogger + +from .core import MetaTrader, TradePosition, TradeAction, OrderType +from .order import Order + +logger = getLogger(__name__) + +class Positions: + """Get Open Positions. + + Attributes: + symbol (str): Financial instrument name. + group (str): The filter for arranging a group of necessary symbols. Optional named parameter. + If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. + ticket (int): Position ticket. + mt5 (MetaTrader): MetaTrader instance. + """ + mt5: MetaTrader = MetaTrader() + + def __init__(self, *, symbol: str = "", group: str = "", ticket: int = 0): + """Get Open Positions. + + Keyword Args: + symbol (str): Financial instrument name. + group (str): The filter for arranging a group of necessary symbols. Optional named parameter. If the group + is specified, the function returns only positions meeting a specified criteria for a symbol name. + ticket (int): Position ticket + + """ + self.symbol = symbol + self.group = group + self.ticket = ticket + + async def positions_total(self) -> int: + """Get the number of open positions. + + Returns: + int: Return total number of open positions + """ + return await self.mt5.positions_total() + + async def positions_get(self, symbol: str = '', group: str = '', ticket: int = 0): + """Get open positions with the ability to filter by symbol or ticket. + + Keyword Args: + symbol (str): Financial instrument name. + group (str): The filter for arranging a group of necessary symbols. Optional named parameter. If the group + is specified, the function returns only positions meeting a specified criteria for a symbol name. + ticket (int): Position ticket + + Returns: + list[TradePosition]: A list of open trade positions + """ + symbol = symbol or self.symbol + group = group or self.group + ticket = ticket or self.ticket + positions = await self.mt5.positions_get(group=group, symbol=symbol, ticket=ticket) + if not positions: + return [] + return [TradePosition(**pos._asdict()) for pos in positions] + + async def close_all(self, symbol: str = '', group: str = '') -> int: + """Close all open positions for the trading account. + + Returns: + int: Return number of positions closed. + """ + orders = [Order(action=TradeAction.DEAL, price=pos.price_current, position=pos.ticket, + type=OrderType(pos.type).opposite, + **pos.get_dict(include={'symbol', 'volume'})) for pos in + (await self.positions_get(symbol=symbol, group=group))] + + results = await asyncio.gather(*[order.send() for order in orders], return_exceptions=True) + amount_closed = len([res for res in results if res.retcode == 10009]) + pos = await self.positions_total() + if pos > 0: + logger.warning(f'Failed to close {pos} positions') + else: + logger.info('All positions closed') + return amount_closed diff --git a/src/aiomql/ram.py b/src/aiomql/ram.py new file mode 100644 index 0000000..04dc4c8 --- /dev/null +++ b/src/aiomql/ram.py @@ -0,0 +1,64 @@ +"""Risk Assessment and Management""" +from .account import Account +from .symbol import Symbol + + +class RAM: + account: Account = Account() + risk_to_reward: float + risk: float + amount: float + pips: float + volume: float + + def __init__(self, **kwargs): + """Risk Assessment and Management. All provided keyword arguments are set as attributes. + + Args: + kwargs (Dict): Keyword arguments. + + Defaults: + risk_to_reward (float): Risk to reward ratio 1 + risk (float): Percentage of account balance to risk per trade 0.01 # 1% + amount (float): Amount to risk per trade in terms of account currency 0 + pips (float): Target pips 0 + volume (float): Volume to trade 0 + """ + self.risk_to_reward = kwargs.pop('risk_to_reward', 1) + self.risk = kwargs.pop('risk', 0.01) + self.amount = kwargs.pop('amount', 0) + self.pips = kwargs.pop('pips', 0) + self.volume = kwargs.pop('volume', 0) + [setattr(self, key, value) for key, value in kwargs.items()] + + async def get_amount(self, risk: float = 0) -> float: + """Calculate the amount to risk per trade as a percentage of free margin. + + Keyword Args: + risk (float): Percentage of account balance to risk per trade. Defaults to zero. + + Returns: + float: Amount to risk per trade + """ + await self.account.refresh() + risk = risk or self.risk + return self.account.margin_free * risk + + async def get_volume(self, *, symbol: Symbol, pips: float = 0, amount: float = 0) -> float: + """Calculate the volume to trade. if pips is not provided, the pips attribute is used. + If the amount attribute or amount argument is zero, the amount is calculated using the get_amount method based on the risk. + + Args: + symbol (Symbol): Financial instrument + + Keyword Args: + pips (float): Target pips. Defaults to zero. + amount (float): Amount to risk per trade. Defaults to zero. + + Returns: + float: Volume to trade + """ + pips = pips or self.pips + amount = amount or self.amount or await self.get_amount() + return await symbol.compute_volume(amount=amount, pips=pips) + diff --git a/src/aiomql/records.py b/src/aiomql/records.py new file mode 100644 index 0000000..3aabca7 --- /dev/null +++ b/src/aiomql/records.py @@ -0,0 +1,79 @@ +"""This module contains the Records class, which is used to read and update trade records from csv files.""" + +import asyncio +from pathlib import Path +import csv + +from .history import History +from .core import Config + + +class Records: + """This utility class read trade records from csv files, and update them based on their closing positions. + + Attributes: + config: Config object + records_dir(Path): Path to directory containing record of placed trades, If not given takes the default + from the config + """ + config: Config = Config() + + def __init__(self, records_dir: Path = ''): + """Initialize the Records class. The main method of this class is update_records which you should call to update + all the records specified in the records_dir. + + Keyword Args: + records_dir (Path): Path to directory containing record of placed trades. + """ + self.records_dir = records_dir or self.config.records_dir + + async def get_records(self): + """Get trade records from records_dir folder + + Yields: + files: Trade record files + """ + for file in self.records_dir.iterdir(): + if file.is_file() and file.name.endswith('.csv'): + yield file + + async def read_update(self, file: Path): + """Read and update trade records + + Args: + file: Trade record file + """ + fr = open(file, mode='r', newline='') + reader = csv.DictReader(fr) + rows = [row for row in reader] + rows = await self.update_rows(rows) + fr.close() + fw = open(file, mode='w', newline='') + writer = csv.DictWriter(fw, fieldnames=reader.fieldnames) + writer.writeheader() + writer.writerows(rows) + fw.close() + + async def update_rows(self, rows: list[dict]) -> list[dict]: + """Update the rows of entered trades in the csv file with the actual profit. + + Args: + rows: A list of dictionaries from the dictionary writer object of the csv file. + + Returns: + list[dict]: A list of dictionaries with the actual profit and win status. + """ + tasks = [History(position=int(row['order'])).get_deals() for row in rows] + deals = [deal for deals in await asyncio.gather(*tasks) for deal in deals] + deals = {str(deal.position_id): deal.profit for deal in deals if deal.order != deal.position_id} + [row.update(actual_profit=(profit := deals[order]), win=profit > 0) for row in rows if (order := row['order']) in deals] + return rows + + async def update_records(self): + """Update trade records in the records_dir folder.""" + records = [self.read_update(record) async for record in self.get_records()] + await asyncio.gather(*records) + + async def update_record(self, file: Path | str): + """Update a single trade record file.""" + await self.read_update(file) diff --git a/src/aiomql/result.py b/src/aiomql/result.py new file mode 100644 index 0000000..b8922f6 --- /dev/null +++ b/src/aiomql/result.py @@ -0,0 +1,58 @@ +import asyncio +import csv +from logging import getLogger + +from .core import Config +from .core.models import OrderSendResult + +logger = getLogger(__name__) + + +class Result: + """A base class for handling trade results and strategy parameters for record keeping and reference purpose. + The data property must be implemented in the subclass + + Attributes: + config (Config): The configuration object + name: Any desired name for the result file object + """ + config = Config() + data: dict + + def __init__(self, result: OrderSendResult, parameters: dict = None, name: str = ''): + """ + Prepare result data + Args: + result: + parameters: + name: + """ + self.parameters = parameters or {} + self.result = result + self.name = name or parameters.get('name', 'Strategy') + + def get_data(self) -> dict: + result = self.result.get_dict(exclude={'retcode', 'retcode_external', 'request_id', 'request'}) + return self.parameters | result | {'actual_profit': 0, 'closed': False, 'win': False} + + def to_csv(self): + """Record trade results and associated parameters as a csv file + """ + try: + self.data = self.get_data() + file = self.config.records_dir / f"{self.name}.csv" + exists = file.exists() + with open(file, 'a', newline='') as fh: + writer = csv.DictWriter(fh, fieldnames=sorted(list(self.data.keys())), extrasaction='ignore', restval=None) + if not exists: + writer.writeheader() + writer.writerow(self.data) + except Exception as err: + logger.error(f'Error: {err}. Unable to save trade results') + + async def save_csv(self): + """Save trade results and associated parameters as a csv file in a separate thread + """ + # exe = self.config.executor + loop = asyncio.get_running_loop() + loop.run_in_executor(None, self.to_csv) diff --git a/src/aiomql/sessions.py b/src/aiomql/sessions.py new file mode 100644 index 0000000..848d31a --- /dev/null +++ b/src/aiomql/sessions.py @@ -0,0 +1,60 @@ +from datetime import time, timedelta, datetime +from asyncio import sleep + + +class Session: + + def __init__(self, start: int | time, end: int | time): + self.start = start if isinstance(start, time) else time(hour=start) + self.end = end if isinstance(end, time) else time(hour=end) + self.__from = self.delta(self.start) + self.__to = self.delta(self.end) + + def __contains__(self, item: time): + return self.start <= item < self.end + + def delta(self, obj): + return timedelta(hours=obj.hour, minutes=obj.minute, seconds=obj.second, microseconds=obj.microsecond) + + def __len__(self): + return (self.__to - self.__from).seconds + + def until(self): + now = lambda: datetime.utcnow().time() + return (self.__from - self.delta(now())).seconds + + +class Sessions: + def __init__(self, *sessions: Session): + self.sessions = list(sessions) + self.sessions.sort(key=lambda x: x.start) + + def find(self, obj): + for session in self.sessions: + if obj in session: + return session + return None + + def find_next(self, obj): + for session in self.sessions: + if obj < session.start: + return session + return self.sessions[-1] + + def __contains__(self, item: time): + return True if self.find(item) is not None else False + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + pass + + async def check(self): + now = datetime.utcnow().time() + if now in self: + return + next_session = self.find_next(now) + secs = next_session.until() + print(f'sleeping for {secs} seconds') + await sleep(secs) diff --git a/src/aiomql/strategy.py b/src/aiomql/strategy.py new file mode 100644 index 0000000..6a1c372 --- /dev/null +++ b/src/aiomql/strategy.py @@ -0,0 +1,73 @@ +"""The base class for creating strategies.""" +import asyncio +from time import time +from typing import TypeVar +from abc import ABC, abstractmethod +from datetime import time as dtime + +from .core.meta_trader import MetaTrader +from .symbol import Symbol as _Symbol +from .account import Account +from .core import Config +from .sessions import Sessions, Session + +Symbol = TypeVar('Symbol', bound=_Symbol) + + +class Strategy(ABC): + """The base class for creating strategies. + + Attributes: + symbol (Symbol): The Financial Instrument as a Symbol Object + parameters (Dict): A dictionary of parameters for the strategy. + + Class Attributes: + name (str): A name for the strategy. + account (Account): Account instance. + mt5 (MetaTrader): MetaTrader instance. + config (Config): Config instance. + + Notes: + Define the name of a strategy as a class attribute. If not provided, the class name will be used as the name. + """ + name: str = '' + account = Account() + mt5: MetaTrader() + config = Config() + + def __init__(self, *, symbol: Symbol, params: dict = None, session: Session): + """Initiate the parameters dict and add name and symbol fields. + Use class name as strategy name if name is not provided + + Args: + symbol (Symbol): The Financial instrument + params (Dict): Trading strategy parameters + """ + self.symbol = symbol + self.parameters = params.copy() if isinstance(params, dict) else {} + self.parameters['symbol'] = symbol.name + self.parameters['name'] = self.name or self.__class__.__name__ + self.session = session or Sessions(Session(8, 13)) + + def __repr__(self): + return f"{self.name}({self.symbol!r})" + + @staticmethod + async def sleep(secs: float): + """Sleep for the needed amount of seconds in between requests to the terminal. + computes the accurate amount of time needed to sleep ensuring that the next request is made at the start of + a new bar and making cooperative multitasking possible. + + Args: + secs (float): The time in seconds. Usually the timeframe you are trading on. + """ + mod = time() % secs + secs = secs - mod if mod != 0 else mod + await asyncio.sleep(secs + 0.1) + + + @abstractmethod + async def trade(self): + """Place trades using this method. This is the main method of the strategy. + It will be called by the strategy runner. + """ diff --git a/src/aiomql/symbol.py b/src/aiomql/symbol.py new file mode 100644 index 0000000..84754d7 --- /dev/null +++ b/src/aiomql/symbol.py @@ -0,0 +1,301 @@ +"""Symbol class for handling a financial instrument.""" +from datetime import datetime +from logging import getLogger + +from .core.constants import TimeFrame, CopyTicks +from .core.models import SymbolInfo, BookInfo +from .ticks import Tick +from .account import Account +from .candle import Candles +from .ticks import Ticks + +logger = getLogger(__name__) + + +class Symbol(SymbolInfo): + """Main class for handling a financial instrument. A subclass of SymbolInfo and Base it has attributes and methods + for working with a financial instrument. + + Attributes: + tick (Tick): Price tick object for instrument + account: An instance of the current trading account + + Notes: + Full properties are on the SymbolInfo Object. + Make sure Symbol is always initialized with a name argument + """ + tick: Tick + account = Account() + + @property + def pip(self): + """Returns the pip value of the symbol. This is ten times the point value for forex symbols. + + Returns: + float: The pip value of the symbol. + """ + return self.point * 10 + + async def info_tick(self, *, name: str = "") -> Tick: + """Get the current price tick of a financial instrument. + + Args: + name: if name is supplied get price tick of that financial instrument + + Returns: + Tick: Return a Tick Object + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + tick = await self.mt5.symbol_info_tick(name or self.name) + if tick is None: + raise ValueError(f'Could not get tick for {name or self.name}') + tick = Tick(**tick._asdict()) + setattr(self, 'tick', tick) if not name else ... + return tick + + async def symbol_select(self, *, enable: bool = True) -> bool: + """Select a symbol in the MarketWatch window or remove a symbol from the window. + Update the select property + + Args: + enable (bool): Switch. Optional unnamed parameter. If 'false', a symbol should be removed from + the MarketWatch window. + + Returns: + bool: True if successful, otherwise – False. + """ + self.select = await self.mt5.symbol_select(self.name, enable) + return self.select + + async def info(self) -> SymbolInfo: + """Get data on the specified financial instrument and update the symbol object properties + + Returns: + (SymbolInfo): SymbolInfo if successful + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + + info = await self.mt5.symbol_info(self.name) + if info: + self.set_attributes(**info._asdict()) + return SymbolInfo(**info._asdict()) + raise ValueError(f'Could not get info for {self.name}') + + async def init(self) -> bool: + """Initialized the symbol by pulling properties from the terminal + + Returns: + bool: Returns True if symbol info was successful initialized + """ + try: + if await self.symbol_select(): + await self.book_add() + await self.info() + return True + logger.warning(f'Unable to initialized symbol {self}') + return False + except Exception as err: + self.select = False + logger.warning(err) + return False + + async def book_add(self) -> bool: + """Subscribes the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. + If the symbol is not in the list of instruments for the market, This method will return False + + Returns: + bool: True if successful, otherwise – False. + """ + return await self.mt5.market_book_add(self.name) + + async def book_get(self) -> tuple[BookInfo]: + """Returns a tuple of BookInfo featuring Market Depth entries for the specified symbol. + + Returns: + tuple[BookInfo]: Returns the Market Depth contents as a tuples of BookInfo Objects + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + infos = await self.mt5.market_book_get(self.name) + if infos is None: + raise ValueError(f'Could not get book info for {self.name}') + book_infos = (BookInfo(**info._asdict()) for info in infos) + return tuple(book_infos) + + async def book_release(self) -> bool: + """Cancels subscription of the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. + + Returns: + bool: True if successful, otherwise – False. + """ + return await self.mt5.market_book_release(self.name) + + async def compute_volume(self, *, amount: float, pips: float, use_minimum: bool = True) -> float: + """Computes the volume of a trade based on the amount and the number of pips to target. + This is a dummy method that returns the minimum volume of the symbol. It is meant to be overridden by a subclass + Checkout Forex Symbol implementation in src\aiomql\lib\ForexSymbol.py + + Args: + amount (float): Amount to risk in the trade + pips (float): Number of pips to target + + Keyword Args: + use_minimum (bool): If True, the minimum volume is returned if the computed volume is less than the minimum volume. + + Returns: + float: Returns the volume of the trade + """ + return self.volume_min + + async def currency_conversion(self, *, amount: float, base: str, quote: str) -> float: + """Convert from one currency to the other. + + Args: + amount: amount to convert given in terms of the quote currency + base: The base currency of the pair + quote: The quote currency of the pair + + Returns: + float: Amount in terms of the base currency or None if it failed to convert + + Raises: + ValueError: If conversion is impossible + """ + try: + pair = f'{base}{quote}' + if self.account.has_symbol(pair): + tick = await self.info_tick(name=pair) + if tick is not None: + return amount / tick.ask + + pair = f'{quote}{base}' + if self.account.has_symbol(pair): + tick = await self.info_tick(name=pair) + if tick is not None: + amount = amount * tick.bid + return amount + except Exception as err: + logger.warning(f'Currency conversion failed: Unable to convert {amount} in {quote} to {base}') + raise ValueError(f'Currency Conversion Failed: {err}') + else: + logger.warning(f'Currency conversion failed: Unable to convert {amount} in {quote} to {base}') + + async def copy_rates_from(self, *, timeframe: TimeFrame, date_from: datetime | int, count: int = 500) -> Candles: + """ + Get bars from the MetaTrader 5 terminal starting from the specified date. + + Args: + timeframe (TimeFrame): Timeframe the bars are requested for. Set by a value from the TimeFrame enumeration. Required unnamed parameter. + + date_from (datetime | int): Date of opening of the first bar from the requested sample. Set by the 'datetime' object or as a number + of seconds elapsed since 1970.01.01. Required unnamed parameter. + + count (int): Number of bars to receive. Required unnamed parameter. + + Returns: + Candles: Returns a Candles object as a collection of rates ordered chronologically + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + rates = await self.mt5.copy_rates_from(self.name, timeframe, date_from, count) + if rates is not None: + return Candles(data=rates) + raise ValueError(f'Could not get rates for {self.name}') + + async def copy_rates_from_pos(self, *,timeframe: TimeFrame, count: int = 500, start_position: int = 0) -> Candles: + """Get bars from the MetaTrader 5 terminal starting from the specified index. + + Args: + timeframe (TimeFrame): TimeFrame value from TimeFrame Enum. Required keyword only parameter + + count (int): Number of bars to return. Keyword argument defaults to 500 + + start_position (int): Initial index of the bar the data are requested from. The numbering of bars goes from + present to past. Thus, the zero bar means the current one. Keyword argument defaults to 0. + + Returns: + Candles: Returns a Candles object as a collection of rates ordered chronologically. + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + rates = await self.mt5.copy_rates_from_pos(self.name, timeframe, start_position, count) + if rates is not None: + return Candles(data=rates) + raise ValueError(f'Could not get rates for {self.name}') + + async def copy_rates_range(self, *, timeframe: TimeFrame, date_from: datetime | int, + date_to: datetime | int) -> Candles: + """Get bars in the specified date range from the MetaTrader 5 terminal. + + Args: + timeframe (TimeFrame): Timeframe for the bars using the TimeFrame enumeration. Required unnamed parameter. + + date_from (datetime | int): Date the bars are requested from. Set by the 'datetime' object or as a number of seconds + elapsed since 1970.01.01. Bars with the open time >= date_from are returned. Required unnamed parameter. + + date_to (datetime | int): Date, up to which the bars are requested. Set by the 'datetime' object or as a number of + seconds elapsed since 1970.01.01. Bars with the open time <= date_to are returned. Required unnamed parameter. + + Returns: + Candles: Returns a Candles object as a collection of rates ordered chronologically. + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + rates = await self.mt5.copy_rates_range(symbol=self.name, timeframe=timeframe, date_from=date_from, + date_to=date_to) + if rates is not None: + return Candles(data=rates) + raise ValueError(f'Could not get rates for {self.name}') + + async def copy_ticks_from(self, *, date_from: datetime | int, count: int = 100, flags: CopyTicks = CopyTicks.ALL) -> Ticks: + """ + Get ticks from the MetaTrader 5 terminal starting from the specified date. + + Args: date_from (datetime | int): Date the ticks are requested from. Set by the 'datetime' object or as a + number of seconds elapsed since 1970.01.01. + + count (int): Number of requested ticks. Defaults to 100 + + flags (CopyTicks): A flag to define the type of the requested ticks from CopyTicks enum. INFO is the default + + Returns: + Candles: Returns a Candles object as a collection of ticks ordered chronologically. + + Raises: + ValueError: If request was unsuccessful and None was returned + """ + ticks = await self.mt5.copy_ticks_from(self.name, date_from, count, flags) + if ticks is not None: + return Ticks(data=ticks) + raise ValueError(f'Could not get ticks for {self.name}') + + async def copy_ticks_range(self, *, date_from: datetime | int, date_to: datetime | int, flags: CopyTicks = CopyTicks.ALL) -> Ticks: + """Get ticks for the specified date range from the MetaTrader 5 terminal. + + Args: + date_from: Date the bars are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars with + the open time >= date_from are returned. Required unnamed parameter. + + date_to: Date, up to which the bars are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars + with the open time <= date_to are returned. Required unnamed parameter. + + flags (CopyTicks): + + Returns: + Candles: Returns a Candles object as a collection of ticks ordered chronologically. + + Raises: + ValueError: If request was unsuccessful and None was returned. + """ + ticks = await self.mt5.copy_ticks_range(self.name, date_from, date_to, flags) + if ticks is not None: + return Ticks(data=ticks) + raise ValueError(f'Could not get ticks for {self.name}') diff --git a/src/aiomql/terminal.py b/src/aiomql/terminal.py new file mode 100644 index 0000000..6a894de --- /dev/null +++ b/src/aiomql/terminal.py @@ -0,0 +1,70 @@ +"""Terminal related functions and properties""" + +from typing import NamedTuple +from logging import getLogger +from .core.models import TerminalInfo + +logger = getLogger() + + +class Terminal(TerminalInfo): + """Terminal Class. Get information about the MetaTrader 5 terminal. The class is a subclass of the TerminalInfo + class. It inherits all the attributes and methods of the TerminalInfo class and adds some useful methods. + + Notes: + Other attributes are defined in the TerminalInfo Class + """ + + Version = NamedTuple("Version", (('version', str), ('build', int), ('release_date', str))) + + async def initialize(self) -> bool: + """Establish a connection with the MetaTrader 5 terminal. There are three call options. Call without parameters. + The terminal for connection is found automatically. Call specifying the path to the MetaTrader 5 terminal we + want to connect to. word path as a keyword argument Call specifying the trading account path and parameters + i.e login, password, server, as keyword arguments, path can be omitted. + + Returns: + bool: True if successful else False + """ + self.connected = await self.mt5.initialize(**self.config.account_info()) + + if not self.connected: + err = await self.mt5.last_error() + logger.critical(f'Failed to initialize Terminal. Error Code: {err}') + raise SystemExit + return self.connected + + async def version(self): + """Get the MetaTrader 5 terminal version. This method returns the terminal version, build and release date as + a tuple of three values + + Returns: + Version: version of tuple as Version object + + Raises: + ValueError: If the terminal version cannot be obtained + """ + res = await self.mt5.version() + if res is None: + raise ValueError('Failed to get terminal version') + return self.Version(*res) + + async def info(self): + """Get the connected MetaTrader 5 client terminal status and settings. gets terminal info in the form of a + named tuple structure (namedtuple). Return None in case of an error. The info on the error can be + obtained using last_error(). + + Returns: + Terminal: Terminal status and settings as a terminal object. + """ + info = await self.mt5.terminal_info() + self.set_attributes(**info._asdict()) + return self + + async def symbols_total(self) -> int: + """Get the number of all financial instruments in the MetaTrader 5 terminal. + + Returns: + int: Total number of available symbols + """ + return await self.mt5.symbols_total() diff --git a/src/aiomql/ticks.py b/src/aiomql/ticks.py new file mode 100644 index 0000000..729e009 --- /dev/null +++ b/src/aiomql/ticks.py @@ -0,0 +1,167 @@ +"""Module for working with price ticks.""" + +from typing import TypeVar, Iterable +import reprlib + +from pandas import DataFrame, Series +import pandas_ta as ta + +from .core.constants import TickFlag + + +Self = TypeVar('Self', bound='Ticks') + + +class Tick: + """Price Tick of a Financial Instrument. + + Attributes: + time (int): Time of the last prices update for the symbol + bid (float): Current Bid price + ask (float): Current Ask price + last (float): Price of the last deal (Last) + volume (float): Volume for the current Last price + time_msc (int): Time of the last prices update for the symbol in milliseconds + flags (TickFlag): Tick flags + volume_real (float): Volume for the current Last price + Index (int): Custom attribute representing the position of the tick in a sequence. + """ + time: float + bid: float + ask: float + last: float + volume: float + time_msc:float + flags: float + volume_real:float + Index: int + def __init__(self, **kwargs): + self.time = kwargs.pop('time', 0) + self.Index = kwargs.pop('Index', 0) + self.set_attributes(**kwargs) + + def __repr__(self): + keys = reprlib.repr(', '.join('%s=%s' % (i, j) for i, j in self.__dict__.items()))[1:-1] + return '%(class)s(%(args)s)' % {'class': self.__class__.__name__, 'args': keys} + + + def set_attributes(self, **kwargs): + """Set attributes from keyword arguments""" + for key, value in kwargs.items(): + setattr(self, key, value) + +_Ticks = TypeVar('_Ticks', bound='Ticks') + +class Ticks: + """Container data class for price ticks. Arrange in chronological order. + Supports iteration, slicing and assignment + + Args: + data (DataFrame | tuple[tuple]): Dataframe of price ticks or a tuple of tuples + + Keyword Args: + flip (bool): If flip is True reverse data chronological order. + + Attributes: + data: Dataframe Object holding the ticks + """ + time: Series + bid: Series + ask: Series + last: Series + volume: Series + time_msc: Series + flags: Series + volume_real: Series + Index: Series + + def __init__(self, *, data: DataFrame | Iterable, flip=False): + """Initialize the Ticks class. Creates a DataFrame of price ticks from the data argument. + + Args: + data (DataFrame | Iterable): Dataframe of price ticks or any iterable object that can be converted to a + pandas DataFrame + flip (bool): If flip is True reverse data chronological order. + """ + if isinstance(data, DataFrame): + data = data + elif isinstance(data, type(self)): + data = DataFrame(data.data) + elif isinstance(data, Iterable): + data = DataFrame(data) + else: + raise ValueError(f'Cannot create DataFrame from object of {type(data)}') + self._data = data.iloc[::-1] if flip else data + + def __repr__(self): + return self._data.__repr__() + + def __len__(self): + return self._data.shape[0] + + def __contains__(self, item: Tick) -> bool: + return item.time_msc == self[item.Index].time_msc + + def __getattr__(self, item): + if item in list(self._data.columns.values): + return self._data[item] + raise AttributeError(f'Attribute {item} not defined on class {self.__class__.__name__}') + + def __getitem__(self, index) -> Tick | Self: + if isinstance(index, slice): + cls = self.__class__ + data = self._data.iloc[index] + data.reset_index(drop=True, inplace=True) + return cls(data=data) + + if isinstance(index, str): + return self._data[index] + + item = self._data.iloc[index] + return Tick(Index=index, **item) + + def __setitem__(self, index, value: Series): + if isinstance(value, Series): + self._data[index] = value + return + raise TypeError(f'Expected Series got {type(value)}') + + def __iter__(self): + return (Tick(**row._asdict()) for row in self._data.itertuples()) + + @property + def ta(self): + """Access to the pandas_ta library for performing technical analysis on the underlying data attribute. + + Returns: + pandas_ta: The pandas_ta library + """ + return self._data.ta + + @property + def ta_lib(self): + """Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. + + Returns: + ta: The ta library + """ + return ta + + @property + def data(self) -> DataFrame: + """DataFrame of price ticks arranged in chronological order.""" + return self._data + + def rename(self, inplace=True, **kwargs) -> _Ticks | None : + """Rename columns of the candle class. + + Keyword Args: + inplace (bool): Rename the columns inplace or return a new instance of the class with the renamed columns + **kwargs: The new names of the columns + + Returns: + Ticks: A new instance of the class with the renamed columns if inplace is False. + None: If inplace is True + """ + res = self._data.rename(columns=kwargs, inplace=inplace) + return res if inplace else self.__class__(data=res) diff --git a/src/aiomql/trader.py b/src/aiomql/trader.py new file mode 100644 index 0000000..3dc5788 --- /dev/null +++ b/src/aiomql/trader.py @@ -0,0 +1,121 @@ +"""Trader class module. Handles the creation of an order and the placing of trades""" + +from datetime import datetime +from typing import TypeVar +from logging import getLogger +from zoneinfo import ZoneInfo + +from .order import Order +from .symbol import Symbol as _Symbol +from .ram import RAM +from .core.models import OrderType +from .core.config import Config +from .utils import dict_to_string +from .result import Result + +logger = getLogger(__name__) +Symbol = TypeVar('Symbol', bound=_Symbol) + + +class Trader: + """Base class for creating a Trader object. Handles the creation of an order and the placing of trades + + Attributes: + symbol (Symbol): Financial instrument class Symbol class or any subclass of it. + ram (RAM): RAM instance + order (Order): Trade order + + Class Attributes: + name (str): A name for the strategy. + account (Account): Account instance. + mt5 (MetaTrader): MetaTrader instance. + config (Config): Config instance. + """ + config = Config() + + def __init__(self, *, symbol: Symbol, ram: RAM = None): + """Initializes the order object and RAM instance + + Args: + symbol (Symbol): Financial instrument + ram (RAM): Risk Assessment and Management instance + """ + self.symbol = symbol + self.order = Order(symbol=symbol.name) + self.ram = ram or RAM() + + async def create_order(self, *, order_type: OrderType, **kwargs): + """Complete the order object with the required values. Creates a simple order. + Uses the ram instance to set the volume. + + Args: + order_type (OrderType): Type of order + kwargs: keyword arguments as required for the specific trader + """ + # check if pips is passed in as a keyword argument, if not use the pips attribute of the ram instance + pips = kwargs.get('pips', 0) or self.ram.pips + self.order.volume = self.ram.volume or await self.ram.get_volume(symbol=self.symbol, pips=pips) + self.order.type = order_type + await self.set_order_limits(pips=pips) + + async def set_order_limits(self, pips: float): + """Sets the stop loss and take profit for the order. + This method uses pips as defined for forex instruments. + + Args: + pips: Target pips + """ + # use passed in pips and the pip value of the symbol to calculate the stop loss and take profit. + # this is sure to work for forex instruments. + pips = pips * self.symbol.pip + sl, tp = pips, pips * self.ram.risk_to_reward + tick = await self.symbol.info_tick() + if self.order.type == OrderType.BUY: + self.order.sl, self.order.tp = tick.ask - sl, tick.ask + tp + self.order.price = tick.ask + else: + self.order.sl, self.order.tp = tick.bid + sl, tick.bid - tp + self.order.price = tick.bid + + async def place_trade(self, order_type: OrderType, params: dict = None, **kwargs): + """Places a trade based on the order_type. + + Args: + order_type (OrderType): Type of order + params: parameters to be saved with the trade + kwargs: keyword arguments as required for the specific trader + """ + try: + await self.create_order(order_type=order_type, **kwargs) + + # Check the order before placing it + check = await self.order.check() + if check.retcode != 0: + logger.warning( + f"Symbol: {self.order.symbol}\nResult:\n{dict_to_string(check.get_dict(include={'comment', 'retcode'}), multi=True)}") + return + + # check expected profit + profit = await self.order.calc_profit() + + # Send the order. + result = await self.order.send() + if result.retcode != 10009: + logger.warning( + f"Symbol: {self.order.symbol}\nResult:\n{dict_to_string(result.get_dict(include={'comment', 'retcode'}), multi=True)}") + return + + logger.info(f"Symbol: {self.order.symbol}\nOrder: {dict_to_string(result.dict, multi=True)}\n") + + # save trade result and passed in parameters + if result.retcode == 10009 and self.config.record_trades: + params = params or {} + params['expected_profit'] = profit + date = datetime.utcnow() + date = date.replace(tzinfo=ZoneInfo('UTC')) + params['date'] = date + params['time'] = date.timestamp() + res = Result(result=result, parameters=params) + await res.save_csv() + except Exception as err: + logger.error(f"{err}. Symbol: {self.order.symbol}\n {self.__class__.__name__}.place_trade") diff --git a/src/aiomql/utils.py b/src/aiomql/utils.py new file mode 100644 index 0000000..a8e1b3e --- /dev/null +++ b/src/aiomql/utils.py @@ -0,0 +1,13 @@ +"""Utility functions for aiomql.""" +def dict_to_string(data: dict, multi=False) -> str: + """Convert a dict to a string. Use for logging. + + Args: + data (dict): The dict to convert. + multi (bool, optional): If True, each key-value pair will be on a new line. Defaults to False. + + Returns: + str: The string representation of the dict. + """ + sep = '\n' if multi else ', ' + return f"{sep}".join(f"{key}: {value}\n" for key, value in data.items()) \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..c25cfdb --- /dev/null +++ b/test.py @@ -0,0 +1,29 @@ +import asyncio +from datetime import datetime +from zoneinfo import ZoneInfo +import pytz +from aiomql import MetaTrader, Symbol, TimeFrame, Account, ForexSymbol + + +async def main(): + async with Account() as account: + d = datetime.now() + start = d.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=pytz.timezone('UTC')) + print(start.timestamp()) + end = d.replace(hour=7, minute=0, second=0, microsecond=0, tzinfo=pytz.timezone('UTC')) + # print(end.timestamp()) + s = ForexSymbol(name='EURUSD') + s1 = ForexSymbol(name='USDJPY') + await s.init() + await s1.init() + # t = await s.copy_ticks_range(date_from=start, date_to=end) + # t1 = await s1.copy_ticks_range(date_from=start, date_to=end) + r = await s1.copy_rates_range(date_from=start, date_to=end, timeframe=TimeFrame.M1) + # rc = await s1.copy_rates_from(date_from=end, timeframe=TimeFrame.M15, count=96) + print(datetime.fromtimestamp(r[0].time, tz=pytz.timezone('UTC')), datetime.fromtimestamp(r[-1].time), len(r)) + # f = await s.copy_ticks_range(date_from=start, date_to=end) + # print(datetime.fromtimestamp(f[0].time), datetime.fromtimestamp(f[-1].time), len(f)) + # print(len(f), len(rc), rc[50].open) + # print(len(r), r[-1].time, len(rc), rc[-1].time - rc[-2].time, end.timestamp(),rc[0].time) + +asyncio.run(main()) diff --git a/tests/test_order.py b/tests/test_order.py new file mode 100644 index 0000000..67db0ef --- /dev/null +++ b/tests/test_order.py @@ -0,0 +1,28 @@ +# import asyncio +# from pprint import pprint as pp +# from aiomql import Order, Account, ForexSymbol, RAM, OrderType +# +# +# async def test_send_order(): +# await Account().sign_in() +# symbol = ForexSymbol(name='Volatility 50 Index') +# await symbol.init() +# tick = await symbol.info_tick() +# pips = 100 +# volume = await symbol.compute_volume(amount=100, pips=pips) +# sls = symbol.trade_stops_level +# cls = pips * symbol.pip +# print(sls, cls) +# sl = tick.ask - (cls) +# tp = tick.ask + (cls) +# sls = symbol.trade_stops_level +# po = sls * symbol.point +# pi = po * 10 +# # print(sls, po, pi, sl) +# print(volume) +# order = Order(symbol=symbol.name, type=OrderType.BUY, sl=sl, tp=tp, volume=volume, price=tick.ask) +# res = await order.send() +# pp(res.dict) + +asyncio.run(test_send_order()) +