trdr 0.0.1__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.
- trdr-0.0.1/.github/workflows/publish.yml +32 -0
- trdr-0.0.1/.gitignore +5 -0
- trdr-0.0.1/.vscode/extensions.json +11 -0
- trdr-0.0.1/.vscode/launch.json +18 -0
- trdr-0.0.1/.vscode/settings.json +27 -0
- trdr-0.0.1/CLAUDE.md +35 -0
- trdr-0.0.1/PKG-INFO +13 -0
- trdr-0.0.1/README.md +190 -0
- trdr-0.0.1/examples/README.md +28 -0
- trdr-0.0.1/examples/no_telemetry/script.py +25 -0
- trdr-0.0.1/examples/strategies/first-strat.trdr +21 -0
- trdr-0.0.1/examples/with_telemetry/script.py +40 -0
- trdr-0.0.1/pyproject.toml +28 -0
- trdr-0.0.1/src/__init__.py +0 -0
- trdr-0.0.1/src/trdr/__init__.py +0 -0
- trdr-0.0.1/src/trdr/conftest.py +148 -0
- trdr-0.0.1/src/trdr/core/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/bar_provider/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/bar_provider/base_bar_provider.py +115 -0
- trdr-0.0.1/src/trdr/core/bar_provider/exceptions.py +40 -0
- trdr-0.0.1/src/trdr/core/bar_provider/models.py +38 -0
- trdr-0.0.1/src/trdr/core/bar_provider/yf_bar_provider/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/bar_provider/yf_bar_provider/test_yf_bar_provider.py +73 -0
- trdr-0.0.1/src/trdr/core/bar_provider/yf_bar_provider/yf_bar_provider.py +301 -0
- trdr-0.0.1/src/trdr/core/broker/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/broker/alpaca_broker/README.md +68 -0
- trdr-0.0.1/src/trdr/core/broker/alpaca_broker/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/broker/alpaca_broker/alpaca_broker.py +404 -0
- trdr-0.0.1/src/trdr/core/broker/base_broker.py +391 -0
- trdr-0.0.1/src/trdr/core/broker/exceptions.py +6 -0
- trdr-0.0.1/src/trdr/core/broker/mock_broker/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/broker/mock_broker/mock_broker.py +130 -0
- trdr-0.0.1/src/trdr/core/broker/mock_broker/test_mock_broker.py +394 -0
- trdr-0.0.1/src/trdr/core/broker/mock_broker/test_mock_broker_pdt_validation.py +277 -0
- trdr-0.0.1/src/trdr/core/broker/models.py +145 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/README.md +82 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/base_pdt_strategy.py +32 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/exceptions.py +16 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/models.py +40 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/nun_strategy.py +86 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/test_pdt_evaluation.py +247 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/wiggle_strategy.py +65 -0
- trdr-0.0.1/src/trdr/core/broker/pdt/yolo_strategy.py +48 -0
- trdr-0.0.1/src/trdr/core/broker/test_order_model.py +407 -0
- trdr-0.0.1/src/trdr/core/broker/test_position_model.py +638 -0
- trdr-0.0.1/src/trdr/core/security_provider/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/security_provider/base_security_provider.py +108 -0
- trdr-0.0.1/src/trdr/core/security_provider/models.py +197 -0
- trdr-0.0.1/src/trdr/core/security_provider/security_provider.py +43 -0
- trdr-0.0.1/src/trdr/core/security_provider/test_security_model.py +74 -0
- trdr-0.0.1/src/trdr/core/security_provider/test_security_provider.py +30 -0
- trdr-0.0.1/src/trdr/core/shared/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/shared/exceptions.py +2 -0
- trdr-0.0.1/src/trdr/core/shared/models.py +299 -0
- trdr-0.0.1/src/trdr/core/trading_context/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/trading_context/exceptions.py +2 -0
- trdr-0.0.1/src/trdr/core/trading_context/test_trading_context.py +481 -0
- trdr-0.0.1/src/trdr/core/trading_context/trading_context.py +318 -0
- trdr-0.0.1/src/trdr/core/trading_engine/__init__.py +0 -0
- trdr-0.0.1/src/trdr/core/trading_engine/trading_engine.py +222 -0
- trdr-0.0.1/src/trdr/dsl/__init__.py +0 -0
- trdr-0.0.1/src/trdr/dsl/dsl_ast.py +375 -0
- trdr-0.0.1/src/trdr/dsl/dsl_loader.py +99 -0
- trdr-0.0.1/src/trdr/dsl/lexer.py +202 -0
- trdr-0.0.1/src/trdr/dsl/parser.py +288 -0
- trdr-0.0.1/src/trdr/test_utils/__init__.py +0 -0
- trdr-0.0.1/src/trdr/test_utils/fake_yf_download.py +37 -0
- trdr-0.0.1/src/trdr/test_utils/order_generator.py +149 -0
- trdr-0.0.1/src/trdr/test_utils/position_generator.py +152 -0
- trdr-0.0.1/src/trdr/test_utils/security_generator.py +162 -0
- trdr-0.0.1/src/trdr/test_utils/strategies/always_buy.trdr +13 -0
- trdr-0.0.1/src/trdr/test_utils/strategies/always_sell.trdr +13 -0
- trdr-0.0.1/src/trdr/test_utils/strategies/test_strat.trdr +28 -0
- trdr-0.0.1/src/trdr/test_utils/test_symbols.py +505 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
pypi-publish:
|
|
9
|
+
name: upload release to PyPI
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: '3.12'
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade pip
|
|
26
|
+
pip install build
|
|
27
|
+
|
|
28
|
+
- name: Build package
|
|
29
|
+
run: python -m build
|
|
30
|
+
|
|
31
|
+
- name: Publish to PyPI
|
|
32
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
trdr-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
"name": "Python Debugger: Current File",
|
|
11
|
+
"type": "debugpy",
|
|
12
|
+
"request": "launch",
|
|
13
|
+
"program": "${file}",
|
|
14
|
+
"console": "integratedTerminal",
|
|
15
|
+
"cwd": "${workspaceFolder}/examples",
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"git.enableSmartCommit": true,
|
|
3
|
+
"git.autofetch": true,
|
|
4
|
+
"editor.accessibilitySupport": "off",
|
|
5
|
+
"editor.foldingMaximumRegions": 15000,
|
|
6
|
+
"editor.formatOnSave": true,
|
|
7
|
+
"editor.inlineSuggest.enabled": true,
|
|
8
|
+
"github.copilot.enable": {
|
|
9
|
+
"*": true
|
|
10
|
+
},
|
|
11
|
+
"[python]": {
|
|
12
|
+
"editor.formatOnSave": true,
|
|
13
|
+
"editor.defaultFormatter": "ms-python.black-formatter"
|
|
14
|
+
},
|
|
15
|
+
"javascript.updateImportsOnFileMove.enabled": "always",
|
|
16
|
+
"[json]": {
|
|
17
|
+
"editor.defaultFormatter": "vscode.json-language-features"
|
|
18
|
+
},
|
|
19
|
+
"black-formatter.args": [
|
|
20
|
+
"--line-length=120"
|
|
21
|
+
],
|
|
22
|
+
"python.testing.pytestArgs": [
|
|
23
|
+
"."
|
|
24
|
+
],
|
|
25
|
+
"python.testing.unittestEnabled": false,
|
|
26
|
+
"python.testing.pytestEnabled": true,
|
|
27
|
+
}
|
trdr-0.0.1/CLAUDE.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# TRDR - Trading Framework
|
|
2
|
+
|
|
3
|
+
## Build & Test Commands
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Install dev dependencies
|
|
7
|
+
pip install -e ".[dev]"
|
|
8
|
+
|
|
9
|
+
# Run all tests
|
|
10
|
+
pytest
|
|
11
|
+
|
|
12
|
+
# Run specific test file
|
|
13
|
+
pytest src/trdr/path/to/test_file.py
|
|
14
|
+
|
|
15
|
+
# Run specific test
|
|
16
|
+
pytest src/trdr/path/to/test_file.py::TestClass::test_method
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Code Style Guidelines
|
|
20
|
+
|
|
21
|
+
- **Naming**: Classes=PascalCase, functions/methods=snake_case, constants=UPPER_SNAKE_CASE
|
|
22
|
+
- **Imports**: Standard lib → third-party → local, specific imports preferred
|
|
23
|
+
- **Types**: Always use type annotations for parameters and return values
|
|
24
|
+
- **Async**: Use async/await pattern with factory `create()` methods
|
|
25
|
+
- **Error handling**: Custom exception hierarchy in each module
|
|
26
|
+
- **Documentation**: Descriptive docstrings for public methods
|
|
27
|
+
- **DSL**: Trading strategies defined in `.trdr` files with STRATEGY, ENTRY, EXIT sections
|
|
28
|
+
- **Async testing**: use asyncio.run() to run async calls in tests
|
|
29
|
+
|
|
30
|
+
## Project Structure
|
|
31
|
+
|
|
32
|
+
- `src/trdr/core/`: Core trading components (broker, bar_provider, strategy)
|
|
33
|
+
- `src/trdr/dsl/`: Domain-specific language for strategy definition
|
|
34
|
+
- `src/trdr/conftest.py`: pytest fixtures
|
|
35
|
+
- `src/trdr/test_utils/`: Test utilities
|
trdr-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trdr
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Algorithmic trading library
|
|
5
|
+
Author-email: developer_jeb <developer_jeb@outlook.com>
|
|
6
|
+
Requires-Dist: aiohttp==3.11.11
|
|
7
|
+
Requires-Dist: opentelemetry-api==1.30.0
|
|
8
|
+
Requires-Dist: opentelemetry-sdk==1.30.0
|
|
9
|
+
Requires-Dist: pydantic==2.10.6
|
|
10
|
+
Requires-Dist: yfinance==0.2.55
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest-asyncio==0.25.2; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest==8.3.4; extra == 'dev'
|
trdr-0.0.1/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# TRDR - Trading Framework
|
|
2
|
+
|
|
3
|
+
TRDR is a framework for algorithmic trading in Python. It features a custom Domain-Specific Language (DSL) for expressing trading strategies in a clear, concise manner.
|
|
4
|
+
|
|
5
|
+
## 🌟 Features
|
|
6
|
+
|
|
7
|
+
- **Custom DSL**: Define trading strategies with a readable, declarative syntax
|
|
8
|
+
- **Modular Architecture**: Easily swap components like brokers and data providers
|
|
9
|
+
- **Async First**: Built from the ground up with Python's async/await pattern
|
|
10
|
+
- **Mock Trading**: Test strategies with a mock broker before using real money
|
|
11
|
+
- **Telemetry Integration**: Optional OpenTelemetry support for performance monitoring
|
|
12
|
+
- **Pattern Day Trading Controls**: Built-in [PDT rule compliance strategies](src/trdr/core/broker/pdt/README.md) (NunStrategy, WiggleStrategy, YoloStrategy)
|
|
13
|
+
|
|
14
|
+
## 📦 Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Basic installation
|
|
18
|
+
pip install trdr
|
|
19
|
+
|
|
20
|
+
# Development installation (with testing tools)
|
|
21
|
+
pip install -e ".[dev]"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 🚀 Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Define Your Strategy
|
|
27
|
+
|
|
28
|
+
Create a file `my-strategy.trdr` with your trading strategy:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
STRATEGY
|
|
32
|
+
NAME "Moving Average Crossover"
|
|
33
|
+
DESCRIPTION "Basic MA crossover strategy with risk management"
|
|
34
|
+
ENTRY
|
|
35
|
+
ALL_OF
|
|
36
|
+
MA5 CROSSED_ABOVE MA20
|
|
37
|
+
MA20 > MA50
|
|
38
|
+
CURRENT_PRICE > 100
|
|
39
|
+
EXIT
|
|
40
|
+
ANY_OF
|
|
41
|
+
CURRENT_PRICE > (AVERAGE_COST * 1.06) # 6% profit target
|
|
42
|
+
CURRENT_PRICE < (AVERAGE_COST * 0.98) # 2% stop loss
|
|
43
|
+
SIZING
|
|
44
|
+
RULE
|
|
45
|
+
CONDITION
|
|
46
|
+
ALL_OF
|
|
47
|
+
ACCOUNT_EXPOSURE < 0.5
|
|
48
|
+
NUMBER_OF_OPEN_POSITIONS < 3
|
|
49
|
+
DOLLAR_AMOUNT
|
|
50
|
+
(AVAILABLE_CASH * 0.20)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Run Your Strategy
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
from trdr.core.bar_provider.yf_bar_provider.yf_bar_provider import YFBarProvider
|
|
58
|
+
from trdr.core.security_provider.security_provider import SecurityProvider
|
|
59
|
+
from trdr.core.broker.mock_broker.mock_broker import MockBroker
|
|
60
|
+
from trdr.core.trading_engine.trading_engine import TradingEngine
|
|
61
|
+
from trdr.core.trading_context.trading_context import TradingContext
|
|
62
|
+
from trdr.core.broker.pdt.nun_strategy import NunStrategy
|
|
63
|
+
|
|
64
|
+
async def main():
|
|
65
|
+
try:
|
|
66
|
+
pdt_strategy = NunStrategy.create()
|
|
67
|
+
async with await MockBroker.create(pdt_strategy=pdt_strategy) as broker:
|
|
68
|
+
bar_provider = await YFBarProvider.create(["TSLA"])
|
|
69
|
+
security_provider = await SecurityProvider.create(bar_provider)
|
|
70
|
+
context = await TradingContext.create(security_provider, broker)
|
|
71
|
+
engine = await TradingEngine.create("my-strategy", context)
|
|
72
|
+
await engine.execute()
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print(e)
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 🛠️ Architecture
|
|
81
|
+
|
|
82
|
+
TRDR is built with a modular, component-based architecture:
|
|
83
|
+
|
|
84
|
+
- **Bar Provider**: Supplies price/volume data (Yahoo Finance implementation included)
|
|
85
|
+
- **Security Provider**: Manages available securities for trading
|
|
86
|
+
- **Broker**: Handles order execution
|
|
87
|
+
- [Mock Broker](src/trdr/core/broker/mock_broker/) - Local simulation for testing
|
|
88
|
+
- [Alpaca Broker](src/trdr/core/broker/alpaca_broker/README.md) - Real trading with Alpaca API
|
|
89
|
+
- **Trading Context**: Coordinates components and maintains state
|
|
90
|
+
- **Trading Engine**: Executes strategies using the DSL parser
|
|
91
|
+
- **[PDT Strategies](src/trdr/core/broker/pdt/README.md)**: Enforces Pattern Day Trading rules with multiple compliance strategies
|
|
92
|
+
|
|
93
|
+
## 📊 DSL Reference
|
|
94
|
+
|
|
95
|
+
The TRDR Domain Specific Language provides a clean (I hope) syntax for expressing trading logic:
|
|
96
|
+
|
|
97
|
+
### Strategy Structure
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
STRATEGY
|
|
101
|
+
NAME "Strategy Name"
|
|
102
|
+
DESCRIPTION "Strategy Description"
|
|
103
|
+
ENTRY
|
|
104
|
+
# Entry conditions
|
|
105
|
+
EXIT
|
|
106
|
+
# Exit conditions
|
|
107
|
+
SIZING
|
|
108
|
+
# Position sizing rules
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Logical Operators
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
ALL_OF # All conditions must be true
|
|
115
|
+
ANY_OF # Any condition can be true
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Technical Indicators
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
MA{period} # Moving average (e.g., MA5, MA20, MA50, MA100, MA200)
|
|
122
|
+
AV{period} # Average volume (e.g., AV5, AV20, AV50, AV100, AV200)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Comparison Operators
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
> # Greater than
|
|
129
|
+
< # Less than
|
|
130
|
+
>= # Greater than or equal to
|
|
131
|
+
<= # Less than or equal to
|
|
132
|
+
== # Equal to
|
|
133
|
+
CROSSED_ABOVE # Indicator crossed above another
|
|
134
|
+
CROSSED_BELOW # Indicator crossed below another
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Price Metrics
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
CURRENT_PRICE # Current price of the security
|
|
141
|
+
CURRENT_VOLUME # Current volume of the security
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Account Metrics
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
ACCOUNT_EXPOSURE # Percentage of account exposed to market
|
|
148
|
+
AVAILABLE_CASH # Available cash for trading
|
|
149
|
+
AVERAGE_COST # Average cost of current position
|
|
150
|
+
NUMBER_OF_OPEN_POSITIONS # Number of currently open positions
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Mathematical Operators
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
+ # Addition
|
|
157
|
+
- # Subtraction
|
|
158
|
+
* # Multiplication
|
|
159
|
+
/ # Division
|
|
160
|
+
(expression) # Parentheses for grouping expressions
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 📚 Examples
|
|
164
|
+
|
|
165
|
+
Check the `examples/` directory for complete examples:
|
|
166
|
+
|
|
167
|
+
- **No Telemetry Example**: Basic usage without OpenTelemetry
|
|
168
|
+
- **With Telemetry Example**: Using OpenTelemetry for monitoring
|
|
169
|
+
- **Strategy Examples**: Sample trading strategies
|
|
170
|
+
|
|
171
|
+
## 🧪 Testing
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Run all tests
|
|
175
|
+
pytest
|
|
176
|
+
|
|
177
|
+
# Run specific test file
|
|
178
|
+
pytest src/trdr/path/to/test_file.py
|
|
179
|
+
|
|
180
|
+
# Run specific test
|
|
181
|
+
pytest src/trdr/path/to/test_file.py::TestClass::test_method
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 📝 License
|
|
185
|
+
|
|
186
|
+
[MIT License](LICENSE)
|
|
187
|
+
|
|
188
|
+
## 🤝 Contributing
|
|
189
|
+
|
|
190
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# TRDR Examples
|
|
2
|
+
|
|
3
|
+
This directory contains examples demonstrating how to use the TRDR trading framework in different scenarios.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
### No Telemetry
|
|
8
|
+
|
|
9
|
+
The `no_telemetry` directory demonstrates how to use TRDR without OpenTelemetry integration. This is a simpler setup that doesn't require any tracing infrastructure.
|
|
10
|
+
|
|
11
|
+
**Key features:**
|
|
12
|
+
- Basic usage with MockBroker
|
|
13
|
+
- No telemetry/tracing configuration
|
|
14
|
+
- Yahoo Finance bar provider for historical data
|
|
15
|
+
|
|
16
|
+
### With Telemetry
|
|
17
|
+
|
|
18
|
+
The `with_telemetry` directory shows how to set up TRDR with OpenTelemetry for distributed tracing and monitoring.
|
|
19
|
+
|
|
20
|
+
**Key features:**
|
|
21
|
+
- OpenTelemetry integration with OTLP exporter
|
|
22
|
+
- MockBroker integration with telemetry
|
|
23
|
+
- Yahoo Finance bar provider for historical data
|
|
24
|
+
- Pattern Day Trading (PDT) strategy integration
|
|
25
|
+
|
|
26
|
+
## Strategy Examples
|
|
27
|
+
|
|
28
|
+
The `strategies` directory contains example strategy files in TRDR's domain-specific language (DSL).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from trdr.core.bar_provider.yf_bar_provider.yf_bar_provider import YFBarProvider
|
|
4
|
+
from trdr.core.security_provider.security_provider import SecurityProvider
|
|
5
|
+
from trdr.core.broker.mock_broker.mock_broker import MockBroker
|
|
6
|
+
from trdr.core.trading_engine.trading_engine import TradingEngine
|
|
7
|
+
from trdr.core.trading_context.trading_context import TradingContext
|
|
8
|
+
from trdr.core.broker.pdt.nun_strategy import NunStrategy
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
|
|
12
|
+
async def main():
|
|
13
|
+
try:
|
|
14
|
+
pdt_strategy = NunStrategy.create()
|
|
15
|
+
async with await MockBroker.create(pdt_strategy=pdt_strategy) as broker:
|
|
16
|
+
bar_provider = await YFBarProvider.create(["TSLA"])
|
|
17
|
+
security_provider = await SecurityProvider.create(bar_provider)
|
|
18
|
+
context = await TradingContext.create(security_provider, broker)
|
|
19
|
+
engine = await TradingEngine.create("first-strat", context, strategies_dir="../strategies")
|
|
20
|
+
await engine.execute()
|
|
21
|
+
|
|
22
|
+
except Exception as e:
|
|
23
|
+
print(e)
|
|
24
|
+
|
|
25
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
STRATEGY
|
|
2
|
+
NAME "Basic Strategy"
|
|
3
|
+
DESCRIPTION "Basic testing strategy"
|
|
4
|
+
ENTRY
|
|
5
|
+
ALL_OF
|
|
6
|
+
MA5 CROSSED_ABOVE MA20
|
|
7
|
+
MA20 > MA50
|
|
8
|
+
CURRENT_PRICE > 100
|
|
9
|
+
EXIT
|
|
10
|
+
ANY_OF
|
|
11
|
+
CURRENT_PRICE > (AVERAGE_COST * 1.06)
|
|
12
|
+
CURRENT_PRICE < (AVERAGE_COST * 0.98)
|
|
13
|
+
SIZING
|
|
14
|
+
RULE
|
|
15
|
+
CONDITION
|
|
16
|
+
ALL_OF
|
|
17
|
+
ACCOUNT_EXPOSURE < 0.5
|
|
18
|
+
NUMBER_OF_OPEN_POSITIONS < 3
|
|
19
|
+
DOLLAR_AMOUNT
|
|
20
|
+
(AVAILABLE_CASH * 0.20)
|
|
21
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from opentelemetry import trace
|
|
3
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
4
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
5
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
6
|
+
|
|
7
|
+
from trdr.core.bar_provider.yf_bar_provider.yf_bar_provider import YFBarProvider
|
|
8
|
+
from trdr.core.security_provider.security_provider import SecurityProvider
|
|
9
|
+
from trdr.core.broker.mock_broker.mock_broker import MockBroker
|
|
10
|
+
from trdr.core.trading_engine.trading_engine import TradingEngine
|
|
11
|
+
from trdr.core.trading_context.trading_context import TradingContext
|
|
12
|
+
from trdr.core.broker.pdt.nun_strategy import NunStrategy
|
|
13
|
+
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
|
|
16
|
+
async def main():
|
|
17
|
+
tracer_provider = TracerProvider()
|
|
18
|
+
"""
|
|
19
|
+
I run the collector as a container locally for testing purposes.
|
|
20
|
+
"""
|
|
21
|
+
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
|
|
22
|
+
span_processor = BatchSpanProcessor(otlp_exporter, max_export_batch_size=20)
|
|
23
|
+
tracer_provider.add_span_processor(span_processor)
|
|
24
|
+
trace.set_tracer_provider(tracer_provider)
|
|
25
|
+
tracer = trace.get_tracer("trdr")
|
|
26
|
+
try:
|
|
27
|
+
pdt_strategy = NunStrategy.create(tracer)
|
|
28
|
+
async with await MockBroker.create(pdt_strategy, tracer) as broker:
|
|
29
|
+
bar_provider = await YFBarProvider.create(["TSLA"], tracer)
|
|
30
|
+
security_provider = await SecurityProvider.create(bar_provider, tracer)
|
|
31
|
+
context = await TradingContext.create(security_provider, broker, tracer)
|
|
32
|
+
engine = await TradingEngine.create(
|
|
33
|
+
"first-strat", context, strategies_dir="../strategies", tracer=tracer
|
|
34
|
+
)
|
|
35
|
+
await engine.execute()
|
|
36
|
+
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(e)
|
|
39
|
+
|
|
40
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trdr"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "developer_jeb", email = "developer_jeb@outlook.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Algorithmic trading library"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"yfinance==0.2.55",
|
|
14
|
+
"aiohttp==3.11.11",
|
|
15
|
+
"opentelemetry-api==1.30.0",
|
|
16
|
+
"opentelemetry-sdk==1.30.0",
|
|
17
|
+
"pydantic==2.10.6",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = [
|
|
22
|
+
"pytest==8.3.4",
|
|
23
|
+
"pytest-asyncio==0.25.2",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
pythonpath = ["src"]
|
|
28
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import pytest
|
|
3
|
+
import yfinance as yf
|
|
4
|
+
import random
|
|
5
|
+
import datetime
|
|
6
|
+
|
|
7
|
+
from .core.security_provider.security_provider import SecurityProvider
|
|
8
|
+
from .core.bar_provider.yf_bar_provider.yf_bar_provider import YFBarProvider
|
|
9
|
+
from .test_utils.fake_yf_download import fake_yf_download
|
|
10
|
+
from .test_utils.security_generator import SecurityGenerator, SecurityCriteria
|
|
11
|
+
from .test_utils.position_generator import PositionGenerator, PositionCriteria
|
|
12
|
+
from .test_utils.order_generator import OrderGenerator, OrderCriteria
|
|
13
|
+
from .core.broker.mock_broker.mock_broker import MockBroker
|
|
14
|
+
from .core.broker.pdt.nun_strategy import NunStrategy
|
|
15
|
+
from .core.broker.pdt.wiggle_strategy import WiggleStrategy
|
|
16
|
+
from .core.broker.pdt.yolo_strategy import YoloStrategy
|
|
17
|
+
from .core.trading_context.trading_context import TradingContext
|
|
18
|
+
from .core.shared.models import TradingDateTime
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture(scope="function")
|
|
22
|
+
def weekday_trading_datetime():
|
|
23
|
+
trading_now = TradingDateTime.now()
|
|
24
|
+
while trading_now.is_weekend:
|
|
25
|
+
trading_now = trading_now + datetime.timedelta(days=1)
|
|
26
|
+
return trading_now
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture(scope="function")
|
|
30
|
+
def weekend_trading_datetime():
|
|
31
|
+
trading_now = TradingDateTime.now()
|
|
32
|
+
while not trading_now.is_weekend:
|
|
33
|
+
trading_now = trading_now + datetime.timedelta(days=1)
|
|
34
|
+
return trading_now
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.fixture(scope="function")
|
|
38
|
+
def yf_bar_provider_with_fake_data(monkeypatch):
|
|
39
|
+
monkeypatch.setattr(yf, "download", fake_yf_download)
|
|
40
|
+
monkeypatch.setattr(yf.shared, "_ERRORS", {"ABCDEFG": "YFTzMissingError()", "AMZN": "JSONDecodeError()"})
|
|
41
|
+
return asyncio.run(YFBarProvider.create(["AAPL", "MSFT", "ABCDEFG", "AMZN"]))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.fixture(scope="function")
|
|
45
|
+
def security_provider_with_fake_data(yf_bar_provider_with_fake_data):
|
|
46
|
+
return asyncio.run(SecurityProvider.create(yf_bar_provider_with_fake_data))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture(scope="function")
|
|
50
|
+
def random_security(symbol="AAPL", bar_count=200):
|
|
51
|
+
"""Create a random security with the given symbol and bar count.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
symbol: The ticker symbol to use
|
|
55
|
+
count: The number of bars to generate
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A Security instance with randomly generated price and volume data
|
|
59
|
+
"""
|
|
60
|
+
generator = SecurityGenerator(SecurityCriteria(bar_count=bar_count))
|
|
61
|
+
security = generator.find_suitable_security()
|
|
62
|
+
# Override the symbol if requested
|
|
63
|
+
if symbol != security.symbol:
|
|
64
|
+
security.symbol = symbol
|
|
65
|
+
return security
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.fixture(scope="function")
|
|
69
|
+
def get_random_security():
|
|
70
|
+
"""Legacy fixture name for backward compatibility."""
|
|
71
|
+
generator = SecurityGenerator(SecurityCriteria(bar_count=200))
|
|
72
|
+
return generator.find_suitable_security()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@pytest.fixture(scope="module")
|
|
76
|
+
def security_generator():
|
|
77
|
+
"""Return a configured SecurityGenerator instance."""
|
|
78
|
+
return SecurityGenerator(SecurityCriteria(bar_count=200))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pytest.fixture(scope="function")
|
|
82
|
+
def short_dummy_position():
|
|
83
|
+
"""Create a test position with default values."""
|
|
84
|
+
position = PositionGenerator(criteria=PositionCriteria(bar_count=1, net_position_bias=0)).generate_positions()[0]
|
|
85
|
+
return position
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.fixture(scope="function")
|
|
89
|
+
def long_dummy_position():
|
|
90
|
+
"""Create a test position with default values."""
|
|
91
|
+
position = PositionGenerator(criteria=PositionCriteria(bar_count=1, net_position_bias=1)).generate_positions()[0]
|
|
92
|
+
return position
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.fixture(scope="function")
|
|
96
|
+
def dummy_positions():
|
|
97
|
+
"""Create a dictionary of test positions."""
|
|
98
|
+
num_positions = random.randint(1, 9)
|
|
99
|
+
positions = PositionGenerator(
|
|
100
|
+
criteria=PositionCriteria(bar_count=num_positions, net_position_bias=0.5)
|
|
101
|
+
).generate_positions()
|
|
102
|
+
return positions
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@pytest.fixture(scope="function")
|
|
106
|
+
def mock_broker_with_nun_strategy():
|
|
107
|
+
nun_strategy = NunStrategy.create()
|
|
108
|
+
broker = asyncio.run(MockBroker.create(pdt_strategy=nun_strategy))
|
|
109
|
+
yield broker
|
|
110
|
+
asyncio.run(broker._session.close())
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.fixture(scope="function")
|
|
114
|
+
def mock_broker_with_wiggle_strategy():
|
|
115
|
+
pdt_strategy = WiggleStrategy.create()
|
|
116
|
+
pdt_strategy.wiggle_room = 2
|
|
117
|
+
broker = asyncio.run(MockBroker.create(pdt_strategy=pdt_strategy))
|
|
118
|
+
yield broker
|
|
119
|
+
asyncio.run(broker._session.close())
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@pytest.fixture(scope="function")
|
|
123
|
+
def mock_broker_with_yolo_strategy():
|
|
124
|
+
pdt_strategy = YoloStrategy.create()
|
|
125
|
+
broker = asyncio.run(MockBroker.create(pdt_strategy=pdt_strategy))
|
|
126
|
+
yield broker
|
|
127
|
+
asyncio.run(broker._session.close())
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.fixture(scope="function")
|
|
131
|
+
def mock_trading_context(security_provider_with_fake_data, mock_broker_with_nun_strategy):
|
|
132
|
+
return asyncio.run(TradingContext.create(security_provider_with_fake_data, mock_broker_with_nun_strategy))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@pytest.fixture(scope="function")
|
|
136
|
+
def create_order():
|
|
137
|
+
|
|
138
|
+
def _create_order(symbol: str, side=None):
|
|
139
|
+
return OrderGenerator(criteria=OrderCriteria(count=1, symbol=symbol, side=side)).generate_orders()[0]
|
|
140
|
+
|
|
141
|
+
return _create_order
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@pytest.fixture(scope="function")
|
|
145
|
+
def prepared_trading_context(mock_trading_context: TradingContext):
|
|
146
|
+
"""Prepare trading context with a valid symbol and security."""
|
|
147
|
+
asyncio.run(mock_trading_context.next_symbol())
|
|
148
|
+
return mock_trading_context
|
|
File without changes
|
|
File without changes
|