diff --git a/.gitignore b/.gitignore
index 10839b0..30ff9ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,7 +78,6 @@ aiomql.json
# development
terminals/
-*.pkl
backtesting/
trade_records/
-
+plots/
diff --git a/docs/core/config.md b/docs/core/config.md
index a0d3636..832cc7f 100644
--- a/docs/core/config.md
+++ b/docs/core/config.md
@@ -29,6 +29,7 @@ A single instance of this class is created and used per bot instance.
| `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 |
+| `plots_dir` | `Path` | Save chart plots as images |
| `backtest_dir` | `Path` | The directory to store backtest results, relative to the root directory |
| `task_queue` | `TaskQueue` | The TaskQueue object for handling background tasks |
| `_backtest_engine` | `BackTestEngine` | The backtest engine object |
diff --git a/docs/lib/candle.md b/docs/lib/candle.md
index 80bb15c..bca6ed9 100644
--- a/docs/lib/candle.md
+++ b/docs/lib/candle.md
@@ -14,6 +14,8 @@ Candle and Candles classes for handling bars from the MetaTrader 5 terminal.
- [ta_lib](#candles.ta_lib)
- [data](#candles.data)
- [rename](#candles.rename)
+ - [plot](#candles.plot)
+ - [make_subplot](#candles.make_subplot)
### Candle
@@ -22,18 +24,21 @@ class Candle
```
A class representing bars from the MetaTrader 5 terminal as a customized class analogous to Japanese Candlesticks.
You can subclass this class for added customization.
+
### Attributes
-| Name | Type | Description |
-|---------------|---------|-------------------------------------------------------------------------|
-| `time` | `int` | Period start time |
-| `open` | `int` | Open price |
-| `high` | `float` | The highest price of the period |
-| `low` | `float` | The lowest price of the period |
-| `close` | `float` | Close price |
-| `tick_volume` | `float` | Tick volume |
-| `real_volume` | `float` | Trade volume |
-| `spread` | `float` | Spread |
-| `Index` | `int` | Custom attribute representing the position of the candle in a sequence. |
+
+| Name | Type | Description |
+|---------------|-------------|-------------------------------------------------------------------------|
+| `time` | `int` | Period start time |
+| `open` | `int` | Open price |
+| `high` | `float` | The highest price of the period |
+| `low` | `float` | The lowest price of the period |
+| `close` | `float` | Close price |
+| `tick_volume` | `float` | Tick volume |
+| `real_volume` | `float` | Trade volume |
+| `spread` | `float` | Spread |
+| `Index` | `int` | Custom attribute representing the position of the candle in a sequence. |
+| `index` | `Timestamp` | Index of the object in the DataFrame, as a timestamp. |
### \_\_init\_\_
@@ -118,24 +123,25 @@ object for accessing the columns of the underlying data attribute.
The attributes of this class vary depending on the columns of underlying **data** attribute. i.e. each column of the **data**
attribute is an attribute of the class.
-| Name | Type | Description |
-|---------------|-----------------|-------------------------------------------------------------------|
-| `data` | `DataFrame` | The pandas DataFrame containing the data. |
-| `Index` | `Series['int']` | A pandas Series of the indexes of all candles in the object |
-| `time` | `Series['int']` | A pandas Series of the time of all candles in the object |
-| `open` | `Series[float]` | A pandas Series of the opening price of all candles in the object |
-| `high` | `Series[float]` | A pandas Series of the high price of all candles in the object |
-| `low` | `Series[float]` | A pandas Series of the low price of all candles in the object |
-| `close` | `Series[float]` | A pandas Series of the closing price of all candles in the object |
-| `tick_volume` | `Series[float]` | A pandas Series of the tick volume of all candles in the object |
-| `real_volume` | `Series[float]` | A pandas Series of the real volume of all candles in the object |
-| `spread` | `Series[float]` | A pandas Series of the spread of all candles in the object |
-| `timeframe` | `TimeFrame` | The timeframe of the candles in the object |
-| `Candle` | `Type[Candle]` | The Candle class for representing the candles in the object. |
-| `data` | `DataFrame` | A pandas DataFrame of all candles in the object. |
+| Name | Type | Description |
+|---------------|-----------------------|-------------------------------------------------------------------|
+| `data` | `DataFrame` | The pandas DataFrame containing the data. |
+| `Index` | `Series['int']` | A pandas Series of the indexes of all candles in the object |
+| `index` | `Series['Timestamp']` | DatetimeIndex of the underlying DataFrame object. |
+| `time` | `Series['int']` | A pandas Series of the time of all candles in the object |
+| `open` | `Series[float]` | A pandas Series of the opening price of all candles in the object |
+| `high` | `Series[float]` | A pandas Series of the high price of all candles in the object |
+| `low` | `Series[float]` | A pandas Series of the low price of all candles in the object |
+| `close` | `Series[float]` | A pandas Series of the closing price of all candles in the object |
+| `tick_volume` | `Series[float]` | A pandas Series of the tick volume of all candles in the object |
+| `real_volume` | `Series[float]` | A pandas Series of the real volume of all candles in the object |
+| `spread` | `Series[float]` | A pandas Series of the spread of all candles in the object |
+| `timeframe` | `TimeFrame` | The timeframe of the candles in the object |
+| `Candle` | `Type[Candle]` | The Candle class for representing the candles in the object. |
+| `data` | `DataFrame` | A pandas DataFrame of all candles in the object. |
#### Notes
-When subclassing this class make sure the Candle attribute is set to your desired candle class.
+When subclassing this class, make sure the Candle attribute is set to your desired candle class.
### \_\_init\_\_
@@ -203,3 +209,34 @@ Rename columns of the data object.
| Type | Description |
|-----------|---------------------------------------------------------------------------|
| `Candles` | A new instance of the class with the renamed columns if inplace is False. |
+
+
+
+### plot
+```python
+def plot(subplots=None, span: int = None, filename="", **kwargs):
+```
+Create a plot with mplfinance, can be saved as png.
+
+#### Parameters:
+| Name | Type | Description | Default |
+|------------|--------|-------------------------|---------|
+| `subplots` | `dict` | Add subplots | None |
+| `span` | `int` | Take the last n candles | None |
+| `filename` | `str` | A name to save plot | "" |
+| `**kwargs` | `Any` | Kwargs to plot function | |
+
+
+
+### make_subplot
+```python
+def make_subplot(column: str | list[str], span: int = None, **kwargs):
+```
+Create a plot with mplfinance, can be saved as png.
+
+#### Parameters:
+| Name | Type | Description | Default |
+|------------|-------------------|--------------------------------------------------|---------|
+| `column` | `list[str]\| str` | An iterable of column names as a list or strings | None |
+| `span` | `int` | Take the last n candles | None |
+| `**kwargs` | `Any` | Kwargs to plot function | |
diff --git a/docs/lib/ticks.md b/docs/lib/ticks.md
index 6330195..b5fcb56 100644
--- a/docs/lib/ticks.md
+++ b/docs/lib/ticks.md
@@ -33,6 +33,7 @@ Price Tick of a Financial Instrument.
| `flags` | `TickFlag` | Tick flags | None |
| `volume_real` | `float` | Volume for the current Last price | None |
| `Index` | `int` | Custom attribute representing the position of the tick in a sequence. | None |
+| `index` | `int` | Index of the tick in the input dataframe object. | None |
@@ -86,12 +87,13 @@ Supports iteration, slicing and assignment. Similar to `Candles` class but for p
| `flags` | `Series` | Tick flags | None |
| `volume_real` | `Series` | Volume for the current Last price | None |
| `Index` | `Series` | Custom attribute representing the position of the tick in a sequence | None |
+| `index` | `Series` | Custom attribute representing the index of the tick in the DataFrame | None |
### \__init\__
```python
-def __init__(*, data: DataFrame | Iterable, flip=False)
+def __init__(*, data: DataFrame | Iterable, flip=False):
```
Initialize the Ticks class. Creates a DataFrame of price ticks from the data argument.
#### Arguments:
diff --git a/requirements.txt b/requirements.txt
index ac3ed1a..fa86efa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,158 +1,118 @@
-anyio==4.3.0
-argon2-cffi==23.1.0
+-e git+ssh://git@github.com/VChijioke/aiomql.git@626658d68ac771353b424d50a33e7dd0de6a9054#egg=aiomql
+anyio==4.9.0
+argon2-cffi==25.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
-asttokens==2.4.1
-async-lru==2.0.4
-attrs==23.2.0
-Babel==2.14.0
-beautifulsoup4==4.12.3
-black==23.9.1
-bleach==6.1.0
-build==1.2.2.post1
-certifi==2023.7.22
-cffi==1.16.0
-charset-normalizer==3.3.0
-click==8.1.7
+asttokens==3.0.0
+async-lru==2.0.5
+attrs==25.3.0
+babel==2.17.0
+beautifulsoup4==4.13.4
+bleach==6.2.0
+certifi==2025.4.26
+cffi==1.17.1
+charset-normalizer==3.4.2
colorama==0.4.6
comm==0.2.2
-contourpy==1.2.1
+contourpy==1.3.2
cycler==0.12.1
-databind.core==4.4.1
-databind.json==4.4.1
-debugpy==1.8.1
-decorator==5.1.1
+debugpy==1.8.14
+decorator==5.2.1
defusedxml==0.7.1
-Deprecated==1.2.14
-docspec==2.2.1
-docspec-python==2.2.1
-docstring-parser==0.11
-docutils==0.20.1
-docutils-stubs==0.0.22
-executing==2.0.1
-fastjsonschema==2.19.1
-fonttools==4.51.0
+executing==2.2.0
+fastjsonschema==2.21.1
+fonttools==4.58.2
fqdn==1.5.1
-h11==0.14.0
-httpcore==1.0.5
-httpx==0.27.0
-idna==3.4
-importlib-metadata==6.8.0
-iniconfig==2.0.0
-ipykernel==6.29.4
-ipython==8.23.0
-ipywidgets==8.1.2
+h11==0.16.0
+httpcore==1.0.9
+httpx==0.28.1
+idna==3.10
+iniconfig==2.1.0
+ipykernel==6.29.5
+ipython==9.3.0
+ipython_pygments_lexers==1.1.1
+ipywidgets==8.1.7
isoduration==20.11.0
-jaraco.classes==3.3.0
-jedi==0.19.1
-Jinja2==3.1.2
-json5==0.9.24
-jsonpointer==2.4
-jsonschema==4.21.1
-jsonschema-specifications==2023.12.1
-jupyter==1.0.0
+jedi==0.19.2
+Jinja2==3.1.6
+json5==0.12.0
+jsonpointer==3.0.0
+jsonschema==4.24.0
+jsonschema-specifications==2025.4.1
+jupyter==1.1.1
jupyter-console==6.6.3
-jupyter-events==0.10.0
+jupyter-events==0.12.0
jupyter-lsp==2.2.5
-jupyter_client==8.6.1
-jupyter_core==5.7.2
-jupyter_server==2.13.0
+jupyter_client==8.6.3
+jupyter_core==5.8.1
+jupyter_server==2.16.0
jupyter_server_terminals==0.5.3
-jupyterlab==4.1.6
+jupyterlab==4.4.3
jupyterlab_pygments==0.3.0
-jupyterlab_server==2.26.0
-jupyterlab_widgets==3.0.10
-keyring==24.2.0
-kiwisolver==1.4.5
-markdown-it-py==3.0.0
-MarkupSafe==2.1.3
-matplotlib==3.8.4
-matplotlib-inline==0.1.6
-mdurl==0.1.2
-MetaTrader5==5.0.4424
-mistune==3.0.2
-more-itertools==10.1.0
+jupyterlab_server==2.27.3
+jupyterlab_widgets==3.0.15
+kiwisolver==1.4.8
+MarkupSafe==3.0.2
+matplotlib==3.10.3
+matplotlib-inline==0.1.7
+MetaTrader5==5.0.5050
+mistune==3.1.3
mplfinance==0.12.10b0
-mypy-extensions==1.0.0
-nbclient==0.10.0
-nbconvert==7.16.3
+nbclient==0.10.2
+nbconvert==7.16.6
nbformat==5.10.4
nest-asyncio==1.6.0
-nh3==0.2.14
-notebook==7.1.2
+notebook==7.4.3
notebook_shim==0.2.4
-nr-date==2.1.0
-nr-stream==1.1.5
-nr.util==0.8.12
numpy==1.26.0
overrides==7.7.0
-packaging==23.2
-pandas==2.1.1
-pandas-ta==0.3.14b0
+packaging==25.0
+pandas==2.3.0
+pandas_ta==0.3.14b0
pandocfilters==1.5.1
parso==0.8.4
-pathspec==0.11.2
-pillow==10.3.0
-pkginfo==1.9.6
-platformdirs==3.11.0
-pluggy==1.5.0
-prometheus_client==0.20.0
-prompt-toolkit==3.0.43
-psutil==5.9.8
-pure-eval==0.2.2
+pillow==11.2.1
+platformdirs==4.3.8
+pluggy==1.6.0
+prometheus_client==0.22.1
+prompt_toolkit==3.0.51
+psutil==7.0.0
+pure_eval==0.2.3
pycparser==2.22
-pydoc-markdown==4.8.2
-Pygments==2.16.1
-pyparsing==3.1.2
-pyproject_hooks==1.0.0
-pytest==8.3.3
-pytest-asyncio==0.24.0
+Pygments==2.19.1
+pyparsing==3.2.3
+pytest==8.4.1
+pytest-asyncio==1.0.0
pytest-order==1.3.0
-python-dateutil==2.8.2
-python-json-logger==2.0.7
-python-telegram-bot==21.0.1
-pytz==2023.3.post1
-pywin32==306
-pywin32-ctypes==0.2.2
-pywinpty==2.0.13
-PyYAML==6.0.1
-pyzmq==25.1.2
-qtconsole==5.5.1
-QtPy==2.4.1
-readme-renderer==42.0
-referencing==0.34.0
-requests==2.31.0
-requests-toolbelt==1.0.0
+python-dateutil==2.9.0.post0
+python-json-logger==3.3.0
+pytz==2025.2
+pywin32==310
+pywinpty==2.0.15
+PyYAML==6.0.2
+pyzmq==26.4.0
+referencing==0.36.2
+requests==2.32.3
rfc3339-validator==0.1.4
-rfc3986==2.0.0
rfc3986-validator==0.1.1
-rich==13.6.0
-rpds-py==0.18.0
-ruff==0.7.3
+rpds-py==0.25.1
Send2Trash==1.8.3
-six==1.16.0
+setuptools==80.9.0
+six==1.17.0
sniffio==1.3.1
-soupsieve==2.5
+soupsieve==2.7
stack-data==0.6.3
terminado==0.18.1
-tinycss2==1.2.1
-tomli==2.0.1
-tomli_w==1.0.0
-tornado==6.4
-traitlets==5.14.2
-twine==5.1.1
-typeapi==2.1.1
-types-python-dateutil==2.9.0.20240316
-typing_extensions==4.6.3
-tzdata==2023.3
+tinycss2==1.4.0
+tornado==6.5.1
+traitlets==5.14.3
+types-python-dateutil==2.9.0.20250516
+typing_extensions==4.14.0
+tzdata==2025.2
uri-template==1.3.0
-urllib3==2.0.6
-watchdog==3.0.0
+urllib3==2.4.0
wcwidth==0.2.13
-webcolors==1.13
+webcolors==24.11.1
webencodings==0.5.1
-websocket-client==1.7.0
-widgetsnbextension==4.0.10
-wrapt==1.15.0
-yapf==0.40.2
-zipp==3.17.0
+websocket-client==1.8.0
+widgetsnbextension==4.0.14
diff --git a/src/aiomql/core/config.py b/src/aiomql/core/config.py
index 1e42dab..e61d523 100644
--- a/src/aiomql/core/config.py
+++ b/src/aiomql/core/config.py
@@ -25,8 +25,10 @@ class Config:
root: Path
record_trades: bool
records_dir: Path
+ plots_dir: Path
backtest_dir: Path
records_dir_name: str
+ plots_dir_name: str
backtest_dir_name: str #Todo: add to docs
task_queue: TaskQueue
_backtest_engine: BackTestEngine
@@ -54,6 +56,7 @@ class Config:
"shutdown": False,
"force_shutdown": False,
"root": None,
+ "plots_dir_name": "plots"
}
def __new__(cls, *args, **kwargs):
@@ -110,7 +113,7 @@ class Config:
if config_file.exists():
return config_file
if current == self.root:
- return
+ return None
for dirname in current.parents:
config_file = dirname / self.filename
if config_file.exists():
@@ -118,9 +121,10 @@ class Config:
if self.root == dirname:
break
+ return None
except Exception as err:
logger.debug(f"Error finding config file: {err}")
-
+ return None
def load_config(self, *, config_file: str | Path = None, filename: str = None, root: str | Path = None, **kwargs) -> Self:
"""Load configuration settings from a file and reset the config object.
@@ -182,6 +186,12 @@ class Config:
b_dir.mkdir(parents=True, exist_ok=True) if b_dir.exists() is False else ...
return b_dir
+ @property
+ def plots_dir(self):
+ p_dir = self.root / self.plots_dir_name or "plots"
+ p_dir.mkdir(parents=True, exist_ok=True) if p_dir.exists() is False else ...
+ return p_dir
+
def account_info(self) -> dict[str, int | str]:
"""Returns Account login details as found in the config object if available
diff --git a/src/aiomql/lib/candle.py b/src/aiomql/lib/candle.py
index 7cbdeb1..ed476ea 100644
--- a/src/aiomql/lib/candle.py
+++ b/src/aiomql/lib/candle.py
@@ -5,10 +5,12 @@ from typing import Type, Self, Iterable
from logging import getLogger
import pandas as pd
+import mplfinance as mpf
import pandas_ta as ta
from pandas import DataFrame, Series, DatetimeIndex, Timestamp
from ..core.constants import TimeFrame
+from ..core._core import Config
logger = getLogger(__name__)
@@ -203,6 +205,7 @@ class Candles:
self._data.index = pd.DatetimeIndex(self._data.time, dtype=dtype)
self.Candle = candle_class or Candle
+ self.config = Config()
def __repr__(self):
return repr(self._data)
@@ -346,3 +349,39 @@ class Candles:
return self
else:
raise TypeError("Expected Series, DataFrame or Candle, got {}".format(type(obj)))
+
+ def plot(self, subplots: dict = None, span: int = None, filename="", **kwargs):
+ """
+ Create a plot of the candles
+
+ Args:
+ subplots (dict): Subplots to bed added to the main plot
+ span (int): Last 'n' candles to be used for making the subplot
+ filename (str): A filename to saved the plot
+ **kwargs: Kwargs to be passed to the plot
+ """
+ type_ = kwargs.pop("type", "candle")
+ subplots = subplots or []
+ span = 0 if span is None else span
+ data = self._data[-span:]
+ if filename and not kwargs.get("savefig"):
+ kwargs["savefig"] = self.config.plots_dir / filename
+
+ mpf.plot(data, type=type_, addplot=subplots, **kwargs)
+
+ def make_subplot(self, *, column: str | list[str], span: int = None, **kwargs) -> dict:
+ """
+ Create a subplot
+ Args:
+ column (list[str] | str): Name of columns for the subplot
+ span (int): Last 'n' candles to be used for making the subplot
+ **kwargs: Keywords arguments to pass to the subplot
+
+ Returns:
+ dict: Subplots
+ """
+ column = column if isinstance(column, list) else [column]
+ span = 0 if span is None else span
+ data = self._data[-span:]
+ data = data[column]
+ return mpf.make_addplot(data, **kwargs)
diff --git a/src/aiomql/lib/ticks.py b/src/aiomql/lib/ticks.py
index 510ab2d..4750d89 100644
--- a/src/aiomql/lib/ticks.py
+++ b/src/aiomql/lib/ticks.py
@@ -23,6 +23,7 @@ class Tick:
flags (TickFlag): Tick flags
volume_real (float): Volume for the current Last price
Index (int): Custom attribute representing the position of the tick in a sequence.
+ index (int): Index of the tick in the input dataframe object.
"""
time: float
bid: float