Skip to content

Commit 8eedb39

Browse files
committed
update to v1.0.1, change return value in subprocess to avoid large object copy between processes
1 parent 5da004b commit 8eedb39

File tree

9 files changed

+32
-26
lines changed

9 files changed

+32
-26
lines changed

demeter/core/_typing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,3 @@ class BacktestConfig:
5555
print_actions:bool = False
5656
print_result: bool = False
5757
interval: str = "1min"
58-
callback: Callable[[Actuator], None] | None = None
59-
# save_result = False

demeter/core/actuator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def run(self, print_result: bool = True):
476476
self.print_result()
477477

478478
self.__backtest_duration = time.time() - self.__start_time
479-
self.logger.info(f"Backtesting finished, execute time {time.time() - self.__start_time}s")
479+
self.logger.info(f"Backtest with process id: {os.getpid()} finished, execute time {(time.time() - self.__start_time):.3f}s")
480480

481481
def _generate_account_status_df(self):
482482
self._account_status_df: pd.DataFrame = AccountStatus.to_dataframe(self._account_status_list)
@@ -596,6 +596,7 @@ def init_strategy(self):
596596
self._strategy.actions = self._action_list
597597
self._strategy.assets = self.broker.assets
598598
self._strategy.account_status_df = self.account_status_df
599+
self._strategy.actuator = self
599600
self._strategy.comment_last_action = self.comment_last_action
600601
self._strategy.log = self._log
601602
for k, v in self.broker.markets.items():

