akquant 0.1.0__cp39-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of akquant might be problematic. Click here for more details.
- akquant/__init__.py +81 -0
- akquant/__pycache__/__init__.cpython-312.pyc +0 -0
- akquant/__pycache__/__init__.cpython-313.pyc +0 -0
- akquant/__pycache__/analyzer.cpython-312.pyc +0 -0
- akquant/__pycache__/analyzer.cpython-313.pyc +0 -0
- akquant/__pycache__/backtest.cpython-312.pyc +0 -0
- akquant/__pycache__/backtest.cpython-313.pyc +0 -0
- akquant/__pycache__/config.cpython-312.pyc +0 -0
- akquant/__pycache__/config.cpython-313.pyc +0 -0
- akquant/__pycache__/data.cpython-312.pyc +0 -0
- akquant/__pycache__/data.cpython-313.pyc +0 -0
- akquant/__pycache__/indicator.cpython-312.pyc +0 -0
- akquant/__pycache__/indicator.cpython-313.pyc +0 -0
- akquant/__pycache__/log.cpython-312.pyc +0 -0
- akquant/__pycache__/log.cpython-313.pyc +0 -0
- akquant/__pycache__/sizer.cpython-312.pyc +0 -0
- akquant/__pycache__/sizer.cpython-313.pyc +0 -0
- akquant/__pycache__/strategy.cpython-312.pyc +0 -0
- akquant/__pycache__/strategy.cpython-313.pyc +0 -0
- akquant/__pycache__/utils.cpython-312.pyc +0 -0
- akquant/__pycache__/utils.cpython-313.pyc +0 -0
- akquant/akquant.pyd +0 -0
- akquant/akquant.pyi +518 -0
- akquant/backtest.py +414 -0
- akquant/config.py +36 -0
- akquant/data.py +122 -0
- akquant/indicator.py +56 -0
- akquant/log.py +135 -0
- akquant/sizer.py +82 -0
- akquant/strategy.py +516 -0
- akquant/utils.py +167 -0
- akquant-0.1.0.dist-info/METADATA +149 -0
- akquant-0.1.0.dist-info/RECORD +35 -0
- akquant-0.1.0.dist-info/WHEEL +4 -0
- akquant-0.1.0.dist-info/licenses/LICENSE +21 -0
akquant/log.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
# Default format: Time | Level | Message
|
|
6
|
+
DEFAULT_FORMAT = "%(asctime)s | %(levelname)s | %(message)s"
|
|
7
|
+
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Logger:
|
|
11
|
+
r"""
|
|
12
|
+
akquant 日志封装
|
|
13
|
+
|
|
14
|
+
:description: 提供控制台与文件日志的快捷配置
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
_instance = None
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self._logger = logging.getLogger("akquant")
|
|
21
|
+
self._logger.setLevel(logging.INFO)
|
|
22
|
+
self._handlers = {} # key -> handler
|
|
23
|
+
|
|
24
|
+
# Add default console handler if not present
|
|
25
|
+
if not self._logger.handlers:
|
|
26
|
+
self.enable_console()
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_logger(cls):
|
|
30
|
+
if cls._instance is None:
|
|
31
|
+
cls._instance = Logger()
|
|
32
|
+
return cls._instance._logger
|
|
33
|
+
|
|
34
|
+
def set_level(self, level: Union[str, int]):
|
|
35
|
+
r"""
|
|
36
|
+
设置日志等级
|
|
37
|
+
|
|
38
|
+
:param level: 日志等级字符串或整数 (DEBUG/INFO/WARNING/ERROR/CRITICAL)
|
|
39
|
+
:type level: str | int
|
|
40
|
+
"""
|
|
41
|
+
self._logger.setLevel(level)
|
|
42
|
+
|
|
43
|
+
def enable_console(self, format_str: str = DEFAULT_FORMAT):
|
|
44
|
+
r"""
|
|
45
|
+
启用控制台日志
|
|
46
|
+
|
|
47
|
+
:param format_str: 日志格式字符串
|
|
48
|
+
:type format_str: str
|
|
49
|
+
"""
|
|
50
|
+
if "console" in self._handlers:
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
54
|
+
handler.setFormatter(logging.Formatter(format_str, datefmt=DATE_FORMAT))
|
|
55
|
+
self._logger.addHandler(handler)
|
|
56
|
+
self._handlers["console"] = handler
|
|
57
|
+
|
|
58
|
+
def disable_console(self):
|
|
59
|
+
r"""
|
|
60
|
+
禁用控制台日志
|
|
61
|
+
"""
|
|
62
|
+
if "console" in self._handlers:
|
|
63
|
+
self._logger.removeHandler(self._handlers["console"])
|
|
64
|
+
del self._handlers["console"]
|
|
65
|
+
|
|
66
|
+
def enable_file(
|
|
67
|
+
self, filename: str, format_str: str = DEFAULT_FORMAT, mode: str = "a"
|
|
68
|
+
):
|
|
69
|
+
r"""
|
|
70
|
+
启用文件日志
|
|
71
|
+
|
|
72
|
+
:param filename: 日志文件路径
|
|
73
|
+
:type filename: str
|
|
74
|
+
:param format_str: 日志格式字符串
|
|
75
|
+
:type format_str: str
|
|
76
|
+
:param mode: 文件打开模式 ('a' 追加 或 'w' 覆写)
|
|
77
|
+
:type mode: str
|
|
78
|
+
"""
|
|
79
|
+
# Remove existing file handler if path matches (simple check)
|
|
80
|
+
key = f"file_{filename}"
|
|
81
|
+
if key in self._handlers:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
handler = logging.FileHandler(filename, mode=mode, encoding="utf-8")
|
|
85
|
+
handler.setFormatter(logging.Formatter(format_str, datefmt=DATE_FORMAT))
|
|
86
|
+
self._logger.addHandler(handler)
|
|
87
|
+
self._handlers[key] = handler
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Global helper functions
|
|
91
|
+
def get_logger():
|
|
92
|
+
r"""
|
|
93
|
+
获取全局 logger 实例
|
|
94
|
+
|
|
95
|
+
:return: 已初始化的 logger
|
|
96
|
+
:rtype: logging.Logger
|
|
97
|
+
"""
|
|
98
|
+
return Logger.get_logger()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def set_log_level(level: Union[str, int]):
|
|
102
|
+
r"""
|
|
103
|
+
设置全局日志等级
|
|
104
|
+
|
|
105
|
+
:param level: 日志等级字符串或整数
|
|
106
|
+
:type level: str | int
|
|
107
|
+
"""
|
|
108
|
+
Logger.get_logger().setLevel(level)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def register_logger(
|
|
112
|
+
filename: Optional[str] = None, console: bool = True, level: str = "INFO"
|
|
113
|
+
):
|
|
114
|
+
r"""
|
|
115
|
+
日志一体化配置
|
|
116
|
+
|
|
117
|
+
:param filename: 日志文件路径,提供则写入文件
|
|
118
|
+
:type filename: str, optional
|
|
119
|
+
:param console: 是否输出到控制台
|
|
120
|
+
:type console: bool
|
|
121
|
+
:param level: 日志等级 ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
|
|
122
|
+
:type level: str
|
|
123
|
+
"""
|
|
124
|
+
logger_manager = Logger._instance or Logger()
|
|
125
|
+
Logger._instance = logger_manager
|
|
126
|
+
|
|
127
|
+
logger_manager.set_level(level.upper())
|
|
128
|
+
|
|
129
|
+
if console:
|
|
130
|
+
logger_manager.enable_console()
|
|
131
|
+
else:
|
|
132
|
+
logger_manager.disable_console()
|
|
133
|
+
|
|
134
|
+
if filename:
|
|
135
|
+
logger_manager.enable_file(filename)
|
akquant/sizer.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from .akquant import StrategyContext
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Sizer(ABC):
|
|
9
|
+
"""
|
|
10
|
+
仓位管理基类 (Sizer Base Class)
|
|
11
|
+
用于计算下单数量
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def get_size(
|
|
16
|
+
self, price: float, cash: float, context: "StrategyContext", symbol: str
|
|
17
|
+
) -> float:
|
|
18
|
+
"""
|
|
19
|
+
计算下单数量
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
price (float): 当前价格
|
|
23
|
+
cash (float): 当前可用资金
|
|
24
|
+
context (StrategyContext): 策略上下文
|
|
25
|
+
symbol (str): 标的代码
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
float: 下单数量
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FixedSize(Sizer):
|
|
34
|
+
"""
|
|
35
|
+
固定数量 Sizer
|
|
36
|
+
每次交易固定数量
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, size: float = 100.0):
|
|
40
|
+
self.size = size
|
|
41
|
+
|
|
42
|
+
def get_size(
|
|
43
|
+
self, price: float, cash: float, context: "StrategyContext", symbol: str
|
|
44
|
+
) -> float:
|
|
45
|
+
return self.size
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class PercentSizer(Sizer):
|
|
49
|
+
"""
|
|
50
|
+
百分比 Sizer
|
|
51
|
+
使用当前资金的一定百分比买入
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, percents: float = 10.0):
|
|
55
|
+
"""
|
|
56
|
+
Args:
|
|
57
|
+
percents (float): 资金百分比 (0-100)
|
|
58
|
+
"""
|
|
59
|
+
self.percents = percents
|
|
60
|
+
|
|
61
|
+
def get_size(
|
|
62
|
+
self, price: float, cash: float, context: "StrategyContext", symbol: str
|
|
63
|
+
) -> float:
|
|
64
|
+
if price <= 0:
|
|
65
|
+
return 0.0
|
|
66
|
+
|
|
67
|
+
target_cash = cash * (self.percents / 100.0)
|
|
68
|
+
return int(target_cash / price)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class AllInSizer(Sizer):
|
|
72
|
+
"""
|
|
73
|
+
全仓 Sizer
|
|
74
|
+
使用所有可用资金买入
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def get_size(
|
|
78
|
+
self, price: float, cash: float, context: "StrategyContext", symbol: str
|
|
79
|
+
) -> float:
|
|
80
|
+
if price <= 0:
|
|
81
|
+
return 0.0
|
|
82
|
+
return int(cash / price)
|