Add visualization with mplfinance.

Add config attribute to Candles class.
Add plots_dir property and plots_dir_name attribute to the Config class.
Update documentation for Candle, Candles and Config classes.
This commit is contained in:
Victor Joseph
2025-06-29 11:48:28 +01:00
parent 50ac62b0a8
commit 626658d68a
8 changed files with 121 additions and 190 deletions
+1 -2
View File
@@ -78,7 +78,6 @@ aiomql.json
# development
terminals/
*.pkl
backtesting/
trade_records/
plots/
+1
View File
@@ -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 |
+64 -27
View File
@@ -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)
<a id="candle.candle"></a>
### 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. |
<a id='candle.__init__'></a>
### \_\_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.
<a id="candles.__init__"></a>
### \_\_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. |
<a id="candles.plot"></a>
### 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 | |
<a id="candles.make_subplot"></a>
### 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 | |
+3 -1
View File
@@ -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 |
<a id="tick.__init__"></a>
@@ -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 |
<a id="ticks.__init__"></a>
### \__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:
-158
View File
@@ -1,158 +0,0 @@
anyio==4.3.0
argon2-cffi==23.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
colorama==0.4.6
comm==0.2.2
contourpy==1.2.1
cycler==0.12.1
databind.core==4.4.1
databind.json==4.4.1
debugpy==1.8.1
decorator==5.1.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
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
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
jupyter-console==6.6.3
jupyter-events==0.10.0
jupyter-lsp==2.2.5
jupyter_client==8.6.1
jupyter_core==5.7.2
jupyter_server==2.13.0
jupyter_server_terminals==0.5.3
jupyterlab==4.1.6
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
mplfinance==0.12.10b0
mypy-extensions==1.0.0
nbclient==0.10.0
nbconvert==7.16.3
nbformat==5.10.4
nest-asyncio==1.6.0
nh3==0.2.14
notebook==7.1.2
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
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
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
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
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
Send2Trash==1.8.3
six==1.16.0
sniffio==1.3.1
soupsieve==2.5
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
uri-template==1.3.0
urllib3==2.0.6
watchdog==3.0.0
wcwidth==0.2.13
webcolors==1.13
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
+12 -2
View File
@@ -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
+39
View File
@@ -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)
+1
View File
@@ -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