17 Commits
Author SHA1 Message Date
UbuntuandCursor 7e339d2a54 research: 影子交易器落在 Hummingbot 上,并修掉 Bitget 连接器的换根延迟
1m 腿的滑点余量只有几个 bp,所以要测的必须是生产路径的滑点——换个运行时
测出来的数就不作数。框架因此从「滑点已知后再定」提前到测量阶段就定为
Hummingbot(Spot/Perp 连接器均 v2.0,Bitget 是 Foundation Partner)。

新增 research/live/。前置测量:

- bench_compute.py 本机算力,1m 单币 0.318s、三币串行 1.38s
- venue_parity.py Binance 与 Bitget 同根信号重合仅 14.6~42.6%
- signal_sensitivity.py 0.25bp 扰动就换掉一半信号
- aggregate_robustness.py 但总体期望不降——脆的是信号身份,不是 alpha
- bitget_baseline.py 因此改用 Bitget 原生基线定预算:余量 BTC -0.13bp、
  ETH +4.02bp、SOL +2.92bp。BTC 本就为负,只作延迟测量的参照物

运行时选型:

- parity_env.py 容器与本机信号逐一相同(下标、中枢数、checksum 全等),
  容器内 0.26s/币反而更快。故 chanlun 直接挂载进容器,不必另起信号服务。
  装进现有 .venv 那条路走不通:Hummingbot 要 numba>=0.61.2 与
  aiohttp<3.14,与本机 Python 3.14 冲突
- latency_ccxt.py / latency_hummingbot.py / latency_compare.py 初测显示
  Hummingbot 比 ccxt.pro 慢约 1030ms,90 根逐根配对里 80~97% 更慢
- probe_ws_action.py 否掉「丢弃 snapshot」的猜测:换根首条就是 update
- probe_hb_vs_raw.py 与 latency_attribute.py 四路归因——容器网络 2~18ms、
  Hummingbot 处理 -10~-30ms,1350~1480ms 全落在解析方式上
- probe_ws_payload.py 定位根因:Bitget 换根会推一条带两根的消息
  [上一根, 新一根],而上游取 data["data"][0] 拿到的是上一根,新一根要等
  下一条单元素消息

修复:

- patched_candles.py 处理消息里的全部元素。不能简单改成 [-1]——那样上一根
  的收盘价会永远停在换根前约 1 秒的那次推送上,而信号对 0.25bp 都敏感
- verify_patch.py 60 根配对验证:拿回 1060~1090ms,与原始 WS 只差 5~14ms
  已贴理论下限,19 根已收盘 K 线 OHLCV 逐根未变。折算 ETH 省 0.54bp、
  SOL 省 0.42bp。此 bug 值得向上游反馈

影子交易器:

- shadow_hb.py 不下单,读连接器真实盘口按仓位吃单深度算成交价,与次根开盘价
  (回测 entry_delay=1 的口径)相减,分解成延迟漂移、盘口价差、深度冲击。
  盘口 10Hz 滚动缓冲 30 秒,把延迟变成自变量:每个信号记 0.5/1/2/5s 与实际
  算完时刻各一个滑点值,本机算得慢也不影响能读出的曲线
- shadow_signal.py 信号计算隔离到子进程。0.26s 是纯 CPU 且 chanlun 受 GIL
  限制,放进 asyncio 循环会把行情处理一起卡住
- shadow_report.py 首日延迟门槛与滑点曲线报表

不用 paper trade 测滑点:它的成交由 Hummingbot 自己的撮合模型模拟,
测出来是模型行为而非市场行为。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 23:51:57 +08:00
jackyu66gitandCursor 66061f79a1 research: 低滞后信号口径定稿与实盘前偏差审计
fast_bsp3 改用 tol=-1 + require_touch=False,信号滞后从 5.8 根降到 2.2 根。
滞后与收益严格单调(年化 370% -> 906%,同一份数据同一套成本),
这是本轮提升的主因,也意味着实盘延迟会直接侵蚀收益。

新增 step31~39 验证策略能否落地:
- 跨品种样本外——8 个未参与调参的币,PF 2.73 / t 28.5,无一为负
- 时点重建——只喂到信号那一根重算,同根命中 100%,确认无未来函数;
  1m 在 2000 根窗口即饱和,计算耗时 0.20s
- 偏差审计——多空对称、中枢生效时刻零回退、滑点稳健至 30bp、持仓几乎不重叠
- 消融——alpha 来自缠论中枢的上下文定位,而非「收盘转强」这个触发动作

补 research/HANDOFF.md:记录确切口径与参数、已排除的偏差、
已验证无效因而不必重做的方向,以及下一步用影子交易器实测执行滑点的方案。

清理 step1~20 的输出:早期方法论已被推翻(存在未来函数偏差),
其结论不再被引用;脚本保留,需要时可重跑。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 17:47:41 +08:00
UbuntuandCursor b286cb0137 chore: 忽略 Office 文档与虚拟环境目录
手续费.xlsx 一类文件放在仓库旁但不属于仓库;~$ 开头的是 Excel 打开工作簿
时生成的锁文件,每次都会重新出现并占据 git status。

.venv 一并声明:venv 自 3.11 起会在它创建的目录内写 .gitignore,但仅覆盖
该目录,换个位置或用旧版本就失效。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 02:08:12 +08:00
UbuntuandCursor a243edd2d3 docs: 补依赖清单与 README
此前仓库无任何依赖声明。按 AST 扫描全部 import 后按用途切分:
requirements.txt 为核心运行时 8 个包,requirements-dev.txt 追加测试与
research/ 所需。

matplotlib / mplfinance / xgboost / scikit-learn 只出现在 ChanPY.py、
ChanLun_Classifier.py、Find_Trend.py、ChanHeng.py 这四个零引用文件中,
故不纳入清单;ChanPY.py 依赖的外部 chan.py 库本就未安装。

README 记录目录结构、启动方式、库用法、分析流程九步与数据约定,并注明
三处现存问题:web/DEPLOY_GUIDE.md 引用的 6 个部署脚本已在 7f393b9 删除、
web/README.txt 指向不存在的 web/requirements.txt、systemd unit 的端口
8123 与 config.py 默认的 8128 不一致且 gunicorn 未声明。

清单已在全新空 venv 中验证:仅装 requirements.txt 时 57 个模块可导入、
Flask 16 条路由正常;装 dev 清单后测试 24 通过。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 02:01:53 +08:00
UbuntuandCursor 206b27fe72 refactor: 以自实现指标替换 talib 与 technical 依赖
chanlun/indicators/ta.py 接口兼容 talib.abstract,实现代码实际用到的
SMA/MA/EMA/RSI/ATR/MACD/BBANDS;chanlun/pipeline/resample.py 替代
technical.util.resample_to_interval。调用点只改 import,逻辑未动。

暖机长度与平滑种子按 TA-Lib 的约定实现,差一根 K 线就会让下游所有
笔/线段/中枢整体位移。其中 MACD 需特别处理:TA-Lib 让快慢两条 EMA
在同一根 K 线出首值,因而快线的种子取 x[slow-fast:slow] 的均值,而非
从 fastperiod-1 一路递推——两者在百元价位上相差约 0.17。

BBANDS 是有意的分歧:TA-Lib 用 sumsq/n - mean² 求方差,短窗口远离零
时灾难性抵消(timeperiod=2 误差 8.7e-7),本实现用 rolling std,对 50
位精度基准误差为 0。项目实际使用的周期两者一致到 1e-10。

顺带清理 12 个文件中 16 处从未调用的 talib/technical 导入。

