ghosalquant 0.1.0__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 (133) hide show
  1. ghosalquant-0.1.0/LICENSE +21 -0
  2. ghosalquant-0.1.0/PKG-INFO +113 -0
  3. ghosalquant-0.1.0/README.md +67 -0
  4. ghosalquant-0.1.0/pyproject.toml +65 -0
  5. ghosalquant-0.1.0/setup.cfg +4 -0
  6. ghosalquant-0.1.0/src/ghosalquant/__init__.py +10 -0
  7. ghosalquant-0.1.0/src/ghosalquant/backtest_ui/__init__.py +7 -0
  8. ghosalquant-0.1.0/src/ghosalquant/backtest_ui/__main__.py +10 -0
  9. ghosalquant-0.1.0/src/ghosalquant/backtest_ui/app.py +148 -0
  10. ghosalquant-0.1.0/src/ghosalquant/backtest_ui/runner.py +109 -0
  11. ghosalquant-0.1.0/src/ghosalquant/backtester/__init__.py +7 -0
  12. ghosalquant-0.1.0/src/ghosalquant/backtester/backtester_base.py +129 -0
  13. ghosalquant-0.1.0/src/ghosalquant/backtester/dataframe_backtester.py +95 -0
  14. ghosalquant-0.1.0/src/ghosalquant/bench/__init__.py +7 -0
  15. ghosalquant-0.1.0/src/ghosalquant/bench/benchmark_buy_and_hold.py +51 -0
  16. ghosalquant-0.1.0/src/ghosalquant/bench/benchmark_trend_vol_target.py +179 -0
  17. ghosalquant-0.1.0/src/ghosalquant/brokerage/__init__.py +26 -0
  18. ghosalquant-0.1.0/src/ghosalquant/brokerage/broker_base.py +123 -0
  19. ghosalquant-0.1.0/src/ghosalquant/brokerage/builder.py +42 -0
  20. ghosalquant-0.1.0/src/ghosalquant/brokerage/commissions.py +38 -0
  21. ghosalquant-0.1.0/src/ghosalquant/brokerage/fills.py +470 -0
  22. ghosalquant-0.1.0/src/ghosalquant/brokerage/ibkr.py +496 -0
  23. ghosalquant-0.1.0/src/ghosalquant/brokerage/open_orders.py +27 -0
  24. ghosalquant-0.1.0/src/ghosalquant/brokerage/order_book_sim.py +77 -0
  25. ghosalquant-0.1.0/src/ghosalquant/brokerage/simulated.py +80 -0
  26. ghosalquant-0.1.0/src/ghosalquant/brokerage/simulation.py +303 -0
  27. ghosalquant-0.1.0/src/ghosalquant/compatibility/legacybacktester.py +253 -0
  28. ghosalquant-0.1.0/src/ghosalquant/compatibility/plotManager.py +71 -0
  29. ghosalquant-0.1.0/src/ghosalquant/compatibility/test_strategy.py +63 -0
  30. ghosalquant-0.1.0/src/ghosalquant/compatibility/trade.py +70 -0
  31. ghosalquant-0.1.0/src/ghosalquant/core/__init__.py +59 -0
  32. ghosalquant-0.1.0/src/ghosalquant/core/blackscholes.py +169 -0
  33. ghosalquant-0.1.0/src/ghosalquant/core/contracts.py +155 -0
  34. ghosalquant-0.1.0/src/ghosalquant/core/metadata_keys.py +59 -0
  35. ghosalquant-0.1.0/src/ghosalquant/core/types.py +454 -0
  36. ghosalquant-0.1.0/src/ghosalquant/core/volatility.py +163 -0
  37. ghosalquant-0.1.0/src/ghosalquant/data_layer/__init__.py +29 -0
  38. ghosalquant-0.1.0/src/ghosalquant/data_layer/cboe_datasource.py +150 -0
  39. ghosalquant-0.1.0/src/ghosalquant/data_layer/dataLayer.py +127 -0
  40. ghosalquant-0.1.0/src/ghosalquant/data_layer/dataframe_datasource.py +76 -0
  41. ghosalquant-0.1.0/src/ghosalquant/data_layer/datasource_base.py +18 -0
  42. ghosalquant-0.1.0/src/ghosalquant/data_layer/datasource_factory.py +55 -0
  43. ghosalquant-0.1.0/src/ghosalquant/data_layer/dolt_datasource.py +159 -0
  44. ghosalquant-0.1.0/src/ghosalquant/data_layer/listEditor.py +372 -0
  45. ghosalquant-0.1.0/src/ghosalquant/data_layer/list_datasource.py +27 -0
  46. ghosalquant-0.1.0/src/ghosalquant/data_layer/live_alpaca_datasource.py +190 -0
  47. ghosalquant-0.1.0/src/ghosalquant/data_layer/live_ibkr_datasource.py +246 -0
  48. ghosalquant-0.1.0/src/ghosalquant/data_layer/live_polygon_datasource.py +264 -0
  49. ghosalquant-0.1.0/src/ghosalquant/data_layer/merge.py +52 -0
  50. ghosalquant-0.1.0/src/ghosalquant/data_layer/nse_datasource.py +126 -0
  51. ghosalquant-0.1.0/src/ghosalquant/data_layer/parquet_polygon_datasource.py +108 -0
  52. ghosalquant-0.1.0/src/ghosalquant/data_layer/recording_datasource.py +251 -0
  53. ghosalquant-0.1.0/src/ghosalquant/data_layer/shocks.py +99 -0
  54. ghosalquant-0.1.0/src/ghosalquant/data_layer/staticUpdater.py +408 -0
  55. ghosalquant-0.1.0/src/ghosalquant/data_layer/yfinance_datasource.py +60 -0
  56. ghosalquant-0.1.0/src/ghosalquant/data_layer/yfinance_multi_datasource.py +120 -0
  57. ghosalquant-0.1.0/src/ghosalquant/execution/__init__.py +16 -0
  58. ghosalquant-0.1.0/src/ghosalquant/execution/context.py +54 -0
  59. ghosalquant-0.1.0/src/ghosalquant/execution/execution_system.py +533 -0
  60. ghosalquant-0.1.0/src/ghosalquant/execution/sinks.py +122 -0
  61. ghosalquant-0.1.0/src/ghosalquant/execution/strategy_base.py +109 -0
  62. ghosalquant-0.1.0/src/ghosalquant/metrics/__init__.py +25 -0
  63. ghosalquant-0.1.0/src/ghosalquant/metrics/gates.py +150 -0
  64. ghosalquant-0.1.0/src/ghosalquant/metrics/risk_adjusted.py +196 -0
  65. ghosalquant-0.1.0/src/ghosalquant/portfolio/__init__.py +30 -0
  66. ghosalquant-0.1.0/src/ghosalquant/portfolio/attribution.py +81 -0
  67. ghosalquant-0.1.0/src/ghosalquant/portfolio/basic_portfolio.py +373 -0
  68. ghosalquant-0.1.0/src/ghosalquant/portfolio/hedging.py +87 -0
  69. ghosalquant-0.1.0/src/ghosalquant/portfolio/lots.py +63 -0
  70. ghosalquant-0.1.0/src/ghosalquant/portfolio/marks.py +58 -0
  71. ghosalquant-0.1.0/src/ghosalquant/portfolio/portfolio_base.py +104 -0
  72. ghosalquant-0.1.0/src/ghosalquant/portfolio/valuation.py +694 -0
  73. ghosalquant-0.1.0/src/ghosalquant/recording/__init__.py +111 -0
  74. ghosalquant-0.1.0/src/ghosalquant/recording/events.py +204 -0
  75. ghosalquant-0.1.0/src/ghosalquant/recording/intervals.py +236 -0
  76. ghosalquant-0.1.0/src/ghosalquant/recording/recorder.py +531 -0
  77. ghosalquant-0.1.0/src/ghosalquant/recording/storage.py +607 -0
  78. ghosalquant-0.1.0/src/ghosalquant/risk/__init__.py +56 -0
  79. ghosalquant-0.1.0/src/ghosalquant/risk/builder.py +151 -0
  80. ghosalquant-0.1.0/src/ghosalquant/risk/cash_allocation.py +285 -0
  81. ghosalquant-0.1.0/src/ghosalquant/risk/risk_manager.py +109 -0
  82. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_confidence_sizer.py +32 -0
  83. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_correlation_filter.py +105 -0
  84. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_drawdown_killswitch.py +75 -0
  85. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_integer_quantity.py +27 -0
  86. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_kelly_sizer.py +74 -0
  87. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_max_open_positions.py +27 -0
  88. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_max_symbol_exposure.py +118 -0
  89. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_regime_filter.py +50 -0
  90. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_signal_quantity_cap.py +28 -0
  91. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_symbol_capital_sizer.py +78 -0
  92. ghosalquant-0.1.0/src/ghosalquant/risk/rules/rule_tick_rounding.py +40 -0
  93. ghosalquant-0.1.0/src/ghosalquant/risk/sizing.py +46 -0
  94. ghosalquant-0.1.0/src/ghosalquant/risk/types.py +69 -0
  95. ghosalquant-0.1.0/src/ghosalquant/state/__init__.py +23 -0
  96. ghosalquant-0.1.0/src/ghosalquant/state/atomic.py +74 -0
  97. ghosalquant-0.1.0/src/ghosalquant/state/event_store.py +249 -0
  98. ghosalquant-0.1.0/src/ghosalquant/state/json_store.py +85 -0
  99. ghosalquant-0.1.0/src/ghosalquant/util/backtester_utils.py +118 -0
  100. ghosalquant-0.1.0/src/ghosalquant/util/config.py +522 -0
  101. ghosalquant-0.1.0/src/ghosalquant/util/deprecation.py +19 -0
  102. ghosalquant-0.1.0/src/ghosalquant/util/rounding.py +25 -0
  103. ghosalquant-0.1.0/src/ghosalquant/util/visualisation.py +162 -0
  104. ghosalquant-0.1.0/src/ghosalquant.egg-info/PKG-INFO +113 -0
  105. ghosalquant-0.1.0/src/ghosalquant.egg-info/SOURCES.txt +131 -0
  106. ghosalquant-0.1.0/src/ghosalquant.egg-info/dependency_links.txt +1 -0
  107. ghosalquant-0.1.0/src/ghosalquant.egg-info/entry_points.txt +2 -0
  108. ghosalquant-0.1.0/src/ghosalquant.egg-info/requires.txt +22 -0
  109. ghosalquant-0.1.0/src/ghosalquant.egg-info/top_level.txt +1 -0
  110. ghosalquant-0.1.0/tests/test_cboe_datasource.py +143 -0
  111. ghosalquant-0.1.0/tests/test_config.py +445 -0
  112. ghosalquant-0.1.0/tests/test_contracts.py +627 -0
  113. ghosalquant-0.1.0/tests/test_dolt_datasource.py +108 -0
  114. ghosalquant-0.1.0/tests/test_example_options_momentum.py +179 -0
  115. ghosalquant-0.1.0/tests/test_example_order_book.py +23 -0
  116. ghosalquant-0.1.0/tests/test_execution.py +429 -0
  117. ghosalquant-0.1.0/tests/test_execution_order_book.py +821 -0
  118. ghosalquant-0.1.0/tests/test_ibkr_broker.py +895 -0
  119. ghosalquant-0.1.0/tests/test_ibkr_real_client.py +278 -0
  120. ghosalquant-0.1.0/tests/test_legacy_backtester.py +96 -0
  121. ghosalquant-0.1.0/tests/test_limit_orders.py +184 -0
  122. ghosalquant-0.1.0/tests/test_live_sources.py +311 -0
  123. ghosalquant-0.1.0/tests/test_metrics.py +235 -0
  124. ghosalquant-0.1.0/tests/test_nse_datasource.py +110 -0
  125. ghosalquant-0.1.0/tests/test_order_book_sim_broker.py +495 -0
  126. ghosalquant-0.1.0/tests/test_order_book_types.py +233 -0
  127. ghosalquant-0.1.0/tests/test_recording.py +790 -0
  128. ghosalquant-0.1.0/tests/test_replace_orders.py +188 -0
  129. ghosalquant-0.1.0/tests/test_risk_rules.py +823 -0
  130. ghosalquant-0.1.0/tests/test_shocks.py +169 -0
  131. ghosalquant-0.1.0/tests/test_state.py +233 -0
  132. ghosalquant-0.1.0/tests/test_valuation.py +426 -0
  133. ghosalquant-0.1.0/tests/test_volatility.py +319 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Ghosal Quants
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: ghosalquant
3
+ Version: 0.1.0
4
+ Summary: Event-driven quantitative trading research framework
5
+ Author: Ghosal Quants
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Ghosal-Quants/GhosalQuant
8
+ Project-URL: Documentation, https://github.com/Ghosal-Quants/GhosalQuant/blob/main/design/user_guide.md
9
+ Project-URL: Repository, https://github.com/Ghosal-Quants/GhosalQuant
10
+ Project-URL: Issues, https://github.com/Ghosal-Quants/GhosalQuant/issues
11
+ Keywords: quant,trading,backtesting,algorithmic-trading,finance
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Office/Business :: Financial :: Investment
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy
27
+ Requires-Dist: pandas
28
+ Requires-Dist: pytz
29
+ Requires-Dist: requests
30
+ Requires-Dist: yfinance
31
+ Requires-Dist: PyYAML
32
+ Requires-Dist: python-dotenv
33
+ Requires-Dist: pyarrow
34
+ Requires-Dist: fastparquet
35
+ Requires-Dist: ib_insync
36
+ Requires-Dist: websockets
37
+ Provides-Extra: alpaca
38
+ Requires-Dist: alpaca-py; extra == "alpaca"
39
+ Provides-Extra: plotting
40
+ Requires-Dist: matplotlib; extra == "plotting"
41
+ Requires-Dist: plotly; extra == "plotting"
42
+ Provides-Extra: ui
43
+ Requires-Dist: customtkinter; extra == "ui"
44
+ Requires-Dist: matplotlib; extra == "ui"
45
+ Dynamic: license-file
46
+
47
+ # GhosalQuant
48
+ Ghosal Fund | Quantitative Trading Team Repository
49
+
50
+ A modular Python framework for developing, backtesting, and executing quantitative trading strategies.
51
+
52
+ ## Documentation
53
+ - [System Overview](https://github.com/Ghosal-Quants/GhosalQuant/blob/main/design/system-overview.md) - architecture + data flow
54
+ - [User Guide](https://github.com/Ghosal-Quants/GhosalQuant/blob/main/design/user_guide.md) - getting started + quick reference
55
+
56
+ ## Installation
57
+ ```bash
58
+ pip install ghosalquant
59
+ ```
60
+ Optional extras: `ghosalquant[alpaca]` (Alpaca market data), `ghosalquant[plotting]` (matplotlib and plotly charts),
61
+ `ghosalquant[ui]` (the prototype backtest GUI, launched with `ghosalquant-ui`).
62
+
63
+ To work on the framework itself, install from source in editable mode:
64
+ ```bash
65
+ git clone https://github.com/Ghosal-Quants/GhosalQuant.git
66
+ cd GhosalQuant
67
+ pip install -e .
68
+ ```
69
+
70
+ ## Quick Start
71
+ 1. **Write a strategy** — subclass `Strategy`, implement `on_market_data(bar) -> Signal | None`
72
+ 2. **Create a config** — see `examples/config.yaml`
73
+ 3. **Run a backtest**:
74
+ ```python
75
+ from ghosalquant.backtester.backtester_base import Backtester
76
+ from my_strategy import MyStrategy
77
+
78
+ kpms = Backtester.from_config(strategy=MyStrategy(), config_path="config.yaml").run()
79
+ print(kpms)
80
+ ```
81
+
82
+ See `examples/` for more examples for: DataFrame backtesting, strategy comparison, IBKR paper trading, and live data.
83
+
84
+ ## Project Structure
85
+ ```
86
+ src/ghosalquant/
87
+ core/ # Bar, Signal, Order, Fill, Trade types
88
+ execution/ # Strategy base, ExecutionSystem, signal sinks, benchmarks
89
+ data_layer/ # DataSource implementations (yfinance, Polygon, DataFrame, list)
90
+ backtester/ # Backtester, DataFrameBacktester
91
+ risk/ # RiskManager, rules (MaxOpenPositions, ConfidenceSizer)
92
+ portfolio/ # Portfolio protocol, BasicPortfolio
93
+ brokerage/ # Broker base, SimBroker, IBKRBroker
94
+ util/ # Config, visualisation, helpers
95
+ backtest_ui/ # Prototype GUI (the [ui] extra)
96
+ examples/ # Ready-to-run scripts and sample config
97
+ ```
98
+
99
+ ## Tom's Notes
100
+
101
+ ### What are we actually trying to do?
102
+ - Find ideas for interesting strategies for a given asset class
103
+ - Implement the mathematical theory algorithmically
104
+ - Provide quantitative evidence that they work, over a period of time
105
+ - Execute the orders
106
+
107
+ ### Some existing open source frameworks
108
+ - https://amor71.github.io/LiuAlgoTrader/
109
+ - https://www.backtrader.com/
110
+ - https://github.com/gbeced/basana (crypto orientated)
111
+ - https://github.com/panpanpandas/ultrafinance
112
+ - https://github.com/ematvey/pybacktest
113
+ - https://github.com/mhallsmoore/qstrader
@@ -0,0 +1,67 @@
1
+ # GhosalQuant
2
+ Ghosal Fund | Quantitative Trading Team Repository
3
+
4
+ A modular Python framework for developing, backtesting, and executing quantitative trading strategies.
5
+
6
+ ## Documentation
7
+ - [System Overview](https://github.com/Ghosal-Quants/GhosalQuant/blob/main/design/system-overview.md) - architecture + data flow
8
+ - [User Guide](https://github.com/Ghosal-Quants/GhosalQuant/blob/main/design/user_guide.md) - getting started + quick reference
9
+
10
+ ## Installation
11
+ ```bash
12
+ pip install ghosalquant
13
+ ```
14
+ Optional extras: `ghosalquant[alpaca]` (Alpaca market data), `ghosalquant[plotting]` (matplotlib and plotly charts),
15
+ `ghosalquant[ui]` (the prototype backtest GUI, launched with `ghosalquant-ui`).
16
+
17
+ To work on the framework itself, install from source in editable mode:
18
+ ```bash
19
+ git clone https://github.com/Ghosal-Quants/GhosalQuant.git
20
+ cd GhosalQuant
21
+ pip install -e .
22
+ ```
23
+
24
+ ## Quick Start
25
+ 1. **Write a strategy** — subclass `Strategy`, implement `on_market_data(bar) -> Signal | None`
26
+ 2. **Create a config** — see `examples/config.yaml`
27
+ 3. **Run a backtest**:
28
+ ```python
29
+ from ghosalquant.backtester.backtester_base import Backtester
30
+ from my_strategy import MyStrategy
31
+
32
+ kpms = Backtester.from_config(strategy=MyStrategy(), config_path="config.yaml").run()
33
+ print(kpms)
34
+ ```
35
+
36
+ See `examples/` for more examples for: DataFrame backtesting, strategy comparison, IBKR paper trading, and live data.
37
+
38
+ ## Project Structure
39
+ ```
40
+ src/ghosalquant/
41
+ core/ # Bar, Signal, Order, Fill, Trade types
42
+ execution/ # Strategy base, ExecutionSystem, signal sinks, benchmarks
43
+ data_layer/ # DataSource implementations (yfinance, Polygon, DataFrame, list)
44
+ backtester/ # Backtester, DataFrameBacktester
45
+ risk/ # RiskManager, rules (MaxOpenPositions, ConfidenceSizer)
46
+ portfolio/ # Portfolio protocol, BasicPortfolio
47
+ brokerage/ # Broker base, SimBroker, IBKRBroker
48
+ util/ # Config, visualisation, helpers
49
+ backtest_ui/ # Prototype GUI (the [ui] extra)
50
+ examples/ # Ready-to-run scripts and sample config
51
+ ```
52
+
53
+ ## Tom's Notes
54
+
55
+ ### What are we actually trying to do?
56
+ - Find ideas for interesting strategies for a given asset class
57
+ - Implement the mathematical theory algorithmically
58
+ - Provide quantitative evidence that they work, over a period of time
59
+ - Execute the orders
60
+
61
+ ### Some existing open source frameworks
62
+ - https://amor71.github.io/LiuAlgoTrader/
63
+ - https://www.backtrader.com/
64
+ - https://github.com/gbeced/basana (crypto orientated)
65
+ - https://github.com/panpanpandas/ultrafinance
66
+ - https://github.com/ematvey/pybacktest
67
+ - https://github.com/mhallsmoore/qstrader
@@ -0,0 +1,65 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ghosalquant"
7
+ dynamic = ["version"]
8
+ description = "Event-driven quantitative trading research framework"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Ghosal Quants" }]
13
+ requires-python = ">=3.10"
14
+ keywords = ["quant", "trading", "backtesting", "algorithmic-trading", "finance"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Financial and Insurance Industry",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Office/Business :: Financial :: Investment",
27
+ ]
28
+
29
+ dependencies = [
30
+ "numpy",
31
+ "pandas",
32
+ "pytz",
33
+ "requests",
34
+ "yfinance",
35
+ "PyYAML",
36
+ "python-dotenv",
37
+ "pyarrow",
38
+ "fastparquet",
39
+ "ib_insync",
40
+ "websockets"
41
+ ]
42
+
43
+ [project.optional-dependencies]
44
+ alpaca = ["alpaca-py"]
45
+ plotting = ["matplotlib", "plotly"]
46
+ ui = ["customtkinter", "matplotlib"]
47
+
48
+ [project.gui-scripts]
49
+ ghosalquant-ui = "ghosalquant.backtest_ui.app:main"
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/Ghosal-Quants/GhosalQuant"
53
+ Documentation = "https://github.com/Ghosal-Quants/GhosalQuant/blob/main/design/user_guide.md"
54
+ Repository = "https://github.com/Ghosal-Quants/GhosalQuant"
55
+ Issues = "https://github.com/Ghosal-Quants/GhosalQuant/issues"
56
+
57
+ [tool.setuptools.dynamic]
58
+ version = { attr = "ghosalquant.__version__" }
59
+
60
+ [tool.setuptools.packages.find]
61
+ where = ["src"]
62
+ include = ["ghosalquant*"]
63
+
64
+ [tool.pytest.ini_options]
65
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ from ghosalquant.execution.strategy_base import OrderBookStrategy, Strategy
2
+ import ghosalquant.core.types as types
3
+
4
+ __version__ = "0.1.0"
5
+
6
+ __all__ = [
7
+ "OrderBookStrategy",
8
+ "Strategy",
9
+ "types"
10
+ ]
@@ -0,0 +1,7 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def project_root() -> Path:
5
+ """The source checkout when run from one, otherwise the directory the UI was started in."""
6
+ root = Path(__file__).resolve().parents[3]
7
+ return root if (root / "pyproject.toml").exists() else Path.cwd()
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+ import sys
3
+
4
+ from .runner import run_from_config
5
+
6
+ if __name__ == "__main__":
7
+ if len(sys.argv) < 3:
8
+ print("Usage: python -m ghosalquant.backtest_ui <config.yaml> <strategy.py>")
9
+ raise SystemExit(2)
10
+ raise SystemExit(run_from_config(sys.argv[1], sys.argv[2]))
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ import threading
5
+ import subprocess
6
+ from pathlib import Path
7
+ from queue import Queue, Empty
8
+
9
+ import customtkinter as ctk
10
+ from tkinter import filedialog, messagebox
11
+
12
+ from . import project_root
13
+
14
+ """
15
+ WARNING: VIBED because I hate UI development and this is a prototype for now
16
+
17
+ This is a rough experimental prototype of a UI to abstract backtesting from QRs.
18
+ """
19
+
20
+
21
+ class App(ctk.CTk):
22
+ def __init__(self) -> None:
23
+ super().__init__()
24
+ self.title("GhosalQuant Backtest (Prototype)")
25
+ self.geometry("900x600")
26
+
27
+ self.root = project_root()
28
+ self.default_cfg = self.root / "examples" / "config.yaml"
29
+
30
+ self.cfg_var = ctk.StringVar(value=str(self.default_cfg) if self.default_cfg.exists() else "")
31
+
32
+ self.log_q: Queue[str] = Queue()
33
+
34
+ top = ctk.CTkFrame(self)
35
+ top.pack(fill="x", padx=12, pady=12)
36
+
37
+ row1 = ctk.CTkFrame(top)
38
+ row1.pack(fill="x")
39
+
40
+ row2 = ctk.CTkFrame(top)
41
+ row2.pack(fill="x")
42
+
43
+ # Config + Run
44
+ ctk.CTkLabel(row1, text="Config:").pack(side="left", padx=(10, 8), pady=10)
45
+ self.cfg_entry = ctk.CTkEntry(row1, textvariable=self.cfg_var, width=560)
46
+ self.cfg_entry.pack(side="left", padx=8, pady=10)
47
+ ctk.CTkButton(row1, text="Browse…", command=self.browse).pack(side="left", padx=8, pady=10)
48
+ self.run_btn = ctk.CTkButton(row1, text="Run", command=self.run)
49
+ self.run_btn.pack(side="left", padx=8, pady=10)
50
+
51
+ # Strategy
52
+ self.strategy_var = ctk.StringVar(value="")
53
+ ctk.CTkLabel(row2, text="Strategy:").pack(side="left", padx=(10, 8), pady=10)
54
+ self.strategy_entry = ctk.CTkEntry(row2, textvariable=self.strategy_var, width=560)
55
+ self.strategy_entry.pack(side="left", padx=8, pady=10)
56
+ ctk.CTkButton(row2, text="Browse…", command=self.browse_strategy).pack(side="left", padx=8, pady=10)
57
+
58
+ self.log = ctk.CTkTextbox(self, wrap="none")
59
+ self.log.pack(fill="both", expand=True, padx=12, pady=(0, 12))
60
+
61
+ self.after(100, self.drain_logs)
62
+
63
+ def browse(self) -> None:
64
+ p = filedialog.askopenfilename(
65
+ title="Select config.yaml",
66
+ filetypes=[("YAML files", "*.yaml *.yml"), ("All files", "*.*")]
67
+ )
68
+ if p:
69
+ self.cfg_var.set(p)
70
+
71
+ def browse_strategy(self) -> None:
72
+ p = filedialog.askopenfilename(
73
+ title="Select strategy .py",
74
+ filetypes=[("Python files", "*.py"), ("All files", "*.*")]
75
+ )
76
+ if p:
77
+ self.strategy_var.set(p)
78
+
79
+ def append(self, line: str) -> None:
80
+ self.log.insert("end", line + "\n")
81
+ self.log.see("end")
82
+
83
+ def drain_logs(self) -> None:
84
+ try:
85
+ while True:
86
+ self.append(self.log_q.get_nowait())
87
+ except Empty:
88
+ pass
89
+ self.after(100, self.drain_logs)
90
+
91
+ def run(self) -> None:
92
+ cfg = Path(self.cfg_var.get().strip())
93
+ if not cfg.exists():
94
+ messagebox.showerror("Missing config", f"Config not found:\n{cfg}")
95
+ return
96
+
97
+ strategy = Path(self.strategy_var.get().strip())
98
+ if not strategy.exists():
99
+ messagebox.showerror("Missing strategy", f"Strategy not found:\n{strategy}")
100
+ self.run_btn.configure(state="normal")
101
+ return
102
+
103
+ self.run_btn.configure(state="disabled")
104
+ self.append(f"Running with config: {cfg}")
105
+
106
+ strategy = Path(self.strategy_var.get().strip()) # add a strategy picker var in UI
107
+ cmd = [sys.executable, "-m", "ghosalquant.backtest_ui", str(cfg), str(strategy)]
108
+
109
+ def worker() -> None:
110
+ try:
111
+ proc = subprocess.Popen(
112
+ cmd,
113
+ cwd=str(self.root),
114
+ stdout=subprocess.PIPE,
115
+ stderr=subprocess.STDOUT,
116
+ text=True,
117
+ )
118
+ assert proc.stdout is not None
119
+ for line in proc.stdout:
120
+ self.log_q.put(line.rstrip("\n"))
121
+ rc = proc.wait()
122
+ self.log_q.put(f"[done] exit code = {rc}")
123
+ except Exception as e:
124
+ self.log_q.put(f"[error] {e}")
125
+ finally:
126
+ self.log_q.put("__enable__")
127
+
128
+ def enable_later() -> None:
129
+ # wait for the marker then re-enable the button in UI thread
130
+ while True:
131
+ msg = self.log_q.get()
132
+ if msg == "__enable__":
133
+ self.run_btn.configure(state="normal")
134
+ break
135
+ else:
136
+ self.append(msg)
137
+
138
+ threading.Thread(target=worker, daemon=True).start()
139
+ threading.Thread(target=enable_later, daemon=True).start()
140
+
141
+
142
+ def main() -> None:
143
+ ctk.set_appearance_mode("System")
144
+ App().mainloop()
145
+
146
+
147
+ if __name__ == "__main__":
148
+ main()
@@ -0,0 +1,109 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+ from typing import Any, Type
6
+ import importlib.util
7
+
8
+ import yaml
9
+ import yfinance as yf
10
+ from matplotlib import pyplot as plt
11
+
12
+ from ghosalquant.backtester.dataframe_backtester import DataFrameBacktester
13
+ from ghosalquant.execution.strategy_base import Strategy
14
+ from ghosalquant.util.visualisation import plot_equity_curve
15
+
16
+ from . import project_root
17
+
18
+ def _safe_stem(s: str) -> str:
19
+ # sanitiser
20
+ chars: list[str] = []
21
+ for ch in s:
22
+ if ch.isalnum() or ch in ("-", "_"):
23
+ chars.append(ch)
24
+ else:
25
+ chars.append("_")
26
+
27
+ stem = "".join(chars).strip("_")
28
+ return stem or "run"
29
+
30
+
31
+ def _load_strategy_from_file(path: Path) -> Strategy:
32
+ spec = importlib.util.spec_from_file_location(path.stem, str(path))
33
+ if spec is None or spec.loader is None:
34
+ raise RuntimeError(f"Could not import strategy file: {path}")
35
+
36
+ mod = importlib.util.module_from_spec(spec)
37
+ spec.loader.exec_module(mod) # type: ignore[assignment]
38
+
39
+ # Explicit export as STRATEGY_CLASS
40
+ if hasattr(mod, "STRATEGY_CLASS"):
41
+ cls = getattr(mod, "STRATEGY_CLASS")
42
+ if isinstance(cls, type) and issubclass(cls, Strategy):
43
+ return cls()
44
+ raise TypeError("STRATEGY_CLASS exists but is not a Strategy subclass.")
45
+
46
+ # As a fallback, find first Strategy subclass in module
47
+ for obj in mod.__dict__.values():
48
+ if isinstance(obj, type) and issubclass(obj, Strategy) and obj is not Strategy:
49
+ return obj()
50
+
51
+ raise ValueError(
52
+ "No strategy found. Define STRATEGY_CLASS = YourStrategy, "
53
+ "or include a Strategy subclass in the file."
54
+ )
55
+
56
+
57
+ def run_from_config(config_path: str | Path, strategy_path: str | Path) -> int:
58
+ cfg_path = Path(config_path).resolve()
59
+ strat_path = Path(strategy_path).resolve()
60
+
61
+ if not cfg_path.exists():
62
+ print(f"Config not found: {cfg_path}")
63
+ return 2
64
+ if not strat_path.exists():
65
+ print(f"Strategy not found: {strat_path}")
66
+ return 2
67
+
68
+ cfg: dict[str, Any] = yaml.safe_load(cfg_path.read_text(encoding="utf-8")) or {}
69
+ system = cfg.get("SYSTEM", {}) or {}
70
+ tr = cfg.get("TIME_RANGE", {}) or {}
71
+
72
+ symbols = system.get("symbols", ["AAPL"]) or ["AAPL"]
73
+ symbol = symbols[0] # prototype: single symbol
74
+
75
+ start_date = tr.get("start_date", "2024-01-01")
76
+ end_date = tr.get("end_date", "2025-12-31")
77
+
78
+ print("UI Runner: DataFrameBacktester")
79
+ print(f"Config: {cfg_path}")
80
+ print(f"Strategy: {strat_path}")
81
+ print(f"Symbol: {symbol}")
82
+ print(f"Range: {start_date} -> {end_date}")
83
+
84
+ df = yf.download(symbol, start=start_date, end=end_date, auto_adjust=True, progress=False)
85
+
86
+ strategy = _load_strategy_from_file(strat_path)
87
+ backtester = DataFrameBacktester(strategy=strategy, dataframe=df, config_path=str(cfg_path))
88
+
89
+ kpms = backtester.run()
90
+ print("\nKEY PERFORMANCE METRICS:\n" + str(kpms))
91
+ print("REALISED PNL:", backtester.realised_pnl)
92
+
93
+ # Plot and save to file
94
+ plot_equity_curve(backtester.equity_curve, show_realised_unrealised=True)
95
+
96
+ out_dir = project_root() / "ui_outputs"
97
+ out_dir.mkdir(parents=True, exist_ok=True)
98
+
99
+ strategy_name = _safe_stem(strat_path.stem)
100
+ symbol_name = _safe_stem(symbol)
101
+ now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
102
+ out_path = out_dir / f"equity_curve_{now}_{symbol_name}_{strategy_name}.png"
103
+
104
+ plt.savefig(out_path, dpi=150, bbox_inches="tight")
105
+ plt.close()
106
+
107
+ print(f"Saved plot: {out_path}")
108
+
109
+ return 0
@@ -0,0 +1,7 @@
1
+ from .backtester_base import Backtester
2
+ from .dataframe_backtester import DataFrameBacktester
3
+
4
+ __all__ = [
5
+ "Backtester",
6
+ "DataFrameBacktester",
7
+ ]