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.
Files changed (53) hide show
  1. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PKG-INFO +1 -1
  2. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
  3. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/__init__.py +1 -1
  4. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/sim_match.py +50 -12
  5. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/setup.py +35 -0
  6. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/LICENSE +0 -0
  7. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
  8. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
  9. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/requires.txt +0 -0
  10. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/PyAlgoEngine.egg-info/top_level.txt +0 -0
  11. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/README.md +0 -0
  12. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/__init__.py +0 -0
  13. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/__init__.py +0 -0
  14. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/doc_server.py +0 -0
  15. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/tester.py +0 -0
  16. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/backtest/web_app.py +0 -0
  17. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/bokeh_server.py +0 -0
  18. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/demo/__init__.py +0 -0
  19. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/demo/test.py +0 -0
  20. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/__init__.py +0 -0
  21. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/client.py +0 -0
  22. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
  23. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
  24. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/apps/sim_input/window.py +0 -0
  25. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/__init__.py +0 -0
  26. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/__main__.py +0 -0
  27. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/metrics.py +0 -0
  28. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/backtest/replay.py +0 -0
  29. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/__init__.py +0 -0
  30. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/console_utils.py +0 -0
  31. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/finance_decimal.py +0 -0
  32. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_buffer.py +0 -0
  33. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_utils.py +0 -0
  34. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_utils_nt.py +0 -0
  35. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/market_utils_posix.py +0 -0
  36. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/technical_analysis.py +0 -0
  37. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/telemetrics.py +0 -0
  38. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/base/trade_utils.py +0 -0
  39. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/__init__.py +0 -0
  40. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/algo_engine.py +0 -0
  41. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/event_engine.py +0 -0
  42. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/market_engine.py +0 -0
  43. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/engine/trade_engine.py +0 -0
  44. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/monitor/__init__.py +0 -0
  45. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/monitor/advanced_data_interface.py +0 -0
  46. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/profile/__init__.py +0 -0
  47. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/profile/cn.py +0 -0
  48. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/strategy/__init__.py +0 -0
  49. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/strategy/strategy_engine.py +0 -0
  50. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/utils/__init__.py +0 -0
  51. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/utils/commit_regularizer.py +0 -0
  52. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/algo_engine/utils/data_utils.py +0 -0
  53. {pyalgoengine-0.7.6.post8 → pyalgoengine-0.7.7a1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyAlgoEngine
3
- Version: 0.7.6.post8
3
+ Version: 0.7.7a1
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyAlgoEngine
3
- Version: 0.7.6.post8
3
+ Version: 0.7.7a1
4
4
  Summary: Basic algo engine
5
5
  Home-page: https://github.com/BolunHan/PyAlgoEngine
6
6
  Author: Bolun.Han
@@ -1,4 +1,4 @@
1
- __version__ = "0.7.6.post8"
1
+ __version__ = "0.7.7.alpha1"
2
2
 
3
3
  import logging
4
4
  import os
@@ -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, instant_fill: bool = False, event_engine=None, topic_set=None, fee_rate: float = 0.):
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 not self.instant_fill:
74
- self.working[order.order_id] = order
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
- if self.instant_fill:
79
- if limit := order.limit_price:
80
- self._match(order=order, match_price=limit)
81
- else:
82
- LOGGER.warning(f'No limit price provided for {order}, instant_fill mode not available.')
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
  )