PyAlgoEngine 0.7.7.post1__tar.gz → 0.8.0a2__tar.gz
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.
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PKG-INFO +3 -2
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/PKG-INFO +3 -2
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/SOURCES.txt +1 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/__init__.py +1 -1
- pyalgoengine-0.8.0a2/algo_engine/base/market_data_wrapper.py +483 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/LICENSE +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/requires.txt +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/top_level.txt +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/README.md +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/doc_server.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/tester.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/backtest/web_app.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/bokeh_server.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/demo/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/demo/test.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/client.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/window.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/__main__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/metrics.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/replay.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/backtest/sim_match.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/console_utils.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/finance_decimal.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_buffer.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_utils.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_utils_nt.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/market_utils_posix.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/technical_analysis.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/telemetrics.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/base/trade_utils.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/algo_engine.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/event_engine.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/market_engine.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/engine/trade_engine.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/monitor/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/monitor/advanced_data_interface.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/profile/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/profile/cn.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/strategy/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/strategy/strategy_engine.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/utils/__init__.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/utils/commit_regularizer.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/utils/data_utils.py +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/setup.cfg +0 -0
- {pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: PyAlgoEngine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0a2
|
|
4
4
|
Summary: Basic algo engine
|
|
5
5
|
Home-page: https://github.com/BolunHan/PyAlgoEngine
|
|
6
6
|
Author: Bolun.Han
|
|
@@ -28,6 +28,7 @@ Dynamic: classifier
|
|
|
28
28
|
Dynamic: description
|
|
29
29
|
Dynamic: description-content-type
|
|
30
30
|
Dynamic: home-page
|
|
31
|
+
Dynamic: license-file
|
|
31
32
|
Dynamic: provides-extra
|
|
32
33
|
Dynamic: requires-dist
|
|
33
34
|
Dynamic: requires-python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: PyAlgoEngine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0a2
|
|
4
4
|
Summary: Basic algo engine
|
|
5
5
|
Home-page: https://github.com/BolunHan/PyAlgoEngine
|
|
6
6
|
Author: Bolun.Han
|
|
@@ -28,6 +28,7 @@ Dynamic: classifier
|
|
|
28
28
|
Dynamic: description
|
|
29
29
|
Dynamic: description-content-type
|
|
30
30
|
Dynamic: home-page
|
|
31
|
+
Dynamic: license-file
|
|
31
32
|
Dynamic: provides-extra
|
|
32
33
|
Dynamic: requires-dist
|
|
33
34
|
Dynamic: requires-python
|
|
@@ -29,6 +29,7 @@ algo_engine/base/__init__.py
|
|
|
29
29
|
algo_engine/base/console_utils.py
|
|
30
30
|
algo_engine/base/finance_decimal.py
|
|
31
31
|
algo_engine/base/market_buffer.py
|
|
32
|
+
algo_engine/base/market_data_wrapper.py
|
|
32
33
|
algo_engine/base/market_utils.py
|
|
33
34
|
algo_engine/base/market_utils_nt.py
|
|
34
35
|
algo_engine/base/market_utils_posix.py
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import datetime
|
|
3
|
+
import enum
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Self, Literal
|
|
6
|
+
|
|
7
|
+
import candlestick as candlestick_cython
|
|
8
|
+
import market_data as market_data_cython
|
|
9
|
+
import transaction as transaction_cython
|
|
10
|
+
|
|
11
|
+
LOGGER = logging.getLogger(__name__)
|
|
12
|
+
from algo_engine.profile import PROFILE
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Direction(enum.IntEnum):
|
|
16
|
+
DIRECTION_UNKNOWN = 1
|
|
17
|
+
DIRECTION_SHORT = 0
|
|
18
|
+
DIRECTION_LONG = 2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Offset(enum.IntEnum):
|
|
22
|
+
OFFSET_CANCEL = 0
|
|
23
|
+
OFFSET_ORDER = 4
|
|
24
|
+
OFFSET_OPEN = 8
|
|
25
|
+
OFFSET_CLOSE = 16
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TransactionSide(enum.IntEnum):
|
|
29
|
+
# Long Side
|
|
30
|
+
SIDE_LONG_OPEN = Direction.DIRECTION_LONG + Offset.OFFSET_OPEN # 2 + 8 = 10
|
|
31
|
+
SIDE_LONG_CLOSE = Direction.DIRECTION_LONG + Offset.OFFSET_CLOSE # 2 + 16 = 18
|
|
32
|
+
SIDE_LONG_CANCEL = Direction.DIRECTION_LONG + Offset.OFFSET_CANCEL # 2 + 0 = 2
|
|
33
|
+
|
|
34
|
+
# Short Side
|
|
35
|
+
SIDE_SHORT_OPEN = Direction.DIRECTION_SHORT + Offset.OFFSET_OPEN # 0 + 8 = 8
|
|
36
|
+
SIDE_SHORT_CLOSE = Direction.DIRECTION_SHORT + Offset.OFFSET_CLOSE # 0 + 16 = 16
|
|
37
|
+
SIDE_SHORT_CANCEL = Direction.DIRECTION_SHORT + Offset.OFFSET_CANCEL # 0 + 0 = 0
|
|
38
|
+
|
|
39
|
+
# Order
|
|
40
|
+
SIDE_BID = Direction.DIRECTION_LONG + Offset.OFFSET_ORDER # 2 + 4 = 6
|
|
41
|
+
SIDE_ASK = Direction.DIRECTION_SHORT + Offset.OFFSET_ORDER # 0 + 4 = 4
|
|
42
|
+
|
|
43
|
+
# Generic Cancel
|
|
44
|
+
SIDE_CANCEL = Direction.DIRECTION_UNKNOWN + Offset.OFFSET_CANCEL # 1 + 0 = 1
|
|
45
|
+
|
|
46
|
+
# Alias
|
|
47
|
+
SIDE_UNKNOWN = SIDE_CANCEL # 1
|
|
48
|
+
SIDE_LONG = SIDE_LONG_OPEN # 10
|
|
49
|
+
SIDE_SHORT = SIDE_SHORT_OPEN # 8
|
|
50
|
+
|
|
51
|
+
# Backward compatibility
|
|
52
|
+
ShortOrder = AskOrder = Ask = SIDE_ASK
|
|
53
|
+
LongOrder = BidOrder = Bid = SIDE_BID
|
|
54
|
+
|
|
55
|
+
ShortFilled = Unwind = Sell = SIDE_SHORT_CLOSE
|
|
56
|
+
LongFilled = LongOpen = Buy = SIDE_LONG_OPEN
|
|
57
|
+
|
|
58
|
+
ShortOpen = Short = SIDE_SHORT_OPEN
|
|
59
|
+
Cover = SIDE_LONG_CLOSE
|
|
60
|
+
|
|
61
|
+
UNKNOWN = CANCEL = SIDE_CANCEL
|
|
62
|
+
|
|
63
|
+
def __neg__(self) -> Self:
|
|
64
|
+
return self.__class__([market_data_cython.TransactionHelper.pyget_opposite(self.value)])
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def _missing_(cls, value: str | int):
|
|
68
|
+
"""
|
|
69
|
+
Handle missing values in the enumeration.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
value (str | int): The value to resolve.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
TransactionSide: The resolved transaction side, or UNKNOWN if not recognized.
|
|
76
|
+
"""
|
|
77
|
+
side_str = str(value).lower()
|
|
78
|
+
|
|
79
|
+
match side_str:
|
|
80
|
+
case 'long' | 'buy' | 'b':
|
|
81
|
+
trade_side = cls.SIDE_LONG_OPEN
|
|
82
|
+
case 'short' | 'sell' | 's':
|
|
83
|
+
trade_side = cls.SIDE_SHORT_CLOSE
|
|
84
|
+
case 'short' | 'ss':
|
|
85
|
+
trade_side = cls.SIDE_SHORT_OPEN
|
|
86
|
+
case 'cover' | 'bc':
|
|
87
|
+
trade_side = cls.SIDE_LONG_CLOSE
|
|
88
|
+
case 'ask':
|
|
89
|
+
trade_side = cls.SIDE_ASK
|
|
90
|
+
case 'bid':
|
|
91
|
+
trade_side = cls.SIDE_BID
|
|
92
|
+
case _:
|
|
93
|
+
try:
|
|
94
|
+
trade_side = cls.__getitem__(value)
|
|
95
|
+
except Exception as _:
|
|
96
|
+
trade_side = cls.SIDE_UNKNOWN
|
|
97
|
+
LOGGER.warning('{} is not recognized, return TransactionSide.UNKNOWN'.format(value))
|
|
98
|
+
|
|
99
|
+
return trade_side
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def sign(self) -> int:
|
|
103
|
+
return market_data_cython.TransactionHelper.pyget_sign(self.value)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def offset(self) -> Offset:
|
|
107
|
+
"""
|
|
108
|
+
Get the offset of the transaction side.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
int: The offset value, equivalent to the sign.
|
|
112
|
+
"""
|
|
113
|
+
return Offset(market_data_cython.TransactionHelper.pyget_offset(self.value))
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def direction(self) -> Direction:
|
|
117
|
+
"""
|
|
118
|
+
Get the offset of the transaction side.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
int: The offset value, equivalent to the sign.
|
|
122
|
+
"""
|
|
123
|
+
return Direction(market_data_cython.TransactionHelper.pyget_direction(self.value))
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def side_name(self) -> str:
|
|
127
|
+
return market_data_cython.TransactionHelper.pyget_side_name(self.value)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def offset_name(self) -> str:
|
|
131
|
+
return market_data_cython.TransactionHelper.pyget_offset_name(self.value)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def direction_name(self) -> str:
|
|
135
|
+
return market_data_cython.TransactionHelper.pyget_direction_name(self.value)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class OrderType(enum.IntEnum):
|
|
139
|
+
UNKNOWN = -20
|
|
140
|
+
CancelOrder = -10
|
|
141
|
+
Generic = 0
|
|
142
|
+
LimitOrder = 10
|
|
143
|
+
LimitMarketMaking = 11
|
|
144
|
+
MarketOrder = 2
|
|
145
|
+
FOK = 21
|
|
146
|
+
FAK = 22
|
|
147
|
+
IOC = 23
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class MarketData(market_data_cython.MarketData, metaclass=abc.ABCMeta):
|
|
151
|
+
"""
|
|
152
|
+
Python wrapper for MarketData Cython class.
|
|
153
|
+
Provides pickle serialization support.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def from_buffer(cls, buffer, **kwargs):
|
|
158
|
+
self = super().from_buffer(buffer)
|
|
159
|
+
self.__dict__.update(kwargs)
|
|
160
|
+
return self
|
|
161
|
+
|
|
162
|
+
@abc.abstractmethod
|
|
163
|
+
def __reduce__(self):
|
|
164
|
+
...
|
|
165
|
+
|
|
166
|
+
def __copy__(self):
|
|
167
|
+
return self.__class__.from_buffer(memoryview(self), **self.__dict__)
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def topic(self) -> str:
|
|
171
|
+
return f'{self.ticker}.{self.__class__.__name__}'
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def market_time(self) -> datetime.datetime | datetime.date:
|
|
175
|
+
return datetime.datetime.fromtimestamp(self.timestamp, tz=PROFILE.time_zone)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class TransactionData(transaction_cython.TransactionData, MarketData):
|
|
179
|
+
def __init__(
|
|
180
|
+
self,
|
|
181
|
+
ticker: str,
|
|
182
|
+
timestamp: float,
|
|
183
|
+
price: float,
|
|
184
|
+
volume: float,
|
|
185
|
+
side: int | TransactionSide,
|
|
186
|
+
multiplier: float = 1.0,
|
|
187
|
+
notional: float = 0.0,
|
|
188
|
+
transaction_id: int | str | bytes = None,
|
|
189
|
+
buy_id: int | str | bytes = None,
|
|
190
|
+
sell_id: int | str | bytes = None,
|
|
191
|
+
**kwargs
|
|
192
|
+
):
|
|
193
|
+
super().__init__(ticker=ticker, timestamp=timestamp, price=price, volume=volume, side=side, multiplier=multiplier, notional=notional, transaction_id=transaction_id, buy_id=buy_id, sell_id=sell_id)
|
|
194
|
+
self.__dict__.update(kwargs)
|
|
195
|
+
|
|
196
|
+
def __reduce__(self):
|
|
197
|
+
"""Support for pickle serialization"""
|
|
198
|
+
return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
|
|
199
|
+
|
|
200
|
+
def __setstate__(self, state):
|
|
201
|
+
"""Restore state from pickle"""
|
|
202
|
+
self.__dict__.update(state)
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def side_int(self) -> int:
|
|
206
|
+
return super().side
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def side(self) -> TransactionSide:
|
|
210
|
+
return TransactionSide(super().side)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class BarData(candlestick_cython.BarData, MarketData):
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
ticker: str,
|
|
217
|
+
timestamp: float,
|
|
218
|
+
high_price: float,
|
|
219
|
+
low_price: float,
|
|
220
|
+
open_price: float,
|
|
221
|
+
close_price: float,
|
|
222
|
+
volume: float = 0.0,
|
|
223
|
+
notional: float = 0.0,
|
|
224
|
+
trade_count: int = 0,
|
|
225
|
+
start_timestamp: float = None,
|
|
226
|
+
bar_span: float = None,
|
|
227
|
+
**kwargs: object
|
|
228
|
+
) -> None:
|
|
229
|
+
if bar_span is None:
|
|
230
|
+
if start_timestamp is None:
|
|
231
|
+
raise ValueError('Must assign either start_timestamp or bar_span or both.')
|
|
232
|
+
else:
|
|
233
|
+
bar_span = timestamp - start_timestamp
|
|
234
|
+
else:
|
|
235
|
+
if isinstance(bar_span, datetime.timedelta):
|
|
236
|
+
bar_span = bar_span.total_seconds()
|
|
237
|
+
else:
|
|
238
|
+
bar_span = float(bar_span)
|
|
239
|
+
|
|
240
|
+
super().__init__(ticker=ticker, timestamp=timestamp, high_price=high_price, low_price=low_price, open_price=open_price, close_price=close_price, volume=volume, notional=notional, trade_count=trade_count, bar_span=bar_span)
|
|
241
|
+
self.__dict__.update(kwargs)
|
|
242
|
+
|
|
243
|
+
def __reduce__(self):
|
|
244
|
+
"""Support for pickle serialization"""
|
|
245
|
+
return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
|
|
246
|
+
|
|
247
|
+
def __setstate__(self, state):
|
|
248
|
+
"""Restore state from pickle"""
|
|
249
|
+
self.__dict__.update(state)
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def bar_type(self) -> Literal['Hourly-Plus', 'Hourly', 'Minute-Plus', 'Minute', 'Sub-Minute']:
|
|
253
|
+
"""
|
|
254
|
+
Determines the type of the bar based on its span.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Literal['Hourly-Plus', 'Hourly', 'Minute-Plus', 'Minute', 'Sub-Minute']: The type of the bar.
|
|
258
|
+
"""
|
|
259
|
+
bar_span = super().bar_span
|
|
260
|
+
|
|
261
|
+
if bar_span > 3600:
|
|
262
|
+
return 'Hourly-Plus'
|
|
263
|
+
elif bar_span == 3600:
|
|
264
|
+
return 'Hourly'
|
|
265
|
+
elif bar_span > 60:
|
|
266
|
+
return 'Minute-Plus'
|
|
267
|
+
elif bar_span == 60:
|
|
268
|
+
return 'Minute'
|
|
269
|
+
else:
|
|
270
|
+
return 'Sub-Minute'
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def bar_end_time(self) -> datetime.datetime | datetime.date:
|
|
274
|
+
"""
|
|
275
|
+
The end time of the bar.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
datetime.datetime | datetime.date: The end time of the bar.
|
|
279
|
+
"""
|
|
280
|
+
return self.market_time
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def bar_start_time(self) -> datetime.datetime:
|
|
284
|
+
"""
|
|
285
|
+
The start time of the bar.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
datetime.datetime: The start time of the bar.
|
|
289
|
+
"""
|
|
290
|
+
return datetime.datetime.fromtimestamp(super().start_timestamp, tz=PROFILE.time_zone)
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def bar_span(self) -> datetime.timedelta:
|
|
294
|
+
return datetime.timedelta(seconds=super().bar_span)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class DailyBar(candlestick_cython.BarData):
|
|
298
|
+
def __init__(
|
|
299
|
+
self,
|
|
300
|
+
ticker: str,
|
|
301
|
+
market_date: datetime.date, # The market date of the bar, if with 1D data, or the END date of the bar.
|
|
302
|
+
high_price: float,
|
|
303
|
+
low_price: float,
|
|
304
|
+
open_price: float,
|
|
305
|
+
close_price: float,
|
|
306
|
+
volume: float = 0.0,
|
|
307
|
+
notional: float = 0.0,
|
|
308
|
+
trade_count: int = 0,
|
|
309
|
+
bar_span: datetime.timedelta | int = None, # Expect to be a timedelta for several days, or the number of days
|
|
310
|
+
start_date: datetime.date = None,
|
|
311
|
+
**kwargs
|
|
312
|
+
):
|
|
313
|
+
if bar_span is None:
|
|
314
|
+
if start_date is None:
|
|
315
|
+
bar_span = 1
|
|
316
|
+
else:
|
|
317
|
+
bar_span = (market_date - start_date).days
|
|
318
|
+
else:
|
|
319
|
+
if isinstance(bar_span, datetime.timedelta):
|
|
320
|
+
bar_span = bar_span.days
|
|
321
|
+
else:
|
|
322
|
+
bar_span = float(bar_span)
|
|
323
|
+
|
|
324
|
+
timestamp = 10000 * market_date.year + 100 * market_date.month + market_date.day
|
|
325
|
+
|
|
326
|
+
super().__init__(ticker=ticker, timestamp=timestamp, high_price=high_price, low_price=low_price, open_price=open_price, close_price=close_price, volume=volume, notional=notional, trade_count=trade_count, bar_span=bar_span)
|
|
327
|
+
self.__dict__.update(kwargs)
|
|
328
|
+
|
|
329
|
+
def __repr__(self) -> str:
|
|
330
|
+
if (bar_span := super().bar_span) == 1:
|
|
331
|
+
return f"DailyBar(ticker='{self.ticker}', date={self.market_date}, open={self.open_price}, high={self.high_price}, low={self.low_price}, close={self.close_price}, volume={self.volume})"
|
|
332
|
+
else:
|
|
333
|
+
return f"DailyBar(ticker='{self.ticker}', date={self.market_date}, span={bar_span}d, open={self.open_price}, high={self.high_price}, low={self.low_price}, close={self.close_price}, volume={self.volume})"
|
|
334
|
+
|
|
335
|
+
def __reduce__(self):
|
|
336
|
+
"""Support for pickle serialization"""
|
|
337
|
+
return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
|
|
338
|
+
|
|
339
|
+
def __setstate__(self, state):
|
|
340
|
+
"""Restore state from pickle"""
|
|
341
|
+
self.__dict__.update(state)
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def market_date(self) -> datetime.date:
|
|
345
|
+
"""
|
|
346
|
+
The market date of the bar.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
datetime.date: The market date of the bar.
|
|
350
|
+
"""
|
|
351
|
+
|
|
352
|
+
int_date = int(self.timestamp)
|
|
353
|
+
y, _m = divmod(int_date, 10000)
|
|
354
|
+
m, d = divmod(_m, 100)
|
|
355
|
+
|
|
356
|
+
return datetime.date(year=y, month=m, day=d)
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def market_time(self) -> datetime.date:
|
|
360
|
+
"""
|
|
361
|
+
The market date of the bar (same as `market_date`).
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
datetime.date: The market date of the bar.
|
|
365
|
+
"""
|
|
366
|
+
return self.market_date
|
|
367
|
+
|
|
368
|
+
@property
|
|
369
|
+
def bar_start_time(self) -> datetime.date:
|
|
370
|
+
"""
|
|
371
|
+
The start date of the bar period.
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
datetime.date: The start date of the bar.
|
|
375
|
+
"""
|
|
376
|
+
return self.market_date - self.bar_span
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def bar_end_time(self) -> datetime.date:
|
|
380
|
+
"""
|
|
381
|
+
The end date of the bar period.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
datetime.date: The end date of the bar.
|
|
385
|
+
"""
|
|
386
|
+
return self.market_date
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def bar_span(self) -> datetime.timedelta:
|
|
390
|
+
return datetime.timedelta(days=super().bar_span)
|
|
391
|
+
|
|
392
|
+
@property
|
|
393
|
+
def bar_type(self) -> Literal['Daily', 'Daily-Plus']:
|
|
394
|
+
"""
|
|
395
|
+
Determines the type of the bar based on its span.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
Literal['Daily', 'Daily-Plus']: The type of the bar.
|
|
399
|
+
|
|
400
|
+
Raises:
|
|
401
|
+
ValueError: If `bar_span` is not valid for a daily bar.
|
|
402
|
+
"""
|
|
403
|
+
if super().bar_span == 1:
|
|
404
|
+
return 'Daily'
|
|
405
|
+
elif super().bar_span > 1:
|
|
406
|
+
return 'Daily-Plus'
|
|
407
|
+
else:
|
|
408
|
+
raise ValueError(f'Invalid bar_span for {self.__class__.__name__}! Expect an int greater or equal to 1, got {super().bar_span}.')
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class TickDataLite(market_data_cython.TickDataLite, MarketData):
|
|
412
|
+
"""
|
|
413
|
+
Python wrapper for TickDataLite Cython class.
|
|
414
|
+
Represents tick data for a specific ticker without the order_book field.
|
|
415
|
+
"""
|
|
416
|
+
|
|
417
|
+
def __init__(
|
|
418
|
+
self,
|
|
419
|
+
ticker: str,
|
|
420
|
+
timestamp: float,
|
|
421
|
+
last_price: float,
|
|
422
|
+
bid_price: float = float('nan'),
|
|
423
|
+
bid_volume: float = float('nan'),
|
|
424
|
+
ask_price: float = float('nan'),
|
|
425
|
+
ask_volume: float = float('nan'),
|
|
426
|
+
total_traded_volume: float = 0.0,
|
|
427
|
+
total_traded_notional: float = 0.0,
|
|
428
|
+
total_trade_count: int = 0,
|
|
429
|
+
**kwargs
|
|
430
|
+
):
|
|
431
|
+
"""
|
|
432
|
+
Initialize a new instance of TickDataLite.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
ticker (str): The ticker symbol for the market data.
|
|
436
|
+
timestamp (float): The timestamp of the tick data.
|
|
437
|
+
last_price (float): The last traded price.
|
|
438
|
+
bid_price (float, optional): The bid price. Defaults to None.
|
|
439
|
+
bid_volume (float, optional): The bid volume. Defaults to None.
|
|
440
|
+
ask_price (float, optional): The ask price. Defaults to None.
|
|
441
|
+
ask_volume (float, optional): The ask volume. Defaults to None.
|
|
442
|
+
total_traded_volume (float, optional): The total traded volume. Defaults to 0.0.
|
|
443
|
+
total_traded_notional (float, optional): The total traded notional value. Defaults to 0.0.
|
|
444
|
+
total_trade_count (int, optional): The total number of trades. Defaults to 0.
|
|
445
|
+
**kwargs: Additional keyword arguments.
|
|
446
|
+
"""
|
|
447
|
+
# Create the Cython object
|
|
448
|
+
super().__init__(
|
|
449
|
+
ticker=ticker,
|
|
450
|
+
timestamp=timestamp,
|
|
451
|
+
last_price=last_price,
|
|
452
|
+
bid_price=bid_price,
|
|
453
|
+
bid_volume=bid_volume,
|
|
454
|
+
ask_price=ask_price,
|
|
455
|
+
ask_volume=ask_volume,
|
|
456
|
+
total_traded_volume=total_traded_volume,
|
|
457
|
+
total_traded_notional=total_traded_notional,
|
|
458
|
+
total_trade_count=total_trade_count
|
|
459
|
+
)
|
|
460
|
+
self.__dict__.update(kwargs)
|
|
461
|
+
|
|
462
|
+
def __reduce__(self):
|
|
463
|
+
"""Support for pickle serialization"""
|
|
464
|
+
return self.__class__.from_bytes, (self.to_bytes(),), self.__dict__
|
|
465
|
+
|
|
466
|
+
def __setstate__(self, state):
|
|
467
|
+
"""Restore state from pickle"""
|
|
468
|
+
self.__dict__.update(state)
|
|
469
|
+
|
|
470
|
+
def __repr__(self) -> str:
|
|
471
|
+
"""
|
|
472
|
+
Returns a string representation of the TickDataLite instance.
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
str: A string representation of the TickDataLite instance.
|
|
476
|
+
"""
|
|
477
|
+
return f'<TickDataLite>([{self.market_time:%Y-%m-%d %H:%M:%S}] {self.ticker}, bid={self.bid_price}, ask={self.ask_price})'
|
|
478
|
+
|
|
479
|
+
@classmethod
|
|
480
|
+
def from_buffer(cls, buffer, **kwargs):
|
|
481
|
+
self = super().from_buffer(buffer)
|
|
482
|
+
self.__dict__.update(kwargs)
|
|
483
|
+
return self
|
|
File without changes
|
{pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/PyAlgoEngine.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/apps/sim_input/sim_keyboard.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyalgoengine-0.7.7.post1 → pyalgoengine-0.8.0a2}/algo_engine/monitor/advanced_data_interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|