mirror of
https://github.com/Ichinga-Samuel/aiomql.git
synced 2026-08-24 01:08:07 +00:00
testdata
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
{"a": 5}{"af": 5}
|
||||||
@@ -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 @@
|
|||||||
from .backtester import *
|
# from .backtester import *
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+19
-23
@@ -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__}()"
|
||||||
@@ -71,14 +71,11 @@ class TestData:
|
|||||||
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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,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:
|
||||||
|
|||||||
@@ -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 {}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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})"
|
||||||
|
|||||||
Reference in New Issue
Block a user