Initial commit

This commit is contained in:
doge-8
2026-05-31 13:49:36 +08:00
commit cd988d9c3a
55 changed files with 27001 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
# BTC 5m User Edition
## Included Files
- `server.ts`
- `index.html`
- `package.json`
- `package-lock.json`
- `.env.example`
- `start.sh`
- `start.bat`
- `STRATEGIES-GUIDE.md`
## How to Use
1. Install Node.js 20 or higher.
2. Copy `.env.example` to `.env`.
3. Fill in:
- `POLYMARKET_PRIVATE_KEY` — Polygon wallet private key
- `POLYMARKET_PROXY_ADDRESS`**NOT the deposit address!** Log in to polymarket.com → top-right avatar → Settings → Wallet → copy "Proxy Wallet" (maps one-to-one with the private key; a wrong value triggers invalid signature)
4. Adjust as needed:
- `APP_MODE=full`: with the web panel
- `APP_MODE=headless`: backend only
- `STRATEGY_S1_ENABLED/STRATEGY_S2_ENABLED/STRATEGY_S3_ENABLED`
- `STRATEGY_S1_AMOUNT/STRATEGY_S2_AMOUNT/STRATEGY_S3_AMOUNT`
5. Start:
- macOS / Linux: `./start.sh`
- Windows: double-click `start.bat`
## Notes
- On every startup, the default strategy config is initialized from `.env`.
- Changing strategy switches and amounts in the web UI only applies to the current run; after restart it follows `.env` again.
- In `full` mode you can view status, place orders manually, and temporarily toggle strategies via the web UI.
- In `headless` mode you can view status via `/api/state`.
- `STRATEGIES-GUIDE.md` summarizes the triggers, take-profit/stop-loss, buy confirmation, and state-machine semantics of the current 3 strategies.
## Security
- Do not give your real `.env` and `.polymarket-creds.json` to anyone.
- If deploying to a cloud server, we recommend accessing the web panel via an SSH tunnel and not exposing the port directly to the public internet.
## Accessing the Panel via SSH Tunnel
If the service runs on a cloud server, we recommend using an SSH tunnel to access the panel from your local browser.
### 1. Server-Side Requirements
- Set `APP_MODE=full` in `.env`
- The service is already running normally
### 2. Password Login
Run on your own computer:
```bash
ssh -L 3456:127.0.0.1:3456 username@server_IP
```
For example:
```bash
ssh -L 3456:127.0.0.1:3456 root@1.2.3.4
```
Then open in your local browser:
```text
http://127.0.0.1:3456
```
### 3. Key-Based Login
If the server uses private-key login:
```bash
ssh -i ~/.ssh/your_private_key_file -L 3456:127.0.0.1:3456 username@server_IP
```
For example:
```bash
ssh -i ~/.ssh/my-server.pem -L 3456:127.0.0.1:3456 ubuntu@1.2.3.4
```
Then open in your local browser:
```text
http://127.0.0.1:3456
```
### 4. If the SSH Port Is Not 22
For example, if the SSH port is `2222`:
```bash
ssh -i ~/.ssh/my-server.pem -p 2222 -L 3456:127.0.0.1:3456 ubuntu@1.2.3.4
```
### 5. If Local Port 3456 Is Already in Use
You can change the local port to `8888`:
```bash
ssh -L 8888:127.0.0.1:3456 username@server_IP
```
Or:
```bash
ssh -i ~/.ssh/my-server.pem -L 8888:127.0.0.1:3456 ubuntu@1.2.3.4
```
Then open in the browser:
```text
http://127.0.0.1:8888
```
### 6. Notes
- As long as you can SSH into the server, you can access the panel this way.
- Closing the SSH tunnel only affects local viewing; it does not affect the program continuing to run on the server.
- If you only want to check the API status, you can also access it locally:
```bash
curl http://127.0.0.1:3456/api/state
```
+183
View File
@@ -0,0 +1,183 @@
# Strategy Guide
> **Version:** v4.2.0
> **Author:** Penguin Sensei · 岳 · [@x_188888_x](https://x.com/x_188888_x)
## ⚠ Important Disclaimer
The strategies built into this tool are **examples only**, intended to demonstrate how to use the strategy framework, and **cannot guarantee profits**.
The Polymarket BTC 5-minute market is highly volatile, and any strategy with fixed parameters carries the risk of becoming ineffective.
**Recommendations:**
- Run one or two windows with the smallest amount and observe whether the entry/exit logic matches your judgment
- Hover in the frontend to see each strategy's entry/exit conditions
- If you have good entry/exit ideas, new data patterns, or want to work on backtest optimization together, **feel free to contact the author and refine them jointly**,
to achieve a 1+1 > 2 effect
---
## Overview
There are currently 3 built-in example strategies (only the diff and momentum types are shown; the prob-chase type is not included as an example):
| Key | Name | Type | Summary |
|-----|------|------|------|
| D1 | Diff 1 · Standard Enhanced | Diff | diff cross entry + trailing stop + drawdown take-profit + stepped take-profit |
| D2 | Diff 2 · Tail Sweep | Diff | large-diff entry at the window tail + stepped take-profit |
| M1 | Momentum 1 | Momentum | 6-factor scoring entry, holds to window end and is decided by settlement |
Core principles:
- The authoritative state of automated strategies lives in the backend `server.ts`
- Buy confirmation relies on the local position `localSize` advanced by `UserWS`
- API positions are used only for reconciliation, releasing timed-out buy orders, and clearing residual positions after a sell
- Closing the frontend does not affect the backend strategy from continuing to run
---
## Common Terms
- **`diff`** — Binance latest price - (PriceToBeat - BinanceOffset); the core indicator for diff-strategy entry
- **`upPct / dnPct`** — the current up/down order book implied probability
- **`rem`** — seconds remaining in the current 5-minute window
- **`localSize`** — the local position advanced by UserWS; both buy confirmation and sell tracking rely on it
- **`apiVerified`** — the API and local positions are aligned
---
## Backend State Machine
- `IDLE` — no strategy is enabled
- `SCANNING` — scanning for entry conditions
- `BUYING` — buy triggered, order being sent
- `WAIT_FILL` — the first 10 seconds after the buy order is sent, only waiting for UserWS fill confirmation
- `RECONCILING_FILL` — not confirmed within 10 seconds, entering the deferred-confirmation state; after 15 seconds, only if the API also confirms no position does it return to `SCANNING`
- `HOLDING` — position confirmed, starting to run take-profit/stop-loss
- `SELLING` / `WAIT_SELL_FILL` — selling / waiting for sell confirmation
- `DONE` — round ended; when the position is not reconciled, it waits for API reconciliation before checking for residual positions
---
## Strategy D1 · Standard Enhanced (Diff Type)
### Entry Window
- Detected between `210s ~ 50s` remaining
### Entry Conditions
- **Buy up**: previous tick diff ≤ +35, current tick diff > +35, up probability < 80%
- **Buy down**: previous tick diff ≥ -35, current tick diff < -35, down probability < 80%
("Re-cross above/below triggers," not "buy whenever the current value is met")
### Cooldown Lock (Prevents Chasing Highs and Flip-Flopping)
**Neutral reset**: `|diff| ≤ 25` sustained for 3 seconds → release all cooldown locks
**Single-direction lock** (locks that direction if any is met, until returning to neutral):
- High-probability contamination seen first: while diff is within the trigger threshold, the up/down probability is already ≥ 80%
- Overheated: diff ≥ +55 and up probability ≥ 85% (buy-up direction) / diff ≤ -55 and down probability ≥ 85% (buy-down direction)
### Exit Mechanisms (Multiple)
1. **Stepped take-profit** — rises linearly from 90% at 210s to 100% at 10s; sells when the current probability reaches the threshold of the moment
2. **Drawdown take-profit** — after the probability peak during holding reaches ≥ 85%, sells once it pulls back 8 percentage points
3. **Trailing stop** — enabled after a minimum holding of 3 seconds; triggered when diff pulls back 20 points from its peak
4. **Backstop stop-loss** — buy-up diff ≤ +5 / buy-down diff ≥ -5, stop out immediately
5. **Forced close** — when rem ≤ 10s: take profit if probability ≥ 70%, otherwise stop out
---
## Strategy D2 · Tail Sweep (Diff Type)
### Entry Window
- Detected between `60s ~ 1s` remaining
### Entry Conditions
- **Buy up**: diff > +50 and up probability < 95%
- **Buy down**: diff < -50 and down probability < 95%
### Exit Mechanisms
**Stepped take-profit** (tightened in tiers by time remaining):
- `rem ≥ 40s`: probability ≥ 98%
- `20s ≤ rem < 40s`: probability ≥ 99%
- `10s ≤ rem < 20s`: probability ≥ 100%
- `rem < 10s`: hold to the end, decided by settlement
**Stop-loss**:
- Buy-up diff ≤ +5
- Buy-down diff ≥ -5
---
## Strategy M1 · Momentum (Momentum Type)
### Entry Window
- Detected when more than 60s remain (the final segment of the window does not participate in momentum evaluation)
### Entry Logic
Based on **6-factor momentum scoring** (see `strategies/_core/s6-core.ts` for details):
- RSI deviation
- Volume expansion
- 1-minute candle direction
- Price change magnitude
- Candle body ratio
- Number of consecutive same-color candles
- MA7 position
- (Auxiliary filter) MA120 long-term trend + 5-minute structure
An UP threshold triggers buy up, a DOWN threshold triggers buy down (the short threshold is stricter).
### Exit Mechanisms
**No take-profit, no stop-loss, no forced close**; holds to the window end and the win/loss is decided by Polymarket settlement.
This is a "pure settlement" style strategy example: verifying "whether the momentum direction judgment is accurate" rather than "agonizing over mid-window take-profit/stop-loss."
---
## Buy/Sell Confirmation and Residual-Position Handling
### Buy Confirmation Flow
1. Strategy triggers → `BUYING` sends order → `WAIT_FILL` waits for UserWS
2. Not confirmed within 10 seconds: enter `RECONCILING_FILL`, keep waiting for UserWS
3. After 15 seconds, only if the API also confirms no position is the buy order released and it returns to scanning
### Selling
- When selling an unaligned position, reserve a `0.05`-share buffer to avoid insufficient balance
- If residual positions remain after API alignment, clear them again
---
## Configuration Source
- On startup, strategy config is read from `.env` (`STRATEGY_{D1,D2,M1}_ENABLED`, etc.)
- Frontend changes only affect the current process and are not persisted across restarts
- After restart, `.env` still takes precedence
---
## Usage Recommendations
- We recommend running on the premise that "the account has no position in the current window at startup"
- If you need to view status remotely, prefer `APP_MODE=full` + an SSH tunnel
- The example strategy parameters are all empirical values under historical data; please backtest and verify them yourself before live trading
---
## Co-Development
Got a good strategy? Let's optimize it together! Contact the author: [@x_188888_x](https://x.com/x_188888_x)
+66
View File
@@ -0,0 +1,66 @@
Currently, after one strategy finishes executing, the subsequent strategies are not executed; optimize to place multiple orders
bestBid/bestAsk is sometimes fetched inaccurately; use the REST API to periodically calibrate the WS, fetching once every 5 seconds, and reconnect this WS after 3 consecutive readings exceed the threshold
Do a code review and optimize the code
Build a backend-only version for deployment on a cloud server
Crash bug to reproduce (observed that every time memory fills up, the Binance line lags on the time axis, but the data trend is real-time; only the overall line becomes laggy, and the yellow dots also drift toward the left of the x-axis; after switching the time window, memory is released again)
<--- Last few GCs --->
[57571:0x748400000] 855007 ms: Mark-Compact 3998.8 (4144.0) -> 3998.8 (4144.0) MB, pooled: 0 MB, 29.54 / 0.00 ms (average mu = 0.384, current mu = 0.086) allocation failure; scavenge might not succeed
[57571:0x748400000] 855051 ms: Mark-Compact 3999.0 (4144.2) -> 3998.9 (4144.2) MB, pooled: 0 MB, 40.33 / 0.00 ms (average mu = 0.242, current mu = 0.072) allocation failure; scavenge might not succeed
<--- JS stacktrace --->
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
1: 0x1006039c0 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
2: 0x1007dae90 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
3: 0x100a044cc v8::internal::Heap::stack() [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
4: 0x100a0272c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
5: 0x1009f83c0 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
6: 0x1009f8d8c v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
7: 0x1009cb68c v8::internal::FactoryBase<v8::internal::Factory>::NewRawOneByteString(int, v8::internal::AllocationType) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
8: 0x1009cb4c8 v8::internal::FactoryBase<v8::internal::Factory>::NewStringFromOneByte(v8::base::Vector<unsigned char const>, v8::internal::AllocationType) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
9: 0x100b0dee0 v8::internal::JsonStringifier::Stringify(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
10: 0x100b0dc50 v8::internal::JsonStringify(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
11: 0x100850114 v8::internal::Builtin_JsonStringify(int, unsigned long*, v8::internal::Isolate*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
12: 0x1014bb914 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
13: 0x10bfcf98c
14: 0x10bf3ea20
15: 0x10c082408
16: 0x10c0808cc
17: 0x10bfacaf4
18: 0x10c12b3d0
19: 0x10c0a9a44
20: 0x10bfd4390
21: 0x10c173f18
22: 0x10c0b03ec
23: 0x10bf8f344
24: 0x10c09eed0
25: 0x10bea3fc4
26: 0x10142250c Builtins_JSEntryTrampoline [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
27: 0x1014221b0 Builtins_JSEntry [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
28: 0x100957ebc v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
29: 0x10095781c v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
30: 0x1007f2930 v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
31: 0x100505cec node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context, v8::Local<v8::Value>) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
32: 0x10051b1cc node::AsyncWrap::MakeCallback(v8::Local<v8::Function>, int, v8::Local<v8::Value>*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
33: 0x1007324b0 node::StreamBase::CallJSOnreadMethod(long, v8::Local<v8::ArrayBuffer>, unsigned long, node::StreamBase::StreamBaseJSChecks) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
34: 0x100733c48 node::EmitToJSStreamListener::OnStreamRead(long, uv_buf_t const&) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
35: 0x1007b4c34 node::crypto::TLSWrap::ClearOut() [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
36: 0x1007b6b98 node::crypto::TLSWrap::OnStreamRead(long, uv_buf_t const&) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
37: 0x100738080 node::LibuvStreamWrap::OnUvRead(long, uv_buf_t const*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
38: 0x1007387c8 node::LibuvStreamWrap::ReadStart()::$_1::__invoke(uv_stream_s*, long, uv_buf_t const*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
39: 0x10140c8c4 uv__stream_io [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
40: 0x101414edc uv__io_poll [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
41: 0x101401850 uv_run [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
42: 0x100506508 node::SpinEventLoopInternal(node::Environment*) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
43: 0x10064d250 node::NodeMainInstance::Run() [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
44: 0x1005bf5f4 node::Start(int, char**) [/Users/yuepin/.nvm/versions/node/v23.11.0/bin/node]
45: 0x186b79d54 start [/usr/lib/dyld]
Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB