opentradex 0.1.0 → 0.1.3

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.
package/.env.example CHANGED
@@ -1,8 +1,47 @@
1
- KALSHI_API_KEY_ID=
2
- KALSHI_PRIVATE_KEY_PATH=
3
- KALSHI_USE_DEMO=true
4
- APIFY_API_TOKEN=
1
+ # Welcome to OpenTradex
2
+ # Our implementation. Your strategy.
3
+
4
+ # Runtime profile
5
+ OPENTRADEX_RUNTIME=claude-code
6
+ OPENTRADEX_PACKAGE_MANAGER=npm
7
+ OPENTRADEX_EXECUTION_MODE=paper
8
+ OPENTRADEX_PRIMARY_MARKET=kalshi
9
+ OPENTRADEX_ENABLED_MARKETS=kalshi
10
+ OPENTRADEX_ENABLED_INTEGRATIONS=apify,rss
11
+ OPENTRADEX_LIVE_EXECUTION_MARKET=kalshi
12
+ NEWS_PROVIDER=apify,rss
13
+
14
+ # Risk configuration
5
15
  BANKROLL=30.00
6
16
  MIN_EDGE=0.10
7
17
  MAX_POSITION_PCT=0.30
8
18
  CYCLE_INTERVAL=900
19
+ LIVE_TRADING=false
20
+
21
+ # Kalshi
22
+ KALSHI_API_KEY_ID=
23
+ KALSHI_PRIVATE_KEY_PATH=
24
+ KALSHI_USE_DEMO=true
25
+
26
+ # Polymarket
27
+ POLYMARKET_GAMMA_URL=https://gamma-api.polymarket.com
28
+ POLYMARKET_WALLET_ADDRESS=
29
+ POLYMARKET_PRIVATE_KEY=
30
+
31
+ # TradingView
32
+ TRADINGVIEW_USERNAME=
33
+ TRADINGVIEW_PASSWORD=
34
+ TRADINGVIEW_WATCHLIST=SPY,QQQ,BTCUSD,NQ1!
35
+
36
+ # Robinhood and Groww
37
+ ROBINHOOD_USERNAME=
38
+ ROBINHOOD_PASSWORD=
39
+ ROBINHOOD_MFA_CODE=
40
+ GROWW_ACCESS_TOKEN=
41
+
42
+ # Optional LLM provider secrets
43
+ OPENAI_API_KEY=
44
+ GEMINI_API_KEY=
45
+
46
+ # Data integrations
47
+ APIFY_API_TOKEN=
package/README.md CHANGED
@@ -1,35 +1,67 @@
1
- # Open Trademaxxxing
1
+ # OpenTradex
2
2
 
3
- **An autonomous AI agent that trades prediction markets by reading the news before the crowd.**
3
+ **Our implementation. Your strategy.**
4
+
5
+ OpenTradex is an open-source onboarding and execution layer for AI-assisted trading workflows. It helps you choose a runtime, wire market rails, connect optional data APIs, and launch a live dashboard without pretending to own your strategy.
4
6
 
5
7
  Preferred setup: run `opentradex onboard`.
6
8
 
7
9
  ## Install
8
10
 
9
- OpenClaw-style first run flow:
11
+ Welcome to OpenTradex:
10
12
 
11
13
  ```bash
12
- # install from npm
13
- npm install -g opentradex
14
-
15
- # guided setup
14
+ # npm
15
+ npm install -g opentradex@latest
16
16
  opentradex onboard
17
17
  ```
18
18
 
19
- `opentradex onboard` creates a workspace, writes `.env`, optionally installs the Python and dashboard dependencies, and saves your default workspace in `~/.opentradex/config.json`.
19
+ Alternative entry points:
20
+
21
+ ```bash
22
+ # npx
23
+ npx opentradex@latest onboard
24
+
25
+ # bunx
26
+ bunx opentradex@latest onboard
27
+
28
+ # curl bootstrap
29
+ curl -fsSL https://opentradex.vercel.app/install.sh | bash
30
+ ```
20
31
 
