diff --git a/backtesting/backtest_data_01_05_24_06_05_24.json b/backtesting/backtest_data_01_05_24_06_05_24.json index 7c255b0..6b09c90 100644 --- a/backtesting/backtest_data_01_05_24_06_05_24.json +++ b/backtesting/backtest_data_01_05_24_06_05_24.json @@ -1,17 +1,17 @@ { - "balance": 281.16, + "balance": 434.73, "profit": 0, - "equity": 281.16, + "equity": 434.73, "margin": 0.0, - "margin_free": 281.16, + "margin_free": 434.73, "margin_level": 0, - "wins": 85, - "losses": 95, - "total": 180, - "win_percentage": 47.22, - "win": 877.56, - "loss": -946.4, - "net_profit": -68.84, - "profit_factor": 0.93, - "profitability": -19.67 + "wins": 97, + "losses": 110, + "total": 207, + "win_percentage": 46.86, + "win": 1101.92, + "loss": -1017.19, + "net_profit": 84.73, + "profit_factor": 1.08, + "profitability": 24.21 } \ No newline at end of file diff --git a/check.py b/check.py new file mode 100644 index 0000000..cab2017 --- /dev/null +++ b/check.py @@ -0,0 +1,20 @@ +import asyncio + + +def run(): + for i in range(10): + print('running') + + +async def run_async(): + for i in range(10): + await asyncio.sleep(1) + print('running async') + + +async def main(): + await run_async() + run() + + +asyncio.run(main()) diff --git a/docs/lib/candle.md b/docs/lib/candle.md index ec2fb88..80bb15c 100644 --- a/docs/lib/candle.md +++ b/docs/lib/candle.md @@ -2,19 +2,20 @@ Candle and Candles classes for handling bars from the MetaTrader 5 terminal. ## Table of Contents -- [Candle](#candle) +- [Candle](#candle.candle) - [\_\_init\_\_](#candle.__init__) - [set_attributes](#candle.set_attributes) - [is_bullish](#candle.is_bullish) - [is_bearish](#candle.is_bearish) -- [Candles](#candles) + - [dict](#candle.dict) +- [Candles](#candles.candles) - [\_\_init\_\_](#candles.__init__) - [ta](#candles.ta) - [ta_lib](#candles.ta_lib) - [data](#candles.data) - [rename](#candles.rename) - + ### Candle ```python class Candle @@ -91,18 +92,18 @@ def dict(self, exclude: set = None, include: set = None) -> Dict[str, Any] ``` Return a dictionary representation of the Candle object. #### Parameters: -| Name | Type | Description | -|-----------|------------|-----------------------------------------------------------------------------| -| `exclude` | `set[str]` | A set of attributes to exclude from the dictionary. | -| `include` | `set[str]` | A set of attributes to include in the dictionary. | +| Name | Type | Description | +|-----------|------------|-----------------------------------------------------| +| `exclude` | `set[str]` | A set of attributes to exclude from the dictionary. | +| `include` | `set[str]` | A set of attributes to include in the dictionary. | #### Returns: -| Type | Description | -|---------------|--------------------------------------------------| -| `Dict[str, Any]` | A dictionary representation of the Candle object.| +| Type | Description | +|------------------|---------------------------------------------------| +| `Dict[str, Any]` | A dictionary representation of the Candle object. | -### Candles +### Candles ```python class Candles(Generic[_Candle]) ``` @@ -111,10 +112,9 @@ object. All the data pulled from the chart is stored as a pandas DataFrame objec This class can be sliced, iterated over, and indexed like a sequence. It also has access to the pandas_ta library. Indexing it returns a Candle object. It can be sliced to return a new instance of the class with the sliced candles. This slices and resets the index of the underlying dataframe object. Key based indexing is also supported on the candles -object for accessing the columns of the underlying data attribute. Add operations between two candles objects or between a -candles object and a candle object are also supported. +object for accessing the columns of the underlying data attribute. -### Attributes +### Attributes: 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. @@ -203,40 +203,3 @@ Rename columns of the data object. | Type | Description | |-----------|---------------------------------------------------------------------------| | `Candles` | A new instance of the class with the renamed columns if inplace is False. | - - - -### visualize -```python -async def visualize(self, *, count: int = 50, type='candle', savefig: str | dict = None, addplot: dict = None, - style: str = 'charles', ylabel: str = 'Price', title: str = 'Chart', **kwargs) -``` -Visualize the candles using the mplfinance library. -#### Parameters: -| Name | Type | Description | Default | -|----------|------------------|-----------------------------------------------------------------------------------------------|---------| -| `count` | `int` | The number of candles to visualize. | 50 | -| `type` | `str` | The type of chart to plot. | 'candle'| -| `savefig`| `str` or `dict` | The path to save the figure or a dictionary of keyword arguments to pass to the savefig method.| None | -| `addplot`| `dict` | A dictionary of keyword arguments to pass to the addplot method. | None | -| `style` | `str` | The style of the chart. | 'charles'| -| `ylabel` | `str` | The label of the y-axis. | 'Price' | -| `title` | `str` | The title of the chart. | 'Chart' | -| `kwargs` | `Any` | Additional keyword arguments to pass to the plot method. | | - - -```python -def make_addplot(self, *, count: int = 50, columns: list = None, **kwargs) -> dict -``` -Make subplots for adding to the main plot. -#### Parameters: -| Name | Type | Description | Default | -|-----------|--------|-----------------------------------------------------------------------------------------------|---------| -| `count` | `int` | The number of candles to visualize. | 50 | -| `columns` | `list` | The columns to plot. | None | -| `kwargs` | `Any` | Additional keyword arguments to pass to the addplot method. | | - -#### Returns: -| Type | Description | -|------|-----------------| -| dict | A makeplot dict | diff --git a/docs/lib/executor.md b/docs/lib/executor.md index 0c4d153..dcd3cd2 100644 --- a/docs/lib/executor.md +++ b/docs/lib/executor.md @@ -3,11 +3,9 @@ ## Table of Contents - [Executor](#executor.Executor) - [__init__](#executor.__init__) -- [add_workers](#executor.add_workers) -- [remove_workers](#executor.remove_workers) -- [add_worker](#executor.add_worker) -- [run](#executor.run) -- [trade](#executor.trade) +- [add_function](#executor.add_function) +- [add_coroutine](#executor.add_coroutine) +- [run_function](#executor.run_function) - [execute](#executor.execute) @@ -17,12 +15,12 @@ class Executor ``` Executor class for running multiple strategies on multiple symbols concurrently. #### Attributes: -| Name | Type | Description | Default | -|--------------|----------------------|------------------------------------------------|---------| -| `executor` | `ThreadPoolExecutor` | The default thread executor. | None | -| `workers` | `list` | List of strategies. | [] | -| `coroutines` | `dict` | Dictionary of coroutines and keyword arguments | {} | -| `functions` | `dict` | Dictionary of functions and keyword arguments | {} | +| Name | Type | Description | Default | +|--------------------|----------------------|------------------------------------------------|---------| +| `executor` | `ThreadPoolExecutor` | The default thread executor. | None | +| `strategy_runners` | `list[Strategy]` | List of strategies. | [] | +| `coroutines` | `dict` | Dictionary of coroutines and keyword arguments | {} | +| `functions` | `dict` | Dictionary of functions and keyword arguments | {} | #### \_\_init\_\_ @@ -31,72 +29,66 @@ def __init__(self): ``` Initialize the executor class. - -### add\_workers + +### add_coroutine ```python -def add_workers(strategies: Sequence[type(Strategy)]) +def add_coroutine(self,*,coroutine: Callable | Coroutine,kwargs: dict = None,on_separate_thread=False): ``` -Add multiple strategies at once -#### Arguments: -| Name | Type | Description | -|--------------|----------------------------|---------------------------| -| `strategies` | `Sequence[type(Strategy)]` | A sequence of strategies. | +Submit a coroutine to the executor. The coroutines are run in parallel using *asyncio.gather* except when the +on_spate_thread flag is set to True. In that case, the coroutine is run in a separate thread. - -### remove\_workers +#### Arguments: +| Name | Type | Description | +|----------------------|------------|-------------------------------------------------| +| `coroutine` | `Callable` | The coroutine | +| `kwargs` | `Dict` | The keyword arguments to pass to the coroutine. | +| `on_separate_thread` | `bool` | If True run the coroutine on a separate thread | + + +### add_function ```python -def remove_workers(*symbols: Sequence[Symbol]) +def add_function(self, *, function: Callable, kwargs: dict = None) ``` -Removes any worker running on a symbol not successfully initialized. -#### Arguments: -| Name | Type | Description | -|-----------|--------------------|------------------------| -| `symbols` | `Sequence[Symbol]` | A sequence of symbols. | +Submit a function to the executor. Each functions runs on a separate thread. - -### add\_worker -```python -def add_worker(strategy: type(Strategy)) -``` -Add a strategy instance to the list of workers #### Arguments: -| Name | Type | Description | -|------------|------------------|----------------------| -| `strategy` | `type(Strategy)` | A strategy instance. | +| Name | Type | Description | +|------------|------------|--------------------------------------------| +| `function` | `Callable` | The function to run in the executor | +| `kwargs` | `Dict` | Keyword arguments to pass to the function. | - -### run + +### run_function ```python @staticmethod -def run(func: Callable|Coroutine, kwargs: dict) +def run_function(function: Callable, kwargs: dict) ``` Wrap the input coroutine function with 'asyncio.run' so that it can be executed in a threadpool executor. #### Arguments: -| Name | Type | Description | -|----------|-----------|--------------------------------------------| -| `func` | `Callable | Coroutine` |A coroutine function.| -| `kwargs` | `Dict` | Keyword arguments to pass to the function. | +| Name | Type | Description | +|------------|------------|--------------------------------------------| +| `function` | `Callable` | Run a function in the executor | +| `kwargs` | `Dict` | Keyword arguments to pass to the function. | - -### trade + +### exit ```python -def trade(strategy: Strategy) +async def exit() ``` -Wrap coroutine trade method of each strategy with 'asyncio.run'. -#### Arguments: -| Name | Type | Description | -|------------|------------|----------------------| -| `strategy` | `Strategy` | A strategy instance. | +Shutdowns the executor. Due to the nature of threadpool executors, shutdown is not usually an immediate process. +This exit function is added as a coroutine function to the bot or backtester during initialization. ### execute ```python -async def execute(workers: int = 5) +def execute(workers: int = 5) ``` Run the strategies with a threadpool executor. #### Arguments: | Name | Type | Description | |-----------|-------|-----------------------------------------------------------| | `workers` | `int` | Number of workers to use in executor pool. Defaults to 5. | + #### Notes: -No matter the number specified, the executor will always use a minimum of 5 workers. +No matter the number specified, the number of workers will always be greater than equal to the minimum number of +workers required to run all functions, coroutines and strategies added to the executor. diff --git a/docs/lib/history.md b/docs/lib/history.md index d554be3..ff9bf18 100644 --- a/docs/lib/history.md +++ b/docs/lib/history.md @@ -1,19 +1,17 @@ # History ## Table of contents -- [History](#history) -- [\_\_init\_\_](#__init__) -- [init](#init) -- [get_deals](#get_deals) -- [get_deals_ticket](#get_deals_ticket) -- [get_deals_position](#get_deals_position) -- [deals_total](#deals_total) -- [get_orders](#get_orders) -- [get_orders_position](#get_orders_position) -- [get_order_ticket](#get_order_ticket) -- [orders_total](#orders_total) +- [History](#history.history) +- [\_\_init\_\_](#history.__init__) +- [initialize](#history.initialize) +- [get_deals](#history.get_deals) +- [get_deals_by_ticket](#history.get_deals_by_ticket) +- [get_deals_by_position](#history.get_deals_by_position) +- [get_orders](#history.get_orders) +- [get_orders_by_position](#history.get_orders_by_position) +- [get_orders_by_ticket](#history.get_orders_by_ticket) - + ### History ```python class History @@ -27,170 +25,111 @@ The history class handles completed trade deals and trade orders in the trading | `total_deals` | `int` | Total number of deals | 0 | | `total_orders` | `int` | Total number orders | 0 | | `group` | `str` | Filter for selecting history by symbols. | "" | -| `ticket` | `int` | Filter for selecting history by ticket number | 0 | -| `position` | `int` | Filter for selecting history deals by position | 0 | -| `initialized` | `bool` | check if initial request has been sent to the terminal to get history. | False | | `mt5` | `MetaTrader` | MetaTrader instance | None | | `config` | `Config` | Config instance | None | - + ### \_\_init\_\_ ```python -def __init__(*, - date_from: datetime | float = 0, - date_to: datetime | float = 0, - group: str = "", - ticket: int = 0, - position: int = 0) +def __init__(*, date_from: datetime | float, date_to: datetime | float, group: str = "") ``` -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |-------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| | `date_from` | `datetime\|float` | Date the deals are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' | 0 | | `date_to` | `datetime\|float` | Date up to which the deals are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" | 0 | | `group` | `str` | Filter for selecting history by symbols. | "" | -| `ticket` | `int` | Filter for selecting history by ticket number | 0 | -| `position` | `int` | Filter for selecting history deals by position | 0 | - -### init + +### initialize ```python -async def init(deals=True, orders=True) -> bool +async def initialize() -> bool ``` -Get history deals and orders -#### Parameters -| Name | Type | Description | Default | -|----------|--------|---------------------------------------------------------------|---------| -| `deals` | `bool` | If true get history deals during initial request to terminal | True | -| `orders` | `bool` | If true get history orders during initial request to terminal | True | -#### Returns -| Name | Type | Description | Default | -|--------|--------|-------------------------------------------------|---------| -| `bool` | `bool` | True if all requests were successful else False | False | +Get deals and orders within the timeframe specified in the constructor. - + ### get_deals ```python -async def get_deals(self, *, date_from: datetime | int = None, date_to: datetime | int = None, group: str = '', - retries=3) -> list[TradeDeal] +async def get_deals(self) -> tuple[TradeDeal, ...] ``` -#### Parameters: -| Name | Type | Description | Default | -|-------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `date_from` | `datetime\|float` | Date the deals are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' | None | -| `date_to` | `datetime\|float` | Date up to which the deals are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" | None | -| `group` | `str` | Filter for selecting history by symbols. | "" | -| `retries` | `int` | Number of retries if the request fails. | 3 | +Get deals from trading history using the parameters set in the constructor. + +#### Returns +| Name | Type | Description | +|---------|-------------------------|-----------------------| +| `deals` | `tuple[TradeDeal, ...]` | A list of trade deals | + + +### get_deals_by_ticket +```python +def get_deals_by_ticket(self, *, ticket: int) -> tuple[TradeDeal, ...] +``` +Get deals by ticket number. This filters deals by ticket based on the deals already fetched in initialize. + +#### Parameters +| Name | Type | Description | +|----------|-------|----------------------| +| `ticket` | `int` | Ticket number to get | #### Returns: -| Name | Type | Description | Default | -|---------|--------------------|-----------------------|---------| -| `deals` | `tuple[TradeDeal]` | A list of trade deals | [] | +| Name | Type | Description | +|---------|-------------------------|------------------------| +| `deals` | `tuple[TradeDeal, ...]` | A tuple of trade deals | - -### get_deals_ticket + +### get_deals_by_position ```python -async def get_deals_ticket(self, *, ticket: int) -> tuple[TradeDeal] -``` -Get deals by ticket number -#### Parameters: -| Name | Type | Description | Default | -|----------|------|-----------------------|---------| -| `ticket` | `int`| Ticket number to get | 0 | - -#### Returns: -| Name | Type | Description | Default | -|---------|--------------------|-----------------------|---------| -| `deals` | `tuple[TradeDeal]` | A list of trade deals | [] | - - -### get_deals_position -```python -async def get_deals_position(self, *, position: int) -> list[TradeDeal] +async def get_deals_by_position(self, *, position: int) -> tuple[TradeDeal, ...] ``` Get deals by position -#### Parameters: -| Name | Type | Description | Default | -|------------|------|-----------------------|---------| -| `position` | `int`| Position number to get | 0 | +#### Parameters +| Name | Type | Description | +|------------|-------|------------------------| +| `position` | `int` | Position number to get | -#### Returns: -| Name | Type | Description | Default | -|---------|--------------------|-----------------------|---------| -| `deals` | `tuple[TradeDeal]` | A list of trade deals | [] | - - - -### deals_total -```python -async def deals_total() -> int -``` -Get total number of deals within the specified period in the constructor. #### Returns -| Name | Type | Description | Default | -|---------------|-------|-----------------------|---------| -| `total_deals` | `int` | Total number of deals | 0 | +| Name | Type | Description | +|---------|-------------------------|------------------------| +| `deals` | `tuple[TradeDeal, ...]` | A tuple of trade deals | - + ### get_orders ```python -async def get_orders(self, *, date_from: datetime | int = None, date_to: datetime | int = None, group: str = '', - retries=3) -> tuple[TradeOrder] +async def get_orders(self) -> tuple[TradeOrder, ...] ``` Get orders from trading history using the parameters set in the constructor. -#### Parameters -| Name | Type | Description | Default | -|-------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `date_from` | `datetime\|float` | Date the deals are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' | None | -| `date_to` | `datetime\|float` | Date up to which the deals are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" | None | -| `group` | `str` | Filter for selecting history by symbols. | "" | - -#### Returns -| Name | Type | Description | Default | -|----------|---------------------|------------------------|---------| -| `orders` | `tuple[TradeOrder]` | A list of trade orders | [] | - - -### get_orders_position + +### get_orders_by_position ```python -async def get_orders_position(self, *, position: int) -> tuple[TradeOrder] +def get_orders_by_position(self, *, position: int) -> tuple[TradeOrder, ...] ``` Get orders by position. #### Parameters -| Name | Type | Description | Default | -|------------|------|-----------------------|---------| -| `position` | `int`| Position number to get | 0 | +| Name | Type | Description | +|------------|-------|------------------------| +| `position` | `int` | Position number to get | #### Returns -| Name | Type | Description | Default | -|----------|---------------------|------------------------|---------| -| `orders` | `tuple[TradeOrder]` | A list of trade orders | [] | +| Name | Type | Description | +|----------|--------------------------|-------------------------| +| `orders` | `tuple[TradeOrder, ...]` | A tuple of trade orders | - -### get_order_ticket + +### get_orders_by_ticket ```python -async def get_order_ticket(self, ticket: int | None = None) -> TradeOrder +def get_orders_by_ticket(self, *, position: int) -> tuple[TradeOrder, ...] ``` -Get a single order by ticket number. + +Get orders by ticket number. This filters orders by ticket based on the orders already fetched in initialize. #### Parameters -| Name | Type | Description | Default | -|----------|-------|----------------------|---------| -| `ticket` | `int` | Ticket number to get | 0 | +| Name | Type | Description | +|----------|-------|----------------------| +| `ticket` | `int` | ticket number to get | #### Returns -| Name | Type | Description | Default | -|----------|---------------------|------------------------|---------| -| `order` | `TradeOrder` | A single trade order | None | - - -### orders_total -```python -async def orders_total() -> int -``` -Get total number of orders within the specified period in the constructor. -#### Returns -| Name | Type | Description | Default | -|----------------|-------|---------------------|---------| -| `total_orders` | `int` | Total number orders | 0 | +| Name | Type | Description | +|----------|--------------------------|-------------------------| +| `orders` | `tuple[TradeOrder, ...]` | A tuple of trade orders | diff --git a/docs/lib/order.md b/docs/lib/order.md index 5091081..3630bee 100644 --- a/docs/lib/order.md +++ b/docs/lib/order.md @@ -1,24 +1,26 @@ # Order ## Table of contents -- [Order](#Order) -- [\_\_init\_\_](#__init__) -- [orders_total](#orders_total) -- [get_order](#get_order) -- [get_orders](#get_orders) -- [check](#check) -- [send](#send) -- [calc_margin](#calc_margin) -- [calc_profit](#calc_profit) +- [Order](#order.order) +- [\_\_init\_\_](#order.__init__) +- [orders_total](#order.orders_total) +- [get_order](#order.get_pending_order) +- [get_orders](#order.get_pending_orders) +- [check](#order.check) +- [send](#order.send) +- [calc_margin](#order.calc_margin) +- [calc_profit](#order.calc_profit) +- [calc_loss](#order.calc_loss) +- [request](#order.request) - + ### Order ```python -class Order(TradeRequest) +class Order(_Base, TradeRequest) ``` Trade order related functions and attributes. Subclass of TradeRequest. - + ### \_\_init\_\_ ```python def __init__(**kwargs) @@ -32,102 +34,129 @@ Provides default values for action, type_time and type_filling if not provided. | `type_time` | `OrderTime` | Order time | OrderTime.DAY | | `type_filling` | `OrderFilling` | Order filling | OrderFilling.FOK | - -### orders_total + ```python async def orders_total() ``` -Get the total number of active orders. +Get the total number of active pending orders. #### Returns | Type | Description | |-------|-------------------------------| | `int` | total number of active orders | - -### get_order + +### get_pending order ```python -async def get_order(self, ticket: int) -> TradeOrder +async def get_pending_order(self, ticket: int) -> TradeOrder ``` -Get an active trade order by ticket. +Get an active pending trade order by ticket. - -### get_orders + +### get_pending_orders ```python -async def get_orders(self, *, ticket: int = 0, symbol: str = '', group: str = '', retries=3) -> tuple[TradeOrder]: +async def get_pending_orders(self, *, ticket: int = 0, symbol: str = '', group: str = '') -> tuple[TradeOrder, ...]: ``` Get active trade orders. If ticket is provided, it will return the order with the specified ticket. If symbol is provided, it will return all orders for the specified symbol. If group is provided, it will return all orders for the specified group. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |----------|--------|--------------------------------------|---------| | `ticket` | `int` | Order ticket | 0 | | `symbol` | `str` | Symbol name | '' | | `group` | `str` | Group name | '' | -#### Returns -| Type | Description | -|---------------------|------------------------------------------------------| -| `tuple[TradeOrder]` | A Tuple of active trade orders as TradeOrder objects | -#### Raises -| Exception | Description | -|--------------|-------------------| -| `OrderError` | If not successful | +#### Returns: +| Type | Description | +|--------------------------|------------------------------------------------------| +| `tuple[TradeOrder, ...]` | A Tuple of active trade orders as TradeOrder objects | - + ### check ```python -async def check() -> OrderCheckResult +async def check(**kwargs) -> OrderCheckResult ``` -Check funds sufficiency for performing a required trading operation and the possibility of executing it at the current market price. -#### Returns +#### Parameters: +| Type | Description | +|--------|----------------------------------------------| +| kwargs | Update the request dict with extra arguments | + +Check if an order is okay. + +#### Returns: | Type | Description | |--------------------|----------------------------| | `OrderCheckResult` | An OrderCheckResult object | + #### Raises: | Exception | Description | |--------------|-------------------| | `OrderError` | If not successful | - - + ### send ```python async def send() -> OrderSendResult ``` Send a request to perform a trading operation from the terminal to the trade server. -#### Returns + +#### Returns: | Type | Description | |-------------------|---------------------------| | `OrderSendResult` | An OrderSendResult object | + #### Raises: | Exception | Description | |--------------|-------------------| | `OrderError` | If not successful | - -### calc_margin + +### calc_margin: ```python async def calc_margin() -> float ``` Return the required margin in the account currency to perform a specified trading operation. -#### Returns + +#### Returns: | Type | Description | |---------|-----------------------------------| | `float` | Returns float value if successful | -#### Raises -| Exception | Description | -|--------------|-------------------| -| `OrderError` | If not successful | - + ### calc_profit ```python async def calc_profit() -> float ``` Return profit in the account currency for a specified trading operation. + +#### Returns: +| Type | Description | +|---------|-----------------------------------| +| `float` | Returns float value if successful | +| `None` | If not successful | + + +### calc_profit +```python +async def calc_loss() -> float +``` +Return loss in the account currency for a specified trading operation. #### Returns | Type | Description | |---------|-----------------------------------| | `float` | Returns float value if successful | | `None` | If not successful | + + +### request +```python +@property +async def request() -> dict +``` +Return the trade request object as a dict + +#### Returns +| Type | Description | +|--------|----------------------------------| +| `dict` | Returns the trade request object | diff --git a/docs/lib/positions.md b/docs/lib/positions.md index b98b8b9..2fb307b 100644 --- a/docs/lib/positions.md +++ b/docs/lib/positions.md @@ -1,18 +1,17 @@ # Positions ## Table of contents -- [Positions](#positions) -- [Attributes](#attributes) -- [\_\_init\_\_](#__init__) -- [positions_total](#positions_total) -- [position_get](#position_get) -- [positions_get](#positions_get) -- [close](#close) -- [close_by](#close_by) -- [close_position](#close_position) -- [close_all](#close_all) +- [Positions](#positions.positions) +- [\_\_init\_\_](#positions.__init__) +- [get_positions](#positions.get_positions) +- [get_position_by_ticket](#positions.get_position_by_ticket) +- [get_positions_by_symbol](#positions.get_positions_by_symbol) +- [close](#positions.close) +- [close_position_by_ticket](#positions.close_position_by_ticket) +- [close_position](#positions.close_position) +- [close_all](#positions.close_all) - + ### Positions ```python class Positions @@ -20,116 +19,134 @@ class Positions Get and handle Open positions. #### Attributes -| Name | Type | Description | Default | -|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `symbol` | `str` | Financial instrument name. | "" | -| `group` | `str` | The filter for arranging a group of necessary symbols. Optional named parameter. If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. | "" | -| `ticket` | `int` | Position ticket. | 0 | -| `mt5` | `MetaTrader` | MetaTrader instance. | None | +| Name | Type | Description | +|-------------|-----------------------------|----------------------------| +| `positions` | `tuple[TradePosition, ...]` | Financial instrument name. | +| `mt5` | `MetaTrader` | MetaTrader instance. | - + ### \_\_init\_\_ ```python -def __init__(*, symbol: str = "", group: str = "", ticket: int = 0) +def __init__() ``` -Get Open Positions. -#### Arguments -| Name | Type | Description | Default | -|----------|-------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `symbol` | `str` | Financial instrument name. | "" | -| `group` | `str` | The filter for arranging a group of symbols. Optional named parameter. If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. | "" | -| `ticket` | `int` | Position ticket. | 0 | +Initialize a position instance - -### positions_total -```python -async def positions_total() -> int -``` -Get the number of open positions. -#### Returns -| Type | Description | -|-------|---------------------------------------| -| `int` | Return total number of open positions | - -### positions_get + +### get_positions ```python -async def positions_get(self, symbol: str = '', group: str = '', ticket: int = 0, retries=3) -> list[TradePosition]: +async def get_positions(self) -> tuple[TradePosition, ...]: ``` -Get open positions with the ability to filter by symbol or ticket. -#### Arguments -| Name | Type | Description | Default | -|----------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `symbol` | `str` | Financial instrument name. | "" | -| `group` | `str` | The filter for arranging a group of symbols. Optional named parameter. If the group is specified, the function returns only positions meeting a specified criteria for a symbol name. | "" | -| `ticket` | `int` | Position ticket. | 0 | +Get open positions #### Returns -| Type | Description | -|-----------------------|--------------------------------| -| `list[TradePosition]` | A list of open trade positions | +| Type | Description | +|-----------------------------|--------------------------------| +| `tuple[TradePosition, ...]` | A list of open trade positions | - -### position_get + + +### get_position_by_ticket ```python -async def position_get(self, *, ticket: int) -> TradePosition +async def get_position_by_ticket(self, *, ticket: int) -> TradePosition ``` -Get a position by ticket number. -#### Arguments +Get a position by ticket id. + +#### Parameters: | Name | Type | Description | |----------|-------|-----------------| | `ticket` | `int` | Position ticket | -#### Returns +#### Returns: | Type | Description | |-----------------|----------------| | `TradePosition` | Trade position | - + + +### get_positions_by_symbol +```python +async def get_positions_by_symbol(self, *, symbol: str) -> tuple[TradePosition, ...] +``` +Filter positions by symbols + +#### Parameters: +| Name | Type | Description | +|----------|-------|-------------| +| `symbol` | `str` | Symbol | + +#### Returns: +| Type | Description | +|-----------------------------|----------------| +| `tuple[TradePosition, ...]` | Trade position | + + + ### close ```python -async def close(self, *, ticket: int, symbol: str, price: float, volume: float, order_type: OrderType): +async def close(self, *, ticket: int, symbol: str, price: float, volume: float, order_type: OrderType) -> OrderSendResult: ``` -Close a position by ticket number. -#### Arguments -| Name | Type | Description | Default | -|--------------|-------------|----------------------------|---------| -| `ticket` | `int` | Position ticket. | | -| `symbol` | `str` | Financial instrument name. | | -| `price` | `float` | Closing price. | | -| `volume` | `float` | Volume to close. | | -| `order_type` | `OrderType` | Order type. | | +Close a position using its details. - -### close_by -```python -async def close_by(self, pos: TradePosition): -``` +#### Parameters: +| Name | Type | Description | +|--------------|-------------|----------------------------| +| `ticket` | `int` | Position ticket. | +| `symbol` | `str` | Financial instrument name. | +| `price` | `float` | Closing price. | +| `volume` | `float` | Volume to close. | +| `order_type` | `OrderType` | Order type. | -Close a position by position object. -#### Arguments -| Name | Type | Description | -|-------|-----------------|-----------------| -| `pos` | `TradePosition` | Position object | +#### Returns: +| Type | Description | +|--------------------|----------------------------------------------------| +| `OrderSendResult ` | The result of the order sent to close the position | - + + ### close_position ```python -async def close_position(self, *, position: TradePosition): +async def close_position(self, *, position: TradePosition) -> OrderSendResult: ``` Close a position by position object. -#### Arguments + +#### Parameters: | Name | Type | Description | |------------|-----------------|-----------------| | `position` | `TradePosition` | Position object | - +#### Returns: +| Type | Description | +|-------------------|----------------------------------------------------| +| `OrderSendResult` | The result of the order sent to close the position | + + + +### close_position_by_ticket +```python +async def close_position_by_ticket(self, *, position: TradePosition) -> OrderSendResult: +``` +Close a position by position object. + +#### Parameters: +| Name | Type | Description | +|------------|-----------------|-----------------| +| `position` | `TradePosition` | Position object | + +#### Returns: +| Type | Description | +|-------------------|----------------------------------------------------| +| `OrderSendResult` | The result of the order sent to close the position | + + + ### close_all ```python async def close_all() -> int ``` Close all open positions for the trading account. -#### Returns + +#### Returns: | Type | Description | |-------|--------------------------------------| | `int` | Return total number of closed trades | diff --git a/docs/lib/ram.md b/docs/lib/ram.md index c32e0ca..2ab19eb 100644 --- a/docs/lib/ram.md +++ b/docs/lib/ram.md @@ -1,74 +1,77 @@ # Risk Assessment and Management ## Table of Contents -- [RAM](#RAM) -- [\_\_init\_\_](#__init__) -- [get\_amount](#get_amount) -- [check_losing_positions](#check_losing_positions) -- [check_risk_level](#check_balance_level) +- [RAM](#ram.ram) +- [\__init\__](#ram.__init__) +- [get_amount](#ram.get_amount) +- [check_losing_positions](#ram.check_losing_positions) +- [check_open_positions](#ram.check_open_positions) - + ### RAM ```python class RAM ``` Risk Assessment and Management. You can customize this class based on how you want to manage risk. -#### Attributes -| Name | Type | Description | Default | -|------------------|---------|--------------------------------------------------------|---------| -| `risk_to_reward` | `float` | Risk to reward ratio | 1 | -| `risk` | `float` | Percentage of account balance to risk per trade | | -| `points` | `float` | A fixed number of points per trade can be fixed here | | -| `pips` | `float` | A fixed number of pips per trade can be fixed here | | -| `min_amount` | `float` | Minimum amount to risk per trade | | -| `max_amount` | `float` | Maximum amount to risk per trade | | -| `risk_level` | `float` | Ratio of free margin to current equity as a percentage | 50 | -| `loss_limit` | `int` | Number of open losing trades to allow at any time | 3 | + +#### Attributes: +| Name | Type | Description | Default | +|------------------|-----------|---------------------------------------------------|-----------| +| `account` | `Account` | The account object | Account() | +| `risk_to_reward` | `float` | Risk to reward ratio | 2 | +| `risk` | `float` | Percentage of account balance to risk per trade | 1% | +| `fixed_amount` | `float` | A fixed amount to risk per trade | | +| `min_amount` | `float` | Minimum amount to risk per trade | | +| `max_amount` | `float` | Maximum amount to risk per trade | | +| `loss_limit` | `int` | Number of open losing trades to allow at any time | 3 | +| `open_limit` | `int` | Number of open trades to allow at any time | 3 | - + ### \_\_init\_\_ ```python -def __init__(self, *, risk_to_reward: float = 1, risk: float = 0.01, **kwargs): +def __init__(self, **kwargs): ``` Risk Assessment and Management. All provided keyword arguments are set as attributes. #### Parameters -| Name | Type | Description | Default | -|------------------|---------|----------------------------------------------------|-----------| -| `risk_to_reward` | `float` | Risk to reward ratio | 1 | -| `risk` | `float` | Percentage of account balance to risk per trade | 0.01 # 1% | -| `kwargs` | `Dict` | Keyword arguments to be set as instance attributes | {} | +| Name | Type | Description | Default | +|------------------|--------|----------------------------------------------------|-----------| +| `kwargs` | `dict` | Keyword arguments to be set as instance attributes | {} | - -### get\_amount + +### ram.get_amount ```python async def get_amount() -> float ``` Calculate the amount to risk per trade as a percentage of balance. -#### Returns -| Type | Description | -|--------|-------------------------------------------------------| -| float | Amount to risk per trade in terms of account currency | - +#### Returns: +| Type | Description | +|---------|-------------------------------------------------------| +| `float` | Amount to risk per trade in terms of account currency | + + ### check_losing_positions ```python async def check_losing_positions(self) -> bool: ``` Check if the number of open losing trades is greater than or equal to the loss limit. -#### Returns -| Type | Description | -|------|---------------------------------------------------------------------------------------| -| bool | True if the number of open losing trades is more than the loss limit, False otherwise | - -### check\_balance\_level +#### Returns: +| Type | Description | +|--------|---------------------------------------------------------------------------------------| +| `bool` | True if the number of open losing trades is more than the loss limit, False otherwise | + + + +### check_open_positions ```python -async def check_balance_level(self) -> bool: +async def check_open_positions(self) -> bool: ``` -Check if the balance level is greater than or equal to the fixed balance level. -#### Returns -| Type | Description | -|------|---------------------------------------------------------------------------------| -| bool | True if the balance level is more than the fixed balance level, False otherwise | +Check if the number of open positions is less than or equal the loss limit. + +#### Returns: +| Type | Description | +|--------|---------------------------------------------------------------------------------------| +| `bool` | True if the number of open losing trades is more than the loss limit, False otherwise | diff --git a/docs/lib/records.md b/docs/lib/records.md deleted file mode 100644 index 75a10cb..0000000 --- a/docs/lib/records.md +++ /dev/null @@ -1,96 +0,0 @@ -# Records - -## Table of contents -- [Records](#records) -- [\_\_init\_\_](#__init__) -- [get\_records](#get_records) -- [read\_update](#read_update) -- [update\_rows](#update_rows) -- [update\_records](#update_records) -- [update\_record](#update_record) - - -### Records -```python -class Records() -``` -This utility class read trade records from csv files, and update them based on their closing positions. To use this default -implementation the csv files should at least have the following columns `['order', 'symbol', 'actual_profit', 'win', 'closed']` -Once a trade have been closed, the actual profit and win status will be updated in the csv file. -#### Default Headers -| column | type | description | -|---------------|-------|-------------------------------------------------------| -| order | int | Order id of the trade | -| symbol | str | the name of the Symbol | -| actual_profit | float | The actual profit of the trade, this zero by default | -| win | bool | The win status of the trade, this is False by default | -| closed | bool | The status of the trade, this is False by default | -#### Attributes -| name | type | description | -|-------------|--------|--------------------------------------------------------------| -| records_dir | Path | Absolut path to directory containing record of placed trades | -| config | Config | Config object | - - -### \_\_init\_\_ -```python -def __init__(records_dir: Path | str = '') -``` -Initialize an instance of the class -#### Parameters -| name | type | description | -|--------------|------|----------------------------------------------------------------| -| records_dir | Path | Absolute path to directory containing record of placed trades. | - - -### get\_records -```python -async def get_records() -``` -Get trade records from records_dir folder -#### Yields -| type | description | -|------|--------------------| -| Path | Trade record files | - - -### read\_update -```python -async def read_update(file: Path) -``` -Read and update trade records -#### Parameters -| name | type | description | -|------|------|-------------------| -| file | Path | Trade record file | - - -### update\_rows -```python -async def update_rows(rows: list[dict]) -> list[dict] -``` -Update the rows of entered trades in the csv file with the actual profit. -#### Parameters -| name | type | description | -|------|------------|---------------------------------------------------------------------------| -| rows | list[dict] | A list of dictionaries from the dictionary writer object of the csv file. | - -#### Returns -| type | description | -|------------|---------------------------------------------------------------| -| list[dict] | A list of dictionaries with the actual profit and win status. | - - - -### update\_records -```python -async def update_records() -``` -Update trade records in the records_dir folder. - - -### update\_record -```python -async def update_record(file: Path | str) -``` -Update a single trade record file. diff --git a/docs/lib/result.md b/docs/lib/result.md index fec7685..04de226 100644 --- a/docs/lib/result.md +++ b/docs/lib/result.md @@ -1,18 +1,20 @@ # Result ## Table of Contents -- [Result](#result) -- [__init__](#__init__) -- [get_data](#get_data) -- [to_csv](#to_csv) -- [to_json](#to_json) +- [Result](#result.result) +- [__init__](#result.__init__) +- [save](#result.save) +- [get_data](#result.get_data) +- [to_csv](#result.to_csv) +- [to_json](#result.to_json) - + + ```python -class Result() +class Result ``` A base class for handling trade results and strategy parameters for record keeping and analysis. -#### Attributes +#### Attributes: | Name | Type | Description | |--------------|-------------------|-----------------------------------| | `result` | `OrderSendResult` | The result of the trade | @@ -20,39 +22,57 @@ A base class for handling trade results and strategy parameters for record keepi | `name` | `str` | The name of the result object | - -### \_\_init\_\_ + +### \__init\__ ```python -def __init__(result: OrderSendResult, parameters: dict = None, name: str = '') +def __init__(*, result: OrderSendResult, parameters: dict = None, name: str = '') ``` Prepare result data for record keeping and analysis. -#### Parameters + +#### Parameters: | Name | Type | Description | |--------------|-------------------|-----------------------------------| | `result` | `OrderSendResult` | The result of the trade | | `parameters` | `dict` | The parameters used for the trade | | `name` | `str` | The name of the result object | - -### get\_data + + +### get_data ```python -def get_data(self) -> dict: +def get_data(self) -> dict ``` Get the result data as a dictionary -#### Returns + +#### Returns: | Type | Description | |--------|-----------------| | `dict` | The result data | - -### to\_csv + + +### to_save +```python +async def to_save(*, trade_record_mode: Literal["csv", "json"] = None) +``` +Save to json or csv depending on the trade record mode. + +#### Returns: +| Name | Type | Description | Default | +|---------------------|--------------------------|-----------------------|---------| +| `trade_record_mode` | `Literal["csv", "json"]` | The trade record mode | None | + + + +### to_csv ```python async def to_csv() ``` Record trade results and associated parameters as a csv file - -### to\_json + + +### to_json ```python async def to_json() ``` diff --git a/docs/lib/sessions.md b/docs/lib/sessions.md index a93c1aa..f3595a8 100644 --- a/docs/lib/sessions.md +++ b/docs/lib/sessions.md @@ -1,28 +1,38 @@ +from aiomql import TradePositionfrom aiomql.lib.sessions import Duration + # Session and Sessions Sessions allow you to run a strategy at specific times of the day. ## Table of Contents - [Session](#session) - - [\_\_init\_\_](#session.__init__) + - [\__init\__](#session.__init__) - [begin](#session.begin) - [close](#session.close) - [action](#session.action) -- [Sessions](#sessions) - - [\_\_init\_\_](#sessions.__init__) + - [in_session](#session.in_session) + - [duration](#session.duration) + - [close_positions](#session.close_positions) + - [close_all](#session.close_all) + - [close_win](#session.close_win) + - [close_loss](#session.close_loss) + - [close_until](#session.until) +- [Sessions](#sessions.sessions) + - [\__init\__](#sessions.__init__) - [find](#sessions.find) - [find_next](#sessions.find_next) - [check](#sessions.check) -- [delta](#delta) -- [until](#until) +- [delta](#sessions_mod.delta) +- [backtest_sleep](#sessions_mod.backtest_sleep) - + ## Session ```python class Session ``` A session is a time period between two `datetime.time` objects specified in utc. -#### Attributes + +#### Attributes: | Name | Type | Description | Default | |----------------|-------------------------------------------------------------------|------------------------------------------------------------------------|---------| | `start` | `datetime.time` | The start time of the session. | None | @@ -39,7 +49,7 @@ opened during the session or not or even by a strategy using the session. This i positions opened by the strategy. This will be handled in a future release. -### \_\_init\_\_ +### \__init\__ ```python def __init__(*, start: int | time, @@ -51,8 +61,8 @@ def __init__(*, custom_start: Callable = None, custom_end: Callable = None) ``` -Create a session. -#### Arguments +Create a session +#### Parameters: | Name | Type | Description | Default | |----------------|-------------------------------------------------------------------|---------------------------------------------------------------------|---------| | `start` | `int` \| `datetime.time` | The start time of the session in UTC. | None | @@ -63,6 +73,7 @@ Create a session. | `custom_end` | `Callable` | A custom function to call when the session ends. Default is None. | None | | `name` | `str` | The name of the session. Default is None. | None | + ### begin ```python @@ -70,6 +81,7 @@ async def begin() ``` Call the action specified in on_start or custom_start. + ### close ```python @@ -77,72 +89,143 @@ async def close() ``` Call the action specified in on_end or custom_end. + + +### in_session +```python +def in_session() -> bool +``` +Check if the current time is within the current session. + + + +### duration +```python +def duration() -> Duration +``` +Get the duration of the session in hours, minutes, and seconds. + + + +### close_positions +```python +async def close_positions(*, positions: tuple[TradePosition, ...]) +``` +Close positions in the sessions. This is used by the `close_all` action. + +#### Parameters: +| Name | Type | Description | +|-------------|-----------------------------|---------------------------------------| +| `positions` | `tuple[TradePosition, ...]` | A tuple of TradePosition objects. | + + + +### close_all +```python +async def close_all() +``` +Close all open positions + + + +### close_win +```python +async def close_win() +``` +Close only winning positions + + + +### close_loss +```python +async def close_loss() +``` +Close only losing positions + + + ### action ```python -async def action(action): pass +async def action(*, action: Literal["close_all", "close_win", "close_loss", "custom_start", "custom_end"]): pass ``` Used by begin and close to call the action specified. -#### Arguments -| Name | Type | Description | Default | -|----------|---------------------------------------------------------------------------------|---------------------|---------| -| `action` | `Literal['close_all', 'close_win', 'close_loss', 'custom_start', 'custom_end']` | The action to take. | None | +#### Parameters: +| Name | Type | Description | +|----------|---------------------------------------------------------------------------------|---------------------| +| `action` | `Literal['close_all', 'close_win', 'close_loss', 'custom_start', 'custom_end']` | The action to take. | - + +### until +```python +def until() -> int +``` +Get the seconds until the session starts from the current time. + + + ## Sessions ```python class Sessions() ``` Sessions allow you to run code at specific times of the day. It is a collection of Session objects. Sessions are sorted by start time. The sessions object is an asynchronous context manager. + ### Attributes: | Name | Type | Description | Default | |-------------------|-----------------|----------------------------|---------| | `sessions` | `list[Session]` | A list of Session objects. | [] | | `current_session` | `Session` | The current session. | None | + -#### \_\_init\_\_ +#### \__init\__ ```python -def __init__(*sessions) +def __init__(*sessions: Iterable[Session]) ``` Create a Sessions object. -#### Arguments -| Name | Type | Description | Default | -|------------|------------------|-----------------------------|---------| -| `sessions` | `tuple[Session]` | A tuple of Session objects. | None | +#### Parameters: +| Name | Type | Description | +|------------|---------------------|--------------------------------| +| `sessions` | `Iterable[Session]` | A iterable of Session objects. | + ### find ```python -def find(obj: time) -> Session | None +def find(*, moment: time = None) -> Session | None ``` Find a session that contains a datetime.time object. -#### Arguments -| Name | Type | Description | Default | -|-------|-----------------|-------------------------|---------| -| `obj` | `datetime.time` | A datetime.time object. | None | -#### Returns +#### Parameters: +| Name | Type | Description | Default | +|----------|--------|-------------------------|---------| +| `moment` | `time` | A datetime.time object. | None | + +#### Returns: | Type | Description | |-----------|----------------------------------------| | `Session` | A Session object or None if not found. | + -### find\_next +### find_next ```python -def find_next(obj: time) -> Session +def find_next(*, moment: time = None) -> Session ``` Find the next session that contains a datetime.time object. -#### Arguments -| Name | Type | Description | Default | -|-------|-----------------|-------------------------|---------| -| `obj` | `datetime.time` | A datetime.time object. | | -#### Returns +#### Parameters: +| Name | Type | Description | Default | +|----------|--------|-------------------------|---------| +| `moment` | `time` | A datetime.time object. | | + +#### Returns: | Type | Description | |-----------|-------------------| | `Session` | A Session object. | + + ### check ```python @@ -150,13 +233,15 @@ async def check(): pass ``` Check if the current session has started and if not, wait until it starts. - + + ### delta ```python def delta(obj: time) -> timedelta: pass ``` Get the timedelta of a datetime.time object. -#### Arguments: + +#### Parameters: | Name | Type | Description | Default | |-------|-----------------|-------------------------|---------| | `obj` | `datetime.time` | A datetime.time object. | None | @@ -165,13 +250,10 @@ Get the timedelta of a datetime.time object. |-------------|---------------------| | `timedelta` | A timedelta object. | - -### until + + +### backtest_sleep ```python -def until() +async def backtest_sleep(secs) ``` -Get the seconds until the session starts from the current time. -#### Returns: -| Type | Description | -|-------|---------------------------------------| -| `int` | The seconds until the session starts. | +Sleep method for backtesting. diff --git a/docs/lib/strategy.md b/docs/lib/strategy.md index ad20454..0e53231 100644 --- a/docs/lib/strategy.md +++ b/docs/lib/strategy.md @@ -2,46 +2,56 @@ The base class for creating strategies. ## Table of Contents -- [Strategy](#strategy) -- [\_\_init\_\_](#init) -- [sleep](#sleep) -- [trade](#trade) +- [Strategy](#strategy.strategy) +- [\_\_init\_\_](#strategy.__init__) +- [sleep](#strategy.sleep) +- [live_sleep](#strategy.live_sleep) +- [backtest_sleep](#strategy.backtest_sleep) +- [run_strategy](#strategy.run_strategy) +- [live_strategy](#strategy.live_strategy) +- [backtest_strategy](#strategy.backtest_strategy) +- [trade](#strategy.trade) +- [test](#strategy.test) - + ### Strategy ```python class Strategy(ABC) ``` The base class for creating strategies. -#### Attributes -| Name | Type | Description | Default | -|--------------|--------------|----------------------------------------------|---------| -| `name` | `str` | A name for the strategy. | None | -| `account` | `Account` | Account instance. | None | -| `mt5` | `MetaTrader` | MetaTrader instance. | None | -| `config` | `Config` | Config instance. | None | -| `symbol` | `Symbol` | The Financial Instrument as a Symbol Object | None | -| `parameters` | `Dict` | A dictionary of parameters for the strategy. | None | -| `sessions` | `Sessions` | Trading sessions. | None | -### Notes -Define the name of a strategy as a class attribute. If not provided, the class name will be used as the name. +#### Attributes: +| Name | Type | Description | Default | +|-----------------------|--------------------------------|------------------------------------------------|---------| +| `name` | `str` | A name for the strategy. | None | +| `symbol` | `Symbol` | The Financial Instrument as a Symbol Object | None | +| `sessions` | `Sessions` | Trading sessions. | None | +| `mt5` | `MetaTrader \| MetaBackTester` | MetaTrader instance. | None | +| `config` | `Config` | Config instance. | None | +| `parameters` | `dict` | A dictionary of parameters for the strategy. | None | +| `backtest_controller` | `BackTesterController` | A controller for the backtester. | +| `current_session` | `Session` | The current trading session | +| `running` | `bool` | A flag to indicate if the strategy is running. | True | - + + ### \_\_init\_\_ ```python -def __init__(*, symbol: Symbol, params: dict = None, sessions: Sessions) +def __init__(*, symbol: Symbol, params: dict = None, sessions: Sessions, name: str = "") ``` Initiate the parameters dict and add name and symbol fields. Use class name as strategy name if name is not provided. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |------------|------------|-----------------------------|---------| -| `symbol` | `Symbol` | The Financial instrument | None | +| `symbol` | `Symbol` | The Financial instrument | | | `params` | `Dict` | Trading strategy parameters | None | | `sessions` | `Sessions` | Trading sessions | None | +| `name` | `str` | The name of the strategy | "" | - + + ### sleep ```python @staticmethod @@ -50,16 +60,69 @@ async def sleep(secs: float) Sleep for the needed amount of seconds in between requests to the terminal. computes the accurate amount of time needed to sleep ensuring that the next request is made at the start of a new bar and making cooperative multitasking possible. -#### Parameters +This method calls the `live_sleep` method during live trading or `backtest_sleep`. + +#### Parameters: | Name | Type | Description | Default | |--------|---------|----------------------------------------------------------------|---------| | `secs` | `float` | The time in seconds. Usually the timeframe you are trading on. | None | - + + +### live_sleep +```python +async def live_sleep(*, secs: float) +``` +Sleep method for live trading + + + +### backtest_sleep +```python +async def backtest_sleep(*, secs: float) +``` +Sleep method for backtesting + + + ### trade ```python @abstractmethod async def trade() ``` -Place trades using this method. This is the main method of the strategy. -It will be called by the strategy runner. +Place trades using this method. +Implement this method in your own strategy as you wish. + + + +### test +```python +@abstractmethod +async def test() +``` +Use for backtesting. If not implemented use the trade method. + + + +### run_strategy +```python +async def run_strategy() +``` +Run the strategy by calling the trade or test method repeatedly in a while loop. +This method actually calls the `live_strategy` or `backtest_strategy` depending on the mode. + + + +### live_strategy +```python +async def live_strategy() +``` +Runs the strategy in live mode. + + + +### backtest_strategy +```python +async def live_strategy() +``` +Runs the strategy in backtest mode. diff --git a/docs/lib/symbol.md b/docs/lib/symbol.md index 426029d..fc43e38 100644 --- a/docs/lib/symbol.md +++ b/docs/lib/symbol.md @@ -2,336 +2,350 @@ Symbol class for handling a financial instrument. ## Table of Contents -- [Symbol](#Symbol) - - [info_tick](#info_tick) - - [symbol_select](#symbol_select) - - [info](#info) - - [init](#init) - - [book_add](#book_add) - - [book_get](#book_get) - - [book_release](#book_release) - - [compute_volume](#compute_volume) - - [currency_conversion](#currency_conversion) - - [convert_currency](#convert_currency) - - [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) - - [check_volume](#check_volume) - - [round_off_volume](#round_off_volume) +- [Symbol](#symbol.symbol) +- [info_tick](#symbol.info_tick) +- [symbol_select](#symbol.symbol_select) +- [info](#symbol.info) +- [initialize](#symbol.initialize) +- [book_add](#symbol.book_add) +- [book_get](#symbol.book_get) +- [book_release](#symbol.book_release) +- [compute_volume](#symbol.compute_volume) +- [convert_currency](#symbol.convert_currency) +- [copy_rates_from](#symbol.copy_rates_from) +- [copy_rates_from_pos](#symbol.copy_rates_from_pos) +- [copy_rates_range](#symbol.copy_rates_range) +- [copy_ticks_from](#symbol.copy_ticks_from) +- [copy_ticks_range](#symbol.copy_ticks_range) +- [check_volume](#symbol.check_volume) +- [round_off_volume](#symbol.round_off_volume) - + + ### Symbol ```python -class Symbol(SymbolInfo) +class Symbol(_Base, SymbolInfo) ``` Main class for handling a financial instrument. A subclass of `SymbolInfo` where most of the attributes are defined. for working with a financial instrument. -#### Attributes + +#### Attributes: | Name | Type | Description | Default | |-----------|--------------|---------------------------------------|---------| -| `name` | `str` | The name of the symbol. | None | -| `mt5` | `MetaTrader` | MetaTrader instance. | None | -| `config` | `Config` | Config instance. | None | | `account` | `Account` | Account instance. | None | | `tick` | `Tick` | The current price tick of the symbol. | None | -#### Notes -Full properties are on the SymbolInfo Object. -Make sure Symbol is always initialized with a name argument +#### Notes: +Make sure Symbol is always initialized with a name argument. - -### info\_tick + +### info_tick ```python async def info_tick(*, name: str = "") -> Tick ``` Get the current price tick of a financial instrument. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |--------|-------|-------------------------|---------| | `name` | `str` | The name of the symbol. | '' | -#### Returns + +#### Returns: | Type | Description | |--------|----------------------| | `Tick` | Return a Tick Object | -#### Raises -| Exception | Description | -|--------------|---------------------------------------------------| -| `ValueError` | If request was unsuccessful and None was returned | - -### symbol\_select + + +### symbol_select ```python async def symbol_select(*, enable: bool = True) -> bool ``` Select a symbol in the MarketWatch window or remove a symbol from the window. Update the select property -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |----------|--------|---------------------------------------------------------------------------------------------------------|---------| | `enable` | `bool` | Switch. Optional unnamed parameter. If 'false', a symbol should be removed from the MarketWatch window. | None | -#### Returns + +#### Returns: | Type | Description | |--------|--------------------------------------| | `bool` | True if successful, otherwise False. | - + + ### info ```python async def info() -> SymbolInfo ``` Get data on the specified financial instrument and update the symbol object properties -#### Returns + +#### Returns: | Type | Description | |--------------|--------------------------| | `SymbolInfo` | SymbolInfo if successful | -#### Raises + +#### Raises: | Exception | Description | |--------------|---------------------------------------------------| | `ValueError` | If request was unsuccessful and None was returned | - + + ### init ```python -async def init() -> bool +async def initialize() -> bool ``` + Initialized the symbol by pulling properties from the terminal -#### Returns +#### Returns: | Type | Description | |--------|--------------------------------------------------------| | `bool` | Returns True if symbol info was successful initialized | - -### book\_add + + +### book_add ```python async def book_add() -> bool ``` Subscribes the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. -If the symbol is not in the list of instruments for the market, This method will return False -#### Returns +If the symbol is not in the list of instruments for the market, This method will return False. + +#### Returns: | Type | Description | |--------|--------------------------------------| | `bool` | True if successful, otherwise False. | - -### book\_get + + +### book_get ```python -async def book_get() -> tuple[BookInfo] +async def book_get() -> tuple[BookInfo, ...] ``` Returns a tuple of BookInfo featuring Market Depth entries for the specified symbol. -#### Returns -| Type | Description | -|-------------------|-------------------------------------------------------------------| -| `tuple[BookInfo]` | Returns the Market Depth contents as a tuples of BookInfo Objects | -#### Raises -| Exception | Description | -|--------------|---------------------------------------------------| -| `ValueError` | If request was unsuccessful and None was returned | +#### Returns: +| Type | Description | +|------------------------|-------------------------------------------------------------------| +| `tuple[BookInfo, ...]` | Returns the Market Depth contents as a tuples of BookInfo Objects | - -### book\_release + + +### book_release ```python async def book_release() -> bool ``` Cancels subscription of the MetaTrader 5 terminal to the Market Depth change events for a specified symbol. -#### Returns + +#### Returns: | Type | Description | |--------|--------------------------------------| | `bool` | True if successful, otherwise False. | - + + ### compute_volume ```python -async def compute_volume(*args, **kwargs) -> float +async def compute_volume(self) -> float ``` Computes the volume of a trade based on the amount or any other parameter. -This is a dummy method that returns the minimum volume of the symbol. It is meant to be overridden by a subclass. +This default implementation returns the minimum volume of the symbol. It is meant to be overridden by a subclass. -#### Parameters -| Name | Type | Description | Default | -|--------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `amount` | `float` | Amount to risk in the trade | None | -| `points` | `float` | Number of pips to target | None | -| `use_limits` | `bool` | If True, the minimum volume is returned if the computed volume is less than the minimum volume and the maximum volume is returned if the computed volume is greater than the maximum volume for the symbol | False | #### Returns | Type | Description | |---------|---------------------------------| | `float` | Returns the volume of the trade | - -### currency\_conversion + + +### check_volume ```python -async def currency_conversion(*, amount: float, base: str, - quote: str) -> float +async def check_volume(*, volume: float) -> tuple[bool, float] ``` -Convert from one currency to the other. Returns the amount in terms of the base currency. -#### Parameters -| Name | Type | Description | Default | -|----------|---------|--------------------------------------------------------|---------| -| `amount` | `float` | Amount to convert given in terms of the quote currency | None | -| `base` | `str` | The base currency of the pair | None | -| `quote` | `str` | The quote currency of the pair | None | -#### Returns +Check if the volume is within the limits of permitted volume for +the symbol. If not, return the nearest limit. + +#### Returns: +| Type | Description | +|----------------------|-------------------------------------------------------------------------------| +| `tuple[bool, float]` | True and the input volume if within bounds, else False and the nearest limit. | + + + +### round_off_volume +```python +async def round_off_volume(*, volume: float, round_down: bool = False) -> float +``` +Round off the volume to the nearest volume step. + +#### Parameters: +| Name | Type | Description | Default | +|--------------|---------|---------------------------------------------------|---------| +| `volume` | `float` | The volume | | +| `round_down` | `float` | Round up or round down to the nearest volume step | False | + +#### Returns: +| Type | Description | +|---------|---------------------------------| +| `float` | Returns the volume of the trade | + + + +### amount_in_quote_currency +```python +async def amount_quote_currency(*, amount: float) -> float +``` +Convert an amount in the account_currency to the quote currency of the symbol. + +#### Parameters: +| Name | Type | Description | +|----------|---------|-----------------------| +| `amount` | `float` | The amount to convert | + + + +### convert_currency +```python +async def convert_currency(*, amount: float, from_currency: str, to_currency: str) -> float +``` +Convert from one currency to the other. + +#### Parameters: +| Name | Type | Description | +|-----------------|---------|--------------------------------------------------------| +| `amount` | `float` | Amount to convert given in terms of the quote currency | +| `from_currency` | `str` | The currency to convert from | +| `to_currency` | `str` | The currency to convert to | + +#### Returns: | Type | Description | |---------|--------------------------------------| | `float` | Amount in terms of the base currency | -#### Raises -| Exception | Description | -|--------------|----------------------| -| `ValueError` | If conversion failed | - -```python -async def convert_currency(self, *, amount: float, base: str, quote: str) -> float: -``` -Alias for currency_conversion - -### copy\_rates\_from + +### copy_rates_from ```python -async def copy_rates_from(*, - timeframe: TimeFrame, - date_from: datetime | int, - count: int = 500) -> Candles +async def copy_rates_from(*, timeframe: TimeFrame, date_from: datetime | int, count: int = 500) -> Candles ``` Get bars from the MetaTrader 5 terminal starting from the specified date. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |-------------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------| | `timeframe` | `TimeFrame` | Timeframe the bars are requested for. Set by a value from the TimeFrame enumeration. | Required unnamed parameter | | `date_from` | `datetime, int` | Date of opening of the first bar from the requested sample. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. | Required unnamed parameter | | `count` | `int` | Number of bars to receive. | Required unnamed parameter | -#### Returns + +#### Returns: | Type | Description | |-----------|---------------------------------------------------------------------------| | `Candles` | Returns a Candles object as a collection of rates ordered chronologically | -#### Raises + +#### Raises: | Exception | Description | |--------------|---------------------------------------------------| | `ValueError` | If request was unsuccessful and None was returned | - -### copy\_rates\_from\_pos + + +### copy_rates_from_pos ```python -async def copy_rates_from_pos(*, - timeframe: TimeFrame, - count: int = 500, - start_position: int = 0) -> Candles +async def copy_rates_from_pos(*,timeframe: TimeFrame, count: int = 500, start_position: int = 0) -> Candles ``` Get bars from the MetaTrader 5 terminal starting from the specified index. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------| | `timeframe` | `TimeFrame` | TimeFrame value from TimeFrame Enum. Required keyword only parameter | Required keyword only parameter | | `count` | `int` | Number of bars to return. Keyword argument defaults to 500 | 500 | | `start_position` | `int` | Initial index of the bar the data are requested from. The numbering of bars goes from present to past. Thus, the zero bar means the current one. Keyword argument defaults to 0. | 0 | -#### Returns + +#### Returns: | Type | Description | |-----------|----------------------------------------------------------------------------| | `Candles` | Returns a Candles object as a collection of rates ordered chronologically. | -#### Raises + +#### Raises: | Exception | Description | |--------------|---------------------------------------------------| | `ValueError` | If request was unsuccessful and None was returned | - -### copy\_rates\_range + +### copy_rates_range ```python async def copy_rates_range(*, timeframe: TimeFrame, date_from: datetime | int, date_to: datetime | int) -> Candles ``` Get bars in the specified date range from the MetaTrader 5 terminal. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |-------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------| | `timeframe` | `TimeFrame` | Timeframe for the bars using the TimeFrame enumeration. Required unnamed parameter. | Required unnamed parameter | | date_from | datetime, int | Date the bars are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars with the open time >= date_from are returned. Required unnamed parameter. | Required unnamed parameter | | date_to | datetime, int | Date, up to which the bars are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Bars with the open time <= date_to are returned. Required unnamed parameter. | Required unnamed parameter | -#### Returns + +#### Returns: | Type | Description | |-----------|----------------------------------------------------------------------------| | `Candles` | Returns a Candles object as a collection of rates ordered chronologically. | -#### Raises + +#### Raises: | Exception | Description | |--------------|---------------------------------------------------| | `ValueError` | If request was unsuccessful and None was returned | - -### copy\_ticks\_from + + +### copy_ticks_from ```python -async def copy_ticks_from(*, - date_from: datetime | int, - count: int = 100, - flags: CopyTicks = CopyTicks.ALL) -> Ticks +async def copy_ticks_from(*, date_from: datetime | int, count: int = 100, flags: CopyTicks = CopyTicks.ALL) -> Ticks ``` + Get ticks from the MetaTrader 5 terminal starting from the specified date. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |-------------|-----------------|---------------------------------------------------------------------------------------------------------------------|----------------------------| | `date_from` | `datetime, int` | Date the ticks are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. | Required unnamed parameter | | `count` | `int` | Number of requested ticks. Defaults to 100 | Required unnamed parameter | | `flags` | `CopyTicks` | A flag to define the type of the requested ticks from CopyTicks enum. INFO is the default | Required unnamed parameter | -#### Returns + +#### Returns: | Type | Description | |---------|--------------------------------------------------------------------------| | `Ticks` | Returns a Ticks object as a collection of ticks ordered chronologically. | -#### Raises + +#### Raises: | Exception | Description | |--------------|---------------------------------------------------| | `ValueError` | If request was unsuccessful and None was returned | - -### copy\_ticks\_range + + +### copy_ticks_range ```python -async def copy_ticks_range(*, - date_from: datetime | int, - date_to: datetime | int, - flags: CopyTicks = CopyTicks.ALL) -> Ticks +async def copy_ticks_range(*, date_from: datetime | int, date_to: datetime | int, flags: CopyTicks = CopyTicks.ALL) -> Ticks ``` Get ticks for the specified date range from the MetaTrader 5 terminal. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |-------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------|----------------------------| | `date_from` | `datetime, int` | Date the ticks are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. | Required unnamed parameter | | `date_to` | `datetime, int` | Date, up to which the ticks are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. | Required unnamed parameter | | `flags` | `CopyTicks` | A flag to define the type of the requested ticks from CopyTicks enum. INFO is the default | Required unnamed parameter | -#### Returns + +#### Returns: | Type | Description | |---------|--------------------------------------------------------------------------| | `Ticks` | Returns a Ticks object as a collection of ticks ordered chronologically. | -#### Raises + +#### Raises: | Exception | Description | |--------------|---------------------------------------------------| | `ValueError` | If request was unsuccessful and None was returned | - - -### check_volume -```python -async def check_volume(*, volume: float) -> tuple[bool, float] -``` -Checks if the volume is within the minimum and maximum volume for the symbol. If not, return the nearest limit. -#### Parameters -| Name | Type | Description | Default | -|--------|---------|---------------------|---------| -| volume | `float` | The volume to check | None | -#### Returns -| Type | Description | -|----------------------|-----------------------------------------------------| -| `tuple[bool, float]` | The boolean is True if the volume is within limits. | - - -### round_off_volume -```python -async def round_off_volume(*, volume: float, round_down: bool = True) -> float -``` -Rounds off the volume to the nearest minimum or maximum volume for the symbol. -#### Parameters -| Name | Type | Description | Default | -|--------------|---------|---------------------------|---------| -| `volume` | `float` | The volume to round off | None | -| `round_down` | `bool` | To round_up or round_down | True | -#### Returns -| Type | Description | -|---------|---------------------| -| `float` | The rounded volume. | diff --git a/docs/lib/terminal.md b/docs/lib/terminal.md index e36b212..ffa649a 100644 --- a/docs/lib/terminal.md +++ b/docs/lib/terminal.md @@ -1,27 +1,27 @@ # Terminal ## Table of Contents -- [Terminal](#terminal) -- [initialize](#initialize) -- [version](#version) -- [info](#info) -- [symbols_total](#symbols_total) +- [Terminal](#terminal.terminal) +- [initialize](#terminal.initialize) +- [version](#terminal.version) +- [info](#terminal.info) +- [symbols_total](#terminal.symbols_total) - + ### Terminal ```python -class Terminal(TerminalInfo) +class Terminal(_Base, TerminalInfo) ``` Terminal Class. Get information about the MetaTrader 5 terminal. The class is a subclass of the TerminalInfo class. It inherits all the attributes and methods of the TerminalInfo class and adds some useful methods. -#### Attributes -| Name | Type | Description | Default | -|---------------|--------------|------------------------------------------------------------------------------|---------| -| `initialized` | `bool` | check if initial request has been sent to the terminal to get terminal info. | False | -| `mt5` | `MetaTrader` | MetaTrader instance | None | -| `config` | `Config` | Config instance | None | - +#### Attributes: +| Name | Type | Description | Default | +|-----------|--------------|-------------------------------|---------| +| `version` | `Version` | MetaTrader5 Terminal Version. | None | + + + ### initialize ```python async def initialize() -> bool @@ -30,28 +30,33 @@ Establish a connection with the MetaTrader 5 terminal. There are three call opti The terminal for connection is found automatically. Call specifying the path to the MetaTrader 5 terminal we want to connect to. word path as a keyword argument Call specifying the trading account path and parameters i.e. login, password, server, as keyword arguments, path can be omitted. -#### Returns + +#### Returns: | Type | Description | |--------|-------------------------------| | `bool` | True if successful else False | - + + ### version ```python async def version() ``` Get the MetaTrader 5 terminal version. This method returns the terminal version, build and release date as a tuple of three values -#### Returns + +#### Returns: | Type | Description | |-----------|------------------------------------| | `Version` | version of tuple as Version object | -#### Raises + +#### Raises: | Exception | Description | |--------------|--------------------------------------------| | `ValueError` | If the terminal version cannot be obtained | - + + ### info ```python async def info() @@ -59,19 +64,21 @@ async def info() Get the connected MetaTrader 5 client terminal status and settings. gets terminal info in the form of a named tuple structure (namedtuple). Return None in case of an error. The info on the error can be obtained using last_error(). -#### Returns + +#### Returns: | Type | Description | |----------------|----------------------------------------------------| | `TerminalInfo` | Terminal status and settings as a terminal object. | - -### symbols\_total + +### symbols_total ```python async def symbols_total() -> int ``` Get the number of all financial instruments in the MetaTrader 5 terminal. -#### Returns + +#### Returns: | Type | Description | |-------|-----------------------------------| | `int` | Total number of available symbols | diff --git a/docs/lib/ticks.md b/docs/lib/ticks.md index 666aa23..6330195 100644 --- a/docs/lib/ticks.md +++ b/docs/lib/ticks.md @@ -2,23 +2,25 @@ Module for working with price ticks. ## Table of Contents -- [Tick](#tick) +- [Tick](#tick.tick) - [\_\_init\_\_](#tick.__init__) - - [set\_attributes](#tick.set_attributes) -- [Ticks](#ticks) - - [\_\_init\_\_](#ticks.__init__) + - [set_attributes](#tick.set_attributes) + +- [Ticks](#ticks.ticks) + - [\__init\__](#ticks.__init__) - [ta](#ticks.ta) - - [ta\_lib](#ticks.ta_lib) + - [ta_lib](#ticks.ta_lib) - [data](#ticks.data) - [rename](#ticks.rename) - + + ## Tick ```python class Tick() ``` Price Tick of a Financial Instrument. -#### Attributes +#### Attributes: | Name | Type | Description | Default | |---------------|------------|-----------------------------------------------------------------------|---------| | `symbol` | `Symbol` | The Financial Instrument as a Symbol Object | None | @@ -32,35 +34,62 @@ Price Tick of a Financial Instrument. | `volume_real` | `float` | Volume for the current Last price | None | | `Index` | `int` | Custom attribute representing the position of the tick in a sequence. | None | + -### \_\_init\_\_ +### \__init\__ ```python def __init__(self, **kwargs): ``` Initialize the Tick class. Set attributes from keyword arguments.The `bid`, `ask`, `last`, `time` and `volume` must be present + -### set\_attributes +### set_attributes ```python def set_attributes(**kwargs) ``` Set attributes from keyword arguments - + + +### dict +```python +def dict(exclude: set = None, include: set = None) -> dict +``` +Return a dictionary of the tick attributes. + +#### Parameters: +| Name | Type | Description | Default | +|-----------|-------|-----------------------------------------------------|---------| +| `exclude` | `set` | A set of attributes to exclude from the dictionary. | None | +| `include` | `set` | A set of attributes to include in the dictionary. | None | + + + ## Ticks ```python -class Ticks() +class Ticks ``` Container data class for price ticks. Arrange in chronological order. Saves data with a pandas DataFrame. Supports iteration, slicing and assignment. Similar to `Candles` class but for price ticks. + +#### Attributes: +| Name | Type | Description | Default | +|---------------|-------------|----------------------------------------------------------------------|---------| +| `data` | `DataFrame` | DataFrame of price ticks arranged in chronological order. | None | +| `time` | `Series` | Time of the last prices update for the symbol | None | +| `bid` | `Series` | Current Bid price | None | +| `ask` | `Series` | Current Ask price | None | +| `last` | `Series` | Price of the last deal (Last) | None | +| `volume` | `Series` | Volume for the current Last price | None | +| `time_msc` | `Series` | Time of the last prices update for the symbol in milliseconds | None | +| `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 | -#### Attributes -| Name | Type | Description | Default | -|--------|-------------|-----------------------------------------------------------|---------| -| `data` | `DataFrame` | DataFrame of price ticks arranged in chronological order. | None | -### \_\_init\_\_ +### \__init\__ ```python def __init__(*, data: DataFrame | Iterable, flip=False) ``` @@ -71,6 +100,7 @@ Initialize the Ticks class. Creates a DataFrame of price ticks from the data arg | `data` | `DataFrame` \| `Iterable` | Dataframe of price ticks or any iterable object that can be converted to a pandas DataFrame | None | | `flip` | `bool` | If flip is True reverse data chronological order. | False | + ### ta ```python @@ -78,23 +108,25 @@ Initialize the Ticks class. Creates a DataFrame of price ticks from the data arg def ta() ``` Access to the pandas_ta library for performing technical analysis on the underlying data attribute. -#### Returns +#### Returns: | Name | Type | Description | |-------------|-------------|-----------------------| | `pandas_ta` | `pandas_ta` | The pandas_ta library | + -### ta\_lib +### ta_lib ```python @property def ta_lib() ``` Access to the ta library for performing technical analysis. Not dependent on the underlying data attribute. -#### Returns +#### Returns: | Name | Type | Description | |------|------|----------------| | `ta` | `ta` | The ta library | + ### data ```python @@ -102,23 +134,25 @@ Access to the ta library for performing technical analysis. Not dependent on the def data() -> DataFrame ``` DataFrame of price ticks arranged in chronological order. -#### Returns +#### Returns: | Name | Type | Description | |--------|-------------|-----------------------------------------------------------| | `data` | `DataFrame` | DataFrame of price ticks arranged in chronological order. | + ### rename ```python def rename(inplace=True, **kwargs) -> _Ticks | None ``` Rename columns of the candle class. -#### Arguments +#### Arguments: | Name | Type | Description | Default | |-----------|--------|-------------------------------------------------------------------------------------------|---------| | `inplace` | `bool` | Rename the columns inplace or return a new instance of the class with the renamed columns | True | | `kwargs` | | The new names of the columns | | -#### Returns + +#### Returns: | Type | Description | |---------|---------------------------------------------------------------------------| | `Ticks` | A new instance of the class with the renamed columns if inplace is False. | diff --git a/docs/lib/trade_records.md b/docs/lib/trade_records.md index 31680d8..07ec1b1 100644 --- a/docs/lib/trade_records.md +++ b/docs/lib/trade_records.md @@ -2,126 +2,170 @@ ## Table of contents - [Trade Records](#trade_records) -- [\_\_init\_\_](#__init__) -- [get_csv_records](#get_csv_records) -- [get_json_records](#get_json_records) -- [read_update_csv](#read_update_csv) -- [read_update_json](#read_update_json) -- [update_rows](#update_rows) -- [update_row](#update_row) -- [update_csv_records](#update_csv_records) -- [update_json_records](#update_json_records) -- [update_csv_record](#update_csv_record) -- [update_json_record](#update_json_record) +- [\_\_init\_\_](#trade_records.__init__) +- [get_csv_records](#trade_records.get_csv_records) +- [get_json_records](#trade_records.get_json_records) +- [read_update_csv](#trade_records.read_update_csv) +- [read_update_json](#trade_records.read_update_json) +- [update_rows](#trade_records.update_rows) +- [update_row](#trade_records.update_row) +- [update_csv_records](#trade_records.update_csv_records) +- [update_json_records](#trade_records.update_json_records) +- [update_csv_record](#trade_records.update_csv_record) +- [update_json_record](#trade_records.update_json_record) - + ### Trade Records ```python class TradeRecords() ``` This utility class read trade records from csv and json files, and update them based on their closing positions. -To use this default implementation the csv or json file should be able to provide the following data. -`['order', 'symbol', 'actual_profit', 'win', 'closed']` -Once a trade have been closed, the actual profit and win status will be updated in the csv file. +Once a trade have been closed, the actual profit and win status will be updated in the file. -#### Default Headers -| column | type | description | -|---------------|-------|-------------------------------------------------------| -| order | int | Order id of the trade | -| symbol | str | the name of the Symbol | -| actual_profit | float | The actual profit of the trade, this zero by default | -| win | bool | The win status of the trade, this is False by default | -| closed | bool | The status of the trade, this is False by default | +#### Attributes: +| name | type | description | +|---------------|----------|-------------------------------------------| +| `config` | `Config` | Config object | +| `records_dir` | `Path` | A directory for finding the trade records | -#### Attributes -| name | type | description | -|-------------|--------|--------------------------------------------------------------| -| records_dir | Path | Absolut path to directory containing record of placed trades | -| config | Config | Config object | - - -### \_\_init\_\_ + +### \__init\__ ```python -def __init__(records_dir: Path | str = '') +def __init__(*, records_dir: Path | str = '') ``` Initialize an instance of the class. -#### Parameters -| name | type | description | -|--------------|------|----------------------------------------------------------------| -| records_dir | Path | Absolute path to directory containing record of placed trades. | - +#### Parameters: +| name | type | description | +|---------------|--------|----------------------------------------------------------------| +| `records_dir` | `Path` | Absolute path to directory containing record of placed trades. | + + + ### get_csv_records ```python async def get_csv_records() ``` Get trade records from records_dir folder. -#### Yields + +#### Yields: | type | description | |------|--------------------| | Path | Trade record files | - + + ### get_json_records ```python async def get_json_records() ``` Get trade records from records_dir folder. + #### Yields | type | description | |------|--------------------| | Path | Trade record files | - + + ### read_update_csv ```python -async def read_update_csv(file: Path) +async def read_update_csv(*, file: Path) ``` Read and update trade records from a csv file. -#### Parameters -| name | type | description | -|------|------|-------------------| -| file | Path | Trade record file | - +#### Parameters: +| name | type | description | +|--------|--------|-------------------| +| `file` | `Path` | Trade record file | + + + ### read_update_json ```python -async def read_update_json(file: Path) +async def read_update_json(*, file: Path) ``` Read and update trade records from a json file. -#### Parameters -| name | type | description | -|------|------|-------------------| -| file | Path | Trade record file | - +#### Parameters: +| name | type | description | +|--------|--------|-------------------| +| `file` | `Path` | Trade record file | + + + ### update_rows ```python -async def update_rows(rows: list[dict]) -> list[dict] +async def update_rows(*, rows: list[dict]) -> list[dict] ``` Update the rows of entered trades with the actual profit. -#### Parameters -| name | type | description | -|------|------------|---------------------------------------------------------------------------| -| rows | list[dict] | A list of dictionaries from the dictionary writer object of the csv file. | -#### Returns -| type | description | -|------------|---------------------------------------------------------------| -| list[dict] | A list of dictionaries with the actual profit and win status. | +#### Parameters: +| name | type | description | +|--------|--------------|---------------------------------------------------------------------------| +| `rows` | `list[dict]` | A list of dictionaries from the dictionary writer object of the csv file. | - +#### Returns: +| Type | Description | +|--------------|---------------------------------------------------------------| +| `list[dict]` | A list of dictionaries with the actual profit and win status. | + + + ### update_row ```python async def update_row(row: dict) -> dict ``` Update the row of an entered trade with the actual profit. -#### Parameters -| name | type | description | -|------|------|-------------------------------------------| -| row | dict | A dictionary from the csv file row object | -#### Returns -| type | description | -|------|------------------------------------| +#### Parameters: +| Name | Type | Description | +|-------|--------|-------------------------------------------| +| `row` | `dict` | A dictionary from the csv file row object | + +#### Returns: +| Type | Description | +|------|-------------------------------------| | dict | A dictionary with the actual profit | + + + +### update_csv_record +```python +async def update_csv_record(*, file: Path | str) +``` +Update a single trade record csv file + +#### Parameters: +| Name | Type | Description | +|--------|--------|-------------------------| +| `file` | `Path` | A trade record csv file | + + + +### update_csv_records +```python +def update_csv_records() +``` +Update csv trade records in the records_dir folder. + + + +### update_csv_record +```python +async def update_json_record(*, file: Path | str) +``` +Update a single trade record json file + +#### Parameters: +| Name | Type | Description | +|--------|--------|--------------------------| +| `file` | `Path` | A trade record json file | + + + +### update_json_records +```python +def update_json_records() +``` +Update json trade records in the records_dir folder. diff --git a/docs/lib/trader.md b/docs/lib/trader.md index 4b7f5d8..53ccd2a 100644 --- a/docs/lib/trader.md +++ b/docs/lib/trader.md @@ -3,111 +3,180 @@ Trader class module. Handles the creation of an order and the placing of trades ## Table of Contents - [Trader](#trader) -- [\_\_init\_\_](#__init__) -- [create\_order](#create_order) -- [set\_order\_limits](#set_order_limits) -- [set\_trade\_stop_levels](#set_trade_stop_levels) -- [send\_order](#send_order) -- [check_order](#check_order) -- [record_trade](#record_trade) -- [place\_trade](#place_trade) +- [\_\_init\_\_](#trader.__init__) +- [set_trade_stop_levels_points](#trader.set_trade_stop_levels_points) +- [set_trade_stop_levels_pips](#trader.set_trade_stop_levels_pips) +- [create_order_with_points](#trade.create_order_with_points) +- [create_order_with_sl](#trade.create_order_with_sl) +- [create_order_with_stops](#trade.create_order_with_stops) +- [create_order_no_stops](#trade.create_order_no_stops) +- [send_order](#trader.send_order) +- [check_order](#trader.check_order) +- [record_trade](#trader.record_trade) +- [place_trade](#trader.place_trade) - + ### Trader ```python class Trader() ``` Base class for creating a Trader object. Handles the creation of an order and the placing of trades -#### Attributes -| Name | Type | Description | Default | -|----------|----------|-------------------------------------------------------|---------| -| `ram` | `RAM` | Risk Assessment Management System. | None | -| `config` | `Config` | Config instance. | None | -| `order` | `Order` | Order instance. | None | -| `symbol` | `Symbol` | The Financial Instrument | None | -| `params` | `Dict` | A dictionary of parameters associated with the trade. | None | - +#### Attributes: +| Name | Type | Description | Default | +|--------------|----------|-------------------------------------------------------|---------| +| `ram` | `RAM` | Risk Assessment Management System. | None | +| `config` | `Config` | Config instance. | None | +| `order` | `Order` | Order instance. | None | +| `symbol` | `Symbol` | The Financial Instrument | None | +| `parameters` | `dict` | A dictionary of parameters associated with the trade. | None | + + ### \_\_init\_\_ ```python def __init__(*, symbol: Symbol, ram: RAM = None) ``` -#### Parameters +#### Parameters: | Name | Type | Description | Default | |----------|----------|-----------------------------------------|---------| -| `symbol` | `Symbol` | The Financial instrument | None | +| `symbol` | `Symbol` | The Financial instrument | | | `ram` | `RAM` | Risk Assessment and Management instance | None | - -### create\_order -```python -async def create_order(*, order_type: OrderType, `kwargs) -``` -Complete the order object with the required values. Creates a simple order. -#### Parameters -| Name | Type | Description | Default | -|--------------|-------------|-------------------------------------------------------|---------| -| `order_type` | `OrderType` | Type of order | None | -| `kwargs` | | keyword arguments as required for the specific trader | | - -### set\_order\_limits + +### set_trade_stop_levels_pips ```python -async def set_order_limits(pips: float): +async def set_trade_stop_levels_pips(*, pips: float, risk_to_reward: float = None): ``` Sets the stop loss and take profit for the order. This method uses pips as defined for forex instruments. -#### Parameters -| Name | Type | Description | Default | -|--------|---------|-------------|---------| -| `pips` | `float` | Target pips | None | - -### set\_trade\_stop\_levels +#### Parameters: +| Name | Type | Description | Default | +|------------------|---------|-------------------------------|---------| +| `pips` | `float` | Target pips | | +| `risk_to_reward` | `float` | Optional risk to reward ratio | None | + + + +### set_trade_stop_levels_points ```python -async def set_trade_stop_levels(*, points) +async def set_trade_stop_levels_points(*, points: float, risk_to_reward: float = None): ``` -sets the stop loss and take profit for the order. This method uses points as defined by MetaTrader5 for all symbols. -#### Parameters -| Name | Type | Description | Default | -|----------|---------|---------------|---------| -| `points` | `float` | Target points | None | +Sets the stop loss and take profit for the order. This method uses points as defined for forex instruments. - -### send\_order +#### Parameters: +| Name | Type | Description | Default | +|------------------|---------|-------------------------------|---------| +| `points` | `float` | Target points | | +| `risk_to_reward` | `float` | Optional risk to reward ratio | None | + + + +### create_order_no_stops ```python -async def send_order() +async def create_order_no_stops(*, order_type: OrderType, volume: float = None) ``` -Sends the order to the broker for execution. Record the trade. +Create an order without setting stop loss and take profit. Using minimum lot size. - +#### Parameters: +| Name | Type | Description | Default | +|--------------|-------------|---------------------|---------| +| `order_type` | `OrderType` | The order type | | +| `volume` | `float` | The volume to trade | None | + + + +### create_order_with_stops +```python +async def create_order_with_stops(*, order_type: OrderType, sl: float, tp: float, amount_to_risk: float = None) +``` +Create an order with stop loss and take profit levels. Use the amount to risk per trade to +calculate the volume. + +#### Parameters: +| Name | Type | Description | Default | +|------------------|-------------|--------------------|---------| +| `order_type` | `OrderType` | The order type | | +| `sl` | `float` | The stop loss` | | +| `tp` | `float` | The take profit` | | +| `amount_to_risk` | `float` | The amount to risk | None | + + + +### create_order_with_sl +```python +async def create_order_with_sl(*, order_type: OrderType, sl: float, amount_to_risk: float = None, risk_to_reward: float = None) +``` +Create an order with a given stop_loss level. Use the amount to risk per trade to calculate the volume. + +#### Parameters: +| Name | Type | Description | Default | +|------------------|-------------|------------------------------------------|---------| +| `order_type` | `OrderType` | The order type | | +| `sl` | `float` | The stop loss` | | +| `risk_to_reward` | `float` | Risk to reward ratio. Optional parameter | None | +| `amount_to_risk` | `float` | The amount to risk | None | + + + +### create_order_with_points +```python +async def create_order_with_points(*, order_type: OrderType, points: float, amount_to_risk: float = None, risk_to_reward: float = None) +``` +Create an order with specific points to risk. Use the amount to risk per trade to calculate the volume. + +#### Parameters: +| Name | Type | Description | Default | +|------------------|-------------|------------------------------------------|---------| +| `order_type` | `OrderType` | The order type | | +| `points` | `float` | Points to risk | | +| `risk_to_reward` | `float` | Risk to reward ratio. Optional parameter | None | +| `amount_to_risk` | `float` | The amount to risk | None | + + + +### send_order +```python +async def send_order() -> OrderSendResult +``` +Sends the order to the broker for execution. + +#### Returns: +| Type | Description | +|-------------------|----------------------------| +| `OrderSendResult` | The OrderSendResult object | + + + ### check_order ```python -async def check_order() +async def check_order() -> OrderCheckResult ``` Checks the status of the order before placing the trade. -#### Returns -| Type | Description | -|--------|-----------------------------------| -| `bool` | True if order is valid else False | - +#### Returns: +| Type | Description | +|--------------------|-----------------------------| +| `OrderCheckResult` | The OrderCheckResult object | + + ### record_trade ```python -async def record_trade(result: OrderSendResult, parameters: dict = None, name: str = '', exclude: set = None) +async def record_trade(*, result: OrderSendResult, parameters: dict = None, name: str = '') ``` Records the trade and the order details if `Config.record_trades` is true. Trades are recorded as either json or csv. -#### Parameters + +#### Parameters: | Name | Type | Description | Default | |--------------|-------------------|--------------------------------------------------------------|---------| -| `result` | `OrderSendResult` | The result of the placed order | None | +| `result` | `OrderSendResult` | The result of the placed order | | | `parameters` | `dict` | parameters to saved instead of the ones in `self.parameters` | None | | `name` | `str` | Name for the csv or json file | '' | -| `exclude` | `set` | Set of keys to exclude from the saved parameters | None | - -### place\_trade + +### place_trade ```python @abstractmethod - async def place_trade(self, *args, **kwargs): +async def place_trade(self, *args, **kwargs) ``` Places a trade. All traders must implement this method. diff --git a/sample_backtest.py b/sample_backtest.py index 413a20d..b11fff3 100644 --- a/sample_backtest.py +++ b/sample_backtest.py @@ -30,13 +30,13 @@ async def back_tester(): back_test_engine = BackTestEngine( start=start, end=end, - speed=300, + speed=3600, stop_time=stop_time, close_open_positions_on_exit=True, assign_to_config=True, preload=True, + account_info={'balance': 350} ) - await back_test_engine.setup_account(balance=350) backtester = BackTester(backtest_engine=back_test_engine) backtester.add_strategies(strategies=strategies) await backtester.start() diff --git a/src/aiomql/core/backtesting/backtest_controller.py b/src/aiomql/core/backtesting/backtest_controller.py index 1a2476c..5384834 100644 --- a/src/aiomql/core/backtesting/backtest_controller.py +++ b/src/aiomql/core/backtesting/backtest_controller.py @@ -69,7 +69,7 @@ class BackTestController: if self.backtest_engine.stop_testing: logger.info( "Stop trading called in control at %s", - self.backtest_engine.cursor.time, + datetime.fromtimestamp(self.backtest_engine.cursor.time).strftime("%Y-%m-%d %H:%M:%S"), ) break await self.backtest_engine.wrap_up() diff --git a/src/aiomql/core/backtesting/backtest_engine.py b/src/aiomql/core/backtesting/backtest_engine.py index 9b14ee1..0b8ce0e 100644 --- a/src/aiomql/core/backtesting/backtest_engine.py +++ b/src/aiomql/core/backtesting/backtest_engine.py @@ -75,7 +75,7 @@ class BackTestEngine: preloaded_ticks: dict[str, DataFrame] preload: bool account_lock: RLock - + account_info: dict def __init__( self, *, @@ -89,7 +89,8 @@ class BackTestEngine: stop_time: float | datetime = None, close_open_positions_on_exit: bool = True, preload=True, - assign_to_config: bool = False, + assign_to_config: bool = True, + account_info: dict = None ): self._data = data or BackTestData() self.mt5 = MetaTrader() @@ -127,6 +128,7 @@ class BackTestEngine: self.preload = preload self.preloaded_ticks = {} self.account_lock = RLock() + self.account_info = account_info or {} def __next__(self) -> Cursor: try: @@ -152,7 +154,7 @@ class BackTestEngine: ): if self._data.span and self._data.range: start = start or self._data.span[0] - end = end or self._data.span[-1] + 1 + end = end or self._data.span[-1] + speed start = ( start.astimezone(tz=UTC) if isinstance(start, datetime) @@ -172,8 +174,9 @@ class BackTestEngine: if restart is False and self._data.cursor is not None: self.cursor = self._data.cursor - new_range = range(self.range[self.cursor.index], self.range.stop) - new_span = range(self.span[self.cursor.index], self.span.stop) + index = self.cursor.index // speed + new_range = range(self.range[index], self.range.stop, speed) + new_span = range(self.span[index], self.span.stop, speed) self.iter = zip(new_range, new_span) # self.go_to(time=self.cursor.time) else: @@ -647,6 +650,7 @@ class BackTestEngine: @error_handler async def setup_account(self, **kwargs): + kwargs = {**self.account_info, **kwargs} default = { "profit": self._account.profit, "margin": self._account.margin, @@ -664,6 +668,26 @@ class BackTestEngine: self._account.set_attrs(**default) self.update_account() + @error_handler_sync + def setup_account_sync(self, **kwargs): + kwargs = {**self.account_info, **kwargs} + default = { + "profit": self._account.profit, + "margin": self._account.margin, + "equity": self._account.equity, + "margin_free": self._account.margin_free, + "margin_level": self._account.margin_level, + "balance": self._account.balance, + **{k: v for k, v in kwargs.items() if k in self._account.__match_args__}, + } + + if self.use_terminal: + acc_info = self.mt5._account_info() + default = {**acc_info._asdict(), **default} + + self._account.set_attrs(**default) + self.update_account() + @cached_property def prices(self) -> dict[str, DataFrame]: prices = {} diff --git a/src/aiomql/core/meta_backtester.py b/src/aiomql/core/meta_backtester.py index 9ac2b34..00a5ada 100644 --- a/src/aiomql/core/meta_backtester.py +++ b/src/aiomql/core/meta_backtester.py @@ -43,7 +43,10 @@ class MetaBackTester(MetaTrader): self.config.backtest_engine = value async def last_error(self) -> tuple[int, str]: - return -1, "" + if self.config.use_terminal_for_backtesting is False: + return -1, "" + else: + return await super().last_error() async def initialize( self, @@ -66,6 +69,41 @@ class MetaBackTester(MetaTrader): return True + def initialize_sync( + self, + *, + path: str = "", + login: int = 0, + password: str = "", + server: str = "", + timeout: int | None = None, + portable=False, + ) -> bool: + if self.config.use_terminal_for_backtesting: + return super().initialize_sync( + path=path, + login=login, + password=password, + server=server, + timeout=timeout, + ) + + return True + + def login_sync( + self, + *, + login: int = 0, + password: str = "", + server: str = "", + timeout: int = 60000, + ) -> bool: + if self.config.use_terminal_for_backtesting: + return super().login_sync( + login=login, password=password, server=server, timeout=timeout + ) + return True + async def login( self, *, diff --git a/src/aiomql/lib/backtester.py b/src/aiomql/lib/backtester.py index 616dffc..75c2d74 100644 --- a/src/aiomql/lib/backtester.py +++ b/src/aiomql/lib/backtester.py @@ -1,12 +1,16 @@ import asyncio -from typing import Type, Iterable, Callable, Coroutine import logging +from typing import Type, Iterable, Callable, Coroutine +from datetime import datetime, UTC + +from MetaTrader5 import Tick from .executor import Executor from ..core.config import Config from ..core.backtesting.backtest_controller import BackTestController from ..core.meta_backtester import MetaBackTester from ..core.backtesting.backtest_engine import BackTestEngine +from ..core.constants import CopyTicks from .symbol import Symbol as Symbol from .strategy import Strategy as Strategy @@ -19,10 +23,8 @@ class BackTester: Attributes: executor: The default thread executor. config (Config): Config instance - mt (MetaTrader): MetaTrader instance - + mt (MetaBackTester): MetaTrader instance """ - config: Config executor: Executor mt: MetaBackTester @@ -38,6 +40,41 @@ class BackTester: self.backtest_controller = BackTestController() self.strategies = [] + def initialize_sync(self): + """Prepares the bot by signing in to the trading account and initializing the symbols for the trading session. + Starts the global task queue. + + Raises: + SystemExit if sign in was not successful + """ + try: + self.mt.initialize_sync() + login = self.mt.login_sync() + if not login: + logger.critical(f"Unable to sign in to MetaTrder 5 Terminal") + raise Exception("Unable to sign in to MetaTrader 5 Terminal") + logger.info("Login Successful") + self.backtest_engine.setup_account_sync() + self.init_strategies_sync() + if (strategies := len(self.executor.strategy_runners)) == 0: + logger.warning( + "No strategies were added to the backtester. Exiting ..." + ) + raise Exception("No strategies added to the backtester") + self.config.task_queue.worker_timeout = 5 + self.add_coroutine( + coroutine=self.config.task_queue.run, on_separate_thread=True + ) + self.add_coroutine(coroutine=self.executor.exit) + self.add_coroutine( + coroutine=self.backtest_controller.control, on_separate_thread=True + ) + parties = strategies + 1 + self.backtest_controller.set_parties(parties=parties) + except Exception as err: + logger.error(f"{err}. Backtester initialization failed") + raise SystemExit + async def initialize(self): """Prepares the bot by signing in to the trading account and initializing the symbols for the trading session. Starts the global task queue. @@ -52,7 +89,13 @@ class BackTester: logger.critical(f"Unable to sign in to MetaTrder 5 Terminal") raise Exception("Unable to sign in to MetaTrader 5 Terminal") logger.info("Login Successful") + await self.backtest_engine.setup_account() await self.init_strategies() + if (strategies := len(self.executor.strategy_runners)) == 0: + logger.warning( + "No strategies were added to the backtester. Exiting ..." + ) + raise Exception("No strategies added to the backtester") self.config.task_queue.worker_timeout = 5 self.add_coroutine( coroutine=self.config.task_queue.run, on_separate_thread=True @@ -61,11 +104,6 @@ class BackTester: self.add_coroutine( coroutine=self.backtest_controller.control, on_separate_thread=True ) - if (strategies := len(self.executor.strategy_runners)) == 0: - logger.warning( - "No strategies were added to the backtester. Exiting ..." - ) - raise Exception("No strategies added to the backtester") parties = strategies + 1 self.backtest_controller.set_parties(parties=parties) except Exception as err: @@ -95,7 +133,8 @@ class BackTester: def execute(self): """Execute the bot.""" - asyncio.run(self.start()) + self.initialize_sync() + self.executor.execute() async def start(self): """Initialize the bot and execute it. Similar to calling `execute` method but is a coroutine.""" @@ -150,7 +189,53 @@ class BackTester: self.executor.add_strategy(strategy=strategy) return res + def init_strategy_sync(self, *, strategy: Strategy) -> bool: + """Initialize a single strategy. This method is called internally by the bot.""" + try: + if self.backtest_engine.use_terminal is False: + info = self.backtest_engine.symbols[strategy.symbol.name] + tick = self.backtest_engine.prices[strategy.symbol.name].loc[self.backtest_engine.cursor.time] + tick = Tick(tick) + info = info._asdict() | { + "bid": tick.bid, + "bidhigh": tick.bid, + "bidlow": tick.bid, + "ask": tick.ask, + "askhigh": tick.ask, + "asklow": tick.bid, + "last": tick.last, + "volume_real": tick.volume_real, + } + strategy.symbol.set_attributes(**info) + self.executor.add_strategy(strategy=strategy) + return True + else: + info = self.mt._symbol_info(strategy.symbol.name) + time = datetime.fromtimestamp(self.backtest_engine.cursor.time, tz=UTC) + tick = self.mt._copy_ticks_from(strategy.symbol.name, time, 1, CopyTicks.ALL) + tick = Tick(tick[-1]) if tick is not None else None + info = info._asdict() | { + "bid": tick.bid, + "bidhigh": tick.bid, + "bidlow": tick.bid, + "ask": tick.ask, + "askhigh": tick.ask, + "asklow": tick.bid, + "last": tick.last, + "volume_real": tick.volume_real, + } + strategy.symbol.set_attributes(**info) + self.executor.add_strategy(strategy=strategy) + return True + except Exception as err: + logger.error("%s: Unable to initialize strategy", err) + return False + async def init_strategies(self): """Initialize the symbols for the current trading session. This method is called internally by the bot.""" tasks = [self.init_strategy(strategy=strategy) for strategy in self.strategies] await asyncio.gather(*tasks) + + def init_strategies_sync(self): + """Initialize the symbols for the current trading session. This method is called internally by the bot.""" + [self.init_strategy_sync(strategy=strategy) for strategy in self.strategies] diff --git a/src/aiomql/lib/candle.py b/src/aiomql/lib/candle.py index f88bd2a..7f97d8e 100644 --- a/src/aiomql/lib/candle.py +++ b/src/aiomql/lib/candle.py @@ -78,7 +78,7 @@ class Candle: return self.time < other.time def __hash__(self): - return hash(self.time) + return id(self) def __getitem__(self, item): return self.__dict__[item] @@ -117,7 +117,7 @@ class Candle: Returns: bool: True or False """ - return self.open > self.close + return self.close < self.open def dict(self, *, exclude: set = None, include: set = None) -> dict: """ @@ -185,7 +185,7 @@ class Candles: data (DataFrame|Candles|Iterable): A pandas dataframe, a Candles object or any suitable iterable Keyword Args: - flip (bool): Reverse the chronological order of the candles to the oldest first. Defaults to False. + flip (bool): Reverse the chronological order of the candles to the most recent first. Defaults to False. candle_class: A subclass of Candle to use as the candle class. Defaults to Candle. """ if isinstance(data, DataFrame): @@ -289,58 +289,3 @@ class Candles: """ res = self._data.rename(columns=kwargs, inplace=inplace) return self if inplace else self.__class__(data=res) - - def make_addplot(self, *, count: int = 50, columns: list = None, **kwargs) -> dict: - """ - Make subplots for adding to the main plot - - Args: - count (int): The numbers of candles to make the addplot for. Defaults to 50. - columns (list[str]): The columns to make the plot from. Defaults to None. - **kwargs: Valid arguments for the mplfinance make_addplot function - """ - columns = columns or [] - data = self._data[-count:] - data.index = pd.to_datetime(data["time"], unit="s") - return mplt.make_addplot(data[columns], **kwargs) - - def visualize( - self, - *, - count: int = 50, - _type="candle", - savefig: str | dict = None, - addplot: dict = None, - style: str = "charles", - ylabel: str = "Price", - title: str = "Chart", - **kwargs, - ): - """Visualize the candles using the mplfinance library. - Args: - count (int): The number of candles to visualize, counting from behind, i.e the most recent candles. - Defaults to 50. - _type: Type of chart, defaults to candle - savefig (str|dict): The path to save the figure or a dictionary of parameters to pass to the savefig method. - addplot: Additional plots to add to the chart. Defaults to None. They should match the dimension of the - original data which is specified via the count parameter. - style (str): The style of the chart. Defaults to 'charles'. - ylabel (str): The label of the y-axis. Defaults to 'Price'. - title (str): The title of the chart. Defaults to 'Chart'. - kwargs: valid kwargs for the plot function. - """ - kwargs |= { - key: arg - for key, arg in ( - ("savefig", savefig), - ("addplot", addplot), - ("style", style), - ("ylabel", ylabel), - ("title", title), - ("type", _type), - ) - if arg - } - data = self._data[-count:] - data.index = pd.to_datetime(data["time"], unit="s") - mplt.plot(data, **kwargs) diff --git a/src/aiomql/lib/executor.py b/src/aiomql/lib/executor.py index 3391abc..7087215 100644 --- a/src/aiomql/lib/executor.py +++ b/src/aiomql/lib/executor.py @@ -19,7 +19,6 @@ class Executor: coroutines (list[Coroutine]): A list of coroutines to run in the executor functions (dict[Callable, dict]): A dictionary of functions to run in the executor """ - executor: ThreadPoolExecutor tasks: list[asyncio.Task] config: Config diff --git a/src/aiomql/lib/history.py b/src/aiomql/lib/history.py index 3bd7cb3..4e1a024 100644 --- a/src/aiomql/lib/history.py +++ b/src/aiomql/lib/history.py @@ -3,11 +3,9 @@ from datetime import datetime from logging import getLogger import pytz -from pandas import DataFrame -import pandas as pd from ..core.config import Config -from ..core.meta_trader import MetaTrader, CopyTicks, OrderType +from ..core.meta_trader import MetaTrader from ..core.models import TradeDeal, TradeOrder from ..core.meta_backtester import MetaBackTester from .._utils import backoff_decorator @@ -30,21 +28,26 @@ class History: mt5: MetaTrader | MetaBackTester config: Config + deals: tuple[TradeDeal, ...] + orders: tuple[TradeOrder, ...] + total_deals: int + total_orders: int + group: str def __init__( self, *, - date_from: datetime | int, - date_to: datetime | int, + date_from: datetime | float, + date_to: datetime | float, group: str = "", use_utc: bool = True, ): """ Args: - date_from (datetime, int): Date the orders are requested from. Set by the 'datetime' object or as a + date_from (datetime, float): Date the orders are requested from. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to twenty-four hours from the current time in 'utc' - date_to (datetime, int): Date up to which the orders are requested. Set by the 'datetime' object or as a + date_to (datetime, float): Date up to which the orders are requested. Set by the 'datetime' object or as a number of seconds elapsed since 1970.01.01. Defaults to the current time in "utc" group (str): Filter for selecting history by symbols. Defaults to an empty string @@ -73,7 +76,7 @@ class History: """Get history deals and orders""" deals, orders = await asyncio.gather( self.get_deals(), self.get_orders(), return_exceptions=True - ) +) self.deals = deals if isinstance(deals, tuple) else () self.orders = orders if isinstance(orders, tuple) else () self.total_deals = len(self.deals) @@ -82,8 +85,9 @@ class History: @backoff_decorator async def get_deals(self) -> tuple[TradeDeal, ...]: """Get deals from trading history using the parameters set in the constructor. + Returns: - tuple[TradeDeal]: A list of trade deals + tuple[TradeDeal, ...]: A list of trade deals """ deals = await self.mt5.history_deals_get( date_from=self.date_from, date_to=self.date_to, group=self.group @@ -160,52 +164,3 @@ class History: key=lambda x: x.time_done_msc, ) ) - - async def track_order( - self, *, position: int = None, end_time: datetime = None - ) -> DataFrame: - """ - Track an order from the time it was opened to the time it was closed or any given time. - The tracking is done by getting the ticks - for the order symbol from the time the order was opened to the time it was closed. The profit for each tick is - calculated using the order type, symbol, initial volume, open price and the bid or ask price of the tick - depending on the order type. - Args: - end_time (datetime): The time to stop tracking the order. If not provided, the tracking will continue until - the order is closed. - position (int): The position ticket - end_time (int): The time to stop tracking the order in seconds. If not provided, the tracking will continue - until the order is closed. - Returns: - DataFrame: A pandas DataFrame of the ticks and profit for the order. - """ - orders = self.get_orders_by_position(position=position) - deals = self.get_deals_by_position(position=position) - open_order = orders[0] - open_deal = deals[0] - close_deal = deals[-1] - time_done = ( - datetime.timestamp(end_time) if end_time is not None else close_deal.time - ) - time_done_msc = int(time_done * 1000) - open_order.set_attributes( - time_done_msc=time_done_msc, time_done=time_done, price_open=open_deal.price - ) - ticks = await self.mt5.copy_ticks_range( - open_order.symbol, - open_order.time_setup, - open_order.time_done, - CopyTicks.ALL, - ) - data = pd.DataFrame(ticks) - profit = lambda x: self.mt5._order_calc_profit( - open_order.type, - open_order.symbol, - open_order.volume_initial, - open_order.price_open, - x.ask if open_order.type == OrderType.BUY else x.bid, - ) - data["profits"] = data.apply(profit, axis=1) - data["time"] = pd.to_datetime(data["time"], unit="s") - data.set_index("time", inplace=True) - return data diff --git a/src/aiomql/lib/order.py b/src/aiomql/lib/order.py index eef5191..b4af659 100644 --- a/src/aiomql/lib/order.py +++ b/src/aiomql/lib/order.py @@ -37,11 +37,11 @@ class Order(_Base, TradeRequest): """Get the number of active pending orders. Returns: - (int): total number of active orders + (int): total number of active pending orders """ return await self.mt5.orders_total() - async def get_order(self, *, ticket: int) -> TradeOrder | None: + async def get_pending_order(self, *, ticket: int) -> TradeOrder | None: """ Get a pending order by ticket number. @@ -57,17 +57,16 @@ class Order(_Base, TradeRequest): return TradeOrder(**order_._asdict()) return order - async def get_orders( - self, *, ticket: int = 0, symbol: str = "", group: str = "" - ) -> tuple[TradeOrder, ...]: + async def get_pending_orders(self, *, ticket: int = 0, symbol: str = "", group: str = "") -> tuple[TradeOrder, ...]: """Get the list of active pending orders for the current symbol. - Keyword Args: + Args: ticket (int): Order ticket number symbol (str): Symbol name group (str): Group name + Returns: - tuple[TradeOrder]: A Tuple of active trade orders as TradeOrder objects + tuple[TradeOrder, ...]: A Tuple of active pending trade orders as TradeOrder objects """ orders = await self.mt5.orders_get(symbol=symbol, ticket=ticket, group=group) if orders is not None: diff --git a/src/aiomql/lib/positions.py b/src/aiomql/lib/positions.py index 9ee5de9..c30c699 100644 --- a/src/aiomql/lib/positions.py +++ b/src/aiomql/lib/positions.py @@ -58,7 +58,7 @@ class Positions: return None return TradePosition(**position._asdict()) - async def get_position_by_symbol(self, *, symbol: str) -> tuple[TradePosition, ...]: + async def get_positions_by_symbol(self, *, symbol: str) -> tuple[TradePosition, ...]: """Get open positions by symbol. Args: symbol (str): Financial instrument name. diff --git a/src/aiomql/lib/result.py b/src/aiomql/lib/result.py index 2882885..98495ae 100644 --- a/src/aiomql/lib/result.py +++ b/src/aiomql/lib/result.py @@ -12,7 +12,6 @@ logger = getLogger(__name__) class Result: """A base class for handling trade results and strategy parameters for record keeping and reference purpose. - The data property must be implemented in the subclass Attributes: config (Config): The configuration object @@ -22,9 +21,7 @@ class Result: config: Config lock = Lock() - def __init__( - self, *, result: OrderSendResult, parameters: dict = None, name: str = "" - ): + def __init__(self, *, result: OrderSendResult, parameters: dict = None, name: str = ""): """ Prepare result data Args: diff --git a/src/aiomql/lib/sessions.py b/src/aiomql/lib/sessions.py index 3f396d0..8c5d063 100644 --- a/src/aiomql/lib/sessions.py +++ b/src/aiomql/lib/sessions.py @@ -214,11 +214,6 @@ class Sessions: Attributes: sessions (list[Session]): A list of Session objects. current_session (Session): The current session. - - Methods: - find: Find a session that contains a datetime.time object. - find_next: Find the next session that contains a datetime.time object. - check: Check if the current session has started and if not, wait until it starts. """ sessions: list[Session] diff --git a/src/aiomql/lib/strategy.py b/src/aiomql/lib/strategy.py index 2af312b..b4f6dc5 100644 --- a/src/aiomql/lib/strategy.py +++ b/src/aiomql/lib/strategy.py @@ -27,6 +27,10 @@ class Strategy(ABC): parameters (Dict): A dictionary of parameters for the strategy. sessions (Sessions): The sessions to use for the strategy. running (bool): A flag to indicate if the strategy is running. + backtest_controller (BackTestController): A controller for running the backtester. + current_session (Session): The current session. + mt5 (MetaTrader|MetaBackTester): The MetaTrader object. + config (Config): The config object. Notes: Define the name of a strategy as a class attribute. If not provided, the class name will be used as the name. diff --git a/src/aiomql/lib/symbol.py b/src/aiomql/lib/symbol.py index 2f3a8b9..7bf1cc1 100644 --- a/src/aiomql/lib/symbol.py +++ b/src/aiomql/lib/symbol.py @@ -1,5 +1,4 @@ """Symbol class for handling a financial instrument.""" -import asyncio from datetime import datetime from logging import getLogger @@ -41,6 +40,7 @@ class Symbol(_Base, SymbolInfo): super().__init__(**kwargs) self.account = Account() + @backoff_decorator async def info_tick(self, *, name: str = "") -> Tick | None: """Get the current price tick of a financial instrument. @@ -127,14 +127,11 @@ class Symbol(_Base, SymbolInfo): Returns: tuple[BookInfo]: Returns the Market Depth contents as a tuples of BookInfo Objects - - Raises: - ValueError: If request was unsuccessful and None was returned """ - infos = await self.mt5.market_book_get(self.name) + book_info = await self.mt5.market_book_get(self.name) - if infos is not None: - book_infos = (BookInfo(**info._asdict()) for info in infos) + if book_info is not None: + book_infos = (BookInfo(**info._asdict()) for info in book_info) return tuple(book_infos) raise ValueError(f"Could not get book info for {self.name}") @@ -147,7 +144,7 @@ class Symbol(_Base, SymbolInfo): """ return await self.mt5.market_book_release(self.name) - def check_volume(self, *, volume) -> tuple[bool, float]: + def check_volume(self, *, volume: float) -> tuple[bool, float]: """Check if the volume is within the limits of the symbol. If not, return the nearest limit. Args: diff --git a/src/aiomql/lib/terminal.py b/src/aiomql/lib/terminal.py index abf43d7..bf61159 100644 --- a/src/aiomql/lib/terminal.py +++ b/src/aiomql/lib/terminal.py @@ -15,9 +15,6 @@ Version = NamedTuple( class Terminal(_Base, TerminalInfo): """Terminal Class. Get information about the MetaTrader 5 terminal. The class is a subclass of the TerminalInfo class. It inherits all the attributes and methods of the TerminalInfo class and adds some useful methods. - - Notes: - Other attributes are defined in the TerminalInfo Class """ version: Version | None = None diff --git a/src/aiomql/lib/ticks.py b/src/aiomql/lib/ticks.py index 5cca9fe..9b494d4 100644 --- a/src/aiomql/lib/ticks.py +++ b/src/aiomql/lib/ticks.py @@ -5,8 +5,6 @@ import time from pandas import DataFrame, Series import pandas_ta as ta -import mplfinance as mplt -import pandas as pd from ..core.constants import TickFlag @@ -212,58 +210,3 @@ class Ticks: """ res = self._data.rename(columns=kwargs, inplace=inplace) return res if inplace else self.__class__(data=res) - - def make_addplot(self, *, count: int = 50, columns: list = None, **kwargs) -> dict: - """ - Make subplots for adding to the main plot - - Args: - count (int): The numbers of candles to make the addplot for. Defaults to 50. - columns (list[str]): The columns to make the plot from. Defaults to None. - **kwargs: Valid arguments for the mplfinance make_addplot function - """ - columns = columns or [] - data = self._data[-count:] - data.index = pd.to_datetime(data["time"], unit="s") - return mplt.make_addplot(data[columns], **kwargs) - - def visualize( - self, - *, - count: int = 50, - _type="candle", - savefig: str | dict = None, - addplot: dict = None, - style: str = "charles", - ylabel: str = "Price", - title: str = "Chart", - **kwargs, - ): - """Visualize the candles using the mplfinance library. - Args: - count (int): The number of candles to visualize, counting from behind, i.e the most recent candles. - Defaults to 50. - _type: Type of chart, defaults to candle - savefig (str|dict): The path to save the figure or a dictionary of parameters to pass to the savefig method. - addplot: Additional plots to add to the chart. Defaults to None. They should match the dimension of the - original data which is specified via the count parameter. - style (str): The style of the chart. Defaults to 'charles'. - ylabel (str): The label of the y-axis. Defaults to 'Price'. - title (str): The title of the chart. Defaults to 'Chart'. - kwargs: valid kwargs for the plot function. - """ - kwargs |= { - key: arg - for key, arg in ( - ("savefig", savefig), - ("addplot", addplot), - ("style", style), - ("ylabel", ylabel), - ("title", title), - ("type", _type), - ) - if arg - } - data = self._data[-count:] - data.index = pd.to_datetime(data["time"], unit="s") - mplt.plot(data, **kwargs) diff --git a/src/aiomql/lib/trader.py b/src/aiomql/lib/trader.py index 928df8d..2bce467 100644 --- a/src/aiomql/lib/trader.py +++ b/src/aiomql/lib/trader.py @@ -47,16 +47,17 @@ class Trader(ABC): self.order = Order(symbol=symbol.name) self.parameters = {} - def set_trade_stop_levels_pips(self, *, pips: float): + def set_trade_stop_levels_pips(self, *, pips: float, risk_to_reward: float = None): """Sets the stop loss and take profit for the order. This method uses pips as defined for forex instruments. It is assumed that order_type and price are already set before calling this method. Args: pips (float): Target pips + risk_to_reward (float): Optional risk to reward ratio """ pips = pips * self.symbol.pip - sl, tp = pips, pips * self.ram.risk_to_reward + sl, tp = pips, pips * (risk_to_reward or self.ram.risk_to_reward) price = self.order.price if self.order.type == OrderType.BUY: self.order.sl, self.order.tp = round(price - sl, self.symbol.digits), round( @@ -176,9 +177,7 @@ class Trader(ABC): self.order.volume = volume self.set_trade_stop_levels_points(points=points, risk_to_reward=risk_to_reward) - async def create_order_no_stops( - self, *, order_type: OrderType, volume: float = None - ): + async def create_order_no_stops(self, *, order_type: OrderType, volume: float = None): """Create an order without setting stop loss and take profit. Using minimum lot size. Args: diff --git a/tests/backtest/integration/test_backtesting.py b/tests/backtest/integration/test_backtesting.py index 865a468..ef722fd 100644 --- a/tests/backtest/integration/test_backtesting.py +++ b/tests/backtest/integration/test_backtesting.py @@ -150,10 +150,10 @@ async def test_wrapup(positions, buy_order, sell_order, backtest_engine, config) backtest_engine.fast_forward(steps=5000) await backtest_engine.tracker() await positions.close_position_by_ticket(ticket=bo.order) + await backtest_engine.wrap_up() last_balance = backtest_engine._account.balance last_equity = backtest_engine._account.equity last_profit = backtest_engine._account.profit - await backtest_engine.wrap_up() tdata = GetData.load_data(name=config.backtest_dir / f"{backtest_engine.name}.pkl") new_bte = BackTestEngine( data=tdata, restart=False, assign_to_config=False, preload=False diff --git a/tests/live/integration/test_bot_sync.py b/tests/live/integration/test_bot_sync.py index 34b6ea3..5b6c26f 100644 --- a/tests/live/integration/test_bot_sync.py +++ b/tests/live/integration/test_bot_sync.py @@ -22,4 +22,3 @@ def test_bot_sync(): assert len(bot.executor.strategy_runners) == 3 assert len(bot.executor.coroutines) == 1 assert len(bot.executor.coroutine_threads) == 1 - assert bot.config.shutdown is True diff --git a/tests/live/unit/test_backtest_engine.py b/tests/live/unit/test_backtest_engine.py index 7c9bc2f..39e3034 100644 --- a/tests/live/unit/test_backtest_engine.py +++ b/tests/live/unit/test_backtest_engine.py @@ -144,6 +144,12 @@ class TestBackTestEngine: assert acc.margin_free == 62.5 assert acc.margin_level == 2600 + def test_account_sync(self): + balance = 200 + self.bte.setup_account_sync(balance=balance) + acc = self.bte.get_account_info() + assert acc.balance == balance + async def test_bte2_init(self, bte2): assert bte2._data.fully_loaded is True assert bte2.span == self.bte.span diff --git a/tests/live/unit/test_positions.py b/tests/live/unit/test_positions.py index befa762..63f28e2 100644 --- a/tests/live/unit/test_positions.py +++ b/tests/live/unit/test_positions.py @@ -25,6 +25,6 @@ class TestPositions: async def test_get_position_by_symbol(self): symbol = self.positions.positions[0].symbol - positions = await self.positions.get_position_by_symbol(symbol=symbol) + positions = await self.positions.get_positions_by_symbol(symbol=symbol) assert len(positions) >= 0 assert positions[0].symbol == symbol diff --git a/trade_records/Chaos.csv b/trade_records/Chaos.csv index e69de29..8849438 100644 --- a/trade_records/Chaos.csv +++ b/trade_records/Chaos.csv @@ -0,0 +1,177 @@ +name,win,slow_ema,ask,htf,actual_profit,order,expected_profit,ltf,lcc,hcc,fast_ema,price,volume,bid,closed,date,deal,symbol +Chaos,False,20,213158.32,TIMEFRAME_M2,0,878350736,0,TIMEFRAME_M1,100,100,8,213106.78,0.001,213106.78,False,2024-05-01 00:00:00.000000,103158006,Volatility 75 Index +Chaos,False,20,1530.17,TIMEFRAME_M2,0,801551760,0,TIMEFRAME_M1,100,100,8,1530.17,0.5,1529.72,False,2024-05-01 00:00:00.000000,162874674,Volatility 100 Index +Chaos,False,20,6262.652,TIMEFRAME_M2,0,851604968,0,TIMEFRAME_M1,100,100,8,6262.478,0.5,6262.478,False,2024-05-01 00:00:00.000000,135164162,Volatility 10 Index +Chaos,False,20,2223.324,TIMEFRAME_M2,0,810694221,0,TIMEFRAME_M1,100,100,8,2223.172,0.5,2223.172,False,2024-05-01 00:00:00.000000,121070859,Volatility 25 Index +Chaos,False,20,2223.324,TIMEFRAME_M2,0,833616949,0,TIMEFRAME_M1,100,100,8,2223.172,0.5,2223.172,False,2024-05-01 01:00:00.000000,125289226,Volatility 25 Index +Chaos,False,20,1530.17,TIMEFRAME_M2,0,871792218,0,TIMEFRAME_M1,100,100,8,1530.17,0.5,1529.72,False,2024-05-01 01:00:00.000000,161532936,Volatility 100 Index +Chaos,False,20,6274.875,TIMEFRAME_M2,0,877219427,0,TIMEFRAME_M1,100,100,8,6274.701,0.5,6274.701,False,2024-05-01 01:00:00.000000,123593949,Volatility 10 Index +Chaos,False,20,213252.53,TIMEFRAME_M2,0,830942668,0,TIMEFRAME_M1,100,100,8,213252.53,0.001,213200.99,False,2024-05-01 01:00:00.000000,142981254,Volatility 75 Index +Chaos,False,20,1554.67,TIMEFRAME_M2,0,847316898,0,TIMEFRAME_M1,100,100,8,1554.67,0.5,1554.22,False,2024-05-01 02:00:00.000000,191230468,Volatility 100 Index +Chaos,False,20,213252.53,TIMEFRAME_M2,0,839692427,0,TIMEFRAME_M1,100,100,8,213200.99,0.001,213200.99,False,2024-05-01 02:00:00.000000,169216508,Volatility 75 Index +Chaos,False,20,2211.736,TIMEFRAME_M2,0,890538270,0,TIMEFRAME_M1,100,100,8,2211.736,0.5,2211.584,False,2024-05-01 02:00:00.000000,122356467,Volatility 25 Index +Chaos,False,20,6275.654,TIMEFRAME_M2,0,835972740,0,TIMEFRAME_M1,100,100,8,6275.48,0.5,6275.48,False,2024-05-01 02:00:00.000000,151119257,Volatility 10 Index +Chaos,False,20,6275.654,TIMEFRAME_M2,0,816903676,0,TIMEFRAME_M1,100,100,8,6275.654,0.5,6275.48,False,2024-05-01 02:00:00.000000,129721880,Volatility 10 Index +Chaos,False,20,214422.41,TIMEFRAME_M2,0,844736060,0,TIMEFRAME_M1,100,100,8,214370.87,0.001,214370.87,False,2024-05-01 02:00:00.000000,164906038,Volatility 75 Index +Chaos,False,20,1530.52,TIMEFRAME_M2,0,852510195,0,TIMEFRAME_M1,100,100,8,1530.52,0.5,1530.07,False,2024-05-01 03:00:00.000000,151783118,Volatility 100 Index +Chaos,False,20,2212.057,TIMEFRAME_M2,0,847363912,0,TIMEFRAME_M1,100,100,8,2211.905,0.5,2211.905,False,2024-05-01 03:00:00.000000,138362396,Volatility 25 Index +Chaos,False,20,6266.259,TIMEFRAME_M2,0,820156416,0,TIMEFRAME_M1,100,100,8,6266.259,0.5,6266.085,False,2024-05-01 03:00:00.000000,164940319,Volatility 10 Index +Chaos,False,20,2212.057,TIMEFRAME_M2,0,858925801,0,TIMEFRAME_M1,100,100,8,2212.057,0.5,2211.905,False,2024-05-01 03:00:00.000000,116888736,Volatility 25 Index +Chaos,False,20,1541.23,TIMEFRAME_M2,0,866849933,0,TIMEFRAME_M1,100,100,8,1540.78,0.5,1540.78,False,2024-05-01 03:00:00.000000,144159935,Volatility 100 Index +Chaos,False,20,214248.02,TIMEFRAME_M2,0,853614811,0,TIMEFRAME_M1,100,100,8,214248.02,0.001,214196.48,False,2024-05-01 03:00:00.000000,184486804,Volatility 75 Index +Chaos,False,20,2212.057,TIMEFRAME_M2,0,864445967,0,TIMEFRAME_M1,100,100,8,2211.905,0.5,2211.905,False,2024-05-01 03:00:00.000000,193861841,Volatility 25 Index +Chaos,False,20,214248.02,TIMEFRAME_M2,0,873343446,0,TIMEFRAME_M1,100,100,8,214248.02,0.001,214196.48,False,2024-05-01 03:00:00.000000,112619278,Volatility 75 Index +Chaos,False,20,6266.259,TIMEFRAME_M2,0,836486112,0,TIMEFRAME_M1,100,100,8,6266.085,0.5,6266.085,False,2024-05-01 04:00:00.000000,154892406,Volatility 10 Index +Chaos,False,20,1541.23,TIMEFRAME_M2,0,871161343,0,TIMEFRAME_M1,100,100,8,1540.78,0.5,1540.78,False,2024-05-01 04:00:00.000000,174449452,Volatility 100 Index +Chaos,False,20,2204.2,TIMEFRAME_M2,0,813203733,0,TIMEFRAME_M1,100,100,8,2204.048,0.5,2204.048,False,2024-05-01 04:00:00.000000,179122485,Volatility 25 Index +Chaos,False,20,6258.229,TIMEFRAME_M2,0,855824654,0,TIMEFRAME_M1,100,100,8,6258.229,0.5,6258.055,False,2024-05-01 04:00:00.000000,127729398,Volatility 10 Index +Chaos,False,20,214393.69,TIMEFRAME_M2,0,870424059,0,TIMEFRAME_M1,100,100,8,214393.69,0.001,214342.15,False,2024-05-01 04:00:00.000000,163872308,Volatility 75 Index +Chaos,False,20,1532.57,TIMEFRAME_M2,0,836080689,0,TIMEFRAME_M1,100,100,8,1532.12,0.5,1532.12,False,2024-05-01 04:00:00.000000,148053187,Volatility 100 Index +Chaos,False,20,1532.57,TIMEFRAME_M2,0,844757802,0,TIMEFRAME_M1,100,100,8,1532.12,0.5,1532.12,False,2024-05-01 04:00:00.000000,164739883,Volatility 100 Index +Chaos,False,20,214393.69,TIMEFRAME_M2,0,844907343,0,TIMEFRAME_M1,100,100,8,214393.69,0.001,214342.15,False,2024-05-01 04:00:00.000000,128393904,Volatility 75 Index +Chaos,False,20,2204.2,TIMEFRAME_M2,0,889174320,0,TIMEFRAME_M1,100,100,8,2204.048,0.5,2204.048,False,2024-05-01 04:00:00.000000,114266815,Volatility 25 Index +Chaos,False,20,6258.229,TIMEFRAME_M2,0,851849681,0,TIMEFRAME_M1,100,100,8,6258.055,0.5,6258.055,False,2024-05-01 04:00:00.000000,176764971,Volatility 10 Index +Chaos,False,20,2199.649,TIMEFRAME_M2,0,843785198,0,TIMEFRAME_M1,100,100,8,2199.649,0.5,2199.497,False,2024-05-01 05:00:00.000000,185687277,Volatility 25 Index +Chaos,False,20,1533.0,TIMEFRAME_M2,0,806097447,0,TIMEFRAME_M1,100,100,8,1533.0,0.5,1532.55,False,2024-05-01 05:00:00.000000,180410065,Volatility 100 Index +Chaos,False,20,214234.49,TIMEFRAME_M2,0,867650998,0,TIMEFRAME_M1,100,100,8,214234.49,0.001,214182.95,False,2024-05-01 05:00:00.000000,156476267,Volatility 75 Index +Chaos,False,20,6264.311,TIMEFRAME_M2,0,822654546,0,TIMEFRAME_M1,100,100,8,6264.311,0.5,6264.137,False,2024-05-01 05:00:00.000000,161222099,Volatility 10 Index +Chaos,False,20,6264.311,TIMEFRAME_M2,0,822027320,0,TIMEFRAME_M1,100,100,8,6264.311,0.5,6264.137,False,2024-05-01 06:00:00.000000,159342582,Volatility 10 Index +Chaos,False,20,214234.49,TIMEFRAME_M2,0,800079192,0,TIMEFRAME_M1,100,100,8,214234.49,0.001,214182.95,False,2024-05-01 06:00:00.000000,126684464,Volatility 75 Index +Chaos,False,20,1551.9,TIMEFRAME_M2,0,871338447,0,TIMEFRAME_M1,100,100,8,1551.9,0.5,1551.45,False,2024-05-01 06:00:00.000000,193824236,Volatility 100 Index +Chaos,False,20,2197.204,TIMEFRAME_M2,0,885139336,0,TIMEFRAME_M1,100,100,8,2197.052,0.5,2197.052,False,2024-05-01 06:00:00.000000,167682497,Volatility 25 Index +Chaos,False,20,6260.308,TIMEFRAME_M2,0,877620534,0,TIMEFRAME_M1,100,100,8,6260.134,0.5,6260.134,False,2024-05-01 06:00:00.000000,108058780,Volatility 10 Index +Chaos,False,20,1551.9,TIMEFRAME_M2,0,873706466,0,TIMEFRAME_M1,100,100,8,1551.9,0.5,1551.45,False,2024-05-01 07:00:00.000000,171070574,Volatility 100 Index +Chaos,False,20,213020.43,TIMEFRAME_M2,0,855445602,0,TIMEFRAME_M1,100,100,8,212968.89,0.001,212968.89,False,2024-05-01 07:00:00.000000,191065557,Volatility 75 Index +Chaos,False,20,2190.008,TIMEFRAME_M2,0,899388418,0,TIMEFRAME_M1,100,100,8,2189.856,0.5,2189.856,False,2024-05-01 07:00:00.000000,189449030,Volatility 25 Index +Chaos,False,20,6259.793,TIMEFRAME_M2,0,869073467,0,TIMEFRAME_M1,100,100,8,6259.619,0.5,6259.619,False,2024-05-01 07:00:00.000000,173296439,Volatility 10 Index +Chaos,False,20,2190.008,TIMEFRAME_M2,0,887146622,0,TIMEFRAME_M1,100,100,8,2189.856,0.5,2189.856,False,2024-05-01 07:00:00.000000,199610850,Volatility 25 Index +Chaos,False,20,214214.09,TIMEFRAME_M2,0,811967805,0,TIMEFRAME_M1,100,100,8,214162.55,0.001,214162.55,False,2024-05-01 07:00:00.000000,170460203,Volatility 75 Index +Chaos,False,20,1570.68,TIMEFRAME_M2,0,817816770,0,TIMEFRAME_M1,100,100,8,1570.23,0.5,1570.23,False,2024-05-01 07:00:00.000000,154212076,Volatility 100 Index +Chaos,False,20,214214.09,TIMEFRAME_M2,0,884148623,0,TIMEFRAME_M1,100,100,8,214214.09,0.001,214162.55,False,2024-05-01 07:00:00.000000,100971853,Volatility 75 Index +Chaos,False,20,1570.68,TIMEFRAME_M2,0,861937441,0,TIMEFRAME_M1,100,100,8,1570.23,0.5,1570.23,False,2024-05-01 07:00:00.000000,140379711,Volatility 100 Index +Chaos,False,20,6259.793,TIMEFRAME_M2,0,851780777,0,TIMEFRAME_M1,100,100,8,6259.619,0.5,6259.619,False,2024-05-01 08:00:00.000000,104321399,Volatility 10 Index +Chaos,False,20,2190.008,TIMEFRAME_M2,0,865554473,0,TIMEFRAME_M1,100,100,8,2190.008,0.5,2189.856,False,2024-05-01 08:00:00.000000,113071616,Volatility 25 Index +Chaos,False,20,2195.456,TIMEFRAME_M2,0,882065437,0,TIMEFRAME_M1,100,100,8,2195.456,0.5,2195.304,False,2024-05-01 08:00:00.000000,175555279,Volatility 25 Index +Chaos,False,20,213829.91,TIMEFRAME_M2,0,822701548,0,TIMEFRAME_M1,100,100,8,213829.91,0.001,213778.37,False,2024-05-01 08:00:00.000000,163660466,Volatility 75 Index +Chaos,False,20,1551.23,TIMEFRAME_M2,0,837493772,0,TIMEFRAME_M1,100,100,8,1551.23,0.5,1550.78,False,2024-05-01 08:00:00.000000,184177483,Volatility 100 Index +Chaos,False,20,6264.7,TIMEFRAME_M2,0,848384485,0,TIMEFRAME_M1,100,100,8,6264.526,0.5,6264.526,False,2024-05-01 08:00:00.000000,109838346,Volatility 10 Index +Chaos,False,20,6264.7,TIMEFRAME_M2,0,872386905,0,TIMEFRAME_M1,100,100,8,6264.526,0.5,6264.526,False,2024-05-01 08:00:00.000000,170607941,Volatility 10 Index +Chaos,False,20,2195.456,TIMEFRAME_M2,0,805883016,0,TIMEFRAME_M1,100,100,8,2195.304,0.5,2195.304,False,2024-05-01 09:00:00.000000,146304663,Volatility 25 Index +Chaos,False,20,1551.23,TIMEFRAME_M2,0,805013163,0,TIMEFRAME_M1,100,100,8,1551.23,0.5,1550.78,False,2024-05-01 09:00:00.000000,163150971,Volatility 100 Index +Chaos,False,20,213482.63,TIMEFRAME_M2,0,856110839,0,TIMEFRAME_M1,100,100,8,213482.63,0.001,213431.09,False,2024-05-01 09:00:00.000000,197625059,Volatility 75 Index +Chaos,False,20,2197.637,TIMEFRAME_M2,0,856826096,0,TIMEFRAME_M1,100,100,8,2197.637,0.5,2197.485,False,2024-05-01 09:00:00.000000,121012840,Volatility 25 Index +Chaos,False,20,1556.55,TIMEFRAME_M2,0,858513739,0,TIMEFRAME_M1,100,100,8,1556.55,0.5,1556.1,False,2024-05-01 09:00:00.000000,171822038,Volatility 100 Index +Chaos,False,20,213482.63,TIMEFRAME_M2,0,871005991,0,TIMEFRAME_M1,100,100,8,213482.63,0.001,213431.09,False,2024-05-01 09:00:00.000000,164094613,Volatility 75 Index +Chaos,False,20,6264.3,TIMEFRAME_M2,0,828666719,0,TIMEFRAME_M1,100,100,8,6264.126,0.5,6264.126,False,2024-05-01 09:00:00.000000,189567334,Volatility 10 Index +Chaos,False,20,213482.63,TIMEFRAME_M2,0,842779615,0,TIMEFRAME_M1,100,100,8,213482.63,0.001,213431.09,False,2024-05-01 09:00:00.000000,180610889,Volatility 75 Index +Chaos,False,20,6264.3,TIMEFRAME_M2,0,879920558,0,TIMEFRAME_M1,100,100,8,6264.3,0.5,6264.126,False,2024-05-01 09:00:00.000000,118956851,Volatility 10 Index +Chaos,False,20,1556.55,TIMEFRAME_M2,0,889959635,0,TIMEFRAME_M1,100,100,8,1556.55,0.5,1556.1,False,2024-05-01 09:00:00.000000,133586157,Volatility 100 Index +Chaos,False,20,2197.637,TIMEFRAME_M2,0,897734980,0,TIMEFRAME_M1,100,100,8,2197.485,0.5,2197.485,False,2024-05-01 10:00:00.000000,197574097,Volatility 25 Index +Chaos,False,20,2186.164,TIMEFRAME_M2,0,819243808,0,TIMEFRAME_M1,100,100,8,2186.012,0.5,2186.012,False,2024-05-01 10:00:00.000000,134709849,Volatility 25 Index +Chaos,False,20,6265.107,TIMEFRAME_M2,0,884028760,0,TIMEFRAME_M1,100,100,8,6264.933,0.5,6264.933,False,2024-05-01 10:00:00.000000,132973006,Volatility 10 Index +Chaos,False,20,210894.16,TIMEFRAME_M2,0,819333305,0,TIMEFRAME_M1,100,100,8,210894.16,0.001,210842.62,False,2024-05-01 10:00:00.000000,198437136,Volatility 75 Index +Chaos,False,20,1548.61,TIMEFRAME_M2,0,892662359,0,TIMEFRAME_M1,100,100,8,1548.61,0.5,1548.16,False,2024-05-01 10:00:00.000000,181673758,Volatility 100 Index +Chaos,False,20,210894.16,TIMEFRAME_M2,0,821406374,0,TIMEFRAME_M1,100,100,8,210894.16,0.001,210842.62,False,2024-05-01 11:00:00.000000,136397781,Volatility 75 Index +Chaos,False,20,2186.164,TIMEFRAME_M2,0,824473751,0,TIMEFRAME_M1,100,100,8,2186.164,0.5,2186.012,False,2024-05-01 11:00:00.000000,187165393,Volatility 25 Index +Chaos,False,20,1571.51,TIMEFRAME_M2,0,849750456,0,TIMEFRAME_M1,100,100,8,1571.51,0.5,1571.06,False,2024-05-01 11:00:00.000000,146511016,Volatility 100 Index +Chaos,False,20,6261.79,TIMEFRAME_M2,0,871122005,0,TIMEFRAME_M1,100,100,8,6261.616,0.5,6261.616,False,2024-05-01 11:00:00.000000,170479206,Volatility 10 Index +Chaos,False,20,6261.79,TIMEFRAME_M2,0,878985962,0,TIMEFRAME_M1,100,100,8,6261.616,0.5,6261.616,False,2024-05-01 11:00:00.000000,111854922,Volatility 10 Index +Chaos,False,20,213342.62,TIMEFRAME_M2,0,808390587,0,TIMEFRAME_M1,100,100,8,213291.08,0.001,213291.08,False,2024-05-01 12:00:00.000000,187618552,Volatility 75 Index +Chaos,False,20,1571.51,TIMEFRAME_M2,0,859645359,0,TIMEFRAME_M1,100,100,8,1571.51,0.5,1571.06,False,2024-05-01 12:00:00.000000,174899942,Volatility 100 Index +Chaos,False,20,2184.36,TIMEFRAME_M2,0,889766108,0,TIMEFRAME_M1,100,100,8,2184.208,0.5,2184.208,False,2024-05-01 12:00:00.000000,181639229,Volatility 25 Index +Chaos,False,20,211308.28,TIMEFRAME_M2,0,895814254,0,TIMEFRAME_M1,100,100,8,211308.28,0.001,211256.74,False,2024-05-01 12:00:00.000000,147219970,Volatility 75 Index +Chaos,False,20,2184.36,TIMEFRAME_M2,0,826074130,0,TIMEFRAME_M1,100,100,8,2184.36,0.5,2184.208,False,2024-05-01 12:00:00.000000,173317554,Volatility 25 Index +Chaos,False,20,1551.57,TIMEFRAME_M2,0,874742078,0,TIMEFRAME_M1,100,100,8,1551.12,0.5,1551.12,False,2024-05-01 12:00:00.000000,110827018,Volatility 100 Index +Chaos,False,20,6262.48,TIMEFRAME_M2,0,835724910,0,TIMEFRAME_M1,100,100,8,6262.306,0.5,6262.306,False,2024-05-01 12:00:00.000000,133221773,Volatility 10 Index +Chaos,False,20,1551.57,TIMEFRAME_M2,0,866447363,0,TIMEFRAME_M1,100,100,8,1551.57,0.5,1551.12,False,2024-05-01 12:00:00.000000,131784787,Volatility 100 Index +Chaos,False,20,6262.48,TIMEFRAME_M2,0,874161936,0,TIMEFRAME_M1,100,100,8,6262.306,0.5,6262.306,False,2024-05-01 13:00:00.000000,116269068,Volatility 10 Index +Chaos,False,20,211308.28,TIMEFRAME_M2,0,803669555,0,TIMEFRAME_M1,100,100,8,211256.74,0.001,211256.74,False,2024-05-01 13:00:00.000000,196328190,Volatility 75 Index +Chaos,False,20,2175.088,TIMEFRAME_M2,0,815139691,0,TIMEFRAME_M1,100,100,8,2175.088,0.5,2174.936,False,2024-05-01 13:00:00.000000,163638333,Volatility 25 Index +Chaos,False,20,1565.71,TIMEFRAME_M2,0,867211000,0,TIMEFRAME_M1,100,100,8,1565.71,0.5,1565.26,False,2024-05-01 13:00:00.000000,144628636,Volatility 100 Index +Chaos,False,20,2175.088,TIMEFRAME_M2,0,806576041,0,TIMEFRAME_M1,100,100,8,2175.088,0.5,2174.936,False,2024-05-01 13:00:00.000000,107502192,Volatility 25 Index +Chaos,False,20,6261.929,TIMEFRAME_M2,0,868786283,0,TIMEFRAME_M1,100,100,8,6261.929,0.5,6261.755,False,2024-05-01 13:00:00.000000,185420943,Volatility 10 Index +Chaos,False,20,211384.64,TIMEFRAME_M2,0,867523434,0,TIMEFRAME_M1,100,100,8,211333.1,0.001,211333.1,False,2024-05-01 13:00:00.000000,125206956,Volatility 75 Index +Chaos,False,20,6261.929,TIMEFRAME_M2,0,816916727,0,TIMEFRAME_M1,100,100,8,6261.755,0.5,6261.755,False,2024-05-01 13:00:00.000000,173261037,Volatility 10 Index +Chaos,False,20,211384.64,TIMEFRAME_M2,0,863896740,0,TIMEFRAME_M1,100,100,8,211333.1,0.001,211333.1,False,2024-05-01 13:00:00.000000,128242877,Volatility 75 Index +Chaos,False,20,1565.71,TIMEFRAME_M2,0,819587790,0,TIMEFRAME_M1,100,100,8,1565.71,0.5,1565.26,False,2024-05-01 13:00:00.000000,134961525,Volatility 100 Index +Chaos,False,20,2175.088,TIMEFRAME_M2,0,834812760,0,TIMEFRAME_M1,100,100,8,2175.088,0.5,2174.936,False,2024-05-01 14:00:00.000000,145588819,Volatility 25 Index +Chaos,False,20,1578.53,TIMEFRAME_M2,0,837036352,0,TIMEFRAME_M1,100,100,8,1578.08,0.5,1578.08,False,2024-05-01 14:00:00.000000,192562003,Volatility 100 Index +Chaos,False,20,6258.246,TIMEFRAME_M2,0,868259743,0,TIMEFRAME_M1,100,100,8,6258.246,0.5,6258.072,False,2024-05-01 14:00:00.000000,160280327,Volatility 10 Index +Chaos,False,20,208866.34,TIMEFRAME_M2,0,871349145,0,TIMEFRAME_M1,100,100,8,208866.34,0.001,208814.8,False,2024-05-01 14:00:00.000000,101409495,Volatility 75 Index +Chaos,False,20,2179.785,TIMEFRAME_M2,0,844410053,0,TIMEFRAME_M1,100,100,8,2179.785,0.5,2179.633,False,2024-05-01 14:00:00.000000,137160060,Volatility 25 Index +Chaos,False,20,208866.34,TIMEFRAME_M2,0,851344138,0,TIMEFRAME_M1,100,100,8,208866.34,0.001,208814.8,False,2024-05-01 14:00:00.000000,123394571,Volatility 75 Index +Chaos,False,20,2179.785,TIMEFRAME_M2,0,804253642,0,TIMEFRAME_M1,100,100,8,2179.785,0.5,2179.633,False,2024-05-01 14:00:00.000000,118936559,Volatility 25 Index +Chaos,False,20,1578.53,TIMEFRAME_M2,0,894055545,0,TIMEFRAME_M1,100,100,8,1578.08,0.5,1578.08,False,2024-05-01 15:00:00.000000,102218061,Volatility 100 Index +Chaos,False,20,6254.581,TIMEFRAME_M2,0,880406162,0,TIMEFRAME_M1,100,100,8,6254.581,0.5,6254.407,False,2024-05-01 15:00:00.000000,173031416,Volatility 10 Index +Chaos,False,20,6254.581,TIMEFRAME_M2,0,871037679,0,TIMEFRAME_M1,100,100,8,6254.407,0.5,6254.407,False,2024-05-01 15:00:00.000000,161083029,Volatility 10 Index +Chaos,False,20,209468.3,TIMEFRAME_M2,0,821219140,0,TIMEFRAME_M1,100,100,8,209468.3,0.001,209416.76,False,2024-05-01 15:00:00.000000,110425855,Volatility 75 Index +Chaos,False,20,2174.784,TIMEFRAME_M2,0,812179023,0,TIMEFRAME_M1,100,100,8,2174.784,0.5,2174.632,False,2024-05-01 15:00:00.000000,158445464,Volatility 25 Index +Chaos,False,20,1598.74,TIMEFRAME_M2,0,832261318,0,TIMEFRAME_M1,100,100,8,1598.29,0.5,1598.29,False,2024-05-01 15:00:00.000000,158409954,Volatility 100 Index +Chaos,False,20,1598.74,TIMEFRAME_M2,0,842443741,0,TIMEFRAME_M1,100,100,8,1598.29,0.5,1598.29,False,2024-05-01 15:00:00.000000,148320657,Volatility 100 Index +Chaos,False,20,2174.784,TIMEFRAME_M2,0,821932525,0,TIMEFRAME_M1,100,100,8,2174.632,0.5,2174.632,False,2024-05-01 16:00:00.000000,191304440,Volatility 25 Index +Chaos,False,20,209090.97,TIMEFRAME_M2,0,899127463,0,TIMEFRAME_M1,100,100,8,209090.97,0.001,209039.43,False,2024-05-01 16:00:00.000000,169912394,Volatility 75 Index +Chaos,False,20,6254.581,TIMEFRAME_M2,0,818220824,0,TIMEFRAME_M1,100,100,8,6254.581,0.5,6254.407,False,2024-05-01 16:00:00.000000,118836168,Volatility 10 Index +Chaos,False,20,6256.754,TIMEFRAME_M2,0,802488375,0,TIMEFRAME_M1,100,100,8,6256.754,0.5,6256.58,False,2024-05-01 16:00:00.000000,136615413,Volatility 10 Index +Chaos,False,20,209090.97,TIMEFRAME_M2,0,849928315,0,TIMEFRAME_M1,100,100,8,209039.43,0.001,209039.43,False,2024-05-01 16:00:00.000000,102706032,Volatility 75 Index +Chaos,False,20,2175.301,TIMEFRAME_M2,0,815034759,0,TIMEFRAME_M1,100,100,8,2175.149,0.5,2175.149,False,2024-05-01 16:00:00.000000,144107545,Volatility 25 Index +Chaos,False,20,1630.55,TIMEFRAME_M2,0,888911969,0,TIMEFRAME_M1,100,100,8,1630.55,0.5,1630.1,False,2024-05-01 16:00:00.000000,109934193,Volatility 100 Index +Chaos,False,20,209090.97,TIMEFRAME_M2,0,812325147,0,TIMEFRAME_M1,100,100,8,209090.97,0.001,209039.43,False,2024-05-01 16:00:00.000000,140455541,Volatility 75 Index +Chaos,False,20,2175.301,TIMEFRAME_M2,0,866260404,0,TIMEFRAME_M1,100,100,8,2175.149,0.5,2175.149,False,2024-05-01 16:00:00.000000,125635890,Volatility 25 Index +Chaos,False,20,1630.55,TIMEFRAME_M2,0,825464245,0,TIMEFRAME_M1,100,100,8,1630.1,0.5,1630.1,False,2024-05-01 17:00:00.000000,171464512,Volatility 100 Index +Chaos,False,20,6256.754,TIMEFRAME_M2,0,841026179,0,TIMEFRAME_M1,100,100,8,6256.754,0.5,6256.58,False,2024-05-01 17:00:00.000000,120742885,Volatility 10 Index +Chaos,False,20,1632.52,TIMEFRAME_M2,0,846497225,0,TIMEFRAME_M1,100,100,8,1632.52,0.5,1632.07,False,2024-05-01 17:00:00.000000,128876527,Volatility 100 Index +Chaos,False,20,211423.72,TIMEFRAME_M2,0,837311963,0,TIMEFRAME_M1,100,100,8,211423.72,0.001,211372.18,False,2024-05-01 17:00:00.000000,126408106,Volatility 75 Index +Chaos,False,20,6260.409,TIMEFRAME_M2,0,860730063,0,TIMEFRAME_M1,100,100,8,6260.235,0.5,6260.235,False,2024-05-01 17:00:00.000000,138339666,Volatility 10 Index +Chaos,False,20,2177.192,TIMEFRAME_M2,0,889145564,0,TIMEFRAME_M1,100,100,8,2177.192,0.5,2177.04,False,2024-05-01 17:00:00.000000,140414322,Volatility 25 Index +Chaos,False,20,2177.192,TIMEFRAME_M2,0,836307418,0,TIMEFRAME_M1,100,100,8,2177.192,0.5,2177.04,False,2024-05-01 17:00:00.000000,184179294,Volatility 25 Index +Chaos,False,20,1632.52,TIMEFRAME_M2,0,811365730,0,TIMEFRAME_M1,100,100,8,1632.07,0.5,1632.07,False,2024-05-01 17:00:00.000000,164537415,Volatility 100 Index +Chaos,False,20,6260.409,TIMEFRAME_M2,0,802703670,0,TIMEFRAME_M1,100,100,8,6260.235,0.5,6260.235,False,2024-05-01 17:00:00.000000,122381127,Volatility 10 Index +Chaos,False,20,211423.72,TIMEFRAME_M2,0,875174652,0,TIMEFRAME_M1,100,100,8,211423.72,0.001,211372.18,False,2024-05-01 17:00:00.000000,129015696,Volatility 75 Index +Chaos,False,20,209131.24,TIMEFRAME_M2,0,894281854,0,TIMEFRAME_M1,100,100,8,209079.7,0.001,209079.7,False,2024-05-01 18:00:00.000000,199099609,Volatility 75 Index +Chaos,False,20,1628.45,TIMEFRAME_M2,0,821822575,0,TIMEFRAME_M1,100,100,8,1628.45,0.5,1628.0,False,2024-05-01 18:00:00.000000,167835857,Volatility 100 Index +Chaos,False,20,2178.522,TIMEFRAME_M2,0,804685075,0,TIMEFRAME_M1,100,100,8,2178.522,0.5,2178.37,False,2024-05-01 18:00:00.000000,104150461,Volatility 25 Index +Chaos,False,20,6260.906,TIMEFRAME_M2,0,878169391,0,TIMEFRAME_M1,100,100,8,6260.906,0.5,6260.732,False,2024-05-01 18:00:00.000000,170084390,Volatility 10 Index +Chaos,False,20,6260.906,TIMEFRAME_M2,0,897710204,0,TIMEFRAME_M1,100,100,8,6260.906,0.5,6260.732,False,2024-05-01 18:00:00.000000,109393349,Volatility 10 Index +Chaos,False,20,2178.522,TIMEFRAME_M2,0,829288385,0,TIMEFRAME_M1,100,100,8,2178.522,0.5,2178.37,False,2024-05-01 18:00:00.000000,199032948,Volatility 25 Index +Chaos,False,20,1628.45,TIMEFRAME_M2,0,820316612,0,TIMEFRAME_M1,100,100,8,1628.0,0.5,1628.0,False,2024-05-01 18:00:00.000000,137320254,Volatility 100 Index +Chaos,False,20,209131.24,TIMEFRAME_M2,0,845210213,0,TIMEFRAME_M1,100,100,8,209079.7,0.001,209079.7,False,2024-05-01 18:00:00.000000,103274434,Volatility 75 Index +Chaos,False,20,1673.81,TIMEFRAME_M2,0,830286560,0,TIMEFRAME_M1,100,100,8,1673.81,0.5,1673.36,False,2024-05-01 19:00:00.000000,187664511,Volatility 100 Index +Chaos,False,20,6254.249,TIMEFRAME_M2,0,855390499,0,TIMEFRAME_M1,100,100,8,6254.075,0.5,6254.075,False,2024-05-01 19:00:00.000000,193728725,Volatility 10 Index +Chaos,False,20,208042.57,TIMEFRAME_M2,0,894231567,0,TIMEFRAME_M1,100,100,8,207991.03,0.001,207991.03,False,2024-05-01 19:00:00.000000,194060391,Volatility 75 Index +Chaos,False,20,2182.66,TIMEFRAME_M2,0,870875379,0,TIMEFRAME_M1,100,100,8,2182.66,0.5,2182.508,False,2024-05-01 19:00:00.000000,116811669,Volatility 25 Index +Chaos,False,20,6254.249,TIMEFRAME_M2,0,891086013,0,TIMEFRAME_M1,100,100,8,6254.075,0.5,6254.075,False,2024-05-01 19:00:00.000000,105095312,Volatility 10 Index +Chaos,False,20,2182.66,TIMEFRAME_M2,0,871825138,0,TIMEFRAME_M1,100,100,8,2182.508,0.5,2182.508,False,2024-05-01 19:00:00.000000,111189903,Volatility 25 Index +Chaos,False,20,1673.81,TIMEFRAME_M2,0,887017267,0,TIMEFRAME_M1,100,100,8,1673.81,0.5,1673.36,False,2024-05-01 19:00:00.000000,153037981,Volatility 100 Index +Chaos,False,20,208042.57,TIMEFRAME_M2,0,839045519,0,TIMEFRAME_M1,100,100,8,207991.03,0.001,207991.03,False,2024-05-01 20:00:00.000000,150923912,Volatility 75 Index +Chaos,False,20,208681.37,TIMEFRAME_M2,0,819583624,0,TIMEFRAME_M1,100,100,8,208681.37,0.001,208629.83,False,2024-05-01 20:00:00.000000,188630765,Volatility 75 Index +Chaos,False,20,6254.646,TIMEFRAME_M2,0,899250913,0,TIMEFRAME_M1,100,100,8,6254.472,0.5,6254.472,False,2024-05-01 20:00:00.000000,192999506,Volatility 10 Index +Chaos,False,20,2185.591,TIMEFRAME_M2,0,863278517,0,TIMEFRAME_M1,100,100,8,2185.591,0.5,2185.439,False,2024-05-01 20:00:00.000000,130503742,Volatility 25 Index +Chaos,False,20,1656.13,TIMEFRAME_M2,0,820517786,0,TIMEFRAME_M1,100,100,8,1656.13,0.5,1655.68,False,2024-05-01 20:00:00.000000,188068169,Volatility 100 Index +Chaos,False,20,1656.13,TIMEFRAME_M2,0,879739555,0,TIMEFRAME_M1,100,100,8,1656.13,0.5,1655.68,False,2024-05-01 20:00:00.000000,111597550,Volatility 100 Index +Chaos,False,20,2185.591,TIMEFRAME_M2,0,895888927,0,TIMEFRAME_M1,100,100,8,2185.591,0.5,2185.439,False,2024-05-01 20:00:00.000000,147283128,Volatility 25 Index +Chaos,False,20,208681.37,TIMEFRAME_M2,0,849703279,0,TIMEFRAME_M1,100,100,8,208681.37,0.001,208629.83,False,2024-05-01 20:00:00.000000,198155528,Volatility 75 Index +Chaos,False,20,6254.646,TIMEFRAME_M2,0,857450318,0,TIMEFRAME_M1,100,100,8,6254.472,0.5,6254.472,False,2024-05-01 20:00:00.000000,116520943,Volatility 10 Index +Chaos,False,20,6254.884,TIMEFRAME_M2,0,878367571,0,TIMEFRAME_M1,100,100,8,6254.884,0.5,6254.71,False,2024-05-01 21:00:00.000000,104370218,Volatility 10 Index +Chaos,False,20,2185.689,TIMEFRAME_M2,0,891681576,0,TIMEFRAME_M1,100,100,8,2185.537,0.5,2185.537,False,2024-05-01 21:00:00.000000,160283629,Volatility 25 Index +Chaos,False,20,206845.33,TIMEFRAME_M2,0,878880656,0,TIMEFRAME_M1,100,100,8,206845.33,0.001,206793.79,False,2024-05-01 21:00:00.000000,157239642,Volatility 75 Index +Chaos,False,20,1659.26,TIMEFRAME_M2,0,843249470,0,TIMEFRAME_M1,100,100,8,1659.26,0.5,1658.81,False,2024-05-01 21:00:00.000000,195888645,Volatility 100 Index +Chaos,False,20,1659.26,TIMEFRAME_M2,0,837798992,0,TIMEFRAME_M1,100,100,8,1659.26,0.5,1658.81,False,2024-05-01 21:00:00.000000,183262809,Volatility 100 Index +Chaos,False,20,2185.689,TIMEFRAME_M2,0,873559425,0,TIMEFRAME_M1,100,100,8,2185.689,0.5,2185.537,False,2024-05-01 21:00:00.000000,114878546,Volatility 25 Index +Chaos,False,20,206845.33,TIMEFRAME_M2,0,803373551,0,TIMEFRAME_M1,100,100,8,206845.33,0.001,206793.79,False,2024-05-01 21:00:00.000000,140922469,Volatility 75 Index +Chaos,False,20,6254.884,TIMEFRAME_M2,0,893174131,0,TIMEFRAME_M1,100,100,8,6254.71,0.5,6254.71,False,2024-05-01 22:00:00.000000,198319737,Volatility 10 Index +Chaos,False,20,6251.351,TIMEFRAME_M2,0,807893174,0,TIMEFRAME_M1,100,100,8,6251.351,0.5,6251.177,False,2024-05-01 22:00:00.000000,135875687,Volatility 10 Index +Chaos,False,20,2189.993,TIMEFRAME_M2,0,829178053,0,TIMEFRAME_M1,100,100,8,2189.993,0.5,2189.841,False,2024-05-01 22:00:00.000000,151482337,Volatility 25 Index +Chaos,False,20,206447.56,TIMEFRAME_M2,0,876904701,0,TIMEFRAME_M1,100,100,8,206396.02,0.001,206396.02,False,2024-05-01 22:00:00.000000,157587894,Volatility 75 Index +Chaos,False,20,1626.37,TIMEFRAME_M2,0,843626687,0,TIMEFRAME_M1,100,100,8,1625.92,0.5,1625.92,False,2024-05-01 22:00:00.000000,195164579,Volatility 100 Index +Chaos,False,20,206447.56,TIMEFRAME_M2,0,879242952,0,TIMEFRAME_M1,100,100,8,206396.02,0.001,206396.02,False,2024-05-01 22:00:00.000000,156081780,Volatility 75 Index +Chaos,False,20,1626.37,TIMEFRAME_M2,0,860168201,0,TIMEFRAME_M1,100,100,8,1625.92,0.5,1625.92,False,2024-05-01 22:00:00.000000,149723319,Volatility 100 Index +Chaos,False,20,6251.351,TIMEFRAME_M2,0,875897222,0,TIMEFRAME_M1,100,100,8,6251.351,0.5,6251.177,False,2024-05-01 22:00:00.000000,181975920,Volatility 10 Index +Chaos,False,20,2189.993,TIMEFRAME_M2,0,843022095,0,TIMEFRAME_M1,100,100,8,2189.841,0.5,2189.841,False,2024-05-01 22:00:00.000000,176917055,Volatility 25 Index +Chaos,False,20,1635.15,TIMEFRAME_M2,0,839471806,0,TIMEFRAME_M1,100,100,8,1635.15,0.5,1634.7,False,2024-05-01 23:00:00.000000,139746036,Volatility 100 Index +Chaos,False,20,205355.18,TIMEFRAME_M2,0,899076458,0,TIMEFRAME_M1,100,100,8,205303.64,0.001,205303.64,False,2024-05-01 23:00:00.000000,194980537,Volatility 75 Index +Chaos,False,20,2183.635,TIMEFRAME_M2,0,848731357,0,TIMEFRAME_M1,100,100,8,2183.483,0.5,2183.483,False,2024-05-01 23:00:00.000000,199898987,Volatility 25 Index +Chaos,False,20,6254.688,TIMEFRAME_M2,0,896618061,0,TIMEFRAME_M1,100,100,8,6254.514,0.5,6254.514,False,2024-05-01 23:00:00.000000,157000097,Volatility 10 Index +Chaos,False,20,6254.688,TIMEFRAME_M2,0,884931917,0,TIMEFRAME_M1,100,100,8,6254.688,0.5,6254.514,False,2024-05-01 23:00:00.000000,137085470,Volatility 10 Index +Chaos,False,20,1635.15,TIMEFRAME_M2,0,878294418,0,TIMEFRAME_M1,100,100,8,1635.15,0.5,1634.7,False,2024-05-01 23:00:00.000000,130821199,Volatility 100 Index +Chaos,False,20,205355.18,TIMEFRAME_M2,0,822383277,0,TIMEFRAME_M1,100,100,8,205355.18,0.001,205303.64,False,2024-05-01 23:00:00.000000,107811481,Volatility 75 Index +Chaos,False,20,2183.635,TIMEFRAME_M2,0,886310952,0,TIMEFRAME_M1,100,100,8,2183.635,0.5,2183.483,False,2024-05-01 23:00:00.000000,137929296,Volatility 25 Index