pfund 0.0.1.dev9__tar.gz → 0.0.1.dev11__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 (187) hide show
  1. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/PKG-INFO +6 -2
  2. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/CONTRIBUTING.md +3 -0
  3. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/adapter.py +4 -7
  4. pfund-0.0.1.dev11/pfund/analyzer.py +198 -0
  5. pfund-0.0.1.dev11/pfund/config/binance/linear/config.yml +7 -0
  6. pfund-0.0.1.dev11/pfund/config/binance/linear/lot_sizes_linear.yml +2 -0
  7. pfund-0.0.1.dev11/pfund/config/binance/linear/pdt_matchings_linear.yml +2 -0
  8. pfund-0.0.1.dev11/pfund/config/binance/linear/tick_sizes_linear.yml +2 -0
  9. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/configuration.py +5 -7
  10. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config_handler.py +4 -0
  11. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/const/paths.py +1 -0
  12. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/data_tools/data_tool_pandas.py +2 -6
  13. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/engines/backtest_engine.py +21 -28
  14. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/engines/base_engine.py +5 -3
  15. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/engines/trade_engine.py +11 -12
  16. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/engines/train_engine.py +4 -2
  17. pfund-0.0.1.dev11/pfund/exchanges/binance/__init__.py +3 -0
  18. pfund-0.0.1.dev11/pfund/exchanges/binance/exchange.py +37 -0
  19. pfund-0.0.1.dev11/pfund/exchanges/binance/linear/exchange.py +6 -0
  20. pfund-0.0.1.dev11/pfund/exchanges/binance/rest_api.py +15 -0
  21. pfund-0.0.1.dev11/pfund/exchanges/binance/ws_api.py +33 -0
  22. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/__init__.py +1 -2
  23. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/exchange.py +10 -6
  24. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api.py +3 -1
  25. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/ws_api.py +4 -2
  26. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/exchange_base.py +12 -9
  27. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/investment_profile.py +2 -0
  28. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/strategy_manager.py +1 -1
  29. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/models/model_backtest.py +2 -2
  30. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/strategy_backtest.py +6 -1
  31. pfund-0.0.1.dev11/pfund/templates/dashboards/pfund-overview.streamlit.py +0 -0
  32. pfund-0.0.1.dev11/pfund/templates/notebooks/pfund-analytics.ipynb +49 -0
  33. pfund-0.0.1.dev11/pfund/templates/notebooks/pfund-overview.ipynb +49 -0
  34. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/utils/utils.py +11 -0
  35. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pyproject.toml +14 -4
  36. pfund-0.0.1.dev9/pfund/analyzer.py +0 -3
  37. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/LICENSE +0 -0
  38. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/README.md +0 -0
  39. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/__init__.py +0 -0
  40. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/accounts/__init__.py +0 -0
  41. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/accounts/account_base.py +0 -0
  42. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/accounts/account_crypto.py +0 -0
  43. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/accounts/account_ib.py +0 -0
  44. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/balances/__init__.py +0 -0
  45. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/balances/balance_base.py +0 -0
  46. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/balances/balance_crypto.py +0 -0
  47. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/balances/balance_ib.py +0 -0
  48. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/__init__.py +0 -0
  49. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/broker_backtest.py +0 -0
  50. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/broker_base.py +0 -0
  51. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/broker_crypto.py +0 -0
  52. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/broker_live.py +0 -0
  53. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/ib/__init__.py +0 -0
  54. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/ib/broker_ib.py +0 -0
  55. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/ib/ib_api.py +0 -0
  56. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/ib/ib_client.py +0 -0
  57. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/brokers/ib/ib_wrapper.py +0 -0
  58. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/cli/__init__.py +0 -0
  59. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/cli/commands/__init__.py +0 -0
  60. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/cli/commands/config.py +0 -0
  61. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/cli/commands/docker_compose.py +0 -0
  62. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/cli/main.py +0 -0
  63. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/config.yml +0 -0
  64. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/lot_sizes_inverse.yml +0 -0
  65. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/lot_sizes_linear.yml +0 -0
  66. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/lot_sizes_option.yml +0 -0
  67. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/lot_sizes_spot.yml +0 -0
  68. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/pdt_matchings_inverse.yml +0 -0
  69. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/pdt_matchings_linear.yml +0 -0
  70. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/pdt_matchings_option.yml +0 -0
  71. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/pdt_matchings_spot.yml +0 -0
  72. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/tick_sizes_inverse.yml +0 -0
  73. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/tick_sizes_linear.yml +0 -0
  74. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/tick_sizes_option.yml +0 -0
  75. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/bybit/tick_sizes_spot.yml +0 -0
  76. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/ib/config.yml +0 -0
  77. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/config/logging.yml +0 -0
  78. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/const/__init__.py +0 -0
  79. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/const/_zmq_routes.py +0 -0
  80. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/const/commons.py +0 -0
  81. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/data_tools/data_tool_base.py +0 -0
  82. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/__init__.py +0 -0
  83. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/data_bar.py +0 -0
  84. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/data_base.py +0 -0
  85. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/data_quote.py +0 -0
  86. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/data_tick.py +0 -0
  87. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/data_time_based.py +0 -0
  88. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/resolution.py +0 -0
  89. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/datas/timeframe.py +0 -0
  90. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/engines/__init__.py +0 -0
  91. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/engines/test_engine.py +0 -0
  92. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/errors.py +0 -0
  93. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/__init__.py +0 -0
  94. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_inverse +0 -0
  95. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_linear +0 -0
  96. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_option +0 -0
  97. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_spot +0 -0
  98. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_inverse +0 -0
  99. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_linear +0 -0
  100. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_option +0 -0
  101. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_spot +0 -0
  102. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/rest_api_base.py +0 -0
  103. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/exchanges/ws_api_base.py +0 -0
  104. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/__init__.py +0 -0
  105. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/account_summary_tags.py +0 -0
  106. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/client.py +0 -0
  107. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/comm.py +0 -0
  108. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/commission_report.py +0 -0
  109. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/common.py +0 -0
  110. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/connection.py +0 -0
  111. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/contract.py +0 -0
  112. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/decoder.py +0 -0
  113. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/enum_implem.py +0 -0
  114. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/errors.py +0 -0
  115. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/execution.py +0 -0
  116. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/ibapi.pyproj +0 -0
  117. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/message.py +0 -0
  118. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/news.py +0 -0
  119. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/object_implem.py +0 -0
  120. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/order.py +0 -0
  121. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/order_condition.py +0 -0
  122. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/order_state.py +0 -0
  123. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/orderdecoder.py +0 -0
  124. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/reader.py +0 -0
  125. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/scanner.py +0 -0
  126. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/server_versions.py +0 -0
  127. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/softdollartier.py +0 -0
  128. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/tag_value.py +0 -0
  129. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/ticktype.py +0 -0
  130. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/utils.py +0 -0
  131. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/externals/ibapi/wrapper.py +0 -0
  132. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/indicators/__init__.py +0 -0
  133. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/indicators/indicator_base.py +0 -0
  134. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/indicators/ta_indicator.py +0 -0
  135. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/indicators/talib_indicator.py +0 -0
  136. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/main.py +0 -0
  137. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/__init__.py +0 -0
  138. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/base_manager.py +0 -0
  139. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/connection_manager.py +0 -0
  140. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/data_manager.py +0 -0
  141. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/order_manager.py +0 -0
  142. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/portfolio_manager.py +0 -0
  143. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/managers/risk_manager.py +0 -0
  144. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/mixins/backtest.py +0 -0
  145. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/models/__init__.py +0 -0
  146. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/models/model_base.py +0 -0
  147. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/models/model_meta.py +0 -0
  148. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/models/pytorch_model.py +0 -0
  149. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/models/sklearn_model.py +0 -0
  150. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/orders/__init__.py +0 -0
  151. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/orders/order_base.py +0 -0
  152. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/orders/order_crypto.py +0 -0
  153. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/orders/order_ib.py +0 -0
  154. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/orders/order_statuses.py +0 -0
  155. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/orders/order_time_in_force.py +0 -0
  156. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/plogging/__init__.py +0 -0
  157. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/plogging/config.py +0 -0
  158. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/plogging/filters.py +0 -0
  159. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/plogging/formatter.py +0 -0
  160. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/plogging/handlers.py +0 -0
  161. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/portfolio.py +0 -0
  162. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/positions/__init__.py +0 -0
  163. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/positions/position_base.py +0 -0
  164. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/positions/position_crypto.py +0 -0
  165. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/positions/position_ib.py +0 -0
  166. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/products/__init__.py +0 -0
  167. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/products/product_base.py +0 -0
  168. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/products/product_crypto.py +0 -0
  169. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/products/product_ib.py +0 -0
  170. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/risk_monitor.py +0 -0
  171. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/__init__.py +0 -0
  172. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/allocation_strategy.py +0 -0
  173. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/diversification_strategy.py +0 -0
  174. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/hedging_strategy.py +0 -0
  175. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/optimization_strategy.py +0 -0
  176. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/portfolio_strategy.py +0 -0
  177. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/rebalancing_strategy.py +0 -0
  178. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/strategy_base.py +0 -0
  179. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/strategies/strategy_meta.py +0 -0
  180. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/types/backtest.py +0 -0
  181. /pfund-0.0.1.dev9/pfund/exchanges/bybit/types.py → /pfund-0.0.1.dev11/pfund/types/bybit.py +0 -0
  182. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/types/common_literals.py +0 -0
  183. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/types/core.py +0 -0
  184. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/universe.py +0 -0
  185. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/utils/aliases.py +0 -0
  186. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/utils/envs.py +0 -0
  187. {pfund-0.0.1.dev9 → pfund-0.0.1.dev11}/pfund/zeromq.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pfund
