pfund 0.0.1.dev11__tar.gz → 0.0.1.dev13__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.
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/PKG-INFO +2 -2
- pfund-0.0.1.dev13/pfund/__init__.py +70 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/analyzer.py +10 -5
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/ib/broker_ib.py +4 -2
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/const/commons.py +3 -1
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/engines/backtest_engine.py +37 -11
- pfund-0.0.1.dev13/pfund/git_controller.py +60 -0
- pfund-0.0.1.dev13/pfund/main.py +18 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/models/model_base.py +39 -1
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/strategy_base.py +39 -1
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/templates/notebooks/pfund-overview.ipynb +17 -11
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/types/common_literals.py +3 -1
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/utils/utils.py +12 -3
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pyproject.toml +13 -11
- pfund-0.0.1.dev11/pfund/__init__.py +0 -32
- pfund-0.0.1.dev11/pfund/main.py +0 -10
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/LICENSE +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/README.md +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/CONTRIBUTING.md +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/accounts/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/accounts/account_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/accounts/account_crypto.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/accounts/account_ib.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/adapter.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/balances/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/balances/balance_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/balances/balance_crypto.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/balances/balance_ib.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/broker_backtest.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/broker_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/broker_crypto.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/broker_live.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/ib/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/ib/ib_api.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/ib/ib_client.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/brokers/ib/ib_wrapper.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/cli/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/cli/commands/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/cli/commands/config.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/cli/commands/docker_compose.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/cli/main.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/binance/linear/config.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/binance/linear/lot_sizes_linear.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/binance/linear/pdt_matchings_linear.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/binance/linear/tick_sizes_linear.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/config.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/lot_sizes_inverse.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/lot_sizes_linear.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/lot_sizes_option.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/lot_sizes_spot.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/pdt_matchings_inverse.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/pdt_matchings_linear.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/pdt_matchings_option.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/pdt_matchings_spot.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/tick_sizes_inverse.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/tick_sizes_linear.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/tick_sizes_option.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/bybit/tick_sizes_spot.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/configuration.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/ib/config.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config/logging.yml +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/config_handler.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/const/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/const/_zmq_routes.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/const/paths.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/data_tools/data_tool_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/data_tools/data_tool_pandas.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/data_bar.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/data_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/data_quote.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/data_tick.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/data_time_based.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/resolution.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/datas/timeframe.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/engines/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/engines/base_engine.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/engines/test_engine.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/engines/trade_engine.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/engines/train_engine.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/errors.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/binance/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/binance/exchange.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/binance/linear/exchange.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/binance/rest_api.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/binance/ws_api.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/exchange.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_inverse +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_linear +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_option +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_result_spot +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_inverse +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_linear +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_option +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/rest_api_samples/get_markets_return_spot +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/bybit/ws_api.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/exchange_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/rest_api_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/exchanges/ws_api_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/account_summary_tags.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/client.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/comm.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/commission_report.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/common.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/connection.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/contract.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/decoder.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/enum_implem.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/errors.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/execution.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/ibapi.pyproj +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/message.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/news.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/object_implem.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/order.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/order_condition.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/order_state.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/orderdecoder.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/reader.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/scanner.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/server_versions.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/softdollartier.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/tag_value.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/ticktype.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/utils.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/externals/ibapi/wrapper.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/indicators/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/indicators/indicator_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/indicators/ta_indicator.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/indicators/talib_indicator.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/investment_profile.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/base_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/connection_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/data_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/order_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/portfolio_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/risk_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/managers/strategy_manager.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/mixins/backtest.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/models/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/models/model_backtest.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/models/model_meta.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/models/pytorch_model.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/models/sklearn_model.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/orders/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/orders/order_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/orders/order_crypto.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/orders/order_ib.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/orders/order_statuses.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/orders/order_time_in_force.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/plogging/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/plogging/config.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/plogging/filters.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/plogging/formatter.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/plogging/handlers.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/portfolio.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/positions/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/positions/position_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/positions/position_crypto.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/positions/position_ib.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/products/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/products/product_base.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/products/product_crypto.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/products/product_ib.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/risk_monitor.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/__init__.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/allocation_strategy.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/diversification_strategy.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/hedging_strategy.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/optimization_strategy.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/portfolio_strategy.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/rebalancing_strategy.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/strategy_backtest.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/strategies/strategy_meta.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/templates/dashboards/pfund-overview.streamlit.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/templates/notebooks/pfund-analytics.ipynb +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/types/backtest.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/types/bybit.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/types/core.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/universe.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/utils/aliases.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/utils/envs.py +0 -0
- {pfund-0.0.1.dev11 → pfund-0.0.1.dev13}/pfund/zeromq.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pfund
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev13
|
|
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
|
|
@@ -21,7 +21,7 @@ Requires-Dist: gitpython (>=3.1.43,<4.0.0)
|
|
|
21
21
|
Requires-Dist: mlflow (>=2.11.3,<3.0.0) ; extra == "ml"
|
|
22
22
|
Requires-Dist: orjson (>=3.9.14,<4.0.0) ; extra == "data"
|
|
23
23
|
Requires-Dist: papermill (>=2.5.0,<3.0.0) ; extra == "analytics"
|
|
24
|
-
Requires-Dist: pfeed[boost,data,df] (>=0.0.1.
|
|
24
|
+
Requires-Dist: pfeed[boost,data,df] (>=0.0.1.dev11,<0.0.2) ; extra == "data"
|
|
25
25
|
Requires-Dist: pfolio[bayesian,data,portfolio,temporary] (>=0.0.1.dev4,<0.0.2) ; extra == "analytics"
|
|
26
26
|
Requires-Dist: platformdirs (>=4.2.0,<5.0.0)
|
|
27
27
|
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
|
|
4
|
+
from pfund.config_handler import configure
|
|
5
|
+
from pfund.utils.aliases import ALIASES
|
|
6
|
+
from pfund.const.paths import PROJ_PATH
|
|
7
|
+
# add python path so that for files like "ibapi" (official python code from IB) can find their modules
|
|
8
|
+
sys.path.append(f'{PROJ_PATH}/externals')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# NOTE: dynamically import modules to avoid click cli latency (reduced from ~4s to ~0.2s)
|
|
12
|
+
def __getattr__(name):
|
|
13
|
+
"""
|
|
14
|
+
Dynamically import and return modules and classes based on their name.
|
|
15
|
+
|
|
16
|
+
Supports dynamic loading of data sources and feed classes to minimize
|
|
17
|
+
initial load time.
|
|
18
|
+
"""
|
|
19
|
+
import importlib
|
|
20
|
+
if 'Engine' in name:
|
|
21
|
+
Engine = getattr(importlib.import_module('pfund.engines'), name)
|
|
22
|
+
globals()[name] = Engine
|
|
23
|
+
return Engine
|
|
24
|
+
elif 'Strategy' in name:
|
|
25
|
+
Strategy = getattr(importlib.import_module('pfund.strategies'), name)
|
|
26
|
+
globals()[name] = Strategy
|
|
27
|
+
return Strategy
|
|
28
|
+
elif 'Model' in name or 'Feature' in name:
|
|
29
|
+
Model = getattr(importlib.import_module('pfund.models'), name)
|
|
30
|
+
globals()[name] = Model
|
|
31
|
+
return Model
|
|
32
|
+
elif 'Indicator' in name:
|
|
33
|
+
Indicator = getattr(importlib.import_module('pfund.indicators'), name)
|
|
34
|
+
globals()[name] = Indicator
|
|
35
|
+
return Indicator
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# NOTE: dummy classes/modules for type hinting
|
|
39
|
+
# e.g. import pfund as pf, when you type "pf.",
|
|
40
|
+
# you will still see the following suggestions even they are dynamically imported:
|
|
41
|
+
BacktestEngine: ...
|
|
42
|
+
TradeEngine: ...
|
|
43
|
+
TrainEngine: ...
|
|
44
|
+
TestEngine: ...
|
|
45
|
+
Strategy: ...
|
|
46
|
+
Model: ...
|
|
47
|
+
PyTorchModel: ...
|
|
48
|
+
SKLearnModel: ...
|
|
49
|
+
Feature: ...
|
|
50
|
+
TAIndicator: ...
|
|
51
|
+
TALibIndicator: ...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__version__ = version('pfund')
|
|
55
|
+
__all__ = (
|
|
56
|
+
'__version__',
|
|
57
|
+
'configure',
|
|
58
|
+
'ALIASES',
|
|
59
|
+
'BacktestEngine',
|
|
60
|
+
'TradeEngine',
|
|
61
|
+
'TrainEngine',
|
|
62
|
+
'TestEngine',
|
|
63
|
+
'Strategy',
|
|
64
|
+
'Model',
|
|
65
|
+
'PyTorchModel',
|
|
66
|
+
'SKLearnModel',
|
|
67
|
+
'Feature',
|
|
68
|
+
'TAIndicator',
|
|
69
|
+
'TALibIndicator',
|
|
70
|
+
)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
|
|
4
|
-
from typing import
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from pfund.types.common_literals import tSUPPORTED_CODE_EDITORS, tSUPPORTED_TEMPLATE_TYPES
|
|
5
9
|
|
|
6
10
|
from pfund.utils import utils
|
|
7
11
|
from pfund.config_handler import ConfigHandler
|
|
@@ -30,7 +34,7 @@ class Analyzer:
|
|
|
30
34
|
return False
|
|
31
35
|
|
|
32
36
|
@staticmethod
|
|
33
|
-
def _derive_template_type(template: str) ->
|
|
37
|
+
def _derive_template_type(template: str) -> tSUPPORTED_TEMPLATE_TYPES:
|
|
34
38
|
if '.ipynb' in template:
|
|
35
39
|
template_type = 'notebook'
|
|
36
40
|
elif '.grid' in template:
|
|
@@ -56,7 +60,7 @@ class Analyzer:
|
|
|
56
60
|
else:
|
|
57
61
|
raise FileNotFoundError(f"Template {template} not found in pfund's templates or user's templates")
|
|
58
62
|
|
|
59
|
-
def _get_editor_cmd(self, editor:
|
|
63
|
+
def _get_editor_cmd(self, editor: tSUPPORTED_CODE_EDITORS) -> str:
|
|
60
64
|
if editor == 'vscode':
|
|
61
65
|
cmd = 'code'
|
|
62
66
|
if utils.is_command_available(cmd):
|
|
@@ -82,7 +86,7 @@ class Analyzer:
|
|
|
82
86
|
show_results_only: bool=True,
|
|
83
87
|
open_outputs: bool=False,
|
|
84
88
|
outputs_path: str | None=None,
|
|
85
|
-
editor:
|
|
89
|
+
editor: tSUPPORTED_CODE_EDITORS='vscode'
|
|
86
90
|
) -> None:
|
|
87
91
|
'''
|
|
88
92
|
Args:
|
|
@@ -183,7 +187,8 @@ class Analyzer:
|
|
|
183
187
|
for process in voila_processes:
|
|
184
188
|
process.terminate()
|
|
185
189
|
process.wait()
|
|
186
|
-
|
|
190
|
+
|
|
191
|
+
# TODO:
|
|
187
192
|
def run_spreadsheets(
|
|
188
193
|
self,
|
|
189
194
|
spreadsheets: list[str] | str
|
|
@@ -5,6 +5,7 @@ from collections import defaultdict
|
|
|
5
5
|
|
|
6
6
|
from pfund.adapter import Adapter
|
|
7
7
|
from pfund.config.configuration import Configuration
|
|
8
|
+
from pfund.const.paths import PROJ_CONFIG_PATH
|
|
8
9
|
from pfund.const.commons import SUPPORTED_PRODUCT_TYPES
|
|
9
10
|
from pfund.products import IBProduct
|
|
10
11
|
from pfund.accounts import IBAccount
|
|
@@ -19,8 +20,9 @@ from pfund.brokers.ib.ib_api import IBApi
|
|
|
19
20
|
class IBBroker(LiveBroker):
|
|
20
21
|
def __init__(self, env, **configs):
|
|
21
22
|
super().__init__(env, 'IB', **configs)
|
|
22
|
-
|
|
23
|
-
self.
|
|
23
|
+
config_path = f'{PROJ_CONFIG_PATH}/{self.bkr.lower()}'
|
|
24
|
+
self.configs = Configuration(config_path, 'config')
|
|
25
|
+
self.adapter = Adapter(config_path, self.configs.load_config_section('adapter'))
|
|
24
26
|
self.account = None
|
|
25
27
|
|
|
26
28
|
# API
|
|
@@ -20,4 +20,6 @@ SUPPORTED_TIMEFRAMES = [
|
|
|
20
20
|
]
|
|
21
21
|
SUPPORTED_DATA_CHANNELS = ['orderbook', 'tradebook', 'kline']
|
|
22
22
|
SUPPORTED_BACKTEST_MODES = ['vectorized', 'event_driven']
|
|
23
|
-
SUPPORTED_DATA_TOOLS = ['pandas']
|
|
23
|
+
SUPPORTED_DATA_TOOLS = ['pandas']
|
|
24
|
+
SUPPORTED_CODE_EDITORS = ['vscode', 'pycharm']
|
|
25
|
+
SUPPORTED_TEMPLATE_TYPES = ['notebook', 'spreadsheet', 'dashboard']
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
|
+
import inspect
|
|
4
5
|
import os
|
|
5
6
|
import time
|
|
6
7
|
import datetime
|
|
@@ -13,6 +14,7 @@ if TYPE_CHECKING:
|
|
|
13
14
|
from pfund.types.core import tStrategy, tModel, tFeature, tIndicator
|
|
14
15
|
|
|
15
16
|
import pfund as pf
|
|
17
|
+
from pfund.git_controller import GitController
|
|
16
18
|
from pfund.engines.base_engine import BaseEngine
|
|
17
19
|
from pfund.brokers.broker_backtest import BacktestBroker
|
|
18
20
|
from pfund.strategies.strategy_base import BaseStrategy
|
|
@@ -23,7 +25,7 @@ from pfund.mixins.backtest import BacktestMixin
|
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
class BacktestEngine(BaseEngine):
|
|
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):
|
|
28
|
+
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, auto_git_commit=False, **settings):
|
|
27
29
|
if not hasattr(cls, 'mode'):
|
|
28
30
|
cls.mode = mode.lower()
|
|
29
31
|
if not hasattr(cls, 'append_to_strategy_df'):
|
|
@@ -32,13 +34,18 @@ class BacktestEngine(BaseEngine):
|
|
|
32
34
|
# instead of recalculating the signals. This will make event-driven backtesting faster but less consistent with live trading
|
|
33
35
|
if not hasattr(cls, 'use_prepared_signals'):
|
|
34
36
|
cls.use_prepared_signals = use_prepared_signals
|
|
37
|
+
if not hasattr(cls, 'auto_git_commit'):
|
|
38
|
+
cls.auto_git_commit = auto_git_commit
|
|
35
39
|
return super().__new__(cls, env, data_tool=data_tool, config=config, **settings)
|
|
36
40
|
|
|
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):
|
|
38
|
-
super().__init__(env, data_tool=data_tool)
|
|
41
|
+
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, auto_git_commit=False, **settings):
|
|
39
42
|
# avoid re-initialization to implement singleton class correctly
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
if not hasattr(self, '_initialized'):
|
|
44
|
+
# Get the current frame and then the outer frame (where the engine instance is created)
|
|
45
|
+
caller_frame = inspect.currentframe().f_back
|
|
46
|
+
file_path = caller_frame.f_code.co_filename # Extract the file path from the frame
|
|
47
|
+
self._git = GitController(os.path.abspath(file_path))
|
|
48
|
+
super().__init__(env, data_tool=data_tool)
|
|
42
49
|
|
|
43
50
|
# HACK: since python doesn't support dynamic typing, true return type should be subclass of BacktestMixin and tStrategy
|
|
44
51
|
# write -> BacktestMixin | tStrategy for better intellisense in IDEs
|
|
@@ -131,23 +138,35 @@ class BacktestEngine(BaseEngine):
|
|
|
131
138
|
except:
|
|
132
139
|
self.logger.exception(f"Error writing to {file_path}:")
|
|
133
140
|
|
|
134
|
-
def _generate_backtest_iteration(self,
|
|
141
|
+
def _generate_backtest_iteration(self, backtest_hash: str) -> int:
|
|
135
142
|
'''Generate backtest iteration number for the same backtest_hash.
|
|
136
143
|
Read the existing backtest.json file to get the iteration number for the same strategy hash
|
|
137
144
|
If the backtest hash is not found, create a new entry with iteration number 1
|
|
138
145
|
else increment the iteration number by 1.
|
|
139
146
|
'''
|
|
140
|
-
backtest_hash = self._generate_backtest_hash(strategy)
|
|
141
147
|
file_name = 'backtest.json'
|
|
142
148
|
backtest_json = self.read_json(file_name)
|
|
143
149
|
backtest_json[backtest_hash] = backtest_json.get(backtest_hash, 0) + 1
|
|
144
150
|
self._write_json(file_name, backtest_json)
|
|
145
151
|
return backtest_json[backtest_hash]
|
|
146
152
|
|
|
147
|
-
def
|
|
153
|
+
def _commit_strategy(self, strategy: BaseStrategy) -> str | None:
|
|
154
|
+
engine_name = self.__class__.__name__
|
|
155
|
+
strat = strategy.name
|
|
156
|
+
commit_hash: str | None = self._git.commit(strategy._file_path, f'[PFund] {engine_name}: auto-commit strategy "{strat}"')
|
|
157
|
+
if commit_hash:
|
|
158
|
+
self.logger.debug(f"Strategy {strat} committed. {commit_hash=}")
|
|
159
|
+
else:
|
|
160
|
+
commit_hash = self._git.get_last_n_commit(n=1)[0]
|
|
161
|
+
self.logger.debug(f"Strategy {strat} has no changes to commit, return the last {commit_hash=}")
|
|
162
|
+
return commit_hash
|
|
163
|
+
|
|
164
|
+
def _output_backtest_results(self, strat: str, df, start_time: float, end_time: float, commit_hash: str | None):
|
|
148
165
|
strategy = self.get_strategy(strat)
|
|
149
166
|
backtest_id = self._generate_backtest_id()
|
|
167
|
+
backtest_hash = self._generate_backtest_hash(strategy)
|
|
150
168
|
backtest_name = self._create_backtest_name(strat, backtest_id)
|
|
169
|
+
backtest_iter = self._generate_backtest_iteration(backtest_hash)
|
|
151
170
|
local_tz = utils.get_local_timezone()
|
|
152
171
|
duration = end_time - start_time
|
|
153
172
|
df_file_path = os.path.join(self.config.backtest_path, f'{backtest_name}.parquet')
|
|
@@ -155,7 +174,10 @@ class BacktestEngine(BaseEngine):
|
|
|
155
174
|
'metadata': {
|
|
156
175
|
'pfund_version': pf.__version__,
|
|
157
176
|
'backtest_id': backtest_id,
|
|
158
|
-
'
|
|
177
|
+
'backtest_hash': backtest_hash,
|
|
178
|
+
'backtest_name': backtest_name,
|
|
179
|
+
'backtest_iteration': backtest_iter,
|
|
180
|
+
'commit_hash': commit_hash,
|
|
159
181
|
'duration': f'{duration:.2f}s' if duration > 1 else f'{duration*1000:.2f}ms',
|
|
160
182
|
'start_time': datetime.datetime.fromtimestamp(start_time, tz=local_tz).strftime('%Y-%m-%dT%H:%M:%S%z'),
|
|
161
183
|
'end_time': datetime.datetime.fromtimestamp(end_time, tz=local_tz).strftime('%Y-%m-%dT%H:%M:%S%z'),
|
|
@@ -167,7 +189,7 @@ class BacktestEngine(BaseEngine):
|
|
|
167
189
|
self.data_tool.output_df_to_parquet(df, df_file_path)
|
|
168
190
|
self._write_json(f'{backtest_name}.json', backtest_history)
|
|
169
191
|
return backtest_history
|
|
170
|
-
|
|
192
|
+
|
|
171
193
|
def run(self):
|
|
172
194
|
for broker in self.brokers.values():
|
|
173
195
|
broker.start()
|
|
@@ -181,11 +203,15 @@ class BacktestEngine(BaseEngine):
|
|
|
181
203
|
continue
|
|
182
204
|
if not hasattr(strategy, 'backtest'):
|
|
183
205
|
raise Exception(f'Strategy {strat} does not have backtest() method, cannot run vectorized backtesting')
|
|
206
|
+
if self.auto_git_commit and self._git.is_git_repo():
|
|
207
|
+
commit_hash = self._commit_strategy(strategy)
|
|
208
|
+
else:
|
|
209
|
+
commit_hash = None
|
|
184
210
|
start_time = time.time()
|
|
185
211
|
strategy.backtest()
|
|
186
212
|
end_time = time.time()
|
|
187
213
|
df = strategy.get_df()
|
|
188
|
-
backtest_history: dict = self._output_backtest_results(strat, df, start_time, end_time)
|
|
214
|
+
backtest_history: dict = self._output_backtest_results(strat, df, start_time, end_time, commit_hash)
|
|
189
215
|
backtests[strat] = backtest_history
|
|
190
216
|
elif self.mode == 'event_driven':
|
|
191
217
|
for strat, strategy in self.strategy_manager.strategies.items():
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from git import Repo, InvalidGitRepositoryError, NoSuchPathError, Commit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GitController:
|
|
7
|
+
def __init__(self, file_path: str):
|
|
8
|
+
# find the root directory of the Git repository that contains the given file
|
|
9
|
+
try:
|
|
10
|
+
self._repo = Repo(file_path, search_parent_directories=True)
|
|
11
|
+
self._repo_path = self._repo.git.rev_parse("--show-toplevel")
|
|
12
|
+
except (InvalidGitRepositoryError, NoSuchPathError):
|
|
13
|
+
self._repo = self._repo_path = None
|
|
14
|
+
print(f'{file_path} is not a git repository')
|
|
15
|
+
|
|
16
|
+
def is_git_repo(self):
|
|
17
|
+
return self._repo is not None
|
|
18
|
+
|
|
19
|
+
def get_last_n_commit(self, n=1) -> list[str]:
|
|
20
|
+
if not self._repo:
|
|
21
|
+
raise ValueError('No git repository found')
|
|
22
|
+
commits: list[Commit] = list(self._repo.iter_commits(paths=self._repo_path, max_count=n))
|
|
23
|
+
return [commit.hexsha for commit in commits]
|
|
24
|
+
|
|
25
|
+
def commit(self, file_path: str, commit_message: str) -> str | None:
|
|
26
|
+
if not os.path.exists(file_path):
|
|
27
|
+
raise FileNotFoundError(f'{file_path} not found')
|
|
28
|
+
if not self._repo:
|
|
29
|
+
raise ValueError('No git repository found')
|
|
30
|
+
try:
|
|
31
|
+
file_relpath = os.path.relpath(file_path, self._repo_path)
|
|
32
|
+
changed_files = [d.a_path for d in self._repo.index.diff(None)]
|
|
33
|
+
if file_relpath in self._repo.untracked_files or file_relpath in changed_files:
|
|
34
|
+
self._repo.index.add([file_relpath]) # Stage the file
|
|
35
|
+
commit = self._repo.index.commit(commit_message) # Commit the changes
|
|
36
|
+
return commit.hexsha # Return the commit hash
|
|
37
|
+
else:
|
|
38
|
+
print(f"No changes detected in {file_path}.")
|
|
39
|
+
return None
|
|
40
|
+
except Exception as e:
|
|
41
|
+
print(f"Error committing {file_path}: {e}")
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
# e.g. git checkout <commit_hash> -- strategy._file_path
|
|
45
|
+
# and git checkout HEAD -- strategy._file_path
|
|
46
|
+
def checkout_file_from_commit(self, commit_hash, file_path):
|
|
47
|
+
"""Check out a specific file from a specific commit into the working directory."""
|
|
48
|
+
try:
|
|
49
|
+
commit = self._repo.commit(commit_hash) # Get the specific commit
|
|
50
|
+
file_relpath = os.path.relpath(file_path, self._repo_path)
|
|
51
|
+
# Access the blob (file content) for the file at the specified commit
|
|
52
|
+
blob = commit.tree / file_relpath
|
|
53
|
+
# Write the content of the blob to the file in the working directory
|
|
54
|
+
with open(file_path, 'wb') as file:
|
|
55
|
+
blob.stream_data(file)
|
|
56
|
+
print(f"File {file_path} has been successfully checked out from commit {commit_hash}.")
|
|
57
|
+
except KeyError:
|
|
58
|
+
print(f"File {file_path} not found in commit {commit_hash}.")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"Failed to checkout commit {commit_hash}: {e}")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
|
|
3
|
+
from pfund.cli import pfund_group
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def exit_cli():
|
|
7
|
+
"""Application Exitpoint."""
|
|
8
|
+
print("Cleanup actions here...")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_cli() -> None:
|
|
12
|
+
"""Application Entrypoint."""
|
|
13
|
+
# atexit.register(exit_cli)
|
|
14
|
+
pfund_group(obj={})
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == '__main__':
|
|
18
|
+
run_cli()
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
+
import sys
|
|
5
6
|
import importlib
|
|
7
|
+
from pathlib import Path
|
|
6
8
|
from abc import ABC, abstractmethod
|
|
7
9
|
from collections import defaultdict
|
|
8
10
|
|
|
@@ -41,11 +43,42 @@ if TYPE_CHECKING:
|
|
|
41
43
|
from pfund.const.paths import MODEL_PATH
|
|
42
44
|
from pfund.models.model_meta import MetaModel
|
|
43
45
|
from pfund.products.product_base import BaseProduct
|
|
44
|
-
from pfund.utils.utils import short_path, get_engine_class
|
|
46
|
+
from pfund.utils.utils import short_path, get_engine_class, load_yaml_file
|
|
45
47
|
from pfund.plogging import create_dynamic_logger
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
class BaseModel(ABC, metaclass=MetaModel):
|
|
51
|
+
|
|
52
|
+
_file_path: Path | None = None # Get the file path where the model was defined
|
|
53
|
+
config = {}
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def load_config(cls, config: dict | None=None):
|
|
57
|
+
if config:
|
|
58
|
+
cls.config = config
|
|
59
|
+
elif cls._file_path:
|
|
60
|
+
for file_name in ['config.yml', 'config.yaml']:
|
|
61
|
+
if config := load_yaml_file(cls._file_path.parent / file_name):
|
|
62
|
+
cls.config = config
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
def load_params(self, params: dict | None=None):
|
|
66
|
+
if params:
|
|
67
|
+
self.params = params
|
|
68
|
+
elif self._file_path:
|
|
69
|
+
for file_name in ['params.yml', 'params.yaml']:
|
|
70
|
+
if params := load_yaml_file(self._file_path.parent / file_name):
|
|
71
|
+
self.params = params
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
def __new__(cls, *args, **kwargs):
|
|
75
|
+
if not cls._file_path:
|
|
76
|
+
module = sys.modules[cls.__module__]
|
|
77
|
+
if strategy_file_path := getattr(module, '__file__', None):
|
|
78
|
+
cls._file_path = Path(strategy_file_path)
|
|
79
|
+
cls.load_config()
|
|
80
|
+
return super().__new__(cls)
|
|
81
|
+
|
|
49
82
|
def __init__(self, ml_model: MachineLearningModel, *args, **kwargs):
|
|
50
83
|
self._args = args
|
|
51
84
|
self._kwargs = kwargs
|
|
@@ -73,6 +106,9 @@ class BaseModel(ABC, metaclass=MetaModel):
|
|
|
73
106
|
self.models = {}
|
|
74
107
|
self.predictions = {}
|
|
75
108
|
self.data = None # last data
|
|
109
|
+
|
|
110
|
+
self.params = {}
|
|
111
|
+
self.load_params()
|
|
76
112
|
|
|
77
113
|
@abstractmethod
|
|
78
114
|
def predict(self, *args, **kwargs) -> pd.DataFrame | torch.Tensor | np.ndarray:
|
|
@@ -92,6 +128,8 @@ class BaseModel(ABC, metaclass=MetaModel):
|
|
|
92
128
|
return {
|
|
93
129
|
'class': self.__class__.__name__,
|
|
94
130
|
'name': self.name,
|
|
131
|
+
'config': self.config,
|
|
132
|
+
'params': self.params,
|
|
95
133
|
'ml_model': self.ml_model,
|
|
96
134
|
'datas': [repr(data) for product in self.datas for data in self.datas[product].values()],
|
|
97
135
|
'models': [model.to_dict() for model in self.models.values()],
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
+
import sys
|
|
5
6
|
import time
|
|
6
7
|
import importlib
|
|
7
8
|
import datetime
|
|
9
|
+
from pathlib import Path
|
|
8
10
|
from collections import defaultdict, deque
|
|
9
11
|
from abc import ABC
|
|
10
12
|
|
|
@@ -24,11 +26,42 @@ from pfund.zeromq import ZeroMQ
|
|
|
24
26
|
from pfund.risk_monitor import RiskMonitor
|
|
25
27
|
from pfund.const.commons import SUPPORTED_CRYPTO_EXCHANGES
|
|
26
28
|
from pfund.strategies.strategy_meta import MetaStrategy
|
|
27
|
-
from pfund.utils.utils import convert_to_uppercases, get_engine_class
|
|
29
|
+
from pfund.utils.utils import convert_to_uppercases, get_engine_class, load_yaml_file
|
|
28
30
|
from pfund.plogging import create_dynamic_logger
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
class BaseStrategy(ABC, metaclass=MetaStrategy):
|
|
34
|
+
|
|
35
|
+
_file_path: Path | None = None # Get the file path where the strategy was defined
|
|
36
|
+
config = {}
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def load_config(cls, config: dict | None=None):
|
|
40
|
+
if config:
|
|
41
|
+
cls.config = config
|
|
42
|
+
elif cls._file_path:
|
|
43
|
+
for file_name in ['config.yml', 'config.yaml']:
|
|
44
|
+
if config := load_yaml_file(cls._file_path.parent / file_name):
|
|
45
|
+
cls.config = config
|
|
46
|
+
break
|
|
47
|
+
|
|
48
|
+
def load_params(self, params: dict | None=None):
|
|
49
|
+
if params:
|
|
50
|
+
self.params = params
|
|
51
|
+
elif self._file_path:
|
|
52
|
+
for file_name in ['params.yml', 'params.yaml']:
|
|
53
|
+
if params := load_yaml_file(self._file_path.parent / file_name):
|
|
54
|
+
self.params = params
|
|
55
|
+
break
|
|
56
|
+
|
|
57
|
+
def __new__(cls, *args, **kwargs):
|
|
58
|
+
if not cls._file_path:
|
|
59
|
+
module = sys.modules[cls.__module__]
|
|
60
|
+
if strategy_file_path := getattr(module, '__file__', None):
|
|
61
|
+
cls._file_path = Path(strategy_file_path)
|
|
62
|
+
cls.load_config()
|
|
63
|
+
return super().__new__(cls)
|
|
64
|
+
|
|
32
65
|
def __init__(self, *args, **kwargs):
|
|
33
66
|
self._args = args
|
|
34
67
|
self._kwargs = kwargs
|
|
@@ -66,6 +99,9 @@ class BaseStrategy(ABC, metaclass=MetaStrategy):
|
|
|
66
99
|
self.strategies = {}
|
|
67
100
|
self.predictions = {}
|
|
68
101
|
self.data = None # last data
|
|
102
|
+
|
|
103
|
+
self.params = {}
|
|
104
|
+
self.load_params()
|
|
69
105
|
|
|
70
106
|
def __getattr__(self, attr):
|
|
71
107
|
'''gets triggered only when the attribute is not found'''
|
|
@@ -96,6 +132,8 @@ class BaseStrategy(ABC, metaclass=MetaStrategy):
|
|
|
96
132
|
return {
|
|
97
133
|
'class': self.__class__.__name__,
|
|
98
134
|
'name': self.name,
|
|
135
|
+
'config': self.config,
|
|
136
|
+
'params': self.params,
|
|
99
137
|
'accounts': [repr(account) for trading_venue in self.accounts for account in self.accounts[trading_venue].values()],
|
|
100
138
|
'datas': [repr(data) for product in self.datas for data in self.datas[product].values()],
|
|
101
139
|
'strategies': [strategy.to_dict() for strategy in self.strategies.values()],
|
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
"cells": [
|
|
3
3
|
{
|
|
4
4
|
"cell_type": "code",
|
|
5
|
-
"execution_count":
|
|
5
|
+
"execution_count": 2,
|
|
6
6
|
"metadata": {},
|
|
7
|
-
"outputs": [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
]
|
|
7
|
+
"outputs": [],
|
|
8
|
+
"source": [
|
|
9
|
+
"%load_ext autoreload\n",
|
|
10
|
+
"# Reload all modules (except those excluded by %aimport) every time before executing the Python code typed.\n",
|
|
11
|
+
"%autoreload 2\n",
|
|
12
|
+
"\n",
|
|
13
|
+
"import pfund as pf\n",
|
|
14
|
+
"import pfolio as po"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"cell_type": "code",
|
|
19
|
+
"execution_count": 3,
|
|
20
|
+
"metadata": {},
|
|
21
|
+
"outputs": [],
|
|
16
22
|
"source": [
|
|
17
|
-
"
|
|
23
|
+
"import quantstats"
|
|
18
24
|
]
|
|
19
25
|
},
|
|
20
26
|
{
|
|
@@ -22,4 +22,6 @@ tSUPPORTED_TIMEFRAMES = Literal[
|
|
|
22
22
|
]
|
|
23
23
|
tSUPPORTED_DATA_CHANNELS = Literal['orderbook', 'tradebook', 'kline']
|
|
24
24
|
tSUPPORTED_BACKTEST_MODES = Literal['vectorized', 'event_driven']
|
|
25
|
-
tSUPPORTED_DATA_TOOLS = Literal['pandas']
|
|
25
|
+
tSUPPORTED_DATA_TOOLS = Literal['pandas']
|
|
26
|
+
tSUPPORTED_CODE_EDITORS = Literal['vscode', 'pycharm']
|
|
27
|
+
tSUPPORTED_TEMPLATE_TYPES = Literal['notebook', 'spreadsheet', 'dashboard']
|
|
@@ -64,9 +64,18 @@ def lowercase(func):
|
|
|
64
64
|
return wrapper
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def load_yaml_file(file_path):
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
def load_yaml_file(file_path) -> dict | list[dict]:
|
|
68
|
+
if os.path.exists(file_path):
|
|
69
|
+
with open(file_path, 'r') as f:
|
|
70
|
+
contents = list(yaml.safe_load_all(f))
|
|
71
|
+
if not contents:
|
|
72
|
+
return {}
|
|
73
|
+
elif len(contents) == 1:
|
|
74
|
+
return contents[0]
|
|
75
|
+
else:
|
|
76
|
+
return contents
|
|
77
|
+
else:
|
|
78
|
+
return {}
|
|
70
79
|
|
|
71
80
|
|
|
72
81
|
def flatten_dict(d, parent_key='', sep='.'):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pfund"
|
|
3
|
-
version = "0.0.1.
|
|
3
|
+
version = "0.0.1.dev13"
|
|
4
4
|
description = "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
|
license = "Apache-2.0"
|
|
6
6
|
authors = ["Stephen Yau <softwareentrepreneer+pfund@gmail.com>"]
|
|
@@ -23,7 +23,7 @@ requests = "^2.31.0"
|
|
|
23
23
|
websocket-client = "^1.7.0"
|
|
24
24
|
python-telegram-bot = "^20.7"
|
|
25
25
|
gitpython = "^3.1.43"
|
|
26
|
-
pfeed = { version = "^0.0.1.
|
|
26
|
+
pfeed = { version = "^0.0.1.dev11", optional = true, extras = ["df", "data", "boost"] }
|
|
27
27
|
orjson = { version = "^3.9.14", optional = true }
|
|
28
28
|
pyzmq = { version = "^25.1.2", optional = true }
|
|
29
29
|
ta = { version = "^0.11.0", optional = true }
|
|
@@ -47,26 +47,28 @@ optional = true
|
|
|
47
47
|
|
|
48
48
|
[tool.poetry.group.dev.dependencies]
|
|
49
49
|
# pfeed = { path = "../pfeed", develop = true, extras = ["df", "data", "boost"] }
|
|
50
|
-
|
|
50
|
+
pfolio = { path = "../pfolio", develop = true, extras = ["bayesian", "data", "portfolio", "temporary"] }
|
|
51
51
|
pybit = "^5.6.2"
|
|
52
52
|
ta-lib = "^0.4.28"
|
|
53
|
+
commitizen = "^3.24.0"
|
|
54
|
+
mypy = "^1.9.0"
|
|
55
|
+
ruff = "^0.3.5"
|
|
56
|
+
grayskull = "^2.5.3"
|
|
53
57
|
|
|
54
58
|
[tool.poetry.group.test.dependencies]
|
|
55
59
|
pytest = "^8.0.0"
|
|
56
|
-
pre-commit = "^3.6.1"
|
|
57
|
-
bandit = "^1.7.7"
|
|
58
|
-
grayskull = "^2.5.3"
|
|
59
|
-
pytest-xdist = "^3.5.0"
|
|
60
|
-
faker = "^24.4.0"
|
|
61
|
-
tox = "^4.14.2"
|
|
62
|
-
mypy = "^1.9.0"
|
|
63
|
-
ruff = "^0.3.5"
|
|
64
60
|
pytest-mock = "^3.14.0"
|
|
65
61
|
pytest-cov = "^5.0.0"
|
|
62
|
+
pytest-xdist = "^3.5.0"
|
|
63
|
+
tox = "^4.14.2"
|
|
64
|
+
faker = "^24.4.0"
|
|
65
|
+
bandit = "^1.7.7"
|
|
66
|
+
pre-commit = "^3.6.1"
|
|
66
67
|
|
|
67
68
|
[tool.poetry.group.doc.dependencies]
|
|
68
69
|
jupyter-book = "^1.0.0"
|
|
69
70
|
notebook = "^7.1.2"
|
|
71
|
+
sphinxawesome-theme = "^5.1.1"
|
|
70
72
|
|
|
71
73
|
[build-system]
|
|
72
74
|
requires = ["poetry-core"]
|