This commit is contained in:
Ichinga Samuel
2024-09-26 06:07:04 +01:00
parent 693c475b23
commit a96cf6a4f5
16 changed files with 101 additions and 120 deletions
+1
View File
@@ -0,0 +1 @@
{"a": 5}{"af": 5}
+1 -1
View File
@@ -18,4 +18,4 @@ from .terminal import Terminal
from .sessions import Session, Sessions from .sessions import Session, Sessions
from .utils import dict_to_string, round_off, backoff_decorator, error_handler, error_handler_sync, round_up, round_down from .utils import dict_to_string, round_off, backoff_decorator, error_handler, error_handler_sync, round_up, round_down
from .lib import * from .lib import *
from .contrib import * # from .contrib import *
+1 -1
View File
@@ -1 +1 @@
from .backtester import * # from .backtester import *
+1 -1
View File
@@ -1,5 +1,5 @@
from .meta_tester import MetaTester from .meta_tester import MetaTester
from .test_data import TestData from .backtest_engine import BackTestEngine
from .get_data import GetData from .get_data import GetData
from .test_strategy import TestStrategy from .test_strategy import TestStrategy
from .event_manager import EventManager from .event_manager import EventManager
@@ -1,16 +1,14 @@
import asyncio import asyncio
from collections import namedtuple
from datetime import datetime from datetime import datetime
from typing import Literal from typing import Literal
from itertools import zip_longest from itertools import zip_longest
import random import random
import json
from functools import cached_property from functools import cached_property
from logging import getLogger
import pandas as pd import pandas as pd
import pytz import pytz
import numpy as np import numpy as np
from debugpy.common.timestamp import current
from pandas import DataFrame from pandas import DataFrame
from MetaTrader5 import (Tick, SymbolInfo, AccountInfo, TradeOrder, TradePosition, TradeDeal, from MetaTrader5 import (Tick, SymbolInfo, AccountInfo, TradeOrder, TradePosition, TradeDeal,
TradeRequest, OrderCheckResult, OrderSendResult, TerminalInfo) TradeRequest, OrderCheckResult, OrderSendResult, TerminalInfo)
@@ -18,18 +16,18 @@ from MetaTrader5 import (Tick, SymbolInfo, AccountInfo, TradeOrder, TradePositio
from ...core.meta_trader import MetaTrader from ...core.meta_trader import MetaTrader
from ...core.constants import TimeFrame, CopyTicks, OrderType, TradeAction, AccountStopOutMode, PositionReason from ...core.constants import TimeFrame, CopyTicks, OrderType, TradeAction, AccountStopOutMode, PositionReason
from ...core.config import Config from ...core.config import Config
from ...lib.strategies.finger_trap import logger
from ...utils import round_down, round_up, error_handler, error_handler_sync, async_cache from ...utils import round_down, round_up, error_handler, error_handler_sync, async_cache
from .get_data import Data, GetData from .get_data import Data, GetData, Cursor
from .test_account import TestAccount from .test_account import TestAccount
from .types import PositionsManager, OrdersManager, DealsManager from .types import PositionsManager, OrdersManager, DealsManager
tz = pytz.timezone('Etc/UTC') tz = pytz.timezone('Etc/UTC')
Cursor = namedtuple('Cursor', ['index', 'time'])
logger = getLogger(__name__)
class TestData: class BackTestEngine:
mt5: MetaTrader = MetaTrader() mt5: MetaTrader = MetaTrader()
span: range span: range
range: range range: range
@@ -45,8 +43,8 @@ class TestData:
self.margins: dict[int, float] = {} self.margins: dict[int, float] = {}
self.config = Config(test_data=self) self.config = Config(test_data=self)
self.set_up(start=start, end=end, speed=speed) self.set_up(start=start, end=end, speed=speed)
self.cursor: Cursor = self._data.cursor or Cursor(index=self.range.start, time=self.span.start)
self._data.name = self._data.name or f"{datetime.fromtimestamp(self.span[0]):%d-%m-%y}_{datetime.fromtimestamp(self.span[-1]):%d-%m-%y}" self._data.name = self._data.name or f"{datetime.fromtimestamp(self.span[0]):%d-%m-%y}_{datetime.fromtimestamp(self.span[-1]):%d-%m-%y}"
self.fh = open(f'{self.config.test_data_dir}/data.json', 'a')
def set_up(self, start: float | datetime = 0, end: float | datetime = 0, speed: int = 1): def set_up(self, start: float | datetime = 0, end: float | datetime = 0, speed: int = 1):
span_start = (int(start.timestamp()) if isinstance(start, datetime) else int(start)) or self._data.span.start span_start = (int(start.timestamp()) if isinstance(start, datetime) else int(start)) or self._data.span.start
@@ -54,12 +52,14 @@ class TestData:
self.span = range(span_start, span_end, speed) self.span = range(span_start, span_end, speed)
self.range = range(0, span_end - span_start, speed) self.range = range(0, span_end - span_start, speed)
self.iter = zip_longest(self.range, self.span) self.iter = zip_longest(self.range, self.span)
self.cursor: Cursor = Cursor(index=self.range.start, time=self.span.start)
def __next__(self) -> Cursor: def __next__(self) -> Cursor:
index, time = next(self.iter) try:
self.cursor = Cursor(index=index, time=time) index, time = next(self.iter)
return self.cursor self.cursor = Cursor(index=index, time=time)
return self.cursor
except StopIteration:
logger.warning('End of time')
def __repr__(self): def __repr__(self):
return f"{self.__class__.__name__}()" return f"{self.__class__.__name__}()"
@@ -70,15 +70,12 @@ class TestData:
@property @property
def data(self): def data(self):
return self._data return self._data
def to_json(self, data):
json.dump(data, self.fh)
def reset(self): def reset(self):
self.iter = zip_longest(self.range, self.span) self.iter = zip_longest(self.range, self.span)
self.cursor = Cursor(index=self.range.start, time=self.span.start) self.cursor = Cursor(index=self.range.start, time=self.span.start)
def go_to(self, time: datetime | int): def go_to(self, *, time: datetime | int):
time = int(time.timestamp()) if isinstance(time, datetime) else int(time) time = int(time.timestamp()) if isinstance(time, datetime) else int(time)
steps = time - self.cursor.time steps = time - self.cursor.time
@@ -92,7 +89,7 @@ class TestData:
self.iter = zip_longest(range_, span) self.iter = zip_longest(range_, span)
self.cursor = Cursor(index=range_.start, time=span.start) self.cursor = Cursor(index=range_.start, time=span.start)
def fast_forward(self, steps: int): def fast_forward(self, *, steps: int):
for _ in range(steps): for _ in range(steps):
self.next() self.next()
@@ -108,7 +105,6 @@ class TestData:
self.update_account(profit=profit) self.update_account(profit=profit)
def save(self): def save(self):
self.fh.close()
try: try:
if len(self.orders) or len(self.deals): if len(self.orders) or len(self.deals):
for symbol in self.orders: for symbol in self.orders:
@@ -243,7 +239,7 @@ class TestData:
osr = {'retcode': 10013, 'comment': 'Invalid request', osr = {'retcode': 10013, 'comment': 'Invalid request',
'request': TradeRequest(request.get(k, (0 if k != 'comment' else '')) for k in 'request': TradeRequest(request.get(k, (0 if k != 'comment' else '')) for k in
TradeRequest.__match_args__)} TradeRequest.__match_args__)}
trade_order = {'ticket': order_ticket, **{k: v for k, v in request.items() if k in TradeOrder.__match_args__}} trade_order = {k: v for k, v in request.items() if k in TradeOrder.__match_args__}
current_tick = await self.get_price_tick(request.get('symbol'), self.cursor.time) current_tick = await self.get_price_tick(request.get('symbol'), self.cursor.time)
if current_tick is None: if current_tick is None:
osr['comment'] = 'Market is closed' osr['comment'] = 'Market is closed'
@@ -297,7 +293,7 @@ class TestData:
return OrderSendResult((osr.get(k, 0) for k in OrderSendResult.__match_args__)) return OrderSendResult((osr.get(k, 0) for k in OrderSendResult.__match_args__))
# self.to_json(osr) # ToDo: remove later # self.to_json(osr) # ToDo: remove later
# return OrderSendResult(osr) # return OrderSendResult(osr)
price = current_tick.ask if order_type == OrderType.BUY else current.tick.bid price = current_tick.ask if order_type == OrderType.BUY else current_tick.bid
# ToDo: Cross check this values with actual values. # ToDo: Cross check this values with actual values.
position = {'comment': 'Position Opened', 'ticket': order_ticket, 'symbol': symbol, 'volume': volume, position = {'comment': 'Position Opened', 'ticket': order_ticket, 'symbol': symbol, 'volume': volume,
'price_open': price, 'price_current': price, 'type': order_type, 'profit': 0, 'reason': PositionReason.EXPERT, 'price_open': price, 'price_current': price, 'type': order_type, 'profit': 0, 'reason': PositionReason.EXPERT,
@@ -315,9 +311,9 @@ class TestData:
self.positions[order.ticket] = pos self.positions[order.ticket] = pos
self.orders[order.ticket] = order self.orders[order.ticket] = order
osr.update({'order': order_ticket, 'price': price, 'volume': volume, 'bid': tick.bid, osr.update({'order': order_ticket, 'price': price, 'volume': volume, 'bid': current_tick.bid,
'ask': tick.ask, 'deal': deal_ticket}) 'ask': current_tick.ask, 'deal': deal_ticket})
margin = await self.order_calc_margin(action, symbol, volume, price, use_) margin = await self.order_calc_margin(action, symbol, volume, price)
self.margins[order_ticket] = margin self.margins[order_ticket] = margin
self.update_account(margin=margin) self.update_account(margin=margin)
self.to_json(osr) # ToDo: remove later self.to_json(osr) # ToDo: remove later
+17 -38
View File
@@ -1,42 +1,21 @@
from functools import cached_property from turtledemo.penrose import start
class Data:
class Tre:
def __init__(self): def __init__(self):
self.a = 1 self.start = 0
self.b = 2 self.end = 3
self.span = iter(range(self.start, self.end))
class TData: def __next__(self):
_a: int try:
_b: int next(self.span)
_dat: dict except StopIteration:
print('End of range')
def __init__(self):
self._data = Data()
def __getattr__(self, item):
if val := self.__annotations__.get(item):
return getattr(self._data, item, val())
@property
def a(self):
return self._a
@a.setter
def a(self, val):
self._a = val
@property
def dat(self):
return self._dat
@dat.setter
def dat(self, key, val):
self._dat |= val
@dat.deleter
def dat(self):
self._dat = {}
f = TData() r = Tre()
# f.data = {'a': 1} next(r)
print(f.a) next(r)
next(r)
next(r)
View File
+10 -10
View File
@@ -5,6 +5,7 @@ import lzma
from datetime import datetime from datetime import datetime
from logging import getLogger from logging import getLogger
from typing import Sequence, ClassVar from typing import Sequence, ClassVar
from collections import namedtuple
import pytz import pytz
import pandas as pd import pandas as pd
@@ -21,11 +22,10 @@ logger = getLogger(__name__)
from MetaTrader5 import TradePosition, TradeOrder, TradeDeal from MetaTrader5 import TradePosition, TradeOrder, TradeDeal
tof = list(TradeOrder.__match_args__) tof = list(TradeOrder.__match_args__)
tof.append('symbol')
tpf = list(TradePosition.__match_args__) tpf = list(TradePosition.__match_args__)
tpf.append('symbol')
tdf = list(TradeDeal.__match_args__) tdf = list(TradeDeal.__match_args__)
tdf.append('symbol')
Cursor = namedtuple('Cursor', ['index', 'time'])
@dataclass @dataclass
@@ -40,12 +40,12 @@ class Data:
rates: dict[str, dict[str, DataFrame]] = field(default_factory=dict) rates: dict[str, dict[str, DataFrame]] = field(default_factory=dict)
span: range = range(0) span: range = range(0)
range: range = range(0) range: range = range(0)
history_orders: DataFrame = field(default_factory=lambda: DataFrame([], columns=tof)) orders: dict[int, dict] = field(default_factory=lambda: {})
history_deals: DataFrame = field(default_factory=lambda: DataFrame([], columns=tdf)) deals: dict[int, dict] = field(default_factory=lambda: {})
positions: dict[str, DataFrame] = field(default_factory=dict) positions: dict[int, dict] = field(default_factory=lambda: {})
orders: dict[str, DataFrame] = field(default_factory=dict) active_orders: tuple[int, ...] = field(default_factory=lambda: ())
open_positions: tuple[int, ...] = field(default_factory=lambda: ())
_fields: list[ClassVar[str]] = field(default_factory=list) cursor: Cursor = None
def __str__(self): def __str__(self):
return f""" return f"""
@@ -68,7 +68,7 @@ class Data:
@property @property
def fields(self): def fields(self):
return self._fields or [name for f in fields(self) if (name := f.name) != '_fields'] return [f.name for f in fields(self)]
class GetData: class GetData:
+5 -5
View File
@@ -5,7 +5,7 @@ from numpy import ndarray
from MetaTrader5 import (Tick, SymbolInfo, AccountInfo, TerminalInfo, TradeOrder, TradePosition, TradeDeal, from MetaTrader5 import (Tick, SymbolInfo, AccountInfo, TerminalInfo, TradeOrder, TradePosition, TradeDeal,
OrderCheckResult, OrderSendResult) OrderCheckResult, OrderSendResult)
from .test_data import TestData from .backtest_engine import BackTestEngine
from .get_data import GetData from .get_data import GetData
from ...core.meta_trader import MetaTrader from ...core.meta_trader import MetaTrader
@@ -18,17 +18,17 @@ logger = getLogger(__name__)
class MetaTester(MetaTrader): class MetaTester(MetaTrader):
"""A class for testing trading strategies in the MetaTrader 5 terminal. A subclass of MetaTrader.""" """A class for testing trading strategies in the MetaTrader 5 terminal. A subclass of MetaTrader."""
def __init__(self, test_data: TestData = None): def __init__(self, test_data: BackTestEngine = None):
super().__init__() super().__init__()
if test_data is not None: if test_data is not None:
self.config.test_data = test_data self.config.test_data = test_data
@property @property
def test_data(self) -> TestData | None: def test_data(self) -> BackTestEngine | None:
return self.config.test_data return self.config.test_data
@test_data.setter @test_data.setter
def test_data(self, value: TestData): def test_data(self, value: BackTestEngine):
self.config.test_data = value self.config.test_data = value
async def last_error(self) -> tuple[int, str]: async def last_error(self) -> tuple[int, str]:
@@ -47,7 +47,7 @@ class MetaTester(MetaTrader):
name = f"{self.config.test_data_dir_name}/{test_data_file}" name = f"{self.config.test_data_dir_name}/{test_data_file}"
data = GetData.load_data(name=name, compressed=self.config.compress_test_data) data = GetData.load_data(name=name, compressed=self.config.compress_test_data)
if data is not None: if data is not None:
self.test_data = TestData(data) self.test_data = BackTestEngine(data)
success = True success = True
except Exception as err: except Exception as err:
+14 -18
View File
@@ -1,18 +1,24 @@
from typing import Generic from typing import TypeVar, Generic
from MetaTrader5 import TradePosition, TradeOrder, TradeDeal from MetaTrader5 import TradePosition, TradeOrder, TradeDeal
from aiomql.utils import logger from aiomql.utils import logger
TradeData = TypeVar('TradeData', bound=TradePosition | TradeOrder | TradeDeal)
class TradingData[Generic]:
_data: dict[int, TradePosition | TradeOrder | TradeDeal] class TradingData(Generic[TradeData]):
_data: dict[int, TradeData]
_open_items: set[int] _open_items: set[int]
def __init__(self, open_items: set[int] = None, data: dict = None):
self._data = data or {}
self._open_items = open_items or {trade.ticket for trade in self._data.values()}
def __getitem__(self, item): def __getitem__(self, item):
return self._data[item] return self._data[item]
def __setitem__(self, key, value: TradePosition | TradeOrder | TradeDeal): def __setitem__(self, key, value: TradeData):
self._open_items.add(value.ticket) self._open_items.add(value.ticket)
self._data[key] = value self._data[key] = value
@@ -23,7 +29,7 @@ class TradingData[Generic]:
except KeyError: except KeyError:
logger.warning(f'{key} not found') logger.warning(f'{key} not found')
def __contains__(self, item): def __contains__(self, item: int):
return item in self._open_items return item in self._open_items
def __iter__(self): def __iter__(self):
@@ -32,10 +38,10 @@ class TradingData[Generic]:
def __len__(self): def __len__(self):
return len(self._data) return len(self._data)
def get(self, key, default=None): def get(self, key, default=None) -> TradeData | None:
return self._data.get(key, default) if key in self._open_items else default return self._data.get(key, default) if key in self._open_items else default
def pop(self, key, default=None): def pop(self, key, default=None) -> TradeData | None:
self._open_items.discard(key) self._open_items.discard(key)
return self._data.get(key, default) return self._data.get(key, default)
@@ -59,10 +65,6 @@ class TradingData[Generic]:
class PositionsManager(TradingData): class PositionsManager(TradingData):
_data: dict[int, TradePosition] _data: dict[int, TradePosition]
def __init__(self, open_items: set[int] = None, data: dict = None):
self._open_items = open_items or set()
self._data = data or {}
@property @property
def open_positions(self) -> tuple[TradePosition, ...]: def open_positions(self) -> tuple[TradePosition, ...]:
return tuple(position for position in self._data.values() if position.ticket in self.open_items) return tuple(position for position in self._data.values() if position.ticket in self.open_items)
@@ -71,16 +73,10 @@ class PositionsManager(TradingData):
class OrdersManager(TradingData): class OrdersManager(TradingData):
_data = dict[int, TradeOrder] _data = dict[int, TradeOrder]
def __init__(self, open_items: set[int] = None, data: dict = None):
self._open_items = open_items or set()
self._data = data or {}
@property @property
def active_orders(self) -> tuple[TradeOrder, ...]: def active_orders(self) -> tuple[TradeOrder, ...]:
return tuple(order for order in self._data.values() if order.ticket in self.open_items) return tuple(order for order in self._data.values() if order.ticket in self.open_items)
class DealsManager(TradingData): class DealsManager(TradingData):
def __init__(self, open_items: set[int] = None, data: dict = None): ...
self._open_items = open_items or set()
self._data = data or {}
+2 -2
View File
@@ -4,7 +4,7 @@ from logging import getLogger
from .config import Config from .config import Config
from .meta_trader import MetaTrader from .meta_trader import MetaTrader
from ..contrib.backtester import MetaTester # from ..contrib.backtester import MetaTester
logger = getLogger(__name__) logger = getLogger(__name__)
@@ -22,7 +22,7 @@ class Base:
**kwargs: Set instance attributes with keyword arguments. Only if they are annotated on the class body. **kwargs: Set instance attributes with keyword arguments. Only if they are annotated on the class body.
""" """
self.config = Config() self.config = Config()
self.mt5 = MetaTrader() if self.config.mode == 'live' else MetaTester() self.mt5 = MetaTrader() #if self.config.mode == 'live' else MetaTester()
self.exclude = {'mt5', "config", 'exclude', 'include', 'annotations', 'class_vars', 'dict'} self.exclude = {'mt5', "config", 'exclude', 'include', 'annotations', 'class_vars', 'dict'}
self.include = set() self.include = set()
self.set_attributes(**kwargs) self.set_attributes(**kwargs)
+15 -6
View File
@@ -91,8 +91,8 @@ class MetaTrader(metaclass=BaseMeta):
self.error = Error(*err) self.error = Error(*err)
if self.error.is_connection_error(): if self.error.is_connection_error():
await self.initialize(**self.config.account_info(), path=self.config.path) await self.initialize(path=self.config.path)
await self.login(**self.config.account_info()) await self.login()
res = await asyncio.to_thread(func, *args, **kwargs) res = await asyncio.to_thread(func, *args, **kwargs)
if res is None: if res is None:
@@ -103,7 +103,7 @@ class MetaTrader(metaclass=BaseMeta):
logger.warning(f'{error_msg}:{self.error.description}') logger.warning(f'{error_msg}:{self.error.description}')
return res return res
async def login(self, login: int, password: str, server: str, timeout: int = 60000) -> bool: async def login(self, *, login: int = 0, password: str = '', server: str = '', timeout: int = 60000) -> bool:
""" """
Connects to the MetaTrader terminal using the specified login, password and server. Connects to the MetaTrader terminal using the specified login, password and server.
@@ -116,6 +116,10 @@ class MetaTrader(metaclass=BaseMeta):
Returns: Returns:
bool: True if successful, False otherwise. bool: True if successful, False otherwise.
""" """
acc_details = self.config.account_info()
login = login or acc_details.get('login', 0)
password = password or acc_details.get('password', '')
server = server or acc_details.get('server', '')
return await asyncio.to_thread(self._login, login, password=password, server=server, timeout=timeout) return await asyncio.to_thread(self._login, login, password=password, server=server, timeout=timeout)
async def initialize(self, path: str = "", login: int = 0, password: str = "", server: str = "", async def initialize(self, path: str = "", login: int = 0, password: str = "", server: str = "",
@@ -128,15 +132,20 @@ class MetaTrader(metaclass=BaseMeta):
login (int): The trading account number. login (int): The trading account number.
password (str): The trading account password. password (str): The trading account password.
server (str): The trading server name. server (str): The trading server name.
timeout (int): The timeout for the connection in seconds. timeout (int): The timeout for the connection in milliseconds.
portable (bool): If True, the terminal will be launched in portable mode. portable (bool): If True, the terminal will be launched in portable mode.
Returns: Returns:
bool: True if successful, False otherwise. bool: True if successful, False otherwise.
""" """
path = path or self.config.path
args = (str(path),) if path else () args = (str(path),) if path else ()
kwargs = {key: value for key, value in (('login', login), ('password', password), ('server', server), acc = self.config.account_info()
('timeout', timeout), ('portable', portable)) if value} kwargs = {key: value for key, value in (('login', login or acc.get('login')),
('password', password or acc.get('password')),
('server', server or acc.get('server')),
('timeout', timeout or 60000),
('portable', portable)) if key is not None}
res = await asyncio.to_thread(self._initialize, *args, **kwargs) res = await asyncio.to_thread(self._initialize, *args, **kwargs)
return res return res
+1 -1
View File
@@ -1,3 +1,3 @@
from .finger_trap import FingerTrap from .finger_trap import FingerTrap
from .tracker import Tracker from .tracker import Tracker
from .finger_trap_back_test import FingerTrapTest, FingerTrapSingleTest # from .finger_trap_back_test import FingerTrapTest, FingerTrapSingleTest
+3 -3
View File
@@ -7,7 +7,7 @@ from .core.models import TradePosition, TradeAction
from .core.constants import OrderType from .core.constants import OrderType
from .core.config import Config from .core.config import Config
from .contrib.backtester.meta_tester import MetaTester # from .contrib.backtester.meta_tester import MetaTester
from .order import Order from .order import Order
from .utils import backoff_decorator from .utils import backoff_decorator
@@ -25,7 +25,7 @@ class Positions:
ticket (int): Position ticket. ticket (int): Position ticket.
mt5 (MetaTrader): MetaTrader instance. mt5 (MetaTrader): MetaTrader instance.
""" """
mt5: MetaTrader | MetaTester mt5: MetaTrader #| MetaTester
def __init__(self, *, symbol: str = "", group: str = "", ticket: int = 0): def __init__(self, *, symbol: str = "", group: str = "", ticket: int = 0):
"""Get Open Positions. """Get Open Positions.
@@ -38,7 +38,7 @@ class Positions:
""" """
self.config = Config() self.config = Config()
self.mt5 = MetaTrader() if self.config.mode == 'live' else MetaTester() self.mt5 = MetaTrader() # if self.config.mode == 'live' else MetaTester()
self.symbol = symbol self.symbol = symbol
self.group = group self.group = group
self.ticket = ticket self.ticket = ticket
+9 -9
View File
@@ -6,7 +6,7 @@ from logging import getLogger
from .positions import Positions from .positions import Positions
from .core.config import Config from .core.config import Config
from.contrib.backtester.event_manager import EventManager # from.contrib.backtester.event_manager import EventManager
logger = getLogger(__name__) logger = getLogger(__name__)
@@ -20,13 +20,13 @@ def delta(obj: time) -> timedelta:
return timedelta(hours=obj.hour, minutes=obj.minute, seconds=obj.second, microseconds=obj.microsecond) return timedelta(hours=obj.hour, minutes=obj.minute, seconds=obj.second, microseconds=obj.microsecond)
async def backtest_sleep(secs): # async def backtest_sleep(secs):
"""A custom function to call when the session starts.""" # """A custom function to call when the session starts."""
em = EventManager() # # em = EventManager()
#
async with em.condition: # async with em.condition:
while em.config.test_data.cursor.time < (em.config.test_data.cursor.time + secs): # while em.config.test_data.cursor.time < (em.config.test_data.cursor.time + secs):
await em.condition.wait() # await em.condition.wait()
class Session: class Session:
@@ -213,7 +213,7 @@ class Sessions:
current_session = self.find_next(now) current_session = self.find_next(now)
secs = current_session.until() + 10 secs = current_session.until() + 10
logger.info(f'sleeping for {secs} seconds until next {current_session} session') logger.info(f'sleeping for {secs} seconds until next {current_session} session')
sleep_func = sleep if Config().mode == 'live' else backtest_sleep sleep_func = sleep # if Config().mode == 'live' else backtest_sleep
await sleep_func(secs) await sleep_func(secs)
self.current_session = current_session self.current_session = current_session
await self.current_session.begin() await self.current_session.begin()
+2 -2
View File
@@ -8,7 +8,7 @@ from datetime import time as dtime
from .core.meta_trader import MetaTrader from .core.meta_trader import MetaTrader
from .symbol import Symbol as _Symbol from .symbol import Symbol as _Symbol
from .core import Config from .core import Config
from .contrib.backtester.meta_tester import MetaTester # from .contrib.backtester.meta_tester import MetaTester
from .sessions import Sessions, Session from .sessions import Sessions, Session
Symbol = TypeVar("Symbol", bound=_Symbol) Symbol = TypeVar("Symbol", bound=_Symbol)
@@ -48,7 +48,7 @@ class Strategy(ABC):
self.parameters["name"] = self.name self.parameters["name"] = self.name
self.sessions = sessions or Sessions(Session(start=0, end=dtime(hour=23, minute=59, second=59))) self.sessions = sessions or Sessions(Session(start=0, end=dtime(hour=23, minute=59, second=59)))
self.config = Config() self.config = Config()
self.mt5 = MetaTrader() if self.config.mode == 'live' else MetaTester() self.mt5 = MetaTrader() #if self.config.mode == 'live' else MetaTester()
def __repr__(self): def __repr__(self):
return f"{self.name}({self.symbol!r})" return f"{self.name}({self.symbol!r})"