blot-sdk 0.1.1__py3-none-any.whl
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.
- blot/__init__.py +28 -0
- blot/ai.py +161 -0
- blot/bot.py +152 -0
- blot/bots/__init__.py +16 -0
- blot/bots/base.py +44 -0
- blot/bots/copy_trade.py +80 -0
- blot/bots/dca.py +75 -0
- blot/bots/grid.py +101 -0
- blot/bots/sniper.py +80 -0
- blot/config.py +56 -0
- blot/strategies/__init__.py +15 -0
- blot_sdk-0.1.1.dist-info/METADATA +233 -0
- blot_sdk-0.1.1.dist-info/RECORD +18 -0
- blot_sdk-0.1.1.dist-info/WHEEL +5 -0
- blot_sdk-0.1.1.dist-info/licenses/LICENSE +22 -0
- blot_sdk-0.1.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +2 -0
- tests/test_bots.py +96 -0
blot/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BLOT SDK - Autonomous Trading Bots for Solana
|
|
3
|
+
|
|
4
|
+
Self-replicating, AI-powered trading infrastructure.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1.0"
|
|
8
|
+
__author__ = "BLOT Team"
|
|
9
|
+
|
|
10
|
+
from blot.bot import Bot
|
|
11
|
+
from blot.bots.sniper import SniperBot
|
|
12
|
+
from blot.bots.dca import DCABot
|
|
13
|
+
from blot.bots.copy_trade import CopyTradeBot
|
|
14
|
+
from blot.bots.grid import GridBot
|
|
15
|
+
from blot.ai import AI
|
|
16
|
+
from blot.config import Config
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"Bot",
|
|
20
|
+
"SniperBot",
|
|
21
|
+
"DCABot",
|
|
22
|
+
"CopyTradeBot",
|
|
23
|
+
"GridBot",
|
|
24
|
+
"AI",
|
|
25
|
+
"Config",
|
|
26
|
+
"__version__",
|
|
27
|
+
]
|
|
28
|
+
|
blot/ai.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""AI integration for BLOT SDK - Powered by OpenClaw."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional, Literal
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
from blot.config import Config
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Sentiment(str, Enum):
|
|
11
|
+
"""Market sentiment."""
|
|
12
|
+
BULLISH = "bullish"
|
|
13
|
+
BEARISH = "bearish"
|
|
14
|
+
NEUTRAL = "neutral"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Recommendation(str, Enum):
|
|
18
|
+
"""Trading recommendation."""
|
|
19
|
+
BUY = "buy"
|
|
20
|
+
SELL = "sell"
|
|
21
|
+
HOLD = "hold"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class TokenAnalysis:
|
|
26
|
+
"""Result of AI token analysis."""
|
|
27
|
+
|
|
28
|
+
token: str
|
|
29
|
+
sentiment: Sentiment
|
|
30
|
+
risk_score: int # 0-100
|
|
31
|
+
recommendation: Recommendation
|
|
32
|
+
confidence: float # 0-1
|
|
33
|
+
reasons: list[str]
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def is_promising(self) -> bool:
|
|
37
|
+
"""Check if token looks promising based on analysis."""
|
|
38
|
+
return (
|
|
39
|
+
self.sentiment == Sentiment.BULLISH
|
|
40
|
+
and self.risk_score < 50
|
|
41
|
+
and self.confidence > 0.7
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class MarketAnalysis:
|
|
47
|
+
"""Result of AI market analysis."""
|
|
48
|
+
|
|
49
|
+
token: str
|
|
50
|
+
signal: Literal["BUY", "SELL", "HOLD"]
|
|
51
|
+
strength: float # 0-1
|
|
52
|
+
support_level: Optional[float]
|
|
53
|
+
resistance_level: Optional[float]
|
|
54
|
+
trend: Literal["up", "down", "sideways"]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AI:
|
|
58
|
+
"""AI assistant for market analysis - Powered by OpenClaw.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
```python
|
|
62
|
+
from blot import Bot
|
|
63
|
+
|
|
64
|
+
bot = Bot(private_key="...")
|
|
65
|
+
|
|
66
|
+
# Analyze a token
|
|
67
|
+
analysis = await bot.ai.analyze_token("JUP")
|
|
68
|
+
print(analysis.sentiment) # bullish/bearish
|
|
69
|
+
print(analysis.risk_score) # 0-100
|
|
70
|
+
print(analysis.recommendation) # buy/sell/hold
|
|
71
|
+
|
|
72
|
+
# Check if token is promising
|
|
73
|
+
if analysis.is_promising:
|
|
74
|
+
print("Token looks good!")
|
|
75
|
+
```
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, config: Optional[Config] = None):
|
|
79
|
+
self.config = config or Config()
|
|
80
|
+
|
|
81
|
+
async def analyze_token(self, token: str) -> TokenAnalysis:
|
|
82
|
+
"""Analyze a token using AI.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
token: Token symbol or address.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
TokenAnalysis with AI insights.
|
|
89
|
+
"""
|
|
90
|
+
# TODO: Implement actual AI analysis via OpenClaw API
|
|
91
|
+
return TokenAnalysis(
|
|
92
|
+
token=token,
|
|
93
|
+
sentiment=Sentiment.NEUTRAL,
|
|
94
|
+
risk_score=50,
|
|
95
|
+
recommendation=Recommendation.HOLD,
|
|
96
|
+
confidence=0.5,
|
|
97
|
+
reasons=["Analysis pending - connect to BLOT infrastructure"],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def analyze_market(self, token: str) -> MarketAnalysis:
|
|
101
|
+
"""Analyze market conditions for a token.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
token: Token symbol or address.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
MarketAnalysis with trading signals.
|
|
108
|
+
"""
|
|
109
|
+
# TODO: Implement actual market analysis
|
|
110
|
+
return MarketAnalysis(
|
|
111
|
+
token=token,
|
|
112
|
+
signal="HOLD",
|
|
113
|
+
strength=0.5,
|
|
114
|
+
support_level=None,
|
|
115
|
+
resistance_level=None,
|
|
116
|
+
trend="sideways",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
async def check_honeypot(self, token_address: str) -> bool:
|
|
120
|
+
"""Check if a token is a honeypot.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
token_address: Token contract address.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
True if token appears to be a honeypot.
|
|
127
|
+
"""
|
|
128
|
+
# TODO: Implement honeypot detection
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
async def check_rug_risk(self, token_address: str) -> float:
|
|
132
|
+
"""Assess rug pull risk for a token.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
token_address: Token contract address.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Risk score from 0.0 (safe) to 1.0 (high risk).
|
|
139
|
+
"""
|
|
140
|
+
# TODO: Implement rug pull detection
|
|
141
|
+
return 0.5
|
|
142
|
+
|
|
143
|
+
async def analyze_wallet(self, wallet_address: str) -> dict:
|
|
144
|
+
"""Analyze a wallet's trading performance.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
wallet_address: Wallet address to analyze.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Dict with wallet statistics and performance metrics.
|
|
151
|
+
"""
|
|
152
|
+
# TODO: Implement wallet analysis
|
|
153
|
+
return {
|
|
154
|
+
"address": wallet_address,
|
|
155
|
+
"win_rate": 0.0,
|
|
156
|
+
"total_trades": 0,
|
|
157
|
+
"profit_loss": 0.0,
|
|
158
|
+
"avg_hold_time": "unknown",
|
|
159
|
+
"top_tokens": [],
|
|
160
|
+
}
|
|
161
|
+
|
blot/bot.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Main Bot class for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Union, TYPE_CHECKING
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from blot.config import Config
|
|
7
|
+
from blot.ai import AI
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from blot.bots.base import BaseBot
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class DeploymentResult:
|
|
15
|
+
"""Result of bot deployment."""
|
|
16
|
+
|
|
17
|
+
deployment_id: str
|
|
18
|
+
status: str
|
|
19
|
+
clones: int
|
|
20
|
+
regions: list
|
|
21
|
+
expires_at: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Bot:
|
|
25
|
+
"""Main BLOT Bot client.
|
|
26
|
+
|
|
27
|
+
This is the main entry point for interacting with BLOT infrastructure.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
```python
|
|
31
|
+
from blot import Bot, SniperBot
|
|
32
|
+
|
|
33
|
+
bot = Bot(private_key="...", rpc_url="...")
|
|
34
|
+
|
|
35
|
+
sniper = SniperBot(target="new", buy_amount=0.5)
|
|
36
|
+
result = bot.deploy(sniper, duration_days=30)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
config: Bot configuration.
|
|
41
|
+
ai: AI assistant for market analysis.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
private_key: Optional[str] = None,
|
|
47
|
+
rpc_url: str = "https://api.mainnet-beta.solana.com",
|
|
48
|
+
config: Optional[Config] = None,
|
|
49
|
+
):
|
|
50
|
+
"""Initialize BLOT Bot.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
private_key: Your Solana wallet private key (base58 encoded).
|
|
54
|
+
rpc_url: Solana RPC endpoint URL.
|
|
55
|
+
config: Optional Config object for advanced settings.
|
|
56
|
+
"""
|
|
57
|
+
self.config = config or Config(rpc_url=rpc_url)
|
|
58
|
+
self._private_key = private_key
|
|
59
|
+
self._ai: Optional[AI] = None
|
|
60
|
+
self._connected = False
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def ai(self) -> AI:
|
|
64
|
+
"""Get AI assistant for market analysis."""
|
|
65
|
+
if self._ai is None:
|
|
66
|
+
self._ai = AI(config=self.config)
|
|
67
|
+
return self._ai
|
|
68
|
+
|
|
69
|
+
async def connect(self) -> bool:
|
|
70
|
+
"""Connect to BLOT infrastructure.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if connection successful.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ConnectionError: If unable to connect.
|
|
77
|
+
"""
|
|
78
|
+
# TODO: Implement actual connection logic
|
|
79
|
+
self._connected = True
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
async def deploy(
|
|
83
|
+
self,
|
|
84
|
+
bot: "BaseBot",
|
|
85
|
+
duration_days: int = 30,
|
|
86
|
+
tier: str = "standard",
|
|
87
|
+
) -> DeploymentResult:
|
|
88
|
+
"""Deploy a trading bot to BLOT infrastructure.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
bot: The bot instance to deploy (SniperBot, DCABot, etc.)
|
|
92
|
+
duration_days: How long to run the bot.
|
|
93
|
+
tier: Service tier ('starter', 'standard', 'premium', 'enterprise').
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
DeploymentResult with deployment details.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If bot configuration is invalid.
|
|
100
|
+
InsufficientFundsError: If not enough $BLOT tokens.
|
|
101
|
+
"""
|
|
102
|
+
if not self._connected:
|
|
103
|
+
await self.connect()
|
|
104
|
+
|
|
105
|
+
# TODO: Implement actual deployment logic
|
|
106
|
+
return DeploymentResult(
|
|
107
|
+
deployment_id="deploy_" + "x" * 16,
|
|
108
|
+
status="running",
|
|
109
|
+
clones=self.config.clone_count,
|
|
110
|
+
regions=self.config.regions,
|
|
111
|
+
expires_at="2026-03-02T00:00:00Z",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
async def stop(self, deployment_id: str) -> bool:
|
|
115
|
+
"""Stop a running bot deployment.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
deployment_id: The deployment ID to stop.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
True if successfully stopped.
|
|
122
|
+
"""
|
|
123
|
+
# TODO: Implement actual stop logic
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
async def status(self, deployment_id: str) -> dict:
|
|
127
|
+
"""Get status of a bot deployment.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
deployment_id: The deployment ID to check.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dict with deployment status and statistics.
|
|
134
|
+
"""
|
|
135
|
+
# TODO: Implement actual status logic
|
|
136
|
+
return {
|
|
137
|
+
"deployment_id": deployment_id,
|
|
138
|
+
"status": "running",
|
|
139
|
+
"clones_active": 3,
|
|
140
|
+
"trades_executed": 0,
|
|
141
|
+
"profit_loss": 0.0,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async def list_deployments(self) -> list:
|
|
145
|
+
"""List all active bot deployments.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
List of deployment summaries.
|
|
149
|
+
"""
|
|
150
|
+
# TODO: Implement actual list logic
|
|
151
|
+
return []
|
|
152
|
+
|
blot/bots/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""BLOT Trading Bots."""
|
|
2
|
+
|
|
3
|
+
from blot.bots.base import BaseBot
|
|
4
|
+
from blot.bots.sniper import SniperBot
|
|
5
|
+
from blot.bots.dca import DCABot
|
|
6
|
+
from blot.bots.copy_trade import CopyTradeBot
|
|
7
|
+
from blot.bots.grid import GridBot
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"BaseBot",
|
|
11
|
+
"SniperBot",
|
|
12
|
+
"DCABot",
|
|
13
|
+
"CopyTradeBot",
|
|
14
|
+
"GridBot",
|
|
15
|
+
]
|
|
16
|
+
|
blot/bots/base.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Base bot class for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Optional, List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class BaseBot(ABC):
|
|
10
|
+
"""Base class for all BLOT trading bots.
|
|
11
|
+
|
|
12
|
+
All bot types inherit from this class.
|
|
13
|
+
|
|
14
|
+
Attributes:
|
|
15
|
+
clone_count: Number of bot clones to maintain.
|
|
16
|
+
regions: Geographic regions for clone distribution.
|
|
17
|
+
failover_timeout: Milliseconds before electing new leader.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Cloning settings
|
|
21
|
+
clone_count: int = 3
|
|
22
|
+
regions: List[str] = field(default_factory=lambda: ["us", "eu", "asia"])
|
|
23
|
+
failover_timeout: int = 100 # ms
|
|
24
|
+
sync_interval: int = 50 # ms
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def validate(self) -> bool:
|
|
28
|
+
"""Validate bot configuration.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
True if configuration is valid.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If configuration is invalid.
|
|
35
|
+
"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def to_dict(self) -> dict:
|
|
39
|
+
"""Convert bot configuration to dictionary."""
|
|
40
|
+
return {
|
|
41
|
+
k: v for k, v in self.__dict__.items()
|
|
42
|
+
if not k.startswith('_')
|
|
43
|
+
}
|
|
44
|
+
|
blot/bots/copy_trade.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Copy Trade Bot for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional, List, Literal
|
|
5
|
+
|
|
6
|
+
from blot.bots.base import BaseBot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class CopyTradeBot(BaseBot):
|
|
11
|
+
"""Copy Trade Bot - Mirror trades of successful wallets.
|
|
12
|
+
|
|
13
|
+
The Copy Trade Bot monitors specified wallet addresses and
|
|
14
|
+
automatically replicates their trades in real-time.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
```python
|
|
18
|
+
from blot import CopyTradeBot
|
|
19
|
+
|
|
20
|
+
bot = CopyTradeBot(
|
|
21
|
+
wallets=["wallet1...", "wallet2..."],
|
|
22
|
+
size_mode="percentage",
|
|
23
|
+
size_value=10, # 10% of their trade size
|
|
24
|
+
copy_sells=True,
|
|
25
|
+
ai_filter=True,
|
|
26
|
+
)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
wallets: List of wallet addresses to copy.
|
|
31
|
+
size_mode: How to size positions ('fixed' or 'percentage').
|
|
32
|
+
size_value: Position size value (SOL if fixed, percent if percentage).
|
|
33
|
+
max_position: Maximum position size in SOL.
|
|
34
|
+
copy_sells: Whether to also copy sell orders.
|
|
35
|
+
min_trade_size: Minimum trade size to copy in SOL.
|
|
36
|
+
blacklist_tokens: Token symbols to never trade.
|
|
37
|
+
only_verified: Only trade verified tokens.
|
|
38
|
+
ai_filter: Use AI to filter trades.
|
|
39
|
+
ai_confidence_threshold: Minimum AI confidence to copy trade.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Wallets to copy
|
|
43
|
+
wallets: List[str] = field(default_factory=list)
|
|
44
|
+
|
|
45
|
+
# Position sizing
|
|
46
|
+
size_mode: Literal["fixed", "percentage"] = "percentage"
|
|
47
|
+
size_value: float = 10.0 # SOL if fixed, percent if percentage
|
|
48
|
+
max_position: float = 1.0 # SOL
|
|
49
|
+
|
|
50
|
+
# Trade filters
|
|
51
|
+
copy_sells: bool = True
|
|
52
|
+
min_trade_size: float = 0.1 # SOL
|
|
53
|
+
|
|
54
|
+
# Token filters
|
|
55
|
+
blacklist_tokens: List[str] = field(default_factory=list)
|
|
56
|
+
whitelist_tokens: Optional[List[str]] = None
|
|
57
|
+
only_verified: bool = False
|
|
58
|
+
|
|
59
|
+
# AI settings
|
|
60
|
+
ai_filter: bool = False
|
|
61
|
+
ai_confidence_threshold: float = 0.7
|
|
62
|
+
|
|
63
|
+
# Execution
|
|
64
|
+
delay: int = 0 # seconds delay before copying
|
|
65
|
+
slippage: float = 1.0 # percent
|
|
66
|
+
|
|
67
|
+
def validate(self) -> bool:
|
|
68
|
+
"""Validate bot configuration."""
|
|
69
|
+
if not self.wallets:
|
|
70
|
+
raise ValueError("At least one wallet address is required")
|
|
71
|
+
if self.size_mode not in ["fixed", "percentage"]:
|
|
72
|
+
raise ValueError("size_mode must be 'fixed' or 'percentage'")
|
|
73
|
+
if self.size_value <= 0:
|
|
74
|
+
raise ValueError("size_value must be positive")
|
|
75
|
+
if self.max_position <= 0:
|
|
76
|
+
raise ValueError("max_position must be positive")
|
|
77
|
+
if self.slippage < 0 or self.slippage > 100:
|
|
78
|
+
raise ValueError("slippage must be between 0 and 100")
|
|
79
|
+
return True
|
|
80
|
+
|
blot/bots/dca.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""DCA Bot for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional, List
|
|
5
|
+
|
|
6
|
+
from blot.bots.base import BaseBot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class DCABot(BaseBot):
|
|
11
|
+
"""DCA Bot - Dollar-cost averaging into any Solana token.
|
|
12
|
+
|
|
13
|
+
The DCA Bot automatically purchases tokens on a schedule,
|
|
14
|
+
reducing the impact of volatility by spreading purchases over time.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
```python
|
|
18
|
+
from blot import DCABot
|
|
19
|
+
|
|
20
|
+
bot = DCABot(
|
|
21
|
+
token="SOL",
|
|
22
|
+
amount=100, # USDC per purchase
|
|
23
|
+
frequency="daily",
|
|
24
|
+
time="09:00",
|
|
25
|
+
buy_only_below=150, # Only buy if SOL < $150
|
|
26
|
+
)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
token: Token symbol or address to accumulate.
|
|
31
|
+
amount: Amount in quote currency per purchase.
|
|
32
|
+
frequency: Purchase frequency ('hourly', 'daily', 'weekly', or cron expression).
|
|
33
|
+
time: Time of purchase in UTC (HH:MM format).
|
|
34
|
+
buy_only_below: Only buy if price is below this value.
|
|
35
|
+
time_variance: Random offset in minutes to avoid detection.
|
|
36
|
+
max_single_order: Maximum single order size (will split if exceeded).
|
|
37
|
+
order_delay: Delay between split orders in seconds.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Target settings
|
|
41
|
+
token: str = "SOL"
|
|
42
|
+
quote_token: str = "USDC"
|
|
43
|
+
|
|
44
|
+
# Purchase settings
|
|
45
|
+
amount: float = 100.0 # Quote currency per purchase
|
|
46
|
+
frequency: str = "daily" # hourly, daily, weekly, or cron
|
|
47
|
+
time: str = "09:00" # UTC
|
|
48
|
+
|
|
49
|
+
# Conditions
|
|
50
|
+
buy_only_below: Optional[float] = None
|
|
51
|
+
buy_only_above: Optional[float] = None
|
|
52
|
+
|
|
53
|
+
# Anti-detection
|
|
54
|
+
time_variance: int = 0 # minutes
|
|
55
|
+
|
|
56
|
+
# Order splitting
|
|
57
|
+
max_single_order: Optional[float] = None
|
|
58
|
+
order_delay: int = 30 # seconds
|
|
59
|
+
|
|
60
|
+
# Routing
|
|
61
|
+
use_jupiter: bool = True
|
|
62
|
+
slippage: float = 0.5 # percent
|
|
63
|
+
|
|
64
|
+
def validate(self) -> bool:
|
|
65
|
+
"""Validate bot configuration."""
|
|
66
|
+
if self.amount <= 0:
|
|
67
|
+
raise ValueError("amount must be positive")
|
|
68
|
+
if self.frequency not in ["hourly", "daily", "weekly"] and not self.frequency.startswith("cron:"):
|
|
69
|
+
raise ValueError("frequency must be 'hourly', 'daily', 'weekly', or a cron expression")
|
|
70
|
+
if self.slippage < 0 or self.slippage > 100:
|
|
71
|
+
raise ValueError("slippage must be between 0 and 100")
|
|
72
|
+
if self.time_variance < 0:
|
|
73
|
+
raise ValueError("time_variance must be non-negative")
|
|
74
|
+
return True
|
|
75
|
+
|
blot/bots/grid.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Grid Bot for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional, Literal
|
|
5
|
+
|
|
6
|
+
from blot.bots.base import BaseBot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class GridBot(BaseBot):
|
|
11
|
+
"""Grid Bot - Profit from sideways markets.
|
|
12
|
+
|
|
13
|
+
The Grid Bot places a series of buy and sell orders at predetermined
|
|
14
|
+
price intervals, creating a "grid" of orders that profits from
|
|
15
|
+
price fluctuations.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
```python
|
|
19
|
+
from blot import GridBot
|
|
20
|
+
|
|
21
|
+
bot = GridBot(
|
|
22
|
+
base_token="SOL",
|
|
23
|
+
quote_token="USDC",
|
|
24
|
+
lower_price=120,
|
|
25
|
+
upper_price=180,
|
|
26
|
+
grid_count=20,
|
|
27
|
+
total_investment=100, # USDC
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
base_token: Token to trade (e.g., "SOL").
|
|
33
|
+
quote_token: Quote currency (e.g., "USDC").
|
|
34
|
+
lower_price: Bottom of the grid range.
|
|
35
|
+
upper_price: Top of the grid range.
|
|
36
|
+
grid_count: Number of grid lines.
|
|
37
|
+
total_investment: Total investment in quote currency.
|
|
38
|
+
mode: Grid type ('arithmetic' or 'geometric').
|
|
39
|
+
ai_assisted: Let AI adjust grid range automatically.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Trading pair
|
|
43
|
+
base_token: str = "SOL"
|
|
44
|
+
quote_token: str = "USDC"
|
|
45
|
+
|
|
46
|
+
# Grid settings
|
|
47
|
+
lower_price: float = 100.0
|
|
48
|
+
upper_price: float = 200.0
|
|
49
|
+
grid_count: int = 20
|
|
50
|
+
|
|
51
|
+
# Investment
|
|
52
|
+
total_investment: float = 100.0 # Quote currency
|
|
53
|
+
|
|
54
|
+
# Grid type
|
|
55
|
+
mode: Literal["arithmetic", "geometric"] = "arithmetic"
|
|
56
|
+
|
|
57
|
+
# AI assistance
|
|
58
|
+
ai_assisted: bool = False
|
|
59
|
+
|
|
60
|
+
# Execution
|
|
61
|
+
slippage: float = 0.5 # percent
|
|
62
|
+
|
|
63
|
+
def validate(self) -> bool:
|
|
64
|
+
"""Validate bot configuration."""
|
|
65
|
+
if self.lower_price >= self.upper_price:
|
|
66
|
+
raise ValueError("lower_price must be less than upper_price")
|
|
67
|
+
if self.grid_count < 2:
|
|
68
|
+
raise ValueError("grid_count must be at least 2")
|
|
69
|
+
if self.total_investment <= 0:
|
|
70
|
+
raise ValueError("total_investment must be positive")
|
|
71
|
+
if self.mode not in ["arithmetic", "geometric"]:
|
|
72
|
+
raise ValueError("mode must be 'arithmetic' or 'geometric'")
|
|
73
|
+
if self.slippage < 0 or self.slippage > 100:
|
|
74
|
+
raise ValueError("slippage must be between 0 and 100")
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def grid_spacing(self) -> float:
|
|
79
|
+
"""Calculate grid spacing based on mode."""
|
|
80
|
+
if self.mode == "arithmetic":
|
|
81
|
+
return (self.upper_price - self.lower_price) / (self.grid_count - 1)
|
|
82
|
+
else:
|
|
83
|
+
# Geometric: equal percentage between levels
|
|
84
|
+
return (self.upper_price / self.lower_price) ** (1 / (self.grid_count - 1))
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def grid_levels(self) -> list[float]:
|
|
88
|
+
"""Generate all grid price levels."""
|
|
89
|
+
levels = []
|
|
90
|
+
if self.mode == "arithmetic":
|
|
91
|
+
spacing = self.grid_spacing
|
|
92
|
+
for i in range(self.grid_count):
|
|
93
|
+
levels.append(self.lower_price + i * spacing)
|
|
94
|
+
else:
|
|
95
|
+
ratio = self.grid_spacing
|
|
96
|
+
price = self.lower_price
|
|
97
|
+
for _ in range(self.grid_count):
|
|
98
|
+
levels.append(price)
|
|
99
|
+
price *= ratio
|
|
100
|
+
return levels
|
|
101
|
+
|
blot/bots/sniper.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Sniper Bot for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional, List, Union
|
|
5
|
+
|
|
6
|
+
from blot.bots.base import BaseBot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class SniperBot(BaseBot):
|
|
11
|
+
"""Sniper Bot - Catch new token launches instantly.
|
|
12
|
+
|
|
13
|
+
The Sniper Bot monitors the Solana mempool for new token listings
|
|
14
|
+
and automatically purchases tokens the moment liquidity is added.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
```python
|
|
18
|
+
from blot import SniperBot
|
|
19
|
+
|
|
20
|
+
bot = SniperBot(
|
|
21
|
+
target="new",
|
|
22
|
+
buy_amount=0.5,
|
|
23
|
+
min_liquidity=10,
|
|
24
|
+
take_profit=[2.0, 5.0, 10.0],
|
|
25
|
+
stop_loss=0.5,
|
|
26
|
+
check_honeypot=True,
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
target: Token address to snipe, or "new" for all new tokens.
|
|
32
|
+
buy_amount: Amount in SOL to buy with.
|
|
33
|
+
min_liquidity: Minimum liquidity in SOL required.
|
|
34
|
+
take_profit: List of profit multipliers to sell at.
|
|
35
|
+
stop_loss: Stop loss multiplier (e.g., 0.5 = -50%).
|
|
36
|
+
check_honeypot: Whether to check for honeypot contracts.
|
|
37
|
+
check_rug: Whether to check for rug pull indicators.
|
|
38
|
+
max_dev_holding: Maximum developer holding percentage.
|
|
39
|
+
dex: DEX to monitor ('raydium', 'orca', 'jupiter', 'meteora', 'pump.fun').
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Target settings
|
|
43
|
+
target: Union[str, List[str]] = "new"
|
|
44
|
+
dex: str = "raydium"
|
|
45
|
+
|
|
46
|
+
# Buy settings
|
|
47
|
+
buy_amount: float = 0.5 # SOL
|
|
48
|
+
min_liquidity: float = 10.0 # SOL
|
|
49
|
+
slippage: float = 1.0 # percent
|
|
50
|
+
|
|
51
|
+
# Auto-sell settings
|
|
52
|
+
take_profit: List[float] = field(default_factory=lambda: [2.0, 5.0, 10.0])
|
|
53
|
+
stop_loss: float = 0.5
|
|
54
|
+
|
|
55
|
+
# Safety checks
|
|
56
|
+
check_honeypot: bool = True
|
|
57
|
+
check_rug: bool = True
|
|
58
|
+
max_dev_holding: Optional[float] = 10.0 # percent
|
|
59
|
+
min_holders: Optional[int] = None
|
|
60
|
+
|
|
61
|
+
# AI settings
|
|
62
|
+
ai_filter: bool = False
|
|
63
|
+
ai_confidence_threshold: float = 0.7
|
|
64
|
+
|
|
65
|
+
def validate(self) -> bool:
|
|
66
|
+
"""Validate bot configuration."""
|
|
67
|
+
if self.buy_amount <= 0:
|
|
68
|
+
raise ValueError("buy_amount must be positive")
|
|
69
|
+
if self.min_liquidity < 0:
|
|
70
|
+
raise ValueError("min_liquidity must be non-negative")
|
|
71
|
+
if self.slippage < 0 or self.slippage > 100:
|
|
72
|
+
raise ValueError("slippage must be between 0 and 100")
|
|
73
|
+
if self.stop_loss < 0 or self.stop_loss > 1:
|
|
74
|
+
raise ValueError("stop_loss must be between 0 and 1")
|
|
75
|
+
if not self.take_profit:
|
|
76
|
+
raise ValueError("take_profit must have at least one level")
|
|
77
|
+
if self.dex not in ["raydium", "orca", "jupiter", "meteora", "pump.fun"]:
|
|
78
|
+
raise ValueError(f"Invalid DEX: {self.dex}")
|
|
79
|
+
return True
|
|
80
|
+
|
blot/config.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Configuration management for BLOT SDK."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional, List
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Config:
|
|
10
|
+
"""BLOT SDK Configuration.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
api_key: Your BLOT API key for accessing the infrastructure.
|
|
14
|
+
rpc_url: Solana RPC endpoint URL.
|
|
15
|
+
network: Network to use ('mainnet-beta', 'devnet', 'testnet').
|
|
16
|
+
clone_count: Number of bot clones to maintain.
|
|
17
|
+
regions: Geographic regions for clone distribution.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
api_key: Optional[str] = field(default_factory=lambda: os.getenv("BLOT_API_KEY"))
|
|
21
|
+
rpc_url: str = "https://api.mainnet-beta.solana.com"
|
|
22
|
+
network: str = "mainnet-beta"
|
|
23
|
+
clone_count: int = 3
|
|
24
|
+
regions: List[str] = field(default_factory=lambda: ["us", "eu", "asia"])
|
|
25
|
+
failover_timeout: int = 100 # ms
|
|
26
|
+
sync_interval: int = 50 # ms
|
|
27
|
+
|
|
28
|
+
# Security limits
|
|
29
|
+
max_trade_size: Optional[float] = None # SOL
|
|
30
|
+
max_daily_volume: Optional[float] = None # SOL
|
|
31
|
+
max_slippage: float = 5.0 # percent
|
|
32
|
+
|
|
33
|
+
def __post_init__(self):
|
|
34
|
+
if self.network not in ["mainnet-beta", "devnet", "testnet"]:
|
|
35
|
+
raise ValueError(f"Invalid network: {self.network}")
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_env(cls) -> "Config":
|
|
39
|
+
"""Create config from environment variables."""
|
|
40
|
+
return cls(
|
|
41
|
+
api_key=os.getenv("BLOT_API_KEY"),
|
|
42
|
+
rpc_url=os.getenv("BLOT_RPC_URL", "https://api.mainnet-beta.solana.com"),
|
|
43
|
+
network=os.getenv("BLOT_NETWORK", "mainnet-beta"),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_file(cls, path: str) -> "Config":
|
|
48
|
+
"""Load config from YAML file."""
|
|
49
|
+
try:
|
|
50
|
+
import yaml
|
|
51
|
+
with open(path, 'r') as f:
|
|
52
|
+
data = yaml.safe_load(f)
|
|
53
|
+
return cls(**data)
|
|
54
|
+
except ImportError:
|
|
55
|
+
raise ImportError("PyYAML required for config file support: pip install pyyaml")
|
|
56
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""BLOT Trading Strategies - Aliases for bots."""
|
|
2
|
+
|
|
3
|
+
# Re-export bots as strategies for backwards compatibility
|
|
4
|
+
from blot.bots.sniper import SniperBot as SniperStrategy
|
|
5
|
+
from blot.bots.dca import DCABot as DCAStrategy
|
|
6
|
+
from blot.bots.copy_trade import CopyTradeBot as CopyTradeStrategy
|
|
7
|
+
from blot.bots.grid import GridBot as GridStrategy
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"SniperStrategy",
|
|
11
|
+
"DCAStrategy",
|
|
12
|
+
"CopyTradeStrategy",
|
|
13
|
+
"GridStrategy",
|
|
14
|
+
]
|
|
15
|
+
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blot-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Autonomous trading bots SDK for Solana - Self-replicating, AI-powered trading infrastructure
|
|
5
|
+
Author-email: BLOT Team <dev@blot.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://blot.com
|
|
8
|
+
Project-URL: Documentation, https://blot.com/docs
|
|
9
|
+
Project-URL: PyPI, https://pypi.org/project/blot-sdk
|
|
10
|
+
Keywords: solana,trading,bot,defi,cryptocurrency,autonomous,ai,openclaw
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: solana>=0.30.0
|
|
26
|
+
Requires-Dist: solders>=0.18.0
|
|
27
|
+
Requires-Dist: httpx>=0.24.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Requires-Dist: websockets>=11.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# BLOT SDK
|
|
38
|
+
|
|
39
|
+
**Autonomous trading bots for Solana** — Self-replicating, AI-powered trading infrastructure.
|
|
40
|
+
|
|
41
|
+
[](https://badge.fury.io/py/blot-sdk)
|
|
42
|
+
[](https://www.python.org/downloads/)
|
|
43
|
+
[](https://opensource.org/licenses/MIT)
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- **AI-Powered** — Integrated with OpenClaw for intelligent decision making
|
|
48
|
+
- **24/7 Autonomous** — Bots run continuously without human intervention
|
|
49
|
+
- **Self-Cloning** — Automatic replication across distributed nodes for maximum resilience
|
|
50
|
+
- **Zero-Logging** — Complete privacy, no transaction data stored
|
|
51
|
+
- **High Speed** — Sub-50ms execution on Solana
|
|
52
|
+
- **Easy to Use** — Simple Python API for complex trading strategies
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install blot-sdk
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from blot_sdk import Bot, SniperBot, DCABot
|
|
64
|
+
|
|
65
|
+
# Initialize with your wallet
|
|
66
|
+
bot = Bot(
|
|
67
|
+
private_key="your_private_key",
|
|
68
|
+
rpc_url="https://api.mainnet-beta.solana.com"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Create a sniper bot
|
|
72
|
+
sniper = SniperBot(
|
|
73
|
+
target="new", # Snipe all new tokens
|
|
74
|
+
buy_amount=0.5, # Buy with 0.5 SOL
|
|
75
|
+
take_profit=[2.0, 5.0], # Sell at 2x and 5x
|
|
76
|
+
stop_loss=0.5, # Stop loss at -50%
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Deploy to BLOT infrastructure
|
|
80
|
+
bot.deploy(sniper, duration_days=30)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Available Bots
|
|
84
|
+
|
|
85
|
+
### Sniper Bot
|
|
86
|
+
Instantly purchase tokens the moment liquidity is added.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from blot_sdk import SniperBot
|
|
90
|
+
|
|
91
|
+
bot = SniperBot(
|
|
92
|
+
target="new",
|
|
93
|
+
buy_amount=0.5,
|
|
94
|
+
min_liquidity=10,
|
|
95
|
+
take_profit=[2.0, 5.0, 10.0],
|
|
96
|
+
stop_loss=0.5,
|
|
97
|
+
check_honeypot=True,
|
|
98
|
+
check_rug=True,
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### DCA Bot
|
|
103
|
+
Dollar-cost averaging into any Solana token.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from blot_sdk import DCABot
|
|
107
|
+
|
|
108
|
+
bot = DCABot(
|
|
109
|
+
token="SOL",
|
|
110
|
+
amount=100, # USDC per purchase
|
|
111
|
+
frequency="daily",
|
|
112
|
+
time="09:00",
|
|
113
|
+
buy_only_below=150, # Only buy if SOL < $150
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Copy Trade Bot
|
|
118
|
+
Mirror the trades of successful wallets.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from blot_sdk import CopyTradeBot
|
|
122
|
+
|
|
123
|
+
bot = CopyTradeBot(
|
|
124
|
+
wallets=["wallet1...", "wallet2..."],
|
|
125
|
+
size_mode="percentage",
|
|
126
|
+
size_value=10, # 10% of their trade size
|
|
127
|
+
copy_sells=True,
|
|
128
|
+
)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Grid Bot
|
|
132
|
+
Profit from sideways markets with automated grid trading.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from blot_sdk import GridBot
|
|
136
|
+
|
|
137
|
+
bot = GridBot(
|
|
138
|
+
base_token="SOL",
|
|
139
|
+
quote_token="USDC",
|
|
140
|
+
lower_price=120,
|
|
141
|
+
upper_price=180,
|
|
142
|
+
grid_count=20,
|
|
143
|
+
total_investment=100,
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Autonomy & Self-Preservation
|
|
148
|
+
|
|
149
|
+
BLOT bots operate 24/7 without downtime, automatically clone themselves across distributed nodes, and execute trades with zero human intervention:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from blot_sdk import Bot
|
|
153
|
+
|
|
154
|
+
bot = Bot(private_key="...")
|
|
155
|
+
|
|
156
|
+
# Enable 24/7 autonomous operation
|
|
157
|
+
bot.enable_autonomy(
|
|
158
|
+
mode="persistent",
|
|
159
|
+
auto_restart=True,
|
|
160
|
+
health_checks=True
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Enable self-cloning on threat detection
|
|
164
|
+
bot.enable_self_cloning(
|
|
165
|
+
trigger="on_threat",
|
|
166
|
+
target_nodes=3,
|
|
167
|
+
regions=["us", "eu", "asia"]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Zero-logging mode for privacy
|
|
171
|
+
bot.set_logging(enabled=False)
|
|
172
|
+
|
|
173
|
+
# Auto-migrate if node goes down
|
|
174
|
+
bot.on_node_failure(action="migrate", priority="immediate")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Self-Cloning
|
|
178
|
+
|
|
179
|
+
BLOT bots automatically clone themselves across distributed nodes:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from blot_sdk import SniperBot
|
|
183
|
+
|
|
184
|
+
bot = SniperBot(
|
|
185
|
+
# ... config ...
|
|
186
|
+
clone_count=5, # Minimum active clones
|
|
187
|
+
regions=["us", "eu", "asia"], # Geographic distribution
|
|
188
|
+
failover_timeout=100, # ms before electing new leader
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## AI Integration
|
|
193
|
+
|
|
194
|
+
Leverage OpenClaw AI for intelligent trading decisions:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from blot_sdk import Bot, AI
|
|
198
|
+
|
|
199
|
+
bot = Bot(private_key="...")
|
|
200
|
+
|
|
201
|
+
# AI-powered market analysis
|
|
202
|
+
analysis = await bot.ai.analyze_token("JUP")
|
|
203
|
+
print(analysis.sentiment) # bullish/bearish
|
|
204
|
+
print(analysis.risk_score) # 0-100
|
|
205
|
+
print(analysis.recommendation) # buy/sell/hold
|
|
206
|
+
|
|
207
|
+
# AI-filtered copy trading
|
|
208
|
+
copy_bot = CopyTradeBot(
|
|
209
|
+
wallets=["..."],
|
|
210
|
+
ai_filter=True,
|
|
211
|
+
ai_confidence_threshold=0.7,
|
|
212
|
+
)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Documentation
|
|
216
|
+
|
|
217
|
+
Full documentation available at [blot.com/docs](/docs)
|
|
218
|
+
|
|
219
|
+
## Requirements
|
|
220
|
+
|
|
221
|
+
- Python 3.9+
|
|
222
|
+
- Solana wallet with SOL for transaction fees
|
|
223
|
+
- $BLOT tokens for runtime access
|
|
224
|
+
|
|
225
|
+
## Links
|
|
226
|
+
|
|
227
|
+
- PyPI: [pypi.org/project/blot-sdk](https://pypi.org/project/blot-sdk)
|
|
228
|
+
- Documentation: [/docs](/docs)
|
|
229
|
+
- Twitter: [@blot_xyz](https://x.com/blot_xyz)
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
blot/__init__.py,sha256=P5nEJfeKUSzZpCHiyThuJT_U8un7tPi_grI6Zby8Bvc,553
|
|
2
|
+
blot/ai.py,sha256=QP8ERD8V2fjlASwvXVtKOI1EXUuYkZrjsUD8QJgtAOM,4483
|
|
3
|
+
blot/bot.py,sha256=qZ-L4LP4lCjDGHX7hjYcIC61ZmgER-0-Oy80rSmunc0,4361
|
|
4
|
+
blot/config.py,sha256=yPqvfqUnSprjG4EvcQocjKP34YnZ5MaYyj2_dFPFLQ8,1986
|
|
5
|
+
blot/bots/__init__.py,sha256=IYpYcYyqj5_3WXJ9F5oRmsqwBIGFy6BIO8nR_DmdxVQ,327
|
|
6
|
+
blot/bots/base.py,sha256=MFhh4dZapQK1HsxQ9aM4p1A7auQn1jjIZO1i0_9zFzc,1210
|
|
7
|
+
blot/bots/copy_trade.py,sha256=WcWW0sYb-FDQmcDdVvvS4kBjsigWm6M9w9ejHZZQxLI,2788
|
|
8
|
+
blot/bots/dca.py,sha256=2_MNU11x3MORZI3AfrzW0QZV8RjqObWJAt1GvkZeook,2556
|
|
9
|
+
blot/bots/grid.py,sha256=MzREZ2L97cHHr1gxkWMicyCbVvp9bp0VjcsWR7XbF84,3311
|
|
10
|
+
blot/bots/sniper.py,sha256=iYwcR6Xhm36bS3RZu5GB_ClF9lsQF0yogUOSuaL7qkY,2791
|
|
11
|
+
blot/strategies/__init__.py,sha256=8dzEwllwnRPJoJJ1jAC6ux43N1vZbdxyitJOuNP9g0A,450
|
|
12
|
+
blot_sdk-0.1.1.dist-info/licenses/LICENSE,sha256=LB8FNZpf_DHhu0mISyI5WzZF9UmqIeCfGDdd1SxtWvk,1084
|
|
13
|
+
tests/__init__.py,sha256=2-jkIqGMWE39TwbZJjx16iLIzaKmJ-dwbXolFXeQpKo,25
|
|
14
|
+
tests/test_bots.py,sha256=mjHxATuFe9ifttTrx4MivMkW1P1oUBvTy1OKFiga7O8,3071
|
|
15
|
+
blot_sdk-0.1.1.dist-info/METADATA,sha256=A77W_3K3_8tvwReadk9mFmO_APIzfkUijHIcM3Ow61M,6215
|
|
16
|
+
blot_sdk-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
17
|
+
blot_sdk-0.1.1.dist-info/top_level.txt,sha256=jziFZiQA_msjykcSPMCSRbMVRsu7oyxwpArs0f3JYuw,11
|
|
18
|
+
blot_sdk-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BLOT
|
|
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.
|
|
22
|
+
|
tests/__init__.py
ADDED
tests/test_bots.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Tests for BLOT bots."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from blot import SniperBot, DCABot, CopyTradeBot, GridBot
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestSniperBot:
|
|
8
|
+
"""Tests for SniperBot."""
|
|
9
|
+
|
|
10
|
+
def test_default_config(self):
|
|
11
|
+
"""Test default configuration."""
|
|
12
|
+
bot = SniperBot()
|
|
13
|
+
assert bot.target == "new"
|
|
14
|
+
assert bot.buy_amount == 0.5
|
|
15
|
+
assert bot.check_honeypot is True
|
|
16
|
+
|
|
17
|
+
def test_validate_positive_amount(self):
|
|
18
|
+
"""Test that buy_amount must be positive."""
|
|
19
|
+
bot = SniperBot(buy_amount=0)
|
|
20
|
+
with pytest.raises(ValueError, match="buy_amount must be positive"):
|
|
21
|
+
bot.validate()
|
|
22
|
+
|
|
23
|
+
def test_validate_valid_dex(self):
|
|
24
|
+
"""Test that DEX must be valid."""
|
|
25
|
+
bot = SniperBot(dex="invalid")
|
|
26
|
+
with pytest.raises(ValueError, match="Invalid DEX"):
|
|
27
|
+
bot.validate()
|
|
28
|
+
|
|
29
|
+
def test_valid_config(self):
|
|
30
|
+
"""Test valid configuration passes validation."""
|
|
31
|
+
bot = SniperBot(
|
|
32
|
+
target="new",
|
|
33
|
+
buy_amount=1.0,
|
|
34
|
+
take_profit=[2.0, 5.0],
|
|
35
|
+
stop_loss=0.5,
|
|
36
|
+
)
|
|
37
|
+
assert bot.validate() is True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestDCABot:
|
|
41
|
+
"""Tests for DCABot."""
|
|
42
|
+
|
|
43
|
+
def test_default_config(self):
|
|
44
|
+
"""Test default configuration."""
|
|
45
|
+
bot = DCABot()
|
|
46
|
+
assert bot.token == "SOL"
|
|
47
|
+
assert bot.amount == 100.0
|
|
48
|
+
assert bot.frequency == "daily"
|
|
49
|
+
|
|
50
|
+
def test_validate_positive_amount(self):
|
|
51
|
+
"""Test that amount must be positive."""
|
|
52
|
+
bot = DCABot(amount=0)
|
|
53
|
+
with pytest.raises(ValueError, match="amount must be positive"):
|
|
54
|
+
bot.validate()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TestCopyTradeBot:
|
|
58
|
+
"""Tests for CopyTradeBot."""
|
|
59
|
+
|
|
60
|
+
def test_default_config(self):
|
|
61
|
+
"""Test default configuration."""
|
|
62
|
+
bot = CopyTradeBot(wallets=["wallet1"])
|
|
63
|
+
assert bot.size_mode == "percentage"
|
|
64
|
+
assert bot.copy_sells is True
|
|
65
|
+
|
|
66
|
+
def test_validate_requires_wallets(self):
|
|
67
|
+
"""Test that at least one wallet is required."""
|
|
68
|
+
bot = CopyTradeBot()
|
|
69
|
+
with pytest.raises(ValueError, match="At least one wallet"):
|
|
70
|
+
bot.validate()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TestGridBot:
|
|
74
|
+
"""Tests for GridBot."""
|
|
75
|
+
|
|
76
|
+
def test_default_config(self):
|
|
77
|
+
"""Test default configuration."""
|
|
78
|
+
bot = GridBot()
|
|
79
|
+
assert bot.base_token == "SOL"
|
|
80
|
+
assert bot.mode == "arithmetic"
|
|
81
|
+
|
|
82
|
+
def test_validate_price_range(self):
|
|
83
|
+
"""Test that lower_price must be less than upper_price."""
|
|
84
|
+
bot = GridBot(lower_price=200, upper_price=100)
|
|
85
|
+
with pytest.raises(ValueError, match="lower_price must be less than upper_price"):
|
|
86
|
+
bot.validate()
|
|
87
|
+
|
|
88
|
+
def test_grid_levels_arithmetic(self):
|
|
89
|
+
"""Test arithmetic grid level calculation."""
|
|
90
|
+
bot = GridBot(lower_price=100, upper_price=200, grid_count=3)
|
|
91
|
+
levels = bot.grid_levels
|
|
92
|
+
assert len(levels) == 3
|
|
93
|
+
assert levels[0] == 100
|
|
94
|
+
assert levels[1] == 150
|
|
95
|
+
assert levels[2] == 200
|
|
96
|
+
|