3
- Version: 0.0.1.dev9
3
+ Version: 0.0.1.dev11
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
@@ -13,13 +13,16 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Provides-Extra: analytics
16
17
  Provides-Extra: data
17
18
  Provides-Extra: ml
18
19
  Requires-Dist: click (>=8.1.7,<9.0.0)
19
20
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
20
21
  Requires-Dist: mlflow (>=2.11.3,<3.0.0) ; extra == "ml"
21
22
  Requires-Dist: orjson (>=3.9.14,<4.0.0) ; extra == "data"
22
- Requires-Dist: pfeed[boost,data,df] (>=0.0.1.dev9,<0.0.2) ; extra == "data"
23
+ Requires-Dist: papermill (>=2.5.0,<3.0.0) ; extra == "analytics"
24
+ Requires-Dist: pfeed[boost,data,df] (>=0.0.1.dev10,<0.0.2) ; extra == "data"
25
+ Requires-Dist: pfolio[bayesian,data,portfolio,temporary] (>=0.0.1.dev4,<0.0.2) ; extra == "analytics"
23
26
  Requires-Dist: platformdirs (>=4.2.0,<5.0.0)
24
27
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
25
28
  Requires-Dist: python-telegram-bot (>=20.7,<21.0)
@@ -32,6 +35,7 @@ Requires-Dist: scikit-learn (>=1.4.0,<2.0.0) ; extra == "ml"
32
35
  Requires-Dist: ta (>=0.11.0,<0.12.0) ; extra == "ml"
