pfund 0.0.1.dev5__tar.gz → 0.0.1.dev7__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 (174) hide show
  1. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/PKG-INFO +5 -8
  2. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/README.md +0 -1
  3. pfund-0.0.1.dev7/pfund/analyzer.py +3 -0
  4. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/configuration.py +0 -7
  5. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config_handler.py +38 -6
  6. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/const/commons.py +3 -1
  7. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/const/paths.py +8 -4
  8. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/data_tools/data_tool_base.py +0 -5
  9. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/data_tools/data_tool_pandas.py +8 -0
  10. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/engines/backtest_engine.py +123 -15
  11. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/engines/base_engine.py +12 -8
  12. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/engines/trade_engine.py +10 -11
  13. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/engines/train_engine.py +6 -4
  14. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/exchange.py +1 -1
  15. pfund-0.0.1.dev7/pfund/investment_profile.py +16 -0
  16. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/mixins/backtest.py +31 -13
  17. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/models/model_backtest.py +22 -11
  18. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/models/model_base.py +22 -8
  19. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/portfolio.py +1 -1
  20. pfund-0.0.1.dev7/pfund/strategies/allocation_strategy.py +10 -0
  21. pfund-0.0.1.dev7/pfund/strategies/diversification_strategy.py +13 -0
  22. pfund-0.0.1.dev7/pfund/strategies/hedging_strategy.py +10 -0
  23. pfund-0.0.1.dev7/pfund/strategies/optimization_strategy.py +13 -0
  24. pfund-0.0.1.dev7/pfund/strategies/portfolio_strategy.py +27 -0
  25. pfund-0.0.1.dev7/pfund/strategies/rebalancing_strategy.py +10 -0
  26. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/strategies/strategy_backtest.py +21 -8
  27. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/strategies/strategy_base.py +30 -16
  28. pfund-0.0.1.dev7/pfund/types/backtest.py +10 -0
  29. pfund-0.0.1.dev7/pfund/types/common_literals.py +25 -0
  30. pfund-0.0.1.dev7/pfund/types/core.py +14 -0
  31. pfund-0.0.1.dev7/pfund/universe.py +19 -0
  32. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/utils/utils.py +2 -0
  33. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pyproject.toml +15 -9
  34. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/LICENSE +0 -0
  35. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/CONTRIBUTING.md +0 -0
  36. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/__init__.py +0 -0
  37. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/accounts/__init__.py +0 -0
  38. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/accounts/account_base.py +0 -0
  39. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/accounts/account_crypto.py +0 -0
  40. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/accounts/account_ib.py +0 -0
  41. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/adapter.py +0 -0
  42. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/balances/__init__.py +0 -0
  43. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/balances/balance_base.py +0 -0
  44. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/balances/balance_crypto.py +0 -0
  45. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/balances/balance_ib.py +0 -0
  46. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/__init__.py +0 -0
  47. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/broker_backtest.py +0 -0
  48. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/broker_base.py +0 -0
  49. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/broker_crypto.py +0 -0
  50. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/broker_live.py +0 -0
  51. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/ib/__init__.py +0 -0
  52. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/ib/broker_ib.py +0 -0
  53. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/ib/ib_api.py +0 -0
  54. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/ib/ib_client.py +0 -0
  55. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/brokers/ib/ib_wrapper.py +0 -0
  56. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/cli/__init__.py +0 -0
  57. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/cli/commands/__init__.py +0 -0
  58. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/cli/commands/config.py +0 -0
  59. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/cli/commands/docker_compose.py +0 -0
  60. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/cli/main.py +0 -0
  61. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/config.yml +0 -0
  62. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/lot_sizes_inverse.yml +0 -0
  63. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/lot_sizes_linear.yml +0 -0
  64. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/lot_sizes_option.yml +0 -0
  65. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/lot_sizes_spot.yml +0 -0
  66. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/pdt_matchings_inverse.yml +0 -0
  67. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/pdt_matchings_linear.yml +0 -0
  68. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/pdt_matchings_option.yml +0 -0
  69. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/pdt_matchings_spot.yml +0 -0
  70. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/tick_sizes_inverse.yml +0 -0
  71. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/tick_sizes_linear.yml +0 -0
  72. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/tick_sizes_option.yml +0 -0
  73. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/bybit/tick_sizes_spot.yml +0 -0
  74. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/ib/config.yml +0 -0
  75. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/config/logging.yml +0 -0
  76. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/const/__init__.py +0 -0
  77. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/const/_zmq_routes.py +0 -0
  78. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/__init__.py +0 -0
  79. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/data_bar.py +0 -0
  80. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/data_base.py +0 -0
  81. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/data_quote.py +0 -0
  82. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/data_tick.py +0 -0
  83. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/data_time_based.py +0 -0
  84. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/resolution.py +0 -0
  85. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/datas/timeframe.py +0 -0
  86. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/engines/__init__.py +0 -0
  87. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/engines/test_engine.py +0 -0
  88. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/errors.py +0 -0
  89. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/__init__.py +0 -0
  90. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/__init__.py +0 -0
  91. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api.py +0 -0
  92. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_inverse +0 -0
  93. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_linear +0 -0
  94. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_option +0 -0
  95. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_spot +0 -0
  96. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_inverse +0 -0
  97. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_linear +0 -0
  98. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_option +0 -0
  99. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_spot +0 -0
  100. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/types.py +0 -0
  101. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/bybit/ws_api.py +0 -0
  102. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/exchange_base.py +0 -0
  103. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/rest_api_base.py +0 -0
  104. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/exchanges/ws_api_base.py +0 -0
  105. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/__init__.py +0 -0
  106. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/account_summary_tags.py +0 -0
  107. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/client.py +0 -0
  108. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/comm.py +0 -0
  109. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/commission_report.py +0 -0
  110. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/common.py +0 -0
  111. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/connection.py +0 -0
  112. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/contract.py +0 -0
  113. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/decoder.py +0 -0
  114. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/enum_implem.py +0 -0
  115. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/errors.py +0 -0
  116. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/execution.py +0 -0
  117. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/ibapi.pyproj +0 -0
  118. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/message.py +0 -0
  119. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/news.py +0 -0
  120. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/object_implem.py +0 -0
  121. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/order.py +0 -0
  122. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/order_condition.py +0 -0
  123. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/order_state.py +0 -0
  124. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/orderdecoder.py +0 -0
  125. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/reader.py +0 -0
  126. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/scanner.py +0 -0
  127. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/server_versions.py +0 -0
  128. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/softdollartier.py +0 -0
  129. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/tag_value.py +0 -0
  130. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/ticktype.py +0 -0
  131. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/utils.py +0 -0
  132. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/externals/ibapi/wrapper.py +0 -0
  133. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/indicators/__init__.py +0 -0
  134. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/indicators/indicator_base.py +0 -0
  135. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/indicators/ta_indicator.py +0 -0
  136. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/indicators/talib_indicator.py +0 -0
  137. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/main.py +0 -0
  138. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/__init__.py +0 -0
  139. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/base_manager.py +0 -0
  140. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/connection_manager.py +0 -0
  141. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/data_manager.py +0 -0
  142. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/order_manager.py +0 -0
  143. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/portfolio_manager.py +0 -0
  144. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/risk_manager.py +0 -0
  145. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/managers/strategy_manager.py +0 -0
  146. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/models/__init__.py +0 -0
  147. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/models/model_meta.py +0 -0
  148. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/models/pytorch_model.py +0 -0
  149. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/models/sklearn_model.py +0 -0
  150. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/orders/__init__.py +0 -0
  151. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/orders/order_base.py +0 -0
  152. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/orders/order_crypto.py +0 -0
  153. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/orders/order_ib.py +0 -0
  154. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/orders/order_statuses.py +0 -0
  155. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/orders/order_time_in_force.py +0 -0
  156. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/plogging/__init__.py +0 -0
  157. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/plogging/config.py +0 -0
  158. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/plogging/filters.py +0 -0
  159. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/plogging/formatter.py +0 -0
  160. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/plogging/handlers.py +0 -0
  161. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/positions/__init__.py +0 -0
  162. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/positions/position_base.py +0 -0
  163. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/positions/position_crypto.py +0 -0
  164. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/positions/position_ib.py +0 -0
  165. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/products/__init__.py +0 -0
  166. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/products/product_base.py +0 -0
  167. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/products/product_crypto.py +0 -0
  168. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/products/product_ib.py +0 -0
  169. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/risk_monitor.py +0 -0
  170. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/strategies/__init__.py +0 -0
  171. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/strategies/strategy_meta.py +0 -0
  172. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/utils/aliases.py +0 -0
  173. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/utils/envs.py +0 -0
  174. {pfund-0.0.1.dev5 → pfund-0.0.1.dev7}/pfund/zeromq.py +0 -0
