PyAlgoEngine 0.7.6.post8__tar.gz → 0.7.7a1__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.6.post8 → pyalgoengine-0.7.7a1}/PKG-INFO +1 -1
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/__init__.py +1 -1
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/sim_match.py +50 -12
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/setup.py +35 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/LICENSE +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/requires.txt +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/top_level.txt +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/README.md +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/doc_server.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/tester.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/web_app.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/bokeh_server.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/demo/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/demo/test.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/client.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/window.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/__main__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/metrics.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/replay.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/console_utils.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/finance_decimal.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_buffer.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_utils.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_utils_nt.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_utils_posix.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/technical_analysis.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/telemetrics.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/trade_utils.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/algo_engine.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/event_engine.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/market_engine.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/trade_engine.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/monitor/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/monitor/advanced_data_interface.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/profile/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/profile/cn.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/strategy/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/strategy/strategy_engine.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/utils/__init__.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/utils/commit_regularizer.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/utils/data_utils.py +0 -0
- {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/setup.cfg +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
3
5
|
from . import LOGGER
|
|
4
|
-
from ..base import OrderType, MarketData, BarData, TradeData, TickData, OrderState, OrderBook, TradeReport, TradeInstruction
|
|
6
|
+
from ..base import OrderType, MarketData, BarData, TradeData, TickData, OrderState, OrderBook, TradeReport, TradeInstruction, TransactionSide
|
|
5
7
|
from ..engine.event_engine import TOPIC, EVENT_ENGINE
|
|
6
8
|
from ..profile import PROFILE
|
|
7
9
|
|
|
@@ -9,17 +11,26 @@ LOGGER = LOGGER.getChild('SimMatch')
|
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class SimMatch(object):
|
|
12
|
-
def __init__(self, ticker,
|
|
14
|
+
def __init__(self, ticker, event_engine=None, topic_set=None, **kwargs):
|
|
13
15
|
self.ticker = ticker
|
|
14
|
-
self.instant_fill = instant_fill
|
|
15
16
|
self.event_engine = event_engine if event_engine is not None else EVENT_ENGINE
|
|
16
17
|
self.topic_set = topic_set if topic_set is not None else TOPIC
|
|
17
|
-
self.fee_rate = fee_rate
|
|
18
|
+
self.fee_rate = kwargs.get('fee_rate', 0.)
|
|
18
19
|
|
|
19
20
|
self.working: dict[str, TradeInstruction] = {}
|
|
20
21
|
self.history: dict[str, TradeInstruction] = {}
|
|
21
22
|
|
|
22
23
|
self.timestamp = 0.
|
|
24
|
+
self.last_price = None
|
|
25
|
+
self.matching_config = {
|
|
26
|
+
'instant_fill': kwargs.get('instant_fill', False),
|
|
27
|
+
'lag': {
|
|
28
|
+
'ts': kwargs.get('lag_ts', 0.),
|
|
29
|
+
'n_transaction': kwargs.get('lag_n_transaction', 0)
|
|
30
|
+
},
|
|
31
|
+
'hit_prob': kwargs.get('hit_prob', 1.), # affecting FoK
|
|
32
|
+
'slippery_rate': kwargs.get('slippery_rate', 0.0001)
|
|
33
|
+
}
|
|
23
34
|
|
|
24
35
|
def __call__(self, **kwargs):
|
|
25
36
|
order: TradeInstruction | None = kwargs.pop('order', None)
|
|
@@ -35,6 +46,7 @@ class SimMatch(object):
|
|
|
35
46
|
|
|
36
47
|
if market_data is not None:
|
|
37
48
|
self.timestamp = market_data.timestamp
|
|
49
|
+
self.last_price = market_data.market_price
|
|
38
50
|
|
|
39
51
|
if isinstance(market_data, BarData):
|
|
40
52
|
self._check_bar_data(market_data=market_data)
|
|
@@ -69,17 +81,33 @@ class SimMatch(object):
|
|
|
69
81
|
# raise ValueError(f'Invalid instruction {order}, instruction must have a LimitPrice')
|
|
70
82
|
|
|
71
83
|
order.set_order_state(order_state=OrderState.Placed, timestamp=self.timestamp)
|
|
84
|
+
short_circuit = self._check_short_circuit(order=order)
|
|
72
85
|
|
|
73
|
-
if
|
|
74
|
-
self.
|
|
86
|
+
if short_circuit:
|
|
87
|
+
self.on_order(order=order, **kwargs)
|
|
88
|
+
# in short circuit mode, the worst price will be applied.
|
|
89
|
+
self._match(order=order, match_price=self.worst_price(order.limit_price, self.last_price, side=order.side))
|
|
75
90
|
|
|
91
|
+
self.working[order.order_id] = order
|
|
76
92
|
self.on_order(order=order, **kwargs)
|
|
77
93
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
@classmethod
|
|
95
|
+
def best_price(cls, *price, side: TransactionSide | int):
|
|
96
|
+
if side > 0:
|
|
97
|
+
return min(_ for _ in price if _ is not None and np.isfinite(_))
|
|
98
|
+
elif side < 0:
|
|
99
|
+
return max(_ for _ in price if _ is not None and np.isfinite(_))
|
|
100
|
+
|
|
101
|
+
raise ValueError(f'Invalid side {side}!')
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def worst_price(cls, *price, side: TransactionSide | int):
|
|
105
|
+
if side > 0:
|
|
106
|
+
return max(_ for _ in price if _ is not None and np.isfinite(_))
|
|
107
|
+
elif side < 0:
|
|
108
|
+
return min(_ for _ in price if _ is not None and np.isfinite(_))
|
|
109
|
+
|
|
110
|
+
raise ValueError(f'Invalid side {side}!')
|
|
83
111
|
|
|
84
112
|
def cancel_order(self, order: TradeInstruction = None, order_id: str = None, **kwargs):
|
|
85
113
|
if order is None and order_id is None:
|
|
@@ -104,6 +132,15 @@ class SimMatch(object):
|
|
|
104
132
|
self.history[order_id] = order
|
|
105
133
|
self.on_order(order=order, **kwargs)
|
|
106
134
|
|
|
135
|
+
def _check_short_circuit(self, order: TradeInstruction, **kwargs):
|
|
136
|
+
if order.limit_price is None and self.last_price is None:
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
if self.matching_config['instant_fill'] and all(not _ for _ in self.matching_config['lag'].values()):
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
return False
|
|
143
|
+
|
|
107
144
|
def _check_bar_data(self, market_data: BarData):
|
|
108
145
|
for order_id in list(self.working):
|
|
109
146
|
order = self.working.get(order_id)
|
|
@@ -285,10 +322,11 @@ class SimMatch(object):
|
|
|
285
322
|
self.cancel_order(order_id=order_id)
|
|
286
323
|
|
|
287
324
|
def clear(self):
|
|
288
|
-
# self.fee_rate = 0.
|
|
289
325
|
self.working.clear()
|
|
290
326
|
self.history.clear()
|
|
327
|
+
|
|
291
328
|
self.timestamp = 0.
|
|
329
|
+
self.last_price = None
|
|
292
330
|
|
|
293
331
|
@property
|
|
294
332
|
def market_time(self) -> datetime.datetime:
|
|
@@ -2,6 +2,8 @@ import codecs
|
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
4
|
import setuptools
|
|
5
|
+
from setuptools import Extension
|
|
6
|
+
from setuptools.command.build_ext import build_ext
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
def read(rel_path):
|
|
@@ -21,6 +23,32 @@ def get_version(rel_path):
|
|
|
21
23
|
raise RuntimeError("Unable to find version string.")
|
|
22
24
|
|
|
23
25
|
|
|
26
|
+
class BuildExtWithFallback(build_ext):
|
|
27
|
+
"""Custom build_ext to handle Cython compilation with fallback."""
|
|
28
|
+
|
|
29
|
+
def run(self):
|
|
30
|
+
try:
|
|
31
|
+
print("Attempting to compile Cython modules...")
|
|
32
|
+
super().run()
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print("Cython compilation failed:", e)
|
|
35
|
+
print("Falling back to pure Python implementation.")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Skip building ext_modules in CI
|
|
39
|
+
if os.getenv('GITHUB_ACTIONS') == 'true':
|
|
40
|
+
print("Skipping ext_modules as we're in a CI environment.")
|
|
41
|
+
ext_modules = []
|
|
42
|
+
else:
|
|
43
|
+
# Define Cython extension (use the .pyx file)
|
|
44
|
+
ext_modules = [
|
|
45
|
+
Extension(
|
|
46
|
+
"algo_engine.base.market_utils_posix",
|
|
47
|
+
["algo_engine/base/market_utils_posix.pyx"],
|
|
48
|
+
)
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
24
52
|
long_description = read("README.md")
|
|
25
53
|
|
|
26
54
|
setuptools.setup(
|
|
@@ -58,6 +86,8 @@ setuptools.setup(
|
|
|
58
86
|
"bokeh"
|
|
59
87
|
],
|
|
60
88
|
},
|
|
89
|
+
ext_modules=ext_modules,
|
|
90
|
+
cmdclass={"build_ext": BuildExtWithFallback},
|
|
61
91
|
command_options={
|
|
62
92
|
'nuitka': {
|
|
63
93
|
# boolean option, e.g. if you cared for C compilation commands
|
|
@@ -68,6 +98,11 @@ setuptools.setup(
|
|
|
68
98
|
# '--enable-plugin': ("setup.py", "pyside2"),
|
|
69
99
|
# options with several values, e.g. avoiding including modules
|
|
70
100
|
# '--nofollow-import-to': ("setup.py", ["*.tests", "*.distutils"]),
|
|
101
|
+
# disable LTO
|
|
102
|
+
'--lto': ("setup.py", 'yes'),
|
|
103
|
+
# include some common 3rd party packages
|
|
104
|
+
'--include-package': ("setup.py", ['ctypes', 'datetime', 'typing', 'multiprocessing']),
|
|
105
|
+
# '--mode': ("setup.py", 'standalone')
|
|
71
106
|
}
|
|
72
107
|
}
|
|
73
108
|
)
|
|
File without changes
|
|
File without changes
|
{pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/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.6.post8 → pyalgoengine-0.7.7a1}/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
|
{pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/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
|