wayfinder-paths 0.1.19__py3-none-any.whl → 0.1.20__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.

Potentially problematic release.


This version of wayfinder-paths might be problematic. Click here for more details.

Files changed (98) hide show
  1. wayfinder_paths/__init__.py +0 -2
  2. wayfinder_paths/adapters/balance_adapter/README.md +59 -45
  3. wayfinder_paths/adapters/balance_adapter/adapter.py +0 -21
  4. wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -14
  5. wayfinder_paths/adapters/brap_adapter/README.md +61 -184
  6. wayfinder_paths/adapters/brap_adapter/__init__.py +0 -4
  7. wayfinder_paths/adapters/brap_adapter/adapter.py +0 -147
  8. wayfinder_paths/adapters/brap_adapter/test_adapter.py +0 -15
  9. wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -4
  10. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -9
  11. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +0 -17
  12. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +3 -312
  13. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +1 -71
  14. wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -57
  15. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -17
  16. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +2 -42
  17. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +1 -9
  18. wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +15 -47
  19. wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -7
  20. wayfinder_paths/adapters/ledger_adapter/README.md +54 -74
  21. wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -4
  22. wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -106
  23. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -12
  24. wayfinder_paths/adapters/moonwell_adapter/README.md +67 -106
  25. wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -4
  26. wayfinder_paths/adapters/moonwell_adapter/adapter.py +9 -121
  27. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +84 -83
  28. wayfinder_paths/adapters/pool_adapter/README.md +30 -51
  29. wayfinder_paths/adapters/pool_adapter/__init__.py +0 -4
  30. wayfinder_paths/adapters/pool_adapter/adapter.py +0 -19
  31. wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -8
  32. wayfinder_paths/adapters/token_adapter/README.md +41 -49
  33. wayfinder_paths/adapters/token_adapter/adapter.py +0 -32
  34. wayfinder_paths/adapters/token_adapter/test_adapter.py +1 -12
  35. wayfinder_paths/conftest.py +0 -8
  36. wayfinder_paths/core/__init__.py +0 -2
  37. wayfinder_paths/core/adapters/BaseAdapter.py +0 -22
  38. wayfinder_paths/core/adapters/__init__.py +0 -5
  39. wayfinder_paths/core/adapters/models.py +0 -5
  40. wayfinder_paths/core/analytics/__init__.py +0 -2
  41. wayfinder_paths/core/analytics/bootstrap.py +0 -16
  42. wayfinder_paths/core/analytics/stats.py +0 -7
  43. wayfinder_paths/core/analytics/test_analytics.py +5 -34
  44. wayfinder_paths/core/clients/BRAPClient.py +0 -35
  45. wayfinder_paths/core/clients/ClientManager.py +0 -51
  46. wayfinder_paths/core/clients/HyperlendClient.py +0 -77
  47. wayfinder_paths/core/clients/LedgerClient.py +2 -122
  48. wayfinder_paths/core/clients/PoolClient.py +0 -2
  49. wayfinder_paths/core/clients/TokenClient.py +0 -39
  50. wayfinder_paths/core/clients/WalletClient.py +0 -15
  51. wayfinder_paths/core/clients/WayfinderClient.py +0 -24
  52. wayfinder_paths/core/clients/__init__.py +0 -4
  53. wayfinder_paths/core/clients/protocols.py +25 -98
  54. wayfinder_paths/core/config.py +0 -24
  55. wayfinder_paths/core/constants/__init__.py +0 -7
  56. wayfinder_paths/core/constants/base.py +2 -9
  57. wayfinder_paths/core/constants/erc20_abi.py +0 -5
  58. wayfinder_paths/core/constants/hyperlend_abi.py +0 -7
  59. wayfinder_paths/core/constants/moonwell_abi.py +0 -35
  60. wayfinder_paths/core/engine/StrategyJob.py +0 -32
  61. wayfinder_paths/core/strategies/Strategy.py +0 -99
  62. wayfinder_paths/core/strategies/__init__.py +0 -2
  63. wayfinder_paths/core/utils/__init__.py +0 -1
  64. wayfinder_paths/core/utils/erc20_service.py +0 -1
  65. wayfinder_paths/core/utils/evm_helpers.py +0 -50
  66. wayfinder_paths/core/utils/transaction.py +0 -1
  67. wayfinder_paths/run_strategy.py +0 -46
  68. wayfinder_paths/scripts/create_strategy.py +0 -17
  69. wayfinder_paths/scripts/make_wallets.py +1 -4
  70. wayfinder_paths/strategies/basis_trading_strategy/README.md +71 -163
  71. wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -24
  72. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +36 -400
  73. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +15 -64
  74. wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -4
  75. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +65 -56
  76. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +4 -27
  77. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -10
  78. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +71 -72
  79. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +23 -227
  80. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +120 -113
  81. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +64 -59
  82. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +4 -44
  83. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +2 -35
  84. wayfinder_paths/templates/adapter/README.md +107 -46
  85. wayfinder_paths/templates/adapter/adapter.py +0 -9
  86. wayfinder_paths/templates/adapter/test_adapter.py +0 -19
  87. wayfinder_paths/templates/strategy/README.md +113 -59
  88. wayfinder_paths/templates/strategy/strategy.py +0 -22
  89. wayfinder_paths/templates/strategy/test_strategy.py +0 -28
  90. wayfinder_paths/tests/test_test_coverage.py +2 -12
  91. wayfinder_paths/tests/test_utils.py +1 -31
  92. wayfinder_paths-0.1.20.dist-info/METADATA +355 -0
  93. wayfinder_paths-0.1.20.dist-info/RECORD +129 -0
  94. wayfinder_paths/core/adapters/base.py +0 -5
  95. wayfinder_paths-0.1.19.dist-info/METADATA +0 -592
  96. wayfinder_paths-0.1.19.dist-info/RECORD +0 -130
  97. {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.20.dist-info}/LICENSE +0 -0
  98. {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.20.dist-info}/WHEEL +0 -0
@@ -16,15 +16,6 @@ class StrategyJob:
16
16
  clients: dict[str, Any] | None = None,
17
17
  skip_auth: bool = False,
18
18
  ):
19
- """
20
- Initialize a StrategyJob.
21
-
22
- Args:
23
- strategy: The strategy to execute.
24
- config: Strategy job configuration.
25
- clients: Optional dict of pre-instantiated clients to inject directly.
26
- skip_auth: If True, skips authentication (for SDK usage).
27
- """
28
19
  self.strategy = strategy
29
20
  self.config = config
30
21
 
@@ -32,18 +23,12 @@ class StrategyJob:
32
23
  self.clients = ClientManager(clients=clients, skip_auth=skip_auth)
33
24
 
34
25
  def _setup_strategy(self):
35
- """Setup the strategy instance"""
36
26
  if not self.strategy:
37
27
  raise ValueError("No strategy provided to StrategyJob")
38
28
 
39
29
  self.strategy.log = self.log
40
30
 
41
31
  async def setup(self):
42
- """
43
- Initialize the strategy job and strategy.
44
-
45
- Sets up authentication and initializes the strategy with merged configuration.
46
- """
47
32
  self._setup_strategy()
48
33
 
49
34
  # Ensure API key is set for API calls
@@ -62,7 +47,6 @@ class StrategyJob:
62
47
  await self.strategy.setup()
63
48
 
64
49
  async def execute_strategy(self, action: str, **kwargs) -> dict[str, Any]:
65
- """Execute a strategy action (deposit, withdraw, update, status, exit, partial_liquidate)"""
66
50
  try:
67
51
  if action == "deposit":
68
52
  result = await self.strategy.deposit(**kwargs)
@@ -96,7 +80,6 @@ class StrategyJob:
96
80
  return {"success": False, "error": str(e)}
97
81
 
98
82
  async def run_continuous(self, interval_seconds: int | None = None):
99
- """Run the strategy continuously at specified intervals"""
100
83
  interval = interval_seconds or self.config.system.update_interval