@@ -1,28 +1,26 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pfund
3
- Version: 0.0.1.dev5
3
+ Version: 0.0.1.dev7
4
4
  Summary: A Complete Algo-Trading Framework for Machine Learning, enabling trading across TradFi, CeFi and DeFi. Supports Vectorized and Event-Driven Backtesting, Paper and Live Trading
5
5
  Home-page: https://pfund.ai
6
6
  License: Apache-2.0
7
7
  Keywords: trading,algo-trading,stocks,cryptos,cryptocurrencies,TradFi,CeFi,DeFi,portfolio management,investment,backtesting,machine learning
8
8
  Author: Stephen Yau
9
9
  Author-email: softwareentrepreneer+pfund@gmail.com
10
- Requires-Python: >=3.10,<3.12
10
+ Requires-Python: >=3.10,<3.13
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
15
16
  Requires-Dist: click (>=8.1.7,<9.0.0)
16
- Requires-Dist: orjson (>=3.9.14,<4.0.0)
17
- Requires-Dist: pfeed (>=0.0.1.dev5,<0.0.2)
17
+ Requires-Dist: gitpython (>=3.1.43,<4.0.0)
18
+ Requires-Dist: pfeed (>=0.0.1.dev7,<0.0.2)
18
19
  Requires-Dist: platformdirs (>=4.2.0,<5.0.0)
19
- Requires-Dist: psutil (>=5.9.8,<6.0.0)
20
20
  Requires-Dist: python-telegram-bot (>=20.7,<21.0)