33
36
  Requires-Dist: torch (>=2.1.2,<3.0.0) ; extra == "ml"
34
37
  Requires-Dist: tqdm (>=4.66.2,<5.0.0)
38
+ Requires-Dist: voila (>=0.5.6,<0.6.0) ; extra == "analytics"
35
39
  Requires-Dist: websocket-client (>=1.7.0,<2.0.0)
36
40
  Project-URL: Documentation, https://pfund-docs.pfund.ai
37
41
  Project-URL: Repository, https://github.com/PFund-Software-Ltd/pfund
@@ -16,4 +16,7 @@ git pull --recurse-submodules # = git pull + git submodule update --recursive
16
16
  ```bash
17
17
  # at the root directory, run:
18
18
  jb build docs/ [--all]
19
+
20
+ # check if external links are broken:
21
+ jb build docs/ --builder linkcheck
19
22
  ```
@@ -1,12 +1,10 @@
1
1
  import yaml
2
2
  import os
3
3
 
4
- from pfund.const.paths import PROJ_CONFIG_PATH
5
-
6
4
 
7
5
  class Adapter:
8
- def __init__(self, trading_venue, adapter_dict):
9
- self._trading_venue = trading_venue
6
+ def __init__(self, config_path, adapter_dict):
7
+ self.config_path = config_path
10
8
  self._adapter_dict = adapter_dict
11
9
  self._adapter = {}
12
10
  self._ref_keys = []
@@ -25,9 +23,8 @@ class Adapter:
25
23
  return pdt
26
24
 
27
25
  def load_pdt_matchings(self):
28
- file_path = f'{PROJ_CONFIG_PATH}/{self._trading_venue.lower()}'
29
26
  config_name = 'pdt_matchings'
30
- for file_name in os.listdir(file_path):
27
+ for file_name in os.listdir(self.config_path):
31
28
  if not file_name.startswith(config_name):
32
29
  continue
33
30
  file_splits = file_name.split('_')
@@ -35,7 +32,7 @@ class Adapter:
35
32
  category = file_splits[-1].split('.')[0]
36
33
  else:
37
34
  category = ''
38
- with open(file_path + '/' + file_name, 'r') as f:
35
+ with open(self.config_path + '/' + file_name, 'r') as f:
39
36
  if pdt_macthings := yaml.safe_load(f):
40
37
  for pdt, epdt in pdt_macthings.items():
41
38
  self.update(pdt, epdt, ref_key=category)
