aptis-strategy-sdk 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.
- aptis_strategy_sdk-0.1.0/PKG-INFO +112 -0
- aptis_strategy_sdk-0.1.0/README.md +93 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk/__init__.py +22 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk/client.py +115 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk/exceptions.py +17 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk/models.py +47 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk/strategy.py +59 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk.egg-info/PKG-INFO +112 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk.egg-info/SOURCES.txt +12 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk.egg-info/dependency_links.txt +1 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk.egg-info/requires.txt +8 -0
- aptis_strategy_sdk-0.1.0/aptis_strategy_sdk.egg-info/top_level.txt +1 -0
- aptis_strategy_sdk-0.1.0/pyproject.toml +33 -0
- aptis_strategy_sdk-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aptis-strategy-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for building plugin trading strategies on the Aptis platform
|
|
5
|
+
Author: Richard Chung
|
|
6
|
+
Project-URL: Homepage, https://aptis.com
|
|
7
|
+
Project-URL: Documentation, https://aptis.com/docs
|
|
8
|
+
Classifier: License :: Other/Proprietary License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: requests>=2.31.0
|
|
13
|
+
Requires-Dist: pandas>=2.0.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
18
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# Aptis Strategy SDK
|
|
21
|
+
|
|
22
|
+
Python SDK for building trading strategies on the Aptis platform.
|
|
23
|
+
|
|
24
|
+
**Author:** Richard Chung
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install aptis-strategy-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Environment Variables
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
APTIS_API_KEY=your_api_key_here # Your API key
|
|
36
|
+
APTIS_ACCOUNT=Theme # Account to trade under (Theme or QSpark)
|
|
37
|
+
APTIS_API_URL=http://localhost:8082 # Backend URL
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Never hardcode these — always read from env vars.
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import os
|
|
46
|
+
from aptis_strategy_sdk import AptisClient, Strategy, Signal
|
|
47
|
+
from datetime import date
|
|
48
|
+
|
|
49
|
+
client = AptisClient(
|
|
50
|
+
api_key=os.getenv("APTIS_API_KEY", "test_key_123"),
|
|
51
|
+
base_url=os.getenv("APTIS_API_URL", "http://localhost:8082")
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
class MyStrategy(Strategy):
|
|
55
|
+
def generate_signals(self, run_date: date):
|
|
56
|
+
# your logic here
|
|
57
|
+
return [Signal(symbol="AAPL", asset_class="equity",
|
|
58
|
+
quantity=100, side="BUY", signal_type="ENTRY")]
|
|
59
|
+
|
|
60
|
+
strategy = MyStrategy(
|
|
61
|
+
name="My Strategy",
|
|
62
|
+
client=client,
|
|
63
|
+
account=os.getenv("APTIS_ACCOUNT", "Theme")
|
|
64
|
+
)
|
|
65
|
+
result = strategy.run(date.today())
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Accounts
|
|
69
|
+
|
|
70
|
+
The platform supports multiple accounts (trading scenarios). Each account has its own API key. Set `APTIS_ACCOUNT` to target the correct account:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Run against Theme account
|
|
74
|
+
APTIS_ACCOUNT=Theme APTIS_API_KEY=test_key_123 python my_strategy.py
|
|
75
|
+
|
|
76
|
+
# Run against QSpark account
|
|
77
|
+
APTIS_ACCOUNT=QSpark APTIS_API_KEY=qspark_key_123 python my_strategy.py
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Methods
|
|
81
|
+
|
|
82
|
+
### AptisClient
|
|
83
|
+
|
|
84
|
+
- `submit_signal(signal, strategy_name, account)` — submit a trading signal
|
|
85
|
+
- `get_config(strategy_name)` — get NAV, funds, portfolio_weight, enabled
|
|
86
|
+
- `get_quotes(symbols, asset_class)` — get market quotes
|
|
87
|
+
- `get_bars(symbol, asset_class, timeframe, start, end)` — get OHLCV bars
|
|
88
|
+
- `get_positions(account, strategy_name)` — get current positions
|
|
89
|
+
- `get_pnl(account, start_date, end_date, strategy_name)` — get P&L
|
|
90
|
+
|
|
91
|
+
### Strategy Base Class
|
|
92
|
+
|
|
93
|
+
- `generate_signals(date)` — override with your logic, return `List[Signal]`
|
|
94
|
+
- `run(date)` — calls `generate_signals`, submits all signals
|
|
95
|
+
- `get_features(symbols, date, features)` — fetch Dagster features
|
|
96
|
+
- `get_positions()` — get positions for this strategy
|
|
97
|
+
|
|
98
|
+
## Signal Fields
|
|
99
|
+
|
|
100
|
+
| Field | Required | Description |
|
|
101
|
+
|-------|----------|-------------|
|
|
102
|
+
| `symbol` | Yes | Ticker |
|
|
103
|
+
| `asset_class` | Yes | `equity`, `etf`, `crypto`, `forex`, `commodity` |
|
|
104
|
+
| `quantity` | Yes | Units |
|
|
105
|
+
| `side` | Yes | `BUY` or `SELL` |
|
|
106
|
+
| `signal_type` | Yes | `ENTRY`, `EXIT`, `ADJUST` |
|
|
107
|
+
| `order_parameters` | No | `order_type`, `limit_price`, etc. |
|
|
108
|
+
| `metadata` | No | Notes only, not used for execution |
|
|
109
|
+
|
|
110
|
+
## Full Documentation
|
|
111
|
+
|
|
112
|
+
See [docs/customer-integration/](../docs/customer-integration/) for complete guides.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Aptis Strategy SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for building trading strategies on the Aptis platform.
|
|
4
|
+
|
|
5
|
+
**Author:** Richard Chung
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install aptis-strategy-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Environment Variables
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
APTIS_API_KEY=your_api_key_here # Your API key
|
|
17
|
+
APTIS_ACCOUNT=Theme # Account to trade under (Theme or QSpark)
|
|
18
|
+
APTIS_API_URL=http://localhost:8082 # Backend URL
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Never hardcode these — always read from env vars.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import os
|
|
27
|
+
from aptis_strategy_sdk import AptisClient, Strategy, Signal
|
|
28
|
+
from datetime import date
|
|
29
|
+
|
|
30
|
+
client = AptisClient(
|
|
31
|
+
api_key=os.getenv("APTIS_API_KEY", "test_key_123"),
|
|
32
|
+
base_url=os.getenv("APTIS_API_URL", "http://localhost:8082")
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
class MyStrategy(Strategy):
|
|
36
|
+
def generate_signals(self, run_date: date):
|
|
37
|
+
# your logic here
|
|
38
|
+
return [Signal(symbol="AAPL", asset_class="equity",
|
|
39
|
+
quantity=100, side="BUY", signal_type="ENTRY")]
|
|
40
|
+
|
|
41
|
+
strategy = MyStrategy(
|
|
42
|
+
name="My Strategy",
|
|
43
|
+
client=client,
|
|
44
|
+
account=os.getenv("APTIS_ACCOUNT", "Theme")
|
|
45
|
+
)
|
|
46
|
+
result = strategy.run(date.today())
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Accounts
|
|
50
|
+
|
|
51
|
+
The platform supports multiple accounts (trading scenarios). Each account has its own API key. Set `APTIS_ACCOUNT` to target the correct account:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Run against Theme account
|
|
55
|
+
APTIS_ACCOUNT=Theme APTIS_API_KEY=test_key_123 python my_strategy.py
|
|
56
|
+
|
|
57
|
+
# Run against QSpark account
|
|
58
|
+
APTIS_ACCOUNT=QSpark APTIS_API_KEY=qspark_key_123 python my_strategy.py
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API Methods
|
|
62
|
+
|
|
63
|
+
### AptisClient
|
|
64
|
+
|
|
65
|
+
- `submit_signal(signal, strategy_name, account)` — submit a trading signal
|
|
66
|
+
- `get_config(strategy_name)` — get NAV, funds, portfolio_weight, enabled
|
|
67
|
+
- `get_quotes(symbols, asset_class)` — get market quotes
|
|
68
|
+
- `get_bars(symbol, asset_class, timeframe, start, end)` — get OHLCV bars
|
|
69
|
+
- `get_positions(account, strategy_name)` — get current positions
|
|
70
|
+
- `get_pnl(account, start_date, end_date, strategy_name)` — get P&L
|
|
71
|
+
|
|
72
|
+
### Strategy Base Class
|
|
73
|
+
|
|
74
|
+
- `generate_signals(date)` — override with your logic, return `List[Signal]`
|
|
75
|
+
- `run(date)` — calls `generate_signals`, submits all signals
|
|
76
|
+
- `get_features(symbols, date, features)` — fetch Dagster features
|
|
77
|
+
- `get_positions()` — get positions for this strategy
|
|
78
|
+
|
|
79
|
+
## Signal Fields
|
|
80
|
+
|
|
81
|
+
| Field | Required | Description |
|
|
82
|
+
|-------|----------|-------------|
|
|
83
|
+
| `symbol` | Yes | Ticker |
|
|
84
|
+
| `asset_class` | Yes | `equity`, `etf`, `crypto`, `forex`, `commodity` |
|
|
85
|
+
| `quantity` | Yes | Units |
|
|
86
|
+
| `side` | Yes | `BUY` or `SELL` |
|
|
87
|
+
| `signal_type` | Yes | `ENTRY`, `EXIT`, `ADJUST` |
|
|
88
|
+
| `order_parameters` | No | `order_type`, `limit_price`, etc. |
|
|
89
|
+
| `metadata` | No | Notes only, not used for execution |
|
|
90
|
+
|
|
91
|
+
## Full Documentation
|
|
92
|
+
|
|
93
|
+
See [docs/customer-integration/](../docs/customer-integration/) for complete guides.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aptis Strategy SDK - Build trading strategies on Aptis platform
|
|
3
|
+
|
|
4
|
+
Author: Richard Chung
|
|
5
|
+
"""
|
|
6
|
+
from aptis_strategy_sdk.client import AptisClient
|
|
7
|
+
from aptis_strategy_sdk.strategy import Strategy
|
|
8
|
+
from aptis_strategy_sdk.models import Signal, Position, Bar, Quote
|
|
9
|
+
from aptis_strategy_sdk.exceptions import AptisAPIError, AuthenticationError
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"AptisClient",
|
|
15
|
+
"Strategy",
|
|
16
|
+
"Signal",
|
|
17
|
+
"Position",
|
|
18
|
+
"Bar",
|
|
19
|
+
"Quote",
|
|
20
|
+
"AptisAPIError",
|
|
21
|
+
"AuthenticationError",
|
|
22
|
+
]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Aptis API client"""
|
|
2
|
+
import requests
|
|
3
|
+
from typing import List, Dict, Any, Optional
|
|
4
|
+
from datetime import date
|
|
5
|
+
from aptis_strategy_sdk.models import Signal, Position, Bar, Quote
|
|
6
|
+
from aptis_strategy_sdk.exceptions import AptisAPIError, AuthenticationError
|
|
7
|
+
|
|
8
|
+
class AptisClient:
|
|
9
|
+
"""Client for Aptis platform API"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, api_key: str, base_url: str = "http://localhost:8082"):
|
|
12
|
+
self.api_key = api_key
|
|
13
|
+
self.base_url = base_url.rstrip("/")
|
|
14
|
+
self.session = requests.Session()
|
|
15
|
+
self.session.headers.update({"Authorization": f"Bearer {api_key}"})
|
|
16
|
+
self._config_cache = {}
|
|
17
|
+
|
|
18
|
+
def get_config(self, strategy_name: str) -> Dict[str, Any]:
|
|
19
|
+
"""Get strategy configuration (NAV, funds, portfolio_weight)"""
|
|
20
|
+
if strategy_name in self._config_cache:
|
|
21
|
+
return self._config_cache[strategy_name]
|
|
22
|
+
|
|
23
|
+
response = self._request("GET", "/api/v1/strategy/config", params={"strategy_name": strategy_name})
|
|
24
|
+
config = response.get("config", {})
|
|
25
|
+
self._config_cache[strategy_name] = config
|
|
26
|
+
return config
|
|
27
|
+
|
|
28
|
+
def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
29
|
+
"""Make API request with error handling"""
|
|
30
|
+
url = f"{self.base_url}{endpoint}"
|
|
31
|
+
try:
|
|
32
|
+
response = self.session.request(method, url, **kwargs)
|
|
33
|
+
response.raise_for_status()
|
|
34
|
+
return response.json()
|
|
35
|
+
except requests.exceptions.HTTPError as e:
|
|
36
|
+
if e.response.status_code == 401:
|
|
37
|
+
raise AuthenticationError("Invalid API key")
|
|
38
|
+
raise AptisAPIError(f"API error: {e.response.text}")
|
|
39
|
+
except requests.exceptions.RequestException as e:
|
|
40
|
+
raise AptisAPIError(f"Request failed: {str(e)}")
|
|
41
|
+
|
|
42
|
+
def submit_signal(self, signal: Signal = None, strategy_name: str = None,
|
|
43
|
+
account: str = None, symbol: str = None, asset_class: str = None,
|
|
44
|
+
quantity: float = None, side: str = None, signal_type: str = None,
|
|
45
|
+
order_parameters: dict = None, metadata: dict = None) -> Dict[str, Any]:
|
|
46
|
+
"""Submit trading signal - accepts Signal object or individual kwargs"""
|
|
47
|
+
if signal is None:
|
|
48
|
+
signal = Signal(symbol=symbol, asset_class=asset_class, quantity=quantity,
|
|
49
|
+
side=side, signal_type=signal_type,
|
|
50
|
+
order_parameters=order_parameters, metadata=metadata)
|
|
51
|
+
payload = {
|
|
52
|
+
"symbol": signal.symbol,
|
|
53
|
+
"asset_class": signal.asset_class,
|
|
54
|
+
"quantity": signal.quantity,
|
|
55
|
+
"side": signal.side,
|
|
56
|
+
"signal_type": signal.signal_type,
|
|
57
|
+
"strategy_name": strategy_name,
|
|
58
|
+
"account": account,
|
|
59
|
+
"order_parameters": signal.order_parameters,
|
|
60
|
+
"metadata": signal.metadata,
|
|
61
|
+
}
|
|
62
|
+
return self._request("POST", "/api/v1/strategy/signals/submit", json=payload)
|
|
63
|
+
|
|
64
|
+
def get_features(self, symbols: List[str], date: date, features: List[str]) -> Dict[str, Dict[str, float]]:
|
|
65
|
+
"""Get Dagster-calculated features"""
|
|
66
|
+
params = {
|
|
67
|
+
"symbols": ",".join(symbols),
|
|
68
|
+
"date": date.isoformat(),
|
|
69
|
+
"features": ",".join(features),
|
|
70
|
+
}
|
|
71
|
+
response = self._request("GET", "/api/v1/strategy/features", params=params)
|
|
72
|
+
return response.get("data", {})
|
|
73
|
+
|
|
74
|
+
def get_quotes(self, symbols: List[str], asset_class: Optional[str] = None) -> List[Dict]:
|
|
75
|
+
"""Get market quotes"""
|
|
76
|
+
params = {"symbols": ",".join(symbols)}
|
|
77
|
+
if asset_class:
|
|
78
|
+
params["asset_class"] = asset_class
|
|
79
|
+
response = self._request("GET", "/api/v1/strategy/market-data/quotes", params=params)
|
|
80
|
+
return response.get("data", [])
|
|
81
|
+
|
|
82
|
+
def get_bars(self, symbol: str, asset_class: str, timeframe: str,
|
|
83
|
+
start: str, end: str, limit: int = 1000) -> List[Bar]:
|
|
84
|
+
"""Get OHLCV bars"""
|
|
85
|
+
params = {
|
|
86
|
+
"symbol": symbol,
|
|
87
|
+
"asset_class": asset_class,
|
|
88
|
+
"timeframe": timeframe,
|
|
89
|
+
"start": start,
|
|
90
|
+
"end": end,
|
|
91
|
+
"limit": limit,
|
|
92
|
+
}
|
|
93
|
+
response = self._request("GET", "/api/v1/strategy/market-data/bars", params=params)
|
|
94
|
+
return response.get("bars", [])
|
|
95
|
+
|
|
96
|
+
def get_positions(self, account: str, strategy_name: Optional[str] = None) -> List[Position]:
|
|
97
|
+
"""Get current positions"""
|
|
98
|
+
params = {"account": account}
|
|
99
|
+
if strategy_name:
|
|
100
|
+
params["strategy_name"] = strategy_name
|
|
101
|
+
response = self._request("GET", "/api/v1/strategy/positions", params=params)
|
|
102
|
+
return [Position(**p) for p in response.get("positions", [])]
|
|
103
|
+
|
|
104
|
+
def get_pnl(self, account: str, start_date: date, end_date: date,
|
|
105
|
+
strategy_name: Optional[str] = None) -> Dict[str, Any]:
|
|
106
|
+
"""Get P&L summary"""
|
|
107
|
+
params = {
|
|
108
|
+
"account": account,
|
|
109
|
+
"start_date": start_date.isoformat(),
|
|
110
|
+
"end_date": end_date.isoformat(),
|
|
111
|
+
}
|
|
112
|
+
if strategy_name:
|
|
113
|
+
params["strategy_name"] = strategy_name
|
|
114
|
+
response = self._request("GET", "/api/v1/strategy/pnl", params=params)
|
|
115
|
+
return response.get("summary", {})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""SDK exceptions"""
|
|
2
|
+
|
|
3
|
+
class AptisAPIError(Exception):
|
|
4
|
+
"""Base exception for Aptis API errors"""
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
class AuthenticationError(AptisAPIError):
|
|
8
|
+
"""Authentication failed"""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class ValidationError(AptisAPIError):
|
|
12
|
+
"""Request validation failed"""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
class RateLimitError(AptisAPIError):
|
|
16
|
+
"""API rate limit exceeded"""
|
|
17
|
+
pass
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Data models for Aptis SDK"""
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class Signal:
|
|
8
|
+
"""Trading signal"""
|
|
9
|
+
symbol: str
|
|
10
|
+
asset_class: str
|
|
11
|
+
quantity: float
|
|
12
|
+
side: str # BUY or SELL
|
|
13
|
+
signal_type: str # ENTRY, EXIT, ADJUST
|
|
14
|
+
order_parameters: Optional[Dict[str, Any]] = None
|
|
15
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Position:
|
|
19
|
+
"""Current position"""
|
|
20
|
+
symbol: str
|
|
21
|
+
asset_class: str
|
|
22
|
+
quantity: float
|
|
23
|
+
avg_entry_price: float
|
|
24
|
+
current_price: float
|
|
25
|
+
unrealized_pnl: float
|
|
26
|
+
realized_pnl: float
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class Bar:
|
|
30
|
+
"""OHLCV bar"""
|
|
31
|
+
symbol: str
|
|
32
|
+
timestamp: datetime
|
|
33
|
+
open: float
|
|
34
|
+
high: float
|
|
35
|
+
low: float
|
|
36
|
+
close: float
|
|
37
|
+
volume: float
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Quote:
|
|
41
|
+
"""Market quote"""
|
|
42
|
+
symbol: str
|
|
43
|
+
timestamp: datetime
|
|
44
|
+
bid: float
|
|
45
|
+
ask: float
|
|
46
|
+
last: float
|
|
47
|
+
volume: float
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Base strategy class"""
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from datetime import date
|
|
5
|
+
from aptis_strategy_sdk.client import AptisClient
|
|
6
|
+
from aptis_strategy_sdk.models import Signal
|
|
7
|
+
|
|
8
|
+
class Strategy(ABC):
|
|
9
|
+
"""Base class for trading strategies"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, name: str, client: AptisClient, account: str):
|
|
12
|
+
self.name = name
|
|
13
|
+
self.client = client
|
|
14
|
+
self.account = account
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def generate_signals(self, date: date) -> List[Signal]:
|
|
18
|
+
"""
|
|
19
|
+
Generate trading signals for the given date
|
|
20
|
+
|
|
21
|
+
Override this method with your strategy logic
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
List of Signal objects to execute
|
|
25
|
+
"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def run(self, date: date) -> Dict[str, Any]:
|
|
29
|
+
"""
|
|
30
|
+
Run strategy for the given date
|
|
31
|
+
|
|
32
|
+
1. Generate signals
|
|
33
|
+
2. Submit to Aptis platform
|
|
34
|
+
3. Return results
|
|
35
|
+
"""
|
|
36
|
+
signals = self.generate_signals(date)
|
|
37
|
+
|
|
38
|
+
results = []
|
|
39
|
+
for signal in signals:
|
|
40
|
+
response = self.client.submit_signal(signal, self.name)
|
|
41
|
+
results.append(response)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"date": date.isoformat(),
|
|
45
|
+
"signals_generated": len(signals),
|
|
46
|
+
"results": results,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def get_features(self, symbols: List[str], date: date, features: List[str]) -> Dict[str, Dict[str, float]]:
|
|
50
|
+
"""Get Dagster-calculated features"""
|
|
51
|
+
return self.client.get_features(symbols, date, features)
|
|
52
|
+
|
|
53
|
+
def get_positions(self) -> List:
|
|
54
|
+
"""Get current positions for this strategy"""
|
|
55
|
+
return self.client.get_positions(self.account, self.name)
|
|
56
|
+
|
|
57
|
+
def get_pnl(self, start_date: date, end_date: date) -> Dict[str, Any]:
|
|
58
|
+
"""Get P&L for this strategy"""
|
|
59
|
+
return self.client.get_pnl(self.account, start_date, end_date, self.name)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aptis-strategy-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for building plugin trading strategies on the Aptis platform
|
|
5
|
+
Author: Richard Chung
|
|
6
|
+
Project-URL: Homepage, https://aptis.com
|
|
7
|
+
Project-URL: Documentation, https://aptis.com/docs
|
|
8
|
+
Classifier: License :: Other/Proprietary License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: requests>=2.31.0
|
|
13
|
+
Requires-Dist: pandas>=2.0.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
18
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# Aptis Strategy SDK
|
|
21
|
+
|
|
22
|
+
Python SDK for building trading strategies on the Aptis platform.
|
|
23
|
+
|
|
24
|
+
**Author:** Richard Chung
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install aptis-strategy-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Environment Variables
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
APTIS_API_KEY=your_api_key_here # Your API key
|
|
36
|
+
APTIS_ACCOUNT=Theme # Account to trade under (Theme or QSpark)
|
|
37
|
+
APTIS_API_URL=http://localhost:8082 # Backend URL
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Never hardcode these — always read from env vars.
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import os
|
|
46
|
+
from aptis_strategy_sdk import AptisClient, Strategy, Signal
|
|
47
|
+
from datetime import date
|
|
48
|
+
|
|
49
|
+
client = AptisClient(
|
|
50
|
+
api_key=os.getenv("APTIS_API_KEY", "test_key_123"),
|
|
51
|
+
base_url=os.getenv("APTIS_API_URL", "http://localhost:8082")
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
class MyStrategy(Strategy):
|
|
55
|
+
def generate_signals(self, run_date: date):
|
|
56
|
+
# your logic here
|
|
57
|
+
return [Signal(symbol="AAPL", asset_class="equity",
|
|
58
|
+
quantity=100, side="BUY", signal_type="ENTRY")]
|
|
59
|
+
|
|
60
|
+
strategy = MyStrategy(
|
|
61
|
+
name="My Strategy",
|
|
62
|
+
client=client,
|
|
63
|
+
account=os.getenv("APTIS_ACCOUNT", "Theme")
|
|
64
|
+
)
|
|
65
|
+
result = strategy.run(date.today())
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Accounts
|
|
69
|
+
|
|
70
|
+
The platform supports multiple accounts (trading scenarios). Each account has its own API key. Set `APTIS_ACCOUNT` to target the correct account:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Run against Theme account
|
|
74
|
+
APTIS_ACCOUNT=Theme APTIS_API_KEY=test_key_123 python my_strategy.py
|
|
75
|
+
|
|
76
|
+
# Run against QSpark account
|
|
77
|
+
APTIS_ACCOUNT=QSpark APTIS_API_KEY=qspark_key_123 python my_strategy.py
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Methods
|
|
81
|
+
|
|
82
|
+
### AptisClient
|
|
83
|
+
|
|
84
|
+
- `submit_signal(signal, strategy_name, account)` — submit a trading signal
|
|
85
|
+
- `get_config(strategy_name)` — get NAV, funds, portfolio_weight, enabled
|
|
86
|
+
- `get_quotes(symbols, asset_class)` — get market quotes
|
|
87
|
+
- `get_bars(symbol, asset_class, timeframe, start, end)` — get OHLCV bars
|
|
88
|
+
- `get_positions(account, strategy_name)` — get current positions
|
|
89
|
+
- `get_pnl(account, start_date, end_date, strategy_name)` — get P&L
|
|
90
|
+
|
|
91
|
+
### Strategy Base Class
|
|
92
|
+
|
|
93
|
+
- `generate_signals(date)` — override with your logic, return `List[Signal]`
|
|
94
|
+
- `run(date)` — calls `generate_signals`, submits all signals
|
|
95
|
+
- `get_features(symbols, date, features)` — fetch Dagster features
|
|
96
|
+
- `get_positions()` — get positions for this strategy
|
|
97
|
+
|
|
98
|
+
## Signal Fields
|
|
99
|
+
|
|
100
|
+
| Field | Required | Description |
|
|
101
|
+
|-------|----------|-------------|
|
|
102
|
+
| `symbol` | Yes | Ticker |
|
|
103
|
+
| `asset_class` | Yes | `equity`, `etf`, `crypto`, `forex`, `commodity` |
|
|
104
|
+
| `quantity` | Yes | Units |
|
|
105
|
+
| `side` | Yes | `BUY` or `SELL` |
|
|
106
|
+
| `signal_type` | Yes | `ENTRY`, `EXIT`, `ADJUST` |
|
|
107
|
+
| `order_parameters` | No | `order_type`, `limit_price`, etc. |
|
|
108
|
+
| `metadata` | No | Notes only, not used for execution |
|
|
109
|
+
|
|
110
|
+
## Full Documentation
|
|
111
|
+
|
|
112
|
+
See [docs/customer-integration/](../docs/customer-integration/) for complete guides.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
aptis_strategy_sdk/__init__.py
|
|
4
|
+
aptis_strategy_sdk/client.py
|
|
5
|
+
aptis_strategy_sdk/exceptions.py
|
|
6
|
+
aptis_strategy_sdk/models.py
|
|
7
|
+
aptis_strategy_sdk/strategy.py
|
|
8
|
+
aptis_strategy_sdk.egg-info/PKG-INFO
|
|
9
|
+
aptis_strategy_sdk.egg-info/SOURCES.txt
|
|
10
|
+
aptis_strategy_sdk.egg-info/dependency_links.txt
|
|
11
|
+
aptis_strategy_sdk.egg-info/requires.txt
|
|
12
|
+
aptis_strategy_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
aptis_strategy_sdk
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aptis-strategy-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for building plugin trading strategies on the Aptis platform"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"License :: Other/Proprietary License",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
]
|
|
15
|
+
authors = [
|
|
16
|
+
{name = "Richard Chung"}
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"requests>=2.31.0",
|
|
20
|
+
"pandas>=2.0.0",
|
|
21
|
+
"pydantic>=2.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
dev = ["pytest>=7.0.0", "black>=23.0.0", "mypy>=1.0.0"]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://aptis.com"
|
|
29
|
+
Documentation = "https://aptis.com/docs"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["."]
|
|
33
|
+
include = ["aptis_strategy_sdk*"]
|