wayfinder-paths 0.1.22__py3-none-any.whl → 0.1.24__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.
- wayfinder_paths/__init__.py +0 -4
- wayfinder_paths/adapters/balance_adapter/README.md +0 -1
- wayfinder_paths/adapters/balance_adapter/adapter.py +313 -167
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -124
- wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
- wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
- wayfinder_paths/adapters/boros_adapter/client.py +476 -0
- wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
- wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
- wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
- wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
- wayfinder_paths/adapters/boros_adapter/types.py +70 -0
- wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
- wayfinder_paths/adapters/brap_adapter/README.md +22 -75
- wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
- wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +180 -92
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +82 -14
- wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +586 -61
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
- wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
- wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +649 -547
- wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +160 -239
- wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
- wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
- wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
- wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
- wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
- wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
- wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
- wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
- wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
- wayfinder_paths/adapters/token_adapter/examples.json +0 -4
- wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
- wayfinder_paths/conftest.py +24 -17
- wayfinder_paths/core/__init__.py +0 -3
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
- wayfinder_paths/core/adapters/models.py +17 -7
- wayfinder_paths/core/clients/BRAPClient.py +4 -1
- wayfinder_paths/core/clients/ClientManager.py +0 -7
- wayfinder_paths/core/clients/LedgerClient.py +196 -172
- wayfinder_paths/core/clients/TokenClient.py +47 -1
- wayfinder_paths/core/clients/WayfinderClient.py +1 -3
- wayfinder_paths/core/clients/__init__.py +0 -5
- wayfinder_paths/core/clients/protocols.py +21 -35
- wayfinder_paths/core/clients/test_ledger_client.py +448 -0
- wayfinder_paths/core/config.py +10 -162
- wayfinder_paths/core/constants/__init__.py +73 -2
- wayfinder_paths/core/constants/base.py +8 -17
- wayfinder_paths/core/constants/chains.py +36 -0
- wayfinder_paths/core/constants/contracts.py +52 -0
- wayfinder_paths/core/constants/erc20_abi.py +0 -1
- wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
- wayfinder_paths/core/constants/hyperliquid.py +16 -0
- wayfinder_paths/core/constants/moonwell_abi.py +0 -15
- wayfinder_paths/core/constants/tokens.py +9 -0
- wayfinder_paths/core/engine/manifest.py +66 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -71
- wayfinder_paths/core/strategies/__init__.py +10 -1
- wayfinder_paths/core/strategies/opa_loop.py +167 -0
- wayfinder_paths/core/utils/evm_helpers.py +5 -15
- wayfinder_paths/core/utils/test_transaction.py +289 -0
- wayfinder_paths/core/utils/tokens.py +28 -0
- wayfinder_paths/core/utils/transaction.py +57 -8
- wayfinder_paths/core/utils/web3.py +8 -3
- wayfinder_paths/mcp/__init__.py +5 -0
- wayfinder_paths/mcp/preview.py +185 -0
- wayfinder_paths/mcp/scripting.py +84 -0
- wayfinder_paths/mcp/server.py +52 -0
- wayfinder_paths/mcp/state/profile_store.py +195 -0
- wayfinder_paths/mcp/state/store.py +89 -0
- wayfinder_paths/mcp/test_scripting.py +267 -0
- wayfinder_paths/mcp/tools/__init__.py +0 -0
- wayfinder_paths/mcp/tools/balances.py +290 -0
- wayfinder_paths/mcp/tools/discovery.py +158 -0
- wayfinder_paths/mcp/tools/execute.py +770 -0
- wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
- wayfinder_paths/mcp/tools/quotes.py +288 -0
- wayfinder_paths/mcp/tools/run_script.py +286 -0
- wayfinder_paths/mcp/tools/strategies.py +188 -0
- wayfinder_paths/mcp/tools/tokens.py +46 -0
- wayfinder_paths/mcp/tools/wallets.py +354 -0
- wayfinder_paths/mcp/utils.py +129 -0
- wayfinder_paths/policies/enso.py +1 -2
- wayfinder_paths/policies/hyper_evm.py +6 -3
- wayfinder_paths/policies/hyperlend.py +1 -2
- wayfinder_paths/policies/hyperliquid.py +1 -1
- wayfinder_paths/policies/lifi.py +18 -0
- wayfinder_paths/policies/moonwell.py +12 -7
- wayfinder_paths/policies/prjx.py +1 -3
- wayfinder_paths/policies/util.py +8 -2
- wayfinder_paths/run_strategy.py +97 -300
- wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +47 -133
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
- wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
- wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
- wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
- wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
- wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
- wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
- wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
- wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
- wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
- wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
- wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
- wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
- wayfinder_paths/{templates/strategy → strategies/boros_hype_strategy}/test_strategy.py +99 -63
- wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
- wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +15 -23
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +27 -62
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +84 -58
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -164
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +43 -76
- wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
- wayfinder_paths/tests/test_test_coverage.py +1 -4
- wayfinder_paths-0.1.24.dist-info/METADATA +378 -0
- wayfinder_paths-0.1.24.dist-info/RECORD +185 -0
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
- wayfinder_paths/core/clients/WalletClient.py +0 -41
- wayfinder_paths/core/engine/StrategyJob.py +0 -110
- wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
- wayfinder_paths/scripts/create_strategy.py +0 -139
- wayfinder_paths/scripts/make_wallets.py +0 -142
- wayfinder_paths/templates/adapter/README.md +0 -150
- wayfinder_paths/templates/adapter/adapter.py +0 -16
- wayfinder_paths/templates/adapter/examples.json +0 -8
- wayfinder_paths/templates/adapter/test_adapter.py +0 -30
- wayfinder_paths/templates/strategy/README.md +0 -186
- wayfinder_paths/templates/strategy/examples.json +0 -11
- wayfinder_paths/templates/strategy/strategy.py +0 -35
- wayfinder_paths/tests/test_smoke_manifest.py +0 -63
- wayfinder_paths-0.1.22.dist-info/METADATA +0 -355
- wayfinder_paths-0.1.22.dist-info/RECORD +0 -129
- /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
wayfinder_paths/run_strategy.py
CHANGED
|
@@ -2,345 +2,142 @@
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import asyncio
|
|
5
|
+
import importlib
|
|
6
|
+
import inspect
|
|
5
7
|
import json
|
|
6
8
|
import sys
|
|
7
|
-
from
|
|
9
|
+
from typing import Any
|
|
8
10
|
|
|
9
11
|
from loguru import logger
|
|
10
12
|
|
|
11
|
-
from wayfinder_paths.core.config import
|
|
12
|
-
from wayfinder_paths.core.
|
|
13
|
+
from wayfinder_paths.core.config import CONFIG
|
|
14
|
+
from wayfinder_paths.core.strategies.Strategy import Strategy
|
|
13
15
|
from wayfinder_paths.core.utils.evm_helpers import resolve_private_key_for_from_address
|
|
14
16
|
from wayfinder_paths.core.utils.web3 import get_transaction_chain_id, web3_from_chain_id
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
config: StrategyJobConfig,
|
|
21
|
-
):
|
|
22
|
-
strategies_dir = Path(__file__).parent / "strategies"
|
|
23
|
-
strategy_dir = strategies_dir / strategy_name
|
|
19
|
+
def get_strategy_config(strategy_name: str) -> dict[str, Any]:
|
|
20
|
+
config = dict(CONFIG.get("strategy", {}))
|
|
21
|
+
wallets = {w["label"]: w for w in CONFIG.get("wallets", [])}
|
|
24
22
|
|
|
25
|
-
if not
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
for path in strategies_dir.iterdir():
|
|
30
|
-
if path.is_dir() and (path / "strategy.py").exists():
|
|
31
|
-
available.append(path.name)
|
|
32
|
-
available_str = ", ".join(available) if available else "none"
|
|
33
|
-
raise ValueError(
|
|
34
|
-
f"Unknown strategy: {strategy_name}. Available strategies: {available_str}"
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
module_path = f"strategies.{strategy_name}.strategy"
|
|
38
|
-
module = __import__(module_path, fromlist=[""])
|
|
39
|
-
|
|
40
|
-
# Find the Strategy subclass in the module
|
|
41
|
-
from wayfinder_paths.core.strategies.Strategy import Strategy
|
|
42
|
-
|
|
43
|
-
strategy_class = None
|
|
44
|
-
for attr_name in dir(module):
|
|
45
|
-
attr = getattr(module, attr_name)
|
|
46
|
-
if (
|
|
47
|
-
isinstance(attr, type)
|
|
48
|
-
and issubclass(attr, Strategy)
|
|
49
|
-
and attr is not Strategy
|
|
50
|
-
):
|
|
51
|
-
strategy_class = attr
|
|
52
|
-
break
|
|
23
|
+
if "main_wallet" not in config and "main" in wallets:
|
|
24
|
+
config["main_wallet"] = {"address": wallets["main"]["address"]}
|
|
25
|
+
if "strategy_wallet" not in config and strategy_name in wallets:
|
|
26
|
+
config["strategy_wallet"] = {"address": wallets[strategy_name]["address"]}
|
|
53
27
|
|
|
54
|
-
|
|
55
|
-
|
|
28
|
+
by_addr = {w["address"].lower(): w for w in CONFIG.get("wallets", [])}
|
|
29
|
+
for key in ("main_wallet", "strategy_wallet"):
|
|
30
|
+
if wallet := config.get(key):
|
|
31
|
+
if entry := by_addr.get(wallet.get("address", "").lower()):
|
|
32
|
+
if pk := entry.get("private_key") or entry.get("private_key_hex"):
|
|
33
|
+
wallet["private_key_hex"] = pk
|
|
34
|
+
return config
|
|
56
35
|
|
|
57
|
-
main_wallet = config.strategy_config.get("main_wallet") or {}
|
|
58
|
-
strategy_wallet = config.strategy_config.get("strategy_wallet") or {}
|
|
59
|
-
main_wallet_address = main_wallet.get("address")
|
|
60
|
-
strategy_wallet_address = strategy_wallet.get("address")
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
37
|
+
def create_signing_callback(address: str, config: dict[str, Any]):
|
|
38
|
+
async def sign(transaction: dict) -> str:
|
|
39
|
+
pk = resolve_private_key_for_from_address(address, config)
|
|
66
40
|
async with web3_from_chain_id(get_transaction_chain_id(transaction)) as web3:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def strategy_wallet_signing_callback(transaction):
|
|
71
|
-
private_key = resolve_private_key_for_from_address(
|
|
72
|
-
strategy_wallet_address,
|
|
73
|
-
config.strategy_config,
|
|
74
|
-
)
|
|
75
|
-
async with web3_from_chain_id(get_transaction_chain_id(transaction)) as web3:
|
|
76
|
-
signed = web3.eth.account.sign_transaction(transaction, private_key)
|
|
77
|
-
return signed.raw_transaction.hex()
|
|
78
|
-
|
|
79
|
-
return strategy_class(
|
|
80
|
-
config=config.strategy_config,
|
|
81
|
-
main_wallet_signing_callback=main_wallet_signing_callback,
|
|
82
|
-
strategy_wallet_signing_callback=strategy_wallet_signing_callback,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def load_config(
|
|
87
|
-
config_path: str | None = None, strategy_name: str | None = None
|
|
88
|
-
) -> StrategyJobConfig:
|
|
89
|
-
# Default to config.json if not provided
|
|
90
|
-
if not config_path:
|
|
91
|
-
config_path = "config.json"
|
|
41
|
+
return web3.eth.account.sign_transaction(
|
|
42
|
+
transaction, pk
|
|
43
|
+
).raw_transaction.hex()
|
|
92
44
|
|
|
93
|
-
|
|
94
|
-
if not config_file.exists():
|
|
95
|
-
raise FileNotFoundError(
|
|
96
|
-
f"Config file not found: {config_path}. "
|
|
97
|
-
"Please create config.json (see wayfinder_paths/config.example.json for template)"
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
logger.info(f"Loading config from {config_path}")
|
|
101
|
-
with open(config_file) as f:
|
|
102
|
-
config_data = json.load(f)
|
|
45
|
+
return sign
|
|
103
46
|
|
|
104
|
-
config = StrategyJobConfig.from_dict(config_data, strategy_name=strategy_name)
|
|
105
|
-
if strategy_name:
|
|
106
|
-
config.strategy_config["_strategy_name"] = strategy_name
|
|
107
|
-
config.__post_init__()
|
|
108
|
-
return config
|
|
109
47
|
|
|
48
|
+
def find_strategy_class(module) -> type[Strategy]:
|
|
49
|
+
for _, obj in inspect.getmembers(module, inspect.isclass):
|
|
50
|
+
if issubclass(obj, Strategy) and obj is not Strategy:
|
|
51
|
+
return obj
|
|
52
|
+
raise ValueError(f"No Strategy subclass found in {module.__name__}")
|
|
110
53
|
|
|
111
|
-
async def run_strategy(
|
|
112
|
-
strategy_name: str | None = None,
|
|
113
|
-
config_path: str | None = None,
|
|
114
|
-
action: str = "run",
|
|
115
|
-
**kwargs,
|
|
116
|
-
):
|
|
117
|
-
try:
|
|
118
|
-
if not strategy_name:
|
|
119
|
-
raise ValueError("strategy_name is required")
|
|
120
54
|
|
|
121
|
-
|
|
55
|
+
async def run_strategy(strategy_name: str, action: str = "status", **kw):
|
|
56
|
+
config = get_strategy_config(strategy_name)
|
|
122
57
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
logger.debug(
|
|
128
|
-
"Loaded config: wallets(main={} strategy={})",
|
|
129
|
-
main_wallet_cfg.get("address") or "none",
|
|
130
|
-
strategy_wallet_cfg.get("address") or "none",
|
|
131
|
-
)
|
|
58
|
+
def signing_cb(key: str):
|
|
59
|
+
if addr := config.get(key, {}).get("address"):
|
|
60
|
+
return create_signing_callback(addr, config)
|
|
61
|
+
return None
|
|
132
62
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
63
|
+
module = importlib.import_module(
|
|
64
|
+
f"wayfinder_paths.strategies.{strategy_name}.strategy"
|
|
65
|
+
)
|
|
66
|
+
strategy_cls = find_strategy_class(module)
|
|
67
|
+
strategy = strategy_cls(
|
|
68
|
+
config,
|
|
69
|
+
main_wallet_signing_callback=signing_cb("main_wallet"),
|
|
70
|
+
strategy_wallet_signing_callback=signing_cb("strategy_wallet"),
|
|
71
|
+
)
|
|
72
|
+
await strategy.setup()
|
|
73
|
+
|
|
74
|
+
if action == "policy":
|
|
75
|
+
policies = strategy.policies() if hasattr(strategy, "policies") else []
|
|
76
|
+
if wallet_id := kw.get("wallet_id"):
|
|
77
|
+
policies = [p.replace("FORMAT_WALLET_ID", wallet_id) for p in policies]
|
|
78
|
+
result = {"policies": policies}
|
|
79
|
+
elif action == "status":
|
|
80
|
+
result = await strategy.status()
|
|
81
|
+
elif action == "deposit":
|
|
82
|
+
result = await strategy.deposit(
|
|
83
|
+
main_token_amount=kw.get("main_token_amount", 0.0),
|
|
84
|
+
gas_token_amount=kw.get("gas_token_amount", 0.0),
|
|
138
85
|
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# Execute action
|
|
148
|
-
if action == "run":
|
|
149
|
-
logger.info("Starting continuous execution...")
|
|
150
|
-
await strategy_job.run_continuous(interval_seconds=kwargs.get("interval"))
|
|
151
|
-
|
|
152
|
-
elif action == "deposit":
|
|
153
|
-
main_token_amount = kwargs.get("main_token_amount")
|
|
154
|
-
gas_token_amount = kwargs.get("gas_token_amount")
|
|
155
|
-
|
|
156
|
-
if main_token_amount is None and gas_token_amount is None:
|
|
157
|
-
raise ValueError(
|
|
158
|
-
"Either main token amount or gas token amount required for deposit (use --main-token-amount and/or --gas-token-amount)"
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
# Default to 0.0 if not provided
|
|
162
|
-
if main_token_amount is None:
|
|
163
|
-
main_token_amount = 0.0
|
|
164
|
-
if gas_token_amount is None:
|
|
165
|
-
gas_token_amount = 0.0
|
|
166
|
-
|
|
167
|
-
result = await strategy_job.execute_strategy(
|
|
168
|
-
"deposit",
|
|
169
|
-
main_token_amount=main_token_amount,
|
|
170
|
-
gas_token_amount=gas_token_amount,
|
|
171
|
-
)
|
|
172
|
-
logger.info(f"Deposit result: {result}")
|
|
173
|
-
|
|
174
|
-
elif action == "withdraw":
|
|
175
|
-
amount = kwargs.get("amount")
|
|
176
|
-
result = await strategy_job.execute_strategy("withdraw", amount=amount)
|
|
177
|
-
logger.info(f"Withdraw result: {result}")
|
|
178
|
-
|
|
179
|
-
elif action == "status":
|
|
180
|
-
result = await strategy_job.execute_strategy("status")
|
|
181
|
-
logger.info(f"Status: {json.dumps(result, indent=2)}")
|
|
182
|
-
|
|
183
|
-
elif action == "update":
|
|
184
|
-
result = await strategy_job.execute_strategy("update")
|
|
185
|
-
logger.info(f"Update result: {result}")
|
|
186
|
-
|
|
187
|
-
elif action == "exit":
|
|
188
|
-
result = await strategy_job.execute_strategy("exit")
|
|
189
|
-
logger.info(f"Exit result: {result}")
|
|
190
|
-
|
|
191
|
-
elif action == "partial-liquidate":
|
|
192
|
-
usd_value = kwargs.get("amount")
|
|
193
|
-
if not usd_value:
|
|
194
|
-
raise ValueError("Amount (USD value) required for partial-liquidate")
|
|
195
|
-
result = await strategy_job.execute_strategy(
|
|
196
|
-
"partial_liquidate", usd_value=usd_value
|
|
197
|
-
)
|
|
198
|
-
logger.info(f"Partial liquidation result: {result}")
|
|
199
|
-
|
|
200
|
-
elif action == "policy":
|
|
201
|
-
policies: list[str] = []
|
|
202
|
-
|
|
86
|
+
elif action == "withdraw":
|
|
87
|
+
result = await strategy.withdraw(amount=kw.get("amount"))
|
|
88
|
+
elif action == "update":
|
|
89
|
+
result = await strategy.update()
|
|
90
|
+
elif action == "exit":
|
|
91
|
+
result = await strategy.exit()
|
|
92
|
+
elif action == "run":
|
|
93
|
+
while True:
|
|
203
94
|
try:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if isinstance(result, list) and result:
|
|
208
|
-
policies = [p for p in result if isinstance(p, str)]
|
|
209
|
-
except Exception:
|
|
210
|
-
pass
|
|
211
|
-
|
|
212
|
-
seen = set()
|
|
213
|
-
deduped: list[str] = []
|
|
214
|
-
for p in policies:
|
|
215
|
-
if p not in seen:
|
|
216
|
-
seen.add(p)
|
|
217
|
-
deduped.append(p)
|
|
218
|
-
|
|
219
|
-
wallet_id = kwargs.get("wallet_id")
|
|
220
|
-
if not wallet_id:
|
|
221
|
-
wallet_id = config.strategy_config.get("wallet_id")
|
|
222
|
-
if not wallet_id:
|
|
223
|
-
wallet_id = config.system.wallet_id
|
|
224
|
-
|
|
225
|
-
# Render policies with wallet_id if available
|
|
226
|
-
if wallet_id:
|
|
227
|
-
rendered = [
|
|
228
|
-
p.replace("FORMAT_WALLET_ID", str(wallet_id)) for p in deduped
|
|
229
|
-
]
|
|
230
|
-
else:
|
|
231
|
-
rendered = deduped
|
|
232
|
-
logger.info(
|
|
233
|
-
"Policy rendering without wallet_id - policies contain FORMAT_WALLET_ID placeholder"
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
logger.info(json.dumps({"policies": rendered}, indent=2))
|
|
237
|
-
|
|
238
|
-
elif action == "script":
|
|
239
|
-
duration = kwargs.get("duration") or 300
|
|
240
|
-
logger.info(f"Running script mode for {duration}s...")
|
|
241
|
-
task = asyncio.create_task(
|
|
242
|
-
strategy_job.run_continuous(
|
|
243
|
-
interval_seconds=kwargs.get("interval") or 60
|
|
244
|
-
)
|
|
245
|
-
)
|
|
246
|
-
await asyncio.sleep(duration)
|
|
247
|
-
task.cancel()
|
|
248
|
-
try:
|
|
249
|
-
await task
|
|
95
|
+
result = await strategy.update()
|
|
96
|
+
logger.info(f"Update: {result}")
|
|
97
|
+
await asyncio.sleep(kw.get("interval", 60))
|
|
250
98
|
except asyncio.CancelledError:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
finally:
|
|
262
|
-
if "strategy_job" in locals():
|
|
263
|
-
await strategy_job.stop()
|
|
99
|
+
result = (True, "stopped")
|
|
100
|
+
break
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(f"Unknown action: {action}")
|
|
103
|
+
|
|
104
|
+
print(
|
|
105
|
+
json.dumps(result, indent=2)
|
|
106
|
+
if isinstance(result, dict)
|
|
107
|
+
else f"{action}: {result}"
|
|
108
|
+
)
|
|
264
109
|
|
|
265
110
|
|
|
266
111
|
def main():
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
help="Strategy to run (stablecoin_yield_strategy)",
|
|
271
|
-
)
|
|
272
|
-
parser.add_argument(
|
|
273
|
-
"--config", help="Path to config file (defaults to config.json)"
|
|
274
|
-
)
|
|
275
|
-
parser.add_argument(
|
|
112
|
+
p = argparse.ArgumentParser()
|
|
113
|
+
p.add_argument("strategy")
|
|
114
|
+
p.add_argument(
|
|
276
115
|
"--action",
|
|
277
|
-
default="
|
|
278
|
-
choices=[
|
|
279
|
-
"run",
|
|
280
|
-
"deposit",
|
|
281
|
-
"withdraw",
|
|
282
|
-
"status",
|
|
283
|
-
"update",
|
|
284
|
-
"exit",
|
|
285
|
-
"policy",
|
|
286
|
-
"script",
|
|
287
|
-
"partial-liquidate",
|
|
288
|
-
],
|
|
289
|
-
help="Action to perform (default: run)",
|
|
116
|
+
default="status",
|
|
117
|
+
choices=["run", "deposit", "withdraw", "status", "update", "exit", "policy"],
|
|
290
118
|
)
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
119
|
+
p.add_argument("--amount", type=float)
|
|
120
|
+
p.add_argument("--main-token-amount", type=float, dest="main_token_amount")
|
|
121
|
+
p.add_argument(
|
|
122
|
+
"--gas-token-amount", type=float, dest="gas_token_amount", default=0.0
|
|
295
123
|
)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
dest="main_token_amount",
|
|
301
|
-
help="Main token amount for deposit action",
|
|
302
|
-
)
|
|
303
|
-
parser.add_argument(
|
|
304
|
-
"--gas-token-amount",
|
|
305
|
-
"--gas_token_amount",
|
|
306
|
-
type=float,
|
|
307
|
-
dest="gas_token_amount",
|
|
308
|
-
default=0.0,
|
|
309
|
-
help="Gas token amount for deposit action (default: 0.0)",
|
|
310
|
-
)
|
|
311
|
-
parser.add_argument(
|
|
312
|
-
"--interval",
|
|
313
|
-
type=int,
|
|
314
|
-
help="Update interval in seconds for continuous/script modes",
|
|
315
|
-
)
|
|
316
|
-
parser.add_argument(
|
|
317
|
-
"--duration", type=int, help="Duration in seconds for script action"
|
|
318
|
-
)
|
|
319
|
-
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
|
320
|
-
parser.add_argument(
|
|
321
|
-
"--wallet-id",
|
|
322
|
-
help="Wallet ID for policy rendering (replaces FORMAT_WALLET_ID in policies)",
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
args = parser.parse_args()
|
|
124
|
+
p.add_argument("--interval", type=int, default=60)
|
|
125
|
+
p.add_argument("--wallet-id", dest="wallet_id")
|
|
126
|
+
p.add_argument("--debug", action="store_true")
|
|
127
|
+
args = p.parse_args()
|
|
326
128
|
|
|
327
|
-
# Configure logging
|
|
328
|
-
log_level = "DEBUG" if args.debug else "INFO"
|
|
329
129
|
logger.remove()
|
|
330
|
-
logger.add(sys.stderr, level=
|
|
130
|
+
logger.add(sys.stderr, level="DEBUG" if args.debug else "INFO")
|
|
331
131
|
|
|
332
|
-
# Run strategy
|
|
333
132
|
asyncio.run(
|
|
334
133
|
run_strategy(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
action=args.action,
|
|
134
|
+
args.strategy,
|
|
135
|
+
args.action,
|
|
338
136
|
amount=args.amount,
|
|
339
137
|
main_token_amount=args.main_token_amount,
|
|
340
138
|
gas_token_amount=args.gas_token_amount,
|
|
341
139
|
interval=args.interval,
|
|
342
|
-
|
|
343
|
-
wallet_id=getattr(args, "wallet_id", None),
|
|
140
|
+
wallet_id=args.wallet_id,
|
|
344
141
|
)
|
|
345
142
|
)
|
|
346
143
|
|