验证:9440 组随机对拨;真实 K 线端到端比对 add_indicators 全部 33 个
指标列,NaN 模式一致、MACD 柱符号 100% 相同;屏蔽两个包后 60 个模块
均可导入。新增 test_ta_compat.py 将输出逐 bar 钉在 TA-Lib 上,但该文件
在 TA-Lib 缺失时静默跳过,改动 ta.py 需在装有 TA-Lib 的环境复跑。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 02:01:43 +08:00
jackyu66gitandCursor 7f393b93ed refactor: 精简仓库为 chanlun 核心与 web 分析,移除威科夫与遗留模块
删除根目录旧 Chan 模块、策略、配置、文档及 wyckoff 相关代码;更新缠论 pipeline 与笔中枢计算;补充 research 研究与 web 测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 01:05:12 +08:00
jackyu66gitandCursor 5c10e35b76 refactor(web): 移除主图威科夫选项与叠层
去掉区间/阶段/时间/VP 开关、Cycle 摘要面板及绘制逻辑;分析请求默认 include_wyckoff=0。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 01:27:51 +08:00
jackyu66gitandCursor 8c165f11cd fix(web): 未完成笔/线段终点对齐图表最新 K 线
各周期使用对应 kline 数据,终点时间 snap 到 candles,优先使用分析 end_price。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 00:40:41 +08:00
jackyu66gitandCursor 97e77847d0 fix(web): 开关缠论元素保留视窗;分周期 Trend 涨跌配色
本地重绘统一冻结视窗;次/次次周期 Trend 上涨下跌使用独立颜色。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 23:56:55 +08:00
jackyu66gitandCursor 90499533fb fix(web): 分析/自动刷新后保留 K 线视窗位置
拆分手动分析与自动刷新拉数路径;全量重建用 logical 优先恢复视窗,
增量 recent 用 scroll+barDelta;避免 barSpacing 重锚与重复冻结导致往右跳。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 23:38:20 +08:00
jackyu66gitandCursor 8ee11317d3 fix(web): 自动刷新保留 K 线视窗;威科夫与图表增量更新
自动刷新改用 tail update 与 scrollToPosition 恢复视窗,避免 setData 后跳到最右;拆分 chart_tv 模块并扩展 analyze/recent API。同步威科夫分析、pipeline 增量构建及相关策略与配置。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 22:57:43 +08:00
jackyu66gitandCursor 1e60ab3bfa docs: 补强 ECR-004 CODE_REVIEW 复审记录
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 18:47:18 +08:00
jackyu66gitandCursor d3188ca83c fix: ECR-004 威科夫区间评分硬化与 VP 绘图减负(已审)
评分选 TR、阶段最小跨度、elements_only 门闩、Top-8 VP;无币种独立参数。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 18:46:08 +08:00
jackyu66gitandCursor ac6be80278 docs: 开启 ECR-004 威科夫硬化与 VP 减负(Draft)
跟进 ECR-003 Review Findings;待 Approve 后实现。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 18:35:25 +08:00
jackyu66gitandCursor 081a57a90e feat: ECR-003 主站威科夫分析与图表叠层(已审)
独立 wyckoff 引擎 + 按需 include_wyckoff;主站 Lightweight 绘制区间/阶段/事件/VP。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 18:33:57 +08:00
jackyu66gitandCursor df27b4dde8 refactor: ECR-002 拆分 runtime 包并加深 analyze 契约(已审)
将 web/services/runtime.py 拆为 runtime/ 子模块并保持门面兼容;补齐 ESS 文档、门面/契约/TF_DF 测试与 CODE_REVIEW Approve。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 18:15:23 +08:00
jackyu66gitandCursor 9f1e7361b6 fix: 修复主站自动刷新内存泄漏,并完善 chan_tv 图表体验
主站重建前完整 dispose、去掉重复 sync 监听,自动刷新默认增量更新;顺带消除首屏重复 analyze、复用 ChanMACD,以及全版 TV 指标/未完成中枢/布局本地缓存。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 16:09:48 +08:00
334 changed files with 187518 additions and 30700 deletions
+26 -28
View File
@@ -1,42 +1,40 @@
# MacOS # MacOS
.DS_Store .DS_Store
# Python编译文件和缓存 # Python
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
*.pyc *.pyc
*.pyo *.pyo
.pytest_cache/
# 策略文件的缓存 # Logs & databases
strategies/__pycache__/
# Machine Learning / AI model files
*_model*_xgb_model.json
*modelchan*.json
*.libsvm
feature_meta
*_model_feature_data.csv
*.pem
# Log files
*.log *.log
# Database files
*.sqlite *.sqlite
*.sqlite-shm *.sqlite-shm
*.sqlite-wal *.sqlite-wal
.DS_Store
交易记录/~$交易规则.docx # Office documents kept alongside the repo but not part of it.
/datasvc/data # "~$" files are Excel's lock files, recreated every time a workbook is opened.
.DS_Store *.xlsx
.DS_Store *.xls
/data_provider/data ~$*
.DS_Store
.DS_Store # Local data
.DS_Store data/
.DS_Store
.DS_Store # Exchange history fetched by research/live/*.py. 40MB and re-fetchable from
.DS_Store # the venue, so it stays local; the small result CSVs it feeds are committed.
data_provider/._config.json research/live/cache/
# Scratch outputs from short shakedown runs, superseded by the real collection.
research/out/archive/
# Virtualenvs. venv writes its own .gitignore since 3.11, but only for the
# directory it creates — declare it here so other layouts are covered too.
.venv/
venv/
# Local tooling
.gstack/ .gstack/
-114
View File
@@ -1,114 +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
- ESS 文档:`docs/PROJECT_PROFILE.md``docs/ECR/``docs/ENGINEERING_SPEC/`
- **正式引擎包**`chanlun/`strategies / web 已用 `from chanlun import ...`
- 根目录 `Chan*.py` / `TF_DF.py` 仍为 **兼容 shim**(旧脚本可用)
## 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
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanBI import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanBIZS import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanBSP import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanCTime import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanEnum import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.ChanHeng import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanKLC import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanKLU import * # noqa: F403
-3
View File
@@ -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
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.ChanLun_Classifier import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.indicators.ChanMACD import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.indicators.ChanMACDHistSet import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.indicators.ChanMACDSeg import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.indicators.ChanMACDUnitTF import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.ChanPY import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.ChanPivotClassifier import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.ChanPivotMonitor import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanSBI import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanSEG import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.ChanZS import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.ChanZone import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.core.Chan_FX_Box import * # noqa: F403
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.analysis.Find_Trend import * # noqa: F403
+130
View File
@@ -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.11pandas 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——这些都**不在**依赖清单里,
是有意为之。要用得自行安装。
-2
View File
@@ -1,2 +0,0 @@
"""兼容 shim — 请优先 from chanlun import ..."""
from chanlun.pipeline.timeframe import TF_DF # noqa: F401
View File
-2
View File
@@ -14,12 +14,10 @@ from chanlun.core.ChanSBI import ChanSBI
from chanlun.core.ChanSEG import ChanSEG from chanlun.core.ChanSEG import ChanSEG
from chanlun.core.ChanZS import ChanZS from chanlun.core.ChanZS import ChanZS
from chanlun.core.ChanBSP import ChanBSP from chanlun.core.ChanBSP import ChanBSP
import talib.abstract as ta
import pandas as pd import pandas as pd
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, date2num from matplotlib.dates import DateFormatter, date2num
import matplotlib.patches as patches import matplotlib.patches as patches
from technical.util import resample_to_interval
from decimal import Decimal from decimal import Decimal
from chanlun.pipeline.orchestrator import ChanLun from chanlun.pipeline.orchestrator import ChanLun
import xgboost as xgb import xgboost as xgb
+4 -3
View File
@@ -2,7 +2,7 @@ import ccxt
import pandas as pd import pandas as pd
import numpy as np import numpy as np
import mplfinance as mpf import mplfinance as mpf
from talib import MACD, SMA from chanlun.indicators import ta
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
import datetime as dt import datetime as dt
@@ -249,8 +249,9 @@ def analyze_higher_timeframe(df_30m):
# 8. Back-divergence detection (enhanced) # 8. Back-divergence detection (enhanced)
def detect_back_divergence(df, strokes, higher_trend): def detect_back_divergence(df, strokes, higher_trend):
try: try:
macd, signal, hist = MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9) macd_df = ta.MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9)
sma20 = SMA(df['Close'], timeperiod=20) macd, hist = macd_df['macd'], macd_df['macdhist']
sma20 = ta.SMA(df['Close'], timeperiod=20)
df['macd'] = macd df['macd'] = macd
df['hist'] = hist df['hist'] = hist
df['sma20'] = sma20 df['sma20'] = sma20
+196
View File
@@ -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,
)
+2 -4
View File
@@ -6,9 +6,7 @@ from decimal import Decimal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
from technical.util import resample_to_interval
from chanlun.core.ChanBI import ChanBI from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS from chanlun.core.ChanBIZS import ChanBIZS
@@ -467,7 +465,7 @@ class BiBuilderMixin:
pre_last_bi = bi_list[-2] pre_last_bi = bi_list[-2]
last_bi = bi_list[-1] 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: 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) bi_list.remove(last_bi)
pre_last_bi.set_next(None) pre_last_bi.set_next(None)
#last_top.set_fx(Chan_FX_TYPE.PTOP) #last_top.set_fx(Chan_FX_TYPE.PTOP)
@@ -585,7 +583,7 @@ class BiBuilderMixin:
pre_last_bi = bi_list[-2] pre_last_bi = bi_list[-2]
last_bi = bi_list[-1] 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: 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) bi_list.remove(last_bi)
pre_last_bi.set_next(None) pre_last_bi.set_next(None)
last_bottom = klc last_bottom = klc
-2
View File
@@ -6,9 +6,7 @@ from decimal import Decimal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
from technical.util import resample_to_interval
from chanlun.core.ChanBI import ChanBI from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS from chanlun.core.ChanBIZS import ChanBIZS
+171
View File
@@ -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)
+1 -2
View File
@@ -6,9 +6,8 @@ from decimal import Decimal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta from chanlun.indicators import ta
from pandas import DataFrame from pandas import DataFrame
from technical.util import resample_to_interval
from chanlun.core.ChanBI import ChanBI from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS from chanlun.core.ChanBIZS import ChanBIZS
+58 -39
View File
@@ -6,9 +6,7 @@ from decimal import Decimal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
from technical.util import resample_to_interval
from chanlun.core.ChanBI import ChanBI from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS from chanlun.core.ChanBIZS import ChanBIZS
@@ -86,7 +84,8 @@ class KlineBuilderMixin:
return Chan_FX_TYPE.UNKNOWN return Chan_FX_TYPE.UNKNOWN
def check_fx(self, klc): 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.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: #if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0:
klc.set_fx(Chan_FX_TYPE.TOP) klc.set_fx(Chan_FX_TYPE.TOP)
@@ -99,6 +98,21 @@ class KlineBuilderMixin:
return Chan_FX_TYPE.BOTTOM return Chan_FX_TYPE.BOTTOM
return Chan_FX_TYPE.UNKNOWN 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): def check_fx2(self, klc):
if klc.pre and klc.next: 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: 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,11 +185,50 @@ class KlineBuilderMixin:
def get_kl_data(self, dataframe:DataFrame): def get_kl_data(self, dataframe:DataFrame):
return self.cal_kl_data(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): def get_klc_list(self, klu_list):
klc_list = [] klc_list = []
last_klu = None last_klu = None
# ChanMACD.__init__ 已调用 cal_macd_state,切勿再调一次(会重复堆积 seg/unittf)
macd = ChanMACD(klu_list) macd = ChanMACD(klu_list)
klu_list = macd.cal_macd_state() klu_list = macd.klu_list
self._last_chan_macd = macd
ema_up_list = [] ema_up_list = []
ema_down_list = [] ema_down_list = []
ema_up_count = 0 ema_up_count = 0
@@ -196,41 +249,7 @@ class KlineBuilderMixin:
ema_down_list.append(ema_down_count) ema_down_list.append(ema_down_count)
#print(last_klu.time, ema_down_count, "DOWN END") #print(last_klu.time, ema_down_count, "DOWN END")
ema_down_count = 0 ema_down_count = 0
if len(klc_list) > 0: self._push_klu_into_klc_list(klc_list, klu, last_klu)
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)
last_klu = klu last_klu = klu
klc_list = self.cal_trend(klc_list) klc_list = self.cal_trend(klc_list)
#print(ema52_up_list, ema52_down_list) #print(ema52_up_list, ema52_down_list)
-2
View File
@@ -6,9 +6,7 @@ from decimal import Decimal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
from technical.util import resample_to_interval
from chanlun.core.ChanBI import ChanBI from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS from chanlun.core.ChanBIZS import ChanBIZS
+11 -8
View File
@@ -6,9 +6,7 @@ from decimal import Decimal
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
from technical.util import resample_to_interval
from chanlun.core.ChanBI import ChanBI from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS from chanlun.core.ChanBIZS import ChanBIZS
@@ -355,9 +353,12 @@ class ZsBuilderMixin:
return bi_zs_list return bi_zs_list
def get_zs_range(bis): def get_zs_range(bis):
zg = min(bi.high for bi in bis) bis_list = bis[0:3]
zd = max(bi.low for bi in bis) zg = min(bi.high for bi in bis_list)
return zg, zd 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): def is_bi_overlap_range(bi, zg, zd):
return bi.high >= zd and bi.low <= zg return bi.high >= zd and bi.low <= zg
@@ -375,8 +376,8 @@ class ZsBuilderMixin:
zs.bi_list = list(bis) zs.bi_list = list(bis)
for bi in zs.bi_list: for bi in zs.bi_list:
bi.set_bi_zs(zs) bi.set_bi_zs(zs)
zs.set_gg(max(bi.high 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.set_dd(min(bi.low for bi in zs.bi_list))
zs.classify_zs() zs.classify_zs()
last_zs = None last_zs = None
@@ -394,7 +395,7 @@ class ZsBuilderMixin:
start_idx += 1 start_idx += 1
continue continue
zg, zd = get_zs_range([bi1, bi2, bi3]) zg, zd, dd, gg = get_zs_range([bi1, bi2, bi3])
if zg <= zd: if zg <= zd:
start_idx += 1 start_idx += 1
continue continue
@@ -420,6 +421,8 @@ class ZsBuilderMixin:
zs = ChanBIZS(bi1, len(bi_zs_list), zs_dir) zs = ChanBIZS(bi1, len(bi_zs_list), zs_dir)
zs.set_zg(zg) zs.set_zg(zg)
zs.set_zd(zd) zs.set_zd(zd)
zs.set_dd(dd)
zs.set_gg(gg)
set_zs_bi_list(zs, bis_for_zs) set_zs_bi_list(zs, bis_for_zs)
zs.set_end_bi(bis_for_zs[-1], bis_for_zs[-1].sure_time) zs.set_end_bi(bis_for_zs[-1], bis_for_zs[-1].sure_time)
+9 -2
View File
@@ -17,9 +17,7 @@ from chanlun.core.ChanSBI import ChanSBI
from chanlun.core.ChanSEG import ChanSEG from chanlun.core.ChanSEG import ChanSEG
from chanlun.core.ChanZS import ChanZS from chanlun.core.ChanZS import ChanZS
from chanlun.core.ChanBSP import ChanBSP from chanlun.core.ChanBSP import ChanBSP
import talib.abstract as ta
import pandas as pd import pandas as pd
from technical.util import resample_to_interval
from decimal import Decimal from decimal import Decimal
import numpy as np import numpy as np
from chanlun.indicators.ChanMACD import ChanMACD from chanlun.indicators.ChanMACD import ChanMACD
@@ -178,6 +176,15 @@ class ChanLun():
def cal_bi_zs_list(self, bi_list): def cal_bi_zs_list(self, bi_list):
#return self.tf_df.cal_bi_zs(bi_list) #return self.tf_df.cal_bi_zs(bi_list)
return self.tf_df.cal_bi_zs_list(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): def get_bi_zs_list(self, bi_list):
return self.tf_df.get_bi_zs_list(bi_list) return self.tf_df.get_bi_zs_list(bi_list)
def get_decimal(self, value): def get_decimal(self, value):
+52
View File
@@ -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
+10 -5
View File
@@ -2,9 +2,8 @@ from datetime import timedelta
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import talib.abstract as ta
from pandas import DataFrame 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.ChanBI import ChanBI
from chanlun.core.ChanBIZS import ChanBIZS 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.indicators.ChanMACD import ChanMACD
from chanlun.pipeline.builders.bi import BiBuilderMixin from chanlun.pipeline.builders.bi import BiBuilderMixin
from chanlun.pipeline.builders.bsp import BspBuilderMixin 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.indicators import IndicatorsBuilderMixin
from chanlun.pipeline.builders.kline import KlineBuilderMixin from chanlun.pipeline.builders.kline import KlineBuilderMixin
from chanlun.pipeline.builders.seg import SegBuilderMixin from chanlun.pipeline.builders.seg import SegBuilderMixin
from chanlun.pipeline.builders.zs import ZsBuilderMixin 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): def __init__(self, df=None, interval=0, timeframe=None):
if df is not None: if df is not None:
self.init_TF_DF(df, interval, timeframe) self.init_TF_DF(df, interval, timeframe)
@@ -59,17 +59,22 @@ class TF_DF(IndicatorsBuilderMixin, KlineBuilderMixin, BiBuilderMixin, SegBuilde
self.klc_list = [] self.klc_list = []
self.bi_list = [] self.bi_list = []
self.zs_list = [] self.zs_list = []
self.bi_zs_list = []
self.bsp_list = [] self.bsp_list = []
self.seg_list = [] self.seg_list = []
self.klc_fx_list = [] self.klc_fx_list = []
self.klu_list = self.cal_kl_data(self.dataframe) self.klu_list = self.cal_kl_data(self.dataframe)
self.klc_list = self.get_klc_list(self.klu_list) self.klc_list = self.get_klc_list(self.klu_list)
self.bi_list = self.cal_bi_list(self.klc_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.seg_list = self.get_seg_list(self.bi_list)
self.zs_list = self.get_zs_list(self.bi_list, self.seg_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) self.big_zs_list = self.get_big_zs_list(self.zs_list)
self.chanmacd = ChanMACD(self.klu_list) # get_klc_list 内已算过 ChanMACD,直接复用
self.klu_list = self.chanmacd.cal_macd_state() self.chanmacd = getattr(self, '_last_chan_macd', None)
if self.chanmacd is None:
self.chanmacd = ChanMACD(self.klu_list)
self.klu_list = self.chanmacd.klu_list
def get_current_klc(self): def get_current_klc(self):
+1
View File
@@ -0,0 +1 @@
from __future__ import annotations
+141
View File
@@ -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()
+198
View File
@@ -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()
-84
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-89
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-68
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-87
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-151
View File
@@ -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"]
}
}
]
}
-125
View File
@@ -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
}
}
-103
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-84
View File
@@ -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
}
}
-93
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-70
View File
@@ -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"
}
-123
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-83
View File
@@ -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
}
}
-81
View File
@@ -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
}
}
-83
View File
@@ -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.
-82
View File
@@ -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 停止服务"
-22
View File
@@ -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)。
-31
View File
@@ -1,31 +0,0 @@
# CHANGELOG
## 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/`
-75
View File
@@ -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 OKpytest 含 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 再导出符合计划,建议 ECR-002 继续按 data/analyze/serialize 物理拆分。
2. **`web/static/js/app/chart_tv.js` ~4664 行** — `initTradingView` 单体;行为冻结下可接受。
3. **`/api/analyze` 契约测试偏浅** — 仅关键字段清单 + 路由存在;无固定 fixture 的端到端 JSON 快照(需 mock 行情)。
4. **TEST_REPORT 写「5 passed」** — 现为 6(含 shim 兼容测);Release 前可改正文(L0 docs)。
5. **L1`TF_DF.get_zs_list` 恢复** — 合理兼容修复;golden 走 analyze 路径未覆盖 `TF_DF(df,...)` 全量 `__init__`,建议后续加一条 init 冒烟(非阻断)。
### 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) |
-70
View File
@@ -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,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,38 +0,0 @@
# Handoff
**From:** ARCHITECT
**To:** ENGINEER
**ECR:** ECR-001
**State:** design → coding
**Date:** 2026-08-05
## Artifacts
- [x] ECR (Approved)
- [x] PRODUCT_SPEC
- [x] ENGINEERING_SPEC
- [x] RISK_REVIEW (N/A)
- [x] ADR-001
- [x] TRACEABILITY
- [ ] TEST_REPORT / CODE_REVIEW / EXP (engineer / reviewer)
## Restrictions — Do not modify
- `config/``strategies/` 内容
- 缠论算法语义、`/api/analyze` 破坏性变更
- 计划文件本身
## Goal for receiver
按 ENGINEERING_SPEC 完成包化、TF_DF 拆分、Web 分层、前端模块化、golden 测试与文档收尾。
## Done for this hop
- [x] ESS docs 落盘
- [x] ECR Approved(计划实施授权)
## References
- `docs/ECR/ECR-001-chan-web-restructure.md`
- `docs/ENGINEERING_SPEC/ECR-001-restructure.md`
- Plan: ESS Chan Refactor1B+2B
@@ -1,29 +0,0 @@
# Handoff
**From:** ENGINEER
**To:** REVIEWER
**ECR:** ECR-001
**State:** coding → review
**Date:** 2026-08-05
## Artifacts
- [x] ECR (Approved / Done pending review)
- [x] PRODUCT_SPEC / ENGINEERING_SPEC / ADR / RISK N/A
- [x] IMPLEMENTATION_REPORT
- [x] TEST_REPORT
- [x] CODE_REVIEW → Approve`docs/CODE_REVIEW/ECR-001.md`
## Goal for receiver
对照 ECR-001 Acceptance 做代码审阅;确认 strategies/config 无 diffgolden 与路由契约通过。
## Reviewer outcome
**Approve**strategies 仅 import 迁移,记为范围修订)。Next → release_manager。
## References
- `docs/IMPLEMENTATION_REPORT/ECR-001.md`
- `docs/TEST_REPORT/ECR-001.md`
- `docs/CHANGELOG/CHANGELOG.md`
-3
View File
@@ -1,3 +0,0 @@
# IDEA: 引擎与 Web 结构重整
根目录与 `web/app.py` / `index.html` 过于臃肿,需在行为冻结前提下包化与分层。见 ECR-001。
-31
View File
@@ -1,31 +0,0 @@
# IMPLEMENTATION_REPORT — ECR-001
**Date:** 2026-08-05
**Role:** ENGINEER
**Status:** Complete
## Summary
完成缠论引擎包化、`TF_DF` builders 拆分、Web services/blueprints 分层、前端 `index.html` JS 外置模块化;算法与 `/api/analyze` 契约冻结;`config/` / `strategies/` 无改动。
## Changes
| Area | Result |
|------|--------|
| `chanlun/` | core / pipeline(+builders) / indicators / analysis |
| Root shims | `ChanLun.py` `TF_DF.py` `ChanEnum.py` 等再导出 |
| L1 fix | 恢复 `TF_DF.get_zs_list``get_seg_zs_list` |
| Web | `config.py` `services/*` `api/*` `create_app()` |
| Frontend | `static/js/app/*`;未接线 TSX/TS → `_unused/` |
| Examples/notes | `examples/` `docs/notes/` |
| Tests | golden pipeline + analyze contract smoke |
## Config
- 代理:`CHAN_HTTP_PROXY` / `HTTP_PROXY`(默认不强制 7897
- `DATA_SERVICE_URL` / `FLASK_PORT`
## Follow-ups(非本 ECR
- (已完成)策略与 web/tests 改为 `from chanlun import ...`;根 shim 仅兼容旧脚本
- (已完成)`chart.js` 拆为 format/view/tv/sync/tableschan_tv 共用 `static/js/app/`
-22
View File
@@ -1,22 +0,0 @@
# PRODUCT_SPEC: ECR-001 结构重构
## Goal
在不改变缠论计算结果与 Web 分析 API 对外语义的前提下,提升代码可维护性。
## User-visible behavior
| 项 | 期望 |
|----|------|
| 图表页 /chan_tv / 首页 | 功能与交互保持 |
| `/api/analyze` | 字段兼容;可增不可删 |
| Freqtrade 策略 | 无需改 import 路径即可加载引擎 |
## Non-goals
- 新买卖点规则、新 UI、新行情后端、策略超参优化
## Acceptance(产品视角)
1. 用户打开 Web 图表仍能看到笔/段/中枢/买卖点叠加。
2. 既有策略代码不因本次重构而修改。
-39
View File
@@ -1,39 +0,0 @@
# Project Profile — chan (缠论)
> Agent 第一次读这个文件。不要重新猜技术栈;偏离见 Forbidden + ADR。
## Type
Trading System(缠论分析引擎 + 可视化 Web;Freqtrade 策略目录独立、本 ECR 不改)
## Stack Lock
| Layer | Choice |
|-------|--------|
| Language | Python 3 |
| Engine package | `chanlun/` |
| Backend | Flask |
| Realtime | 无(请求式分析) |
| Database | 无(行情外部 DATA_SERVICE / CCXT / A 股接口) |
| Frontend | TradingView Charting Library + 原生 JS |
| Deployment | gunicorn / systemdweb |
| Architecture Pattern | 包化引擎 + Web services/blueprints + 根目录兼容 shim |
## Forbidden(无 ADR / 无 ECR 禁止)
- 无 ADR 修改笔 / 线段 / 中枢 / 买卖点算法语义
- 无 ECR 修改 `config/``strategies/` 策略逻辑或参数
- 无 ECR 破坏 `/api/analyze` JSON 契约(可增不可删)
- 引入 Kafka / MongoDB / 微服务拆分(除非新 ADR)
- 本轮引入 Vite/React/TS 构建流水线
## Active anchors
- ECR: ECR-001
- EXP: N/A(本变更不改交易行为语义)
- TRACEABILITY: `docs/TRACEABILITY.md`
## Pointers
- Rules: `PROJECT_RULES.md`
- Stack detail: `TECH_STACK.md`
- Memory: `AGENT_MEMORY.md`(若存在)
-24
View File
@@ -1,24 +0,0 @@
# Project Rules — chan
## Scope boundaries
1. `config/``strategies/`:Freqtrade 策略资产,默认只读;任何改动需独立 ECR。
2. `chanlun/`:缠论引擎正式包;算法变更需 L2+ ECR + 回归基线。
3. 根目录 `Chan*.py` / `TF_DF.py`:兼容 shim,保持 `from ChanLun import ChanLun` 可用。
4. `web/`:可视化与 API;契约冻结于 ECR-001。
## Change levels
- L0 docs / L1 bugfix / L2 behavior / L3 architecture — 见 ESS CHANGE_MANAGEMENT。
- 结构重构默认 L3;若触及缠论识别结果 → 升为 L2 并要求 RISK_REVIEW + EXP。
## Compatibility
- 正式导入:`from chanlun import ChanLun, TF_DF``from chanlun.core.ChanEnum import ...`
- 根目录 `Chan*.py` / `TF_DF.py` 为兼容 shim(旧脚本可用);新代码与 strategies 应使用 `chanlun`
- Web URL 与 `/api/analyze` 字段名/结构保持兼容
- 策略文件通过 `sys.path.append(仓库根)` 保证能找到 `chanlun`
## Language
- 代码注释与提交说明优先中文。
-56
View File
@@ -1,56 +0,0 @@
# RELEASE_REPORT — ECR-001 / v1.0.0
**Role:** RELEASE_MANAGER
**Date:** 2026-08-05
**System version:** `v1.0.0`(首个正式 Release
**Strategy version:** 不变(本 release 不改策略交易语义;strategies 仅 import 迁移)
**Git commit:** `74dec4e` + 本 release 文档提交
**Branch:** `dev`
## Scope
缠论引擎包化(`chanlun/`)+ Web 分层 + 前端模块化 + ESS 治理文档落盘(ECR-001 L3,行为冻结)。
## Gates
| Gate | Status | Evidence |
|------|--------|----------|
| ECR Approved | PASS | `docs/ECR/ECR-001-chan-web-restructure.md` |
| ENGINEERING_SPEC / ADR | PASS | `docs/ENGINEERING_SPEC/` · `docs/ADR/ADR-001-package-layout.md` |
| TEST_REPORT | PASS | `docs/TEST_REPORT/ECR-001.md`golden + pytest |
| CODE_REVIEW | PASS Approve | `docs/CODE_REVIEW/ECR-001.md` |
| RISK_REVIEW / EXP | N/A | 不改交易决策语义 |
| Human release intent | PASS | Human:「作为第一 release 版本」 |
## Checklist
- [x] Acceptance 已由 Reviewer 对照通过
- [x] `config/` 无策略参数变更
- [x] Rollback`git revert` / 回到 tag 前 commitWeb 回滚同 tag
- [x] 环境:Web Flask`FLASK_PORT` 默认 8128);行情依赖 `DATA_SERVICE_URL`
- [x] Tag`v1.0.0`
## Artifacts
| Kind | Path |
|------|------|
| CODE_REVIEW | `docs/CODE_REVIEW/ECR-001.md` |
| RELEASE | 本文件 |
| CHANGELOG | `docs/CHANGELOG/CHANGELOG.md` |
## Promote note
本 tag 标记 **系统软件 v1.0.0**(分析引擎 + Web)。
**非** Freqtrade Live 交易 Promote;若上 Live 策略仍需独立 Paper/EXP + Human 批准。
## Rollback
```bash
git checkout v1.0.0 # 钉在本版本
# 或回退到上一提交
git checkout e2e45bc
```
## Decision
**Release `v1.0.0`** — ship on `dev` with annotated tag.
-6
View File
@@ -1,6 +0,0 @@
# RISK_REVIEW: ECR-001
**Status:** N/A
**Reason:** 本变更仅软件结构重构;缠论识别与买卖点语义冻结,不改变交易决策逻辑。无需 Experiment / Live Promote 门禁。
若后续发现 golden 差异表明算法被意外改动,升级为 L2 并重开 RISK_REVIEW。
-11
View File
@@ -1,11 +0,0 @@
# STATE
**owner:** done
**active_ecr:** ECR-001
**phase:** released
**system_version:** v1.0.0
**updated:** 2026-08-05
## Notes
First release `v1.0.0` shipped. See `docs/RELEASE/ECR-001-v1.0.0.md`.
-29
View File
@@ -1,29 +0,0 @@
task_id: ECR-001
role: engineer
phase: coding
tool: cursor
input:
- docs/PROJECT_PROFILE.md
- docs/PROJECT_RULES.md
- docs/TECH_STACK.md
- docs/ECR/ECR-001-chan-web-restructure.md
- docs/ENGINEERING_SPEC/ECR-001-restructure.md
- docs/ADR/ADR-001-package-layout.md
- docs/HANDOFF/ECR-001-architect-to-engineer.md
output:
- chanlun/
- root shims
- web/services web/api web/config
- web/static/js/app/
- tests/ + docs/TEST_REPORT + IMPLEMENTATION_REPORT + CHANGELOG
forbidden:
- redesign_scope
- modify config/ or strategies/
- change chan algorithm semantics
- break /api/analyze contract
next_agent: reviewer
notes: "1B+2B; behavior freeze; shim for strategies"
-20
View File
@@ -1,20 +0,0 @@
# Tech Stack — chan
## Engine
- Python, pandas, numpy, TA-Lib / technical
- 包名:`chanlun`
- 流水线:KLU → KLC → BI → SBI → SEG → ZS → BSP`ChanLun` / `TF_DF` 门面)
## Web
- Flask + Jinja2 templates
- TradingView Charting Library`web/charting_library/`
- 前端运行时:原生 JS`web/static/js/app/`
- 行情:`DATA_SERVICE_URL` / CCXT / A 股数据服务
## Out of scope this release
- data_provider 仓库内重建
- React/TS 构建
- Freqtrade config/strategies 重构
-26
View File
@@ -1,26 +0,0 @@
# TEST_REPORT — ECR-001
**Date:** 2026-08-05
## Commands
```bash
python tests/generate_golden.py --check
python -m pytest tests/test_golden_pipeline.py web/tests/test_analyze_contract.py -q
python -c "from ChanLun import ChanLun, TF_DF; from ChanEnum import Chan_BI_DIR"
```
## Results
| Case | Result |
|------|--------|
| Golden pipeline | PASScounts klu=400 klc=208 bi=14 seg=2 zs=0 bsp=3 |
| Shim imports | PASS |
| Analyze contract keys + routes | PASS |
| pytest suite above | **5 passed** |
| `git diff config strategies` | empty(无改动) |
## Notes
- Golden 经 CSV 往返 + 浮点 round(10) 稳定化
- 行情服务不可达时 metadata refresh 会警告,不影响路由注册测试
-9
View File
@@ -1,9 +0,0 @@
# TRACEABILITY — ECR-001
| ECR | Requirement | Spec | Code | Test |
|-----|-------------|------|------|------|
| ECR-001 | 引擎包化 + shim | ENG-001 / ADR-001 | `chanlun/` + root shims | import smoke + golden |
| ECR-001 | TF_DF 门面拆分 | ENG-001 | `chanlun/pipeline/` | golden pipeline |
| ECR-001 | Web 分层 | ENG-001 | `web/services` `web/api` | analyze contract |
| ECR-001 | 前端模块化 | ENG-001 | `web/static/js/app/` | manual / smoke |
| ECR-001 | 策略零改动 | PROFILE | no edits under strategies/ | git diff empty |
-239
View File
@@ -1,239 +0,0 @@
均线
5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 16h, 1d, 2d, 3d, 1w, 2w, 1M
参考时间周期
大周期:1h
小周期:15m
价格在1h周期ema156之上为大周期上涨,反之为大周期下跌
在1h大周期上涨时,小周期15m,下跌触碰到
顺大逆小
大周期看多,小周期跌完做多,跌完:顶分型和EMA52归零轴反弹
大周期看空,小周期涨完做空,涨完:顶分型和EMA52归零轴反抽
中枢分类
常规中枢
上升中枢
收敛中枢
扩散中枢
下行中枢
止损放到顶底分型的高低点
1. 从大周期开始找到价格接近ema52,MACD也接近零轴的周期,需要看这个周期的长级别是否高位空,大趋势方向
2. 然后去小于这个时间周期的周期找买卖点,小级趋势方向和大趋势相反并且开始反向,小级别需要检查MACD是否归零轴反转,同时价格是否接近EMA52
EMA52线的反弹比零轴的反弹弱
EMA52线和MACD白线同时归零轴同时满足的话是完美形态,最佳买卖点
跟随策略,先调整小级别,然后依次往大级别调整,直到整个周期结束
大级别MACD在零轴之上为多头趋势,回调踩EMA52做多,直到跳空背离,隐形,更大级别EMA52顶部归零轴平仓
大级别MACD在零轴之下为空头趋势,上涨踩EMA52做空,直到跳空背离,隐形,更大级别EMA52底部归零轴平仓
盘整趋势在零轴上下移动,价格在大级别EMA52之间移动,根据连续跳空背离,隐形,归零轴EMA52线开仓和平仓
MACD归零轴的两种情况,两者是或的关系,满足任意一种都是归零轴,归零轴的四种走势:
1. K线下跌或者上涨后触碰当前时间级别的EMA52附近
2. MACD的白线快线无限接近零轴,
3. MACD归零轴时,如果此时k线始终保持在EMA24附近,如果一直是EMA24之上之后出现反弹行情就会很大(最强反弹)这种反弹是2个时间级别同时归零轴形成的反弹,容易创新高新低,一般出现在强势行情。
4. K线先触碰EMA52,而MACD黄白线都未归零轴
MACD归零轴反弹/反抽的完美形态是K线触碰EMA52附近,MACD的白线无限接近零轴之后出现上涨或下跌
高位空
当MACD的黄白线远离零轴运行时,与零轴有一定的距离,形成了零轴的高危形态。随着K线出现缓慢上涨或者下跌,或者盘整,MACD的能量柱出现衰减,同时能量柱与MACD黄白线形成空间夹角,随着能量柱越来越小,夹角越来越大形成高位空。这种容易形成回调下跌,特别是导致次一级的MACD穿越零轴
穿越零轴的定义,需要同时满足以下条件
1. 在某个时间级别,k线的价格或者指数有效击穿当前时间级别的EMA52
2. MACD黄线慢线有效击穿零轴
MACD黄白线和零轴的几种形态:
离开零轴
当MACD黄白线穿过零轴那么进入第一阶段离开零轴,此时能量柱变化越来越大,不断增长,k线加速上涨
高位
当MACD黄白线离开零轴,到一高点时,能量柱此时处于最大,开始减弱时MACD处于高位,高位时MACD黄白线和能量柱是同向的,高位过后是高位空
高位空
当MACD黄白线处于高位,随着K线出现缓慢上涨或者横盘整理,MACD黄白线保持高位出现平滑横盘走势,此时,MACD的能量柱出现衰减变化,同时能量柱和黄白线之间形成一定的空间夹脚,随着能量柱的不断衰减就导致黄白线和能量柱之间的空间夹脚越来越大,因此就形成高位空
归零轴
当MACD黄白线在高位,驱动K线上涨的能量所产生的加速度小于或者等于零,K线减速上涨或者下跌,能量变化越来越小,能量柱呈现出一根比一根短的排列方式
穿零轴
同时满足以下两个条件
1. 在某个时间级别,K线的价格或者指数有效击穿当前时间级别的EMA52
2. 当前时间级别MACD的黄线慢线有效击穿零轴
有效的定义是:当前K线正好击穿EMA52的支撑位后,如果当前这个K线收盘后的第二根K线任然保持在EMA52之下才算有效击穿,如果只是上下影线击穿,后期K线任然运行在EMA52之上不算有效击穿,MACD同理
零轴缠绕/纠缠
具体是指MACD跟零轴无限接近或者缠绕的状态,或者是已经完成第一次归零轴调整之后,在等待大级别调整的时候。
分为无限接近和上下缠绕状态。代表本级别已经调整完毕,即不产生反弹支撑,也不形成阻力压力,不考虑次级别的技术形态,通过更大的时间级别或者其他时间级别进行分析
隐形形态
当MACD的黄白线发生交叉时,必有相应的能量柱释放出来。金叉,则会释放零轴之上的能量柱,反之死叉,则会释放出零轴之下的能量柱。如果黄白线无交叉,而k线出现上涨或者下跌,则代表能量柱的隐形状态,代表K线的运行无能量配合,那么这种上涨或者下跌就变成无效的结果。无能量配合的上涨必下跌,无能量配合的下跌必反弹
1. 远离零轴的高位隐形形态
某个时间级别的MACD黄白线处在远离零轴的高位,且K线继续拉升上涨或者下跌,但是并没有释放出相对应方向的能量柱,此后,K线将出现归零轴的走势下跌或者上涨。此形态是高位隐形形态+高位空的形态,则当前级别的MACD在后续走势中必将出现会拉零轴甚至穿零轴的走势。因此,远离零轴的高位隐形形态解决的是当前时间级别归零轴的需求。
2. 归零轴的隐形形态
归零轴后反弹出现的隐形形态,我们称之为归零轴的隐形形态。这种形态必然会导致当前级别的MACD黄白线出现穿零轴的走势。MACD在归零轴的情况下,零轴所提供的反弹或者支撑能量是最大的,而如果零轴所能提供的最大能量都产生不了相应的能量柱,这种支撑就变成了无效的支撑,MACD的黄白线就只能击穿零轴渠道零轴的反方向。
如何确认隐形形态的顶部
1. 通过顶底分型来确认
2. 通过次级别的背离确认隐形高位
3. 通过单位调整周期之内的时间级别嵌套逻辑确认隐形的高位
顶底分型在K线动能理论的应用
1. 分型对应的MACD处在高位空的形态
2. 分型所对应的MACD出现隐形形态
零轴之上高位隐形 + K线顶分型 = 下跌归零轴
零轴之上归零轴隐形 + K线顶分型 = 下跌穿零轴
零轴之下高位隐形 + K线底分型 = 上涨归零轴
零轴之下归零轴隐形 + K线底分型 = 上涨穿零轴
K线的3种盘整结构,上涨和下跌均适用,下面是上涨结构的分析,下跌反之
1. K线强势的走势结构
一般应用是在单边上涨行情中,某个时间级别的K线经过一轮拉升之后,进入调整的阶段,此时K线如果在高位一直处于横盘震荡走势,而MACD的黄白线却趋于归零轴运行,则当黄白线归零轴之后,出现有效反弹行情。
2. K线超强势结构
超强势结构往往容易发生破前高的走势。在某个时间级别,当K线经过一轮强势拉升上涨后,变为倾斜向上缓慢上行,K线一直处于这个时间级别的EMA24之上或者附近,经过一段时间的运行,导致当前级别的MACD黄白线无限归零轴的形态,此时,K线往往容易出现速度快,力度强且破前高的走势。对于超强势调整结构,最重要的是次级别不破零轴,并保持在当前级别的EMA24之上或者附近,而当前级别的黄白线运动一段时间后无限趋于零轴。一般来说,超强势调整结构容易出现在某个大的时间级别处于单边行情中。
3. 弱势调整结构
在弱势调整结构中,K线是通过下跌的方式快速地将当前级别的MACD的黄白线拉回零轴,同时,K线的价格也会快速下跌到本级别EMA52附近位置。在弱势调整结构中,MACD归零轴后所形成的支撑反弹往往不会破前高,而是走出能量不足的走势形态。在此结构中,只有MACD的黄白线出现死叉后才会继续下跌,并且只有在弱势调整结构中,死叉下跌才有效。
单位调整周期
指K线在某个时间级别,MACD的黄白线由零轴出发到远离零轴再到回到零轴的区间段称为这个时间级别的调整周期。可以分为零轴同方向和穿零轴出发两种。一个单位调整周期的起点往往是买点,同时终点也是另一个周期的买点。单位周期的判断依据是黄白线归零轴不是量能柱的多少。
注意:如果单位调整周期的起始和终止都直接穿零轴的,代表这个时间级别在运行的过程中,是无效的时间级别,也就是这个时间级别在我们的分析的过程中要跳过的。
隐形单位调整周期
MACD黄白线归零轴后,由于零轴的支撑或者压力而发生的反弹或者反抽,没有释放出相应的能量柱而形成的周期,为隐形单位调整周期。隐形单位调整周期会引起黄白线反向穿零轴的走势。
零轴粘合
MACD黄白线在刚穿零轴的时候会出现:黄白线离零轴的距离比较近,黄白线沿着能量柱运行,黄白线在运行的过程中没有释放出反向能量柱。零轴粘合属性是:强支撑,弱反弹。这种形态我们更强调支撑能量,弱化反弹属性。零轴粘合几乎是贴近零轴运行,因此其反弹的动能就是为无效。斜率小,黄白线喝能量柱之间基本没有空隙。能量柱可以略微减弱但是不能释放下跌方向的能量柱。由此可见黄线没有跟白线有交叉,黄线对白线有支撑作用。如果当前时间级别内部逻辑关系走完,这个时间级别则被视为无效时间级别。
零轴倒挂
MACD黄白线在穿零轴的时候与零轴的距离比较近,同时黄白线沿着能量柱运行,在运行的过程中,能量柱衰减导致它跟黄白线之间形成夹角空位,同时黄白线产生交叉并释放反向能量柱。
1. 黄白线穿零轴后未远离零轴形成一定高度,而是靠近零轴运行
2. 能量柱的衰减导致其与黄白线之间形成了一定的夹角空位
3. 黄白线在运行的过程中发生了交叉而放出反向能量柱
弱支撑,弱反弹。如果当前时间级别内部逻辑关系走完,这个时间级别则被视为无效时间级别。如果某个时间级别的盘口形态是零轴倒挂,那么这个时间级别很容易直接击穿零轴,而无法形成有效的反弹和反抽行情。
零轴粘合和倒挂的有效性
在上涨行情中,当K线处在零轴粘合或者零轴倒挂的形态时,如果K线的价格处在当前级别的EMA52之上或者处在多级别EMA均线交汇处之上和附近时,此时由于K线受到EMA均线的支撑,零轴粘合或者零轴倒挂反而容易形成强支撑的特点。此时需要结合MACD的形态和K线均线支撑综合分析盘面。
线段
上涨线段是指MACD的黄白线第一次上穿零轴到下一次下穿零轴中间的运行区域,以黄线穿零轴为准。在这个线段找到阶段性的卖点,阶段性高点。
下跌线段是指MACD的黄白线第一次下穿零轴到下一次上穿零轴中间的运行区域,以黄线穿零轴为准。在这个线段找到阶段性的买点,阶段性低点。
1. 同一条线段是比较背离的区域,背离的比较不可再跨线段的区域进行
2. 线段可以将不同时间级别的K线化繁为简,一个时间级别的形态只需要确定盘面所处的线段即可
背离
在K线分析中,背离是指价格跟能量之间的相悖性,当价格创出阶段性新高点,而推动价格上涨的能量出现衰减,这种情况就是背离,也就是说价格和能量之间产生了不匹配关系。
顶背离 - 确认卖点
在某个时间级别,MACD运行在零轴上方,当K线的价格走势一峰比一峰高,价格一直处在上涨趋势中时,MACD的黄白线或者能量柱的高度却一波比一波低,即当价格的高点比前一次价格的高点高,而MACD指标的高点比前一次高点低,这种形态称为顶背离形态。需要注意的是,这两次高点在运行的过程中,MACD的黄白线始终处在同一条上涨线段周期中,不能跨线段比较。
顶背离包括黄白线背离和柱背离两种情况,也可能出现多次背离的情况。线背离以白线的最高点作为参考点,K线上影线最高点作为K线的参考点,能量堆的最高点可能和K线的最高点不是一一对应,但是不影响判断,可以使用能量堆的面积进行计算。
底背离 - 确认买点
在某个时间级别,MACD运行在零轴下方,一般出现价格的低位区。当K线的价格走势持续下跌,而MACD的黄白线或者能量柱却持续靠近零轴,即当前价格的低点比前一次低点好要低,而MACD指标的低点却比前一次低点高,但是下跌能量却在衰减的现象,于是价格跌无可跌,是短期买入信号
底背离包括黄白线背离和柱背离两种情况,也可能出现多次背离的情况。线背离以白线的最低点作为参考点,K线上影线最低点作为K线的参考点,能量堆的最低点可能和K线的最低点不是一一对应,但是不影响判断,可以使用能量堆的面积进行计算。
顶底背离高低点有效性的方法
1. 通过顶底分型确认顶底背离的高低点
2. 通过次级别的背离确认当前级别的背离高点,这里的次级别不是单指一级次级别,可以是多级的
单位调整周期内的连续跳空背离
K线经过一波上涨或者下跌后,MACD的黄白线由高位回零轴且未归到零轴,能量柱在连续的衰减调整过程中,反而逐渐由衰减转为增长,于是就出现了跳空走势,这种走势称为MACD的连续跳空形态。
1. 连续跳空发生在单位调整周期内,线跟柱在零轴同方向
2. MACD的能量柱需包含在黄白线之内
3. 能量柱在衰减的过程中未放出反向能量柱,而是由衰减转为增长
单位调整周期之内的背离是价格跟能量柱之间的关系,同时单位调整周期之内的背离,解决的是归零轴的需求
连续跳空的应用和意义
单位调整周期之内,能量堆连接在一起的时候,出现的价格跟能量柱之间的关系即为连续跳空,而连续跳空的意义
1. 连续跳空背离解决归零轴的需求,主要是小级别的走势,比如5分钟的连续跳空背离则为归零轴走势,因为5分钟级别只包含一个3分钟级别,是5分钟内的小级别,因此,5分钟级别如果出现连续跳空背离,会导致5分钟级别MACD黄白线归零轴走势
2. 连续跳空更大的作用是确认穿零轴之后的第一个背离参考点,当MACD黄白线穿零轴的时候,最重要的是确认当前线段的1号参考点,有了1号参考点,后续行情才有参考的对象。连续跳空往往发生在MACD黄白线刚刚穿零轴的位置,一般由零轴粘合的形态演化而成连续跳空,这是因为零轴粘合具有强支撑的特点,容易形成跳空走势。
穿零轴时的参考点确认方法
1. 如果MACD黄白线穿零轴之后出现连续跳空,则以连续跳空的高点作为1号参考点,此时连续跳空形态代表新的单位周期调整周期的开始。当黄白线穿零轴后,黄线之后的第一个能量堆没有更高的能量柱,而是到了第一个跳空高低点出现第一个高低点。穿零轴产生的能量柱左侧黄白线处在下跌线段,右侧处在上涨线段。因此穿零轴的能量柱被切成两半,左侧在下跌线段,所以不能以黄线击穿零轴的左侧作为上涨线段的1号参考点。背离一定要在同一线段中进行比较,而不能跨线段找背离。那么穿零轴之后跳空产生的高低点才能作为1号参考点。后续的背离要以这个参考点进行比较。
2. 如果MACD黄白线穿零轴后无连续跳空,没有更高的能量柱出现则不能确定1号参考点,如果穿零轴后没有找到更高低的能量柱,那么这个线段的第一个单位调整周期是无效周期。
3. 如果黄线穿零轴之后有更高低的能量柱,则以更高的能量柱作为1号参考点,此参考点可以是穿零轴时的能量柱高点确定。
单位调整周期之内的分立跳空背离
在某个时间级别,当一个单位调整周期之内包含两个或者两个以上的能量堆,能量堆之间被反向能量堆分隔开,同时MACD黄白线一直处于远离零轴的高位,未归零轴,且一直保持原有的趋势运行,被分割的能量堆与黄白线都处在零轴的同方向,能量堆包含在黄白线之内,就形成分离跳空形态。
分立跳空背离
如果在某个时间级别的单位调整周期内,黄白线未归零轴,K线在经过一段时间的调整并放出反向能量柱之后,继续沿着原有方向运行,导致再一次出现的能量堆,同时黄白线再一次远离零轴,能量堆和能量堆之间形成背离关系,这种就是分离跳空背离。分立跳空背离解决的是归零轴的需求
分立跳空背离 + 黄白线高位 = 归零轴
分立跳空不背离 = 单边上涨或者下跌行情
最佳买卖点
单位周期之内 + 隐形 + 分立跳空 + 背离 + 黄白线高位空
跳空产生的原理:跳空的产生是当前级别之下的小级别归零轴反弹或反抽导致的,比如1小时时间级别的单位调整周期跳空,是因为1小时时间级别之内包含3分钟,5分钟,15分钟,30分钟这些下级别。1小时级别的MACD归零轴的途中,必然导致其内部的小级别先于本级别归零轴。因此,当这些小级别归零轴后,如果产生反弹反抽的走势,则会导致当前1小时级别的MACD黄白线再一次被拉高,此时便产生了跳空的走势。
时间级别在高位中归零轴的顺序是:由小级别到大级别依此归零轴。当K线经过一轮拉升下跌后,当前级别的MACD黄白线处在高位,此时如果K线进入调整状态,则这个时间级别所包含的小级别由小到大依次归零轴,直至本级别归零轴为止,如此才完成了次级别的单位调整周期的调整。
单位周期之内的分立跳空顶背离
MACD黄白线在零轴之上第一个单位调整周期 + 分立跳空背离(一次或多次) + 黄白线处在零轴的高位空 + 分立跳空隐形状态
单位周期之内的分立跳空底背离
MACD黄白线在零轴之下第一个单位调整周期 + 分立跳空背离(一次或多次) + 黄白线处在零轴的高位空 + 分立跳空隐形形态
单位调整周期之内的跳空非背离
如果在单位调整周期内发生了跳空的形态,当跳空能量堆的高度高于前一个能量堆的高度,此时的跳空则为非背离跳空。非背离跳空可以理解为单位周期的单边行情,前一个能量堆失效,以新的最高的能量堆作为后续行情的1号参考点。不管是连续跳空还是分立跳空都遵守这个法则。
单位调整周期之间的背离
单位调整周期之间的背离,是指两个或者两个以上的单位调整周期相比较而建立的关系,相比较的周期必须在同一个线段周期内,不可跨线段比较。当K线在某个时间级别的线段中,MACD经过了一个单位调整周期的运行,黄白线在此回零轴后,由于零轴的支撑反弹或压力反抽的作用,因此出现第二个单位调整周期,当第二个单位调整周期的黄白线这里特指白线离开零轴的距离小于第一个单位调整周期黄白线离开零轴的距离时,单位调整周期之间就产生了相悖的关系,即价格穿新高或新低,而白线能量区出现了减弱或增强,这种背离称为单位调整周期之间的背离,单位调整周期之间的背离也叫区间背离。
上涨线段单位调整周期之间的顶背离为卖点(前提:长级别MACD在高位)
下跌线段单位调整周期之间的底背离为买点(前提:长级别MACD在高位)
单位调整周期之间的背离,其核心的本质是描述某个时间级别在线段中的运行逻辑,即为线段完成调整的重要依据。
单位调整周期之间的背离是为了满足线段调整的需求
判断某个时间级别线段结束趋势的依据为:在某个时间级别的线段中,第二个单位调整周期与第一个单位调整周期之间产生背离关系,即为此线段终结的依据,而后出现的单位调整周期无论归零轴多少次,从能量产生的逻辑上都是依次减弱直至趋于零。
单位调整周期之间的背离而穿零轴变盘的依据是:当某个级别在线段中出现周期之间的背离形态,同时完成了线段的调整,但是其长级别MACD的黄白线处在高位空的形态时,当前级别的背离会导致穿零轴走势。
在K线的时间逻辑中,判断一个时间级别完成自己的当值任务的标准是:本级别在当前的线段中产生了周期间的背离关系,并趋向于零轴的调整。本级别是否穿零轴并非由本级别背离的属性决定,而是由长级别决定。因此,当本级别完成线段调整之后,就要看长级别处在什么形态之下,长级别的形态属性决定了后续的行情走势。
时间级别升级:是指当本级别在其线段中产生了背离关系而趋向于零轴,达到了平衡状态且保持在零轴之上(代表完成了其线段的调整任务),其长级别同时也处在归零轴的形态,此时当前级别即要发生时间级别升级。时间级别升级之后,本级别将会产生新的线段,之后本级别的线段关系则升级为线段与线段之间的关系。
确定时间级别升级的条件
1. 当前级别多次归零轴背离而完成了线段的调整,之后新的单位调整周期MACD的白线DIF比前一个单位调整周期的白线DIF高
2. 当前级别完成线段调整,长级别MACD的黄白线无限接近零轴
线段背离
当某个时间级别升级之后,便产生了一条新的线段,如果线段和线段之间构成相悖关系时,我们称为线段背离,而线段背离则必然导致当前时间级别穿零轴。线段背离比较的是两个线段中最高或最低的白线DIF。
动能不足
如果K线走势中不破前高点上涨动能衰减,或者不破前低点,下跌动能也衰减,这种形态称为动能不足,本质上和背离是一样的,都是能量衰减的一种变现,背离所具有的属性和原理适用于动能不足。
单位调整周期内,之间,线段之内,线段之间的隐形背离或隐形动能不足
主级别归零轴启动反弹的内部过程:
1. 第一过程,主级别所包含的小级别在零轴之下首先完成超跌反弹的过程
2. 第二过程,小级别完成底部调整后,才正式启动主级别归零轴反弹
底部形态变盘的四个阶段
确认底部区域需要四个条件
1. 确认引起下跌行情中的主要时间级别
当K线出现下跌行情时,一定是某个时间级别在零轴之上进行的归零轴调整而引起的,受到零轴的引力作用,这个级别的MACD黄白线会被拉回零轴。这个时间级别是:上穿零轴后第一次开始归零轴的时间级别。
2. 确认主级别归零轴后的形态属性
K线价格要触碰到EMA52均线的位置附近,同时MACD的黄白线无限接近零轴。也就是说,K线的价格一旦触碰到EMA52的位置附近,因为这个位置能否形成支撑反弹,主要是看归零轴的这两个条件能否一直保持,并开始归零轴反弹的第一个过程,即主级别所包含的小级别在零轴之下的超跌反弹。
3. 在归零轴的形态满足条件的情况下,确认子级别是否有高位空的形态
当主级别第一次触碰到当前级别EMA52均线的位置附近时,我们要看包含在主级别之下的小级别在零轴的下方是否产生了高位空的形态。只有当这些小级别出现高位空的形态时,才能出现有效的归零轴的超跌反弹,而超跌反弹的变向则为主级别归零轴后出现的止跌反弹的走势。
4. 确认零轴之下的最大子级别运行底部变盘的四个阶段
当主级别进入底部区域后,小级别的超跌反弹将逐级别开始,而时间级别的运行逻辑则是从小级别依次运行。因此,当主级别包含的小级别零轴之下的最后一个子级别完成调整后,主级别的归零轴反弹才正式开启。这些零轴之下的子级别的运行逻辑即为主级别底部变盘的四个阶段。
第一阶段:零轴之下的子级别的单边下跌
第二阶段:零轴之下的子级别的超跌反弹
第三阶段:零轴之下的子级别的归零轴反抽之后产生背离/动能不足
第四阶段:零轴之下的子级别跟零轴形成粘合或者零轴纠缠
底部形态V字反转的条件
当主级别归零轴后,由于小级别的超跌反弹和反抽容易在行情的底部走出横盘震荡的走势,当某个时间级别的超跌反弹导致K线突破这个横盘区间时,我们便把这种走势称为V字反转的走势
V字反转走势发生的条件
1. 主级别保持归零轴形态且MACD出现收敛的状态
2. 零轴之下大多数小级别已经完成线段调整,并形成了底部的横盘结构,剩下未调整的级别没有明显的高位空
3. 下一个长级别的超跌反弹归零轴所触碰到EMA52均线的位置需要有效突破底部横盘震荡的区间
MACD收敛
K线在下跌行情中进入底部区域,当MACD的黄白线由倾斜向下趋于零轴的方向转为拐头形态,同时下跌能量柱由逐渐增长转为衰减时,我们把这种形态称为MACD收敛形态
抢底原理
当行情运行到当前级别底部调整的第三阶段时,即背离/下跌动能不足时,即为我们最佳买入机会。因为一旦启动了V字反转的走势,K线的价格将不容易再出现大的回调机会,而V字反转的行情极容易导致剩下的海味调整的其他小级别直接击穿零轴,或者出现以横代跌的走势而不在出现明显的反抽下跌。这就是V字反转结构形成的条件下的抢底原理。
第一代时间级别当值的有效性满足两个条件
1. 第一代时间级别不能击穿零轴,如果击穿零轴,则本级别当值作用失效
2. 每个当值的第一代时间级别的反弹行情必须推动其长级别在上涨线段中的第一个单位调整周期处于高位的形态

Some files were not shown because too many files have changed in this diff Show More