mirror of
https://github.com/Ichinga-Samuel/aiomql.git
synced 2026-08-04 07:47:44 +00:00
v4
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Table of Contents
|
||||
- [MetaTrader](https://github.com/Ichinga-Samuel/aiomql/blob/master/docs/core/meta_trader.md)
|
||||
- [Config](https://github.com/Ichinga-Samuel/aiomql/blob/master/docs/core/config.md)
|
||||
- [MetaTrader](core/meta_trader.md)
|
||||
- [Config](core/config.md)
|
||||
- [Base](https://github.com/Ichinga-Samuel/aiomql/blob/master/docs/core/base.md)
|
||||
- [Constants](https://github.com/Ichinga-Samuel/aiomql/blob/master/docs/core/constants.md)
|
||||
- [TaskQueue](https://github.com/Ichinga-Samuel/aiomql/blob/master/docs/core/task_queue.md)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Fractals
|
||||
|
||||
## Table of Contents
|
||||
- [fractals](#fractals)
|
||||
- [find_bearish_fractal](#fractals.find_bearish_fractal)
|
||||
- [find_bullish_fractal](#fractals.find_bullish_fractal)
|
||||
|
||||
|
||||
<a id="fractals.find_bearish_fractal"></a>
|
||||
### find_bearish_fractal
|
||||
```python
|
||||
def find_bearish_fractal(candles: Candles) -> Candle | None
|
||||
```
|
||||
Given a candles object, find the most recent bearish fractal.
|
||||
|
||||
|
||||
<a id="fractals.find_bullish_fractal"></a>
|
||||
### find_bullish_fractal
|
||||
```python
|
||||
def find_bullish_fractal(candles: Candles) -> Candle | None
|
||||
```
|
||||
Given a candles object, find the most recent bullish fractal.
|
||||
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
# Table of Contents
|
||||
|
||||
* [backtest\_account](#backtest_account)
|
||||
* [BackTestAccount](#backtest_account.BackTestAccount)
|
||||
* [get\_dict](#backtest_account.BackTestAccount.get_dict)
|
||||
* [asdict](#backtest_account.BackTestAccount.asdict)
|
||||
* [set\_attrs](#backtest_account.BackTestAccount.set_attrs)
|
||||
|
||||
<a id="backtest_account"></a>
|
||||
|
||||
# backtest\_account
|
||||
|
||||
<a id="backtest_account.BackTestAccount"></a>
|
||||
|
||||
## BackTestAccount Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class BackTestAccount()
|
||||
```
|
||||
|
||||
Account data for backtesting
|
||||
|
||||
<a id="backtest_account.BackTestAccount.get_dict"></a>
|
||||
|
||||
#### get\_dict
|
||||
|
||||
```python
|
||||
def get_dict(exclude: set = None, include: set = None)
|
||||
```
|
||||
|
||||
Returns a dictionary of the account data. Using the exclude and include arguments, you can filter the data
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `exclude` _set_ - A set of keys to exclude
|
||||
- `include` _set_ - A set of keys to include
|
||||
|
||||
<a id="backtest_account.BackTestAccount.asdict"></a>
|
||||
|
||||
#### asdict
|
||||
|
||||
```python
|
||||
def asdict()
|
||||
```
|
||||
|
||||
Returns a dictionary of the account data
|
||||
|
||||
<a id="backtest_account.BackTestAccount.set_attrs"></a>
|
||||
|
||||
#### set\_attrs
|
||||
|
||||
```python
|
||||
def set_attrs(**kwargs)
|
||||
```
|
||||
|
||||
Se the attributes of the account data to the instance
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# Table of Contents
|
||||
|
||||
* [backtest\_controller](#backtest_controller)
|
||||
* [BackTestController](#backtest_controller.BackTestController)
|
||||
* [backtest\_engine](#backtest_controller.BackTestController.backtest_engine)
|
||||
* [add\_tasks](#backtest_controller.BackTestController.add_tasks)
|
||||
* [set\_parties](#backtest_controller.BackTestController.set_parties)
|
||||
* [parties](#backtest_controller.BackTestController.parties)
|
||||
* [control](#backtest_controller.BackTestController.control)
|
||||
* [stop\_backtesting](#backtest_controller.BackTestController.stop_backtesting)
|
||||
* [wait](#backtest_controller.BackTestController.wait)
|
||||
* [abort](#backtest_controller.BackTestController.abort)
|
||||
|
||||
<a id="backtest_controller"></a>
|
||||
|
||||
# backtest\_controller
|
||||
|
||||
<a id="backtest_controller.BackTestController"></a>
|
||||
|
||||
## BackTestController Objects
|
||||
|
||||
```python
|
||||
class BackTestController()
|
||||
```
|
||||
|
||||
The controller for the backtesting engine.
|
||||
It also act's as a synchronizier for running multiple strategies (tasks) using a threading.Barrier primitive.
|
||||
It handles the updating of open positions and close them when necessary.
|
||||
It handles the iterator for the backtesting engine and handles it movement in time by moving it to the next time step.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `_instance` _Self_ - The instance of the controller
|
||||
- `config` _Config_ - The configuration for the backtesting engine
|
||||
- `tasks` _list[Task]_ - The tasks that are being run
|
||||
- `barrier` _Barrier_ - The barrier for synchronizing the tasks
|
||||
|
||||
<a id="backtest_controller.BackTestController.backtest_engine"></a>
|
||||
|
||||
#### backtest\_engine
|
||||
|
||||
```python
|
||||
@property
|
||||
def backtest_engine()
|
||||
```
|
||||
|
||||
Returns the backtest engine
|
||||
|
||||
<a id="backtest_controller.BackTestController.add_tasks"></a>
|
||||
|
||||
#### add\_tasks
|
||||
|
||||
```python
|
||||
def add_tasks(*tasks: Task)
|
||||
```
|
||||
|
||||
Adds tasks to the tasks list
|
||||
|
||||
<a id="backtest_controller.BackTestController.set_parties"></a>
|
||||
|
||||
#### set\_parties
|
||||
|
||||
```python
|
||||
def set_parties(*, parties: int)
|
||||
```
|
||||
|
||||
Sets the number of parties for the barrier. The barrier will wait for the number of parties to reach the barrier.
|
||||
This has to be done here as it can be impossible to know the eventual number of parties to set the barrier to during initialization.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `parties` _int_ - The number of parties to set the barrier to
|
||||
|
||||
<a id="backtest_controller.BackTestController.parties"></a>
|
||||
|
||||
#### parties
|
||||
|
||||
```python
|
||||
@property
|
||||
def parties()
|
||||
```
|
||||
|
||||
Returns the number of parties for the barrier
|
||||
|
||||
<a id="backtest_controller.BackTestController.control"></a>
|
||||
|
||||
#### control
|
||||
|
||||
```python
|
||||
async def control()
|
||||
```
|
||||
|
||||
The backtest controller. It controls the backtesting engine and the tasks that are being run.
|
||||
It acts as a synchronizer for the tasks and the backtesting engine.
|
||||
|
||||
<a id="backtest_controller.BackTestController.stop_backtesting"></a>
|
||||
|
||||
#### stop\_backtesting
|
||||
|
||||
```python
|
||||
def stop_backtesting()
|
||||
```
|
||||
|
||||
Stop the backtester, and shutdown the executor
|
||||
|
||||
<a id="backtest_controller.BackTestController.wait"></a>
|
||||
|
||||
#### wait
|
||||
|
||||
```python
|
||||
def wait()
|
||||
```
|
||||
|
||||
Called by individual tasks to indicate completion of their cycle
|
||||
|
||||
<a id="backtest_controller.BackTestController.abort"></a>
|
||||
|
||||
#### abort
|
||||
|
||||
```python
|
||||
def abort()
|
||||
```
|
||||
|
||||
Aborts the barrier
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,185 @@
|
||||
# Table of Contents
|
||||
|
||||
* [get\_data](#get_data)
|
||||
* [Cursor](#get_data.Cursor)
|
||||
* [BackTestData](#get_data.BackTestData)
|
||||
* [set\_attrs](#get_data.BackTestData.set_attrs)
|
||||
* [fields](#get_data.BackTestData.fields)
|
||||
* [GetData](#get_data.GetData)
|
||||
* [\_\_init\_\_](#get_data.GetData.__init__)
|
||||
* [pickle\_data](#get_data.GetData.pickle_data)
|
||||
* [load\_data](#get_data.GetData.load_data)
|
||||
* [save\_data](#get_data.GetData.save_data)
|
||||
* [get\_data](#get_data.GetData.get_data)
|
||||
|
||||
<a id="get_data"></a>
|
||||
|
||||
# get\_data
|
||||
|
||||
<a id="get_data.Cursor"></a>
|
||||
|
||||
## Cursor Objects
|
||||
|
||||
```python
|
||||
class Cursor(NamedTuple)
|
||||
```
|
||||
|
||||
A cursor to iterate over the data. Marks the current position.
|
||||
|
||||
<a id="get_data.BackTestData"></a>
|
||||
|
||||
## BackTestData Objects
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class BackTestData()
|
||||
```
|
||||
|
||||
The data class to store the backtesting data.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `name` _str_ - The name of the backtest data.
|
||||
- `terminal` _dict_ - The terminal information.
|
||||
- `version` _tuple_ - The version of the terminal.
|
||||
- `account` _dict_ - The account information.
|
||||
- `symbols` _dict_ - The symbols information.
|
||||
- `ticks` _dict_ - The ticks data.
|
||||
- `rates` _dict_ - The rates data.
|
||||
- `span` _range_ - The range of the data.
|
||||
- `range` _range_ - The range of the data.
|
||||
- `orders` _dict_ - The orders data.
|
||||
- `deals` _dict_ - The deals data.
|
||||
- `positions` _dict_ - The positions data.
|
||||
- `open_positions` _set_ - The open positions.
|
||||
- `cursor` _Cursor_ - The cursor to iterate over the data.
|
||||
- `margins` _dict_ - The margins data.
|
||||
- `fully_loaded` _bool_ - A flag to indicate if the data is fully loaded
|
||||
|
||||
<a id="get_data.BackTestData.set_attrs"></a>
|
||||
|
||||
#### set\_attrs
|
||||
|
||||
```python
|
||||
def set_attrs(**kwargs)
|
||||
```
|
||||
|
||||
Set the attributes of the class on the instance.
|
||||
|
||||
<a id="get_data.BackTestData.fields"></a>
|
||||
|
||||
#### fields
|
||||
|
||||
```python
|
||||
@property
|
||||
def fields()
|
||||
```
|
||||
|
||||
A list of the fields of the class.
|
||||
|
||||
<a id="get_data.GetData"></a>
|
||||
|
||||
## GetData Objects
|
||||
|
||||
```python
|
||||
class GetData()
|
||||
```
|
||||
|
||||
A class to get the backtesting data from the MetaTrader5 terminal.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `start` _datetime_ - The start date of the data.
|
||||
- `end` _datetime_ - The end date of the data.
|
||||
- `symbols` _Sequence[str]_ - The symbols to get the data for.
|
||||
- `timeframes` _Sequence[TimeFrame]_ - The timeframes to get the data for.
|
||||
- `name` _str_ - The name of the backtest data.
|
||||
- `range` _range_ - The range of the data.
|
||||
- `span` _range_ - The span of the data.
|
||||
- `data` _BackTestData_ - The backtesting data.
|
||||
- `mt5` _MetaTrader_ - The MetaTrader5 instance.
|
||||
- `task_queue` _TaskQueue_ - The task queue to handle the requests.
|
||||
|
||||
<a id="get_data.GetData.__init__"></a>
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(*,
|
||||
start: datetime,
|
||||
end: datetime,
|
||||
symbols: Sequence[str],
|
||||
timeframes: Sequence[TimeFrame],
|
||||
name: str = "")
|
||||
```
|
||||
|
||||
Get the backtesting data from the MetaTrader5 terminal.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `start` _datetime_ - The start date of the data.
|
||||
- `end` _datetime_ - The end date of the data.
|
||||
- `symbols` _Sequence[str]_ - The symbols to get the data for.
|
||||
- `timeframes` _Sequence[TimeFrame]_ - The timeframes to get the data for.
|
||||
- `name` _str_ - The name of the backtest data.
|
||||
|
||||
<a id="get_data.GetData.pickle_data"></a>
|
||||
|
||||
#### pickle\_data
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def pickle_data(cls, *, data: BackTestData, name: str | Path)
|
||||
```
|
||||
|
||||
Pickle the data to a file.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `data` _BackTestData_ - The data to pickle.
|
||||
- `name` _str | Path_ - The name of the file to pickle the data to.
|
||||
|
||||
<a id="get_data.GetData.load_data"></a>
|
||||
|
||||
#### load\_data
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def load_data(cls, *, name: str | Path) -> BackTestData
|
||||
```
|
||||
|
||||
Load the data from a file.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `name` _str | Path_ - The name of the file to load the data from.
|
||||
|
||||
<a id="get_data.GetData.save_data"></a>
|
||||
|
||||
#### save\_data
|
||||
|
||||
```python
|
||||
def save_data(*, name: str | Path = "")
|
||||
```
|
||||
|
||||
Save the data to a file.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `name` _str | Path_ - The name of the file to save the data to. If not provided, the name of the data is used.
|
||||
|
||||
<a id="get_data.GetData.get_data"></a>
|
||||
|
||||
#### get\_data
|
||||
|
||||
```python
|
||||
async def get_data(workers: int = None)
|
||||
```
|
||||
|
||||
Use the task queue to get the data from the MetaTrader5 terminal.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `workers` _int_ - The number of workers to use in the task queue. If not provided, the default number of workers
|
||||
is used.
|
||||
|
||||
@@ -0,0 +1,447 @@
|
||||
# Table of Contents
|
||||
|
||||
* [trades\_manager](#trades_manager)
|
||||
* [TradeManager](#trades_manager.TradeManager)
|
||||
* [update](#trades_manager.TradeManager.update)
|
||||
* [values](#trades_manager.TradeManager.values)
|
||||
* [keys](#trades_manager.TradeManager.keys)
|
||||
* [items](#trades_manager.TradeManager.items)
|
||||
* [to\_dict](#trades_manager.TradeManager.to_dict)
|
||||
* [PositionsManager](#trades_manager.PositionsManager)
|
||||
* [\_\_init\_\_](#trades_manager.PositionsManager.__init__)
|
||||
* [margin](#trades_manager.PositionsManager.margin)
|
||||
* [close](#trades_manager.PositionsManager.close)
|
||||
* [get\_margin](#trades_manager.PositionsManager.get_margin)
|
||||
* [delete\_margin](#trades_manager.PositionsManager.delete_margin)
|
||||
* [set\_margin](#trades_manager.PositionsManager.set_margin)
|
||||
* [positions\_get](#trades_manager.PositionsManager.positions_get)
|
||||
* [positions\_total](#trades_manager.PositionsManager.positions_total)
|
||||
* [open\_positions](#trades_manager.PositionsManager.open_positions)
|
||||
* [OrdersManager](#trades_manager.OrdersManager)
|
||||
* [get\_orders\_range](#trades_manager.OrdersManager.get_orders_range)
|
||||
* [history\_orders\_get](#trades_manager.OrdersManager.history_orders_get)
|
||||
* [history\_orders\_total](#trades_manager.OrdersManager.history_orders_total)
|
||||
* [DealsManager](#trades_manager.DealsManager)
|
||||
* [get\_deals\_range](#trades_manager.DealsManager.get_deals_range)
|
||||
* [history\_deals\_get](#trades_manager.DealsManager.history_deals_get)
|
||||
* [history\_deals\_total](#trades_manager.DealsManager.history_deals_total)
|
||||
|
||||
<a id="trades_manager"></a>
|
||||
|
||||
# trades\_manager
|
||||
|
||||
<a id="trades_manager.TradeManager"></a>
|
||||
|
||||
## TradeManager Objects
|
||||
|
||||
```python
|
||||
class TradeManager(Generic[TradeData])
|
||||
```
|
||||
|
||||
A generic class to manage trades data during a backtest. It is the parent class of the
|
||||
PositionsManager, OrdersManager, and DealsManager. It implements some dict-like methods to manage the data.
|
||||
It has a private attribute _data to store the data. It exposes the data through the values, keys, and items methods.
|
||||
It also has a to_dict method to convert the data to a dictionary.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `_data` _dict[int, TradeData]_ - The data to store the trades.
|
||||
|
||||
|
||||
**Examples**:
|
||||
|
||||
>>> manager = TradeManager()
|
||||
>>> manager[123456] = TradePosition(ticket=123456, symbol="EURUSD", volume=0.1)
|
||||
>>> manager.update(ticket=123456, symbol="EURUSD", volume=0.1)
|
||||
>>> manager[123456]
|
||||
TradePosition(ticket=123456, symbol='EURUSD', volume=0.1)
|
||||
>>> manager.values()
|
||||
(TradePosition(ticket=123456, symbol='EURUSD', volume=0.1),)
|
||||
>>> manager.keys()
|
||||
(123456,)
|
||||
>>> manager.items()
|
||||
((123456, TradePosition(ticket=123456, symbol='EURUSD', volume=0.1)),)
|
||||
>>> manager.to_dict()
|
||||
- `{123456` - {'ticket': 123456, 'symbol': 'EURUSD', 'volume': 0.1}}
|
||||
>>> pos = manager.get(123456)
|
||||
>>> pos
|
||||
TradePosition(ticket=123456, symbol='EURUSD', volume=0.1)
|
||||
>>> pos in manager
|
||||
True
|
||||
>>> len(manager)
|
||||
1
|
||||
>>> pos in manager
|
||||
False
|
||||
|
||||
<a id="trades_manager.TradeManager.update"></a>
|
||||
|
||||
#### update
|
||||
|
||||
```python
|
||||
def update(*, ticket: int, **kwargs)
|
||||
```
|
||||
|
||||
Update the data of a trade. Given the ticket of the trade and the new data to update.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ticket` _int_ - The ticket of the trade to update.
|
||||
- `**kwargs` - The new data to update.
|
||||
|
||||
<a id="trades_manager.TradeManager.values"></a>
|
||||
|
||||
#### values
|
||||
|
||||
```python
|
||||
def values() -> tuple[TradeData, ...]
|
||||
```
|
||||
|
||||
Returns the values of the data.
|
||||
|
||||
<a id="trades_manager.TradeManager.keys"></a>
|
||||
|
||||
#### keys
|
||||
|
||||
```python
|
||||
def keys() -> tuple[int, ...]
|
||||
```
|
||||
|
||||
Returns the keys of the data.
|
||||
|
||||
<a id="trades_manager.TradeManager.items"></a>
|
||||
|
||||
#### items
|
||||
|
||||
```python
|
||||
def items() -> tuple[tuple[int, TradeData], ...]
|
||||
```
|
||||
|
||||
Returns the items of the data.
|
||||
|
||||
<a id="trades_manager.TradeManager.to_dict"></a>
|
||||
|
||||
#### to\_dict
|
||||
|
||||
```python
|
||||
def to_dict()
|
||||
```
|
||||
|
||||
Convert the data to a dictionary.
|
||||
|
||||
<a id="trades_manager.PositionsManager"></a>
|
||||
|
||||
## PositionsManager Objects
|
||||
|
||||
```python
|
||||
class PositionsManager(TradeManager)
|
||||
```
|
||||
|
||||
A class to manage the open positions during a backtest. It is a subclass of TradeManager. It has an additional
|
||||
attribute _open_positions to store the open positions. It also has a margins attribute to store the margins of the
|
||||
open positions. It overrides some methods of the TradeManager class to manage the open positions.
|
||||
|
||||
**Attributes**:
|
||||
|
||||
- `_open_positions` _set[int]_ - The open positions.
|
||||
- `margins` _dict[int, float]_ - The margins of the open positions.
|
||||
|
||||
<a id="trades_manager.PositionsManager.__init__"></a>
|
||||
|
||||
#### \_\_init\_\_
|
||||
|
||||
```python
|
||||
def __init__(*,
|
||||
data: dict = None,
|
||||
open_positions: set[int] = None,
|
||||
margins: dict = None)
|
||||
```
|
||||
|
||||
Positions manager manages the open positions during a backtest. It is a subclass of TradeManager. It has an
|
||||
additional attribute _open_positions to store the open positions. It also has a margins attribute to store the
|
||||
margins of the open positions. It overrides some methods of the TradeManager class to manage the open positions.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `data` _dict, optional_ - The data to store the trades. This used for continuation of the backtesting, if it
|
||||
was stopped with some open positions.
|
||||
|
||||
- `open_positions` _set, optional_ - The open positions. Defaults to None.
|
||||
|
||||
- `margins` _dict, optional_ - The margins of the open positions. Defaults to None.
|
||||
|
||||
<a id="trades_manager.PositionsManager.margin"></a>
|
||||
|
||||
#### margin
|
||||
|
||||
```python
|
||||
@property
|
||||
def margin()
|
||||
```
|
||||
|
||||
Returns the total margin of all open positions
|
||||
|
||||
<a id="trades_manager.PositionsManager.close"></a>
|
||||
|
||||
#### close
|
||||
|
||||
```python
|
||||
def close(*, ticket: int) -> bool
|
||||
```
|
||||
|
||||
Close a position. Given the ticket of the position to close.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ticket` _int_ - The ticket of the position to close.
|
||||
|
||||
<a id="trades_manager.PositionsManager.get_margin"></a>
|
||||
|
||||
#### get\_margin
|
||||
|
||||
```python
|
||||
def get_margin(*, ticket: int) -> float
|
||||
```
|
||||
|
||||
Get the margin of a position. Given the ticket of the position.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ticket` _int_ - The ticket of the position.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- `float` - The margin of the position.
|
||||
|
||||
<a id="trades_manager.PositionsManager.delete_margin"></a>
|
||||
|
||||
#### delete\_margin
|
||||
|
||||
```python
|
||||
def delete_margin(*, ticket: int)
|
||||
```
|
||||
|
||||
Delete the margin of a position. Given the ticket of the position.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ticket` _int_ - The ticket of the position.
|
||||
|
||||
<a id="trades_manager.PositionsManager.set_margin"></a>
|
||||
|
||||
#### set\_margin
|
||||
|
||||
```python
|
||||
def set_margin(*, ticket: int, margin: float)
|
||||
```
|
||||
|
||||
Set the margin of a position. Given the ticket of the position and the margin.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ticket` _int_ - The ticket of the position.
|
||||
- `margin` _float_ - The margin of the position
|
||||
|
||||
<a id="trades_manager.PositionsManager.positions_get"></a>
|
||||
|
||||
#### positions\_get
|
||||
|
||||
```python
|
||||
def positions_get(*,
|
||||
ticket: int = None,
|
||||
symbol: str = None,
|
||||
group: None = None) -> tuple[TradePosition, ...]
|
||||
```
|
||||
|
||||
Get positions. Given the ticket, symbol, or group of the positions.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `ticket` _int_ - The ticket of the position.
|
||||
- `symbol` _str_ - The symbol of the position.
|
||||
- `group` _str_ - The group
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
tuple[TradePosition, ...]: The positions
|
||||
|
||||
<a id="trades_manager.PositionsManager.positions_total"></a>
|
||||
|
||||
#### positions\_total
|
||||
|
||||
```python
|
||||
def positions_total() -> int
|
||||
```
|
||||
|
||||
Get the total number of open positions.
|
||||
|
||||
**Returns**:
|
||||
|
||||
- `int` - The total number of open positions.
|
||||
|
||||
<a id="trades_manager.PositionsManager.open_positions"></a>
|
||||
|
||||
#### open\_positions
|
||||
|
||||
```python
|
||||
@property
|
||||
def open_positions() -> tuple[TradePosition, ...]
|
||||
```
|
||||
|
||||
Returns the open positions.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
tuple[TradePosition, ...]: The open positions.
|
||||
|
||||
<a id="trades_manager.OrdersManager"></a>
|
||||
|
||||
## OrdersManager Objects
|
||||
|
||||
```python
|
||||
class OrdersManager(TradeManager)
|
||||
```
|
||||
|
||||
Managers orders data during a backtest. It is a subclass of TradeManager. It manages access to the historical
|
||||
orders data
|
||||
|
||||
<a id="trades_manager.OrdersManager.get_orders_range"></a>
|
||||
|
||||
#### get\_orders\_range
|
||||
|
||||
```python
|
||||
def get_orders_range(*, date_from: float,
|
||||
date_to: float) -> tuple[TradeData, ...]
|
||||
```
|
||||
|
||||
Get orders within a date range. Given the start and end date of the range.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `date_from` _float_ - The start date of the range.
|
||||
- `date_to` _float_ - The end date of the range.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
tuple[TradeData, ...]: The orders within the date range.
|
||||
|
||||
<a id="trades_manager.OrdersManager.history_orders_get"></a>
|
||||
|
||||
#### history\_orders\_get
|
||||
|
||||
```python
|
||||
def history_orders_get(*,
|
||||
date_from: float | datetime = None,
|
||||
date_to: float | datetime = None,
|
||||
group: str = "",
|
||||
ticket: int = None,
|
||||
position: int = None) -> tuple[TradeOrder, ...]
|
||||
```
|
||||
|
||||
Get historical orders. Given the start and end date of the range, the group, ticket, or position of the
|
||||
orders.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `date_from` _float, datetime_ - The start date of the range.
|
||||
- `date_to` _float, datetime_ - The end date of the range.
|
||||
- `group` _str_ - The group of the orders.
|
||||
- `ticket` _int_ - The ticket of the order.
|
||||
- `position` _int_ - The position of the order.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
tuple[TradeOrder, ...]: The historical orders.
|
||||
|
||||
<a id="trades_manager.OrdersManager.history_orders_total"></a>
|
||||
|
||||
#### history\_orders\_total
|
||||
|
||||
```python
|
||||
def history_orders_total(*, date_from: datetime | float,
|
||||
date_to: datetime | float) -> int
|
||||
```
|
||||
|
||||
Get the total number of historical orders. Given the start and end date of the range.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `date_from` _datetime, float_ - The start date of the range.
|
||||
- `date_to` _datetime, float_ - The end date of the range.
|
||||
|
||||
<a id="trades_manager.DealsManager"></a>
|
||||
|
||||
## DealsManager Objects
|
||||
|
||||
```python
|
||||
class DealsManager(TradeManager)
|
||||
```
|
||||
|
||||
<a id="trades_manager.DealsManager.get_deals_range"></a>
|
||||
|
||||
#### get\_deals\_range
|
||||
|
||||
```python
|
||||
def get_deals_range(*, date_from: float,
|
||||
date_to: float) -> tuple[TradeData, ...]
|
||||
```
|
||||
|
||||
Get deals within a date range. Given the start and end date of the range.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `date_from` _float_ - The start date of the range.
|
||||
- `date_to` _float_ - The end date of the range.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
tuple[TradeData, ...]: The deals within the date range.
|
||||
|
||||
<a id="trades_manager.DealsManager.history_deals_get"></a>
|
||||
|
||||
#### history\_deals\_get
|
||||
|
||||
```python
|
||||
def history_deals_get(*,
|
||||
date_from: float | datetime = None,
|
||||
date_to: float | datetime = None,
|
||||
group: str = "",
|
||||
ticket: int = None,
|
||||
position: int = None) -> tuple[TradeDeal, ...]
|
||||
```
|
||||
|
||||
History deals get. Given the start and end date of the range, the group, ticket, or position of the deals.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `date_from` _float, datetime_ - The start date of the range.
|
||||
- `date_to` _float, datetime_ - The end date of the range.
|
||||
- `group` _str_ - The group of the deals.
|
||||
- `ticket` _int_ - The ticket of the deal.
|
||||
- `position` _int_ - The position of the deal.
|
||||
|
||||
<a id="trades_manager.DealsManager.history_deals_total"></a>
|
||||
|
||||
#### history\_deals\_total
|
||||
|
||||
```python
|
||||
def history_deals_total(*, date_from: datetime | float,
|
||||
date_to: datetime | float) -> int
|
||||
```
|
||||
|
||||
Get the total number of historical deals. Given the start and end date of the range
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- `date_from` _datetime, float_ - The start date of the range.
|
||||
- `date_to` _datetime, float_ - The end date of the range.
|
||||
|
||||
|
||||
**Returns**:
|
||||
|
||||
- `int` - The total number of historical deals.
|
||||
|
||||
+53
-27
@@ -1,14 +1,17 @@
|
||||
# Base Class
|
||||
# Base
|
||||
|
||||
## Table of Contents
|
||||
- [Base](#base)
|
||||
- [set\_attributes](#base.set_attributes)
|
||||
- [annotations](#base.annotations)
|
||||
- [get\_dict](#base.get_dict)
|
||||
- [class\_vars](#base.class_vars)
|
||||
- [dict](#base.dict)
|
||||
- [Base](#base.base)
|
||||
- [set_attributes](#base.set_attributes)
|
||||
- [annotations](#base.annotations)
|
||||
- [get_dict](#base.get_dict)
|
||||
- [class_vars](#base.class_vars)
|
||||
- [dict](#base.dict)
|
||||
|
||||
<a id="base"></a>
|
||||
- [_Base](#_base._base)
|
||||
|
||||
|
||||
<a id="base.base"></a>
|
||||
### Base
|
||||
```python
|
||||
class Base
|
||||
@@ -16,11 +19,12 @@ 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.
|
||||
|
||||
#### Class Attributes
|
||||
| Name | Type | Description | Default |
|
||||
|----------|--------------|-------------------------------------|---------|
|
||||
| `mt5` | `MetaTrader` | An instance of the MetaTrader class | |
|
||||
| `config` | `Config` | An instance of the Config class | |
|
||||
#### Attributes:
|
||||
| Name | Type | Description |
|
||||
|-----------|-------|------------------------------------------------------------------------------------------------------|
|
||||
| `exclude` | `set` | A set of attributes to be excluded when retrieving attributes using the *get_dict* and *dict* method |
|
||||
| `include` | `set` | A set of attributes to be included when retrieving attributes using the *get_dict* and *dict* method |
|
||||
|
||||
|
||||
<a id="base.__init__"></a>
|
||||
### __init__
|
||||
@@ -32,25 +36,28 @@ def __init__(**kwargs)
|
||||
|----------|-------|---------------------------------------------------|
|
||||
| `kwargs` | `Any` | Object attributes and values as keyword arguments |
|
||||
|
||||
|
||||
<a id="base.set_attributes"></a>
|
||||
### set_attributes
|
||||
```python
|
||||
def set_attributes(**kwargs)
|
||||
```
|
||||
Set keyword arguments as object attributes. Only sets attributes that have been annotated on the class body.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|---------------------------------------------------|
|
||||
| `kwargs` | `Any` | Object attributes and values as keyword arguments |
|
||||
|
||||
#### Raises
|
||||
#### Raises:
|
||||
| Exception | Description |
|
||||
|------------------|-----------------------------------------------------------------------------------|
|
||||
| `AttributeError` | When assigning an attribute that does not belong to the class or any parent class |
|
||||
|
||||
#### Notes
|
||||
#### Notes:
|
||||
Only sets attributes that have been annotated on the class body.
|
||||
|
||||
|
||||
<a id="base.annotations"></a>
|
||||
### annotations
|
||||
```python
|
||||
@@ -59,43 +66,49 @@ Only sets attributes that have been annotated on the class body.
|
||||
def annotations() -> dict
|
||||
```
|
||||
Class annotations from all ancestor classes and the current class.
|
||||
#### Returns
|
||||
| Type | Description |
|
||||
|--------|-----------------------------------|
|
||||
| `dict` | A dictionary of class annotations |
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|------------------|-----------------------------------|
|
||||
| `dict[str, Any]` | A dictionary of class annotations |
|
||||
|
||||
|
||||
<a id="base.get_dict"></a>
|
||||
#### get\_dict
|
||||
#### get_dict
|
||||
```python
|
||||
def get_dict(exclude: set = None, include: set = None) -> dict
|
||||
```
|
||||
Returns class attributes as a dict, with the ability to filter
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-----------|-------|------------------------------------|
|
||||
| `exclude` | `set` | A set of attributes to be excluded |
|
||||
| `include` | `set` | Specific attributes to be returned |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------------|
|
||||
| `dict` | A dictionary of specified class attributes |
|
||||
|
||||
#### Notes
|
||||
#### Notes:
|
||||
You can only set either of include or exclude. If you set both, include will take precedence
|
||||
|
||||
|
||||
<a id="base.class_vars"></a>
|
||||
### class\_vars
|
||||
### class_vars
|
||||
```python
|
||||
@property
|
||||
@cache
|
||||
def class_vars()
|
||||
```
|
||||
Annotated class attributes
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|-------------------------------------------------------------------------------------------|
|
||||
| `dict` | A dictionary of available class attributes in all ancestor classes and the current class. |
|
||||
|
||||
|
||||
<a id="base.dict"></a>
|
||||
### dict
|
||||
```python
|
||||
@@ -103,7 +116,20 @@ Annotated class attributes
|
||||
def dict() -> dict
|
||||
```
|
||||
All instance and class attributes as a dictionary, except those excluded in the Meta class.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|-----------------------------------------------|
|
||||
| `dict` | A dictionary of instance and class attributes |
|
||||
|
||||
|
||||
<a id="_base._base></a>
|
||||
### _Base(Base)
|
||||
Base class that provides access to the MetaTrader and Config classes as well as the MetaBackTester class for
|
||||
backtesting mode.
|
||||
|
||||
#### Attributes:
|
||||
| Name | Type | Description | Default |
|
||||
|----------|--------------|-------------------------------------|---------|
|
||||
| `mt5` | `MetaTrader` | An instance of the MetaTrader class | |
|
||||
| `config` | `Config` | An instance of the Config class | |
|
||||
|
||||
+326
-239
@@ -2,41 +2,43 @@
|
||||
The MetaTrader Class provides an asynchronous wrapper around the MetaTrader5 API.
|
||||
|
||||
## Table of Contents
|
||||
- [MetaTrader](#MetaTrader)
|
||||
- [\_\_aenter\_\_](#__aenter__)
|
||||
- [\_\_aexit\_\_](#__aexit__)
|
||||
- [login](#login)
|
||||
- [initialize](#initialize)
|
||||
- [shutdown](#shutdown)
|
||||
- [version](#version)
|
||||
- [account\_info](#account_info)
|
||||
- [terminal\_info](#terminal_info)
|
||||
- [last\_error](#last_error)
|
||||
- [symbols\_total](#symbols_total)
|
||||
- [symbols\_get](#symbols_get)
|
||||
- [symbol\_info](#symbol_info)
|
||||
- [symbol\_info\_tick](#symbol_info_tick)
|
||||
- [symbol\_select](#symbol_select)
|
||||
- [market\_book\_add](#market_book_add)
|
||||
- [market\_book\_get](#market_book_get)
|
||||
- [market\_book\_release](#market_book_release)
|
||||
- [copy\_rates\_from](#copy_rates_from)
|
||||
- [copy\_rates\_from\_pos](#copy_rates_from_pos)
|
||||
- [copy\_rates\_range](#copy_rates_range)
|
||||
- [copy\_ticks\_from](#copy_ticks_from)
|
||||
- [copy\_ticks\_range](#copy_ticks_range)
|
||||
- [orders\_total](#orders_total)
|
||||
- [orders\_get](#orders_get)
|
||||
- [order\_calc\_margin](#order_calc_margin)
|
||||
- [order\_calc\_profit](#order_calc_profit)
|
||||
- [order\_check](#order_check)
|
||||
- [order\_send](#order_send)
|
||||
- [positions\_total](#positions_total)
|
||||
- [positions\_get](#positions_get)
|
||||
- [history\_orders\_total](#history_orders_total)
|
||||
- [history\_orders\_get](#history_orders_get)
|
||||
- [history\_deals\_total](#history_deals_total)
|
||||
- [history\_deals\_get](#history_deals_get)
|
||||
- [MetaTrader](#meta_trader.meta_trader)
|
||||
- [\__aenter\__](#meta_trader.__aenter__)
|
||||
- [\__aexit\__](#meta_trader.__aexit__)
|
||||
- [login](#meta_trader.login)
|
||||
- [initialize](#meta_trader.initialize)
|
||||
- [login_sync](#meta_trader.login_sync)
|
||||
- [initialize_sync](#meta_trader.initialize_sync)
|
||||
- [shutdown](#meta_trader.shutdown)
|
||||
- [version](#meta_trader.version)
|
||||
- [account_info](#meta_trader.account_info)
|
||||
- [terminal_info](#meta_trader.terminal_info)
|
||||
- [last_error](#meta_trader.last_error)
|
||||
- [symbols_total](#meta_trader.symbols_total)
|
||||
- [symbols_get](#meta_trader.symbols_get)
|
||||
- [symbol_info](#meta_trader.symbol_info)
|
||||
- [symbol_info_tick](#meta_trader.symbol_info_tick)
|
||||
- [symbol_select](#meta_trader.symbol_select)
|
||||
- [market_book_add](#meta_trader.market_book_add)
|
||||
- [market_book_get](#meta_trader.market_book_get)
|
||||
- [market_book_release](#meta_trader.market_book_release)
|
||||
- [copy_rates_from](#meta_trader.copy_rates_from)
|
||||
- [copy_rates_from_pos](#meta_trader.copy_rates_from_pos)
|
||||
- [copy_rates_range](#meta_trader.copy_rates_range)
|
||||
- [copy_ticks_from](#meta_trader.copy_ticks_from)
|
||||
- [copy_ticks_range](#meta_trader.copy_ticks_range)
|
||||
- [orders_total](#meta_trader.orders_total)
|
||||
- [orders_get](#meta_trader.orders_get)
|
||||
- [order_calc_margin](#meta_trader.order_calc_margin)
|
||||
- [order_calc_profit](#meta_trader.order_calc_profit)
|
||||
- [order_check](#meta_trader.order_check)
|
||||
- [order_send](#meta_trader.order_send)
|
||||
- [positions_total](#meta_trader.positions_total)
|
||||
- [positions_get](#meta_trader.positions_get)
|
||||
- [history_orders_total](#meta_trader.history_orders_total)
|
||||
- [history_orders_get](#meta_trader.history_orders_get)
|
||||
- [history_deals_total](#meta_trader.history_deals_total)
|
||||
- [history_deals_get](#meta_trader.history_deals_get)
|
||||
|
||||
<a id="meta_trader.meta_trader"></a>
|
||||
### MetaTrader
|
||||
@@ -51,12 +53,13 @@ It provides methods for connecting to the MetaTrader terminal and retrieving dat
|
||||
|-------|-------|--------------------------------------------------------|------------------------|
|
||||
| error | Error | The last error encountered by the MetaTrader terminal. | Error(1, 'Successful') |
|
||||
|
||||
#### Notes
|
||||
#### Notes:
|
||||
All the attributes, enums and constants of the MetaTrader5 class are also available here. Although, they are more easily
|
||||
accessible and used via the various enums and models defined in the module.
|
||||
|
||||
<a id="__aenter__"></a>
|
||||
#### \_\_aenter\_\_
|
||||
|
||||
<a id="meta_trader.__aenter__"></a>
|
||||
### \__aenter\__
|
||||
```python
|
||||
async def __aenter__() -> 'MetaTrader'
|
||||
```
|
||||
@@ -68,405 +71,478 @@ Initializes the connection to the MetaTrader terminal.
|
||||
|--------------|-------------------------------------|
|
||||
| `MetaTrader` | An instance of the MetaTrader class |
|
||||
|
||||
<a id="__aexit__"></a>
|
||||
#### \_\_aexit\_\_
|
||||
|
||||
<a id="meta_trader.__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="login"></a>
|
||||
#### login
|
||||
|
||||
<a id="meta_trader.login"></a>
|
||||
### login
|
||||
```python
|
||||
async def login(login: int,
|
||||
password: str,
|
||||
server: str,
|
||||
timeout: int = 60000) -> bool
|
||||
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.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|------------|-------|--------------------------------------------|
|
||||
| `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:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
<a id="initialize"></a>
|
||||
#### initialize
|
||||
|
||||
<a id="meta_trader.login_sync"></a>
|
||||
#### login_sync
|
||||
```python
|
||||
async def initialize(path: str = "",
|
||||
login: int = 0,
|
||||
password: str = "",
|
||||
server: str = "",
|
||||
timeout: int | None = None,
|
||||
portable=False) -> bool
|
||||
async def login_sync(*, login: int, password: str, server: str, timeout: int = 60000) -> bool
|
||||
```
|
||||
Initializes the connection to the MetaTrader terminal. All parameters are optional.
|
||||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------------|-----------------|----------------------------------------------------------|
|
||||
| `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` or `None` | The timeout for the connection in seconds. |
|
||||
| `portable` | `bool` | If True, the terminal will be launched in portable mode. |
|
||||
#### Returns
|
||||
A synchronous version of the login method.
|
||||
Connects to the MetaTrader terminal using the specified login, password and server.
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|------------|-------|--------------------------------------------|
|
||||
| `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:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
<a id="shutdown"></a>
|
||||
#### shutdown
|
||||
|
||||
<a id="meta_trader.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.
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|------------|---------------|----------------------------------------------------------|
|
||||
| `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 \| None` | The timeout for the connection in seconds. |
|
||||
| `portable` | `bool` | If True, the terminal will be launched in portable mode. |
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
|
||||
<a id="meta_trader.initialize_sync"></a>
|
||||
### initialize_sync
|
||||
```python
|
||||
async def initialize_sync(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.
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|------------|---------------|----------------------------------------------------------|
|
||||
| `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 \| None` | The timeout for the connection in seconds. |
|
||||
| `portable` | `bool` | If True, the terminal will be launched in portable mode. |
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
|
||||
<a id="meta_trader.shutdown"></a>
|
||||
### shutdown
|
||||
```python
|
||||
async def shutdown() -> None
|
||||
```
|
||||
Closes the connection to the MetaTrader terminal.
|
||||
|
||||
<a id="version"></a>
|
||||
#### version
|
||||
|
||||
<a id="meta_trader.version"></a>
|
||||
### version
|
||||
```python
|
||||
async def version() -> tuple[int, int, str] | None
|
||||
```
|
||||
Returns the version of the MetaTrader terminal.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|------------------------|-----------------------------------------------------------------------------------------------|
|
||||
| `tuple[int, int, str]` | A tuple of the MetaTrader terminal version. `Terminal Version`, `Build`, `Build Release Date` |
|
||||
|
||||
<a id="account_info"></a>
|
||||
#### account\_info
|
||||
|
||||
<a id="meta_trader.account_info"></a>
|
||||
### account_info
|
||||
```python
|
||||
async def account_info() -> AccountInfo | None
|
||||
```
|
||||
Returns the account information for the connected account.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|---------------|--------------------------------------|
|
||||
| `AccountInfo` | An instance of the AccountInfo class |
|
||||
|
||||
<a id="terminal_info"></a>
|
||||
#### terminal\_info
|
||||
|
||||
<a id="meta_trader.terminal_info"></a>
|
||||
### terminal_info
|
||||
```python
|
||||
async def terminal_info() -> TerminalInfo | None
|
||||
```
|
||||
|
||||
Returns the terminal information for the connected terminal.
|
||||
#### Returns
|
||||
### Returns
|
||||
| Type | Description |
|
||||
|----------------|------------------------------------------------|
|
||||
| `TerminalInfo` | An instance of the TerminalInfo class. A tuple |
|
||||
|
||||
<a id="last_error"></a>
|
||||
#### last\_error
|
||||
|
||||
<a id="meta_trader.last_error"></a>
|
||||
### last_error
|
||||
```python
|
||||
async def last_error() -> tuple[int, str]
|
||||
```
|
||||
Returns the last error code and description.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------------------|-------------------------------------------------|
|
||||
| `tuple[int, str]` | A tuple of the last error code and description. |
|
||||
|
||||
<a id="symbols_total"></a>
|
||||
#### symbols\_total
|
||||
|
||||
<a id="meta_trader.symbols_total"></a>
|
||||
### symbols_total
|
||||
```python
|
||||
async def symbols_total() -> int
|
||||
```
|
||||
Returns the total number of symbols.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------|------------------------------|
|
||||
| `int` | The total number of symbols. |
|
||||
|
||||
<a id="symbols_get"></a>
|
||||
#### symbols\_get
|
||||
|
||||
<a id="meta_trader.symbols_get"></a>
|
||||
### symbols_get
|
||||
```python
|
||||
async def symbols_get(group: str = "") -> tuple[SymbolInfo] | None
|
||||
```
|
||||
Returns the symbol information for all symbols or for a specified group.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|---------|-------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `group` | `str` | The group name. Optional named parameter. If the group is specified, the function returns only symbols meeting a specified criteria for a symbol name. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|---------------------|--------------------------------|
|
||||
| `tuple[SymbolInfo]` | A tuple of SymbolInfo objects. |
|
||||
|
||||
|
||||
<a id="symbol_info"></a>
|
||||
#### symbol\_info
|
||||
<a id="meta_trader.symbol_info"></a>
|
||||
### symbol_info
|
||||
```python
|
||||
async def symbol_info(symbol: str) -> SymbolInfo | None
|
||||
```
|
||||
Returns the symbol information for the specified symbol.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------------|--------------------------------------|
|
||||
| `SymbolInfo` | An instance of the SymbolInfo class. |
|
||||
|
||||
<a id="symbol_info_tick"></a>
|
||||
#### symbol\_info\_tick
|
||||
|
||||
<a id="meta_trader.symbol_info_tick"></a>
|
||||
### symbol_info_tick
|
||||
```python
|
||||
async def symbol_info_tick(symbol: str) -> Tick | None
|
||||
```
|
||||
Returns the latest tick for the specified symbol.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------|
|
||||
| `Tick` | An instance of the Tick class. |
|
||||
|
||||
<a id="symbol_select"></a>
|
||||
#### symbol\_select
|
||||
|
||||
<a id="meta_trader.symbol_select"></a>
|
||||
### symbol_select
|
||||
```python
|
||||
async def symbol_select(symbol: str, enable: bool) -> bool
|
||||
```
|
||||
Selects or unselects the specified symbol in the Market Watch window.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|--------|--------------------------------------------------------------------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `enable` | `bool` | If True, the symbol will be selected. If False, the symbol will be unselected. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
<a id="market_book_add"></a>
|
||||
#### market\_book\_add
|
||||
|
||||
<a id="meta_trader.market_book_add"></a>
|
||||
### market_book_add
|
||||
```python
|
||||
async def market_book_add(symbol: str) -> bool
|
||||
```
|
||||
Adds the specified symbol to the market book.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
|
||||
<a id="market_book_get"></a>
|
||||
#### market\_book\_get
|
||||
<a id="meta_trader.market_book_get"></a>
|
||||
### market_book_get
|
||||
```python
|
||||
async def market_book_get(symbol: str) -> tuple[BookInfo] | None
|
||||
```
|
||||
Returns the market depth for the specified symbol.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------------------|------------------------------|
|
||||
| `tuple[BookInfo]` | A tuple of BookInfo objects. |
|
||||
|
||||
<a id="market_book_release"></a>
|
||||
#### market\_book\_release
|
||||
|
||||
<a id="meta_trader.market_book_release"></a>
|
||||
### market_book_release
|
||||
```python
|
||||
async def market_book_release(symbol: str) -> bool
|
||||
```
|
||||
Removes the specified symbol from the market book.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------|--------------------------------------|
|
||||
| `bool` | True if successful, False otherwise. |
|
||||
|
||||
<a id="copy_rates_from"></a>
|
||||
#### copy\_rates\_from
|
||||
|
||||
<a id="meta_trader.copy_rates_from"></a>
|
||||
### copy_rates_from
|
||||
```python
|
||||
import numpy
|
||||
|
||||
|
||||
async def copy_rates_from(symbol: str,
|
||||
timeframe: TimeFrame,
|
||||
date_from: datetime | int,
|
||||
async def copy_rates_from(symbol: str, timeframe: TimeFrame, date_from: datetime | int,
|
||||
count: int) -> numpy.ndarray | None
|
||||
```
|
||||
Returns the OHLCV rates for the specified symbol and timeframe starting from the specified date.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|--------------------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `timeframe` | `TimeFrame` | The timeframe. |
|
||||
| `date_from` | `datetime` or `int` | The date to start from. |
|
||||
| `count` | `int` | The number of rates to return. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-----------------|-------------------------------|
|
||||
| `numpy.ndarray` | A numpy array of OHLCV rates. |
|
||||
|
||||
<a id="copy_rates_from_pos"></a>
|
||||
#### copy\_rates\_from\_pos
|
||||
|
||||
<a id="meta_trader.copy_rates_from_pos"></a>
|
||||
### copy_rates_from_pos
|
||||
```python
|
||||
async def copy_rates_from_pos(symbol: str,
|
||||
timeframe: TimeFrame,
|
||||
start_pos: int,
|
||||
count: int) -> numpy.ndarray | None
|
||||
async def copy_rates_from_pos(symbol: str, timeframe: TimeFrame, start_pos: int, count: int) -> numpy.ndarray | None
|
||||
```
|
||||
Returns the OHLCV rates for the specified symbol and timeframe starting from the specified position.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|-------------|--------------------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `timeframe` | `TimeFrame` | The timeframe. |
|
||||
| `start_pos` | `int` | The position to start from. |
|
||||
| `count` | `int` | The number of rates to return. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-----------------|-------------------------------|
|
||||
| `numpy.ndarray` | A numpy array of OHLCV rates. |
|
||||
|
||||
<a id="copy_rates_range"></a>
|
||||
#### copy\_rates\_range
|
||||
|
||||
<a id="meta_trader.copy_rates_range"></a>
|
||||
### copy_rates_range
|
||||
```python
|
||||
async def copy_rates_range(symbol: str,
|
||||
timeframe: TimeFrame,
|
||||
date_from: datetime | int,
|
||||
async def copy_rates_range(symbol: str, timeframe: TimeFrame, date_from: datetime | int,
|
||||
date_to: datetime | int) -> numpy.ndarray | None
|
||||
```
|
||||
Returns the OHLCV rates for the specified symbol and timeframe between the specified dates.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `timeframe` | `TimeFrame` | The timeframe. |
|
||||
| `date_from` | `datetime` or `int` | The start date. |
|
||||
| `date_to` | `datetime` or `int` | The end date. |
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-----------------|-------------------------------|
|
||||
| `numpy.ndarray` | A numpy array of OHLCV rates. |
|
||||
|
||||
<a id="copy_ticks_from"></a>
|
||||
#### copy\_ticks\_from
|
||||
|
||||
<a id="meta_trader.copy_ticks_from"></a>
|
||||
### copy_ticks_from
|
||||
```python
|
||||
async def copy_ticks_from(symbol: str,
|
||||
date_from: datetime | int,
|
||||
count: int,
|
||||
flags: CopyTicks) -> tuple[Tick] | None
|
||||
async def copy_ticks_from(symbol: str, date_from: datetime | int, count: int, flags: CopyTicks) -> tuple[Tick] | None
|
||||
```
|
||||
Returns the ticks for the specified symbol starting from the specified date.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|--------------------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `date_from` | `datetime` or `int` | The date to start from. |
|
||||
| `count` | `int` | The number of ticks to return. |
|
||||
| `flags` | `CopyTicks` | The CopyTicks flags. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|---------------|--------------------------|
|
||||
| `tuple[Tick]` | A tuple of Tick objects. |
|
||||
|
||||
<a id="copy_ticks_range"></a>
|
||||
#### copy\_ticks\_range
|
||||
|
||||
<a id="meta_trader.copy_ticks_range"></a>
|
||||
### copy_ticks_range
|
||||
```python
|
||||
async def copy_ticks_range(symbol: str,
|
||||
date_from: datetime | int,
|
||||
date_to: datetime | int,
|
||||
async def copy_ticks_range(symbol: str, date_from: datetime | int, date_to: datetime | int,
|
||||
flags: CopyTicks) -> tuple[Tick] | None
|
||||
```
|
||||
Returns the ticks for the specified symbol between the specified dates.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|----------------------|
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `date_from` | `datetime` or `int` | The start date. |
|
||||
| `date_to` | `datetime` or `int` | The end date. |
|
||||
| `flags` | `CopyTicks` | The CopyTicks flags. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|---------------|--------------------------|
|
||||
| `tuple[Tick]` | A tuple of Tick objects. |
|
||||
|
||||
<a id="orders_total"></a>
|
||||
#### orders\_total
|
||||
|
||||
<a id="meta_trader.orders_total"></a>
|
||||
### orders_total
|
||||
```python
|
||||
async def orders_total() -> int
|
||||
```
|
||||
Returns the total number of active orders.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------|------------------------------------|
|
||||
| `int` | The total number of active orders. |
|
||||
|
||||
<a id="orders_get"></a>
|
||||
#### orders\_get
|
||||
|
||||
<a id="meta_trader.orders_get"></a>
|
||||
### orders_get
|
||||
```python
|
||||
async def orders_get(group: str = "",
|
||||
ticket: int = 0,
|
||||
symbol: str = "") -> tuple[TradeOrder] | None
|
||||
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
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `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. |
|
||||
| `symbol` | `str` | Symbol name. Optional named parameter. If a symbol is specified, the ticket parameter is ignored. |
|
||||
#### Returns
|
||||
| Type | Description |
|
||||
|---------------------|------------------------------------------------------|
|
||||
| `tuple[TradeOrder]` | A tuple of active trade orders as TradeOrder objects |
|
||||
#### Returns
|
||||
| Type | Description |
|
||||
|---------------------|------------------------------------------------------|
|
||||
| `tuple[TradeOrder]` | A tuple of active trade orders as TradeOrder objects |
|
||||
|
||||
<a id="order_calc_margin"></a>
|
||||
#### order\_calc\_margin
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------------------------|------------------------------------------------------|
|
||||
| `tuple[TradeOrder, ...]` | A tuple of active trade orders as TradeOrder objects |
|
||||
|
||||
|
||||
<a id="meta_trader.order_calc_margin"></a>
|
||||
### order_calc_margin
|
||||
```python
|
||||
async def order_calc_margin(action: OrderType,
|
||||
symbol: str,
|
||||
volume: float,
|
||||
price: float) -> float | None
|
||||
async def order_calc_margin(action: OrderType, symbol: str, volume: float, price: float) -> float | None
|
||||
```
|
||||
Calculates the margin required to open a trade.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------------|-------------------|
|
||||
| `action` | `OrderType` | The order type. |
|
||||
| `symbol` | `str` | The symbol name. |
|
||||
| `volume` | `float` | The order volume. |
|
||||
| `price` | `float` | The order price. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|---------|--------------------------------------|
|
||||
| `float` | The margin required to open a trade. |
|
||||
|
||||
<a id="order_calc_profit"></a>
|
||||
#### order\_calc\_profit
|
||||
|
||||
<a id="meta_trader.order_calc_profit"></a>
|
||||
### order_calc_profit
|
||||
```python
|
||||
async def order_calc_profit(action: OrderType,
|
||||
symbol: str,
|
||||
volume: float,
|
||||
price_open: float,
|
||||
async def order_calc_profit(action: OrderType, symbol: str, volume: float, price_open: float,
|
||||
price_close: float) -> float | None
|
||||
```
|
||||
Calculates the profit for a closed trade.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|---------------|-------------|------------------------|
|
||||
| `action` | `OrderType` | The order type. |
|
||||
@@ -474,103 +550,112 @@ Calculates the profit for a closed trade.
|
||||
| `volume` | `float` | The order volume. |
|
||||
| `price_open` | `float` | The order open price. |
|
||||
| `price_close` | `float` | The order close price. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|---------|--------------------------------|
|
||||
| `float` | The profit for a closed trade. |
|
||||
|
||||
<a id="order_check"></a>
|
||||
#### order\_check
|
||||
|
||||
<a id="meta_trader.order_check"></a>
|
||||
### order_check
|
||||
```python
|
||||
async def order_check(request: dict) -> OrderCheckResult
|
||||
```
|
||||
Checks the specified order for validity.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-----------|--------|--------------------|
|
||||
| `request` | `dict` | The order request. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------------------|--------------------------------------------|
|
||||
| `OrderCheckResult` | An instance of the OrderCheckResult class. |
|
||||
|
||||
<a id="order_send"></a>
|
||||
#### order\_send
|
||||
|
||||
<a id="meta_trader.order_send"></a>
|
||||
### order_send
|
||||
```python
|
||||
async def order_send(request: dict) -> OrderSendResult
|
||||
```
|
||||
Sends the specified order request to the MetaTrader terminal.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-----------|--------|--------------------|
|
||||
| `request` | `dict` | The order request. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------------------|-------------------------------------------|
|
||||
| `OrderSendResult` | An instance of the OrderSendResult class. |
|
||||
|
||||
<a id="positions_total"></a>
|
||||
#### positions\_total
|
||||
|
||||
<a id="meta_trader.positions_total"></a>
|
||||
### positions_total
|
||||
```python
|
||||
async def positions_total() -> int
|
||||
```
|
||||
Returns the total number of open positions.
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------|-------------------------------------|
|
||||
| `int` | The total number of open positions. |
|
||||
|
||||
|
||||
<a id="positions_get"></a>
|
||||
#### positions\_get
|
||||
<a id="meta_trader.positions_get"></a>
|
||||
### positions_get
|
||||
```python
|
||||
async def positions_get(group: str = "",
|
||||
ticket: int = 0,
|
||||
symbol: str = "") -> tuple[TradePosition] | None
|
||||
async def positions_get(group: str = "", ticket: int = 0, symbol: str = "") -> tuple[TradePosition, ...] | None
|
||||
```
|
||||
Returns the open positions with the ability to filter by symbol or ticket. There are three call options.
|
||||
Call without parameters. Return open positions on all symbols
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|-------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `group` | `str` | The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function returns only open positions meeting a specified criteria for a symbol name. |
|
||||
| `ticket` | `int` | Position ticket (POSITION_TICKET). Optional named parameter. |
|
||||
| `symbol` | `str` | Symbol name. Optional named parameter. If a symbol is specified, the ticket parameter is ignored. |
|
||||
#### Returns
|
||||
| Type | Description |
|
||||
|------------------------|----------------------------------------------------------|
|
||||
| `tuple[TradePosition]` | A tuple of open trade positions as TradePosition objects |
|
||||
|
||||
<a id="history_orders_total"></a>
|
||||
#### history\_orders\_total
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-----------------------------|----------------------------------------------------------|
|
||||
| `tuple[TradePosition, ...]` | A tuple of open trade positions as TradePosition objects |
|
||||
|
||||
|
||||
<a id="meta_trader.history_orders_total"></a>
|
||||
### history_orders_total
|
||||
```python
|
||||
async def history_orders_total(date_from: datetime | int,
|
||||
date_to: datetime | int) -> int
|
||||
async def history_orders_total(date_from: datetime | int, date_to: datetime | int) -> int
|
||||
```
|
||||
Returns the total number of closed orders for the specified period.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|-----------------|
|
||||
| `date_from` | `datetime` or `int` | The start date. |
|
||||
| `date_to` | `datetime` or `int` | The end date. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------|-------------------------------------------------------------|
|
||||
| `int` | The total number of closed orders for the specified period. |
|
||||
|
||||
|
||||
<a id="history_orders_get"></a>
|
||||
#### history\_orders\_get
|
||||
<a id="meta_trader.history_orders_get"></a>
|
||||
### history_orders_get
|
||||
```python
|
||||
async def history_orders_get(date_from: datetime | int = None,
|
||||
date_to: datetime | int = None,
|
||||
group: str = "",
|
||||
ticket: int = 0,
|
||||
position: int = 0) -> tuple[TradeOrder] | None
|
||||
async def history_orders_get(date_from: datetime | int = None, date_to: datetime | int = None, group: str = "",
|
||||
ticket: int = 0, position: int = 0) -> tuple[TradeOrder, ...] | None
|
||||
```
|
||||
Returns the closed orders for the specified period with the ability to filter by symbol or ticket. There are three call options.
|
||||
Call without parameters. Return closed orders on all symbols
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `date_from` | `datetime` or `int` | The start date. Optional named parameter. |
|
||||
@@ -578,40 +663,41 @@ Call without parameters. Return closed orders on all symbols
|
||||
| `group` | `str` | The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function returns only closed orders meeting a specified criteria for a symbol name. |
|
||||
| `ticket` | `int` | Order ticket (ORDER_TICKET). Optional named parameter. |
|
||||
| `position` | `int` | Position ticket (POSITION_TICKET). Optional named parameter. |
|
||||
#### Returns
|
||||
| Type | Description |
|
||||
|---------------------|------------------------------------------------------|
|
||||
| `tuple[TradeOrder]` | A tuple of closed trade orders as TradeOrder objects |
|
||||
|
||||
<a id="history_deals_total"></a>
|
||||
#### history\_deals\_total
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|--------------------------|------------------------------------------------------|
|
||||
| `tuple[TradeOrder, ...]` | A tuple of closed trade orders as TradeOrder objects |
|
||||
|
||||
|
||||
<a id="meta_trader.history_deals_total"></a>
|
||||
### history_deals_total
|
||||
```python
|
||||
async def history_deals_total(date_from: datetime | int,
|
||||
date_to: datetime | int) -> int
|
||||
async def history_deals_total(date_from: datetime | int, date_to: datetime | int) -> int
|
||||
```
|
||||
Returns the total number of closed deals for the specified period.
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|-----------------|
|
||||
| `date_from` | `datetime` or `int` | The start date. |
|
||||
| `date_to` | `datetime` or `int` | The end date. |
|
||||
#### Returns
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------|------------------------------------------------------------|
|
||||
| `int` | The total number of closed deals for the specified period. |
|
||||
|
||||
<a id="history_deals_get"></a>
|
||||
#### history\_deals\_get
|
||||
<a id="meta_trader.history_deals_get"></a>
|
||||
### history_deals_get
|
||||
```python
|
||||
async def history_deals_get(date_from: datetime | int = None,
|
||||
date_to: datetime | int = None,
|
||||
group: str = "",
|
||||
ticket: int = 0,
|
||||
position: int = 0) -> tuple[TradeDeal] | None
|
||||
async def history_deals_get(date_from: datetime | int = None, date_to: datetime | int = None, group: str = "",
|
||||
ticket: int = 0,position: int = 0) -> tuple[TradeDeal, ...] | None
|
||||
```
|
||||
Returns the closed deals for the specified period with the ability to filter by symbol or ticket. There are three call options.
|
||||
Call without parameters. Return closed deals on all symbols
|
||||
#### Parameters
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `date_from` | `datetime` or `int` | The start date. Optional named parameter. |
|
||||
@@ -619,7 +705,8 @@ Call without parameters. Return closed deals on all symbols
|
||||
| `group` | `str` | The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function returns only closed deals meeting a specified criteria for a symbol name. |
|
||||
| `ticket` | `int` | Order ticket (ORDER_TICKET). Optional named parameter. |
|
||||
| `position` | `int` | Position ticket (POSITION_TICKET). Optional named parameter. |
|
||||
#### Returns
|
||||
| Type | Description |
|
||||
|--------------------|----------------------------------------------------|
|
||||
| `tuple[TradeDeal]` | A tuple of closed trade deals as TradeDeal objects |
|
||||
|
||||
#### Returns:
|
||||
| Type | Description |
|
||||
|-------------------------|----------------------------------------------------|
|
||||
| `tuple[TradeDeal, ...]` | A tuple of closed trade deals as TradeDeal objects |
|
||||
|
||||
+122
-32
@@ -1,69 +1,126 @@
|
||||
# TaskQueue and QueueItem
|
||||
|
||||
## Table of Contents
|
||||
- [QueueItem](#queue_item)
|
||||
- [run](#run)
|
||||
- [QueueItem](#queue_item.queue_item)
|
||||
- [\__init\__](#queue_item.__init__)
|
||||
- [run](#queue_item.run)
|
||||
|
||||
- [TaskQueue](#task_queue)
|
||||
- [TaskQueue.add](#task_queue.add)
|
||||
- [TaskQueue.add_task](#task_queue.add_task)
|
||||
- [TaskQueue.worker](#task_queue.worker)
|
||||
- [TaskQueue.start](#task_queue.start)
|
||||
- [TaskQueue](#task_queue.task_queue)
|
||||
- [\__init\__](#task_queue.__init__)
|
||||
- [add](#task_queue.add)
|
||||
- [add_task](#task_queue.add_task)
|
||||
- [worker](#task_queue.worker)
|
||||
- [run](#task_queue.run)
|
||||
- [stop_queue](#task_queue.stop_queue)
|
||||
- [clean_up](#task_queue.clean_up)
|
||||
- [cancel](#task_queue.cancel)
|
||||
|
||||
|
||||
<a id="queue_item"></a>
|
||||
<a id="queue_item.queue_item"></a>
|
||||
### QueueItem
|
||||
```python
|
||||
class QueueItem:
|
||||
def __init__(self, task: Callable | Awaitable, *args, **kwargs):
|
||||
class QueueItem
|
||||
```
|
||||
A task to be executed by the `TaskQueue`. The task can be a callable or an awaitable. The task is wrapped as a
|
||||
A task to be executed by the `TaskQueue`. The task can be any coroutine callable. The task is wrapped as a
|
||||
`QueueItem` object, which is then added to the `TaskQueue` for execution. The arguments and keyword arguments are
|
||||
passed to the task when it is executed. All parameters are created as attributes of the `QueueItem` object.
|
||||
passed to the task when it is executed.
|
||||
|
||||
#### Attributes:
|
||||
| Name | Type | Description |
|
||||
|-----------------|---------------------------|-----------------------------------------------------------------------|
|
||||
| `task_item` | `Callable` \| `Coroutine` | A coroutine function to be executed by the `TaskQueue` |
|
||||
| `args` | `tuple[Any, ...]` | Positional arguments to be passed to the task_item |
|
||||
| `kwargs` | `dict[str, Any]` | Keyword arguments to be passed to the task_item |
|
||||
| `must_complete` | `bool` | If True, the item must be completed even if the queue is stopped. |
|
||||
| `time` | `float` | The time the item was added to the queue. For sorting priority queues |
|
||||
|
||||
|
||||
<a id="queue_item.__init__"></a>
|
||||
### \__init\__
|
||||
```python
|
||||
def __init__(self, task: Callable | Coroutine, *args, **kwargs):
|
||||
```
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|----------|---------------------------|-------------------------------------------------------------------|
|
||||
| `task` | `Callable` \| `Awaitable` | A callable or awaitable task to be executed by the `TaskQueue` |
|
||||
| `task` | `Callable` \| `Coroutine` | A coroutine to be executed by the `TaskQueue` |
|
||||
| `args` | `Any` | Positional arguments to be passed to the task when it is executed |
|
||||
| `kwargs` | `Any` | Keyword arguments to be passed to the task when it is executed |
|
||||
|
||||
<a id="run"></a>
|
||||
|
||||
<a id="queue.run"></a>
|
||||
### run
|
||||
```python
|
||||
def run(self) -> Any
|
||||
def run(self)
|
||||
```
|
||||
Run the task. If the task is a coroutine, it is awaited. If the task is a callable, it is called.
|
||||
|
||||
|
||||
<a id="task_queue.task_queue"></a>
|
||||
### TaskQueue
|
||||
```python
|
||||
class TaskQueue:
|
||||
def __init__(self):
|
||||
class TaskQueue
|
||||
```
|
||||
A perpetual task queue that processes `QueueItem` objects. The `TaskQueue` runs indefinitely, processing `QueueItem`
|
||||
objects as they are added to the queue. The `TaskQueue` is a wrapper around an `asyncio.Queue` that can be passed in as
|
||||
an argument or defaults to an `asyncio.PriorityQueue`. It is added to the bot executor of the `Bot` class on a
|
||||
separate thread.
|
||||
|
||||
#### Attributes:
|
||||
| Name | Type | Description |
|
||||
|---------------|-----------------|---------------------------------------------------------------------------------|
|
||||
| `queue` | `asyncio.Queue` | An asyncio.Queue queue of `QueueItem` objects to be executed by the `TaskQueue` |
|
||||
| Name | Type | Description |
|
||||
|------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `queue` | `asyncio.Queue` | An `asyncio.Queue` queue of `QueueItem` objects to be executed by the `TaskQueue`. If not provided during instantiation, an `asyncio.PriorityQueue` is used |
|
||||
| `stop` | `bool` | A flag to stop the task_queue instance. |
|
||||
| `workers` | `int` | The number of workers to process the queue items. Defaults to 10. |
|
||||
| `timeout` | `int` | The maximum time to wait for the queue to complete. Default is None. If timeout is provided the queue is joined using `asyncio.wait_for` with the timeout |
|
||||
| `on_exit` | `Literal["cancel", "complete_priority"]` | The action to take when the queue is stopped. If "cancel" the queue is cancelled and the remaining items are not processed. If "complete_priority" the queue is completed with the priority items. Default is "cancel" |
|
||||
| `mode` | `Literal["finite", "infinite"]` | The mode of the queue. If `finite` the queue will stop when all tasks are completed. If `infinite` the queue will continue to run until stopped. |
|
||||
| `worker_timeout` | `int` | The time to wait for a task to be added to the queue before stopping the worker or adding a dummy sleep task to the queue. |
|
||||
| `tasks` | `List[Task]` | A list of the worker tasks running concurrently, including the main task that joins the queue. |
|
||||
| `priority_tasks` | `set[QueueItem]` | A set to store the `QueueItems` that must complete before the queue stops |
|
||||
|
||||
|
||||
<a id="task_queue.__init__"></a>
|
||||
### \__init\__
|
||||
```python
|
||||
def __init__(self, queue: asyncio.Queue = None, workers: int = 10, timeout: int = None, size: int = None,
|
||||
on_exit: Literal["cancel", "complete_priority"] = "cancel",
|
||||
mode: Literal["finite", "infinite"] = "infinite", worker_timeout: int = 60)
|
||||
```
|
||||
Create a new `TaskQueue` instance.
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description | Default |
|
||||
|------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|---------------------|
|
||||
| `queue` | `asyncio.Queue` | An `asyncio.Queue` queue instance | None |
|
||||
| `workers` | `int` | The number of workers to process the queue items. | 10 |
|
||||
| `timeout` | `int` | The maximum time to wait for the queue to complete. | None |
|
||||
| `size` | `int` | The maximum size of the queue. | None |
|
||||
| `on_exit` | `Literal["cancel", "complete_priority"]` | The action to take when the queue is stopped. | "complete_priority" |
|
||||
| `mode` | `Literal["finite", "infinite"]` | The mode of the queue. | "infinite" |
|
||||
| `worker_timeout` | `int` | The time to wait for a task to be added to the queue before stopping the worker or adding a dummy sleep task to the queue. | 60 |
|
||||
|
||||
|
||||
<a id="task_queue.add"></a>
|
||||
### add
|
||||
```python
|
||||
def add(self, item: QueueItem, *args, **kwargs) -> None
|
||||
def add(*, item: QueueItem, priority: int = 3, must_complete_false: bool = False) -> None
|
||||
```
|
||||
Add a `QueueItem` to the `TaskQueue` queue.
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|--------|-------------|----------------------------------------|
|
||||
| `item` | `QueueItem` | A `QueueItem` to be added to the queue |
|
||||
| Name | Type | Description |
|
||||
|-----------------------|-------------|----------------------------------------------------------------------------------------|
|
||||
| `item` | `QueueItem` | A `QueueItem` to be added to the queue |
|
||||
| `priority` | `int` | The priority of the item. The lower the number, the higher the priority. Default is 3. |
|
||||
| `must_complete_false` | `bool` | If True, the item must be completed even if the queue is stopped. Default is False. |
|
||||
|
||||
|
||||
<a id="task_queue.add_task"></a>
|
||||
### add_task
|
||||
```python
|
||||
def add_task(self, task: Callable | Awaitable, *args, **kwargs) -> None
|
||||
def add_task(self, task: Callable | Awaitable, *args, **kwargs)
|
||||
```
|
||||
Create a QueueItem from the task and add it to the `TaskQueue` queue. The task can be a callable or an awaitable.
|
||||
The arguments and keyword arguments are passed to the QueueItem.
|
||||
@@ -78,14 +135,47 @@ The arguments and keyword arguments are passed to the QueueItem.
|
||||
<a id="task_queue.worker"></a>
|
||||
### worker
|
||||
```python
|
||||
async def worker(self) -> None
|
||||
async def worker()
|
||||
```
|
||||
A worker that processes the `QueueItem` objects in the `TaskQueue` queue. The worker runs indefinitely, processing
|
||||
`QueueItem` objects as they are added to the queue.
|
||||
A worker that processes the `QueueItem` objects in the `TaskQueue` queue.
|
||||
|
||||
<a id="task_queue.start"></a>
|
||||
### start
|
||||
|
||||
<a id="task_queue.run"></a>
|
||||
### run
|
||||
```python
|
||||
def start(self) -> None
|
||||
async def run(timeout: int = None)
|
||||
```
|
||||
Start the worker that processes the `QueueItem` objects in the `TaskQueue` queue.
|
||||
Start the `TaskQueue` instance. If a timeout is provided, the queue is joined using `asyncio.wait_for` with the timeout.
|
||||
This is the main entry point for the `TaskQueue` instance. It is added to the bot executor of the `Bot` class on a
|
||||
separate thread.
|
||||
|
||||
#### Parameters:
|
||||
| Name | Type | Description |
|
||||
|-----------|-------|----------------------------------------------------------------------|
|
||||
| `timeout` | `int` | The maximum time to wait for the queue to complete. Default is None. |
|
||||
|
||||
|
||||
<a id="task_queue.stop_queue"></a>
|
||||
### stop_queue
|
||||
```python
|
||||
def stop_queue()
|
||||
```
|
||||
Stop the `TaskQueue` instance. This sets the `stop` attribute to True, changes the `on_exit` attribute to "cancel",
|
||||
and cancels the queue.
|
||||
|
||||
|
||||
<a id="task_queue.clean_up"></a>
|
||||
### clean_up
|
||||
```python
|
||||
async def clean_up()
|
||||
```
|
||||
Clean up the `TaskQueue` instance. This is called when the queue is stopped. It cancels the queue and processes the
|
||||
remaining priority items based on the `on_exit` attribute.
|
||||
|
||||
|
||||
<a id="task_queue.cancel"></a>
|
||||
### cancel
|
||||
```python
|
||||
def cancel()
|
||||
```
|
||||
Cancel all remaining tasks.
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "aiomql"
|
||||
version = "3.23"
|
||||
version = "4.0.0"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
classifiers = [
|
||||
@@ -16,7 +16,7 @@ classifiers = [
|
||||
"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", "matplotlib>=3.8.4", "mplfinance>=0.12.10b0"]
|
||||
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 Algorithmic Trading Framework"
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@ from ...lib.candle import Candle, Candles
|
||||
|
||||
|
||||
def find_bearish_fractal(candles: Candles) -> Candle | None:
|
||||
"""Given a candles object, find the most recent bearish fractal."""
|
||||
for i in range(len(candles) - 3, 1, -1):
|
||||
if candles[i].high > max(candles[i - 1].high, candles[i + 1].high, candles[i - 2].high, candles[i + 2].high):
|
||||
return candles[i]
|
||||
|
||||
|
||||
def find_bullish_fractal(candles: Candles) -> Candle | None:
|
||||
"""Given a candles object, find the most recent bullish fractal."""
|
||||
for i in range(len(candles) - 3, 1, -1):
|
||||
if candles[i].low < min(candles[i - 1].low, candles[i + 1].low, candles[i - 2].low, candles[i + 2].low):
|
||||
return candles[i]
|
||||
|
||||
@@ -36,6 +36,7 @@ from .trades_manager import PositionsManager, OrdersManager, DealsManager
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
class BackTestEngine:
|
||||
mt5: MetaTrader
|
||||
span: range
|
||||
@@ -77,43 +78,51 @@ class BackTestEngine:
|
||||
account_info: dict = None,
|
||||
):
|
||||
"""The BackTestEngine class is used to simulate trading strategies on historical data.
|
||||
It can accept already saved data or create new data for backtesting on the fly. Ideally only one instance of this class should be created per session.
|
||||
By default it is automatically assigned to the global config instance during instantiation,
|
||||
replacing any existing backtest engine instance. But this is a configurable behaviour.
|
||||
The start and end time can still be specified even when test data is provided. In that case it will be used to set the range of the backtest.
|
||||
It can accept already saved data or create new data for backtesting on the fly. Ideally only one instance of
|
||||
this class should be created per session. By default it is automatically assigned to the global config instance
|
||||
during instantiation, replacing any existing backtest engine instance. But this is a configurable behaviour.
|
||||
The start and end time can still be specified even when test data is provided. In that case it will be used
|
||||
to set the range of the backtest.
|
||||
|
||||
Args:
|
||||
data (BackTestData, optional): The data to use for backtesting. Defaults to None.
|
||||
|
||||
speed (int, optional): The speed of the backtest. Defaults to 60 seconds.
|
||||
|
||||
start (float | datetime, optional): The start time of the backtest. Defaults to 0. If a float is passed, it is assumed to be a timestamp.
|
||||
start (float | datetime, optional): The start time of the backtest. Defaults to 0. If a float is passed,
|
||||
it is assumed to be a timestamp.
|
||||
|
||||
end (float | datetime, optional): The end time of the backtest. Defaults to 0. If a float is passed, it is assumed to be a timestamp.
|
||||
end (float | datetime, optional): The end time of the backtest. Defaults to 0. If a float is passed,
|
||||
it is assumed to be a timestamp.
|
||||
|
||||
restart (bool, optional): Whether to restart the backtest from the beginning. Defaults to True. This is useful when resuming a backtest
|
||||
using a saved BackTestData instance.
|
||||
restart (bool, optional): Whether to restart the backtest from the beginning. Defaults to True.
|
||||
This is useful when resuming a backtest using a saved BackTestData instance.
|
||||
|
||||
use_terminal (bool, optional): Whether to use the terminal for backtesting. Defaults to None. If None, it uses the global config setting.
|
||||
If use terminal is true, the backtest engine will use the terminal to get price data, compute margins, profit and check order viability.
|
||||
If false, it will use the data provided in the BackTestData instance and default algorithm for the calculations
|
||||
use_terminal (bool, optional): Whether to use the terminal for backtesting. Defaults to None. If None,
|
||||
it uses the global config setting. If use terminal is true, the backtest engine will use the terminal to
|
||||
get price data, compute margins, profit and check order viability. If false, it will use the data
|
||||
provided in the BackTestData instance and default algorithm for the calculations
|
||||
|
||||
name (str, optional): The name of the backtest. Defaults to "". If not provided, it is generated from the start and end times.
|
||||
name (str, optional): The name of the backtest. Defaults to "". If not provided,
|
||||
it is generated from the start and end times.
|
||||
|
||||
stop_time (float | datetime, optional): The time to stop the backtest. Defaults to None. If a float is passed, it is assumed to be a timestamp.
|
||||
If not given it is asummed to be the end of the backtest range.
|
||||
stop_time (float | datetime, optional): The time to stop the backtest. Defaults to None.
|
||||
If a float is passed, it is assumed to be a timestamp. If not given it is assumed to be the end of the backtest range.
|
||||
|
||||
close_open_positions_on_exit (bool, optional): Whether to close all open positions when the backtest is stopped. Defaults to True.
|
||||
close_open_positions_on_exit (bool, optional): Whether to close all open positions when the backtest
|
||||
is stopped. Defaults to True.
|
||||
|
||||
preload (bool, optional): Whether to preload the ticks for the backtest. Defaults to True.
|
||||
|
||||
assign_to_config (bool, optional): Whether to assign the backtest engine to the global config instance. Defaults to True.
|
||||
assign_to_config (bool, optional): Whether to assign the backtest engine to the global config instance.
|
||||
Defaults to True.
|
||||
|
||||
account_info (dict, optional): A dictionary of account information to use for the backtest. Defaults to None. Use this to set
|
||||
the account information for the backtest.
|
||||
|
||||
Attributes:
|
||||
_data (BackTestData): The data used for backtesting. This is the data that is saved to disk when the backtest is stopped.
|
||||
_data (BackTestData): The data used for backtesting. This is the data that is saved to disk when the
|
||||
backtest is stopped.
|
||||
|
||||
mt5 (MetaTrader): The MetaTrader instance for the backtest engine.
|
||||
|
||||
@@ -174,14 +183,22 @@ class BackTestEngine:
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}()"
|
||||
|
||||
def setup_test_range(self, *, start: float | datetime = None, end: float | datetime = None, speed: int = 60, restart: bool = True):
|
||||
"""Setup the test range for the backtest engine. This is used to set the range of the backtest and the speed at which it runs.
|
||||
def setup_test_range(self, *, start: float | datetime = None, end: float | datetime = None, speed:
|
||||
int = 60, restart: bool = True):
|
||||
"""Setup the test range for the backtest engine. This is used to set the range of the backtest and the speed
|
||||
at which it runs.
|
||||
|
||||
Args:
|
||||
start (float | datetime, optional): The start time of the backtest. Defaults to None. If a float is passed, it is assumed to be a timestamp.
|
||||
end (float | datetime, optional): The end time of the backtest. Defaults to None. If a float is passed, it is assumed to be a timestamp.
|
||||
start (float | datetime, optional): The start time of the backtest. Defaults to None. If a float is passed,
|
||||
it is assumed to be a timestamp.
|
||||
|
||||
end (float | datetime, optional): The end time of the backtest. Defaults to None. If a float is passed,
|
||||
it is assumed to be a timestamp.
|
||||
|
||||
speed (int, optional): The speed of the backtest. Defaults to 60.
|
||||
restart (bool, optional): Whether to restart the backtest. Defaults to True. This is useful when resuming a backtest using a saved BackTestData.
|
||||
|
||||
restart (bool, optional): Whether to restart the backtest. Defaults to True.
|
||||
This is useful when resuming a backtest using a saved BackTestData.
|
||||
"""
|
||||
if self._data.span and self._data.range:
|
||||
start = start or self._data.span[0]
|
||||
@@ -206,6 +223,13 @@ class BackTestEngine:
|
||||
self.cursor = Cursor(index=self.range.start, time=self.span.start)
|
||||
|
||||
def setup_data(self, *, restart: bool = True):
|
||||
"""Sets up the data for the backtest engine. This includes the orders, positions, deals and account
|
||||
information. This data is handled by specialized classes such as the BackTestAccount and the TradeManager
|
||||
classes.
|
||||
|
||||
Args:
|
||||
restart (bool, optional): Whether to restart the data. Defaults to True.
|
||||
"""
|
||||
if restart is True:
|
||||
self.orders = OrdersManager()
|
||||
self.positions = PositionsManager()
|
||||
@@ -231,19 +255,26 @@ class BackTestEngine:
|
||||
self._account = BackTestAccount(**self._data.account)
|
||||
|
||||
def next(self) -> Cursor:
|
||||
"""Move the cursor to the next time step in the backtest range."""
|
||||
return next(self)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""The BackTestData instance used for the backtest. If not provided, a new instance is created,
|
||||
and the data is made persistent when the backtest is stopped."""
|
||||
return self._data
|
||||
|
||||
def reset(self, clear_data: bool = False):
|
||||
"""Reset the backtest engine. This is useful when restarting the backtest from the beginning."""
|
||||
self.iter = zip(self.range, self.span)
|
||||
self.cursor = Cursor(index=self.range.start, time=self.span.start)
|
||||
if clear_data:
|
||||
self.setup_data(restart=True)
|
||||
|
||||
def go_to(self, *, time: datetime | float):
|
||||
"""Move the cursor to a specific time in the backtest range. You can pass a datetime object or a timestamp.
|
||||
You can't go back in time or beyond the limits of the range.
|
||||
"""
|
||||
time = time.astimezone(tz=UTC) if isinstance(time, datetime) else datetime.fromtimestamp(time, tz=UTC)
|
||||
time = int(time.timestamp())
|
||||
steps = time - self.cursor.time
|
||||
@@ -255,6 +286,7 @@ class BackTestEngine:
|
||||
raise ValueError("Can't go back in time or beyond the limits of the range")
|
||||
|
||||
def fast_forward(self, *, steps: int):
|
||||
"""Fast-forward the backtester by the given steps."""
|
||||
for _ in range(steps):
|
||||
self.next()
|
||||
|
||||
@@ -263,6 +295,7 @@ class BackTestEngine:
|
||||
return [(c, t) for c, t in zip(df.columns, df.dtypes)]
|
||||
|
||||
async def tracker(self):
|
||||
"""The tracker monitors and updates open positions on every iteration. It is called by the controller."""
|
||||
try:
|
||||
pos_tasks = [self.check_position(ticket=ticket) for ticket in self.positions._open_positions]
|
||||
await asyncio.gather(*pos_tasks)
|
||||
@@ -274,6 +307,7 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def save_result_to_json(self):
|
||||
"""Saves the result to a json file at the end of testing."""
|
||||
data = self._account.get_dict(include={"balance", "profit", "equity", "margin", "margin_free", "margin_level"})
|
||||
wins = [position for ticket in self.positions if (position := self.positions.get(ticket)).profit > 0]
|
||||
losses = [position for ticket in self.positions if (position := self.positions.get(ticket)).profit <= 0]
|
||||
@@ -303,6 +337,7 @@ class BackTestEngine:
|
||||
json.dump(data, file, indent=4)
|
||||
|
||||
async def close_all_open(self):
|
||||
"""Closes all open position at the end of testing"""
|
||||
tasks = [self.check_position(ticket=position.ticket) for position in self.positions.open_positions]
|
||||
await asyncio.gather(*tasks)
|
||||
for position in self.positions.open_positions:
|
||||
@@ -310,6 +345,8 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def wrap_up(self):
|
||||
"""Wraps up the backtest. This is called at the end of testing to save the results and close all open
|
||||
positions."""
|
||||
if self.close_open_positions_on_exit:
|
||||
await self.close_all_open()
|
||||
self.save_result_to_json()
|
||||
@@ -329,7 +366,11 @@ class BackTestEngine:
|
||||
GetData.pickle_data(data=self._data, name=path)
|
||||
|
||||
async def preload_ticks(self, *, symbol: str):
|
||||
"""Pull a month data on ticks from the terminal. Starting from the current time"""
|
||||
"""Pull a month data on ticks from the terminal. Starting from the current time.
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to preload ticks for.
|
||||
"""
|
||||
try:
|
||||
start = self.cursor.time
|
||||
end = start + (30 * 24 * 60 * 60)
|
||||
@@ -348,6 +389,13 @@ class BackTestEngine:
|
||||
|
||||
@async_cache
|
||||
async def get_price_tick(self, *, symbol: str, time: int) -> Tick | None:
|
||||
"""Get the price tick for a symbol at a given time. If the preload option is set to True,
|
||||
it will use the preloaded ticks when available.
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to get the price tick for.
|
||||
time (int): The time to get the price tick.
|
||||
"""
|
||||
try:
|
||||
if self.use_terminal and self.preload:
|
||||
if (ticks := self.preloaded_ticks.get(symbol)) is not None and time in ticks.index:
|
||||
@@ -420,6 +468,7 @@ class BackTestEngine:
|
||||
...
|
||||
|
||||
def check_account(self):
|
||||
"""Checks an account status. This method is called at each iteration to check if the account has burned out."""
|
||||
account = self._account
|
||||
level = account.margin_level if account.margin_so_mode == AccountStopOutMode.PERCENT else account.margin_so_call
|
||||
if level < account.margin_so_call and level != 0 and account.equity < 0:
|
||||
@@ -428,7 +477,8 @@ class BackTestEngine:
|
||||
|
||||
async def check_position(self, *, ticket: int):
|
||||
"""
|
||||
Update the profit of an open position based on the current price of the symbol.
|
||||
Update the profit of an open position based on the current price of the symbol. It is called by the
|
||||
tracker to update the profit of open positions.
|
||||
|
||||
Args:
|
||||
ticket (int): Position ticket
|
||||
@@ -445,6 +495,7 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
async def close_position_manually(self, *, ticket: int):
|
||||
"""Close a position manually without. Usually at the end of testing."""
|
||||
res = await self.close_position(ticket=ticket)
|
||||
if not res:
|
||||
return
|
||||
@@ -524,9 +575,9 @@ class BackTestEngine:
|
||||
Modify the stop loss and take profit levels of an open position.
|
||||
|
||||
Args:
|
||||
ticket: Position ticket
|
||||
sl: stop loss level
|
||||
tp: Take profit level
|
||||
ticket (int): Position ticket
|
||||
sl (int): stop loss level
|
||||
tp (int): Take profit level
|
||||
|
||||
Returns:
|
||||
bool: True if the stops are modified successfully, False otherwise
|
||||
@@ -535,6 +586,14 @@ class BackTestEngine:
|
||||
return True
|
||||
|
||||
def update_account(self, *, profit: float = None, margin: float = 0, gain: float = 0):
|
||||
"""
|
||||
Update the account. This method is protected by thread lock.
|
||||
|
||||
Args:
|
||||
profit (float): The current profit of one or more open positions. Can be positive or negative.
|
||||
margin (float): The margin set aside for a trade. It is released when the trade is closed.
|
||||
gain (gain): The gain realized when the trade is closed.
|
||||
"""
|
||||
self.account_lock.acquire()
|
||||
try:
|
||||
self._account.balance += round(gain, self._account.currency_digits)
|
||||
@@ -561,14 +620,21 @@ class BackTestEngine:
|
||||
self.account_lock.release()
|
||||
|
||||
def deposit(self, *, amount: float):
|
||||
"""Make deposit to the trading account"""
|
||||
self.update_account(gain=amount)
|
||||
|
||||
def withdraw(self, *, amount: float):
|
||||
"""Make a withdrawal from the trading account. You can not withdraw more than what you have"""
|
||||
assert amount <= self._account.balance, "Insufficient funds"
|
||||
self.update_account(gain=-amount)
|
||||
|
||||
@error_handler
|
||||
async def setup_account(self, **kwargs):
|
||||
"""Setup the trading account before the begining of a backtesting session.
|
||||
|
||||
Args:
|
||||
(**kwargs, Any): Attributes for the backetest account object can be set here.
|
||||
"""
|
||||
kwargs = {**self.account_info, **kwargs}
|
||||
default = {
|
||||
"profit": self._account.profit,
|
||||
@@ -589,6 +655,7 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def setup_account_sync(self, **kwargs):
|
||||
"""Set up the backtesting account in sync mode"""
|
||||
kwargs = {**self.account_info, **kwargs}
|
||||
default = {
|
||||
"profit": self._account.profit,
|
||||
@@ -609,6 +676,16 @@ class BackTestEngine:
|
||||
|
||||
@cached_property
|
||||
def prices(self) -> dict[str, DataFrame]:
|
||||
"""Get the prices for instruments used in the backtesting. This class is called when the use_terminal option
|
||||
is set to False and trading data is provided in the data attribute. It makes sure that there is a price for each
|
||||
symbol for every second covered in the backtesting range, by reindexing the price ticks using the backtesting
|
||||
time span and filling up missing data using the nearest method.
|
||||
This method returns a dictionaries of dataframe containing the prices for each symbol.
|
||||
It's cached and there computed only once per backtesting session.
|
||||
|
||||
Returns:
|
||||
dict[str, DataFrame]: A dictionary mapping dataframe of prices to symbols.
|
||||
"""
|
||||
prices = {}
|
||||
for symbol in self._data.ticks.keys():
|
||||
res = self._data.ticks[symbol]
|
||||
@@ -621,6 +698,11 @@ class BackTestEngine:
|
||||
|
||||
@cached_property
|
||||
def ticks(self) -> dict[str, DataFrame]:
|
||||
"""Similar to prices above, but returns prices exactly as they are without reindexing and filling up.
|
||||
|
||||
Returns:
|
||||
dict[str, DataFrame]: A dictionary mapping symbols to dataframes of ticks.
|
||||
"""
|
||||
ticks = {}
|
||||
for symbol in self._data.ticks.keys():
|
||||
res = self._data.ticks[symbol]
|
||||
@@ -630,6 +712,12 @@ class BackTestEngine:
|
||||
|
||||
@cached_property
|
||||
def rates(self) -> dict[str, dict[int, DataFrame]]:
|
||||
"""This property is useful when backtesting with the use_terminal option set to false. It returns a nested dict
|
||||
that maps symbols to a dict mapping timeframes to rates. The timeframes are mapped using their integer values.
|
||||
|
||||
Returns:
|
||||
dict[str, dict[int, DataFrame]]: A dictionary containing the symbol rates.
|
||||
"""
|
||||
rates = {}
|
||||
for symbol in self._data.rates.keys():
|
||||
for timeframe in self._data.rates[symbol].keys():
|
||||
@@ -640,6 +728,11 @@ class BackTestEngine:
|
||||
|
||||
@cached_property
|
||||
def symbols(self) -> dict[str, SymbolInfo]:
|
||||
"""A dictionary of symbols and SymbolInfo object. Used when use_terminal is set to false.
|
||||
|
||||
Returns:
|
||||
dict[str, SymbolInfo]
|
||||
"""
|
||||
symbols = {}
|
||||
for symbol, info in self._data.symbols.items():
|
||||
symbols[symbol] = SymbolInfo((info.get(key) for key in SymbolInfo.__match_args__))
|
||||
@@ -647,6 +740,20 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def order_send(self, *, request: dict, use_terminal=False) -> OrderSendResult:
|
||||
"""Simulates the sending of an order to the broker. An OrderSendResult is object is created at the end of this
|
||||
operation as would be created if it was done in live trading. When an order is successful a positions object is
|
||||
created, an order and deal object is created as well. When use_terminal is set to true the margin and profit
|
||||
are calculated by sending to the broker. This increases accuracy but slows down the backtester. Check order is
|
||||
called to make sure the order is valid and would go through if it was a live trade.
|
||||
|
||||
Args:
|
||||
request (dict): The order request as a dict.
|
||||
use_terminal (bool): A flag to override the use_terminal attribute. If true, the terminal will
|
||||
be used even if the use_terminal attribute is True.
|
||||
|
||||
Returns:
|
||||
OrderSendResult: An object containing the result of the order send operation.
|
||||
"""
|
||||
use_terminal = self.use_terminal or use_terminal
|
||||
osr = {
|
||||
"retcode": 10013,
|
||||
@@ -814,6 +921,16 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def order_check(self, *, request: dict, use_terminal: bool = False) -> OrderCheckResult:
|
||||
"""Checks the order before placing it. If use_terminal, the order is checked with the broker, but the entire result
|
||||
is not used. Details such as balance, profit, equity, margin, and margin level are calculated by the backtester.
|
||||
|
||||
Args:
|
||||
request (dict): The order request as a dict.
|
||||
use_terminal (bool): A flag to override the use_terminal attribute. If true, the terminal will used.
|
||||
|
||||
Returns:
|
||||
OrderCheckResult: The result of the order check.
|
||||
"""
|
||||
use_terminal = self.use_terminal or use_terminal
|
||||
ocr = {
|
||||
"retcode": 10013,
|
||||
@@ -892,6 +1009,11 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_terminal_info(self) -> TerminalInfo:
|
||||
"""Get the terminal information
|
||||
|
||||
Returns:
|
||||
TerminalInfo: The terminal information
|
||||
"""
|
||||
if self.use_terminal:
|
||||
res = await self.mt5.terminal_info()
|
||||
return res
|
||||
@@ -899,6 +1021,11 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_version(self) -> tuple[int, int, str]:
|
||||
"""Get the version of the terminal.
|
||||
|
||||
Returns:
|
||||
tuple[int, int, str]: The version of the terminal
|
||||
"""
|
||||
if self.use_terminal:
|
||||
res = await self.mt5.version()
|
||||
return res
|
||||
@@ -906,6 +1033,11 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_symbols_total(self) -> int:
|
||||
"""Get the total number of symbols available in the terminal.
|
||||
|
||||
Returns:
|
||||
int: The total number of symbols available.
|
||||
"""
|
||||
if self.use_terminal:
|
||||
syms = await self.mt5.symbols_total()
|
||||
return syms
|
||||
@@ -913,6 +1045,14 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_symbols(self, *, group: str = "") -> tuple[SymbolInfo, ...]:
|
||||
"""Get the symbols available in the terminal. Filter by group if provided.
|
||||
|
||||
Args:
|
||||
group (str): The group to filter by (default is "")
|
||||
|
||||
Returns:
|
||||
tuple[SymbolInfo, ...]: A tuple of symbol information
|
||||
"""
|
||||
if self.use_terminal:
|
||||
syms = await self.mt5.symbols_get(group=group)
|
||||
return syms
|
||||
@@ -920,10 +1060,23 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_account_info(self) -> AccountInfo:
|
||||
"""Get the account information
|
||||
|
||||
Returns:
|
||||
AccountInfo: The account information
|
||||
"""
|
||||
return AccountInfo(self._account.asdict().values())
|
||||
|
||||
@error_handler
|
||||
async def get_symbol_info_tick(self, *, symbol: str) -> Tick | None:
|
||||
async def get_symbol_info_tick(self, *, symbol: str) -> Tick:
|
||||
"""Get the price tick for a symbol at the current time
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol
|
||||
|
||||
Returns:
|
||||
Tick: The price tick
|
||||
"""
|
||||
tick = await self.get_price_tick(symbol=symbol, time=self.cursor.time)
|
||||
return tick
|
||||
|
||||
@@ -937,6 +1090,14 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_symbol_info(self, *, symbol: str) -> SymbolInfo:
|
||||
"""Get the symbol information
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to get information for
|
||||
|
||||
Returns:
|
||||
SymbolInfo: The symbol information
|
||||
"""
|
||||
if self.use_terminal:
|
||||
info = await self._symbol_info(symbol=symbol)
|
||||
else:
|
||||
@@ -957,6 +1118,17 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_rates_from(self, *, symbol: str, timeframe: TimeFrame, date_from: datetime | float, count: int) -> np.ndarray:
|
||||
"""Get rates from a specific date to the current date. Used by the backtester to get rates for a symbol
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to get rates for
|
||||
timeframe (TimeFrame): The timeframe of the rates
|
||||
date_from (datetime | float): The date from which to get the rates
|
||||
count (int): The number of rates to get
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of rates
|
||||
"""
|
||||
date_from = date_from.astimezone(tz=UTC) if isinstance(date_from, datetime) else datetime.fromtimestamp(date_from, tz=UTC)
|
||||
if self.use_terminal:
|
||||
rates = await self.mt5.copy_rates_from(symbol, timeframe, date_from, count)
|
||||
@@ -970,6 +1142,17 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def get_rates_from_pos(self, *, symbol: str, timeframe: TimeFrame, start_pos: int, count: int) -> np.ndarray:
|
||||
"""Get a number of rates counting from a specific position. With position zero being the current time.
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to get rates for
|
||||
timeframe (TimeFrame): The timeframe of the rates
|
||||
start_pos (int): The position to start from
|
||||
count (int): The number of rates to get
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of rates
|
||||
"""
|
||||
if self.use_terminal:
|
||||
current_time = self.cursor.time if start_pos == 0 else self.cursor.time - start_pos * timeframe.seconds
|
||||
current_time = round_up(current_time, timeframe.seconds)
|
||||
@@ -988,7 +1171,19 @@ class BackTestEngine:
|
||||
return np.fromiter((tuple(i) for i in rates.iloc), dtype=self.get_dtype(df=rates))
|
||||
|
||||
@error_handler
|
||||
async def get_rates_range(self, *, symbol: str, timeframe: TimeFrame, date_from: datetime | float, date_to: datetime | float) -> np.ndarray:
|
||||
async def get_rates_range(self, *, symbol: str, timeframe: TimeFrame, date_from: datetime | float,
|
||||
date_to: datetime | float) -> np.ndarray:
|
||||
"""Get rates within a specific date range. Used by the backtester to get rates for a symbol
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to get rates for
|
||||
timeframe (TimeFrame): The timeframe of the rates
|
||||
date_from (datetime | float): The date from which to get the rates
|
||||
date_to (datetime | float): The date to which to get the rates
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of rates
|
||||
"""
|
||||
date_from = date_from.astimezone(tz=UTC) if isinstance(date_from, datetime) else datetime.fromtimestamp(date_from, tz=UTC)
|
||||
date_to = date_to.astimezone(tz=UTC) if isinstance(date_to, datetime) else datetime.fromtimestamp(date_to, tz=UTC)
|
||||
if self.use_terminal:
|
||||
@@ -1002,7 +1197,18 @@ class BackTestEngine:
|
||||
return np.fromiter((tuple(i) for i in rates.iloc), dtype=self.get_dtype(df=rates))
|
||||
|
||||
@error_handler
|
||||
async def get_ticks_from(self, *, symbol: str, date_from: datetime | float, count: int, flags: CopyTicks = CopyTicks.ALL) -> np.ndarray:
|
||||
async def get_ticks_from(self, *, symbol: str, date_from: datetime | float, count: int,
|
||||
flags: CopyTicks = CopyTicks.ALL) -> np.ndarray:
|
||||
"""Get a specified number of ticks counting from a specific date.
|
||||
Args:
|
||||
symbol (str): The symbol to get ticks for
|
||||
date_from (datetime | float): The date from which to get the ticks
|
||||
count (int): The number of ticks to get
|
||||
flags (CopyTicks): The flags to use when getting the ticks
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of ticks
|
||||
"""
|
||||
date_from = date_from.astimezone(tz=UTC) if isinstance(date_from, datetime) else datetime.fromtimestamp(date_from, tz=UTC)
|
||||
if self.use_terminal:
|
||||
ticks = await self.mt5.copy_ticks_from(symbol, date_from, count, flags)
|
||||
@@ -1017,6 +1223,17 @@ class BackTestEngine:
|
||||
async def get_ticks_range(
|
||||
self, *, symbol: str, date_from: datetime | float, date_to: datetime | float, flags: CopyTicks = CopyTicks.ALL
|
||||
) -> np.ndarray:
|
||||
"""Get ticks within a specific date range.
|
||||
|
||||
Args:
|
||||
symbol (str): The symbol to get ticks for
|
||||
date_from (datetime | float): The date from which to get the ticks
|
||||
date_to (datetime | float): The date to which to get the ticks
|
||||
flags (CopyTicks): The flags to use when getting the ticks
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of ticks
|
||||
"""
|
||||
date_from = date_from.astimezone(tz=UTC) if isinstance(date_from, datetime) else datetime.fromtimestamp(date_from, tz=UTC)
|
||||
date_to = date_to.astimezone(tz=UTC) if isinstance(date_to, datetime) else datetime.fromtimestamp(date_to, tz=UTC)
|
||||
if self.use_terminal:
|
||||
@@ -1031,8 +1248,21 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def order_calc_margin(
|
||||
self, *, action: Literal[OrderType.BUY, OrderType.SELL], symbol: str, volume: float, price: float, use_terminal: bool = None
|
||||
):
|
||||
self, *, action: Literal[OrderType.BUY, OrderType.SELL], symbol: str, volume: float,
|
||||
price: float, use_terminal: bool = None):
|
||||
"""Calculate the margin required for a trade.
|
||||
|
||||
Args:
|
||||
action (Literal[OrderType.BUY, OrderType.SELL]): Type of order
|
||||
symbol (str): Symbol name
|
||||
volume (float): Volume of the trade
|
||||
price (float): The price at which the trade is opened
|
||||
use_terminal (bool): A flag to override the use_terminal attribute. If true, the terminal will be used
|
||||
even if the use_terminal attribute is True.
|
||||
|
||||
Returns:
|
||||
float: The margin required for the trade
|
||||
"""
|
||||
use_terminal = use_terminal if use_terminal is not None else self.use_terminal
|
||||
if use_terminal:
|
||||
return await self.mt5.order_calc_margin(action, symbol, volume, price)
|
||||
@@ -1045,8 +1275,23 @@ class BackTestEngine:
|
||||
|
||||
@error_handler
|
||||
async def order_calc_profit(
|
||||
self, *, action: Literal[OrderType.BUY, OrderType.SELL], symbol: str, volume: float, price_open: float, price_close: float, use_terminal=None
|
||||
):
|
||||
self, *, action: Literal[OrderType.BUY, OrderType.SELL], symbol: str, volume: float,
|
||||
price_open: float, price_close: float, use_terminal=None):
|
||||
"""
|
||||
Calculate the profit for a trade.
|
||||
|
||||
Args:
|
||||
action (Literal[OrderType.BUY, OrderType.SELL]): Type of order
|
||||
symbol (str): Symbol name
|
||||
volume (float): Volume of the trade
|
||||
price_open (float): The price at which the trade is opened
|
||||
price_close (float): The price at which the trade is closed
|
||||
use_terminal (bool): A flag to override the use_terminal attribute. If true, the terminal will be used
|
||||
even if the use_terminal attribute is True.
|
||||
|
||||
Returns:
|
||||
float: The profit of the trade
|
||||
"""
|
||||
use_terminal = use_terminal if use_terminal is not None else self.use_terminal
|
||||
|
||||
if use_terminal:
|
||||
@@ -1060,8 +1305,7 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_orders_total(self) -> int:
|
||||
"""
|
||||
Get the total number of pending orders.
|
||||
"""Get the total number of pending orders.
|
||||
|
||||
Returns:
|
||||
int: Total number of pending orders
|
||||
@@ -1070,8 +1314,7 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_orders(self, *, symbol: str = "", group: str = "", ticket: int = None) -> tuple[TradeOrder, ...]:
|
||||
"""
|
||||
Get pending orders from the terminal history. This has to do with pending orders, which this backtester
|
||||
"""Get pending orders from the terminal history. This has to do with pending orders, which this backtester
|
||||
doesn't support yet.
|
||||
|
||||
Args:
|
||||
@@ -1080,7 +1323,7 @@ class BackTestEngine:
|
||||
ticket: Order ticket
|
||||
|
||||
Returns:
|
||||
tuple[TradeOrder]
|
||||
tuple[TradeOrder, ...]: Pending orders
|
||||
"""
|
||||
if symbol and group and ticket:
|
||||
return tuple()
|
||||
@@ -1088,8 +1331,7 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_positions_total(self) -> int:
|
||||
"""
|
||||
Get the total number of open positions.
|
||||
"""Get the total number of open positions.
|
||||
|
||||
Returns:
|
||||
int: Total number of open positions
|
||||
@@ -1098,23 +1340,21 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_positions(self, *, symbol: str = None, group: str = None, ticket: int = None) -> tuple[TradePosition, ...]:
|
||||
"""
|
||||
Get open positions from the terminal history.
|
||||
"""Get open positions from the terminal history.
|
||||
|
||||
Keyword Args:
|
||||
Args:
|
||||
symbol: The symbol name
|
||||
group: Group argument to filter by
|
||||
ticket: Position ticket
|
||||
|
||||
Returns:
|
||||
tuple[TradePosition]: Open positions
|
||||
tuple[TradePosition, ...]: Open positions
|
||||
"""
|
||||
return self.positions.positions_get(ticket=ticket, symbol=symbol, group=group)
|
||||
|
||||
@error_handler_sync
|
||||
def get_history_orders_total(self, *, date_from: datetime | float, date_to: datetime | float) -> int:
|
||||
"""
|
||||
Get the total number of orders in the terminal history.
|
||||
"""Get the total number of orders in the terminal history.
|
||||
|
||||
Args:
|
||||
date_from: The start date of the history
|
||||
@@ -1128,12 +1368,11 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_history_orders(
|
||||
self, *, date_from: datetime | float = None, date_to: datetime | float = None, group: str = "", ticket: int = None, position: int = None
|
||||
) -> tuple[TradeOrder, ...]:
|
||||
"""
|
||||
Get orders from the terminal history.
|
||||
self, *, date_from: datetime | float = None, date_to: datetime | float = None, group: str = "",
|
||||
ticket: int = None, position: int = None) -> tuple[TradeOrder, ...]:
|
||||
"""Get orders from the terminal history.
|
||||
|
||||
Keyword Args:
|
||||
Args:
|
||||
date_from: Date from which to start the history
|
||||
date_to: Date to which to end the history
|
||||
group: group keyword to filter by
|
||||
@@ -1141,14 +1380,14 @@ class BackTestEngine:
|
||||
position: position id to filter by
|
||||
|
||||
Returns:
|
||||
tuple[TradeOrder]: Orders in the history
|
||||
tuple[TradeOrder, ...]: Orders in the history
|
||||
"""
|
||||
return self.orders.history_orders_get(date_from=date_from, date_to=date_to, group=group, ticket=ticket, position=position)
|
||||
return self.orders.history_orders_get(date_from=date_from, date_to=date_to, group=group,
|
||||
ticket=ticket, position=position)
|
||||
|
||||
@error_handler_sync
|
||||
def get_history_deals_total(self, *, date_from: datetime | float, date_to: datetime | float) -> int:
|
||||
"""
|
||||
Get the total number of deals in the terminal history.
|
||||
"""Get the total number of deals in the terminal history.
|
||||
|
||||
Args:
|
||||
date_from: Date from which to start the history
|
||||
@@ -1161,12 +1400,11 @@ class BackTestEngine:
|
||||
|
||||
@error_handler_sync
|
||||
def get_history_deals(
|
||||
self, *, date_from: datetime | float = None, date_to: datetime | float = None, group: str = None, position: int = None, ticket: int = None
|
||||
) -> tuple[TradeDeal, ...]:
|
||||
"""
|
||||
Get deals from the terminal history.
|
||||
self, *, date_from: datetime | float = None, date_to: datetime | float = None, group: str = None,
|
||||
position: int = None, ticket: int = None) -> tuple[TradeDeal, ...]:
|
||||
"""Get deals from the terminal history.
|
||||
|
||||
Keyword Args:
|
||||
Args:
|
||||
date_from: Date from which to start the history
|
||||
date_to: Date to which to end the history
|
||||
group: group keyword to filter by
|
||||
@@ -1176,4 +1414,5 @@ class BackTestEngine:
|
||||
Returns:
|
||||
tuple[TradeDeal, ...]: Deals in the history
|
||||
"""
|
||||
return self.deals.history_deals_get(date_from=date_from, date_to=date_to, group=group, position=position, ticket=ticket)
|
||||
return self.deals.history_deals_get(date_from=date_from, date_to=date_to, group=group,
|
||||
position=position, ticket=ticket)
|
||||
|
||||
@@ -18,12 +18,33 @@ logger = getLogger(__name__)
|
||||
|
||||
|
||||
class Cursor(NamedTuple):
|
||||
"""A cursor to iterate over the data. Marks the current position."""
|
||||
index: int
|
||||
time: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class BackTestData:
|
||||
"""The data class to store the backtesting data.
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the backtest data.
|
||||
terminal (dict): The terminal information.
|
||||
version (tuple): The version of the terminal.
|
||||
account (dict): The account information.
|
||||
symbols (dict): The symbols information.
|
||||
ticks (dict): The ticks data.
|
||||
rates (dict): The rates data.
|
||||
span (range): The range of the data.
|
||||
range (range): The range of the data.
|
||||
orders (dict): The orders data.
|
||||
deals (dict): The deals data.
|
||||
positions (dict): The positions data.
|
||||
open_positions (set): The open positions.
|
||||
cursor (Cursor): The cursor to iterate over the data.
|
||||
margins (dict): The margins data.
|
||||
fully_loaded (bool): A flag to indicate if the data is fully loaded
|
||||
"""
|
||||
name: str = ""
|
||||
terminal: dict[str, [str | int | bool | float]] = field(default_factory=dict)
|
||||
version: tuple[int, int, str] = (0, 0, "")
|
||||
@@ -48,18 +69,44 @@ class BackTestData:
|
||||
return f"{self.__class__.__name__}({self.name})"
|
||||
|
||||
def set_attrs(self, **kwargs):
|
||||
"""Set the attributes of the class on the instance."""
|
||||
[setattr(self, k, v) for k, v in kwargs.items() if k in self.fields]
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
"""A list of the fields of the class."""
|
||||
return [f.name for f in fields(self)]
|
||||
|
||||
|
||||
class GetData:
|
||||
"""A class to get the backtesting data from the MetaTrader5 terminal.
|
||||
|
||||
Attributes:
|
||||
start (datetime): The start date of the data.
|
||||
end (datetime): The end date of the data.
|
||||
symbols (Sequence[str]): The symbols to get the data for.
|
||||
timeframes (Sequence[TimeFrame]): The timeframes to get the data for.
|
||||
name (str): The name of the backtest data.
|
||||
range (range): The range of the data.
|
||||
span (range): The span of the data.
|
||||
data (BackTestData): The backtesting data.
|
||||
mt5 (MetaTrader): The MetaTrader5 instance.
|
||||
task_queue (TaskQueue): The task queue to handle the requests.
|
||||
"""
|
||||
data: BackTestData
|
||||
|
||||
def __init__(self, *, start: datetime, end: datetime, symbols: Sequence[str], timeframes: Sequence[TimeFrame], name: str = ""):
|
||||
""""""
|
||||
def __init__(self, *, start: datetime, end: datetime, symbols: Sequence[str],
|
||||
timeframes: Sequence[TimeFrame], name: str = ""):
|
||||
"""
|
||||
Get the backtesting data from the MetaTrader5 terminal.
|
||||
|
||||
Args:
|
||||
start (datetime): The start date of the data.
|
||||
end (datetime): The end date of the data.
|
||||
symbols (Sequence[str]): The symbols to get the data for.
|
||||
timeframes (Sequence[TimeFrame]): The timeframes to get the data for.
|
||||
name (str): The name of the backtest data.
|
||||
"""
|
||||
self.config = Config()
|
||||
self.start = start.astimezone(tz=UTC)
|
||||
self.end = end.astimezone(tz=UTC)
|
||||
@@ -76,7 +123,12 @@ class GetData:
|
||||
|
||||
@classmethod
|
||||
def pickle_data(cls, *, data: BackTestData, name: str | Path):
|
||||
""""""
|
||||
"""Pickle the data to a file.
|
||||
|
||||
Args:
|
||||
data (BackTestData): The data to pickle.
|
||||
name (str | Path): The name of the file to pickle the data to.
|
||||
"""
|
||||
try:
|
||||
with open(name, "wb") as fo:
|
||||
pickle.dump(data, fo, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
@@ -85,7 +137,11 @@ class GetData:
|
||||
|
||||
@classmethod
|
||||
def load_data(cls, *, name: str | Path) -> BackTestData:
|
||||
""""""
|
||||
"""Load the data from a file.
|
||||
|
||||
Args:
|
||||
name (str | Path): The name of the file to load the data from.
|
||||
"""
|
||||
try:
|
||||
with open(name, "rb") as fo:
|
||||
data = pickle.load(fo)
|
||||
@@ -94,13 +150,23 @@ class GetData:
|
||||
logger.error(f"Error: {err}")
|
||||
|
||||
def save_data(self, *, name: str | Path = ""):
|
||||
"""Save the data to a file.
|
||||
|
||||
Args:
|
||||
name (str | Path): The name of the file to save the data to. If not provided, the name of the data is used.
|
||||
"""
|
||||
name = name or (self.name + ".pkl" if not self.name.endswith(".pkl") else self.name)
|
||||
name = Path(self.config.backtest_dir) / name if not isinstance(name, Path) else name
|
||||
with open(name, "wb") as fo:
|
||||
pickle.dump(self.data, fo, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
async def get_data(self, workers: int = None):
|
||||
""""""
|
||||
"""Use the task queue to get the data from the MetaTrader5 terminal.
|
||||
|
||||
Args:
|
||||
workers (int): The number of workers to use in the task queue. If not provided, the default number of workers
|
||||
is used.
|
||||
"""
|
||||
if workers:
|
||||
self.task_queue.workers = workers
|
||||
|
||||
@@ -124,7 +190,6 @@ class GetData:
|
||||
self.data = BackTestData(name=self.name, span=self.span, range=self.range, fully_loaded=False)
|
||||
|
||||
async def get_terminal_info(self):
|
||||
""""""
|
||||
terminal = await self.mt5.terminal_info()
|
||||
if terminal is None:
|
||||
self.data.fully_loaded = False
|
||||
@@ -133,7 +198,6 @@ class GetData:
|
||||
self.data.set_attrs(terminal=terminal)
|
||||
|
||||
async def get_version(self):
|
||||
""""""
|
||||
version = await self.mt5.version()
|
||||
if version is None:
|
||||
self.data.fully_loaded = False
|
||||
@@ -142,7 +206,6 @@ class GetData:
|
||||
|
||||
@backoff_decorator
|
||||
async def get_account_info(self):
|
||||
""""""
|
||||
res = await self.mt5.account_info()
|
||||
if res is None:
|
||||
self.data.fully_loaded = False
|
||||
@@ -151,15 +214,14 @@ class GetData:
|
||||
self.data.set_attrs(account=res)
|
||||
|
||||
async def get_symbols_info(self):
|
||||
""""""
|
||||
[self.task_queue.add(item=QueueItem(self.get_symbol_info, symbol=symbol)) for symbol in self.symbols if self.data.symbols.get(symbol) is None]
|
||||
[self.task_queue.add(item=QueueItem(self.get_symbol_info, symbol=symbol)) for symbol in self.symbols
|
||||
if self.data.symbols.get(symbol) is None]
|
||||
|
||||
async def get_symbols_ticks(self):
|
||||
""""""
|
||||
[self.task_queue.add(item=QueueItem(self.get_symbol_ticks, symbol=symbol)) for symbol in self.symbols if self.data.ticks.get(symbol) is None]
|
||||
[self.task_queue.add(item=QueueItem(self.get_symbol_ticks, symbol=symbol)) for symbol in self.symbols if
|
||||
self.data.ticks.get(symbol) is None]
|
||||
|
||||
async def get_symbols_rates(self):
|
||||
""""""
|
||||
[
|
||||
self.task_queue.add(item=QueueItem(self.get_symbol_rates, symbol=symbol, timeframe=timeframe), priority=4)
|
||||
for symbol in self.symbols
|
||||
@@ -169,7 +231,6 @@ class GetData:
|
||||
|
||||
@backoff_decorator
|
||||
async def get_symbol_info(self, *, symbol: str):
|
||||
""""""
|
||||
res = await self.mt5.symbol_info(symbol)
|
||||
if res is None:
|
||||
self.data.fully_loaded = False
|
||||
@@ -178,7 +239,6 @@ class GetData:
|
||||
|
||||
@backoff_decorator
|
||||
async def get_symbol_ticks(self, *, symbol: str):
|
||||
""""""
|
||||
res = await self.mt5.copy_ticks_range(symbol, self.start, self.end, MetaTrader5.COPY_TICKS_ALL)
|
||||
if res is None:
|
||||
self.data.fully_loaded = False
|
||||
@@ -187,7 +247,6 @@ class GetData:
|
||||
|
||||
@backoff_decorator
|
||||
async def get_symbol_rates(self, *, symbol: str, timeframe: TimeFrame):
|
||||
""""""
|
||||
res = await self.mt5.copy_rates_range(symbol, timeframe, self.start, self.end)
|
||||
if res is None:
|
||||
self.data.fully_loaded = False
|
||||
|
||||
@@ -10,6 +10,38 @@ TradeData = TypeVar("TradeData", bound=TradePosition | TradeOrder | TradeDeal)
|
||||
|
||||
|
||||
class TradeManager(Generic[TradeData]):
|
||||
"""A generic class to manage trades data during a backtest. It is the parent class of the
|
||||
PositionsManager, OrdersManager, and DealsManager. It implements some dict-like methods to manage the data.
|
||||
It has a private attribute _data to store the data. It exposes the data through the values, keys, and items methods.
|
||||
It also has a to_dict method to convert the data to a dictionary.
|
||||
|
||||
Attributes:
|
||||
_data (dict[int, TradeData]): The data to store the trades.
|
||||
|
||||
Examples:
|
||||
>>> manager = TradeManager()
|
||||
>>> manager[123456] = TradePosition(ticket=123456, symbol="EURUSD", volume=0.1)
|
||||
>>> manager.update(ticket=123456, symbol="EURUSD", volume=0.1)
|
||||
>>> manager[123456]
|
||||
TradePosition(ticket=123456, symbol='EURUSD', volume=0.1)
|
||||
>>> manager.values()
|
||||
(TradePosition(ticket=123456, symbol='EURUSD', volume=0.1),)
|
||||
>>> manager.keys()
|
||||
(123456,)
|
||||
>>> manager.items()
|
||||
((123456, TradePosition(ticket=123456, symbol='EURUSD', volume=0.1)),)
|
||||
>>> manager.to_dict()
|
||||
{123456: {'ticket': 123456, 'symbol': 'EURUSD', 'volume': 0.1}}
|
||||
>>> pos = manager.get(123456)
|
||||
>>> pos
|
||||
TradePosition(ticket=123456, symbol='EURUSD', volume=0.1)
|
||||
>>> pos in manager
|
||||
True
|
||||
>>> len(manager)
|
||||
1
|
||||
>>> pos in manager
|
||||
False
|
||||
"""
|
||||
_data: dict[int, TradeData]
|
||||
|
||||
def __init__(self, *, data: dict = None):
|
||||
@@ -37,6 +69,12 @@ class TradeManager(Generic[TradeData]):
|
||||
return self._data.get(key, default)
|
||||
|
||||
def update(self, *, ticket: int, **kwargs):
|
||||
"""Update the data of a trade. Given the ticket of the trade and the new data to update.
|
||||
|
||||
Args:
|
||||
ticket (int): The ticket of the trade to update.
|
||||
**kwargs: The new data to update.
|
||||
"""
|
||||
try:
|
||||
res = self[ticket]
|
||||
klass = type(res)
|
||||
@@ -49,24 +87,48 @@ class TradeManager(Generic[TradeData]):
|
||||
logger.error(f"Update Operation Failed: Could Not Find Ticket")
|
||||
|
||||
def values(self) -> tuple[TradeData, ...]:
|
||||
"""Returns the values of the data."""
|
||||
return tuple(value for value in self._data.values())
|
||||
|
||||
def keys(self) -> tuple[int, ...]:
|
||||
"""Returns the keys of the data."""
|
||||
return tuple(key for key in self._data.keys())
|
||||
|
||||
def items(self) -> tuple[tuple[int, TradeData], ...]:
|
||||
"""Returns the items of the data."""
|
||||
return tuple((key, value) for key, value in self._data.items())
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert the data to a dictionary."""
|
||||
return {key: value._asdict() for key, value in self._data.items()}
|
||||
|
||||
|
||||
class PositionsManager(TradeManager):
|
||||
"""A class to manage the open positions during a backtest. It is a subclass of TradeManager. It has an additional
|
||||
attribute _open_positions to store the open positions. It also has a margins attribute to store the margins of the
|
||||
open positions. It overrides some methods of the TradeManager class to manage the open positions.
|
||||
|
||||
Attributes:
|
||||
_open_positions (set[int]): The open positions.
|
||||
margins (dict[int, float]): The margins of the open positions.
|
||||
"""
|
||||
_data: dict[int, TradePosition]
|
||||
_open_positions: set[int]
|
||||
margins: dict[int, float]
|
||||
|
||||
def __init__(self, *, data: dict = None, open_positions: set = None, margins: dict = None):
|
||||
def __init__(self, *, data: dict = None, open_positions: set[int] = None, margins: dict = None):
|
||||
"""Positions manager manages the open positions during a backtest. It is a subclass of TradeManager. It has an
|
||||
additional attribute _open_positions to store the open positions. It also has a margins attribute to store the
|
||||
margins of the open positions. It overrides some methods of the TradeManager class to manage the open positions.
|
||||
|
||||
Args:
|
||||
data (dict, optional): The data to store the trades. This used for continuation of the backtesting, if it
|
||||
was stopped with some open positions.
|
||||
|
||||
open_positions (set, optional): The open positions. Defaults to None.
|
||||
|
||||
margins (dict, optional): The margins of the open positions. Defaults to None.
|
||||
"""
|
||||
super().__init__(data=data)
|
||||
self._open_positions = open_positions or {trade.ticket for trade in self._data.values()}
|
||||
self.margins: dict[int, float] = margins or dict()
|
||||
@@ -96,20 +158,54 @@ class PositionsManager(TradeManager):
|
||||
return sum(self.margins.values())
|
||||
|
||||
def close(self, *, ticket: int) -> bool:
|
||||
"""Close a position. Given the ticket of the position to close.
|
||||
|
||||
Args:
|
||||
ticket (int): The ticket of the position to close.
|
||||
"""
|
||||
is_open = ticket in self._open_positions
|
||||
self._open_positions.discard(ticket)
|
||||
return is_open
|
||||
|
||||
def get_margin(self, *, ticket: int) -> float:
|
||||
"""Get the margin of a position. Given the ticket of the position.
|
||||
|
||||
Args:
|
||||
ticket (int): The ticket of the position.
|
||||
|
||||
Returns:
|
||||
float: The margin of the position.
|
||||
"""
|
||||
return self.margins.get(ticket, 0.0)
|
||||
|
||||
def delete_margin(self, *, ticket: int):
|
||||
"""Delete the margin of a position. Given the ticket of the position.
|
||||
|
||||
Args:
|
||||
ticket (int): The ticket of the position.
|
||||
"""
|
||||
return self.margins.pop(ticket, 0)
|
||||
|
||||
def set_margin(self, *, ticket: int, margin: float):
|
||||
"""Set the margin of a position. Given the ticket of the position and the margin.
|
||||
|
||||
Args:
|
||||
ticket (int): The ticket of the position.
|
||||
margin (float): The margin of the position
|
||||
"""
|
||||
self.margins[ticket] = margin
|
||||
|
||||
def positions_get(self, *, ticket: int = None, symbol: str = None, group: None = None) -> tuple[TradePosition, ...]:
|
||||
"""Get positions. Given the ticket, symbol, or group of the positions.
|
||||
|
||||
Args:
|
||||
ticket (int): The ticket of the position.
|
||||
symbol (str): The symbol of the position.
|
||||
group (str): The group
|
||||
|
||||
Returns:
|
||||
tuple[TradePosition, ...]: The positions
|
||||
"""
|
||||
if ticket:
|
||||
return tuple(position for position in self.open_positions if position.ticket == ticket)
|
||||
|
||||
@@ -125,24 +221,58 @@ class PositionsManager(TradeManager):
|
||||
return tuple()
|
||||
|
||||
def positions_total(self) -> int:
|
||||
"""Get the total number of open positions.
|
||||
|
||||
Returns:
|
||||
int: The total number of open positions.
|
||||
"""
|
||||
return len(self._open_positions)
|
||||
|
||||
@property
|
||||
def open_positions(self) -> tuple[TradePosition, ...]:
|
||||
"""Returns the open positions.
|
||||
|
||||
Args:
|
||||
tuple[TradePosition, ...]: The open positions.
|
||||
"""
|
||||
return tuple(position for position in self.values() if position.ticket in self._open_positions)
|
||||
|
||||
|
||||
class OrdersManager(TradeManager):
|
||||
"""Managers orders data during a backtest. It is a subclass of TradeManager. It manages access to the historical
|
||||
orders data
|
||||
"""
|
||||
_data = dict[int, TradeOrder]
|
||||
|
||||
def get_orders_range(self, *, date_from: float, date_to: float) -> tuple[TradeData, ...]:
|
||||
"""Get orders within a date range. Given the start and end date of the range.
|
||||
|
||||
Args:
|
||||
date_from (float): The start date of the range.
|
||||
date_to (float): The end date of the range.
|
||||
|
||||
Returns:
|
||||
tuple[TradeData, ...]: The orders within the date range.
|
||||
"""
|
||||
start = date_from.timestamp() if isinstance(date_from, datetime) else date_from
|
||||
end = date_to.timestamp() if isinstance(date_to, datetime) else date_to
|
||||
return tuple(order for order in self.values() if start <= order.time_setup <= end)
|
||||
|
||||
def history_orders_get(
|
||||
self, *, date_from: float | datetime = None, date_to: float | datetime = None, group: str = "", ticket: int = None, position: int = None
|
||||
) -> tuple[TradeOrder, ...]:
|
||||
def history_orders_get(self, *, date_from: float | datetime = None, date_to: float | datetime = None,
|
||||
group: str = "", ticket: int = None, position: int = None) -> tuple[TradeOrder, ...]:
|
||||
"""Get historical orders. Given the start and end date of the range, the group, ticket, or position of the
|
||||
orders.
|
||||
|
||||
Args:
|
||||
date_from (float, datetime): The start date of the range.
|
||||
date_to (float, datetime): The end date of the range.
|
||||
group (str): The group of the orders.
|
||||
ticket (int): The ticket of the order.
|
||||
position (int): The position of the order.
|
||||
|
||||
Returns:
|
||||
tuple[TradeOrder, ...]: The historical orders.
|
||||
"""
|
||||
if date_from and date_to:
|
||||
orders = self.get_orders_range(date_from=date_from, date_to=date_to)
|
||||
if group:
|
||||
@@ -158,6 +288,12 @@ class OrdersManager(TradeManager):
|
||||
return ()
|
||||
|
||||
def history_orders_total(self, *, date_from: datetime | float, date_to: datetime | float) -> int:
|
||||
"""Get the total number of historical orders. Given the start and end date of the range.
|
||||
|
||||
Args:
|
||||
date_from (datetime, float): The start date of the range.
|
||||
date_to (datetime, float): The end date of the range.
|
||||
"""
|
||||
return len(self.get_orders_range(date_from=date_from, date_to=date_to))
|
||||
|
||||
|
||||
@@ -165,13 +301,30 @@ class DealsManager(TradeManager):
|
||||
_data = dict[int, TradeDeal]
|
||||
|
||||
def get_deals_range(self, *, date_from: float, date_to: float) -> tuple[TradeData, ...]:
|
||||
"""Get deals within a date range. Given the start and end date of the range.
|
||||
|
||||
Args:
|
||||
date_from (float): The start date of the range.
|
||||
date_to (float): The end date of the range.
|
||||
|
||||
Returns:
|
||||
tuple[TradeData, ...]: The deals within the date range.
|
||||
"""
|
||||
start = date_from.timestamp() if isinstance(date_from, datetime) else date_from
|
||||
end = date_to.timestamp() if isinstance(date_to, datetime) else date_to
|
||||
return tuple(deal for deal in self.values() if start <= deal.time <= end)
|
||||
|
||||
def history_deals_get(
|
||||
self, *, date_from: float | datetime = None, date_to: float | datetime = None, group: str = "", ticket: int = None, position: int = None
|
||||
) -> tuple[TradeDeal, ...]:
|
||||
def history_deals_get(self, *, date_from: float | datetime = None, date_to: float | datetime = None,
|
||||
group: str = "", ticket: int = None, position: int = None) -> tuple[TradeDeal, ...]:
|
||||
"""History deals get. Given the start and end date of the range, the group, ticket, or position of the deals.
|
||||
|
||||
Args:
|
||||
date_from (float, datetime): The start date of the range.
|
||||
date_to (float, datetime): The end date of the range.
|
||||
group (str): The group of the deals.
|
||||
ticket (int): The ticket of the deal.
|
||||
position (int): The position of the deal.
|
||||
"""
|
||||
if date_from and date_to:
|
||||
deals = self.get_deals_range(date_from=date_from, date_to=date_to)
|
||||
if group:
|
||||
@@ -187,4 +340,13 @@ class DealsManager(TradeManager):
|
||||
return ()
|
||||
|
||||
def history_deals_total(self, *, date_from: datetime | float, date_to: datetime | float) -> int:
|
||||
"""Get the total number of historical deals. Given the start and end date of the range
|
||||
|
||||
Args:
|
||||
date_from (datetime, float): The start date of the range.
|
||||
date_to (datetime, float): The end date of the range.
|
||||
|
||||
Returns:
|
||||
int: The total number of historical deals.
|
||||
"""
|
||||
return len(self.get_deals_range(date_from=date_from, date_to=date_to))
|
||||
|
||||
+16
-7
@@ -12,10 +12,16 @@ logger = getLogger(__name__)
|
||||
class Base:
|
||||
"""A base class for all data structure classes in the aiomql package. This class provides a set of common methods
|
||||
and attributes for handling data.
|
||||
|
||||
Attributes:
|
||||
exclude (set[str]): A set of attributes to be excluded when retrieving attributes
|
||||
using the get_dict and dict method.
|
||||
include (set [str]): A set of attributes to be included when retrieving attributes
|
||||
using the get_dict and dict method.
|
||||
"""
|
||||
|
||||
exclude: set
|
||||
include: set
|
||||
exclude: set[str]
|
||||
include: set[str]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
@@ -74,12 +80,12 @@ class Base:
|
||||
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
|
||||
def get_dict(self, exclude: set[str] = None, include: set[str] = None) -> dict:
|
||||
"""Returns class attributes as a dict, with the ability to filter out specific attributes
|
||||
|
||||
Keyword Args:
|
||||
exclude: A set of attributes to be excluded
|
||||
include: Specific attributes to be returned
|
||||
Args:
|
||||
exclude (set[str]): A set of attributes to be excluded
|
||||
include (set[str]): Specific attributes to be returned
|
||||
|
||||
Returns:
|
||||
dict: A dictionary of specified class attributes
|
||||
@@ -120,6 +126,9 @@ class Base:
|
||||
|
||||
|
||||
class _Base(Base):
|
||||
"""Base class that provides access to the MetaTrader and Config classes as well as the MetaBackTester class for
|
||||
backtesting mode.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
self.config = Config()
|
||||
self.mt5 = MetaTrader() if self.config.mode != "backtest" else MetaBackTester()
|
||||
|
||||
+29
-24
@@ -1,4 +1,3 @@
|
||||
import inspect
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Iterator, Literal, TypeVar, Self
|
||||
@@ -12,28 +11,32 @@ Bot = TypeVar("Bot")
|
||||
BackTestEngine = TypeVar("BackTestEngine")
|
||||
|
||||
|
||||
def func():
|
||||
stack = inspect.stack()
|
||||
calling_context = next(context for context in stack if context.filename != __file__)
|
||||
print(calling_context.filename)
|
||||
return calling_context.filename
|
||||
|
||||
|
||||
class Config:
|
||||
"""A class for handling configuration settings for the aiomql package.
|
||||
|
||||
Attributes:
|
||||
record_trades (bool): Whether to keep record of trades or not.
|
||||
trade_record_mode: How to save trade, json or csv. Defaults to json
|
||||
filename (str): Name of the config file
|
||||
records_dir (str): Path to the directory where trade records are saved
|
||||
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
|
||||
state (dict): A global state dictionary for storing data across the framework
|
||||
root (Path): Root directory of the project
|
||||
login (int): The account login number
|
||||
trade_record_mode (Literal["csv", "json"]): The mode for recording trades
|
||||
password (str): The account password
|
||||
server (str): The account server
|
||||
path (str | Path): The path to the terminal
|
||||
timeout (int): The timeout argument for the terminal
|
||||
filename (str): The filename of the config file
|
||||
state (dict): The
|
||||
root (Path): The root directory of the project
|
||||
record_trades (bool): To record trades or not. Default is True
|
||||
records_dir (Path): The directory to store trade records, relative to the root directory
|
||||
records_dir_name (str): The name of the trade records directory
|
||||
backtest_dir (Path): The directory to store backtest results, relative to the root directory
|
||||
backtest_dir_name (str): The name of the backtest directory
|
||||
task_queue (TaskQueue): The TaskQueue object for handling background tasks
|
||||
_backtest_engine (BackTestEngine): The backtest engine object
|
||||
bot (Bot): The bot object
|
||||
_instance (Self): The instance of the Config class
|
||||
mode (Literal["backtest", "live"]): The trading mode, either backtest or live, default is live
|
||||
use_terminal_for_backtesting (bool): Use the terminal for backtesting, default is True
|
||||
shutdown (bool): A signal to shut down the terminal, default is False
|
||||
force_shutdown (bool): A signal to force shut down the terminal, default is False
|
||||
|
||||
Notes:
|
||||
By default, the config class looks for a file named aiomql.json.
|
||||
@@ -41,7 +44,6 @@ class Config:
|
||||
or the load_config method.
|
||||
By passing reload=True to the load_config method, you can reload and search again for the config file.
|
||||
"""
|
||||
|
||||
login: int
|
||||
trade_record_mode: Literal["csv", "json"]
|
||||
password: str
|
||||
@@ -93,6 +95,7 @@ class Config:
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize the Config object. The root directory can be set here or in the load_config method."""
|
||||
root = kwargs.pop("root", None)
|
||||
if root is not None:
|
||||
self.load_config(root=root, **kwargs)
|
||||
@@ -101,16 +104,18 @@ class Config:
|
||||
|
||||
@property
|
||||
def backtest_engine(self):
|
||||
"""Returns the backtest engine object"""
|
||||
return self._backtest_engine
|
||||
|
||||
@backtest_engine.setter
|
||||
def backtest_engine(self, value: BackTestEngine):
|
||||
"""Set the backtest engine object"""
|
||||
self._backtest_engine = value
|
||||
|
||||
def set_attributes(self, **kwargs):
|
||||
"""Set keyword arguments as object attributes, The root folder attribute can't be set here.
|
||||
|
||||
Keyword Args:
|
||||
Args:
|
||||
**kwargs: Object attributes and values as keyword arguments
|
||||
"""
|
||||
if kwargs.pop("root", None) is not None:
|
||||
@@ -146,11 +151,11 @@ class Config:
|
||||
def load_config(self, *, file: str | Path = None, filename: str = None, root: str | Path = None, **kwargs) -> Self:
|
||||
"""Load configuration settings from a file.
|
||||
|
||||
Keyword Args:
|
||||
Args:
|
||||
file (str | Path): The absolute path to the config file.
|
||||
filename (str): The name of the file to load if file path is not specified. If not provided aiomql.json is used
|
||||
root (str): The root directory of the project.
|
||||
kwargs: Additional keyword arguments to set as object attributes.
|
||||
**kwargs: Additional keyword arguments to be set on the config object.
|
||||
"""
|
||||
if root is not None:
|
||||
root = Path(root).resolve()
|
||||
@@ -197,6 +202,6 @@ class Config:
|
||||
"""Returns Account login details as found in the config object if available
|
||||
|
||||
Returns:
|
||||
dict: A dictionary of login details
|
||||
dict[str, int | str]: A dictionary of login details
|
||||
"""
|
||||
return {"login": self.login, "password": self.password, "server": self.server}
|
||||
|
||||
@@ -15,7 +15,11 @@ BackTestEngine = TypeVar("BackTestEngine")
|
||||
|
||||
|
||||
class MetaBackTester(MetaTrader):
|
||||
"""A class for testing trading strategies in the MetaTrader 5 terminal. A subclass of MetaTrader."""
|
||||
"""A class for testing trading strategies in the MetaTrader 5 terminal. A subclass of MetaTrader.
|
||||
|
||||
Attributes:
|
||||
backtest_engine (BackTestEngine): The backtesting engine to use for testing trading strategies.
|
||||
"""
|
||||
|
||||
backtest_engine: BackTestEngine
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from logging import getLogger
|
||||
from typing import Literal
|
||||
from typing import Literal, Self
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
@@ -23,7 +23,7 @@ class MetaTrader(MetaCore):
|
||||
self.config = Config()
|
||||
self.error: Error = Error(1)
|
||||
|
||||
async def __aenter__(self) -> "MetaTrader":
|
||||
async def __aenter__(self) -> Self:
|
||||
"""
|
||||
Async context manager entry point.
|
||||
Initializes the connection to the MetaTrader terminal.
|
||||
|
||||
@@ -26,25 +26,40 @@ class QueueItem:
|
||||
try:
|
||||
if asyncio.iscoroutinefunction(self.task_item):
|
||||
await self.task_item(*self.args, **self.kwargs)
|
||||
|
||||
else:
|
||||
self.task_item(*self.args, **self.kwargs)
|
||||
|
||||
except Exception as err:
|
||||
logger.error(f"Error {err} occurred in {self.task_item.__name__} with args {self.args} and kwargs {self.kwargs}")
|
||||
logger.error(f"Error {err} occurred in {self.task_item.__name__} with args {self.args} and kwargs"
|
||||
f" {self.kwargs}")
|
||||
|
||||
|
||||
class TaskQueue:
|
||||
def __init__(
|
||||
self,
|
||||
size: int = 0,
|
||||
workers: int = 10,
|
||||
timeout: int = None,
|
||||
queue: asyncio.Queue = None,
|
||||
on_exit: Literal["cancel", "complete_priority"] = "complete_priority",
|
||||
mode: Literal["finite", "infinite"] = "infinite",
|
||||
worker_timeout: int = 60,
|
||||
):
|
||||
"""TaskQueue is a class that allows you to queue tasks and run them concurrently with a specified number of workers.
|
||||
|
||||
Attributes:
|
||||
- `workers` (int): The number of workers to run concurrently. Default is 10.
|
||||
|
||||
- `timeout` (int): The maximum time to wait for the queue to complete. Default is None. If timeout is provided
|
||||
the queue is joined using `asyncio.wait_for` with the timeout.
|
||||
|
||||
- `queue` (asyncio.Queue): The queue to store the tasks. Default is `asyncio.PriorityQueue` with no size limit.
|
||||
|
||||
- `on_exit` (Literal["cancel", "complete_priority"]): The action to take when the queue is stopped.
|
||||
|
||||
- `mode` (Literal["finite", "infinite"]): The mode of the queue. If `finite` the queue will stop when all tasks
|
||||
are completed. If `infinite` the queue will continue to run until stopped.
|
||||
|
||||
- `worker_timeout` (int): The time to wait for a task to be added to the queue before stopping the worker or
|
||||
adding a dummy sleep task to the queue.
|
||||
|
||||
- `stop` (bool): A flag to stop the queue instance.
|
||||
|
||||
- `tasks` (list): A list of the worker tasks running concurrently, including the main task that joins the queue.
|
||||
|
||||
- `priority_tasks` (set): A set to store the QueueItems that must complete before the queue stops.
|
||||
"""
|
||||
|
||||
def __init__(self, size: int = 0, workers: int = 10, timeout: int = None, queue: asyncio.Queue = None,
|
||||
on_exit: Literal["cancel", "complete_priority"] = "complete_priority",
|
||||
mode: Literal["finite", "infinite"] = "infinite", worker_timeout: int = 60):
|
||||
self.queue = queue or asyncio.PriorityQueue(maxsize=size)
|
||||
self.workers = workers
|
||||
self.tasks = []
|
||||
@@ -55,7 +70,14 @@ class TaskQueue:
|
||||
self.mode = mode
|
||||
self.worker_timeout = worker_timeout
|
||||
|
||||
def add(self, *, item: QueueItem, priority=3, must_complete=False):
|
||||
def add(self, *, item: QueueItem, priority: int = 3, must_complete: bool = False):
|
||||
"""Add a task to the queue.
|
||||
|
||||
Args:
|
||||
item (QueueItem): The task to add to the queue.
|
||||
priority (int): The priority of the task. Default is 3.
|
||||
must_complete (bool): A flag to indicate if the task must complete before the queue stops. Default is False.
|
||||
"""
|
||||
try:
|
||||
if self.stop:
|
||||
return
|
||||
@@ -67,6 +89,9 @@ class TaskQueue:
|
||||
except asyncio.QueueFull:
|
||||
logger.error("Queue is full")
|
||||
|
||||
except Exception as err:
|
||||
logger.error("%s: Error occurred in %s.add", err, self.__class__.__name__)
|
||||
|
||||
async def worker(self):
|
||||
while True:
|
||||
try:
|
||||
@@ -98,7 +123,7 @@ class TaskQueue:
|
||||
await asyncio.sleep(self.worker_timeout)
|
||||
|
||||
except Exception as err:
|
||||
logger.error("%s: Error occurred in worker", err)
|
||||
logger.error("%s: Error occurred in %s worker", err, self.__class__.__name__)
|
||||
|
||||
async def run(self, timeout: int = 0):
|
||||
start = time.perf_counter()
|
||||
@@ -145,7 +170,7 @@ class TaskQueue:
|
||||
...
|
||||
|
||||
except Exception as err:
|
||||
logger.error(f"%s: Error occurred in %s.clean_up", err, self.__class__.__name__)
|
||||
logger.error("%s: Error occurred in %s.clean_up", err, self.__class__.__name__)
|
||||
|
||||
finally:
|
||||
self.cancel()
|
||||
|
||||
@@ -194,10 +194,10 @@ class Candles:
|
||||
def __len__(self):
|
||||
return len(self._data.index)
|
||||
|
||||
def __contains__(self, item: Self):
|
||||
def __contains__(self, item: Candle):
|
||||
return item.time == self[item.Index].time
|
||||
|
||||
def __getitem__(self, index) -> Self | Self | Series:
|
||||
def __getitem__(self, index: slice | int | str) -> Self | Series | Candle:
|
||||
if isinstance(index, slice):
|
||||
cls = self.__class__
|
||||
data = self._data.iloc[index]
|
||||
|
||||
Reference in New Issue
Block a user