21
21
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
22
- Requires-Dist: pyzmq (>=25.1.2,<26.0.0)
23
22
  Requires-Dist: rich (>=13.7.0,<14.0.0)
24
23
  Requires-Dist: schedule (>=1.2.1,<2.0.0)
25
- Requires-Dist: ta (>=0.11.0,<0.12.0)
26
24
  Requires-Dist: websocket-client (>=1.7.0,<2.0.0)
27
25
  Project-URL: Documentation, https://pfund-docs.pfund.ai
28
26
  Project-URL: Repository, https://github.com/PFund-Software-Ltd/pfund
@@ -131,7 +129,6 @@ This overview already omits some intricate steps, such as data handling and API
131
129
 
132
130
 
133
131
  ## Installation
134
- > Python 3.12 is not supported until [PyTorch]supports it
135
132
 
136
133
  ### Using [Poetry] (Recommended)
137
134
  ```bash
@@ -101,7 +101,6 @@ This overview already omits some intricate steps, such as data handling and API
101
101
 
102
102
 
103
103
  ## Installation
104
- > Python 3.12 is not supported until [PyTorch]supports it
105
104
 
106
105
  ### Using [Poetry] (Recommended)
107
106
  ```bash
@@ -0,0 +1,3 @@
1
+ class Analyzer:
2
+ def __init__(self):
3
+ pass
@@ -6,9 +6,6 @@ from pfund.const.paths import PROJ_CONFIG_PATH
6
6
  from pfund.utils.utils import short_path
7
7
 
8
8
 
9
- has_printed = False
10
-
11
-
12
9
  class Configuration:
13
10
  def __init__(self, config_dir, config_name):
14
11
  self.config_dir = config_dir.lower()
@@ -24,16 +21,12 @@ class Configuration:
24
21
  return self.config_dir
25
22
 
26
23
  def read_config(self, config_name):
27
- global has_printed
28
24
  file_path = f'{self.config_path}/{config_name}.yml'
29
25
  # short_file_path = short_path(file_path)
30
26
  if not os.path.exists(file_path):
31
27
  print(f'cannot find config {file_path}')
32
28
  else:
33
29
  with open(file_path, 'r') as f:
34
- if not has_printed:
35
- has_printed = True
36
- print(f'loaded config {file_path}')
37
30
  return list(yaml.safe_load_all(f))
38
31
 
39
32
  def write_config(self, config_name, content):