@@ -0,0 +1,198 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ from typing import Literal
5
+
6
+ from pfund.utils import utils
7
+ from pfund.config_handler import ConfigHandler
8
+ from pfund.const.paths import PROJ_PATH
9
+
10
+
11
+ class Analyzer:
12
+ try:
13
+ Engine = utils.get_engine_class()
14
+ config = Engine.config
15
+ except:
16
+ config = ConfigHandler.load_config()
17
+
18
+ notebook_path = Path(config.notebook_path)
19
+ spreadsheet_path = Path(config.spreadsheet_path)
20
+ dashboard_path = Path(config.dashboard_path)
21
+
22
+ def __init__(self, data: dict | None=None):
23
+ self.data = data or {}
24
+
25
+ @staticmethod
26
+ def _is_file(template: str) -> bool:
27
+ if '\\' in template or '/' in template:
28
+ assert Path(template).resolve().is_file(), f"File {template} does not exist"
29
+ return True
30
+ return False
31
+
32
+ @staticmethod
33
+ def _derive_template_type(template: str) -> Literal['notebook', 'spreadsheet', 'dashboard']:
34
+ if '.ipynb' in template:
35
+ template_type = 'notebook'
36
+ elif '.grid' in template:
37
+ template_type = 'spreadsheet'
38
+ elif '.py' in template:
39
+ template_type = 'dashboard'
40
+ else:
41
+ raise ValueError(f"Template {template} is not a valid template, only .ipynb, .grid, .py are supported.")
42
+ return template_type
43
+
44
+ def _find_template(self, template: str) -> str:
45
+ '''Check if the template exists in pfund's templates or user's templates
46
+ e.g. template = 'notebook.ipynb' or 'spreadsheet.grid' or 'dashboard.py'
47
+ '''
48
+ template_type = self._derive_template_type(template)
49
+ pfund_templates_dir = PROJ_PATH / 'templates' / (template_type+'s')
50
+ user_templates_dir = getattr(self, f'{template_type}_path')
51
+ for templates_dir in [pfund_templates_dir, user_templates_dir]:
52
+ for file_name in os.listdir(templates_dir):
53
+ if template == file_name:
54
+ template_file_path = templates_dir / template
55
+ return str(template_file_path)
56
+ else:
57
+ raise FileNotFoundError(f"Template {template} not found in pfund's templates or user's templates")
58
+
59
+ def _get_editor_cmd(self, editor: Literal['vscode', 'pycharm']) -> str:
60
+ if editor == 'vscode':
61
+ cmd = 'code'
62
+ if utils.is_command_available(cmd):
63
+ return cmd
64
+ else:
65
+ print("VSCode command 'code' is not available, cannot open the output notebook")
66
+ elif editor == 'pycharm':
67
+ for cmd in ['charm', 'pycharm']:
68
+ if utils.is_command_available(cmd):
69
+ return cmd
70
+ else:
71
+ print("PyCharm commands 'charm'/'pycharm' are both not available, cannot open the output notebook")
72
+ else:
73
+ print(f"Editor '{editor}' is not supported, cannot open the output notebook")
74
+
75
+ def run_notebooks(
76
+ self,
77
+ notebooks: list[str] | str,
78
+ *voila_args,
79
+ data: dict | None=None,
80
+ display: bool=True,
81
+ port: int=8866,
82
+ show_results_only: bool=True,
83
+ open_outputs: bool=False,
84
+ outputs_path: str | None=None,
85
+ editor: Literal['vscode', 'pycharm']='vscode'
86
+ ) -> None:
87
+ '''
88
+ Args:
89
+ notebook:
90
+ - notebook_template's name
91
+ - notebook's full path in str or Path
92
+ voila_args: additional arguments to pass to voila
93
+ data: data to be analyzed, if None, use the data passed to the Analyzer instance during initialization
94
+ display: if True, display the notebook in voila
95
+ show_results_only: if True, display only the results (no source code) in voila
96
+ open_outputs: if True, open the output notebook in the editor
97
+ outputs_path: path to save the output notebooks, if None, do not save the output notebooks
98
+ '''
99
+ import subprocess
100
+ import papermill as pm
101
+
102
+ def _find_available_port(_port):
103
+ retry_num = 100
104
+ while retry_num:
105
+ if not utils.is_port_in_use(_port):
106
+ return _port
107
+ retry_num -= 1
108
+ _port += 1
109
+ else:
110
+ raise Exception(f"No available ports found starting from {_port - 100}, cannot display the notebook")
111
+
112
+ def _assert_voila_args_are_valid():
113
+ for arg in voila_args:
114
+ if not arg.startswith('--'):
115
+ raise ValueError(f"Voila argument '{arg}' should start with '--'")
116
+ if arg.startswith('--port='):
117
+ raise ValueError(f"Voila argument '{arg}' should not be passed in, use the 'port' argument instead")
118
+ if arg.startswith('--strip_sources='):
119
+ raise ValueError(f"Voila argument '{arg}' should not be passed in, use the 'show_results_only' argument instead")
120
+
121
+ voila_processes = []
122
+ nb_output_file_paths = []
123
+ if isinstance(notebooks, str):
124
+ notebooks = [notebooks]
125
+ data = data or self.data
126
+ if not data:
127
+ raise ValueError("No data passed in or stored in the Analyzer instance, please pass in the data to be analyzed.")
128
+
129
+ if open_outputs:
130
+ assert outputs_path is not None, f"{outputs_path=}, cannot open the output notebook without saving it."
131
+ editor_cmd = self._get_editor_cmd(editor)
132
+
133
+ _assert_voila_args_are_valid()
134
+ is_theme_provided = any(arg.startswith('--theme=') for arg in voila_args)
135
+ if not is_theme_provided:
136
+ default_theme = 'dark'
137
+ voila_args = [f'--theme={default_theme}', *voila_args]
138
+
139
+ try:
140
+ for notebook in notebooks:
141
+ if self._is_file(notebook):
142
+ nb_input_file_path: str = notebook
143
+ notebook = Path(notebook).name # e.g. 'notebook.ipynb'
144
+ else:
145
+ nb_input_file_path: str = self._find_template(notebook)
146
+ nb_output_file_path = Path(outputs_path or '.').resolve() / f'{notebook.replace(".ipynb", "")}_output.ipynb'
147
+ nb_output_file_path = str(nb_output_file_path)
148
+ nb_output_file_paths.append(nb_output_file_path)
149
+
150
+ print(f"Executing notebook: {notebook}")
151
+ pm.execute_notebook(
152
+ nb_input_file_path,
153
+ nb_output_file_path,
154
+ parameters=data
155
+ )
156
+
157
+ if open_outputs:
158
+ # e.g. code notebook_output.ipynb if using vscode
159
+ if editor_cmd:
160
+ subprocess.run([editor_cmd, nb_output_file_path])
161
+
162
+ if display:
163
+ port = _find_available_port(port)
164
+ is_last_notebook = (notebook == notebooks[-1])
165
+ subprocess_func = subprocess.run if is_last_notebook else subprocess.Popen
166
+ process = subprocess_func([
167
+ 'voila',
168
+ f'--port={port}',
169
+ f'--strip_sources={show_results_only}',
170
+ *voila_args,
171
+ nb_output_file_path
172
+ ])
173
+ voila_processes.append(process)
174
+ except KeyboardInterrupt:
175
+ print("KeyboardInterrupt: Stopping the execution of the notebooks")
176
+ except Exception:
177
+ raise
178
+ finally:
179
+ if outputs_path is None:
180
+ for nb_output_file_path in nb_output_file_paths:
181
+ print(f"{outputs_path=}, removing output notebook: {nb_output_file_path}")
182
+ os.remove(nb_output_file_path)
183
+ for process in voila_processes:
184
+ process.terminate()
185
+ process.wait()
186
+
187
+ def run_spreadsheets(
188
+ self,
189
+ spreadsheets: list[str] | str
190
+ ):
191
+ pass
192
+
193
+ # TODO:
194
+ def run_dashboards(
195
+ self,
196
+ dashboards: list[str] | str
197
+ ):
198
+ pass
@@ -0,0 +1,7 @@
1
+ ---
2
+ settings:
3
+ private_channels: []
4
+ ...
5
+ ---
6
+ adapter:
7
+ pdts: {}
@@ -0,0 +1,2 @@
1
+ # FIXME: fake
2
+ BTC_USDT_PERP: 0.001
@@ -0,0 +1,2 @@
1
+ BTC_USDT_PERP: BTCUSDT
2
+ ETH_USDT_PERP: ETHUSDT
@@ -0,0 +1,2 @@
1
+ # FIXME: fake
2
+ BTC_USDT_PERP: 0.01
@@ -2,23 +2,21 @@ import os
2
2
 