21
- You can also use `npx opentradex onboard` if you do not want a global install.
32
+ `opentradex onboard` creates a workspace, writes a grouped `.env`, saves an `opentradex.config.json` workspace profile, optionally installs Python and web dependencies, and stores your default workspace in `~/.opentradex/config.json`.
33
+
34
+ During onboarding you can choose:
35
+
36
+ - Your agent runtime profile
37
+ - Your primary market rail
38
+ - Extra rails like `polymarket`, `tradingview`, `robinhood`, or `groww`
39
+ - Optional data integrations like `apify`, `rss`, `reddit`, `twitter`, `truthsocial`, and `tiktok`
40
+ - Your package manager for local web workflows
22
41
 
23
42
  ### CLI Commands
24
43
 
25
44
  ```bash
26
45
  opentradex onboard # guided setup
27
- opentradex doctor # verify Claude, Python, npm, and env config
46
+ opentradex doctor # verify runtime, packages, env config, and rails
47
+ opentradex providers # show supported runtime, market, and data rails
28
48
  opentradex start # run the continuous trading loop
29
49
  opentradex cycle --rationale "..." # run one cycle or research a thesis
30
50
  opentradex web # launch the Next.js dashboard
31
51
  ```
32
52
 
53
+ ## Current Rail Support
54
+
55
+ | Rail | Current role |
56
+ |---|---|
57
+ | Kalshi | Best live execution path |
58
+ | Polymarket | Public market discovery and comparison |
59
+ | TradingView | Watchlist and chart context |
60
+ | Robinhood | Broker profile placeholder |
61
+ | Groww | Broker profile placeholder |
62
+
63
+ Kalshi is still the strongest live execution rail today. The others are intentionally exposed as discovery/profile rails so you can build the workflow now and decide later what deserves real execution adapters.
64
+
33
65
  ---
34
66
 
35
67
  ## How It Works
@@ -229,7 +261,7 @@ cd web && npm run dev
229
261
  # → http://localhost:3000
