ocean-agent 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. ocean_agent-0.3.0/LICENSE +21 -0
  2. ocean_agent-0.3.0/PKG-INFO +202 -0
  3. ocean_agent-0.3.0/README.md +185 -0
  4. ocean_agent-0.3.0/ocean_agent/__init__.py +133 -0
  5. ocean_agent-0.3.0/ocean_agent/advisor.py +210 -0
  6. ocean_agent-0.3.0/ocean_agent/api_client.py +327 -0
  7. ocean_agent-0.3.0/ocean_agent/autonomous.py +1390 -0
  8. ocean_agent-0.3.0/ocean_agent/backtest.py +181 -0
  9. ocean_agent-0.3.0/ocean_agent/brain.py +181 -0
  10. ocean_agent-0.3.0/ocean_agent/curve_seed.json +831 -0
  11. ocean_agent-0.3.0/ocean_agent/doctor.py +100 -0
  12. ocean_agent-0.3.0/ocean_agent/harness.py +690 -0
  13. ocean_agent-0.3.0/ocean_agent/historical_data.py +453 -0
  14. ocean_agent-0.3.0/ocean_agent/indicators.py +378 -0
  15. ocean_agent-0.3.0/ocean_agent/main.py +214 -0
  16. ocean_agent-0.3.0/ocean_agent/market_context.py +112 -0
  17. ocean_agent-0.3.0/ocean_agent/matrix_seed.json +1 -0
  18. ocean_agent-0.3.0/ocean_agent/mcp_client.py +164 -0
  19. ocean_agent-0.3.0/ocean_agent/mcp_server.py +833 -0
  20. ocean_agent-0.3.0/ocean_agent/notify.py +21 -0
  21. ocean_agent-0.3.0/ocean_agent/observer.py +396 -0
  22. ocean_agent-0.3.0/ocean_agent/oi_planner.py +121 -0
  23. ocean_agent-0.3.0/ocean_agent/policy_default.yaml +136 -0
  24. ocean_agent-0.3.0/ocean_agent/position.py +130 -0
  25. ocean_agent-0.3.0/ocean_agent/postmortem.py +174 -0
  26. ocean_agent-0.3.0/ocean_agent/print_eval.py +547 -0
  27. ocean_agent-0.3.0/ocean_agent/rematrix.py +561 -0
  28. ocean_agent-0.3.0/ocean_agent/scanner.py +136 -0
  29. ocean_agent-0.3.0/ocean_agent/signal_scanner.py +912 -0
  30. ocean_agent-0.3.0/ocean_agent/signing.py +44 -0
  31. ocean_agent-0.3.0/ocean_agent/state.py +23 -0
  32. ocean_agent-0.3.0/ocean_agent/walkforward.py +603 -0
  33. ocean_agent-0.3.0/ocean_agent.egg-info/PKG-INFO +202 -0
  34. ocean_agent-0.3.0/ocean_agent.egg-info/SOURCES.txt +38 -0
  35. ocean_agent-0.3.0/ocean_agent.egg-info/dependency_links.txt +1 -0
  36. ocean_agent-0.3.0/ocean_agent.egg-info/entry_points.txt +2 -0
  37. ocean_agent-0.3.0/ocean_agent.egg-info/requires.txt +6 -0
  38. ocean_agent-0.3.0/ocean_agent.egg-info/top_level.txt +1 -0
  39. ocean_agent-0.3.0/pyproject.toml +33 -0
  40. ocean_agent-0.3.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mustache-mcp / Ocean Agent contributors
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.
@@ -0,0 +1,202 @@
1
+ Metadata-Version: 2.4
2
+ Name: ocean-agent
3
+ Version: 0.3.0
4
+ Summary: Pacifica trading MCP server — measured-edge setups, exchange-native brackets, funding carry, and a policy-governed autonomous trading entity
5
+ License: MIT
6
+ Keywords: mcp,pacifica,trading,funding-rate,delta-neutral,solana
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: requests>=2.31.0
11
+ Requires-Dist: solders>=0.19.0
12
+ Requires-Dist: base58>=2.1.1
13
+ Requires-Dist: PyYAML>=6.0
14
+ Requires-Dist: python-dotenv>=1.0.0
15
+ Requires-Dist: mcp<2,>=1.0
16
+ Dynamic: license-file
17
+
18
+ # ocean-agent
19
+
20
+ > English · [한국어](README.ko.md)
21
+
22
+ **Tell your AI to trade.** An MCP server for [Pacifica](https://app.pacifica.fi)
23
+ that turns natural language into correct, risk-sized perpetual futures orders —
24
+ plus a 24/7 autonomous trading entity governed by a policy file you control.
25
+
26
+ ```bash
27
+ uvx ocean-agent # no install needed
28
+ ```
29
+
30
+ > ⚠️ This places real orders with real money. Read the
31
+ > [disclaimer](DISCLAIMER.md) before connecting an account.
32
+
33
+ Built entirely on Pacifica. Calls the Pacifica REST API directly with the same
34
+ Ed25519 agent-key signing the official tooling uses — no npm dependency.
35
+
36
+ ---
37
+
38
+ ## Why this instead of raw API access
39
+
40
+ The official Pacifica MCP exposes the API as-is: your AI must compute exact
41
+ prices and sizes itself, and a price that isn't a multiple of the market's tick
42
+ size is rejected by the exchange. ocean-agent adds the layer above that:
43
+
44
+ | | Raw API / official MCP | ocean-agent |
45
+ |---|---|---|
46
+ | Order prices | AI computes exact values | Say *"3% stop"* — tick/lot/min-order corrected automatically |
47
+ | Position sizing | Manual | Risk-based (fixed % of capital at risk per trade) |
48
+ | Safety | None | Two-step confirm gate on every money-moving tool |
49
+ | Statistics | None | Measured win rates and expected value, not textbook theory |
50
+
51
+ ---
52
+
53
+ ## MCP tools
54
+
55
+ **Market & analysis**
56
+ - `analyze_chart` — multi-timeframe indicator snapshot with *measured* hit rates
57
+ per signal on that specific coin and timeframe. Says "no edge detected" when
58
+ there isn't one.
59
+ - `top_setups` — live ranking of statistically-proven setups (EV × win rate ×
60
+ sample confidence), with entry, stop, target and leverage
61
+ - `market_context` — Fear & Greed regime read
62
+ - `scan_funding` — every market ranked by funding APR
63
+ - `learned_winrates` / `learned_combos` — win-rate database built from live
64
+ observation, including multi-signal combinations
65
+ - `review_predictions` — past calls graded against what actually happened
66
+
67
+ **Trading**
68
+ - `open_with_bracket` — entry plus exchange-native TP/SL in one call. The stops
69
+ live on the exchange, so they fire even with your machine off.
70
+ - `protect_position` — retrofit native TP/SL onto any open position
71
+ - `open_funding_position` / `close_funding_position` — delta-neutral funding
72
+ carry (spot buy + perp short) executed atomically as a batch
73
+ - `plan_oi_hedge` — sizes an OI-farming position with its cross-exchange hedge,
74
+ fee and funding math included
75
+ - `open_pacifica_leg`, `check_position`, `account_status`
76
+
77
+ **Print** (experimental — uses an endpoint Pacifica has not documented; may
78
+ change without notice)
79
+ - `print_quote` — live premium, implied volatility and liquidation price
80
+ - `print_order` / `print_status` / `print_close`
81
+ - `evaluate_print` — statistical verdict on whether a Print offer is worth it:
82
+ fill probability, average overshoot, and the breakeven APY that would
83
+ compensate for it
84
+
85
+ ---
86
+
87
+ ## Autonomous trading entity
88
+
89
+ A self-directed trader governed by `policy.yaml` — a delegation contract. It
90
+ cannot act outside those bounds.
91
+
92
+ This is a **separate always-on process**, not an MCP tool. An MCP server only
93
+ runs when your AI client calls it; a trader that must hold positions and manage
94
+ stops around the clock needs its own process. Start it deliberately, and it
95
+ keeps running whether or not any AI is connected.
96
+
97
+ ```bash
98
+ python -m ocean_agent.autonomous --init # create policy.yaml to edit
99
+ python -m ocean_agent.autonomous --dry # decide, but place no orders
100
+ python -m ocean_agent.autonomous # run continuously
101
+ python -m ocean_agent.autonomous --once # single cycle
102
+ python -m ocean_agent.autonomous --report # performance summary
103
+ ```
104
+
105
+ Read `policy.yaml` before the first real run — capital, leverage cap, risk per
106
+ trade and the hard-stop threshold all live there. Start with `--dry` on testnet.
107
+
108
+ Each cycle it reads the market, grades what it learned, manages open positions,
109
+ and enters only setups that clear every gate.
110
+
111
+ **Portfolio buckets** — capital split across directional trading, funding carry
112
+ and a cash reserve, rebalanced every cycle.
113
+
114
+ **Position aftercare** — moves the stop to breakeven once a trade is ahead,
115
+ trails it as profit grows, and takes partial profit at target. Stops only ever
116
+ move in your favour.
117
+
118
+ **Liquidity gate** — skips markets where your own order would be a large share
119
+ of daily volume. Thin books are the real hazard: an order that only partly
120
+ fills, and a stop that cannot be executed at its price.
121
+
122
+ **Net-exposure limit** — caps how one-directional the book can get, so a single
123
+ market reversal cannot hit every position at once.
124
+
125
+ **Self-remeasurement** — this is the actual learning engine. On a schedule the
126
+ bot re-measures the full matrix of coins × timeframes × signals and updates
127
+ which timeframes it trades and which signals it trusts. Regimes change: in one
128
+ measurement the 8h timeframe showed no edge at all; weeks later it was the
129
+ best-performing band. Fixed parameters go stale, so they are not fixed.
130
+
131
+ ```bash
132
+ python -m ocean_agent.rematrix # remeasure now
133
+ python -m ocean_agent.rematrix --show # what it currently believes
134
+ ```
135
+
136
+ **Adaptation** — signals that lose in live grading are suspended, size is cut
137
+ during drawdown and restored on recovery. Parameters adapt within policy bounds;
138
+ the bot never rewrites its own code.
139
+
140
+ **Final stop** — a single hard halt at catastrophic loss. Otherwise it does not
141
+ stop, it adapts.
142
+
143
+ ---
144
+
145
+ ## Setup
146
+
147
+ Requires Python 3.10+.
148
+
149
+ 1. Create an **agent wallet key** at [app.pacifica.fi/apikey](https://app.pacifica.fi/apikey).
150
+ API keys can trade but **cannot withdraw funds**, and you can revoke them
151
+ at any time.
152
+
153
+ 2. Put it in `.env`:
154
+
155
+ ```ini
156
+ ADDRESS=your_main_wallet_address
157
+ PACIFICA_API_KEY=your_agent_wallet_key
158
+
159
+ # optional — testnet uses separate keys
160
+ ADDRESS_TESTNET=...
161
+ PACIFICA_API_KEY_TESTNET=...
162
+ ```
163
+
164
+ 3. Point your MCP client at it:
165
+
166
+ ```json
167
+ {
168
+ "mcpServers": {
169
+ "ocean-agent": {
170
+ "command": "uvx",
171
+ "args": ["ocean-agent"],
172
+ "env": { "PACIFICA_ENV_FILE": "/absolute/path/to/.env" }
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ Defaults to testnet. Set `PACIFICA_BASE_URL=https://api.pacifica.fi` for live
179
+ trading.
180
+
181
+ Check your setup with `python -m ocean_agent.doctor`.
182
+
183
+ ---
184
+
185
+ ## Safety
186
+
187
+ - API keys are trading-only — this software cannot move your funds out
188
+ - Every order tool previews first and executes only on explicit confirmation
189
+ - Testnet and mainnet keys are kept separate
190
+ - The autonomous entity acts only within `policy.yaml`
191
+
192
+ ## Risk
193
+
194
+ This is trading software. Leveraged perpetual futures can lose more than the
195
+ margin you post. Measured win rates come from historical data and are
196
+ regime-dependent — an edge that held for months can vanish when the market
197
+ changes character. Nothing here is financial advice. Run it on testnet until
198
+ you understand exactly what it does, and only risk what you can afford to lose.
199
+
200
+ ## License
201
+
202
+ MIT
@@ -0,0 +1,185 @@
1
+ # ocean-agent
2
+
3
+ > English · [한국어](README.ko.md)
4
+
5
+ **Tell your AI to trade.** An MCP server for [Pacifica](https://app.pacifica.fi)
6
+ that turns natural language into correct, risk-sized perpetual futures orders —
7
+ plus a 24/7 autonomous trading entity governed by a policy file you control.
8
+
9
+ ```bash
10
+ uvx ocean-agent # no install needed
11
+ ```
12
+
13
+ > ⚠️ This places real orders with real money. Read the
14
+ > [disclaimer](DISCLAIMER.md) before connecting an account.
15
+
16
+ Built entirely on Pacifica. Calls the Pacifica REST API directly with the same
17
+ Ed25519 agent-key signing the official tooling uses — no npm dependency.
18
+
19
+ ---
20
+
21
+ ## Why this instead of raw API access
22
+
23
+ The official Pacifica MCP exposes the API as-is: your AI must compute exact
24
+ prices and sizes itself, and a price that isn't a multiple of the market's tick
25
+ size is rejected by the exchange. ocean-agent adds the layer above that:
26
+
27
+ | | Raw API / official MCP | ocean-agent |
28
+ |---|---|---|
29
+ | Order prices | AI computes exact values | Say *"3% stop"* — tick/lot/min-order corrected automatically |
30
+ | Position sizing | Manual | Risk-based (fixed % of capital at risk per trade) |
31
+ | Safety | None | Two-step confirm gate on every money-moving tool |
32
+ | Statistics | None | Measured win rates and expected value, not textbook theory |
33
+
34
+ ---
35
+
36
+ ## MCP tools
37
+
38
+ **Market & analysis**
39
+ - `analyze_chart` — multi-timeframe indicator snapshot with *measured* hit rates
40
+ per signal on that specific coin and timeframe. Says "no edge detected" when
41
+ there isn't one.
42
+ - `top_setups` — live ranking of statistically-proven setups (EV × win rate ×
43
+ sample confidence), with entry, stop, target and leverage
44
+ - `market_context` — Fear & Greed regime read
45
+ - `scan_funding` — every market ranked by funding APR
46
+ - `learned_winrates` / `learned_combos` — win-rate database built from live
47
+ observation, including multi-signal combinations
48
+ - `review_predictions` — past calls graded against what actually happened
49
+
50
+ **Trading**
51
+ - `open_with_bracket` — entry plus exchange-native TP/SL in one call. The stops
52
+ live on the exchange, so they fire even with your machine off.
53
+ - `protect_position` — retrofit native TP/SL onto any open position
54
+ - `open_funding_position` / `close_funding_position` — delta-neutral funding
55
+ carry (spot buy + perp short) executed atomically as a batch
56
+ - `plan_oi_hedge` — sizes an OI-farming position with its cross-exchange hedge,
57
+ fee and funding math included
58
+ - `open_pacifica_leg`, `check_position`, `account_status`
59
+
60
+ **Print** (experimental — uses an endpoint Pacifica has not documented; may
61
+ change without notice)
62
+ - `print_quote` — live premium, implied volatility and liquidation price
63
+ - `print_order` / `print_status` / `print_close`
64
+ - `evaluate_print` — statistical verdict on whether a Print offer is worth it:
65
+ fill probability, average overshoot, and the breakeven APY that would
66
+ compensate for it
67
+
68
+ ---
69
+
70
+ ## Autonomous trading entity
71
+
72
+ A self-directed trader governed by `policy.yaml` — a delegation contract. It
73
+ cannot act outside those bounds.
74
+
75
+ This is a **separate always-on process**, not an MCP tool. An MCP server only
76
+ runs when your AI client calls it; a trader that must hold positions and manage
77
+ stops around the clock needs its own process. Start it deliberately, and it
78
+ keeps running whether or not any AI is connected.
79
+
80
+ ```bash
81
+ python -m ocean_agent.autonomous --init # create policy.yaml to edit
82
+ python -m ocean_agent.autonomous --dry # decide, but place no orders
83
+ python -m ocean_agent.autonomous # run continuously
84
+ python -m ocean_agent.autonomous --once # single cycle
85
+ python -m ocean_agent.autonomous --report # performance summary
86
+ ```
87
+
88
+ Read `policy.yaml` before the first real run — capital, leverage cap, risk per
89
+ trade and the hard-stop threshold all live there. Start with `--dry` on testnet.
90
+
91
+ Each cycle it reads the market, grades what it learned, manages open positions,
92
+ and enters only setups that clear every gate.
93
+
94
+ **Portfolio buckets** — capital split across directional trading, funding carry
95
+ and a cash reserve, rebalanced every cycle.
96
+
97
+ **Position aftercare** — moves the stop to breakeven once a trade is ahead,
98
+ trails it as profit grows, and takes partial profit at target. Stops only ever
99
+ move in your favour.
100
+
101
+ **Liquidity gate** — skips markets where your own order would be a large share
102
+ of daily volume. Thin books are the real hazard: an order that only partly
103
+ fills, and a stop that cannot be executed at its price.
104
+
105
+ **Net-exposure limit** — caps how one-directional the book can get, so a single
106
+ market reversal cannot hit every position at once.
107
+
108
+ **Self-remeasurement** — this is the actual learning engine. On a schedule the
109
+ bot re-measures the full matrix of coins × timeframes × signals and updates
110
+ which timeframes it trades and which signals it trusts. Regimes change: in one
111
+ measurement the 8h timeframe showed no edge at all; weeks later it was the
112
+ best-performing band. Fixed parameters go stale, so they are not fixed.
113
+
114
+ ```bash
115
+ python -m ocean_agent.rematrix # remeasure now
116
+ python -m ocean_agent.rematrix --show # what it currently believes
117
+ ```
118
+
119
+ **Adaptation** — signals that lose in live grading are suspended, size is cut
120
+ during drawdown and restored on recovery. Parameters adapt within policy bounds;
121
+ the bot never rewrites its own code.
122
+
123
+ **Final stop** — a single hard halt at catastrophic loss. Otherwise it does not
124
+ stop, it adapts.
125
+
126
+ ---
127
+
128
+ ## Setup
129
+
130
+ Requires Python 3.10+.
131
+
132
+ 1. Create an **agent wallet key** at [app.pacifica.fi/apikey](https://app.pacifica.fi/apikey).
133
+ API keys can trade but **cannot withdraw funds**, and you can revoke them
134
+ at any time.
135
+
136
+ 2. Put it in `.env`:
137
+
138
+ ```ini
139
+ ADDRESS=your_main_wallet_address
140
+ PACIFICA_API_KEY=your_agent_wallet_key
141
+
142
+ # optional — testnet uses separate keys
143
+ ADDRESS_TESTNET=...
144
+ PACIFICA_API_KEY_TESTNET=...
145
+ ```
146
+
147
+ 3. Point your MCP client at it:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "ocean-agent": {
153
+ "command": "uvx",
154
+ "args": ["ocean-agent"],
155
+ "env": { "PACIFICA_ENV_FILE": "/absolute/path/to/.env" }
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
161
+ Defaults to testnet. Set `PACIFICA_BASE_URL=https://api.pacifica.fi` for live
162
+ trading.
163
+
164
+ Check your setup with `python -m ocean_agent.doctor`.
165
+
166
+ ---
167
+
168
+ ## Safety
169
+
170
+ - API keys are trading-only — this software cannot move your funds out
171
+ - Every order tool previews first and executes only on explicit confirmation
172
+ - Testnet and mainnet keys are kept separate
173
+ - The autonomous entity acts only within `policy.yaml`
174
+
175
+ ## Risk
176
+
177
+ This is trading software. Leveraged perpetual futures can lose more than the
178
+ margin you post. Measured win rates come from historical data and are
179
+ regime-dependent — an edge that held for months can vanish when the market
180
+ changes character. Nothing here is financial advice. Run it on testnet until
181
+ you understand exactly what it does, and only risk what you can afford to lose.
182
+
183
+ ## License
184
+
185
+ MIT
@@ -0,0 +1,133 @@
1
+ import os
2
+
3
+
4
+ def _is_testnet(base_url: str) -> bool:
5
+ return "test-api" in base_url
6
+
7
+
8
+ def data_tag() -> str:
9
+ """라이브 계좌 데이터의 네임스페이스: 'testnet' vs 'mainnet'.
10
+
11
+ 두 네트워크의 실매매 기록(상태·관측·승률·calibration·postmortem·예측·로그)이
12
+ 절대 섞이지 않도록 파일을 분리한다. 판단 기준은 PACIFICA_BASE_URL 환경변수
13
+ (MCP는 .mcp.json이, 자율 개체는 시작 시 policy의 base_url로 설정)이며,
14
+ 알 수 없으면 안전하게 'testnet'으로 본다.
15
+ MUSTACHE_DATA_TAG 로 강제 지정하면 그것이 최우선(멀티 인스턴스 격리용)."""
16
+ override = os.environ.get("MUSTACHE_DATA_TAG")
17
+ if override:
18
+ return override
19
+ base = os.environ.get("PACIFICA_BASE_URL", "")
20
+ return "mainnet" if (base and "test-api" not in base) else "testnet"
21
+
22
+
23
+ def data_file(name: str) -> str:
24
+ """네트워크별 라이브 데이터 파일의 홈 경로를 돌려준다.
25
+
26
+ 예) data_file("autonomous.json") -> ~/.ocean_agent_testnet_autonomous.json
27
+ 시장 백테스트 매트릭스는 네트워크와 무관(가격 히스토리)하므로 여기 쓰지 않고
28
+ ~/.ocean_agent_matrix.json 하나로 공유한다."""
29
+ return os.path.join(os.path.expanduser("~"),
30
+ f".ocean_agent_{data_tag()}_{name}")
31
+
32
+
33
+ # Mainnet-only brand name. Testnet keeps the original generic label so the
34
+ # testnet experience is byte-for-byte unchanged. This is DISPLAY ONLY (Telegram
35
+ # alerts, logs, MCP handshake) — it never touches data-file names, state keys,
36
+ # the PyPI package id, or the .mcp.json tool namespace, so it can't collide with
37
+ # the storage/logic layer.
38
+ _BRAND_MAINNET = "Ocean Agent"
39
+ _BRAND_TESTNET = "자율 개체"
40
+
41
+
42
+ def agent_name(default: str = _BRAND_TESTNET) -> str:
43
+ """User-facing display name. 'Ocean Agent' on mainnet, otherwise `default`.
44
+ Driven by the same PACIFICA_BASE_URL as data_tag(), so name and data
45
+ namespace always agree (mainnet name ↔ mainnet files)."""
46
+ return _BRAND_MAINNET if data_tag() == "mainnet" else default
47
+
48
+
49
+ # 네트워크 분리 이전(구버전)의 라이브 파일 이름들. 매트릭스는 공유라 제외.
50
+ _LIVE_FILES = ["autonomous.json", "equity.jsonl", "bot.log", "calibration.json",
51
+ "observations.jsonl", "signal_stats.json", "observe_rotation.txt",
52
+ "postmortem.jsonl", "bot_state.json", "predictions.json"]
53
+
54
+
55
+ def migrate_brand_rename() -> list:
56
+ """옛 이름(~/.mustache_*)의 데이터를 새 이름(~/.ocean_agent_*)으로 1회 옮긴다.
57
+
58
+ 0.3.0 에서 배포 이름이 mustache-mcp → ocean-agent 로 바뀌면서 홈 데이터
59
+ 파일 접두어도 함께 바뀌었다. 이사가 없으면 기존 사용자는 쌓아온 매트릭스·
60
+ 관측·손익 기록을 통째로 잃고 빈 상태로 다시 시작하게 된다.
61
+
62
+ 안전 규칙 두 가지:
63
+ · 새 이름이 이미 있으면 건드리지 않는다 (멱등 — 여러 번 불러도 안전)
64
+ · 복사가 아니라 이름 변경이라 디스크를 두 배로 쓰지 않는다
65
+ (bincache 는 378MB 라 복사하면 느리고, 어차피 재다운로드 가능하다)
66
+ """
67
+ home = os.path.expanduser("~")
68
+ moved = []
69
+ try:
70
+ entries = os.listdir(home)
71
+ except OSError:
72
+ return moved
73
+ for entry in entries:
74
+ if not entry.startswith(".mustache_"):
75
+ continue
76
+ old = os.path.join(home, entry)
77
+ new = os.path.join(home, ".ocean_agent_" + entry[len(".mustache_"):])
78
+ if os.path.exists(new):
79
+ continue # 이미 옮겨졌거나 새로 만들어진 것
80
+ try:
81
+ os.rename(old, new) # 파일·디렉터리 모두 처리
82
+ moved.append(entry)
83
+ except OSError:
84
+ pass # 사용 중이면 다음 실행에 다시 시도
85
+ return moved
86
+
87
+
88
+ def migrate_legacy_data() -> list:
89
+ """구버전 untagged 라이브 파일(~/.ocean_agent_X)을 testnet 태그본으로 1회 이전한다.
90
+
91
+ 지금까지 봇은 테스트넷에서만 돌았으므로 기존 데이터는 전부 테스트넷 것이다 →
92
+ 항상 'testnet' 태그로만 옮긴다(현재 네트워크가 메인넷이어도 옛 테스트넷 데이터를
93
+ 메인넷에 섞지 않기 위해). 태그본이 이미 있으면 건드리지 않아 멱등하며,
94
+ .bak/.polluted 등 접미사 백업은 정확한 이름이 아니라 옮기지 않는다."""
95
+ home = os.path.expanduser("~")
96
+ moved = []
97
+ for name in _LIVE_FILES:
98
+ legacy = os.path.join(home, f".ocean_agent_{name}")
99
+ tagged = os.path.join(home, f".ocean_agent_testnet_{name}")
100
+ if os.path.exists(legacy) and not os.path.exists(tagged):
101
+ try:
102
+ os.replace(legacy, tagged)
103
+ moved.append(name)
104
+ except OSError:
105
+ pass
106
+ return moved
107
+
108
+
109
+ def api_key_from_env(base_url: str = "") -> str:
110
+ """네트워크에 맞는 API 키를 고른다.
111
+
112
+ 에이전트 키는 메인넷·테스트넷에서 각각 따로 승인되므로 키가 다를 수 있다.
113
+ 테스트넷 대상이고 PACIFICA_API_KEY_TESTNET 이 있으면 그것을, 아니면
114
+ PACIFICA_API_KEY (구 AGENT_PRIVATE_KEY 호환)를 사용한다.
115
+ """
116
+ if _is_testnet(base_url):
117
+ tn = os.environ.get("PACIFICA_API_KEY_TESTNET")
118
+ if tn:
119
+ return tn
120
+ return os.environ.get("PACIFICA_API_KEY") or os.environ.get("AGENT_PRIVATE_KEY", "")
121
+
122
+
123
+ def address_from_env(base_url: str = "") -> str:
124
+ """네트워크에 맞는 계정 주소를 고른다.
125
+
126
+ 테스트넷은 별도 지갑을 쓸 수 있으므로 ADDRESS_TESTNET 을 우선 사용한다.
127
+ 없으면 ADDRESS 로 폴백 (양쪽 같은 지갑을 쓰는 경우).
128
+ """
129
+ if _is_testnet(base_url):
130
+ tn = os.environ.get("ADDRESS_TESTNET")
131
+ if tn:
132
+ return tn
133
+ return os.environ.get("ADDRESS", "")