3
3
  import yaml
4
4
 
5
- from pfund.const.paths import PROJ_CONFIG_PATH
6
5
  from pfund.utils.utils import short_path
7
6
 
8
7
 
9
8
  class Configuration:
10
- def __init__(self, config_dir, config_name):
11
- self.config_dir = config_dir.lower()
9
+ def __init__(self, config_path, config_name):
10
+ self.config_path = config_path
12
11
  self.config_name = config_name
13
- self.config_path = f'{PROJ_CONFIG_PATH}/{self.config_dir}'
14
12
  self.configs = None
15
13
  self.reload()
16
14
 
17
15
  def reload(self):
18
16
  self.configs = self.read_config(self.config_name)
19
17
 
20
- def get_config_dir(self):
21
- return self.config_dir
18
+ def get_config_path(self):
19
+ return self.config_path
22
20
 
23
21
  def read_config(self, config_name):
24
22
  file_path = f'{self.config_path}/{config_name}.yml'
@@ -40,7 +38,7 @@ class Configuration:
40
38
  raise Exception(f'could not find section {section} for config {self.config_name}')
41
39
 
42
40
  def check_if_config_exists_and_not_empty(self, config_name):
43
- file_path = f'{PROJ_CONFIG_PATH}/{self.config_dir}/{config_name}.yml'
41
+ file_path = f'{self.config_path}/{config_name}.yml'
44
42
  if os.path.exists(file_path) and os.stat(file_path).st_size != 0:
45
43
  return True
46
44
  else:
@@ -98,6 +98,10 @@ class ConfigHandler:
98
98
  def dashboard_path(self):
99
99
  return f'{self.data_path}/templates/dashboards'
100
100
 
101
+ @property
102
+ def artifact_path(self):
103
+ return f'{self.data_path}/.artifacts'
104
+
101
105
  def __post_init__(self):
102
106
  self.logging_config = self.logging_config or {}
103
107
 
@@ -24,3 +24,4 @@ BACKTEST_PATH = DATA_PATH / 'backtests'
24
24
  NOTEBOOK_PATH = DATA_PATH / 'templates' / 'notebooks'
25
25
  SPREADSHEET_PATH = DATA_PATH / 'templates' / 'spreadsheets'
