This commit is contained in:
Ichinga Samuel
2023-10-11 09:49:06 +01:00
commit 027f9fd8de
65 changed files with 15274 additions and 0 deletions
+73
View File
@@ -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
+21
View File
@@ -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.
+3606
View File
File diff suppressed because it is too large Load Diff
+101
View File
@@ -0,0 +1,101 @@
## <a id="account"></a> 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|
+107
View File
@@ -0,0 +1,107 @@
## <a id="bot_builder"></a> 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|
+181
View File
@@ -0,0 +1,181 @@
## <a id="candle"></a> 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|
## <a id="candles"></a> 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.|
+164
View File
@@ -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)
<a id="aiomql.core.base"></a>
# aiomql.core.base
<a id="aiomql.core.base.Base"></a>
## 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
<a id="aiomql.core.base.Base.set_attributes"></a>
#### 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.
<a id="aiomql.core.base.Base.annotations"></a>
#### annotations
```python
@property
@cache
def annotations() -> dict
```
Class annotations from all ancestor classes and the current class.
**Returns**:
- `dict` - A dictionary of class annotations
<a id="aiomql.core.base.Base.get_dict"></a>
#### 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
<a id="aiomql.core.base.Base.class_vars"></a>
#### 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.
<a id="aiomql.core.base.Base.dict"></a>
#### 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
<a id="aiomql.core.base.Base.Meta"></a>
## 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.
<a id="aiomql.core.base.Base.Meta.filter"></a>
#### 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
+59
View File
@@ -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)
<a id="aiomql.core.config"></a>
# aiomql.core.config
<a id="aiomql.core.config.Config"></a>
## 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.
<a id="aiomql.core.config.Config.account_info"></a>
#### 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
+736
View File
@@ -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)
<a id="aiomql.core.constants"></a>
# aiomql.core.constants
<a id="aiomql.core.constants.TradeAction"></a>
## 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
<a id="aiomql.core.constants.OrderFilling"></a>
## 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.
<a id="aiomql.core.constants.OrderTime"></a>
## 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.
<a id="aiomql.core.constants.OrderType"></a>
## 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
<a id="aiomql.core.constants.OrderType.opposite"></a>
#### 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
<a id="aiomql.core.constants.BookType"></a>
## 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
<a id="aiomql.core.constants.TimeFrame"></a>
## 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
<a id="aiomql.core.constants.TimeFrame.time"></a>
#### 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
<a id="aiomql.core.constants.CopyTicks"></a>
## 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
<a id="aiomql.core.constants.PositionType"></a>
## 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
<a id="aiomql.core.constants.PositionReason"></a>
## 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
<a id="aiomql.core.constants.DealType"></a>
## 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.
<a id="aiomql.core.constants.DealEntry"></a>
## 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
<a id="aiomql.core.constants.DealReason"></a>
## 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
<a id="aiomql.core.constants.OrderReason"></a>
## 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
<a id="aiomql.core.constants.SymbolChartMode"></a>
## 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
<a id="aiomql.core.constants.SymbolCalcMode"></a>
## 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
<a id="aiomql.core.constants.SymbolTradeMode"></a>
## 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
<a id="aiomql.core.constants.SymbolTradeExecution"></a>
## 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.
<a id="aiomql.core.constants.SymbolSwapMode"></a>
## 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)
<a id="aiomql.core.constants.DayOfWeek"></a>
## 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
<a id="aiomql.core.constants.SymbolOrderGTCMode"></a>
## 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
<a id="aiomql.core.constants.SymbolOptionRight"></a>
## 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.
<a id="aiomql.core.constants.SymbolOptionMode"></a>
## 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.
<a id="aiomql.core.constants.AccountTradeMode"></a>
## 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
<a id="aiomql.core.constants.TickFlag"></a>
## 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
<a id="aiomql.core.constants.TradeRetcode"></a>
## 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)
<a id="aiomql.core.constants.AccountStopOutMode"></a>
## 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
<a id="aiomql.core.constants.AccountMarginMode"></a>
## 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).
+19
View File
@@ -0,0 +1,19 @@
# Table of Contents
* [aiomql.core.errors](#aiomql.core.errors)
* [Error](#aiomql.core.errors.Error)
<a id="aiomql.core.errors"></a>
# aiomql.core.errors
<a id="aiomql.core.errors.Error"></a>
## Error Objects
```python
class Error()
```
Error class for handling errors from MetaTrader 5.
+54
View File
@@ -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)
<a id="aiomql.core.exceptions"></a>
# aiomql.core.exceptions
Exceptions for the aiomql package.
<a id="aiomql.core.exceptions.LoginError"></a>
## LoginError Objects
```python
class LoginError(Exception)
```
Raised when an error occurs when logging in.
<a id="aiomql.core.exceptions.VolumeError"></a>
## VolumeError Objects
```python
class VolumeError(Exception)
```
Raised when a volume is not valid or out of range for a symbol.
<a id="aiomql.core.exceptions.SymbolError"></a>
## SymbolError Objects
```python
class SymbolError(Exception)
```
Raised when a symbol is not provided where required or not available in the Market Watch.
<a id="aiomql.core.exceptions.OrderError"></a>
## OrderError Objects
```python
class OrderError(Exception)
```
Raised when an error occurs when working with the order class.
+165
View File
@@ -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)
<a id="aiomql.core.meta_trader"></a>
# aiomql.core.meta\_trader
<a id="aiomql.core.meta_trader.MetaTrader"></a>
## MetaTrader Objects
```python
class MetaTrader(metaclass=BaseMeta)
```
<a id="aiomql.core.meta_trader.MetaTrader.__aenter__"></a>
#### \_\_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.
<a id="aiomql.core.meta_trader.MetaTrader.__aexit__"></a>
#### \_\_aexit\_\_
```python
async def __aexit__(exc_type, exc_val, exc_tb)
```
Async context manager exit point. Closes the connection to the MetaTrader terminal.
<a id="aiomql.core.meta_trader.MetaTrader.login"></a>
#### 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.
<a id="aiomql.core.meta_trader.MetaTrader.initialize"></a>
#### 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.
<a id="aiomql.core.meta_trader.MetaTrader.shutdown"></a>
#### shutdown
```python
async def shutdown() -> None
```
Closes the connection to the MetaTrader terminal.
**Returns**:
- `None` - None
<a id="aiomql.core.meta_trader.MetaTrader.version"></a>
#### version
```python
async def version() -> tuple[int, int, str] | None
```
<a id="aiomql.core.meta_trader.MetaTrader.account_info"></a>
#### account\_info
```python
async def account_info() -> AccountInfo | None
```
<a id="aiomql.core.meta_trader.MetaTrader.orders_get"></a>
#### 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
View File
+405
View File
@@ -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)
<a id="aiomql.core.models"></a>
# aiomql.core.models
<a id="aiomql.core.models.AccountInfo"></a>
## 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
<a id="aiomql.core.models.TerminalInfo"></a>
## 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
<a id="aiomql.core.models.SymbolInfo"></a>
## 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
<a id="aiomql.core.models.BookInfo"></a>
## BookInfo Objects
```python
class BookInfo(Base)
```
Book Information Class.
**Attributes**:
- `type` - BookType
- `price` - float
- `volume` - float
- `volume_dbl` - float
<a id="aiomql.core.models.TradeOrder"></a>
## 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
<a id="aiomql.core.models.TradeRequest"></a>
## 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
<a id="aiomql.core.models.OrderCheckResult"></a>
## 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
<a id="aiomql.core.models.OrderSendResult"></a>
## 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
<a id="aiomql.core.models.TradePosition"></a>
## 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
<a id="aiomql.core.models.TradeDeal"></a>
## 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
+93
View File
@@ -0,0 +1,93 @@
## <a id="executor"></a> 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.
+110
View File
@@ -0,0 +1,110 @@
## <a id="aiomhistory"></a> 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 |
+3842
View File
File diff suppressed because it is too large Load Diff
+135
View File
@@ -0,0 +1,135 @@
## <a id="order"></a> 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|
#### <a id=order.Order.orders_total> 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
#### <a id="order.Order.send"></a> 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|
#### <a id="order.Order.calc_margin"></a> 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|
#### <a id="order.Order.calc_profit"></a> 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|
+72
View File
@@ -0,0 +1,72 @@
## <a id="positions"></a> 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.
<a id="positions.Positions.__init__"></a> #### \_\_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|
#### <a id="positions.Positions.positions_total"></a> positions_total
```python
async def positions_total() -> int
```
Get the number of open positions.
**Returns**:
|Type|Description|
|---|---|
|**int**|Return total number of open positions|
#### <a id="positions.Positions.positions_get"></a> 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|
#### <a id="aiomql.positions.Positions.close_all"></a> close_all
```python
async def close_all() -> int
```
Close all open positions for the trading account.
**Returns**:
- `int` - Return number of positions closed.
+83
View File
@@ -0,0 +1,83 @@
<a id="aiomql.ram"></a>
# aiomql.ram
Risk Assessment and Management
<a id="aiomql.ram.RAM"></a>
## RAM Objects
```python
class RAM()
```
<a id="aiomql.ram.RAM.__init__"></a>
#### \_\_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
<a id="aiomql.ram.RAM.get_amount"></a>
#### 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
<a id="aiomql.ram.RAM.get_volume"></a>
#### 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
+103
View File
@@ -0,0 +1,103 @@
<a id="aiomql.records"></a>
# aiomql.records
This module contains the Records class, which is used to read and update trade records from csv files.
<a id="aiomql.records.Records"></a>
## 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
<a id="aiomql.records.Records.__init__"></a>
#### \_\_init\_\_
```python
def __init__(records_dir: Path = '')
```
Initialize the Records class.
**Arguments**:
- `records_dir` _Path_ - Path to directory containing record of placed trades.
<a id="aiomql.records.Records.get_records"></a>
#### get\_records
```python
async def get_records()
```
Get trade records from records_dir folder
**Yields**:
- `files` - Trade record files
<a id="aiomql.records.Records.read_update"></a>
#### read\_update
```python
async def read_update(file: Path)
```
Read and update trade records
**Arguments**:
- `file` - Trade record file
<a id="aiomql.records.Records.update_rows"></a>
#### 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.
<a id="aiomql.records.Records.update_records"></a>
#### update\_records
```python
async def update_records()
```
Update trade records in the records_dir folder.
<a id="aiomql.records.Records.update_record"></a>
#### update\_record
```python
async def update_record(file: Path | str)
```
Update a single trade record file.
+56
View File
@@ -0,0 +1,56 @@
<a id="aiomql.result"></a>
# aiomql.result
<a id="aiomql.result.Result"></a>
## 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
<a id="aiomql.result.Result.__init__"></a>
#### \_\_init\_\_
```python
def __init__(result: OrderSendResult, parameters: dict = None, name: str = '')
```
Prepare result data
**Arguments**:
result:
parameters:
name:
<a id="aiomql.result.Result.to_csv"></a>
#### to\_csv
```python
async def to_csv()
```
Record trade results and associated parameters as a csv file
<a id="aiomql.result.Result.save_csv"></a>
#### save\_csv
```python
async def save_csv()
```
Save trade results and associated parameters as a csv file in a separate thread
+77
View File
@@ -0,0 +1,77 @@
<a id="aiomql.strategy"></a>
# aiomql.strategy
The base class for creating strategies.
<a id="aiomql.strategy.Strategy"></a>
## 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.
<a id="aiomql.strategy.Strategy.__init__"></a>
#### \_\_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
<a id="aiomql.strategy.Strategy.sleep"></a>
#### 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.
<a id="aiomql.strategy.Strategy.trade"></a>
#### 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.
+383
View File
@@ -0,0 +1,383 @@
<a id="aiomql.symbol"></a>
# aiomql.symbol
Symbol class for handling a financial instrument.
<a id="aiomql.symbol.Symbol"></a>
## 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
<a id="aiomql.symbol.Symbol.pip"></a>
#### 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.
<a id="aiomql.symbol.Symbol.info_tick"></a>
#### 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
<a id="aiomql.symbol.Symbol.symbol_select"></a>
#### 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.
<a id="aiomql.symbol.Symbol.info"></a>
#### 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
<a id="aiomql.symbol.Symbol.init"></a>
#### 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
<a id="aiomql.symbol.Symbol.book_add"></a>
#### 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.
<a id="aiomql.symbol.Symbol.book_get"></a>
#### 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
<a id="aiomql.symbol.Symbol.book_release"></a>
#### 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.
<a id="aiomql.symbol.Symbol.compute_volume"></a>
#### 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
<a id="aiomql.symbol.Symbol.currency_conversion"></a>
#### 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
<a id="aiomql.symbol.Symbol.copy_rates_from"></a>
#### 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
<a id="aiomql.symbol.Symbol.copy_rates_from_pos"></a>
#### 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
<a id="aiomql.symbol.Symbol.copy_rates_range"></a>
#### 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
<a id="aiomql.symbol.Symbol.copy_ticks_from"></a>
#### 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
<a id="aiomql.symbol.Symbol.copy_ticks_range"></a>
#### 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.
+88
View File
@@ -0,0 +1,88 @@
<a id="aiomql.terminal"></a>
# aiomql.terminal
Terminal related functions and properties
<a id="aiomql.terminal.Terminal"></a>
## 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
<a id="aiomql.terminal.Terminal.initialize"></a>
#### 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
<a id="aiomql.terminal.Terminal.version"></a>
#### 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
<a id="aiomql.terminal.Terminal.info"></a>
#### 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.
<a id="aiomql.terminal.Terminal.symbols_total"></a>
#### 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
+141
View File
@@ -0,0 +1,141 @@
<a id="aiomql.ticks"></a>
# aiomql.ticks
Module for working with price ticks.
<a id="aiomql.ticks.Tick"></a>
## 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.
<a id="aiomql.ticks.Tick.set_attributes"></a>
#### set\_attributes
```python
def set_attributes(**kwargs)
```
Set attributes from keyword arguments
<a id="aiomql.ticks.Ticks"></a>
## 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
<a id="aiomql.ticks.Ticks.__init__"></a>
#### \_\_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.
<a id="aiomql.ticks.Ticks.ta"></a>
#### 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
<a id="aiomql.ticks.Ticks.ta_lib"></a>
#### 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
<a id="aiomql.ticks.Ticks.data"></a>
#### data
```python
@property
def data() -> DataFrame
```
DataFrame of price ticks arranged in chronological order.
<a id="aiomql.ticks.Ticks.rename"></a>
#### 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
+90
View File
@@ -0,0 +1,90 @@
<a id="aiomql.trader"></a>
# aiomql.trader
Trader class module. Handles the creation of an order and the placing of trades
<a id="aiomql.trader.Trader"></a>
## 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.
<a id="aiomql.trader.Trader.__init__"></a>
#### \_\_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
<a id="aiomql.trader.Trader.create_order"></a>
#### 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
<a id="aiomql.trader.Trader.set_order_limits"></a>
#### 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
<a id="aiomql.trader.Trader.place_trade"></a>
#### 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
+39
View File
@@ -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())
+44
View File
@@ -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())
+37
View File
@@ -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())
+59
View File
@@ -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())
+37
View File
@@ -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())
+17
View File
@@ -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())
+25
View File
@@ -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"
View File
+47
View File
@@ -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()]
+3
View File
@@ -0,0 +1,3 @@
from setuptools import setup
setup()
+23
View File
@@ -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 *
+109
View File
@@ -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}
+116
View File
@@ -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')
+234
View File
@@ -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)
+7
View File
@@ -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 *
+137
View File
@@ -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)
+110
View File
@@ -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}
+786
View File
@@ -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
+30
View File
@@ -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}
"""
+19
View File
@@ -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."""
+355
View File
@@ -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
+613
View File
@@ -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
+87
View File
@@ -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()]
+119
View File
@@ -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
+112
View File
@@ -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
+82
View File
@@ -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
+64
View File
@@ -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)
+79
View File
@@ -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)
+58
View File
@@ -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)
+60
View File
@@ -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)
+73
View File
@@ -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.
"""
+301
View File
@@ -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}')
+70
View File
@@ -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()
+167
View File
@@ -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)
+121
View File
@@ -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")
+13
View File
@@ -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())
+29
View File
@@ -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())
+28
View File
@@ -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())