Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a243edd2d3 | ||
|
|
206b27fe72 | ||
|
|
7f393b93ed | ||
|
|
5c10e35b76 | ||
|
|
8c165f11cd | ||
|
|
97e77847d0 | ||
|
|
90499533fb | ||
|
|
8ee11317d3 |
+8
-28
@@ -1,42 +1,22 @@
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
||||
# Python编译文件和缓存
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.pyc
|
||||
*.pyo
|
||||
.pytest_cache/
|
||||
|
||||
# 策略文件的缓存
|
||||
strategies/__pycache__/
|
||||
|
||||
# Machine Learning / AI model files
|
||||
*_model*_xgb_model.json
|
||||
*modelchan*.json
|
||||
*.libsvm
|
||||
feature_meta
|
||||
*_model_feature_data.csv
|
||||
*.pem
|
||||
|
||||
# Log files
|
||||
# Logs & databases
|
||||
*.log
|
||||
|
||||
# Database files
|
||||
*.sqlite
|
||||
*.sqlite-shm
|
||||
*.sqlite-wal
|
||||
.DS_Store
|
||||
交易记录/~$交易规则.docx
|
||||
/datasvc/data
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
/data_provider/data
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
data_provider/._config.json
|
||||
|
||||
# Local data
|
||||
data/
|
||||
|
||||
# Local tooling
|
||||
.gstack/
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# chan — Agent Entry
|
||||
|
||||
本仓受 ESS 约束。不要一上来扫全库或加载全部 governance。
|
||||
|
||||
## Boot
|
||||
|
||||
1. `docs/PROJECT_PROFILE.md`
|
||||
2. `docs/PROJECT_RULES.md`
|
||||
3. `docs/STATE/CURRENT.md` + `docs/AGENT_MEMORY.md`
|
||||
4. 有进行中任务再读 `docs/TASKS/` / 对应 ECR / HANDOFF
|
||||
5. 角色文件:ESS 根目录 `agents/{ARCHITECT|ENGINEER|REVIEWER|RELEASE_MANAGER}.md`
|
||||
|
||||
## Roles(选一)
|
||||
|
||||
| 意图 | 角色 |
|
||||
|------|------|
|
||||
| 规格 / 架构 / ECR | ARCHITECT |
|
||||
| 实现 / 修 bug | ENGINEER |
|
||||
| 审阅 | REVIEWER |
|
||||
| 发版 / tag | RELEASE_MANAGER |
|
||||
|
||||
## Never
|
||||
|
||||
- 无 ECR 改 `config/` / `strategies/` 交易逻辑
|
||||
- 无 ADR 改缠论算法语义
|
||||
- 无 ECR 删减 `/api/analyze` 字段
|
||||
- 把聊天记录当成完成;阶段结束须落盘 `docs/`
|
||||
|
||||
## Pointers
|
||||
|
||||
- TRACEABILITY: `docs/TRACEABILITY.md`
|
||||
- CHANGELOG: `docs/CHANGELOG/CHANGELOG.md`
|
||||
- 人类向导:`CLAUDE.md`
|
||||
@@ -1,116 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
缠论 (Chan Theory) technical analysis system for Freqtrade. Implements Chan Zhong Shui Chan's theory for crypto/stock trading, including fractal (分型), stroke (笔), segment (线段), pivot/center (中枢), and buy/sell point (买卖点) detection.
|
||||
|
||||
## Governance
|
||||
|
||||
- Agent 入口:`AGENTS.md`(boot 顺序)· `docs/PROJECT_PROFILE.md` · `docs/AGENT_MEMORY.md` · `docs/STATE/CURRENT.md`
|
||||
- ESS 文档:`docs/ECR/`、`docs/ENGINEERING_SPEC/`、`docs/TRACEABILITY.md`、`docs/CHANGELOG/`
|
||||
- **正式引擎包**:`chanlun/`;strategies / web 已用 `from chanlun import ...`
|
||||
- 根目录 `Chan*.py` / `TF_DF.py` 仍为 **兼容 shim**(旧脚本可用)
|
||||
- 变更分级:无 ECR 不改 strategies/config;无 ADR 不改缠论算法语义
|
||||
|
||||
## Core Architecture
|
||||
|
||||
### Chan Theory Engine (`chanlun/`)
|
||||
|
||||
```text
|
||||
chanlun/
|
||||
core/ # KLU KLC BI SBI SEG ZS BIZS BSP Enum CTime
|
||||
pipeline/ # orchestrator(ChanLun) + timeframe(TF_DF) + builders/
|
||||
indicators/ # ChanMACD*
|
||||
analysis/ # Zone Classifier Pivot Heng PY Find_Trend ...
|
||||
```
|
||||
|
||||
Data processing pipeline (each step feeds the next):
|
||||
|
||||
1. **`chanlun.core.ChanKLU`** — Raw K-line unit with TA indicators and pattern recognition
|
||||
2. **`chanlun.core.ChanKLC`** — Combined K-line: inclusion + fractal; `.next`/`.pre` linked list
|
||||
3. **`chanlun.core.ChanBI`** — Stroke (笔)
|
||||
4. **`chanlun.core.ChanSBI`** — Special stroke → SEG
|
||||
5. **`chanlun.core.ChanSEG`** — Segment (线段)
|
||||
6. **`chanlun.core.ChanZS`** / **`ChanBIZS`** — Centers (中枢)
|
||||
7. **`chanlun.core.ChanBSP`** — Buy/Sell points
|
||||
8. **`chanlun.pipeline.orchestrator.ChanLun`** — Orchestrator
|
||||
9. **`chanlun.pipeline.timeframe.TF_DF`** — Timeframe facade;实现拆在 `pipeline/builders/`
|
||||
|
||||
### Services
|
||||
|
||||
- **外部 DATA_SERVICE** — 行情服务(env: `DATA_SERVICE_URL`);本仓库可不含 data_provider 源码
|
||||
- **`web/`** — Flask UI:`create_app()` + `api/` blueprints + `services/`;前端 `static/js/app/`。默认端口见 `web/config.py`(`FLASK_PORT`,常见 8128)
|
||||
- **`strategies/`** — Freqtrade strategies(本 ECR 不改)
|
||||
- **`config/`** — Freqtrade configs(本 ECR 不改)
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
Exchange / DATA_SERVICE → Freqtrade Strategy / web → ChanLun → TF_DF
|
||||
→ KLU → KLC → BI → SBI → SEG → ZS → BSP
|
||||
```
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Freqtrade Trading
|
||||
|
||||
```bash
|
||||
# Live trade
|
||||
freqtrade trade -c ./user_data/Chan/config/<config>.json --strategy <StrategyName> --strategy-path ./user_data/Chan/strategies
|
||||
|
||||
# Backtest
|
||||
freqtrade backtesting -c ./user_data/Chan/config/<config>.json --strategy <StrategyName> --strategy-path ./user_data/Chan/strategies --timerange=20251008-
|
||||
|
||||
# Download data
|
||||
freqtrade download-data -c ./user_data/Chan/config/<config>.json -t 1m 1h 1d --pairs BTC/USDT:USDT --timerange=20240101-
|
||||
|
||||
# Hyperopt
|
||||
freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy <StrategyName> --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/<config>.json -e 200 --timerange=20250201-20250901
|
||||
|
||||
# Plot
|
||||
freqtrade plot-dataframe --strategy <StrategyName> --datadir user_data/data/binance -c ./user_data/Chan/config/<config>.json --timerange=20250721-
|
||||
```
|
||||
|
||||
### Data Provider
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
cd data_provider && docker compose up -d
|
||||
|
||||
# Direct
|
||||
cd data_provider && python main.py
|
||||
|
||||
# With custom config
|
||||
CONFIG_PATH=./config.json python main.py
|
||||
```
|
||||
|
||||
### Web UI
|
||||
|
||||
```bash
|
||||
cd web && python app.py
|
||||
# or via gunicorn:
|
||||
gunicorn -w 4 -b 0.0.0.0:8123 app:app
|
||||
|
||||
# Deploy scripts:
|
||||
cd web && ./deploy.sh # standard
|
||||
cd web && ./deploy_venv.sh # Ubuntu 22.04+ (venv)
|
||||
```
|
||||
|
||||
### Docker (Freqtrade)
|
||||
|
||||
```bash
|
||||
sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/<config>.json --strategy <StrategyName> --strategy-path ./user_data/Chan/strategies --timerange=20250721-
|
||||
```
|
||||
|
||||
## Key Conventions
|
||||
|
||||
- All Chan theory classes are prefixed with `Chan` (e.g., `ChanBI`, `ChanZS`)
|
||||
- Strategies import `ChanLun` and add `sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))` to import from parent
|
||||
- MACD params: `MACD(26, 52, 9)` by default (slow period 52 instead of standard 26)
|
||||
- Enums in `ChanEnum.py` use `auto()` values
|
||||
- `ChanKLC` is a linked-list style data structure with `.next`/`.pre` pointers
|
||||
- The `TF_DF` class is the primary data container per timeframe
|
||||
- K-line direction uses `Chan_KLINE_DIR` (UP/DOWN/COMBINE/INCLUDED)
|
||||
- All text comments/commits are in Chinese
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanBI import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanBIZS import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanBSP import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanCTime import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanEnum import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.ChanHeng import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanKLC import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanKLU import * # noqa: F403
|
||||
@@ -1,3 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.pipeline.orchestrator import ChanLun # noqa: F401
|
||||
from chanlun.pipeline.timeframe import TF_DF # noqa: F401
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.ChanLun_Classifier import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.indicators.ChanMACD import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.indicators.ChanMACDHistSet import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.indicators.ChanMACDSeg import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.indicators.ChanMACDUnitTF import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.ChanPY import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.ChanPivotClassifier import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.ChanPivotMonitor import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanSBI import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanSEG import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.ChanZS import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.ChanZone import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.core.Chan_FX_Box import * # noqa: F403
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.analysis.Find_Trend import * # noqa: F403
|
||||
@@ -0,0 +1,130 @@
|
||||
# Chan — 缠论分析引擎
|
||||
|
||||
把 OHLCV K 线拆成缠论结构(K 线单元 → 合并 K 线 → 分型 → 笔 → 线段 → 中枢 → 买卖点),
|
||||
配一个 TradingView Charting Library 的 Web 界面,外加一套验证信号有效性的回测脚本。
|
||||
|
||||
标的不限:加密永续(ccxt)与 A 股(akshare)都走同一条分析链路。
|
||||
|
||||
```
|
||||
chanlun/ 缠论引擎,纯 pandas/numpy,无外部指标库依赖
|
||||
web/ Flask API + 图表界面
|
||||
research/ 信号有效性验证脚本(step1 ~ step30)
|
||||
data/ 本地 K 线(freqtrade 的 feather 格式)
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
需要 Python ≥ 3.11(pandas 3.x / numpy 2.x 的要求,不是本项目代码的限制)。
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
.venv/bin/pip install -r requirements.txt # 核心运行时,8 个包
|
||||
.venv/bin/pip install -r requirements-dev.txt # 另加测试与 research/ 所需
|
||||
```
|
||||
|
||||
## 跑 Web
|
||||
|
||||
```bash
|
||||
cd web
|
||||
../.venv/bin/python app.py # 默认 http://0.0.0.0:8128
|
||||
```
|
||||
|
||||
从仓库根跑 `.venv/bin/python web/app.py` 也可以——Python 会把脚本所在目录放进 `sys.path`。
|
||||
但**不能用 `python -m web.app`**,也不能 `import web.app`:`web/` 内部是无前缀导入
|
||||
(`import config`、`from api.analyze import bp`),`-m` 方式下 `sys.path` 里是仓库根而不是
|
||||
`web/`,会 `ModuleNotFoundError: No module named 'config'`。
|
||||
|
||||
配置全部走环境变量,见 `web/config.py`:
|
||||
|
||||
| 变量 | 默认值 | 用途 |
|
||||
|------|--------|------|
|
||||
| `FLASK_HOST` / `FLASK_PORT` | `0.0.0.0` / `8128` | 监听地址 |
|
||||
| `DATA_SERVICE_URL` | `https://provider.jackyu66.com` | 行情 REST 源 |
|
||||
| `DATA_SERVICE_WS_URL` | `wss://jackyu66.com/ws` | 行情 WebSocket 源 |
|
||||
| `ASHARE_DP_URL` | `http://103.179.242.166:8000` | A 股数据源 |
|
||||
| `CHAN_HTTP_PROXY` | 未设置则不走代理 | ccxt / HTTP 代理 |
|
||||
| `MACD_FACTOR` / `MACD_SMOOTH` | `1` / `1` | MACD 周期倍数,默认 12/26/9 |
|
||||
|
||||
主要接口:`GET /api/analyze` 返回某标的某周期的完整缠论结构,`/api/klines/recent`
|
||||
取最新 K 线,`/api/trend_filter` 与 `/api/trend_detail` 做多周期趋势筛选,
|
||||
`/api/symbols`、`/api/search_stock`、`/api/sectors` 等负责标的检索。页面在 `/` 与 `/chan_tv`。
|
||||
|
||||
## 作为库使用
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
from chanlun import TF_DF
|
||||
|
||||
# 需要 date/open/high/low/close/volume 六列,date 为 datetime
|
||||
df = pd.read_feather("data/binance/futures/BTC_USDT_USDT-1h-futures.feather")
|
||||
|
||||
tf = TF_DF(df, interval=1, timeframe="1h")
|
||||
|
||||
len(tf.klu_list) # K 线单元
|
||||
len(tf.klc_list) # 合并 K 线(处理包含关系后)
|
||||
len(tf.bi_list) # 笔
|
||||
len(tf.seg_list) # 线段
|
||||
len(tf.zs_list) # 中枢
|
||||
tf.bi_list[-1].dir # Chan_BI_DIR.DOWN
|
||||
tf.chanmacd # MACD 结构分析(背驰判定用)
|
||||
```
|
||||
|
||||
**`interval` 的单位是分钟**,对传入的 df 做重采样;`interval=1` 是特例,表示原样使用、
|
||||
不重采样。所以拿 1h 的 feather 要传 `interval=1`,拿 1m 数据想看 1h 才传 `interval=60`:
|
||||
|
||||
```python
|
||||
df1m = pd.read_feather("data/binance/futures/BTC_USDT_USDT-1m-futures.feather")
|
||||
TF_DF(df1m, interval=5, timeframe="5m")
|
||||
TF_DF(df1m, interval=60, timeframe="1h")
|
||||
```
|
||||
|
||||
传错不会报错,只会静默给出错误周期的结构——1h 数据配 `interval=4` 相当于按 4 分钟
|
||||
重采样,结果与 `interval=1` 完全相同。
|
||||
|
||||
## 分析流程
|
||||
|
||||
`TF_DF.init_TF_DF()` 按顺序做这几步,每步的实现在 `chanlun/pipeline/builders/` 下同名文件:
|
||||
|
||||
1. `resample_to_interval` — 重采样(`interval != 1` 时)
|
||||
2. `add_indicators` — 追加 33 列指标(MACD / BBANDS / EMA / RSI / ATR 等)
|
||||
3. `cal_kl_data` → `klu_list` — K 线单元
|
||||
4. `get_klc_list` → `klc_list` — 按包含关系合并 K 线,并标记分型
|
||||
5. `cal_bi_list` → `bi_list` — 笔
|
||||
6. `cal_bi_zs_list_pure` → `bi_zs_list` — 笔中枢
|
||||
7. `get_seg_list` → `seg_list` — 线段
|
||||
8. `get_zs_list` / `get_big_zs_list` — 中枢与大级别中枢
|
||||
9. `ChanMACD(klu_list)` — MACD 段 / 柱堆结构,供背驰判定
|
||||
|
||||
## 数据
|
||||
|
||||
`data/<交易所>/futures/<SYMBOL>-<周期>-futures.feather`,即 freqtrade 的下载格式,
|
||||
如 `data/binance/futures/BTC_USDT_USDT-1h-futures.feather`。
|
||||
`research/lib/data.py` 负责定位:`BTC/USDT:USDT` + `1h` 会解析到上面这个路径,
|
||||
找不到本地文件则回落到远端拉取。
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest chanlun/tests web/tests -q
|
||||
```
|
||||
|
||||
`chanlun/tests/test_ta_compat.py` 有个**需要注意的陷阱**:它把 `chanlun/indicators/ta.py`
|
||||
的输出逐 bar 钉在 TA-Lib 上,但 **TA-Lib 不存在时会静默跳过**。也就是说改了 `ta.py`
|
||||
之后在没装 TA-Lib 的环境里跑,测试会显示通过,其实一项都没验证。改动那个文件时请先装:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y libta-lib0 ta-lib-dev
|
||||
.venv/bin/pip install TA-Lib technical
|
||||
```
|
||||
|
||||
## 已知问题
|
||||
|
||||
- **`web/DEPLOY_GUIDE.md` 已失效**:它引用的 `deploy_venv.sh`、`stop_venv.sh`、
|
||||
`status_venv.sh` 等 6 个脚本都在 `7f393b9` 精简提交里删掉了,目前没有部署脚本。
|
||||
两个 systemd unit 文件(`web/chanlun-web*.service`)仍可参考,但它们用 gunicorn
|
||||
且写死端口 8123,与 `config.py` 默认的 8128 不一致,gunicorn 也不在依赖清单里。
|
||||
- **`web/README.txt` 已过时**:它说的 `web/requirements.txt` 不存在,依赖清单在仓库根目录。
|
||||
- **四个零引用的死文件**:`chanlun/analysis/` 下的 `ChanPY.py`、`ChanLun_Classifier.py`、
|
||||
`Find_Trend.py`、`ChanHeng.py` 全项目无人引用。`ChanPY.py` 依赖未安装的外部 chan.py 库,
|
||||
另外三个需要 matplotlib / mplfinance / xgboost / scikit-learn——这些都**不在**依赖清单里,
|
||||
是有意为之。要用得自行安装。
|
||||
@@ -1,2 +0,0 @@
|
||||
"""兼容 shim — 请优先 from chanlun import ..."""
|
||||
from chanlun.pipeline.timeframe import TF_DF # noqa: F401
|
||||
@@ -14,12 +14,10 @@ from chanlun.core.ChanSBI import ChanSBI
|
||||
from chanlun.core.ChanSEG import ChanSEG
|
||||
from chanlun.core.ChanZS import ChanZS
|
||||
from chanlun.core.ChanBSP import ChanBSP
|
||||
import talib.abstract as ta
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.dates import DateFormatter, date2num
|
||||
import matplotlib.patches as patches
|
||||
from technical.util import resample_to_interval
|
||||
from decimal import Decimal
|
||||
from chanlun.pipeline.orchestrator import ChanLun
|
||||
import xgboost as xgb
|
||||
|
||||
@@ -2,7 +2,7 @@ import ccxt
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import mplfinance as mpf
|
||||
from talib import MACD, SMA
|
||||
from chanlun.indicators import ta
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import datetime as dt
|
||||
@@ -249,8 +249,9 @@ def analyze_higher_timeframe(df_30m):
|
||||
# 8. Back-divergence detection (enhanced)
|
||||
def detect_back_divergence(df, strokes, higher_trend):
|
||||
try:
|
||||
macd, signal, hist = MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9)
|
||||
sma20 = SMA(df['Close'], timeperiod=20)
|
||||
macd_df = ta.MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9)
|
||||
macd, hist = macd_df['macd'], macd_df['macdhist']
|
||||
sma20 = ta.SMA(df['Close'], timeperiod=20)
|
||||
df['macd'] = macd
|
||||
df['hist'] = hist
|
||||
df['sma20'] = sma20
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
"""威科夫分析(启发式):交易区间 / 阶段 / 事件 / Volume Profile。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .engine import analyze_wyckoff
|
||||
|
||||
__all__ = ["analyze_wyckoff"]
|
||||
@@ -1,80 +0,0 @@
|
||||
"""威科夫分析入口。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from .events import build_phases, detect_bias_and_events
|
||||
from .range import detect_trading_range
|
||||
from .volume_profile import compute_volume_profile
|
||||
|
||||
|
||||
def _fmt_time(v) -> Optional[str]:
|
||||
if v is None:
|
||||
return None
|
||||
if hasattr(v, "isoformat"):
|
||||
try:
|
||||
return v.isoformat()
|
||||
except Exception:
|
||||
pass
|
||||
return str(v)
|
||||
|
||||
|
||||
def analyze_wyckoff(df: pd.DataFrame, lookback: int = 120, vp_bins: int = 50) -> Dict[str, Any]:
|
||||
"""
|
||||
对主周期 OHLCV DataFrame 做威科夫启发式分析。
|
||||
需要列: open, high, low, close, volume;建议有 date 或 timestamp。
|
||||
"""
|
||||
empty = {
|
||||
"trading_range": None,
|
||||
"bias": "unknown",
|
||||
"phases": [],
|
||||
"events": [],
|
||||
"volume_profile": {"bins": [], "poc": None, "vah": None, "val": None, "bin_count": vp_bins},
|
||||
"volume_confirm": {"avg_volume": 0.0, "event_checks": {}},
|
||||
}
|
||||
if df is None or len(df) < 30:
|
||||
return empty
|
||||
if not all(c in df.columns for c in ("open", "high", "low", "close")):
|
||||
return empty
|
||||
work = df.copy()
|
||||
if "volume" not in work.columns:
|
||||
work["volume"] = 1.0
|
||||
|
||||
tr = detect_trading_range(work, lookback=lookback)
|
||||
if tr is None:
|
||||
return empty
|
||||
|
||||
bias, events, volume_confirm = detect_bias_and_events(work, tr)
|
||||
phases = build_phases(work, tr, bias, events)
|
||||
vp = compute_volume_profile(
|
||||
work,
|
||||
int(tr["abs_start_idx"]),
|
||||
int(tr["abs_end_idx"]),
|
||||
bin_count=vp_bins,
|
||||
)
|
||||
|
||||
trading_range = {
|
||||
"start_time": _fmt_time(tr.get("start_time")),
|
||||
"end_time": _fmt_time(tr.get("end_time")),
|
||||
"high": float(tr["high"]),
|
||||
"low": float(tr["low"]),
|
||||
"mid": float(tr["mid"]),
|
||||
"active": bool(tr.get("active", True)),
|
||||
"bars": int(tr.get("bars", 0)),
|
||||
}
|
||||
for ev in events:
|
||||
ev["time"] = _fmt_time(ev.get("time"))
|
||||
for ph in phases:
|
||||
ph["start_time"] = _fmt_time(ph.get("start_time"))
|
||||
ph["end_time"] = _fmt_time(ph.get("end_time"))
|
||||
|
||||
return {
|
||||
"trading_range": trading_range,
|
||||
"bias": bias,
|
||||
"phases": phases,
|
||||
"events": events,
|
||||
"volume_profile": vp,
|
||||
"volume_confirm": volume_confirm,
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
"""威科夫阶段与事件(启发式)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def _bar_time(df: pd.DataFrame, i: int):
|
||||
row = df.iloc[i]
|
||||
if "date" in df.columns and pd.notna(row["date"]):
|
||||
return row["date"]
|
||||
if "timestamp" in df.columns:
|
||||
return row["timestamp"]
|
||||
return i
|
||||
|
||||
|
||||
def _avg_vol(df: pd.DataFrame, i: int, win: int = 20) -> float:
|
||||
a = max(0, i - win + 1)
|
||||
v = df["volume"].astype(float).iloc[a : i + 1]
|
||||
m = float(v.mean()) if len(v) else 0.0
|
||||
return m if m > 0 else 1.0
|
||||
|
||||
|
||||
def detect_bias_and_events(
|
||||
df: pd.DataFrame,
|
||||
tr: Dict[str, Any],
|
||||
) -> Tuple[str, List[Dict[str, Any]], Dict[str, Any]]:
|
||||
"""
|
||||
返回 bias、events、volume_confirm。
|
||||
"""
|
||||
hi = float(tr["high"])
|
||||
lo = float(tr["low"])
|
||||
mid = float(tr["mid"])
|
||||
tol = float(tr.get("tol") or (hi - lo) * 0.05)
|
||||
s = int(tr["abs_start_idx"])
|
||||
e = int(tr["abs_end_idx"])
|
||||
events: List[Dict[str, Any]] = []
|
||||
|
||||
# 扫描区间内及之后(含 tail_reserve)
|
||||
scan_end = int(tr.get("abs_scan_end_idx", min(len(df) - 1, e + 15)))
|
||||
scan_end = min(len(df) - 1, max(scan_end, e))
|
||||
spring = None
|
||||
utad = None
|
||||
sos = None
|
||||
sod = None # sign of weakness / distribution breakdown
|
||||
lps = None
|
||||
lpsy = None
|
||||
|
||||
for i in range(s + 2, scan_end + 1):
|
||||
row = df.iloc[i]
|
||||
low = float(row["low"])
|
||||
high = float(row["high"])
|
||||
close = float(row["close"])
|
||||
vol = float(row["volume"]) if "volume" in df.columns else 0.0
|
||||
avg_v = _avg_vol(df, i)
|
||||
ratio = vol / avg_v if avg_v else 0.0
|
||||
|
||||
# Spring: pierce below low then close back above low
|
||||
if spring is None and low < lo - tol * 0.5 and close >= lo - tol * 0.2:
|
||||
vol_ok = ratio <= 1.35 or (i + 1 <= scan_end and float(df.iloc[min(i + 1, scan_end)]["volume"]) / avg_v < 1.2)
|
||||
spring = {
|
||||
"type": "Spring",
|
||||
"time": _bar_time(df, i),
|
||||
"price": low,
|
||||
"note": "假破下沿后收回",
|
||||
"volume_ratio": round(ratio, 3),
|
||||
"volume_ok": bool(vol_ok),
|
||||
"idx": i,
|
||||
}
|
||||
|
||||
# UTAD: pierce above high then close back below
|
||||
if utad is None and high > hi + tol * 0.5 and close <= hi + tol * 0.2:
|
||||
vol_ok = ratio >= 0.8
|
||||
utad = {
|
||||
"type": "UTAD",
|
||||
"time": _bar_time(df, i),
|
||||
"price": high,
|
||||
"note": "假破上沿后跌回",
|
||||
"volume_ratio": round(ratio, 3),
|
||||
"volume_ok": bool(vol_ok),
|
||||
"idx": i,
|
||||
}
|
||||
|
||||
# SOS: close above high with volume
|
||||
if sos is None and close > hi + tol * 0.15:
|
||||
vol_ok = ratio >= 1.15
|
||||
sos = {
|
||||
"type": "SOS",
|
||||
"time": _bar_time(df, i),
|
||||
"price": close,
|
||||
"note": "放量上破交易区间",
|
||||
"volume_ratio": round(ratio, 3),
|
||||
"volume_ok": bool(vol_ok),
|
||||
"idx": i,
|
||||
}
|
||||
|
||||
# SOW / breakdown
|
||||
if sod is None and close < lo - tol * 0.15:
|
||||
vol_ok = ratio >= 1.15
|
||||
sod = {
|
||||
"type": "SOW",
|
||||
"time": _bar_time(df, i),
|
||||
"price": close,
|
||||
"note": "放量下破交易区间",
|
||||
"volume_ratio": round(ratio, 3),
|
||||
"volume_ok": bool(vol_ok),
|
||||
"idx": i,
|
||||
}
|
||||
|
||||
# LPS after SOS: pullback that holds above mid/high-band with lighter volume
|
||||
if sos is not None:
|
||||
si = int(sos["idx"])
|
||||
for i in range(si + 1, min(len(df), si + 25)):
|
||||
row = df.iloc[i]
|
||||
low = float(row["low"])
|
||||
close = float(row["close"])
|
||||
vol = float(row["volume"]) if "volume" in df.columns else 0.0
|
||||
avg_v = _avg_vol(df, i)
|
||||
ratio = vol / avg_v if avg_v else 0.0
|
||||
if low >= mid - tol and close >= hi - tol * 2:
|
||||
vol_ok = ratio <= 1.05
|
||||
lps = {
|
||||
"type": "LPS",
|
||||
"time": _bar_time(df, i),
|
||||
"price": low,
|
||||
"note": "突破后缩量回踩不破",
|
||||
"volume_ratio": round(ratio, 3),
|
||||
"volume_ok": bool(vol_ok),
|
||||
"idx": i,
|
||||
}
|
||||
break
|
||||
|
||||
if sod is not None:
|
||||
si = int(sod["idx"])
|
||||
for i in range(si + 1, min(len(df), si + 25)):
|
||||
row = df.iloc[i]
|
||||
high = float(row["high"])
|
||||
close = float(row["close"])
|
||||
vol = float(row["volume"]) if "volume" in df.columns else 0.0
|
||||
avg_v = _avg_vol(df, i)
|
||||
ratio = vol / avg_v if avg_v else 0.0
|
||||
if high <= mid + tol and close <= lo + tol * 2:
|
||||
vol_ok = ratio <= 1.05
|
||||
lpsy = {
|
||||
"type": "LPSY",
|
||||
"time": _bar_time(df, i),
|
||||
"price": high,
|
||||
"note": "下跌突破后缩量反抽不过",
|
||||
"volume_ratio": round(ratio, 3),
|
||||
"volume_ok": bool(vol_ok),
|
||||
"idx": i,
|
||||
}
|
||||
break
|
||||
|
||||
for ev in (spring, sos, lps, utad, sod, lpsy):
|
||||
if ev:
|
||||
events.append({k: v for k, v in ev.items() if k != "idx"})
|
||||
|
||||
# bias
|
||||
last_c = float(df["close"].iloc[-1])
|
||||
bias = "unknown"
|
||||
if sos and (not sod or int(sos.get("idx", 0)) >= int(sod.get("idx", 0))):
|
||||
bias = "accumulation"
|
||||
elif sod and (not sos or int(sod.get("idx", 0)) > int(sos.get("idx", 0))):
|
||||
bias = "distribution"
|
||||
elif spring and not utad:
|
||||
bias = "accumulation"
|
||||
elif utad and not spring:
|
||||
bias = "distribution"
|
||||
elif last_c >= mid:
|
||||
bias = "accumulation"
|
||||
else:
|
||||
bias = "distribution"
|
||||
|
||||
avg_volume = float(df["volume"].astype(float).iloc[max(0, e - 20) : e + 1].mean()) if "volume" in df.columns else 0.0
|
||||
volume_confirm = {
|
||||
"avg_volume": avg_volume,
|
||||
"event_checks": {ev["type"]: {"volume_ok": ev.get("volume_ok"), "volume_ratio": ev.get("volume_ratio")} for ev in events},
|
||||
}
|
||||
return bias, events, volume_confirm
|
||||
|
||||
|
||||
def build_phases(
|
||||
df: pd.DataFrame,
|
||||
tr: Dict[str, Any],
|
||||
bias: str,
|
||||
events: List[Dict[str, Any]],
|
||||
min_bars: int = 3,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""按时间切分 A–E 粗阶段;保证非重叠且每段至少 min_bars 根(空间不足则截断尾部阶段)。"""
|
||||
s = int(tr["abs_start_idx"])
|
||||
e = int(tr["abs_end_idx"])
|
||||
n_last = len(df) - 1
|
||||
min_span = max(2, min_bars - 1)
|
||||
|
||||
event_idx = {}
|
||||
for ev in events:
|
||||
t = ev.get("time")
|
||||
for i in range(s, min(len(df), e + 20)):
|
||||
if _bar_time(df, i) == t:
|
||||
event_idx[ev["type"]] = i
|
||||
break
|
||||
|
||||
a_end = s + max(min_bars, (e - s) // 5)
|
||||
c_anchor = event_idx.get("Spring") or event_idx.get("UTAD") or (s + (e - s) // 2)
|
||||
d_anchor = event_idx.get("SOS") or event_idx.get("SOW") or e
|
||||
|
||||
def _lab(phase: str) -> str:
|
||||
if bias == "distribution":
|
||||
m = {"A": "A停止上涨", "B": "B筑顶", "C": "C测试", "D": "D派发", "E": "E下跌"}
|
||||
else:
|
||||
m = {"A": "A停止下跌", "B": "B筑底", "C": "C测试", "D": "D拉升", "E": "E离开"}
|
||||
return m.get(phase, phase)
|
||||
|
||||
# 理想切点(随后再强制非重叠 + 最小跨度)
|
||||
raw = [
|
||||
("A", s, a_end),
|
||||
("B", a_end, c_anchor),
|
||||
("C", c_anchor, d_anchor),
|
||||
("D", d_anchor, min(n_last, d_anchor + max(min_bars, (e - s) // 6))),
|
||||
("E", min(n_last, d_anchor + max(min_bars, (e - s) // 6)), min(n_last, max(e, d_anchor + max(min_bars * 2, 8)))),
|
||||
]
|
||||
phases: List[Dict[str, Any]] = []
|
||||
cursor = s
|
||||
for phase, _a, _b in raw:
|
||||
if cursor >= n_last:
|
||||
break
|
||||
a = max(int(_a), cursor)
|
||||
b = int(max(_b, a + min_span))
|
||||
b = int(np.clip(b, a, n_last))
|
||||
if b - a < min_span:
|
||||
# 尾部空间不足:并入上一段终点并停止新增
|
||||
if phases:
|
||||
phases[-1]["end_time"] = _bar_time(df, n_last)
|
||||
break
|
||||
phases.append(
|
||||
{
|
||||
"phase": phase,
|
||||
"label": _lab(phase),
|
||||
"start_time": _bar_time(df, a),
|
||||
"end_time": _bar_time(df, b),
|
||||
}
|
||||
)
|
||||
cursor = b
|
||||
return phases
|
||||
@@ -1,128 +0,0 @@
|
||||
"""交易区间检测:ATR 容差下按评分选取近期震荡箱。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def _atr(df: pd.DataFrame, period: int = 14) -> pd.Series:
|
||||
high = df["high"].astype(float)
|
||||
low = df["low"].astype(float)
|
||||
close = df["close"].astype(float)
|
||||
prev_close = close.shift(1)
|
||||
tr = pd.concat(
|
||||
[
|
||||
(high - low).abs(),
|
||||
(high - prev_close).abs(),
|
||||
(low - prev_close).abs(),
|
||||
],
|
||||
axis=1,
|
||||
).max(axis=1)
|
||||
return tr.rolling(period, min_periods=max(3, period // 2)).mean()
|
||||
|
||||
|
||||
def _score_segment(
|
||||
length: int,
|
||||
near_hi: int,
|
||||
near_lo: int,
|
||||
inside: float,
|
||||
width: float,
|
||||
atr: float,
|
||||
) -> float:
|
||||
"""触边密度 + 箱内比例 − 相对宽度;弱奖励长度以免只追最长。"""
|
||||
touch_density = (near_hi + near_lo) / float(max(length, 1))
|
||||
width_pen = (width / atr) if atr > 0 else width
|
||||
return touch_density * 50.0 + float(inside) * 30.0 - width_pen * 3.0 + min(length / 40.0, 2.0)
|
||||
|
||||
|
||||
def detect_trading_range(
|
||||
df: pd.DataFrame,
|
||||
lookback: int = 120,
|
||||
min_bars: int = 24,
|
||||
atr_mult: float = 1.2,
|
||||
tail_reserve: int = 12,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
在最近 lookback 根内寻找高低点波动受控的连续段作为交易区间。
|
||||
尾部预留 tail_reserve 根用于事件(Spring/SOS),不参与箱体边界计算。
|
||||
在硬门槛之上按评分取最优段(非仅最长窗口)。
|
||||
"""
|
||||
if df is None or len(df) < min_bars + 5:
|
||||
return None
|
||||
work = df.tail(lookback).reset_index(drop=True)
|
||||
n = len(work)
|
||||
reserve = min(tail_reserve, max(0, n - min_bars - 2))
|
||||
core_end = n - reserve if reserve > 0 else n
|
||||
core = work.iloc[:core_end]
|
||||
if len(core) < min_bars:
|
||||
core = work
|
||||
core_end = n
|
||||
reserve = 0
|
||||
|
||||
atr = _atr(work)
|
||||
last_atr = float(atr.iloc[core_end - 1]) if atr.notna().iloc[:core_end].any() else float(
|
||||
(core["high"] - core["low"]).mean()
|
||||
)
|
||||
if not np.isfinite(last_atr) or last_atr <= 0:
|
||||
last_atr = float(core["close"].iloc[-1]) * 0.01
|
||||
|
||||
best = None
|
||||
best_score = float("-inf")
|
||||
cn = len(core)
|
||||
for length in range(min(cn, lookback), min_bars - 1, -4):
|
||||
seg = core.iloc[-length:]
|
||||
hi = float(seg["high"].max())
|
||||
lo = float(seg["low"].min())
|
||||
width = hi - lo
|
||||
if width <= 0 or width > last_atr * atr_mult * 3.5:
|
||||
continue
|
||||
tol = last_atr * atr_mult * 0.35
|
||||
near_hi = int((seg["high"] >= hi - tol).sum())
|
||||
near_lo = int((seg["low"] <= lo + tol).sum())
|
||||
if near_hi < 2 or near_lo < 2:
|
||||
continue
|
||||
inside = float(((seg["close"] >= lo - tol) & (seg["close"] <= hi + tol)).mean())
|
||||
if inside < 0.75:
|
||||
continue
|
||||
score = _score_segment(length, near_hi, near_lo, inside, width, last_atr)
|
||||
if score <= best_score:
|
||||
continue
|
||||
start_i = cn - length
|
||||
end_i = cn - 1
|
||||
mid = (hi + lo) / 2.0
|
||||
last_c = float(work["close"].iloc[-1])
|
||||
active = (lo - tol * 1.5) <= last_c <= (hi + tol * 1.5)
|
||||
best_score = score
|
||||
best = {
|
||||
"start_idx": int(start_i),
|
||||
"end_idx": int(end_i),
|
||||
"high": hi,
|
||||
"low": lo,
|
||||
"mid": mid,
|
||||
"active": bool(active),
|
||||
"atr": last_atr,
|
||||
"tol": tol,
|
||||
"bars": int(length),
|
||||
"score": float(score),
|
||||
}
|
||||
|
||||
if best is None:
|
||||
return None
|
||||
|
||||
def _ts(row) -> Any:
|
||||
if "date" in work.columns and pd.notna(row["date"]):
|
||||
return row["date"]
|
||||
if "timestamp" in work.columns:
|
||||
return row["timestamp"]
|
||||
return None
|
||||
|
||||
best["start_time"] = _ts(work.iloc[best["start_idx"]])
|
||||
# 区间时间结束取 core 末,事件可落在其后
|
||||
best["end_time"] = _ts(work.iloc[best["end_idx"]])
|
||||
offset = len(df) - len(work)
|
||||
best["abs_start_idx"] = offset + best["start_idx"]
|
||||
best["abs_end_idx"] = offset + best["end_idx"]
|
||||
best["abs_scan_end_idx"] = offset + n - 1
|
||||
return best
|
||||
@@ -1,72 +0,0 @@
|
||||
"""区间内 Volume Profile。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def compute_volume_profile(
|
||||
df: pd.DataFrame,
|
||||
start_idx: int,
|
||||
end_idx: int,
|
||||
bin_count: int = 50,
|
||||
value_area_pct: float = 0.70,
|
||||
) -> Dict[str, Any]:
|
||||
seg = df.iloc[start_idx : end_idx + 1]
|
||||
if seg.empty:
|
||||
return {"bins": [], "poc": None, "vah": None, "val": None, "bin_count": bin_count}
|
||||
|
||||
typical = (seg["high"].astype(float) + seg["low"].astype(float) + seg["close"].astype(float)) / 3.0
|
||||
vol = seg["volume"].astype(float).fillna(0.0)
|
||||
lo = float(seg["low"].min())
|
||||
hi = float(seg["high"].max())
|
||||
if not np.isfinite(lo) or not np.isfinite(hi) or hi <= lo:
|
||||
mid = float(seg["close"].iloc[-1])
|
||||
return {
|
||||
"bins": [{"price": mid, "volume": float(vol.sum())}],
|
||||
"poc": mid,
|
||||
"vah": mid,
|
||||
"val": mid,
|
||||
"bin_count": 1,
|
||||
}
|
||||
|
||||
edges = np.linspace(lo, hi, bin_count + 1)
|
||||
# 右开最后一桶闭合
|
||||
idx = np.clip(np.digitize(typical.values, edges) - 1, 0, bin_count - 1)
|
||||
vols = np.zeros(bin_count, dtype=float)
|
||||
for i, v in zip(idx, vol.values):
|
||||
vols[i] += float(v)
|
||||
|
||||
centers = (edges[:-1] + edges[1:]) / 2.0
|
||||
poc_i = int(np.argmax(vols)) if vols.sum() > 0 else bin_count // 2
|
||||
poc = float(centers[poc_i])
|
||||
|
||||
# Value Area:从 POC 向两侧扩展直到累计 >= value_area_pct
|
||||
total = float(vols.sum()) or 1.0
|
||||
target = total * value_area_pct
|
||||
left = right = poc_i
|
||||
acc = float(vols[poc_i])
|
||||
while acc < target and (left > 0 or right < bin_count - 1):
|
||||
left_v = vols[left - 1] if left > 0 else -1.0
|
||||
right_v = vols[right + 1] if right < bin_count - 1 else -1.0
|
||||
if right_v >= left_v and right < bin_count - 1:
|
||||
right += 1
|
||||
acc += float(vols[right])
|
||||
elif left > 0:
|
||||
left -= 1
|
||||
acc += float(vols[left])
|
||||
else:
|
||||
break
|
||||
|
||||
bins: List[Dict[str, float]] = [
|
||||
{"price": float(centers[i]), "volume": float(vols[i])} for i in range(bin_count)
|
||||
]
|
||||
return {
|
||||
"bins": bins,
|
||||
"poc": poc,
|
||||
"vah": float(centers[right]),
|
||||
"val": float(centers[left]),
|
||||
"bin_count": bin_count,
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
"""Drop-in replacement for the `talib.abstract` calls this project makes.
|
||||
|
||||
Same call signatures, same column names, same NaN warm-up lengths, so call
|
||||
sites only change their import line.
|
||||
|
||||
Only what the codebase actually uses is implemented: SMA, MA, EMA, RSI, ATR,
|
||||
MACD, BBANDS. Numerical agreement with TA-Lib is enforced by
|
||||
`chanlun/tests/test_ta_compat.py`, which skips when talib is absent.
|
||||
|
||||
The warm-up conventions below are TA-Lib's, not the textbook ones, and they
|
||||
differ between functions — getting them wrong shifts every downstream Chan
|
||||
structure by a bar:
|
||||
|
||||
SMA/BBANDS first value at index period-1
|
||||
EMA seeded with the SMA of the first `period` values, at index period-1
|
||||
RSI/ATR Wilder smoothing (alpha = 1/period), first value at index period
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
__all__ = ["SMA", "MA", "EMA", "RSI", "ATR", "MACD", "BBANDS"]
|
||||
|
||||
|
||||
def _series(data, price: str = "close") -> pd.Series:
|
||||
"""Accept the abstract-API shapes: DataFrame, Series, or ndarray."""
|
||||
if isinstance(data, pd.DataFrame):
|
||||
return data[price].astype(float)
|
||||
if isinstance(data, pd.Series):
|
||||
return data.astype(float)
|
||||
return pd.Series(np.asarray(data, dtype=float))
|
||||
|
||||
|
||||
def _recursive(values: np.ndarray, seed: float, start: int, alpha: float, n: int) -> np.ndarray:
|
||||
"""out[start] = seed; out[i] = alpha*values[i] + (1-alpha)*out[i-1].
|
||||
|
||||
Delegates the recursion to pandas' C implementation rather than a Python
|
||||
loop — `research/` runs this over long histories.
|
||||
"""
|
||||
out = np.full(n, np.nan)
|
||||
if start >= n:
|
||||
return out
|
||||
tail = values[start:].astype(float).copy()
|
||||
tail[0] = seed
|
||||
out[start:] = pd.Series(tail).ewm(alpha=alpha, adjust=False).mean().to_numpy()
|
||||
return out
|
||||
|
||||
|
||||
def SMA(data, timeperiod: int = 30, price: str = "close") -> pd.Series:
|
||||
s = _series(data, price)
|
||||
return s.rolling(window=timeperiod, min_periods=timeperiod).mean()
|
||||
|
||||
|
||||
def MA(data, timeperiod: int = 30, matype: int = 0, price: str = "close") -> pd.Series:
|
||||
if matype != 0:
|
||||
raise NotImplementedError(f"MA matype={matype} is not used by this codebase")
|
||||
return SMA(data, timeperiod, price=price)
|
||||
|
||||
|
||||
def _ema(x: np.ndarray, period: int, start: int) -> np.ndarray:
|
||||
"""EMA whose first output lands on `start`, seeded by the SMA of the
|
||||
`period` values ending there.
|
||||
|
||||
`start` is a parameter because MACD needs the fast EMA to begin later than
|
||||
it naturally would; see the note in MACD().
|
||||
"""
|
||||
n = x.size
|
||||
if n <= start or start < period - 1:
|
||||
return np.full(n, np.nan)
|
||||
seed = x[start - period + 1: start + 1].mean()
|
||||
return _recursive(x, seed, start, 2.0 / (period + 1.0), n)
|
||||
|
||||
|
||||
def EMA(data, timeperiod: int = 30, price: str = "close") -> pd.Series:
|
||||
s = _series(data, price)
|
||||
x = s.to_numpy(dtype=float)
|
||||
return pd.Series(_ema(x, timeperiod, timeperiod - 1), index=s.index)
|
||||
|
||||
|
||||
def RSI(data, timeperiod: int = 14, price: str = "close") -> pd.Series:
|
||||
s = _series(data, price)
|
||||
x = s.to_numpy(dtype=float)
|
||||
n = x.size
|
||||
out = np.full(n, np.nan)
|
||||
if n <= timeperiod:
|
||||
return pd.Series(out, index=s.index)
|
||||
|
||||
delta = np.diff(x)
|
||||
gain = np.where(delta > 0.0, delta, 0.0)
|
||||
loss = np.where(delta < 0.0, -delta, 0.0)
|
||||
|
||||
# delta[k] corresponds to bar k+1, so the first `timeperiod` deltas seed bar `timeperiod`.
|
||||
alpha = 1.0 / timeperiod
|
||||
avg_gain = _recursive(gain, gain[:timeperiod].mean(), timeperiod - 1, alpha, n - 1)
|
||||
avg_loss = _recursive(loss, loss[:timeperiod].mean(), timeperiod - 1, alpha, n - 1)
|
||||
|
||||
ag = avg_gain[timeperiod - 1:]
|
||||
al = avg_loss[timeperiod - 1:]
|
||||
with np.errstate(divide="ignore", invalid="ignore"):
|
||||
rsi = np.where(al == 0.0, 100.0, 100.0 - 100.0 / (1.0 + ag / al))
|
||||
out[timeperiod:] = rsi
|
||||
return pd.Series(out, index=s.index)
|
||||
|
||||
|
||||
def ATR(data, timeperiod: int = 14) -> pd.Series:
|
||||
if not isinstance(data, pd.DataFrame):
|
||||
raise TypeError("ATR needs a DataFrame with high/low/close")
|
||||
high = data["high"].to_numpy(dtype=float)
|
||||
low = data["low"].to_numpy(dtype=float)
|
||||
close = data["close"].to_numpy(dtype=float)
|
||||
n = high.size
|
||||
out = np.full(n, np.nan)
|
||||
if n <= timeperiod:
|
||||
return pd.Series(out, index=data.index)
|
||||
|
||||
prev_close = close[:-1]
|
||||
tr = np.maximum.reduce([
|
||||
high[1:] - low[1:],
|
||||
np.abs(high[1:] - prev_close),
|
||||
np.abs(low[1:] - prev_close),
|
||||
])
|
||||
|
||||
# tr[k] is bar k+1; the first `timeperiod` true ranges seed bar `timeperiod`.
|
||||
smoothed = _recursive(tr, tr[:timeperiod].mean(), timeperiod - 1, 1.0 / timeperiod, n - 1)
|
||||
out[timeperiod:] = smoothed[timeperiod - 1:]
|
||||
return pd.Series(out, index=data.index)
|
||||
|
||||
|
||||
def MACD(
|
||||
data,
|
||||
fastperiod: int = 12,
|
||||
slowperiod: int = 26,
|
||||
signalperiod: int = 9,
|
||||
price: str = "close",
|
||||
) -> pd.DataFrame:
|
||||
if slowperiod < fastperiod:
|
||||
fastperiod, slowperiod = slowperiod, fastperiod
|
||||
|
||||
s = _series(data, price)
|
||||
x = s.to_numpy(dtype=float)
|
||||
n = x.size
|
||||
|
||||
macd = np.full(n, np.nan)
|
||||
signal = np.full(n, np.nan)
|
||||
hist = np.full(n, np.nan)
|
||||
empty = pd.DataFrame({"macd": macd, "macdsignal": signal, "macdhist": hist}, index=s.index)
|
||||
|
||||
# Both EMAs emit their first value on the same bar. That makes the slow one
|
||||
# ordinary, but re-seeds the fast one from the SMA of the `fastperiod`
|
||||
# values ending there instead of carrying the recursion forward from bar
|
||||
# fastperiod-1 — the two disagree by ~0.2 on a 100-priced series.
|
||||
macd_start = slowperiod - 1
|
||||
if n <= macd_start:
|
||||
return empty
|
||||
line = _ema(x, fastperiod, macd_start) - _ema(x, slowperiod, macd_start)
|
||||
|
||||
# The signal EMA runs over the MACD line, so everything shifts by another
|
||||
# signalperiod-1 bars, and TA-Lib trims the MACD line to match.
|
||||
valid = line[macd_start:]
|
||||
if valid.size < signalperiod:
|
||||
return empty
|
||||
sig = _recursive(
|
||||
valid, valid[:signalperiod].mean(), signalperiod - 1, 2.0 / (signalperiod + 1.0), valid.size
|
||||
)
|
||||
start = macd_start + signalperiod - 1
|
||||
macd[start:] = line[start:]
|
||||
signal[macd_start:] = sig
|
||||
hist = macd - signal
|
||||
|
||||
return pd.DataFrame({"macd": macd, "macdsignal": signal, "macdhist": hist}, index=s.index)
|
||||
|
||||
|
||||
def BBANDS(
|
||||
data,
|
||||
timeperiod: int = 5,
|
||||
nbdevup: float = 2.0,
|
||||
nbdevdn: float = 2.0,
|
||||
matype: int = 0,
|
||||
price: str = "close",
|
||||
) -> pd.DataFrame:
|
||||
if matype != 0:
|
||||
raise NotImplementedError(f"BBANDS matype={matype} is not used by this codebase")
|
||||
s = _series(data, price)
|
||||
middle = s.rolling(window=timeperiod, min_periods=timeperiod).mean()
|
||||
# TA-Lib uses the population standard deviation.
|
||||
std = s.rolling(window=timeperiod, min_periods=timeperiod).std(ddof=0)
|
||||
return pd.DataFrame(
|
||||
{
|
||||
"upperband": middle + nbdevup * std,
|
||||
"middleband": middle,
|
||||
"lowerband": middle - nbdevdn * std,
|
||||
},
|
||||
index=s.index,
|
||||
)
|
||||
@@ -6,9 +6,7 @@ from decimal import Decimal
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
@@ -467,7 +465,7 @@ class BiBuilderMixin:
|
||||
pre_last_bi = bi_list[-2]
|
||||
last_bi = bi_list[-1]
|
||||
if pre_last_bi.is_sure and not last_bi.is_sure and pre_last_bi.dir == Chan_BI_DIR.UP and False:
|
||||
pre_last_bi.update_bi(klc)
|
||||
#pre_last_bi.update_bi(klc)
|
||||
bi_list.remove(last_bi)
|
||||
pre_last_bi.set_next(None)
|
||||
#last_top.set_fx(Chan_FX_TYPE.PTOP)
|
||||
@@ -585,7 +583,7 @@ class BiBuilderMixin:
|
||||
pre_last_bi = bi_list[-2]
|
||||
last_bi = bi_list[-1]
|
||||
if pre_last_bi.is_sure and not last_bi.is_sure and pre_last_bi.dir == Chan_BI_DIR.DOWN and False:
|
||||
pre_last_bi.update_bi(klc)
|
||||
#pre_last_bi.update_bi(klc)
|
||||
bi_list.remove(last_bi)
|
||||
pre_last_bi.set_next(None)
|
||||
last_bottom = klc
|
||||
|
||||
@@ -6,9 +6,7 @@ from decimal import Decimal
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
"""增量更新:新K只追加 KLU/KLC,笔与笔中枢在当前列表上重算。
|
||||
|
||||
不改 init_TF_DF 的整段语义。笔必须整表重扫:最后一笔 is_sure 允许收回
|
||||
(OWN_CHAN_ZS_001 上 60 天出现 7 次)。笔中枢用 cal_bi_zs_list_pure。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
from pandas import DataFrame
|
||||
from chanlun.pipeline.resample import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanEnum import Chan_FX_TYPE, Chan_KLC_FX, Chan_KLC_STATE
|
||||
from chanlun.core.ChanKLU import ChanKLU
|
||||
|
||||
|
||||
class IncrementalBuilderMixin:
|
||||
def init_stream(self, df, interval=1, timeframe=None):
|
||||
"""用历史K线初始化流式状态,之后用 append_bar / replace_last_bar。"""
|
||||
if df is None or df.empty:
|
||||
raise ValueError("DataFrame for stream is empty.")
|
||||
if "date" not in df.columns:
|
||||
raise ValueError(f"DataFrame missing 'date' column. Columns: {df.columns.tolist()}")
|
||||
self.timeframe = timeframe
|
||||
self.interval = interval
|
||||
if interval == 1:
|
||||
self.dataframe = df.copy()
|
||||
else:
|
||||
self.dataframe = resample_to_interval(df, interval)
|
||||
self.dataframe = self.add_indicators(self.dataframe)
|
||||
self.klu_list = []
|
||||
self.klc_list = []
|
||||
self.bi_list = []
|
||||
self.bi_zs_list = []
|
||||
self.seg_list = []
|
||||
self.zs_list = []
|
||||
self.bsp_list = []
|
||||
self.klc_fx_list = []
|
||||
self.big_zs_list = []
|
||||
self._klc_feed_last_klu = None
|
||||
for i in range(len(self.dataframe)):
|
||||
self._append_row_at(i, rebuild=False)
|
||||
self.rebuild_bi_zs()
|
||||
return self
|
||||
|
||||
def append_bar(self, row):
|
||||
"""追加一根已收盘K线。同一时间戳则改为替换最后一根。"""
|
||||
self._ensure_stream_state()
|
||||
item = self._normalize_row(row)
|
||||
if self.klu_list and self.klu_list[-1].time == self._row_time_str(item):
|
||||
return self.replace_last_bar(item)
|
||||
self._append_item_to_dataframe(item)
|
||||
self.dataframe = self.add_indicators(self.dataframe)
|
||||
self._append_row_at(len(self.dataframe) - 1, rebuild=True)
|
||||
return self
|
||||
|
||||
def replace_last_bar(self, row):
|
||||
"""更新最后一根K(未完成K线走新OHLC)。包含关系从 KLU 列表重放。"""
|
||||
self._ensure_stream_state()
|
||||
if not self.klu_list:
|
||||
return self.append_bar(row)
|
||||
item = self._normalize_row(row)
|
||||
idx = self.dataframe.index[-1]
|
||||
for key, val in item.items():
|
||||
self.dataframe.at[idx, key] = val
|
||||
self.dataframe = self.add_indicators(self.dataframe)
|
||||
self._apply_item_to_klu(self.klu_list[-1], self.dataframe.iloc[-1])
|
||||
self._rebuild_klc_from_klu()
|
||||
self.rebuild_bi_zs()
|
||||
return self
|
||||
|
||||
def rebuild_bi_zs(self):
|
||||
"""在当前 KLC 上重算笔 + cal_bi_zs_list_pure。会先清分型标记。"""
|
||||
self._reset_klc_bi_marks(self.klc_list)
|
||||
self.bi_list = self.cal_bi_list(self.klc_list) if self.klc_list else []
|
||||
self.bi_zs_list = self.cal_bi_zs_list_pure(self.bi_list) if self.bi_list else []
|
||||
return self.bi_zs_list
|
||||
|
||||
def _ensure_stream_state(self):
|
||||
if not hasattr(self, "klu_list") or self.klu_list is None:
|
||||
self.klu_list = []
|
||||
if not hasattr(self, "klc_list") or self.klc_list is None:
|
||||
self.klc_list = []
|
||||
if not hasattr(self, "dataframe") or self.dataframe is None:
|
||||
self.dataframe = DataFrame(
|
||||
columns=["date", "open", "high", "low", "close", "volume"]
|
||||
)
|
||||
if not hasattr(self, "_klc_feed_last_klu"):
|
||||
self._klc_feed_last_klu = self.klu_list[-1] if self.klu_list else None
|
||||
if not hasattr(self, "bi_zs_list"):
|
||||
self.bi_zs_list = []
|
||||
|
||||
def _rebuild_klc_from_klu(self):
|
||||
self.klc_list = []
|
||||
last_klu = None
|
||||
for klu in self.klu_list:
|
||||
self._push_klu_into_klc_list(self.klc_list, klu, last_klu)
|
||||
last_klu = klu
|
||||
self._klc_feed_last_klu = last_klu
|
||||
|
||||
def _append_row_at(self, idx, rebuild=True):
|
||||
item = self.dataframe.iloc[idx]
|
||||
klu = self._klu_from_item(item, idx)
|
||||
if self.klu_list:
|
||||
self.klu_list[-1].set_next(klu)
|
||||
klu.set_pre(self.klu_list[-1])
|
||||
self._push_klu_into_klc_list(self.klc_list, klu, self._klc_feed_last_klu)
|
||||
self._klc_feed_last_klu = klu
|
||||
self.klu_list.append(klu)
|
||||
if rebuild:
|
||||
self.rebuild_bi_zs()
|
||||
|
||||
def _klu_from_item(self, item, idx):
|
||||
klu = ChanKLU(
|
||||
self._item_time_str(item),
|
||||
item["open"],
|
||||
item["high"],
|
||||
item["low"],
|
||||
item["close"],
|
||||
item["volume"],
|
||||
)
|
||||
klu.set_idx(idx)
|
||||
if not hasattr(klu, "ema13"):
|
||||
klu.ema13 = 0
|
||||
if "macd" in item:
|
||||
klu.set_indicators(item)
|
||||
return klu
|
||||
|
||||
def _apply_item_to_klu(self, klu, item):
|
||||
klu.time = self._item_time_str(item)
|
||||
klu.open = item["open"]
|
||||
klu.high = item["high"]
|
||||
klu.low = item["low"]
|
||||
klu.close = item["close"]
|
||||
klu.volume = item["volume"]
|
||||
klu.range = klu.high - klu.low
|
||||
klu.body = abs(klu.close - klu.open)
|
||||
if "macd" in item:
|
||||
klu.set_indicators(item)
|
||||
|
||||
def _reset_klc_bi_marks(self, klc_list):
|
||||
for klc in klc_list:
|
||||
klc.fx = Chan_FX_TYPE.UNKNOWN
|
||||
klc.klc_fx_type = Chan_KLC_FX.UNKNOWN
|
||||
klc.klc_state = Chan_KLC_STATE.UNKNOWN
|
||||
klc.bi = None
|
||||
klc.fx_confirmed = False
|
||||
|
||||
def _item_time_str(self, item):
|
||||
date = item["date"]
|
||||
if hasattr(date, "to_pydatetime"):
|
||||
date = date.to_pydatetime()
|
||||
if isinstance(date, datetime):
|
||||
return date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
return str(date)
|
||||
|
||||
def _row_time_str(self, item):
|
||||
return self._item_time_str(item)
|
||||
|
||||
def _normalize_row(self, row):
|
||||
if isinstance(row, pd.Series):
|
||||
return row
|
||||
return pd.Series(row)
|
||||
|
||||
def _append_item_to_dataframe(self, item):
|
||||
row_df = DataFrame([item])
|
||||
if self.dataframe is None or self.dataframe.empty:
|
||||
self.dataframe = row_df
|
||||
else:
|
||||
self.dataframe = pd.concat([self.dataframe, row_df], ignore_index=True)
|
||||
@@ -6,9 +6,8 @@ from decimal import Decimal
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from chanlun.indicators import ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
|
||||
@@ -6,9 +6,7 @@ from decimal import Decimal
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
@@ -86,7 +84,8 @@ class KlineBuilderMixin:
|
||||
return Chan_FX_TYPE.UNKNOWN
|
||||
|
||||
def check_fx(self, klc):
|
||||
if klc.pre and klc.next:
|
||||
# 右K未完成(仍在包含合并)时不分型:否则确认笔会随 next 扩区间被 check_*_fx 收回
|
||||
if klc.pre and klc.next and klc.next.end_klu is not None:
|
||||
if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low:
|
||||
#if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0:
|
||||
klc.set_fx(Chan_FX_TYPE.TOP)
|
||||
@@ -99,6 +98,21 @@ class KlineBuilderMixin:
|
||||
return Chan_FX_TYPE.BOTTOM
|
||||
return Chan_FX_TYPE.UNKNOWN
|
||||
|
||||
def check_fx3(self, klc):
|
||||
# 右K未完成(仍在包含合并)时不分型:否则确认笔会随 next 扩区间被 check_*_fx 收回
|
||||
if klc.pre and klc.next and klc.next.end_klu is not None:
|
||||
next_klu = klc.next.end_klu.next
|
||||
if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low and klc.high > next_klu.high:
|
||||
#if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0:
|
||||
klc.set_fx(Chan_FX_TYPE.TOP)
|
||||
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "TOP")
|
||||
return Chan_FX_TYPE.TOP
|
||||
elif klc.low < klc.pre.low and klc.low < klc.next.low and klc.high < klc.pre.high and klc.high < klc.next.high and klc.low < next_klu.low:
|
||||
#if (klc.close < klc.ema52 or klc.next.close < klc.next.ema52) and klc.macd < 0:
|
||||
klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
||||
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "BOTTOM")
|
||||
return Chan_FX_TYPE.BOTTOM
|
||||
return Chan_FX_TYPE.UNKNOWN
|
||||
def check_fx2(self, klc):
|
||||
if klc.pre and klc.next:
|
||||
if klc.high > klc.pre.close and klc.close > klc.next.close and klc.close > klc.pre.close and klc.close > klc.next.close:
|
||||
@@ -171,6 +185,43 @@ class KlineBuilderMixin:
|
||||
def get_kl_data(self, dataframe:DataFrame):
|
||||
return self.cal_kl_data(dataframe)
|
||||
|
||||
def _push_klu_into_klc_list(self, klc_list, klu, last_klu):
|
||||
"""把一根 KLU 并入包含K线列表。与 get_klc_list 的几何规则相同。"""
|
||||
if len(klc_list) > 0:
|
||||
last_klc = klc_list[-1]
|
||||
if klu.exception:
|
||||
ddir = Chan_KLINE_DIR.DOWN
|
||||
if last_klc.high < klu.high:
|
||||
ddir = Chan_KLINE_DIR.UP
|
||||
klc = ChanKLC(klu, index=len(klc_list), ddir=ddir)
|
||||
klc.high = klu.close if klu.close > klu.open else klu.open
|
||||
klc.low = klu.open if klu.close > klu.open else klu.close
|
||||
klc_list.append(klc)
|
||||
last_klc.set_next(klc)
|
||||
klc.set_pre(last_klc)
|
||||
last_klc.set_end_klu(last_klu)
|
||||
klc.set_pre_fx()
|
||||
else:
|
||||
included = last_klc.check_klu_included(klu)
|
||||
if not included:
|
||||
ddir = Chan_KLINE_DIR.DOWN
|
||||
if last_klc.high < klu.high:
|
||||
ddir = Chan_KLINE_DIR.UP
|
||||
klc = ChanKLC(klu, index=len(klc_list), ddir=ddir)
|
||||
klc_list.append(klc)
|
||||
last_klc.set_next(klc)
|
||||
klc.set_pre(last_klc)
|
||||
last_klc.set_end_klu(last_klu)
|
||||
klc.set_pre_fx()
|
||||
else:
|
||||
last_klc.add_klu(klu)
|
||||
else:
|
||||
ddir = Chan_KLINE_DIR.UP
|
||||
if klu.open > klu.close:
|
||||
ddir = Chan_KLINE_DIR.DOWN
|
||||
klc = ChanKLC(klu, 0, ddir)
|
||||
klc_list.append(klc)
|
||||
|
||||
def get_klc_list(self, klu_list):
|
||||
klc_list = []
|
||||
last_klu = None
|
||||
@@ -198,41 +249,7 @@ class KlineBuilderMixin:
|
||||
ema_down_list.append(ema_down_count)
|
||||
#print(last_klu.time, ema_down_count, "DOWN END")
|
||||
ema_down_count = 0
|
||||
if len(klc_list) > 0:
|
||||
last_klc = klc_list[-1]
|
||||
if klu.exception:
|
||||
ddir = Chan_KLINE_DIR.DOWN
|
||||
if last_klc.high < klu.high:
|
||||
ddir = Chan_KLINE_DIR.UP
|
||||
klc = ChanKLC(klu, index=len(klc_list), ddir=ddir)
|
||||
klc.high = klu.close if klu.close > klu.open else klu.open
|
||||
klc.low = klu.open if klu.close > klu.open else klu.close
|
||||
klc_list.append(klc)
|
||||
last_klc.set_next(klc)
|
||||
klc.set_pre(last_klc)
|
||||
last_klc.set_end_klu(last_klu)
|
||||
klc.set_pre_fx()
|
||||
#print(klu.time, klu.high, klu.low, klu.close, klu.open, klu.exception)
|
||||
else:
|
||||
included = last_klc.check_klu_included(klu)
|
||||
if not included:
|
||||
ddir = Chan_KLINE_DIR.DOWN
|
||||
if last_klc.high < klu.high:
|
||||
ddir = Chan_KLINE_DIR.UP
|
||||
klc = ChanKLC(klu, index=len(klc_list), ddir=ddir)
|
||||
klc_list.append(klc)
|
||||
last_klc.set_next(klc)
|
||||
klc.set_pre(last_klc)
|
||||
last_klc.set_end_klu(last_klu)
|
||||
klc.set_pre_fx()
|
||||
else:
|
||||
last_klc.add_klu(klu)
|
||||
else:
|
||||
ddir = Chan_KLINE_DIR.UP
|
||||
if klu.open > klu.close:
|
||||
ddir = Chan_KLINE_DIR.DOWN
|
||||
klc = ChanKLC(klu, 0, ddir)
|
||||
klc_list.append(klc)
|
||||
self._push_klu_into_klc_list(klc_list, klu, last_klu)
|
||||
last_klu = klu
|
||||
klc_list = self.cal_trend(klc_list)
|
||||
#print(ema52_up_list, ema52_down_list)
|
||||
|
||||
@@ -6,9 +6,7 @@ from decimal import Decimal
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
|
||||
@@ -6,9 +6,7 @@ from decimal import Decimal
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
@@ -355,9 +353,12 @@ class ZsBuilderMixin:
|
||||
return bi_zs_list
|
||||
|
||||
def get_zs_range(bis):
|
||||
zg = min(bi.high for bi in bis)
|
||||
zd = max(bi.low for bi in bis)
|
||||
return zg, zd
|
||||
bis_list = bis[0:3]
|
||||
zg = min(bi.high for bi in bis_list)
|
||||
zd = max(bi.low for bi in bis_list)
|
||||
dd = min(bi.low for bi in bis_list)
|
||||
gg = max(bi.high for bi in bis_list)
|
||||
return zg, zd, dd, gg
|
||||
|
||||
def is_bi_overlap_range(bi, zg, zd):
|
||||
return bi.high >= zd and bi.low <= zg
|
||||
@@ -375,8 +376,8 @@ class ZsBuilderMixin:
|
||||
zs.bi_list = list(bis)
|
||||
for bi in zs.bi_list:
|
||||
bi.set_bi_zs(zs)
|
||||
zs.set_gg(max(bi.high for bi in zs.bi_list))
|
||||
zs.set_dd(min(bi.low for bi in zs.bi_list))
|
||||
#zs.set_gg(max(bi.high for bi in zs.bi_list))
|
||||
#zs.set_dd(min(bi.low for bi in zs.bi_list))
|
||||
zs.classify_zs()
|
||||
|
||||
last_zs = None
|
||||
@@ -394,7 +395,7 @@ class ZsBuilderMixin:
|
||||
start_idx += 1
|
||||
continue
|
||||
|
||||
zg, zd = get_zs_range([bi1, bi2, bi3])
|
||||
zg, zd, dd, gg = get_zs_range([bi1, bi2, bi3])
|
||||
if zg <= zd:
|
||||
start_idx += 1
|
||||
continue
|
||||
@@ -420,6 +421,8 @@ class ZsBuilderMixin:
|
||||
zs = ChanBIZS(bi1, len(bi_zs_list), zs_dir)
|
||||
zs.set_zg(zg)
|
||||
zs.set_zd(zd)
|
||||
zs.set_dd(dd)
|
||||
zs.set_gg(gg)
|
||||
|
||||
set_zs_bi_list(zs, bis_for_zs)
|
||||
zs.set_end_bi(bis_for_zs[-1], bis_for_zs[-1].sure_time)
|
||||
|
||||
@@ -17,9 +17,7 @@ from chanlun.core.ChanSBI import ChanSBI
|
||||
from chanlun.core.ChanSEG import ChanSEG
|
||||
from chanlun.core.ChanZS import ChanZS
|
||||
from chanlun.core.ChanBSP import ChanBSP
|
||||
import talib.abstract as ta
|
||||
import pandas as pd
|
||||
from technical.util import resample_to_interval
|
||||
from decimal import Decimal
|
||||
import numpy as np
|
||||
from chanlun.indicators.ChanMACD import ChanMACD
|
||||
@@ -178,6 +176,15 @@ class ChanLun():
|
||||
def cal_bi_zs_list(self, bi_list):
|
||||
#return self.tf_df.cal_bi_zs(bi_list)
|
||||
return self.tf_df.cal_bi_zs_list(bi_list)
|
||||
def cal_bi_zs_list_pure(self, bi_list):
|
||||
return self.tf_df.cal_bi_zs_list_pure(bi_list)
|
||||
def init_stream(self, dataframe, interval=1, timeframe=None):
|
||||
self.tf_df.init_stream(dataframe, interval, timeframe)
|
||||
return self.tf_df
|
||||
def append_bar(self, row):
|
||||
return self.tf_df.append_bar(row)
|
||||
def replace_last_bar(self, row):
|
||||
return self.tf_df.replace_last_bar(row)
|
||||
def get_bi_zs_list(self, bi_list):
|
||||
return self.tf_df.get_bi_zs_list(bi_list)
|
||||
def get_decimal(self, value):
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""OHLCV resampling — replaces `technical.util.resample_to_interval`.
|
||||
|
||||
That was the only symbol this project imported from `technical`, which in turn
|
||||
pulled in the freqtrade dependency chain. Behaviour is preserved exactly,
|
||||
including the left-labelled bins (rows are candle *open* times) and the
|
||||
`dropna()` that drops empty intervals.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pandas as pd
|
||||
|
||||
__all__ = ["TICKER_INTERVAL_MINUTES", "resample_to_interval"]
|
||||
|
||||
TICKER_INTERVAL_MINUTES: dict[str, int] = {
|
||||
"1m": 1,
|
||||
"5m": 5,
|
||||
"15m": 15,
|
||||
"30m": 30,
|
||||
"1h": 60,
|
||||
"60m": 60,
|
||||
"2h": 120,
|
||||
"4h": 240,
|
||||
"6h": 360,
|
||||
"12h": 720,
|
||||
"1d": 1440,
|
||||
"1w": 10080,
|
||||
}
|
||||
|
||||
_OHLC_AGG = {
|
||||
"open": "first",
|
||||
"high": "max",
|
||||
"low": "min",
|
||||
"close": "last",
|
||||
"volume": "sum",
|
||||
}
|
||||
|
||||
|
||||
def resample_to_interval(dataframe: pd.DataFrame, interval: int | str) -> pd.DataFrame:
|
||||
"""Resample OHLCV rows to `interval` minutes (or a timeframe string).
|
||||
|
||||
Merging the result back onto a finer frame requires care to avoid lookahead
|
||||
bias; this function only resamples.
|
||||
"""
|
||||
if isinstance(interval, str):
|
||||
interval = TICKER_INTERVAL_MINUTES[interval]
|
||||
|
||||
df = dataframe.copy()
|
||||
df = df.set_index(pd.DatetimeIndex(df["date"]))
|
||||
df = df.resample(f"{interval}min", label="left").agg(_OHLC_AGG).dropna()
|
||||
df.reset_index(inplace=True)
|
||||
return df
|
||||
@@ -2,9 +2,8 @@ from datetime import timedelta
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from technical.util import resample_to_interval
|
||||
from chanlun.pipeline.resample import resample_to_interval
|
||||
|
||||
from chanlun.core.ChanBI import ChanBI
|
||||
from chanlun.core.ChanBIZS import ChanBIZS
|
||||
@@ -31,12 +30,13 @@ from chanlun.core.ChanZS import ChanZS, ChanZS_Big
|
||||
from chanlun.indicators.ChanMACD import ChanMACD
|
||||
from chanlun.pipeline.builders.bi import BiBuilderMixin
|
||||
from chanlun.pipeline.builders.bsp import BspBuilderMixin
|
||||
from chanlun.pipeline.builders.incremental import IncrementalBuilderMixin
|
||||
from chanlun.pipeline.builders.indicators import IndicatorsBuilderMixin
|
||||
from chanlun.pipeline.builders.kline import KlineBuilderMixin
|
||||
from chanlun.pipeline.builders.seg import SegBuilderMixin
|
||||
from chanlun.pipeline.builders.zs import ZsBuilderMixin
|
||||
|
||||
class TF_DF(IndicatorsBuilderMixin, KlineBuilderMixin, BiBuilderMixin, SegBuilderMixin, ZsBuilderMixin, BspBuilderMixin):
|
||||
class TF_DF(IndicatorsBuilderMixin, KlineBuilderMixin, BiBuilderMixin, SegBuilderMixin, ZsBuilderMixin, BspBuilderMixin, IncrementalBuilderMixin):
|
||||
def __init__(self, df=None, interval=0, timeframe=None):
|
||||
if df is not None:
|
||||
self.init_TF_DF(df, interval, timeframe)
|
||||
@@ -59,12 +59,14 @@ class TF_DF(IndicatorsBuilderMixin, KlineBuilderMixin, BiBuilderMixin, SegBuilde
|
||||
self.klc_list = []
|
||||
self.bi_list = []
|
||||
self.zs_list = []
|
||||
self.bi_zs_list = []
|
||||
self.bsp_list = []
|
||||
self.seg_list = []
|
||||
self.klc_fx_list = []
|
||||
self.klu_list = self.cal_kl_data(self.dataframe)
|
||||
self.klc_list = self.get_klc_list(self.klu_list)
|
||||
self.bi_list = self.cal_bi_list(self.klc_list)
|
||||
self.bi_zs_list = self.cal_bi_zs_list_pure(self.bi_list)
|
||||
self.seg_list = self.get_seg_list(self.bi_list)
|
||||
self.zs_list = self.get_zs_list(self.bi_list, self.seg_list)
|
||||
self.big_zs_list = self.get_big_zs_list(self.zs_list)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from __future__ import annotations
|
||||
@@ -0,0 +1,141 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
_CHAN = Path(__file__).resolve().parents[2]
|
||||
if str(_CHAN) not in sys.path:
|
||||
sys.path.insert(0, str(_CHAN))
|
||||
|
||||
from chanlun.pipeline.timeframe import TF_DF # noqa: E402
|
||||
|
||||
|
||||
def _zigzag_df(n=160, step=8):
|
||||
dates = pd.date_range("2024-01-01", periods=n, freq="5min")
|
||||
rows = []
|
||||
price = 100.0
|
||||
for i, date in enumerate(dates):
|
||||
up = (i // step) % 2 == 0
|
||||
if up:
|
||||
o = price
|
||||
c = price + 1.5
|
||||
h = c + 0.3
|
||||
l = o - 0.2
|
||||
else:
|
||||
o = price
|
||||
c = price - 1.5
|
||||
h = o + 0.2
|
||||
l = c - 0.3
|
||||
price = c
|
||||
rows.append(
|
||||
{
|
||||
"date": date,
|
||||
"open": o,
|
||||
"high": h,
|
||||
"low": l,
|
||||
"close": c,
|
||||
"volume": 1.0,
|
||||
}
|
||||
)
|
||||
return pd.DataFrame(rows)
|
||||
|
||||
|
||||
def _sure_bi_key(bi):
|
||||
return (str(bi.start_time), bi.dir.name, round(float(bi.high), 6), round(float(bi.low), 6))
|
||||
|
||||
|
||||
def _zs_key(zs):
|
||||
return (
|
||||
str(zs.start_time),
|
||||
round(float(zs.zg), 6),
|
||||
round(float(zs.zd), 6),
|
||||
len(zs.bi_list),
|
||||
)
|
||||
|
||||
|
||||
class TestIncremental(unittest.TestCase):
|
||||
def test_init_stream_matches_batch_push(self):
|
||||
df = _zigzag_df()
|
||||
stream = TF_DF()
|
||||
stream.init_stream(df, 1, "5m")
|
||||
|
||||
batch = TF_DF()
|
||||
indexed = batch.add_indicators(df.copy())
|
||||
klu = batch.cal_kl_data(indexed)
|
||||
klc = []
|
||||
last = None
|
||||
for k in klu:
|
||||
batch._push_klu_into_klc_list(klc, k, last)
|
||||
last = k
|
||||
batch.klc_list = klc
|
||||
batch.rebuild_bi_zs()
|
||||
|
||||
self.assertEqual(len(stream.klu_list), len(klu))
|
||||
self.assertEqual(len(stream.klc_list), len(klc))
|
||||
self.assertEqual(
|
||||
[_sure_bi_key(b) for b in stream.bi_list if b.is_sure],
|
||||
[_sure_bi_key(b) for b in batch.bi_list if b.is_sure],
|
||||
)
|
||||
self.assertEqual(
|
||||
[_zs_key(z) for z in stream.bi_zs_list],
|
||||
[_zs_key(z) for z in batch.bi_zs_list],
|
||||
)
|
||||
|
||||
def test_append_bar_matches_init_stream(self):
|
||||
df = _zigzag_df()
|
||||
stream = TF_DF()
|
||||
stream.init_stream(df, 1, "5m")
|
||||
|
||||
inc = TF_DF()
|
||||
for _, row in df.iterrows():
|
||||
inc.append_bar(row)
|
||||
|
||||
self.assertEqual(len(inc.klu_list), len(stream.klu_list))
|
||||
self.assertEqual(len(inc.klc_list), len(stream.klc_list))
|
||||
self.assertEqual(
|
||||
[_sure_bi_key(b) for b in inc.bi_list if b.is_sure],
|
||||
[_sure_bi_key(b) for b in stream.bi_list if b.is_sure],
|
||||
)
|
||||
self.assertEqual(
|
||||
[_zs_key(z) for z in inc.bi_zs_list],
|
||||
[_zs_key(z) for z in stream.bi_zs_list],
|
||||
)
|
||||
|
||||
def test_replace_last_bar_keeps_count(self):
|
||||
df = _zigzag_df(n=80)
|
||||
tf = TF_DF()
|
||||
tf.init_stream(df, 1, "5m")
|
||||
n_klu = len(tf.klu_list)
|
||||
last = df.iloc[-1].copy()
|
||||
last["close"] = float(last["close"]) + 0.01
|
||||
last["high"] = max(float(last["high"]), float(last["close"]))
|
||||
tf.replace_last_bar(last)
|
||||
self.assertEqual(len(tf.klu_list), n_klu)
|
||||
self.assertGreater(len(tf.klc_list), 0)
|
||||
|
||||
def test_check_fx_skips_forming_right_wing(self):
|
||||
from types import SimpleNamespace
|
||||
|
||||
from chanlun.core.ChanEnum import Chan_FX_TYPE
|
||||
|
||||
tf = TF_DF()
|
||||
pre = SimpleNamespace(high=10, low=8)
|
||||
nxt_open = SimpleNamespace(high=11, low=7, end_klu=None)
|
||||
nxt_done = SimpleNamespace(high=11, low=7, end_klu=object())
|
||||
center = SimpleNamespace(
|
||||
pre=pre,
|
||||
next=nxt_open,
|
||||
high=12,
|
||||
low=9,
|
||||
set_fx=lambda *_a, **_k: None,
|
||||
)
|
||||
self.assertEqual(tf.check_fx(center), Chan_FX_TYPE.UNKNOWN)
|
||||
center.next = nxt_done
|
||||
self.assertEqual(tf.check_fx(center), Chan_FX_TYPE.TOP)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,198 @@
|
||||
"""Pin chanlun.indicators.ta to TA-Lib's output, bar for bar.
|
||||
|
||||
These indicators feed the Chan structure builders, so a one-bar shift in the
|
||||
warm-up or a different smoothing seed silently changes every downstream
|
||||
bi/seg/zs. Equality against the reference implementation is the only check
|
||||
that catches that.
|
||||
|
||||
Skipped when talib is unavailable — which is the point of the replacement, so
|
||||
the suite still has to pass without it. Run in an environment that has talib
|
||||
whenever chanlun/indicators/ta.py changes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
|
||||
from chanlun.indicators import ta # noqa: E402
|
||||
from chanlun.pipeline.resample import resample_to_interval # noqa: E402
|
||||
|
||||
try:
|
||||
import talib.abstract as reference
|
||||
except ImportError: # pragma: no cover
|
||||
reference = None
|
||||
|
||||
requires_talib = unittest.skipIf(reference is None, "talib not installed")
|
||||
|
||||
|
||||
def make_ohlcv(n: int = 900, seed: int = 7) -> pd.DataFrame:
|
||||
"""Random walk with enough range for BBANDS(365) and EMA(208) to warm up."""
|
||||
rng = np.random.default_rng(seed)
|
||||
close = 100.0 + np.cumsum(rng.normal(0.0, 1.0, n))
|
||||
spread = np.abs(rng.normal(0.0, 0.6, n)) + 0.05
|
||||
high = close + spread
|
||||
low = close - spread
|
||||
open_ = np.concatenate([[close[0]], close[:-1]])
|
||||
return pd.DataFrame(
|
||||
{
|
||||
"date": pd.date_range("2024-01-01", periods=n, freq="1min", tz="UTC"),
|
||||
"open": open_,
|
||||
"high": np.maximum.reduce([high, open_, close]),
|
||||
"low": np.minimum.reduce([low, open_, close]),
|
||||
"close": close,
|
||||
"volume": rng.uniform(1.0, 100.0, n),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class TAEquivalence(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.df = make_ohlcv()
|
||||
|
||||
def assertSameSeries(self, got, expected, label: str) -> None:
|
||||
g = np.asarray(got, dtype=float)
|
||||
e = np.asarray(expected, dtype=float)
|
||||
self.assertEqual(g.shape, e.shape, f"{label}: shape")
|
||||
np.testing.assert_array_equal(
|
||||
np.isnan(g), np.isnan(e), err_msg=f"{label}: NaN warm-up differs"
|
||||
)
|
||||
mask = ~np.isnan(e)
|
||||
np.testing.assert_allclose(
|
||||
g[mask], e[mask], rtol=1e-9, atol=1e-8, err_msg=f"{label}: values differ"
|
||||
)
|
||||
|
||||
@requires_talib
|
||||
def test_sma(self) -> None:
|
||||
for period in (5, 20, 90, 250):
|
||||
self.assertSameSeries(
|
||||
ta.SMA(self.df, timeperiod=period),
|
||||
reference.SMA(self.df, timeperiod=period),
|
||||
f"SMA({period})",
|
||||
)
|
||||
|
||||
@requires_talib
|
||||
def test_ma(self) -> None:
|
||||
for period in (5, 10, 250):
|
||||
self.assertSameSeries(
|
||||
ta.MA(self.df, timeperiod=period),
|
||||
reference.MA(self.df, timeperiod=period),
|
||||
f"MA({period})",
|
||||
)
|
||||
|
||||
@requires_talib
|
||||
def test_ema(self) -> None:
|
||||
for period in (5, 7, 10, 13, 24, 26, 30, 52, 104, 156, 208):
|
||||
self.assertSameSeries(
|
||||
ta.EMA(self.df, timeperiod=period),
|
||||
reference.EMA(self.df, timeperiod=period),
|
||||
f"EMA({period})",
|
||||
)
|
||||
|
||||
@requires_talib
|
||||
def test_rsi(self) -> None:
|
||||
for period in (7, 14, 21):
|
||||
self.assertSameSeries(
|
||||
ta.RSI(self.df, timeperiod=period),
|
||||
reference.RSI(self.df, timeperiod=period),
|
||||
f"RSI({period})",
|
||||
)
|
||||
|
||||
@requires_talib
|
||||
def test_atr(self) -> None:
|
||||
for period in (7, 14, 30):
|
||||
self.assertSameSeries(
|
||||
ta.ATR(self.df, timeperiod=period),
|
||||
reference.ATR(self.df, timeperiod=period),
|
||||
f"ATR({period})",
|
||||
)
|
||||
|
||||
@requires_talib
|
||||
def test_macd(self) -> None:
|
||||
for fast, slow, signal in ((12, 26, 9), (26, 52, 9), (5, 35, 5)):
|
||||
got = ta.MACD(self.df, fastperiod=fast, slowperiod=slow, signalperiod=signal)
|
||||
exp = reference.MACD(self.df, fastperiod=fast, slowperiod=slow, signalperiod=signal)
|
||||
for col in ("macd", "macdsignal", "macdhist"):
|
||||
self.assertSameSeries(got[col], exp[col], f"MACD({fast},{slow},{signal}).{col}")
|
||||
|
||||
@requires_talib
|
||||
def test_bbands(self) -> None:
|
||||
cases = (
|
||||
(365, 3.0, 3.0),
|
||||
(120, 3.0, 3.0),
|
||||
(41, 2.3, 2.3),
|
||||
(41, 2.0, 2.0),
|
||||
(26, 3.0, 3.0),
|
||||
(20, 2.0, 2.0),
|
||||
(14, 2.0, 2.0),
|
||||
)
|
||||
for period, up, dn in cases:
|
||||
got = ta.BBANDS(self.df, timeperiod=period, nbdevup=up, nbdevdn=dn, matype=0)
|
||||
exp = reference.BBANDS(self.df, timeperiod=period, nbdevup=up, nbdevdn=dn, matype=0)
|
||||
for col in ("upperband", "middleband", "lowerband"):
|
||||
self.assertSameSeries(got[col], exp[col], f"BBANDS({period},{up},{dn}).{col}")
|
||||
|
||||
@requires_talib
|
||||
def test_bbands_is_more_accurate_than_talib_on_tiny_windows(self) -> None:
|
||||
"""A deliberate divergence, documented so nobody "fixes" it back.
|
||||
|
||||
TA-Lib derives the variance from sumsq/n - mean**2, which cancels
|
||||
catastrophically when the window is short and prices are far from zero;
|
||||
at timeperiod=2 it drifts ~1e-6. Rolling std is accurate there, so the
|
||||
two disagree. No timeperiod below 14 is used in this codebase, and the
|
||||
periods that are used agree to ~1e-10 (covered by test_bbands).
|
||||
"""
|
||||
got = ta.BBANDS(self.df, timeperiod=2, nbdevup=1.0, nbdevdn=1.0, matype=0)["upperband"]
|
||||
exp = reference.BBANDS(self.df, timeperiod=2, nbdevup=1.0, nbdevdn=1.0, matype=0)["upperband"]
|
||||
|
||||
window = self.df["close"].rolling(2)
|
||||
truth = window.mean() + window.std(ddof=0)
|
||||
ours = np.nanmax(np.abs((got - truth).to_numpy()))
|
||||
theirs = np.nanmax(np.abs((exp - truth).to_numpy()))
|
||||
self.assertLess(ours, 1e-9)
|
||||
self.assertLess(ours, theirs)
|
||||
|
||||
@requires_talib
|
||||
def test_matches_on_real_price_scale(self) -> None:
|
||||
"""Guard against tolerances that only hold near 100."""
|
||||
df = self.df.copy()
|
||||
for col in ("open", "high", "low", "close"):
|
||||
df[col] *= 900.0
|
||||
self.assertSameSeries(
|
||||
ta.ATR(df, timeperiod=14), reference.ATR(df, timeperiod=14), "ATR@scale"
|
||||
)
|
||||
self.assertSameSeries(
|
||||
ta.RSI(df, timeperiod=14), reference.RSI(df, timeperiod=14), "RSI@scale"
|
||||
)
|
||||
|
||||
|
||||
class ResampleEquivalence(unittest.TestCase):
|
||||
@unittest.skipIf(
|
||||
__import__("importlib").util.find_spec("technical") is None,
|
||||
"technical not installed",
|
||||
)
|
||||
def test_matches_technical(self) -> None:
|
||||
from technical.util import resample_to_interval as ref_resample
|
||||
|
||||
df = make_ohlcv(600)
|
||||
for interval in (5, 15, 60, "5m", "1h"):
|
||||
got = resample_to_interval(df, interval)
|
||||
exp = ref_resample(df, interval)
|
||||
pd.testing.assert_frame_equal(got, exp, check_exact=False, rtol=1e-12)
|
||||
|
||||
def test_shapes_without_reference(self) -> None:
|
||||
df = make_ohlcv(120)
|
||||
out = resample_to_interval(df, 5)
|
||||
self.assertEqual(list(out.columns), ["date", "open", "high", "low", "close", "volume"])
|
||||
self.assertLessEqual(len(out), 120 // 5 + 1)
|
||||
self.assertTrue((out["high"] >= out["low"]).all())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.btc_chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8882,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.btc_perpetual.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short": true,
|
||||
"timeframe": "1m",
|
||||
"process_only_new_candles": false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "YOUR_BINANCE_API_KEY",
|
||||
"secret": "YOUR_BINANCE_API_SECRET",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "YOUR_TELEGRAM_BOT_TOKEN",
|
||||
"chat_id": "YOUR_TELEGRAM_CHAT_ID"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8820,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "change_me_to_a_random_secret_key",
|
||||
"ws_token": "change_me_to_a_random_ws_token",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "BTC_Perpetual_Bot",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8800,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_15.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8811,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_1m.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"order_types": {
|
||||
"entry": "limit",
|
||||
"exit": "limit",
|
||||
"stoploss": "limit",
|
||||
"stoploss_on_exchange": false
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {
|
||||
"proxies": {
|
||||
"http": "http://127.0.0.1:7897",
|
||||
"https": "http://127.0.0.1:7897"
|
||||
}
|
||||
},
|
||||
"ccxt_async_config": {
|
||||
"aiohttp_proxy": "http://127.0.0.1:7897"
|
||||
},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "8197349375:AAH208JghCq8raFYF-IpnobYknCr6iGDH_0",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8814,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 1
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_15.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8813,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_5m.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "5m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 5,
|
||||
"exit": 5,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"order_types": {
|
||||
"entry": "limit",
|
||||
"exit": "limit",
|
||||
"stoploss": "limit",
|
||||
"stoploss_on_exchange": false
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {
|
||||
"proxies": {
|
||||
"http": "http://127.0.0.1:7897",
|
||||
"https": "http://127.0.0.1:7897"
|
||||
}
|
||||
},
|
||||
"ccxt_async_config": {
|
||||
"aiohttp_proxy": "http://127.0.0.1:7897"
|
||||
},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"internals": {
|
||||
"process_throttle_secs": 5
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_60.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8814,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_k.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8815,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_k.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short": true,
|
||||
"timeframe": "1m",
|
||||
"process_only_new_candles": false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8815,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_15.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8820,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_15.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8820,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_eth_60.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"ETH/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8813,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_15.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "5m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "other",
|
||||
"use_order_book": false,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "other",
|
||||
"use_order_book": false,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8813,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_sol.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8811,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 2,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.95,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_sol_optimized.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short": true,
|
||||
"timeframe": "5m",
|
||||
"process_only_new_candles": false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 2,
|
||||
"exit": 2,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "other",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "",
|
||||
"secret": "",
|
||||
"ccxt_config": {
|
||||
"options": {"defaultType": "swap"}
|
||||
},
|
||||
"ccxt_async_config": {
|
||||
"enableRateLimit": true,
|
||||
"rateLimit": 1000,
|
||||
"timeout": 30000
|
||||
},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": []
|
||||
},
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "",
|
||||
"chat_id": ""
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": false,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8080,
|
||||
"verbosity": "error",
|
||||
"jwt_secret_key": "",
|
||||
"username": "",
|
||||
"password": ""
|
||||
},
|
||||
"discord": {
|
||||
"enabled": false,
|
||||
"webhook": "",
|
||||
"webhook_avatar": "",
|
||||
"poll_delay_seconds": 10
|
||||
},
|
||||
"notification_settings": {
|
||||
"status": "on",
|
||||
"status_inactive_after": 7,
|
||||
"timeframe_condition_change": "on",
|
||||
"telegram": { },
|
||||
"discord": { },
|
||||
"notify_all": true
|
||||
},
|
||||
"bot_name": "SOL_Chan_Optimized",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 5
|
||||
},
|
||||
"edge": {
|
||||
"enabled": false,
|
||||
"process_throttle_secs": 3600,
|
||||
"calculate_since_number_of_days": 7,
|
||||
"allowed_risk": 0.01,
|
||||
"stoploss_range_min": -0.01,
|
||||
"stoploss_range_max": -0.007,
|
||||
"stoploss_range_step": 0.001,
|
||||
"minimum_winrate": 0.60,
|
||||
"minimum_expectancy": 0.20,
|
||||
"min_trade_number": 10,
|
||||
"max_trade_duration_minute": 1440,
|
||||
"remove_pumps": false
|
||||
},
|
||||
"order_types": {
|
||||
"entry": "limit",
|
||||
"exit": "market",
|
||||
"emergency_exit": "market",
|
||||
"force_exit": "market",
|
||||
"force_entry": "market",
|
||||
"stoploss": "market",
|
||||
"stoploss_on_exchange": false,
|
||||
"stoploss_on_exchange_interval": 60
|
||||
},
|
||||
"order_time_in_force": {
|
||||
"entry": "GTC",
|
||||
"exit": "GTC"
|
||||
},
|
||||
"strategy_path": "./user_data/Chan/strategies/",
|
||||
"strategy": "ChanLun_SOL_Optimized",
|
||||
"minimal_roi": {
|
||||
"0": 0.012,
|
||||
"120": 0.010,
|
||||
"240": 0.007,
|
||||
"360": 0.005
|
||||
},
|
||||
"stoploss": -0.007,
|
||||
"trailing_stop": true,
|
||||
"trailing_stop_positive": 0.003,
|
||||
"trailing_stop_positive_offset": 0.005,
|
||||
"trailing_only_offset_is_reached": true,
|
||||
"use_custom_stoploss": true,
|
||||
"max_open_trades_per_pair": 1,
|
||||
"dry_run_wallet_refresh_time": 5,
|
||||
"caches": {
|
||||
"dataframe": {
|
||||
"enabled": true,
|
||||
"refresh_period": 60
|
||||
},
|
||||
"strategy": {
|
||||
"enabled": true,
|
||||
"refresh_period": 300
|
||||
}
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"config": {
|
||||
"pairs": ["SOL/USDT:USDT"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_sol.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : true,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"freqai": {
|
||||
"enabled": true,
|
||||
"purge_old_models": true,
|
||||
"train_period_days": 30,
|
||||
"backtest_period_days": 7,
|
||||
"identifier": "chanLun1",
|
||||
"live_retrain_hours": 1,
|
||||
"expiration_hours": 48,
|
||||
"fit_live_predictions_candles": 0,
|
||||
"data_kitchen_thread_count": 4,
|
||||
"save_backtest_models": true,
|
||||
"save_metadata": true,
|
||||
"feature_parameters": {
|
||||
"include_timeframes": [
|
||||
"1m",
|
||||
"5m",
|
||||
"15m"
|
||||
],
|
||||
"include_corr_pairlist": [
|
||||
"BTC/USDT:USDT",
|
||||
"ETH/USDT:USDT"
|
||||
],
|
||||
"label_period_candles": 24,
|
||||
"include_shifted_candles": 2,
|
||||
"indicator_periods_candles": [10, 20, 30],
|
||||
"allow_duplicate_train": true
|
||||
},
|
||||
"data_split_parameters": {
|
||||
"test_size": 0.25
|
||||
},
|
||||
"model_training_parameters": {
|
||||
"n_estimators": 100,
|
||||
"learning_rate": 0.1,
|
||||
"max_depth": 5,
|
||||
"subsample": 0.8,
|
||||
"colsample_bytree": 0.8,
|
||||
"use_label_for_weight": true,
|
||||
"booster": "gbtree",
|
||||
"num_class": 2
|
||||
}
|
||||
},
|
||||
"freqaimodel": "XGBoostClassifier",
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8811,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 3,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.btc_chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "30m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "ocQUqAPSD9PDhIL2lTMlMan0wMFwvvu5Fv8eYF3wUM8yPytm2jBgz51cgiHXw7J6",
|
||||
"secret": "yHIc6FOnSoOI2FvygpRKKku4FKaZGI5DSwC83Ip4wRfUcxszennF6hy2vhbVuLYJ",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT",
|
||||
"ETH/USDT:USDT",
|
||||
"SOL/USDT:USDT",
|
||||
"WIF/USDT:USDT",
|
||||
"1000PEPE/USDT:USDT",
|
||||
"DOGS/USDT:USDT",
|
||||
"ORDI/USDT:USDT",
|
||||
"AAVE/USDT:USDT",
|
||||
"REEF/USDT:USDT",
|
||||
"1000SATS/USDT:USDT",
|
||||
"SUI/USDT:USDT",
|
||||
"1INCH/USDT:USDT",
|
||||
"DOGE/USDT:USDT",
|
||||
"TON/USDT:USDT",
|
||||
"UNI/USDT:USDT",
|
||||
"XRP/USDT:USDT",
|
||||
"SUN/USDT:USDT",
|
||||
"NOT/USDT:USDT",
|
||||
"RARE/USDT:USDT",
|
||||
"RDNT/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "VolumePairList",
|
||||
"number_assets": 10,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8088,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "5m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "5985766683:AAEx2Nm_4y2IC0Tj4Hhz7djVRJRso0JKaj0",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8088,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.btc_chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": false,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8888,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"strategy": "ChanStrategy",
|
||||
"db_url": "sqlite:///tradesv3.btc_chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": false,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8818,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8815,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDC",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "hyperliquid",
|
||||
"walletAddress": "0xA834b6d3Fa1D8A55ea8e502685ef5cbD2b2D3343",
|
||||
"privateKey": "0xa399cea4c01be67c16b88e1d2121ed6e72bab6b4e8d81684ed03ce78f8b4f827",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"PURR/USDC:USDC",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8815,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chan_sol_30.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8818,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chan.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800,
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8801,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 3,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"db_url": "sqlite:///tradesv3.deepseek_trader.sqlite",
|
||||
"dry_run": true,
|
||||
"dry_run_wallet": 10000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 0,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT",
|
||||
"ETH/USDT:USDT",
|
||||
"SOL/USDT:USDT",
|
||||
"WIF/USDT:USDT",
|
||||
"1000PEPE/USDT:USDT",
|
||||
"DOGS/USDT:USDT",
|
||||
"ORDI/USDT:USDT",
|
||||
"AAVE/USDT:USDT",
|
||||
"REEF/USDT:USDT",
|
||||
"1000SATS/USDT:USDT",
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 10,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "5985766683:AAEx2Nm_4y2IC0Tj4Hhz7djVRJRso0JKaj0",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8001,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": true,
|
||||
"internals": {
|
||||
"process_throttle_secs": 15
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.ema26_ema52_cross.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8820,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.ema_pattern.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1h",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8888,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 2,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.elliottwave_btc.sqlite",
|
||||
"dry_run_wallet": 10000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"unfilledtimeout": {
|
||||
"entry": 5,
|
||||
"exit": 5,
|
||||
"exit_timeout_count": 3,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "",
|
||||
"secret": "",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "",
|
||||
"chat_id": ""
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": false,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8080,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "freqtrade_secret",
|
||||
"ws_token": "freqtrade_ws",
|
||||
"username": "freqtrade",
|
||||
"password": "freqtrade"
|
||||
},
|
||||
"bot_name": "ElliottWaveBTC"
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.freqai_sol.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short": true,
|
||||
"timeframe": "5m",
|
||||
"process_only_new_candles": true,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "",
|
||||
"secret": "",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList"
|
||||
}
|
||||
],
|
||||
"freqai": {
|
||||
"enabled": true,
|
||||
"purge_old_models": 2,
|
||||
"train_period_days": 10,
|
||||
"backtest_period_days": 7,
|
||||
"live_retrain_hours": 1,
|
||||
"identifier": "sol_futures_lgbm_v1",
|
||||
"feature_parameters": {
|
||||
"include_timeframes": [
|
||||
"5m",
|
||||
"15m"
|
||||
],
|
||||
"include_corr_pairlist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"label_period_candles": 12,
|
||||
"include_shifted_candles": 1,
|
||||
"DI_threshold": 0.9,
|
||||
"weight_factor": 0.9,
|
||||
"principal_component_analysis": false,
|
||||
"use_SVM_to_remove_outliers": true,
|
||||
"indicator_periods_candles": [
|
||||
14
|
||||
],
|
||||
"plot_feature_importances": 0
|
||||
},
|
||||
"data_split_parameters": {
|
||||
"test_size": 0.15,
|
||||
"random_state": 42
|
||||
},
|
||||
"model_training_parameters": {
|
||||
"n_estimators": 300,
|
||||
"learning_rate": 0.05,
|
||||
"max_depth": 5,
|
||||
"num_leaves": 31,
|
||||
"min_child_samples": 20,
|
||||
"subsample": 0.8,
|
||||
"colsample_bytree": 0.8,
|
||||
"reg_alpha": 0.1,
|
||||
"reg_lambda": 0.1,
|
||||
"n_jobs": 1,
|
||||
"verbosity": -1
|
||||
}
|
||||
},
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "",
|
||||
"chat_id": ""
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8822,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqai_sol",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 5
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.heikinashi_btc.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "1m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8814,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.ema26_ema52_cross.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"timeframe": "1m",
|
||||
"can_short" : true,
|
||||
"process_only_new_candles" : true,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "other",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "other",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"BTC/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8821,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.sol5m.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short": true,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "other",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing": {
|
||||
"price_side": "other",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"SOL/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "0.0.0.0",
|
||||
"listen_port": 8822,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "SOL5m",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 5
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"max_open_trades": 1,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": "unlimited",
|
||||
"tradable_balance_ratio": 0.99,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite:///tradesv3.chanlun_btc_15.sqlite",
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "15m",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
"exit": 1,
|
||||
"exit_timeout_count": 5,
|
||||
"unit": "minutes"
|
||||
},
|
||||
"entry_pricing": {
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1,
|
||||
"price_last_balance": 0.0,
|
||||
"check_depth_of_market": {
|
||||
"enabled": false,
|
||||
"bids_to_ask_delta": 1
|
||||
}
|
||||
},
|
||||
"exit_pricing":{
|
||||
"price_side": "same",
|
||||
"use_order_book": true,
|
||||
"order_book_top": 1
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8",
|
||||
"secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l",
|
||||
"ccxt_config": {},
|
||||
"ccxt_async_config": {},
|
||||
"pair_whitelist": [
|
||||
"WIF/USDT:USDT"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BNB/.*"
|
||||
]
|
||||
},
|
||||
"pairlists": [
|
||||
{
|
||||
"method": "StaticPairList",
|
||||
"number_assets": 1,
|
||||
"sort_key": "quoteVolume",
|
||||
"min_value": 0,
|
||||
"refresh_period": 1800
|
||||
}
|
||||
],
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
|
||||
"chat_id": "580807463"
|
||||
},
|
||||
"api_server": {
|
||||
"enabled": true,
|
||||
"listen_ip_address": "127.0.0.1",
|
||||
"listen_port": 8811,
|
||||
"verbosity": "error",
|
||||
"enable_openapi": false,
|
||||
"jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d",
|
||||
"ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg",
|
||||
"CORS_origins": [],
|
||||
"username": "freqtrader",
|
||||
"password": "FreqTrade007"
|
||||
},
|
||||
"bot_name": "freqtrade",
|
||||
"initial_state": "running",
|
||||
"force_entry_enable": false,
|
||||
"internals": {
|
||||
"process_throttle_secs": 2
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,82 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 缠论分析系统生产环境部署脚本
|
||||
|
||||
# 显示执行的命令
|
||||
set -x
|
||||
|
||||
# 确保脚本在错误时停止
|
||||
set -e
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_DIR=$(pwd)
|
||||
echo "项目将部署在: $PROJECT_DIR"
|
||||
|
||||
# 创建虚拟环境
|
||||
echo "创建Python虚拟环境..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# 安装依赖
|
||||
echo "安装依赖包..."
|
||||
pip install --upgrade pip
|
||||
pip install flask pandas matplotlib ccxt pytz talib-binary gunicorn
|
||||
|
||||
# 安装任何额外的系统依赖
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y python3-dev
|
||||
|
||||
# 检查目录结构
|
||||
echo "检查目录结构..."
|
||||
mkdir -p web/templates
|
||||
|
||||
# 创建日志目录
|
||||
mkdir -p logs
|
||||
|
||||
# 创建启动脚本
|
||||
echo "创建启动脚本..."
|
||||
cat > start_service.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 缠论分析系统启动脚本
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_DIR=$(pwd)
|
||||
cd $PROJECT_DIR
|
||||
|
||||
# 激活虚拟环境
|
||||
source venv/bin/activate
|
||||
|
||||
# 启动服务
|
||||
cd web
|
||||
echo "启动缠论分析系统服务..."
|
||||
gunicorn app:app --bind=0.0.0.0:8123 --workers=4 --timeout=120 --log-level=info --log-file=logs/chanlun.log --daemon
|
||||
|
||||
echo "服务已启动,端口8123"
|
||||
echo "日志文件位置: $PROJECT_DIR/web/logs/chanlun.log"
|
||||
EOF
|
||||
|
||||
# 创建停止脚本
|
||||
echo "创建停止脚本..."
|
||||
cat > stop_service.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 停止缠论分析系统服务
|
||||
|
||||
echo "停止缠论分析系统服务..."
|
||||
pkill -f "gunicorn app:app"
|
||||
echo "服务已停止"
|
||||
EOF
|
||||
|
||||
# 添加执行权限
|
||||
chmod +x start_service.sh
|
||||
chmod +x stop_service.sh
|
||||
|
||||
# 修改app.py中的调试模式(生产环境应关闭调试模式)
|
||||
if [ -f web/app.py ]; then
|
||||
echo "配置app.py为生产环境模式..."
|
||||
sed -i 's/app.run(debug=True, host='\''0.0.0.0'\'', port=8123)/# 在生产环境中,使用gunicorn启动服务\n# app.run(debug=False, host='\''0.0.0.0'\'', port=8124)/' user_data/Chan/web/app.py
|
||||
else
|
||||
echo "警告: 未找到app.py文件"
|
||||
fi
|
||||
|
||||
echo "部署完成!"
|
||||
echo "使用 ./start_service.sh 启动服务"
|
||||
echo "使用 ./stop_service.sh 停止服务"
|
||||
@@ -1,22 +0,0 @@
|
||||
# ADR-001: 包布局与兼容 shim
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-08-05
|
||||
**ECR:** ECR-001
|
||||
|
||||
## Context
|
||||
|
||||
根目录扁平模块被 strategies 与 web 通过模块名直接 import;完全改名会破坏 Freqtrade 策略。需要正式包边界,同时零改 `strategies/`。
|
||||
|
||||
## Decision
|
||||
|
||||
1. 正式包名:`chanlun`(`core` / `pipeline` / `indicators` / `analysis`)。
|
||||
2. 根目录保留同名 shim 文件,再导出公共符号。
|
||||
3. Web 使用 Flask blueprints + services;前端 JS 模块化,不引入 TS 构建。
|
||||
4. `TF_DF` 保留门面类名与公开方法,内部委托 builders。
|
||||
|
||||
## Consequences
|
||||
|
||||
- 策略无需修改。
|
||||
- 长期可逐步引导新代码 `from chanlun import ...`。
|
||||
- shim 需保持至策略侧显式迁移(另立 ECR)。
|
||||
@@ -1,39 +0,0 @@
|
||||
# AGENT_MEMORY — chan
|
||||
|
||||
> Agent 短记忆。先读 `PROJECT_PROFILE.md`,再读本文件。不要把猜测写进这里。
|
||||
|
||||
## 双前端
|
||||
|
||||
| 入口 | 引擎 | 实时 |
|
||||
|------|------|------|
|
||||
| `/` | Lightweight Charts | HTTP 定时自动刷新(增量 + 每 6 次全量) |
|
||||
| `/chan_tv` | Charting Library 全版 | datafeed `subscribeBars` → WS |
|
||||
|
||||
勿把主站 `live_feed` 方案与 chan_tv datafeed 混为一谈;主站 WS 实时已回退。
|
||||
|
||||
## 版本
|
||||
|
||||
- `system_version`:`v1.0.0`(ECR-001)
|
||||
- `strategy_version`:与 system 解耦;默认不改 `config/` / `strategies/`
|
||||
|
||||
## 近期变更
|
||||
|
||||
- IDEA-002 / `9f1e736`:主站内存泄漏 dispose、首屏单次 analyze、ChanMACD 复用、chan_tv 体验
|
||||
- ECR-002 Reviewed:拆 `web/services/runtime/`、加深 analyze 契约
|
||||
- ECR-003 Reviewed:主站威科夫叠层(`chanlun/analysis/wyckoff/` + `include_wyckoff`)→ `081a57a`
|
||||
- ECR-004 Reviewed:TR 评分硬化 + VP 少系列 + 阶段/门闩/单测(无币种参数)
|
||||
|
||||
## 硬约束提醒
|
||||
|
||||
- `/api/analyze` 字段可增不可删
|
||||
- 无 ADR 不改笔/段/中枢/买卖点语义
|
||||
- 威科夫为独立叠层(ECR-003);勿借机改缠论算法
|
||||
- 交易 L2+ → RISK_REVIEW + EXP;Live 须 Human
|
||||
|
||||
## 已知债务
|
||||
|
||||
- `chart_tv.js` 单体巨大 → 后续可选 ECR
|
||||
- analyze 契约已加深(mock HTTP + wyckoff opt-in);可再加固定 JSON 快照文件
|
||||
- 内存泄漏尚无自动化 heap/监听断言
|
||||
- `macd_config` POST 写本地 global 的历史 quirks(未改)
|
||||
- 威科夫启发式参数未做 UI 调参
|
||||
@@ -1,73 +0,0 @@
|
||||
# CHANGELOG
|
||||
|
||||
## Unreleased — 2026-08-06
|
||||
|
||||
### ECR-004(L2,Reviewed)
|
||||
|
||||
- 威科夫 TR 评分选段(防吞前置趋势);阶段非重叠最小跨度
|
||||
- 主站 VP Top-8 + bins≤24;填充线减负
|
||||
- `elements_only` 时不跑威科夫;收紧单测(无币种独立参数)
|
||||
|
||||
### ECR-003(L2,Reviewed)
|
||||
|
||||
- 新增 `chanlun/analysis/wyckoff/`:交易区间、阶段 A–E、Spring/SOS/LPS/UTAD 等事件、区间 VP(POC/VAH/VAL)、量能确认
|
||||
- `/api/analyze` 按需 `include_wyckoff=1` 返回顶层 `wyckoff`
|
||||
- 主站「威科夫」开关与 Lightweight 叠层(区间/阶段/事件/VP)
|
||||
- 单测与 analyze 契约 opt-in 断言
|
||||
|
||||
### ECR-002(L3,Reviewed)
|
||||
|
||||
- 拆分 `web/services/runtime.py` 为包 `web/services/runtime/`(state / timeframes / market_data / indicators / analyze / serialize)
|
||||
- 加深 analyze 契约测试(mock HTTP + analyze_chan 键集 + serialize JSON)
|
||||
- 新增 TF_DF 全量 init 冒烟与 runtime 门面测试
|
||||
|
||||
### IDEA-002(L1 补档)
|
||||
|
||||
对应 commit `9f1e736`。无新 system tag(仍为 `v1.0.0`)。
|
||||
|
||||
#### Fixed
|
||||
|
||||
- 主站自动刷新内存泄漏:`disposeTradingViewCharts`、去掉重复 sync 监听、默认增量刷新(每 6 次全量重建笔/段/中枢)
|
||||
- 加密货币首屏重复调用 `/api/analyze`
|
||||
- ChanMACD 同周期重复全量分析(复用 `get_klc_list` 结果)
|
||||
|
||||
#### Changed
|
||||
|
||||
- `/chan_tv`:WS/REST 可分离配置、指标布局 localStorage、未完成中枢与 datafeed 实时 tick 行为完善
|
||||
- `PROJECT_PROFILE` Realtime 条目与 chan_tv WS 对齐(文档)
|
||||
|
||||
#### Docs
|
||||
|
||||
- ESS:IDEA-002、AGENT_MEMORY、AGENTS;ECR-002 实现与报告
|
||||
|
||||
---
|
||||
|
||||
## v1.0.0 — 2026-08-05(首个正式 Release)
|
||||
|
||||
对应 ECR-001 / tag `v1.0.0`。详见 `docs/RELEASE/ECR-001-v1.0.0.md`。
|
||||
|
||||
### Added
|
||||
|
||||
- 正式包 `chanlun/`(core / pipeline / builders / indicators / analysis)
|
||||
- ESS 文档树 `docs/`(PROFILE / ECR / SPEC / ADR / HANDOFF / CODE_REVIEW / RELEASE)
|
||||
- Web `config.py`、`services/`、`api/` blueprints
|
||||
- 前端 `web/static/js/app/` 模块
|
||||
- Golden 回归 `tests/generate_golden.py` + fixtures
|
||||
|
||||
### Changed
|
||||
|
||||
- 根目录 `Chan*.py` / `TF_DF.py` 改为兼容 shim
|
||||
- `TF_DF` 实现拆至 builders,门面签名保持
|
||||
- `web/app.py` 瘦身为 `create_app()`
|
||||
- `index.html` 去掉巨型 inline 业务 JS
|
||||
- HTTP 代理改为环境变量配置
|
||||
- strategies / web / tests / examples → `from chanlun...` 导入
|
||||
|
||||
### Fixed
|
||||
|
||||
- 恢复缺失的 `TF_DF.get_zs_list`(委托 `get_seg_zs_list`)
|
||||
|
||||
### Moved
|
||||
|
||||
- 示例 → `examples/`;笔记 → `docs/notes/`
|
||||
- 未接线 TSX/TS → `static/js/_unused/`
|
||||
@@ -1,75 +0,0 @@
|
||||
# CODE_REVIEW — ECR-001
|
||||
|
||||
**Role:** REVIEWER
|
||||
**Date:** 2026-08-05
|
||||
**Commit:** `74dec4e` (`refactor: 缠论引擎包化与 Web 分层(ECR-001)`)
|
||||
**Decision:** Approve
|
||||
|
||||
## Evidence loaded
|
||||
|
||||
- `docs/ECR/ECR-001-chan-web-restructure.md`
|
||||
- `docs/ENGINEERING_SPEC/ECR-001-restructure.md`
|
||||
- `docs/IMPLEMENTATION_REPORT/ECR-001.md`
|
||||
- `docs/TEST_REPORT/ECR-001.md`
|
||||
- `docs/HANDOFF/ECR-001-engineer-to-reviewer.md`
|
||||
- Diff `e2e45bc..74dec4e`;本地复跑测试
|
||||
|
||||
## Acceptance ↔ Evidence
|
||||
|
||||
| Acceptance | Verdict | Evidence |
|
||||
|------------|---------|----------|
|
||||
| `from ChanLun import ChanLun` / `ChanEnum` 仍可用 | PASS | 复跑 shim+package 同一对象;`test_compat_shim_still_works` |
|
||||
| Golden bi/seg/zs/bsp 与基线一致 | PASS | `python tests/generate_golden.py --check` → GOLDEN OK;pytest 含 golden |
|
||||
| `/api/analyze` 关键字段兼容 | PASS | `analyze_contract_keys.json` + 路由注册冒烟(未做实盘拉行情 E2E,见 Findings) |
|
||||
| `web/app.py` 瘦身 factory | PASS | `web/app.py` 32 行;`create_app` + blueprints |
|
||||
| `index.html` 无大体量 inline 业务 JS | PASS | ~1482 行;业务在 `static/js/app/*` |
|
||||
| ESS docs / TEST / IMPL / CHANGELOG | PASS | `docs/` 齐全 |
|
||||
| `config/` 无内容变更 | PASS | `git diff e2e45bc..HEAD -- config` 空 |
|
||||
| `strategies/` 无内容变更 | **AMENDED** | 见下「范围修订」 |
|
||||
|
||||
## 范围修订(Human 后续指示)
|
||||
|
||||
原 ECR Forbidden 写「不改 strategies/」。实现后期 Human 要求「一次性做完」导入迁移:strategies 仅改 import / `sys.path`(26 files, +75/−75),**无策略交易逻辑变更**。
|
||||
审阅结论:视为 **L3 结构收尾的允许增补**,不构成交易语义 L2;建议 ECR Acceptance 改为「strategies 仅允许 import/path 迁移,禁止改买卖逻辑」。
|
||||
**不据此 Request changes。**
|
||||
|
||||
## 复跑结果(Reviewer)
|
||||
|
||||
```text
|
||||
shim+package OK
|
||||
GOLDEN OK {klu:400, klc:208, bi:14, seg:2, zs:0, bsp:3}
|
||||
pytest tests/test_golden_pipeline.py web/tests/test_analyze_contract.py → 6 passed
|
||||
```
|
||||
|
||||
## Findings
|
||||
|
||||
### Non-blocking(记入债务,需新 ECR 再动)
|
||||
|
||||
1. **`web/services/runtime.py` ~1176 行** — 已从 app 抽出但仍是大模块;facade 再导出符合计划 → **已起草 `docs/ECR/ECR-002-runtime-split.md`(Draft)**。
|
||||
2. **`web/static/js/app/chart_tv.js` ~4664 行** — `initTradingView` 单体;行为冻结下可接受;ECR-002 可选范围。
|
||||
3. **`/api/analyze` 契约测试偏浅** — 仅关键字段清单 + 路由存在;无固定 fixture 的端到端 JSON 快照(需 mock 行情)→ ECR-002。
|
||||
4. **TEST_REPORT 写「5 passed」** — 现为 6(含 shim 兼容测);Release 前可改正文(L0 docs)。
|
||||
5. **L1:`TF_DF.get_zs_list` 恢复** — 合理兼容修复;golden 走 analyze 路径未覆盖 `TF_DF(df,...)` 全量 `__init__` → ECR-002 Acceptance。
|
||||
|
||||
### No blockers
|
||||
|
||||
未发现违反「算法语义冻结 / API 可增不可删 / 无 Vite-React / config 未改」的证据。
|
||||
|
||||
## Decision
|
||||
|
||||
**Approve**
|
||||
|
||||
- ECR-001 可进入 Release(本变更无交易 EXP 门禁)。
|
||||
- 非阻断项进入 backlog / 未来 ECR,不阻塞 tag。
|
||||
|
||||
## Next owner
|
||||
|
||||
`release_manager` — 写 RELEASE_REPORT、打 tag(需 Human 确认发布动作)。
|
||||
|
||||
## Traceability
|
||||
|
||||
| Item | Updated |
|
||||
|------|---------|
|
||||
| Acceptance mapping | 本文件 |
|
||||
| STATE.owner | → release_manager |
|
||||
| ECR Status | → Done (Reviewed) |
|
||||
@@ -1,74 +0,0 @@
|
||||
# CODE_REVIEW — ECR-002
|
||||
|
||||
**Role:** REVIEWER
|
||||
**Date:** 2026-08-06
|
||||
**Scope:** 工作区未提交实现(相对 `HEAD`/`9f1e736`);包 `web/services/runtime/` + 测试 + ESS 文档
|
||||
**Decision:** Approve
|
||||
|
||||
## Evidence loaded
|
||||
|
||||
- `docs/ECR/ECR-002-runtime-split.md`
|
||||
- `docs/ENGINEERING_SPEC/ECR-002-runtime-split.md`
|
||||
- `docs/IMPLEMENTATION_REPORT/ECR-002.md`
|
||||
- `docs/TEST_REPORT/ECR-002.md`
|
||||
- `docs/HANDOFF/ECR-002-engineer-to-reviewer.md`
|
||||
- 包源码:`web/services/runtime/{__init__,state,timeframes,market_data,indicators,analyze,serialize}.py`
|
||||
- Diff:删除 `web/services/runtime.py`;新增包与测试
|
||||
|
||||
## Acceptance ↔ Evidence
|
||||
|
||||
| Acceptance | Verdict | Evidence |
|
||||
|------------|---------|----------|
|
||||
| runtime 门面公开符号兼容(含历史 `import *` 漏出) | PASS | 手工核对 api 所需符号;`timezone`/`OrderedDict`/`np`/`StructureZone*`/`ThreadPoolExecutor` 等在门面;`test_runtime_facade` |
|
||||
| Golden 通过 | PASS | 复跑 `tests/test_golden_pipeline.py` |
|
||||
| Analyze 契约加深 | PASS | `test_analyze_contract`:键清单 + analyze_chan 键集 + serialize JSON + mock HTTP |
|
||||
| TF_DF 全量 init 冒烟 | PASS | `tests/test_tf_df_init.py`(`interval=1`) |
|
||||
| config/strategies 无交易逻辑 diff | PASS | 工作区无 `config/`/`strategies/` 变更 |
|
||||
| IMPL / TEST / CHANGELOG / TRACEABILITY | PASS | docs 已落盘 |
|
||||
| CODE_REVIEW Approve | PASS | 本文件 |
|
||||
|
||||
## 复跑结果(Reviewer)
|
||||
|
||||
```text
|
||||
PYTHONPATH=.:web python -m pytest \
|
||||
tests/test_golden_pipeline.py \
|
||||
tests/test_tf_df_init.py \
|
||||
web/tests/test_runtime_facade.py \
|
||||
web/tests/test_analyze_contract.py -q
|
||||
→ 13 passed
|
||||
```
|
||||
|
||||
算法冻结抽查:`analyze.py` 仍为 `cal_bi_zs(seg_list)` + `_last_chan_macd` 复用;未改笔段中枢语义。
|
||||
|
||||
## Findings
|
||||
|
||||
### Non-blocking(不挡 Approve)
|
||||
|
||||
1. **门面标量同步只做一次** — `__init__` 在首次 `refresh` 后把 `DATA_SERVICE_AVAILABLE` / `macd_*` 写入模块 dict;之后 `refresh_data_service_metadata` 只改 `state.*`。通过 `R.DATA_SERVICE_AVAILABLE` 读取可能与 state 短期不一致;`from services.runtime import *` 的 bool 拷贝问题在 monolith 时代已存在。建议后续 L1:在 `refresh` 末尾同步写回门面模块,或让标量只经 `state`/`__getattr__` 暴露。
|
||||
2. **`__getattr__` 对已绑定名无效** — 与上条相关;属清理项。
|
||||
3. **`chart_tv.js` 拆分未做** — ECR 明确可选;继续记入 backlog。
|
||||
4. **契约测试仍无「固定 JSON 快照文件」** — 已有 mock HTTP + 键集,比 ECR-001 深;完整响应快照可另开 L1/ECR。
|
||||
5. **`web/tests/test_cn_stock_data_fetch.py` 仍因旧 `user_data.Chan...` 路径无法收集** — 既有问题,非本 ECR 引入。
|
||||
|
||||
### No blockers
|
||||
|
||||
未发现违反「算法语义冻结 / API 可增不可删 / 无 Vite-React / 未动 strategies·config / 未引主站 WS」的证据。
|
||||
|
||||
## Decision
|
||||
|
||||
**Approve**
|
||||
|
||||
- ECR-002 可标 Done(Reviewed);不强制新 system tag(仍为 `v1.0.0` Unreleased 文档变更)。
|
||||
- 非阻断项进 backlog;不阻塞合并本实现。
|
||||
|
||||
## Next owner
|
||||
|
||||
`engineer` / Human — 提交合并;若要发版再交 `release_manager`(本 ECR 未要求 bump tag)。
|
||||
|
||||
## Traceability
|
||||
|
||||
| Item | Updated |
|
||||
|------|---------|
|
||||
| Acceptance mapping | 本文件 |
|
||||
| STATE.owner | → idle / merge |
|
||||
| ECR Status | → Done (Reviewed) |
|
||||
@@ -1,77 +0,0 @@
|
||||
# CODE_REVIEW — ECR-003
|
||||
|
||||
**Role:** REVIEWER
|
||||
**Date:** 2026-08-06
|
||||
**Scope:** 工作区未提交 ECR-003(相对 `origin/dev` @ `df27b4d`)
|
||||
**Decision:** Approve(带非阻断 Findings;建议合并前勿提交 `.DS_Store`)
|
||||
|
||||
## Evidence loaded
|
||||
|
||||
- `chanlun/analysis/wyckoff/{engine,range,events,volume_profile}.py`
|
||||
- `web/api/analyze.py`(`include_wyckoff`)
|
||||
- `web/templates/index.html`、`chart_view.js`、`macd_ui.js`、`chart_tv.js` 威科夫块
|
||||
- `tests/test_wyckoff.py`、`web/tests/test_analyze_contract.py`
|
||||
- ESS:ECR/PRODUCT/ENG/IMPL/TEST/HANDOFF
|
||||
|
||||
## Acceptance ↔ Evidence
|
||||
|
||||
| Acceptance | Verdict | Evidence |
|
||||
|------------|---------|----------|
|
||||
| `include_wyckoff=1` 返回约定键;默认不强制 | PASS | 契约测试;默认无 `wyckoff` 键 |
|
||||
| 合成 TR + 事件;VP POC | PASS | `test_wyckoff.py`(12 相关套件全绿) |
|
||||
| 主站可开关绘制 | PASS | 主开关按需拉取;子项本地重绘 |
|
||||
| golden 不变 | PASS | `test_golden_pipeline` |
|
||||
| 未改缠论算法 / strategies / chan_tv | PASS | diff 范围核对 |
|
||||
| ESS 闭环 | PASS | IMPL/TEST/TRACE/CHANGELOG/本文件 |
|
||||
|
||||
## 复跑
|
||||
|
||||
```text
|
||||
PYTHONPATH=.:web python -m pytest \
|
||||
tests/test_wyckoff.py tests/test_golden_pipeline.py \
|
||||
web/tests/test_analyze_contract.py -q
|
||||
→ 12 passed
|
||||
```
|
||||
|
||||
## Findings
|
||||
|
||||
### Important(不挡 Approve,建议跟进)
|
||||
|
||||
1. **交易区间易吞并前置趋势**
|
||||
`detect_trading_range` 从最长窗口向下搜,合成夹具下 `abs_start_idx=0`,箱体前下跌段被算进 TR。单测只断言「有区间 + 有事件」,未锁定高低/起点。
|
||||
*建议:* 用「宽度/触边密度」评分取最优段,或要求近端触边;测试断言 `high≈60/low≈40` 与起点靠近箱体。
|
||||
|
||||
2. **VP 叠层系列数偏多,可能加压自动刷新内存**
|
||||
开启 VP 时约每个 bin 一条 `addLineSeries`(默认 ~50),再加区间填充/阶段。与 IDEA-002 内存修复同路径全量重建时放大。
|
||||
*建议:* 只画非零 bin 或合并为少量 series / histogram;或限制 `vp_bins` 上限到 24。
|
||||
|
||||
### Medium
|
||||
|
||||
3. **阶段 C–E 在事件扎堆时常退化重叠**
|
||||
夹具输出中 D/E 起止几乎相同;状态机按事件锚点硬切,缺少最小阶段长度。展示可用,语义偏弱。
|
||||
|
||||
4. **`elements_only=true` 仍可能跑威科夫**
|
||||
威科夫挂在路由末尾,不依赖 `not elements_only`。主站当前不这么发,但契约上奇怪;建议与主周期分析同门闩。
|
||||
|
||||
5. **单测断言偏松**
|
||||
`Spring in types or SOS`、`abs(poc-50)<2` 对回归保护不足。
|
||||
|
||||
### Low
|
||||
|
||||
6. 失败时 `wyckoff.error` 回传异常字符串(与结构区 print 风格一致,信息暴露轻微)。
|
||||
7. 事件 marker 一律 `arrowUp`(跌破类也可 `arrowDown`)。
|
||||
8. 工作区 `.DS_Store` 脏文件——**勿纳入 commit**。
|
||||
|
||||
### No blockers
|
||||
|
||||
未发现:契约删键、缠论语义改动、策略/config 改动、未鉴权危险写操作、主站误引 WS。
|
||||
|
||||
## Decision
|
||||
|
||||
**Approve**
|
||||
|
||||
可合并提交(排除 `.DS_Store`)。Important #1/#2 可开后续 L1/L2,不阻塞本 ECR 着陆。
|
||||
|
||||
## Next owner
|
||||
|
||||
`engineer` / Human — commit(勿含 `.DS_Store`);可选跟进 TR 评分与 VP 绘图优化。
|
||||
@@ -1,53 +0,0 @@
|
||||
# CODE_REVIEW — ECR-004
|
||||
|
||||
**Role:** REVIEWER
|
||||
**Date:** 2026-08-06
|
||||
**Scope:** `d3188ca`(相对 ECR-003)威科夫硬化
|
||||
**Decision:** Approve
|
||||
|
||||
## Evidence loaded
|
||||
|
||||
- Diff `d3188ca`:`range.py` / `events.py` / `analyze.py` / `chart_tv.js` / tests / ESS
|
||||
- 复跑:`tests/test_wyckoff.py` + golden + analyze contract → **14 passed**
|
||||
- 合成夹具抽查:`abs_start_idx=20`,low/high≈40.1/59.9(相对 003 的 bar0 已修好)
|
||||
|
||||
## Acceptance ↔ Evidence
|
||||
|
||||
| Acceptance | Verdict | Evidence |
|
||||
|------------|---------|----------|
|
||||
| TR 不吞明显前置趋势;边界近箱体 | PASS | 评分选段;单测 low/high 带 + `abs_start≥12` + start 时间容差 |
|
||||
| VP series 减负 | PASS | Top-8 + 填充 3 + POC/VAH/VAL;API bins≤24 |
|
||||
| 阶段最小跨度 / 不重合 | PASS | 链式 cursor;unique (start,end) 断言 |
|
||||
| elements_only 门闩 | PASS | `include_wyckoff and not elements_only` + 契约测试 |
|
||||
| golden 不变 / 无策略改动 / 无币种表 | PASS | golden 绿;diff 无 config/strategies |
|
||||
|
||||
## Findings
|
||||
|
||||
### Medium(不挡 Approve)
|
||||
|
||||
1. **同分 tie-break 偏向更长窗口**
|
||||
循环从长到短,`score <= best_score` 时保留已有(更长)。多数情况分数拉开;若实盘出现「长窗与短窗同分」,仍可能略偏长。可选:同分取更短,或加 `1/length` 微项。
|
||||
|
||||
2. **阶段常截断为 A–C**
|
||||
Spring/SOS 落在尾部时 D/E 因 `min_span` 被吃掉——与 ENG「空间不足截断」一致,但 UI 勾选「阶段」时用户可能期望总见 D/E。属产品预期,非缺陷;可在 UI/文档标明「尾部不足则省略」。
|
||||
|
||||
### Low
|
||||
|
||||
3. **`abs_start_idx >= 12` 弱于「箱体起点」** —— 主测已用时间容差;该断言可再收紧到 `>= 16` 一类。
|
||||
4. **VP Top-N 无自动化 series 计数** —— 靠代码审查 + ENG 约定。
|
||||
5. 事件 marker 仍一律 `arrowUp`(003 遗留)。
|
||||
6. 失败路径仍回传 `wyckoff.error` 字符串。
|
||||
|
||||
### No blockers
|
||||
|
||||
未发现契约删键、缠论语义改动、策略改动、或回归红灯。
|
||||
|
||||
## Decision
|
||||
|
||||
**Approve**
|
||||
|
||||
ECR-004 可维持 Done (Reviewed)。Medium 项进 backlog,不必立刻新 ECR,除非实盘 TR 仍偏长。
|
||||
|
||||
## Next owner
|
||||
|
||||
Human — 主站 BTC 勾选威科夫目测;无发版要求则保持 `v1.0.0` Unreleased 累计。
|
||||
@@ -1,70 +0,0 @@
|
||||
# ECR-001
|
||||
|
||||
**Title:** 缠论引擎包化 + Web 分层重构(行为冻结)
|
||||
**Status:** Approved
|
||||
**Date:** 2026-08-05
|
||||
**Change Level:** L3
|
||||
|
||||
## Change
|
||||
|
||||
将根目录扁平 `Chan*.py` / `TF_DF.py` 包化为 `chanlun/`,拆分 `web/app.py` 与巨型 `index.html` inline JS;算法与 `/api/analyze` 契约冻结;`config/` / `strategies/` 不动。
|
||||
|
||||
## Motivation
|
||||
|
||||
根目录与 Web 单体过大、职责混杂,难以维护与测试;需在不影响 Freqtrade 策略导入的前提下重整结构。
|
||||
|
||||
## Scope
|
||||
|
||||
### Allowed
|
||||
|
||||
- 创建 `chanlun/`(core / pipeline / indicators / analysis)与根目录兼容 shim
|
||||
- 拆分 `TF_DF` 为 builders + 门面(公开方法签名不变)
|
||||
- Web:`config` + `services` + `api` blueprints;配置外置(proxy / DATA_SERVICE)
|
||||
- 前端:`index.html` 业务 JS 外置到 `static/js/app/`;隔离未接线 TS/TSX
|
||||
- 示例与笔记迁入 `examples/` / `docs/notes/`
|
||||
- Golden / 契约回归测试
|
||||
|
||||
### Forbidden
|
||||
|
||||
- 修改笔 / 线段 / 中枢 / 买卖点算法语义
|
||||
- 破坏 `/api/analyze` JSON 字段(可增不可删)
|
||||
- 修改 `config/`;修改 `strategies/` 交易逻辑或参数(import/`sys.path` 迁移除外,见 CODE_REVIEW)
|
||||
- 引入 Vite/React/TS 构建
|
||||
- 重做 UI 视觉或更换 TradingView
|
||||
|
||||
## Risk
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 策略 import 断裂 | 根 shim + 冒烟导入 |
|
||||
| 拆文件改算法 | 仅搬移;golden fixture |
|
||||
| 前端事件遗漏 | 按块抽取 + 手工/冒烟 |
|
||||
| API 字段漂移 | analyze 契约测试 |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] `from ChanLun import ChanLun` / `from ChanEnum import ...` 仍可用
|
||||
- [x] Golden:同一 fixture 下 bi/seg/zs/bsp 序列化结果与基线一致
|
||||
- [x] `/api/analyze` 关键字段集合兼容(契约冒烟)
|
||||
- [x] `web/app.py` 瘦身为 factory;业务在 services/api
|
||||
- [x] `index.html` 不再含大体量业务 inline JS
|
||||
- [x] ESS docs 齐全;TEST_REPORT / IMPLEMENTATION_REPORT / CHANGELOG
|
||||
- [x] `config/` 无内容变更;`strategies/` 仅允许 import/`sys.path` 迁移(Human 增补,无交易逻辑变更)
|
||||
|
||||
**Status:** Done (Released as `v1.0.0`)
|
||||
|
||||
## Rollback
|
||||
|
||||
单分支 / 单 PR 回滚;shim 期可整体 `git revert`。
|
||||
|
||||
## Risk Review
|
||||
|
||||
- Path: `docs/RISK_REVIEW/ECR-001.md` — N/A(不改交易语义)
|
||||
|
||||
## Linked
|
||||
|
||||
- PRD / PRODUCT_SPEC: `docs/PRODUCT_SPEC/ECR-001-restructure.md`
|
||||
- ENGINEERING_SPEC: `docs/ENGINEERING_SPEC/ECR-001-restructure.md`
|
||||
- ADR: `docs/ADR/ADR-001-package-layout.md`
|
||||
- EXPERIMENT: N/A
|
||||
- TRACEABILITY: Yes
|
||||
@@ -1,74 +0,0 @@
|
||||
# ECR-002
|
||||
|
||||
**Title:** 拆分 `web/services/runtime.py` + 加深 `/api/analyze` 契约测试
|
||||
**Status:** Done (Reviewed)
|
||||
**Date:** 2026-08-06
|
||||
**Change Level:** L3(行为冻结;若 golden 漂移则升 L2)
|
||||
|
||||
## Change
|
||||
|
||||
将仍偏大的 `web/services/runtime.py` 按职责拆为可维护子模块;加深 analyze API 契约/快照测试;可选拆分主站巨型 `chart_tv.js`(本轮未做)。
|
||||
|
||||
## Motivation
|
||||
|
||||
ECR-001 CODE_REVIEW 非阻断债务:runtime 过大、契约测试偏浅、chart_tv 单体。不处理会继续抬高 Web 改动风险。
|
||||
|
||||
## Scope
|
||||
|
||||
### Allowed
|
||||
|
||||
- 物理拆分 `web/services/runtime.py` → 包 `web/services/runtime/`(state / timeframes / market_data / indicators / analyze / serialize + 门面)
|
||||
- 加深 `web/tests/`:固定 fixture / mock 行情下的关键字段快照与契约
|
||||
- 补 `TF_DF(..., interval=1)` 全量 `__init__` 冒烟
|
||||
- 更新 TECH_STACK / TRACEABILITY / CHANGELOG
|
||||
|
||||
### Forbidden
|
||||
|
||||
- 修改笔 / 线段 / 中枢 / 买卖点算法语义
|
||||
- 破坏 `/api/analyze` JSON 字段(可增不可删)
|
||||
- 修改 `config/`、`strategies/` 交易逻辑或参数
|
||||
- 引入 Vite/React/TS 构建
|
||||
- 为主站重新引入 WebSocket 实时(须另 ECR)
|
||||
- 无 Approve 即大规模改前端视觉
|
||||
|
||||
## Risk
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 拆文件隐式改行为 | 仅搬移;golden + analyze 契约/快照 |
|
||||
| 门面漏导出 | 保留 `runtime` re-export + 历史 import * 兼容符号 |
|
||||
| 测试依赖真实行情 | mock / fixture;不绑生产 WS |
|
||||
| chart_tv 拆分漏事件 | 本轮不做 |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] `runtime` 门面公开符号与拆分前兼容(含 `timezone`/`OrderedDict`/`np`/StructureZone 等历史漏出)
|
||||
- [x] Golden:`pytest tests/test_golden_pipeline.py` 通过
|
||||
- [x] Analyze 契约/快照测试通过且覆盖关键字段清单以上
|
||||
- [x] TF_DF 全量 init 冒烟通过
|
||||
- [x] `config/` / `strategies/` 无交易逻辑 diff
|
||||
- [x] IMPLEMENTATION_REPORT / TEST_REPORT / CHANGELOG / TRACEABILITY 更新
|
||||
- [x] CODE_REVIEW Approve
|
||||
|
||||
## Rollback
|
||||
|
||||
`git revert` 本 ECR 提交;门面保留期可整包回滚。
|
||||
|
||||
## Risk Review
|
||||
|
||||
- Path: `docs/RISK_REVIEW/ECR-002.md` — N/A(不改交易决策语义)
|
||||
|
||||
## Linked
|
||||
|
||||
- IDEA: `docs/IDEA/IDEA-003-runtime-split.md`
|
||||
- PRODUCT_SPEC: `docs/PRODUCT_SPEC/ECR-002-runtime-split.md`
|
||||
- ENGINEERING_SPEC: `docs/ENGINEERING_SPEC/ECR-002-runtime-split.md`
|
||||
- ADR: 引用 ADR-001(包内再拆,无新顶层布局 ADR)
|
||||
- EXPERIMENT: N/A
|
||||
- TRACEABILITY: Yes
|
||||
- IMPLEMENTATION_REPORT: `docs/IMPLEMENTATION_REPORT/ECR-002.md`
|
||||
- TEST_REPORT: `docs/TEST_REPORT/ECR-002.md`
|
||||
|
||||
## Origin
|
||||
|
||||
- `docs/CODE_REVIEW/ECR-001.md` Findings 1–3、5
|
||||
@@ -1,62 +0,0 @@
|
||||
# ECR-003
|
||||
|
||||
**Title:** 主站威科夫分析与图表展示
|
||||
**Status:** Done (Reviewed)
|
||||
**Date:** 2026-08-06
|
||||
**Change Level:** L2
|
||||
|
||||
## Change
|
||||
|
||||
在主站 `/` 增加威科夫交易区间、阶段(A–E)、关键事件(Spring/SOS/LPS/UTAD 等)、区间内简易 VP(POC/VAH/VAL)与量能确认;按需接入 `/api/analyze`。
|
||||
|
||||
## Motivation
|
||||
|
||||
用户需要在缠论图上叠加威科夫结构解读;与现有结构区语义分离。
|
||||
|
||||
## Scope
|
||||
|
||||
### Allowed
|
||||
|
||||
- 新建 `chanlun/analysis/wyckoff/`
|
||||
- `/api/analyze` 增加可选 `include_wyckoff` 与响应字段 `wyckoff`(可增不可删既有字段)
|
||||
- 主站 UI 开关与 Lightweight 绘图
|
||||
- 单测 + ESS 文档
|
||||
|
||||
### Forbidden
|
||||
|
||||
- 修改笔/段/中枢/买卖点算法语义
|
||||
- 改 `config/` / `strategies/`
|
||||
- `/chan_tv` Study
|
||||
- Vite/React、主站 WebSocket 实时(另 ECR)
|
||||
|
||||
## Risk
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 启发式误标 | 规格写明启发式;UI 可关;单测合成形态 |
|
||||
| 负载 | 默认关闭,勾选才计算 |
|
||||
| 与结构区混淆 | 独立开关与字段名 |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] `include_wyckoff=1` 返回约定 `wyckoff` 键;默认不强制计算
|
||||
- [x] 合成 fixture:能检出 TR + 至少一类事件;VP POC 可测
|
||||
- [x] 主站可开关绘制区间/阶段/事件/VP
|
||||
- [x] golden 缠论基线不变
|
||||
- [x] TEST/IMPL/CHANGELOG/TRACEABILITY + CODE_REVIEW
|
||||
|
||||
## Rollback
|
||||
|
||||
`git revert`;关闭 UI 开关即可无图面影响。
|
||||
|
||||
## Risk Review
|
||||
|
||||
- `docs/RISK_REVIEW/ECR-003.md` — N/A(展示分析,非 Live 策略)
|
||||
|
||||
## Linked
|
||||
|
||||
- IDEA: `docs/IDEA/IDEA-004-wyckoff-main.md`
|
||||
- PRODUCT_SPEC / ENGINEERING_SPEC: 同目录 ECR-003-*
|
||||
- EXPERIMENT: N/A
|
||||
- TRACEABILITY: Yes
|
||||
- CODE_REVIEW: `docs/CODE_REVIEW/ECR-003.md` — Approve
|
||||
@@ -1,69 +0,0 @@
|
||||
# ECR-004
|
||||
|
||||
**Title:** 威科夫区间评分硬化与主站 VP 绘图减负
|
||||
**Status:** Done (Reviewed)
|
||||
**Date:** 2026-08-06
|
||||
**Change Level:** L2
|
||||
|
||||
## Change
|
||||
|
||||
跟进 ECR-003 CODE_REVIEW Findings:改进交易区间选取启发式、阶段最小长度、收紧单测;主站 VP/叠层降低 Lightweight series 数量;`include_wyckoff` 与主周期分析同门闩。
|
||||
|
||||
## Motivation
|
||||
|
||||
003 已 Approve 合入;质量与内存项不得回塞已审变更,须独立可审闭环。
|
||||
|
||||
## Scope
|
||||
|
||||
### Allowed
|
||||
|
||||
- `chanlun/analysis/wyckoff/range.py` / `events.py`(阶段)启发式与单测
|
||||
- `web/static/js/app/chart_tv.js` 威科夫 VP/填充绘制路径
|
||||
- `web/api/analyze.py`:`elements_only` 时不跑威科夫;默认 `vp_bins` 上限 24
|
||||
- ESS 文档与契约测试补充断言(不删既有 `wyckoff` 键)
|
||||
|
||||
### Forbidden
|
||||
|
||||
- 改笔/段/中枢/买卖点语义
|
||||
- `config/` / `strategies/`
|
||||
- `/chan_tv`
|
||||
- 新数据源 / 订单流
|
||||
- **按币种独立参数表**(全局 ATR 相对即可;当前以 BTC 场景验证)
|
||||
|
||||
## Decisions(Approve 时锁定)
|
||||
|
||||
- VP:**A+C**(前端 Top-N 有量 bin + 服务端 bins 上限 24)
|
||||
- 不做 per-symbol 参数
|
||||
|
||||
## Risk
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| TR 结果相对 003 漂移 | 合成夹具锁定高低与起点;文档标明启发式迭代 |
|
||||
| 前端 VP 观感变化 | 保留 POC/VAH/VAL;密度用 Top-N |
|
||||
| 回归 | 扩展 `tests/test_wyckoff.py` + 既有契约套件 |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] 合成箱体夹具:`trading_range` 高低接近箱体边界,起点不落入明显前置趋势段
|
||||
- [x] 开启 VP 时主图新增 series 数显著低于「每 bin 一条」(目标:填充+VP ≤ ~15 或等价合并策略)
|
||||
- [x] 阶段输出满足最小跨度或合并退化段;文档说明规则
|
||||
- [x] `elements_only=true` 即使 `include_wyckoff=1` 也不返回 `wyckoff`
|
||||
- [x] golden 缠论基线不变;相关 pytest 绿
|
||||
- [x] TEST/IMPL/CHANGELOG/TRACEABILITY + CODE_REVIEW
|
||||
|
||||
## Rollback
|
||||
|
||||
`git revert`;UI 关威科夫即可无图面影响。
|
||||
|
||||
## Risk Review
|
||||
|
||||
- `docs/RISK_REVIEW/ECR-004.md` — N/A(展示/启发式,非 Live 策略)
|
||||
|
||||
## Linked
|
||||
|
||||
- IDEA: `docs/IDEA/IDEA-005-wyckoff-harden.md`
|
||||
- 上游: `docs/CODE_REVIEW/ECR-003.md` Findings 1–5
|
||||
- PRODUCT_SPEC / ENGINEERING_SPEC: 同目录 ECR-004-*
|
||||
- TRACEABILITY: Yes
|
||||
- CODE_REVIEW: `docs/CODE_REVIEW/ECR-004.md` — Approve
|
||||
@@ -1,105 +0,0 @@
|
||||
# Engineering Spec: ECR-001 引擎 + Web 重构
|
||||
|
||||
## Related
|
||||
|
||||
| Doc | Link |
|
||||
|-----|------|
|
||||
| ECR | `docs/ECR/ECR-001-chan-web-restructure.md` |
|
||||
| PRODUCT_SPEC | `docs/PRODUCT_SPEC/ECR-001-restructure.md` |
|
||||
| ADR | `docs/ADR/ADR-001-package-layout.md` |
|
||||
|
||||
## Module Design
|
||||
|
||||
### `chanlun/core/*`
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| Responsibility | K 线单元、合并 K、笔/段/中枢/买卖点数据结构与枚举 |
|
||||
| Can | 表达缠论基础对象 |
|
||||
| Cannot | 拉行情、写 Flask 路由 |
|
||||
| Layer | domain |
|
||||
|
||||
### `chanlun/pipeline/*`
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| Responsibility | `ChanLun` 编排、`TF_DF` 门面、builders |
|
||||
| Can | 从 DataFrame 构建结构 |
|
||||
| Cannot | 改公开方法语义 |
|
||||
| Layer | application |
|
||||
|
||||
### `chanlun/indicators/*` / `chanlun/analysis/*`
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| Responsibility | MACD 状态、Zone/Classifier/Trend 等分析 |
|
||||
| Layer | domain / application |
|
||||
|
||||
### Root shims (`ChanLun.py`, `ChanEnum.py`, …)
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| Responsibility | 转发到 `chanlun.*`,兼容 strategies |
|
||||
| Cannot | 含业务逻辑 |
|
||||
|
||||
### `web/config.py` + `web/services/*` + `web/api/*`
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| Responsibility | 配置、行情、分析、序列化、HTTP |
|
||||
| Cannot | 修改缠论算法 |
|
||||
|
||||
### `web/static/js/app/*`
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| Responsibility | 图表、overlay、UI、API 客户端 |
|
||||
| Cannot | 本轮引入 React 构建 |
|
||||
|
||||
## Interfaces
|
||||
|
||||
```python
|
||||
class TF_DF:
|
||||
# 公开方法签名保持与重构前一致
|
||||
def get_kl_data(self, dataframe): ...
|
||||
def get_bi_list(self, dataframe): ...
|
||||
# ... 门面委托 builders
|
||||
|
||||
class ChanLun:
|
||||
def init_dataframes(...): ...
|
||||
```
|
||||
|
||||
`/api/analyze`:查询参数与 JSON 顶层字段兼容。
|
||||
|
||||
## Algorithm Sketch
|
||||
|
||||
1. 物理迁移模块到 `chanlun/`,修正 import
|
||||
2. 根 shim 再导出
|
||||
3. `TF_DF` 方法体迁移到 builders,门面调用
|
||||
4. Web 按职责拆文件,路由注册不变
|
||||
5. HTML 抽 JS;未接线 TS 入 `_unused`
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Case | Behavior |
|
||||
|------|----------|
|
||||
| 行情失败 | 保持现有 JSON error |
|
||||
| 分析异常 | 保持现有日志与错误返回 |
|
||||
|
||||
## Config / Constants
|
||||
|
||||
| Name | Source |
|
||||
|------|--------|
|
||||
| DATA_SERVICE_URL | env / config |
|
||||
| HTTP(S)_PROXY | env / config(替代硬编码 7897) |
|
||||
| MACD periods | config |
|
||||
|
||||
## Test Plan Pointer
|
||||
|
||||
- `tests/test_golden_pipeline.py` + `tests/fixtures/golden_*`
|
||||
- `web/tests/` analyze 契约冒烟
|
||||
- `python -c "from ChanLun import ChanLun"`
|
||||
|
||||
## Migration / API Impact
|
||||
|
||||
无破坏性 API 变更;仅内部路径变化。
|
||||
@@ -1,23 +0,0 @@
|
||||
# ENGINEERING_SPEC — ECR-002
|
||||
|
||||
**Status:** Implemented
|
||||
**Date:** 2026-08-06
|
||||
|
||||
## Design
|
||||
|
||||
1. **包目录** `web/services/runtime/`(不用平铺 `runtime_*.py`)
|
||||
2. **边界**
|
||||
- `state`:可变全局与客户端
|
||||
- `timeframes`:周期工具
|
||||
- `market_data`:行情
|
||||
- `indicators`:技术指标列
|
||||
- `analyze`:缠论编排 + 趋势分类
|
||||
- `serialize`:JSON 整形
|
||||
- `__init__`:门面 + 历史 `import *` 兼容再导出
|
||||
3. **测试**:facade / analyze_chan 键 / serialize / HTTP mock 契约 / TF_DF init / golden
|
||||
4. **chart_tv 拆分**:本轮不做(仍可选后续 ECR)
|
||||
|
||||
## Open questions(已决)
|
||||
|
||||
- [x] 采用包目录 `services/runtime/`
|
||||
- [x] chart_tv 拆分不纳入本 PR
|
||||
@@ -1,46 +0,0 @@
|
||||
# ENGINEERING_SPEC — ECR-003
|
||||
|
||||
**Status:** Approved
|
||||
**Date:** 2026-08-06
|
||||
|
||||
## Package
|
||||
|
||||
`chanlun/analysis/wyckoff/`:
|
||||
|
||||
- `engine.py` — `analyze_wyckoff(df) -> dict`
|
||||
- `range.py` — 交易区间检测(ATR 容差震荡箱)
|
||||
- `phases.py` — A–E 状态机
|
||||
- `events.py` — Spring/SOS/LPS/UTAD(及 distribution 对称)
|
||||
- `volume_profile.py` — 区间内分桶 VP
|
||||
- `__init__.py` — 导出 `analyze_wyckoff`
|
||||
|
||||
## API
|
||||
|
||||
`GET /api/analyze?include_wyckoff=1` → `result["wyckoff"]`:
|
||||
|
||||
```json
|
||||
{
|
||||
"trading_range": {"start_time","end_time","high","low","mid","active"},
|
||||
"bias": "accumulation|distribution|unknown",
|
||||
"phases": [{"phase","label","start_time","end_time"}],
|
||||
"events": [{"type","time","price","note","volume_ratio","volume_ok"}],
|
||||
"volume_profile": {"bins":[{"price","volume"}],"poc","vah","val","bin_count"},
|
||||
"volume_confirm": {"avg_volume","event_checks":{}}
|
||||
}
|
||||
```
|
||||
|
||||
默认 `include_wyckoff` 假:可不返回或返回 `null`(实现选:不返回键以减负)。
|
||||
|
||||
## Detection heuristics
|
||||
|
||||
1. ATR(14) 容差;扫描最近窗口找高低点接近的连续段作为 TR。
|
||||
2. 阶段:价格在 TR 内相对位置 + 假破/真破时间序。
|
||||
3. Spring:下破 TR.low 后收回且收盘回到区间内;量能相对均量判断。
|
||||
4. SOS:收盘站上 TR.high 且放量。
|
||||
5. LPS:SOS 后回踩不破 mid/high 带且缩量。
|
||||
6. UTAD:上破后跌回区间内(派发)。
|
||||
7. VP:typical=(H+L+C)/3,volume 加权分桶,VA≈70% 围绕 POC。
|
||||
|
||||
## Frontend
|
||||
|
||||
主站 checkbox + `chart_view` 传参;`chart_tv.js` 绘制。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user