@@ -25,11 +25,12 @@ def _custom_excepthook(exception_class: type[BaseException], exception: BaseExce
25
25
  logging.getLogger(PROJ_NAME).exception('Uncaught exception:')
26
26
 
27
27
 
28
- def import_strategies_models_features_or_indicators(path: str):
28
+ def dynamic_import(path: str):
29
29
  for item in os.listdir(path):
30
30
  item_path = os.path.join(path, item)
31
31
  if os.path.isdir(item_path) and '__pycache__' not in item_path:
32
- for type_ in ['strategies', 'models', 'features', 'indicators']:
32
+ for type_ in ['strategies', 'models', 'features', 'indicators',
33
+ 'backtests', 'notebooks', 'spreadsheets', 'dashboards']:
33
34
  if type_ in path:
34
35
  break
35
36
  else:
@@ -65,17 +66,48 @@ class ConfigHandler:
65
66
  config = {}
66
67
  return cls(**config)
67
68
 
69
+ @property
70
+ def strategy_path(self):
71
+ return f'{self.data_path}/hub/strategies'
72
+
73
+ @property
74
+ def model_path(self):
75
+ return f'{self.data_path}/hub/models'
76
+
77
+ @property
78
+ def feature_path(self):
79
+ return f'{self.data_path}/hub/features'
80
+
81
+ @property
82
+ def indicator_path(self):
83
+ return f'{self.data_path}/hub/indicators'
84
+
85
+ @property
86
+ def backtest_path(self):
87
+ return f'{self.data_path}/backtests'
88
+
89
+ @property
90
+ def notebook_path(self):
91
+ return f'{self.data_path}/templates/notebooks'
92
+
93
+ @property
94
+ def spreadsheet_path(self):
95
+ return f'{self.data_path}/templates/spreadsheets'
96
+
97
+ @property
98
+ def dashboard_path(self):
99
+ return f'{self.data_path}/templates/dashboards'
100
+
68
101
  def __post_init__(self):
69
102
  self.logging_config = self.logging_config or {}
70
103
 
71
- strategy_path, model_path = f'{self.data_path}/strategies', f'{self.data_path}/models'
72
- feature_path, indicator_path = f'{self.data_path}/features', f'{self.data_path}/indicators'
73
- for path in [strategy_path, model_path, feature_path, indicator_path]:
104
+ for path in [self.strategy_path, self.model_path, self.feature_path, self.indicator_path,
105
+ self.backtest_path, self.notebook_path, self.spreadsheet_path, self.dashboard_path]:
74
106
  if not os.path.exists(path):
75
107
  os.makedirs(path)
76
108
  print(f'created {path}')
77
109
  sys.path.append(path)
78
- import_strategies_models_features_or_indicators(path)
110
+ dynamic_import(path)
79
111
 
80
112
  if self.use_fork_process and sys.platform != 'win32':
81
113
  multiprocessing.set_start_method('fork', force=True)
@@ -18,4 +18,6 @@ SUPPORTED_TIMEFRAMES = [
18
18
  'weeks', 'week', 'w',
19
19
  'months', 'month', 'M',
20
20
  ]
21
- SUPPORTED_DATA_CHANNELS = ['orderbook', 'tradebook', 'kline']
21
+ SUPPORTED_DATA_CHANNELS = ['orderbook', 'tradebook', 'kline']
22
+ SUPPORTED_BACKTEST_MODES = ['vectorized', 'event_driven']
23
+ SUPPORTED_DATA_TOOLS = ['pandas']
@@ -16,7 +16,11 @@ LOG_PATH = Path(user_log_dir()) / PROJ_NAME
16
16
  USER_CONFIG_PATH = Path(user_config_dir()) / PROJ_NAME
17
17
  USER_CONFIG_FILE_PATH = USER_CONFIG_PATH / f'{PROJ_NAME}_config.yml'
18
18
  DATA_PATH = Path(user_data_dir()) / PROJ_NAME
19
- STRATEGY_PATH = DATA_PATH / 'strategies'
20
- MODEL_PATH = DATA_PATH / 'models'
21
- FEATURE_PATH = DATA_PATH / 'features'
22
- INDICATOR_PATH = DATA_PATH / 'indicators'
19
+ STRATEGY_PATH = DATA_PATH / 'hub' / 'strategies'
20
+ MODEL_PATH = DATA_PATH / 'hub' / 'models'
21
+ FEATURE_PATH = DATA_PATH / 'hub' / 'features'
22
+ INDICATOR_PATH = DATA_PATH / 'hub' / 'indicators'
23
+ BACKTEST_PATH = DATA_PATH / 'backtests'
24
+ NOTEBOOK_PATH = DATA_PATH / 'templates' / 'notebooks'
25
+ SPREADSHEET_PATH = DATA_PATH / 'templates' / 'spreadsheets'
26
+ DASHBOARD_PATH = DATA_PATH / 'templates' / 'dashboards'
@@ -1,12 +1,7 @@
1
- from typing import Literal
2
-
3
1
  from pfund.datas.data_base import BaseData
4
2
  from pfund.utils.utils import convert_ts_to_dt
5
3
 
6
4
 
7
- DataTool = Literal['pandas']
8
-
9
-
10
5
  class BaseDataTool:
11
6
  def __init__(self):
12
7
  self.train_periods = {} # {product: ('start_date', 'end_date')}
@@ -1,5 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from decimal import Decimal
3
+ from pathlib import Path
3
4
 
4
5
  import pandas as pd
5
6
 
@@ -150,6 +151,13 @@ class PandasDataTool(BaseDataTool):
150
151
  index=pd.MultiIndex(levels=[[]]*len(index_names), codes=[[]]*len(index_names), names=index_names)
151
152
  )
152
153
 
154
+ def output_df_to_parquet(self, name: str, df: pd.DataFrame,path: str | Path):
155
+ if '.parquet' not in name:
156
+ name = name + '.parquet'
157
+ if type(path) is str:
158
+ path = Path(path)
159
+ df.to_parquet(path / name)
160
+
153
161
  def _create_multi_index(self, index_data: dict, index_names: list[str]) -> pd.MultiIndex:
154
162
  return pd.MultiIndex.from_tuples([tuple(index_data[name] for name in index_names)], names=index_names)
155
163
 
@@ -1,21 +1,27 @@
1
- from typing import Literal
1
+ import hashlib
2
+ import os
3
+ import time
4
+ import datetime
5
+ import json
6
+ import uuid
2
7
 
3
- from pfund.data_tools.data_tool_base import DataTool
8
+ from typing import TYPE_CHECKING
9
+ if TYPE_CHECKING:
10
+ from pfund.types.common_literals import tSUPPORTED_BACKTEST_MODES, tSUPPORTED_DATA_TOOLS
11
+
12
+ import pfund as pf
13
+ from pfund.types.core import tStrategy, tModel, tFeature, tIndicator
4
14
  from pfund.engines.base_engine import BaseEngine
5
- from pfund.models.model_base import BaseModel, BaseFeature
6
- from pfund.indicators.indicator_base import BaseIndicator
7
15
  from pfund.brokers.broker_backtest import BacktestBroker
8
16
  from pfund.strategies.strategy_base import BaseStrategy
9
17
  from pfund.strategies.strategy_backtest import BacktestStrategy
10
- from pfund.const.commons import *
11
18
  from pfund.config_handler import ConfigHandler
19
+ from pfund.utils import utils
20
+ from pfund.mixins.backtest import BacktestMixin
12
21
 
13
22
 
14
- BacktestMode = Literal['vectorized', 'event_driven']
15
-
16
-
17
23
  class BacktestEngine(BaseEngine):
