Initial commit
This commit is contained in:
@@ -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
|
||||
```
|
||||
@@ -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)
|
||||
@@ -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 |
Reference in New Issue
Block a user