mirror of
https://github.com/Ichinga-Samuel/aiomql.git
synced 2026-08-20 23:38:10 +00:00
testdata
This commit is contained in:
@@ -5,4 +5,4 @@ from .test_strategy import TestStrategy
|
|||||||
from .event_manager import EventManager
|
from .event_manager import EventManager
|
||||||
from .strategy_tester import StrategyTester, SingleStrategyTester
|
from .strategy_tester import StrategyTester, SingleStrategyTester
|
||||||
from .test_account import TestAccount
|
from .test_account import TestAccount
|
||||||
from .types import TradingData, PositionsManager, OrdersManager
|
from .types import PositionsManager, OrdersManager, DealsManager
|
||||||
|
|||||||
@@ -21,37 +21,38 @@ from ...utils import round_down, round_up, error_handler, error_handler_sync, as
|
|||||||
|
|
||||||
from .get_data import Data, GetData
|
from .get_data import Data, GetData
|
||||||
from .test_account import TestAccount
|
from .test_account import TestAccount
|
||||||
from .types import PositionsManager, OrdersManager
|
from .types import PositionsManager, OrdersManager, DealsManager
|
||||||
|
|
||||||
tz = pytz.timezone('Etc/UTC')
|
tz = pytz.timezone('Etc/UTC')
|
||||||
Cursor = namedtuple('Cursor', ['index', 'time'])
|
Cursor = namedtuple('Cursor', ['index', 'time'])
|
||||||
|
|
||||||
|
|
||||||
class TestData:
|
class TestData:
|
||||||
history_orders: DataFrame
|
mt5: MetaTrader = MetaTrader()
|
||||||
history_deals: DataFrame
|
span: range
|
||||||
|
range: range
|
||||||
|
cursor: Cursor
|
||||||
|
iter: zip_longest
|
||||||
|
|
||||||
def __init__(self, data: Data = None, speed: int = 1, start: float | datetime = 0, end: float | datetime = 0):
|
def __init__(self, data: Data = None, speed: int = 1, start: float | datetime = 0, end: float | datetime = 0):
|
||||||
self._data = data or Data()
|
self._data = data or Data()
|
||||||
self._account: TestAccount = TestAccount(**self._data.account)
|
self._account: TestAccount = TestAccount(**self._data.account)
|
||||||
|
self.positions: PositionsManager = PositionsManager()
|
||||||
|
self.orders: OrdersManager = OrdersManager()
|
||||||
|
self.deals: DealsManager = DealsManager()
|
||||||
|
self.margins: dict[int, float] = {}
|
||||||
|
self.config = Config(test_data=self)
|
||||||
|
self.set_up(start=start, end=end, speed=speed)
|
||||||
|
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):
|
||||||
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
|
||||||
span_end = (int(end.timestamp()) if isinstance(end, datetime) else int(end)) or self._data.span.stop
|
span_end = (int(end.timestamp()) if isinstance(end, datetime) else int(end)) or self._data.span.stop
|
||||||
self.span: range = range(span_start, span_end, speed)
|
self.span = range(span_start, span_end, speed)
|
||||||
self.range: range = range(0, span_end - span_start, speed)
|
self.range = range(0, span_end - span_start, speed)
|
||||||
self.orders: dict[str, dict[int, TradeOrder]] = {}
|
|
||||||
self.deals: dict[str, dict[int, TradeDeal]] = {}
|
|
||||||
self.open_orders: dict[int, TradeOrder] = {}
|
|
||||||
self.positions: dict[str, dict[int, TradePosition]] = {}
|
|
||||||
self.open_positions: dict[int, TradePosition] = {}
|
|
||||||
self.history_orders = self._data.history_orders
|
|
||||||
self.history_deals = self._data.history_deals
|
|
||||||
self.margins: dict[int, float] = {}
|
|
||||||
self.mt5 = MetaTrader()
|
|
||||||
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)
|
self.cursor: Cursor = Cursor(index=self.range.start, time=self.span.start)
|
||||||
self.config = Config(test_data=self)
|
|
||||||
self._data.name = self._data.name or f"{datetime.fromtimestamp(span_start):%d-%m-%y}_{datetime.fromtimestamp(span_end):%d-%m-%y}"
|
|
||||||
self.fh = open(f'{self.config.test_data_dir}/data.json', 'a')
|
|
||||||
|
|
||||||
def __next__(self) -> Cursor:
|
def __next__(self) -> Cursor:
|
||||||
index, time = next(self.iter)
|
index, time = next(self.iter)
|
||||||
@@ -95,11 +96,11 @@ class TestData:
|
|||||||
return [(c, t) for c, t in zip(df.columns, df.dtypes)]
|
return [(c, t) for c, t in zip(df.columns, df.dtypes)]
|
||||||
|
|
||||||
async def tracker(self):
|
async def tracker(self):
|
||||||
pos_tasks = [self.check_position(ticket) for ticket in self.open_positions]
|
pos_tasks = [self.check_position(ticket) for ticket in self.positions.open_items]
|
||||||
await asyncio.gather(*pos_tasks)
|
await asyncio.gather(*pos_tasks)
|
||||||
order_tasks = [self.check_order(ticket) for ticket in self.open_orders]
|
order_tasks = [self.check_order(ticket) for ticket in self.orders.open_items]
|
||||||
await asyncio.gather(*order_tasks)
|
await asyncio.gather(*order_tasks)
|
||||||
profit = sum(pos.profit for pos in self.open_positions.values())
|
profit = sum(pos.profit for pos in self.positions.open_positions)
|
||||||
self.update_account(profit=profit)
|
self.update_account(profit=profit)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
@@ -129,9 +130,9 @@ class TestData:
|
|||||||
|
|
||||||
@error_handler
|
@error_handler
|
||||||
async def check_order(self, ticket: int):
|
async def check_order(self, ticket: int):
|
||||||
order = self.open_orders[ticket]
|
order = self.orders[ticket]
|
||||||
order_type, symbol = order.type, order.symbol
|
order_type, symbol = order.type, order.symbol
|
||||||
tick = self.prices[symbol].loc[self.cursor.time]
|
tick = await self.get_price_tick(symbol, self.cursor.time)
|
||||||
tp, sl = order.tp, order.sl
|
tp, sl = order.tp, order.sl
|
||||||
|
|
||||||
match order_type:
|
match order_type:
|
||||||
@@ -147,23 +148,18 @@ class TestData:
|
|||||||
|
|
||||||
@error_handler
|
@error_handler
|
||||||
async def check_position(self, ticket: int, use_terminal=True):
|
async def check_position(self, ticket: int, use_terminal=True):
|
||||||
pos = self.open_positions[ticket]
|
pos = self.positions[ticket]
|
||||||
order_type, symbol, volume, price_open, prev_profit = pos.type, pos.symbol, pos.volume, pos.price_open, pos.profit
|
order_type, symbol, volume, price_open, prev_profit = pos.type, pos.symbol, pos.volume, pos.price_open, pos.profit
|
||||||
tick = self.prices[symbol].loc[self.cursor.time]
|
tick = await self.get_price_tick(symbol, self.cursor.time)
|
||||||
price_current = tick.bid if order_type == OrderType.BUY else tick.ask
|
price_current = tick.bid if order_type == OrderType.BUY else tick.ask
|
||||||
profit = await self.order_calc_profit(order_type, symbol, volume, price_open, price_current, use_terminal)
|
profit = await self.order_calc_profit(order_type, symbol, volume, price_open, price_current, use_terminal)
|
||||||
pos = pos._asdict()
|
self.positions.update(ticket=pos.ticket, profit=profit, price_current=price_current, time_update=self.cursor.time)
|
||||||
pos.update(profit=profit, price_current=price_current, time_update=self.cursor.time)
|
|
||||||
pos = TradePosition(pos)
|
|
||||||
self.open_positions[ticket] = pos
|
|
||||||
self.positions[symbol][ticket] = pos
|
|
||||||
|
|
||||||
def close_position(self, ticket: int):
|
def close_position(self, ticket: int):
|
||||||
position = self.open_positions.pop(ticket)
|
position = self.positions.pop(ticket)
|
||||||
margin = self.margins.pop(position.ticket)
|
margin = self.margins.pop(position.ticket)
|
||||||
order = self.open_orders.pop(ticket)
|
del self.orders[ticket]
|
||||||
order = order._asdict()
|
self.orders.update(ticket=ticket, time_done=self.cursor.time)
|
||||||
order.update(time_done=self.cursor.time)
|
|
||||||
self.update_account(gain=position.profit, margin=-margin) # ToDo: Create a deal object here? modify update account
|
self.update_account(gain=position.profit, margin=-margin) # ToDo: Create a deal object here? modify update account
|
||||||
|
|
||||||
def modify_stops(self, ticket: int, sl: int = None, tp: int = None):
|
def modify_stops(self, ticket: int, sl: int = None, tp: int = None):
|
||||||
@@ -334,13 +330,14 @@ class TestData:
|
|||||||
sym = await self.get_symbol_info(symbol)
|
sym = await self.get_symbol_info(symbol)
|
||||||
tsl = sym.trade_stops_level + sym.spread
|
tsl = sym.trade_stops_level + sym.spread
|
||||||
sl, tp = request.get('sl', 0), request.get('tp', 0)
|
sl, tp = request.get('sl', 0), request.get('tp', 0)
|
||||||
|
current_price = price
|
||||||
if tp or sl:
|
if tp or sl:
|
||||||
current_price = price
|
|
||||||
if action == TradeAction.SLTP:
|
if action == TradeAction.SLTP:
|
||||||
pos = self.open_positions.get(request.get('position')) # ToDo: use positions manager
|
pos = self.positions.get(request.get('position')) # ToDo: use positions manager
|
||||||
sym = pos.symbol
|
sym = pos.symbol
|
||||||
current_price = await self.get_price_tick(sym, self.cursor.time)
|
current_tick = await self.get_price_tick(sym, self.cursor.time)
|
||||||
|
current_price = current_tick.bid if pos.type == OrderType.BUY else current_tick.ask
|
||||||
|
|
||||||
min_sl = min(sl, tp)
|
min_sl = min(sl, tp)
|
||||||
dsl = abs(current_price - min_sl) / sym.point
|
dsl = abs(current_price - min_sl) / sym.point
|
||||||
if int(dsl) < int(tsl):
|
if int(dsl) < int(tsl):
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
from MetaTrader5 import TradePosition, TradeOrder, TradeDeal
|
from MetaTrader5 import TradePosition, TradeOrder, TradeDeal
|
||||||
|
|
||||||
|
from aiomql.utils import logger
|
||||||
|
|
||||||
|
|
||||||
class TradingData:
|
class TradingData:
|
||||||
_data: dict
|
_data: dict[int, TradePosition | TradeOrder | TradeDeal]
|
||||||
_open_items: set[int]
|
_open_items: set[int]
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
@@ -13,8 +15,11 @@ class TradingData:
|
|||||||
self._data[key] = value
|
self._data[key] = value
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
del self._data[key]
|
try:
|
||||||
self._open_items.discard(key)
|
self._open_items.discard(key)
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
logger.warning(f'{key} not found')
|
||||||
|
|
||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
return item in self._open_items
|
return item in self._open_items
|
||||||
@@ -30,16 +35,50 @@ class TradingData:
|
|||||||
|
|
||||||
def pop(self, key, default=None):
|
def pop(self, key, default=None):
|
||||||
self._open_items.discard(key)
|
self._open_items.discard(key)
|
||||||
return self._data.pop(key, default)
|
return self._data.get(key, default)
|
||||||
|
|
||||||
|
def update(self, *, ticket: int, **kwargs):
|
||||||
|
try:
|
||||||
|
res = self[ticket]
|
||||||
|
klass = type(res)
|
||||||
|
res = res._asdict()
|
||||||
|
res.update(**kwargs)
|
||||||
|
res = klass(res.get(v) for v in klass.__match_args__)
|
||||||
|
self[res.ticket] = res
|
||||||
|
return res
|
||||||
|
except KeyError:
|
||||||
|
logger.error(f"Update Operation Failed: Could Not Find Ticket")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def open_items(self) -> set[int]:
|
||||||
|
return self._open_items
|
||||||
|
|
||||||
|
|
||||||
class PositionsManager(TradingData):
|
class PositionsManager(TradingData):
|
||||||
|
_data: dict[int, TradePosition]
|
||||||
|
|
||||||
def __init__(self, open_items: set[int] = None, data: dict = None):
|
def __init__(self, open_items: set[int] = None, data: dict = None):
|
||||||
self._open_items = open_items or set()
|
self._open_items = open_items or set()
|
||||||
self._data = data or {}
|
self._data = data or {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def open_positions(self) -> tuple[TradePosition, ...]:
|
||||||
|
return tuple(position for position in self._data.values() if position.ticket in self.open_items)
|
||||||
|
|
||||||
|
|
||||||
class OrdersManager(TradingData):
|
class OrdersManager(TradingData):
|
||||||
|
_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
|
||||||
|
def active_orders(self) -> tuple[TradeOrder, ...]:
|
||||||
|
return tuple(order for order in self._data.values() if order.ticket in self.open_items)
|
||||||
|
|
||||||
|
|
||||||
|
class DealsManager(TradingData):
|
||||||
def __init__(self, open_items: set[int] = None, data: dict = None):
|
def __init__(self, open_items: set[int] = None, data: dict = None):
|
||||||
self._open_items = open_items or set()
|
self._open_items = open_items or set()
|
||||||
self._data = data or {}
|
self._data = data or {}
|
||||||
|
|||||||
Reference in New Issue
Block a user