From 3cf15f578fcd0ab8d7675c1c5350ffc6ebb73c84 Mon Sep 17 00:00:00 2001 From: Ichinga Samuel Date: Mon, 11 Aug 2025 02:43:53 +0100 Subject: [PATCH] 4.0.15dev --- pyproject.toml | 2 +- src/aiomql/contrib/__init__.py | 1 - .../contrib/candle_patterns/fractals.py | 2 +- src/aiomql/contrib/quants/__init__.py | 1 - src/aiomql/contrib/trackers/open_position.py | 24 +++- .../trackers/position_tracking_functions.py | 5 +- src/aiomql/core/base.py | 16 +-- src/aiomql/lib/bot.py | 4 +- src/aiomql/lib/executor.py | 9 +- src/aiomql/lib/history.py | 11 +- src/aiomql/lib/order.py | 13 +- src/aiomql/lib/positions.py | 2 +- src/aiomql/lib/ram.py | 39 +++++- src/aiomql/lib/result.py | 25 +++- src/aiomql/lib/sync/history.py | 130 ++++++++++++++++++ src/aiomql/lib/sync/order.py | 6 + src/aiomql/lib/sync/positions.py | 2 +- src/aiomql/lib/trade_records.py | 3 +- src/aiomql/lib/trader.py | 1 - src/aiomql/utils/__init__.py | 1 + .../{contrib/quants => utils}/change.py | 0 src/pandas_ta/__init__.py | 5 - src/pandas_ta/__main__.py | 7 - src/pandas_ta/_typing.py | 70 ++++------ src/pandas_ta/py.typed | 0 25 files changed, 278 insertions(+), 101 deletions(-) delete mode 100644 src/aiomql/contrib/quants/__init__.py create mode 100644 src/aiomql/lib/sync/history.py rename src/aiomql/{contrib/quants => utils}/change.py (100%) delete mode 100644 src/pandas_ta/__main__.py delete mode 100644 src/pandas_ta/py.typed diff --git a/pyproject.toml b/pyproject.toml index 4a8f15a..93f12fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "aiomql" -version = "4.0.15b" +version = "4.0.15dev" readme = "README.md" requires-python = ">=3.11" classifiers = [ diff --git a/src/aiomql/contrib/__init__.py b/src/aiomql/contrib/__init__.py index 1115a2a..4ca81bc 100644 --- a/src/aiomql/contrib/__init__.py +++ b/src/aiomql/contrib/__init__.py @@ -3,5 +3,4 @@ from .candle_patterns import * from .symbols import * from .utils import * from .traders import * -from .quants import * from .trackers import * diff --git a/src/aiomql/contrib/candle_patterns/fractals.py b/src/aiomql/contrib/candle_patterns/fractals.py index fdfab89..1572449 100644 --- a/src/aiomql/contrib/candle_patterns/fractals.py +++ b/src/aiomql/contrib/candle_patterns/fractals.py @@ -1,6 +1,6 @@ from aiomql import Candle, Candles -from ..quants.change import percentage_difference +from ...utils.change import percentage_difference def is_bullish_fractal(candles: Candles) -> tuple[bool, Candle|None]: diff --git a/src/aiomql/contrib/quants/__init__.py b/src/aiomql/contrib/quants/__init__.py deleted file mode 100644 index 683eefb..0000000 --- a/src/aiomql/contrib/quants/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .change import * diff --git a/src/aiomql/contrib/trackers/open_position.py b/src/aiomql/contrib/trackers/open_position.py index 31e003b..ea500c5 100644 --- a/src/aiomql/contrib/trackers/open_position.py +++ b/src/aiomql/contrib/trackers/open_position.py @@ -6,7 +6,7 @@ from ...lib import Symbol, Positions, Order from ...core.models import TradePosition, TradeAction, OrderSendResult from ...core.constants import OrderType from ...core.config import Config -from ..quants import percentage_increase, percentage_decrease +from ...utils.change import percentage_increase, percentage_decrease from .position_tracker import PositionTracker logger = getLogger() @@ -54,26 +54,38 @@ class OpenPosition: await self.update_position() if not self.is_open: self.remove_from_state() + await self.close_pending_order() except Exception as exe: logger.error("%s: Unable to remove closed position from state", exe) + async def close_pending_order(self) -> tuple[bool, OrderSendResult | None]: + try: + if self.pending_hedge: + res = await Order.cancel_order(order=self.pending_hedge.order, symbol=self.symbol.name) + if res.retcode != 10009: + logger.critical("%s: Unable to cancel pending order", res.comment) + return False, res + self.pending_hedge = None + return True, res + except Exception as exe: + logger.error("%s: Unable to cancel pending order", exe) + return False, None + async def update_position(self) -> bool: - # ToDo: remove pending order pos = await self.positions.get_position_by_ticket(ticket=self.ticket) if pos is not None: self.position = pos self.is_open = True else: self.is_open = False + await self.close_pending_order() return self.is_open async def modify_stops(self, *, sl: float = None, tp: float = None, use_stop_levels=False) -> tuple[bool, OrderSendResult | None]: try: - # todo: add stops tick = await self.symbol.info_tick() - # modify stop_loss if sl is not None and use_stop_levels is True: min_stops_value = (self.symbol.trade_stops_level + self.symbol.spread) * self.symbol.point if self.position.type == OrderType.BUY: @@ -149,8 +161,8 @@ class OpenPosition: logger.error("%s: Error occurred in track method of Open Position for %d:%s", exe, self.symbol.name, self.ticket) - async def profit_to_price(self, profit): - action = OrderType.BUY if self.position.type == 0 else OrderType.SELL + async def profit_to_price(self, *, profit): + action = self.position.type volume = self.position.volume price_open = self.position.price_open price_close = percentage_increase(price_open, 50) if action == 0 else percentage_decrease(price_open, 50) diff --git a/src/aiomql/contrib/trackers/position_tracking_functions.py b/src/aiomql/contrib/trackers/position_tracking_functions.py index 16032ca..0dbeba8 100644 --- a/src/aiomql/contrib/trackers/position_tracking_functions.py +++ b/src/aiomql/contrib/trackers/position_tracking_functions.py @@ -2,7 +2,7 @@ from logging import getLogger from .open_position import OpenPosition -from ..quants import extend_interval_by_percentage, get_percentage_position, percentage_position +from ...utils.change import extend_interval_by_percentage, get_percentage_position, percentage_position logger = getLogger(__name__) @@ -56,7 +56,7 @@ async def exit_at_checkpoint(pos: OpenPosition, /, start: float = 80, trail: flo if is_open is False: return position = pos.position - if position.profit > 0 and percentage_position(position.price_open, position.tp, position.price_current) >= start: + if percentage_position(position.price_open, position.tp, position.price_current) >= start: new_checkpoint = get_percentage_position(position.price_open, position.price_current, 100-trail) change_checkpoint = False if position.type.long and new_checkpoint > (pos.checkpoint or position.price_open): @@ -66,6 +66,7 @@ async def exit_at_checkpoint(pos: OpenPosition, /, start: float = 80, trail: flo if change_checkpoint: pos.checkpoint = new_checkpoint pos.use_checkpoint = True + await pos.modify_stops(sl=new_checkpoint, use_stop_levels=True) logger.info("New checkpoint created for %s:%d at %f:%f", position.symbol, position.ticket, new_checkpoint, position.profit) close = False diff --git a/src/aiomql/core/base.py b/src/aiomql/core/base.py index 561f74d..21bd7b9 100644 --- a/src/aiomql/core/base.py +++ b/src/aiomql/core/base.py @@ -119,15 +119,13 @@ class Base: Returns: dict: A dictionary of instance and class attributes """ - try: - _filter = self.exclude.difference(self.include) - return { - key: value - for key, value in (self.class_vars | self.__dict__).items() - if key not in _filter and value is not None - } - except Exception as err: - logger.warning(err) + _filter = self.exclude.difference(self.include) + return { + key: value + for key, value in (self.class_vars | self.__dict__).items() + if key not in _filter and value is not None + } + class _Base(Base): diff --git a/src/aiomql/lib/bot.py b/src/aiomql/lib/bot.py index 48b230f..17349f8 100644 --- a/src/aiomql/lib/bot.py +++ b/src/aiomql/lib/bot.py @@ -86,7 +86,7 @@ class Bot: logger.info("Login Successful") await self.init_strategies() self.add_coroutine(coroutine=self.config.task_queue.run, on_separate_thread=True) - self.add_coroutine(coroutine=self.executor.exit) + self.add_function(function=self.executor.exit) if len(self.executor.strategy_runners) == 0: logger.warning("No strategies were added to the bot. Exiting in one second") @@ -112,7 +112,7 @@ class Bot: logger.info("Login Successful") self.init_strategies_sync() self.add_coroutine(coroutine=self.config.task_queue.run, on_separate_thread=True) - self.add_coroutine(coroutine=self.executor.exit) + self.add_function(function=self.executor.exit) if len(self.executor.strategy_runners) == 0: logger.warning("No strategies were added to the bot. Exiting in one second") diff --git a/src/aiomql/lib/executor.py b/src/aiomql/lib/executor.py index f3145e4..1a4f3a5 100644 --- a/src/aiomql/lib/executor.py +++ b/src/aiomql/lib/executor.py @@ -1,5 +1,6 @@ import asyncio import os +import time from concurrent.futures import ThreadPoolExecutor from signal import signal, SIGINT from typing import Coroutine, Callable @@ -93,16 +94,16 @@ class Executor: def sigint_handle(self, signum, frame): self.config.shutdown = True - async def exit(self): + def exit(self): """Shutdown the executor""" - start = asyncio.get_event_loop().time() + start = time.time() try: while self.config.shutdown is False and self.config.force_shutdown is False: - if self.timeout is not None and self.timeout < (asyncio.get_event_loop().time() - start): + if self.timeout is not None and self.timeout < (time.time() - start): self.config.shutdown = True break timeout = self.timeout or 1 - await asyncio.sleep(timeout) + time.sleep(timeout) for strategy in self.strategy_runners: strategy.running = False self.config.task_queue.cancel() diff --git a/src/aiomql/lib/history.py b/src/aiomql/lib/history.py index 682cc5e..4bb08d9 100644 --- a/src/aiomql/lib/history.py +++ b/src/aiomql/lib/history.py @@ -32,12 +32,11 @@ class History: group: str def __new__(cls, *args, **kwargs): - instance = super().__new__(cls) - if not hasattr(instance.__class__, 'config'): - instance.__class__.config = Config() - if not hasattr(instance.__class__, 'mt5'): - instance.__class__.mt5 = MetaTrader() if instance.config.mode != "backtest" else MetaBackTester() - return instance + if not hasattr(cls, 'config'): + cls.config = Config() + if not hasattr(cls, 'mt5'): + cls.mt5 = MetaTrader() if cls.config.mode != "backtest" else MetaBackTester() + return super().__new__(cls) def __init__( self, *, date_from: datetime | float, date_to: datetime | float, group: str = "", use_utc: bool = float diff --git a/src/aiomql/lib/order.py b/src/aiomql/lib/order.py index cb94bc5..02cde8f 100644 --- a/src/aiomql/lib/order.py +++ b/src/aiomql/lib/order.py @@ -1,10 +1,10 @@ from logging import getLogger from ..core.models import TradeRequest, TradeOrder, OrderCheckResult, OrderSendResult -from ..core.constants import TradeAction, OrderTime, OrderFilling +from ..core.constants import TradeAction, OrderTime, OrderFilling, OrderType from ..core.exceptions import OrderError from ..core.base import _Base -from ..utils import error_handler +from ..utils import error_handler, percentage_decrease, percentage_increase logger = getLogger(__name__) @@ -152,3 +152,12 @@ class Order(_Base, TradeRequest): def request(self) -> dict: """Return the order request as a dictionary.""" return {key: value for key, value in self.dict.items() if key in self.mt5.TradeRequest.__match_args__} + + @classmethod + async def profit_to_price(cls, *, profit: float, order_type: OrderType, volume: float, symbol: str, price_open: float): + price_close = percentage_increase(price_open, 50) if order_type == 0 else percentage_decrease(price_open, 50) + half_profit = await cls.mt5.order_calc_profit(symbol=symbol, action=order_type, volume=volume, + price_open=price_open, price_close=price_close) + rate = profit / half_profit * 50 + rate = percentage_increase(price_open, rate) if order_type == 0 else percentage_decrease(price_open, rate) + return rate diff --git a/src/aiomql/lib/positions.py b/src/aiomql/lib/positions.py index 1bcdf8e..7229151 100644 --- a/src/aiomql/lib/positions.py +++ b/src/aiomql/lib/positions.py @@ -71,7 +71,7 @@ class Positions: kwargs["group"] = group positions = await cls.mt5.positions_get(**kwargs) if positions is not None: - return cls.positions + return tuple(TradePosition(**pos._asdict()) for pos in positions) logger.warning("Failed to get open positions") return () diff --git a/src/aiomql/lib/ram.py b/src/aiomql/lib/ram.py index 9fabd3f..811bdef 100644 --- a/src/aiomql/lib/ram.py +++ b/src/aiomql/lib/ram.py @@ -41,6 +41,20 @@ class RAM: """ [setattr(self, key, value) for key, value in kwargs.items()] + def get_amount_sync(self) -> float: + """Calculate the amount to risk per trade as a percentage of margin_free. + + Returns: + float: Amount to risk per trade + """ + if self.fixed_amount: + return self.fixed_amount + self.account.refresh_sync() + amount = self.account.margin_free * (self.risk / 100) + if self.min_amount and self.max_amount: + return max(self.min_amount, min(self.max_amount, amount)) + return amount + async def get_amount(self) -> float: """Calculate the amount to risk per trade as a percentage of margin_free. @@ -61,8 +75,8 @@ class RAM: Returns: bool: True if the number of losing positions is less than or equal the loss limit """ - positions = await self.positions.get_positions() - loosing = [position for position in positions if position.profit < 0] + positions = await self.account.mt5.positions_get() + loosing = [position for position in positions if position.profit <= 0] return len(loosing) <= self.loss_limit async def check_open_positions(self) -> bool: @@ -71,5 +85,24 @@ class RAM: Returns: bool: True if the number of open positions is less than the open limit """ - positions = await self.positions.get_positions() + positions = await self.account.mt5.positions_get() + return len(positions) <= self.open_limit + + def check_losing_positions_sync(self) -> bool: + """Check if the number of losing positions is less than the loss limit + + Returns: + bool: True if the number of losing positions is less than or equal the loss limit + """ + positions = self.account.mt5._positions_get() + loosing = [position for position in positions if position.profit <= 0] + return len(loosing) <= self.loss_limit + + def check_open_positions_sync(self) -> bool: + """Check if the number of open positions is less than or equal the loss limit. + + Returns: + bool: True if the number of open positions is less than the open limit + """ + positions = self.account.mt5._positions_get() return len(positions) <= self.open_limit diff --git a/src/aiomql/lib/result.py b/src/aiomql/lib/result.py index 0bc30ad..d01a518 100644 --- a/src/aiomql/lib/result.py +++ b/src/aiomql/lib/result.py @@ -61,15 +61,32 @@ class Result: with self.lock: trade_record_mode = trade_record_mode or self.config.trade_record_mode if trade_record_mode == "csv": - await self.to_csv() + self.to_csv() elif trade_record_mode == "json": - await self.to_json() + self.to_json() elif trade_record_mode == "sql": self.to_sql() else: logger.error(f"Invalid trade record mode: {trade_record_mode}") - async def to_csv(self): + def save_sync(self, *, trade_record_mode: Literal["csv", "json"] = None): + """Record trade results as a csv or json file + + Args: + trade_record_mode (Literal['csv'|'json']): Mode of saving trade records + """ + with self.lock: + trade_record_mode = trade_record_mode or self.config.trade_record_mode + if trade_record_mode == "csv": + self.to_csv() + elif trade_record_mode == "json": + self.to_json() + elif trade_record_mode == "sql": + self.to_sql() + else: + logger.error(f"Invalid trade record mode: {trade_record_mode}") + + def to_csv(self): """Record trade results and associated parameters as a csv file""" try: data = self.get_data() @@ -99,7 +116,7 @@ class Result: logger.error("%s: Unable to serialize value", err) return "" - async def to_json(self): + def to_json(self): """Save trades and strategy parameters in a json file""" try: file = self.config.records_dir / f"{self.name}.json" diff --git a/src/aiomql/lib/sync/history.py b/src/aiomql/lib/sync/history.py new file mode 100644 index 0000000..b8c25f5 --- /dev/null +++ b/src/aiomql/lib/sync/history.py @@ -0,0 +1,130 @@ +from typing import ClassVar +from datetime import datetime, UTC +from logging import getLogger + +from ...core.config import Config +from ...core.sync.meta_trader import MetaTrader +from ...core.models import TradeDeal, TradeOrder +from ...core.meta_backtester import MetaBackTester + +logger = getLogger(__name__) + + +class History: + """The history class handles completed trade deals and trade orders in the trading history of an account. + + Attributes: + deals (list[TradeDeal]): Iterable of trade deals + orders (list[TradeOrder]): Iterable of trade orders + total_deals: Total number of deals + total_orders (int): Total number orders + group (str): Filter for selecting history by symbols. + mt5 (MetaTrader): MetaTrader instance + config (Config): Config instance + """ + mt5: ClassVar[MetaTrader | MetaBackTester] + config: ClassVar[Config] + deals: tuple[TradeDeal, ...] + orders: tuple[TradeOrder, ...] + total_deals: int + total_orders: int + group: str + + def __new__(cls, *args, **kwargs): + if not hasattr(cls, 'config'): + cls.config = Config() + if not hasattr(cls, 'mt5'): + cls.mt5 = MetaTrader() if cls.config.mode != "backtest" else MetaBackTester() + return super().__new__(cls) + + def __init__( + self, *, date_from: datetime | float, date_to: datetime | float, group: str = "", use_utc: bool = float + ): + """ + Args: + 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. + + 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. + + use_utc (bool): Convert date_from and date_to to UTC. Default is False. + + group (str): Filter for selecting history by symbols. This defaults to an empty string + """ + date_from = date_from if isinstance(date_from, datetime) else datetime.fromtimestamp(date_from) + date_to = date_to if isinstance(date_to, datetime) else datetime.fromtimestamp(date_to) + self.date_from = date_from.astimezone(UTC) if use_utc else date_from + self.date_to = date_to.astimezone(UTC) if use_utc else date_to + self.group = group + self.deals: tuple[TradeDeal, ...] = () + self.orders: tuple[TradeOrder, ...] = () + self.total_deals: int = 0 + self.total_orders: int = 0 + + def initialize(self): + """Get history deals and orders""" + deals, orders = [self.get_deals(), self.get_orders()] + self.deals = deals if isinstance(deals, tuple) else () + self.orders = orders if isinstance(orders, tuple) else () + self.total_deals = len(self.deals) + self.total_orders = len(self.orders) + + 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 + """ + deals = self.mt5.history_deals_get(date_from=self.date_from, date_to=self.date_to, group=self.group) + if deals is not None: + return tuple(TradeDeal(**deal._asdict()) for deal in deals) + logger.warning(f"Failed to get deals") + return tuple() + + def get_deals_by_ticket(self, *, ticket: int) -> tuple[TradeDeal, ...]: + """Call specifying the order ticket. Return all deals having the specified order ticket in the DEAL_ORDER + property. + + Args: + ticket (int): The order ticket + + Returns: + tuple[TradeDeal]: A tuple of all deals with the order ticket + """ + return tuple(sorted((deal for deal in self.deals if deal.order == ticket), key=lambda x: x.time_msc)) + + def get_deals_by_position(self, *, position: int = None) -> tuple[TradeDeal, ...]: + """ + Get all deals with the specified position ticket in the DEAL_POSITION_ID property + Args: + position (int): The position ticket + + Returns: + tuple[TradeDeal]: A tuple of all deals with the position ticket + """ + return tuple(sorted((deal for deal in self.deals if deal.position_id == position), key=lambda x: x.time_msc)) + + def get_orders(self) -> tuple[TradeOrder, ...]: + """Get orders from trading history using the parameters set in the constructor or the method arguments. + + Returns: + list[TradeOrder]: A list of trade orders + """ + orders = self.mt5.history_orders_get(date_from=self.date_from, date_to=self.date_to, group=self.group) + + if orders is not None: + return tuple(TradeOrder(**order._asdict()) for order in orders) + + logger.warning(f"Failed to get orders") + return tuple() + + def get_orders_by_ticket(self, *, ticket: int) -> tuple[TradeOrder, ...]: + """filter orders by ticket""" + return tuple(sorted((order for order in self.orders if order.ticket == ticket), key=lambda x: x.time_done_msc)) + + def get_orders_by_position(self, *, position: int) -> tuple[TradeOrder, ...]: + """filter orders by position""" + return tuple( + sorted((order for order in self.orders if order.position_id == position), key=lambda x: x.time_done_msc) + ) diff --git a/src/aiomql/lib/sync/order.py b/src/aiomql/lib/sync/order.py index 329b7ca..aa420b1 100644 --- a/src/aiomql/lib/sync/order.py +++ b/src/aiomql/lib/sync/order.py @@ -48,6 +48,12 @@ class Order(_Base, TradeRequest): """ return cls.mt5.orders_total() + @classmethod + def cancel_order(cls, *, order: int, symbol: str) -> OrderSendResult: + """Cancel an active pending order by ticket number.""" + res = cls.mt5.order_send({"symbol": symbol, "order": order, "action": TradeAction.REMOVE}) + return res + @classmethod def get_pending_order(cls, *, ticket: int) -> TradeOrder | None: """ diff --git a/src/aiomql/lib/sync/positions.py b/src/aiomql/lib/sync/positions.py index a4f97c7..ecef65d 100644 --- a/src/aiomql/lib/sync/positions.py +++ b/src/aiomql/lib/sync/positions.py @@ -69,7 +69,7 @@ class Positions: kwargs["group"] = group positions = cls.mt5.positions_get(**kwargs) if positions is not None: - return cls.positions + return tuple(TradePosition(**pos._asdict()) for pos in positions) logger.warning("Failed to get open positions") return () diff --git a/src/aiomql/lib/trade_records.py b/src/aiomql/lib/trade_records.py index 10e4de6..b9e6886 100644 --- a/src/aiomql/lib/trade_records.py +++ b/src/aiomql/lib/trade_records.py @@ -7,11 +7,10 @@ import csv import logging from typing import Iterable -from MetaTrader5 import TradePosition - from ..core.config import Config from ..core.meta_trader import MetaTrader from ..core.meta_backtester import MetaBackTester +from ..core.models import TradePosition logger = logging.getLogger(__name__) diff --git a/src/aiomql/lib/trader.py b/src/aiomql/lib/trader.py index 099e7ae..c938584 100644 --- a/src/aiomql/lib/trader.py +++ b/src/aiomql/lib/trader.py @@ -3,7 +3,6 @@ from datetime import datetime, UTC from typing import TypeVar from logging import getLogger - from ..core.models import OrderType, OrderSendResult, OrderCheckResult from ..core.config import Config from ..core.task_queue import QueueItem diff --git a/src/aiomql/utils/__init__.py b/src/aiomql/utils/__init__.py index a0157d9..085c441 100644 --- a/src/aiomql/utils/__init__.py +++ b/src/aiomql/utils/__init__.py @@ -1,2 +1,3 @@ from .utils import * from .process_pool import * +from .change import * diff --git a/src/aiomql/contrib/quants/change.py b/src/aiomql/utils/change.py similarity index 100% rename from src/aiomql/contrib/quants/change.py rename to src/aiomql/utils/change.py diff --git a/src/pandas_ta/__init__.py b/src/pandas_ta/__init__.py index 177223b..e03e73f 100644 --- a/src/pandas_ta/__init__.py +++ b/src/pandas_ta/__init__.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- -# from importlib.metadata import version -# version = version("pandas-ta") - from pandas_ta.maps import EXCHANGE_TZ, RATE, Category, Imports from pandas_ta.utils import * from pandas_ta.utils import __all__ as utils_all @@ -42,7 +38,6 @@ __all__ = [ "RATE", "Category", "Imports", - # "version", "ma", "create_dir", "import_dir", diff --git a/src/pandas_ta/__main__.py b/src/pandas_ta/__main__.py deleted file mode 100644 index ab54de4..0000000 --- a/src/pandas_ta/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ -# #-*- coding: utf-8 -*- -# from pandas_ta import version -# -# SUPPORT="http://www.pandas-ta.dev/support" -# -# if __name__ == "__main__": -# print(f"Pandas TA: {version}\nSupport: {SUPPORT}") diff --git a/src/pandas_ta/_typing.py b/src/pandas_ta/_typing.py index d916dcb..6d3834d 100644 --- a/src/pandas_ta/_typing.py +++ b/src/pandas_ta/_typing.py @@ -1,17 +1,5 @@ -# -*- coding: utf-8 -*- from pathlib import Path -from typing import ( - Any, - Dict, - Iterable, - List, - Optional, - Sequence, - TextIO, - Tuple, - TypeVar, - Union -) +from typing import Any, Iterable, Sequence, TypeVar, Dict, List, Tuple, TextIO, Union, Optional from numpy import ndarray, recarray, void from numpy import bool_ as np_bool_ @@ -21,40 +9,38 @@ from numpy import integer as np_integer from numpy import number as np_number from pandas import DataFrame, Series - - # Generic types T = TypeVar("T") # Scalars -Scalar = Union[str, float, int, complex, bool, object, np_generic] -Number = Union[int, float, complex, np_number, np_bool_] -Int = Union[int, np_integer] -Float = Union[float, np_floating] -IntFloat = Union[Int, Float] +Scalar = str | float | int | complex | bool | object | np_generic +Number = int | float | complex | np_number | np_bool_ +Int = int | np_integer +Float = float | np_floating +IntFloat = Int | Float # Basic sequences -MaybeTuple = Union[T, Tuple[T, ...]] -MaybeList = Union[T, List[T]] -TupleList = Union[List[T], Tuple[T, ...]] -MaybeTupleList = Union[T, List[T], Tuple[T, ...]] -MaybeIterable = Union[T, Iterable[T]] -MaybeSequence = Union[T, Sequence[T]] -ListStr = List[str] +MaybeTuple = T | tuple[T, ...] +MaybeList = T | list[T] +TupleList = list[T] | tuple[T, ...] +MaybeTupleList = T | list[T] | tuple[T, ...] +MaybeIterable = T | Iterable[T] +MaybeSequence = T | Sequence[T] +ListStr = list[str] -DictLike = Union[None, dict] +DictLike = None | dict DictLikeSequence = MaybeSequence[DictLike] -Args = Tuple[Any, ...] -ArgsLike = Union[None, Args] -Kwargs = Dict[str, Any] -KwargsLike = Union[None, Kwargs] +Args = tuple[Any, ...] +ArgsLike = None | Args +Kwargs = dict[str, Any] +KwargsLike = None | Kwargs KwargsLikeSequence = MaybeSequence[KwargsLike] -FileName = Union[str, Path] +FileName = str | Path DTypeLike = Any PandasDTypeLike = Any -Shape = Tuple[int, ...] -RelaxedShape = Union[int, Shape] +Shape = tuple[int, ...] +RelaxedShape = int | Shape Array = ndarray Array1d = ndarray Array2d = ndarray @@ -62,10 +48,10 @@ Array3d = ndarray Record = void RecordArray = ndarray RecArray = recarray -MaybeArray = Union[T, Array] -SeriesFrame = Union[Series, DataFrame] -MaybeSeries = Union[T, Series] -MaybeSeriesFrame = Union[T, Series, DataFrame] -AnyArray = Union[Array, Series, DataFrame] -AnyArray1d = Union[Array1d, Series] -AnyArray2d = Union[Array2d, DataFrame] +MaybeArray = T | Array +SeriesFrame = Series | DataFrame +MaybeSeries = T | Series +MaybeSeriesFrame = T | Series | DataFrame +AnyArray = Array | Series | DataFrame +AnyArray1d = Array1d | Series +AnyArray2d = Array2d | DataFrame diff --git a/src/pandas_ta/py.typed b/src/pandas_ta/py.typed deleted file mode 100644 index e69de29..0000000