26
26
  DASHBOARD_PATH = DATA_PATH / 'templates' / 'dashboards'
27
+ ARTIFACT_PATH = DATA_PATH / '.artifacts'
@@ -151,12 +151,8 @@ class PandasDataTool(BaseDataTool):
151
151
  index=pd.MultiIndex(levels=[[]]*len(index_names), codes=[[]]*len(index_names), names=index_names)
152
152
  )
153
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)
154
+ def output_df_to_parquet(self, df: pd.DataFrame, file_path: str):
155
+ df.to_parquet(file_path)
160
156
 
161
157
  def _create_multi_index(self, index_data: dict, index_names: list[str]) -> pd.MultiIndex:
162
158
  return pd.MultiIndex.from_tuples([tuple(index_data[name] for name in index_names)], names=index_names)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import hashlib
2
4
  import os
3
5
  import time
@@ -8,9 +10,9 @@ import uuid
8
10
  from typing import TYPE_CHECKING
9
11
  if TYPE_CHECKING:
10
12
  from pfund.types.common_literals import tSUPPORTED_BACKTEST_MODES, tSUPPORTED_DATA_TOOLS
13
+ from pfund.types.core import tStrategy, tModel, tFeature, tIndicator
11
14
 
12
15
  import pfund as pf
13
- from pfund.types.core import tStrategy, tModel, tFeature, tIndicator
14
16
  from pfund.engines.base_engine import BaseEngine
15
17
  from pfund.brokers.broker_backtest import BacktestBroker
16
18
  from pfund.strategies.strategy_base import BaseStrategy
@@ -21,7 +23,7 @@ from pfund.mixins.backtest import BacktestMixin
21
23
 
22
24
 
23
25
  class BacktestEngine(BaseEngine):
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):
26
+ 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):
25
27
  if not hasattr(cls, 'mode'):
26
28
  cls.mode = mode.lower()
27
29
  if not hasattr(cls, 'append_to_strategy_df'):
@@ -32,7 +34,7 @@ class BacktestEngine(BaseEngine):
32
34
  cls.use_prepared_signals = use_prepared_signals
33
35
  return super().__new__(cls, env, data_tool=data_tool, config=config, **settings)
34
36
 
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):
37
+ 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):
36
38
  super().__init__(env, data_tool=data_tool)
37
39
  # avoid re-initialization to implement singleton class correctly
38
40
  # if not hasattr(self, '_initialized'):
@@ -86,11 +88,11 @@ class BacktestEngine(BaseEngine):
86
88
  def _generate_backtest_id() -> str:
87
89
  return uuid.uuid4().hex
88
90
 
89
- def _create_backtest_name(self, strat: str):
91
+ def _create_backtest_name(self, strat: str, backtest_id: str, backtest_id_length: int=12):
90
92
  local_tz = utils.get_local_timezone()
91
93
  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
+ trimmed_backtest_id = backtest_id[:backtest_id_length]
95
+ return '.'.join([strat, utcnow, trimmed_backtest_id])
94
96
 
95
97
  @staticmethod
96
98
  def _generate_backtest_hash(strategy: BaseStrategy):
@@ -142,47 +144,36 @@ class BacktestEngine(BaseEngine):
142
144
  self._write_json(file_name, backtest_json)
143
145
  return backtest_json[backtest_hash]
144
146
 
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]
147
+ def _output_backtest_results(self, strat: str, df, start_time: float, end_time: float):
148
148
  strategy = self.get_strategy(strat)
149
+ backtest_id = self._generate_backtest_id()
150
+ backtest_name = self._create_backtest_name(strat, backtest_id)
149
151
  local_tz = utils.get_local_timezone()
150
152
  duration = end_time - start_time
153
+ df_file_path = os.path.join(self.config.backtest_path, f'{backtest_name}.parquet')
151
154
  backtest_history = {
152
- 'settings': self.settings,
153
155
  'metadata': {
154
156
  'pfund_version': pf.__version__,
155
- 'backtest_name': backtest_name,
156
157
  'backtest_id': backtest_id,
157
158
  'backtest_iteration': self._generate_backtest_iteration(strategy),
158
159
  'duration': f'{duration:.2f}s' if duration > 1 else f'{duration*1000:.2f}ms',
159
160
  'start_time': datetime.datetime.fromtimestamp(start_time, tz=local_tz).strftime('%Y-%m-%dT%H:%M:%S%z'),
160
161
  'end_time': datetime.datetime.fromtimestamp(end_time, tz=local_tz).strftime('%Y-%m-%dT%H:%M:%S%z'),
162
+ 'settings': self.settings,
161
163
  },
162
164
  'strategy': strategy.to_dict(),
163
- 'results': {
164
- 'df_file_path': os.path.join(self.config.backtest_path, f'{backtest_name_trimmed}.parquet'),
165
- }
165
+ 'result': df_file_path
166
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)
167
+ self.data_tool.output_df_to_parquet(df, df_file_path)
168
+ self._write_json(f'{backtest_name}.json', backtest_history)
169
+ return backtest_history
174
170
 
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)
180
-
181
171
  def run(self):