18
- def __new__(cls, *, env: str='BACKTEST', data_tool: DataTool='pandas', mode: BacktestMode='vectorized', append_to_strategy_df=False, use_prepared_signals=True, config: ConfigHandler | None=None, **settings):
24
+ def __new__(cls, *, env: str='BACKTEST', data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', mode: 'tSUPPORTED_BACKTEST_MODES'='vectorized', append_to_strategy_df=False, use_prepared_signals=True, config: ConfigHandler | None=None, **settings):
19
25
  if not hasattr(cls, 'mode'):
20
26
  cls.mode = mode.lower()
21
27
  if not hasattr(cls, 'append_to_strategy_df'):
@@ -26,13 +32,15 @@ class BacktestEngine(BaseEngine):
26
32
  cls.use_prepared_signals = use_prepared_signals
27
33
  return super().__new__(cls, env, data_tool=data_tool, config=config, **settings)
28
34
 
29
- def __init__(self, *, env: str='BACKTEST', data_tool: DataTool='pandas', mode: BacktestMode='vectorized', append_to_strategy_df=False, use_prepared_signals=True, config: ConfigHandler | None=None, **settings):
35
+ def __init__(self, *, env: str='BACKTEST', data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', mode: 'tSUPPORTED_BACKTEST_MODES'='vectorized', append_to_strategy_df=False, use_prepared_signals=True, config: ConfigHandler | None=None, **settings):
30
36
  super().__init__(env, data_tool=data_tool)
31
37
  # avoid re-initialization to implement singleton class correctly
32
38
  # if not hasattr(self, '_initialized'):
33
39
  # pass
34
40
 
35
- def add_strategy(self, strategy: BaseStrategy, name: str='', is_parallel=False) -> BaseStrategy:
41
+ # HACK: since python doesn't support dynamic typing, true return type should be subclass of BacktestMixin and tStrategy
42
+ # write -> BacktestMixin | tStrategy for better intellisense in IDEs
43
+ def add_strategy(self, strategy: tStrategy, name: str='', is_parallel=False) -> BacktestMixin | tStrategy:
36
44
  is_dummy_strategy_exist = '_dummy' in self.strategy_manager.strategies
37
45
  assert not is_dummy_strategy_exist, 'dummy strategy is being used for model backtesting, adding another strategy is not allowed'
38
46
  if is_parallel:
@@ -42,7 +50,7 @@ class BacktestEngine(BaseEngine):
42
50
  strategy = BacktestStrategy(type(strategy), *strategy._args, **strategy._kwargs)
43
51
  return super().add_strategy(strategy, name=name, is_parallel=is_parallel)
44
52
 
45
- def add_model(self, model: BaseModel, name: str='', model_path: str='', is_load: bool=True) -> BaseModel:
53
+ def add_model(self, model: tModel, name: str='', model_path: str='', is_load: bool=True) -> BacktestMixin | tModel:
46
54
  '''Add model without creating a strategy (using dummy strategy)'''
47
55
  is_non_dummy_strategy_exist = bool([strat for strat in self.strategy_manager.strategies if strat != '_dummy'])
48
56
  assert not is_non_dummy_strategy_exist, 'Please use strategy.add_model(...) instead of engine.add_model(...) when a strategy is already created'
@@ -57,10 +65,10 @@ class BacktestEngine(BaseEngine):
57
65
  model = strategy.add_model(model, name=name, model_path=model_path, is_load=is_load)
58
66
  return model
59
67
 
60
- def add_feature(self, feature: BaseFeature, name: str='', feature_path: str='', is_load: bool=True) -> BaseFeature:
68
+ def add_feature(self, feature: tFeature, name: str='', feature_path: str='', is_load: bool=True) -> BacktestMixin | tFeature:
61
69
  return self.add_model(feature, name=name, model_path=feature_path, is_load=is_load)
62
70
 
63
- def add_indicator(self, indicator: BaseIndicator, name: str='', indicator_path: str='', is_load: bool=True) -> BaseIndicator:
71
+ def add_indicator(self, indicator: tIndicator, name: str='', indicator_path: str='', is_load: bool=True) -> BacktestMixin | tIndicator:
64
72
  return self.add_model(indicator, name=name, model_path=indicator_path, is_load=is_load)
65
73
 
66
74
  def add_broker(self, bkr: str):
@@ -73,12 +81,108 @@ class BacktestEngine(BaseEngine):
73
81
  self.brokers[bkr] = broker
74
82
  self.logger.debug(f'added {bkr=}')
75
83
  return broker
84
+
85
+ @staticmethod
86
+ def _generate_backtest_id() -> str:
87
+ return uuid.uuid4().hex
88
+
89
+ def _create_backtest_name(self, strat: str):
90
+ local_tz = utils.get_local_timezone()
91
+ utcnow = datetime.datetime.now(tz=local_tz).strftime('%Y-%m-%d_%H:%M:%S_UTC%z')
92
+ backtest_id = self._generate_backtest_id()
93
+ return '.'.join([strat, utcnow, backtest_id])
94
+
95
+ @staticmethod
96
+ def _generate_backtest_hash(strategy: BaseStrategy):
97
+ '''Generate hash based on strategy for backtest traceability
98
+ backtest_hash is used to identify if the backtests are generated by the same strategy.
99
+ Useful for avoiding overfitting the strategy on the same dataset.
100
+ '''
101
+ # REVIEW: currently only use strategy to generate hash, may include other settings in the future
102
+ strategy_dict = strategy.to_dict()
103
+ # since conceptually backtest_hash should be the same regardless of the
104
+ # strategy_signature (params) and data_signatures (e.g. backtest_kwargs, train_kwargs, data_source, resolution etc.)
105
+ # remove them
106
+ del strategy_dict['strategy_signature']
107
+ del strategy_dict['data_signatures']
108
+ strategy_str = json.dumps(strategy_dict)
109
+ return hashlib.sha256(strategy_str.encode()).hexdigest()
110
+
111
+ def read_json(self, file_name: str) -> dict:
112
+ '''Reads json file from backtest_path'''
113
+ file_path = os.path.join(self.config.backtest_path, file_name)
114
+ backtest_json = {}
115
+ try:
116
+ if os.path.exists(file_path):
117
+ with open(file_path, 'r') as f:
118
+ backtest_json = json.load(f)
119
+ except:
120
+ self.logger.exception(f"Error reading from {file_path}:")
121
+ return backtest_json
122
+
123
+ def _write_json(self, file_name: str, json_file: dict) -> None:
124
+ '''Writes json file to backtest_path'''
125
+ file_path = os.path.join(self.config.backtest_path, file_name)
126
+ try:
127
+ with open(file_path, 'w') as f:
128
+ json.dump(json_file, f, indent=4)
129
+ except:
130
+ self.logger.exception(f"Error writing to {file_path}:")
131
+
132
+ def _generate_backtest_iteration(self, strategy: BaseStrategy) -> int:
133
+ '''Generate backtest iteration number for the same backtest_hash.
134
+ Read the existing backtest.json file to get the iteration number for the same strategy hash
135
+ If the backtest hash is not found, create a new entry with iteration number 1
136
+ else increment the iteration number by 1.
137
+ '''
138
+ backtest_hash = self._generate_backtest_hash(strategy)
139
+ file_name = 'backtest.json'
140
+ backtest_json = self.read_json(file_name)
141
+ backtest_json[backtest_hash] = backtest_json.get(backtest_hash, 0) + 1
142
+ self._write_json(file_name, backtest_json)
143
+ return backtest_json[backtest_hash]
144
+
145
+ def _write_backtest_history(self, backtest_name: str, backtest_name_trimmed: str, start_time: float, end_time: float):
146
+ splits = backtest_name.split('.')
147
+ strat, backtest_id = splits[0], splits[-1]
148
+ strategy = self.get_strategy(strat)
149
+ local_tz = utils.get_local_timezone()
150
+ duration = end_time - start_time
151
+ backtest_history = {
152
+ 'settings': self.settings,
153
+ 'metadata': {
154
+ 'pfund_version': pf.__version__,
155
+ 'backtest_name': backtest_name,
156
+ 'backtest_id': backtest_id,
157
+ 'backtest_iteration': self._generate_backtest_iteration(strategy),
158
+ 'duration': f'{duration:.2f}s' if duration > 1 else f'{duration*1000:.2f}ms',
159
+ 'start_time': datetime.datetime.fromtimestamp(start_time, tz=local_tz).strftime('%Y-%m-%dT%H:%M:%S%z'),
160
+ 'end_time': datetime.datetime.fromtimestamp(end_time, tz=local_tz).strftime('%Y-%m-%dT%H:%M:%S%z'),
161
+ },
162
+ 'strategy': strategy.to_dict(),
163
+ 'results': {
164
+ 'df_file_path': os.path.join(self.config.backtest_path, f'{backtest_name_trimmed}.parquet'),
165
+ }
166
+ }
167
+ self._write_json(f'{backtest_name_trimmed}.json', backtest_history)
168
+
169
+ def trim_backtest_name(self, backtest_name: str) -> str:
170
+ splits = backtest_name.split('.')
171
+ backtest_id_len = 12
172
+ splits[-1] = splits[-1][:backtest_id_len]
173
+ return '.'.join(splits)
174
+
175
+ def output_backtest_results(self, strat: str, df, start_time: float, end_time: float):
176
+ backtest_name = self._create_backtest_name(strat)
177
+ backtest_name_trimmed = self.trim_backtest_name(backtest_name)
178
+ self.data_tool.output_df_to_parquet(backtest_name_trimmed, df, self.config.backtest_path)
179
+ self._write_backtest_history(backtest_name, backtest_name_trimmed, start_time, end_time)
76
180
 
77
181
  def run(self):
78
182
  for broker in self.brokers.values():
79
183
  broker.start()
80
184
  self.strategy_manager.start()
81
-
185
+
82
186
  if self.mode == 'vectorized':
83
187
  for strat, strategy in self.strategy_manager.strategies.items():
84
188
  # _dummy strategy is only created for model training, do nothing
@@ -86,7 +190,11 @@ class BacktestEngine(BaseEngine):
86
190
  continue
87
191
  if not hasattr(strategy, 'backtest'):
88
192
  raise Exception(f'Strategy {strat} does not have backtest() method, cannot run vectorized backtesting')
193
+ start_time = time.time()
89
194
  strategy.backtest()
195
+ end_time = time.time()
196
+ df = strategy.get_df()
197
+ self.output_backtest_results(strat, df, start_time, end_time)
90
198
  elif self.mode == 'event_driven':
91
199
  for strat, strategy in self.strategy_manager.strategies.items():
92
200
  if strat == '_dummy':
@@ -2,14 +2,18 @@ import os
2
2
  import logging
3
3
  import importlib
4
4
 
5
+ from typing import TYPE_CHECKING
6
+ if TYPE_CHECKING:
7
+ from pfund.types.core import tStrategy
8
+ from pfund.types.common_literals import tSUPPORTED_DATA_TOOLS
9
+
5
10
  from rich.console import Console
6
11
 
7
12
  from pfund.utils.utils import Singleton
8
- from pfund.data_tools.data_tool_base import DataTool
9
13
  from pfund.strategies.strategy_base import BaseStrategy
10
14
  from pfund.brokers.broker_base import BaseBroker
11
15
  from pfund.managers.strategy_manager import StrategyManager
12
- from pfund.const.commons import *
16
+ from pfund.const.commons import SUPPORTED_ENVIRONMENTS, SUPPORTED_BROKERS
13
17
  from pfund.config_handler import ConfigHandler
14
18
  from pfund.plogging import set_up_loggers
15
19
  from pfund.plogging.config import LoggingDictConfigurator
@@ -27,9 +31,9 @@ ENV_COLORS = {
27
31
  class BaseEngine(Singleton):
28
32
  _PROCESS_NO_PONG_TOLERANCE_IN_SECONDS = 30
29
33
 
30
- def __new__(cls, env, data_tool: DataTool='pandas', config: ConfigHandler | None=None, **settings):
34
+ def __new__(cls, env, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', config: ConfigHandler | None=None, **settings):
31
35
  if not hasattr(cls, 'env'):
32
- cls.env = env.upper() if type(env) is str else str(env).upper()
36
+ cls.env = env.upper() if isinstance(env, str) else str(env).upper()
33
37
  assert cls.env in SUPPORTED_ENVIRONMENTS, f'env={cls.env} is not supported'
34
38
 
35
39
  # TODO, do NOT allow LIVE env for now
@@ -50,7 +54,7 @@ class BaseEngine(Singleton):
50
54
  cls.logging_configurator: LoggingDictConfigurator = set_up_loggers(log_path, logging_config_file_path, user_logging_config=cls.config.logging_config)
51
55
  return super().__new__(cls)
52
56
 
53
- def __init__(self, env, data_tool: DataTool='pandas', config: ConfigHandler | None=None, **settings):
57
+ def __init__(self, env, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', config: ConfigHandler | None=None, **settings):
54
58
  # avoid re-initialization to implement singleton class correctly
55
59
  if not hasattr(self, '_initialized'):
56
60
  self.logger = logging.getLogger('pfund')
@@ -70,7 +74,7 @@ class BaseEngine(Singleton):
70
74
  def get_strategy(self, strat: str) -> BaseStrategy | None:
71
75
  return self.strategy_manager.get_strategy(strat)
72
76
 
73
- def add_strategy(self, strategy: BaseStrategy, name: str='', is_parallel=False) -> BaseStrategy:
77
+ def add_strategy(self, strategy: 'tStrategy', name: str='', is_parallel=False) -> 'tStrategy':
74
78
  return self.strategy_manager.add_strategy(strategy, name=name, is_parallel=is_parallel)
75
79
 
76
80
  def remove_strategy(self, strat: str):
@@ -83,7 +87,7 @@ class BaseEngine(Singleton):
83
87
  bkr = bkr.upper()
84
88
  assert bkr in SUPPORTED_BROKERS, f'broker {bkr} is not supported'
85
89
  if bkr == 'CRYPTO':
86
- Broker = getattr(importlib.import_module(f'pfund.brokers.broker_{bkr.lower()}'), f'CryptoBroker')
90
+ Broker = getattr(importlib.import_module(f'pfund.brokers.broker_{bkr.lower()}'), 'CryptoBroker')
87
91
  elif bkr == 'IB':
88
- Broker = getattr(importlib.import_module(f'pfund.brokers.ib.broker_{bkr.lower()}'), f'IBBroker')
92
+ Broker = getattr(importlib.import_module(f'pfund.brokers.ib.broker_{bkr.lower()}'), 'IBBroker')
89
93
  return Broker
@@ -8,19 +8,19 @@ components at the highest level such as:
8
8
  strategies (your trading strategies)
9
9
  In order to communicate with other processes, it uses ZeroMQ as the core
10
10
  message queue.
11
-
12
- Please refer to #TODO for more examples.
13
11
  """
14
12
  import time
15
13
  from threading import Thread
16
14
 
15
+ from typing import TYPE_CHECKING
16
+ if TYPE_CHECKING:
17
+ from pfund.types.common_literals import tSUPPORTED_DATA_TOOLS
18
+
17
19
  import schedule
18
20
  import psutil
19
21
 
20
- from pfund.data_tools.data_tool_base import DataTool
21
22
  from pfund.engines.base_engine import BaseEngine
22
23
  from pfund.brokers.broker_base import BaseBroker
23
- from pfund.const.commons import *
24
24
  from pfund.utils.utils import flatten_dict
25
25
  from pfund.zeromq import ZeroMQ
26
26
  from pfund.config_handler import ConfigHandler
@@ -29,13 +29,13 @@ from pfund.config_handler import ConfigHandler
29
29
  class TradeEngine(BaseEngine):
30
30
  zmq_ports = {}
31
31
 
32
- def __new__(cls, *, env: str='PAPER', data_tool: DataTool='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
32
+ def __new__(cls, *, env: str='PAPER', data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
33
33
  if not hasattr(cls, 'zmq_port'):
34
- assert type(zmq_port) is int, f'{zmq_port=} must be an integer'
34
+ assert isinstance(zmq_port, int), f'{zmq_port=} must be an integer'
35
35
  cls._zmq_port = zmq_port
36
36
  return super().__new__(cls, env, data_tool=data_tool, config=config, **settings)
37
37
 
38
- def __init__(self, *, env: str='PAPER', data_tool: DataTool='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
38
+ def __init__(self, *, env: str='PAPER', data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
39
39
  super().__init__(env, data_tool=data_tool)
40
40
  # avoid re-initialization to implement singleton class correctly
41
41
  if not hasattr(self, '_initialized'):
@@ -157,7 +157,7 @@ class TradeEngine(BaseEngine):
157
157
  schedule.run_all() # run all tasks at start
158
158
  self._background_thread = Thread(target=self._run_background_tasks, daemon=True)
159
159
  self._background_thread.start()
160
- self.logger.debug(f'background thread started')
160
+ self.logger.debug('background thread started')
161
161
 
162
162
  # TEMP, zeromq examples
163
163
  # self._zmq.send(
@@ -174,7 +174,6 @@ class TradeEngine(BaseEngine):
174
174
  # )
175
175
 
176
176
  while self._is_running:
177
- lats = []
178
177
  if msg := self._zmq.recv():
179
178
  channel, topic, info = msg
180
179
  # TODO
@@ -193,7 +192,7 @@ class TradeEngine(BaseEngine):
193
192
  schedule.clear()
194
193
  self._is_running = False
195
194
  while self._background_thread.is_alive():
196
- self.logger.debug(f'waiting for background thread to finish')
195
+ self.logger.debug('waiting for background thread to finish')
197
196
  time.sleep(1)
198
197
  else:
199
- self.logger.debug(f'background thread is finished')
198
+ self.logger.debug('background thread is finished')
@@ -1,14 +1,16 @@
1
+ from typing import TYPE_CHECKING
2
+ if TYPE_CHECKING:
3
+ from pfund.types.common_literals import tSUPPORTED_BACKTEST_MODES, tSUPPORTED_DATA_TOOLS
4
+
1
5
  from pfund.engines.backtest_engine import BacktestEngine
2
- from pfund.data_tools.data_tool_base import DataTool
3
- from pfund.engines.backtest_engine import BacktestMode
4
6
  from pfund.config_handler import ConfigHandler
5
7
 
6
8
 
7
9
  class TrainEngine(BacktestEngine):
8
- def __new__(cls, *, data_tool: DataTool='pandas', mode: BacktestMode='vectorized', config: ConfigHandler | None=None, **settings):
10
+ def __new__(cls, *, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', mode: 'tSUPPORTED_BACKTEST_MODES'='vectorized', config: ConfigHandler | None=None, **settings):
9
11
  return super().__new__(cls, env='TRAIN', data_tool=data_tool, mode=mode, config=config, **settings)
10
12
 
11
- def __init__(self, *, data_tool: DataTool='pandas', mode: BacktestMode='vectorized', config: ConfigHandler | None=None, **settings):
13
+ def __init__(self, *, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', mode: 'tSUPPORTED_BACKTEST_MODES'='vectorized', config: ConfigHandler | None=None, **settings):
12
14
  super().__init__(env='TRAIN', data_tool=data_tool)
13
15
  # avoid re-initialization to implement singleton class correctly
14
16
  # if not hasattr(self, '_initialized'):
@@ -236,7 +236,7 @@ class Exchange(BaseExchange):
236
236
  """
237
237
  def _convert_to_date(time_):
238
238
  if type(time_) is float:
239
- date = datetime.datetime.fromtimestamp(tz=datetime.timezone.utc)
239
+ date = datetime.datetime.fromtimestamp(time_, tz=datetime.timezone.utc)
240
240
  elif type(time_) is str:
241
241
  date = datetime.datetime.strptime(time_, date_format)
242
242
  date = date.replace(tzinfo=datetime.timezone.utc)
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass
2
+
3
+ from typing import TYPE_CHECKING
4
+ if TYPE_CHECKING:
5
+ from pfund.types.common_literals import tSUPPORTED_PRODUCT_TYPES, tSUPPORTED_CRYPTO_PRODUCT_TYPES
6
+
7
+
8
+ # TODO
9
+ @dataclass
10
+ class InvestmentProfile:
11
+ investment_objectives: list[str]
12
+ risk_tolerance: str
13
+ investment_horizon: str
14
+ asset_classes: list[tSUPPORTED_PRODUCT_TYPES | tSUPPORTED_CRYPTO_PRODUCT_TYPES]
15
+ diversification: str
16
+ rebalancing_period: str