230
262
  ```
231
263
 
232
- If you want the simpler package flow instead, use `npm install -g opentradex` and then `opentradex onboard`.
264
+ If you want the simpler package flow instead, use `npm install -g opentradex@latest` and then `opentradex onboard`.
233
265
 
234
266
  ## Stack
235
267
 
@@ -0,0 +1,196 @@
1
+ """
2
+ Polymarket discovery rail.
3
+
4
+ CLI usage:
5
+ python3 gossip/polymarket.py scan --limit 40
6
+ python3 gossip/polymarket.py search "bitcoin"
7
+ python3 gossip/polymarket.py market <id-or-slug>
8
+
9
+ This module intentionally focuses on public Gamma API discovery so the agent can
10
+ compare Polymarket pricing with other rails without requiring a wallet up front.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+ import sys
19
+ from pathlib import Path
20
+ from urllib.parse import urlencode
21
+ from urllib.request import Request, urlopen
22
+
23
+ from dotenv import load_dotenv
24
+
25
+ load_dotenv(Path(__file__).resolve().parent.parent / ".env")
26
+
27
+ BASE_URL = os.getenv("POLYMARKET_GAMMA_URL", "https://gamma-api.polymarket.com").rstrip("/")
28
+
29
+
30
+ def log(message: str) -> None:
31
+ print(message, file=sys.stderr)
32
+
33
+
34
+ async def api_get(path: str, params: dict | None = None):
35
+ url = f"{BASE_URL}{path}"
36
+ if params:
37
+ url = f"{url}?{urlencode(params)}"
38
+
39
+ request = Request(
40
+ url,
41
+ headers={
42
+ "User-Agent": "OpenTradex/0.1 (+https://github.com/deonmenezes/open-trademaxxxing)",
43
+ "Accept": "application/json",
44
+ },
45
+ )
46
+
47
+ with urlopen(request, timeout=20) as response:
48
+ return json.loads(response.read().decode("utf-8"))
49
+
50
+
51
+ def parse_outcome_prices(raw_prices: str | list | None) -> tuple[float | None, float | None]:
52
+ if not raw_prices:
53
+ return None, None
54
+
55
+ prices = raw_prices
56
+ if isinstance(raw_prices, str):
57
+ try:
58
+ prices = json.loads(raw_prices)
59
+ except json.JSONDecodeError:
60
+ return None, None
61
+
62
+ if not isinstance(prices, list) or len(prices) < 2:
63
+ return None, None
64
+
65
+ try:
66
+ return float(prices[0]), float(prices[1])
67
+ except (TypeError, ValueError):
68
+ return None, None
69
+
70
+
71
+ def simplify_market(market: dict) -> dict:
72
+ yes_price, no_price = parse_outcome_prices(market.get("outcomePrices"))
73
+ event_title = ""
74
+ if market.get("events"):
75
+ event_title = market["events"][0].get("title", "")
76
+
77
+ return {
78
+ "id": market.get("id"),
79
+ "slug": market.get("slug"),
80
+ "question": market.get("question"),
81
+ "event_title": event_title,
82
+ "description": market.get("description", "")[:700],
83
+ "end_date": market.get("endDate"),
84
+ "yes_price": yes_price,
85
+ "no_price": no_price,
86
+ "volume": float(market.get("volumeNum") or market.get("volume") or 0),
87
+ "liquidity": float(market.get("liquidityNum") or market.get("liquidity") or 0),
88
+ "volume_24h": float(market.get("volume24hr") or 0),
89
+ "active": bool(market.get("active")),
90
+ "closed": bool(market.get("closed")),
91
+ "accepting_orders": bool(market.get("acceptingOrders")),
92
+ "url": f"https://polymarket.com/event/{market.get('slug')}" if market.get("slug") else "",
93
+ }
94
+
95
+
96
+ async def scan_markets(limit: int = 40, min_volume: float = 1000, min_liquidity: float = 1000) -> list[dict]:
97
+ data = await api_get(
98
+ "/markets",
99
+ {
100
+ "active": "true",
101
+ "closed": "false",
102
+ "limit": max(limit * 3, 120),
103
+ },
104
+ )
105
+ markets = [simplify_market(m) for m in data]
106
+ filtered = [
107
+ market
108
+ for market in markets
109
+ if market["volume"] >= min_volume and market["liquidity"] >= min_liquidity
110
+ ]
111
+ filtered.sort(key=lambda market: market["volume"], reverse=True)
112
+ return filtered[:limit]
113
+
114
+
115
+ async def search_markets(query: str, limit: int = 20) -> list[dict]:
116
+ data = await api_get(
117
+ "/markets",
118
+ {
119
+ "active": "true",
120
+ "closed": "false",
121
+ "limit": 300,
122
+ },
123
+ )
124
+ needle = query.lower()
125
+ matched = []
126
+ for market in data:
127
+ haystack = " ".join(
128
+ [
129
+ str(market.get("question", "")),
130
+ str(market.get("description", "")),
131
+ str(market.get("slug", "")),
132
+ str(market.get("groupItemTitle", "")),
133
+ ]
134
+ ).lower()
135
+ if needle in haystack:
136
+ matched.append(simplify_market(market))
137
+
138
+ matched.sort(key=lambda market: (market["volume"], market["liquidity"]), reverse=True)
139
+ return matched[:limit]
140
+
141
+
142
+ async def get_market(identifier: str) -> dict | None:
143
+ data = await api_get(
144
+ "/markets",
145
+ {
146
+ "active": "true",
147
+ "closed": "false",
148
+ "limit": 500,
149
+ },
150
+ )
151
+ for market in data:
152
+ if str(market.get("id")) == identifier or market.get("slug") == identifier:
153
+ return simplify_market(market)
154
+ return None
155
+
156
+
157
+ async def main_async():
158
+ parser = argparse.ArgumentParser(description="Polymarket discovery rail")
159
+ subparsers = parser.add_subparsers(dest="command", required=True)
160
+
161
+ scan_parser = subparsers.add_parser("scan")
162
+ scan_parser.add_argument("--limit", type=int, default=40)
163
+ scan_parser.add_argument("--min-volume", type=float, default=1000)
164
+ scan_parser.add_argument("--min-liquidity", type=float, default=1000)
165
+
166
+ search_parser = subparsers.add_parser("search")
167
+ search_parser.add_argument("query", type=str)
168
+ search_parser.add_argument("--limit", type=int, default=20)
169
+
170
+ market_parser = subparsers.add_parser("market")
171
+ market_parser.add_argument("identifier", type=str)
172
+
173
+ args = parser.parse_args()
174
+
175
+ if args.command == "scan":
176
+ result = await scan_markets(args.limit, args.min_volume, args.min_liquidity)
177
+ print(json.dumps(result, indent=2))
178
+ return
179
+
180
+ if args.command == "search":
181
+ result = await search_markets(args.query, args.limit)
182
+ print(json.dumps(result, indent=2))
183
+ return
184
+
185
+ result = await get_market(args.identifier)
186
+ if result is None:
187
+ log(f"No Polymarket market found for {args.identifier}")
188
+ print(json.dumps({"error": "market_not_found", "identifier": args.identifier}, indent=2))
189
+ return
190
+ print(json.dumps(result, indent=2))
191
+
192
+
193
+ if __name__ == "__main__":
194
+ import asyncio
195
+
196
+ asyncio.run(main_async())
package/main.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Open Trademaxxxing - agent orchestrator.
2
+ OpenTradex - agent orchestrator.
3
3
 
4
4
  Spawns Claude Code as a subprocess. The agent reads SOUL.md for personality,
5
5
  uses tools directly (web search, Apify, Kalshi API), and persists state to SQLite.
@@ -31,8 +31,77 @@ PROJECT_DIR = Path(__file__).resolve().parent
31
31
  DATA_DIR = PROJECT_DIR / "data"
32
32
  SESSION_FILE = DATA_DIR / "session_id.txt"
33
33
 
34
- # The agent prompt: agentic, not scripted. Agent uses its own tools.
35
- CYCLE_PROMPT = """Read SOUL.md for your identity and strategy principles.
34
+
35
+ def parse_csv_env(name: str, fallback: str = "") -> list[str]:
36
+ raw = os.getenv(name, fallback)
37
+ return [item.strip().lower() for item in raw.split(",") if item.strip()]
38
+
39
+
40
+ def build_runtime_context() -> str:
41
+ runtime = os.getenv("OPENTRADEX_RUNTIME", "claude-code")
42
+ primary_market = os.getenv("OPENTRADEX_PRIMARY_MARKET", "kalshi")
43
+ enabled_markets = parse_csv_env("OPENTRADEX_ENABLED_MARKETS", primary_market)
44
+ integrations = parse_csv_env("OPENTRADEX_ENABLED_INTEGRATIONS", "apify,rss")
45
+
46
+ lines = [
47
+ "Workspace profile:",
48
+ f"- Runtime profile: {runtime}",
49
+ f"- Primary market: {primary_market}",
50
+ f"- Enabled market rails: {', '.join(enabled_markets)}",
51
+ f"- Enabled data integrations: {', '.join(integrations)}",
52
+ "- Use only the rails enabled in this workspace.",
53
+ "- Live execution is currently routed through Kalshi only. Other rails are for discovery, research, and cross-market context unless explicitly extended.",
54
+ "",
55
+ "Available market/data rails for this workspace:",
56
+ ]
57
+
58
+ if "kalshi" in enabled_markets:
59
+ lines.extend(
60
+ [
61
+ "- Kalshi discovery and execution:",
62
+ " `PYTHONPATH=. python3 gossip/kalshi.py quick --limit 60`",
63
+ " `PYTHONPATH=. python3 gossip/kalshi.py search \"specific topic\"`",
64
+ " `PYTHONPATH=. python3 gossip/trader.py trade TICKER --side yes/no --estimate 0.XX --confidence high/medium --reasoning \"...\"`",
65
+ ]
66
+ )
67
+
68
+ if "polymarket" in enabled_markets:
69
+ lines.extend(
70
+ [
71
+ "- Polymarket discovery rail:",
72
+ " `PYTHONPATH=. python3 gossip/polymarket.py scan --limit 40`",
73
+ " `PYTHONPATH=. python3 gossip/polymarket.py search \"specific topic\"`",
74
+ " Use Polymarket for cross-market validation, sentiment, and mispricing comparisons.",
75
+ ]
76
+ )
77
+
78
+ if "tradingview" in enabled_markets:
79
+ lines.extend(
80
+ [
81
+ "- TradingView watchlist rail:",
82
+ " Read `TRADINGVIEW_WATCHLIST` from `.env` and use it to focus macro/equity/crypto research.",
83
+ " Treat this as a watchlist/context source unless a dedicated adapter is added.",
84
+ ]
85
+ )
86
+
87
+ if "robinhood" in enabled_markets:
88
+ lines.append("- Robinhood is enabled as a broker profile placeholder. Use it for planning and watchlist context unless you add a dedicated execution adapter.")
89
+
90
+ if "groww" in enabled_markets:
91
+ lines.append("- Groww is enabled as a broker profile placeholder. Use it for planning and watchlist context unless you add a dedicated execution adapter.")
92
+
93
+ if "apify" in integrations:
94
+ lines.append("- Apify-backed news scraping is available through `PYTHONPATH=. python3 gossip/news.py --keywords \"...\"`.")
95
+ if "rss" in integrations:
96
+ lines.append("- RSS/live news fallbacks are enabled through the web dashboard and news routes.")
97
+
98
+ return "\n".join(lines)
99
+
100
+
101
+ def build_cycle_prompt() -> str:
102
+ return f"""{build_runtime_context()}
103
+
104
+ Read SOUL.md for your identity and strategy principles.
36
105
  Read data/strategy_notes.md for lessons from past sessions.
37
106
  Check data/user_rationales.json for any pending user theses to research.
38
107
 
@@ -48,43 +117,45 @@ Then run a full trading cycle:
48
117
  Review each open position's unrealized P&L and current market price vs your entry.
49
118
 
50
119
  3. POSITION REVIEW — for each open position:
51
- - If current price moved significantly toward your thesis (e.g. YES at 95c+ when you entered at 82c), consider selling now to lock profit vs waiting for settlement.
52
- - If thesis has weakened or news contradicts it, EXIT: `PYTHONPATH=. python3 gossip/trader.py exit TICKER --reasoning "..."`
120
+ - If current price moved significantly toward your thesis, consider selling now to lock profit vs waiting for settlement.
121
+ - If thesis has weakened or news contradicts it, EXIT: `PYTHONPATH=. python3 gossip/trader.py exit TICKER --reasoning "..."`.
53
122
  - If thesis still holds and edge remains, HOLD.
54
123
  - Use web search to verify — don't just check prices, check if the underlying event happened.
55
124
 
56
125
  4. MARKET DISCOVERY — use targeted searches, not just broad scans:
57
- - `PYTHONPATH=. python3 gossip/kalshi.py quick --limit 60` for broad overview
58
- - `PYTHONPATH=. python3 gossip/kalshi.py search "specific topic"` for targeted lookups
59
- - Focus on categories where news creates edge: Politics, Economics, Macro events
60
- - Search for current events you already know about from news/web search
61
- - Skip sports, entertainment, and illiquid markets (volume < 500, spread > 15c)
126
+ - Use the enabled market rails above.
127
+ - Focus on categories where news creates edge: Politics, Economics, Macro events, liquid crypto and cross-market event contracts.
128
+ - Search for current events you already know about from news/web search.
129
+ - Skip thin or noisy setups unless the edge is exceptional.
62
130
 
63
131
  5. RESEARCH — pick 3-5 promising markets:
64
- - Use web search to find relevant news and primary sources
65
- - Use `PYTHONPATH=. python3 gossip/news.py --keywords "..."` for broader news scraping
66
- - Estimate the true probability based on evidence
67
- - Look for near-arbitrage: events that already happened but market hasn't caught up
132
+ - Use web search to find relevant news and primary sources.
133
+ - Use `PYTHONPATH=. python3 gossip/news.py --keywords "..."` for broader news scraping.
134
+ - Use Polymarket or watchlist rails for comparison when they are enabled.
135
+ - Estimate the true probability based on evidence.
136
+ - Look for near-arbitrage: events that already happened but markets have not caught up.
68
137
 
69
138
  6. TRADE if you find edge > 10pp with clear reasoning:
70
139
  Before executing, answer in one line: "Evidence type: [hard/soft/speculation]. Weakest assumption: [X]."
71
140
  If the evidence is speculation, PASS unless edge is overwhelming (>25pp).
72
- If you can't name what would make you wrong, your thesis isn't specific enough — PASS.
73
- `PYTHONPATH=. python3 gossip/trader.py trade TICKER --side yes/no --estimate 0.XX --confidence high/medium --reasoning "..."`
141
+ If you cannot name what would make you wrong, your thesis is not specific enough — PASS.
142
+ Only route live orders through supported execution rails.
74
143
 
75
144
  7. Update data/strategy_notes.md with what you learned this cycle.
76
145
 
77
146
  EXECUTION DISCIPLINE:
78
- - Be decisive. Research conclude act. Don't loop endlessly.
147
+ - Be decisive. Research -> conclude -> act. Do not loop endlessly.
79
148
  - For each market: reach a YES/NO/PASS decision within 2-3 tool calls.
80
- - Evaluate 3-5 markets per cycle, trade the best 1-2. Don't try to cover everything.
81
- - If you can't find edge after 5 minutes on a market, pass and move on.
149
+ - Evaluate 3-5 markets per cycle, trade the best 1-2. Do not try to cover everything.
150
+ - If you cannot find edge after 5 minutes on a market, pass and move on.
82
151
  - Write your conclusion even when you pass — future cycles benefit from it.
83
152
  """