182
172
  for broker in self.brokers.values():
183
173
  broker.start()
184
174
  self.strategy_manager.start()
185
175
 
176
+ backtests = {}
186
177
  if self.mode == 'vectorized':
187
178
  for strat, strategy in self.strategy_manager.strategies.items():
188
179
  # _dummy strategy is only created for model training, do nothing
@@ -194,7 +185,8 @@ class BacktestEngine(BaseEngine):
194
185
  strategy.backtest()
195
186
  end_time = time.time()
196
187
  df = strategy.get_df()
197
- self.output_backtest_results(strat, df, start_time, end_time)
188
+ backtest_history: dict = self._output_backtest_results(strat, df, start_time, end_time)
189
+ backtests[strat] = backtest_history
198
190
  elif self.mode == 'event_driven':
199
191
  for strat, strategy in self.strategy_manager.strategies.items():
200
192
  if strat == '_dummy':
@@ -246,6 +238,7 @@ class BacktestEngine(BaseEngine):
246
238
  else:
247
239
  raise NotImplementedError(f'Backtesting mode {self.mode} is not supported')
248
240
  self.strategy_manager.stop(reason='finished backtesting')
241
+ return backtests
249
242
 
250
243
  def end(self):
251
244
  self.strategy_manager.stop(reason='finished backtesting')
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import logging
3
5
  import importlib
@@ -31,7 +33,7 @@ ENV_COLORS = {
31
33
  class BaseEngine(Singleton):
32
34
  _PROCESS_NO_PONG_TOLERANCE_IN_SECONDS = 30
33
35
 
34
- def __new__(cls, env, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', config: ConfigHandler | None=None, **settings):
36
+ def __new__(cls, env, data_tool: tSUPPORTED_DATA_TOOLS='pandas', config: ConfigHandler | None=None, **settings):
35
37
  if not hasattr(cls, 'env'):
36
38
  cls.env = env.upper() if isinstance(env, str) else str(env).upper()
37
39
  assert cls.env in SUPPORTED_ENVIRONMENTS, f'env={cls.env} is not supported'
@@ -54,7 +56,7 @@ class BaseEngine(Singleton):
54
56
  cls.logging_configurator: LoggingDictConfigurator = set_up_loggers(log_path, logging_config_file_path, user_logging_config=cls.config.logging_config)
55
57
  return super().__new__(cls)
56
58
 
57
- def __init__(self, env, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', config: ConfigHandler | None=None, **settings):
59
+ def __init__(self, env, data_tool: tSUPPORTED_DATA_TOOLS='pandas', config: ConfigHandler | None=None, **settings):
58
60
  # avoid re-initialization to implement singleton class correctly
59
61
  if not hasattr(self, '_initialized'):
60
62
  self.logger = logging.getLogger('pfund')
@@ -74,7 +76,7 @@ class BaseEngine(Singleton):
74
76
  def get_strategy(self, strat: str) -> BaseStrategy | None:
75
77
  return self.strategy_manager.get_strategy(strat)
76
78
 
77
- def add_strategy(self, strategy: 'tStrategy', name: str='', is_parallel=False) -> 'tStrategy':
79
+ def add_strategy(self, strategy: tStrategy, name: str='', is_parallel=False) -> tStrategy:
78
80
  return self.strategy_manager.add_strategy(strategy, name=name, is_parallel=is_parallel)
79
81
 
80
82
  def remove_strategy(self, strat: str):
@@ -9,6 +9,8 @@ components at the highest level such as:
9
9
  In order to communicate with other processes, it uses ZeroMQ as the core
10
10
  message queue.
11
11
  """
12
+ from __future__ import annotations
13
+
12
14
  import time
13
15
  from threading import Thread
14
16
 
@@ -20,7 +22,7 @@ import schedule
20
22
 
21
23
  from pfund.engines.base_engine import BaseEngine
22
24
  from pfund.brokers.broker_base import BaseBroker
23
- from pfund.utils.utils import flatten_dict
25
+ from pfund.utils.utils import flatten_dict, is_port_in_use
24
26
  from pfund.zeromq import ZeroMQ
25
27
  from pfund.config_handler import ConfigHandler
26
28
 
@@ -28,13 +30,13 @@ from pfund.config_handler import ConfigHandler
28
30
  class TradeEngine(BaseEngine):
29
31
  zmq_ports = {}
30
32
 
31
- def __new__(cls, *, env: str='PAPER', data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
33
+ def __new__(cls, *, env: str='PAPER', data_tool: tSUPPORTED_DATA_TOOLS='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
32
34
  if not hasattr(cls, 'zmq_port'):
33
35
  assert isinstance(zmq_port, int), f'{zmq_port=} must be an integer'
34
36
  cls._zmq_port = zmq_port
35
37
  return super().__new__(cls, env, data_tool=data_tool, config=config, **settings)
36
38
 
37
- def __init__(self, *, env: str='PAPER', data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
39
+ def __init__(self, *, env: str='PAPER', data_tool: tSUPPORTED_DATA_TOOLS='pandas', zmq_port=5557, config: ConfigHandler | None=None, **settings):
38
40
  super().__init__(env, data_tool=data_tool)
39
41
  # avoid re-initialization to implement singleton class correctly
40
42
  if not hasattr(self, '_initialized'):
@@ -55,15 +57,12 @@ class TradeEngine(BaseEngine):
55
57
  def _assign_zmq_ports(self) -> dict:
56
58
  _assigned_ports = []
57
59
  def _is_port_available(_port):
58
- import socket
59
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
60
- _is_port_in_use = (s.connect_ex(('localhost', _port)) == 0)
61
- _is_port_assigned = (_port in _assigned_ports)
62
- if _is_port_in_use or _is_port_assigned:
63
- return False
64
- else:
65
- _assigned_ports.append(_port)
66
- return True
60
+ _is_port_assigned = (_port in _assigned_ports)
61
+ if is_port_in_use(_port) or _is_port_assigned:
62
+ return False
63
+ else:
64
+ _assigned_ports.append(_port)
65
+ return True
67
66
  def _get_port(start_port=None):
68
67
  _port = start_port or self._zmq_port
69
68
  if _is_port_available(_port):
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import TYPE_CHECKING
2
4
  if TYPE_CHECKING:
3
5
  from pfund.types.common_literals import tSUPPORTED_BACKTEST_MODES, tSUPPORTED_DATA_TOOLS
@@ -7,10 +9,10 @@ from pfund.config_handler import ConfigHandler
7
9
 
8
10
 
9
11
  class TrainEngine(BacktestEngine):
10
- def __new__(cls, *, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', mode: 'tSUPPORTED_BACKTEST_MODES'='vectorized', config: ConfigHandler | None=None, **settings):
12
+ def __new__(cls, *, data_tool: tSUPPORTED_DATA_TOOLS='pandas', mode: tSUPPORTED_BACKTEST_MODES='vectorized', config: ConfigHandler | None=None, **settings):
11
13
  return super().__new__(cls, env='TRAIN', data_tool=data_tool, mode=mode, config=config, **settings)
12
14
 
13
- def __init__(self, *, data_tool: 'tSUPPORTED_DATA_TOOLS'='pandas', mode: 'tSUPPORTED_BACKTEST_MODES'='vectorized', config: ConfigHandler | None=None, **settings):
15
+ def __init__(self, *, data_tool: tSUPPORTED_DATA_TOOLS='pandas', mode: tSUPPORTED_BACKTEST_MODES='vectorized', config: ConfigHandler | None=None, **settings):
14
16
  super().__init__(env='TRAIN', data_tool=data_tool)
15
17
  # avoid re-initialization to implement singleton class correctly
16
18
  # if not hasattr(self, '_initialized'):
@@ -0,0 +1,3 @@
1
+ from pfund.exchanges.binance.exchange import Exchange as Binance
2
+ from pfund.exchanges.binance.rest_api import RestApi as BinanceRestApi
3
+ from pfund.exchanges.binance.ws_api import WebsocketApi as BinanceWebsocketApi
@@ -0,0 +1,37 @@
1
+ from pathlib import Path
2
+
3
+ from pfund.exchanges.exchange_base import BaseExchange
4
+
5
+
6
+ class Exchange(BaseExchange):
7
+ SUPPORTED_CATEGORIES = ['linear', 'inverse', 'spot', 'option']
8
+ PTYPE_TO_CATEGORY = {
9
+ 'PERP': 'linear',
10
+ 'FUT': 'linear',
11
+ 'IPERP': 'inverse',
12
+ 'IFUT': 'inverse',
13
+ 'SPOT': 'spot',
14
+ 'OPT': 'option',
15
+ }
16
+ def __new__(cls, env: str, ptype: str):
17
+ from pfund.exchanges.binance.linear.exchange import ExchangeLinear
18
+
19
+ ptype = ptype.upper()
20
+ category = cls.PTYPE_TO_CATEGORY[ptype]
21
+
22
+ if category == 'linear':
23
+ instance = super().__new__(ExchangeLinear)
24
+ instance.category = category
25
+ return instance
26
+ # EXTEND: Add other categories
27
+ else:
28
+ raise ValueError(f"Invalid {category=}")
29
+
30
+ def __init__(self, env: str, ptype: str):
31
+ exch = Path(__file__).parent.name
32
+ super().__init__(env, exch)
33
+
34
+ # FIXME: temporarily override the method, remove it later
35
+ def _setup_configs(self):
36
+ pass
37
+
@@ -0,0 +1,6 @@
1
+ from pfund.exchanges.binance.exchange import Exchange
2
+
3
+
4
+ class ExchangeLinear(Exchange):
5
+ def __init__(self, env: str, ptype: str):
6
+ super().__init__(env, ptype)