demeter/core/backtest.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import os
33
import platform
4+
import time
45
import traceback
56
from multiprocessing import Pool, cpu_count, set_start_method
67
from multiprocessing.pool import ApplyResult
@@ -26,8 +27,8 @@ def _start_with_param_data(config: StrategyConfig, data: BacktestData, strategy:
2627
return _start(config, data, strategy, bk_config)
2728

2829

29-
def _start(config: StrategyConfig, data: BacktestData, strategy: Strategy, bk_config: BacktestConfig) -> Actuator:
30-
logger.info(f"Process id: {os.getpid()}, id of data object {id(data)}")
30+
def _start(config: StrategyConfig, data: BacktestData, strategy: Strategy, bk_config: BacktestConfig):
31+
logger.info(f"Start with process id: {os.getpid()}, id of data object {id(data)}")
3132
actuator = Actuator()
3233
for market in config.markets:
3334
# add market to broker
@@ -42,9 +43,6 @@ def _start(config: StrategyConfig, data: BacktestData, strategy: Strategy, bk_co
4243
actuator.print_action = bk_config.print_actions
4344
actuator.interval = bk_config.interval
4445
actuator.run(bk_config.print_result)
45-
if bk_config.callback is not None:
46-
bk_config.callback(actuator)
47-
return actuator
4846

4947

5048
def e_callback(e):
@@ -78,14 +76,14 @@ def run(self):
7876
raise RuntimeError("Config has not set")
7977
if self.data is None:
8078
raise RuntimeError("Data has not set")
81-
79+
start_time = time.time() # 1681718968.267463
8280
if len(self.strategies) < 1:
8381
return
8482
elif len(self.strategies) == 1 or self.threads == 1:
8583
# start in single thread by default
8684
for strategy in self.strategies:
87-
_start_with_param_data(self.config, self.data, strategy, self.backtest_config)
88-
85+
actuator = _start_with_param_data(self.config, self.data, strategy, self.backtest_config)
86+
e_callback(actuator)
8987
else:
9088
if self.threads > cpu_count():
9189
raise RuntimeError("Threads should lower than " + cpu_count())
@@ -105,7 +103,7 @@ def run(self):
105103
tasks.append(result1)
106104
[x.wait() for x in tasks]
107105
else:
108-
set_start_method("fork") # ensure linux and macos have the same behavior
106+
set_start_method("fork") # ensure linux and macos have the same behavior
109107
global global_data
110108
global_data = self.data # to keep there only one instance among processes
111109
with Pool(processes=self.threads) as pool:
@@ -123,3 +121,4 @@ def run(self):
123121
tasks.append(result1)
124122
[x.wait() for x in tasks]
125123
pass
124+
logger.info(f"All backtest finished, total execute time {(time.time() - start_time):.3f}s")

demeter/strategy/strategy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from .trigger import Trigger
77
from .. import Broker, MarketDict, AccountStatus, AssetDict, Asset, Snapshot
8+
9+
# from ..core import Actuator
810
from .._typing import DemeterError
911
from ..broker import MarketInfo, BaseAction, Market
1012

@@ -25,6 +27,7 @@ def __init__(self):
2527
self.comment_last_action: Callable[[str], None] | None = None
2628
self.assets: AssetDict[Asset] = AssetDict()
2729
self.actions: List[BaseAction] = []
30+
self.actuator = None
2831
self.log: Callable[[datetime, str, int], None] | None = None
2932

3033
def initialize(self):

docs/source/concurrent.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ class DemoStrategy(Strategy):
4141
self.comment_last_action("Add liquidity because ...") # add comment to last transaction
4242

4343

44-
def after_backtest(actuator: Actuator):
45-
# save result and etc.
46-
files = actuator.save_result(path="./result", file_name=f"strategy-{actuator.strategy.lp_range}")
47-
pass
44+
def finalize(self):
45+
# save result and etc.
46+
files = self.actuator.save_result(path="./result", file_name=f"strategy-{self.lp_range}")
47+
pass
4848

4949

5050
"""

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
project = 'Demeter'
1616
copyright = '2024, zelos research'
1717
author = 'zelos research'
18-
release = '1.0.0'
18+
release = '1.0.1'
1919

2020
# -- General configuration ---------------------------------------------------
2121
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

release_note.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Ver 1.0.1
2+
3+
[Breaking change] Remove callback in BacktestManager, because if subprocess return actuator,
4+
it will cause object copy between subprocess and main process, which will cost a lot of time.
5+
You can do saving or calculating performance in Strategy.finialize().
6+
17
# Ver 1.0.0
28

39
* Add `BacktestManager` who can start multiple backtest in one or multiple subprocesses.

samples/strategy-example/62_multiprocess.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,10 @@ def add(self, snapshot: Snapshot):
4646
) # add liquidity
4747
self.comment_last_action("Add liquidity because ...") # add comment to last transaction
4848

49-
50-
def after_backtest(actuator: Actuator):
51-
# save result and etc.
52-
files = actuator.save_result(path="./result", file_name=f"strategy-{actuator.strategy.lp_range}")
53-
pass
49+
def finalize(self):
50+
# save result and etc.
51+
files = self.actuator.save_result(path="./result", file_name=f"strategy-{self.lp_range}")
52+
pass
5453

5554

5655
"""
@@ -85,9 +84,9 @@ def after_backtest(actuator: Actuator):
8584
backtest = BacktestManager(
8685
config=strategy_config,
8786
data=BacktestData({market_key: data_df}, price_data),
88-
strategies=[DemoStrategy((1000, 3000)), DemoStrategy((1500, 2500))], # two strategies
89-
backtest_config=BacktestConfig(callback=after_backtest),
90-
threads=2, # concurrent number
87+
strategies=[DemoStrategy((1000, 3000)), DemoStrategy((1500, 2500))], # two strategies
88+
backtest_config=BacktestConfig(),
89+
threads=2, # concurrent number
9190
)
9291
backtest.run()
9392
pass

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
setup(
99
name="zelos-demeter",
10-
version="1.0.0",
10+
version="1.0.1",
1111
packages=find_packages(exclude=["tests", "tests.*", "samples", "samples.*"]),
1212
url="https://zelos-demeter.readthedocs.io",
1313
license="MIT",

0 commit comments

Comments
 (0)