101
84
  logger.info(
102
85
  f"Starting continuous execution for strategy: {self.strategy.name} with interval {interval}s"
@@ -115,27 +98,12 @@ class StrategyJob:
115
98
  await asyncio.sleep(interval)
116
99
 
117
100
  async def log(self, msg: str):
118
- """Log messages for the job"""
119
101
  logger.info(f"Job {self.job_id}: {msg}")
120
102
 
121
103
  async def handle_error(self, error_data: dict[str, Any]) -> None:
122
- """
123
- Handle errors that occur during strategy execution.
124
-
125
- Args:
126
- error_data: Dictionary containing error information. Expected keys:
127
- - error: Error message or exception string
128
- - action: Strategy action that failed (e.g., "deposit", "update")
129
-
130
- Note:
131
- Base implementation is a no-op. Subclasses or external systems
132
- can override this method to implement custom error handling,
133
- logging, alerting, or recovery logic.
134
- """
135
104
  pass
136
105
 
137
106
  async def stop(self):
138
- """Stop the strategy job and cleanup"""
139
107
  if hasattr(self.strategy, "stop"):
140
108
  await self.strategy.stop()
141
109
 
@@ -23,8 +23,6 @@ StatusTuple = tuple[bool, str]
23
23
 
24
24
 
25
25
  class WalletConfig(TypedDict, total=False):
26
- """Wallet configuration structure - allows additional fields for flexibility"""
27
-
28
26
  address: str
29
27
  private_key: str | None
30
28
  private_key_hex: str | None
@@ -32,8 +30,6 @@ class WalletConfig(TypedDict, total=False):
32
30
 
33
31
 
34
32
  class StrategyConfig(TypedDict, total=False):
35
- """Base strategy configuration structure - allows additional fields for flexibility"""
36
-
37
33
  main_wallet: WalletConfig | None
38
34
  strategy_wallet: WalletConfig | None
39
35
  wallet_type: str | None
@@ -68,19 +64,15 @@ class Strategy(ABC):
68
64
  self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
69
65
 
70
66
  async def setup(self) -> None:
71
- """Initialize strategy-specific setup after construction"""
72
67
  pass
73
68
 
74
69
  async def log(self, msg: str) -> None:
75
- """Log messages - can be overridden by subclasses"""
76
70
  self.logger.info(msg)
77
71
 
78
72
  async def quote(self) -> None:
79
- """Get quotes for potential trades - optional for strategies"""
80
73
  pass
81
74
 
82
75
  def _get_strategy_wallet_address(self) -> str:
83
- """Get strategy wallet address with validation."""
84
76
  strategy_wallet = self.config.get("strategy_wallet")
85
77
  if not strategy_wallet or not isinstance(strategy_wallet, dict):
86
78
  raise ValueError("strategy_wallet not configured in strategy config")
@@ -90,7 +82,6 @@ class Strategy(ABC):
90
82
  return str(address)
91
83
 
92
84
  def _get_main_wallet_address(self) -> str:
93
- """Get main wallet address with validation."""
94
85
  main_wallet = self.config.get("main_wallet")
95
86
  if not main_wallet or not isinstance(main_wallet, dict):
96
87
  raise ValueError("main_wallet not configured in strategy config")
@@ -101,37 +92,9 @@ class Strategy(ABC):
101
92
 
102
93
  @abstractmethod
103
94
  async def deposit(self, **kwargs) -> StatusTuple:
104
- """
105
- Deposit funds into the strategy.
106
-
107
- Args:
108
- **kwargs: Strategy-specific deposit parameters. Common parameters include:
109
- - main_token_amount: Amount of main token to deposit (float)
110
- - gas_token_amount: Amount of gas token to deposit (float)
111
-
112
- Returns:
113
- Tuple of (success: bool, message: str)
114
-
115
- Raises:
116
- ValueError: If required parameters are missing or invalid.
117
- """
118
95
  pass
119
96
 
120
97
  async def withdraw(self, **kwargs) -> StatusTuple:
121
- """
122
- Withdraw funds from the strategy.
123
- Default implementation unwinds all operations.
124
-
125
- Args:
126
- **kwargs: Strategy-specific withdrawal parameters (optional).
127
-
128
- Returns:
129
- Tuple of (success: bool, message: str)
130
-
131
- Note:
132
- Subclasses may override this method to add validation or custom
133
- withdrawal logic. The base implementation unwinds all ledger operations.
134
- """
135
98
  if hasattr(self, "ledger_adapter") and self.ledger_adapter:
136
99
  while self.ledger_adapter.positions.operations:
137
100
  node = self.ledger_adapter.positions.operations[-1]
@@ -146,46 +109,21 @@ class Strategy(ABC):
146
109
 
147
110
  @abstractmethod
148
111
  async def update(self) -> StatusTuple:
149
- """
150
- Deploy funds to protocols (no main wallet access).
151
- Called after deposit() has transferred assets to strategy wallet.
152
-
153
- Returns:
154
- Tuple of (success: bool, message: str)
155
- """
156
112
  pass
157
113
 
158
114
  @abstractmethod
159
115
  async def exit(self, **kwargs) -> StatusTuple:
160
- """
161
- Transfer funds from strategy wallet to main wallet.
162
- Called after withdraw() has liquidated all positions.
163
-
164
- Returns:
165
- Tuple of (success: bool, message: str)
166
- """
167
116
  pass
168
117
 
169
118
  @staticmethod
170
119
  async def policies() -> list[str]:
171
- """Return policy strings for this strategy."""
172
120
  raise NotImplementedError
173
121
 
174
122
  @abstractmethod
175
123
  async def _status(self) -> StatusDict:
176
- """
177
- Return status payload. Subclasses should implement this.
178
- Should include keys (portfolio_value, net_deposit, strategy_status).
179
- Backward-compatible keys (active_amount, total_earned) may also be included.
180
- """
181
124
  pass
182
125
 
183
126
  async def status(self) -> StatusDict:
184
- """
185
- Wrapper to compute and return strategy status and record a snapshot.
186
- Here we simply delegate to _status for compatibility.
187
- """
188
-
189
127
  status = await self._status()
190
128
  await self.ledger_adapter.record_strategy_snapshot(
191
129
  wallet_address=self._get_strategy_wallet_address(),
@@ -195,7 +133,6 @@ class Strategy(ABC):
195
133
  return status
196
134
 
197
135
  def register_adapters(self, adapters: list[Any]) -> None:
198
- """Register adapters for use by the strategy"""
199
136
  self.adapters = {}
200
137
  for adapter in adapters:
201
138
  if hasattr(adapter, "adapter_type"):
@@ -206,11 +143,6 @@ class Strategy(ABC):
206
143
  def unwind_on_error(
207
144
  self, func: Callable[..., Awaitable[StatusTuple]]
208
145
  ) -> Callable[..., Awaitable[StatusTuple]]:
209
- """
210
- Decorator to unwind operations on error
211
- Useful for deposit operations that need cleanup on failure
212
- """
213
-
214
146
  async def wrapper(*args: Any, **kwargs: Any) -> StatusTuple:
215
147
  try:
216
148
  return await func(*args, **kwargs)
@@ -236,17 +168,6 @@ class Strategy(ABC):
236
168
 
237
169
  @classmethod
238
170
  def get_metadata(cls) -> dict[str, Any]:
239
- """
240
- Return metadata about this strategy.
241
- Can be overridden to provide discovery information.
242
-
243
- Returns:
244
- Dictionary containing strategy metadata. The following keys are optional
245
- and will be None if not defined on the class:
246
- - name: Strategy name
247
- - description: Strategy description
248
- - summary: Strategy summary
249
- """
250
171
  return {
251
172
  "name": getattr(cls, "name", None),
252
173
  "description": getattr(cls, "description", None),
@@ -254,9 +175,6 @@ class Strategy(ABC):
254
175
  }
255
176
 
256
177
  async def health_check(self) -> dict[str, Any]:
257
- """
258
- Check strategy health and dependencies
259
- """
260
178
  health = {"status": "healthy", "strategy": self.name, "adapters": {}}
261
179
 
262
180
  for name, adapter in self.adapters.items():
@@ -270,23 +188,6 @@ class Strategy(ABC):
270
188
  async def partial_liquidate(
271
189
  self, usd_value: float
272
190
  ) -> tuple[bool, LiquidationResult]:
273
- """
274
- Partially liquidate strategy positions by USD value.
275
- Optional method that can be overridden by subclasses.
276
-
277
- Args:
278
- usd_value: USD value to liquidate (must be positive).
279
-
280
- Returns:
281
- Tuple of (success: bool, message: str)
282
-
283
- Raises:
284
- ValueError: If usd_value is not positive.
285
-
286
- Note:
287
- Base implementation returns failure. Subclasses should override
288
- to implement partial liquidation logic.
289
- """
290
191
  if usd_value <= 0:
291
192
  raise ValueError(f"usd_value must be positive, got {usd_value}")
292
193
  return (False, "Partial liquidation not implemented for this strategy")
@@ -1,5 +1,3 @@
1
- """Core Strategy Module - SDK surface re-export"""
2
-
3
1
  from .base import StatusDict, StatusTuple, Strategy
4
2
 
5
3
  __all__ = ["Strategy", "StatusDict", "StatusTuple"]
@@ -1 +0,0 @@
1
- """Utility helpers for local functionality not tied to external APIs."""
@@ -49,7 +49,6 @@ async def build_approve_transaction(
49
49
  spender_address: str,
50
50
  amount: int,
51
51
  ) -> dict:
52
- """Build an ERC20 approve transaction."""
53
52
  async with web3_from_chain_id(chain_id) as web3:
54
53
  contract = web3.eth.contract(
55
54
  address=web3.to_checksum_address(token_address), abi=ERC20_ABI
@@ -1,10 +1,3 @@
1
- """
2
- EVM helper utilities for common blockchain operations.
3
-
4
- This module provides reusable functions for EVM-related operations that are shared
5
- across multiple adapters.
6
- """
7
-
8
1
  import json
9
2
  import os
10
3
  from typing import Any
@@ -15,31 +8,12 @@ from wayfinder_paths.core.constants.base import CHAIN_CODE_TO_ID
15
8
 
16
9
 
17
10
  def chain_code_to_chain_id(chain_code: str | None) -> int | None:
18
- """
19
- Convert chain code to chain ID.
20
-
21
- Args:
22
- chain_code: Chain code string (e.g., "ethereum", "base")
23
-
24
- Returns:
25
- Chain ID as integer, or None if not found
26
- """
27
11
  if not chain_code:
28
12
  return None
29
13
  return CHAIN_CODE_TO_ID.get(chain_code.lower())
30
14
 
31
15
 
32
16
  def resolve_chain_id(token_info: dict[str, Any], logger_instance=None) -> int | None:
33
- """
34
- Extract chain ID from token_info dictionary.
35
-
36
- Args:
37
- token_info: Dictionary containing token information with 'chain' key
38
- logger_instance: Optional logger instance for debug messages
39
-
40
- Returns:
41
- Chain ID as integer, or None if not found
42
- """
43
17
  log = logger_instance or logger
44
18
  chain_meta = token_info.get("chain") or {}
45
19
  chain_id = chain_meta.get("id")
@@ -56,20 +30,6 @@ def resolve_rpc_url(
56
30
  config: dict[str, Any],
57
31
  explicit_rpc_url: str | None = None,
58
32
  ) -> str:
59
- """
60
- Resolve RPC URL from config.
61
-
62
- Args:
63
- chain_id: Chain ID to look up RPC URL for
64
- config: Configuration dictionary
65
- explicit_rpc_url: Explicitly provided RPC URL (takes precedence)
66
-
67
- Returns:
68
- RPC URL string
69
-
70
- Raises:
71
- ValueError: If RPC URL cannot be resolved
72
- """
73
33
  if explicit_rpc_url:
74
34
  return explicit_rpc_url
75
35
  strategy_cfg = config.get("strategy") or {}
@@ -91,16 +51,6 @@ def resolve_rpc_url(
91
51
  def resolve_private_key_for_from_address(
92
52
  from_address: str, config: dict[str, Any]
93
53
  ) -> str | None:
94
- """
95
- Resolve private key for the given address from config.
96
-
97
- Args:
98
- from_address: Address to resolve private key for
99
- config: Configuration dictionary containing wallet information
100
-
101
- Returns:
102
- Private key string, or None if not found
103
- """
104
54
  from_addr_norm = (from_address or "").lower()
105
55
  main_wallet = config.get("main_wallet")
106
56
  strategy_wallet = config.get("strategy_wallet")
@@ -91,7 +91,6 @@ async def gas_price_transaction(
91
91
  base_fee = max(base_fees)
92
92
  priority_fee = max(priority_fees)
93
93
 
94
- # The next block can grow base fee by up to 12.5%, we give a flew blocks of landing room. log_1.125(2) ~ 6 blocks of landing room. GPT says this is also what Metamask does.
95
94
  max_base_fee_growth_multiplier = 2
96
95
  transaction["maxFeePerGas"] = int(
97
96
  base_fee * max_base_fee_growth_multiplier
@@ -1,8 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- Strategy Runner
4
- Main entry point for running strategies locally
5
- """
6
2
 
7
3
  import argparse
8
4
  import asyncio
@@ -23,17 +19,6 @@ def load_strategy(
23
19
  *,
24
20
  config: StrategyJobConfig,
25
21
  ):
26
- """
27
- Dynamically load a strategy by name
28
-
29
- Args:
30
- strategy_name: Name of the strategy to load (directory name in strategies/)
31
- config: StrategyJobConfig instance containing user and strategy configuration
32
-
33
- Returns:
34
- Strategy instance
35
- """
36
- # Build the expected module path from strategy name
37
22
  strategies_dir = Path(__file__).parent / "strategies"
38
23
  strategy_dir = strategies_dir / strategy_name
39
24
 
@@ -49,7 +34,6 @@ def load_strategy(
49
34
  f"Unknown strategy: {strategy_name}. Available strategies: {available_str}"
50
35
  )
51
36
 
52
- # Import strategy module and find Strategy class
53
37
  module_path = f"strategies.{strategy_name}.strategy"
54
38
  module = __import__(module_path, fromlist=[""])
55
39
 
@@ -70,7 +54,6 @@ def load_strategy(
70
54
  if strategy_class is None:
71
55
  raise ValueError(f"No Strategy class found in {module_path}")
72
56
 
73
- # Get wallet addresses from strategy_config (enriched from wallets array in config.json)
74
57
  main_wallet = config.strategy_config.get("main_wallet") or {}
75
58
  strategy_wallet = config.strategy_config.get("strategy_wallet") or {}
76
59
  main_wallet_address = main_wallet.get("address")
@@ -103,19 +86,6 @@ def load_strategy(
103
86
  def load_config(
104
87
  config_path: str | None = None, strategy_name: str | None = None
105
88
  ) -> StrategyJobConfig:
106
- """
107
- Load configuration from config.json file
108
-
109
- Args:
110
- config_path: Path to config file (defaults to "config.json")
111
- strategy_name: Optional strategy name for per-strategy wallet lookup
112
-
113
- Returns:
114
- StrategyJobConfig instance
115
-
116
- Raises:
117
- FileNotFoundError: If config file does not exist
118
- """
119
89
  # Default to config.json if not provided
120
90
  if not config_path:
121
91
  config_path = "config.json"
@@ -144,22 +114,12 @@ async def run_strategy(
144
114
  action: str = "run",
145
115
  **kwargs,
146
116
  ):
147
- """
148
- Run a strategy
149
-
150
- Args:
151
- strategy_name: Name of the strategy to run
152
- config_path: Optional path to config file
153
- action: Action to perform (run, deposit, withdraw, status)
154
- **kwargs: Additional arguments for the action
155
- """
156
117
  try:
157
118
  if not strategy_name:
158
119
  raise ValueError("strategy_name is required")
159
120
 
160
121
  logger.debug(f"Loading strategy by name: {strategy_name}")
161
122
 
162
- # Load configuration with strategy name for wallet lookup
163
123
  logger.debug(f"Config path provided: {config_path}")
164
124
  config = load_config(config_path, strategy_name=strategy_name)
165
125
  main_wallet_cfg = config.strategy_config.get("main_wallet") or {}
@@ -170,20 +130,16 @@ async def run_strategy(
170
130
  strategy_wallet_cfg.get("address") or "none",
171
131
  )
172
132
 
173
- # Validate required configuration
174
133
  # Authentication is via system.api_key in config.json
175
134
 
176
- # Load strategy with the enriched config
177
135
  strategy = load_strategy(
178
136
  strategy_name,
179
137
  config=config,
180
138
  )
181
139
  logger.info(f"Loaded strategy: {strategy.name}")
182
140
 
183
- # Create strategy job
184
141
  strategy_job = StrategyJob(strategy, config)
185
142
 
186
- # Setup strategy job
187
143
  logger.info("Setting up strategy job...")
188
144
  logger.debug("Auth mode: API key (from system.api_key)")
189
145
  await strategy_job.setup()
@@ -260,7 +216,6 @@ async def run_strategy(
260
216
  seen.add(p)
261
217
  deduped.append(p)
262
218
 
263
- # Get wallet_id from CLI arg, config, or leave as None
264
219
  wallet_id = kwargs.get("wallet_id")
265
220
  if not wallet_id:
266
221
  wallet_id = config.strategy_config.get("wallet_id")
@@ -309,7 +264,6 @@ async def run_strategy(
309
264
 
310
265
 
311
266
  def main():
312
- """Main entry point"""
313
267
  parser = argparse.ArgumentParser(description="Run strategy strategies")
314
268
  parser.add_argument(
315
269
  "strategy",
@@ -1,12 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- Create a new strategy from template and generate a dedicated wallet for it.
4
-
5
- This script:
6
- 1. Copies the strategy template to a new directory
7
- 2. Generates a wallet with label matching the strategy name
8
- 3. Updates the strategy files with the correct names
9
- """
10
2
 
11
3
  import argparse
12
4
  import re
@@ -17,18 +9,14 @@ from wayfinder_paths.core.utils.wallets import make_random_wallet, write_wallet_
17
9
 
18
10
 
19
11
  def sanitize_name(name: str) -> str:
20
- """Convert a strategy name to a valid directory/identifier name."""
21
12
  # Replace spaces and special chars with underscores, lowercase
22
13
  name = re.sub(r"[^a-zA-Z0-9_-]", "_", name)
23
- # Remove multiple underscores
24
14
  name = re.sub(r"_+", "_", name)
25
- # Remove leading/trailing underscores
26
15
  name = name.strip("_")
27
16
  return name.lower()
28
17
 
29
18
 
30
19
  def update_strategy_file(strategy_path: Path, class_name: str) -> None:
31
- """Update strategy.py with new class name."""
32
20
  content = strategy_path.read_text()
33
21
  # Replace MyStrategy with the new class name
34
22
  content = content.replace("MyStrategy", class_name)
@@ -76,18 +64,15 @@ def main():
76
64
  dir_name = sanitize_name(args.name)
77
65
  strategy_dir = args.strategies_dir / dir_name
78
66
 
79
- # Check if directory already exists
80
67
  if strategy_dir.exists() and not args.override:
81
68
  raise SystemExit(
82
69
  f"Strategy directory already exists: {strategy_dir}\n"
83
70
  "Use --override to replace it"
84
71
  )
85
72
 
86
- # Check template exists
87
73
  if not args.template_dir.exists():
88
74
  raise SystemExit(f"Template directory not found: {args.template_dir}")
89
75
 
90
- # Create strategy directory
91
76
  if strategy_dir.exists():
92
77
  print(f"Removing existing directory: {strategy_dir}")
93
78
  shutil.rmtree(strategy_dir)
@@ -109,12 +94,10 @@ def main():
109
94
  print(f" Copied {filename}")
110
95
 
111
96
  # Generate class name from strategy name
112
- # Convert "my_awesome_strategy" -> "MyAwesomeStrategy"
113
97
  class_name = "".join(word.capitalize() for word in dir_name.split("_"))
114
98
  if not class_name.endswith("Strategy"):
115
99
  class_name += "Strategy"
116
100
 
117
- # Update strategy.py with new class name
118
101
  strategy_file = strategy_dir / "strategy.py"
119
102
  if strategy_file.exists():
120
103
  update_strategy_file(strategy_file, class_name)
@@ -54,7 +54,6 @@ def main():
54
54
 
55
55
  args.out_dir.mkdir(parents=True, exist_ok=True)
56
56
 
57
- # Load existing wallets
58
57
  existing = load_wallets(args.out_dir, "config.json")
59
58
  has_main = any(w.get("label") in ("main", "default") for w in existing)
60
59
 
@@ -67,7 +66,6 @@ def main():
67
66
  if any(w.get("label") == args.label for w in existing):
68
67
  print(f"Wallet with label '{args.label}' already exists, skipping...")
69
68
  else:
70
- # Create wallet with specified label
71
69
  w = make_random_wallet()
72
70
  w["label"] = args.label
73
71
  rows.append(w)
@@ -96,9 +94,8 @@ def main():
96
94
  ks_path.write_text(json.dumps(ks))
97
95
  index += 1
98
96
  else:
99
- # Create wallets with auto-generated labels: first one is "main" if main doesn't exist, others are "temporary_N"
100
97
  if args.n == 0:
101
- args.n = 1 # Default to 1 wallet if neither -n nor --label specified
98
+ args.n = 1
102
99
 
103
100
  # Find next temporary number
104
101
  existing_labels = {