84
153
 
85
154
 
86
155
  def build_rationale_prompt(rationale: str) -> str:
87
- return f"""Read SOUL.md for your identity and strategy principles.
156
+ return f"""{build_runtime_context()}
157
+
158
+ Read SOUL.md for your identity and strategy principles.
88
159
 
89
160
  A user has submitted this thesis for you to research and potentially trade on:
90
161
 
@@ -100,7 +171,7 @@ Your job:
100
171
  7. Update data/strategy_notes.md if you learned something new.
101
172
 
102
173
  Check portfolio first: `PYTHONPATH=. python3 gossip/trader.py portfolio`
103
- Scan markets: `PYTHONPATH=. python3 gossip/kalshi.py scan` or `PYTHONPATH=. python3 gossip/kalshi.py search "relevant keywords"`
174
+ Scan markets using the enabled rails above. Use Kalshi for execution and Polymarket/watchlist rails for comparison where available.
104
175
  """
105
176
 
106
177
 
@@ -115,6 +186,7 @@ def write_status(status: str, **extra) -> None:
115
186
 
116
187
  def run_agent(prompt: str, timeout: int = 600) -> dict:
117
188
  """Spawn Claude Code as a subprocess. Stream output to live log file."""
189
+ configured_runtime = os.getenv("OPENTRADEX_RUNTIME", "claude-code")
118
190
  cmd = [
119
191
  "claude",
120
192
  "--print", "-",
@@ -132,7 +204,7 @@ def run_agent(prompt: str, timeout: int = 600) -> dict:
132
204
  DATA_DIR.mkdir(parents=True, exist_ok=True)
133
205
  # Clear live log for this cycle
134
206
  LIVE_LOG.write_text("")
135
- write_status("running")
207
+ write_status("running", configured_runtime=configured_runtime, runner="claude-code")
136
208
 
137
209
  start = time.time()
138
210
  session_id = None
@@ -241,7 +313,7 @@ def _now() -> str:
241
313
 
242
314
 
243
315
  def main():
244
- parser = argparse.ArgumentParser(description="Open Trademaxxxing agent")
316
+ parser = argparse.ArgumentParser(description="OpenTradex agent")
245
317
  parser.add_argument("--loop", action="store_true", help="Run continuously")
246
318
  parser.add_argument("--interval", type=int, default=None, help="Cycle interval in seconds")
247
319
  parser.add_argument("--prompt", type=str, default=None, help="Custom prompt")
@@ -251,18 +323,24 @@ def main():
251
323
 
252
324
  args = parser.parse_args()
253
325
  interval = args.interval or int(os.getenv("CYCLE_INTERVAL", "900"))
326
+ configured_runtime = os.getenv("OPENTRADEX_RUNTIME", "claude-code")
254
327
 
255
328
  if args.rationale:
256
329
  submit_rationale(args.rationale)
257
330
  prompt = build_rationale_prompt(args.rationale)
258
331
  else:
259
- prompt = args.prompt or CYCLE_PROMPT
332
+ prompt = args.prompt or build_cycle_prompt()
260
333
 
261
334
  if args.dry_run:
262
335
  print(prompt)
263
336
  return
264
337
 
265
- print(f"[Open Trademaxxxing] Starting agent", file=sys.stderr)
338
+ print(f"[OpenTradex] Starting agent", file=sys.stderr)
339
+ if configured_runtime != "claude-code":
340
+ print(
341
+ f" Runtime profile: {configured_runtime} (executing with Claude Code runner)",
342
+ file=sys.stderr,
343
+ )
266
344
  print(f" Mode: {'loop (' + str(interval) + 's)' if args.loop else 'single cycle'}", file=sys.stderr)
267
345
 
268
346
  while True:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opentradex",
3
- "version": "0.1.0",
4
- "description": "OpenTradex CLI and onboarding flow for Open Trademaxxxing.",
3
+ "version": "0.1.3",
4
+ "description": "OpenTradex CLI, onboarding flow, and market-rail toolkit for AI-assisted trading workflows.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opentradex": "./bin/opentradex.mjs"
@@ -13,6 +13,7 @@
13
13
  "files": [
14
14
  "bin/",
15
15
  "src/",
16
+ "scripts/",
16
17
  ".env.example",
17
18
  "README.md",
18
19
  "CLAUDE.md",
@@ -30,7 +31,7 @@
30
31
  ],
31
32
  "scripts": {
32
33
  "opentradex": "node ./bin/opentradex.mjs",
33
- "test:cli": "node ./bin/opentradex.mjs --help && node ./bin/opentradex.mjs doctor --workspace ."
34
+ "test:cli": "node ./bin/opentradex.mjs --help && node ./bin/opentradex.mjs providers && node ./bin/opentradex.mjs doctor --workspace ."
34
35
  },
35
36
  "keywords": [
36
37
  "kalshi",
@@ -43,5 +44,8 @@
43
44
  "engines": {
44
45
  "node": ">=22.0.0"
45
46
  },
46
- "license": "UNLICENSED"
47
+ "license": "UNLICENSED",
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
47
51
  }
@@ -0,0 +1,25 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:OPENTRADEX_WORKSPACE) { $env:OPENTRADEX_WORKSPACE } else { Join-Path $HOME "opentradex" }
4
+ $packageManager = if ($env:OPENTRADEX_PACKAGE_MANAGER) { $env:OPENTRADEX_PACKAGE_MANAGER } else { "npm" }
5
+
6
+ Write-Host "Welcome to OpenTradex"
7
+ Write-Host "Our implementation. Your strategy."
8
+ Write-Host ""
9
+
10
+ if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
11
+ throw "Node.js 22+ is required before running this installer."
12
+ }
13
+
14
+ if ($packageManager -eq "bun" -and -not (Get-Command bun -ErrorAction SilentlyContinue)) {
15
+ Write-Host "Bun is not installed, so this installer is falling back to npm."
16
+ $packageManager = "npm"
17
+ }
18
+
19
+ if ($packageManager -eq "bun") {
20
+ bun add -g opentradex@latest
21
+ } else {
22
+ npm install -g opentradex@latest
23
+ }
24
+
25
+ opentradex onboard --workspace $workspace
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ WORKSPACE="${OPENTRADEX_WORKSPACE:-$HOME/opentradex}"
5
+ PACKAGE_MANAGER="${OPENTRADEX_PACKAGE_MANAGER:-npm}"
6
+
7
+ echo "Welcome to OpenTradex"
8
+ echo "Our implementation. Your strategy."
9
+ echo
10
+
11
+ if ! command -v node >/dev/null 2>&1; then
12
+ echo "Node.js is required. Install Node.js 22+ and rerun this installer."
13
+ exit 1
14
+ fi
15
+
16
+ if [[ "$PACKAGE_MANAGER" == "bun" ]]; then
17
+ if ! command -v bun >/dev/null 2>&1; then
18
+ echo "Bun is not installed, so this installer is falling back to npm."
19
+ PACKAGE_MANAGER="npm"
20
+ fi
21
+ fi
22
+
23
+ if [[ "$PACKAGE_MANAGER" == "bun" ]]; then
24
+ bun add -g opentradex@latest
25
+ else
26
+ npm install -g opentradex@latest
27
+ fi
28
+
29
+ opentradex onboard --workspace "$WORKSPACE"